aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/3rdparty/masm/assembler/ARM64Assembler.h24
-rw-r--r--src/3rdparty/masm/assembler/ARMAssembler.cpp444
-rw-r--r--src/3rdparty/masm/assembler/ARMAssembler.h1129
-rw-r--r--src/3rdparty/masm/assembler/AbstractMacroAssembler.h11
-rw-r--r--src/3rdparty/masm/assembler/AssemblerBuffer.h2
-rw-r--r--src/3rdparty/masm/assembler/LinkBuffer.cpp17
-rw-r--r--src/3rdparty/masm/assembler/LinkBuffer.h38
-rw-r--r--src/3rdparty/masm/assembler/MacroAssembler.h18
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerARM.cpp99
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerARM.h1386
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerARM64.h23
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerARMv7.h13
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerCodeRef.h9
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerMIPS.h14
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerSH4.h2293
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerX86.h5
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerX86Common.h32
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerX86_64.h20
-rw-r--r--src/3rdparty/masm/assembler/SH4Assembler.h2152
-rw-r--r--src/3rdparty/masm/assembler/X86Assembler.h33
-rw-r--r--src/3rdparty/masm/masm-defs.pri1
-rw-r--r--src/3rdparty/masm/masm.pri14
-rw-r--r--src/3rdparty/masm/qt_attribution.json2
-rw-r--r--src/3rdparty/masm/stubs/ExecutableAllocator.h4
-rw-r--r--src/3rdparty/masm/stubs/Options.h2
-rw-r--r--src/3rdparty/masm/stubs/SuperSampler.h50
-rw-r--r--src/3rdparty/masm/stubs/WTFStubs.cpp6
-rw-r--r--src/3rdparty/masm/stubs/runtime/ConcurrentJSLock.h53
-rw-r--r--src/3rdparty/masm/stubs/runtime/RegExpKey.h46
-rw-r--r--src/3rdparty/masm/stubs/runtime/VM.h50
-rw-r--r--src/3rdparty/masm/stubs/wtf/HashMap.h58
-rw-r--r--src/3rdparty/masm/stubs/wtf/HashSet.h67
-rw-r--r--src/3rdparty/masm/stubs/wtf/Optional.h83
-rw-r--r--src/3rdparty/masm/stubs/wtf/PassRefPtr.h14
-rw-r--r--src/3rdparty/masm/stubs/wtf/Vector.h7
-rw-r--r--src/3rdparty/masm/stubs/wtf/text/CString.h4
-rw-r--r--src/3rdparty/masm/stubs/wtf/text/StringBuilder.h52
-rw-r--r--src/3rdparty/masm/stubs/wtf/text/WTFString.h15
-rw-r--r--src/3rdparty/masm/stubs/wtf/unicode/Unicode.h30
-rw-r--r--src/3rdparty/masm/stubs/wtf/unicode/utypes.h1
-rw-r--r--src/3rdparty/masm/stubs/yarr/YarrUnicodeProperties.cpp70
-rw-r--r--src/3rdparty/masm/wtf/Assertions.h2
-rw-r--r--src/3rdparty/masm/wtf/FilePrintStream.cpp11
-rw-r--r--src/3rdparty/masm/wtf/FilePrintStream.h9
-rw-r--r--src/3rdparty/masm/wtf/OSAllocatorPosix.cpp87
-rw-r--r--src/3rdparty/masm/wtf/Platform.h12
-rw-r--r--src/3rdparty/masm/wtf/PrintStream.h17
-rw-r--r--src/3rdparty/masm/wtf/StdLibExtras.h5
-rw-r--r--src/3rdparty/masm/wtf/VMTags.h8
-rw-r--r--src/3rdparty/masm/yarr/Yarr.h36
-rw-r--r--src/3rdparty/masm/yarr/YarrCanonicalize.h143
-rw-r--r--src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp823
-rw-r--r--src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js304
-rw-r--r--src/3rdparty/masm/yarr/YarrCanonicalizeUnicode.cpp591
-rw-r--r--src/3rdparty/masm/yarr/YarrErrorCode.cpp96
-rw-r--r--src/3rdparty/masm/yarr/YarrErrorCode.h65
-rw-r--r--src/3rdparty/masm/yarr/YarrInterpreter.cpp877
-rw-r--r--src/3rdparty/masm/yarr/YarrInterpreter.h100
-rw-r--r--src/3rdparty/masm/yarr/YarrJIT.cpp1655
-rw-r--r--src/3rdparty/masm/yarr/YarrJIT.h120
-rw-r--r--src/3rdparty/masm/yarr/YarrParser.h549
-rw-r--r--src/3rdparty/masm/yarr/YarrPattern.cpp962
-rw-r--r--src/3rdparty/masm/yarr/YarrPattern.h350
-rw-r--r--src/3rdparty/masm/yarr/YarrSyntaxChecker.cpp15
-rw-r--r--src/3rdparty/masm/yarr/YarrSyntaxChecker.h11
-rw-r--r--src/3rdparty/masm/yarr/YarrUnicodeProperties.h (renamed from src/3rdparty/masm/assembler/MacroAssemblerSH4.cpp)37
-rw-r--r--src/3rdparty/masm/yarr/create_regex_tables (renamed from src/3rdparty/masm/create_regex_tables)31
-rw-r--r--src/3rdparty/masm/yarr/generateYarrCanonicalizeUnicode204
-rw-r--r--src/3rdparty/masm/yarr/yarr.pri5
-rw-r--r--src/imports/builtins/builtins.qmltypes6
-rw-r--r--src/imports/folderlistmodel/folderlistmodel.pro2
-rw-r--r--src/imports/folderlistmodel/plugin.cpp9
-rw-r--r--src/imports/folderlistmodel/plugins.qmltypes4
-rw-r--r--src/imports/folderlistmodel/qquickfolderlistmodel.cpp18
-rw-r--r--src/imports/handlers/handlers.pro11
-rw-r--r--src/imports/handlers/plugins.qmltypes308
-rw-r--r--src/imports/handlers/qmldir5
-rw-r--r--src/imports/imports.pro12
-rw-r--r--src/imports/labsmodels/labsmodels.pro11
-rw-r--r--src/imports/labsmodels/plugin.cpp (renamed from src/imports/handlers/plugin.cpp)38
-rw-r--r--src/imports/labsmodels/plugins.qmltypes41
-rw-r--r--src/imports/labsmodels/qmldir3
-rw-r--r--src/imports/layouts/plugin.cpp8
-rw-r--r--src/imports/layouts/plugins.qmltypes4
-rw-r--r--src/imports/localstorage/dependencies.json2
-rw-r--r--src/imports/localstorage/localstorage.pro2
-rw-r--r--src/imports/localstorage/plugin.cpp60
-rw-r--r--src/imports/localstorage/plugins.qmltypes2
-rw-r--r--src/imports/models/dependencies.json2
-rw-r--r--src/imports/models/plugin.cpp17
-rw-r--r--src/imports/models/plugins.qmltypes2
-rw-r--r--src/imports/particles/plugin.cpp9
-rw-r--r--src/imports/particles/plugins.qmltypes4
-rw-r--r--src/imports/qtqml/plugins.qmltypes20
-rw-r--r--src/imports/qtqml/qtqml.pro4
-rw-r--r--src/imports/qtquick2/dependencies.json2
-rw-r--r--src/imports/qtquick2/plugin.cpp9
-rw-r--r--src/imports/qtquick2/plugins.qmltypes414
-rw-r--r--src/imports/qtquick2/qtquick2.pro2
-rw-r--r--src/imports/settings/dependencies.json2
-rw-r--r--src/imports/settings/plugin.cpp10
-rw-r--r--src/imports/settings/plugins.qmltypes29
-rw-r--r--src/imports/settings/qqmlsettings.cpp71
-rw-r--r--src/imports/settings/qqmlsettings_p.h7
-rw-r--r--src/imports/settings/settings.pro2
-rw-r--r--src/imports/shapes/plugin.cpp17
-rw-r--r--src/imports/shapes/plugins.qmltypes4
-rw-r--r--src/imports/shapes/shapes.pro26
-rw-r--r--src/imports/sharedimage/plugins.qmltypes2
-rw-r--r--src/imports/statemachine/dependencies.json2
-rw-r--r--src/imports/statemachine/plugin.cpp9
-rw-r--r--src/imports/statemachine/plugins.qmltypes2
-rw-r--r--src/imports/statemachine/signaltransition.cpp6
-rw-r--r--src/imports/statemachine/signaltransition.h4
-rw-r--r--src/imports/testlib/TestCase.qml18
-rw-r--r--src/imports/testlib/main.cpp9
-rw-r--r--src/imports/testlib/plugins.qmltypes4
-rw-r--r--src/imports/window/plugin.cpp17
-rw-r--r--src/imports/window/plugins.qmltypes4
-rw-r--r--src/imports/xmllistmodel/plugin.cpp9
-rw-r--r--src/imports/xmllistmodel/plugins.qmltypes4
-rw-r--r--src/imports/xmllistmodel/qqmlxmllistmodel.cpp40
-rw-r--r--src/imports/xmllistmodel/qqmlxmllistmodel_p.h2
-rw-r--r--src/imports/xmllistmodel/xmllistmodel.pro3
-rw-r--r--src/particles/qquickcustomaffector.cpp2
-rw-r--r--src/particles/qquickitemparticle_p.h1
-rw-r--r--src/particles/qquickparticleemitter.cpp2
-rw-r--r--src/particles/qquicktrailemitter.cpp2
-rw-r--r--src/particles/qquickv4particledata.cpp4
-rw-r--r--src/plugins/plugins.pro2
-rw-r--r--src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp79
-rw-r--r--src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h3
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp142
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h19
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp21
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h16
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp280
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h24
-rw-r--r--src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp28
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qmldbg_preview.pro29
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp213
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.h100
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp443
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.h136
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp197
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h116
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp472
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h154
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp113
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.h (renamed from src/quick/scenegraph/compressedtexture/qsgtexturefilehandler_p.h)46
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp195
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h113
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.json3
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp50
-rw-r--r--src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.h (renamed from src/quick/scenegraph/compressedtexture/qsgktxhandler_p.h)31
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp19
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h3
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp17
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h1
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp20
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h3
-rw-r--r--src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp4
-rw-r--r--src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h2
-rw-r--r--src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp52
-rw-r--r--src/plugins/qmltooling/qmltooling.pro9
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.cpp14
-rw-r--r--src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.h2
-rw-r--r--src/qml/animations/qabstractanimationjob.cpp5
-rw-r--r--src/qml/animations/qabstractanimationjob_p.h2
-rw-r--r--src/qml/animations/qanimationgroupjob_p.h2
-rw-r--r--src/qml/animations/qanimationjobutil_p.h2
-rw-r--r--src/qml/animations/qcontinuinganimationgroupjob_p.h2
-rw-r--r--src/qml/animations/qparallelanimationgroupjob_p.h2
-rw-r--r--src/qml/animations/qpauseanimationjob_p.h2
-rw-r--r--src/qml/animations/qsequentialanimationgroupjob_p.h2
-rw-r--r--src/qml/compiler/compiler.pri6
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp348
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h25
-rw-r--r--src/qml/compiler/qqmlpropertycachecreator_p.h28
-rw-r--r--src/qml/compiler/qqmlpropertyvalidator.cpp60
-rw-r--r--src/qml/compiler/qqmlpropertyvalidator_p.h5
-rw-r--r--src/qml/compiler/qqmltypecompiler.cpp70
-rw-r--r--src/qml/compiler/qqmltypecompiler_p.h3
-rw-r--r--src/qml/compiler/qv4bytecodegenerator.cpp51
-rw-r--r--src/qml/compiler/qv4bytecodegenerator_p.h79
-rw-r--r--src/qml/compiler/qv4bytecodehandler.cpp554
-rw-r--r--src/qml/compiler/qv4bytecodehandler_p.h115
-rw-r--r--src/qml/compiler/qv4codegen.cpp2342
-rw-r--r--src/qml/compiler/qv4codegen_p.h175
-rw-r--r--src/qml/compiler/qv4compilationunitmapper_unix.cpp16
-rw-r--r--src/qml/compiler/qv4compilationunitmapper_win.cpp26
-rw-r--r--src/qml/compiler/qv4compileddata.cpp595
-rw-r--r--src/qml/compiler/qv4compileddata_p.h433
-rw-r--r--src/qml/compiler/qv4compiler.cpp337
-rw-r--r--src/qml/compiler/qv4compiler_p.h22
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp341
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h170
-rw-r--r--src/qml/compiler/qv4compilercontrolflow_p.h421
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp713
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h59
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp183
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h242
-rw-r--r--src/qml/compiler/qv4jssimplifier.cpp384
-rw-r--r--src/qml/compiler/qv4jssimplifier_p.h154
-rw-r--r--src/qml/configure.json68
-rw-r--r--src/qml/debugger/qqmlabstractprofileradapter_p.h6
-rw-r--r--src/qml/debugger/qqmldebugconnector.cpp2
-rw-r--r--src/qml/debugger/qqmlprofiler.cpp9
-rw-r--r--src/qml/debugger/qqmlprofiler_p.h2
-rw-r--r--src/qml/doc/images/cpp-qml-integration-flowchart.odgbin16080 -> 15028 bytes
-rw-r--r--src/qml/doc/images/cpp-qml-integration-flowchart.pngbin113781 -> 80498 bytes
-rw-r--r--src/qml/doc/images/objectmodel.png (renamed from src/qml/doc/images/visualitemmodel.png)bin347 -> 347 bytes
-rw-r--r--src/qml/doc/snippets/delegatemodel/delegatemodel.qml (renamed from src/qml/doc/snippets/delegatemodel/visualdatamodel.qml)0
-rw-r--r--src/qml/doc/snippets/delegatemodel/delegatemodel_rootindex/main.cpp (renamed from src/qml/doc/snippets/delegatemodel/visualdatamodel_rootindex/main.cpp)0
-rw-r--r--src/qml/doc/snippets/delegatemodel/delegatemodel_rootindex/view.qml (renamed from src/qml/doc/snippets/delegatemodel/visualdatamodel_rootindex/view.qml)0
-rw-r--r--src/qml/doc/snippets/delegatemodel/delegatemodelgroup.qml (renamed from src/qml/doc/snippets/delegatemodel/visualdatagroup.qml)0
-rw-r--r--src/qml/doc/snippets/qml/workerscript/script.mjs (renamed from src/qml/doc/snippets/qml/workerscript/script.js)0
-rw-r--r--src/qml/doc/snippets/qml/workerscript/workerscript.qml2
-rw-r--r--src/qml/doc/src/cppintegration/data.qdoc45
-rw-r--r--src/qml/doc/src/cppintegration/exposecppattributes.qdoc3
-rw-r--r--src/qml/doc/src/qmlfunctions.qdoc46
-rw-r--r--src/qml/doc/src/qmltypereference.qdoc14
-rw-r--r--src/qml/doc/src/qtqml.qdoc9
-rw-r--r--src/qml/doc/src/statemachine.qdoc14
-rw-r--r--src/qml/jit/jit.pri12
-rw-r--r--src/qml/jit/qv4assemblercommon.cpp386
-rw-r--r--src/qml/jit/qv4assemblercommon_p.h736
-rw-r--r--src/qml/jit/qv4baselineassembler.cpp (renamed from src/qml/jit/qv4assembler.cpp)1250
-rw-r--r--src/qml/jit/qv4baselineassembler_p.h (renamed from src/qml/jit/qv4assembler_p.h)55
-rw-r--r--src/qml/jit/qv4baselinejit.cpp1007
-rw-r--r--src/qml/jit/qv4baselinejit_p.h (renamed from src/qml/jit/qv4jit_p.h)125
-rw-r--r--src/qml/jit/qv4jit.cpp1328
-rw-r--r--src/qml/jit/qv4jithelpers.cpp168
-rw-r--r--src/qml/jit/qv4jithelpers_p.h91
-rw-r--r--src/qml/jsapi/qjsengine.cpp185
-rw-r--r--src/qml/jsapi/qjsengine.h4
-rw-r--r--src/qml/jsapi/qjsvalue.cpp42
-rw-r--r--src/qml/jsapi/qjsvalue_p.h2
-rw-r--r--src/qml/jsapi/qjsvalueiterator.cpp132
-rw-r--r--src/qml/jsapi/qjsvalueiterator_p.h16
-rw-r--r--src/qml/jsruntime/jsruntime.pri49
-rw-r--r--src/qml/jsruntime/qv4argumentsobject.cpp249
-rw-r--r--src/qml/jsruntime/qv4argumentsobject_p.h85
-rw-r--r--src/qml/jsruntime/qv4arraybuffer.cpp143
-rw-r--r--src/qml/jsruntime/qv4arraybuffer_p.h83
-rw-r--r--src/qml/jsruntime/qv4arraydata.cpp105
-rw-r--r--src/qml/jsruntime/qv4arraydata_p.h34
-rw-r--r--src/qml/jsruntime/qv4arrayiterator.cpp106
-rw-r--r--src/qml/jsruntime/qv4arrayiterator_p.h106
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp685
-rw-r--r--src/qml/jsruntime/qv4arrayobject_p.h16
-rw-r--r--src/qml/jsruntime/qv4atomics.cpp257
-rw-r--r--src/qml/jsruntime/qv4atomics_p.h91
-rw-r--r--src/qml/jsruntime/qv4booleanobject.cpp6
-rw-r--r--src/qml/jsruntime/qv4booleanobject_p.h4
-rw-r--r--src/qml/jsruntime/qv4context.cpp221
-rw-r--r--src/qml/jsruntime/qv4context_p.h96
-rw-r--r--src/qml/jsruntime/qv4dataview.cpp218
-rw-r--r--src/qml/jsruntime/qv4dataview_p.h6
-rw-r--r--src/qml/jsruntime/qv4dateobject.cpp206
-rw-r--r--src/qml/jsruntime/qv4dateobject_p.h9
-rw-r--r--src/qml/jsruntime/qv4engine.cpp806
-rw-r--r--src/qml/jsruntime/qv4engine_p.h206
-rw-r--r--src/qml/jsruntime/qv4enginebase_p.h24
-rw-r--r--src/qml/jsruntime/qv4errorobject.cpp78
-rw-r--r--src/qml/jsruntime/qv4errorobject_p.h77
-rw-r--r--src/qml/jsruntime/qv4estable.cpp199
-rw-r--r--src/qml/jsruntime/qv4estable_p.h89
-rw-r--r--src/qml/jsruntime/qv4function.cpp52
-rw-r--r--src/qml/jsruntime/qv4function_p.h25
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp427
-rw-r--r--src/qml/jsruntime/qv4functionobject_p.h164
-rw-r--r--src/qml/jsruntime/qv4generatorobject.cpp251
-rw-r--r--src/qml/jsruntime/qv4generatorobject_p.h151
-rw-r--r--src/qml/jsruntime/qv4global_p.h43
-rw-r--r--src/qml/jsruntime/qv4globalobject.cpp15
-rw-r--r--src/qml/jsruntime/qv4globalobject_p.h2
-rw-r--r--src/qml/jsruntime/qv4identifier.cpp86
-rw-r--r--src/qml/jsruntime/qv4identifier_p.h48
-rw-r--r--src/qml/jsruntime/qv4identifiertable.cpp191
-rw-r--r--src/qml/jsruntime/qv4identifiertable_p.h54
-rw-r--r--src/qml/jsruntime/qv4include.cpp22
-rw-r--r--src/qml/jsruntime/qv4internalclass.cpp671
-rw-r--r--src/qml/jsruntime/qv4internalclass_p.h362
-rw-r--r--src/qml/jsruntime/qv4iterator.cpp64
-rw-r--r--src/qml/jsruntime/qv4iterator_p.h82
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h11
-rw-r--r--src/qml/jsruntime/qv4jsonobject.cpp36
-rw-r--r--src/qml/jsruntime/qv4lookup.cpp139
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h52
-rw-r--r--src/qml/jsruntime/qv4managed.cpp66
-rw-r--r--src/qml/jsruntime/qv4managed_p.h128
-rw-r--r--src/qml/jsruntime/qv4mapiterator.cpp107
-rw-r--r--src/qml/jsruntime/qv4mapiterator_p.h104
-rw-r--r--src/qml/jsruntime/qv4mapobject.cpp379
-rw-r--r--src/qml/jsruntime/qv4mapobject_p.h145
-rw-r--r--src/qml/jsruntime/qv4math_p.h12
-rw-r--r--src/qml/jsruntime/qv4mathobject.cpp261
-rw-r--r--src/qml/jsruntime/qv4mathobject_p.h16
-rw-r--r--src/qml/jsruntime/qv4memberdata.cpp5
-rw-r--r--src/qml/jsruntime/qv4memberdata_p.h12
-rw-r--r--src/qml/jsruntime/qv4module.cpp237
-rw-r--r--src/qml/jsruntime/qv4module_p.h95
-rw-r--r--src/qml/jsruntime/qv4numberobject.cpp70
-rw-r--r--src/qml/jsruntime/qv4numberobject_p.h4
-rw-r--r--src/qml/jsruntime/qv4object.cpp1194
-rw-r--r--src/qml/jsruntime/qv4object_p.h303
-rw-r--r--src/qml/jsruntime/qv4objectiterator.cpp199
-rw-r--r--src/qml/jsruntime/qv4objectiterator_p.h89
-rw-r--r--src/qml/jsruntime/qv4objectproto.cpp356
-rw-r--r--src/qml/jsruntime/qv4objectproto_p.h26
-rw-r--r--src/qml/jsruntime/qv4persistent.cpp18
-rw-r--r--src/qml/jsruntime/qv4profiling.cpp9
-rw-r--r--src/qml/jsruntime/qv4profiling_p.h2
-rw-r--r--src/qml/jsruntime/qv4property_p.h71
-rw-r--r--src/qml/jsruntime/qv4propertykey.cpp109
-rw-r--r--src/qml/jsruntime/qv4propertykey_p.h154
-rw-r--r--src/qml/jsruntime/qv4proxy.cpp782
-rw-r--r--src/qml/jsruntime/qv4proxy_p.h144
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp128
-rw-r--r--src/qml/jsruntime/qv4qmlcontext_p.h9
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp168
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h22
-rw-r--r--src/qml/jsruntime/qv4reflect.cpp285
-rw-r--r--src/qml/jsruntime/qv4reflect_p.h89
-rw-r--r--src/qml/jsruntime/qv4regexp.cpp116
-rw-r--r--src/qml/jsruntime/qv4regexp_p.h45
-rw-r--r--src/qml/jsruntime/qv4regexpobject.cpp729
-rw-r--r--src/qml/jsruntime/qv4regexpobject_p.h55
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp1050
-rw-r--r--src/qml/jsruntime/qv4runtime_p.h1
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h45
-rw-r--r--src/qml/jsruntime/qv4runtimecodegen.cpp9
-rw-r--r--src/qml/jsruntime/qv4scopedvalue_p.h118
-rw-r--r--src/qml/jsruntime/qv4script.cpp38
-rw-r--r--src/qml/jsruntime/qv4script_p.h17
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp111
-rw-r--r--src/qml/jsruntime/qv4sequenceobject_p.h4
-rw-r--r--src/qml/jsruntime/qv4serialize.cpp30
-rw-r--r--src/qml/jsruntime/qv4setiterator.cpp98
-rw-r--r--src/qml/jsruntime/qv4setiterator_p.h103
-rw-r--r--src/qml/jsruntime/qv4setobject.cpp323
-rw-r--r--src/qml/jsruntime/qv4setobject_p.h144
-rw-r--r--src/qml/jsruntime/qv4sparsearray.cpp2
-rw-r--r--src/qml/jsruntime/qv4sparsearray_p.h3
-rw-r--r--src/qml/jsruntime/qv4stackframe.cpp74
-rw-r--r--src/qml/jsruntime/qv4stackframe_p.h221
-rw-r--r--src/qml/jsruntime/qv4string.cpp34
-rw-r--r--src/qml/jsruntime/qv4string_p.h122
-rw-r--r--src/qml/jsruntime/qv4stringiterator.cpp95
-rw-r--r--src/qml/jsruntime/qv4stringiterator_p.h103
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp471
-rw-r--r--src/qml/jsruntime/qv4stringobject_p.h23
-rw-r--r--src/qml/jsruntime/qv4symbol.cpp187
-rw-r--r--src/qml/jsruntime/qv4symbol_p.h126
-rw-r--r--src/qml/jsruntime/qv4typedarray.cpp1434
-rw-r--r--src/qml/jsruntime/qv4typedarray_p.h125
-rw-r--r--src/qml/jsruntime/qv4util_p.h20
-rw-r--r--src/qml/jsruntime/qv4value.cpp56
-rw-r--r--src/qml/jsruntime/qv4value_p.h310
-rw-r--r--src/qml/jsruntime/qv4variantobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4variantobject_p.h3
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp541
-rw-r--r--src/qml/jsruntime/qv4vme_moth_p.h9
-rw-r--r--src/qml/jsruntime/qv4vtable_p.h226
-rw-r--r--src/qml/memory/qv4heap_p.h99
-rw-r--r--src/qml/memory/qv4mm.cpp135
-rw-r--r--src/qml/memory/qv4mm_p.h233
-rw-r--r--src/qml/parser/parser.pri16
-rw-r--r--src/qml/parser/qqmljs.g4856
-rw-r--r--src/qml/parser/qqmljsast.cpp645
-rw-r--r--src/qml/parser/qqmljsast_p.h1306
-rw-r--r--src/qml/parser/qqmljsastfwd_p.h45
-rw-r--r--src/qml/parser/qqmljsastvisitor_p.h106
-rw-r--r--src/qml/parser/qqmljsengine_p.cpp7
-rw-r--r--src/qml/parser/qqmljsengine_p.h28
-rw-r--r--src/qml/parser/qqmljsgrammar.cpp1167
-rw-r--r--src/qml/parser/qqmljsgrammar_p.h215
-rw-r--r--src/qml/parser/qqmljskeywords_p.h131
-rw-r--r--src/qml/parser/qqmljslexer.cpp768
-rw-r--r--src/qml/parser/qqmljslexer_p.h92
-rw-r--r--src/qml/parser/qqmljsmemorypool_p.h15
-rw-r--r--src/qml/parser/qqmljsparser.cpp2010
-rw-r--r--src/qml/parser/qqmljsparser_p.h258
-rw-r--r--src/qml/qml.pro4
-rw-r--r--src/qml/qml/ftw/ftw.pri3
-rw-r--r--src/qml/qml/ftw/qflagpointer_p.h8
-rw-r--r--src/qml/qml/ftw/qqmlrefcount_p.h20
-rw-r--r--src/qml/qml/ftw/qqmlthread.cpp10
-rw-r--r--src/qml/qml/qml.pri21
-rw-r--r--src/qml/qml/qqml.h34
-rw-r--r--src/qml/qml/qqmlapplicationengine.cpp4
-rw-r--r--src/qml/qml/qqmlbinding.cpp15
-rw-r--r--src/qml/qml/qqmlbinding_p.h3
-rw-r--r--src/qml/qml/qqmlboundsignal.cpp54
-rw-r--r--src/qml/qml/qqmlboundsignal_p.h6
-rw-r--r--src/qml/qml/qqmlcomponent.cpp59
-rw-r--r--src/qml/qml/qqmlcomponent.h1
-rw-r--r--src/qml/qml/qqmlcomponent_p.h9
-rw-r--r--src/qml/qml/qqmlcontext.cpp2
-rw-r--r--src/qml/qml/qqmlcustomparser_p.h4
-rw-r--r--src/qml/qml/qqmldata_p.h2
-rw-r--r--src/qml/qml/qqmldelayedcallqueue.cpp4
-rw-r--r--src/qml/qml/qqmldirparser.cpp11
-rw-r--r--src/qml/qml/qqmldirparser_p.h3
-rw-r--r--src/qml/qml/qqmlengine.cpp133
-rw-r--r--src/qml/qml/qqmlengine.h16
-rw-r--r--src/qml/qml/qqmlengine_p.h4
-rw-r--r--src/qml/qml/qqmlerror.cpp2
-rw-r--r--src/qml/qml/qqmlexpression.cpp2
-rw-r--r--src/qml/qml/qqmlextensionplugin.cpp2
-rw-r--r--src/qml/qml/qqmlguard_p.h28
-rw-r--r--src/qml/qml/qqmlimport.cpp36
-rw-r--r--src/qml/qml/qqmlimport_p.h4
-rw-r--r--src/qml/qml/qqmlincubator.cpp3
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp10
-rw-r--r--src/qml/qml/qqmljavascriptexpression_p.h17
-rw-r--r--src/qml/qml/qqmllist.cpp5
-rw-r--r--src/qml/qml/qqmllistwrapper.cpp89
-rw-r--r--src/qml/qml/qqmllistwrapper_p.h7
-rw-r--r--src/qml/qml/qqmllocale.cpp12
-rw-r--r--src/qml/qml/qqmllocale_p.h2
-rw-r--r--src/qml/qml/qqmlloggingcategory.cpp36
-rw-r--r--src/qml/qml/qqmlloggingcategory_p.h13
-rw-r--r--src/qml/qml/qqmlmetatype.cpp144
-rw-r--r--src/qml/qml/qqmlmetatype_p.h7
-rw-r--r--src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp2
-rw-r--r--src/qml/qml/qqmlobjectcreator.cpp135
-rw-r--r--src/qml/qml/qqmlobjectcreator_p.h6
-rw-r--r--src/qml/qml/qqmlopenmetaobject.cpp122
-rw-r--r--src/qml/qml/qqmlopenmetaobject_p.h5
-rw-r--r--src/qml/qml/qqmlparserstatus.cpp2
-rw-r--r--src/qml/qml/qqmlprivate.h10
-rw-r--r--src/qml/qml/qqmlproperty.cpp4
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp10
-rw-r--r--src/qml/qml/qqmlpropertycache_p.h6
-rw-r--r--src/qml/qml/qqmltypeloader.cpp363
-rw-r--r--src/qml/qml/qqmltypeloader_p.h45
-rw-r--r--src/qml/qml/qqmltypewrapper.cpp90
-rw-r--r--src/qml/qml/qqmltypewrapper_p.h16
-rw-r--r--src/qml/qml/qqmlvaluetype.cpp60
-rw-r--r--src/qml/qml/qqmlvaluetypeproxybinding_p.h2
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp86
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper_p.h11
-rw-r--r--src/qml/qml/qqmlvme_p.h4
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp20
-rw-r--r--src/qml/qml/qqmlvmemetaobject_p.h6
-rw-r--r--src/qml/qml/qqmlxmlhttprequest.cpp176
-rw-r--r--src/qml/qml/qqmlxmlhttprequest_p.h4
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions.cpp60
-rw-r--r--src/qml/qml/v8/qqmlbuiltinfunctions_p.h11
-rw-r--r--src/qml/qml/v8/qv4domerrors.cpp34
-rw-r--r--src/qml/qml/v8/qv4domerrors_p.h2
-rw-r--r--src/qml/qml/v8/qv4sqlerrors.cpp16
-rw-r--r--src/qml/qml/v8/qv8engine.cpp39
-rw-r--r--src/qml/qml/v8/qv8engine_p.h10
-rw-r--r--src/qml/qtqmlglobal.h1
-rw-r--r--src/qml/qtqmlglobal_p.h2
-rw-r--r--src/qml/types/qqmlconnections.cpp13
-rw-r--r--src/qml/types/qqmlconnections_p.h6
-rw-r--r--src/qml/types/qqmldelegatecomponent.cpp298
-rw-r--r--src/qml/types/qqmldelegatecomponent_p.h155
-rw-r--r--src/qml/types/qqmldelegatemodel.cpp451
-rw-r--r--src/qml/types/qqmldelegatemodel_p.h4
-rw-r--r--src/qml/types/qqmldelegatemodel_p_p.h31
-rw-r--r--src/qml/types/qqmllistmodel.cpp266
-rw-r--r--src/qml/types/qqmllistmodel_p.h12
-rw-r--r--src/qml/types/qqmllistmodel_p_p.h42
-rw-r--r--src/qml/types/qqmllistmodelworkeragent_p.h2
-rw-r--r--src/qml/types/qqmlmodelsmodule.cpp14
-rw-r--r--src/qml/types/qqmlmodelsmodule_p.h1
-rw-r--r--src/qml/types/qqmlobjectmodel.cpp19
-rw-r--r--src/qml/types/qqmlobjectmodel_p.h2
-rw-r--r--src/qml/types/qqmltableinstancemodel.cpp542
-rw-r--r--src/qml/types/qqmltableinstancemodel_p.h160
-rw-r--r--src/qml/types/qqmltimer_p.h2
-rw-r--r--src/qml/types/qquickworkerscript.cpp295
-rw-r--r--src/qml/types/types.pri39
-rw-r--r--src/qml/util/qqmladaptormodel.cpp242
-rw-r--r--src/qml/util/qqmladaptormodel_p.h28
-rw-r--r--src/qml/util/qqmllistaccessor.cpp21
-rw-r--r--src/qml/util/qqmllistcompositor.cpp2
-rw-r--r--src/qml/util/qqmlpropertymap.cpp4
-rw-r--r--src/qmldebug/qmldebug.pro3
-rw-r--r--src/qmldebug/qqmldebugclient.cpp5
-rw-r--r--src/qmldebug/qqmldebugclient_p.h5
-rw-r--r--src/qmldebug/qqmldebugconnection.cpp4
-rw-r--r--src/qmldebug/qqmldebugmessageclient.cpp5
-rw-r--r--src/qmldebug/qqmldebugmessageclient_p.h5
-rw-r--r--src/qmldebug/qqmlenginecontrolclient.cpp6
-rw-r--r--src/qmldebug/qqmlpreviewclient.cpp139
-rw-r--r--src/qmldebug/qqmlpreviewclient_p.h111
-rw-r--r--src/qmldebug/qqmlpreviewclient_p_p.h (renamed from src/qml/qml/ftw/qdeferredcleanup_p.h)28
-rw-r--r--src/qmldebug/qqmlprofilerclient.cpp7
-rw-r--r--src/qmldebug/qqmlprofilerclient_p.h2
-rw-r--r--src/qmldebug/qqmlprofilerclient_p_p.h3
-rw-r--r--src/qmldevtools/qmldevtools.pro2
-rw-r--r--src/qmltest/doc/src/qtquicktest-index.qdoc8
-rw-r--r--src/qmltest/qtestoptions_p.h6
-rw-r--r--src/qmltest/quicktest.cpp37
-rw-r--r--src/qmltest/quicktestevent.cpp6
-rw-r--r--src/qmltest/quicktestevent_p.h2
-rw-r--r--src/qmltest/quicktestresult.cpp14
-rw-r--r--src/qmltest/quicktestresult_p.h2
-rw-r--r--src/quick/configure.json12
-rw-r--r--src/quick/designer/qqmldesignermetaobject.cpp4
-rw-r--r--src/quick/designer/qquickdesignercustomparserobject.cpp4
-rw-r--r--src/quick/designer/qquickdesignercustomparserobject_p.h4
-rw-r--r--src/quick/doc/images/9BcAYDlpuT8.jpgbin0 -> 8788 bytes
-rw-r--r--src/quick/doc/images/pointDistanceThreshold.pngbin8661 -> 0 bytes
-rw-r--r--src/quick/doc/images/pointerHandlerMargin.pngbin0 -> 11410 bytes
-rw-r--r--src/quick/doc/images/pointerHandlerMargin.svg (renamed from src/quick/doc/images/pointDistanceThreshold.svg)46
-rw-r--r--src/quick/doc/images/qml-item-canvas-lineDash.pngbin0 -> 6254 bytes
-rw-r--r--src/quick/doc/qtquick.qdocconf4
-rw-r--r--src/quick/doc/snippets/pointerHandlers/dragHandler.qml3
-rw-r--r--src/quick/doc/snippets/pointerHandlers/dragHandlerDifferentTarget.qml9
-rw-r--r--src/quick/doc/snippets/pointerHandlers/dragHandlerNullTarget.qml11
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pinchHandler.qml3
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pinchHandlerDifferentTarget.qml3
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pinchHandlerNullTarget.qml3
-rw-r--r--src/quick/doc/snippets/pointerHandlers/pointHandler.qml3
-rw-r--r--src/quick/doc/snippets/qml/mousearea/mousearea-snippet.qml4
-rw-r--r--src/quick/doc/snippets/qml/pathview/pathview.qml4
-rw-r--r--src/quick/doc/snippets/qml/rectangle/rectangle-gradient.qml12
-rw-r--r--src/quick/doc/snippets/qml/tableview/reusabledelegate.qml82
-rw-r--r--src/quick/doc/snippets/qml/tableview/tablemodel.cpp103
-rw-r--r--src/quick/doc/snippets/qml/tableview/tablemodel.qml (renamed from src/quick/doc/snippets/qml/models/visual-model-and-view.qml)33
-rw-r--r--src/quick/doc/snippets/qml/tableview/tableviewwithheader.qml64
-rw-r--r--src/quick/doc/snippets/qml/tableview/tableviewwithprovider.qml69
-rw-r--r--src/quick/doc/snippets/qml/transition-reversible.qml14
-rw-r--r--src/quick/doc/src/concepts/effects/particles.qdoc18
-rw-r--r--src/quick/doc/src/concepts/input/mouse.qdoc25
-rw-r--r--src/quick/doc/src/concepts/input/topic.qdoc30
-rw-r--r--src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc (renamed from src/quick/doc/src/concepts/pointerhandlers/qtquickhandlers-index.qdoc)41
-rw-r--r--src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc8
-rw-r--r--src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc7
-rw-r--r--src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc6
-rw-r--r--src/quick/doc/src/concepts/pointerhandlers/qtquickhandlers.qdoc44
-rw-r--r--src/quick/doc/src/concepts/positioning/righttoleft.qdoc4
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc8
-rw-r--r--src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc16
-rw-r--r--src/quick/doc/src/qmltypereference.qdoc21
-rw-r--r--src/quick/handlers/handlers.pri11
-rw-r--r--src/quick/handlers/qquickdragaxis.cpp75
-rw-r--r--src/quick/handlers/qquickdragaxis_p.h87
-rw-r--r--src/quick/handlers/qquickdraghandler.cpp243
-rw-r--r--src/quick/handlers/qquickdraghandler_p.h53
-rw-r--r--src/quick/handlers/qquickhandlerpoint.cpp352
-rw-r--r--src/quick/handlers/qquickhandlerpoint_p.h121
-rw-r--r--src/quick/handlers/qquickhandlersmodule.cpp106
-rw-r--r--src/quick/handlers/qquickhoverhandler.cpp106
-rw-r--r--src/quick/handlers/qquickhoverhandler_p.h91
-rw-r--r--src/quick/handlers/qquickmultipointhandler.cpp198
-rw-r--r--src/quick/handlers/qquickmultipointhandler_p.h24
-rw-r--r--src/quick/handlers/qquickpinchhandler.cpp242
-rw-r--r--src/quick/handlers/qquickpinchhandler_p.h66
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler.cpp112
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler_p.h29
-rw-r--r--src/quick/handlers/qquickpointerdevicehandler_p_p.h75
-rw-r--r--src/quick/handlers/qquickpointerhandler.cpp243
-rw-r--r--src/quick/handlers/qquickpointerhandler_p.h44
-rw-r--r--src/quick/handlers/qquickpointerhandler_p_p.h84
-rw-r--r--src/quick/handlers/qquickpointhandler.cpp23
-rw-r--r--src/quick/handlers/qquickpointhandler_p.h3
-rw-r--r--src/quick/handlers/qquicksinglepointhandler.cpp327
-rw-r--r--src/quick/handlers/qquicksinglepointhandler_p.h71
-rw-r--r--src/quick/handlers/qquicktaphandler.cpp99
-rw-r--r--src/quick/handlers/qquicktaphandler_p.h25
-rw-r--r--src/quick/items/context2d/qquickcanvasitem.cpp2
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp192
-rw-r--r--src/quick/items/context2d/qquickcontext2d_p.h5
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp26
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h14
-rw-r--r--src/quick/items/items.pri10
-rw-r--r--src/quick/items/qquickanchors_p.h1
-rw-r--r--src/quick/items/qquickanimatedsprite.cpp11
-rw-r--r--src/quick/items/qquickanimatedsprite_p.h2
-rw-r--r--src/quick/items/qquickevents.cpp214
-rw-r--r--src/quick/items/qquickevents_p_p.h183
-rw-r--r--src/quick/items/qquickflickable.cpp104
-rw-r--r--src/quick/items/qquickflickable_p.h18
-rw-r--r--src/quick/items/qquickflickable_p_p.h2
-rw-r--r--src/quick/items/qquickgridview.cpp35
-rw-r--r--src/quick/items/qquickitem.cpp64
-rw-r--r--src/quick/items/qquickitem.h1
-rw-r--r--src/quick/items/qquickitem_p.h24
-rw-r--r--src/quick/items/qquickitemsmodule.cpp48
-rw-r--r--src/quick/items/qquickitemview.cpp112
-rw-r--r--src/quick/items/qquickitemview_p_p.h37
-rw-r--r--src/quick/items/qquickitemviewfxitem.cpp165
-rw-r--r--src/quick/items/qquickitemviewfxitem_p_p.h108
-rw-r--r--src/quick/items/qquickitemviewtransition.cpp2
-rw-r--r--src/quick/items/qquicklistview.cpp18
-rw-r--r--src/quick/items/qquickloader.cpp19
-rw-r--r--src/quick/items/qquickloader_p_p.h3
-rw-r--r--src/quick/items/qquickmultipointtoucharea.cpp3
-rw-r--r--src/quick/items/qquickmultipointtoucharea_p.h4
-rw-r--r--src/quick/items/qquickpainteditem.h1
-rw-r--r--src/quick/items/qquickpositioners.cpp24
-rw-r--r--src/quick/items/qquickrectangle.cpp121
-rw-r--r--src/quick/items/qquickrectangle_p.h17
-rw-r--r--src/quick/items/qquickrectangle_p_p.h2
-rw-r--r--src/quick/items/qquickrendercontrol.cpp3
-rw-r--r--src/quick/items/qquickscalegrid.cpp4
-rw-r--r--src/quick/items/qquickscalegrid_p_p.h12
-rw-r--r--src/quick/items/qquickscreen.cpp40
-rw-r--r--src/quick/items/qquickshadereffectmesh.cpp5
-rw-r--r--src/quick/items/qquickshadereffectmesh_p.h7
-rw-r--r--src/quick/items/qquickspriteengine_p.h2
-rw-r--r--src/quick/items/qquicktableview.cpp2024
-rw-r--r--src/quick/items/qquicktableview_p.h173
-rw-r--r--src/quick/items/qquicktableview_p_p.h387
-rw-r--r--src/quick/items/qquicktext.cpp51
-rw-r--r--src/quick/items/qquicktext_p.h10
-rw-r--r--src/quick/items/qquicktext_p_p.h1
-rw-r--r--src/quick/items/qquickwindow.cpp232
-rw-r--r--src/quick/items/qquickwindow.h1
-rw-r--r--src/quick/items/qquickwindow_p.h10
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp5
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h1
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp2
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp34
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp12
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h2
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp12
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h4
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp4
-rw-r--r--src/quick/scenegraph/adaptations/software/software.pri13
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp14
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp79
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h31
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgktxhandler.cpp186
-rw-r--r--src/quick/scenegraph/compressedtexture/qsgpkmhandler.cpp118
-rw-r--r--src/quick/scenegraph/coreapi/qsgabstractrenderer.h1
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer.cpp27
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h19
-rw-r--r--src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp225
-rw-r--r--src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h2
-rw-r--r--src/quick/scenegraph/qsgcontextplugin.cpp7
-rw-r--r--src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp295
-rw-r--r--src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h4
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext.cpp3
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp2
-rw-r--r--src/quick/scenegraph/scenegraph.pri18
-rw-r--r--src/quick/scenegraph/util/qsgareaallocator.cpp144
-rw-r--r--src/quick/scenegraph/util/qsgareaallocator_p.h4
-rw-r--r--src/quick/scenegraph/util/qsgatlastexture.cpp25
-rw-r--r--src/quick/scenegraph/util/qsgengine.h1
-rw-r--r--src/quick/scenegraph/util/qsgtexture.cpp13
-rw-r--r--src/quick/scenegraph/util/qsgtexturereader.cpp61
-rw-r--r--src/quick/scenegraph/util/qsgtexturereader_p.h8
-rw-r--r--src/quick/util/qquickanimation.cpp22
-rw-r--r--src/quick/util/qquickanimation_p.h1
-rw-r--r--src/quick/util/qquickglobal.cpp2
-rw-r--r--src/quick/util/qquickimageprovider.cpp7
-rw-r--r--src/quick/util/qquickimageprovider.h6
-rw-r--r--src/quick/util/qquickimageprovider_p.h (renamed from src/quick/scenegraph/compressedtexture/qsgpkmhandler_p.h)22
-rw-r--r--src/quick/util/qquickpixmapcache.cpp19
-rw-r--r--src/quick/util/qquickprofiler.cpp3
-rw-r--r--src/quick/util/qquickprofiler_p.h2
-rw-r--r--src/quick/util/qquickpropertychanges.cpp38
-rw-r--r--src/quick/util/qquickpropertychanges_p.h6
-rw-r--r--src/quick/util/qquickstate.cpp2
-rw-r--r--src/quick/util/qquickstate_p_p.h4
-rw-r--r--src/quick/util/qquicksvgparser.cpp3
-rw-r--r--src/quick/util/qquicktransitionmanager.cpp59
-rw-r--r--src/quick/util/qquickutilmodule.cpp3
-rw-r--r--src/quick/util/util.pri1
-rw-r--r--src/quickshapes/qquicknvprfunctions.cpp (renamed from src/imports/shapes/qquicknvprfunctions.cpp)0
-rw-r--r--src/quickshapes/qquicknvprfunctions_p.h (renamed from src/imports/shapes/qquicknvprfunctions_p.h)1
-rw-r--r--src/quickshapes/qquicknvprfunctions_p_p.h (renamed from src/imports/shapes/qquicknvprfunctions_p_p.h)3
-rw-r--r--src/quickshapes/qquickshape.cpp (renamed from src/imports/shapes/qquickshape.cpp)41
-rw-r--r--src/quickshapes/qquickshape_p.h (renamed from src/imports/shapes/qquickshape_p.h)15
-rw-r--r--src/quickshapes/qquickshape_p_p.h (renamed from src/imports/shapes/qquickshape_p_p.h)7
-rw-r--r--src/quickshapes/qquickshapegenericrenderer.cpp (renamed from src/imports/shapes/qquickshapegenericrenderer.cpp)18
-rw-r--r--src/quickshapes/qquickshapegenericrenderer_p.h (renamed from src/imports/shapes/qquickshapegenericrenderer_p.h)3
-rw-r--r--src/quickshapes/qquickshapenvprrenderer.cpp (renamed from src/imports/shapes/qquickshapenvprrenderer.cpp)0
-rw-r--r--src/quickshapes/qquickshapenvprrenderer_p.h (renamed from src/imports/shapes/qquickshapenvprrenderer_p.h)5
-rw-r--r--src/quickshapes/qquickshapesglobal.h56
-rw-r--r--src/quickshapes/qquickshapesglobal_p.h (renamed from src/quick/handlers/qquickhandlersmodule_p.h)17
-rw-r--r--src/quickshapes/qquickshapesoftwarerenderer.cpp (renamed from src/imports/shapes/qquickshapesoftwarerenderer.cpp)0
-rw-r--r--src/quickshapes/qquickshapesoftwarerenderer_p.h (renamed from src/imports/shapes/qquickshapesoftwarerenderer_p.h)3
-rw-r--r--src/quickshapes/qtquickshapes.qrc (renamed from src/imports/shapes/qtquickshapesplugin.qrc)0
-rw-r--r--src/quickshapes/quickshapes.pro33
-rw-r--r--src/quickshapes/shaders/blit.frag (renamed from src/imports/shapes/shaders/blit.frag)0
-rw-r--r--src/quickshapes/shaders/blit.vert (renamed from src/imports/shapes/shaders/blit.vert)0
-rw-r--r--src/quickshapes/shaders/blit_core.frag (renamed from src/imports/shapes/shaders/blit_core.frag)0
-rw-r--r--src/quickshapes/shaders/blit_core.vert (renamed from src/imports/shapes/shaders/blit_core.vert)0
-rw-r--r--src/quickshapes/shaders/conicalgradient.frag (renamed from src/imports/shapes/shaders/conicalgradient.frag)0
-rw-r--r--src/quickshapes/shaders/conicalgradient.vert (renamed from src/imports/shapes/shaders/conicalgradient.vert)0
-rw-r--r--src/quickshapes/shaders/conicalgradient_core.frag (renamed from src/imports/shapes/shaders/conicalgradient_core.frag)0
-rw-r--r--src/quickshapes/shaders/conicalgradient_core.vert (renamed from src/imports/shapes/shaders/conicalgradient_core.vert)0
-rw-r--r--src/quickshapes/shaders/lineargradient.frag (renamed from src/imports/shapes/shaders/lineargradient.frag)0
-rw-r--r--src/quickshapes/shaders/lineargradient.vert (renamed from src/imports/shapes/shaders/lineargradient.vert)0
-rw-r--r--src/quickshapes/shaders/lineargradient_core.frag (renamed from src/imports/shapes/shaders/lineargradient_core.frag)0
-rw-r--r--src/quickshapes/shaders/lineargradient_core.vert (renamed from src/imports/shapes/shaders/lineargradient_core.vert)0
-rw-r--r--src/quickshapes/shaders/radialgradient.frag (renamed from src/imports/shapes/shaders/radialgradient.frag)0
-rw-r--r--src/quickshapes/shaders/radialgradient.vert (renamed from src/imports/shapes/shaders/radialgradient.vert)0
-rw-r--r--src/quickshapes/shaders/radialgradient_core.frag (renamed from src/imports/shapes/shaders/radialgradient_core.frag)0
-rw-r--r--src/quickshapes/shaders/radialgradient_core.vert (renamed from src/imports/shapes/shaders/radialgradient_core.vert)0
-rw-r--r--src/src.pro20
702 files changed, 50432 insertions, 30936 deletions
diff --git a/src/3rdparty/masm/assembler/ARM64Assembler.h b/src/3rdparty/masm/assembler/ARM64Assembler.h
index 1787e921e8..fcf2e485e8 100644
--- a/src/3rdparty/masm/assembler/ARM64Assembler.h
+++ b/src/3rdparty/masm/assembler/ARM64Assembler.h
@@ -1980,6 +1980,13 @@ public:
}
template<int datasize>
+ ALWAYS_INLINE void stp(RegisterID rt, RegisterID rt2, RegisterID rn, unsigned pimm = 0)
+ {
+ CHECK_DATASIZE();
+ insn(loadStoreRegisterPairOffset(MEMPAIROPSIZE_INT(datasize), false, MemOp_STORE, pimm, rn, rt, rt2));
+ }
+
+ template<int datasize>
ALWAYS_INLINE void str(RegisterID rt, RegisterID rn, RegisterID rm)
{
str<datasize>(rt, rn, rm, UXTX, 0);
@@ -3701,6 +3708,23 @@ private:
}
// 'V' means vector
+ ALWAYS_INLINE static int loadStoreRegisterPairOffset(MemPairOpSize size, bool V, MemOp opc, int immediate, RegisterID rn, FPRegisterID rt, FPRegisterID rt2)
+ {
+ ASSERT(size < 3);
+ ASSERT(opc == (opc & 1)); // Only load or store, load signed 64 is handled via size.
+ ASSERT(V || (size != MemPairOp_LoadSigned_32) || (opc == MemOp_LOAD)); // There isn't an integer store signed.
+ unsigned immedShiftAmount = memPairOffsetShift(V, size);
+ int imm7 = immediate >> immedShiftAmount;
+ ASSERT((imm7 << immedShiftAmount) == immediate && isInt7(imm7));
+ return (0x29000000 | size << 30 | V << 26 | opc << 22 | (imm7 & 0x7f) << 15 | rt2 << 10 | xOrSp(rn) << 5 | rt);
+ }
+
+ ALWAYS_INLINE static int loadStoreRegisterPairOffset(MemPairOpSize size, bool V, MemOp opc, int immediate, RegisterID rn, RegisterID rt, RegisterID rt2)
+ {
+ return loadStoreRegisterPairOffset(size, V, opc, immediate, rn, xOrZrAsFPR(rt), xOrZrAsFPR(rt2));
+ }
+
+ // 'V' means vector
// 'S' means shift rm
ALWAYS_INLINE static int loadStoreRegisterRegisterOffset(MemOpSize size, bool V, MemOp opc, RegisterID rm, ExtendType option, bool S, RegisterID rn, FPRegisterID rt)
{
diff --git a/src/3rdparty/masm/assembler/ARMAssembler.cpp b/src/3rdparty/masm/assembler/ARMAssembler.cpp
deleted file mode 100644
index 6912d1ea39..0000000000
--- a/src/3rdparty/masm/assembler/ARMAssembler.cpp
+++ /dev/null
@@ -1,444 +0,0 @@
-/*
- * Copyright (C) 2009 University of Szeged
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. 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.
- *
- * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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.
- */
-
-#include "config.h"
-
-#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL)
-
-#include "ARMAssembler.h"
-
-namespace JSC {
-
-// Patching helpers
-
-void ARMAssembler::patchConstantPoolLoad(void* loadAddr, void* constPoolAddr)
-{
- ARMWord *ldr = reinterpret_cast<ARMWord*>(loadAddr);
- ARMWord diff = reinterpret_cast<ARMWord*>(constPoolAddr) - ldr;
- ARMWord index = (*ldr & 0xfff) >> 1;
-
- ASSERT(diff >= 1);
- if (diff >= 2 || index > 0) {
- diff = (diff + index - 2) * sizeof(ARMWord);
- ASSERT(diff <= 0xfff);
- *ldr = (*ldr & ~0xfff) | diff;
- } else
- *ldr = (*ldr & ~(0xfff | ARMAssembler::DataTransferUp)) | sizeof(ARMWord);
-}
-
-// Handle immediates
-
-ARMWord ARMAssembler::getOp2(ARMWord imm)
-{
- int rol;
-
- if (imm <= 0xff)
- return Op2Immediate | imm;
-
- if ((imm & 0xff000000) == 0) {
- imm <<= 8;
- rol = 8;
- }
- else {
- imm = (imm << 24) | (imm >> 8);
- rol = 0;
- }
-
- if ((imm & 0xff000000) == 0) {
- imm <<= 8;
- rol += 4;
- }
-
- if ((imm & 0xf0000000) == 0) {
- imm <<= 4;
- rol += 2;
- }
-
- if ((imm & 0xc0000000) == 0) {
- imm <<= 2;
- rol += 1;
- }
-
- if ((imm & 0x00ffffff) == 0)
- return Op2Immediate | (imm >> 24) | (rol << 8);
-
- return InvalidImmediate;
-}
-
-int ARMAssembler::genInt(int reg, ARMWord imm, bool positive)
-{
- // Step1: Search a non-immediate part
- ARMWord mask;
- ARMWord imm1;
- ARMWord imm2;
- int rol;
-
- mask = 0xff000000;
- rol = 8;
- while(1) {
- if ((imm & mask) == 0) {
- imm = (imm << rol) | (imm >> (32 - rol));
- rol = 4 + (rol >> 1);
- break;
- }
- rol += 2;
- mask >>= 2;
- if (mask & 0x3) {
- // rol 8
- imm = (imm << 8) | (imm >> 24);
- mask = 0xff00;
- rol = 24;
- while (1) {
- if ((imm & mask) == 0) {
- imm = (imm << rol) | (imm >> (32 - rol));
- rol = (rol >> 1) - 8;
- break;
- }
- rol += 2;
- mask >>= 2;
- if (mask & 0x3)
- return 0;
- }
- break;
- }
- }
-
- ASSERT((imm & 0xff) == 0);
-
- if ((imm & 0xff000000) == 0) {
- imm1 = Op2Immediate | ((imm >> 16) & 0xff) | (((rol + 4) & 0xf) << 8);
- imm2 = Op2Immediate | ((imm >> 8) & 0xff) | (((rol + 8) & 0xf) << 8);
- } else if (imm & 0xc0000000) {
- imm1 = Op2Immediate | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8);
- imm <<= 8;
- rol += 4;
-
- if ((imm & 0xff000000) == 0) {
- imm <<= 8;
- rol += 4;
- }
-
- if ((imm & 0xf0000000) == 0) {
- imm <<= 4;
- rol += 2;
- }
-
- if ((imm & 0xc0000000) == 0) {
- imm <<= 2;
- rol += 1;
- }
-
- if ((imm & 0x00ffffff) == 0)
- imm2 = Op2Immediate | (imm >> 24) | ((rol & 0xf) << 8);
- else
- return 0;
- } else {
- if ((imm & 0xf0000000) == 0) {
- imm <<= 4;
- rol += 2;
- }
-
- if ((imm & 0xc0000000) == 0) {
- imm <<= 2;
- rol += 1;
- }
-
- imm1 = Op2Immediate | ((imm >> 24) & 0xff) | ((rol & 0xf) << 8);
- imm <<= 8;
- rol += 4;
-
- if ((imm & 0xf0000000) == 0) {
- imm <<= 4;
- rol += 2;
- }
-
- if ((imm & 0xc0000000) == 0) {
- imm <<= 2;
- rol += 1;
- }
-
- if ((imm & 0x00ffffff) == 0)
- imm2 = Op2Immediate | (imm >> 24) | ((rol & 0xf) << 8);
- else
- return 0;
- }
-
- if (positive) {
- mov(reg, imm1);
- orr(reg, reg, imm2);
- } else {
- mvn(reg, imm1);
- bic(reg, reg, imm2);
- }
-
- return 1;
-}
-
-ARMWord ARMAssembler::getImm(ARMWord imm, int tmpReg, bool invert)
-{
- ARMWord tmp;
-
- // Do it by 1 instruction
- tmp = getOp2(imm);
- if (tmp != InvalidImmediate)
- return tmp;
-
- tmp = getOp2(~imm);
- if (tmp != InvalidImmediate) {
- if (invert)
- return tmp | Op2InvertedImmediate;
- mvn(tmpReg, tmp);
- return tmpReg;
- }
-
- return encodeComplexImm(imm, tmpReg);
-}
-
-void ARMAssembler::moveImm(ARMWord imm, int dest)
-{
- ARMWord tmp;
-
- // Do it by 1 instruction
- tmp = getOp2(imm);
- if (tmp != InvalidImmediate) {
- mov(dest, tmp);
- return;
- }
-
- tmp = getOp2(~imm);
- if (tmp != InvalidImmediate) {
- mvn(dest, tmp);
- return;
- }
-
- encodeComplexImm(imm, dest);
-}
-
-ARMWord ARMAssembler::encodeComplexImm(ARMWord imm, int dest)
-{
-#if WTF_ARM_ARCH_AT_LEAST(7)
- ARMWord tmp = getImm16Op2(imm);
- if (tmp != InvalidImmediate) {
- movw(dest, tmp);
- return dest;
- }
- movw(dest, getImm16Op2(imm & 0xffff));
- movt(dest, getImm16Op2(imm >> 16));
- return dest;
-#else
- // Do it by 2 instruction
- if (genInt(dest, imm, true))
- return dest;
- if (genInt(dest, ~imm, false))
- return dest;
-
- ldrImmediate(dest, imm);
- return dest;
-#endif
-}
-
-// Memory load/store helpers
-
-void ARMAssembler::dataTransfer32(DataTransferTypeA transferType, RegisterID srcDst, RegisterID base, int32_t offset)
-{
- if (offset >= 0) {
- if (offset <= 0xfff)
- dtrUp(transferType, srcDst, base, offset);
- else if (offset <= 0xfffff) {
- add(ARMRegisters::S0, base, Op2Immediate | (offset >> 12) | (10 << 8));
- dtrUp(transferType, srcDst, ARMRegisters::S0, (offset & 0xfff));
- } else {
- moveImm(offset, ARMRegisters::S0);
- dtrUpRegister(transferType, srcDst, base, ARMRegisters::S0);
- }
- } else {
- if (offset >= -0xfff)
- dtrDown(transferType, srcDst, base, -offset);
- else if (offset >= -0xfffff) {
- sub(ARMRegisters::S0, base, Op2Immediate | (-offset >> 12) | (10 << 8));
- dtrDown(transferType, srcDst, ARMRegisters::S0, (-offset & 0xfff));
- } else {
- moveImm(offset, ARMRegisters::S0);
- dtrUpRegister(transferType, srcDst, base, ARMRegisters::S0);
- }
- }
-}
-
-void ARMAssembler::baseIndexTransfer32(DataTransferTypeA transferType, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset)
-{
- ASSERT(scale >= 0 && scale <= 3);
- ARMWord op2 = lsl(index, scale);
-
- if (!offset) {
- dtrUpRegister(transferType, srcDst, base, op2);
- return;
- }
-
- if (offset <= 0xfffff && offset >= -0xfffff) {
- add(ARMRegisters::S0, base, op2);
- dataTransfer32(transferType, srcDst, ARMRegisters::S0, offset);
- return;
- }
-
- moveImm(offset, ARMRegisters::S0);
- add(ARMRegisters::S0, ARMRegisters::S0, op2);
- dtrUpRegister(transferType, srcDst, base, ARMRegisters::S0);
-}
-
-void ARMAssembler::dataTransfer16(DataTransferTypeB transferType, RegisterID srcDst, RegisterID base, int32_t offset)
-{
- if (offset >= 0) {
- if (offset <= 0xff)
- halfDtrUp(transferType, srcDst, base, getOp2Half(offset));
- else if (offset <= 0xffff) {
- add(ARMRegisters::S0, base, Op2Immediate | (offset >> 8) | (12 << 8));
- halfDtrUp(transferType, srcDst, ARMRegisters::S0, getOp2Half(offset & 0xff));
- } else {
- moveImm(offset, ARMRegisters::S0);
- halfDtrUpRegister(transferType, srcDst, base, ARMRegisters::S0);
- }
- } else {
- if (offset >= -0xff)
- halfDtrDown(transferType, srcDst, base, getOp2Half(-offset));
- else if (offset >= -0xffff) {
- sub(ARMRegisters::S0, base, Op2Immediate | (-offset >> 8) | (12 << 8));
- halfDtrDown(transferType, srcDst, ARMRegisters::S0, getOp2Half(-offset & 0xff));
- } else {
- moveImm(offset, ARMRegisters::S0);
- halfDtrUpRegister(transferType, srcDst, base, ARMRegisters::S0);
- }
- }
-}
-
-void ARMAssembler::baseIndexTransfer16(DataTransferTypeB transferType, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset)
-{
- if (!scale && !offset) {
- halfDtrUpRegister(transferType, srcDst, base, index);
- return;
- }
-
- ARMWord op2 = lsl(index, scale);
-
- if (offset <= 0xffff && offset >= -0xffff) {
- add(ARMRegisters::S0, base, op2);
- dataTransfer16(transferType, srcDst, ARMRegisters::S0, offset);
- return;
- }
-
- moveImm(offset, ARMRegisters::S0);
- add(ARMRegisters::S0, ARMRegisters::S0, op2);
- halfDtrUpRegister(transferType, srcDst, base, ARMRegisters::S0);
-}
-
-void ARMAssembler::dataTransferFloat(DataTransferTypeFloat transferType, FPRegisterID srcDst, RegisterID base, int32_t offset)
-{
- // VFP cannot directly access memory that is not four-byte-aligned
- if (!(offset & 0x3)) {
- if (offset <= 0x3ff && offset >= 0) {
- doubleDtrUp(transferType, srcDst, base, offset >> 2);
- return;
- }
- if (offset <= 0x3ffff && offset >= 0) {
- add(ARMRegisters::S0, base, Op2Immediate | (offset >> 10) | (11 << 8));
- doubleDtrUp(transferType, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff);
- return;
- }
- offset = -offset;
-
- if (offset <= 0x3ff && offset >= 0) {
- doubleDtrDown(transferType, srcDst, base, offset >> 2);
- return;
- }
- if (offset <= 0x3ffff && offset >= 0) {
- sub(ARMRegisters::S0, base, Op2Immediate | (offset >> 10) | (11 << 8));
- doubleDtrDown(transferType, srcDst, ARMRegisters::S0, (offset >> 2) & 0xff);
- return;
- }
- offset = -offset;
- }
-
- moveImm(offset, ARMRegisters::S0);
- add(ARMRegisters::S0, ARMRegisters::S0, base);
- doubleDtrUp(transferType, srcDst, ARMRegisters::S0, 0);
-}
-
-void ARMAssembler::baseIndexTransferFloat(DataTransferTypeFloat transferType, FPRegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset)
-{
- add(ARMRegisters::S1, base, lsl(index, scale));
- dataTransferFloat(transferType, srcDst, ARMRegisters::S1, offset);
-}
-
-PassRefPtr<ExecutableMemoryHandle> ARMAssembler::executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort)
-{
- // 64-bit alignment is required for next constant pool and JIT code as well
- m_buffer.flushWithoutBarrier(true);
- if (!m_buffer.isAligned(8))
- bkpt(0);
-
- RefPtr<ExecutableMemoryHandle> result = m_buffer.executableCopy(globalData, ownerUID, effort);
- char* data = reinterpret_cast<char*>(result->start());
-
- for (Jumps::Iterator iter = m_jumps.begin(); iter != m_jumps.end(); ++iter) {
- // The last bit is set if the constant must be placed on constant pool.
- int pos = (iter->m_offset) & (~0x1);
- ARMWord* ldrAddr = reinterpret_cast_ptr<ARMWord*>(data + pos);
- ARMWord* addr = getLdrImmAddress(ldrAddr);
- if (*addr != InvalidBranchTarget) {
- if (!(iter->m_offset & 1)) {
- intptr_t difference = reinterpret_cast_ptr<ARMWord*>(data + *addr) - (ldrAddr + DefaultPrefetchOffset);
-
- if ((difference <= MaximumBranchOffsetDistance && difference >= MinimumBranchOffsetDistance)) {
- *ldrAddr = B | getConditionalField(*ldrAddr) | (difference & BranchOffsetMask);
- continue;
- }
- }
- *addr = reinterpret_cast<ARMWord>(data + *addr);
- }
- }
-
- return result;
-}
-
-#if OS(LINUX) && COMPILER(RVCT)
-
-__asm void ARMAssembler::cacheFlush(void* code, size_t size)
-{
- ARM
- push {r7}
- add r1, r1, r0
- mov r7, #0xf0000
- add r7, r7, #0x2
- mov r2, #0x0
- svc #0x0
- pop {r7}
- bx lr
-}
-
-#endif
-
-} // namespace JSC
-
-#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL)
diff --git a/src/3rdparty/masm/assembler/ARMAssembler.h b/src/3rdparty/masm/assembler/ARMAssembler.h
deleted file mode 100644
index 16cc25d4db..0000000000
--- a/src/3rdparty/masm/assembler/ARMAssembler.h
+++ /dev/null
@@ -1,1129 +0,0 @@
-/*
- * Copyright (C) 2009, 2010 University of Szeged
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. 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.
- *
- * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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.
- */
-
-#ifndef ARMAssembler_h
-#define ARMAssembler_h
-
-#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL)
-
-#include "AssemblerBufferWithConstantPool.h"
-#include "JITCompilationEffort.h"
-#include <wtf/Assertions.h>
-namespace JSC {
-
- typedef uint32_t ARMWord;
-
- namespace ARMRegisters {
- typedef enum {
- r0 = 0,
- r1,
- r2,
- r3,
- r4,
- r5,
- r6, S0 = r6,
- r7,
- r8,
- r9,
- r10,
- r11,
- r12, S1 = r12,
- r13, sp = r13,
- r14, lr = r14,
- r15, pc = r15
- } RegisterID;
-
- typedef enum {
- d0,
- d1,
- d2,
- d3,
- d4,
- d5,
- d6,
- d7, SD0 = d7, /* Same as thumb assembler. */
- d8,
- d9,
- d10,
- d11,
- d12,
- d13,
- d14,
- d15,
- d16,
- d17,
- d18,
- d19,
- d20,
- d21,
- d22,
- d23,
- d24,
- d25,
- d26,
- d27,
- d28,
- d29,
- d30,
- d31
- } FPRegisterID;
-
- } // namespace ARMRegisters
-
- class ARMAssembler {
- public:
- typedef ARMRegisters::RegisterID RegisterID;
- typedef ARMRegisters::FPRegisterID FPRegisterID;
- typedef AssemblerBufferWithConstantPool<2048, 4, 4, ARMAssembler> ARMBuffer;
- typedef SegmentedVector<AssemblerLabel, 64> Jumps;
-
- ARMAssembler()
- : m_indexOfTailOfLastWatchpoint(1)
- {
- }
-
- // ARM conditional constants
- typedef enum {
- EQ = 0x00000000, // Zero
- NE = 0x10000000, // Non-zero
- CS = 0x20000000,
- CC = 0x30000000,
- MI = 0x40000000,
- PL = 0x50000000,
- VS = 0x60000000,
- VC = 0x70000000,
- HI = 0x80000000,
- LS = 0x90000000,
- GE = 0xa0000000,
- LT = 0xb0000000,
- GT = 0xc0000000,
- LE = 0xd0000000,
- AL = 0xe0000000
- } Condition;
-
- // ARM instruction constants
- enum {
- AND = (0x0 << 21),
- EOR = (0x1 << 21),
- SUB = (0x2 << 21),
- RSB = (0x3 << 21),
- ADD = (0x4 << 21),
- ADC = (0x5 << 21),
- SBC = (0x6 << 21),
- RSC = (0x7 << 21),
- TST = (0x8 << 21),
- TEQ = (0x9 << 21),
- CMP = (0xa << 21),
- CMN = (0xb << 21),
- ORR = (0xc << 21),
- MOV = (0xd << 21),
- BIC = (0xe << 21),
- MVN = (0xf << 21),
- MUL = 0x00000090,
- MULL = 0x00c00090,
- VMOV_F64 = 0x0eb00b40,
- VADD_F64 = 0x0e300b00,
- VDIV_F64 = 0x0e800b00,
- VSUB_F64 = 0x0e300b40,
- VMUL_F64 = 0x0e200b00,
- VCMP_F64 = 0x0eb40b40,
- VSQRT_F64 = 0x0eb10bc0,
- VABS_F64 = 0x0eb00bc0,
- VNEG_F64 = 0x0eb10b40,
- STMDB = 0x09200000,
- LDMIA = 0x08b00000,
- B = 0x0a000000,
- BL = 0x0b000000,
- BX = 0x012fff10,
- VMOV_VFP64 = 0x0c400a10,
- VMOV_ARM64 = 0x0c500a10,
- VMOV_VFP32 = 0x0e000a10,
- VMOV_ARM32 = 0x0e100a10,
- VCVT_F64_S32 = 0x0eb80bc0,
- VCVT_S32_F64 = 0x0ebd0b40,
- VCVT_U32_F64 = 0x0ebc0b40,
- VCVT_F32_F64 = 0x0eb70bc0,
- VCVT_F64_F32 = 0x0eb70ac0,
- VMRS_APSR = 0x0ef1fa10,
- CLZ = 0x016f0f10,
- BKPT = 0xe1200070,
- BLX = 0x012fff30,
-#if WTF_ARM_ARCH_AT_LEAST(7)
- MOVW = 0x03000000,
- MOVT = 0x03400000,
-#endif
- NOP = 0xe1a00000,
- };
-
- enum {
- Op2Immediate = (1 << 25),
- ImmediateForHalfWordTransfer = (1 << 22),
- Op2InvertedImmediate = (1 << 26),
- SetConditionalCodes = (1 << 20),
- Op2IsRegisterArgument = (1 << 25),
- // Data transfer flags.
- DataTransferUp = (1 << 23),
- DataTransferWriteBack = (1 << 21),
- DataTransferPostUpdate = (1 << 24),
- DataTransferLoad = (1 << 20),
- ByteDataTransfer = (1 << 22),
- };
-
- enum DataTransferTypeA {
- LoadUint32 = 0x05000000 | DataTransferLoad,
- LoadUint8 = 0x05400000 | DataTransferLoad,
- StoreUint32 = 0x05000000,
- StoreUint8 = 0x05400000,
- };
-
- enum DataTransferTypeB {
- LoadUint16 = 0x010000b0 | DataTransferLoad,
- LoadInt16 = 0x010000f0 | DataTransferLoad,
- LoadInt8 = 0x010000d0 | DataTransferLoad,
- StoreUint16 = 0x010000b0,
- };
-
- enum DataTransferTypeFloat {
- LoadFloat = 0x0d000a00 | DataTransferLoad,
- LoadDouble = 0x0d000b00 | DataTransferLoad,
- StoreFloat = 0x0d000a00,
- StoreDouble = 0x0d000b00,
- };
-
- // Masks of ARM instructions
- enum {
- BranchOffsetMask = 0x00ffffff,
- ConditionalFieldMask = 0xf0000000,
- DataTransferOffsetMask = 0xfff,
- };
-
- enum {
- MinimumBranchOffsetDistance = -0x00800000,
- MaximumBranchOffsetDistance = 0x007fffff,
- };
-
- enum {
- padForAlign8 = 0x00,
- padForAlign16 = 0x0000,
- padForAlign32 = 0xe12fff7f // 'bkpt 0xffff' instruction.
- };
-
- static const ARMWord InvalidImmediate = 0xf0000000;
- static const ARMWord InvalidBranchTarget = 0xffffffff;
- static const int DefaultPrefetchOffset = 2;
-
- static const ARMWord BlxInstructionMask = 0x012fff30;
- static const ARMWord LdrOrAddInstructionMask = 0x0ff00000;
- static const ARMWord LdrPcImmediateInstructionMask = 0x0f7f0000;
-
- static const ARMWord AddImmediateInstruction = 0x02800000;
- static const ARMWord BlxInstruction = 0x012fff30;
- static const ARMWord LdrImmediateInstruction = 0x05900000;
- static const ARMWord LdrPcImmediateInstruction = 0x051f0000;
-
- // Instruction formating
-
- void emitInstruction(ARMWord op, int rd, int rn, ARMWord op2)
- {
- ASSERT(((op2 & ~Op2Immediate) <= 0xfff) || (((op2 & ~ImmediateForHalfWordTransfer) <= 0xfff)));
- m_buffer.putInt(op | RN(rn) | RD(rd) | op2);
- }
-
- void emitDoublePrecisionInstruction(ARMWord op, int dd, int dn, int dm)
- {
- ASSERT((dd >= 0 && dd <= 31) && (dn >= 0 && dn <= 31) && (dm >= 0 && dm <= 31));
- m_buffer.putInt(op | ((dd & 0xf) << 12) | ((dd & 0x10) << (22 - 4))
- | ((dn & 0xf) << 16) | ((dn & 0x10) << (7 - 4))
- | (dm & 0xf) | ((dm & 0x10) << (5 - 4)));
- }
-
- void emitSinglePrecisionInstruction(ARMWord op, int sd, int sn, int sm)
- {
- ASSERT((sd >= 0 && sd <= 31) && (sn >= 0 && sn <= 31) && (sm >= 0 && sm <= 31));
- m_buffer.putInt(op | ((sd >> 1) << 12) | ((sd & 0x1) << 22)
- | ((sn >> 1) << 16) | ((sn & 0x1) << 7)
- | (sm >> 1) | ((sm & 0x1) << 5));
- }
-
- void bitAnd(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | AND, rd, rn, op2);
- }
-
- void bitAnds(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | AND | SetConditionalCodes, rd, rn, op2);
- }
-
- void eor(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | EOR, rd, rn, op2);
- }
-
- void eors(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | EOR | SetConditionalCodes, rd, rn, op2);
- }
-
- void sub(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | SUB, rd, rn, op2);
- }
-
- void subs(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | SUB | SetConditionalCodes, rd, rn, op2);
- }
-
- void rsb(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | RSB, rd, rn, op2);
- }
-
- void rsbs(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | RSB | SetConditionalCodes, rd, rn, op2);
- }
-
- void add(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | ADD, rd, rn, op2);
- }
-
- void adds(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | ADD | SetConditionalCodes, rd, rn, op2);
- }
-
- void adc(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | ADC, rd, rn, op2);
- }
-
- void adcs(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | ADC | SetConditionalCodes, rd, rn, op2);
- }
-
- void sbc(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | SBC, rd, rn, op2);
- }
-
- void sbcs(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | SBC | SetConditionalCodes, rd, rn, op2);
- }
-
- void rsc(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | RSC, rd, rn, op2);
- }
-
- void rscs(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | RSC | SetConditionalCodes, rd, rn, op2);
- }
-
- void tst(int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | TST | SetConditionalCodes, 0, rn, op2);
- }
-
- void teq(int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | TEQ | SetConditionalCodes, 0, rn, op2);
- }
-
- void cmp(int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | CMP | SetConditionalCodes, 0, rn, op2);
- }
-
- void cmn(int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | CMN | SetConditionalCodes, 0, rn, op2);
- }
-
- void orr(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | ORR, rd, rn, op2);
- }
-
- void orrs(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | ORR | SetConditionalCodes, rd, rn, op2);
- }
-
- void mov(int rd, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | MOV, rd, ARMRegisters::r0, op2);
- }
-
-#if WTF_ARM_ARCH_AT_LEAST(7)
- void movw(int rd, ARMWord op2, Condition cc = AL)
- {
- ASSERT((op2 | 0xf0fff) == 0xf0fff);
- m_buffer.putInt(toARMWord(cc) | MOVW | RD(rd) | op2);
- }
-
- void movt(int rd, ARMWord op2, Condition cc = AL)
- {
- ASSERT((op2 | 0xf0fff) == 0xf0fff);
- m_buffer.putInt(toARMWord(cc) | MOVT | RD(rd) | op2);
- }
-#endif
-
- void movs(int rd, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | MOV | SetConditionalCodes, rd, ARMRegisters::r0, op2);
- }
-
- void bic(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | BIC, rd, rn, op2);
- }
-
- void bics(int rd, int rn, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | BIC | SetConditionalCodes, rd, rn, op2);
- }
-
- void mvn(int rd, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | MVN, rd, ARMRegisters::r0, op2);
- }
-
- void mvns(int rd, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | MVN | SetConditionalCodes, rd, ARMRegisters::r0, op2);
- }
-
- void mul(int rd, int rn, int rm, Condition cc = AL)
- {
- m_buffer.putInt(toARMWord(cc) | MUL | RN(rd) | RS(rn) | RM(rm));
- }
-
- void muls(int rd, int rn, int rm, Condition cc = AL)
- {
- m_buffer.putInt(toARMWord(cc) | MUL | SetConditionalCodes | RN(rd) | RS(rn) | RM(rm));
- }
-
- void mull(int rdhi, int rdlo, int rn, int rm, Condition cc = AL)
- {
- m_buffer.putInt(toARMWord(cc) | MULL | RN(rdhi) | RD(rdlo) | RS(rn) | RM(rm));
- }
-
- void vmov_f64(int dd, int dm, Condition cc = AL)
- {
- emitDoublePrecisionInstruction(toARMWord(cc) | VMOV_F64, dd, 0, dm);
- }
-
- void vadd_f64(int dd, int dn, int dm, Condition cc = AL)
- {
- emitDoublePrecisionInstruction(toARMWord(cc) | VADD_F64, dd, dn, dm);
- }
-
- void vdiv_f64(int dd, int dn, int dm, Condition cc = AL)
- {
- emitDoublePrecisionInstruction(toARMWord(cc) | VDIV_F64, dd, dn, dm);
- }
-
- void vsub_f64(int dd, int dn, int dm, Condition cc = AL)
- {
- emitDoublePrecisionInstruction(toARMWord(cc) | VSUB_F64, dd, dn, dm);
- }
-
- void vmul_f64(int dd, int dn, int dm, Condition cc = AL)
- {
- emitDoublePrecisionInstruction(toARMWord(cc) | VMUL_F64, dd, dn, dm);
- }
-
- void vcmp_f64(int dd, int dm, Condition cc = AL)
- {
- emitDoublePrecisionInstruction(toARMWord(cc) | VCMP_F64, dd, 0, dm);
- }
-
- void vsqrt_f64(int dd, int dm, Condition cc = AL)
- {
- emitDoublePrecisionInstruction(toARMWord(cc) | VSQRT_F64, dd, 0, dm);
- }
-
- void vabs_f64(int dd, int dm, Condition cc = AL)
- {
- emitDoublePrecisionInstruction(toARMWord(cc) | VABS_F64, dd, 0, dm);
- }
-
- void vneg_f64(int dd, int dm, Condition cc = AL)
- {
- emitDoublePrecisionInstruction(toARMWord(cc) | VNEG_F64, dd, 0, dm);
- }
-
- void ldrImmediate(int rd, ARMWord imm, Condition cc = AL)
- {
- m_buffer.putIntWithConstantInt(toARMWord(cc) | LoadUint32 | DataTransferUp | RN(ARMRegisters::pc) | RD(rd), imm, true);
- }
-
- void ldrUniqueImmediate(int rd, ARMWord imm, Condition cc = AL)
- {
- m_buffer.putIntWithConstantInt(toARMWord(cc) | LoadUint32 | DataTransferUp | RN(ARMRegisters::pc) | RD(rd), imm);
- }
-
- void dtrUp(DataTransferTypeA transferType, int rd, int rb, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | transferType | DataTransferUp, rd, rb, op2);
- }
-
- void dtrUpRegister(DataTransferTypeA transferType, int rd, int rb, int rm, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | transferType | DataTransferUp | Op2IsRegisterArgument, rd, rb, rm);
- }
-
- void dtrDown(DataTransferTypeA transferType, int rd, int rb, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | transferType, rd, rb, op2);
- }
-
- void dtrDownRegister(DataTransferTypeA transferType, int rd, int rb, int rm, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | transferType | Op2IsRegisterArgument, rd, rb, rm);
- }
-
- void halfDtrUp(DataTransferTypeB transferType, int rd, int rb, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | transferType | DataTransferUp, rd, rb, op2);
- }
-
- void halfDtrUpRegister(DataTransferTypeB transferType, int rd, int rn, int rm, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | transferType | DataTransferUp, rd, rn, rm);
- }
-
- void halfDtrDown(DataTransferTypeB transferType, int rd, int rb, ARMWord op2, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | transferType, rd, rb, op2);
- }
-
- void halfDtrDownRegister(DataTransferTypeB transferType, int rd, int rn, int rm, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | transferType, rd, rn, rm);
- }
-
- void doubleDtrUp(DataTransferTypeFloat type, int rd, int rb, ARMWord op2, Condition cc = AL)
- {
- ASSERT(op2 <= 0xff && rd <= 15);
- /* Only d0-d15 and s0, s2, s4 ... s30 are supported. */
- m_buffer.putInt(toARMWord(cc) | DataTransferUp | type | (rd << 12) | RN(rb) | op2);
- }
-
- void doubleDtrDown(DataTransferTypeFloat type, int rd, int rb, ARMWord op2, Condition cc = AL)
- {
- ASSERT(op2 <= 0xff && rd <= 15);
- /* Only d0-d15 and s0, s2, s4 ... s30 are supported. */
- m_buffer.putInt(toARMWord(cc) | type | (rd << 12) | RN(rb) | op2);
- }
-
- void push(int reg, Condition cc = AL)
- {
- ASSERT(ARMWord(reg) <= 0xf);
- m_buffer.putInt(toARMWord(cc) | StoreUint32 | DataTransferWriteBack | RN(ARMRegisters::sp) | RD(reg) | 0x4);
- }
-
- void pop(int reg, Condition cc = AL)
- {
- ASSERT(ARMWord(reg) <= 0xf);
- m_buffer.putInt(toARMWord(cc) | (LoadUint32 ^ DataTransferPostUpdate) | DataTransferUp | RN(ARMRegisters::sp) | RD(reg) | 0x4);
- }
-
- inline void poke(int reg, Condition cc = AL)
- {
- dtrDown(StoreUint32, ARMRegisters::sp, 0, reg, cc);
- }
-
- inline void peek(int reg, Condition cc = AL)
- {
- dtrUp(LoadUint32, reg, ARMRegisters::sp, 0, cc);
- }
-
- void vmov_vfp64(int sm, int rt, int rt2, Condition cc = AL)
- {
- ASSERT(rt != rt2);
- m_buffer.putInt(toARMWord(cc) | VMOV_VFP64 | RN(rt2) | RD(rt) | (sm & 0xf) | ((sm & 0x10) << (5 - 4)));
- }
-
- void vmov_arm64(int rt, int rt2, int sm, Condition cc = AL)
- {
- ASSERT(rt != rt2);
- m_buffer.putInt(toARMWord(cc) | VMOV_ARM64 | RN(rt2) | RD(rt) | (sm & 0xf) | ((sm & 0x10) << (5 - 4)));
- }
-
- void vmov_vfp32(int sn, int rt, Condition cc = AL)
- {
- ASSERT(rt <= 15);
- emitSinglePrecisionInstruction(toARMWord(cc) | VMOV_VFP32, rt << 1, sn, 0);
- }
-
- void vmov_arm32(int rt, int sn, Condition cc = AL)
- {
- ASSERT(rt <= 15);
- emitSinglePrecisionInstruction(toARMWord(cc) | VMOV_ARM32, rt << 1, sn, 0);
- }
-
- void vcvt_f64_s32(int dd, int sm, Condition cc = AL)
- {
- ASSERT(!(sm & 0x1)); // sm must be divisible by 2
- emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_F64_S32, dd, 0, (sm >> 1));
- }
-
- void vcvt_s32_f64(int sd, int dm, Condition cc = AL)
- {
- ASSERT(!(sd & 0x1)); // sd must be divisible by 2
- emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_S32_F64, (sd >> 1), 0, dm);
- }
-
- void vcvt_u32_f64(int sd, int dm, Condition cc = AL)
- {
- ASSERT(!(sd & 0x1)); // sd must be divisible by 2
- emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_U32_F64, (sd >> 1), 0, dm);
- }
-
- void vcvt_f64_f32(int dd, int sm, Condition cc = AL)
- {
- ASSERT(dd <= 15 && sm <= 15);
- emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_F64_F32, dd, 0, sm);
- }
-
- void vcvt_f32_f64(int dd, int sm, Condition cc = AL)
- {
- ASSERT(dd <= 15 && sm <= 15);
- emitDoublePrecisionInstruction(toARMWord(cc) | VCVT_F32_F64, dd, 0, sm);
- }
-
- void vmrs_apsr(Condition cc = AL)
- {
- m_buffer.putInt(toARMWord(cc) | VMRS_APSR);
- }
-
- void clz(int rd, int rm, Condition cc = AL)
- {
- m_buffer.putInt(toARMWord(cc) | CLZ | RD(rd) | RM(rm));
- }
-
- void bkpt(ARMWord value)
- {
- m_buffer.putInt(BKPT | ((value & 0xff0) << 4) | (value & 0xf));
- }
-
- void nop()
- {
- m_buffer.putInt(NOP);
- }
-
- void bx(int rm, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | BX, 0, 0, RM(rm));
- }
-
- AssemblerLabel blx(int rm, Condition cc = AL)
- {
- emitInstruction(toARMWord(cc) | BLX, 0, 0, RM(rm));
- return m_buffer.label();
- }
-
- static ARMWord lsl(int reg, ARMWord value)
- {
- ASSERT(reg <= ARMRegisters::pc);
- ASSERT(value <= 0x1f);
- return reg | (value << 7) | 0x00;
- }
-
- static ARMWord lsr(int reg, ARMWord value)
- {
- ASSERT(reg <= ARMRegisters::pc);
- ASSERT(value <= 0x1f);
- return reg | (value << 7) | 0x20;
- }
-
- static ARMWord asr(int reg, ARMWord value)
- {
- ASSERT(reg <= ARMRegisters::pc);
- ASSERT(value <= 0x1f);
- return reg | (value << 7) | 0x40;
- }
-
- static ARMWord lslRegister(int reg, int shiftReg)
- {
- ASSERT(reg <= ARMRegisters::pc);
- ASSERT(shiftReg <= ARMRegisters::pc);
- return reg | (shiftReg << 8) | 0x10;
- }
-
- static ARMWord lsrRegister(int reg, int shiftReg)
- {
- ASSERT(reg <= ARMRegisters::pc);
- ASSERT(shiftReg <= ARMRegisters::pc);
- return reg | (shiftReg << 8) | 0x30;
- }
-
- static ARMWord asrRegister(int reg, int shiftReg)
- {
- ASSERT(reg <= ARMRegisters::pc);
- ASSERT(shiftReg <= ARMRegisters::pc);
- return reg | (shiftReg << 8) | 0x50;
- }
-
- // General helpers
-
- size_t codeSize() const
- {
- return m_buffer.codeSize();
- }
-
- void ensureSpace(int insnSpace, int constSpace)
- {
- m_buffer.ensureSpace(insnSpace, constSpace);
- }
-
- int sizeOfConstantPool()
- {
- return m_buffer.sizeOfConstantPool();
- }
-
- AssemblerLabel labelIgnoringWatchpoints()
- {
- m_buffer.ensureSpaceForAnyInstruction();
- return m_buffer.label();
- }
-
- AssemblerLabel labelForWatchpoint()
- {
- m_buffer.ensureSpaceForAnyInstruction(maxJumpReplacementSize() / sizeof(ARMWord));
- AssemblerLabel result = m_buffer.label();
- if (result.m_offset != (m_indexOfTailOfLastWatchpoint - maxJumpReplacementSize()))
- result = label();
- m_indexOfTailOfLastWatchpoint = result.m_offset + maxJumpReplacementSize();
- return label();
- }
-
- AssemblerLabel label()
- {
- AssemblerLabel result = labelIgnoringWatchpoints();
- while (result.m_offset + 1 < m_indexOfTailOfLastWatchpoint) {
- nop();
- // The available number of instructions are ensured by labelForWatchpoint.
- result = m_buffer.label();
- }
- return result;
- }
-
- AssemblerLabel align(int alignment)
- {
- while (!m_buffer.isAligned(alignment))
- mov(ARMRegisters::r0, ARMRegisters::r0);
-
- return label();
- }
-
- AssemblerLabel loadBranchTarget(int rd, Condition cc = AL, int useConstantPool = 0)
- {
- ensureSpace(sizeof(ARMWord), sizeof(ARMWord));
- m_jumps.append(m_buffer.codeSize() | (useConstantPool & 0x1));
- ldrUniqueImmediate(rd, InvalidBranchTarget, cc);
- return m_buffer.label();
- }
-
- AssemblerLabel jmp(Condition cc = AL, int useConstantPool = 0)
- {
- return loadBranchTarget(ARMRegisters::pc, cc, useConstantPool);
- }
-
- PassRefPtr<ExecutableMemoryHandle> executableCopy(JSGlobalData&, void* ownerUID, JITCompilationEffort);
-
- unsigned debugOffset() { return m_buffer.debugOffset(); }
-
- // DFG assembly helpers for moving data between fp and registers.
- void vmov(RegisterID rd1, RegisterID rd2, FPRegisterID rn)
- {
- vmov_arm64(rd1, rd2, rn);
- }
-
- void vmov(FPRegisterID rd, RegisterID rn1, RegisterID rn2)
- {
- vmov_vfp64(rd, rn1, rn2);
- }
-
- // Patching helpers
-
- static ARMWord* getLdrImmAddress(ARMWord* insn)
- {
- // Check for call
- if ((*insn & LdrPcImmediateInstructionMask) != LdrPcImmediateInstruction) {
- // Must be BLX
- ASSERT((*insn & BlxInstructionMask) == BlxInstruction);
- insn--;
- }
-
- // Must be an ldr ..., [pc +/- imm]
- ASSERT((*insn & LdrPcImmediateInstructionMask) == LdrPcImmediateInstruction);
-
- ARMWord addr = reinterpret_cast<ARMWord>(insn) + DefaultPrefetchOffset * sizeof(ARMWord);
- if (*insn & DataTransferUp)
- return reinterpret_cast<ARMWord*>(addr + (*insn & DataTransferOffsetMask));
- return reinterpret_cast<ARMWord*>(addr - (*insn & DataTransferOffsetMask));
- }
-
- static ARMWord* getLdrImmAddressOnPool(ARMWord* insn, uint32_t* constPool)
- {
- // Must be an ldr ..., [pc +/- imm]
- ASSERT((*insn & LdrPcImmediateInstructionMask) == LdrPcImmediateInstruction);
-
- if (*insn & 0x1)
- return reinterpret_cast<ARMWord*>(constPool + ((*insn & DataTransferOffsetMask) >> 1));
- return getLdrImmAddress(insn);
- }
-
- static void patchPointerInternal(intptr_t from, void* to)
- {
- ARMWord* insn = reinterpret_cast<ARMWord*>(from);
- ARMWord* addr = getLdrImmAddress(insn);
- *addr = reinterpret_cast<ARMWord>(to);
- }
-
- static ARMWord patchConstantPoolLoad(ARMWord load, ARMWord value)
- {
- value = (value << 1) + 1;
- ASSERT(!(value & ~DataTransferOffsetMask));
- return (load & ~DataTransferOffsetMask) | value;
- }
-
- static void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr);
-
- // Read pointers
- static void* readPointer(void* from)
- {
- ARMWord* instruction = reinterpret_cast<ARMWord*>(from);
- ARMWord* address = getLdrImmAddress(instruction);
- return *reinterpret_cast<void**>(address);
- }
-
- // Patch pointers
-
- static void linkPointer(void* code, AssemblerLabel from, void* to)
- {
- patchPointerInternal(reinterpret_cast<intptr_t>(code) + from.m_offset, to);
- }
-
- static void repatchInt32(void* where, int32_t to)
- {
- patchPointerInternal(reinterpret_cast<intptr_t>(where), reinterpret_cast<void*>(to));
- }
-
- static void repatchCompact(void* where, int32_t value)
- {
- ARMWord* instruction = reinterpret_cast<ARMWord*>(where);
- ASSERT((*instruction & 0x0f700000) == LoadUint32);
- if (value >= 0)
- *instruction = (*instruction & 0xff7ff000) | DataTransferUp | value;
- else
- *instruction = (*instruction & 0xff7ff000) | -value;
- cacheFlush(instruction, sizeof(ARMWord));
- }
-
- static void repatchPointer(void* from, void* to)
- {
- patchPointerInternal(reinterpret_cast<intptr_t>(from), to);
- }
-
- // Linkers
- static intptr_t getAbsoluteJumpAddress(void* base, int offset = 0)
- {
- return reinterpret_cast<intptr_t>(base) + offset - sizeof(ARMWord);
- }
-
- void linkJump(AssemblerLabel from, AssemblerLabel to)
- {
- ARMWord* insn = reinterpret_cast<ARMWord*>(getAbsoluteJumpAddress(m_buffer.data(), from.m_offset));
- ARMWord* addr = getLdrImmAddressOnPool(insn, m_buffer.poolAddress());
- *addr = toARMWord(to.m_offset);
- }
-
- static void linkJump(void* code, AssemblerLabel from, void* to)
- {
- patchPointerInternal(getAbsoluteJumpAddress(code, from.m_offset), to);
- }
-
- static void relinkJump(void* from, void* to)
- {
- patchPointerInternal(getAbsoluteJumpAddress(from), to);
- }
-
- static void linkCall(void* code, AssemblerLabel from, void* to)
- {
- patchPointerInternal(getAbsoluteJumpAddress(code, from.m_offset), to);
- }
-
- static void relinkCall(void* from, void* to)
- {
- patchPointerInternal(getAbsoluteJumpAddress(from), to);
- }
-
- static void* readCallTarget(void* from)
- {
- return reinterpret_cast<void*>(readPointer(reinterpret_cast<void*>(getAbsoluteJumpAddress(from))));
- }
-
- static void replaceWithJump(void* instructionStart, void* to)
- {
- ARMWord* instruction = reinterpret_cast<ARMWord*>(instructionStart);
- intptr_t difference = reinterpret_cast<intptr_t>(to) - (reinterpret_cast<intptr_t>(instruction) + DefaultPrefetchOffset * sizeof(ARMWord));
-
- if (!(difference & 1)) {
- difference >>= 2;
- if ((difference <= MaximumBranchOffsetDistance && difference >= MinimumBranchOffsetDistance)) {
- // Direct branch.
- instruction[0] = B | AL | (difference & BranchOffsetMask);
- cacheFlush(instruction, sizeof(ARMWord));
- return;
- }
- }
-
- // Load target.
- instruction[0] = LoadUint32 | AL | RN(ARMRegisters::pc) | RD(ARMRegisters::pc) | 4;
- instruction[1] = reinterpret_cast<ARMWord>(to);
- cacheFlush(instruction, sizeof(ARMWord) * 2);
- }
-
- static ptrdiff_t maxJumpReplacementSize()
- {
- return sizeof(ARMWord) * 2;
- }
-
- static void replaceWithLoad(void* instructionStart)
- {
- ARMWord* instruction = reinterpret_cast<ARMWord*>(instructionStart);
- cacheFlush(instruction, sizeof(ARMWord));
-
- ASSERT((*instruction & LdrOrAddInstructionMask) == AddImmediateInstruction || (*instruction & LdrOrAddInstructionMask) == LdrImmediateInstruction);
- if ((*instruction & LdrOrAddInstructionMask) == AddImmediateInstruction) {
- *instruction = (*instruction & ~LdrOrAddInstructionMask) | LdrImmediateInstruction;
- cacheFlush(instruction, sizeof(ARMWord));
- }
- }
-
- static void replaceWithAddressComputation(void* instructionStart)
- {
- ARMWord* instruction = reinterpret_cast<ARMWord*>(instructionStart);
- cacheFlush(instruction, sizeof(ARMWord));
-
- ASSERT((*instruction & LdrOrAddInstructionMask) == AddImmediateInstruction || (*instruction & LdrOrAddInstructionMask) == LdrImmediateInstruction);
- if ((*instruction & LdrOrAddInstructionMask) == LdrImmediateInstruction) {
- *instruction = (*instruction & ~LdrOrAddInstructionMask) | AddImmediateInstruction;
- cacheFlush(instruction, sizeof(ARMWord));
- }
- }
-
- static void revertBranchPtrWithPatch(void* instructionStart, RegisterID rn, ARMWord imm)
- {
- ARMWord* instruction = reinterpret_cast<ARMWord*>(instructionStart);
-
- ASSERT((instruction[2] & LdrPcImmediateInstructionMask) == LdrPcImmediateInstruction);
- instruction[0] = toARMWord(AL) | ((instruction[2] & 0x0fff0fff) + sizeof(ARMWord)) | RD(ARMRegisters::S1);
- *getLdrImmAddress(instruction) = imm;
- instruction[1] = toARMWord(AL) | CMP | SetConditionalCodes | RN(rn) | RM(ARMRegisters::S1);
- cacheFlush(instruction, 2 * sizeof(ARMWord));
- }
-
- // Address operations
-
- static void* getRelocatedAddress(void* code, AssemblerLabel label)
- {
- return reinterpret_cast<void*>(reinterpret_cast<char*>(code) + label.m_offset);
- }
-
- // Address differences
-
- static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b)
- {
- return b.m_offset - a.m_offset;
- }
-
- static unsigned getCallReturnOffset(AssemblerLabel call)
- {
- return call.m_offset;
- }
-
- // Handle immediates
-
- static ARMWord getOp2(ARMWord imm);
-
- // Fast case if imm is known to be between 0 and 0xff
- static ARMWord getOp2Byte(ARMWord imm)
- {
- ASSERT(imm <= 0xff);
- return Op2Immediate | imm;
- }
-
- static ARMWord getOp2Half(ARMWord imm)
- {
- ASSERT(imm <= 0xff);
- return ImmediateForHalfWordTransfer | (imm & 0x0f) | ((imm & 0xf0) << 4);
- }
-
-#if WTF_ARM_ARCH_AT_LEAST(7)
- static ARMWord getImm16Op2(ARMWord imm)
- {
- if (imm <= 0xffff)
- return (imm & 0xf000) << 4 | (imm & 0xfff);
- return InvalidImmediate;
- }
-#endif
- ARMWord getImm(ARMWord imm, int tmpReg, bool invert = false);
- void moveImm(ARMWord imm, int dest);
- ARMWord encodeComplexImm(ARMWord imm, int dest);
-
- // Memory load/store helpers
-
- void dataTransfer32(DataTransferTypeA, RegisterID srcDst, RegisterID base, int32_t offset);
- void baseIndexTransfer32(DataTransferTypeA, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset);
- void dataTransfer16(DataTransferTypeB, RegisterID srcDst, RegisterID base, int32_t offset);
- void baseIndexTransfer16(DataTransferTypeB, RegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset);
- void dataTransferFloat(DataTransferTypeFloat, FPRegisterID srcDst, RegisterID base, int32_t offset);
- void baseIndexTransferFloat(DataTransferTypeFloat, FPRegisterID srcDst, RegisterID base, RegisterID index, int scale, int32_t offset);
-
- // Constant pool hnadlers
-
- static ARMWord placeConstantPoolBarrier(int offset)
- {
- offset = (offset - sizeof(ARMWord)) >> 2;
- ASSERT((offset <= MaximumBranchOffsetDistance && offset >= MinimumBranchOffsetDistance));
- return AL | B | (offset & BranchOffsetMask);
- }
-
-#if OS(LINUX) && COMPILER(GCC)
- static inline void linuxPageFlush(uintptr_t begin, uintptr_t end)
- {
- asm volatile(
- "push {r7}\n"
- "mov r0, %0\n"
- "mov r1, %1\n"
- "mov r7, #0xf0000\n"
- "add r7, r7, #0x2\n"
- "mov r2, #0x0\n"
- "svc 0x0\n"
- "pop {r7}\n"
- :
- : "r" (begin), "r" (end)
- : "r0", "r1", "r2");
- }
-#endif
-
-#if OS(LINUX) && COMPILER(RVCT)
- static __asm void cacheFlush(void* code, size_t);
-#else
- static void cacheFlush(void* code, size_t size)
- {
-#if OS(LINUX) && COMPILER(GCC)
- size_t page = pageSize();
- uintptr_t current = reinterpret_cast<uintptr_t>(code);
- uintptr_t end = current + size;
- uintptr_t firstPageEnd = (current & ~(page - 1)) + page;
-
- if (end <= firstPageEnd) {
- linuxPageFlush(current, end);
- return;
- }
-
- linuxPageFlush(current, firstPageEnd);
-
- for (current = firstPageEnd; current + page < end; current += page)
- linuxPageFlush(current, current + page);
-
- linuxPageFlush(current, end);
-#elif OS(WINCE)
- CacheRangeFlush(code, size, CACHE_SYNC_ALL);
-#elif OS(QNX) && ENABLE(ASSEMBLER_WX_EXCLUSIVE)
- UNUSED_PARAM(code);
- UNUSED_PARAM(size);
-#elif OS(QNX)
- msync(code, size, MS_INVALIDATE_ICACHE);
-#else
-#error "The cacheFlush support is missing on this platform."
-#endif
- }
-#endif
-
- private:
- static ARMWord RM(int reg)
- {
- ASSERT(reg <= ARMRegisters::pc);
- return reg;
- }
-
- static ARMWord RS(int reg)
- {
- ASSERT(reg <= ARMRegisters::pc);
- return reg << 8;
- }
-
- static ARMWord RD(int reg)
- {
- ASSERT(reg <= ARMRegisters::pc);
- return reg << 12;
- }
-
- static ARMWord RN(int reg)
- {
- ASSERT(reg <= ARMRegisters::pc);
- return reg << 16;
- }
-
- static ARMWord getConditionalField(ARMWord i)
- {
- return i & ConditionalFieldMask;
- }
-
- static ARMWord toARMWord(Condition cc)
- {
- return static_cast<ARMWord>(cc);
- }
-
- static ARMWord toARMWord(uint32_t u)
- {
- return static_cast<ARMWord>(u);
- }
-
- int genInt(int reg, ARMWord imm, bool positive);
-
- ARMBuffer m_buffer;
- Jumps m_jumps;
- uint32_t m_indexOfTailOfLastWatchpoint;
- };
-
-} // namespace JSC
-
-#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL)
-
-#endif // ARMAssembler_h
diff --git a/src/3rdparty/masm/assembler/AbstractMacroAssembler.h b/src/3rdparty/masm/assembler/AbstractMacroAssembler.h
index 1076384900..d0c1c4613e 100644
--- a/src/3rdparty/masm/assembler/AbstractMacroAssembler.h
+++ b/src/3rdparty/masm/assembler/AbstractMacroAssembler.h
@@ -563,12 +563,6 @@ public:
{
ASSERT((type == ARM64Assembler::JumpTestBit) || (type == ARM64Assembler::JumpTestBitFixedSize));
}
-#elif CPU(SH4)
- Jump(AssemblerLabel jmp, SH4Assembler::JumpType type = SH4Assembler::JumpFar)
- : m_label(jmp)
- , m_type(type)
- {
- }
#else
Jump(AssemblerLabel jmp)
: m_label(jmp)
@@ -598,8 +592,6 @@ public:
masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type, m_condition, m_bitNumber, m_compareRegister);
else
masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type, m_condition);
-#elif CPU(SH4)
- masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type);
#else
masm->m_assembler.linkJump(m_label, masm->m_assembler.label());
#endif
@@ -640,9 +632,6 @@ public:
unsigned m_bitNumber;
ARM64Assembler::RegisterID m_compareRegister;
#endif
-#if CPU(SH4)
- SH4Assembler::JumpType m_type;
-#endif
};
#endif
diff --git a/src/3rdparty/masm/assembler/AssemblerBuffer.h b/src/3rdparty/masm/assembler/AssemblerBuffer.h
index 277ec1043c..45874235b6 100644
--- a/src/3rdparty/masm/assembler/AssemblerBuffer.h
+++ b/src/3rdparty/masm/assembler/AssemblerBuffer.h
@@ -102,7 +102,7 @@ namespace JSC {
void putIntegralUnchecked(IntegralType value)
{
ASSERT(isAvailable(sizeof(IntegralType)));
- *reinterpret_cast_ptr<IntegralType*>(m_buffer + m_index) = value;
+ memcpy(m_buffer + m_index, &value, sizeof(IntegralType));
m_index += sizeof(IntegralType);
}
diff --git a/src/3rdparty/masm/assembler/LinkBuffer.cpp b/src/3rdparty/masm/assembler/LinkBuffer.cpp
index 74c278135b..44a11706c9 100644
--- a/src/3rdparty/masm/assembler/LinkBuffer.cpp
+++ b/src/3rdparty/masm/assembler/LinkBuffer.cpp
@@ -75,23 +75,6 @@ void LinkBuffer::dumpCode(void* code, size_t size)
for (unsigned i = 0; i < tsize; i++)
dataLogF("\t.short\t0x%x\n", tcode[i]);
-#elif CPU(ARM_TRADITIONAL)
- // gcc -c jit.s
- // objdump -D jit.o
- static unsigned codeCount = 0;
- unsigned int* tcode = static_cast<unsigned int*>(code);
- size_t tsize = size / sizeof(unsigned int);
- char nameBuf[128];
- snprintf(nameBuf, sizeof(nameBuf), "_jsc_jit%u", codeCount++);
- dataLogF("\t.globl\t%s\n"
- "\t.align 4\n"
- "\t.code 32\n"
- "\t.text\n"
- "# %p\n"
- "%s:\n", nameBuf, code, nameBuf);
-
- for (unsigned i = 0; i < tsize; i++)
- dataLogF("\t.long\t0x%x\n", tcode[i]);
#endif
}
#endif
diff --git a/src/3rdparty/masm/assembler/LinkBuffer.h b/src/3rdparty/masm/assembler/LinkBuffer.h
index bfd0e402ca..c79b0663c8 100644
--- a/src/3rdparty/masm/assembler/LinkBuffer.h
+++ b/src/3rdparty/masm/assembler/LinkBuffer.h
@@ -26,6 +26,8 @@
#ifndef LinkBuffer_h
#define LinkBuffer_h
+#include <Platform.h>
+
#if ENABLE(ASSEMBLER)
#define DUMP_LINK_STATISTICS 0
@@ -66,7 +68,7 @@ struct DefaultExecutableOffsetCalculator {
//
template <typename MacroAssembler, template <typename T> class ExecutableOffsetCalculator>
class LinkBufferBase {
- WTF_MAKE_NONCOPYABLE(LinkBufferBase);
+ WTF_MAKE_NONCOPYABLE(LinkBufferBase)
typedef MacroAssemblerCodeRef CodeRef;
typedef MacroAssemblerCodePtr CodePtr;
typedef typename MacroAssembler::Label Label;
@@ -209,7 +211,7 @@ public:
// displaying disassembly.
inline CodeRef finalizeCodeWithoutDisassembly();
- inline CodeRef finalizeCodeWithDisassembly(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3);
+ inline CodeRef finalizeCodeWithDisassembly(const char *jitKind, const char* format, ...) WTF_ATTRIBUTE_PRINTF(3, 4);
CodePtr trampolineAt(Label label)
{
@@ -263,9 +265,9 @@ protected:
#endif
};
-#define FINALIZE_CODE_IF(condition, linkBufferReference, dataLogFArgumentsForHeading) \
+#define FINALIZE_CODE_IF(condition, linkBufferReference, jitKind, dataLogFArgumentsForHeading) \
(UNLIKELY((condition)) \
- ? ((linkBufferReference).finalizeCodeWithDisassembly dataLogFArgumentsForHeading) \
+ ? ((linkBufferReference).finalizeCodeWithDisassembly (jitKind, dataLogFArgumentsForHeading)) \
: (linkBufferReference).finalizeCodeWithoutDisassembly())
// Use this to finalize code, like so:
@@ -284,11 +286,11 @@ protected:
// Note that the dataLogFArgumentsForHeading are only evaluated when showDisassembly
// is true, so you can hide expensive disassembly-only computations inside there.
-#define FINALIZE_CODE(linkBufferReference, dataLogFArgumentsForHeading) \
- FINALIZE_CODE_IF(Options::showDisassembly(), linkBufferReference, dataLogFArgumentsForHeading)
+#define FINALIZE_CODE(linkBufferReference, jitKind, dataLogFArgumentsForHeading) \
+ FINALIZE_CODE_IF(Options::showDisassembly(), linkBufferReference, jitKind, dataLogFArgumentsForHeading)
-#define FINALIZE_DFG_CODE(linkBufferReference, dataLogFArgumentsForHeading) \
- FINALIZE_CODE_IF((Options::showDisassembly() || Options::showDFGDisassembly()), linkBufferReference, dataLogFArgumentsForHeading)
+#define FINALIZE_DFG_CODE(linkBufferReference, jitKind, dataLogFArgumentsForHeading) \
+ FINALIZE_CODE_IF((Options::showDisassembly() || Options::showDFGDisassembly()), linkBufferReference, jitKind, dataLogFArgumentsForHeading)
template <typename MacroAssembler, template <typename T> class ExecutableOffsetCalculator>
@@ -300,13 +302,13 @@ inline typename LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::Code
}
template <typename MacroAssembler, template <typename T> class ExecutableOffsetCalculator>
-inline typename LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::CodeRef LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::finalizeCodeWithDisassembly(const char* format, ...)
+inline typename LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::CodeRef LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::finalizeCodeWithDisassembly(const char *jitKind, const char* format, ...)
{
ASSERT(Options::showDisassembly() || Options::showDFGDisassembly());
CodeRef result = finalizeCodeWithoutDisassembly();
- dataLogF("Generated JIT code for ");
+ dataLogF("Generated %s code for ", jitKind);
va_list argList;
va_start(argList, format);
WTF::dataLogFV(format, argList);
@@ -350,8 +352,8 @@ inline void LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::performF
#endif
ASSERT(m_size <= INT_MAX);
- ExecutableAllocator::makeExecutable(code(), static_cast<int>(m_size));
MacroAssembler::cacheFlush(code(), m_size);
+ ExecutableAllocator::makeExecutable(code(), static_cast<int>(m_size));
}
template <typename MacroAssembler>
@@ -418,8 +420,8 @@ inline void BranchCompactingLinkBuffer<MacroAssembler>::performFinalization()
this->m_completed = true;
#endif
- ExecutableAllocator::makeExecutable(code(), m_initialSize);
MacroAssembler::cacheFlush(code(), m_size);
+ ExecutableAllocator::makeExecutable(code(), m_initialSize);
}
template <typename MacroAssembler>
@@ -518,6 +520,18 @@ public:
#endif
+#if CPU(ARM_THUMB2)
+typedef LinkBuffer<MacroAssembler<MacroAssemblerARMv7>> DefaultLinkBuffer;
+#elif CPU(ARM64)
+typedef LinkBuffer<MacroAssembler<MacroAssemblerARM64>> DefaultLinkBuffer;
+#elif CPU(MIPS)
+typedef LinkBuffer<MacroAssembler<MacroAssemblerMIPS>> DefaultLinkBuffer;
+#elif CPU(X86)
+typedef LinkBuffer<MacroAssembler<MacroAssemblerX86>> DefaultLinkBuffer;
+#elif CPU(X86_64)
+typedef LinkBuffer<MacroAssembler<MacroAssemblerX86_64>> DefaultLinkBuffer;
+#endif
+
} // namespace JSC
#endif // ENABLE(ASSEMBLER)
diff --git a/src/3rdparty/masm/assembler/MacroAssembler.h b/src/3rdparty/masm/assembler/MacroAssembler.h
index 20ddcadae1..b442a81bd0 100644
--- a/src/3rdparty/masm/assembler/MacroAssembler.h
+++ b/src/3rdparty/masm/assembler/MacroAssembler.h
@@ -39,10 +39,6 @@ namespace JSC { typedef MacroAssemblerARMv7 MacroAssemblerBase; };
#elif CPU(ARM64)
namespace JSC { typedef MacroAssemblerARM64 MacroAssemblerBase; };
-#elif CPU(ARM_TRADITIONAL)
-#include "MacroAssemblerARM.h"
-namespace JSC { typedef MacroAssemblerARM MacroAssemblerBase; };
-
#elif CPU(MIPS)
#include "MacroAssemblerMIPS.h"
namespace JSC {
@@ -57,12 +53,6 @@ namespace JSC { typedef MacroAssemblerX86 MacroAssemblerBase; };
#include "MacroAssemblerX86_64.h"
namespace JSC { typedef MacroAssemblerX86_64 MacroAssemblerBase; };
-#elif CPU(SH4)
-#include "MacroAssemblerSH4.h"
-namespace JSC {
-typedef MacroAssemblerSH4 MacroAssemblerBase;
-};
-
#else
#error "The MacroAssembler is not supported on this platform."
#endif
@@ -805,7 +795,7 @@ public:
Jump branchPtr(RelationalCondition cond, RegisterID left, RegisterID right)
{
- return branch64(cond, left, right);
+ return this->branch64(cond, left, right);
}
Jump branchPtr(RelationalCondition cond, RegisterID left, TrustedImmPtr right)
@@ -840,7 +830,7 @@ public:
Jump branchTestPtr(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1))
{
- return branchTest64(cond, reg, mask);
+ return this->branchTest64(cond, reg, mask);
}
Jump branchTestPtr(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1))
@@ -1489,16 +1479,12 @@ public:
typedef MacroAssembler<MacroAssemblerARMv7> DefaultMacroAssembler;
#elif CPU(ARM64)
typedef MacroAssembler<MacroAssemblerARM64> DefaultMacroAssembler;
-#elif CPU(ARM_TRADITIONAL)
-typedef MacroAssembler<MacroAssemblerARM> DefaultMacroAssembler;
#elif CPU(MIPS)
typedef MacroAssembler<MacroAssemblerMIPS> DefaultMacroAssembler;
#elif CPU(X86)
typedef MacroAssembler<MacroAssemblerX86> DefaultMacroAssembler;
#elif CPU(X86_64)
typedef MacroAssembler<MacroAssemblerX86_64> DefaultMacroAssembler;
-#elif CPU(SH4)
-typedef JSC::MacroAssemblerSH4 DefaultMacroAssembler;
#endif
} // namespace JSC
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARM.cpp b/src/3rdparty/masm/assembler/MacroAssemblerARM.cpp
deleted file mode 100644
index 3ca9c7da80..0000000000
--- a/src/3rdparty/masm/assembler/MacroAssemblerARM.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2009 University of Szeged
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. 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.
- *
- * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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.
- */
-
-#include "config.h"
-
-#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL)
-
-#include "MacroAssemblerARM.h"
-
-#if OS(LINUX)
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <elf.h>
-#define HWCAP_VFP (1 << 6)
-#endif
-
-namespace JSC {
-
-static bool isVFPPresent()
-{
-#if OS(LINUX)
- int fd = open("/proc/self/auxv", O_RDONLY);
- if (fd > 0) {
- Elf32_auxv_t aux;
- while (read(fd, &aux, sizeof(Elf32_auxv_t))) {
- if (aux.a_type == AT_HWCAP) {
- close(fd);
- return aux.a_un.a_val & HWCAP_VFP;
- }
- }
- close(fd);
- }
-#endif
-
-#if (COMPILER(RVCT) && defined(__TARGET_FPU_VFP)) || (COMPILER(GCC) && defined(__VFP_FP__))
- return true;
-#else
- return false;
-#endif
-}
-
-const bool MacroAssemblerARM::s_isVFPPresent = isVFPPresent();
-
-#if CPU(ARMV5_OR_LOWER)
-/* On ARMv5 and below, natural alignment is required. */
-void MacroAssemblerARM::load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest)
-{
- ARMWord op2;
-
- ASSERT(address.scale >= 0 && address.scale <= 3);
- op2 = m_assembler.lsl(address.index, static_cast<int>(address.scale));
-
- if (address.offset >= 0 && address.offset + 0x2 <= 0xff) {
- m_assembler.add(ARMRegisters::S0, address.base, op2);
- m_assembler.halfDtrUp(ARMAssembler::LoadUint16, dest, ARMRegisters::S0, ARMAssembler::getOp2Half(address.offset));
- m_assembler.halfDtrUp(ARMAssembler::LoadUint16, ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Half(address.offset + 0x2));
- } else if (address.offset < 0 && address.offset >= -0xff) {
- m_assembler.add(ARMRegisters::S0, address.base, op2);
- m_assembler.halfDtrDown(ARMAssembler::LoadUint16, dest, ARMRegisters::S0, ARMAssembler::getOp2Half(-address.offset));
- m_assembler.halfDtrDown(ARMAssembler::LoadUint16, ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Half(-address.offset - 0x2));
- } else {
- m_assembler.moveImm(address.offset, ARMRegisters::S0);
- m_assembler.add(ARMRegisters::S0, ARMRegisters::S0, op2);
- m_assembler.halfDtrUpRegister(ARMAssembler::LoadUint16, dest, address.base, ARMRegisters::S0);
- m_assembler.add(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::Op2Immediate | 0x2);
- m_assembler.halfDtrUpRegister(ARMAssembler::LoadUint16, ARMRegisters::S0, address.base, ARMRegisters::S0);
- }
- m_assembler.orr(dest, dest, m_assembler.lsl(ARMRegisters::S0, 16));
-}
-#endif
-
-}
-
-#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL)
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARM.h b/src/3rdparty/masm/assembler/MacroAssemblerARM.h
deleted file mode 100644
index 268fe5fe73..0000000000
--- a/src/3rdparty/masm/assembler/MacroAssemblerARM.h
+++ /dev/null
@@ -1,1386 +0,0 @@
-/*
- * Copyright (C) 2008 Apple Inc.
- * Copyright (C) 2009, 2010 University of Szeged
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
- */
-
-#ifndef MacroAssemblerARM_h
-#define MacroAssemblerARM_h
-
-#if ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL)
-
-#include "ARMAssembler.h"
-#include "AbstractMacroAssembler.h"
-
-namespace JSC {
-
-class MacroAssemblerARM : public AbstractMacroAssembler<ARMAssembler> {
- static const int DoubleConditionMask = 0x0f;
- static const int DoubleConditionBitSpecial = 0x10;
- COMPILE_ASSERT(!(DoubleConditionBitSpecial & DoubleConditionMask), DoubleConditionBitSpecial_should_not_interfere_with_ARMAssembler_Condition_codes);
-public:
- typedef ARMRegisters::FPRegisterID FPRegisterID;
-
- enum RelationalCondition {
- Equal = ARMAssembler::EQ,
- NotEqual = ARMAssembler::NE,
- Above = ARMAssembler::HI,
- AboveOrEqual = ARMAssembler::CS,
- Below = ARMAssembler::CC,
- BelowOrEqual = ARMAssembler::LS,
- GreaterThan = ARMAssembler::GT,
- GreaterThanOrEqual = ARMAssembler::GE,
- LessThan = ARMAssembler::LT,
- LessThanOrEqual = ARMAssembler::LE
- };
-
- enum ResultCondition {
- Overflow = ARMAssembler::VS,
- Signed = ARMAssembler::MI,
- Zero = ARMAssembler::EQ,
- NonZero = ARMAssembler::NE
- };
-
- enum DoubleCondition {
- // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN.
- DoubleEqual = ARMAssembler::EQ,
- DoubleNotEqual = ARMAssembler::NE | DoubleConditionBitSpecial,
- DoubleGreaterThan = ARMAssembler::GT,
- DoubleGreaterThanOrEqual = ARMAssembler::GE,
- DoubleLessThan = ARMAssembler::CC,
- DoubleLessThanOrEqual = ARMAssembler::LS,
- // If either operand is NaN, these conditions always evaluate to true.
- DoubleEqualOrUnordered = ARMAssembler::EQ | DoubleConditionBitSpecial,
- DoubleNotEqualOrUnordered = ARMAssembler::NE,
- DoubleGreaterThanOrUnordered = ARMAssembler::HI,
- DoubleGreaterThanOrEqualOrUnordered = ARMAssembler::CS,
- DoubleLessThanOrUnordered = ARMAssembler::LT,
- DoubleLessThanOrEqualOrUnordered = ARMAssembler::LE,
- };
-
- static const RegisterID stackPointerRegister = ARMRegisters::sp;
- static const RegisterID linkRegister = ARMRegisters::lr;
-
- static const Scale ScalePtr = TimesFour;
-
- void add32(RegisterID src, RegisterID dest)
- {
- m_assembler.adds(dest, dest, src);
- }
-
- void add32(RegisterID op1, RegisterID op2, RegisterID dest)
- {
- m_assembler.adds(dest, op1, op2);
- }
-
- void add32(TrustedImm32 imm, Address address)
- {
- load32(address, ARMRegisters::S1);
- add32(imm, ARMRegisters::S1);
- store32(ARMRegisters::S1, address);
- }
-
- void add32(TrustedImm32 imm, RegisterID dest)
- {
- m_assembler.adds(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0));
- }
-
- void add32(AbsoluteAddress src, RegisterID dest)
- {
- move(TrustedImmPtr(src.m_ptr), ARMRegisters::S1);
- m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S1, ARMRegisters::S1, 0);
- add32(ARMRegisters::S1, dest);
- }
-
- void add32(Address src, RegisterID dest)
- {
- load32(src, ARMRegisters::S1);
- add32(ARMRegisters::S1, dest);
- }
-
- void add32(RegisterID src, TrustedImm32 imm, RegisterID dest)
- {
- m_assembler.adds(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0));
- }
-
- void and32(RegisterID src, RegisterID dest)
- {
- m_assembler.bitAnds(dest, dest, src);
- }
-
- void and32(RegisterID op1, RegisterID op2, RegisterID dest)
- {
- m_assembler.bitAnds(dest, op1, op2);
- }
-
- void and32(TrustedImm32 imm, RegisterID dest)
- {
- ARMWord w = m_assembler.getImm(imm.m_value, ARMRegisters::S0, true);
- if (w & ARMAssembler::Op2InvertedImmediate)
- m_assembler.bics(dest, dest, w & ~ARMAssembler::Op2InvertedImmediate);
- else
- m_assembler.bitAnds(dest, dest, w);
- }
-
- void and32(TrustedImm32 imm, RegisterID src, RegisterID dest)
- {
- ARMWord w = m_assembler.getImm(imm.m_value, ARMRegisters::S0, true);
- if (w & ARMAssembler::Op2InvertedImmediate)
- m_assembler.bics(dest, src, w & ~ARMAssembler::Op2InvertedImmediate);
- else
- m_assembler.bitAnds(dest, src, w);
- }
-
- void and32(Address src, RegisterID dest)
- {
- load32(src, ARMRegisters::S1);
- and32(ARMRegisters::S1, dest);
- }
-
- void lshift32(RegisterID shiftAmount, RegisterID dest)
- {
- lshift32(dest, shiftAmount, dest);
- }
-
- void lshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest)
- {
- ARMWord w = ARMAssembler::getOp2Byte(0x1f);
- m_assembler.bitAnd(ARMRegisters::S0, shiftAmount, w);
-
- m_assembler.movs(dest, m_assembler.lslRegister(src, ARMRegisters::S0));
- }
-
- void lshift32(TrustedImm32 imm, RegisterID dest)
- {
- m_assembler.movs(dest, m_assembler.lsl(dest, imm.m_value & 0x1f));
- }
-
- void lshift32(RegisterID src, TrustedImm32 imm, RegisterID dest)
- {
- m_assembler.movs(dest, m_assembler.lsl(src, imm.m_value & 0x1f));
- }
-
- void mul32(RegisterID op1, RegisterID op2, RegisterID dest)
- {
- if (op2 == dest) {
- if (op1 == dest) {
- move(op2, ARMRegisters::S0);
- op2 = ARMRegisters::S0;
- } else {
- // Swap the operands.
- RegisterID tmp = op1;
- op1 = op2;
- op2 = tmp;
- }
- }
- m_assembler.muls(dest, op1, op2);
- }
-
- void mul32(RegisterID src, RegisterID dest)
- {
- mul32(src, dest, dest);
- }
-
- void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest)
- {
- move(imm, ARMRegisters::S0);
- m_assembler.muls(dest, src, ARMRegisters::S0);
- }
-
- void neg32(RegisterID srcDest)
- {
- m_assembler.rsbs(srcDest, srcDest, ARMAssembler::getOp2Byte(0));
- }
-
- void or32(RegisterID src, RegisterID dest)
- {
- m_assembler.orrs(dest, dest, src);
- }
-
- void or32(RegisterID src, AbsoluteAddress dest)
- {
- move(TrustedImmPtr(dest.m_ptr), ARMRegisters::S0);
- load32(Address(ARMRegisters::S0), ARMRegisters::S1);
- or32(src, ARMRegisters::S1);
- store32(ARMRegisters::S1, ARMRegisters::S0);
- }
-
- void or32(TrustedImm32 imm, RegisterID dest)
- {
- m_assembler.orrs(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0));
- }
-
- void or32(TrustedImm32 imm, RegisterID src, RegisterID dest)
- {
- m_assembler.orrs(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0));
- }
-
- void or32(RegisterID op1, RegisterID op2, RegisterID dest)
- {
- m_assembler.orrs(dest, op1, op2);
- }
-
- void rshift32(RegisterID shiftAmount, RegisterID dest)
- {
- rshift32(dest, shiftAmount, dest);
- }
-
- void rshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest)
- {
- ARMWord w = ARMAssembler::getOp2Byte(0x1f);
- m_assembler.bitAnd(ARMRegisters::S0, shiftAmount, w);
-
- m_assembler.movs(dest, m_assembler.asrRegister(src, ARMRegisters::S0));
- }
-
- void rshift32(TrustedImm32 imm, RegisterID dest)
- {
- rshift32(dest, imm, dest);
- }
-
- void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest)
- {
- m_assembler.movs(dest, m_assembler.asr(src, imm.m_value & 0x1f));
- }
-
- void urshift32(RegisterID shiftAmount, RegisterID dest)
- {
- urshift32(dest, shiftAmount, dest);
- }
-
- void urshift32(RegisterID src, RegisterID shiftAmount, RegisterID dest)
- {
- ARMWord w = ARMAssembler::getOp2Byte(0x1f);
- m_assembler.bitAnd(ARMRegisters::S0, shiftAmount, w);
-
- m_assembler.movs(dest, m_assembler.lsrRegister(src, ARMRegisters::S0));
- }
-
- void urshift32(TrustedImm32 imm, RegisterID dest)
- {
- m_assembler.movs(dest, m_assembler.lsr(dest, imm.m_value & 0x1f));
- }
-
- void urshift32(RegisterID src, TrustedImm32 imm, RegisterID dest)
- {
- m_assembler.movs(dest, m_assembler.lsr(src, imm.m_value & 0x1f));
- }
-
- void sub32(RegisterID src, RegisterID dest)
- {
- m_assembler.subs(dest, dest, src);
- }
-
- void sub32(TrustedImm32 imm, RegisterID dest)
- {
- m_assembler.subs(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0));
- }
-
- void sub32(TrustedImm32 imm, Address address)
- {
- load32(address, ARMRegisters::S1);
- sub32(imm, ARMRegisters::S1);
- store32(ARMRegisters::S1, address);
- }
-
- void sub32(Address src, RegisterID dest)
- {
- load32(src, ARMRegisters::S1);
- sub32(ARMRegisters::S1, dest);
- }
-
- void sub32(RegisterID src, TrustedImm32 imm, RegisterID dest)
- {
- m_assembler.subs(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0));
- }
-
- void xor32(RegisterID src, RegisterID dest)
- {
- m_assembler.eors(dest, dest, src);
- }
-
- void xor32(RegisterID op1, RegisterID op2, RegisterID dest)
- {
- m_assembler.eors(dest, op1, op2);
- }
-
- void xor32(TrustedImm32 imm, RegisterID dest)
- {
- if (imm.m_value == -1)
- m_assembler.mvns(dest, dest);
- else
- m_assembler.eors(dest, dest, m_assembler.getImm(imm.m_value, ARMRegisters::S0));
- }
-
- void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest)
- {
- if (imm.m_value == -1)
- m_assembler.mvns(dest, src);
- else
- m_assembler.eors(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0));
- }
-
- void countLeadingZeros32(RegisterID src, RegisterID dest)
- {
-#if WTF_ARM_ARCH_AT_LEAST(5)
- m_assembler.clz(dest, src);
-#else
- UNUSED_PARAM(src);
- UNUSED_PARAM(dest);
- RELEASE_ASSERT_NOT_REACHED();
-#endif
- }
-
- void load8(ImplicitAddress address, RegisterID dest)
- {
- m_assembler.dataTransfer32(ARMAssembler::LoadUint8, dest, address.base, address.offset);
- }
-
- void load8(BaseIndex address, RegisterID dest)
- {
- m_assembler.baseIndexTransfer32(ARMAssembler::LoadUint8, dest, address.base, address.index, static_cast<int>(address.scale), address.offset);
- }
-
- void load8Signed(BaseIndex address, RegisterID dest)
- {
- m_assembler.baseIndexTransfer16(ARMAssembler::LoadInt8, dest, address.base, address.index, static_cast<int>(address.scale), address.offset);
- }
-
- void load16(ImplicitAddress address, RegisterID dest)
- {
- m_assembler.dataTransfer16(ARMAssembler::LoadUint16, dest, address.base, address.offset);
- }
-
- void load16(BaseIndex address, RegisterID dest)
- {
- m_assembler.baseIndexTransfer16(ARMAssembler::LoadUint16, dest, address.base, address.index, static_cast<int>(address.scale), address.offset);
- }
-
- void load16Signed(BaseIndex address, RegisterID dest)
- {
- m_assembler.baseIndexTransfer16(ARMAssembler::LoadInt16, dest, address.base, address.index, static_cast<int>(address.scale), address.offset);
- }
-
- void load32(ImplicitAddress address, RegisterID dest)
- {
- m_assembler.dataTransfer32(ARMAssembler::LoadUint32, dest, address.base, address.offset);
- }
-
- void load32(BaseIndex address, RegisterID dest)
- {
- m_assembler.baseIndexTransfer32(ARMAssembler::LoadUint32, dest, address.base, address.index, static_cast<int>(address.scale), address.offset);
- }
-
-#if CPU(ARMV5_OR_LOWER)
- void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest);
-#else
- void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest)
- {
- load32(address, dest);
- }
-#endif
-
- void load16Unaligned(BaseIndex address, RegisterID dest)
- {
- load16(address, dest);
- }
-
- ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest)
- {
- ConvertibleLoadLabel result(this);
- ASSERT(address.offset >= 0 && address.offset <= 255);
- m_assembler.dtrUp(ARMAssembler::LoadUint32, dest, address.base, address.offset);
- return result;
- }
-
- DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest)
- {
- DataLabel32 dataLabel(this);
- m_assembler.ldrUniqueImmediate(ARMRegisters::S0, 0);
- m_assembler.dtrUpRegister(ARMAssembler::LoadUint32, dest, address.base, ARMRegisters::S0);
- return dataLabel;
- }
-
- static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value)
- {
- return value >= -4095 && value <= 4095;
- }
-
- DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest)
- {
- DataLabelCompact dataLabel(this);
- ASSERT(isCompactPtrAlignedAddressOffset(address.offset));
- if (address.offset >= 0)
- m_assembler.dtrUp(ARMAssembler::LoadUint32, dest, address.base, address.offset);
- else
- m_assembler.dtrDown(ARMAssembler::LoadUint32, dest, address.base, address.offset);
- return dataLabel;
- }
-
- DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address)
- {
- DataLabel32 dataLabel(this);
- m_assembler.ldrUniqueImmediate(ARMRegisters::S0, 0);
- m_assembler.dtrUpRegister(ARMAssembler::StoreUint32, src, address.base, ARMRegisters::S0);
- return dataLabel;
- }
-
- void store8(RegisterID src, BaseIndex address)
- {
- m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint8, src, address.base, address.index, static_cast<int>(address.scale), address.offset);
- }
-
- void store8(TrustedImm32 imm, const void* address)
- {
- move(TrustedImm32(reinterpret_cast<ARMWord>(address)), ARMRegisters::S0);
- move(imm, ARMRegisters::S1);
- m_assembler.dtrUp(ARMAssembler::StoreUint8, ARMRegisters::S1, ARMRegisters::S0, 0);
- }
-
- void store16(RegisterID src, BaseIndex address)
- {
- m_assembler.baseIndexTransfer16(ARMAssembler::StoreUint16, src, address.base, address.index, static_cast<int>(address.scale), address.offset);
- }
-
- void store32(RegisterID src, ImplicitAddress address)
- {
- m_assembler.dataTransfer32(ARMAssembler::StoreUint32, src, address.base, address.offset);
- }
-
- void store32(RegisterID src, BaseIndex address)
- {
- m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint32, src, address.base, address.index, static_cast<int>(address.scale), address.offset);
- }
-
- void store32(TrustedImm32 imm, ImplicitAddress address)
- {
- move(imm, ARMRegisters::S1);
- store32(ARMRegisters::S1, address);
- }
-
- void store32(TrustedImm32 imm, BaseIndex address)
- {
- move(imm, ARMRegisters::S1);
- m_assembler.baseIndexTransfer32(ARMAssembler::StoreUint32, ARMRegisters::S1, address.base, address.index, static_cast<int>(address.scale), address.offset);
- }
-
- void store32(RegisterID src, const void* address)
- {
- m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast<ARMWord>(address));
- m_assembler.dtrUp(ARMAssembler::StoreUint32, src, ARMRegisters::S0, 0);
- }
-
- void store32(TrustedImm32 imm, const void* address)
- {
- m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast<ARMWord>(address));
- m_assembler.moveImm(imm.m_value, ARMRegisters::S1);
- m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S1, ARMRegisters::S0, 0);
- }
-
- void pop(RegisterID dest)
- {
- m_assembler.pop(dest);
- }
-
- void push(RegisterID src)
- {
- m_assembler.push(src);
- }
-
- void push(Address address)
- {
- load32(address, ARMRegisters::S1);
- push(ARMRegisters::S1);
- }
-
- void push(TrustedImm32 imm)
- {
- move(imm, ARMRegisters::S0);
- push(ARMRegisters::S0);
- }
-
- void move(TrustedImm32 imm, RegisterID dest)
- {
- m_assembler.moveImm(imm.m_value, dest);
- }
-
- void move(RegisterID src, RegisterID dest)
- {
- if (src != dest)
- m_assembler.mov(dest, src);
- }
-
- void move(TrustedImmPtr imm, RegisterID dest)
- {
- move(TrustedImm32(imm), dest);
- }
-
- void swap(RegisterID reg1, RegisterID reg2)
- {
- move(reg1, ARMRegisters::S0);
- move(reg2, reg1);
- move(ARMRegisters::S0, reg2);
- }
-
- void signExtend32ToPtr(RegisterID src, RegisterID dest)
- {
- if (src != dest)
- move(src, dest);
- }
-
- void zeroExtend32ToPtr(RegisterID src, RegisterID dest)
- {
- if (src != dest)
- move(src, dest);
- }
-
- Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right)
- {
- load8(left, ARMRegisters::S1);
- return branch32(cond, ARMRegisters::S1, right);
- }
-
- Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right)
- {
- ASSERT(!(right.m_value & 0xFFFFFF00));
- load8(left, ARMRegisters::S1);
- return branch32(cond, ARMRegisters::S1, right);
- }
-
- Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right, int useConstantPool = 0)
- {
- m_assembler.cmp(left, right);
- return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool));
- }
-
- Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right, int useConstantPool = 0)
- {
- internalCompare32(left, right);
- return Jump(m_assembler.jmp(ARMCondition(cond), useConstantPool));
- }
-
- Jump branch32(RelationalCondition cond, RegisterID left, Address right)
- {
- load32(right, ARMRegisters::S1);
- return branch32(cond, left, ARMRegisters::S1);
- }
-
- Jump branch32(RelationalCondition cond, Address left, RegisterID right)
- {
- load32(left, ARMRegisters::S1);
- return branch32(cond, ARMRegisters::S1, right);
- }
-
- Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right)
- {
- load32(left, ARMRegisters::S1);
- return branch32(cond, ARMRegisters::S1, right);
- }
-
- Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right)
- {
- load32(left, ARMRegisters::S1);
- return branch32(cond, ARMRegisters::S1, right);
- }
-
- Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right)
- {
- load32WithUnalignedHalfWords(left, ARMRegisters::S1);
- return branch32(cond, ARMRegisters::S1, right);
- }
-
- Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1))
- {
- load8(address, ARMRegisters::S1);
- return branchTest32(cond, ARMRegisters::S1, mask);
- }
-
- Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1))
- {
- move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1);
- load8(Address(ARMRegisters::S1), ARMRegisters::S1);
- return branchTest32(cond, ARMRegisters::S1, mask);
- }
-
- Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask)
- {
- ASSERT((cond == Zero) || (cond == NonZero));
- m_assembler.tst(reg, mask);
- return Jump(m_assembler.jmp(ARMCondition(cond)));
- }
-
- Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1))
- {
- ASSERT((cond == Zero) || (cond == NonZero));
- ARMWord w = m_assembler.getImm(mask.m_value, ARMRegisters::S0, true);
- if (w & ARMAssembler::Op2InvertedImmediate)
- m_assembler.bics(ARMRegisters::S0, reg, w & ~ARMAssembler::Op2InvertedImmediate);
- else
- m_assembler.tst(reg, w);
- return Jump(m_assembler.jmp(ARMCondition(cond)));
- }
-
- Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1))
- {
- load32(address, ARMRegisters::S1);
- return branchTest32(cond, ARMRegisters::S1, mask);
- }
-
- Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1))
- {
- load32(address, ARMRegisters::S1);
- return branchTest32(cond, ARMRegisters::S1, mask);
- }
-
- Jump jump()
- {
- return Jump(m_assembler.jmp());
- }
-
- void jump(RegisterID target)
- {
- m_assembler.bx(target);
- }
-
- void jump(Address address)
- {
- load32(address, ARMRegisters::pc);
- }
-
- void jump(AbsoluteAddress address)
- {
- move(TrustedImmPtr(address.m_ptr), ARMRegisters::S0);
- load32(Address(ARMRegisters::S0, 0), ARMRegisters::pc);
- }
-
- void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2)
- {
- m_assembler.vmov(dest1, dest2, src);
- }
-
- void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID)
- {
- m_assembler.vmov(dest, src1, src2);
- }
-
- Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
- add32(src, dest);
- return Jump(m_assembler.jmp(ARMCondition(cond)));
- }
-
- Jump branchAdd32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
- add32(op1, op2, dest);
- return Jump(m_assembler.jmp(ARMCondition(cond)));
- }
-
- Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
- add32(imm, dest);
- return Jump(m_assembler.jmp(ARMCondition(cond)));
- }
-
- Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
- add32(src, imm, dest);
- return Jump(m_assembler.jmp(ARMCondition(cond)));
- }
-
- Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
- add32(imm, dest);
- return Jump(m_assembler.jmp(ARMCondition(cond)));
- }
-
- void mull32(RegisterID op1, RegisterID op2, RegisterID dest)
- {
- if (op2 == dest) {
- if (op1 == dest) {
- move(op2, ARMRegisters::S0);
- op2 = ARMRegisters::S0;
- } else {
- // Swap the operands.
- RegisterID tmp = op1;
- op1 = op2;
- op2 = tmp;
- }
- }
- m_assembler.mull(ARMRegisters::S1, dest, op1, op2);
- m_assembler.cmp(ARMRegisters::S1, m_assembler.asr(dest, 31));
- }
-
- Jump branchMul32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
- if (cond == Overflow) {
- mull32(src1, src2, dest);
- cond = NonZero;
- }
- else
- mul32(src1, src2, dest);
- return Jump(m_assembler.jmp(ARMCondition(cond)));
- }
-
- Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest)
- {
- return branchMul32(cond, src, dest, dest);
- }
-
- Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
- if (cond == Overflow) {
- move(imm, ARMRegisters::S0);
- mull32(ARMRegisters::S0, src, dest);
- cond = NonZero;
- }
- else
- mul32(imm, src, dest);
- return Jump(m_assembler.jmp(ARMCondition(cond)));
- }
-
- Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
- sub32(src, dest);
- return Jump(m_assembler.jmp(ARMCondition(cond)));
- }
-
- Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
- sub32(imm, dest);
- return Jump(m_assembler.jmp(ARMCondition(cond)));
- }
-
- Jump branchSub32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
- sub32(src, imm, dest);
- return Jump(m_assembler.jmp(ARMCondition(cond)));
- }
-
- Jump branchSub32(ResultCondition cond, RegisterID op1, RegisterID op2, RegisterID dest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
- m_assembler.subs(dest, op1, op2);
- return Jump(m_assembler.jmp(ARMCondition(cond)));
- }
-
- Jump branchNeg32(ResultCondition cond, RegisterID srcDest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
- neg32(srcDest);
- return Jump(m_assembler.jmp(ARMCondition(cond)));
- }
-
- Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest)
- {
- ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero));
- or32(src, dest);
- return Jump(m_assembler.jmp(ARMCondition(cond)));
- }
-
- PatchableJump patchableBranch32(RelationalCondition cond, RegisterID reg, TrustedImm32 imm)
- {
- internalCompare32(reg, imm);
- Jump jump(m_assembler.loadBranchTarget(ARMRegisters::S1, ARMCondition(cond), true));
- m_assembler.bx(ARMRegisters::S1, ARMCondition(cond));
- return PatchableJump(jump);
- }
-
- void breakpoint()
- {
- m_assembler.bkpt(0);
- }
-
- Call nearCall()
- {
- m_assembler.loadBranchTarget(ARMRegisters::S1, ARMAssembler::AL, true);
- return Call(m_assembler.blx(ARMRegisters::S1), Call::LinkableNear);
- }
-
- Call call(RegisterID target)
- {
- return Call(m_assembler.blx(target), Call::None);
- }
-
- void call(Address address)
- {
- call32(address.base, address.offset);
- }
-
- void ret()
- {
- m_assembler.bx(linkRegister);
- }
-
- void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest)
- {
- m_assembler.cmp(left, right);
- m_assembler.mov(dest, ARMAssembler::getOp2Byte(0));
- m_assembler.mov(dest, ARMAssembler::getOp2Byte(1), ARMCondition(cond));
- }
-
- void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest)
- {
- m_assembler.cmp(left, m_assembler.getImm(right.m_value, ARMRegisters::S0));
- m_assembler.mov(dest, ARMAssembler::getOp2Byte(0));
- m_assembler.mov(dest, ARMAssembler::getOp2Byte(1), ARMCondition(cond));
- }
-
- void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest)
- {
- load8(left, ARMRegisters::S1);
- compare32(cond, ARMRegisters::S1, right, dest);
- }
-
- void test32(ResultCondition cond, RegisterID reg, TrustedImm32 mask, RegisterID dest)
- {
- if (mask.m_value == -1)
- m_assembler.cmp(0, reg);
- else
- m_assembler.tst(reg, m_assembler.getImm(mask.m_value, ARMRegisters::S0));
- m_assembler.mov(dest, ARMAssembler::getOp2Byte(0));
- m_assembler.mov(dest, ARMAssembler::getOp2Byte(1), ARMCondition(cond));
- }
-
- void test32(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest)
- {
- load32(address, ARMRegisters::S1);
- test32(cond, ARMRegisters::S1, mask, dest);
- }
-
- void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest)
- {
- load8(address, ARMRegisters::S1);
- test32(cond, ARMRegisters::S1, mask, dest);
- }
-
- void add32(TrustedImm32 imm, RegisterID src, RegisterID dest)
- {
- m_assembler.add(dest, src, m_assembler.getImm(imm.m_value, ARMRegisters::S0));
- }
-
- void add32(TrustedImm32 imm, AbsoluteAddress address)
- {
- load32(address.m_ptr, ARMRegisters::S1);
- add32(imm, ARMRegisters::S1);
- store32(ARMRegisters::S1, address.m_ptr);
- }
-
- void add64(TrustedImm32 imm, AbsoluteAddress address)
- {
- ARMWord tmp;
-
- move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1);
- m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S0, ARMRegisters::S1, 0);
-
- if ((tmp = ARMAssembler::getOp2(imm.m_value)) != ARMAssembler::InvalidImmediate)
- m_assembler.adds(ARMRegisters::S0, ARMRegisters::S0, tmp);
- else if ((tmp = ARMAssembler::getOp2(-imm.m_value)) != ARMAssembler::InvalidImmediate)
- m_assembler.subs(ARMRegisters::S0, ARMRegisters::S0, tmp);
- else {
- m_assembler.adds(ARMRegisters::S0, ARMRegisters::S0, m_assembler.getImm(imm.m_value, ARMRegisters::S1));
- move(TrustedImmPtr(address.m_ptr), ARMRegisters::S1);
- }
- m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S0, ARMRegisters::S1, 0);
-
- m_assembler.dtrUp(ARMAssembler::LoadUint32, ARMRegisters::S0, ARMRegisters::S1, sizeof(ARMWord));
- if (imm.m_value >= 0)
- m_assembler.adc(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(0));
- else
- m_assembler.sbc(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(0));
- m_assembler.dtrUp(ARMAssembler::StoreUint32, ARMRegisters::S0, ARMRegisters::S1, sizeof(ARMWord));
- }
-
- void sub32(TrustedImm32 imm, AbsoluteAddress address)
- {
- load32(address.m_ptr, ARMRegisters::S1);
- sub32(imm, ARMRegisters::S1);
- store32(ARMRegisters::S1, address.m_ptr);
- }
-
- void load32(const void* address, RegisterID dest)
- {
- m_assembler.ldrUniqueImmediate(ARMRegisters::S0, reinterpret_cast<ARMWord>(address));
- m_assembler.dtrUp(ARMAssembler::LoadUint32, dest, ARMRegisters::S0, 0);
- }
-
- Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right)
- {
- load32(left.m_ptr, ARMRegisters::S1);
- return branch32(cond, ARMRegisters::S1, right);
- }
-
- Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right)
- {
- load32(left.m_ptr, ARMRegisters::S1);
- return branch32(cond, ARMRegisters::S1, right);
- }
-
- void relativeTableJump(RegisterID index, int scale)
- {
- ASSERT(scale >= 0 && scale <= 31);
- m_assembler.add(ARMRegisters::pc, ARMRegisters::pc, m_assembler.lsl(index, scale));
-
- // NOP the default prefetching
- m_assembler.mov(ARMRegisters::r0, ARMRegisters::r0);
- }
-
- Call call()
- {
- ensureSpace(2 * sizeof(ARMWord), sizeof(ARMWord));
- m_assembler.loadBranchTarget(ARMRegisters::S1, ARMAssembler::AL, true);
- return Call(m_assembler.blx(ARMRegisters::S1), Call::Linkable);
- }
-
- Call tailRecursiveCall()
- {
- return Call::fromTailJump(jump());
- }
-
- Call makeTailRecursiveCall(Jump oldJump)
- {
- return Call::fromTailJump(oldJump);
- }
-
- DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest)
- {
- DataLabelPtr dataLabel(this);
- m_assembler.ldrUniqueImmediate(dest, reinterpret_cast<ARMWord>(initialValue.m_value));
- return dataLabel;
- }
-
- Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0))
- {
- ensureSpace(3 * sizeof(ARMWord), 2 * sizeof(ARMWord));
- dataLabel = moveWithPatch(initialRightValue, ARMRegisters::S1);
- Jump jump = branch32(cond, left, ARMRegisters::S1, true);
- return jump;
- }
-
- Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0))
- {
- load32(left, ARMRegisters::S1);
- ensureSpace(3 * sizeof(ARMWord), 2 * sizeof(ARMWord));
- dataLabel = moveWithPatch(initialRightValue, ARMRegisters::S0);
- Jump jump = branch32(cond, ARMRegisters::S0, ARMRegisters::S1, true);
- return jump;
- }
-
- DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address)
- {
- DataLabelPtr dataLabel = moveWithPatch(initialValue, ARMRegisters::S1);
- store32(ARMRegisters::S1, address);
- return dataLabel;
- }
-
- DataLabelPtr storePtrWithPatch(ImplicitAddress address)
- {
- return storePtrWithPatch(TrustedImmPtr(0), address);
- }
-
- // Floating point operators
- static bool supportsFloatingPoint()
- {
- return s_isVFPPresent;
- }
-
- static bool supportsFloatingPointTruncate()
- {
- return false;
- }
-
- static bool supportsFloatingPointSqrt()
- {
- return s_isVFPPresent;
- }
- static bool supportsFloatingPointAbs() { return false; }
-
- void loadFloat(BaseIndex address, FPRegisterID dest)
- {
- m_assembler.baseIndexTransferFloat(ARMAssembler::LoadFloat, dest, address.base, address.index, static_cast<int>(address.scale), address.offset);
- }
-
- void loadDouble(ImplicitAddress address, FPRegisterID dest)
- {
- m_assembler.dataTransferFloat(ARMAssembler::LoadDouble, dest, address.base, address.offset);
- }
-
- void loadDouble(BaseIndex address, FPRegisterID dest)
- {
- m_assembler.baseIndexTransferFloat(ARMAssembler::LoadDouble, dest, address.base, address.index, static_cast<int>(address.scale), address.offset);
- }
-
- void loadDouble(const void* address, FPRegisterID dest)
- {
- move(TrustedImm32(reinterpret_cast<ARMWord>(address)), ARMRegisters::S0);
- m_assembler.doubleDtrUp(ARMAssembler::LoadDouble, dest, ARMRegisters::S0, 0);
- }
-
- void storeFloat(FPRegisterID src, BaseIndex address)
- {
- m_assembler.baseIndexTransferFloat(ARMAssembler::StoreFloat, src, address.base, address.index, static_cast<int>(address.scale), address.offset);
- }
-
- void storeDouble(FPRegisterID src, ImplicitAddress address)
- {
- m_assembler.dataTransferFloat(ARMAssembler::StoreDouble, src, address.base, address.offset);
- }
-
- void storeDouble(FPRegisterID src, BaseIndex address)
- {
- m_assembler.baseIndexTransferFloat(ARMAssembler::StoreDouble, src, address.base, address.index, static_cast<int>(address.scale), address.offset);
- }
-
- void storeDouble(FPRegisterID src, const void* address)
- {
- move(TrustedImm32(reinterpret_cast<ARMWord>(address)), ARMRegisters::S0);
- m_assembler.dataTransferFloat(ARMAssembler::StoreDouble, src, ARMRegisters::S0, 0);
- }
-
- void moveDouble(FPRegisterID src, FPRegisterID dest)
- {
- if (src != dest)
- m_assembler.vmov_f64(dest, src);
- }
-
- void addDouble(FPRegisterID src, FPRegisterID dest)
- {
- m_assembler.vadd_f64(dest, dest, src);
- }
-
- void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest)
- {
- m_assembler.vadd_f64(dest, op1, op2);
- }
-
- void addDouble(Address src, FPRegisterID dest)
- {
- loadDouble(src, ARMRegisters::SD0);
- addDouble(ARMRegisters::SD0, dest);
- }
-
- void addDouble(AbsoluteAddress address, FPRegisterID dest)
- {
- loadDouble(address.m_ptr, ARMRegisters::SD0);
- addDouble(ARMRegisters::SD0, dest);
- }
-
- void divDouble(FPRegisterID src, FPRegisterID dest)
- {
- m_assembler.vdiv_f64(dest, dest, src);
- }
-
- void divDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest)
- {
- m_assembler.vdiv_f64(dest, op1, op2);
- }
-
- void divDouble(Address src, FPRegisterID dest)
- {
- RELEASE_ASSERT_NOT_REACHED(); // Untested
- loadDouble(src, ARMRegisters::SD0);
- divDouble(ARMRegisters::SD0, dest);
- }
-
- void subDouble(FPRegisterID src, FPRegisterID dest)
- {
- m_assembler.vsub_f64(dest, dest, src);
- }
-
- void subDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest)
- {
- m_assembler.vsub_f64(dest, op1, op2);
- }
-
- void subDouble(Address src, FPRegisterID dest)
- {
- loadDouble(src, ARMRegisters::SD0);
- subDouble(ARMRegisters::SD0, dest);
- }
-
- void mulDouble(FPRegisterID src, FPRegisterID dest)
- {
- m_assembler.vmul_f64(dest, dest, src);
- }
-
- void mulDouble(Address src, FPRegisterID dest)
- {
- loadDouble(src, ARMRegisters::SD0);
- mulDouble(ARMRegisters::SD0, dest);
- }
-
- void mulDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest)
- {
- m_assembler.vmul_f64(dest, op1, op2);
- }
-
- void sqrtDouble(FPRegisterID src, FPRegisterID dest)
- {
- m_assembler.vsqrt_f64(dest, src);
- }
-
- void absDouble(FPRegisterID src, FPRegisterID dest)
- {
- m_assembler.vabs_f64(dest, src);
- }
-
- void negateDouble(FPRegisterID src, FPRegisterID dest)
- {
- m_assembler.vneg_f64(dest, src);
- }
-
- void convertInt32ToDouble(RegisterID src, FPRegisterID dest)
- {
- m_assembler.vmov_vfp32(dest << 1, src);
- m_assembler.vcvt_f64_s32(dest, dest << 1);
- }
-
- void convertInt32ToDouble(Address src, FPRegisterID dest)
- {
- load32(src, ARMRegisters::S1);
- convertInt32ToDouble(ARMRegisters::S1, dest);
- }
-
- void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest)
- {
- move(TrustedImmPtr(src.m_ptr), ARMRegisters::S1);
- load32(Address(ARMRegisters::S1), ARMRegisters::S1);
- convertInt32ToDouble(ARMRegisters::S1, dest);
- }
-
- void convertFloatToDouble(FPRegisterID src, FPRegisterID dst)
- {
- m_assembler.vcvt_f64_f32(dst, src);
- }
-
- void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst)
- {
- m_assembler.vcvt_f32_f64(dst, src);
- }
-
- Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right)
- {
- m_assembler.vcmp_f64(left, right);
- m_assembler.vmrs_apsr();
- if (cond & DoubleConditionBitSpecial)
- m_assembler.cmp(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::VS);
- return Jump(m_assembler.jmp(static_cast<ARMAssembler::Condition>(cond & ~DoubleConditionMask)));
- }
-
- // Truncates 'src' to an integer, and places the resulting 'dest'.
- // If the result is not representable as a 32 bit value, branch.
- // May also branch for some values that are representable in 32 bits
- // (specifically, in this case, INT_MIN).
- enum BranchTruncateType { BranchIfTruncateFailed, BranchIfTruncateSuccessful };
- Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed)
- {
- truncateDoubleToInt32(src, dest);
-
- m_assembler.add(ARMRegisters::S0, dest, ARMAssembler::getOp2Byte(1));
- m_assembler.bic(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(1));
-
- ARMWord w = ARMAssembler::getOp2(0x80000000);
- ASSERT(w != ARMAssembler::InvalidImmediate);
- m_assembler.cmp(ARMRegisters::S0, w);
- return Jump(m_assembler.jmp(branchType == BranchIfTruncateFailed ? ARMAssembler::EQ : ARMAssembler::NE));
- }
-
- Jump branchTruncateDoubleToUint32(FPRegisterID src, RegisterID dest, BranchTruncateType branchType = BranchIfTruncateFailed)
- {
- truncateDoubleToUint32(src, dest);
-
- m_assembler.add(ARMRegisters::S0, dest, ARMAssembler::getOp2Byte(1));
- m_assembler.bic(ARMRegisters::S0, ARMRegisters::S0, ARMAssembler::getOp2Byte(1));
-
- m_assembler.cmp(ARMRegisters::S0, ARMAssembler::getOp2Byte(0));
- return Jump(m_assembler.jmp(branchType == BranchIfTruncateFailed ? ARMAssembler::EQ : ARMAssembler::NE));
- }
-
- // Result is undefined if the value is outside of the integer range.
- void truncateDoubleToInt32(FPRegisterID src, RegisterID dest)
- {
- m_assembler.vcvt_s32_f64(ARMRegisters::SD0 << 1, src);
- m_assembler.vmov_arm32(dest, ARMRegisters::SD0 << 1);
- }
-
- void truncateDoubleToUint32(FPRegisterID src, RegisterID dest)
- {
- m_assembler.vcvt_u32_f64(ARMRegisters::SD0 << 1, src);
- m_assembler.vmov_arm32(dest, ARMRegisters::SD0 << 1);
- }
-
- // Convert 'src' to an integer, and places the resulting 'dest'.
- // If the result is not representable as a 32 bit value, branch.
- // May also branch for some values that are representable in 32 bits
- // (specifically, in this case, 0).
- void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID)
- {
- m_assembler.vcvt_s32_f64(ARMRegisters::SD0 << 1, src);
- m_assembler.vmov_arm32(dest, ARMRegisters::SD0 << 1);
-
- // Convert the integer result back to float & compare to the original value - if not equal or unordered (NaN) then jump.
- m_assembler.vcvt_f64_s32(ARMRegisters::SD0, ARMRegisters::SD0 << 1);
- failureCases.append(branchDouble(DoubleNotEqualOrUnordered, src, ARMRegisters::SD0));
-
- // If the result is zero, it might have been -0.0, and 0.0 equals to -0.0
- failureCases.append(branchTest32(Zero, dest));
- }
-
- Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch)
- {
- m_assembler.mov(ARMRegisters::S0, ARMAssembler::getOp2Byte(0));
- convertInt32ToDouble(ARMRegisters::S0, scratch);
- return branchDouble(DoubleNotEqual, reg, scratch);
- }
-
- Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch)
- {
- m_assembler.mov(ARMRegisters::S0, ARMAssembler::getOp2Byte(0));
- convertInt32ToDouble(ARMRegisters::S0, scratch);
- return branchDouble(DoubleEqualOrUnordered, reg, scratch);
- }
-
- // Invert a relational condition, e.g. == becomes !=, < becomes >=, etc.
- static RelationalCondition invert(RelationalCondition cond)
- {
- ASSERT((static_cast<uint32_t>(cond & 0x0fffffff)) == 0 && static_cast<uint32_t>(cond) < static_cast<uint32_t>(ARMAssembler::AL));
- return static_cast<RelationalCondition>(cond ^ 0x10000000);
- }
-
- void nop()
- {
- m_assembler.nop();
- }
-
- static FunctionPtr readCallTarget(CodeLocationCall call)
- {
- return FunctionPtr(reinterpret_cast<void(*)()>(ARMAssembler::readCallTarget(call.dataLocation())));
- }
-
- static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination)
- {
- ARMAssembler::replaceWithJump(instructionStart.dataLocation(), destination.dataLocation());
- }
-
- static ptrdiff_t maxJumpReplacementSize()
- {
- ARMAssembler::maxJumpReplacementSize();
- return 0;
- }
-
- static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; }
-
- static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr)
- {
- UNREACHABLE_FOR_PLATFORM();
- return CodeLocationLabel();
- }
-
- static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label)
- {
- return label.labelAtOffset(0);
- }
-
- static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID reg, void* initialValue)
- {
- ARMAssembler::revertBranchPtrWithPatch(instructionStart.dataLocation(), reg, reinterpret_cast<uintptr_t>(initialValue) & 0xffff);
- }
-
- static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel, Address, void*)
- {
- UNREACHABLE_FOR_PLATFORM();
- }
-
-protected:
- ARMAssembler::Condition ARMCondition(RelationalCondition cond)
- {
- return static_cast<ARMAssembler::Condition>(cond);
- }
-
- ARMAssembler::Condition ARMCondition(ResultCondition cond)
- {
- return static_cast<ARMAssembler::Condition>(cond);
- }
-
- void ensureSpace(int insnSpace, int constSpace)
- {
- m_assembler.ensureSpace(insnSpace, constSpace);
- }
-
- int sizeOfConstantPool()
- {
- return m_assembler.sizeOfConstantPool();
- }
-
- void call32(RegisterID base, int32_t offset)
- {
- load32(Address(base, offset), ARMRegisters::S1);
- m_assembler.blx(ARMRegisters::S1);
- }
-
-private:
- template <typename, template <typename> class> friend class LinkBufferBase;
- friend class RepatchBuffer;
-
- void internalCompare32(RegisterID left, TrustedImm32 right)
- {
- ARMWord tmp = (static_cast<unsigned>(right.m_value) == 0x80000000) ? ARMAssembler::InvalidImmediate : m_assembler.getOp2(-right.m_value);
- if (tmp != ARMAssembler::InvalidImmediate)
- m_assembler.cmn(left, tmp);
- else
- m_assembler.cmp(left, m_assembler.getImm(right.m_value, ARMRegisters::S0));
- }
-
- static void linkCall(void* code, Call call, FunctionPtr function)
- {
- ARMAssembler::linkCall(code, call.m_label, function.value());
- }
-
- static void repatchCall(CodeLocationCall call, CodeLocationLabel destination)
- {
- ARMAssembler::relinkCall(call.dataLocation(), destination.executableAddress());
- }
-
- static void repatchCall(CodeLocationCall call, FunctionPtr destination)
- {
- ARMAssembler::relinkCall(call.dataLocation(), destination.executableAddress());
- }
-
- static const bool s_isVFPPresent;
-};
-
-}
-
-#endif // ENABLE(ASSEMBLER) && CPU(ARM_TRADITIONAL)
-
-#endif // MacroAssemblerARM_h
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARM64.h b/src/3rdparty/masm/assembler/MacroAssemblerARM64.h
index ba0d7e93f8..e5a704292d 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerARM64.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerARM64.h
@@ -1126,6 +1126,11 @@ public:
m_assembler.ldrh(dest, address.base, memoryTempRegister);
}
+ void load16Unaligned(ImplicitAddress address, RegisterID dest)
+ {
+ load16(address, dest);
+ }
+
void load16Unaligned(BaseIndex address, RegisterID dest)
{
load16(address, dest);
@@ -1283,6 +1288,16 @@ public:
return label;
}
+ void storePair64(RegisterID src1, RegisterID src2, RegisterID dest)
+ {
+ storePair64(src1, src2, dest, TrustedImm32(0));
+ }
+
+ void storePair64(RegisterID src1, RegisterID src2, RegisterID dest, TrustedImm32 offset)
+ {
+ m_assembler.stp<64>(src1, src2, dest, offset.m_value);
+ }
+
void store32(RegisterID src, ImplicitAddress address)
{
if (tryStoreWithOffset<32>(src, address.base, address.offset))
@@ -1420,6 +1435,14 @@ public:
store8(dataTempRegister, address);
}
+ void getEffectiveAddress(BaseIndex address, RegisterID dest)
+ {
+ m_assembler.add<64>(dest, address.base, address.index, ARM64Assembler::LSL, address.scale);
+ if (address.offset)
+ add64(TrustedImm32(address.offset), dest);
+ }
+
+
// Floating-point operations:
static bool supportsFloatingPoint() { return true; }
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h b/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h
index d91122d4a1..99801a0e3b 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h
@@ -255,6 +255,14 @@ public:
store32(dataTempRegister, address.m_ptr);
}
+ void getEffectiveAddress(BaseIndex address, RegisterID dest)
+ {
+ m_assembler.lsl(addressTempRegister, address.index, static_cast<int>(address.scale));
+ m_assembler.add(dest, address.base, addressTempRegister);
+ if (address.offset)
+ add32(TrustedImm32(address.offset), dest);
+ }
+
void add64(TrustedImm32 imm, AbsoluteAddress address)
{
move(TrustedImmPtr(address.m_ptr), addressTempRegister);
@@ -680,6 +688,11 @@ public:
load32(setupArmAddress(address), dest);
}
+ void load16Unaligned(ImplicitAddress address, RegisterID dest)
+ {
+ load16(setupArmAddress(address), dest);
+ }
+
void load16Unaligned(BaseIndex address, RegisterID dest)
{
load16(setupArmAddress(address), dest);
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerCodeRef.h b/src/3rdparty/masm/assembler/MacroAssemblerCodeRef.h
index e3c77d99e6..a7e78ad78f 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerCodeRef.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerCodeRef.h
@@ -27,6 +27,7 @@
#define MacroAssemblerCodeRef_h
#include "Disassembler.h"
+#include <wtf/Platform.h>
#include "ExecutableAllocator.h"
#include "LLIntData.h"
#include <wtf/DataLog.h>
@@ -141,6 +142,8 @@ public:
ASSERT_VALID_CODE_POINTER(m_value);
}
+ inline FunctionPtr(MacroAssemblerCodePtr ptr);
+
// MSVC doesn't seem to treat functions with different calling conventions as
// different types; these methods already defined for fastcall, below.
#if CALLING_CONVENTION_IS_STDCALL && !OS(WINDOWS)
@@ -327,6 +330,12 @@ private:
void* m_value;
};
+
+FunctionPtr::FunctionPtr(MacroAssemblerCodePtr ptr)
+ : m_value(ptr.executableAddress())
+{
+}
+
// MacroAssemblerCodeRef:
//
// A reference to a section of JIT generated code. A CodeRef consists of a
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h b/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h
index f2ad6a4470..07f0ec623f 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h
@@ -27,6 +27,8 @@
#ifndef MacroAssemblerMIPS_h
#define MacroAssemblerMIPS_h
+#include <Platform.h>
+
#if ENABLE(ASSEMBLER) && CPU(MIPS)
#include "AbstractMacroAssembler.h"
@@ -268,6 +270,18 @@ public:
m_assembler.sw(dataTempRegister, addrTempRegister, 4);
}
+ void getEffectiveAddress(BaseIndex address, RegisterID dest)
+ {
+ if (!address.scale && !m_fixedWidth)
+ m_assembler.addu(dest, address.index, address.base);
+ else {
+ m_assembler.sll(addrTempRegister, address.index, address.scale);
+ m_assembler.addu(dest, addrTempRegister, address.base);
+ }
+ if (address.offset)
+ add32(TrustedImm32(address.offset), dest);
+ }
+
void and32(Address src, RegisterID dest)
{
load32(src, dataTempRegister);
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerSH4.h b/src/3rdparty/masm/assembler/MacroAssemblerSH4.h
deleted file mode 100644
index 1e5a3113bb..0000000000
--- a/src/3rdparty/masm/assembler/MacroAssemblerSH4.h
+++ /dev/null
@@ -1,2293 +0,0 @@
-/*
- * Copyright (C) 2009-2011 STMicroelectronics. All rights reserved.
- * Copyright (C) 2008 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
-*/
-
-#ifndef MacroAssemblerSH4_h
-#define MacroAssemblerSH4_h
-
-#if ENABLE(ASSEMBLER) && CPU(SH4)
-
-#include "SH4Assembler.h"
-#include "AbstractMacroAssembler.h"
-#include <wtf/Assertions.h>
-
-namespace JSC {
-
-class MacroAssemblerSH4 : public AbstractMacroAssembler<SH4Assembler> {
-public:
- typedef SH4Assembler::FPRegisterID FPRegisterID;
-
- static const Scale ScalePtr = TimesFour;
- static const FPRegisterID fscratch = SH4Registers::fr10;
- static const RegisterID stackPointerRegister = SH4Registers::sp;
- static const RegisterID linkRegister = SH4Registers::pr;
- static const RegisterID scratchReg3 = SH4Registers::r13;
-
- static const int MaximumCompactPtrAlignedAddressOffset = 60;
-
- static bool isCompactPtrAlignedAddressOffset(ptrdiff_t value)
- {
- return (value >= 0) && (value <= MaximumCompactPtrAlignedAddressOffset);
- }
-
- enum RelationalCondition {
- Equal = SH4Assembler::EQ,
- NotEqual = SH4Assembler::NE,
- Above = SH4Assembler::HI,
- AboveOrEqual = SH4Assembler::HS,
- Below = SH4Assembler::LI,
- BelowOrEqual = SH4Assembler::LS,
- GreaterThan = SH4Assembler::GT,
- GreaterThanOrEqual = SH4Assembler::GE,
- LessThan = SH4Assembler::LT,
- LessThanOrEqual = SH4Assembler::LE
- };
-
- enum ResultCondition {
- Overflow = SH4Assembler::OF,
- Signed = SH4Assembler::SI,
- Zero = SH4Assembler::EQ,
- NonZero = SH4Assembler::NE
- };
-
- enum DoubleCondition {
- // These conditions will only evaluate to true if the comparison is ordered - i.e. neither operand is NaN.
- DoubleEqual = SH4Assembler::EQ,
- DoubleNotEqual = SH4Assembler::NE,
- DoubleGreaterThan = SH4Assembler::GT,
- DoubleGreaterThanOrEqual = SH4Assembler::GE,
- DoubleLessThan = SH4Assembler::LT,
- DoubleLessThanOrEqual = SH4Assembler::LE,
- // If either operand is NaN, these conditions always evaluate to true.
- DoubleEqualOrUnordered = SH4Assembler::EQU,
- DoubleNotEqualOrUnordered = SH4Assembler::NEU,
- DoubleGreaterThanOrUnordered = SH4Assembler::GTU,
- DoubleGreaterThanOrEqualOrUnordered = SH4Assembler::GEU,
- DoubleLessThanOrUnordered = SH4Assembler::LTU,
- DoubleLessThanOrEqualOrUnordered = SH4Assembler::LEU,
- };
-
- RegisterID claimScratch()
- {
- return m_assembler.claimScratch();
- }
-
- void releaseScratch(RegisterID reg)
- {
- m_assembler.releaseScratch(reg);
- }
-
- // Integer arithmetic operations
-
- void add32(RegisterID src, RegisterID dest)
- {
- m_assembler.addlRegReg(src, dest);
- }
-
- void add32(TrustedImm32 imm, RegisterID dest)
- {
- if (m_assembler.isImmediate(imm.m_value)) {
- m_assembler.addlImm8r(imm.m_value, dest);
- return;
- }
-
- RegisterID scr = claimScratch();
- m_assembler.loadConstant(imm.m_value, scr);
- m_assembler.addlRegReg(scr, dest);
- releaseScratch(scr);
- }
-
- void add32(TrustedImm32 imm, RegisterID src, RegisterID dest)
- {
- if (src != dest)
- m_assembler.movlRegReg(src, dest);
- add32(imm, dest);
- }
-
- void add32(TrustedImm32 imm, Address address)
- {
- RegisterID scr = claimScratch();
- load32(address, scr);
- add32(imm, scr);
- store32(scr, address);
- releaseScratch(scr);
- }
-
- void add32(Address src, RegisterID dest)
- {
- RegisterID scr = claimScratch();
- load32(src, scr);
- m_assembler.addlRegReg(scr, dest);
- releaseScratch(scr);
- }
-
- void add32(AbsoluteAddress src, RegisterID dest)
- {
- RegisterID scr = claimScratch();
- load32(src.m_ptr, scr);
- m_assembler.addlRegReg(scr, dest);
- releaseScratch(scr);
- }
-
- void and32(RegisterID src, RegisterID dest)
- {
- m_assembler.andlRegReg(src, dest);
- }
-
- void and32(TrustedImm32 imm, RegisterID dest)
- {
- if ((imm.m_value <= 255) && (imm.m_value >= 0) && (dest == SH4Registers::r0)) {
- m_assembler.andlImm8r(imm.m_value, dest);
- return;
- }
-
- RegisterID scr = claimScratch();
- m_assembler.loadConstant((imm.m_value), scr);
- m_assembler.andlRegReg(scr, dest);
- releaseScratch(scr);
- }
-
- void and32(TrustedImm32 imm, RegisterID src, RegisterID dest)
- {
- if (src != dest) {
- move(imm, dest);
- and32(src, dest);
- return;
- }
-
- and32(imm, dest);
- }
-
- void lshift32(RegisterID shiftamount, RegisterID dest)
- {
- if (shiftamount == SH4Registers::r0)
- m_assembler.andlImm8r(0x1f, shiftamount);
- else {
- RegisterID scr = claimScratch();
- m_assembler.loadConstant(0x1f, scr);
- m_assembler.andlRegReg(scr, shiftamount);
- releaseScratch(scr);
- }
- m_assembler.shllRegReg(dest, shiftamount);
- }
-
- void rshift32(int imm, RegisterID dest)
- {
- RegisterID scr = claimScratch();
- m_assembler.loadConstant(-imm, scr);
- m_assembler.shaRegReg(dest, scr);
- releaseScratch(scr);
- }
-
- void lshift32(TrustedImm32 imm, RegisterID dest)
- {
- if (!imm.m_value)
- return;
-
- if ((imm.m_value == 1) || (imm.m_value == 2) || (imm.m_value == 8) || (imm.m_value == 16)) {
- m_assembler.shllImm8r(imm.m_value, dest);
- return;
- }
-
- RegisterID scr = claimScratch();
- m_assembler.loadConstant((imm.m_value & 0x1f) , scr);
- m_assembler.shllRegReg(dest, scr);
- releaseScratch(scr);
- }
-
- void lshift32(RegisterID src, TrustedImm32 shiftamount, RegisterID dest)
- {
- if (src != dest)
- move(src, dest);
-
- lshift32(shiftamount, dest);
- }
-
- void mul32(RegisterID src, RegisterID dest)
- {
- m_assembler.imullRegReg(src, dest);
- m_assembler.stsmacl(dest);
- }
-
- void mul32(TrustedImm32 imm, RegisterID src, RegisterID dest)
- {
- RegisterID scr = claimScratch();
- move(imm, scr);
- if (src != dest)
- move(src, dest);
- mul32(scr, dest);
- releaseScratch(scr);
- }
-
- void or32(RegisterID src, RegisterID dest)
- {
- m_assembler.orlRegReg(src, dest);
- }
-
- void or32(TrustedImm32 imm, RegisterID dest)
- {
- if ((imm.m_value <= 255) && (imm.m_value >= 0) && (dest == SH4Registers::r0)) {
- m_assembler.orlImm8r(imm.m_value, dest);
- return;
- }
-
- RegisterID scr = claimScratch();
- m_assembler.loadConstant(imm.m_value, scr);
- m_assembler.orlRegReg(scr, dest);
- releaseScratch(scr);
- }
-
- void or32(RegisterID op1, RegisterID op2, RegisterID dest)
- {
- if (op1 == op2)
- move(op1, dest);
- else if (op1 == dest)
- or32(op2, dest);
- else {
- move(op2, dest);
- or32(op1, dest);
- }
- }
-
-
-void or32(TrustedImm32 imm, RegisterID src, RegisterID dest)
- {
- if (src != dest) {
- move(imm, dest);
- or32(src, dest);
- return;
- }
-
- or32(imm, dest);
- }
-
- void xor32(TrustedImm32 imm, RegisterID src, RegisterID dest)
- {
- if (src != dest) {
- move(imm, dest);
- xor32(src, dest);
- return;
- }
-
- xor32(imm, dest);
- }
-
- void rshift32(RegisterID shiftamount, RegisterID dest)
- {
- if (shiftamount == SH4Registers::r0)
- m_assembler.andlImm8r(0x1f, shiftamount);
- else {
- RegisterID scr = claimScratch();
- m_assembler.loadConstant(0x1f, scr);
- m_assembler.andlRegReg(scr, shiftamount);
- releaseScratch(scr);
- }
- m_assembler.neg(shiftamount, shiftamount);
- m_assembler.shaRegReg(dest, shiftamount);
- }
-
- void rshift32(TrustedImm32 imm, RegisterID dest)
- {
- if (imm.m_value & 0x1f)
- rshift32(imm.m_value & 0x1f, dest);
- }
-
- void rshift32(RegisterID src, TrustedImm32 imm, RegisterID dest)
- {
- if (src != dest)
- move(src, dest);
- rshift32(imm, dest);
- }
-
- void sub32(RegisterID src, RegisterID dest)
- {
- m_assembler.sublRegReg(src, dest);
- }
-
- void sub32(TrustedImm32 imm, AbsoluteAddress address, RegisterID scratchReg)
- {
- RegisterID result = claimScratch();
-
- m_assembler.loadConstant(reinterpret_cast<uint32_t>(address.m_ptr), scratchReg);
- m_assembler.movlMemReg(scratchReg, result);
-
- if (m_assembler.isImmediate(-imm.m_value))
- m_assembler.addlImm8r(-imm.m_value, result);
- else {
- m_assembler.loadConstant(imm.m_value, scratchReg3);
- m_assembler.sublRegReg(scratchReg3, result);
- }
-
- store32(result, scratchReg);
- releaseScratch(result);
- }
-
- void sub32(TrustedImm32 imm, AbsoluteAddress address)
- {
- RegisterID result = claimScratch();
- RegisterID scratchReg = claimScratch();
-
- m_assembler.loadConstant(reinterpret_cast<uint32_t>(address.m_ptr), scratchReg);
- m_assembler.movlMemReg(scratchReg, result);
-
- if (m_assembler.isImmediate(-imm.m_value))
- m_assembler.addlImm8r(-imm.m_value, result);
- else {
- m_assembler.loadConstant(imm.m_value, scratchReg3);
- m_assembler.sublRegReg(scratchReg3, result);
- }
-
- store32(result, scratchReg);
- releaseScratch(result);
- releaseScratch(scratchReg);
- }
-
- void add32(TrustedImm32 imm, AbsoluteAddress address, RegisterID scratchReg)
- {
- RegisterID result = claimScratch();
-
- m_assembler.loadConstant(reinterpret_cast<uint32_t>(address.m_ptr), scratchReg);
- m_assembler.movlMemReg(scratchReg, result);
-
- if (m_assembler.isImmediate(imm.m_value))
- m_assembler.addlImm8r(imm.m_value, result);
- else {
- m_assembler.loadConstant(imm.m_value, scratchReg3);
- m_assembler.addlRegReg(scratchReg3, result);
- }
-
- store32(result, scratchReg);
- releaseScratch(result);
- }
-
- void add32(TrustedImm32 imm, AbsoluteAddress address)
- {
- RegisterID result = claimScratch();
- RegisterID scratchReg = claimScratch();
-
- m_assembler.loadConstant(reinterpret_cast<uint32_t>(address.m_ptr), scratchReg);
- m_assembler.movlMemReg(scratchReg, result);
-
- if (m_assembler.isImmediate(imm.m_value))
- m_assembler.addlImm8r(imm.m_value, result);
- else {
- m_assembler.loadConstant(imm.m_value, scratchReg3);
- m_assembler.addlRegReg(scratchReg3, result);
- }
-
- store32(result, scratchReg);
- releaseScratch(result);
- releaseScratch(scratchReg);
- }
-
- void add64(TrustedImm32 imm, AbsoluteAddress address)
- {
- RegisterID scr1 = claimScratch();
- RegisterID scr2 = claimScratch();
-
- // Add 32-bit LSB first.
- m_assembler.loadConstant(reinterpret_cast<uint32_t>(address.m_ptr), scr1);
- m_assembler.movlMemReg(scr1, scr1); // scr1 = 32-bit LSB of int64 @ address
- m_assembler.loadConstant(imm.m_value, scr2);
- m_assembler.clrt();
- m_assembler.addclRegReg(scr1, scr2);
- m_assembler.loadConstant(reinterpret_cast<uint32_t>(address.m_ptr), scr1);
- m_assembler.movlRegMem(scr2, scr1); // Update address with 32-bit LSB result.
-
- // Then add 32-bit MSB.
- m_assembler.addlImm8r(4, scr1);
- m_assembler.movlMemReg(scr1, scr1); // scr1 = 32-bit MSB of int64 @ address
- m_assembler.movt(scr2);
- if (imm.m_value < 0)
- m_assembler.addlImm8r(-1, scr2); // Sign extend imm value if needed.
- m_assembler.addvlRegReg(scr2, scr1);
- m_assembler.loadConstant(reinterpret_cast<uint32_t>(address.m_ptr) + 4, scr2);
- m_assembler.movlRegMem(scr1, scr2); // Update (address + 4) with 32-bit MSB result.
-
- releaseScratch(scr2);
- releaseScratch(scr1);
- }
-
- void sub32(TrustedImm32 imm, RegisterID dest)
- {
- if (m_assembler.isImmediate(-imm.m_value)) {
- m_assembler.addlImm8r(-imm.m_value, dest);
- return;
- }
-
- RegisterID scr = claimScratch();
- m_assembler.loadConstant(imm.m_value, scr);
- m_assembler.sublRegReg(scr, dest);
- releaseScratch(scr);
- }
-
- void sub32(Address src, RegisterID dest)
- {
- RegisterID scr = claimScratch();
- load32(src, scr);
- m_assembler.sublRegReg(scr, dest);
- releaseScratch(scr);
- }
-
- void xor32(RegisterID src, RegisterID dest)
- {
- m_assembler.xorlRegReg(src, dest);
- }
-
- void xor32(TrustedImm32 imm, RegisterID srcDest)
- {
- if (imm.m_value == -1) {
- m_assembler.notlReg(srcDest, srcDest);
- return;
- }
-
- if ((srcDest != SH4Registers::r0) || (imm.m_value > 255) || (imm.m_value < 0)) {
- RegisterID scr = claimScratch();
- m_assembler.loadConstant((imm.m_value), scr);
- m_assembler.xorlRegReg(scr, srcDest);
- releaseScratch(scr);
- return;
- }
-
- m_assembler.xorlImm8r(imm.m_value, srcDest);
- }
-
- void compare32(int imm, RegisterID dst, RelationalCondition cond)
- {
- if (((cond == Equal) || (cond == NotEqual)) && (dst == SH4Registers::r0) && m_assembler.isImmediate(imm)) {
- m_assembler.cmpEqImmR0(imm, dst);
- return;
- }
-
- RegisterID scr = claimScratch();
- m_assembler.loadConstant(imm, scr);
- m_assembler.cmplRegReg(scr, dst, SH4Condition(cond));
- releaseScratch(scr);
- }
-
- void compare32(int offset, RegisterID base, RegisterID left, RelationalCondition cond)
- {
- RegisterID scr = claimScratch();
- if (!offset) {
- m_assembler.movlMemReg(base, scr);
- m_assembler.cmplRegReg(scr, left, SH4Condition(cond));
- releaseScratch(scr);
- return;
- }
-
- if ((offset < 0) || (offset >= 64)) {
- m_assembler.loadConstant(offset, scr);
- m_assembler.addlRegReg(base, scr);
- m_assembler.movlMemReg(scr, scr);
- m_assembler.cmplRegReg(scr, left, SH4Condition(cond));
- releaseScratch(scr);
- return;
- }
-
- m_assembler.movlMemReg(offset >> 2, base, scr);
- m_assembler.cmplRegReg(scr, left, SH4Condition(cond));
- releaseScratch(scr);
- }
-
- void testImm(int imm, int offset, RegisterID base)
- {
- RegisterID scr = claimScratch();
- RegisterID scr1 = claimScratch();
-
- if ((offset < 0) || (offset >= 64)) {
- m_assembler.loadConstant(offset, scr);
- m_assembler.addlRegReg(base, scr);
- m_assembler.movlMemReg(scr, scr);
- } else if (offset)
- m_assembler.movlMemReg(offset >> 2, base, scr);
- else
- m_assembler.movlMemReg(base, scr);
- if (m_assembler.isImmediate(imm))
- m_assembler.movImm8(imm, scr1);
- else
- m_assembler.loadConstant(imm, scr1);
-
- m_assembler.testlRegReg(scr, scr1);
- releaseScratch(scr);
- releaseScratch(scr1);
- }
-
- void testlImm(int imm, RegisterID dst)
- {
- if ((dst == SH4Registers::r0) && (imm <= 255) && (imm >= 0)) {
- m_assembler.testlImm8r(imm, dst);
- return;
- }
-
- RegisterID scr = claimScratch();
- m_assembler.loadConstant(imm, scr);
- m_assembler.testlRegReg(scr, dst);
- releaseScratch(scr);
- }
-
- void compare32(RegisterID right, int offset, RegisterID base, RelationalCondition cond)
- {
- if (!offset) {
- RegisterID scr = claimScratch();
- m_assembler.movlMemReg(base, scr);
- m_assembler.cmplRegReg(right, scr, SH4Condition(cond));
- releaseScratch(scr);
- return;
- }
-
- if ((offset < 0) || (offset >= 64)) {
- RegisterID scr = claimScratch();
- m_assembler.loadConstant(offset, scr);
- m_assembler.addlRegReg(base, scr);
- m_assembler.movlMemReg(scr, scr);
- m_assembler.cmplRegReg(right, scr, SH4Condition(cond));
- releaseScratch(scr);
- return;
- }
-
- RegisterID scr = claimScratch();
- m_assembler.movlMemReg(offset >> 2, base, scr);
- m_assembler.cmplRegReg(right, scr, SH4Condition(cond));
- releaseScratch(scr);
- }
-
- void compare32(int imm, int offset, RegisterID base, RelationalCondition cond)
- {
- if (!offset) {
- RegisterID scr = claimScratch();
- RegisterID scr1 = claimScratch();
- m_assembler.movlMemReg(base, scr);
- m_assembler.loadConstant(imm, scr1);
- m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond));
- releaseScratch(scr1);
- releaseScratch(scr);
- return;
- }
-
- if ((offset < 0) || (offset >= 64)) {
- RegisterID scr = claimScratch();
- RegisterID scr1 = claimScratch();
- m_assembler.loadConstant(offset, scr);
- m_assembler.addlRegReg(base, scr);
- m_assembler.movlMemReg(scr, scr);
- m_assembler.loadConstant(imm, scr1);
- m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond));
- releaseScratch(scr1);
- releaseScratch(scr);
- return;
- }
-
- RegisterID scr = claimScratch();
- RegisterID scr1 = claimScratch();
- m_assembler.movlMemReg(offset >> 2, base, scr);
- m_assembler.loadConstant(imm, scr1);
- m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond));
- releaseScratch(scr1);
- releaseScratch(scr);
- }
-
- // Memory access operation
-
- void load32(ImplicitAddress address, RegisterID dest)
- {
- load32(address.base, address.offset, dest);
- }
-
- void load8(ImplicitAddress address, RegisterID dest)
- {
- load8(address.base, address.offset, dest);
- }
-
- void load8(BaseIndex address, RegisterID dest)
- {
- RegisterID scr = claimScratch();
- move(address.index, scr);
- lshift32(TrustedImm32(address.scale), scr);
- add32(address.base, scr);
- load8(scr, address.offset, dest);
- releaseScratch(scr);
- }
-
- void load8Signed(BaseIndex address, RegisterID dest)
- {
- RegisterID scr = claimScratch();
- move(address.index, scr);
- lshift32(TrustedImm32(address.scale), scr);
- add32(address.base, scr);
- load8Signed(scr, address.offset, dest);
- releaseScratch(scr);
- }
-
- void load32(BaseIndex address, RegisterID dest)
- {
- RegisterID scr = claimScratch();
- move(address.index, scr);
- lshift32(TrustedImm32(address.scale), scr);
- add32(address.base, scr);
- load32(scr, address.offset, dest);
- releaseScratch(scr);
- }
-
- void load32(const void* address, RegisterID dest)
- {
- m_assembler.loadConstant(reinterpret_cast<uint32_t>(const_cast<void*>(address)), dest);
- m_assembler.movlMemReg(dest, dest);
- }
-
- void load32(RegisterID base, int offset, RegisterID dest)
- {
- if (!offset) {
- m_assembler.movlMemReg(base, dest);
- return;
- }
-
- if ((offset >= 0) && (offset < 64)) {
- m_assembler.movlMemReg(offset >> 2, base, dest);
- return;
- }
-
- if ((dest == SH4Registers::r0) && (dest != base)) {
- m_assembler.loadConstant((offset), dest);
- m_assembler.movlR0mr(base, dest);
- return;
- }
-
- RegisterID scr;
- if (dest == base)
- scr = claimScratch();
- else
- scr = dest;
- m_assembler.loadConstant((offset), scr);
- m_assembler.addlRegReg(base, scr);
- m_assembler.movlMemReg(scr, dest);
-
- if (dest == base)
- releaseScratch(scr);
- }
-
- void load8Signed(RegisterID base, int offset, RegisterID dest)
- {
- if (!offset) {
- m_assembler.movbMemReg(base, dest);
- return;
- }
-
- if ((offset > 0) && (offset < 64) && (dest == SH4Registers::r0)) {
- m_assembler.movbMemReg(offset, base, dest);
- return;
- }
-
- if (base != dest) {
- m_assembler.loadConstant((offset), dest);
- m_assembler.addlRegReg(base, dest);
- m_assembler.movbMemReg(dest, dest);
- return;
- }
-
- RegisterID scr = claimScratch();
- m_assembler.loadConstant((offset), scr);
- m_assembler.addlRegReg(base, scr);
- m_assembler.movbMemReg(scr, dest);
- releaseScratch(scr);
- }
-
- void load8(RegisterID base, int offset, RegisterID dest)
- {
- if (!offset) {
- m_assembler.movbMemReg(base, dest);
- m_assembler.extub(dest, dest);
- return;
- }
-
- if ((offset > 0) && (offset < 64) && (dest == SH4Registers::r0)) {
- m_assembler.movbMemReg(offset, base, dest);
- m_assembler.extub(dest, dest);
- return;
- }
-
- if (base != dest) {
- m_assembler.loadConstant((offset), dest);
- m_assembler.addlRegReg(base, dest);
- m_assembler.movbMemReg(dest, dest);
- m_assembler.extub(dest, dest);
- return;
- }
-
- RegisterID scr = claimScratch();
- m_assembler.loadConstant((offset), scr);
- m_assembler.addlRegReg(base, scr);
- m_assembler.movbMemReg(scr, dest);
- m_assembler.extub(dest, dest);
- releaseScratch(scr);
- }
-
- void load32(RegisterID r0, RegisterID src, RegisterID dst)
- {
- ASSERT(r0 == SH4Registers::r0);
- m_assembler.movlR0mr(src, dst);
- }
-
- void load32(RegisterID src, RegisterID dst)
- {
- m_assembler.movlMemReg(src, dst);
- }
-
- void load16(ImplicitAddress address, RegisterID dest)
- {
- if (!address.offset) {
- m_assembler.movwMemReg(address.base, dest);
- extuw(dest, dest);
- return;
- }
-
- if ((address.offset > 0) && (address.offset < 64) && (dest == SH4Registers::r0)) {
- m_assembler.movwMemReg(address.offset, address.base, dest);
- extuw(dest, dest);
- return;
- }
-
- if (address.base != dest) {
- m_assembler.loadConstant((address.offset), dest);
- m_assembler.addlRegReg(address.base, dest);
- m_assembler.movwMemReg(dest, dest);
- extuw(dest, dest);
- return;
- }
-
- RegisterID scr = claimScratch();
- m_assembler.loadConstant((address.offset), scr);
- m_assembler.addlRegReg(address.base, scr);
- m_assembler.movwMemReg(scr, dest);
- extuw(dest, dest);
- releaseScratch(scr);
- }
-
- void load16Unaligned(BaseIndex address, RegisterID dest)
- {
-
- RegisterID scr = claimScratch();
- RegisterID scr1 = claimScratch();
-
- move(address.index, scr);
- lshift32(TrustedImm32(address.scale), scr);
-
- if (address.offset)
- add32(TrustedImm32(address.offset), scr);
-
- add32(address.base, scr);
- load8(scr, scr1);
- add32(TrustedImm32(1), scr);
- load8(scr, dest);
- m_assembler.shllImm8r(8, dest);
- or32(scr1, dest);
-
- releaseScratch(scr);
- releaseScratch(scr1);
- }
-
- void load16(RegisterID src, RegisterID dest)
- {
- m_assembler.movwMemReg(src, dest);
- extuw(dest, dest);
- }
-
- void load16Signed(RegisterID src, RegisterID dest)
- {
- m_assembler.movwMemReg(src, dest);
- }
-
- void load16(RegisterID r0, RegisterID src, RegisterID dest)
- {
- ASSERT(r0 == SH4Registers::r0);
- m_assembler.movwR0mr(src, dest);
- extuw(dest, dest);
- }
-
- void load16Signed(RegisterID r0, RegisterID src, RegisterID dest)
- {
- ASSERT(r0 == SH4Registers::r0);
- m_assembler.movwR0mr(src, dest);
- }
-
- void load16(BaseIndex address, RegisterID dest)
- {
- RegisterID scr = claimScratch();
-
- move(address.index, scr);
- lshift32(TrustedImm32(address.scale), scr);
-
- if (address.offset)
- add32(TrustedImm32(address.offset), scr);
- if (address.base == SH4Registers::r0)
- load16(address.base, scr, dest);
- else {
- add32(address.base, scr);
- load16(scr, dest);
- }
-
- releaseScratch(scr);
- }
-
- void load16Signed(BaseIndex address, RegisterID dest)
- {
- RegisterID scr = claimScratch();
-
- move(address.index, scr);
- lshift32(TrustedImm32(address.scale), scr);
-
- if (address.offset)
- add32(TrustedImm32(address.offset), scr);
- if (address.base == SH4Registers::r0)
- load16Signed(address.base, scr, dest);
- else {
- add32(address.base, scr);
- load16Signed(scr, dest);
- }
-
- releaseScratch(scr);
- }
-
- void store8(RegisterID src, BaseIndex address)
- {
- RegisterID scr = claimScratch();
-
- move(address.index, scr);
- lshift32(TrustedImm32(address.scale), scr);
- add32(address.base, scr);
-
- m_assembler.movbRegMem(src, scr);
-
- releaseScratch(scr);
- }
-
- void store16(RegisterID src, BaseIndex address)
- {
- RegisterID scr = claimScratch();
-
- move(address.index, scr);
- lshift32(TrustedImm32(address.scale), scr);
- add32(address.base, scr);
-
- m_assembler.movwRegMem(src, scr);
-
- releaseScratch(scr);
- }
-
- void store32(RegisterID src, ImplicitAddress address)
- {
- RegisterID scr = claimScratch();
- store32(src, address.offset, address.base, scr);
- releaseScratch(scr);
- }
-
- void store32(RegisterID src, int offset, RegisterID base, RegisterID scr)
- {
- if (!offset) {
- m_assembler.movlRegMem(src, base);
- return;
- }
-
- if ((offset >=0) && (offset < 64)) {
- m_assembler.movlRegMem(src, offset >> 2, base);
- return;
- }
-
- m_assembler.loadConstant((offset), scr);
- if (scr == SH4Registers::r0) {
- m_assembler.movlRegMemr0(src, base);
- return;
- }
-
- m_assembler.addlRegReg(base, scr);
- m_assembler.movlRegMem(src, scr);
- }
-
- void store32(RegisterID src, RegisterID offset, RegisterID base)
- {
- ASSERT(offset == SH4Registers::r0);
- m_assembler.movlRegMemr0(src, base);
- }
-
- void store32(RegisterID src, RegisterID dst)
- {
- m_assembler.movlRegMem(src, dst);
- }
-
- void store32(TrustedImm32 imm, ImplicitAddress address)
- {
- RegisterID scr = claimScratch();
- RegisterID scr1 = claimScratch();
- m_assembler.loadConstant((imm.m_value), scr);
- store32(scr, address.offset, address.base, scr1);
- releaseScratch(scr);
- releaseScratch(scr1);
- }
-
- void store32(RegisterID src, BaseIndex address)
- {
- RegisterID scr = claimScratch();
-
- move(address.index, scr);
- lshift32(TrustedImm32(address.scale), scr);
- add32(address.base, scr);
- store32(src, Address(scr, address.offset));
-
- releaseScratch(scr);
- }
-
- void store32(TrustedImm32 imm, void* address)
- {
- RegisterID scr = claimScratch();
- RegisterID scr1 = claimScratch();
- m_assembler.loadConstant((imm.m_value), scr);
- m_assembler.loadConstant(reinterpret_cast<uint32_t>(address), scr1);
- m_assembler.movlRegMem(scr, scr1);
- releaseScratch(scr);
- releaseScratch(scr1);
- }
-
- void store32(RegisterID src, void* address)
- {
- RegisterID scr = claimScratch();
- m_assembler.loadConstant(reinterpret_cast<uint32_t>(address), scr);
- m_assembler.movlRegMem(src, scr);
- releaseScratch(scr);
- }
-
- DataLabel32 load32WithAddressOffsetPatch(Address address, RegisterID dest)
- {
- RegisterID scr = claimScratch();
- DataLabel32 label(this);
- m_assembler.loadConstantUnReusable(address.offset, scr);
- m_assembler.addlRegReg(address.base, scr);
- m_assembler.movlMemReg(scr, dest);
- releaseScratch(scr);
- return label;
- }
-
- DataLabel32 store32WithAddressOffsetPatch(RegisterID src, Address address)
- {
- RegisterID scr = claimScratch();
- DataLabel32 label(this);
- m_assembler.loadConstantUnReusable(address.offset, scr);
- m_assembler.addlRegReg(address.base, scr);
- m_assembler.movlRegMem(src, scr);
- releaseScratch(scr);
- return label;
- }
-
- DataLabelCompact load32WithCompactAddressOffsetPatch(Address address, RegisterID dest)
- {
- DataLabelCompact dataLabel(this);
- ASSERT(address.offset <= MaximumCompactPtrAlignedAddressOffset);
- ASSERT(address.offset >= 0);
- m_assembler.movlMemRegCompact(address.offset >> 2, address.base, dest);
- return dataLabel;
- }
-
- ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest)
- {
- ConvertibleLoadLabel result(this);
-
- RegisterID scr = claimScratch();
- m_assembler.movImm8(address.offset, scr);
- m_assembler.addlRegReg(address.base, scr);
- m_assembler.movlMemReg(scr, dest);
- releaseScratch(scr);
-
- return result;
- }
-
- // Floating-point operations
-
- static bool supportsFloatingPoint() { return true; }
- static bool supportsFloatingPointTruncate() { return true; }
- static bool supportsFloatingPointSqrt() { return true; }
- static bool supportsFloatingPointAbs() { return false; }
-
- void moveDoubleToInts(FPRegisterID src, RegisterID dest1, RegisterID dest2)
- {
- m_assembler.fldsfpul((FPRegisterID)(src + 1));
- m_assembler.stsfpulReg(dest1);
- m_assembler.fldsfpul(src);
- m_assembler.stsfpulReg(dest2);
- }
-
- void moveIntsToDouble(RegisterID src1, RegisterID src2, FPRegisterID dest, FPRegisterID scratch)
- {
- UNUSED_PARAM(scratch);
- m_assembler.ldsrmfpul(src1);
- m_assembler.fstsfpul((FPRegisterID)(dest + 1));
- m_assembler.ldsrmfpul(src2);
- m_assembler.fstsfpul(dest);
- }
-
- void loadFloat(BaseIndex address, FPRegisterID dest)
- {
- RegisterID scr = claimScratch();
-
- move(address.index, scr);
- lshift32(TrustedImm32(address.scale), scr);
- add32(address.base, scr);
- if (address.offset)
- add32(TrustedImm32(address.offset), scr);
-
- m_assembler.fmovsReadrm(scr, dest);
- releaseScratch(scr);
- }
-
- void loadDouble(BaseIndex address, FPRegisterID dest)
- {
- RegisterID scr = claimScratch();
-
- move(address.index, scr);
- lshift32(TrustedImm32(address.scale), scr);
- add32(address.base, scr);
- if (address.offset)
- add32(TrustedImm32(address.offset), scr);
-
- m_assembler.fmovsReadrminc(scr, (FPRegisterID)(dest + 1));
- m_assembler.fmovsReadrm(scr, dest);
- releaseScratch(scr);
- }
-
- void loadDouble(ImplicitAddress address, FPRegisterID dest)
- {
- RegisterID scr = claimScratch();
-
- m_assembler.loadConstant(address.offset, scr);
- if (address.base == SH4Registers::r0) {
- m_assembler.fmovsReadr0r(scr, (FPRegisterID)(dest + 1));
- m_assembler.addlImm8r(4, scr);
- m_assembler.fmovsReadr0r(scr, dest);
- releaseScratch(scr);
- return;
- }
-
- m_assembler.addlRegReg(address.base, scr);
- m_assembler.fmovsReadrminc(scr, (FPRegisterID)(dest + 1));
- m_assembler.fmovsReadrm(scr, dest);
- releaseScratch(scr);
- }
-
- void loadDouble(const void* address, FPRegisterID dest)
- {
- RegisterID scr = claimScratch();
- m_assembler.loadConstant(reinterpret_cast<uint32_t>(address), scr);
- m_assembler.fmovsReadrminc(scr, (FPRegisterID)(dest + 1));
- m_assembler.fmovsReadrm(scr, dest);
- releaseScratch(scr);
- }
-
- void storeFloat(FPRegisterID src, BaseIndex address)
- {
- RegisterID scr = claimScratch();
-
- move(address.index, scr);
- lshift32(TrustedImm32(address.scale), scr);
- add32(address.base, scr);
- if (address.offset)
- add32(TrustedImm32(address.offset), scr);
-
- m_assembler.fmovsWriterm(src, scr);
-
- releaseScratch(scr);
- }
-
- void storeDouble(FPRegisterID src, ImplicitAddress address)
- {
- RegisterID scr = claimScratch();
- m_assembler.loadConstant(address.offset, scr);
- m_assembler.addlRegReg(address.base, scr);
- m_assembler.fmovsWriterm((FPRegisterID)(src + 1), scr);
- m_assembler.addlImm8r(4, scr);
- m_assembler.fmovsWriterm(src, scr);
- releaseScratch(scr);
- }
-
- void storeDouble(FPRegisterID src, BaseIndex address)
- {
- RegisterID scr = claimScratch();
-
- move(address.index, scr);
- lshift32(TrustedImm32(address.scale), scr);
- add32(address.base, scr);
- if (address.offset)
- add32(TrustedImm32(address.offset), scr);
-
- m_assembler.fmovsWriterm((FPRegisterID)(src + 1), scr);
- m_assembler.addlImm8r(4, scr);
- m_assembler.fmovsWriterm(src, scr);
-
- releaseScratch(scr);
- }
-
- void addDouble(FPRegisterID op1, FPRegisterID op2, FPRegisterID dest)
- {
- if (op1 == dest)
- m_assembler.daddRegReg(op2, dest);
- else {
- m_assembler.dmovRegReg(op1, dest);
- m_assembler.daddRegReg(op2, dest);
- }
- }
-
- void addDouble(FPRegisterID src, FPRegisterID dest)
- {
- m_assembler.daddRegReg(src, dest);
- }
-
- void addDouble(AbsoluteAddress address, FPRegisterID dest)
- {
- loadDouble(address.m_ptr, fscratch);
- addDouble(fscratch, dest);
- }
-
- void addDouble(Address address, FPRegisterID dest)
- {
- loadDouble(address, fscratch);
- addDouble(fscratch, dest);
- }
-
- void subDouble(FPRegisterID src, FPRegisterID dest)
- {
- m_assembler.dsubRegReg(src, dest);
- }
-
- void subDouble(Address address, FPRegisterID dest)
- {
- loadDouble(address, fscratch);
- subDouble(fscratch, dest);
- }
-
- void mulDouble(FPRegisterID src, FPRegisterID dest)
- {
- m_assembler.dmulRegReg(src, dest);
- }
-
- void mulDouble(Address address, FPRegisterID dest)
- {
- loadDouble(address, fscratch);
- mulDouble(fscratch, dest);
- }
-
- void divDouble(FPRegisterID src, FPRegisterID dest)
- {
- m_assembler.ddivRegReg(src, dest);
- }
-
- void convertFloatToDouble(FPRegisterID src, FPRegisterID dst)
- {
- m_assembler.fldsfpul(src);
- m_assembler.dcnvsd(dst);
- }
-
- void convertDoubleToFloat(FPRegisterID src, FPRegisterID dst)
- {
- m_assembler.dcnvds(src);
- m_assembler.fstsfpul(dst);
- }
-
- void convertInt32ToDouble(RegisterID src, FPRegisterID dest)
- {
- m_assembler.ldsrmfpul(src);
- m_assembler.floatfpulDreg(dest);
- }
-
- void convertInt32ToDouble(AbsoluteAddress src, FPRegisterID dest)
- {
- RegisterID scr = claimScratch();
- m_assembler.loadConstant(reinterpret_cast<uint32_t>(src.m_ptr), scr);
- convertInt32ToDouble(scr, dest);
- releaseScratch(scr);
- }
-
- void convertInt32ToDouble(Address src, FPRegisterID dest)
- {
- RegisterID scr = claimScratch();
- load32(src, scr);
- convertInt32ToDouble(scr, dest);
- releaseScratch(scr);
- }
-
- void load32WithUnalignedHalfWords(BaseIndex address, RegisterID dest)
- {
- RegisterID scr = claimScratch();
- RegisterID scr1 = claimScratch();
- Jump m_jump;
- JumpList end;
-
- if (dest != SH4Registers::r0)
- move(SH4Registers::r0, scr1);
-
- move(address.index, scr);
- lshift32(TrustedImm32(address.scale), scr);
- add32(address.base, scr);
-
- if (address.offset)
- add32(TrustedImm32(address.offset), scr);
-
- m_assembler.ensureSpace(m_assembler.maxInstructionSize + 68, sizeof(uint32_t));
- move(scr, SH4Registers::r0);
- m_assembler.andlImm8r(0x3, SH4Registers::r0);
- m_assembler.cmpEqImmR0(0x0, SH4Registers::r0);
- m_jump = Jump(m_assembler.jne(), SH4Assembler::JumpNear);
- if (dest != SH4Registers::r0)
- move(scr1, SH4Registers::r0);
-
- load32(scr, dest);
- end.append(Jump(m_assembler.bra(), SH4Assembler::JumpNear));
- m_assembler.nop();
- m_jump.link(this);
- m_assembler.andlImm8r(0x1, SH4Registers::r0);
- m_assembler.cmpEqImmR0(0x0, SH4Registers::r0);
-
- if (dest != SH4Registers::r0)
- move(scr1, SH4Registers::r0);
-
- m_jump = Jump(m_assembler.jne(), SH4Assembler::JumpNear);
- load16(scr, scr1);
- add32(TrustedImm32(2), scr);
- load16(scr, dest);
- m_assembler.shllImm8r(16, dest);
- or32(scr1, dest);
- end.append(Jump(m_assembler.bra(), SH4Assembler::JumpNear));
- m_assembler.nop();
- m_jump.link(this);
- load8(scr, scr1);
- add32(TrustedImm32(1), scr);
- load16(scr, dest);
- m_assembler.shllImm8r(8, dest);
- or32(dest, scr1);
- add32(TrustedImm32(2), scr);
- load8(scr, dest);
- m_assembler.shllImm8r(8, dest);
- m_assembler.shllImm8r(16, dest);
- or32(scr1, dest);
- end.link(this);
-
- releaseScratch(scr);
- releaseScratch(scr1);
- }
-
- Jump branch32WithUnalignedHalfWords(RelationalCondition cond, BaseIndex left, TrustedImm32 right)
- {
- RegisterID scr = scratchReg3;
- load32WithUnalignedHalfWords(left, scr);
- if (((cond == Equal) || (cond == NotEqual)) && !right.m_value)
- m_assembler.testlRegReg(scr, scr);
- else
- compare32(right.m_value, scr, cond);
-
- if (cond == NotEqual)
- return branchFalse();
- return branchTrue();
- }
-
- Jump branchDoubleNonZero(FPRegisterID reg, FPRegisterID scratch)
- {
- m_assembler.movImm8(0, scratchReg3);
- convertInt32ToDouble(scratchReg3, scratch);
- return branchDouble(DoubleNotEqual, reg, scratch);
- }
-
- Jump branchDoubleZeroOrNaN(FPRegisterID reg, FPRegisterID scratch)
- {
- m_assembler.movImm8(0, scratchReg3);
- convertInt32ToDouble(scratchReg3, scratch);
- return branchDouble(DoubleEqualOrUnordered, reg, scratch);
- }
-
- Jump branchDouble(DoubleCondition cond, FPRegisterID left, FPRegisterID right)
- {
- if (cond == DoubleEqual) {
- m_assembler.dcmppeq(right, left);
- return branchTrue();
- }
-
- if (cond == DoubleNotEqual) {
- RegisterID scr = claimScratch();
- JumpList end;
- m_assembler.loadConstant(0x7fbfffff, scratchReg3);
- m_assembler.dcnvds(right);
- m_assembler.stsfpulReg(scr);
- m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal));
- m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t));
- end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear));
- m_assembler.dcnvds(left);
- m_assembler.stsfpulReg(scr);
- m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal));
- end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear));
- m_assembler.dcmppeq(right, left);
- releaseScratch(scr);
- Jump m_jump = branchFalse();
- end.link(this);
- return m_jump;
- }
-
- if (cond == DoubleGreaterThan) {
- m_assembler.dcmppgt(right, left);
- return branchTrue();
- }
-
- if (cond == DoubleGreaterThanOrEqual) {
- m_assembler.dcmppgt(left, right);
- return branchFalse();
- }
-
- if (cond == DoubleLessThan) {
- m_assembler.dcmppgt(left, right);
- return branchTrue();
- }
-
- if (cond == DoubleLessThanOrEqual) {
- m_assembler.dcmppgt(right, left);
- return branchFalse();
- }
-
- if (cond == DoubleEqualOrUnordered) {
- RegisterID scr = claimScratch();
- JumpList end;
- m_assembler.loadConstant(0x7fbfffff, scratchReg3);
- m_assembler.dcnvds(right);
- m_assembler.stsfpulReg(scr);
- m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal));
- m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t));
- end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear));
- m_assembler.dcnvds(left);
- m_assembler.stsfpulReg(scr);
- m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal));
- end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear));
- m_assembler.dcmppeq(left, right);
- Jump m_jump = Jump(m_assembler.je());
- end.link(this);
- m_assembler.extraInstrForBranch(scr);
- releaseScratch(scr);
- return m_jump;
- }
-
- if (cond == DoubleGreaterThanOrUnordered) {
- RegisterID scr = claimScratch();
- JumpList end;
- m_assembler.loadConstant(0x7fbfffff, scratchReg3);
- m_assembler.dcnvds(right);
- m_assembler.stsfpulReg(scr);
- m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal));
- m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t));
- end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear));
- m_assembler.dcnvds(left);
- m_assembler.stsfpulReg(scr);
- m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal));
- end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear));
- m_assembler.dcmppgt(right, left);
- Jump m_jump = Jump(m_assembler.je());
- end.link(this);
- m_assembler.extraInstrForBranch(scr);
- releaseScratch(scr);
- return m_jump;
- }
-
- if (cond == DoubleGreaterThanOrEqualOrUnordered) {
- RegisterID scr = claimScratch();
- JumpList end;
- m_assembler.loadConstant(0x7fbfffff, scratchReg3);
- m_assembler.dcnvds(right);
- m_assembler.stsfpulReg(scr);
- m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal));
- m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t));
- end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear));
- m_assembler.dcnvds(left);
- m_assembler.stsfpulReg(scr);
- m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal));
- end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear));
- m_assembler.dcmppgt(left, right);
- Jump m_jump = Jump(m_assembler.jne());
- end.link(this);
- m_assembler.extraInstrForBranch(scr);
- releaseScratch(scr);
- return m_jump;
- }
-
- if (cond == DoubleLessThanOrUnordered) {
- RegisterID scr = claimScratch();
- JumpList end;
- m_assembler.loadConstant(0x7fbfffff, scratchReg3);
- m_assembler.dcnvds(right);
- m_assembler.stsfpulReg(scr);
- m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal));
- m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t));
- end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear));
- m_assembler.dcnvds(left);
- m_assembler.stsfpulReg(scr);
- m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal));
- end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear));
- m_assembler.dcmppgt(left, right);
- Jump m_jump = Jump(m_assembler.je());
- end.link(this);
- m_assembler.extraInstrForBranch(scr);
- releaseScratch(scr);
- return m_jump;
- }
-
- if (cond == DoubleLessThanOrEqualOrUnordered) {
- RegisterID scr = claimScratch();
- JumpList end;
- m_assembler.loadConstant(0x7fbfffff, scratchReg3);
- m_assembler.dcnvds(right);
- m_assembler.stsfpulReg(scr);
- m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal));
- m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t));
- end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear));
- m_assembler.dcnvds(left);
- m_assembler.stsfpulReg(scr);
- m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal));
- end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear));
- m_assembler.dcmppgt(right, left);
- Jump m_jump = Jump(m_assembler.jne());
- end.link(this);
- m_assembler.extraInstrForBranch(scr);
- releaseScratch(scr);
- return m_jump;
- }
-
- ASSERT(cond == DoubleNotEqualOrUnordered);
- RegisterID scr = claimScratch();
- JumpList end;
- m_assembler.loadConstant(0x7fbfffff, scratchReg3);
- m_assembler.dcnvds(right);
- m_assembler.stsfpulReg(scr);
- m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal));
- m_assembler.ensureSpace(m_assembler.maxInstructionSize + 22, sizeof(uint32_t));
- end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear));
- m_assembler.dcnvds(left);
- m_assembler.stsfpulReg(scr);
- m_assembler.cmplRegReg(scratchReg3, scr, SH4Condition(Equal));
- end.append(Jump(m_assembler.je(), SH4Assembler::JumpNear));
- m_assembler.dcmppeq(right, left);
- Jump m_jump = Jump(m_assembler.jne());
- end.link(this);
- m_assembler.extraInstrForBranch(scr);
- releaseScratch(scr);
- return m_jump;
- }
-
- Jump branchTrue()
- {
- m_assembler.ensureSpace(m_assembler.maxInstructionSize + 6, sizeof(uint32_t));
- Jump m_jump = Jump(m_assembler.je());
- m_assembler.extraInstrForBranch(scratchReg3);
- return m_jump;
- }
-
- Jump branchFalse()
- {
- m_assembler.ensureSpace(m_assembler.maxInstructionSize + 6, sizeof(uint32_t));
- Jump m_jump = Jump(m_assembler.jne());
- m_assembler.extraInstrForBranch(scratchReg3);
- return m_jump;
- }
-
- Jump branch32(RelationalCondition cond, BaseIndex left, TrustedImm32 right)
- {
- RegisterID scr = claimScratch();
- move(left.index, scr);
- lshift32(TrustedImm32(left.scale), scr);
- add32(left.base, scr);
- load32(scr, left.offset, scr);
- compare32(right.m_value, scr, cond);
- releaseScratch(scr);
-
- if (cond == NotEqual)
- return branchFalse();
- return branchTrue();
- }
-
- void sqrtDouble(FPRegisterID src, FPRegisterID dest)
- {
- if (dest != src)
- m_assembler.dmovRegReg(src, dest);
- m_assembler.dsqrt(dest);
- }
-
- void absDouble(FPRegisterID, FPRegisterID)
- {
- RELEASE_ASSERT_NOT_REACHED();
- }
-
- Jump branchTest8(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1))
- {
- RegisterID addressTempRegister = claimScratch();
- load8(address, addressTempRegister);
- Jump jmp = branchTest32(cond, addressTempRegister, mask);
- releaseScratch(addressTempRegister);
- return jmp;
- }
-
- Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1))
- {
- RegisterID addressTempRegister = claimScratch();
- move(TrustedImmPtr(address.m_ptr), addressTempRegister);
- load8(Address(addressTempRegister), addressTempRegister);
- Jump jmp = branchTest32(cond, addressTempRegister, mask);
- releaseScratch(addressTempRegister);
- return jmp;
- }
-
- void signExtend32ToPtr(RegisterID src, RegisterID dest)
- {
- if (src != dest)
- move(src, dest);
- }
-
- Jump branch8(RelationalCondition cond, Address left, TrustedImm32 right)
- {
- RegisterID addressTempRegister = claimScratch();
- load8(left, addressTempRegister);
- Jump jmp = branch32(cond, addressTempRegister, right);
- releaseScratch(addressTempRegister);
- return jmp;
- }
-
- void compare8(RelationalCondition cond, Address left, TrustedImm32 right, RegisterID dest)
- {
- RegisterID addressTempRegister = claimScratch();
- load8(left, addressTempRegister);
- compare32(cond, addressTempRegister, right, dest);
- releaseScratch(addressTempRegister);
- }
-
- Jump branchTruncateDoubleToInt32(FPRegisterID src, RegisterID dest)
- {
- m_assembler.ftrcdrmfpul(src);
- m_assembler.stsfpulReg(dest);
- m_assembler.loadConstant(0x7fffffff, scratchReg3);
- m_assembler.cmplRegReg(dest, scratchReg3, SH4Condition(Equal));
- m_assembler.ensureSpace(m_assembler.maxInstructionSize + 14, sizeof(uint32_t));
- m_assembler.branch(BT_OPCODE, 2);
- m_assembler.addlImm8r(1, scratchReg3);
- m_assembler.cmplRegReg(dest, scratchReg3, SH4Condition(Equal));
- return branchTrue();
- }
-
- // Stack manipulation operations
-
- void pop(RegisterID dest)
- {
- m_assembler.popReg(dest);
- }
-
- void push(RegisterID src)
- {
- m_assembler.pushReg(src);
- }
-
- void push(Address address)
- {
- if (!address.offset) {
- push(address.base);
- return;
- }
-
- if ((address.offset < 0) || (address.offset >= 64)) {
- RegisterID scr = claimScratch();
- m_assembler.loadConstant(address.offset, scr);
- m_assembler.addlRegReg(address.base, scr);
- m_assembler.movlMemReg(scr, SH4Registers::sp);
- m_assembler.addlImm8r(-4, SH4Registers::sp);
- releaseScratch(scr);
- return;
- }
-
- m_assembler.movlMemReg(address.offset >> 2, address.base, SH4Registers::sp);
- m_assembler.addlImm8r(-4, SH4Registers::sp);
- }
-
- void push(TrustedImm32 imm)
- {
- RegisterID scr = claimScratch();
- m_assembler.loadConstant(imm.m_value, scr);
- push(scr);
- releaseScratch(scr);
- }
-
- // Register move operations
-
- void move(TrustedImm32 imm, RegisterID dest)
- {
- m_assembler.loadConstant(imm.m_value, dest);
- }
-
- DataLabelPtr moveWithPatch(TrustedImmPtr initialValue, RegisterID dest)
- {
- m_assembler.ensureSpace(m_assembler.maxInstructionSize, sizeof(uint32_t));
- DataLabelPtr dataLabel(this);
- m_assembler.loadConstantUnReusable(reinterpret_cast<uint32_t>(initialValue.m_value), dest);
- return dataLabel;
- }
-
- void move(RegisterID src, RegisterID dest)
- {
- if (src != dest)
- m_assembler.movlRegReg(src, dest);
- }
-
- void move(TrustedImmPtr imm, RegisterID dest)
- {
- m_assembler.loadConstant(imm.asIntptr(), dest);
- }
-
- void extuw(RegisterID src, RegisterID dst)
- {
- m_assembler.extuw(src, dst);
- }
-
- void compare32(RelationalCondition cond, RegisterID left, RegisterID right, RegisterID dest)
- {
- m_assembler.cmplRegReg(right, left, SH4Condition(cond));
- if (cond != NotEqual) {
- m_assembler.movt(dest);
- return;
- }
-
- m_assembler.ensureSpace(m_assembler.maxInstructionSize + 4);
- m_assembler.movImm8(0, dest);
- m_assembler.branch(BT_OPCODE, 0);
- m_assembler.movImm8(1, dest);
- }
-
- void compare32(RelationalCondition cond, RegisterID left, TrustedImm32 right, RegisterID dest)
- {
- if (left != dest) {
- move(right, dest);
- compare32(cond, left, dest, dest);
- return;
- }
-
- RegisterID scr = claimScratch();
- move(right, scr);
- compare32(cond, left, scr, dest);
- releaseScratch(scr);
- }
-
- void test8(ResultCondition cond, Address address, TrustedImm32 mask, RegisterID dest)
- {
- ASSERT((cond == Zero) || (cond == NonZero));
-
- load8(address, dest);
- if (mask.m_value == -1)
- compare32(0, dest, static_cast<RelationalCondition>(cond));
- else
- testlImm(mask.m_value, dest);
- if (cond != NonZero) {
- m_assembler.movt(dest);
- return;
- }
-
- m_assembler.ensureSpace(m_assembler.maxInstructionSize + 4);
- m_assembler.movImm8(0, dest);
- m_assembler.branch(BT_OPCODE, 0);
- m_assembler.movImm8(1, dest);
- }
-
- void loadPtrLinkReg(ImplicitAddress address)
- {
- RegisterID scr = claimScratch();
- load32(address, scr);
- m_assembler.ldspr(scr);
- releaseScratch(scr);
- }
-
- Jump branch32(RelationalCondition cond, RegisterID left, RegisterID right)
- {
- m_assembler.cmplRegReg(right, left, SH4Condition(cond));
- /* BT label => BF off
- nop LDR reg
- nop braf @reg
- nop nop
- */
- if (cond == NotEqual)
- return branchFalse();
- return branchTrue();
- }
-
- Jump branch32(RelationalCondition cond, RegisterID left, TrustedImm32 right)
- {
- if (((cond == Equal) || (cond == NotEqual)) && !right.m_value)
- m_assembler.testlRegReg(left, left);
- else
- compare32(right.m_value, left, cond);
-
- if (cond == NotEqual)
- return branchFalse();
- return branchTrue();
- }
-
- Jump branch32(RelationalCondition cond, RegisterID left, Address right)
- {
- compare32(right.offset, right.base, left, cond);
- if (cond == NotEqual)
- return branchFalse();
- return branchTrue();
- }
-
- Jump branch32(RelationalCondition cond, Address left, RegisterID right)
- {
- compare32(right, left.offset, left.base, cond);
- if (cond == NotEqual)
- return branchFalse();
- return branchTrue();
- }
-
- Jump branch32(RelationalCondition cond, Address left, TrustedImm32 right)
- {
- compare32(right.m_value, left.offset, left.base, cond);
- if (cond == NotEqual)
- return branchFalse();
- return branchTrue();
- }
-
- Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right)
- {
- RegisterID scr = claimScratch();
-
- move(TrustedImm32(reinterpret_cast<uint32_t>(left.m_ptr)), scr);
- m_assembler.cmplRegReg(right, scr, SH4Condition(cond));
- releaseScratch(scr);
-
- if (cond == NotEqual)
- return branchFalse();
- return branchTrue();
- }
-
- Jump branch32(RelationalCondition cond, AbsoluteAddress left, TrustedImm32 right)
- {
- RegisterID addressTempRegister = claimScratch();
-
- m_assembler.loadConstant(reinterpret_cast<uint32_t>(left.m_ptr), addressTempRegister);
- m_assembler.movlMemReg(addressTempRegister, addressTempRegister);
- compare32(right.m_value, addressTempRegister, cond);
- releaseScratch(addressTempRegister);
-
- if (cond == NotEqual)
- return branchFalse();
- return branchTrue();
- }
-
- Jump branch8(RelationalCondition cond, BaseIndex left, TrustedImm32 right)
- {
- ASSERT(!(right.m_value & 0xFFFFFF00));
- RegisterID scr = claimScratch();
-
- move(left.index, scr);
- lshift32(TrustedImm32(left.scale), scr);
-
- if (left.offset)
- add32(TrustedImm32(left.offset), scr);
- add32(left.base, scr);
- load8(scr, scr);
- RegisterID scr1 = claimScratch();
- m_assembler.loadConstant(right.m_value, scr1);
- releaseScratch(scr);
- releaseScratch(scr1);
-
- return branch32(cond, scr, scr1);
- }
-
- Jump branchTest32(ResultCondition cond, RegisterID reg, RegisterID mask)
- {
- ASSERT((cond == Zero) || (cond == NonZero));
-
- m_assembler.testlRegReg(reg, mask);
-
- if (cond == NonZero) // NotEqual
- return branchFalse();
- return branchTrue();
- }
-
- Jump branchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1))
- {
- ASSERT((cond == Zero) || (cond == NonZero));
-
- if (mask.m_value == -1)
- m_assembler.testlRegReg(reg, reg);
- else
- testlImm(mask.m_value, reg);
-
- if (cond == NonZero) // NotEqual
- return branchFalse();
- return branchTrue();
- }
-
- Jump branchTest32(ResultCondition cond, Address address, TrustedImm32 mask = TrustedImm32(-1))
- {
- ASSERT((cond == Zero) || (cond == NonZero));
-
- if (mask.m_value == -1)
- compare32(0, address.offset, address.base, static_cast<RelationalCondition>(cond));
- else
- testImm(mask.m_value, address.offset, address.base);
-
- if (cond == NonZero) // NotEqual
- return branchFalse();
- return branchTrue();
- }
-
- Jump branchTest32(ResultCondition cond, BaseIndex address, TrustedImm32 mask = TrustedImm32(-1))
- {
- RegisterID scr = claimScratch();
-
- move(address.index, scr);
- lshift32(TrustedImm32(address.scale), scr);
- add32(address.base, scr);
- load32(scr, address.offset, scr);
-
- if (mask.m_value == -1)
- m_assembler.testlRegReg(scr, scr);
- else
- testlImm(mask.m_value, scr);
-
- releaseScratch(scr);
-
- if (cond == NonZero) // NotEqual
- return branchFalse();
- return branchTrue();
- }
-
- Jump jump()
- {
- return Jump(m_assembler.jmp());
- }
-
- void jump(RegisterID target)
- {
- m_assembler.jmpReg(target);
- }
-
- void jump(Address address)
- {
- RegisterID scr = claimScratch();
-
- if ((address.offset < 0) || (address.offset >= 64)) {
- m_assembler.loadConstant(address.offset, scr);
- m_assembler.addlRegReg(address.base, scr);
- m_assembler.movlMemReg(scr, scr);
- } else if (address.offset)
- m_assembler.movlMemReg(address.offset >> 2, address.base, scr);
- else
- m_assembler.movlMemReg(address.base, scr);
- m_assembler.jmpReg(scr);
-
- releaseScratch(scr);
- }
-
- // Arithmetic control flow operations
-
- Jump branchAdd32(ResultCondition cond, RegisterID src, RegisterID dest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
-
- if (cond == Overflow) {
- m_assembler.addvlRegReg(src, dest);
- return branchTrue();
- }
-
- if (cond == Signed) {
- m_assembler.addlRegReg(src, dest);
- // Check if dest is negative
- m_assembler.cmppz(dest);
- return branchFalse();
- }
-
- m_assembler.addlRegReg(src, dest);
- compare32(0, dest, Equal);
-
- if (cond == NonZero) // NotEqual
- return branchFalse();
- return branchTrue();
- }
-
- Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, RegisterID dest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
-
- move(imm, scratchReg3);
- return branchAdd32(cond, scratchReg3, dest);
- }
-
- Jump branchAdd32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
-
- if (src != dest)
- move(src, dest);
-
- if (cond == Overflow) {
- move(imm, scratchReg3);
- m_assembler.addvlRegReg(scratchReg3, dest);
- return branchTrue();
- }
-
- add32(imm, dest);
-
- if (cond == Signed) {
- m_assembler.cmppz(dest);
- return branchFalse();
- }
-
- compare32(0, dest, Equal);
-
- if (cond == NonZero) // NotEqual
- return branchFalse();
- return branchTrue();
- }
-
- Jump branchMul32(ResultCondition cond, RegisterID src, RegisterID dest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
-
- if (cond == Overflow) {
- RegisterID scr1 = claimScratch();
- RegisterID scr = claimScratch();
- m_assembler.dmullRegReg(src, dest);
- m_assembler.stsmacl(dest);
- m_assembler.movImm8(-31, scr);
- m_assembler.movlRegReg(dest, scr1);
- m_assembler.shaRegReg(scr1, scr);
- m_assembler.stsmach(scr);
- m_assembler.cmplRegReg(scr, scr1, SH4Condition(Equal));
- releaseScratch(scr1);
- releaseScratch(scr);
- return branchFalse();
- }
-
- m_assembler.imullRegReg(src, dest);
- m_assembler.stsmacl(dest);
- if (cond == Signed) {
- // Check if dest is negative
- m_assembler.cmppz(dest);
- return branchFalse();
- }
-
- compare32(0, dest, static_cast<RelationalCondition>(cond));
-
- if (cond == NonZero) // NotEqual
- return branchFalse();
- return branchTrue();
- }
-
- Jump branchMul32(ResultCondition cond, TrustedImm32 imm, RegisterID src, RegisterID dest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
-
- move(imm, scratchReg3);
- if (src != dest)
- move(src, dest);
-
- return branchMul32(cond, scratchReg3, dest);
- }
-
- Jump branchSub32(ResultCondition cond, RegisterID src, RegisterID dest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
-
- if (cond == Overflow) {
- m_assembler.subvlRegReg(src, dest);
- return branchTrue();
- }
-
- if (cond == Signed) {
- // Check if dest is negative
- m_assembler.sublRegReg(src, dest);
- compare32(0, dest, LessThan);
- return branchTrue();
- }
-
- sub32(src, dest);
- compare32(0, dest, static_cast<RelationalCondition>(cond));
-
- if (cond == NonZero) // NotEqual
- return branchFalse();
- return branchTrue();
- }
-
- Jump branchSub32(ResultCondition cond, TrustedImm32 imm, RegisterID dest)
- {
- ASSERT((cond == Overflow) || (cond == Signed) || (cond == Zero) || (cond == NonZero));
-
- move(imm, scratchReg3);
- return branchSub32(cond, scratchReg3, dest);
- }
-
- Jump branchSub32(ResultCondition cond, RegisterID src, TrustedImm32 imm, RegisterID dest)
- {
- move(imm, scratchReg3);
- if (src != dest)
- move(src, dest);
- return branchSub32(cond, scratchReg3, dest);
- }
-
- Jump branchSub32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest)
- {
- if (src1 != dest)
- move(src1, dest);
- return branchSub32(cond, src2, dest);
- }
-
- Jump branchOr32(ResultCondition cond, RegisterID src, RegisterID dest)
- {
- ASSERT((cond == Signed) || (cond == Zero) || (cond == NonZero));
-
- if (cond == Signed) {
- or32(src, dest);
- compare32(0, dest, static_cast<RelationalCondition>(LessThan));
- return branchTrue();
- }
-
- or32(src, dest);
- compare32(0, dest, static_cast<RelationalCondition>(cond));
-
- if (cond == NonZero) // NotEqual
- return branchFalse();
- return branchTrue();
- }
-
- void branchConvertDoubleToInt32(FPRegisterID src, RegisterID dest, JumpList& failureCases, FPRegisterID fpTemp)
- {
- m_assembler.ftrcdrmfpul(src);
- m_assembler.stsfpulReg(dest);
- convertInt32ToDouble(dest, fscratch);
- failureCases.append(branchDouble(DoubleNotEqualOrUnordered, fscratch, src));
-
- if (dest == SH4Registers::r0)
- m_assembler.cmpEqImmR0(0, dest);
- else {
- m_assembler.movImm8(0, scratchReg3);
- m_assembler.cmplRegReg(scratchReg3, dest, SH4Condition(Equal));
- }
- failureCases.append(branchTrue());
- }
-
- void neg32(RegisterID dst)
- {
- m_assembler.neg(dst, dst);
- }
-
- void urshift32(RegisterID shiftamount, RegisterID dest)
- {
- if (shiftamount == SH4Registers::r0)
- m_assembler.andlImm8r(0x1f, shiftamount);
- else {
- RegisterID scr = claimScratch();
- m_assembler.loadConstant(0x1f, scr);
- m_assembler.andlRegReg(scr, shiftamount);
- releaseScratch(scr);
- }
- m_assembler.neg(shiftamount, shiftamount);
- m_assembler.shllRegReg(dest, shiftamount);
- }
-
- void urshift32(TrustedImm32 imm, RegisterID dest)
- {
- RegisterID scr = claimScratch();
- m_assembler.loadConstant(-(imm.m_value & 0x1f), scr);
- m_assembler.shaRegReg(dest, scr);
- releaseScratch(scr);
- }
-
- void urshift32(RegisterID src, TrustedImm32 shiftamount, RegisterID dest)
- {
- if (src != dest)
- move(src, dest);
-
- urshift32(shiftamount, dest);
- }
-
- Call call()
- {
- return Call(m_assembler.call(), Call::Linkable);
- }
-
- Call nearCall()
- {
- return Call(m_assembler.call(), Call::LinkableNear);
- }
-
- Call call(RegisterID target)
- {
- return Call(m_assembler.call(target), Call::None);
- }
-
- void call(Address address, RegisterID target)
- {
- load32(address.base, address.offset, target);
- m_assembler.ensureSpace(m_assembler.maxInstructionSize + 2);
- m_assembler.branch(JSR_OPCODE, target);
- m_assembler.nop();
- }
-
- void breakpoint()
- {
- m_assembler.bkpt();
- m_assembler.nop();
- }
-
- Jump branchPtrWithPatch(RelationalCondition cond, RegisterID left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0))
- {
- RegisterID dataTempRegister = claimScratch();
-
- dataLabel = moveWithPatch(initialRightValue, dataTempRegister);
- m_assembler.cmplRegReg(dataTempRegister, left, SH4Condition(cond));
- releaseScratch(dataTempRegister);
-
- if (cond == NotEqual)
- return branchFalse();
- return branchTrue();
- }
-
- Jump branchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0))
- {
- RegisterID scr = claimScratch();
-
- m_assembler.loadConstant(left.offset, scr);
- m_assembler.addlRegReg(left.base, scr);
- m_assembler.movlMemReg(scr, scr);
- RegisterID scr1 = claimScratch();
- dataLabel = moveWithPatch(initialRightValue, scr1);
- m_assembler.cmplRegReg(scr1, scr, SH4Condition(cond));
- releaseScratch(scr);
- releaseScratch(scr1);
-
- if (cond == NotEqual)
- return branchFalse();
- return branchTrue();
- }
-
- void ret()
- {
- m_assembler.ret();
- m_assembler.nop();
- }
-
- DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address)
- {
- RegisterID scr = claimScratch();
- DataLabelPtr label = moveWithPatch(initialValue, scr);
- store32(scr, address);
- releaseScratch(scr);
- return label;
- }
-
- DataLabelPtr storePtrWithPatch(ImplicitAddress address) { return storePtrWithPatch(TrustedImmPtr(0), address); }
-
- int sizeOfConstantPool()
- {
- return m_assembler.sizeOfConstantPool();
- }
-
- Call tailRecursiveCall()
- {
- RegisterID scr = claimScratch();
-
- m_assembler.loadConstantUnReusable(0x0, scr, true);
- Jump m_jump = Jump(m_assembler.jmp(scr));
- releaseScratch(scr);
-
- return Call::fromTailJump(m_jump);
- }
-
- Call makeTailRecursiveCall(Jump oldJump)
- {
- oldJump.link(this);
- return tailRecursiveCall();
- }
-
- void nop()
- {
- m_assembler.nop();
- }
-
- static FunctionPtr readCallTarget(CodeLocationCall call)
- {
- return FunctionPtr(reinterpret_cast<void(*)()>(SH4Assembler::readCallTarget(call.dataLocation())));
- }
-
- static void replaceWithJump(CodeLocationLabel instructionStart, CodeLocationLabel destination)
- {
- RELEASE_ASSERT_NOT_REACHED();
- }
-
- static ptrdiff_t maxJumpReplacementSize()
- {
- RELEASE_ASSERT_NOT_REACHED();
- return 0;
- }
-
- static bool canJumpReplacePatchableBranchPtrWithPatch() { return false; }
-
- static CodeLocationLabel startOfBranchPtrWithPatchOnRegister(CodeLocationDataLabelPtr label)
- {
- return label.labelAtOffset(0);
- }
-
- static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID, void* initialValue)
- {
- SH4Assembler::revertJump(instructionStart.dataLocation(), reinterpret_cast<uintptr_t>(initialValue) & 0xffff);
- }
-
- static CodeLocationLabel startOfPatchableBranchPtrWithPatchOnAddress(CodeLocationDataLabelPtr)
- {
- UNREACHABLE_FOR_PLATFORM();
- return CodeLocationLabel();
- }
-
- static void revertJumpReplacementToPatchableBranchPtrWithPatch(CodeLocationLabel instructionStart, Address, void* initialValue)
- {
- UNREACHABLE_FOR_PLATFORM();
- }
-
-protected:
- SH4Assembler::Condition SH4Condition(RelationalCondition cond)
- {
- return static_cast<SH4Assembler::Condition>(cond);
- }
-
- SH4Assembler::Condition SH4Condition(ResultCondition cond)
- {
- return static_cast<SH4Assembler::Condition>(cond);
- }
-private:
- template <typename, template <typename> class> friend class LinkBufferBase;
- friend class RepatchBuffer;
-
- static void linkCall(void*, Call, FunctionPtr);
- static void repatchCall(CodeLocationCall, CodeLocationLabel);
- static void repatchCall(CodeLocationCall, FunctionPtr);
-};
-
-} // namespace JSC
-
-#endif // ENABLE(ASSEMBLER)
-
-#endif // MacroAssemblerSH4_h
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerX86.h b/src/3rdparty/masm/assembler/MacroAssemblerX86.h
index 280cf427fc..e3e0bfe5e1 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerX86.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerX86.h
@@ -108,6 +108,11 @@ public:
m_assembler.adcl_im(imm.m_value >> 31, reinterpret_cast<const char*>(address.m_ptr) + sizeof(int32_t));
}
+ void getEffectiveAddress(BaseIndex address, RegisterID dest)
+ {
+ return x86Lea32(address, dest);
+ }
+
void and32(TrustedImm32 imm, AbsoluteAddress address)
{
m_assembler.andl_im(imm.m_value, address.m_ptr);
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerX86Common.h b/src/3rdparty/masm/assembler/MacroAssemblerX86Common.h
index 94771be6a7..769b4346ee 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerX86Common.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerX86Common.h
@@ -146,14 +146,24 @@ public:
m_assembler.andl_rr(src, dest);
}
- void add32(RegisterID op1, RegisterID op2, RegisterID dest)
+ void add32(RegisterID a, RegisterID b, RegisterID dest)
{
- if (op2 == dest) {
- add32(op1, dest);
- } else {
- move(op1, dest);
- add32(op2, dest);
+ x86Lea32(BaseIndex(a, b, TimesOne), dest);
+ }
+
+ void x86Lea32(BaseIndex index, RegisterID dest)
+ {
+ if (!index.scale && !index.offset) {
+ if (index.base == dest) {
+ add32(index.index, dest);
+ return;
+ }
+ if (index.index == dest) {
+ add32(index.base, dest);
+ return;
+ }
}
+ m_assembler.leal_mr(index.offset, index.base, index.index, index.scale, dest);
}
void and32(TrustedImm32 imm, RegisterID dest)
@@ -501,6 +511,11 @@ public:
load32(address, dest);
}
+ void load16Unaligned(ImplicitAddress address, RegisterID dest)
+ {
+ load16(address, dest);
+ }
+
void load16Unaligned(BaseIndex address, RegisterID dest)
{
load16(address, dest);
@@ -558,6 +573,11 @@ public:
m_assembler.movzwl_mr(address.offset, address.base, address.index, address.scale, dest);
}
+ void load16(ImplicitAddress address, RegisterID dest)
+ {
+ m_assembler.movzwl_mr(address.offset, address.base, dest);
+ }
+
void load16(Address address, RegisterID dest)
{
m_assembler.movzwl_mr(address.offset, address.base, dest);
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h b/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h
index 002caaae78..f4349e1f93 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h
@@ -243,6 +243,26 @@ public:
add64(imm, Address(scratchRegister));
}
+ void x86Lea64(BaseIndex index, RegisterID dest)
+ {
+ if (!index.scale && !index.offset) {
+ if (index.base == dest) {
+ add64(index.index, dest);
+ return;
+ }
+ if (index.index == dest) {
+ add64(index.base, dest);
+ return;
+ }
+ }
+ m_assembler.leaq_mr(index.offset, index.base, index.index, index.scale, dest);
+ }
+
+ void getEffectiveAddress(BaseIndex address, RegisterID dest)
+ {
+ return x86Lea64(address, dest);
+ }
+
void and64(RegisterID src, RegisterID dest)
{
m_assembler.andq_rr(src, dest);
diff --git a/src/3rdparty/masm/assembler/SH4Assembler.h b/src/3rdparty/masm/assembler/SH4Assembler.h
deleted file mode 100644
index b7a166ea99..0000000000
--- a/src/3rdparty/masm/assembler/SH4Assembler.h
+++ /dev/null
@@ -1,2152 +0,0 @@
-/*
- * Copyright (C) 2009-2011 STMicroelectronics. All rights reserved.
- * Copyright (C) 2008 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. 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.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
- */
-
-#ifndef SH4Assembler_h
-#define SH4Assembler_h
-
-#if ENABLE(ASSEMBLER) && CPU(SH4)
-
-#include "AssemblerBuffer.h"
-#include "AssemblerBufferWithConstantPool.h"
-#include "JITCompilationEffort.h"
-#include <stdarg.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <wtf/Assertions.h>
-#include <wtf/DataLog.h>
-#include <wtf/Vector.h>
-
-#ifndef NDEBUG
-#define SH4_ASSEMBLER_TRACING
-#endif
-
-namespace JSC {
-typedef uint16_t SH4Word;
-
-enum {
- INVALID_OPCODE = 0xffff,
- ADD_OPCODE = 0x300c,
- ADDIMM_OPCODE = 0x7000,
- ADDC_OPCODE = 0x300e,
- ADDV_OPCODE = 0x300f,
- AND_OPCODE = 0x2009,
- ANDIMM_OPCODE = 0xc900,
- DIV0_OPCODE = 0x2007,
- DIV1_OPCODE = 0x3004,
- BF_OPCODE = 0x8b00,
- BFS_OPCODE = 0x8f00,
- BRA_OPCODE = 0xa000,
- BRAF_OPCODE = 0x0023,
- NOP_OPCODE = 0x0009,
- BSR_OPCODE = 0xb000,
- RTS_OPCODE = 0x000b,
- BT_OPCODE = 0x8900,
- BTS_OPCODE = 0x8d00,
- BSRF_OPCODE = 0x0003,
- BRK_OPCODE = 0x003b,
- FTRC_OPCODE = 0xf03d,
- CMPEQ_OPCODE = 0x3000,
- CMPEQIMM_OPCODE = 0x8800,
- CMPGE_OPCODE = 0x3003,
- CMPGT_OPCODE = 0x3007,
- CMPHI_OPCODE = 0x3006,
- CMPHS_OPCODE = 0x3002,
- CMPPL_OPCODE = 0x4015,
- CMPPZ_OPCODE = 0x4011,
- CMPSTR_OPCODE = 0x200c,
- DT_OPCODE = 0x4010,
- FCMPEQ_OPCODE = 0xf004,
- FCMPGT_OPCODE = 0xf005,
- FMOV_OPCODE = 0xf00c,
- FADD_OPCODE = 0xf000,
- FMUL_OPCODE = 0xf002,
- FSUB_OPCODE = 0xf001,
- FDIV_OPCODE = 0xf003,
- FNEG_OPCODE = 0xf04d,
- JMP_OPCODE = 0x402b,
- JSR_OPCODE = 0x400b,
- LDSPR_OPCODE = 0x402a,
- LDSLPR_OPCODE = 0x4026,
- MOV_OPCODE = 0x6003,
- MOVIMM_OPCODE = 0xe000,
- MOVB_WRITE_RN_OPCODE = 0x2000,
- MOVB_WRITE_RNDEC_OPCODE = 0x2004,
- MOVB_WRITE_R0RN_OPCODE = 0x0004,
- MOVB_WRITE_OFFGBR_OPCODE = 0xc000,
- MOVB_WRITE_OFFRN_OPCODE = 0x8000,
- MOVB_READ_RM_OPCODE = 0x6000,
- MOVB_READ_RMINC_OPCODE = 0x6004,
- MOVB_READ_R0RM_OPCODE = 0x000c,
- MOVB_READ_OFFGBR_OPCODE = 0xc400,
- MOVB_READ_OFFRM_OPCODE = 0x8400,
- MOVL_WRITE_RN_OPCODE = 0x2002,
- MOVL_WRITE_RNDEC_OPCODE = 0x2006,
- MOVL_WRITE_R0RN_OPCODE = 0x0006,
- MOVL_WRITE_OFFGBR_OPCODE = 0xc200,
- MOVL_WRITE_OFFRN_OPCODE = 0x1000,
- MOVL_READ_RM_OPCODE = 0x6002,
- MOVL_READ_RMINC_OPCODE = 0x6006,
- MOVL_READ_R0RM_OPCODE = 0x000e,
- MOVL_READ_OFFGBR_OPCODE = 0xc600,
- MOVL_READ_OFFPC_OPCODE = 0xd000,
- MOVL_READ_OFFRM_OPCODE = 0x5000,
- MOVW_WRITE_RN_OPCODE = 0x2001,
- MOVW_READ_RM_OPCODE = 0x6001,
- MOVW_READ_R0RM_OPCODE = 0x000d,
- MOVW_READ_OFFRM_OPCODE = 0x8500,
- MOVW_READ_OFFPC_OPCODE = 0x9000,
- MOVA_READ_OFFPC_OPCODE = 0xc700,
- MOVT_OPCODE = 0x0029,
- MULL_OPCODE = 0x0007,
- DMULL_L_OPCODE = 0x3005,
- STSMACL_OPCODE = 0x001a,
- STSMACH_OPCODE = 0x000a,
- DMULSL_OPCODE = 0x300d,
- NEG_OPCODE = 0x600b,
- NEGC_OPCODE = 0x600a,
- NOT_OPCODE = 0x6007,
- OR_OPCODE = 0x200b,
- ORIMM_OPCODE = 0xcb00,
- ORBIMM_OPCODE = 0xcf00,
- SETS_OPCODE = 0x0058,
- SETT_OPCODE = 0x0018,
- SHAD_OPCODE = 0x400c,
- SHAL_OPCODE = 0x4020,
- SHAR_OPCODE = 0x4021,
- SHLD_OPCODE = 0x400d,
- SHLL_OPCODE = 0x4000,
- SHLL2_OPCODE = 0x4008,
- SHLL8_OPCODE = 0x4018,
- SHLL16_OPCODE = 0x4028,
- SHLR_OPCODE = 0x4001,
- SHLR2_OPCODE = 0x4009,
- SHLR8_OPCODE = 0x4019,
- SHLR16_OPCODE = 0x4029,
- STSPR_OPCODE = 0x002a,
- STSLPR_OPCODE = 0x4022,
- FLOAT_OPCODE = 0xf02d,
- SUB_OPCODE = 0x3008,
- SUBC_OPCODE = 0x300a,
- SUBV_OPCODE = 0x300b,
- TST_OPCODE = 0x2008,
- TSTIMM_OPCODE = 0xc800,
- TSTB_OPCODE = 0xcc00,
- EXTUB_OPCODE = 0x600c,
- EXTUW_OPCODE = 0x600d,
- XOR_OPCODE = 0x200a,
- XORIMM_OPCODE = 0xca00,
- XORB_OPCODE = 0xce00,
- FMOVS_READ_RM_INC_OPCODE = 0xf009,
- FMOVS_READ_RM_OPCODE = 0xf008,
- FMOVS_READ_R0RM_OPCODE = 0xf006,
- FMOVS_WRITE_RN_OPCODE = 0xf00a,
- FMOVS_WRITE_RN_DEC_OPCODE = 0xf00b,
- FMOVS_WRITE_R0RN_OPCODE = 0xf007,
- FCNVDS_DRM_FPUL_OPCODE = 0xf0bd,
- FCNVSD_FPUL_DRN_OPCODE = 0xf0ad,
- LDS_RM_FPUL_OPCODE = 0x405a,
- FLDS_FRM_FPUL_OPCODE = 0xf01d,
- STS_FPUL_RN_OPCODE = 0x005a,
- FSTS_FPUL_FRN_OPCODE = 0xF00d,
- LDSFPSCR_OPCODE = 0x406a,
- STSFPSCR_OPCODE = 0x006a,
- LDSRMFPUL_OPCODE = 0x405a,
- FSTSFPULFRN_OPCODE = 0xf00d,
- FSQRT_OPCODE = 0xf06d,
- FSCHG_OPCODE = 0xf3fd,
- CLRT_OPCODE = 8,
-};
-
-namespace SH4Registers {
-typedef enum {
- r0,
- r1,
- r2,
- r3,
- r4,
- r5,
- r6,
- r7,
- r8,
- r9,
- r10,
- r11,
- r12,
- r13,
- r14, fp = r14,
- r15, sp = r15,
- pc,
- pr,
-} RegisterID;
-
-typedef enum {
- fr0, dr0 = fr0,
- fr1,
- fr2, dr2 = fr2,
- fr3,
- fr4, dr4 = fr4,
- fr5,
- fr6, dr6 = fr6,
- fr7,
- fr8, dr8 = fr8,
- fr9,
- fr10, dr10 = fr10,
- fr11,
- fr12, dr12 = fr12,
- fr13,
- fr14, dr14 = fr14,
- fr15,
-} FPRegisterID;
-}
-
-inline uint16_t getOpcodeGroup1(uint16_t opc, int rm, int rn)
-{
- return (opc | ((rm & 0xf) << 8) | ((rn & 0xf) << 4));
-}
-
-inline uint16_t getOpcodeGroup2(uint16_t opc, int rm)
-{
- return (opc | ((rm & 0xf) << 8));
-}
-
-inline uint16_t getOpcodeGroup3(uint16_t opc, int rm, int rn)
-{
- return (opc | ((rm & 0xf) << 8) | (rn & 0xff));
-}
-
-inline uint16_t getOpcodeGroup4(uint16_t opc, int rm, int rn, int offset)
-{
- return (opc | ((rm & 0xf) << 8) | ((rn & 0xf) << 4) | (offset & 0xf));
-}
-
-inline uint16_t getOpcodeGroup5(uint16_t opc, int rm)
-{
- return (opc | (rm & 0xff));
-}
-
-inline uint16_t getOpcodeGroup6(uint16_t opc, int rm)
-{
- return (opc | (rm & 0xfff));
-}
-
-inline uint16_t getOpcodeGroup7(uint16_t opc, int rm)
-{
- return (opc | ((rm & 0x7) << 9));
-}
-
-inline uint16_t getOpcodeGroup8(uint16_t opc, int rm, int rn)
-{
- return (opc | ((rm & 0x7) << 9) | ((rn & 0x7) << 5));
-}
-
-inline uint16_t getOpcodeGroup9(uint16_t opc, int rm, int rn)
-{
- return (opc | ((rm & 0xf) << 8) | ((rn & 0x7) << 5));
-}
-
-inline uint16_t getOpcodeGroup10(uint16_t opc, int rm, int rn)
-{
- return (opc | ((rm & 0x7) << 9) | ((rn & 0xf) << 4));
-}
-
-inline uint16_t getOpcodeGroup11(uint16_t opc, int rm, int rn)
-{
- return (opc | ((rm & 0xf) << 4) | (rn & 0xf));
-}
-
-inline uint16_t getRn(uint16_t x)
-{
- return ((x & 0xf00) >> 8);
-}
-
-inline uint16_t getRm(uint16_t x)
-{
- return ((x & 0xf0) >> 4);
-}
-
-inline uint16_t getDisp(uint16_t x)
-{
- return (x & 0xf);
-}
-
-inline uint16_t getImm8(uint16_t x)
-{
- return (x & 0xff);
-}
-
-inline uint16_t getImm12(uint16_t x)
-{
- return (x & 0xfff);
-}
-
-inline uint16_t getDRn(uint16_t x)
-{
- return ((x & 0xe00) >> 9);
-}
-
-inline uint16_t getDRm(uint16_t x)
-{
- return ((x & 0xe0) >> 5);
-}
-
-class SH4Assembler {
-public:
- typedef SH4Registers::RegisterID RegisterID;
- typedef SH4Registers::FPRegisterID FPRegisterID;
- typedef AssemblerBufferWithConstantPool<512, 4, 2, SH4Assembler> SH4Buffer;
- static const RegisterID scratchReg1 = SH4Registers::r3;
- static const RegisterID scratchReg2 = SH4Registers::r11;
- static const uint32_t maxInstructionSize = 16;
-
- enum {
- padForAlign8 = 0x00,
- padForAlign16 = 0x0009,
- padForAlign32 = 0x00090009,
- };
-
- enum JumpType {
- JumpFar,
- JumpNear
- };
-
- SH4Assembler()
- {
- m_claimscratchReg = 0x0;
- }
-
- // SH4 condition codes
- typedef enum {
- EQ = 0x0, // Equal
- NE = 0x1, // Not Equal
- HS = 0x2, // Unsigend Greater Than equal
- HI = 0x3, // Unsigend Greater Than
- LS = 0x4, // Unsigend Lower or Same
- LI = 0x5, // Unsigend Lower
- GE = 0x6, // Greater or Equal
- LT = 0x7, // Less Than
- GT = 0x8, // Greater Than
- LE = 0x9, // Less or Equal
- OF = 0xa, // OverFlow
- SI = 0xb, // Signed
- EQU= 0xc, // Equal or unordered(NaN)
- NEU= 0xd,
- GTU= 0xe,
- GEU= 0xf,
- LTU= 0x10,
- LEU= 0x11,
- } Condition;
-
- // Opaque label types
-public:
- bool isImmediate(int constant)
- {
- return ((constant <= 127) && (constant >= -128));
- }
-
- RegisterID claimScratch()
- {
- ASSERT((m_claimscratchReg != 0x3));
-
- if (!(m_claimscratchReg & 0x1)) {
- m_claimscratchReg = (m_claimscratchReg | 0x1);
- return scratchReg1;
- }
-
- m_claimscratchReg = (m_claimscratchReg | 0x2);
- return scratchReg2;
- }
-
- void releaseScratch(RegisterID scratchR)
- {
- if (scratchR == scratchReg1)
- m_claimscratchReg = (m_claimscratchReg & 0x2);
- else
- m_claimscratchReg = (m_claimscratchReg & 0x1);
- }
-
- // Stack operations
-
- void pushReg(RegisterID reg)
- {
- if (reg == SH4Registers::pr) {
- oneShortOp(getOpcodeGroup2(STSLPR_OPCODE, SH4Registers::sp));
- return;
- }
-
- oneShortOp(getOpcodeGroup1(MOVL_WRITE_RNDEC_OPCODE, SH4Registers::sp, reg));
- }
-
- void popReg(RegisterID reg)
- {
- if (reg == SH4Registers::pr) {
- oneShortOp(getOpcodeGroup2(LDSLPR_OPCODE, SH4Registers::sp));
- return;
- }
-
- oneShortOp(getOpcodeGroup1(MOVL_READ_RMINC_OPCODE, reg, SH4Registers::sp));
- }
-
- void movt(RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup2(MOVT_OPCODE, dst);
- oneShortOp(opc);
- }
-
- // Arithmetic operations
-
- void addlRegReg(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(ADD_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void addclRegReg(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(ADDC_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void addvlRegReg(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(ADDV_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void addlImm8r(int imm8, RegisterID dst)
- {
- ASSERT((imm8 <= 127) && (imm8 >= -128));
-
- uint16_t opc = getOpcodeGroup3(ADDIMM_OPCODE, dst, imm8);
- oneShortOp(opc);
- }
-
- void andlRegReg(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(AND_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void andlImm8r(int imm8, RegisterID dst)
- {
- ASSERT((imm8 <= 255) && (imm8 >= 0));
- ASSERT(dst == SH4Registers::r0);
-
- uint16_t opc = getOpcodeGroup5(ANDIMM_OPCODE, imm8);
- oneShortOp(opc);
- }
-
- void div1lRegReg(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(DIV1_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void div0lRegReg(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(DIV0_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void notlReg(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(NOT_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void orlRegReg(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(OR_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void orlImm8r(int imm8, RegisterID dst)
- {
- ASSERT((imm8 <= 255) && (imm8 >= 0));
- ASSERT(dst == SH4Registers::r0);
-
- uint16_t opc = getOpcodeGroup5(ORIMM_OPCODE, imm8);
- oneShortOp(opc);
- }
-
- void sublRegReg(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(SUB_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void subvlRegReg(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(SUBV_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void xorlRegReg(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(XOR_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void xorlImm8r(int imm8, RegisterID dst)
- {
- ASSERT((imm8 <= 255) && (imm8 >= 0));
- ASSERT(dst == SH4Registers::r0);
-
- uint16_t opc = getOpcodeGroup5(XORIMM_OPCODE, imm8);
- oneShortOp(opc);
- }
-
- void shllImm8r(int imm, RegisterID dst)
- {
- switch (imm) {
- case 1:
- oneShortOp(getOpcodeGroup2(SHLL_OPCODE, dst));
- break;
- case 2:
- oneShortOp(getOpcodeGroup2(SHLL2_OPCODE, dst));
- break;
- case 8:
- oneShortOp(getOpcodeGroup2(SHLL8_OPCODE, dst));
- break;
- case 16:
- oneShortOp(getOpcodeGroup2(SHLL16_OPCODE, dst));
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- }
- }
-
- void neg(RegisterID dst, RegisterID src)
- {
- uint16_t opc = getOpcodeGroup1(NEG_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void shllRegReg(RegisterID dst, RegisterID rShift)
- {
- uint16_t opc = getOpcodeGroup1(SHLD_OPCODE, dst, rShift);
- oneShortOp(opc);
- }
-
- void shlrRegReg(RegisterID dst, RegisterID rShift)
- {
- neg(rShift, rShift);
- shllRegReg(dst, rShift);
- }
-
- void sharRegReg(RegisterID dst, RegisterID rShift)
- {
- neg(rShift, rShift);
- shaRegReg(dst, rShift);
- }
-
- void shaRegReg(RegisterID dst, RegisterID rShift)
- {
- uint16_t opc = getOpcodeGroup1(SHAD_OPCODE, dst, rShift);
- oneShortOp(opc);
- }
-
- void shlrImm8r(int imm, RegisterID dst)
- {
- switch (imm) {
- case 1:
- oneShortOp(getOpcodeGroup2(SHLR_OPCODE, dst));
- break;
- case 2:
- oneShortOp(getOpcodeGroup2(SHLR2_OPCODE, dst));
- break;
- case 8:
- oneShortOp(getOpcodeGroup2(SHLR8_OPCODE, dst));
- break;
- case 16:
- oneShortOp(getOpcodeGroup2(SHLR16_OPCODE, dst));
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- }
- }
-
- void imullRegReg(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(MULL_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void dmullRegReg(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(DMULL_L_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void dmulslRegReg(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(DMULSL_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void stsmacl(RegisterID reg)
- {
- uint16_t opc = getOpcodeGroup2(STSMACL_OPCODE, reg);
- oneShortOp(opc);
- }
-
- void stsmach(RegisterID reg)
- {
- uint16_t opc = getOpcodeGroup2(STSMACH_OPCODE, reg);
- oneShortOp(opc);
- }
-
- // Comparisons
-
- void cmplRegReg(RegisterID left, RegisterID right, Condition cond)
- {
- switch (cond) {
- case NE:
- oneShortOp(getOpcodeGroup1(CMPEQ_OPCODE, right, left));
- break;
- case GT:
- oneShortOp(getOpcodeGroup1(CMPGT_OPCODE, right, left));
- break;
- case EQ:
- oneShortOp(getOpcodeGroup1(CMPEQ_OPCODE, right, left));
- break;
- case GE:
- oneShortOp(getOpcodeGroup1(CMPGE_OPCODE, right, left));
- break;
- case HS:
- oneShortOp(getOpcodeGroup1(CMPHS_OPCODE, right, left));
- break;
- case HI:
- oneShortOp(getOpcodeGroup1(CMPHI_OPCODE, right, left));
- break;
- case LI:
- oneShortOp(getOpcodeGroup1(CMPHI_OPCODE, left, right));
- break;
- case LS:
- oneShortOp(getOpcodeGroup1(CMPHS_OPCODE, left, right));
- break;
- case LE:
- oneShortOp(getOpcodeGroup1(CMPGE_OPCODE, left, right));
- break;
- case LT:
- oneShortOp(getOpcodeGroup1(CMPGT_OPCODE, left, right));
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- }
- }
-
- void cmppl(RegisterID reg)
- {
- uint16_t opc = getOpcodeGroup2(CMPPL_OPCODE, reg);
- oneShortOp(opc);
- }
-
- void cmppz(RegisterID reg)
- {
- uint16_t opc = getOpcodeGroup2(CMPPZ_OPCODE, reg);
- oneShortOp(opc);
- }
-
- void cmpEqImmR0(int imm, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup5(CMPEQIMM_OPCODE, imm);
- oneShortOp(opc);
- }
-
- void testlRegReg(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(TST_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void testlImm8r(int imm, RegisterID dst)
- {
- ASSERT((dst == SH4Registers::r0) && (imm <= 255) && (imm >= 0));
-
- uint16_t opc = getOpcodeGroup5(TSTIMM_OPCODE, imm);
- oneShortOp(opc);
- }
-
- void nop()
- {
- oneShortOp(NOP_OPCODE, false);
- }
-
- void sett()
- {
- oneShortOp(SETT_OPCODE);
- }
-
- void clrt()
- {
- oneShortOp(CLRT_OPCODE);
- }
-
- void fschg()
- {
- oneShortOp(FSCHG_OPCODE);
- }
-
- void bkpt()
- {
- oneShortOp(BRK_OPCODE, false);
- }
-
- void branch(uint16_t opc, int label)
- {
- switch (opc) {
- case BT_OPCODE:
- ASSERT((label <= 127) && (label >= -128));
- oneShortOp(getOpcodeGroup5(BT_OPCODE, label));
- break;
- case BRA_OPCODE:
- ASSERT((label <= 2047) && (label >= -2048));
- oneShortOp(getOpcodeGroup6(BRA_OPCODE, label));
- break;
- case BF_OPCODE:
- ASSERT((label <= 127) && (label >= -128));
- oneShortOp(getOpcodeGroup5(BF_OPCODE, label));
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- }
- }
-
- void branch(uint16_t opc, RegisterID reg)
- {
- switch (opc) {
- case BRAF_OPCODE:
- oneShortOp(getOpcodeGroup2(BRAF_OPCODE, reg));
- break;
- case JMP_OPCODE:
- oneShortOp(getOpcodeGroup2(JMP_OPCODE, reg));
- break;
- case JSR_OPCODE:
- oneShortOp(getOpcodeGroup2(JSR_OPCODE, reg));
- break;
- case BSRF_OPCODE:
- oneShortOp(getOpcodeGroup2(BSRF_OPCODE, reg));
- break;
- default:
- RELEASE_ASSERT_NOT_REACHED();
- }
- }
-
- void ldspr(RegisterID reg)
- {
- uint16_t opc = getOpcodeGroup2(LDSPR_OPCODE, reg);
- oneShortOp(opc);
- }
-
- void stspr(RegisterID reg)
- {
- uint16_t opc = getOpcodeGroup2(STSPR_OPCODE, reg);
- oneShortOp(opc);
- }
-
- void extub(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(EXTUB_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void extuw(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(EXTUW_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- // float operations
-
- void ldsrmfpul(RegisterID src)
- {
- uint16_t opc = getOpcodeGroup2(LDS_RM_FPUL_OPCODE, src);
- oneShortOp(opc);
- }
-
- void fneg(FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup2(FNEG_OPCODE, dst);
- oneShortOp(opc, true, false);
- }
-
- void fsqrt(FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup2(FSQRT_OPCODE, dst);
- oneShortOp(opc, true, false);
- }
-
- void stsfpulReg(RegisterID src)
- {
- uint16_t opc = getOpcodeGroup2(STS_FPUL_RN_OPCODE, src);
- oneShortOp(opc);
- }
-
- void floatfpulfrn(FPRegisterID src)
- {
- uint16_t opc = getOpcodeGroup2(FLOAT_OPCODE, src);
- oneShortOp(opc, true, false);
- }
-
- void fmull(FPRegisterID src, FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(FMUL_OPCODE, dst, src);
- oneShortOp(opc, true, false);
- }
-
- void fmovsReadrm(RegisterID src, FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(FMOVS_READ_RM_OPCODE, dst, src);
- oneShortOp(opc, true, false);
- }
-
- void fmovsWriterm(FPRegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_RN_OPCODE, dst, src);
- oneShortOp(opc, true, false);
- }
-
- void fmovsWriter0r(FPRegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_R0RN_OPCODE, dst, src);
- oneShortOp(opc, true, false);
- }
-
- void fmovsReadr0r(RegisterID src, FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(FMOVS_READ_R0RM_OPCODE, dst, src);
- oneShortOp(opc, true, false);
- }
-
- void fmovsReadrminc(RegisterID src, FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(FMOVS_READ_RM_INC_OPCODE, dst, src);
- oneShortOp(opc, true, false);
- }
-
- void fmovsWriterndec(FPRegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(FMOVS_WRITE_RN_DEC_OPCODE, dst, src);
- oneShortOp(opc, true, false);
- }
-
- void ftrcRegfpul(FPRegisterID src)
- {
- uint16_t opc = getOpcodeGroup2(FTRC_OPCODE, src);
- oneShortOp(opc, true, false);
- }
-
- void fldsfpul(FPRegisterID src)
- {
- uint16_t opc = getOpcodeGroup2(FLDS_FRM_FPUL_OPCODE, src);
- oneShortOp(opc);
- }
-
- void fstsfpul(FPRegisterID src)
- {
- uint16_t opc = getOpcodeGroup2(FSTS_FPUL_FRN_OPCODE, src);
- oneShortOp(opc);
- }
-
- void ldsfpscr(RegisterID reg)
- {
- uint16_t opc = getOpcodeGroup2(LDSFPSCR_OPCODE, reg);
- oneShortOp(opc);
- }
-
- void stsfpscr(RegisterID reg)
- {
- uint16_t opc = getOpcodeGroup2(STSFPSCR_OPCODE, reg);
- oneShortOp(opc);
- }
-
- // double operations
-
- void dcnvds(FPRegisterID src)
- {
- uint16_t opc = getOpcodeGroup7(FCNVDS_DRM_FPUL_OPCODE, src >> 1);
- oneShortOp(opc);
- }
-
- void dcnvsd(FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup7(FCNVSD_FPUL_DRN_OPCODE, dst >> 1);
- oneShortOp(opc);
- }
-
- void dcmppeq(FPRegisterID src, FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup8(FCMPEQ_OPCODE, dst >> 1, src >> 1);
- oneShortOp(opc);
- }
-
- void dcmppgt(FPRegisterID src, FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup8(FCMPGT_OPCODE, dst >> 1, src >> 1);
- oneShortOp(opc);
- }
-
- void dmulRegReg(FPRegisterID src, FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup8(FMUL_OPCODE, dst >> 1, src >> 1);
- oneShortOp(opc);
- }
-
- void dsubRegReg(FPRegisterID src, FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup8(FSUB_OPCODE, dst >> 1, src >> 1);
- oneShortOp(opc);
- }
-
- void daddRegReg(FPRegisterID src, FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup8(FADD_OPCODE, dst >> 1, src >> 1);
- oneShortOp(opc);
- }
-
- void dmovRegReg(FPRegisterID src, FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup8(FMOV_OPCODE, dst >> 1, src >> 1);
- oneShortOp(opc);
- }
-
- void ddivRegReg(FPRegisterID src, FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup8(FDIV_OPCODE, dst >> 1, src >> 1);
- oneShortOp(opc);
- }
-
- void dsqrt(FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup7(FSQRT_OPCODE, dst >> 1);
- oneShortOp(opc);
- }
-
- void dneg(FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup7(FNEG_OPCODE, dst >> 1);
- oneShortOp(opc);
- }
-
- void fmovReadrm(RegisterID src, FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup10(FMOVS_READ_RM_OPCODE, dst >> 1, src);
- oneShortOp(opc);
- }
-
- void fmovWriterm(FPRegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_RN_OPCODE, dst, src >> 1);
- oneShortOp(opc);
- }
-
- void fmovWriter0r(FPRegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_R0RN_OPCODE, dst, src >> 1);
- oneShortOp(opc);
- }
-
- void fmovReadr0r(RegisterID src, FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup10(FMOVS_READ_R0RM_OPCODE, dst >> 1, src);
- oneShortOp(opc);
- }
-
- void fmovReadrminc(RegisterID src, FPRegisterID dst)
- {
- uint16_t opc = getOpcodeGroup10(FMOVS_READ_RM_INC_OPCODE, dst >> 1, src);
- oneShortOp(opc);
- }
-
- void fmovWriterndec(FPRegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup9(FMOVS_WRITE_RN_DEC_OPCODE, dst, src >> 1);
- oneShortOp(opc);
- }
-
- void floatfpulDreg(FPRegisterID src)
- {
- uint16_t opc = getOpcodeGroup7(FLOAT_OPCODE, src >> 1);
- oneShortOp(opc);
- }
-
- void ftrcdrmfpul(FPRegisterID src)
- {
- uint16_t opc = getOpcodeGroup7(FTRC_OPCODE, src >> 1);
- oneShortOp(opc);
- }
-
- // Various move ops
-
- void movImm8(int imm8, RegisterID dst)
- {
- ASSERT((imm8 <= 127) && (imm8 >= -128));
-
- uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, imm8);
- oneShortOp(opc);
- }
-
- void movlRegReg(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(MOV_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void movwRegMem(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(MOVW_WRITE_RN_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void movwMemReg(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(MOVW_READ_RM_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void movwPCReg(int offset, RegisterID base, RegisterID dst)
- {
- ASSERT(base == SH4Registers::pc);
- ASSERT((offset <= 255) && (offset >= 0));
-
- uint16_t opc = getOpcodeGroup3(MOVW_READ_OFFPC_OPCODE, dst, offset);
- oneShortOp(opc);
- }
-
- void movwMemReg(int offset, RegisterID base, RegisterID dst)
- {
- ASSERT(dst == SH4Registers::r0);
-
- uint16_t opc = getOpcodeGroup11(MOVW_READ_OFFRM_OPCODE, base, offset);
- oneShortOp(opc);
- }
-
- void movwR0mr(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(MOVW_READ_R0RM_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void movlRegMem(RegisterID src, int offset, RegisterID base)
- {
- ASSERT((offset <= 15) && (offset >= 0));
-
- if (!offset) {
- oneShortOp(getOpcodeGroup1(MOVL_WRITE_RN_OPCODE, base, src));
- return;
- }
-
- oneShortOp(getOpcodeGroup4(MOVL_WRITE_OFFRN_OPCODE, base, src, offset));
- }
-
- void movlRegMem(RegisterID src, RegisterID base)
- {
- uint16_t opc = getOpcodeGroup1(MOVL_WRITE_RN_OPCODE, base, src);
- oneShortOp(opc);
- }
-
- void movlMemReg(int offset, RegisterID base, RegisterID dst)
- {
- if (base == SH4Registers::pc) {
- ASSERT((offset <= 255) && (offset >= 0));
- oneShortOp(getOpcodeGroup3(MOVL_READ_OFFPC_OPCODE, dst, offset));
- return;
- }
-
- ASSERT((offset <= 15) && (offset >= 0));
- if (!offset) {
- oneShortOp(getOpcodeGroup1(MOVL_READ_RM_OPCODE, dst, base));
- return;
- }
-
- oneShortOp(getOpcodeGroup4(MOVL_READ_OFFRM_OPCODE, dst, base, offset));
- }
-
- void movlMemRegCompact(int offset, RegisterID base, RegisterID dst)
- {
- oneShortOp(getOpcodeGroup4(MOVL_READ_OFFRM_OPCODE, dst, base, offset));
- }
-
- void movbRegMem(RegisterID src, RegisterID base)
- {
- uint16_t opc = getOpcodeGroup1(MOVB_WRITE_RN_OPCODE, base, src);
- oneShortOp(opc);
- }
-
- void movbMemReg(int offset, RegisterID base, RegisterID dst)
- {
- ASSERT(dst == SH4Registers::r0);
-
- uint16_t opc = getOpcodeGroup11(MOVB_READ_OFFRM_OPCODE, base, offset);
- oneShortOp(opc);
- }
-
- void movbR0mr(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(MOVB_READ_R0RM_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void movbMemReg(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(MOVB_READ_RM_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void movlMemReg(RegisterID base, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(MOVL_READ_RM_OPCODE, dst, base);
- oneShortOp(opc);
- }
-
- void movlMemRegIn(RegisterID base, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(MOVL_READ_RMINC_OPCODE, dst, base);
- oneShortOp(opc);
- }
-
- void movlR0mr(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(MOVL_READ_R0RM_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void movlRegMemr0(RegisterID src, RegisterID dst)
- {
- uint16_t opc = getOpcodeGroup1(MOVL_WRITE_R0RN_OPCODE, dst, src);
- oneShortOp(opc);
- }
-
- void movlImm8r(int imm8, RegisterID dst)
- {
- ASSERT((imm8 <= 127) && (imm8 >= -128));
-
- uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, imm8);
- oneShortOp(opc);
- }
-
- void loadConstant(uint32_t constant, RegisterID dst)
- {
- if (((int)constant <= 0x7f) && ((int)constant >= -0x80)) {
- movImm8(constant, dst);
- return;
- }
-
- uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, 0);
-
- m_buffer.ensureSpace(maxInstructionSize, sizeof(uint32_t));
- printInstr(getOpcodeGroup3(MOVIMM_OPCODE, dst, constant), m_buffer.codeSize());
- m_buffer.putShortWithConstantInt(opc, constant, true);
- }
-
- void loadConstantUnReusable(uint32_t constant, RegisterID dst, bool ensureSpace = false)
- {
- uint16_t opc = getOpcodeGroup3(MOVIMM_OPCODE, dst, 0);
-
- if (ensureSpace)
- m_buffer.ensureSpace(maxInstructionSize, sizeof(uint32_t));
-
- printInstr(getOpcodeGroup3(MOVIMM_OPCODE, dst, constant), m_buffer.codeSize());
- m_buffer.putShortWithConstantInt(opc, constant);
- }
-
- // Flow control
-
- AssemblerLabel call()
- {
- RegisterID scr = claimScratch();
- m_buffer.ensureSpace(maxInstructionSize + 4, sizeof(uint32_t));
- loadConstantUnReusable(0x0, scr);
- branch(JSR_OPCODE, scr);
- nop();
- releaseScratch(scr);
- return m_buffer.label();
- }
-
- AssemblerLabel call(RegisterID dst)
- {
- m_buffer.ensureSpace(maxInstructionSize + 2);
- branch(JSR_OPCODE, dst);
- nop();
- return m_buffer.label();
- }
-
- AssemblerLabel jmp()
- {
- RegisterID scr = claimScratch();
- m_buffer.ensureSpace(maxInstructionSize + 4, sizeof(uint32_t));
- AssemblerLabel label = m_buffer.label();
- loadConstantUnReusable(0x0, scr);
- branch(BRAF_OPCODE, scr);
- nop();
- releaseScratch(scr);
- return label;
- }
-
- void extraInstrForBranch(RegisterID dst)
- {
- loadConstantUnReusable(0x0, dst);
- nop();
- nop();
- }
-
- AssemblerLabel jmp(RegisterID dst)
- {
- jmpReg(dst);
- return m_buffer.label();
- }
-
- void jmpReg(RegisterID dst)
- {
- m_buffer.ensureSpace(maxInstructionSize + 2);
- branch(JMP_OPCODE, dst);
- nop();
- }
-
- AssemblerLabel jne()
- {
- AssemblerLabel label = m_buffer.label();
- branch(BF_OPCODE, 0);
- return label;
- }
-
- AssemblerLabel je()
- {
- AssemblerLabel label = m_buffer.label();
- branch(BT_OPCODE, 0);
- return label;
- }
-
- AssemblerLabel bra()
- {
- AssemblerLabel label = m_buffer.label();
- branch(BRA_OPCODE, 0);
- return label;
- }
-
- void ret()
- {
- m_buffer.ensureSpace(maxInstructionSize + 2);
- oneShortOp(RTS_OPCODE, false);
- }
-
- AssemblerLabel labelIgnoringWatchpoints()
- {
- m_buffer.ensureSpaceForAnyInstruction();
- return m_buffer.label();
- }
-
- AssemblerLabel label()
- {
- m_buffer.ensureSpaceForAnyInstruction();
- return m_buffer.label();
- }
-
- int sizeOfConstantPool()
- {
- return m_buffer.sizeOfConstantPool();
- }
-
- AssemblerLabel align(int alignment)
- {
- m_buffer.ensureSpace(maxInstructionSize + 2);
- while (!m_buffer.isAligned(alignment)) {
- nop();
- m_buffer.ensureSpace(maxInstructionSize + 2);
- }
- return label();
- }
-
- static void changePCrelativeAddress(int offset, uint16_t* instructionPtr, uint32_t newAddress)
- {
- uint32_t address = (offset << 2) + ((reinterpret_cast<uint32_t>(instructionPtr) + 4) &(~0x3));
- *reinterpret_cast<uint32_t*>(address) = newAddress;
- }
-
- static uint32_t readPCrelativeAddress(int offset, uint16_t* instructionPtr)
- {
- uint32_t address = (offset << 2) + ((reinterpret_cast<uint32_t>(instructionPtr) + 4) &(~0x3));
- return *reinterpret_cast<uint32_t*>(address);
- }
-
- static uint16_t* getInstructionPtr(void* code, int offset)
- {
- return reinterpret_cast<uint16_t*> (reinterpret_cast<uint32_t>(code) + offset);
- }
-
- static void linkJump(void* code, AssemblerLabel from, void* to)
- {
- ASSERT(from.isSet());
-
- uint16_t* instructionPtr = getInstructionPtr(code, from.m_offset);
- uint16_t instruction = *instructionPtr;
- int offsetBits = (reinterpret_cast<uint32_t>(to) - reinterpret_cast<uint32_t>(code)) - from.m_offset;
-
- if (((instruction & 0xff00) == BT_OPCODE) || ((instruction & 0xff00) == BF_OPCODE)) {
- /* BT label ==> BF 2
- nop LDR reg
- nop braf @reg
- nop nop
- */
- offsetBits -= 8;
- instruction ^= 0x0202;
- *instructionPtr++ = instruction;
- changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits);
- instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00));
- *instructionPtr = instruction;
- printBlockInstr(instructionPtr - 2, from.m_offset, 3);
- return;
- }
-
- /* MOV #imm, reg => LDR reg
- braf @reg braf @reg
- nop nop
- */
- ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE);
-
- offsetBits -= 4;
- if (offsetBits >= -4096 && offsetBits <= 4094) {
- *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1);
- *(++instructionPtr) = NOP_OPCODE;
- printBlockInstr(instructionPtr - 1, from.m_offset, 2);
- return;
- }
-
- changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2);
- printInstr(*instructionPtr, from.m_offset + 2);
- }
-
- static void linkCall(void* code, AssemblerLabel from, void* to)
- {
- uint16_t* instructionPtr = getInstructionPtr(code, from.m_offset);
- instructionPtr -= 3;
- changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast<uint32_t>(to));
- }
-
- static void linkPointer(void* code, AssemblerLabel where, void* value)
- {
- uint16_t* instructionPtr = getInstructionPtr(code, where.m_offset);
- changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast<uint32_t>(value));
- }
-
- static unsigned getCallReturnOffset(AssemblerLabel call)
- {
- ASSERT(call.isSet());
- return call.m_offset;
- }
-
- static uint32_t* getLdrImmAddressOnPool(SH4Word* insn, uint32_t* constPool)
- {
- return (constPool + (*insn & 0xff));
- }
-
- static SH4Word patchConstantPoolLoad(SH4Word load, int value)
- {
- return ((load & ~0xff) | value);
- }
-
- static SH4Buffer::TwoShorts placeConstantPoolBarrier(int offset)
- {
- ASSERT(((offset >> 1) <=2047) && ((offset >> 1) >= -2048));
-
- SH4Buffer::TwoShorts m_barrier;
- m_barrier.high = (BRA_OPCODE | (offset >> 1));
- m_barrier.low = NOP_OPCODE;
- printInstr(((BRA_OPCODE | (offset >> 1))), 0);
- printInstr(NOP_OPCODE, 0);
- return m_barrier;
- }
-
- static void patchConstantPoolLoad(void* loadAddr, void* constPoolAddr)
- {
- SH4Word* instructionPtr = reinterpret_cast<SH4Word*>(loadAddr);
- SH4Word instruction = *instructionPtr;
- SH4Word index = instruction & 0xff;
-
- if ((instruction & 0xf000) != MOVIMM_OPCODE)
- return;
-
- ASSERT((((reinterpret_cast<uint32_t>(constPoolAddr) - reinterpret_cast<uint32_t>(loadAddr)) + index * 4)) < 1024);
-
- int offset = reinterpret_cast<uint32_t>(constPoolAddr) + (index * 4) - ((reinterpret_cast<uint32_t>(instructionPtr) & ~0x03) + 4);
- instruction &=0xf00;
- instruction |= 0xd000;
- offset &= 0x03ff;
- instruction |= (offset >> 2);
- *instructionPtr = instruction;
- printInstr(instruction, reinterpret_cast<uint32_t>(loadAddr));
- }
-
- static void repatchPointer(void* where, void* value)
- {
- patchPointer(where, value);
- }
-
- static void* readPointer(void* code)
- {
- return reinterpret_cast<void*>(readInt32(code));
- }
-
- static void repatchInt32(void* where, int32_t value)
- {
- uint16_t* instructionPtr = reinterpret_cast<uint16_t*>(where);
- changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, value);
- }
-
- static void repatchCompact(void* where, int32_t value)
- {
- ASSERT(value >= 0);
- ASSERT(value <= 60);
- *reinterpret_cast<uint16_t*>(where) = ((*reinterpret_cast<uint16_t*>(where) & 0xfff0) | (value >> 2));
- cacheFlush(reinterpret_cast<uint16_t*>(where), sizeof(uint16_t));
- }
-
- static void relinkCall(void* from, void* to)
- {
- uint16_t* instructionPtr = reinterpret_cast<uint16_t*>(from);
- instructionPtr -= 3;
- changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, reinterpret_cast<uint32_t>(to));
- }
-
- static void relinkJump(void* from, void* to)
- {
- uint16_t* instructionPtr = reinterpret_cast<uint16_t*> (from);
- uint16_t instruction = *instructionPtr;
- int32_t offsetBits = (reinterpret_cast<uint32_t>(to) - reinterpret_cast<uint32_t>(from));
-
- if (((*instructionPtr & 0xff00) == BT_OPCODE) || ((*instructionPtr & 0xff00) == BF_OPCODE)) {
- offsetBits -= 8;
- instructionPtr++;
- changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits);
- instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00));
- *instructionPtr = instruction;
- printBlockInstr(instructionPtr, reinterpret_cast<uint32_t>(from) + 1, 3);
- return;
- }
-
- ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE);
- offsetBits -= 4;
- if (offsetBits >= -4096 && offsetBits <= 4094) {
- *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1);
- *(++instructionPtr) = NOP_OPCODE;
- printBlockInstr(instructionPtr - 2, reinterpret_cast<uint32_t>(from), 2);
- return;
- }
-
- changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2);
- printInstr(*instructionPtr, reinterpret_cast<uint32_t>(from));
- }
-
- // Linking & patching
-
- static void revertJump(void* instructionStart, SH4Word imm)
- {
- SH4Word *insn = reinterpret_cast<SH4Word*>(instructionStart);
- SH4Word disp;
-
- ASSERT((insn[0] & 0xf000) == MOVL_READ_OFFPC_OPCODE);
-
- disp = insn[0] & 0x00ff;
- insn += 2 + (disp << 1); // PC += 4 + (disp*4)
- insn = (SH4Word *) ((unsigned) insn & (~3));
- insn[0] = imm;
- cacheFlush(insn, sizeof(SH4Word));
- }
-
- void linkJump(AssemblerLabel from, AssemblerLabel to, JumpType type = JumpFar)
- {
- ASSERT(to.isSet());
- ASSERT(from.isSet());
-
- uint16_t* instructionPtr = getInstructionPtr(data(), from.m_offset);
- uint16_t instruction = *instructionPtr;
- int offsetBits;
-
- if (type == JumpNear) {
- ASSERT((instruction == BT_OPCODE) || (instruction == BF_OPCODE) || (instruction == BRA_OPCODE));
- int offset = (codeSize() - from.m_offset) - 4;
- *instructionPtr++ = instruction | (offset >> 1);
- printInstr(*instructionPtr, from.m_offset + 2);
- return;
- }
-
- if (((instruction & 0xff00) == BT_OPCODE) || ((instruction & 0xff00) == BF_OPCODE)) {
- /* BT label => BF 2
- nop LDR reg
- nop braf @reg
- nop nop
- */
- offsetBits = (to.m_offset - from.m_offset) - 8;
- instruction ^= 0x0202;
- *instructionPtr++ = instruction;
- if ((*instructionPtr & 0xf000) == 0xe000) {
- uint32_t* addr = getLdrImmAddressOnPool(instructionPtr, m_buffer.poolAddress());
- *addr = offsetBits;
- } else
- changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits);
- instruction = (BRAF_OPCODE | (*instructionPtr++ & 0xf00));
- *instructionPtr = instruction;
- printBlockInstr(instructionPtr - 2, from.m_offset, 3);
- return;
- }
-
- /* MOV # imm, reg => LDR reg
- braf @reg braf @reg
- nop nop
- */
- ASSERT((*(instructionPtr + 1) & BRAF_OPCODE) == BRAF_OPCODE);
- offsetBits = (to.m_offset - from.m_offset) - 4;
- if (offsetBits >= -4096 && offsetBits <= 4094) {
- *instructionPtr = getOpcodeGroup6(BRA_OPCODE, offsetBits >> 1);
- *(++instructionPtr) = NOP_OPCODE;
- printBlockInstr(instructionPtr - 1, from.m_offset, 2);
- return;
- }
-
- instruction = *instructionPtr;
- if ((instruction & 0xf000) == 0xe000) {
- uint32_t* addr = getLdrImmAddressOnPool(instructionPtr, m_buffer.poolAddress());
- *addr = offsetBits - 2;
- printInstr(*instructionPtr, from.m_offset + 2);
- return;
- }
-
- changePCrelativeAddress((*instructionPtr & 0xff), instructionPtr, offsetBits - 2);
- printInstr(*instructionPtr, from.m_offset + 2);
- }
-
- static void* getRelocatedAddress(void* code, AssemblerLabel label)
- {
- return reinterpret_cast<void*>(reinterpret_cast<char*>(code) + label.m_offset);
- }
-
- static int getDifferenceBetweenLabels(AssemblerLabel a, AssemblerLabel b)
- {
- return b.m_offset - a.m_offset;
- }
-
- static void patchPointer(void* code, AssemblerLabel where, void* value)
- {
- patchPointer(reinterpret_cast<uint32_t*>(code) + where.m_offset, value);
- }
-
- static void patchPointer(void* code, void* value)
- {
- patchInt32(code, reinterpret_cast<uint32_t>(value));
- }
-
- static void patchInt32(void* code, uint32_t value)
- {
- changePCrelativeAddress((*(reinterpret_cast<uint16_t*>(code)) & 0xff), reinterpret_cast<uint16_t*>(code), value);
- }
-
- static uint32_t readInt32(void* code)
- {
- return readPCrelativeAddress((*(reinterpret_cast<uint16_t*>(code)) & 0xff), reinterpret_cast<uint16_t*>(code));
- }
-
- static void* readCallTarget(void* from)
- {
- uint16_t* instructionPtr = static_cast<uint16_t*>(from);
- instructionPtr -= 3;
- return reinterpret_cast<void*>(readPCrelativeAddress((*instructionPtr & 0xff), instructionPtr));
- }
-
- PassRefPtr<ExecutableMemoryHandle> executableCopy(JSGlobalData& globalData, void* ownerUID, JITCompilationEffort effort)
- {
- return m_buffer.executableCopy(globalData, ownerUID, effort);
- }
-
- static void cacheFlush(void* code, size_t size)
- {
-#if !OS(LINUX)
-#error "The cacheFlush support is missing on this platform."
-#elif defined CACHEFLUSH_D_L2
- syscall(__NR_cacheflush, reinterpret_cast<unsigned>(code), size, CACHEFLUSH_D_WB | CACHEFLUSH_I | CACHEFLUSH_D_L2);
-#else
- syscall(__NR_cacheflush, reinterpret_cast<unsigned>(code), size, CACHEFLUSH_D_WB | CACHEFLUSH_I);
-#endif
- }
-
- void prefix(uint16_t pre)
- {
- m_buffer.putByte(pre);
- }
-
- void oneShortOp(uint16_t opcode, bool checksize = true, bool isDouble = true)
- {
- printInstr(opcode, m_buffer.codeSize(), isDouble);
- if (checksize)
- m_buffer.ensureSpace(maxInstructionSize);
- m_buffer.putShortUnchecked(opcode);
- }
-
- void ensureSpace(int space)
- {
- m_buffer.ensureSpace(space);
- }
-
- void ensureSpace(int insnSpace, int constSpace)
- {
- m_buffer.ensureSpace(insnSpace, constSpace);
- }
-
- // Administrative methods
-
- void* data() const { return m_buffer.data(); }
- size_t codeSize() const { return m_buffer.codeSize(); }
-
-#ifdef SH4_ASSEMBLER_TRACING
- static void printInstr(uint16_t opc, unsigned size, bool isdoubleInst = true)
- {
- if (!getenv("JavaScriptCoreDumpJIT"))
- return;
-
- const char *format = 0;
- printfStdoutInstr("offset: 0x%8.8x\t", size);
- switch (opc) {
- case BRK_OPCODE:
- format = " BRK\n";
- break;
- case NOP_OPCODE:
- format = " NOP\n";
- break;
- case RTS_OPCODE:
- format =" *RTS\n";
- break;
- case SETS_OPCODE:
- format = " SETS\n";
- break;
- case SETT_OPCODE:
- format = " SETT\n";
- break;
- case CLRT_OPCODE:
- format = " CLRT\n";
- break;
- case FSCHG_OPCODE:
- format = " FSCHG\n";
- break;
- }
- if (format) {
- printfStdoutInstr(format);
- return;
- }
- switch (opc & 0xf0ff) {
- case BRAF_OPCODE:
- format = " *BRAF R%d\n";
- break;
- case DT_OPCODE:
- format = " DT R%d\n";
- break;
- case CMPPL_OPCODE:
- format = " CMP/PL R%d\n";
- break;
- case CMPPZ_OPCODE:
- format = " CMP/PZ R%d\n";
- break;
- case JMP_OPCODE:
- format = " *JMP @R%d\n";
- break;
- case JSR_OPCODE:
- format = " *JSR @R%d\n";
- break;
- case LDSPR_OPCODE:
- format = " LDS R%d, PR\n";
- break;
- case LDSLPR_OPCODE:
- format = " LDS.L @R%d+, PR\n";
- break;
- case MOVT_OPCODE:
- format = " MOVT R%d\n";
- break;
- case SHAL_OPCODE:
- format = " SHAL R%d\n";
- break;
- case SHAR_OPCODE:
- format = " SHAR R%d\n";
- break;
- case SHLL_OPCODE:
- format = " SHLL R%d\n";
- break;
- case SHLL2_OPCODE:
- format = " SHLL2 R%d\n";
- break;
- case SHLL8_OPCODE:
- format = " SHLL8 R%d\n";
- break;
- case SHLL16_OPCODE:
- format = " SHLL16 R%d\n";
- break;
- case SHLR_OPCODE:
- format = " SHLR R%d\n";
- break;
- case SHLR2_OPCODE:
- format = " SHLR2 R%d\n";
- break;
- case SHLR8_OPCODE:
- format = " SHLR8 R%d\n";
- break;
- case SHLR16_OPCODE:
- format = " SHLR16 R%d\n";
- break;
- case STSPR_OPCODE:
- format = " STS PR, R%d\n";
- break;
- case STSLPR_OPCODE:
- format = " STS.L PR, @-R%d\n";
- break;
- case LDS_RM_FPUL_OPCODE:
- format = " LDS R%d, FPUL\n";
- break;
- case STS_FPUL_RN_OPCODE:
- format = " STS FPUL, R%d \n";
- break;
- case FLDS_FRM_FPUL_OPCODE:
- format = " FLDS FR%d, FPUL\n";
- break;
- case FSTS_FPUL_FRN_OPCODE:
- format = " FSTS FPUL, R%d \n";
- break;
- case LDSFPSCR_OPCODE:
- format = " LDS R%d, FPSCR \n";
- break;
- case STSFPSCR_OPCODE:
- format = " STS FPSCR, R%d \n";
- break;
- case STSMACL_OPCODE:
- format = " STS MACL, R%d \n";
- break;
- case STSMACH_OPCODE:
- format = " STS MACH, R%d \n";
- break;
- case BSRF_OPCODE:
- format = " *BSRF R%d";
- break;
- case FTRC_OPCODE:
- format = " FTRC FR%d, FPUL\n";
- break;
- }
- if (format) {
- printfStdoutInstr(format, getRn(opc));
- return;
- }
- switch (opc & 0xf0ff) {
- case FNEG_OPCODE:
- format = " FNEG DR%d\n";
- break;
- case FLOAT_OPCODE:
- format = " FLOAT DR%d\n";
- break;
- case FTRC_OPCODE:
- format = " FTRC FR%d, FPUL\n";
- break;
- case FSQRT_OPCODE:
- format = " FSQRT FR%d\n";
- break;
- case FCNVDS_DRM_FPUL_OPCODE:
- format = " FCNVDS FR%d, FPUL\n";
- break;
- case FCNVSD_FPUL_DRN_OPCODE:
- format = " FCNVSD FPUL, FR%d\n";
- break;
- }
- if (format) {
- if (isdoubleInst)
- printfStdoutInstr(format, getDRn(opc) << 1);
- else
- printfStdoutInstr(format, getRn(opc));
- return;
- }
- switch (opc & 0xf00f) {
- case ADD_OPCODE:
- format = " ADD R%d, R%d\n";
- break;
- case ADDC_OPCODE:
- format = " ADDC R%d, R%d\n";
- break;
- case ADDV_OPCODE:
- format = " ADDV R%d, R%d\n";
- break;
- case AND_OPCODE:
- format = " AND R%d, R%d\n";
- break;
- case DIV1_OPCODE:
- format = " DIV1 R%d, R%d\n";
- break;
- case CMPEQ_OPCODE:
- format = " CMP/EQ R%d, R%d\n";
- break;
- case CMPGE_OPCODE:
- format = " CMP/GE R%d, R%d\n";
- break;
- case CMPGT_OPCODE:
- format = " CMP/GT R%d, R%d\n";
- break;
- case CMPHI_OPCODE:
- format = " CMP/HI R%d, R%d\n";
- break;
- case CMPHS_OPCODE:
- format = " CMP/HS R%d, R%d\n";
- break;
- case MOV_OPCODE:
- format = " MOV R%d, R%d\n";
- break;
- case MOVB_WRITE_RN_OPCODE:
- format = " MOV.B R%d, @R%d\n";
- break;
- case MOVB_WRITE_RNDEC_OPCODE:
- format = " MOV.B R%d, @-R%d\n";
- break;
- case MOVB_WRITE_R0RN_OPCODE:
- format = " MOV.B R%d, @(R0, R%d)\n";
- break;
- case MOVB_READ_RM_OPCODE:
- format = " MOV.B @R%d, R%d\n";
- break;
- case MOVB_READ_RMINC_OPCODE:
- format = " MOV.B @R%d+, R%d\n";
- break;
- case MOVB_READ_R0RM_OPCODE:
- format = " MOV.B @(R0, R%d), R%d\n";
- break;
- case MOVL_WRITE_RN_OPCODE:
- format = " MOV.L R%d, @R%d\n";
- break;
- case MOVL_WRITE_RNDEC_OPCODE:
- format = " MOV.L R%d, @-R%d\n";
- break;
- case MOVL_WRITE_R0RN_OPCODE:
- format = " MOV.L R%d, @(R0, R%d)\n";
- break;
- case MOVL_READ_RM_OPCODE:
- format = " MOV.L @R%d, R%d\n";
- break;
- case MOVL_READ_RMINC_OPCODE:
- format = " MOV.L @R%d+, R%d\n";
- break;
- case MOVL_READ_R0RM_OPCODE:
- format = " MOV.L @(R0, R%d), R%d\n";
- break;
- case MULL_OPCODE:
- format = " MUL.L R%d, R%d\n";
- break;
- case DMULL_L_OPCODE:
- format = " DMULU.L R%d, R%d\n";
- break;
- case DMULSL_OPCODE:
- format = " DMULS.L R%d, R%d\n";
- break;
- case NEG_OPCODE:
- format = " NEG R%d, R%d\n";
- break;
- case NEGC_OPCODE:
- format = " NEGC R%d, R%d\n";
- break;
- case NOT_OPCODE:
- format = " NOT R%d, R%d\n";
- break;
- case OR_OPCODE:
- format = " OR R%d, R%d\n";
- break;
- case SHAD_OPCODE:
- format = " SHAD R%d, R%d\n";
- break;
- case SHLD_OPCODE:
- format = " SHLD R%d, R%d\n";
- break;
- case SUB_OPCODE:
- format = " SUB R%d, R%d\n";
- break;
- case SUBC_OPCODE:
- format = " SUBC R%d, R%d\n";
- break;
- case SUBV_OPCODE:
- format = " SUBV R%d, R%d\n";
- break;
- case TST_OPCODE:
- format = " TST R%d, R%d\n";
- break;
- case XOR_OPCODE:
- format = " XOR R%d, R%d\n";break;
- case MOVW_WRITE_RN_OPCODE:
- format = " MOV.W R%d, @R%d\n";
- break;
- case MOVW_READ_RM_OPCODE:
- format = " MOV.W @R%d, R%d\n";
- break;
- case MOVW_READ_R0RM_OPCODE:
- format = " MOV.W @(R0, R%d), R%d\n";
- break;
- case EXTUB_OPCODE:
- format = " EXTU.B R%d, R%d\n";
- break;
- case EXTUW_OPCODE:
- format = " EXTU.W R%d, R%d\n";
- break;
- }
- if (format) {
- printfStdoutInstr(format, getRm(opc), getRn(opc));
- return;
- }
- switch (opc & 0xf00f) {
- case FSUB_OPCODE:
- format = " FSUB FR%d, FR%d\n";
- break;
- case FADD_OPCODE:
- format = " FADD FR%d, FR%d\n";
- break;
- case FDIV_OPCODE:
- format = " FDIV FR%d, FR%d\n";
- break;
- case FMUL_OPCODE:
- format = " DMULL FR%d, FR%d\n";
- break;
- case FMOV_OPCODE:
- format = " FMOV FR%d, FR%d\n";
- break;
- case FCMPEQ_OPCODE:
- format = " FCMP/EQ FR%d, FR%d\n";
- break;
- case FCMPGT_OPCODE:
- format = " FCMP/GT FR%d, FR%d\n";
- break;
- }
- if (format) {
- if (isdoubleInst)
- printfStdoutInstr(format, getDRm(opc) << 1, getDRn(opc) << 1);
- else
- printfStdoutInstr(format, getRm(opc), getRn(opc));
- return;
- }
- switch (opc & 0xf00f) {
- case FMOVS_WRITE_RN_DEC_OPCODE:
- format = " %s FR%d, @-R%d\n";
- break;
- case FMOVS_WRITE_RN_OPCODE:
- format = " %s FR%d, @R%d\n";
- break;
- case FMOVS_WRITE_R0RN_OPCODE:
- format = " %s FR%d, @(R0, R%d)\n";
- break;
- }
- if (format) {
- if (isdoubleInst)
- printfStdoutInstr(format, "FMOV", getDRm(opc) << 1, getDRn(opc));
- else
- printfStdoutInstr(format, "FMOV.S", getRm(opc), getRn(opc));
- return;
- }
- switch (opc & 0xf00f) {
- case FMOVS_READ_RM_OPCODE:
- format = " %s @R%d, FR%d\n";
- break;
- case FMOVS_READ_RM_INC_OPCODE:
- format = " %s @R%d+, FR%d\n";
- break;
- case FMOVS_READ_R0RM_OPCODE:
- format = " %s @(R0, R%d), FR%d\n";
- break;
- }
- if (format) {
- if (isdoubleInst)
- printfStdoutInstr(format, "FMOV", getDRm(opc), getDRn(opc) << 1);
- else
- printfStdoutInstr(format, "FMOV.S", getRm(opc), getRn(opc));
- return;
- }
- switch (opc & 0xff00) {
- case BF_OPCODE:
- format = " BF %d\n";
- break;
- case BFS_OPCODE:
- format = " *BF/S %d\n";
- break;
- case ANDIMM_OPCODE:
- format = " AND #%d, R0\n";
- break;
- case BT_OPCODE:
- format = " BT %d\n";
- break;
- case BTS_OPCODE:
- format = " *BT/S %d\n";
- break;
- case CMPEQIMM_OPCODE:
- format = " CMP/EQ #%d, R0\n";
- break;
- case MOVB_WRITE_OFFGBR_OPCODE:
- format = " MOV.B R0, @(%d, GBR)\n";
- break;
- case MOVB_READ_OFFGBR_OPCODE:
- format = " MOV.B @(%d, GBR), R0\n";
- break;
- case MOVL_WRITE_OFFGBR_OPCODE:
- format = " MOV.L R0, @(%d, GBR)\n";
- break;
- case MOVL_READ_OFFGBR_OPCODE:
- format = " MOV.L @(%d, GBR), R0\n";
- break;
- case MOVA_READ_OFFPC_OPCODE:
- format = " MOVA @(%d, PC), R0\n";
- break;
- case ORIMM_OPCODE:
- format = " OR #%d, R0\n";
- break;
- case ORBIMM_OPCODE:
- format = " OR.B #%d, @(R0, GBR)\n";
- break;
- case TSTIMM_OPCODE:
- format = " TST #%d, R0\n";
- break;
- case TSTB_OPCODE:
- format = " TST.B %d, @(R0, GBR)\n";
- break;
- case XORIMM_OPCODE:
- format = " XOR #%d, R0\n";
- break;
- case XORB_OPCODE:
- format = " XOR.B %d, @(R0, GBR)\n";
- break;
- }
- if (format) {
- printfStdoutInstr(format, getImm8(opc));
- return;
- }
- switch (opc & 0xff00) {
- case MOVB_WRITE_OFFRN_OPCODE:
- format = " MOV.B R0, @(%d, R%d)\n";
- break;
- case MOVB_READ_OFFRM_OPCODE:
- format = " MOV.B @(%d, R%d), R0\n";
- break;
- }
- if (format) {
- printfStdoutInstr(format, getDisp(opc), getRm(opc));
- return;
- }
- switch (opc & 0xf000) {
- case BRA_OPCODE:
- format = " *BRA %d\n";
- break;
- case BSR_OPCODE:
- format = " *BSR %d\n";
- break;
- }
- if (format) {
- printfStdoutInstr(format, getImm12(opc));
- return;
- }
- switch (opc & 0xf000) {
- case MOVL_READ_OFFPC_OPCODE:
- format = " MOV.L @(%d, PC), R%d\n";
- break;
- case ADDIMM_OPCODE:
- format = " ADD #%d, R%d\n";
- break;
- case MOVIMM_OPCODE:
- format = " MOV #%d, R%d\n";
- break;
- case MOVW_READ_OFFPC_OPCODE:
- format = " MOV.W @(%d, PC), R%d\n";
- break;
- }
- if (format) {
- printfStdoutInstr(format, getImm8(opc), getRn(opc));
- return;
- }
- switch (opc & 0xf000) {
- case MOVL_WRITE_OFFRN_OPCODE:
- format = " MOV.L R%d, @(%d, R%d)\n";
- printfStdoutInstr(format, getRm(opc), getDisp(opc), getRn(opc));
- break;
- case MOVL_READ_OFFRM_OPCODE:
- format = " MOV.L @(%d, R%d), R%d\n";
- printfStdoutInstr(format, getDisp(opc), getRm(opc), getRn(opc));
- break;
- }
- }
-
- static void printfStdoutInstr(const char* format, ...)
- {
- if (getenv("JavaScriptCoreDumpJIT")) {
- va_list args;
- va_start(args, format);
- vprintfStdoutInstr(format, args);
- va_end(args);
- }
- }
-
- static void vprintfStdoutInstr(const char* format, va_list args)
- {
- if (getenv("JavaScriptCoreDumpJIT"))
- WTF::dataLogFV(format, args);
- }
-
- static void printBlockInstr(uint16_t* first, unsigned offset, int nbInstr)
- {
- printfStdoutInstr(">> repatch instructions after link\n");
- for (int i = 0; i <= nbInstr; i++)
- printInstr(*(first + i), offset + i);
- printfStdoutInstr(">> end repatch\n");
- }
-#else
- static void printInstr(uint16_t opc, unsigned size, bool isdoubleInst = true) { };
- static void printBlockInstr(uint16_t* first, unsigned offset, int nbInstr) { };
-#endif
-
- static void replaceWithLoad(void* instructionStart)
- {
- SH4Word* insPtr = reinterpret_cast<SH4Word*>(instructionStart);
-
- insPtr += 2; // skip MOV and ADD opcodes
-
- if (((*insPtr) & 0xf00f) != MOVL_READ_RM_OPCODE) {
- *insPtr = MOVL_READ_RM_OPCODE | (*insPtr & 0x0ff0);
- cacheFlush(insPtr, sizeof(SH4Word));
- }
- }
-
- static void replaceWithAddressComputation(void* instructionStart)
- {
- SH4Word* insPtr = reinterpret_cast<SH4Word*>(instructionStart);
-
- insPtr += 2; // skip MOV and ADD opcodes
-
- if (((*insPtr) & 0xf00f) != MOV_OPCODE) {
- *insPtr = MOV_OPCODE | (*insPtr & 0x0ff0);
- cacheFlush(insPtr, sizeof(SH4Word));
- }
- }
-
-private:
- SH4Buffer m_buffer;
- int m_claimscratchReg;
-};
-
-} // namespace JSC
-
-#endif // ENABLE(ASSEMBLER) && CPU(SH4)
-
-#endif // SH4Assembler_h
diff --git a/src/3rdparty/masm/assembler/X86Assembler.h b/src/3rdparty/masm/assembler/X86Assembler.h
index b71cf290f8..2257cb2b9a 100644
--- a/src/3rdparty/masm/assembler/X86Assembler.h
+++ b/src/3rdparty/masm/assembler/X86Assembler.h
@@ -26,6 +26,8 @@
#ifndef X86Assembler_h
#define X86Assembler_h
+#include <Platform.h>
+
#if ENABLE(ASSEMBLER) && (CPU(X86) || CPU(X86_64))
#include "AssemblerBuffer.h"
@@ -1417,11 +1419,22 @@ public:
{
m_formatter.oneByteOp(OP_LEA, dst, base, offset);
}
+
+ void leal_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
+ {
+ m_formatter.oneByteOp(OP_LEA, dst, base, index, scale, offset);
+ }
+
#if CPU(X86_64)
void leaq_mr(int offset, RegisterID base, RegisterID dst)
{
m_formatter.oneByteOp64(OP_LEA, dst, base, offset);
}
+
+ void leaq_mr(int offset, RegisterID base, RegisterID index, int scale, RegisterID dst)
+ {
+ m_formatter.oneByteOp64(OP_LEA, dst, base, index, scale, offset);
+ }
#endif
// Flow control:
@@ -1892,9 +1905,18 @@ public:
ASSERT(to.isSet());
char* code = reinterpret_cast<char*>(m_formatter.data());
- ASSERT(!reinterpret_cast<int32_t*>(code + from.m_offset)[-1]);
+ ASSERT(!loadPossiblyUnaligned<int32_t>(code, from.m_offset, -1));
setRel32(code + from.m_offset, code + to.m_offset);
}
+
+ template<typename T>
+ T loadPossiblyUnaligned(char *ptr, size_t offset, int idx)
+ {
+ T *t_ptr = &reinterpret_cast<T*>(ptr + offset)[idx];
+ T val;
+ memcpy(&val, t_ptr, sizeof(T));
+ return val;
+ }
static void linkJump(void* code, AssemblerLabel from, void* to)
{
@@ -2095,7 +2117,14 @@ private:
static void setInt32(void* where, int32_t value)
{
- reinterpret_cast<int32_t*>(where)[-1] = value;
+ storePossiblyUnaligned<int32_t>(where, -1, value);
+ }
+
+ template <typename T>
+ static void storePossiblyUnaligned(void *where, int idx, T value)
+ {
+ T *ptr = &reinterpret_cast<T*>(where)[idx];
+ memcpy(ptr, &value, sizeof(T));
}
static void setInt8(void* where, int8_t value)
diff --git a/src/3rdparty/masm/masm-defs.pri b/src/3rdparty/masm/masm-defs.pri
index 34d6a67451..08c46a7ac2 100644
--- a/src/3rdparty/masm/masm-defs.pri
+++ b/src/3rdparty/masm/masm-defs.pri
@@ -20,6 +20,7 @@ INCLUDEPATH += $$PWD/assembler
INCLUDEPATH += $$PWD/runtime
INCLUDEPATH += $$PWD/wtf
INCLUDEPATH += $$PWD/stubs
+INCLUDEPATH += $$PWD/stubs/runtime
INCLUDEPATH += $$PWD/stubs/wtf
INCLUDEPATH += $$PWD
diff --git a/src/3rdparty/masm/masm.pri b/src/3rdparty/masm/masm.pri
index 7dfb24f4b8..0e63ac2ce5 100644
--- a/src/3rdparty/masm/masm.pri
+++ b/src/3rdparty/masm/masm.pri
@@ -79,10 +79,12 @@ HEADERS += $$PWD/disassembler/ARM64/A64DOpcode.h
!qmldevtools_build {
SOURCES += $$PWD/yarr/YarrCanonicalizeUCS2.cpp \
+ $$PWD/yarr/YarrCanonicalizeUnicode.cpp \
$$PWD/yarr/YarrInterpreter.cpp \
$$PWD/yarr/YarrJIT.cpp \
$$PWD/yarr/YarrPattern.cpp \
- $$PWD/yarr/YarrSyntaxChecker.cpp
+ $$PWD/yarr/YarrSyntaxChecker.cpp \
+ $$PWD/stubs/yarr/YarrUnicodeProperties.cpp
HEADERS += $$PWD/yarr/Yarr.h \
$$PWD/yarr/YarrCanonicalizeUCS2.h \
@@ -90,7 +92,8 @@ HEADERS += $$PWD/yarr/Yarr.h \
$$PWD/yarr/YarrJIT.h \
$$PWD/yarr/YarrParser.h \
$$PWD/yarr/YarrPattern.h \
- $$PWD/yarr/YarrSyntaxChecker.h
+ $$PWD/yarr/YarrSyntaxChecker.h \
+ $$PWD/yarr/YarrUnicodeProperties.h
}
#
@@ -107,7 +110,7 @@ debug_and_release {
INCLUDEPATH += $$GENERATEDDIR
retgen.output = $$GENERATEDDIR/RegExpJitTables.h
-retgen.script = $$PWD/create_regex_tables
+retgen.script = $$PWD/yarr/create_regex_tables
retgen.input = retgen.script
retgen.CONFIG += no_link
retgen.commands = python $$retgen.script > ${QMAKE_FILE_OUT}
@@ -125,3 +128,8 @@ QMAKE_EXTRA_COMPILERS += retgen
}
}
}
+
+linux {
+ requires(qtConfig(dlopen))
+ QMAKE_USE_PRIVATE += libdl
+}
diff --git a/src/3rdparty/masm/qt_attribution.json b/src/3rdparty/masm/qt_attribution.json
index f6803c6b1c..aab413ad40 100644
--- a/src/3rdparty/masm/qt_attribution.json
+++ b/src/3rdparty/masm/qt_attribution.json
@@ -8,7 +8,7 @@
"License": "BSD 2-clause \"Simplified\" License",
"LicenseId": "BSD-2-Clause",
"LicenseFile": "LICENSE",
- "Copyright": "Copyright (C) 2003-2015 Apple Inc. All rights reserved.
+ "Copyright": "Copyright (C) 2003-2018 Apple Inc. All rights reserved.
Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
Copyright (C) 2007-2009 Torch Mobile, Inc. All rights reserved. (http://www.torchmobile.com/)
Copyright (C) 2009, 2010 University of Szeged
diff --git a/src/3rdparty/masm/stubs/ExecutableAllocator.h b/src/3rdparty/masm/stubs/ExecutableAllocator.h
index 16b17bd3cd..156b24b4e8 100644
--- a/src/3rdparty/masm/stubs/ExecutableAllocator.h
+++ b/src/3rdparty/masm/stubs/ExecutableAllocator.h
@@ -82,7 +82,7 @@ struct ExecutableMemoryHandle : public RefCounted<ExecutableMemoryHandle> {
inline bool isManaged() const { return true; }
- void* start() { return m_allocation->start(); }
+ void *start() { return m_allocation->start(); }
size_t sizeInBytes() { return m_size; }
QV4::ExecutableAllocator::ChunkOfPages *chunk() const
@@ -98,7 +98,7 @@ struct ExecutableAllocator {
: realAllocator(alloc)
{}
- PassRefPtr<ExecutableMemoryHandle> allocate(JSGlobalData&, size_t size, void*, int)
+ Ref<ExecutableMemoryHandle> allocate(JSGlobalData&, size_t size, void*, int)
{
return adoptRef(new ExecutableMemoryHandle(realAllocator, size));
}
diff --git a/src/3rdparty/masm/stubs/Options.h b/src/3rdparty/masm/stubs/Options.h
index e03cc67690..6339c06033 100644
--- a/src/3rdparty/masm/stubs/Options.h
+++ b/src/3rdparty/masm/stubs/Options.h
@@ -44,6 +44,8 @@ namespace JSC {
struct Options {
static bool showDisassembly();
static bool showDFGDisassembly() { return true; }
+ static bool zeroStackFrame() { return true; }
+ static bool dumpCompiledRegExpPatterns() { return false; }
};
}
diff --git a/src/3rdparty/masm/stubs/SuperSampler.h b/src/3rdparty/masm/stubs/SuperSampler.h
new file mode 100644
index 0000000000..422de528e1
--- /dev/null
+++ b/src/3rdparty/masm/stubs/SuperSampler.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#pragma once
+
+namespace WTF {
+
+struct SuperSamplerScope {
+ SuperSamplerScope(bool) {}
+};
+
+}
+
+using WTF::SuperSamplerScope;
diff --git a/src/3rdparty/masm/stubs/WTFStubs.cpp b/src/3rdparty/masm/stubs/WTFStubs.cpp
index ea7e2d78e0..b26d10b3ab 100644
--- a/src/3rdparty/masm/stubs/WTFStubs.cpp
+++ b/src/3rdparty/masm/stubs/WTFStubs.cpp
@@ -91,7 +91,7 @@ void dataLogFV(const char* format, va_list args)
{
char buffer[1024];
qvsnprintf(buffer, sizeof(buffer), format, args);
- qDebug("%s", buffer);
+ qDebug().nospace().noquote() << buffer;
}
void dataLogF(const char* format, ...)
@@ -101,12 +101,12 @@ void dataLogF(const char* format, ...)
va_start(args, format);
qvsnprintf(buffer, sizeof(buffer), format, args);
va_end(args);
- qDebug("%s", buffer);
+ qDebug().nospace().noquote() << buffer;
}
void dataLogFString(const char* str)
{
- qDebug("%s", str);
+ qDebug().nospace().noquote() << str;
}
}
diff --git a/src/3rdparty/masm/stubs/runtime/ConcurrentJSLock.h b/src/3rdparty/masm/stubs/runtime/ConcurrentJSLock.h
new file mode 100644
index 0000000000..43868feadb
--- /dev/null
+++ b/src/3rdparty/masm/stubs/runtime/ConcurrentJSLock.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#pragma once
+
+namespace JSC {
+
+class NoLock {
+public:
+ void lock() { }
+ void unlock() { }
+ bool isHeld() { return false; }
+};
+
+typedef NoLock ConcurrentJSLock;
+
+} // namespace JSC
diff --git a/src/3rdparty/masm/stubs/runtime/RegExpKey.h b/src/3rdparty/masm/stubs/runtime/RegExpKey.h
new file mode 100644
index 0000000000..392f66fb83
--- /dev/null
+++ b/src/3rdparty/masm/stubs/runtime/RegExpKey.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 University of Szeged
+ * Copyright (C) 2010 Renata Hodovan (hodovan@inf.u-szeged.hu)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED 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.
+ */
+
+#pragma once
+
+#include <wtf/text/WTFString.h>
+
+namespace JSC {
+
+enum RegExpFlags : int8_t {
+ NoFlags = 0,
+ FlagGlobal = 1,
+ FlagIgnoreCase = 2,
+ FlagMultiline = 4,
+ FlagSticky = 8,
+ FlagUnicode = 16,
+ FlagDotAll = 32,
+ InvalidFlags = 64,
+ DeletedValueFlags = -1
+};
+
+} // namespace JSC
diff --git a/src/3rdparty/masm/stubs/runtime/VM.h b/src/3rdparty/masm/stubs/runtime/VM.h
new file mode 100644
index 0000000000..94cce814f3
--- /dev/null
+++ b/src/3rdparty/masm/stubs/runtime/VM.h
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** 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 MASM_VM_H
+#define MASM_VM_H
+
+#include <qv4engine_p.h>
+
+namespace JSC {
+
+class VM : public QV4::ExecutionEngine {};
+
+}
+
+#endif // MASM_VM_H
diff --git a/src/3rdparty/masm/stubs/wtf/HashMap.h b/src/3rdparty/masm/stubs/wtf/HashMap.h
new file mode 100644
index 0000000000..888c6cceb0
--- /dev/null
+++ b/src/3rdparty/masm/stubs/wtf/HashMap.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** 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 HASHMAP_H
+#define HASHMAP_H
+
+#include <QtCore/qhash.h>
+
+namespace WTF {
+
+template<typename Key, typename Value>
+class HashMap final : public QHash<Key, Value>
+{
+public:
+ void add(const Key &k, const Value &v) { QHash<Key, Value>::insert(k, v); }
+ Value get(const Key &k) { return QHash<Key, Value>::value(k); }
+};
+
+}
+
+using WTF::HashMap;
+
+#endif
diff --git a/src/3rdparty/masm/stubs/wtf/HashSet.h b/src/3rdparty/masm/stubs/wtf/HashSet.h
new file mode 100644
index 0000000000..3765c9a8b1
--- /dev/null
+++ b/src/3rdparty/masm/stubs/wtf/HashSet.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** 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 HASHSET_H
+#define HASHSET_H
+
+#include <QtCore/qset.h>
+
+namespace WTF {
+
+template<typename Key>
+class HashSet final : public QSet<Key>
+{
+public:
+ struct SetAddResult {
+ bool isNewEntry;
+ };
+ SetAddResult add(const Key &k)
+ {
+ if (QSet<Key>::find(k) == QSet<Key>::constEnd()) {
+ QSet<Key>::insert(k);
+ return { true };
+ }
+ return { false };
+ }
+};
+
+}
+
+using WTF::HashSet;
+
+#endif
diff --git a/src/3rdparty/masm/stubs/wtf/Optional.h b/src/3rdparty/masm/stubs/wtf/Optional.h
new file mode 100644
index 0000000000..44fa3ee62d
--- /dev/null
+++ b/src/3rdparty/masm/stubs/wtf/Optional.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QtCore/qglobal.h>
+
+#if __cplusplus > 201402L && QT_HAS_INCLUDE(<optional>)
+#include <optional>
+#else
+
+namespace std {
+
+struct nullopt_t {};
+
+constexpr nullopt_t nullopt {};
+
+template<typename T>
+class optional {
+public:
+ optional() = default;
+ optional(nullopt_t) {}
+ optional(const T &v) : _value(v), _hasValue(true) {}
+ ~optional() = default;
+
+ optional &operator =(nullopt_t) {
+ _value = T();
+ _hasValue = false;
+ return *this;
+ }
+
+ T operator->() { return _value; }
+ T operator*() { return _value; }
+
+ operator bool() const { return _hasValue; }
+ bool has_value() const { return _hasValue; }
+
+ T value() const { return _value; }
+
+private:
+ T _value = T();
+ bool _hasValue = false;
+};
+
+}
+
+#endif
diff --git a/src/3rdparty/masm/stubs/wtf/PassRefPtr.h b/src/3rdparty/masm/stubs/wtf/PassRefPtr.h
index f072e70dd7..cc03a5d651 100644
--- a/src/3rdparty/masm/stubs/wtf/PassRefPtr.h
+++ b/src/3rdparty/masm/stubs/wtf/PassRefPtr.h
@@ -83,14 +83,22 @@ public:
private:
PassRefPtr<T>& operator=(const PassRefPtr<T>& t);
- template <typename PtrType> friend PassRefPtr<PtrType> adoptRef(PtrType*);
+protected:
mutable T* m_ptr;
};
template <typename T>
-PassRefPtr<T> adoptRef(T* ptr)
+class Ref : public PassRefPtr<T>
{
- PassRefPtr<T> result;
+ using PassRefPtr<T>::PassRefPtr;
+
+ template <typename PtrType> friend Ref<PtrType> adoptRef(PtrType*);
+};
+
+template <typename T>
+Ref<T> adoptRef(T* ptr)
+{
+ Ref<T> result;
result.m_ptr = ptr;
return result;
}
diff --git a/src/3rdparty/masm/stubs/wtf/Vector.h b/src/3rdparty/masm/stubs/wtf/Vector.h
index 2025acf8a9..f4f4dc5cf4 100644
--- a/src/3rdparty/masm/stubs/wtf/Vector.h
+++ b/src/3rdparty/masm/stubs/wtf/Vector.h
@@ -55,6 +55,8 @@ class Vector : public std::vector<T> {
public:
Vector() {}
Vector(int initialSize) : std::vector<T>(initialSize) {}
+ Vector(const Vector &other) : std::vector<T>(other) {}
+ Vector(std::initializer_list<T> list) : std::vector<T>(list) {}
inline void append(const T& value)
{ this->push_back(value); }
@@ -63,6 +65,9 @@ public:
inline void append(const OtherType& other)
{ this->push_back(T(other)); }
+ inline void append(T&& other)
+ { this->push_back(std::move(other)); }
+
inline void append(const Vector<T>& vector)
{
this->insert(this->end(), vector.begin(), vector.end());
@@ -80,6 +85,8 @@ public:
this->push_back(*it);
}
+ unsigned size() const { return static_cast<unsigned>(std::vector<T>::size()); }
+
using std::vector<T>::insert;
inline void reserveInitialCapacity(size_t size) { this->reserve(size); }
diff --git a/src/3rdparty/masm/stubs/wtf/text/CString.h b/src/3rdparty/masm/stubs/wtf/text/CString.h
index 26f74f7593..7129f5049e 100644
--- a/src/3rdparty/masm/stubs/wtf/text/CString.h
+++ b/src/3rdparty/masm/stubs/wtf/text/CString.h
@@ -39,4 +39,8 @@
#ifndef CSTRING_H
#define CSTRING_H
+class CString : public QByteArray {
+
+};
+
#endif // CSTRING_H
diff --git a/src/3rdparty/masm/stubs/wtf/text/StringBuilder.h b/src/3rdparty/masm/stubs/wtf/text/StringBuilder.h
new file mode 100644
index 0000000000..a382f6da83
--- /dev/null
+++ b/src/3rdparty/masm/stubs/wtf/text/StringBuilder.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+#pragma once
+
+#include <wtf/text/WTFString.h>
+
+namespace WTF {
+
+struct StringBuilder : public String
+{
+ String toString() const { return *this; }
+};
+
+}
+
+using WTF::StringBuilder;
diff --git a/src/3rdparty/masm/stubs/wtf/text/WTFString.h b/src/3rdparty/masm/stubs/wtf/text/WTFString.h
index 928c684fdb..da5183f734 100644
--- a/src/3rdparty/masm/stubs/wtf/text/WTFString.h
+++ b/src/3rdparty/masm/stubs/wtf/text/WTFString.h
@@ -42,26 +42,33 @@
#include <QString>
#include <wtf/ASCIICType.h>
#include <wtf/unicode/Unicode.h>
+#include <memory>
namespace WTF {
+class PrintStream;
+
class String : public QString
{
public:
+ String() = default;
String(const QString& s) : QString(s) {}
bool is8Bit() const { return false; }
const unsigned char *characters8() const { return 0; }
const UChar *characters16() const { return reinterpret_cast<const UChar*>(constData()); }
template <typename T>
- const T* getCharacters() const;
+ const T* characters() const;
+
+ bool operator!() const { return isEmpty(); }
+ void dump(PrintStream &) const {}
};
template <>
-inline const unsigned char* String::getCharacters<unsigned char>() const { return characters8(); }
+inline const unsigned char* String::characters<unsigned char>() const { return characters8(); }
template <>
-inline const UChar* String::getCharacters<UChar>() const { return characters16(); }
+inline const UChar* String::characters<UChar>() const { return characters16(); }
}
@@ -70,4 +77,6 @@ namespace JSC {
using WTF::String;
}
+#define WTFMove(value) std::move(value)
+
#endif // WTFSTRING_H
diff --git a/src/3rdparty/masm/stubs/wtf/unicode/Unicode.h b/src/3rdparty/masm/stubs/wtf/unicode/Unicode.h
index d61cec5c4e..0f7f005c89 100644
--- a/src/3rdparty/masm/stubs/wtf/unicode/Unicode.h
+++ b/src/3rdparty/masm/stubs/wtf/unicode/Unicode.h
@@ -43,6 +43,7 @@
typedef unsigned char LChar;
typedef unsigned short UChar;
+typedef int32_t UChar32;
namespace Unicode {
inline UChar toLower(UChar ch) {
@@ -52,6 +53,35 @@ namespace Unicode {
inline UChar toUpper(UChar ch) {
return QChar::toUpper(ch);
}
+ inline UChar32 u_tolower(UChar32 ch) {
+ return QChar::toLower(ch);
+ }
+ inline UChar32 u_toupper(UChar32 ch) {
+ return QChar::toUpper(ch);
+ }
}
+using Unicode::u_toupper;
+using Unicode::u_tolower;
+
+#define U16_IS_LEAD(ch) QChar::isHighSurrogate((ch))
+#define U16_IS_TRAIL(ch) QChar::isLowSurrogate((ch))
+#define U16_GET_SUPPLEMENTARY(lead, trail) static_cast<UChar32>(QChar::surrogateToUcs4((lead), (trail)))
+#define U_IS_BMP(ch) ((ch) < 0x10000)
+#define U16_LENGTH(c) ((uint32_t)(c)<=0xffff ? 1 : 2)
+#define UCHAR_MAX_VALUE 0x10ffff
+
+#define U_MASK(category) (1u << (category))
+#define U_GET_GC_MASK(c) U_MASK(QChar::category((c)))
+#define U_GC_L_MASK (U_GC_LU_MASK|U_GC_LL_MASK|U_GC_LT_MASK|U_GC_LM_MASK|U_GC_LO_MASK)
+#define U_GC_LU_MASK U_MASK(QChar::Letter_Uppercase)
+#define U_GC_LL_MASK U_MASK(QChar::Letter_Lowercase)
+#define U_GC_LT_MASK U_MASK(QChar::Letter_Titlecase)
+#define U_GC_LM_MASK U_MASK(QChar::Letter_Modifier)
+#define U_GC_LO_MASK U_MASK(QChar::Letter_Other)
+#define U_GC_MN_MASK U_MASK(QChar::Mark_NonSpacing)
+#define U_GC_MC_MASK U_MASK(QChar::Mark_SpacingCombining)
+#define U_GC_ND_MASK U_MASK(QChar::Number_DecimalDigit)
+#define U_GC_PC_MASK U_MASK(QChar::Punctuation_Connector)
+
#endif // UNICODE_H
diff --git a/src/3rdparty/masm/stubs/wtf/unicode/utypes.h b/src/3rdparty/masm/stubs/wtf/unicode/utypes.h
new file mode 100644
index 0000000000..e1b4ff90a6
--- /dev/null
+++ b/src/3rdparty/masm/stubs/wtf/unicode/utypes.h
@@ -0,0 +1 @@
+#include <unicode/Unicode.h>
diff --git a/src/3rdparty/masm/stubs/yarr/YarrUnicodeProperties.cpp b/src/3rdparty/masm/stubs/yarr/YarrUnicodeProperties.cpp
new file mode 100644
index 0000000000..99c925f406
--- /dev/null
+++ b/src/3rdparty/masm/stubs/yarr/YarrUnicodeProperties.cpp
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** 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 "config.h"
+#include "yarr/YarrUnicodeProperties.h"
+#include "qchar.h"
+
+#include "yarr/Yarr.h"
+#include "yarr/YarrPattern.h"
+
+using namespace WTF;
+
+namespace JSC { namespace Yarr {
+
+std::optional<BuiltInCharacterClassID> unicodeMatchPropertyValue(WTF::String unicodePropertyName, WTF::String unicodePropertyValue)
+{
+ Q_UNUSED(unicodePropertyName);
+ Q_UNUSED(unicodePropertyValue);
+ return std::nullopt;
+}
+
+std::optional<BuiltInCharacterClassID> unicodeMatchProperty(WTF::String unicodePropertyValue)
+{
+ Q_UNUSED(unicodePropertyValue);
+ return std::nullopt;
+}
+
+std::unique_ptr<CharacterClass> createUnicodeCharacterClassFor(BuiltInCharacterClassID unicodeClassID)
+{
+ Q_UNUSED(unicodeClassID);
+ return nullptr;
+}
+
+} } // namespace JSC::Yarr
diff --git a/src/3rdparty/masm/wtf/Assertions.h b/src/3rdparty/masm/wtf/Assertions.h
index 491e434498..be25d43826 100644
--- a/src/3rdparty/masm/wtf/Assertions.h
+++ b/src/3rdparty/masm/wtf/Assertions.h
@@ -256,7 +256,7 @@ inline void assertUnused(T& x) { (void)x; }
(void)0)
#define ASSERT_NOT_REACHED() do { \
- WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, 0); \
+ WTFReportAssertionFailure(__FILE__, __LINE__, WTF_PRETTY_FUNCTION, NULL); \
CRASH(); \
} while (0)
diff --git a/src/3rdparty/masm/wtf/FilePrintStream.cpp b/src/3rdparty/masm/wtf/FilePrintStream.cpp
index 45f1565f46..28714ecb6f 100644
--- a/src/3rdparty/masm/wtf/FilePrintStream.cpp
+++ b/src/3rdparty/masm/wtf/FilePrintStream.cpp
@@ -38,17 +38,16 @@ FilePrintStream::~FilePrintStream()
{
if (m_adoptionMode == Borrow)
return;
- if (m_file)
- fclose(m_file);
+ fclose(m_file);
}
-PassOwnPtr<FilePrintStream> FilePrintStream::open(const char* filename, const char* mode)
+std::unique_ptr<FilePrintStream> FilePrintStream::open(const char* filename, const char* mode)
{
FILE* file = fopen(filename, mode);
if (!file)
- return PassOwnPtr<FilePrintStream>();
-
- return adoptPtr(new FilePrintStream(file));
+ return nullptr;
+
+ return std::make_unique<FilePrintStream>(file);
}
void FilePrintStream::vprintf(const char* format, va_list argList)
diff --git a/src/3rdparty/masm/wtf/FilePrintStream.h b/src/3rdparty/masm/wtf/FilePrintStream.h
index bdeab4c479..f32ca49dcb 100644
--- a/src/3rdparty/masm/wtf/FilePrintStream.h
+++ b/src/3rdparty/masm/wtf/FilePrintStream.h
@@ -27,7 +27,6 @@
#define FilePrintStream_h
#include <stdio.h>
-#include <wtf/PassOwnPtr.h>
#include <wtf/PrintStream.h>
namespace WTF {
@@ -40,14 +39,14 @@ public:
};
FilePrintStream(FILE*, AdoptionMode = Adopt);
- virtual ~FilePrintStream();
+ virtual ~FilePrintStream() override;
- static PassOwnPtr<FilePrintStream> open(const char* filename, const char* mode);
+ WTF_EXPORT_PRIVATE static std::unique_ptr<FilePrintStream> open(const char* filename, const char* mode);
FILE* file() { return m_file; }
- void vprintf(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(2, 0);
- void flush();
+ void vprintf(const char* format, va_list) override WTF_ATTRIBUTE_PRINTF(2, 0);
+ void flush() override;
private:
FILE* m_file;
diff --git a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp
index 0c902c7172..3b2a73a39a 100644
--- a/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp
+++ b/src/3rdparty/masm/wtf/OSAllocatorPosix.cpp
@@ -31,13 +31,82 @@
#include <cstdlib>
#include "PageAllocation.h"
+#include <dlfcn.h>
#include <errno.h>
#include <sys/mman.h>
#include <wtf/Assertions.h>
#include <wtf/UnusedParam.h>
+#if OS(LINUX)
+#include <sys/syscall.h>
+#ifndef MFD_CLOEXEC
+#define MFD_CLOEXEC 0x0001U
+#endif
+#endif
+
+#if defined(__ANDROID__) && defined(SYS_memfd_create)
+ // On Android it's been observed that permissions of memory mappings
+ // backed by a memfd could not be changed via mprotect for no obvious
+ // reason.
+# undef SYS_memfd_create
+#endif
+
namespace WTF {
+#ifdef SYS_memfd_create
+static int memfdForUsage(size_t bytes, OSAllocator::Usage usage)
+{
+ const char *type = "unknown-usage:";
+ switch (usage) {
+ case OSAllocator::UnknownUsage:
+ break;
+ case OSAllocator::FastMallocPages:
+ type = "fastmalloc:";
+ break;
+ case OSAllocator::JSGCHeapPages:
+ type = "JSGCHeap:";
+ break;
+ case OSAllocator::JSVMStackPages:
+ type = "JSVMStack:";
+ break;
+ case OSAllocator::JSJITCodePages:
+ type = "JITCode:";
+ break;
+ }
+
+ // try to get our own library name by giving dladdr a pointer pointing to
+ // something we know to be in it (using a pointer to string data)
+ static const char *libname = [=]() {
+ Dl_info info;
+ if (dladdr(type, &info) == 0)
+ info.dli_fname = nullptr;
+ return info.dli_fname;
+ }();
+
+ char buf[PATH_MAX];
+ strcpy(buf, type);
+ if (libname)
+ strcat(buf, libname);
+ else
+ strcat(buf, "QtQml");
+
+ int fd = syscall(SYS_memfd_create, buf, MFD_CLOEXEC);
+ if (fd != -1) {
+ if (ftruncate(fd, bytes) == 0)
+ return fd;
+ }
+ close(fd);
+ return -1;
+}
+#elif OS(LINUX)
+static int memfdForUsage(size_t bytes, OSAllocator::Usage usage)
+{
+ UNUSED_PARAM(bytes);
+ UNUSED_PARAM(usage);
+ return -1;
+}
+#endif
+
void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, bool executable)
{
#if OS(QNX)
@@ -46,14 +115,18 @@ void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable,
if (result == MAP_FAILED)
CRASH();
#elif OS(LINUX)
- UNUSED_PARAM(usage);
UNUSED_PARAM(writable);
UNUSED_PARAM(executable);
+ int fd = memfdForUsage(bytes, usage);
- void* result = mmap(0, bytes, PROT_NONE, MAP_NORESERVE | MAP_PRIVATE | MAP_ANON, -1, 0);
+ void* result = mmap(0, bytes, PROT_NONE, MAP_NORESERVE | MAP_PRIVATE |
+ (fd == -1 ? MAP_ANON : 0), fd, 0);
if (result == MAP_FAILED)
CRASH();
madvise(result, bytes, MADV_DONTNEED);
+
+ if (fd != -1)
+ close(fd);
#else
void* result = reserveAndCommit(bytes, usage, writable, executable);
#if HAVE(MADV_FREE_REUSE)
@@ -83,6 +156,10 @@ void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bo
#if OS(DARWIN)
int fd = usage;
+#elif OS(LINUX)
+ int fd = memfdForUsage(bytes, usage);
+ if (fd != -1)
+ flags &= ~MAP_ANON;
#else
UNUSED_PARAM(usage);
int fd = -1;
@@ -126,6 +203,12 @@ void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bo
mmap(result, pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd, 0);
mmap(static_cast<char*>(result) + bytes - pageSize(), pageSize(), PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, fd, 0);
}
+
+#if OS(LINUX)
+ if (fd != -1)
+ close(fd);
+#endif
+
return result;
}
diff --git a/src/3rdparty/masm/wtf/Platform.h b/src/3rdparty/masm/wtf/Platform.h
index 5905f42f45..d5f69927db 100644
--- a/src/3rdparty/masm/wtf/Platform.h
+++ b/src/3rdparty/masm/wtf/Platform.h
@@ -2,6 +2,7 @@
* Copyright (C) 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved.
* Copyright (C) 2007-2009 Torch Mobile, Inc.
* Copyright (C) 2010, 2011 Research In Motion Limited. All rights reserved.
+ * Copyright (C) 2018 The Qt Company Ltd.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -740,10 +741,6 @@
#define ENABLE_JIT 0
#endif
-#if !defined(ENABLE_JIT) && CPU(SH4) && PLATFORM(QT)
-#define ENABLE_JIT 1
-#endif
-
/* The JIT is enabled by default on all x86, x86-64, ARM & MIPS platforms. */
#if !defined(ENABLE_JIT) \
&& (CPU(X86) || CPU(X86_64) || CPU(ARM) || CPU(MIPS) || CPU(ARM64)) \
@@ -1050,4 +1047,11 @@
#define WTF_USE_CONTENT_FILTERING 1
#endif
+#if ENABLE(YARR_JIT)
+#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
+#endif
+#endif
+
#endif /* WTF_Platform_h */
diff --git a/src/3rdparty/masm/wtf/PrintStream.h b/src/3rdparty/masm/wtf/PrintStream.h
index 6fcf9c1567..4372288aff 100644
--- a/src/3rdparty/masm/wtf/PrintStream.h
+++ b/src/3rdparty/masm/wtf/PrintStream.h
@@ -206,6 +206,10 @@ public:
print(value12);
print(value13);
}
+
+ void println();
+ template<typename ...Types>
+ void println(Types... args);
};
WTF_EXPORT_PRIVATE void printInternal(PrintStream&, const char*);
@@ -227,6 +231,19 @@ void printInternal(PrintStream& out, const T& value)
value.dump(out);
}
+inline
+void PrintStream::println()
+{
+ print("\n");
+}
+
+template<typename ...Types>
+void PrintStream::println(Types... args)
+{
+ print(args...);
+ print("\n");
+}
+
#define MAKE_PRINT_ADAPTOR(Name, Type, function) \
class Name { \
public: \
diff --git a/src/3rdparty/masm/wtf/StdLibExtras.h b/src/3rdparty/masm/wtf/StdLibExtras.h
index f0d792ed52..421712c349 100644
--- a/src/3rdparty/masm/wtf/StdLibExtras.h
+++ b/src/3rdparty/masm/wtf/StdLibExtras.h
@@ -28,6 +28,9 @@
#include <wtf/Assertions.h>
#include <wtf/CheckedArithmetic.h>
+#include <wtf/Platform.h>
+#include <memory>
+#include <qglobal.h>
// Use these to declare and define a static local variable (static T;) so that
// it is leaked so that its destructors are not called at exit. Using this
@@ -71,6 +74,8 @@
#define STRINGIZE(exp) #exp
#define STRINGIZE_VALUE_OF(exp) STRINGIZE(exp)
+#define FALLTHROUGH Q_FALLTHROUGH()
+
/*
* The reinterpret_cast<Type1*>([pointer to Type2]) expressions - where
* sizeof(Type1) > sizeof(Type2) - cause the following warning on ARM with GCC:
diff --git a/src/3rdparty/masm/wtf/VMTags.h b/src/3rdparty/masm/wtf/VMTags.h
index 117bc3721e..af5352e471 100644
--- a/src/3rdparty/masm/wtf/VMTags.h
+++ b/src/3rdparty/masm/wtf/VMTags.h
@@ -62,6 +62,14 @@
#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY VM_MAKE_TAG(69)
#endif // defined(VM_MEMORY_WEBCORE_PURGEABLE_BUFFERS)
+#elif OS(LINUX)
+
+#define VM_TAG_FOR_TCMALLOC_MEMORY 0
+#define VM_TAG_FOR_COLLECTOR_MEMORY 1
+#define VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY 2
+#define VM_TAG_FOR_REGISTERFILE_MEMORY 3
+#define VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY 4
+
#else // OS(DARWIN)
#define VM_TAG_FOR_TCMALLOC_MEMORY -1
diff --git a/src/3rdparty/masm/yarr/Yarr.h b/src/3rdparty/masm/yarr/Yarr.h
index d393e9fa90..ccf78f9880 100644
--- a/src/3rdparty/masm/yarr/Yarr.h
+++ b/src/3rdparty/masm/yarr/Yarr.h
@@ -25,25 +25,25 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef Yarr_h
-#define Yarr_h
+#pragma once
-#include "YarrInterpreter.h"
-#include "YarrPattern.h"
+#include <limits.h>
+#include "YarrErrorCode.h"
namespace JSC { namespace Yarr {
-#define YarrStackSpaceForBackTrackInfoPatternCharacter 1 // Only for !fixed quantifiers.
-#define YarrStackSpaceForBackTrackInfoCharacterClass 1 // Only for !fixed quantifiers.
+#define YarrStackSpaceForBackTrackInfoPatternCharacter 2 // Only for !fixed quantifiers.
+#define YarrStackSpaceForBackTrackInfoCharacterClass 2 // Only for !fixed quantifiers.
#define YarrStackSpaceForBackTrackInfoBackReference 2
#define YarrStackSpaceForBackTrackInfoAlternative 1 // One per alternative.
#define YarrStackSpaceForBackTrackInfoParentheticalAssertion 1
-#define YarrStackSpaceForBackTrackInfoParenthesesOnce 1 // Only for !fixed quantifiers.
+#define YarrStackSpaceForBackTrackInfoParenthesesOnce 2
#define YarrStackSpaceForBackTrackInfoParenthesesTerminal 1
-#define YarrStackSpaceForBackTrackInfoParentheses 2
+#define YarrStackSpaceForBackTrackInfoParentheses 4
+#define YarrStackSpaceForDotStarEnclosure 1
static const unsigned quantifyInfinite = UINT_MAX;
-static const unsigned offsetNoMatch = (unsigned)-1;
+static const unsigned offsetNoMatch = std::numeric_limits<unsigned>::max();
// The below limit restricts the number of "recursive" match calls in order to
// avoid spending exponential time on complex regular expressions.
@@ -53,9 +53,10 @@ enum JSRegExpResult {
JSRegExpMatch = 1,
JSRegExpNoMatch = 0,
JSRegExpErrorNoMatch = -1,
- JSRegExpErrorHitLimit = -2,
- JSRegExpErrorNoMemory = -3,
- JSRegExpErrorInternal = -4
+ JSRegExpJITCodeFailure = -2,
+ JSRegExpErrorHitLimit = -3,
+ JSRegExpErrorNoMemory = -4,
+ JSRegExpErrorInternal = -5,
};
enum YarrCharSize {
@@ -63,7 +64,14 @@ enum YarrCharSize {
Char16
};
-} } // namespace JSC::Yarr
+enum class BuiltInCharacterClassID : unsigned {
+ DigitClassID,
+ SpaceClassID,
+ WordClassID,
+ DotClassID,
+ BaseUnicodePropertyID
+};
-#endif // Yarr_h
+struct BytecodePattern;
+} } // namespace JSC::Yarr
diff --git a/src/3rdparty/masm/yarr/YarrCanonicalize.h b/src/3rdparty/masm/yarr/YarrCanonicalize.h
new file mode 100644
index 0000000000..fb5e0231ac
--- /dev/null
+++ b/src/3rdparty/masm/yarr/YarrCanonicalize.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2012-2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <unicode/utypes.h>
+
+namespace JSC { namespace Yarr {
+
+// This set of data provides information for each UCS2 code point as to the set of code points
+// that it should match under the ES6 case insensitive RegExp matching rules, specified in 21.2.2.8.2.
+// The non-Unicode tables are autogenerated using YarrCanonicalize.js into YarrCanonicalize.cpp.
+// The Unicode tables are autogenerated using the python script generateYarrCanonicalizeUnicode
+// which creates YarrCanonicalizeUnicode.cpp.
+enum UCS2CanonicalizationType {
+ CanonicalizeUnique, // No canonically equal values, e.g. 0x0.
+ CanonicalizeSet, // Value indicates a set in characterSetInfo.
+ CanonicalizeRangeLo, // Value is positive delta to pair, E.g. 0x41 has value 0x20, -> 0x61.
+ CanonicalizeRangeHi, // Value is positive delta to pair, E.g. 0x61 has value 0x20, -> 0x41.
+ CanonicalizeAlternatingAligned, // Aligned consequtive pair, e.g. 0x1f4,0x1f5.
+ CanonicalizeAlternatingUnaligned, // Unaligned consequtive pair, e.g. 0x241,0x242.
+};
+struct CanonicalizationRange {
+ UChar32 begin;
+ UChar32 end;
+ UChar32 value;
+ UCS2CanonicalizationType type;
+};
+
+extern const size_t UCS2_CANONICALIZATION_RANGES;
+extern const UChar32* const ucs2CharacterSetInfo[];
+extern const CanonicalizationRange ucs2RangeInfo[];
+
+extern const size_t UNICODE_CANONICALIZATION_RANGES;
+extern const UChar32* const unicodeCharacterSetInfo[];
+extern const CanonicalizationRange unicodeRangeInfo[];
+
+enum class CanonicalMode { UCS2, Unicode };
+
+inline const UChar32* canonicalCharacterSetInfo(unsigned index, CanonicalMode canonicalMode)
+{
+ const UChar32* const* rangeInfo = canonicalMode == CanonicalMode::UCS2 ? ucs2CharacterSetInfo : unicodeCharacterSetInfo;
+ return rangeInfo[index];
+}
+
+// This searches in log2 time over ~400-600 entries, so should typically result in 9 compares.
+inline const CanonicalizationRange* canonicalRangeInfoFor(UChar32 ch, CanonicalMode canonicalMode = CanonicalMode::UCS2)
+{
+ const CanonicalizationRange* info = canonicalMode == CanonicalMode::UCS2 ? ucs2RangeInfo : unicodeRangeInfo;
+ size_t entries = canonicalMode == CanonicalMode::UCS2 ? UCS2_CANONICALIZATION_RANGES : UNICODE_CANONICALIZATION_RANGES;
+
+ while (true) {
+ size_t candidate = entries >> 1;
+ const CanonicalizationRange* candidateInfo = info + candidate;
+ if (ch < candidateInfo->begin)
+ entries = candidate;
+ else if (ch <= candidateInfo->end)
+ return candidateInfo;
+ else {
+ info = candidateInfo + 1;
+ entries -= (candidate + 1);
+ }
+ }
+}
+
+// Should only be called for characters that have one canonically matching value.
+inline UChar32 getCanonicalPair(const CanonicalizationRange* info, UChar32 ch)
+{
+ ASSERT(ch >= info->begin && ch <= info->end);
+ switch (info->type) {
+ case CanonicalizeRangeLo:
+ return ch + info->value;
+ case CanonicalizeRangeHi:
+ return ch - info->value;
+ case CanonicalizeAlternatingAligned:
+ return ch ^ 1;
+ case CanonicalizeAlternatingUnaligned:
+ return ((ch - 1) ^ 1) + 1;
+ default:
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ RELEASE_ASSERT_NOT_REACHED();
+ return 0;
+}
+
+// Returns true if no other UCS2 codepoint can match this value.
+inline bool isCanonicallyUnique(UChar32 ch, CanonicalMode canonicalMode = CanonicalMode::UCS2)
+{
+ return canonicalRangeInfoFor(ch, canonicalMode)->type == CanonicalizeUnique;
+}
+
+// Returns true if values are equal, under the canonicalization rules.
+inline bool areCanonicallyEquivalent(UChar32 a, UChar32 b, CanonicalMode canonicalMode = CanonicalMode::UCS2)
+{
+ const CanonicalizationRange* info = canonicalRangeInfoFor(a, canonicalMode);
+ switch (info->type) {
+ case CanonicalizeUnique:
+ return a == b;
+ case CanonicalizeSet: {
+ for (const UChar32* set = canonicalCharacterSetInfo(info->value, canonicalMode); (a = *set); ++set) {
+ if (a == b)
+ return true;
+ }
+ return false;
+ }
+ case CanonicalizeRangeLo:
+ return (a == b) || (a + info->value == b);
+ case CanonicalizeRangeHi:
+ return (a == b) || (a - info->value == b);
+ case CanonicalizeAlternatingAligned:
+ return (a | 1) == (b | 1);
+ case CanonicalizeAlternatingUnaligned:
+ return ((a - 1) | 1) == ((b - 1) | 1);
+ }
+
+ RELEASE_ASSERT_NOT_REACHED();
+ return false;
+}
+
+} } // JSC::Yarr
diff --git a/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp b/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp
index 7bb3d08eb5..d91c771590 100644
--- a/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp
+++ b/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2013, 2015-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,33 +23,31 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-// DO NOT EDIT! - this file autogenerated by YarrCanonicalizeUCS2.js
+// DO NOT EDIT! - this file autogenerated by YarrCanonicalize.js
#include "config.h"
-#include "YarrCanonicalizeUCS2.h"
+#include "YarrCanonicalize.h"
namespace JSC { namespace Yarr {
-#include <stdint.h>
-
-uint16_t ucs2CharacterSet0[] = { 0x01c4u, 0x01c5u, 0x01c6u, 0 };
-uint16_t ucs2CharacterSet1[] = { 0x01c7u, 0x01c8u, 0x01c9u, 0 };
-uint16_t ucs2CharacterSet2[] = { 0x01cau, 0x01cbu, 0x01ccu, 0 };
-uint16_t ucs2CharacterSet3[] = { 0x01f1u, 0x01f2u, 0x01f3u, 0 };
-uint16_t ucs2CharacterSet4[] = { 0x0392u, 0x03b2u, 0x03d0u, 0 };
-uint16_t ucs2CharacterSet5[] = { 0x0395u, 0x03b5u, 0x03f5u, 0 };
-uint16_t ucs2CharacterSet6[] = { 0x0398u, 0x03b8u, 0x03d1u, 0 };
-uint16_t ucs2CharacterSet7[] = { 0x0345u, 0x0399u, 0x03b9u, 0x1fbeu, 0 };
-uint16_t ucs2CharacterSet8[] = { 0x039au, 0x03bau, 0x03f0u, 0 };
-uint16_t ucs2CharacterSet9[] = { 0x00b5u, 0x039cu, 0x03bcu, 0 };
-uint16_t ucs2CharacterSet10[] = { 0x03a0u, 0x03c0u, 0x03d6u, 0 };
-uint16_t ucs2CharacterSet11[] = { 0x03a1u, 0x03c1u, 0x03f1u, 0 };
-uint16_t ucs2CharacterSet12[] = { 0x03a3u, 0x03c2u, 0x03c3u, 0 };
-uint16_t ucs2CharacterSet13[] = { 0x03a6u, 0x03c6u, 0x03d5u, 0 };
-uint16_t ucs2CharacterSet14[] = { 0x1e60u, 0x1e61u, 0x1e9bu, 0 };
+const UChar32 ucs2CharacterSet0[] = { 0x01c4, 0x01c5, 0x01c6, 0 };
+const UChar32 ucs2CharacterSet1[] = { 0x01c7, 0x01c8, 0x01c9, 0 };
+const UChar32 ucs2CharacterSet2[] = { 0x01ca, 0x01cb, 0x01cc, 0 };
+const UChar32 ucs2CharacterSet3[] = { 0x01f1, 0x01f2, 0x01f3, 0 };
+const UChar32 ucs2CharacterSet4[] = { 0x0392, 0x03b2, 0x03d0, 0 };
+const UChar32 ucs2CharacterSet5[] = { 0x0395, 0x03b5, 0x03f5, 0 };
+const UChar32 ucs2CharacterSet6[] = { 0x0398, 0x03b8, 0x03d1, 0 };
+const UChar32 ucs2CharacterSet7[] = { 0x0345, 0x0399, 0x03b9, 0x1fbe, 0 };
+const UChar32 ucs2CharacterSet8[] = { 0x039a, 0x03ba, 0x03f0, 0 };
+const UChar32 ucs2CharacterSet9[] = { 0x00b5, 0x039c, 0x03bc, 0 };
+const UChar32 ucs2CharacterSet10[] = { 0x03a0, 0x03c0, 0x03d6, 0 };
+const UChar32 ucs2CharacterSet11[] = { 0x03a1, 0x03c1, 0x03f1, 0 };
+const UChar32 ucs2CharacterSet12[] = { 0x03a3, 0x03c2, 0x03c3, 0 };
+const UChar32 ucs2CharacterSet13[] = { 0x03a6, 0x03c6, 0x03d5, 0 };
+const UChar32 ucs2CharacterSet14[] = { 0x1e60, 0x1e61, 0x1e9b, 0 };
static const size_t UCS2_CANONICALIZATION_SETS = 15;
-uint16_t* characterSetInfo[UCS2_CANONICALIZATION_SETS] = {
+const UChar32* const ucs2CharacterSetInfo[UCS2_CANONICALIZATION_SETS] = {
ucs2CharacterSet0,
ucs2CharacterSet1,
ucs2CharacterSet2,
@@ -67,396 +65,399 @@ uint16_t* characterSetInfo[UCS2_CANONICALIZATION_SETS] = {
ucs2CharacterSet14,
};
-const size_t UCS2_CANONICALIZATION_RANGES = 364;
-UCS2CanonicalizationRange rangeInfo[UCS2_CANONICALIZATION_RANGES] = {
- { 0x0000u, 0x0040u, 0x0000u, CanonicalizeUnique },
- { 0x0041u, 0x005au, 0x0020u, CanonicalizeRangeLo },
- { 0x005bu, 0x0060u, 0x0000u, CanonicalizeUnique },
- { 0x0061u, 0x007au, 0x0020u, CanonicalizeRangeHi },
- { 0x007bu, 0x00b4u, 0x0000u, CanonicalizeUnique },
- { 0x00b5u, 0x00b5u, 0x0009u, CanonicalizeSet },
- { 0x00b6u, 0x00bfu, 0x0000u, CanonicalizeUnique },
- { 0x00c0u, 0x00d6u, 0x0020u, CanonicalizeRangeLo },
- { 0x00d7u, 0x00d7u, 0x0000u, CanonicalizeUnique },
- { 0x00d8u, 0x00deu, 0x0020u, CanonicalizeRangeLo },
- { 0x00dfu, 0x00dfu, 0x0000u, CanonicalizeUnique },
- { 0x00e0u, 0x00f6u, 0x0020u, CanonicalizeRangeHi },
- { 0x00f7u, 0x00f7u, 0x0000u, CanonicalizeUnique },
- { 0x00f8u, 0x00feu, 0x0020u, CanonicalizeRangeHi },
- { 0x00ffu, 0x00ffu, 0x0079u, CanonicalizeRangeLo },
- { 0x0100u, 0x012fu, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x0130u, 0x0131u, 0x0000u, CanonicalizeUnique },
- { 0x0132u, 0x0137u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x0138u, 0x0138u, 0x0000u, CanonicalizeUnique },
- { 0x0139u, 0x0148u, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0x0149u, 0x0149u, 0x0000u, CanonicalizeUnique },
- { 0x014au, 0x0177u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x0178u, 0x0178u, 0x0079u, CanonicalizeRangeHi },
- { 0x0179u, 0x017eu, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0x017fu, 0x017fu, 0x0000u, CanonicalizeUnique },
- { 0x0180u, 0x0180u, 0x00c3u, CanonicalizeRangeLo },
- { 0x0181u, 0x0181u, 0x00d2u, CanonicalizeRangeLo },
- { 0x0182u, 0x0185u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x0186u, 0x0186u, 0x00ceu, CanonicalizeRangeLo },
- { 0x0187u, 0x0188u, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0x0189u, 0x018au, 0x00cdu, CanonicalizeRangeLo },
- { 0x018bu, 0x018cu, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0x018du, 0x018du, 0x0000u, CanonicalizeUnique },
- { 0x018eu, 0x018eu, 0x004fu, CanonicalizeRangeLo },
- { 0x018fu, 0x018fu, 0x00cau, CanonicalizeRangeLo },
- { 0x0190u, 0x0190u, 0x00cbu, CanonicalizeRangeLo },
- { 0x0191u, 0x0192u, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0x0193u, 0x0193u, 0x00cdu, CanonicalizeRangeLo },
- { 0x0194u, 0x0194u, 0x00cfu, CanonicalizeRangeLo },
- { 0x0195u, 0x0195u, 0x0061u, CanonicalizeRangeLo },
- { 0x0196u, 0x0196u, 0x00d3u, CanonicalizeRangeLo },
- { 0x0197u, 0x0197u, 0x00d1u, CanonicalizeRangeLo },
- { 0x0198u, 0x0199u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x019au, 0x019au, 0x00a3u, CanonicalizeRangeLo },
- { 0x019bu, 0x019bu, 0x0000u, CanonicalizeUnique },
- { 0x019cu, 0x019cu, 0x00d3u, CanonicalizeRangeLo },
- { 0x019du, 0x019du, 0x00d5u, CanonicalizeRangeLo },
- { 0x019eu, 0x019eu, 0x0082u, CanonicalizeRangeLo },
- { 0x019fu, 0x019fu, 0x00d6u, CanonicalizeRangeLo },
- { 0x01a0u, 0x01a5u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x01a6u, 0x01a6u, 0x00dau, CanonicalizeRangeLo },
- { 0x01a7u, 0x01a8u, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0x01a9u, 0x01a9u, 0x00dau, CanonicalizeRangeLo },
- { 0x01aau, 0x01abu, 0x0000u, CanonicalizeUnique },
- { 0x01acu, 0x01adu, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x01aeu, 0x01aeu, 0x00dau, CanonicalizeRangeLo },
- { 0x01afu, 0x01b0u, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0x01b1u, 0x01b2u, 0x00d9u, CanonicalizeRangeLo },
- { 0x01b3u, 0x01b6u, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0x01b7u, 0x01b7u, 0x00dbu, CanonicalizeRangeLo },
- { 0x01b8u, 0x01b9u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x01bau, 0x01bbu, 0x0000u, CanonicalizeUnique },
- { 0x01bcu, 0x01bdu, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x01beu, 0x01beu, 0x0000u, CanonicalizeUnique },
- { 0x01bfu, 0x01bfu, 0x0038u, CanonicalizeRangeLo },
- { 0x01c0u, 0x01c3u, 0x0000u, CanonicalizeUnique },
- { 0x01c4u, 0x01c6u, 0x0000u, CanonicalizeSet },
- { 0x01c7u, 0x01c9u, 0x0001u, CanonicalizeSet },
- { 0x01cau, 0x01ccu, 0x0002u, CanonicalizeSet },
- { 0x01cdu, 0x01dcu, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0x01ddu, 0x01ddu, 0x004fu, CanonicalizeRangeHi },
- { 0x01deu, 0x01efu, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x01f0u, 0x01f0u, 0x0000u, CanonicalizeUnique },
- { 0x01f1u, 0x01f3u, 0x0003u, CanonicalizeSet },
- { 0x01f4u, 0x01f5u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x01f6u, 0x01f6u, 0x0061u, CanonicalizeRangeHi },
- { 0x01f7u, 0x01f7u, 0x0038u, CanonicalizeRangeHi },
- { 0x01f8u, 0x021fu, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x0220u, 0x0220u, 0x0082u, CanonicalizeRangeHi },
- { 0x0221u, 0x0221u, 0x0000u, CanonicalizeUnique },
- { 0x0222u, 0x0233u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x0234u, 0x0239u, 0x0000u, CanonicalizeUnique },
- { 0x023au, 0x023au, 0x2a2bu, CanonicalizeRangeLo },
- { 0x023bu, 0x023cu, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0x023du, 0x023du, 0x00a3u, CanonicalizeRangeHi },
- { 0x023eu, 0x023eu, 0x2a28u, CanonicalizeRangeLo },
- { 0x023fu, 0x0240u, 0x2a3fu, CanonicalizeRangeLo },
- { 0x0241u, 0x0242u, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0x0243u, 0x0243u, 0x00c3u, CanonicalizeRangeHi },
- { 0x0244u, 0x0244u, 0x0045u, CanonicalizeRangeLo },
- { 0x0245u, 0x0245u, 0x0047u, CanonicalizeRangeLo },
- { 0x0246u, 0x024fu, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x0250u, 0x0250u, 0x2a1fu, CanonicalizeRangeLo },
- { 0x0251u, 0x0251u, 0x2a1cu, CanonicalizeRangeLo },
- { 0x0252u, 0x0252u, 0x2a1eu, CanonicalizeRangeLo },
- { 0x0253u, 0x0253u, 0x00d2u, CanonicalizeRangeHi },
- { 0x0254u, 0x0254u, 0x00ceu, CanonicalizeRangeHi },
- { 0x0255u, 0x0255u, 0x0000u, CanonicalizeUnique },
- { 0x0256u, 0x0257u, 0x00cdu, CanonicalizeRangeHi },
- { 0x0258u, 0x0258u, 0x0000u, CanonicalizeUnique },
- { 0x0259u, 0x0259u, 0x00cau, CanonicalizeRangeHi },
- { 0x025au, 0x025au, 0x0000u, CanonicalizeUnique },
- { 0x025bu, 0x025bu, 0x00cbu, CanonicalizeRangeHi },
- { 0x025cu, 0x025fu, 0x0000u, CanonicalizeUnique },
- { 0x0260u, 0x0260u, 0x00cdu, CanonicalizeRangeHi },
- { 0x0261u, 0x0262u, 0x0000u, CanonicalizeUnique },
- { 0x0263u, 0x0263u, 0x00cfu, CanonicalizeRangeHi },
- { 0x0264u, 0x0264u, 0x0000u, CanonicalizeUnique },
- { 0x0265u, 0x0265u, 0xa528u, CanonicalizeRangeLo },
- { 0x0266u, 0x0267u, 0x0000u, CanonicalizeUnique },
- { 0x0268u, 0x0268u, 0x00d1u, CanonicalizeRangeHi },
- { 0x0269u, 0x0269u, 0x00d3u, CanonicalizeRangeHi },
- { 0x026au, 0x026au, 0x0000u, CanonicalizeUnique },
- { 0x026bu, 0x026bu, 0x29f7u, CanonicalizeRangeLo },
- { 0x026cu, 0x026eu, 0x0000u, CanonicalizeUnique },
- { 0x026fu, 0x026fu, 0x00d3u, CanonicalizeRangeHi },
- { 0x0270u, 0x0270u, 0x0000u, CanonicalizeUnique },
- { 0x0271u, 0x0271u, 0x29fdu, CanonicalizeRangeLo },
- { 0x0272u, 0x0272u, 0x00d5u, CanonicalizeRangeHi },
- { 0x0273u, 0x0274u, 0x0000u, CanonicalizeUnique },
- { 0x0275u, 0x0275u, 0x00d6u, CanonicalizeRangeHi },
- { 0x0276u, 0x027cu, 0x0000u, CanonicalizeUnique },
- { 0x027du, 0x027du, 0x29e7u, CanonicalizeRangeLo },
- { 0x027eu, 0x027fu, 0x0000u, CanonicalizeUnique },
- { 0x0280u, 0x0280u, 0x00dau, CanonicalizeRangeHi },
- { 0x0281u, 0x0282u, 0x0000u, CanonicalizeUnique },
- { 0x0283u, 0x0283u, 0x00dau, CanonicalizeRangeHi },
- { 0x0284u, 0x0287u, 0x0000u, CanonicalizeUnique },
- { 0x0288u, 0x0288u, 0x00dau, CanonicalizeRangeHi },
- { 0x0289u, 0x0289u, 0x0045u, CanonicalizeRangeHi },
- { 0x028au, 0x028bu, 0x00d9u, CanonicalizeRangeHi },
- { 0x028cu, 0x028cu, 0x0047u, CanonicalizeRangeHi },
- { 0x028du, 0x0291u, 0x0000u, CanonicalizeUnique },
- { 0x0292u, 0x0292u, 0x00dbu, CanonicalizeRangeHi },
- { 0x0293u, 0x0344u, 0x0000u, CanonicalizeUnique },
- { 0x0345u, 0x0345u, 0x0007u, CanonicalizeSet },
- { 0x0346u, 0x036fu, 0x0000u, CanonicalizeUnique },
- { 0x0370u, 0x0373u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x0374u, 0x0375u, 0x0000u, CanonicalizeUnique },
- { 0x0376u, 0x0377u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x0378u, 0x037au, 0x0000u, CanonicalizeUnique },
- { 0x037bu, 0x037du, 0x0082u, CanonicalizeRangeLo },
- { 0x037eu, 0x0385u, 0x0000u, CanonicalizeUnique },
- { 0x0386u, 0x0386u, 0x0026u, CanonicalizeRangeLo },
- { 0x0387u, 0x0387u, 0x0000u, CanonicalizeUnique },
- { 0x0388u, 0x038au, 0x0025u, CanonicalizeRangeLo },
- { 0x038bu, 0x038bu, 0x0000u, CanonicalizeUnique },
- { 0x038cu, 0x038cu, 0x0040u, CanonicalizeRangeLo },
- { 0x038du, 0x038du, 0x0000u, CanonicalizeUnique },
- { 0x038eu, 0x038fu, 0x003fu, CanonicalizeRangeLo },
- { 0x0390u, 0x0390u, 0x0000u, CanonicalizeUnique },
- { 0x0391u, 0x0391u, 0x0020u, CanonicalizeRangeLo },
- { 0x0392u, 0x0392u, 0x0004u, CanonicalizeSet },
- { 0x0393u, 0x0394u, 0x0020u, CanonicalizeRangeLo },
- { 0x0395u, 0x0395u, 0x0005u, CanonicalizeSet },
- { 0x0396u, 0x0397u, 0x0020u, CanonicalizeRangeLo },
- { 0x0398u, 0x0398u, 0x0006u, CanonicalizeSet },
- { 0x0399u, 0x0399u, 0x0007u, CanonicalizeSet },
- { 0x039au, 0x039au, 0x0008u, CanonicalizeSet },
- { 0x039bu, 0x039bu, 0x0020u, CanonicalizeRangeLo },
- { 0x039cu, 0x039cu, 0x0009u, CanonicalizeSet },
- { 0x039du, 0x039fu, 0x0020u, CanonicalizeRangeLo },
- { 0x03a0u, 0x03a0u, 0x000au, CanonicalizeSet },
- { 0x03a1u, 0x03a1u, 0x000bu, CanonicalizeSet },
- { 0x03a2u, 0x03a2u, 0x0000u, CanonicalizeUnique },
- { 0x03a3u, 0x03a3u, 0x000cu, CanonicalizeSet },
- { 0x03a4u, 0x03a5u, 0x0020u, CanonicalizeRangeLo },
- { 0x03a6u, 0x03a6u, 0x000du, CanonicalizeSet },
- { 0x03a7u, 0x03abu, 0x0020u, CanonicalizeRangeLo },
- { 0x03acu, 0x03acu, 0x0026u, CanonicalizeRangeHi },
- { 0x03adu, 0x03afu, 0x0025u, CanonicalizeRangeHi },
- { 0x03b0u, 0x03b0u, 0x0000u, CanonicalizeUnique },
- { 0x03b1u, 0x03b1u, 0x0020u, CanonicalizeRangeHi },
- { 0x03b2u, 0x03b2u, 0x0004u, CanonicalizeSet },
- { 0x03b3u, 0x03b4u, 0x0020u, CanonicalizeRangeHi },
- { 0x03b5u, 0x03b5u, 0x0005u, CanonicalizeSet },
- { 0x03b6u, 0x03b7u, 0x0020u, CanonicalizeRangeHi },
- { 0x03b8u, 0x03b8u, 0x0006u, CanonicalizeSet },
- { 0x03b9u, 0x03b9u, 0x0007u, CanonicalizeSet },
- { 0x03bau, 0x03bau, 0x0008u, CanonicalizeSet },
- { 0x03bbu, 0x03bbu, 0x0020u, CanonicalizeRangeHi },
- { 0x03bcu, 0x03bcu, 0x0009u, CanonicalizeSet },
- { 0x03bdu, 0x03bfu, 0x0020u, CanonicalizeRangeHi },
- { 0x03c0u, 0x03c0u, 0x000au, CanonicalizeSet },
- { 0x03c1u, 0x03c1u, 0x000bu, CanonicalizeSet },
- { 0x03c2u, 0x03c3u, 0x000cu, CanonicalizeSet },
- { 0x03c4u, 0x03c5u, 0x0020u, CanonicalizeRangeHi },
- { 0x03c6u, 0x03c6u, 0x000du, CanonicalizeSet },
- { 0x03c7u, 0x03cbu, 0x0020u, CanonicalizeRangeHi },
- { 0x03ccu, 0x03ccu, 0x0040u, CanonicalizeRangeHi },
- { 0x03cdu, 0x03ceu, 0x003fu, CanonicalizeRangeHi },
- { 0x03cfu, 0x03cfu, 0x0008u, CanonicalizeRangeLo },
- { 0x03d0u, 0x03d0u, 0x0004u, CanonicalizeSet },
- { 0x03d1u, 0x03d1u, 0x0006u, CanonicalizeSet },
- { 0x03d2u, 0x03d4u, 0x0000u, CanonicalizeUnique },
- { 0x03d5u, 0x03d5u, 0x000du, CanonicalizeSet },
- { 0x03d6u, 0x03d6u, 0x000au, CanonicalizeSet },
- { 0x03d7u, 0x03d7u, 0x0008u, CanonicalizeRangeHi },
- { 0x03d8u, 0x03efu, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x03f0u, 0x03f0u, 0x0008u, CanonicalizeSet },
- { 0x03f1u, 0x03f1u, 0x000bu, CanonicalizeSet },
- { 0x03f2u, 0x03f2u, 0x0007u, CanonicalizeRangeLo },
- { 0x03f3u, 0x03f4u, 0x0000u, CanonicalizeUnique },
- { 0x03f5u, 0x03f5u, 0x0005u, CanonicalizeSet },
- { 0x03f6u, 0x03f6u, 0x0000u, CanonicalizeUnique },
- { 0x03f7u, 0x03f8u, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0x03f9u, 0x03f9u, 0x0007u, CanonicalizeRangeHi },
- { 0x03fau, 0x03fbu, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x03fcu, 0x03fcu, 0x0000u, CanonicalizeUnique },
- { 0x03fdu, 0x03ffu, 0x0082u, CanonicalizeRangeHi },
- { 0x0400u, 0x040fu, 0x0050u, CanonicalizeRangeLo },
- { 0x0410u, 0x042fu, 0x0020u, CanonicalizeRangeLo },
- { 0x0430u, 0x044fu, 0x0020u, CanonicalizeRangeHi },
- { 0x0450u, 0x045fu, 0x0050u, CanonicalizeRangeHi },
- { 0x0460u, 0x0481u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x0482u, 0x0489u, 0x0000u, CanonicalizeUnique },
- { 0x048au, 0x04bfu, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x04c0u, 0x04c0u, 0x000fu, CanonicalizeRangeLo },
- { 0x04c1u, 0x04ceu, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0x04cfu, 0x04cfu, 0x000fu, CanonicalizeRangeHi },
- { 0x04d0u, 0x0527u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x0528u, 0x0530u, 0x0000u, CanonicalizeUnique },
- { 0x0531u, 0x0556u, 0x0030u, CanonicalizeRangeLo },
- { 0x0557u, 0x0560u, 0x0000u, CanonicalizeUnique },
- { 0x0561u, 0x0586u, 0x0030u, CanonicalizeRangeHi },
- { 0x0587u, 0x109fu, 0x0000u, CanonicalizeUnique },
- { 0x10a0u, 0x10c5u, 0x1c60u, CanonicalizeRangeLo },
- { 0x10c6u, 0x1d78u, 0x0000u, CanonicalizeUnique },
- { 0x1d79u, 0x1d79u, 0x8a04u, CanonicalizeRangeLo },
- { 0x1d7au, 0x1d7cu, 0x0000u, CanonicalizeUnique },
- { 0x1d7du, 0x1d7du, 0x0ee6u, CanonicalizeRangeLo },
- { 0x1d7eu, 0x1dffu, 0x0000u, CanonicalizeUnique },
- { 0x1e00u, 0x1e5fu, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x1e60u, 0x1e61u, 0x000eu, CanonicalizeSet },
- { 0x1e62u, 0x1e95u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x1e96u, 0x1e9au, 0x0000u, CanonicalizeUnique },
- { 0x1e9bu, 0x1e9bu, 0x000eu, CanonicalizeSet },
- { 0x1e9cu, 0x1e9fu, 0x0000u, CanonicalizeUnique },
- { 0x1ea0u, 0x1effu, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x1f00u, 0x1f07u, 0x0008u, CanonicalizeRangeLo },
- { 0x1f08u, 0x1f0fu, 0x0008u, CanonicalizeRangeHi },
- { 0x1f10u, 0x1f15u, 0x0008u, CanonicalizeRangeLo },
- { 0x1f16u, 0x1f17u, 0x0000u, CanonicalizeUnique },
- { 0x1f18u, 0x1f1du, 0x0008u, CanonicalizeRangeHi },
- { 0x1f1eu, 0x1f1fu, 0x0000u, CanonicalizeUnique },
- { 0x1f20u, 0x1f27u, 0x0008u, CanonicalizeRangeLo },
- { 0x1f28u, 0x1f2fu, 0x0008u, CanonicalizeRangeHi },
- { 0x1f30u, 0x1f37u, 0x0008u, CanonicalizeRangeLo },
- { 0x1f38u, 0x1f3fu, 0x0008u, CanonicalizeRangeHi },
- { 0x1f40u, 0x1f45u, 0x0008u, CanonicalizeRangeLo },
- { 0x1f46u, 0x1f47u, 0x0000u, CanonicalizeUnique },
- { 0x1f48u, 0x1f4du, 0x0008u, CanonicalizeRangeHi },
- { 0x1f4eu, 0x1f50u, 0x0000u, CanonicalizeUnique },
- { 0x1f51u, 0x1f51u, 0x0008u, CanonicalizeRangeLo },
- { 0x1f52u, 0x1f52u, 0x0000u, CanonicalizeUnique },
- { 0x1f53u, 0x1f53u, 0x0008u, CanonicalizeRangeLo },
- { 0x1f54u, 0x1f54u, 0x0000u, CanonicalizeUnique },
- { 0x1f55u, 0x1f55u, 0x0008u, CanonicalizeRangeLo },
- { 0x1f56u, 0x1f56u, 0x0000u, CanonicalizeUnique },
- { 0x1f57u, 0x1f57u, 0x0008u, CanonicalizeRangeLo },
- { 0x1f58u, 0x1f58u, 0x0000u, CanonicalizeUnique },
- { 0x1f59u, 0x1f59u, 0x0008u, CanonicalizeRangeHi },
- { 0x1f5au, 0x1f5au, 0x0000u, CanonicalizeUnique },
- { 0x1f5bu, 0x1f5bu, 0x0008u, CanonicalizeRangeHi },
- { 0x1f5cu, 0x1f5cu, 0x0000u, CanonicalizeUnique },
- { 0x1f5du, 0x1f5du, 0x0008u, CanonicalizeRangeHi },
- { 0x1f5eu, 0x1f5eu, 0x0000u, CanonicalizeUnique },
- { 0x1f5fu, 0x1f5fu, 0x0008u, CanonicalizeRangeHi },
- { 0x1f60u, 0x1f67u, 0x0008u, CanonicalizeRangeLo },
- { 0x1f68u, 0x1f6fu, 0x0008u, CanonicalizeRangeHi },
- { 0x1f70u, 0x1f71u, 0x004au, CanonicalizeRangeLo },
- { 0x1f72u, 0x1f75u, 0x0056u, CanonicalizeRangeLo },
- { 0x1f76u, 0x1f77u, 0x0064u, CanonicalizeRangeLo },
- { 0x1f78u, 0x1f79u, 0x0080u, CanonicalizeRangeLo },
- { 0x1f7au, 0x1f7bu, 0x0070u, CanonicalizeRangeLo },
- { 0x1f7cu, 0x1f7du, 0x007eu, CanonicalizeRangeLo },
- { 0x1f7eu, 0x1fafu, 0x0000u, CanonicalizeUnique },
- { 0x1fb0u, 0x1fb1u, 0x0008u, CanonicalizeRangeLo },
- { 0x1fb2u, 0x1fb7u, 0x0000u, CanonicalizeUnique },
- { 0x1fb8u, 0x1fb9u, 0x0008u, CanonicalizeRangeHi },
- { 0x1fbau, 0x1fbbu, 0x004au, CanonicalizeRangeHi },
- { 0x1fbcu, 0x1fbdu, 0x0000u, CanonicalizeUnique },
- { 0x1fbeu, 0x1fbeu, 0x0007u, CanonicalizeSet },
- { 0x1fbfu, 0x1fc7u, 0x0000u, CanonicalizeUnique },
- { 0x1fc8u, 0x1fcbu, 0x0056u, CanonicalizeRangeHi },
- { 0x1fccu, 0x1fcfu, 0x0000u, CanonicalizeUnique },
- { 0x1fd0u, 0x1fd1u, 0x0008u, CanonicalizeRangeLo },
- { 0x1fd2u, 0x1fd7u, 0x0000u, CanonicalizeUnique },
- { 0x1fd8u, 0x1fd9u, 0x0008u, CanonicalizeRangeHi },
- { 0x1fdau, 0x1fdbu, 0x0064u, CanonicalizeRangeHi },
- { 0x1fdcu, 0x1fdfu, 0x0000u, CanonicalizeUnique },
- { 0x1fe0u, 0x1fe1u, 0x0008u, CanonicalizeRangeLo },
- { 0x1fe2u, 0x1fe4u, 0x0000u, CanonicalizeUnique },
- { 0x1fe5u, 0x1fe5u, 0x0007u, CanonicalizeRangeLo },
- { 0x1fe6u, 0x1fe7u, 0x0000u, CanonicalizeUnique },
- { 0x1fe8u, 0x1fe9u, 0x0008u, CanonicalizeRangeHi },
- { 0x1feau, 0x1febu, 0x0070u, CanonicalizeRangeHi },
- { 0x1fecu, 0x1fecu, 0x0007u, CanonicalizeRangeHi },
- { 0x1fedu, 0x1ff7u, 0x0000u, CanonicalizeUnique },
- { 0x1ff8u, 0x1ff9u, 0x0080u, CanonicalizeRangeHi },
- { 0x1ffau, 0x1ffbu, 0x007eu, CanonicalizeRangeHi },
- { 0x1ffcu, 0x2131u, 0x0000u, CanonicalizeUnique },
- { 0x2132u, 0x2132u, 0x001cu, CanonicalizeRangeLo },
- { 0x2133u, 0x214du, 0x0000u, CanonicalizeUnique },
- { 0x214eu, 0x214eu, 0x001cu, CanonicalizeRangeHi },
- { 0x214fu, 0x215fu, 0x0000u, CanonicalizeUnique },
- { 0x2160u, 0x216fu, 0x0010u, CanonicalizeRangeLo },
- { 0x2170u, 0x217fu, 0x0010u, CanonicalizeRangeHi },
- { 0x2180u, 0x2182u, 0x0000u, CanonicalizeUnique },
- { 0x2183u, 0x2184u, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0x2185u, 0x24b5u, 0x0000u, CanonicalizeUnique },
- { 0x24b6u, 0x24cfu, 0x001au, CanonicalizeRangeLo },
- { 0x24d0u, 0x24e9u, 0x001au, CanonicalizeRangeHi },
- { 0x24eau, 0x2bffu, 0x0000u, CanonicalizeUnique },
- { 0x2c00u, 0x2c2eu, 0x0030u, CanonicalizeRangeLo },
- { 0x2c2fu, 0x2c2fu, 0x0000u, CanonicalizeUnique },
- { 0x2c30u, 0x2c5eu, 0x0030u, CanonicalizeRangeHi },
- { 0x2c5fu, 0x2c5fu, 0x0000u, CanonicalizeUnique },
- { 0x2c60u, 0x2c61u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x2c62u, 0x2c62u, 0x29f7u, CanonicalizeRangeHi },
- { 0x2c63u, 0x2c63u, 0x0ee6u, CanonicalizeRangeHi },
- { 0x2c64u, 0x2c64u, 0x29e7u, CanonicalizeRangeHi },
- { 0x2c65u, 0x2c65u, 0x2a2bu, CanonicalizeRangeHi },
- { 0x2c66u, 0x2c66u, 0x2a28u, CanonicalizeRangeHi },
- { 0x2c67u, 0x2c6cu, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0x2c6du, 0x2c6du, 0x2a1cu, CanonicalizeRangeHi },
- { 0x2c6eu, 0x2c6eu, 0x29fdu, CanonicalizeRangeHi },
- { 0x2c6fu, 0x2c6fu, 0x2a1fu, CanonicalizeRangeHi },
- { 0x2c70u, 0x2c70u, 0x2a1eu, CanonicalizeRangeHi },
- { 0x2c71u, 0x2c71u, 0x0000u, CanonicalizeUnique },
- { 0x2c72u, 0x2c73u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x2c74u, 0x2c74u, 0x0000u, CanonicalizeUnique },
- { 0x2c75u, 0x2c76u, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0x2c77u, 0x2c7du, 0x0000u, CanonicalizeUnique },
- { 0x2c7eu, 0x2c7fu, 0x2a3fu, CanonicalizeRangeHi },
- { 0x2c80u, 0x2ce3u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0x2ce4u, 0x2ceau, 0x0000u, CanonicalizeUnique },
- { 0x2cebu, 0x2ceeu, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0x2cefu, 0x2cffu, 0x0000u, CanonicalizeUnique },
- { 0x2d00u, 0x2d25u, 0x1c60u, CanonicalizeRangeHi },
- { 0x2d26u, 0xa63fu, 0x0000u, CanonicalizeUnique },
- { 0xa640u, 0xa66du, 0x0000u, CanonicalizeAlternatingAligned },
- { 0xa66eu, 0xa67fu, 0x0000u, CanonicalizeUnique },
- { 0xa680u, 0xa697u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0xa698u, 0xa721u, 0x0000u, CanonicalizeUnique },
- { 0xa722u, 0xa72fu, 0x0000u, CanonicalizeAlternatingAligned },
- { 0xa730u, 0xa731u, 0x0000u, CanonicalizeUnique },
- { 0xa732u, 0xa76fu, 0x0000u, CanonicalizeAlternatingAligned },
- { 0xa770u, 0xa778u, 0x0000u, CanonicalizeUnique },
- { 0xa779u, 0xa77cu, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0xa77du, 0xa77du, 0x8a04u, CanonicalizeRangeHi },
- { 0xa77eu, 0xa787u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0xa788u, 0xa78au, 0x0000u, CanonicalizeUnique },
- { 0xa78bu, 0xa78cu, 0x0000u, CanonicalizeAlternatingUnaligned },
- { 0xa78du, 0xa78du, 0xa528u, CanonicalizeRangeHi },
- { 0xa78eu, 0xa78fu, 0x0000u, CanonicalizeUnique },
- { 0xa790u, 0xa791u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0xa792u, 0xa79fu, 0x0000u, CanonicalizeUnique },
- { 0xa7a0u, 0xa7a9u, 0x0000u, CanonicalizeAlternatingAligned },
- { 0xa7aau, 0xff20u, 0x0000u, CanonicalizeUnique },
- { 0xff21u, 0xff3au, 0x0020u, CanonicalizeRangeLo },
- { 0xff3bu, 0xff40u, 0x0000u, CanonicalizeUnique },
- { 0xff41u, 0xff5au, 0x0020u, CanonicalizeRangeHi },
- { 0xff5bu, 0xffffu, 0x0000u, CanonicalizeUnique },
-};
-
-const size_t LATIN_CANONICALIZATION_RANGES = 20;
-LatinCanonicalizationRange latinRangeInfo[LATIN_CANONICALIZATION_RANGES] = {
- { 0x0000u, 0x0040u, 0x0000u, CanonicalizeLatinSelf },
- { 0x0041u, 0x005au, 0x0000u, CanonicalizeLatinMask0x20 },
- { 0x005bu, 0x0060u, 0x0000u, CanonicalizeLatinSelf },
- { 0x0061u, 0x007au, 0x0000u, CanonicalizeLatinMask0x20 },
- { 0x007bu, 0x00bfu, 0x0000u, CanonicalizeLatinSelf },
- { 0x00c0u, 0x00d6u, 0x0000u, CanonicalizeLatinMask0x20 },
- { 0x00d7u, 0x00d7u, 0x0000u, CanonicalizeLatinSelf },
- { 0x00d8u, 0x00deu, 0x0000u, CanonicalizeLatinMask0x20 },
- { 0x00dfu, 0x00dfu, 0x0000u, CanonicalizeLatinSelf },
- { 0x00e0u, 0x00f6u, 0x0000u, CanonicalizeLatinMask0x20 },
- { 0x00f7u, 0x00f7u, 0x0000u, CanonicalizeLatinSelf },
- { 0x00f8u, 0x00feu, 0x0000u, CanonicalizeLatinMask0x20 },
- { 0x00ffu, 0x00ffu, 0x0000u, CanonicalizeLatinSelf },
- { 0x0100u, 0x0177u, 0x0000u, CanonicalizeLatinInvalid },
- { 0x0178u, 0x0178u, 0x00ffu, CanonicalizeLatinOther },
- { 0x0179u, 0x039bu, 0x0000u, CanonicalizeLatinInvalid },
- { 0x039cu, 0x039cu, 0x00b5u, CanonicalizeLatinOther },
- { 0x039du, 0x03bbu, 0x0000u, CanonicalizeLatinInvalid },
- { 0x03bcu, 0x03bcu, 0x00b5u, CanonicalizeLatinOther },
- { 0x03bdu, 0xffffu, 0x0000u, CanonicalizeLatinInvalid },
+const size_t UCS2_CANONICALIZATION_RANGES = 391;
+const CanonicalizationRange ucs2RangeInfo[UCS2_CANONICALIZATION_RANGES] = {
+ { 0x0000, 0x0040, 0x0000, CanonicalizeUnique },
+ { 0x0041, 0x005a, 0x0020, CanonicalizeRangeLo },
+ { 0x005b, 0x0060, 0x0000, CanonicalizeUnique },
+ { 0x0061, 0x007a, 0x0020, CanonicalizeRangeHi },
+ { 0x007b, 0x00b4, 0x0000, CanonicalizeUnique },
+ { 0x00b5, 0x00b5, 0x0009, CanonicalizeSet },
+ { 0x00b6, 0x00bf, 0x0000, CanonicalizeUnique },
+ { 0x00c0, 0x00d6, 0x0020, CanonicalizeRangeLo },
+ { 0x00d7, 0x00d7, 0x0000, CanonicalizeUnique },
+ { 0x00d8, 0x00de, 0x0020, CanonicalizeRangeLo },
+ { 0x00df, 0x00df, 0x0000, CanonicalizeUnique },
+ { 0x00e0, 0x00f6, 0x0020, CanonicalizeRangeHi },
+ { 0x00f7, 0x00f7, 0x0000, CanonicalizeUnique },
+ { 0x00f8, 0x00fe, 0x0020, CanonicalizeRangeHi },
+ { 0x00ff, 0x00ff, 0x0079, CanonicalizeRangeLo },
+ { 0x0100, 0x012f, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0130, 0x0131, 0x0000, CanonicalizeUnique },
+ { 0x0132, 0x0137, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0138, 0x0138, 0x0000, CanonicalizeUnique },
+ { 0x0139, 0x0148, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x0149, 0x0149, 0x0000, CanonicalizeUnique },
+ { 0x014a, 0x0177, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0178, 0x0178, 0x0079, CanonicalizeRangeHi },
+ { 0x0179, 0x017e, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x017f, 0x017f, 0x0000, CanonicalizeUnique },
+ { 0x0180, 0x0180, 0x00c3, CanonicalizeRangeLo },
+ { 0x0181, 0x0181, 0x00d2, CanonicalizeRangeLo },
+ { 0x0182, 0x0185, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0186, 0x0186, 0x00ce, CanonicalizeRangeLo },
+ { 0x0187, 0x0188, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x0189, 0x018a, 0x00cd, CanonicalizeRangeLo },
+ { 0x018b, 0x018c, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x018d, 0x018d, 0x0000, CanonicalizeUnique },
+ { 0x018e, 0x018e, 0x004f, CanonicalizeRangeLo },
+ { 0x018f, 0x018f, 0x00ca, CanonicalizeRangeLo },
+ { 0x0190, 0x0190, 0x00cb, CanonicalizeRangeLo },
+ { 0x0191, 0x0192, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x0193, 0x0193, 0x00cd, CanonicalizeRangeLo },
+ { 0x0194, 0x0194, 0x00cf, CanonicalizeRangeLo },
+ { 0x0195, 0x0195, 0x0061, CanonicalizeRangeLo },
+ { 0x0196, 0x0196, 0x00d3, CanonicalizeRangeLo },
+ { 0x0197, 0x0197, 0x00d1, CanonicalizeRangeLo },
+ { 0x0198, 0x0199, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x019a, 0x019a, 0x00a3, CanonicalizeRangeLo },
+ { 0x019b, 0x019b, 0x0000, CanonicalizeUnique },
+ { 0x019c, 0x019c, 0x00d3, CanonicalizeRangeLo },
+ { 0x019d, 0x019d, 0x00d5, CanonicalizeRangeLo },
+ { 0x019e, 0x019e, 0x0082, CanonicalizeRangeLo },
+ { 0x019f, 0x019f, 0x00d6, CanonicalizeRangeLo },
+ { 0x01a0, 0x01a5, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x01a6, 0x01a6, 0x00da, CanonicalizeRangeLo },
+ { 0x01a7, 0x01a8, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x01a9, 0x01a9, 0x00da, CanonicalizeRangeLo },
+ { 0x01aa, 0x01ab, 0x0000, CanonicalizeUnique },
+ { 0x01ac, 0x01ad, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x01ae, 0x01ae, 0x00da, CanonicalizeRangeLo },
+ { 0x01af, 0x01b0, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x01b1, 0x01b2, 0x00d9, CanonicalizeRangeLo },
+ { 0x01b3, 0x01b6, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x01b7, 0x01b7, 0x00db, CanonicalizeRangeLo },
+ { 0x01b8, 0x01b9, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x01ba, 0x01bb, 0x0000, CanonicalizeUnique },
+ { 0x01bc, 0x01bd, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x01be, 0x01be, 0x0000, CanonicalizeUnique },
+ { 0x01bf, 0x01bf, 0x0038, CanonicalizeRangeLo },
+ { 0x01c0, 0x01c3, 0x0000, CanonicalizeUnique },
+ { 0x01c4, 0x01c6, 0x0000, CanonicalizeSet },
+ { 0x01c7, 0x01c9, 0x0001, CanonicalizeSet },
+ { 0x01ca, 0x01cc, 0x0002, CanonicalizeSet },
+ { 0x01cd, 0x01dc, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x01dd, 0x01dd, 0x004f, CanonicalizeRangeHi },
+ { 0x01de, 0x01ef, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x01f0, 0x01f0, 0x0000, CanonicalizeUnique },
+ { 0x01f1, 0x01f3, 0x0003, CanonicalizeSet },
+ { 0x01f4, 0x01f5, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x01f6, 0x01f6, 0x0061, CanonicalizeRangeHi },
+ { 0x01f7, 0x01f7, 0x0038, CanonicalizeRangeHi },
+ { 0x01f8, 0x021f, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0220, 0x0220, 0x0082, CanonicalizeRangeHi },
+ { 0x0221, 0x0221, 0x0000, CanonicalizeUnique },
+ { 0x0222, 0x0233, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0234, 0x0239, 0x0000, CanonicalizeUnique },
+ { 0x023a, 0x023a, 0x2a2b, CanonicalizeRangeLo },
+ { 0x023b, 0x023c, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x023d, 0x023d, 0x00a3, CanonicalizeRangeHi },
+ { 0x023e, 0x023e, 0x2a28, CanonicalizeRangeLo },
+ { 0x023f, 0x0240, 0x2a3f, CanonicalizeRangeLo },
+ { 0x0241, 0x0242, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x0243, 0x0243, 0x00c3, CanonicalizeRangeHi },
+ { 0x0244, 0x0244, 0x0045, CanonicalizeRangeLo },
+ { 0x0245, 0x0245, 0x0047, CanonicalizeRangeLo },
+ { 0x0246, 0x024f, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0250, 0x0250, 0x2a1f, CanonicalizeRangeLo },
+ { 0x0251, 0x0251, 0x2a1c, CanonicalizeRangeLo },
+ { 0x0252, 0x0252, 0x2a1e, CanonicalizeRangeLo },
+ { 0x0253, 0x0253, 0x00d2, CanonicalizeRangeHi },
+ { 0x0254, 0x0254, 0x00ce, CanonicalizeRangeHi },
+ { 0x0255, 0x0255, 0x0000, CanonicalizeUnique },
+ { 0x0256, 0x0257, 0x00cd, CanonicalizeRangeHi },
+ { 0x0258, 0x0258, 0x0000, CanonicalizeUnique },
+ { 0x0259, 0x0259, 0x00ca, CanonicalizeRangeHi },
+ { 0x025a, 0x025a, 0x0000, CanonicalizeUnique },
+ { 0x025b, 0x025b, 0x00cb, CanonicalizeRangeHi },
+ { 0x025c, 0x025c, 0xa54f, CanonicalizeRangeLo },
+ { 0x025d, 0x025f, 0x0000, CanonicalizeUnique },
+ { 0x0260, 0x0260, 0x00cd, CanonicalizeRangeHi },
+ { 0x0261, 0x0261, 0xa54b, CanonicalizeRangeLo },
+ { 0x0262, 0x0262, 0x0000, CanonicalizeUnique },
+ { 0x0263, 0x0263, 0x00cf, CanonicalizeRangeHi },
+ { 0x0264, 0x0264, 0x0000, CanonicalizeUnique },
+ { 0x0265, 0x0265, 0xa528, CanonicalizeRangeLo },
+ { 0x0266, 0x0266, 0xa544, CanonicalizeRangeLo },
+ { 0x0267, 0x0267, 0x0000, CanonicalizeUnique },
+ { 0x0268, 0x0268, 0x00d1, CanonicalizeRangeHi },
+ { 0x0269, 0x0269, 0x00d3, CanonicalizeRangeHi },
+ { 0x026a, 0x026a, 0x0000, CanonicalizeUnique },
+ { 0x026b, 0x026b, 0x29f7, CanonicalizeRangeLo },
+ { 0x026c, 0x026c, 0xa541, CanonicalizeRangeLo },
+ { 0x026d, 0x026e, 0x0000, CanonicalizeUnique },
+ { 0x026f, 0x026f, 0x00d3, CanonicalizeRangeHi },
+ { 0x0270, 0x0270, 0x0000, CanonicalizeUnique },
+ { 0x0271, 0x0271, 0x29fd, CanonicalizeRangeLo },
+ { 0x0272, 0x0272, 0x00d5, CanonicalizeRangeHi },
+ { 0x0273, 0x0274, 0x0000, CanonicalizeUnique },
+ { 0x0275, 0x0275, 0x00d6, CanonicalizeRangeHi },
+ { 0x0276, 0x027c, 0x0000, CanonicalizeUnique },
+ { 0x027d, 0x027d, 0x29e7, CanonicalizeRangeLo },
+ { 0x027e, 0x027f, 0x0000, CanonicalizeUnique },
+ { 0x0280, 0x0280, 0x00da, CanonicalizeRangeHi },
+ { 0x0281, 0x0282, 0x0000, CanonicalizeUnique },
+ { 0x0283, 0x0283, 0x00da, CanonicalizeRangeHi },
+ { 0x0284, 0x0286, 0x0000, CanonicalizeUnique },
+ { 0x0287, 0x0287, 0xa52a, CanonicalizeRangeLo },
+ { 0x0288, 0x0288, 0x00da, CanonicalizeRangeHi },
+ { 0x0289, 0x0289, 0x0045, CanonicalizeRangeHi },
+ { 0x028a, 0x028b, 0x00d9, CanonicalizeRangeHi },
+ { 0x028c, 0x028c, 0x0047, CanonicalizeRangeHi },
+ { 0x028d, 0x0291, 0x0000, CanonicalizeUnique },
+ { 0x0292, 0x0292, 0x00db, CanonicalizeRangeHi },
+ { 0x0293, 0x029d, 0x0000, CanonicalizeUnique },
+ { 0x029e, 0x029e, 0xa512, CanonicalizeRangeLo },
+ { 0x029f, 0x0344, 0x0000, CanonicalizeUnique },
+ { 0x0345, 0x0345, 0x0007, CanonicalizeSet },
+ { 0x0346, 0x036f, 0x0000, CanonicalizeUnique },
+ { 0x0370, 0x0373, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0374, 0x0375, 0x0000, CanonicalizeUnique },
+ { 0x0376, 0x0377, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0378, 0x037a, 0x0000, CanonicalizeUnique },
+ { 0x037b, 0x037d, 0x0082, CanonicalizeRangeLo },
+ { 0x037e, 0x037e, 0x0000, CanonicalizeUnique },
+ { 0x037f, 0x037f, 0x0074, CanonicalizeRangeLo },
+ { 0x0380, 0x0385, 0x0000, CanonicalizeUnique },
+ { 0x0386, 0x0386, 0x0026, CanonicalizeRangeLo },
+ { 0x0387, 0x0387, 0x0000, CanonicalizeUnique },
+ { 0x0388, 0x038a, 0x0025, CanonicalizeRangeLo },
+ { 0x038b, 0x038b, 0x0000, CanonicalizeUnique },
+ { 0x038c, 0x038c, 0x0040, CanonicalizeRangeLo },
+ { 0x038d, 0x038d, 0x0000, CanonicalizeUnique },
+ { 0x038e, 0x038f, 0x003f, CanonicalizeRangeLo },
+ { 0x0390, 0x0390, 0x0000, CanonicalizeUnique },
+ { 0x0391, 0x0391, 0x0020, CanonicalizeRangeLo },
+ { 0x0392, 0x0392, 0x0004, CanonicalizeSet },
+ { 0x0393, 0x0394, 0x0020, CanonicalizeRangeLo },
+ { 0x0395, 0x0395, 0x0005, CanonicalizeSet },
+ { 0x0396, 0x0397, 0x0020, CanonicalizeRangeLo },
+ { 0x0398, 0x0398, 0x0006, CanonicalizeSet },
+ { 0x0399, 0x0399, 0x0007, CanonicalizeSet },
+ { 0x039a, 0x039a, 0x0008, CanonicalizeSet },
+ { 0x039b, 0x039b, 0x0020, CanonicalizeRangeLo },
+ { 0x039c, 0x039c, 0x0009, CanonicalizeSet },
+ { 0x039d, 0x039f, 0x0020, CanonicalizeRangeLo },
+ { 0x03a0, 0x03a0, 0x000a, CanonicalizeSet },
+ { 0x03a1, 0x03a1, 0x000b, CanonicalizeSet },
+ { 0x03a2, 0x03a2, 0x0000, CanonicalizeUnique },
+ { 0x03a3, 0x03a3, 0x000c, CanonicalizeSet },
+ { 0x03a4, 0x03a5, 0x0020, CanonicalizeRangeLo },
+ { 0x03a6, 0x03a6, 0x000d, CanonicalizeSet },
+ { 0x03a7, 0x03ab, 0x0020, CanonicalizeRangeLo },
+ { 0x03ac, 0x03ac, 0x0026, CanonicalizeRangeHi },
+ { 0x03ad, 0x03af, 0x0025, CanonicalizeRangeHi },
+ { 0x03b0, 0x03b0, 0x0000, CanonicalizeUnique },
+ { 0x03b1, 0x03b1, 0x0020, CanonicalizeRangeHi },
+ { 0x03b2, 0x03b2, 0x0004, CanonicalizeSet },
+ { 0x03b3, 0x03b4, 0x0020, CanonicalizeRangeHi },
+ { 0x03b5, 0x03b5, 0x0005, CanonicalizeSet },
+ { 0x03b6, 0x03b7, 0x0020, CanonicalizeRangeHi },
+ { 0x03b8, 0x03b8, 0x0006, CanonicalizeSet },
+ { 0x03b9, 0x03b9, 0x0007, CanonicalizeSet },
+ { 0x03ba, 0x03ba, 0x0008, CanonicalizeSet },
+ { 0x03bb, 0x03bb, 0x0020, CanonicalizeRangeHi },
+ { 0x03bc, 0x03bc, 0x0009, CanonicalizeSet },
+ { 0x03bd, 0x03bf, 0x0020, CanonicalizeRangeHi },
+ { 0x03c0, 0x03c0, 0x000a, CanonicalizeSet },
+ { 0x03c1, 0x03c1, 0x000b, CanonicalizeSet },
+ { 0x03c2, 0x03c3, 0x000c, CanonicalizeSet },
+ { 0x03c4, 0x03c5, 0x0020, CanonicalizeRangeHi },
+ { 0x03c6, 0x03c6, 0x000d, CanonicalizeSet },
+ { 0x03c7, 0x03cb, 0x0020, CanonicalizeRangeHi },
+ { 0x03cc, 0x03cc, 0x0040, CanonicalizeRangeHi },
+ { 0x03cd, 0x03ce, 0x003f, CanonicalizeRangeHi },
+ { 0x03cf, 0x03cf, 0x0008, CanonicalizeRangeLo },
+ { 0x03d0, 0x03d0, 0x0004, CanonicalizeSet },
+ { 0x03d1, 0x03d1, 0x0006, CanonicalizeSet },
+ { 0x03d2, 0x03d4, 0x0000, CanonicalizeUnique },
+ { 0x03d5, 0x03d5, 0x000d, CanonicalizeSet },
+ { 0x03d6, 0x03d6, 0x000a, CanonicalizeSet },
+ { 0x03d7, 0x03d7, 0x0008, CanonicalizeRangeHi },
+ { 0x03d8, 0x03ef, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x03f0, 0x03f0, 0x0008, CanonicalizeSet },
+ { 0x03f1, 0x03f1, 0x000b, CanonicalizeSet },
+ { 0x03f2, 0x03f2, 0x0007, CanonicalizeRangeLo },
+ { 0x03f3, 0x03f3, 0x0074, CanonicalizeRangeHi },
+ { 0x03f4, 0x03f4, 0x0000, CanonicalizeUnique },
+ { 0x03f5, 0x03f5, 0x0005, CanonicalizeSet },
+ { 0x03f6, 0x03f6, 0x0000, CanonicalizeUnique },
+ { 0x03f7, 0x03f8, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x03f9, 0x03f9, 0x0007, CanonicalizeRangeHi },
+ { 0x03fa, 0x03fb, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x03fc, 0x03fc, 0x0000, CanonicalizeUnique },
+ { 0x03fd, 0x03ff, 0x0082, CanonicalizeRangeHi },
+ { 0x0400, 0x040f, 0x0050, CanonicalizeRangeLo },
+ { 0x0410, 0x042f, 0x0020, CanonicalizeRangeLo },
+ { 0x0430, 0x044f, 0x0020, CanonicalizeRangeHi },
+ { 0x0450, 0x045f, 0x0050, CanonicalizeRangeHi },
+ { 0x0460, 0x0481, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0482, 0x0489, 0x0000, CanonicalizeUnique },
+ { 0x048a, 0x04bf, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x04c0, 0x04c0, 0x000f, CanonicalizeRangeLo },
+ { 0x04c1, 0x04ce, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x04cf, 0x04cf, 0x000f, CanonicalizeRangeHi },
+ { 0x04d0, 0x052f, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0530, 0x0530, 0x0000, CanonicalizeUnique },
+ { 0x0531, 0x0556, 0x0030, CanonicalizeRangeLo },
+ { 0x0557, 0x0560, 0x0000, CanonicalizeUnique },
+ { 0x0561, 0x0586, 0x0030, CanonicalizeRangeHi },
+ { 0x0587, 0x109f, 0x0000, CanonicalizeUnique },
+ { 0x10a0, 0x10c5, 0x1c60, CanonicalizeRangeLo },
+ { 0x10c6, 0x10c6, 0x0000, CanonicalizeUnique },
+ { 0x10c7, 0x10c7, 0x1c60, CanonicalizeRangeLo },
+ { 0x10c8, 0x10cc, 0x0000, CanonicalizeUnique },
+ { 0x10cd, 0x10cd, 0x1c60, CanonicalizeRangeLo },
+ { 0x10ce, 0x1d78, 0x0000, CanonicalizeUnique },
+ { 0x1d79, 0x1d79, 0x8a04, CanonicalizeRangeLo },
+ { 0x1d7a, 0x1d7c, 0x0000, CanonicalizeUnique },
+ { 0x1d7d, 0x1d7d, 0x0ee6, CanonicalizeRangeLo },
+ { 0x1d7e, 0x1dff, 0x0000, CanonicalizeUnique },
+ { 0x1e00, 0x1e5f, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x1e60, 0x1e61, 0x000e, CanonicalizeSet },
+ { 0x1e62, 0x1e95, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x1e96, 0x1e9a, 0x0000, CanonicalizeUnique },
+ { 0x1e9b, 0x1e9b, 0x000e, CanonicalizeSet },
+ { 0x1e9c, 0x1e9f, 0x0000, CanonicalizeUnique },
+ { 0x1ea0, 0x1eff, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x1f00, 0x1f07, 0x0008, CanonicalizeRangeLo },
+ { 0x1f08, 0x1f0f, 0x0008, CanonicalizeRangeHi },
+ { 0x1f10, 0x1f15, 0x0008, CanonicalizeRangeLo },
+ { 0x1f16, 0x1f17, 0x0000, CanonicalizeUnique },
+ { 0x1f18, 0x1f1d, 0x0008, CanonicalizeRangeHi },
+ { 0x1f1e, 0x1f1f, 0x0000, CanonicalizeUnique },
+ { 0x1f20, 0x1f27, 0x0008, CanonicalizeRangeLo },
+ { 0x1f28, 0x1f2f, 0x0008, CanonicalizeRangeHi },
+ { 0x1f30, 0x1f37, 0x0008, CanonicalizeRangeLo },
+ { 0x1f38, 0x1f3f, 0x0008, CanonicalizeRangeHi },
+ { 0x1f40, 0x1f45, 0x0008, CanonicalizeRangeLo },
+ { 0x1f46, 0x1f47, 0x0000, CanonicalizeUnique },
+ { 0x1f48, 0x1f4d, 0x0008, CanonicalizeRangeHi },
+ { 0x1f4e, 0x1f50, 0x0000, CanonicalizeUnique },
+ { 0x1f51, 0x1f51, 0x0008, CanonicalizeRangeLo },
+ { 0x1f52, 0x1f52, 0x0000, CanonicalizeUnique },
+ { 0x1f53, 0x1f53, 0x0008, CanonicalizeRangeLo },
+ { 0x1f54, 0x1f54, 0x0000, CanonicalizeUnique },
+ { 0x1f55, 0x1f55, 0x0008, CanonicalizeRangeLo },
+ { 0x1f56, 0x1f56, 0x0000, CanonicalizeUnique },
+ { 0x1f57, 0x1f57, 0x0008, CanonicalizeRangeLo },
+ { 0x1f58, 0x1f58, 0x0000, CanonicalizeUnique },
+ { 0x1f59, 0x1f59, 0x0008, CanonicalizeRangeHi },
+ { 0x1f5a, 0x1f5a, 0x0000, CanonicalizeUnique },
+ { 0x1f5b, 0x1f5b, 0x0008, CanonicalizeRangeHi },
+ { 0x1f5c, 0x1f5c, 0x0000, CanonicalizeUnique },
+ { 0x1f5d, 0x1f5d, 0x0008, CanonicalizeRangeHi },
+ { 0x1f5e, 0x1f5e, 0x0000, CanonicalizeUnique },
+ { 0x1f5f, 0x1f5f, 0x0008, CanonicalizeRangeHi },
+ { 0x1f60, 0x1f67, 0x0008, CanonicalizeRangeLo },
+ { 0x1f68, 0x1f6f, 0x0008, CanonicalizeRangeHi },
+ { 0x1f70, 0x1f71, 0x004a, CanonicalizeRangeLo },
+ { 0x1f72, 0x1f75, 0x0056, CanonicalizeRangeLo },
+ { 0x1f76, 0x1f77, 0x0064, CanonicalizeRangeLo },
+ { 0x1f78, 0x1f79, 0x0080, CanonicalizeRangeLo },
+ { 0x1f7a, 0x1f7b, 0x0070, CanonicalizeRangeLo },
+ { 0x1f7c, 0x1f7d, 0x007e, CanonicalizeRangeLo },
+ { 0x1f7e, 0x1faf, 0x0000, CanonicalizeUnique },
+ { 0x1fb0, 0x1fb1, 0x0008, CanonicalizeRangeLo },
+ { 0x1fb2, 0x1fb7, 0x0000, CanonicalizeUnique },
+ { 0x1fb8, 0x1fb9, 0x0008, CanonicalizeRangeHi },
+ { 0x1fba, 0x1fbb, 0x004a, CanonicalizeRangeHi },
+ { 0x1fbc, 0x1fbd, 0x0000, CanonicalizeUnique },
+ { 0x1fbe, 0x1fbe, 0x0007, CanonicalizeSet },
+ { 0x1fbf, 0x1fc7, 0x0000, CanonicalizeUnique },
+ { 0x1fc8, 0x1fcb, 0x0056, CanonicalizeRangeHi },
+ { 0x1fcc, 0x1fcf, 0x0000, CanonicalizeUnique },
+ { 0x1fd0, 0x1fd1, 0x0008, CanonicalizeRangeLo },
+ { 0x1fd2, 0x1fd7, 0x0000, CanonicalizeUnique },
+ { 0x1fd8, 0x1fd9, 0x0008, CanonicalizeRangeHi },
+ { 0x1fda, 0x1fdb, 0x0064, CanonicalizeRangeHi },
+ { 0x1fdc, 0x1fdf, 0x0000, CanonicalizeUnique },
+ { 0x1fe0, 0x1fe1, 0x0008, CanonicalizeRangeLo },
+ { 0x1fe2, 0x1fe4, 0x0000, CanonicalizeUnique },
+ { 0x1fe5, 0x1fe5, 0x0007, CanonicalizeRangeLo },
+ { 0x1fe6, 0x1fe7, 0x0000, CanonicalizeUnique },
+ { 0x1fe8, 0x1fe9, 0x0008, CanonicalizeRangeHi },
+ { 0x1fea, 0x1feb, 0x0070, CanonicalizeRangeHi },
+ { 0x1fec, 0x1fec, 0x0007, CanonicalizeRangeHi },
+ { 0x1fed, 0x1ff7, 0x0000, CanonicalizeUnique },
+ { 0x1ff8, 0x1ff9, 0x0080, CanonicalizeRangeHi },
+ { 0x1ffa, 0x1ffb, 0x007e, CanonicalizeRangeHi },
+ { 0x1ffc, 0x2131, 0x0000, CanonicalizeUnique },
+ { 0x2132, 0x2132, 0x001c, CanonicalizeRangeLo },
+ { 0x2133, 0x214d, 0x0000, CanonicalizeUnique },
+ { 0x214e, 0x214e, 0x001c, CanonicalizeRangeHi },
+ { 0x214f, 0x215f, 0x0000, CanonicalizeUnique },
+ { 0x2160, 0x216f, 0x0010, CanonicalizeRangeLo },
+ { 0x2170, 0x217f, 0x0010, CanonicalizeRangeHi },
+ { 0x2180, 0x2182, 0x0000, CanonicalizeUnique },
+ { 0x2183, 0x2184, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x2185, 0x24b5, 0x0000, CanonicalizeUnique },
+ { 0x24b6, 0x24cf, 0x001a, CanonicalizeRangeLo },
+ { 0x24d0, 0x24e9, 0x001a, CanonicalizeRangeHi },
+ { 0x24ea, 0x2bff, 0x0000, CanonicalizeUnique },
+ { 0x2c00, 0x2c2e, 0x0030, CanonicalizeRangeLo },
+ { 0x2c2f, 0x2c2f, 0x0000, CanonicalizeUnique },
+ { 0x2c30, 0x2c5e, 0x0030, CanonicalizeRangeHi },
+ { 0x2c5f, 0x2c5f, 0x0000, CanonicalizeUnique },
+ { 0x2c60, 0x2c61, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x2c62, 0x2c62, 0x29f7, CanonicalizeRangeHi },
+ { 0x2c63, 0x2c63, 0x0ee6, CanonicalizeRangeHi },
+ { 0x2c64, 0x2c64, 0x29e7, CanonicalizeRangeHi },
+ { 0x2c65, 0x2c65, 0x2a2b, CanonicalizeRangeHi },
+ { 0x2c66, 0x2c66, 0x2a28, CanonicalizeRangeHi },
+ { 0x2c67, 0x2c6c, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x2c6d, 0x2c6d, 0x2a1c, CanonicalizeRangeHi },
+ { 0x2c6e, 0x2c6e, 0x29fd, CanonicalizeRangeHi },
+ { 0x2c6f, 0x2c6f, 0x2a1f, CanonicalizeRangeHi },
+ { 0x2c70, 0x2c70, 0x2a1e, CanonicalizeRangeHi },
+ { 0x2c71, 0x2c71, 0x0000, CanonicalizeUnique },
+ { 0x2c72, 0x2c73, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x2c74, 0x2c74, 0x0000, CanonicalizeUnique },
+ { 0x2c75, 0x2c76, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x2c77, 0x2c7d, 0x0000, CanonicalizeUnique },
+ { 0x2c7e, 0x2c7f, 0x2a3f, CanonicalizeRangeHi },
+ { 0x2c80, 0x2ce3, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x2ce4, 0x2cea, 0x0000, CanonicalizeUnique },
+ { 0x2ceb, 0x2cee, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x2cef, 0x2cf1, 0x0000, CanonicalizeUnique },
+ { 0x2cf2, 0x2cf3, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x2cf4, 0x2cff, 0x0000, CanonicalizeUnique },
+ { 0x2d00, 0x2d25, 0x1c60, CanonicalizeRangeHi },
+ { 0x2d26, 0x2d26, 0x0000, CanonicalizeUnique },
+ { 0x2d27, 0x2d27, 0x1c60, CanonicalizeRangeHi },
+ { 0x2d28, 0x2d2c, 0x0000, CanonicalizeUnique },
+ { 0x2d2d, 0x2d2d, 0x1c60, CanonicalizeRangeHi },
+ { 0x2d2e, 0xa63f, 0x0000, CanonicalizeUnique },
+ { 0xa640, 0xa66d, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa66e, 0xa67f, 0x0000, CanonicalizeUnique },
+ { 0xa680, 0xa69b, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa69c, 0xa721, 0x0000, CanonicalizeUnique },
+ { 0xa722, 0xa72f, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa730, 0xa731, 0x0000, CanonicalizeUnique },
+ { 0xa732, 0xa76f, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa770, 0xa778, 0x0000, CanonicalizeUnique },
+ { 0xa779, 0xa77c, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0xa77d, 0xa77d, 0x8a04, CanonicalizeRangeHi },
+ { 0xa77e, 0xa787, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa788, 0xa78a, 0x0000, CanonicalizeUnique },
+ { 0xa78b, 0xa78c, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0xa78d, 0xa78d, 0xa528, CanonicalizeRangeHi },
+ { 0xa78e, 0xa78f, 0x0000, CanonicalizeUnique },
+ { 0xa790, 0xa793, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa794, 0xa795, 0x0000, CanonicalizeUnique },
+ { 0xa796, 0xa7a9, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa7aa, 0xa7aa, 0xa544, CanonicalizeRangeHi },
+ { 0xa7ab, 0xa7ab, 0xa54f, CanonicalizeRangeHi },
+ { 0xa7ac, 0xa7ac, 0xa54b, CanonicalizeRangeHi },
+ { 0xa7ad, 0xa7ad, 0xa541, CanonicalizeRangeHi },
+ { 0xa7ae, 0xa7af, 0x0000, CanonicalizeUnique },
+ { 0xa7b0, 0xa7b0, 0xa512, CanonicalizeRangeHi },
+ { 0xa7b1, 0xa7b1, 0xa52a, CanonicalizeRangeHi },
+ { 0xa7b2, 0xff20, 0x0000, CanonicalizeUnique },
+ { 0xff21, 0xff3a, 0x0020, CanonicalizeRangeLo },
+ { 0xff3b, 0xff40, 0x0000, CanonicalizeUnique },
+ { 0xff41, 0xff5a, 0x0020, CanonicalizeRangeHi },
+ { 0xff5b, 0xffff, 0x0000, CanonicalizeUnique },
};
} } // JSC::Yarr
diff --git a/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js b/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js
index 00361dd46e..dc578cfece 100644
--- a/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js
+++ b/src/3rdparty/masm/yarr/YarrCanonicalizeUCS2.js
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,7 +23,61 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-// See ES 5.1, 15.10.2.8
+function printHeader()
+{
+ var copyright = (
+ "/*" + "\n" +
+ " * Copyright (C) 2012-2013, 2015-2016 Apple Inc. All rights reserved." + "\n" +
+ " *" + "\n" +
+ " * Redistribution and use in source and binary forms, with or without" + "\n" +
+ " * modification, are permitted provided that the following conditions" + "\n" +
+ " * are met:" + "\n" +
+ " * 1. Redistributions of source code must retain the above copyright" + "\n" +
+ " * notice, this list of conditions and the following disclaimer." + "\n" +
+ " * 2. Redistributions in binary form must reproduce the above copyright" + "\n" +
+ " * notice, this list of conditions and the following disclaimer in the" + "\n" +
+ " * documentation and/or other materials provided with the distribution." + "\n" +
+ " *" + "\n" +
+ " * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY" + "\n" +
+ " * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE" + "\n" +
+ " * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR" + "\n" +
+ " * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR" + "\n" +
+ " * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL," + "\n" +
+ " * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO," + "\n" +
+ " * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR" + "\n" +
+ " * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY" + "\n" +
+ " * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT" + "\n" +
+ " * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE" + "\n" +
+ " * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. " + "\n" +
+ " */");
+
+ print(copyright);
+ print();
+ print("// DO NOT EDIT! - this file autogenerated by YarrCanonicalize.js");
+ print();
+ print('#include "config.h"');
+ print('#include "YarrCanonicalize.h"');
+ print();
+ print("namespace JSC { namespace Yarr {");
+ print();
+}
+
+function printFooter()
+{
+ print("} } // JSC::Yarr");
+ print();
+}
+
+// Helper function to convert a number to a fixed width hex representation of a UChar32.
+function hex(x)
+{
+ var s = Number(x).toString(16);
+ while (s.length < 4)
+ s = 0 + s;
+ return "0x" + s;
+}
+
+// See ES 6.0, 21.2.2.8.2 Steps 3
function canonicalize(ch)
{
var u = String.fromCharCode(ch).toUpperCase();
@@ -36,184 +90,104 @@ function canonicalize(ch)
}
var MAX_UCS2 = 0xFFFF;
-var MAX_LATIN = 0xFF;
-
-var groupedCanonically = [];
-// Pass 1: populate groupedCanonically - this is mapping from canonicalized
-// values back to the set of character code that canonicalize to them.
-for (var i = 0; i <= MAX_UCS2; ++i) {
- var ch = canonicalize(i);
- if (!groupedCanonically[ch])
- groupedCanonically[ch] = [];
- groupedCanonically[ch].push(i);
-}
-var typeInfo = [];
-var latinTypeInfo = [];
-var characterSetInfo = [];
-// Pass 2: populate typeInfo & characterSetInfo. For every character calculate
-// a typeInfo value, described by the types above, and a value payload.
-for (cu in groupedCanonically) {
- // The set of characters that canonicalize to cu
- var characters = groupedCanonically[cu];
-
- // If there is only one, it is unique.
- if (characters.length == 1) {
- typeInfo[characters[0]] = "CanonicalizeUnique:0";
- latinTypeInfo[characters[0]] = characters[0] <= MAX_LATIN ? "CanonicalizeLatinSelf:0" : "CanonicalizeLatinInvalid:0";
- continue;
+function createUCS2CanonicalGroups()
+{
+ var groupedCanonically = [];
+ // Pass 1: populate groupedCanonically - this is mapping from canonicalized
+ // values back to the set of character code that canonicalize to them.
+ for (var i = 0; i <= MAX_UCS2; ++i) {
+ var ch = canonicalize(i);
+ if (!groupedCanonically[ch])
+ groupedCanonically[ch] = [];
+ groupedCanonically[ch].push(i);
}
- // Sort the array.
- characters.sort(function(x,y){return x-y;});
+ return groupedCanonically;
+}
- // If there are more than two characters, create an entry in characterSetInfo.
- if (characters.length > 2) {
- for (i in characters)
- typeInfo[characters[i]] = "CanonicalizeSet:" + characterSetInfo.length;
- characterSetInfo.push(characters);
+function createTables(prefix, maxValue, canonicalGroups)
+{
+ var prefixLower = prefix.toLowerCase();
+ var prefixUpper = prefix.toUpperCase();
+ var typeInfo = [];
+ var characterSetInfo = [];
+ // Pass 2: populate typeInfo & characterSetInfo. For every character calculate
+ // a typeInfo value, described by the types above, and a value payload.
+ for (cu in canonicalGroups) {
+ // The set of characters that canonicalize to cu
+ var characters = canonicalGroups[cu];
+
+ // If there is only one, it is unique.
+ if (characters.length == 1) {
+ typeInfo[characters[0]] = "CanonicalizeUnique:0";
+ continue;
+ }
- if (characters[1] <= MAX_LATIN)
- throw new Error("sets with more than one latin character not supported!");
- if (characters[0] <= MAX_LATIN) {
- for (i in characters)
- latinTypeInfo[characters[i]] = "CanonicalizeLatinOther:" + characters[0];
- latinTypeInfo[characters[0]] = "CanonicalizeLatinSelf:0";
- } else {
+ // Sort the array.
+ characters.sort(function(x,y){return x-y;});
+
+ // If there are more than two characters, create an entry in characterSetInfo.
+ if (characters.length > 2) {
for (i in characters)
- latinTypeInfo[characters[i]] = "CanonicalizeLatinInvalid:0";
+ typeInfo[characters[i]] = "CanonicalizeSet:" + characterSetInfo.length;
+ characterSetInfo.push(characters);
+
+ continue;
}
- continue;
+ // We have a pair, mark alternating ranges, otherwise track whether this is the low or high partner.
+ var lo = characters[0];
+ var hi = characters[1];
+ var delta = hi - lo;
+ if (delta == 1) {
+ var type = lo & 1 ? "CanonicalizeAlternatingUnaligned:0" : "CanonicalizeAlternatingAligned:0";
+ typeInfo[lo] = type;
+ typeInfo[hi] = type;
+ } else {
+ typeInfo[lo] = "CanonicalizeRangeLo:" + delta;
+ typeInfo[hi] = "CanonicalizeRangeHi:" + delta;
+ }
}
- // We have a pair, mark alternating ranges, otherwise track whether this is the low or high partner.
- var lo = characters[0];
- var hi = characters[1];
- var delta = hi - lo;
- if (delta == 1) {
- var type = lo & 1 ? "CanonicalizeAlternatingUnaligned:0" : "CanonicalizeAlternatingAligned:0";
- typeInfo[lo] = type;
- typeInfo[hi] = type;
- } else {
- typeInfo[lo] = "CanonicalizeRangeLo:" + delta;
- typeInfo[hi] = "CanonicalizeRangeHi:" + delta;
+ var rangeInfo = [];
+ // Pass 3: coallesce types into ranges.
+ for (var end = 0; end <= maxValue; ++end) {
+ var begin = end;
+ var type = typeInfo[end];
+ while (end < maxValue && typeInfo[end + 1] == type)
+ ++end;
+ rangeInfo.push({begin:begin, end:end, type:type});
}
- if (lo > MAX_LATIN) {
- latinTypeInfo[lo] = "CanonicalizeLatinInvalid:0";
- latinTypeInfo[hi] = "CanonicalizeLatinInvalid:0";
- } else if (hi > MAX_LATIN) {
- latinTypeInfo[lo] = "CanonicalizeLatinSelf:0";
- latinTypeInfo[hi] = "CanonicalizeLatinOther:" + lo;
- } else {
- if (delta != 0x20 || lo & 0x20)
- throw new Error("pairs of latin characters that don't mask with 0x20 not supported!");
- latinTypeInfo[lo] = "CanonicalizeLatinMask0x20:0";
- latinTypeInfo[hi] = "CanonicalizeLatinMask0x20:0";
+ for (i in characterSetInfo) {
+ var characters = ""
+ var set = characterSetInfo[i];
+ for (var j in set)
+ characters += hex(set[j]) + ", ";
+ print("const UChar32 " + prefixLower + "CharacterSet" + i + "[] = { " + characters + "0 };");
}
+ print();
+ print("static const size_t " + prefixUpper + "_CANONICALIZATION_SETS = " + characterSetInfo.length + ";");
+ print("const UChar32* const " + prefixLower + "CharacterSetInfo[" + prefixUpper + "_CANONICALIZATION_SETS] = {");
+ for (i in characterSetInfo)
+ print(" " + prefixLower + "CharacterSet" + i + ",");
+ print("};");
+ print();
+ print("const size_t " + prefixUpper + "_CANONICALIZATION_RANGES = " + rangeInfo.length + ";");
+ print("const CanonicalizationRange " + prefixLower + "RangeInfo[" + prefixUpper + "_CANONICALIZATION_RANGES] = {");
+ for (i in rangeInfo) {
+ var info = rangeInfo[i];
+ var typeAndValue = info.type.split(':');
+ print(" { " + hex(info.begin) + ", " + hex(info.end) + ", " + hex(typeAndValue[1]) + ", " + typeAndValue[0] + " },");
+ }
+ print("};");
+ print();
}
-var rangeInfo = [];
-// Pass 3: coallesce types into ranges.
-for (var end = 0; end <= MAX_UCS2; ++end) {
- var begin = end;
- var type = typeInfo[end];
- while (end < MAX_UCS2 && typeInfo[end + 1] == type)
- ++end;
- rangeInfo.push({begin:begin, end:end, type:type});
-}
+printHeader();
-var latinRangeInfo = [];
-// Pass 4: coallesce latin-1 types into ranges.
-for (var end = 0; end <= MAX_UCS2; ++end) {
- var begin = end;
- var type = latinTypeInfo[end];
- while (end < MAX_UCS2 && latinTypeInfo[end + 1] == type)
- ++end;
- latinRangeInfo.push({begin:begin, end:end, type:type});
-}
+createTables("UCS2", MAX_UCS2, createUCS2CanonicalGroups());
-
-// Helper function to convert a number to a fixed width hex representation of a C uint16_t.
-function hex(x)
-{
- var s = Number(x).toString(16);
- while (s.length < 4)
- s = 0 + s;
- return "0x" + s + "u";
-}
-
-var copyright = (
- "/*" + "\n" +
- " * Copyright (C) 2012 Apple Inc. All rights reserved." + "\n" +
- " *" + "\n" +
- " * Redistribution and use in source and binary forms, with or without" + "\n" +
- " * modification, are permitted provided that the following conditions" + "\n" +
- " * are met:" + "\n" +
- " * 1. Redistributions of source code must retain the above copyright" + "\n" +
- " * notice, this list of conditions and the following disclaimer." + "\n" +
- " * 2. Redistributions in binary form must reproduce the above copyright" + "\n" +
- " * notice, this list of conditions and the following disclaimer in the" + "\n" +
- " * documentation and/or other materials provided with the distribution." + "\n" +
- " *" + "\n" +
- " * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY" + "\n" +
- " * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE" + "\n" +
- " * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR" + "\n" +
- " * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR" + "\n" +
- " * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL," + "\n" +
- " * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO," + "\n" +
- " * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR" + "\n" +
- " * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY" + "\n" +
- " * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT" + "\n" +
- " * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE" + "\n" +
- " * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. " + "\n" +
- " */");
-
-print(copyright);
-print();
-print("// DO NOT EDIT! - this file autogenerated by YarrCanonicalizeUCS2.js");
-print();
-print('#include "config.h"');
-print('#include "YarrCanonicalizeUCS2.h"');
-print();
-print("namespace JSC { namespace Yarr {");
-print();
-print("#include <stdint.h>");
-print();
-
-for (i in characterSetInfo) {
- var characters = ""
- var set = characterSetInfo[i];
- for (var j in set)
- characters += hex(set[j]) + ", ";
- print("uint16_t ucs2CharacterSet" + i + "[] = { " + characters + "0 };");
-}
-print();
-print("static const size_t UCS2_CANONICALIZATION_SETS = " + characterSetInfo.length + ";");
-print("uint16_t* characterSetInfo[UCS2_CANONICALIZATION_SETS] = {");
-for (i in characterSetInfo)
-print(" ucs2CharacterSet" + i + ",");
-print("};");
-print();
-print("const size_t UCS2_CANONICALIZATION_RANGES = " + rangeInfo.length + ";");
-print("UCS2CanonicalizationRange rangeInfo[UCS2_CANONICALIZATION_RANGES] = {");
-for (i in rangeInfo) {
- var info = rangeInfo[i];
- var typeAndValue = info.type.split(':');
- print(" { " + hex(info.begin) + ", " + hex(info.end) + ", " + hex(typeAndValue[1]) + ", " + typeAndValue[0] + " },");
-}
-print("};");
-print();
-print("const size_t LATIN_CANONICALIZATION_RANGES = " + latinRangeInfo.length + ";");
-print("LatinCanonicalizationRange latinRangeInfo[LATIN_CANONICALIZATION_RANGES] = {");
-for (i in latinRangeInfo) {
- var info = latinRangeInfo[i];
- var typeAndValue = info.type.split(':');
- print(" { " + hex(info.begin) + ", " + hex(info.end) + ", " + hex(typeAndValue[1]) + ", " + typeAndValue[0] + " },");
-}
-print("};");
-print();
-print("} } // JSC::Yarr");
-print();
+printFooter();
diff --git a/src/3rdparty/masm/yarr/YarrCanonicalizeUnicode.cpp b/src/3rdparty/masm/yarr/YarrCanonicalizeUnicode.cpp
new file mode 100644
index 0000000000..37bfc5e060
--- /dev/null
+++ b/src/3rdparty/masm/yarr/YarrCanonicalizeUnicode.cpp
@@ -0,0 +1,591 @@
+/*
+* Copyright (C) 2016 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. 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.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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.
+*/
+
+// DO NO EDIT! - This file was generated by generateYarrCanonicalizeUnicode
+
+#include "config.h"
+#include "YarrCanonicalize.h"
+
+namespace JSC { namespace Yarr {
+
+const UChar32 unicodeCharacterSet0[] = { 0x004b, 0x006b, 0x212a, 0 };
+const UChar32 unicodeCharacterSet1[] = { 0x0053, 0x0073, 0x017f, 0 };
+const UChar32 unicodeCharacterSet2[] = { 0x00c5, 0x00e5, 0x212b, 0 };
+const UChar32 unicodeCharacterSet3[] = { 0x01c4, 0x01c5, 0x01c6, 0 };
+const UChar32 unicodeCharacterSet4[] = { 0x01c7, 0x01c8, 0x01c9, 0 };
+const UChar32 unicodeCharacterSet5[] = { 0x01ca, 0x01cb, 0x01cc, 0 };
+const UChar32 unicodeCharacterSet6[] = { 0x01f1, 0x01f2, 0x01f3, 0 };
+const UChar32 unicodeCharacterSet7[] = { 0x0392, 0x03b2, 0x03d0, 0 };
+const UChar32 unicodeCharacterSet8[] = { 0x0395, 0x03b5, 0x03f5, 0 };
+const UChar32 unicodeCharacterSet9[] = { 0x0398, 0x03b8, 0x03d1, 0x03f4, 0 };
+const UChar32 unicodeCharacterSet10[] = { 0x0345, 0x0399, 0x03b9, 0x1fbe, 0 };
+const UChar32 unicodeCharacterSet11[] = { 0x039a, 0x03ba, 0x03f0, 0 };
+const UChar32 unicodeCharacterSet12[] = { 0x00b5, 0x039c, 0x03bc, 0 };
+const UChar32 unicodeCharacterSet13[] = { 0x03a0, 0x03c0, 0x03d6, 0 };
+const UChar32 unicodeCharacterSet14[] = { 0x03a1, 0x03c1, 0x03f1, 0 };
+const UChar32 unicodeCharacterSet15[] = { 0x03a3, 0x03c2, 0x03c3, 0 };
+const UChar32 unicodeCharacterSet16[] = { 0x03a6, 0x03c6, 0x03d5, 0 };
+const UChar32 unicodeCharacterSet17[] = { 0x03a9, 0x03c9, 0x2126, 0 };
+const UChar32 unicodeCharacterSet18[] = { 0x0412, 0x0432, 0x1c80, 0 };
+const UChar32 unicodeCharacterSet19[] = { 0x0414, 0x0434, 0x1c81, 0 };
+const UChar32 unicodeCharacterSet20[] = { 0x041e, 0x043e, 0x1c82, 0 };
+const UChar32 unicodeCharacterSet21[] = { 0x0421, 0x0441, 0x1c83, 0 };
+const UChar32 unicodeCharacterSet22[] = { 0x0422, 0x0442, 0x1c84, 0x1c85, 0 };
+const UChar32 unicodeCharacterSet23[] = { 0x042a, 0x044a, 0x1c86, 0 };
+const UChar32 unicodeCharacterSet24[] = { 0x0462, 0x0463, 0x1c87, 0 };
+const UChar32 unicodeCharacterSet25[] = { 0x1e60, 0x1e61, 0x1e9b, 0 };
+const UChar32 unicodeCharacterSet26[] = { 0x1c88, 0xa64a, 0xa64b, 0 };
+
+static const size_t UNICODE_CANONICALIZATION_SETS = 27;
+const UChar32* const unicodeCharacterSetInfo[UNICODE_CANONICALIZATION_SETS] = {
+ unicodeCharacterSet0,
+ unicodeCharacterSet1,
+ unicodeCharacterSet2,
+ unicodeCharacterSet3,
+ unicodeCharacterSet4,
+ unicodeCharacterSet5,
+ unicodeCharacterSet6,
+ unicodeCharacterSet7,
+ unicodeCharacterSet8,
+ unicodeCharacterSet9,
+ unicodeCharacterSet10,
+ unicodeCharacterSet11,
+ unicodeCharacterSet12,
+ unicodeCharacterSet13,
+ unicodeCharacterSet14,
+ unicodeCharacterSet15,
+ unicodeCharacterSet16,
+ unicodeCharacterSet17,
+ unicodeCharacterSet18,
+ unicodeCharacterSet19,
+ unicodeCharacterSet20,
+ unicodeCharacterSet21,
+ unicodeCharacterSet22,
+ unicodeCharacterSet23,
+ unicodeCharacterSet24,
+ unicodeCharacterSet25,
+ unicodeCharacterSet26,
+};
+
+const size_t UNICODE_CANONICALIZATION_RANGES = 495;
+const CanonicalizationRange unicodeRangeInfo[UNICODE_CANONICALIZATION_RANGES] = {
+ { 0x0000, 0x0040, 0x0000, CanonicalizeUnique },
+ { 0x0041, 0x004a, 0x0020, CanonicalizeRangeLo },
+ { 0x004b, 0x004b, 0x0000, CanonicalizeSet },
+ { 0x004c, 0x0052, 0x0020, CanonicalizeRangeLo },
+ { 0x0053, 0x0053, 0x0001, CanonicalizeSet },
+ { 0x0054, 0x005a, 0x0020, CanonicalizeRangeLo },
+ { 0x005b, 0x0060, 0x0000, CanonicalizeUnique },
+ { 0x0061, 0x006a, 0x0020, CanonicalizeRangeHi },
+ { 0x006b, 0x006b, 0x0000, CanonicalizeSet },
+ { 0x006c, 0x0072, 0x0020, CanonicalizeRangeHi },
+ { 0x0073, 0x0073, 0x0001, CanonicalizeSet },
+ { 0x0074, 0x007a, 0x0020, CanonicalizeRangeHi },
+ { 0x007b, 0x00b4, 0x0000, CanonicalizeUnique },
+ { 0x00b5, 0x00b5, 0x000c, CanonicalizeSet },
+ { 0x00b6, 0x00bf, 0x0000, CanonicalizeUnique },
+ { 0x00c0, 0x00c4, 0x0020, CanonicalizeRangeLo },
+ { 0x00c5, 0x00c5, 0x0002, CanonicalizeSet },
+ { 0x00c6, 0x00d6, 0x0020, CanonicalizeRangeLo },
+ { 0x00d7, 0x00d7, 0x0000, CanonicalizeUnique },
+ { 0x00d8, 0x00de, 0x0020, CanonicalizeRangeLo },
+ { 0x00df, 0x00df, 0x1dbf, CanonicalizeRangeLo },
+ { 0x00e0, 0x00e4, 0x0020, CanonicalizeRangeHi },
+ { 0x00e5, 0x00e5, 0x0002, CanonicalizeSet },
+ { 0x00e6, 0x00f6, 0x0020, CanonicalizeRangeHi },
+ { 0x00f7, 0x00f7, 0x0000, CanonicalizeUnique },
+ { 0x00f8, 0x00fe, 0x0020, CanonicalizeRangeHi },
+ { 0x00ff, 0x00ff, 0x0079, CanonicalizeRangeLo },
+ { 0x0100, 0x012f, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0130, 0x0131, 0x0000, CanonicalizeUnique },
+ { 0x0132, 0x0137, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0138, 0x0138, 0x0000, CanonicalizeUnique },
+ { 0x0139, 0x0148, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x0149, 0x0149, 0x0000, CanonicalizeUnique },
+ { 0x014a, 0x0177, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0178, 0x0178, 0x0079, CanonicalizeRangeHi },
+ { 0x0179, 0x017e, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x017f, 0x017f, 0x0001, CanonicalizeSet },
+ { 0x0180, 0x0180, 0x00c3, CanonicalizeRangeLo },
+ { 0x0181, 0x0181, 0x00d2, CanonicalizeRangeLo },
+ { 0x0182, 0x0185, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0186, 0x0186, 0x00ce, CanonicalizeRangeLo },
+ { 0x0187, 0x0188, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x0189, 0x018a, 0x00cd, CanonicalizeRangeLo },
+ { 0x018b, 0x018c, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x018d, 0x018d, 0x0000, CanonicalizeUnique },
+ { 0x018e, 0x018e, 0x004f, CanonicalizeRangeLo },
+ { 0x018f, 0x018f, 0x00ca, CanonicalizeRangeLo },
+ { 0x0190, 0x0190, 0x00cb, CanonicalizeRangeLo },
+ { 0x0191, 0x0192, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x0193, 0x0193, 0x00cd, CanonicalizeRangeLo },
+ { 0x0194, 0x0194, 0x00cf, CanonicalizeRangeLo },
+ { 0x0195, 0x0195, 0x0061, CanonicalizeRangeLo },
+ { 0x0196, 0x0196, 0x00d3, CanonicalizeRangeLo },
+ { 0x0197, 0x0197, 0x00d1, CanonicalizeRangeLo },
+ { 0x0198, 0x0199, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x019a, 0x019a, 0x00a3, CanonicalizeRangeLo },
+ { 0x019b, 0x019b, 0x0000, CanonicalizeUnique },
+ { 0x019c, 0x019c, 0x00d3, CanonicalizeRangeLo },
+ { 0x019d, 0x019d, 0x00d5, CanonicalizeRangeLo },
+ { 0x019e, 0x019e, 0x0082, CanonicalizeRangeLo },
+ { 0x019f, 0x019f, 0x00d6, CanonicalizeRangeLo },
+ { 0x01a0, 0x01a5, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x01a6, 0x01a6, 0x00da, CanonicalizeRangeLo },
+ { 0x01a7, 0x01a8, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x01a9, 0x01a9, 0x00da, CanonicalizeRangeLo },
+ { 0x01aa, 0x01ab, 0x0000, CanonicalizeUnique },
+ { 0x01ac, 0x01ad, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x01ae, 0x01ae, 0x00da, CanonicalizeRangeLo },
+ { 0x01af, 0x01b0, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x01b1, 0x01b2, 0x00d9, CanonicalizeRangeLo },
+ { 0x01b3, 0x01b6, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x01b7, 0x01b7, 0x00db, CanonicalizeRangeLo },
+ { 0x01b8, 0x01b9, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x01ba, 0x01bb, 0x0000, CanonicalizeUnique },
+ { 0x01bc, 0x01bd, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x01be, 0x01be, 0x0000, CanonicalizeUnique },
+ { 0x01bf, 0x01bf, 0x0038, CanonicalizeRangeLo },
+ { 0x01c0, 0x01c3, 0x0000, CanonicalizeUnique },
+ { 0x01c4, 0x01c6, 0x0003, CanonicalizeSet },
+ { 0x01c7, 0x01c9, 0x0004, CanonicalizeSet },
+ { 0x01ca, 0x01cc, 0x0005, CanonicalizeSet },
+ { 0x01cd, 0x01dc, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x01dd, 0x01dd, 0x004f, CanonicalizeRangeHi },
+ { 0x01de, 0x01ef, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x01f0, 0x01f0, 0x0000, CanonicalizeUnique },
+ { 0x01f1, 0x01f3, 0x0006, CanonicalizeSet },
+ { 0x01f4, 0x01f5, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x01f6, 0x01f6, 0x0061, CanonicalizeRangeHi },
+ { 0x01f7, 0x01f7, 0x0038, CanonicalizeRangeHi },
+ { 0x01f8, 0x021f, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0220, 0x0220, 0x0082, CanonicalizeRangeHi },
+ { 0x0221, 0x0221, 0x0000, CanonicalizeUnique },
+ { 0x0222, 0x0233, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0234, 0x0239, 0x0000, CanonicalizeUnique },
+ { 0x023a, 0x023a, 0x2a2b, CanonicalizeRangeLo },
+ { 0x023b, 0x023c, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x023d, 0x023d, 0x00a3, CanonicalizeRangeHi },
+ { 0x023e, 0x023e, 0x2a28, CanonicalizeRangeLo },
+ { 0x023f, 0x0240, 0x2a3f, CanonicalizeRangeLo },
+ { 0x0241, 0x0242, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x0243, 0x0243, 0x00c3, CanonicalizeRangeHi },
+ { 0x0244, 0x0244, 0x0045, CanonicalizeRangeLo },
+ { 0x0245, 0x0245, 0x0047, CanonicalizeRangeLo },
+ { 0x0246, 0x024f, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0250, 0x0250, 0x2a1f, CanonicalizeRangeLo },
+ { 0x0251, 0x0251, 0x2a1c, CanonicalizeRangeLo },
+ { 0x0252, 0x0252, 0x2a1e, CanonicalizeRangeLo },
+ { 0x0253, 0x0253, 0x00d2, CanonicalizeRangeHi },
+ { 0x0254, 0x0254, 0x00ce, CanonicalizeRangeHi },
+ { 0x0255, 0x0255, 0x0000, CanonicalizeUnique },
+ { 0x0256, 0x0257, 0x00cd, CanonicalizeRangeHi },
+ { 0x0258, 0x0258, 0x0000, CanonicalizeUnique },
+ { 0x0259, 0x0259, 0x00ca, CanonicalizeRangeHi },
+ { 0x025a, 0x025a, 0x0000, CanonicalizeUnique },
+ { 0x025b, 0x025b, 0x00cb, CanonicalizeRangeHi },
+ { 0x025c, 0x025c, 0xa54f, CanonicalizeRangeLo },
+ { 0x025d, 0x025f, 0x0000, CanonicalizeUnique },
+ { 0x0260, 0x0260, 0x00cd, CanonicalizeRangeHi },
+ { 0x0261, 0x0261, 0xa54b, CanonicalizeRangeLo },
+ { 0x0262, 0x0262, 0x0000, CanonicalizeUnique },
+ { 0x0263, 0x0263, 0x00cf, CanonicalizeRangeHi },
+ { 0x0264, 0x0264, 0x0000, CanonicalizeUnique },
+ { 0x0265, 0x0265, 0xa528, CanonicalizeRangeLo },
+ { 0x0266, 0x0266, 0xa544, CanonicalizeRangeLo },
+ { 0x0267, 0x0267, 0x0000, CanonicalizeUnique },
+ { 0x0268, 0x0268, 0x00d1, CanonicalizeRangeHi },
+ { 0x0269, 0x0269, 0x00d3, CanonicalizeRangeHi },
+ { 0x026a, 0x026a, 0xa544, CanonicalizeRangeLo },
+ { 0x026b, 0x026b, 0x29f7, CanonicalizeRangeLo },
+ { 0x026c, 0x026c, 0xa541, CanonicalizeRangeLo },
+ { 0x026d, 0x026e, 0x0000, CanonicalizeUnique },
+ { 0x026f, 0x026f, 0x00d3, CanonicalizeRangeHi },
+ { 0x0270, 0x0270, 0x0000, CanonicalizeUnique },
+ { 0x0271, 0x0271, 0x29fd, CanonicalizeRangeLo },
+ { 0x0272, 0x0272, 0x00d5, CanonicalizeRangeHi },
+ { 0x0273, 0x0274, 0x0000, CanonicalizeUnique },
+ { 0x0275, 0x0275, 0x00d6, CanonicalizeRangeHi },
+ { 0x0276, 0x027c, 0x0000, CanonicalizeUnique },
+ { 0x027d, 0x027d, 0x29e7, CanonicalizeRangeLo },
+ { 0x027e, 0x027f, 0x0000, CanonicalizeUnique },
+ { 0x0280, 0x0280, 0x00da, CanonicalizeRangeHi },
+ { 0x0281, 0x0282, 0x0000, CanonicalizeUnique },
+ { 0x0283, 0x0283, 0x00da, CanonicalizeRangeHi },
+ { 0x0284, 0x0286, 0x0000, CanonicalizeUnique },
+ { 0x0287, 0x0287, 0xa52a, CanonicalizeRangeLo },
+ { 0x0288, 0x0288, 0x00da, CanonicalizeRangeHi },
+ { 0x0289, 0x0289, 0x0045, CanonicalizeRangeHi },
+ { 0x028a, 0x028b, 0x00d9, CanonicalizeRangeHi },
+ { 0x028c, 0x028c, 0x0047, CanonicalizeRangeHi },
+ { 0x028d, 0x0291, 0x0000, CanonicalizeUnique },
+ { 0x0292, 0x0292, 0x00db, CanonicalizeRangeHi },
+ { 0x0293, 0x029c, 0x0000, CanonicalizeUnique },
+ { 0x029d, 0x029d, 0xa515, CanonicalizeRangeLo },
+ { 0x029e, 0x029e, 0xa512, CanonicalizeRangeLo },
+ { 0x029f, 0x0344, 0x0000, CanonicalizeUnique },
+ { 0x0345, 0x0345, 0x000a, CanonicalizeSet },
+ { 0x0346, 0x036f, 0x0000, CanonicalizeUnique },
+ { 0x0370, 0x0373, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0374, 0x0375, 0x0000, CanonicalizeUnique },
+ { 0x0376, 0x0377, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0378, 0x037a, 0x0000, CanonicalizeUnique },
+ { 0x037b, 0x037d, 0x0082, CanonicalizeRangeLo },
+ { 0x037e, 0x037e, 0x0000, CanonicalizeUnique },
+ { 0x037f, 0x037f, 0x0074, CanonicalizeRangeLo },
+ { 0x0380, 0x0385, 0x0000, CanonicalizeUnique },
+ { 0x0386, 0x0386, 0x0026, CanonicalizeRangeLo },
+ { 0x0387, 0x0387, 0x0000, CanonicalizeUnique },
+ { 0x0388, 0x038a, 0x0025, CanonicalizeRangeLo },
+ { 0x038b, 0x038b, 0x0000, CanonicalizeUnique },
+ { 0x038c, 0x038c, 0x0040, CanonicalizeRangeLo },
+ { 0x038d, 0x038d, 0x0000, CanonicalizeUnique },
+ { 0x038e, 0x038f, 0x003f, CanonicalizeRangeLo },
+ { 0x0390, 0x0390, 0x0000, CanonicalizeUnique },
+ { 0x0391, 0x0391, 0x0020, CanonicalizeRangeLo },
+ { 0x0392, 0x0392, 0x0007, CanonicalizeSet },
+ { 0x0393, 0x0394, 0x0020, CanonicalizeRangeLo },
+ { 0x0395, 0x0395, 0x0008, CanonicalizeSet },
+ { 0x0396, 0x0397, 0x0020, CanonicalizeRangeLo },
+ { 0x0398, 0x0398, 0x0009, CanonicalizeSet },
+ { 0x0399, 0x0399, 0x000a, CanonicalizeSet },
+ { 0x039a, 0x039a, 0x000b, CanonicalizeSet },
+ { 0x039b, 0x039b, 0x0020, CanonicalizeRangeLo },
+ { 0x039c, 0x039c, 0x000c, CanonicalizeSet },
+ { 0x039d, 0x039f, 0x0020, CanonicalizeRangeLo },
+ { 0x03a0, 0x03a0, 0x000d, CanonicalizeSet },
+ { 0x03a1, 0x03a1, 0x000e, CanonicalizeSet },
+ { 0x03a2, 0x03a2, 0x0000, CanonicalizeUnique },
+ { 0x03a3, 0x03a3, 0x000f, CanonicalizeSet },
+ { 0x03a4, 0x03a5, 0x0020, CanonicalizeRangeLo },
+ { 0x03a6, 0x03a6, 0x0010, CanonicalizeSet },
+ { 0x03a7, 0x03a8, 0x0020, CanonicalizeRangeLo },
+ { 0x03a9, 0x03a9, 0x0011, CanonicalizeSet },
+ { 0x03aa, 0x03ab, 0x0020, CanonicalizeRangeLo },
+ { 0x03ac, 0x03ac, 0x0026, CanonicalizeRangeHi },
+ { 0x03ad, 0x03af, 0x0025, CanonicalizeRangeHi },
+ { 0x03b0, 0x03b0, 0x0000, CanonicalizeUnique },
+ { 0x03b1, 0x03b1, 0x0020, CanonicalizeRangeHi },
+ { 0x03b2, 0x03b2, 0x0007, CanonicalizeSet },
+ { 0x03b3, 0x03b4, 0x0020, CanonicalizeRangeHi },
+ { 0x03b5, 0x03b5, 0x0008, CanonicalizeSet },
+ { 0x03b6, 0x03b7, 0x0020, CanonicalizeRangeHi },
+ { 0x03b8, 0x03b8, 0x0009, CanonicalizeSet },
+ { 0x03b9, 0x03b9, 0x000a, CanonicalizeSet },
+ { 0x03ba, 0x03ba, 0x000b, CanonicalizeSet },
+ { 0x03bb, 0x03bb, 0x0020, CanonicalizeRangeHi },
+ { 0x03bc, 0x03bc, 0x000c, CanonicalizeSet },
+ { 0x03bd, 0x03bf, 0x0020, CanonicalizeRangeHi },
+ { 0x03c0, 0x03c0, 0x000d, CanonicalizeSet },
+ { 0x03c1, 0x03c1, 0x000e, CanonicalizeSet },
+ { 0x03c2, 0x03c3, 0x000f, CanonicalizeSet },
+ { 0x03c4, 0x03c5, 0x0020, CanonicalizeRangeHi },
+ { 0x03c6, 0x03c6, 0x0010, CanonicalizeSet },
+ { 0x03c7, 0x03c8, 0x0020, CanonicalizeRangeHi },
+ { 0x03c9, 0x03c9, 0x0011, CanonicalizeSet },
+ { 0x03ca, 0x03cb, 0x0020, CanonicalizeRangeHi },
+ { 0x03cc, 0x03cc, 0x0040, CanonicalizeRangeHi },
+ { 0x03cd, 0x03ce, 0x003f, CanonicalizeRangeHi },
+ { 0x03cf, 0x03cf, 0x0008, CanonicalizeRangeLo },
+ { 0x03d0, 0x03d0, 0x0007, CanonicalizeSet },
+ { 0x03d1, 0x03d1, 0x0009, CanonicalizeSet },
+ { 0x03d2, 0x03d4, 0x0000, CanonicalizeUnique },
+ { 0x03d5, 0x03d5, 0x0010, CanonicalizeSet },
+ { 0x03d6, 0x03d6, 0x000d, CanonicalizeSet },
+ { 0x03d7, 0x03d7, 0x0008, CanonicalizeRangeHi },
+ { 0x03d8, 0x03ef, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x03f0, 0x03f0, 0x000b, CanonicalizeSet },
+ { 0x03f1, 0x03f1, 0x000e, CanonicalizeSet },
+ { 0x03f2, 0x03f2, 0x0007, CanonicalizeRangeLo },
+ { 0x03f3, 0x03f3, 0x0074, CanonicalizeRangeHi },
+ { 0x03f4, 0x03f4, 0x0009, CanonicalizeSet },
+ { 0x03f5, 0x03f5, 0x0008, CanonicalizeSet },
+ { 0x03f6, 0x03f6, 0x0000, CanonicalizeUnique },
+ { 0x03f7, 0x03f8, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x03f9, 0x03f9, 0x0007, CanonicalizeRangeHi },
+ { 0x03fa, 0x03fb, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x03fc, 0x03fc, 0x0000, CanonicalizeUnique },
+ { 0x03fd, 0x03ff, 0x0082, CanonicalizeRangeHi },
+ { 0x0400, 0x040f, 0x0050, CanonicalizeRangeLo },
+ { 0x0410, 0x0411, 0x0020, CanonicalizeRangeLo },
+ { 0x0412, 0x0412, 0x0012, CanonicalizeSet },
+ { 0x0413, 0x0413, 0x0020, CanonicalizeRangeLo },
+ { 0x0414, 0x0414, 0x0013, CanonicalizeSet },
+ { 0x0415, 0x041d, 0x0020, CanonicalizeRangeLo },
+ { 0x041e, 0x041e, 0x0014, CanonicalizeSet },
+ { 0x041f, 0x0420, 0x0020, CanonicalizeRangeLo },
+ { 0x0421, 0x0421, 0x0015, CanonicalizeSet },
+ { 0x0422, 0x0422, 0x0016, CanonicalizeSet },
+ { 0x0423, 0x0429, 0x0020, CanonicalizeRangeLo },
+ { 0x042a, 0x042a, 0x0017, CanonicalizeSet },
+ { 0x042b, 0x042f, 0x0020, CanonicalizeRangeLo },
+ { 0x0430, 0x0431, 0x0020, CanonicalizeRangeHi },
+ { 0x0432, 0x0432, 0x0012, CanonicalizeSet },
+ { 0x0433, 0x0433, 0x0020, CanonicalizeRangeHi },
+ { 0x0434, 0x0434, 0x0013, CanonicalizeSet },
+ { 0x0435, 0x043d, 0x0020, CanonicalizeRangeHi },
+ { 0x043e, 0x043e, 0x0014, CanonicalizeSet },
+ { 0x043f, 0x0440, 0x0020, CanonicalizeRangeHi },
+ { 0x0441, 0x0441, 0x0015, CanonicalizeSet },
+ { 0x0442, 0x0442, 0x0016, CanonicalizeSet },
+ { 0x0443, 0x0449, 0x0020, CanonicalizeRangeHi },
+ { 0x044a, 0x044a, 0x0017, CanonicalizeSet },
+ { 0x044b, 0x044f, 0x0020, CanonicalizeRangeHi },
+ { 0x0450, 0x045f, 0x0050, CanonicalizeRangeHi },
+ { 0x0460, 0x0461, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0462, 0x0463, 0x0018, CanonicalizeSet },
+ { 0x0464, 0x0481, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0482, 0x0489, 0x0000, CanonicalizeUnique },
+ { 0x048a, 0x04bf, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x04c0, 0x04c0, 0x000f, CanonicalizeRangeLo },
+ { 0x04c1, 0x04ce, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x04cf, 0x04cf, 0x000f, CanonicalizeRangeHi },
+ { 0x04d0, 0x052f, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x0530, 0x0530, 0x0000, CanonicalizeUnique },
+ { 0x0531, 0x0556, 0x0030, CanonicalizeRangeLo },
+ { 0x0557, 0x0560, 0x0000, CanonicalizeUnique },
+ { 0x0561, 0x0586, 0x0030, CanonicalizeRangeHi },
+ { 0x0587, 0x109f, 0x0000, CanonicalizeUnique },
+ { 0x10a0, 0x10c5, 0x1c60, CanonicalizeRangeLo },
+ { 0x10c6, 0x10c6, 0x0000, CanonicalizeUnique },
+ { 0x10c7, 0x10c7, 0x1c60, CanonicalizeRangeLo },
+ { 0x10c8, 0x10cc, 0x0000, CanonicalizeUnique },
+ { 0x10cd, 0x10cd, 0x1c60, CanonicalizeRangeLo },
+ { 0x10ce, 0x139f, 0x0000, CanonicalizeUnique },
+ { 0x13a0, 0x13ef, 0x97d0, CanonicalizeRangeLo },
+ { 0x13f0, 0x13f5, 0x0008, CanonicalizeRangeLo },
+ { 0x13f6, 0x13f7, 0x0000, CanonicalizeUnique },
+ { 0x13f8, 0x13fd, 0x0008, CanonicalizeRangeHi },
+ { 0x13fe, 0x1c7f, 0x0000, CanonicalizeUnique },
+ { 0x1c80, 0x1c80, 0x0012, CanonicalizeSet },
+ { 0x1c81, 0x1c81, 0x0013, CanonicalizeSet },
+ { 0x1c82, 0x1c82, 0x0014, CanonicalizeSet },
+ { 0x1c83, 0x1c83, 0x0015, CanonicalizeSet },
+ { 0x1c84, 0x1c85, 0x0016, CanonicalizeSet },
+ { 0x1c86, 0x1c86, 0x0017, CanonicalizeSet },
+ { 0x1c87, 0x1c87, 0x0018, CanonicalizeSet },
+ { 0x1c88, 0x1c88, 0x001a, CanonicalizeSet },
+ { 0x1c89, 0x1d78, 0x0000, CanonicalizeUnique },
+ { 0x1d79, 0x1d79, 0x8a04, CanonicalizeRangeLo },
+ { 0x1d7a, 0x1d7c, 0x0000, CanonicalizeUnique },
+ { 0x1d7d, 0x1d7d, 0x0ee6, CanonicalizeRangeLo },
+ { 0x1d7e, 0x1dff, 0x0000, CanonicalizeUnique },
+ { 0x1e00, 0x1e5f, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x1e60, 0x1e61, 0x0019, CanonicalizeSet },
+ { 0x1e62, 0x1e95, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x1e96, 0x1e9a, 0x0000, CanonicalizeUnique },
+ { 0x1e9b, 0x1e9b, 0x0019, CanonicalizeSet },
+ { 0x1e9c, 0x1e9d, 0x0000, CanonicalizeUnique },
+ { 0x1e9e, 0x1e9e, 0x1dbf, CanonicalizeRangeHi },
+ { 0x1e9f, 0x1e9f, 0x0000, CanonicalizeUnique },
+ { 0x1ea0, 0x1eff, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x1f00, 0x1f07, 0x0008, CanonicalizeRangeLo },
+ { 0x1f08, 0x1f0f, 0x0008, CanonicalizeRangeHi },
+ { 0x1f10, 0x1f15, 0x0008, CanonicalizeRangeLo },
+ { 0x1f16, 0x1f17, 0x0000, CanonicalizeUnique },
+ { 0x1f18, 0x1f1d, 0x0008, CanonicalizeRangeHi },
+ { 0x1f1e, 0x1f1f, 0x0000, CanonicalizeUnique },
+ { 0x1f20, 0x1f27, 0x0008, CanonicalizeRangeLo },
+ { 0x1f28, 0x1f2f, 0x0008, CanonicalizeRangeHi },
+ { 0x1f30, 0x1f37, 0x0008, CanonicalizeRangeLo },
+ { 0x1f38, 0x1f3f, 0x0008, CanonicalizeRangeHi },
+ { 0x1f40, 0x1f45, 0x0008, CanonicalizeRangeLo },
+ { 0x1f46, 0x1f47, 0x0000, CanonicalizeUnique },
+ { 0x1f48, 0x1f4d, 0x0008, CanonicalizeRangeHi },
+ { 0x1f4e, 0x1f50, 0x0000, CanonicalizeUnique },
+ { 0x1f51, 0x1f51, 0x0008, CanonicalizeRangeLo },
+ { 0x1f52, 0x1f52, 0x0000, CanonicalizeUnique },
+ { 0x1f53, 0x1f53, 0x0008, CanonicalizeRangeLo },
+ { 0x1f54, 0x1f54, 0x0000, CanonicalizeUnique },
+ { 0x1f55, 0x1f55, 0x0008, CanonicalizeRangeLo },
+ { 0x1f56, 0x1f56, 0x0000, CanonicalizeUnique },
+ { 0x1f57, 0x1f57, 0x0008, CanonicalizeRangeLo },
+ { 0x1f58, 0x1f58, 0x0000, CanonicalizeUnique },
+ { 0x1f59, 0x1f59, 0x0008, CanonicalizeRangeHi },
+ { 0x1f5a, 0x1f5a, 0x0000, CanonicalizeUnique },
+ { 0x1f5b, 0x1f5b, 0x0008, CanonicalizeRangeHi },
+ { 0x1f5c, 0x1f5c, 0x0000, CanonicalizeUnique },
+ { 0x1f5d, 0x1f5d, 0x0008, CanonicalizeRangeHi },
+ { 0x1f5e, 0x1f5e, 0x0000, CanonicalizeUnique },
+ { 0x1f5f, 0x1f5f, 0x0008, CanonicalizeRangeHi },
+ { 0x1f60, 0x1f67, 0x0008, CanonicalizeRangeLo },
+ { 0x1f68, 0x1f6f, 0x0008, CanonicalizeRangeHi },
+ { 0x1f70, 0x1f71, 0x004a, CanonicalizeRangeLo },
+ { 0x1f72, 0x1f75, 0x0056, CanonicalizeRangeLo },
+ { 0x1f76, 0x1f77, 0x0064, CanonicalizeRangeLo },
+ { 0x1f78, 0x1f79, 0x0080, CanonicalizeRangeLo },
+ { 0x1f7a, 0x1f7b, 0x0070, CanonicalizeRangeLo },
+ { 0x1f7c, 0x1f7d, 0x007e, CanonicalizeRangeLo },
+ { 0x1f7e, 0x1f7f, 0x0000, CanonicalizeUnique },
+ { 0x1f80, 0x1f87, 0x0008, CanonicalizeRangeLo },
+ { 0x1f88, 0x1f8f, 0x0008, CanonicalizeRangeHi },
+ { 0x1f90, 0x1f97, 0x0008, CanonicalizeRangeLo },
+ { 0x1f98, 0x1f9f, 0x0008, CanonicalizeRangeHi },
+ { 0x1fa0, 0x1fa7, 0x0008, CanonicalizeRangeLo },
+ { 0x1fa8, 0x1faf, 0x0008, CanonicalizeRangeHi },
+ { 0x1fb0, 0x1fb1, 0x0008, CanonicalizeRangeLo },
+ { 0x1fb2, 0x1fb2, 0x0000, CanonicalizeUnique },
+ { 0x1fb3, 0x1fb3, 0x0009, CanonicalizeRangeLo },
+ { 0x1fb4, 0x1fb7, 0x0000, CanonicalizeUnique },
+ { 0x1fb8, 0x1fb9, 0x0008, CanonicalizeRangeHi },
+ { 0x1fba, 0x1fbb, 0x004a, CanonicalizeRangeHi },
+ { 0x1fbc, 0x1fbc, 0x0009, CanonicalizeRangeHi },
+ { 0x1fbd, 0x1fbd, 0x0000, CanonicalizeUnique },
+ { 0x1fbe, 0x1fbe, 0x000a, CanonicalizeSet },
+ { 0x1fbf, 0x1fc2, 0x0000, CanonicalizeUnique },
+ { 0x1fc3, 0x1fc3, 0x0009, CanonicalizeRangeLo },
+ { 0x1fc4, 0x1fc7, 0x0000, CanonicalizeUnique },
+ { 0x1fc8, 0x1fcb, 0x0056, CanonicalizeRangeHi },
+ { 0x1fcc, 0x1fcc, 0x0009, CanonicalizeRangeHi },
+ { 0x1fcd, 0x1fcf, 0x0000, CanonicalizeUnique },
+ { 0x1fd0, 0x1fd1, 0x0008, CanonicalizeRangeLo },
+ { 0x1fd2, 0x1fd7, 0x0000, CanonicalizeUnique },
+ { 0x1fd8, 0x1fd9, 0x0008, CanonicalizeRangeHi },
+ { 0x1fda, 0x1fdb, 0x0064, CanonicalizeRangeHi },
+ { 0x1fdc, 0x1fdf, 0x0000, CanonicalizeUnique },
+ { 0x1fe0, 0x1fe1, 0x0008, CanonicalizeRangeLo },
+ { 0x1fe2, 0x1fe4, 0x0000, CanonicalizeUnique },
+ { 0x1fe5, 0x1fe5, 0x0007, CanonicalizeRangeLo },
+ { 0x1fe6, 0x1fe7, 0x0000, CanonicalizeUnique },
+ { 0x1fe8, 0x1fe9, 0x0008, CanonicalizeRangeHi },
+ { 0x1fea, 0x1feb, 0x0070, CanonicalizeRangeHi },
+ { 0x1fec, 0x1fec, 0x0007, CanonicalizeRangeHi },
+ { 0x1fed, 0x1ff2, 0x0000, CanonicalizeUnique },
+ { 0x1ff3, 0x1ff3, 0x0009, CanonicalizeRangeLo },
+ { 0x1ff4, 0x1ff7, 0x0000, CanonicalizeUnique },
+ { 0x1ff8, 0x1ff9, 0x0080, CanonicalizeRangeHi },
+ { 0x1ffa, 0x1ffb, 0x007e, CanonicalizeRangeHi },
+ { 0x1ffc, 0x1ffc, 0x0009, CanonicalizeRangeHi },
+ { 0x1ffd, 0x2125, 0x0000, CanonicalizeUnique },
+ { 0x2126, 0x2126, 0x0011, CanonicalizeSet },
+ { 0x2127, 0x2129, 0x0000, CanonicalizeUnique },
+ { 0x212a, 0x212a, 0x0000, CanonicalizeSet },
+ { 0x212b, 0x212b, 0x0002, CanonicalizeSet },
+ { 0x212c, 0x2131, 0x0000, CanonicalizeUnique },
+ { 0x2132, 0x2132, 0x001c, CanonicalizeRangeLo },
+ { 0x2133, 0x214d, 0x0000, CanonicalizeUnique },
+ { 0x214e, 0x214e, 0x001c, CanonicalizeRangeHi },
+ { 0x214f, 0x215f, 0x0000, CanonicalizeUnique },
+ { 0x2160, 0x216f, 0x0010, CanonicalizeRangeLo },
+ { 0x2170, 0x217f, 0x0010, CanonicalizeRangeHi },
+ { 0x2180, 0x2182, 0x0000, CanonicalizeUnique },
+ { 0x2183, 0x2184, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x2185, 0x24b5, 0x0000, CanonicalizeUnique },
+ { 0x24b6, 0x24cf, 0x001a, CanonicalizeRangeLo },
+ { 0x24d0, 0x24e9, 0x001a, CanonicalizeRangeHi },
+ { 0x24ea, 0x2bff, 0x0000, CanonicalizeUnique },
+ { 0x2c00, 0x2c2e, 0x0030, CanonicalizeRangeLo },
+ { 0x2c2f, 0x2c2f, 0x0000, CanonicalizeUnique },
+ { 0x2c30, 0x2c5e, 0x0030, CanonicalizeRangeHi },
+ { 0x2c5f, 0x2c5f, 0x0000, CanonicalizeUnique },
+ { 0x2c60, 0x2c61, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x2c62, 0x2c62, 0x29f7, CanonicalizeRangeHi },
+ { 0x2c63, 0x2c63, 0x0ee6, CanonicalizeRangeHi },
+ { 0x2c64, 0x2c64, 0x29e7, CanonicalizeRangeHi },
+ { 0x2c65, 0x2c65, 0x2a2b, CanonicalizeRangeHi },
+ { 0x2c66, 0x2c66, 0x2a28, CanonicalizeRangeHi },
+ { 0x2c67, 0x2c6c, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x2c6d, 0x2c6d, 0x2a1c, CanonicalizeRangeHi },
+ { 0x2c6e, 0x2c6e, 0x29fd, CanonicalizeRangeHi },
+ { 0x2c6f, 0x2c6f, 0x2a1f, CanonicalizeRangeHi },
+ { 0x2c70, 0x2c70, 0x2a1e, CanonicalizeRangeHi },
+ { 0x2c71, 0x2c71, 0x0000, CanonicalizeUnique },
+ { 0x2c72, 0x2c73, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x2c74, 0x2c74, 0x0000, CanonicalizeUnique },
+ { 0x2c75, 0x2c76, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x2c77, 0x2c7d, 0x0000, CanonicalizeUnique },
+ { 0x2c7e, 0x2c7f, 0x2a3f, CanonicalizeRangeHi },
+ { 0x2c80, 0x2ce3, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x2ce4, 0x2cea, 0x0000, CanonicalizeUnique },
+ { 0x2ceb, 0x2cee, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0x2cef, 0x2cf1, 0x0000, CanonicalizeUnique },
+ { 0x2cf2, 0x2cf3, 0x0000, CanonicalizeAlternatingAligned },
+ { 0x2cf4, 0x2cff, 0x0000, CanonicalizeUnique },
+ { 0x2d00, 0x2d25, 0x1c60, CanonicalizeRangeHi },
+ { 0x2d26, 0x2d26, 0x0000, CanonicalizeUnique },
+ { 0x2d27, 0x2d27, 0x1c60, CanonicalizeRangeHi },
+ { 0x2d28, 0x2d2c, 0x0000, CanonicalizeUnique },
+ { 0x2d2d, 0x2d2d, 0x1c60, CanonicalizeRangeHi },
+ { 0x2d2e, 0xa63f, 0x0000, CanonicalizeUnique },
+ { 0xa640, 0xa649, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa64a, 0xa64b, 0x001a, CanonicalizeSet },
+ { 0xa64c, 0xa66d, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa66e, 0xa67f, 0x0000, CanonicalizeUnique },
+ { 0xa680, 0xa69b, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa69c, 0xa721, 0x0000, CanonicalizeUnique },
+ { 0xa722, 0xa72f, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa730, 0xa731, 0x0000, CanonicalizeUnique },
+ { 0xa732, 0xa76f, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa770, 0xa778, 0x0000, CanonicalizeUnique },
+ { 0xa779, 0xa77c, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0xa77d, 0xa77d, 0x8a04, CanonicalizeRangeHi },
+ { 0xa77e, 0xa787, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa788, 0xa78a, 0x0000, CanonicalizeUnique },
+ { 0xa78b, 0xa78c, 0x0000, CanonicalizeAlternatingUnaligned },
+ { 0xa78d, 0xa78d, 0xa528, CanonicalizeRangeHi },
+ { 0xa78e, 0xa78f, 0x0000, CanonicalizeUnique },
+ { 0xa790, 0xa793, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa794, 0xa795, 0x0000, CanonicalizeUnique },
+ { 0xa796, 0xa7a9, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa7aa, 0xa7aa, 0xa544, CanonicalizeRangeHi },
+ { 0xa7ab, 0xa7ab, 0xa54f, CanonicalizeRangeHi },
+ { 0xa7ac, 0xa7ac, 0xa54b, CanonicalizeRangeHi },
+ { 0xa7ad, 0xa7ad, 0xa541, CanonicalizeRangeHi },
+ { 0xa7ae, 0xa7ae, 0xa544, CanonicalizeRangeHi },
+ { 0xa7af, 0xa7af, 0x0000, CanonicalizeUnique },
+ { 0xa7b0, 0xa7b0, 0xa512, CanonicalizeRangeHi },
+ { 0xa7b1, 0xa7b1, 0xa52a, CanonicalizeRangeHi },
+ { 0xa7b2, 0xa7b2, 0xa515, CanonicalizeRangeHi },
+ { 0xa7b3, 0xa7b3, 0x03a0, CanonicalizeRangeLo },
+ { 0xa7b4, 0xa7b7, 0x0000, CanonicalizeAlternatingAligned },
+ { 0xa7b8, 0xab52, 0x0000, CanonicalizeUnique },
+ { 0xab53, 0xab53, 0x03a0, CanonicalizeRangeHi },
+ { 0xab54, 0xab6f, 0x0000, CanonicalizeUnique },
+ { 0xab70, 0xabbf, 0x97d0, CanonicalizeRangeHi },
+ { 0xabc0, 0xff20, 0x0000, CanonicalizeUnique },
+ { 0xff21, 0xff3a, 0x0020, CanonicalizeRangeLo },
+ { 0xff3b, 0xff40, 0x0000, CanonicalizeUnique },
+ { 0xff41, 0xff5a, 0x0020, CanonicalizeRangeHi },
+ { 0xff5b, 0x103ff, 0x0000, CanonicalizeUnique },
+ { 0x10400, 0x10427, 0x0028, CanonicalizeRangeLo },
+ { 0x10428, 0x1044f, 0x0028, CanonicalizeRangeHi },
+ { 0x10450, 0x104af, 0x0000, CanonicalizeUnique },
+ { 0x104b0, 0x104d3, 0x0028, CanonicalizeRangeLo },
+ { 0x104d4, 0x104d7, 0x0000, CanonicalizeUnique },
+ { 0x104d8, 0x104fb, 0x0028, CanonicalizeRangeHi },
+ { 0x104fc, 0x10c7f, 0x0000, CanonicalizeUnique },
+ { 0x10c80, 0x10cb2, 0x0040, CanonicalizeRangeLo },
+ { 0x10cb3, 0x10cbf, 0x0000, CanonicalizeUnique },
+ { 0x10cc0, 0x10cf2, 0x0040, CanonicalizeRangeHi },
+ { 0x10cf3, 0x1189f, 0x0000, CanonicalizeUnique },
+ { 0x118a0, 0x118bf, 0x0020, CanonicalizeRangeLo },
+ { 0x118c0, 0x118df, 0x0020, CanonicalizeRangeHi },
+ { 0x118e0, 0x1e8ff, 0x0000, CanonicalizeUnique },
+ { 0x1e900, 0x1e921, 0x0022, CanonicalizeRangeLo },
+ { 0x1e922, 0x1e943, 0x0022, CanonicalizeRangeHi },
+ { 0x1e944, 0x10ffff, 0x0000, CanonicalizeUnique },
+};
+
+} } // JSC::Yarr
diff --git a/src/3rdparty/masm/yarr/YarrErrorCode.cpp b/src/3rdparty/masm/yarr/YarrErrorCode.cpp
new file mode 100644
index 0000000000..aaebd4613d
--- /dev/null
+++ b/src/3rdparty/masm/yarr/YarrErrorCode.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 Yusuke Suzuki <utatane.tea@gmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
+ */
+
+#include "config.h"
+#include "YarrErrorCode.h"
+
+#include "Error.h"
+
+namespace JSC { namespace Yarr {
+
+const char* errorMessage(ErrorCode error)
+{
+#define REGEXP_ERROR_PREFIX "Invalid regular expression: "
+ // The order of this array must match the ErrorCode enum.
+ static const char* errorMessages[] = {
+ nullptr, // NoError
+ REGEXP_ERROR_PREFIX "regular expression too large", // PatternTooLarge
+ REGEXP_ERROR_PREFIX "numbers out of order in {} quantifier", // QuantifierOutOfOrder
+ REGEXP_ERROR_PREFIX "nothing to repeat", // QuantifierWithoutAtom
+ REGEXP_ERROR_PREFIX "number too large in {} quantifier", // QuantifierTooLarge
+ REGEXP_ERROR_PREFIX "missing )", // MissingParentheses
+ REGEXP_ERROR_PREFIX "unmatched parentheses", // ParenthesesUnmatched
+ REGEXP_ERROR_PREFIX "unrecognized character after (?", // ParenthesesTypeInvalid
+ REGEXP_ERROR_PREFIX "invalid group specifier name", // InvalidGroupName
+ REGEXP_ERROR_PREFIX "duplicate group specifier name", // DuplicateGroupName
+ REGEXP_ERROR_PREFIX "missing terminating ] for character class", // CharacterClassUnmatched
+ REGEXP_ERROR_PREFIX "range out of order in character class", // CharacterClassOutOfOrder
+ REGEXP_ERROR_PREFIX "\\ at end of pattern", // EscapeUnterminated
+ REGEXP_ERROR_PREFIX "invalid unicode {} escape", // InvalidUnicodeEscape
+ REGEXP_ERROR_PREFIX "invalid backreference for unicode pattern", // InvalidBackreference
+ REGEXP_ERROR_PREFIX "invalid escaped character for unicode pattern", // InvalidIdentityEscape
+ REGEXP_ERROR_PREFIX "invalid property expression", // InvalidUnicodePropertyExpression
+ REGEXP_ERROR_PREFIX "too many nested disjunctions", // TooManyDisjunctions
+ REGEXP_ERROR_PREFIX "pattern exceeds string length limits", // OffsetTooLarge
+ REGEXP_ERROR_PREFIX "invalid flags" // InvalidRegularExpressionFlags
+ };
+
+ return errorMessages[static_cast<unsigned>(error)];
+}
+
+JSObject* errorToThrow(ExecState* exec, ErrorCode error)
+{
+ switch (error) {
+ case ErrorCode::NoError:
+ ASSERT_NOT_REACHED();
+ return nullptr;
+ case ErrorCode::PatternTooLarge:
+ case ErrorCode::QuantifierOutOfOrder:
+ case ErrorCode::QuantifierWithoutAtom:
+ case ErrorCode::QuantifierTooLarge:
+ case ErrorCode::MissingParentheses:
+ case ErrorCode::ParenthesesUnmatched:
+ case ErrorCode::ParenthesesTypeInvalid:
+ case ErrorCode::InvalidGroupName:
+ case ErrorCode::DuplicateGroupName:
+ case ErrorCode::CharacterClassUnmatched:
+ case ErrorCode::CharacterClassOutOfOrder:
+ case ErrorCode::EscapeUnterminated:
+ case ErrorCode::InvalidUnicodeEscape:
+ case ErrorCode::InvalidBackreference:
+ case ErrorCode::InvalidIdentityEscape:
+ case ErrorCode::InvalidUnicodePropertyExpression:
+ case ErrorCode::OffsetTooLarge:
+ case ErrorCode::InvalidRegularExpressionFlags:
+ return createSyntaxError(exec, errorMessage(error));
+ case ErrorCode::TooManyDisjunctions:
+ return createOutOfMemoryError(exec, errorMessage(error));
+ }
+
+ ASSERT_NOT_REACHED();
+ return nullptr;
+}
+
+} } // namespace JSC::Yarr
diff --git a/src/3rdparty/masm/yarr/YarrErrorCode.h b/src/3rdparty/masm/yarr/YarrErrorCode.h
new file mode 100644
index 0000000000..48f2bb7900
--- /dev/null
+++ b/src/3rdparty/masm/yarr/YarrErrorCode.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 Yusuke Suzuki <utatane.tea@gmail.com>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 APPLE INC. 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.
+ */
+
+#pragma once
+
+namespace JSC {
+
+class ExecState;
+class JSObject;
+
+namespace Yarr {
+
+enum class ErrorCode : uint8_t {
+ NoError = 0,
+ PatternTooLarge,
+ QuantifierOutOfOrder,
+ QuantifierWithoutAtom,
+ QuantifierTooLarge,
+ MissingParentheses,
+ ParenthesesUnmatched,
+ ParenthesesTypeInvalid,
+ InvalidGroupName,
+ DuplicateGroupName,
+ CharacterClassUnmatched,
+ CharacterClassOutOfOrder,
+ EscapeUnterminated,
+ InvalidUnicodeEscape,
+ InvalidBackreference,
+ InvalidIdentityEscape,
+ InvalidUnicodePropertyExpression,
+ TooManyDisjunctions,
+ OffsetTooLarge,
+ InvalidRegularExpressionFlags,
+};
+
+JS_EXPORT_PRIVATE const char* errorMessage(ErrorCode);
+inline bool hasError(ErrorCode errorCode)
+{
+ return errorCode != ErrorCode::NoError;
+}
+JS_EXPORT_PRIVATE JSObject* errorToThrow(ExecState*, ErrorCode);
+
+} } // namespace JSC::Yarr
diff --git a/src/3rdparty/masm/yarr/YarrInterpreter.cpp b/src/3rdparty/masm/yarr/YarrInterpreter.cpp
index 16fc183cad..6eb6750dc4 100644
--- a/src/3rdparty/masm/yarr/YarrInterpreter.cpp
+++ b/src/3rdparty/masm/yarr/YarrInterpreter.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009, 2013-2017 Apple Inc. All rights reserved.
* Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged
*
* Redistribution and use in source and binary forms, with or without
@@ -27,17 +27,15 @@
#include "config.h"
#include "YarrInterpreter.h"
+#include "Options.h"
+#include "SuperSampler.h"
#include "Yarr.h"
-#include "YarrCanonicalizeUCS2.h"
+#include "YarrCanonicalize.h"
#include <wtf/BumpPointerAllocator.h>
#include <wtf/DataLog.h>
#include <wtf/text/CString.h>
#include <wtf/text/WTFString.h>
-#ifndef NDEBUG
-#include <stdio.h>
-#endif
-
using namespace WTF;
namespace JSC { namespace Yarr {
@@ -47,28 +45,6 @@ class Interpreter {
public:
struct ParenthesesDisjunctionContext;
- struct BackTrackInfoPatternCharacter {
- uintptr_t matchAmount;
- };
- struct BackTrackInfoCharacterClass {
- uintptr_t matchAmount;
- };
- struct BackTrackInfoBackReference {
- uintptr_t begin; // Not really needed for greedy quantifiers.
- uintptr_t matchAmount; // Not really needed for fixed quantifiers.
- };
- struct BackTrackInfoAlternative {
- uintptr_t offset;
- };
- struct BackTrackInfoParentheticalAssertion {
- uintptr_t begin;
- };
- struct BackTrackInfoParenthesesOnce {
- uintptr_t begin;
- };
- struct BackTrackInfoParenthesesTerminal {
- uintptr_t begin;
- };
struct BackTrackInfoParentheses {
uintptr_t matchAmount;
ParenthesesDisjunctionContext* lastContext;
@@ -158,7 +134,7 @@ public:
ParenthesesDisjunctionContext* allocParenthesesDisjunctionContext(ByteDisjunction* disjunction, unsigned* output, ByteTerm& term)
{
- size_t size = sizeof(ParenthesesDisjunctionContext) - sizeof(unsigned) + (term.atom.parenthesesDisjunction->m_numSubpatterns << 1) * sizeof(unsigned) + sizeof(DisjunctionContext) - sizeof(uintptr_t) + disjunction->m_frameSize * sizeof(uintptr_t);
+ size_t size = sizeof(ParenthesesDisjunctionContext) - sizeof(unsigned) + (term.atom.parenthesesDisjunction->m_numSubpatterns << 1) * sizeof(unsigned) + sizeof(DisjunctionContext) - sizeof(uintptr_t) + static_cast<size_t>(disjunction->m_frameSize) * sizeof(uintptr_t);
allocatorPool = allocatorPool->ensureCapacity(size);
RELEASE_ASSERT(allocatorPool);
return new (allocatorPool->alloc(size)) ParenthesesDisjunctionContext(output, term);
@@ -171,10 +147,11 @@ public:
class InputStream {
public:
- InputStream(const CharType* input, unsigned start, unsigned length)
+ InputStream(const CharType* input, unsigned start, unsigned length, bool decodeSurrogatePairs)
: input(input)
, pos(start)
, length(length)
+ , decodeSurrogatePairs(decodeSurrogatePairs)
{
}
@@ -208,13 +185,40 @@ public:
RELEASE_ASSERT(pos >= negativePositionOffest);
unsigned p = pos - negativePositionOffest;
ASSERT(p < length);
- return input[p];
+ int result = input[p];
+ if (U16_IS_LEAD(result) && decodeSurrogatePairs && p + 1 < length && U16_IS_TRAIL(input[p + 1])) {
+ if (atEnd())
+ return -1;
+
+ result = U16_GET_SUPPLEMENTARY(result, input[p + 1]);
+ next();
+ }
+ return result;
+ }
+
+ int readSurrogatePairChecked(unsigned negativePositionOffset)
+ {
+ RELEASE_ASSERT(pos >= negativePositionOffset);
+ unsigned p = pos - negativePositionOffset;
+ ASSERT(p < length);
+ if (p + 1 >= length)
+ return -1;
+
+ int first = input[p];
+ int second = input[p + 1];
+ if (U16_IS_LEAD(first) && U16_IS_TRAIL(second))
+ return U16_GET_SUPPLEMENTARY(first, second);
+
+ return -1;
}
int reread(unsigned from)
{
ASSERT(from < length);
- return input[from];
+ int result = input[from];
+ if (U16_IS_LEAD(result) && decodeSurrogatePairs && from + 1 < length && U16_IS_TRAIL(input[from + 1]))
+ result = U16_GET_SUPPLEMENTARY(result, input[from + 1]);
+ return result;
}
int prev()
@@ -265,9 +269,9 @@ public:
pos -= count;
}
- bool atStart(unsigned negativePositionOffest)
+ bool atStart(unsigned negativePositionOffset)
{
- return pos == negativePositionOffest;
+ return pos == negativePositionOffset;
}
bool atEnd(unsigned negativePositionOffest)
@@ -285,24 +289,106 @@ public:
const CharType* input;
unsigned pos;
unsigned length;
+ bool decodeSurrogatePairs;
};
bool testCharacterClass(CharacterClass* characterClass, int ch)
{
- if (ch & 0xFF80) {
- for (unsigned i = 0; i < characterClass->m_matchesUnicode.size(); ++i)
- if (ch == characterClass->m_matchesUnicode[i])
+ auto linearSearchMatches = [&ch](const Vector<UChar32>& matches) {
+ for (unsigned i = 0; i < matches.size(); ++i) {
+ if (ch == matches[i])
+ return true;
+ }
+
+ return false;
+ };
+
+ auto binarySearchMatches = [&ch](const Vector<UChar32>& matches) {
+ size_t low = 0;
+ size_t high = matches.size() - 1;
+
+ while (low <= high) {
+ size_t mid = low + (high - low) / 2;
+ int diff = ch - matches[mid];
+ if (!diff)
+ return true;
+
+ if (diff < 0) {
+ if (mid == low)
+ return false;
+ high = mid - 1;
+ } else
+ low = mid + 1;
+ }
+ return false;
+ };
+
+ auto linearSearchRanges = [&ch](const Vector<CharacterRange>& ranges) {
+ for (unsigned i = 0; i < ranges.size(); ++i) {
+ if ((ch >= ranges[i].begin) && (ch <= ranges[i].end))
return true;
- for (unsigned i = 0; i < characterClass->m_rangesUnicode.size(); ++i)
- if ((ch >= characterClass->m_rangesUnicode[i].begin) && (ch <= characterClass->m_rangesUnicode[i].end))
+ }
+
+ return false;
+ };
+
+ auto binarySearchRanges = [&ch](const Vector<CharacterRange>& ranges) {
+ size_t low = 0;
+ size_t high = ranges.size() - 1;
+
+ while (low <= high) {
+ size_t mid = low + (high - low) / 2;
+ int rangeBeginDiff = ch - ranges[mid].begin;
+ if (rangeBeginDiff >= 0 && ch <= ranges[mid].end)
return true;
+
+ if (rangeBeginDiff < 0) {
+ if (mid == low)
+ return false;
+ high = mid - 1;
+ } else
+ low = mid + 1;
+ }
+ return false;
+ };
+
+ if (characterClass->m_anyCharacter)
+ return true;
+
+ const size_t thresholdForBinarySearch = 6;
+
+ if (!isASCII(ch)) {
+ if (characterClass->m_matchesUnicode.size()) {
+ if (characterClass->m_matchesUnicode.size() > thresholdForBinarySearch) {
+ if (binarySearchMatches(characterClass->m_matchesUnicode))
+ return true;
+ } else if (linearSearchMatches(characterClass->m_matchesUnicode))
+ return true;
+ }
+
+ if (characterClass->m_rangesUnicode.size()) {
+ if (characterClass->m_rangesUnicode.size() > thresholdForBinarySearch) {
+ if (binarySearchRanges(characterClass->m_rangesUnicode))
+ return true;
+ } else if (linearSearchRanges(characterClass->m_rangesUnicode))
+ return true;
+ }
} else {
- for (unsigned i = 0; i < characterClass->m_matches.size(); ++i)
- if (ch == characterClass->m_matches[i])
+ if (characterClass->m_matches.size()) {
+ if (characterClass->m_matches.size() > thresholdForBinarySearch) {
+ if (binarySearchMatches(characterClass->m_matches))
+ return true;
+ } else if (linearSearchMatches(characterClass->m_matches))
return true;
- for (unsigned i = 0; i < characterClass->m_ranges.size(); ++i)
- if ((ch >= characterClass->m_ranges[i].begin) && (ch <= characterClass->m_ranges[i].end))
+ }
+
+ if (characterClass->m_ranges.size()) {
+ if (characterClass->m_ranges.size() > thresholdForBinarySearch) {
+ if (binarySearchRanges(characterClass->m_ranges))
+ return true;
+ } else if (linearSearchRanges(characterClass->m_ranges))
return true;
+ }
}
return false;
@@ -313,6 +399,11 @@ public:
return testChar == input.readChecked(negativeInputOffset);
}
+ bool checkSurrogatePair(int testUnicodeChar, unsigned negativeInputOffset)
+ {
+ return testUnicodeChar == input.readSurrogatePairChecked(negativeInputOffset);
+ }
+
bool checkCasedCharacter(int loChar, int hiChar, unsigned negativeInputOffset)
{
int ch = input.readChecked(negativeInputOffset);
@@ -332,32 +423,31 @@ public:
if (!input.checkInput(matchSize))
return false;
- if (pattern->m_ignoreCase) {
- for (unsigned i = 0; i < matchSize; ++i) {
- int oldCh = input.reread(matchBegin + i);
- int ch = input.readChecked(negativeInputOffset + matchSize - i);
-
- if (oldCh == ch)
- continue;
-
- // The definition for canonicalize (see ES 5.1, 15.10.2.8) means that
- // unicode values are never allowed to match against ascii ones.
- if (isASCII(oldCh) || isASCII(ch)) {
+ for (unsigned i = 0; i < matchSize; ++i) {
+ int oldCh = input.reread(matchBegin + i);
+ int ch;
+ if (!U_IS_BMP(oldCh)) {
+ ch = input.readSurrogatePairChecked(negativeInputOffset + matchSize - i);
+ ++i;
+ } else
+ ch = input.readChecked(negativeInputOffset + matchSize - i);
+
+ if (oldCh == ch)
+ continue;
+
+ if (pattern->ignoreCase()) {
+ // See ES 6.0, 21.2.2.8.2 for the definition of Canonicalize(). For non-Unicode
+ // patterns, Unicode values are never allowed to match against ASCII ones.
+ // For Unicode, we need to check all canonical equivalents of a character.
+ if (!unicode && (isASCII(oldCh) || isASCII(ch))) {
if (toASCIIUpper(oldCh) == toASCIIUpper(ch))
continue;
- } else if (areCanonicallyEquivalent(oldCh, ch))
+ } else if (areCanonicallyEquivalent(oldCh, ch, unicode ? CanonicalMode::Unicode : CanonicalMode::UCS2))
continue;
-
- input.uncheckInput(matchSize);
- return false;
- }
- } else {
- for (unsigned i = 0; i < matchSize; ++i) {
- if (!checkCharacter(input.reread(matchBegin + i), negativeInputOffset + matchSize - i)) {
- input.uncheckInput(matchSize);
- return false;
- }
}
+
+ input.uncheckInput(matchSize);
+ return false;
}
return true;
@@ -365,15 +455,15 @@ public:
bool matchAssertionBOL(ByteTerm& term)
{
- return (input.atStart(term.inputPosition)) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.readChecked(term.inputPosition + 1)));
+ return (input.atStart(term.inputPosition)) || (pattern->multiline() && testCharacterClass(pattern->newlineCharacterClass, input.readChecked(term.inputPosition + 1)));
}
bool matchAssertionEOL(ByteTerm& term)
{
if (term.inputPosition)
- return (input.atEnd(term.inputPosition)) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.readChecked(term.inputPosition)));
+ return (input.atEnd(term.inputPosition)) || (pattern->multiline() && testCharacterClass(pattern->newlineCharacterClass, input.readChecked(term.inputPosition)));
- return (input.atEnd()) || (pattern->m_multiline && testCharacterClass(pattern->newlineCharacterClass, input.read()));
+ return (input.atEnd()) || (pattern->multiline() && testCharacterClass(pattern->newlineCharacterClass, input.read()));
}
bool matchAssertionWordBoundary(ByteTerm& term)
@@ -400,18 +490,18 @@ public:
case QuantifierGreedy:
if (backTrack->matchAmount) {
--backTrack->matchAmount;
- input.uncheckInput(1);
+ input.uncheckInput(U16_LENGTH(term.atom.patternCharacter));
return true;
}
break;
case QuantifierNonGreedy:
- if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) {
+ if ((backTrack->matchAmount < term.atom.quantityMaxCount) && input.checkInput(1)) {
++backTrack->matchAmount;
if (checkCharacter(term.atom.patternCharacter, term.inputPosition + 1))
return true;
}
- input.uncheckInput(backTrack->matchAmount);
+ input.setPos(backTrack->begin);
break;
}
@@ -435,7 +525,7 @@ public:
break;
case QuantifierNonGreedy:
- if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) {
+ if ((backTrack->matchAmount < term.atom.quantityMaxCount) && input.checkInput(1)) {
++backTrack->matchAmount;
if (checkCasedCharacter(term.atom.casedCharacter.lo, term.atom.casedCharacter.hi, term.inputPosition + 1))
return true;
@@ -450,11 +540,24 @@ public:
bool matchCharacterClass(ByteTerm& term, DisjunctionContext* context)
{
ASSERT(term.type == ByteTerm::TypeCharacterClass);
- BackTrackInfoPatternCharacter* backTrack = reinterpret_cast<BackTrackInfoPatternCharacter*>(context->frame + term.frameLocation);
+ BackTrackInfoCharacterClass* backTrack = reinterpret_cast<BackTrackInfoCharacterClass*>(context->frame + term.frameLocation);
switch (term.atom.quantityType) {
case QuantifierFixedCount: {
- for (unsigned matchAmount = 0; matchAmount < term.atom.quantityCount; ++matchAmount) {
+ if (unicode) {
+ backTrack->begin = input.getPos();
+ unsigned matchAmount = 0;
+ for (matchAmount = 0; matchAmount < term.atom.quantityMaxCount; ++matchAmount) {
+ if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition - matchAmount)) {
+ input.setPos(backTrack->begin);
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ for (unsigned matchAmount = 0; matchAmount < term.atom.quantityMaxCount; ++matchAmount) {
if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition - matchAmount))
return false;
}
@@ -462,13 +565,16 @@ public:
}
case QuantifierGreedy: {
+ unsigned position = input.getPos();
+ backTrack->begin = position;
unsigned matchAmount = 0;
- while ((matchAmount < term.atom.quantityCount) && input.checkInput(1)) {
+ while ((matchAmount < term.atom.quantityMaxCount) && input.checkInput(1)) {
if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition + 1)) {
- input.uncheckInput(1);
+ input.setPos(position);
break;
}
++matchAmount;
+ position = input.getPos();
}
backTrack->matchAmount = matchAmount;
@@ -476,6 +582,7 @@ public:
}
case QuantifierNonGreedy:
+ backTrack->begin = input.getPos();
backTrack->matchAmount = 0;
return true;
}
@@ -487,14 +594,28 @@ public:
bool backtrackCharacterClass(ByteTerm& term, DisjunctionContext* context)
{
ASSERT(term.type == ByteTerm::TypeCharacterClass);
- BackTrackInfoPatternCharacter* backTrack = reinterpret_cast<BackTrackInfoPatternCharacter*>(context->frame + term.frameLocation);
+ BackTrackInfoCharacterClass* backTrack = reinterpret_cast<BackTrackInfoCharacterClass*>(context->frame + term.frameLocation);
switch (term.atom.quantityType) {
case QuantifierFixedCount:
+ if (unicode)
+ input.setPos(backTrack->begin);
break;
case QuantifierGreedy:
if (backTrack->matchAmount) {
+ if (unicode) {
+ // Rematch one less match
+ input.setPos(backTrack->begin);
+ --backTrack->matchAmount;
+ for (unsigned matchAmount = 0; (matchAmount < backTrack->matchAmount) && input.checkInput(1); ++matchAmount) {
+ if (!checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition + 1)) {
+ input.uncheckInput(1);
+ break;
+ }
+ }
+ return true;
+ }
--backTrack->matchAmount;
input.uncheckInput(1);
return true;
@@ -502,12 +623,12 @@ public:
break;
case QuantifierNonGreedy:
- if ((backTrack->matchAmount < term.atom.quantityCount) && input.checkInput(1)) {
+ if ((backTrack->matchAmount < term.atom.quantityMaxCount) && input.checkInput(1)) {
++backTrack->matchAmount;
if (checkCharacterClass(term.atom.characterClass, term.invert(), term.inputPosition + 1))
return true;
}
- input.uncheckInput(backTrack->matchAmount);
+ input.setPos(backTrack->begin);
break;
}
@@ -539,7 +660,7 @@ public:
switch (term.atom.quantityType) {
case QuantifierFixedCount: {
backTrack->begin = input.getPos();
- for (unsigned matchAmount = 0; matchAmount < term.atom.quantityCount; ++matchAmount) {
+ for (unsigned matchAmount = 0; matchAmount < term.atom.quantityMaxCount; ++matchAmount) {
if (!tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) {
input.setPos(backTrack->begin);
return false;
@@ -550,7 +671,7 @@ public:
case QuantifierGreedy: {
unsigned matchAmount = 0;
- while ((matchAmount < term.atom.quantityCount) && tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition))
+ while ((matchAmount < term.atom.quantityMaxCount) && tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition))
++matchAmount;
backTrack->matchAmount = matchAmount;
return true;
@@ -584,7 +705,7 @@ public:
switch (term.atom.quantityType) {
case QuantifierFixedCount:
- // for quantityCount == 1, could rewind.
+ // for quantityMaxCount == 1, could rewind.
input.setPos(backTrack->begin);
break;
@@ -597,7 +718,7 @@ public:
break;
case QuantifierNonGreedy:
- if ((backTrack->matchAmount < term.atom.quantityCount) && tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) {
+ if ((backTrack->matchAmount < term.atom.quantityMaxCount) && tryConsumeBackReference(matchBegin, matchEnd, term.inputPosition)) {
++backTrack->matchAmount;
return true;
}
@@ -612,8 +733,8 @@ public:
{
if (term.capture()) {
unsigned subpatternId = term.atom.subpatternId;
- output[(subpatternId << 1)] = context->getDisjunctionContext(term)->matchBegin + term.inputPosition;
- output[(subpatternId << 1) + 1] = context->getDisjunctionContext(term)->matchEnd + term.inputPosition;
+ output[(subpatternId << 1)] = context->getDisjunctionContext(term)->matchBegin - term.inputPosition;
+ output[(subpatternId << 1) + 1] = context->getDisjunctionContext(term)->matchEnd - term.inputPosition;
}
}
void resetMatches(ByteTerm& term, ParenthesesDisjunctionContext* context)
@@ -645,7 +766,7 @@ public:
bool matchParenthesesOnceBegin(ByteTerm& term, DisjunctionContext* context)
{
ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceBegin);
- ASSERT(term.atom.quantityCount == 1);
+ ASSERT(term.atom.quantityMaxCount == 1);
BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast<BackTrackInfoParenthesesOnce*>(context->frame + term.frameLocation);
@@ -675,11 +796,11 @@ public:
bool matchParenthesesOnceEnd(ByteTerm& term, DisjunctionContext* context)
{
ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceEnd);
- ASSERT(term.atom.quantityCount == 1);
+ ASSERT(term.atom.quantityMaxCount == 1);
if (term.capture()) {
unsigned subpatternId = term.atom.subpatternId;
- output[(subpatternId << 1) + 1] = input.getPos() + term.inputPosition;
+ output[(subpatternId << 1) + 1] = input.getPos() - term.inputPosition;
}
if (term.atom.quantityType == QuantifierFixedCount)
@@ -692,7 +813,7 @@ public:
bool backtrackParenthesesOnceBegin(ByteTerm& term, DisjunctionContext* context)
{
ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceBegin);
- ASSERT(term.atom.quantityCount == 1);
+ ASSERT(term.atom.quantityMaxCount == 1);
BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast<BackTrackInfoParenthesesOnce*>(context->frame + term.frameLocation);
@@ -711,6 +832,7 @@ public:
return true;
case QuantifierNonGreedy:
ASSERT(backTrack->begin != notFound);
+ FALLTHROUGH;
case QuantifierFixedCount:
break;
}
@@ -721,7 +843,7 @@ public:
bool backtrackParenthesesOnceEnd(ByteTerm& term, DisjunctionContext* context)
{
ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternOnceEnd);
- ASSERT(term.atom.quantityCount == 1);
+ ASSERT(term.atom.quantityMaxCount == 1);
BackTrackInfoParenthesesOnce* backTrack = reinterpret_cast<BackTrackInfoParenthesesOnce*>(context->frame + term.frameLocation);
@@ -731,7 +853,7 @@ public:
context->term -= term.atom.parenthesesWidth;
return false;
}
- Q_FALLTHROUGH();
+ FALLTHROUGH;
case QuantifierNonGreedy:
if (backTrack->begin == notFound) {
backTrack->begin = input.getPos();
@@ -742,11 +864,12 @@ public:
ASSERT((&term - term.atom.parenthesesWidth)->type == ByteTerm::TypeParenthesesSubpatternOnceBegin);
ASSERT((&term - term.atom.parenthesesWidth)->inputPosition == term.inputPosition);
unsigned subpatternId = term.atom.subpatternId;
- output[subpatternId << 1] = input.getPos() + term.inputPosition;
+ output[subpatternId << 1] = input.getPos() - term.inputPosition;
}
context->term -= term.atom.parenthesesWidth;
return true;
}
+ FALLTHROUGH;
case QuantifierFixedCount:
break;
}
@@ -758,7 +881,7 @@ public:
{
ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalBegin);
ASSERT(term.atom.quantityType == QuantifierGreedy);
- ASSERT(term.atom.quantityCount == quantifyInfinite);
+ ASSERT(term.atom.quantityMaxCount == quantifyInfinite);
ASSERT(!term.capture());
BackTrackInfoParenthesesTerminal* backTrack = reinterpret_cast<BackTrackInfoParenthesesTerminal*>(context->frame + term.frameLocation);
@@ -775,7 +898,7 @@ public:
if (backTrack->begin == input.getPos())
return false;
- // Successful match! Okay, what's next? - loop around and try to match moar!
+ // Successful match! Okay, what's next? - loop around and try to match more!
context->term -= (term.atom.parenthesesWidth + 1);
return true;
}
@@ -784,7 +907,7 @@ public:
{
ASSERT(term.type == ByteTerm::TypeParenthesesSubpatternTerminalBegin);
ASSERT(term.atom.quantityType == QuantifierGreedy);
- ASSERT(term.atom.quantityCount == quantifyInfinite);
+ ASSERT(term.atom.quantityMaxCount == quantifyInfinite);
ASSERT(!term.capture());
// If we backtrack to this point, we have failed to match this iteration of the parens.
@@ -804,7 +927,7 @@ public:
bool matchParentheticalAssertionBegin(ByteTerm& term, DisjunctionContext* context)
{
ASSERT(term.type == ByteTerm::TypeParentheticalAssertionBegin);
- ASSERT(term.atom.quantityCount == 1);
+ ASSERT(term.atom.quantityMaxCount == 1);
BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast<BackTrackInfoParentheticalAssertion*>(context->frame + term.frameLocation);
@@ -815,7 +938,7 @@ public:
bool matchParentheticalAssertionEnd(ByteTerm& term, DisjunctionContext* context)
{
ASSERT(term.type == ByteTerm::TypeParentheticalAssertionEnd);
- ASSERT(term.atom.quantityCount == 1);
+ ASSERT(term.atom.quantityMaxCount == 1);
BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast<BackTrackInfoParentheticalAssertion*>(context->frame + term.frameLocation);
@@ -833,7 +956,7 @@ public:
bool backtrackParentheticalAssertionBegin(ByteTerm& term, DisjunctionContext* context)
{
ASSERT(term.type == ByteTerm::TypeParentheticalAssertionBegin);
- ASSERT(term.atom.quantityCount == 1);
+ ASSERT(term.atom.quantityMaxCount == 1);
// We've failed to match parens; if they are inverted, this is win!
if (term.invert()) {
@@ -847,7 +970,7 @@ public:
bool backtrackParentheticalAssertionEnd(ByteTerm& term, DisjunctionContext* context)
{
ASSERT(term.type == ByteTerm::TypeParentheticalAssertionEnd);
- ASSERT(term.atom.quantityCount == 1);
+ ASSERT(term.atom.quantityMaxCount == 1);
BackTrackInfoParentheticalAssertion* backTrack = reinterpret_cast<BackTrackInfoParentheticalAssertion*>(context->frame + term.frameLocation);
@@ -867,36 +990,45 @@ public:
backTrack->matchAmount = 0;
backTrack->lastContext = 0;
- switch (term.atom.quantityType) {
- case QuantifierFixedCount: {
+ ASSERT(term.atom.quantityType != QuantifierFixedCount || term.atom.quantityMinCount == term.atom.quantityMaxCount);
+
+ unsigned minimumMatchCount = term.atom.quantityMinCount;
+ JSRegExpResult fixedMatchResult;
+
+ // Handle fixed matches and the minimum part of a variable length match.
+ if (minimumMatchCount) {
// While we haven't yet reached our fixed limit,
- while (backTrack->matchAmount < term.atom.quantityCount) {
+ while (backTrack->matchAmount < minimumMatchCount) {
// Try to do a match, and it it succeeds, add it to the list.
ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term);
- JSRegExpResult result = matchDisjunction(disjunctionBody, context->getDisjunctionContext(term));
- if (result == JSRegExpMatch)
+ fixedMatchResult = matchDisjunction(disjunctionBody, context->getDisjunctionContext(term));
+ if (fixedMatchResult == JSRegExpMatch)
appendParenthesesDisjunctionContext(backTrack, context);
else {
// The match failed; try to find an alternate point to carry on from.
resetMatches(term, context);
freeParenthesesDisjunctionContext(context);
-
- if (result != JSRegExpNoMatch)
- return result;
+
+ if (fixedMatchResult != JSRegExpNoMatch)
+ return fixedMatchResult;
JSRegExpResult backtrackResult = parenthesesDoBacktrack(term, backTrack);
if (backtrackResult != JSRegExpMatch)
return backtrackResult;
}
}
- ASSERT(backTrack->matchAmount == term.atom.quantityCount);
ParenthesesDisjunctionContext* context = backTrack->lastContext;
recordParenthesesMatch(term, context);
+ }
+
+ switch (term.atom.quantityType) {
+ case QuantifierFixedCount: {
+ ASSERT(backTrack->matchAmount == term.atom.quantityMaxCount);
return JSRegExpMatch;
}
case QuantifierGreedy: {
- while (backTrack->matchAmount < term.atom.quantityCount) {
+ while (backTrack->matchAmount < term.atom.quantityMaxCount) {
ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term);
JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term));
if (result == JSRegExpMatch)
@@ -946,7 +1078,7 @@ public:
switch (term.atom.quantityType) {
case QuantifierFixedCount: {
- ASSERT(backTrack->matchAmount == term.atom.quantityCount);
+ ASSERT(backTrack->matchAmount == term.atom.quantityMaxCount);
ParenthesesDisjunctionContext* context = 0;
JSRegExpResult result = parenthesesDoBacktrack(term, backTrack);
@@ -955,7 +1087,7 @@ public:
return result;
// While we haven't yet reached our fixed limit,
- while (backTrack->matchAmount < term.atom.quantityCount) {
+ while (backTrack->matchAmount < term.atom.quantityMaxCount) {
// Try to do a match, and it it succeeds, add it to the list.
context = allocParenthesesDisjunctionContext(disjunctionBody, output, term);
result = matchDisjunction(disjunctionBody, context->getDisjunctionContext(term));
@@ -975,7 +1107,7 @@ public:
}
}
- ASSERT(backTrack->matchAmount == term.atom.quantityCount);
+ ASSERT(backTrack->matchAmount == term.atom.quantityMaxCount);
context = backTrack->lastContext;
recordParenthesesMatch(term, context);
return JSRegExpMatch;
@@ -988,7 +1120,7 @@ public:
ParenthesesDisjunctionContext* context = backTrack->lastContext;
JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term), true);
if (result == JSRegExpMatch) {
- while (backTrack->matchAmount < term.atom.quantityCount) {
+ while (backTrack->matchAmount < term.atom.quantityMaxCount) {
ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term);
JSRegExpResult parenthesesResult = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term));
if (parenthesesResult == JSRegExpMatch)
@@ -1008,7 +1140,7 @@ public:
popParenthesesDisjunctionContext(backTrack);
freeParenthesesDisjunctionContext(context);
- if (result != JSRegExpNoMatch)
+ if (result != JSRegExpNoMatch || backTrack->matchAmount < term.atom.quantityMinCount)
return result;
}
@@ -1021,7 +1153,7 @@ public:
case QuantifierNonGreedy: {
// If we've not reached the limit, try to add one more match.
- if (backTrack->matchAmount < term.atom.quantityCount) {
+ if (backTrack->matchAmount < term.atom.quantityMaxCount) {
ParenthesesDisjunctionContext* context = allocParenthesesDisjunctionContext(disjunctionBody, output, term);
JSRegExpResult result = matchNonZeroDisjunction(disjunctionBody, context->getDisjunctionContext(term));
if (result == JSRegExpMatch) {
@@ -1070,16 +1202,23 @@ public:
bool matchDotStarEnclosure(ByteTerm& term, DisjunctionContext* context)
{
UNUSED_PARAM(term);
+
+ if (pattern->dotAll()) {
+ context->matchBegin = startOffset;
+ context->matchEnd = input.end();
+ return true;
+ }
+
unsigned matchBegin = context->matchBegin;
- if (matchBegin) {
+ if (matchBegin > startOffset) {
for (matchBegin--; true; matchBegin--) {
if (testCharacterClass(pattern->newlineCharacterClass, input.reread(matchBegin))) {
++matchBegin;
break;
}
- if (!matchBegin)
+ if (matchBegin == startOffset)
break;
}
}
@@ -1091,7 +1230,7 @@ public:
if (((matchBegin && term.anchors.m_bol)
|| ((matchEnd != input.end()) && term.anchors.m_eol))
- && !pattern->m_multiline)
+ && !pattern->multiline())
return false;
context->matchBegin = matchBegin;
@@ -1156,21 +1295,37 @@ public:
case ByteTerm::TypePatternCharacterOnce:
case ByteTerm::TypePatternCharacterFixed: {
- for (unsigned matchAmount = 0; matchAmount < currentTerm().atom.quantityCount; ++matchAmount) {
- if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition - matchAmount))
+ if (unicode) {
+ if (!U_IS_BMP(currentTerm().atom.patternCharacter)) {
+ for (unsigned matchAmount = 0; matchAmount < currentTerm().atom.quantityMaxCount; ++matchAmount) {
+ if (!checkSurrogatePair(currentTerm().atom.patternCharacter, currentTerm().inputPosition - 2 * matchAmount)) {
+ BACKTRACK();
+ }
+ }
+ MATCH_NEXT();
+ }
+ }
+ unsigned position = input.getPos(); // May need to back out reading a surrogate pair.
+
+ for (unsigned matchAmount = 0; matchAmount < currentTerm().atom.quantityMaxCount; ++matchAmount) {
+ if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition - matchAmount)) {
+ input.setPos(position);
BACKTRACK();
+ }
}
MATCH_NEXT();
}
case ByteTerm::TypePatternCharacterGreedy: {
BackTrackInfoPatternCharacter* backTrack = reinterpret_cast<BackTrackInfoPatternCharacter*>(context->frame + currentTerm().frameLocation);
unsigned matchAmount = 0;
- while ((matchAmount < currentTerm().atom.quantityCount) && input.checkInput(1)) {
+ unsigned position = input.getPos(); // May need to back out reading a surrogate pair.
+ while ((matchAmount < currentTerm().atom.quantityMaxCount) && input.checkInput(1)) {
if (!checkCharacter(currentTerm().atom.patternCharacter, currentTerm().inputPosition + 1)) {
- input.uncheckInput(1);
+ input.setPos(position);
break;
}
++matchAmount;
+ position = input.getPos();
}
backTrack->matchAmount = matchAmount;
@@ -1178,13 +1333,29 @@ public:
}
case ByteTerm::TypePatternCharacterNonGreedy: {
BackTrackInfoPatternCharacter* backTrack = reinterpret_cast<BackTrackInfoPatternCharacter*>(context->frame + currentTerm().frameLocation);
+ backTrack->begin = input.getPos();
backTrack->matchAmount = 0;
MATCH_NEXT();
}
case ByteTerm::TypePatternCasedCharacterOnce:
case ByteTerm::TypePatternCasedCharacterFixed: {
- for (unsigned matchAmount = 0; matchAmount < currentTerm().atom.quantityCount; ++matchAmount) {
+ if (unicode) {
+ // Case insensitive matching of unicode characters is handled as TypeCharacterClass.
+ ASSERT(U_IS_BMP(currentTerm().atom.patternCharacter));
+
+ unsigned position = input.getPos(); // May need to back out reading a surrogate pair.
+
+ for (unsigned matchAmount = 0; matchAmount < currentTerm().atom.quantityMaxCount; ++matchAmount) {
+ if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition - matchAmount)) {
+ input.setPos(position);
+ BACKTRACK();
+ }
+ }
+ MATCH_NEXT();
+ }
+
+ for (unsigned matchAmount = 0; matchAmount < currentTerm().atom.quantityMaxCount; ++matchAmount) {
if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition - matchAmount))
BACKTRACK();
}
@@ -1192,8 +1363,12 @@ public:
}
case ByteTerm::TypePatternCasedCharacterGreedy: {
BackTrackInfoPatternCharacter* backTrack = reinterpret_cast<BackTrackInfoPatternCharacter*>(context->frame + currentTerm().frameLocation);
+
+ // Case insensitive matching of unicode characters is handled as TypeCharacterClass.
+ ASSERT(!unicode || U_IS_BMP(currentTerm().atom.patternCharacter));
+
unsigned matchAmount = 0;
- while ((matchAmount < currentTerm().atom.quantityCount) && input.checkInput(1)) {
+ while ((matchAmount < currentTerm().atom.quantityMaxCount) && input.checkInput(1)) {
if (!checkCasedCharacter(currentTerm().atom.casedCharacter.lo, currentTerm().atom.casedCharacter.hi, currentTerm().inputPosition + 1)) {
input.uncheckInput(1);
break;
@@ -1206,6 +1381,10 @@ public:
}
case ByteTerm::TypePatternCasedCharacterNonGreedy: {
BackTrackInfoPatternCharacter* backTrack = reinterpret_cast<BackTrackInfoPatternCharacter*>(context->frame + currentTerm().frameLocation);
+
+ // Case insensitive matching of unicode characters is handled as TypeCharacterClass.
+ ASSERT(!unicode || U_IS_BMP(currentTerm().atom.patternCharacter));
+
backTrack->matchAmount = 0;
MATCH_NEXT();
}
@@ -1287,7 +1466,7 @@ public:
if (offset > 0)
MATCH_NEXT();
- if (input.atEnd())
+ if (input.atEnd() || pattern->sticky())
return JSRegExpNoMatch;
input.next();
@@ -1417,6 +1596,9 @@ public:
if (!input.isAvailableInput(0))
return offsetNoMatch;
+ if (pattern->m_lock)
+ pattern->m_lock->lock();
+
for (unsigned i = 0; i < pattern->m_body->m_numSubpatterns + 1; ++i)
output[i << 1] = offsetNoMatch;
@@ -1436,23 +1618,31 @@ public:
pattern->m_allocator->stopAllocator();
ASSERT((result == JSRegExpMatch) == (output[0] != offsetNoMatch));
+
+ if (pattern->m_lock)
+ pattern->m_lock->unlock();
+
return output[0];
}
Interpreter(BytecodePattern* pattern, unsigned* output, const CharType* input, unsigned length, unsigned start)
: pattern(pattern)
+ , unicode(pattern->unicode())
, output(output)
- , input(input, start, length)
+ , input(input, start, length, pattern->unicode())
, allocatorPool(0)
+ , startOffset(start)
, remainingMatchCount(matchLimit)
{
}
private:
BytecodePattern* pattern;
+ bool unicode;
unsigned* output;
InputStream input;
BumpPointerPool* allocatorPool;
+ unsigned startOffset;
unsigned remainingMatchCount;
};
@@ -1474,13 +1664,18 @@ public:
m_currentAlternativeIndex = 0;
}
- PassOwnPtr<BytecodePattern> compile(BumpPointerAllocator* allocator)
+ std::unique_ptr<BytecodePattern> compile(BumpPointerAllocator* allocator, ConcurrentJSLock* lock)
{
regexBegin(m_pattern.m_numSubpatterns, m_pattern.m_body->m_callFrameSize, m_pattern.m_body->m_alternatives[0]->onceThrough());
emitDisjunction(m_pattern.m_body);
regexEnd();
- return adoptPtr(new BytecodePattern(m_bodyDisjunction.release(), m_allParenthesesInfo, m_pattern, allocator));
+#ifndef NDEBUG
+ if (Options::dumpCompiledRegExpPatterns())
+ dumpDisjunction(m_bodyDisjunction.get());
+#endif
+
+ return std::make_unique<BytecodePattern>(WTFMove(m_bodyDisjunction), m_allParenthesesInfo, m_pattern, allocator, lock);
}
void checkInput(unsigned count)
@@ -1508,45 +1703,44 @@ public:
m_bodyDisjunction->terms.append(ByteTerm::WordBoundary(invert, inputPosition));
}
- void atomPatternCharacter(UChar ch, unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
+ void atomPatternCharacter(UChar32 ch, unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityMaxCount, QuantifierType quantityType)
{
- if (m_pattern.m_ignoreCase) {
- UChar lo = Unicode::toLower(ch);
- UChar hi = Unicode::toUpper(ch);
+ if (m_pattern.ignoreCase()) {
+ UChar32 lo = u_tolower(ch);
+ UChar32 hi = u_toupper(ch);
if (lo != hi) {
- m_bodyDisjunction->terms.append(ByteTerm(lo, hi, inputPosition, frameLocation, quantityCount, quantityType));
+ m_bodyDisjunction->terms.append(ByteTerm(lo, hi, inputPosition, frameLocation, quantityMaxCount, quantityType));
return;
}
}
- m_bodyDisjunction->terms.append(ByteTerm(ch, inputPosition, frameLocation, quantityCount, quantityType));
+ m_bodyDisjunction->terms.append(ByteTerm(ch, inputPosition, frameLocation, quantityMaxCount, quantityType));
}
- void atomCharacterClass(CharacterClass* characterClass, bool invert, unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
+ void atomCharacterClass(CharacterClass* characterClass, bool invert, unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityMaxCount, QuantifierType quantityType)
{
m_bodyDisjunction->terms.append(ByteTerm(characterClass, invert, inputPosition));
- m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet();
+ m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityType = quantityType;
m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation;
}
- void atomBackReference(unsigned subpatternId, unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
+ void atomBackReference(unsigned subpatternId, unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityMaxCount, QuantifierType quantityType)
{
ASSERT(subpatternId);
m_bodyDisjunction->terms.append(ByteTerm::BackReference(subpatternId, inputPosition));
- m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityCount = quantityCount.unsafeGet();
+ m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].atom.quantityType = quantityType;
m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation;
}
void atomParenthesesOnceBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation)
{
- ASSERT(m_bodyDisjunction->terms.size() <= INT_MAX);
- int beginTerm = static_cast<int>(m_bodyDisjunction->terms.size());
+ unsigned beginTerm = m_bodyDisjunction->terms.size();
m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternOnceBegin, subpatternId, capture, false, inputPosition));
m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation;
@@ -1559,8 +1753,7 @@ public:
void atomParenthesesTerminalBegin(unsigned subpatternId, bool capture, unsigned inputPosition, unsigned frameLocation, unsigned alternativeFrameLocation)
{
- ASSERT(m_bodyDisjunction->terms.size() <= INT_MAX);
- int beginTerm = static_cast<int>(m_bodyDisjunction->terms.size());
+ int beginTerm = m_bodyDisjunction->terms.size();
m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternTerminalBegin, subpatternId, capture, false, inputPosition));
m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation;
@@ -1577,8 +1770,7 @@ public:
// then fix this up at the end! - simplifying this should make it much clearer.
// https://bugs.webkit.org/show_bug.cgi?id=50136
- ASSERT(m_bodyDisjunction->terms.size() <= INT_MAX);
- int beginTerm = static_cast<int>(m_bodyDisjunction->terms.size());
+ int beginTerm = m_bodyDisjunction->terms.size();
m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpatternOnceBegin, subpatternId, capture, false, inputPosition));
m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation;
@@ -1591,8 +1783,7 @@ public:
void atomParentheticalAssertionBegin(unsigned subpatternId, bool invert, unsigned frameLocation, unsigned alternativeFrameLocation)
{
- ASSERT(m_bodyDisjunction->terms.size() <= INT_MAX);
- int beginTerm = static_cast<int>(m_bodyDisjunction->terms.size());
+ int beginTerm = m_bodyDisjunction->terms.size();
m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParentheticalAssertionBegin, subpatternId, false, invert, 0));
m_bodyDisjunction->terms[m_bodyDisjunction->terms.size() - 1].frameLocation = frameLocation;
@@ -1603,12 +1794,11 @@ public:
m_currentAlternativeIndex = beginTerm + 1;
}
- void atomParentheticalAssertionEnd(unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
+ void atomParentheticalAssertionEnd(unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityMaxCount, QuantifierType quantityType)
{
unsigned beginTerm = popParenthesesStack();
closeAlternative(beginTerm + 1);
- ASSERT(m_bodyDisjunction->terms.size() <= INT_MAX);
- unsigned endTerm = static_cast<int>(m_bodyDisjunction->terms.size());
+ unsigned endTerm = m_bodyDisjunction->terms.size();
ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParentheticalAssertionBegin);
@@ -1620,9 +1810,9 @@ public:
m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm;
m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation;
- m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet();
+ m_bodyDisjunction->terms[beginTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType;
- m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet();
+ m_bodyDisjunction->terms[endTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType;
}
@@ -1634,8 +1824,7 @@ public:
unsigned popParenthesesStack()
{
ASSERT(m_parenthesesStack.size());
- ASSERT(m_parenthesesStack.size() <= INT_MAX);
- int stackEnd = static_cast<int>(m_parenthesesStack.size()) - 1;
+ int stackEnd = m_parenthesesStack.size() - 1;
unsigned beginTerm = m_parenthesesStack[stackEnd].beginTerm;
m_currentAlternativeIndex = m_parenthesesStack[stackEnd].savedAlternativeIndex;
m_parenthesesStack.shrink(stackEnd);
@@ -1646,22 +1835,11 @@ public:
return beginTerm;
}
-#ifndef NDEBUG
- void dumpDisjunction(ByteDisjunction* disjunction)
- {
- dataLogF("ByteDisjunction(%p):\n\t", disjunction);
- for (unsigned i = 0; i < disjunction->terms.size(); ++i)
- dataLogF("{ %d } ", disjunction->terms[i].type);
- dataLogF("\n");
- }
-#endif
-
void closeAlternative(int beginTerm)
{
int origBeginTerm = beginTerm;
ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeAlternativeBegin);
- ASSERT(m_bodyDisjunction->terms.size() <= INT_MAX);
- int endIndex = static_cast<int>(m_bodyDisjunction->terms.size());
+ int endIndex = m_bodyDisjunction->terms.size();
unsigned frameLocation = m_bodyDisjunction->terms[beginTerm].frameLocation;
@@ -1687,8 +1865,7 @@ public:
int beginTerm = 0;
int origBeginTerm = 0;
ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeBodyAlternativeBegin);
- ASSERT(m_bodyDisjunction->terms.size() <= INT_MAX);
- int endIndex = static_cast<int>(m_bodyDisjunction->terms.size());
+ int endIndex = m_bodyDisjunction->terms.size();
unsigned frameLocation = m_bodyDisjunction->terms[beginTerm].frameLocation;
@@ -1705,12 +1882,11 @@ public:
m_bodyDisjunction->terms[endIndex].frameLocation = frameLocation;
}
- void atomParenthesesSubpatternEnd(unsigned lastSubpatternId, int inputPosition, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType, unsigned callFrameSize = 0)
+ void atomParenthesesSubpatternEnd(unsigned lastSubpatternId, unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityMinCount, Checked<unsigned> quantityMaxCount, QuantifierType quantityType, unsigned callFrameSize = 0)
{
unsigned beginTerm = popParenthesesStack();
closeAlternative(beginTerm + 1);
- ASSERT(m_bodyDisjunction->terms.size() <= INT_MAX);
- unsigned endTerm = static_cast<int>(m_bodyDisjunction->terms.size());
+ unsigned endTerm = m_bodyDisjunction->terms.size();
ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParenthesesSubpatternOnceBegin);
@@ -1720,7 +1896,7 @@ public:
unsigned subpatternId = parenthesesBegin.atom.subpatternId;
unsigned numSubpatterns = lastSubpatternId - subpatternId + 1;
- OwnPtr<ByteDisjunction> parenthesesDisjunction = adoptPtr(new ByteDisjunction(numSubpatterns, callFrameSize));
+ auto parenthesesDisjunction = std::make_unique<ByteDisjunction>(numSubpatterns, callFrameSize);
unsigned firstTermInParentheses = beginTerm + 1;
parenthesesDisjunction->terms.reserveInitialCapacity(endTerm - firstTermInParentheses + 2);
@@ -1733,19 +1909,19 @@ public:
m_bodyDisjunction->terms.shrink(beginTerm);
m_bodyDisjunction->terms.append(ByteTerm(ByteTerm::TypeParenthesesSubpattern, subpatternId, parenthesesDisjunction.get(), capture, inputPosition));
- m_allParenthesesInfo.append(parenthesesDisjunction.release());
+ m_allParenthesesInfo.append(WTFMove(parenthesesDisjunction));
- m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet();
+ m_bodyDisjunction->terms[beginTerm].atom.quantityMinCount = quantityMinCount.unsafeGet();
+ m_bodyDisjunction->terms[beginTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType;
m_bodyDisjunction->terms[beginTerm].frameLocation = frameLocation;
}
- void atomParenthesesOnceEnd(int inputPosition, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
+ void atomParenthesesOnceEnd(unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityMinCount, Checked<unsigned> quantityMaxCount, QuantifierType quantityType)
{
unsigned beginTerm = popParenthesesStack();
closeAlternative(beginTerm + 1);
- ASSERT(m_bodyDisjunction->terms.size() <= INT_MAX);
- unsigned endTerm = static_cast<int>(m_bodyDisjunction->terms.size());
+ unsigned endTerm = m_bodyDisjunction->terms.size();
ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParenthesesSubpatternOnceBegin);
@@ -1757,18 +1933,19 @@ public:
m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm;
m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation;
- m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet();
+ m_bodyDisjunction->terms[beginTerm].atom.quantityMinCount = quantityMinCount.unsafeGet();
+ m_bodyDisjunction->terms[beginTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType;
- m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet();
+ m_bodyDisjunction->terms[endTerm].atom.quantityMinCount = quantityMinCount.unsafeGet();
+ m_bodyDisjunction->terms[endTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType;
}
- void atomParenthesesTerminalEnd(int inputPosition, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
+ void atomParenthesesTerminalEnd(unsigned inputPosition, unsigned frameLocation, Checked<unsigned> quantityMinCount, Checked<unsigned> quantityMaxCount, QuantifierType quantityType)
{
unsigned beginTerm = popParenthesesStack();
closeAlternative(beginTerm + 1);
- ASSERT(m_bodyDisjunction->terms.size() <= INT_MAX);
- unsigned endTerm = static_cast<int>(m_bodyDisjunction->terms.size());
+ unsigned endTerm = m_bodyDisjunction->terms.size();
ASSERT(m_bodyDisjunction->terms[beginTerm].type == ByteTerm::TypeParenthesesSubpatternTerminalBegin);
@@ -1780,15 +1957,17 @@ public:
m_bodyDisjunction->terms[endTerm].atom.parenthesesWidth = endTerm - beginTerm;
m_bodyDisjunction->terms[endTerm].frameLocation = frameLocation;
- m_bodyDisjunction->terms[beginTerm].atom.quantityCount = quantityCount.unsafeGet();
+ m_bodyDisjunction->terms[beginTerm].atom.quantityMinCount = quantityMinCount.unsafeGet();
+ m_bodyDisjunction->terms[beginTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
m_bodyDisjunction->terms[beginTerm].atom.quantityType = quantityType;
- m_bodyDisjunction->terms[endTerm].atom.quantityCount = quantityCount.unsafeGet();
+ m_bodyDisjunction->terms[endTerm].atom.quantityMinCount = quantityMinCount.unsafeGet();
+ m_bodyDisjunction->terms[endTerm].atom.quantityMaxCount = quantityMaxCount.unsafeGet();
m_bodyDisjunction->terms[endTerm].atom.quantityType = quantityType;
}
void regexBegin(unsigned numSubpatterns, unsigned callFrameSize, bool onceThrough)
{
- m_bodyDisjunction = adoptPtr(new ByteDisjunction(numSubpatterns, callFrameSize));
+ m_bodyDisjunction = std::make_unique<ByteDisjunction>(numSubpatterns, callFrameSize);
m_bodyDisjunction->terms.append(ByteTerm::BodyAlternativeBegin(onceThrough));
m_bodyDisjunction->terms[0].frameLocation = 0;
m_currentAlternativeIndex = 0;
@@ -1801,8 +1980,7 @@ public:
void alternativeBodyDisjunction(bool onceThrough)
{
- ASSERT(m_bodyDisjunction->terms.size() <= INT_MAX);
- int newAlternativeIndex = static_cast<int>(m_bodyDisjunction->terms.size());
+ int newAlternativeIndex = m_bodyDisjunction->terms.size();
m_bodyDisjunction->terms[m_currentAlternativeIndex].alternative.next = newAlternativeIndex - m_currentAlternativeIndex;
m_bodyDisjunction->terms.append(ByteTerm::BodyAlternativeDisjunction(onceThrough));
@@ -1811,8 +1989,7 @@ public:
void alternativeDisjunction()
{
- ASSERT(m_bodyDisjunction->terms.size() <= INT_MAX);
- int newAlternativeIndex = static_cast<int>(m_bodyDisjunction->terms.size());
+ int newAlternativeIndex = m_bodyDisjunction->terms.size();
m_bodyDisjunction->terms[m_currentAlternativeIndex].alternative.next = newAlternativeIndex - m_currentAlternativeIndex;
m_bodyDisjunction->terms.append(ByteTerm::AlternativeDisjunction());
@@ -1842,9 +2019,7 @@ public:
currentCountAlreadyChecked += countToCheck;
}
- for (unsigned i = 0; i < alternative->m_terms.size(); ++i) {
- PatternTerm& term = alternative->m_terms[i];
-
+ for (auto& term : alternative->m_terms) {
switch (term.type) {
case PatternTerm::TypeAssertionBOL:
assertionBOL(currentCountAlreadyChecked - term.inputPosition);
@@ -1859,15 +2034,15 @@ public:
break;
case PatternTerm::TypePatternCharacter:
- atomPatternCharacter(term.patternCharacter, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType);
+ atomPatternCharacter(term.patternCharacter, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityMaxCount, term.quantityType);
break;
case PatternTerm::TypeCharacterClass:
- atomCharacterClass(term.characterClass, term.invert(), currentCountAlreadyChecked- term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType);
+ atomCharacterClass(term.characterClass, term.invert(), currentCountAlreadyChecked- term.inputPosition, term.frameLocation, term.quantityMaxCount, term.quantityType);
break;
case PatternTerm::TypeBackReference:
- atomBackReference(term.backReferenceSubpatternId, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityCount, term.quantityType);
+ atomBackReference(term.backReferenceSubpatternId, currentCountAlreadyChecked - term.inputPosition, term.frameLocation, term.quantityMaxCount, term.quantityType);
break;
case PatternTerm::TypeForwardReference:
@@ -1875,27 +2050,30 @@ public:
case PatternTerm::TypeParenthesesSubpattern: {
unsigned disjunctionAlreadyCheckedCount = 0;
- if (term.quantityCount == 1 && !term.parentheses.isCopy) {
+ if (term.quantityMaxCount == 1 && !term.parentheses.isCopy) {
unsigned alternativeFrameLocation = term.frameLocation;
// For QuantifierFixedCount we pre-check the minimum size; for greedy/non-greedy we reserve a slot in the frame.
if (term.quantityType == QuantifierFixedCount)
disjunctionAlreadyCheckedCount = term.parentheses.disjunction->m_minimumSize;
else
alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce;
- unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked;
- atomParenthesesOnceBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, alternativeFrameLocation);
+ ASSERT(currentCountAlreadyChecked >= term.inputPosition);
+ unsigned delegateEndInputOffset = currentCountAlreadyChecked - term.inputPosition;
+ atomParenthesesOnceBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount + delegateEndInputOffset, term.frameLocation, alternativeFrameLocation);
emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, disjunctionAlreadyCheckedCount);
- atomParenthesesOnceEnd(delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType);
+ atomParenthesesOnceEnd(delegateEndInputOffset, term.frameLocation, term.quantityMinCount, term.quantityMaxCount, term.quantityType);
} else if (term.parentheses.isTerminal) {
- unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked;
- atomParenthesesTerminalBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, term.frameLocation + YarrStackSpaceForBackTrackInfoParenthesesOnce);
+ ASSERT(currentCountAlreadyChecked >= term.inputPosition);
+ unsigned delegateEndInputOffset = currentCountAlreadyChecked - term.inputPosition;
+ atomParenthesesTerminalBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount + delegateEndInputOffset, term.frameLocation, term.frameLocation + YarrStackSpaceForBackTrackInfoParenthesesTerminal);
emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, disjunctionAlreadyCheckedCount);
- atomParenthesesTerminalEnd(delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType);
+ atomParenthesesTerminalEnd(delegateEndInputOffset, term.frameLocation, term.quantityMinCount, term.quantityMaxCount, term.quantityType);
} else {
- unsigned delegateEndInputOffset = term.inputPosition - currentCountAlreadyChecked;
- atomParenthesesSubpatternBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount - delegateEndInputOffset, term.frameLocation, 0);
+ ASSERT(currentCountAlreadyChecked >= term.inputPosition);
+ unsigned delegateEndInputOffset = currentCountAlreadyChecked - term.inputPosition;
+ atomParenthesesSubpatternBegin(term.parentheses.subpatternId, term.capture(), disjunctionAlreadyCheckedCount + delegateEndInputOffset, term.frameLocation, 0);
emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, 0);
- atomParenthesesSubpatternEnd(term.parentheses.lastSubpatternId, delegateEndInputOffset, term.frameLocation, term.quantityCount, term.quantityType, term.parentheses.disjunction->m_callFrameSize);
+ atomParenthesesSubpatternEnd(term.parentheses.lastSubpatternId, delegateEndInputOffset, term.frameLocation, term.quantityMinCount, term.quantityMaxCount, term.quantityType, term.parentheses.disjunction->m_callFrameSize);
}
break;
}
@@ -1903,8 +2081,8 @@ public:
case PatternTerm::TypeParentheticalAssertion: {
unsigned alternativeFrameLocation = term.frameLocation + YarrStackSpaceForBackTrackInfoParentheticalAssertion;
- ASSERT(currentCountAlreadyChecked >= static_cast<unsigned>(term.inputPosition));
- unsigned positiveInputOffset = currentCountAlreadyChecked - static_cast<unsigned>(term.inputPosition);
+ ASSERT(currentCountAlreadyChecked >= term.inputPosition);
+ unsigned positiveInputOffset = currentCountAlreadyChecked - term.inputPosition;
unsigned uncheckAmount = 0;
if (positiveInputOffset > term.parentheses.disjunction->m_minimumSize) {
uncheckAmount = positiveInputOffset - term.parentheses.disjunction->m_minimumSize;
@@ -1914,7 +2092,7 @@ public:
atomParentheticalAssertionBegin(term.parentheses.subpatternId, term.invert(), term.frameLocation, alternativeFrameLocation);
emitDisjunction(term.parentheses.disjunction, currentCountAlreadyChecked, positiveInputOffset - uncheckAmount);
- atomParentheticalAssertionEnd(0, term.frameLocation, term.quantityCount, term.quantityType);
+ atomParentheticalAssertionEnd(0, term.frameLocation, term.quantityMaxCount, term.quantityType);
if (uncheckAmount) {
checkInput(uncheckAmount);
currentCountAlreadyChecked += uncheckAmount;
@@ -1929,22 +2107,283 @@ public:
}
}
}
+#ifndef NDEBUG
+ void dumpDisjunction(ByteDisjunction* disjunction, unsigned nesting = 0)
+ {
+ PrintStream& out = WTF::dataFile();
+
+ unsigned termIndexNest = 0;
+
+ if (!nesting) {
+ out.printf("ByteDisjunction(%p):\n", disjunction);
+ nesting = 1;
+ } else {
+ termIndexNest = nesting - 1;
+ nesting = 2;
+ }
+
+ auto outputTermIndexAndNest = [&](size_t index, unsigned termNesting) {
+ for (unsigned nestingDepth = 0; nestingDepth < termIndexNest; nestingDepth++)
+ out.print(" ");
+ out.printf("%4zu", index);
+ for (unsigned nestingDepth = 0; nestingDepth < termNesting; nestingDepth++)
+ out.print(" ");
+ };
+
+ auto dumpQuantity = [&](ByteTerm& term) {
+ if (term.atom.quantityType == QuantifierFixedCount && term.atom.quantityMinCount == 1 && term.atom.quantityMaxCount == 1)
+ return;
+
+ out.print(" {", term.atom.quantityMinCount);
+ if (term.atom.quantityMinCount != term.atom.quantityMaxCount) {
+ if (term.atom.quantityMaxCount == UINT_MAX)
+ out.print(",inf");
+ else
+ out.print(",", term.atom.quantityMaxCount);
+ }
+ out.print("}");
+ if (term.atom.quantityType == QuantifierGreedy)
+ out.print(" greedy");
+ else if (term.atom.quantityType == QuantifierNonGreedy)
+ out.print(" non-greedy");
+ };
+
+ auto dumpCaptured = [&](ByteTerm& term) {
+ if (term.capture())
+ out.print(" captured (#", term.atom.subpatternId, ")");
+ };
+
+ auto dumpInverted = [&](ByteTerm& term) {
+ if (term.invert())
+ out.print(" inverted");
+ };
+
+ auto dumpInputPosition = [&](ByteTerm& term) {
+ out.printf(" inputPosition %u", term.inputPosition);
+ };
+
+ auto dumpFrameLocation = [&](ByteTerm& term) {
+ out.printf(" frameLocation %u", term.frameLocation);
+ };
+
+ auto dumpCharacter = [&](ByteTerm& term) {
+ out.print(" ");
+ dumpUChar32(out, term.atom.patternCharacter);
+ };
+
+ auto dumpCharClass = [&](ByteTerm& term) {
+ out.print(" ");
+ dumpCharacterClass(out, &m_pattern, term.atom.characterClass);
+ };
+
+ for (size_t idx = 0; idx < disjunction->terms.size(); ++idx) {
+ ByteTerm term = disjunction->terms[idx];
+
+ bool outputNewline = true;
+
+ switch (term.type) {
+ case ByteTerm::TypeBodyAlternativeBegin:
+ outputTermIndexAndNest(idx, nesting++);
+ out.print("BodyAlternativeBegin");
+ if (term.alternative.onceThrough)
+ out.print(" onceThrough");
+ dumpFrameLocation(term);
+ break;
+ case ByteTerm::TypeBodyAlternativeDisjunction:
+ outputTermIndexAndNest(idx, nesting - 1);
+ out.print("BodyAlternativeDisjunction");
+ dumpFrameLocation(term);
+ break;
+ case ByteTerm::TypeBodyAlternativeEnd:
+ outputTermIndexAndNest(idx, --nesting);
+ out.print("BodyAlternativeEnd");
+ dumpFrameLocation(term);
+ break;
+ case ByteTerm::TypeAlternativeBegin:
+ outputTermIndexAndNest(idx, nesting++);
+ out.print("AlternativeBegin");
+ dumpFrameLocation(term);
+ break;
+ case ByteTerm::TypeAlternativeDisjunction:
+ outputTermIndexAndNest(idx, nesting - 1);
+ out.print("AlternativeDisjunction");
+ dumpFrameLocation(term);
+ break;
+ case ByteTerm::TypeAlternativeEnd:
+ outputTermIndexAndNest(idx, --nesting);
+ out.print("AlternativeEnd");
+ dumpFrameLocation(term);
+ break;
+ case ByteTerm::TypeSubpatternBegin:
+ outputTermIndexAndNest(idx, nesting++);
+ out.print("SubpatternBegin");
+ break;
+ case ByteTerm::TypeSubpatternEnd:
+ outputTermIndexAndNest(idx, --nesting);
+ out.print("SubpatternEnd");
+ break;
+ case ByteTerm::TypeAssertionBOL:
+ outputTermIndexAndNest(idx, nesting);
+ out.print("AssertionBOL");
+ break;
+ case ByteTerm::TypeAssertionEOL:
+ outputTermIndexAndNest(idx, nesting);
+ out.print("AssertionEOL");
+ break;
+ case ByteTerm::TypeAssertionWordBoundary:
+ outputTermIndexAndNest(idx, nesting);
+ out.print("AssertionWordBoundary");
+ break;
+ case ByteTerm::TypePatternCharacterOnce:
+ outputTermIndexAndNest(idx, nesting);
+ out.print("PatternCharacterOnce");
+ dumpInverted(term);
+ dumpInputPosition(term);
+ dumpFrameLocation(term);
+ dumpCharacter(term);
+ dumpQuantity(term);
+ break;
+ case ByteTerm::TypePatternCharacterFixed:
+ outputTermIndexAndNest(idx, nesting);
+ out.print("PatternCharacterFixed");
+ dumpInverted(term);
+ dumpInputPosition(term);
+ dumpFrameLocation(term);
+ dumpCharacter(term);
+ out.print(" {", term.atom.quantityMinCount, "}");
+ break;
+ case ByteTerm::TypePatternCharacterGreedy:
+ outputTermIndexAndNest(idx, nesting);
+ out.print("PatternCharacterGreedy");
+ dumpInverted(term);
+ dumpInputPosition(term);
+ dumpFrameLocation(term);
+ dumpCharacter(term);
+ dumpQuantity(term);
+ break;
+ case ByteTerm::TypePatternCharacterNonGreedy:
+ outputTermIndexAndNest(idx, nesting);
+ out.print("PatternCharacterNonGreedy");
+ dumpInverted(term);
+ dumpInputPosition(term);
+ dumpFrameLocation(term);
+ dumpCharacter(term);
+ dumpQuantity(term);
+ break;
+ case ByteTerm::TypePatternCasedCharacterOnce:
+ outputTermIndexAndNest(idx, nesting);
+ out.print("PatternCasedCharacterOnce");
+ break;
+ case ByteTerm::TypePatternCasedCharacterFixed:
+ outputTermIndexAndNest(idx, nesting);
+ out.print("PatternCasedCharacterFixed");
+ break;
+ case ByteTerm::TypePatternCasedCharacterGreedy:
+ outputTermIndexAndNest(idx, nesting);
+ out.print("PatternCasedCharacterGreedy");
+ break;
+ case ByteTerm::TypePatternCasedCharacterNonGreedy:
+ outputTermIndexAndNest(idx, nesting);
+ out.print("PatternCasedCharacterNonGreedy");
+ break;
+ case ByteTerm::TypeCharacterClass:
+ outputTermIndexAndNest(idx, nesting);
+ out.print("CharacterClass");
+ dumpInverted(term);
+ dumpInputPosition(term);
+ dumpFrameLocation(term);
+ dumpCharClass(term);
+ dumpQuantity(term);
+ break;
+ case ByteTerm::TypeBackReference:
+ outputTermIndexAndNest(idx, nesting);
+ out.print("BackReference #", term.atom.subpatternId);
+ dumpQuantity(term);
+ break;
+ case ByteTerm::TypeParenthesesSubpattern:
+ outputTermIndexAndNest(idx, nesting);
+ out.print("ParenthesesSubpattern");
+ dumpCaptured(term);
+ dumpInverted(term);
+ dumpInputPosition(term);
+ dumpFrameLocation(term);
+ dumpQuantity(term);
+ out.print("\n");
+ outputNewline = false;
+ dumpDisjunction(term.atom.parenthesesDisjunction, nesting);
+ break;
+ case ByteTerm::TypeParenthesesSubpatternOnceBegin:
+ outputTermIndexAndNest(idx, nesting++);
+ out.print("ParenthesesSubpatternOnceBegin");
+ dumpCaptured(term);
+ dumpInverted(term);
+ dumpInputPosition(term);
+ dumpFrameLocation(term);
+ break;
+ case ByteTerm::TypeParenthesesSubpatternOnceEnd:
+ outputTermIndexAndNest(idx, --nesting);
+ out.print("ParenthesesSubpatternOnceEnd");
+ dumpFrameLocation(term);
+ break;
+ case ByteTerm::TypeParenthesesSubpatternTerminalBegin:
+ outputTermIndexAndNest(idx, nesting++);
+ out.print("ParenthesesSubpatternTerminalBegin");
+ dumpInverted(term);
+ dumpInputPosition(term);
+ dumpFrameLocation(term);
+ break;
+ case ByteTerm::TypeParenthesesSubpatternTerminalEnd:
+ outputTermIndexAndNest(idx, --nesting);
+ out.print("ParenthesesSubpatternTerminalEnd");
+ dumpFrameLocation(term);
+ break;
+ case ByteTerm::TypeParentheticalAssertionBegin:
+ outputTermIndexAndNest(idx, nesting++);
+ out.print("ParentheticalAssertionBegin");
+ dumpInverted(term);
+ dumpInputPosition(term);
+ dumpFrameLocation(term);
+ break;
+ case ByteTerm::TypeParentheticalAssertionEnd:
+ outputTermIndexAndNest(idx, --nesting);
+ out.print("ParentheticalAssertionEnd");
+ dumpFrameLocation(term);
+ break;
+ case ByteTerm::TypeCheckInput:
+ outputTermIndexAndNest(idx, nesting);
+ out.print("CheckInput ", term.checkInputCount);
+ break;
+ case ByteTerm::TypeUncheckInput:
+ outputTermIndexAndNest(idx, nesting);
+ out.print("UncheckInput ", term.checkInputCount);
+ break;
+ case ByteTerm::TypeDotStarEnclosure:
+ outputTermIndexAndNest(idx, nesting);
+ out.print("DotStarEnclosure");
+ break;
+ }
+ if (outputNewline)
+ out.print("\n");
+ }
+ }
+#endif
private:
YarrPattern& m_pattern;
- OwnPtr<ByteDisjunction> m_bodyDisjunction;
+ std::unique_ptr<ByteDisjunction> m_bodyDisjunction;
unsigned m_currentAlternativeIndex;
Vector<ParenthesesStackEntry> m_parenthesesStack;
- Vector<OwnPtr<ByteDisjunction> > m_allParenthesesInfo;
+ Vector<std::unique_ptr<ByteDisjunction>> m_allParenthesesInfo;
};
-PassOwnPtr<BytecodePattern> byteCompile(YarrPattern& pattern, BumpPointerAllocator* allocator)
+std::unique_ptr<BytecodePattern> byteCompile(YarrPattern& pattern, BumpPointerAllocator* allocator, ConcurrentJSLock* lock)
{
- return ByteCompiler(pattern).compile(allocator);
+ return ByteCompiler(pattern).compile(allocator, lock);
}
unsigned interpret(BytecodePattern* bytecode, const String& input, unsigned start, unsigned* output)
{
+ SuperSamplerScope superSamplerScope(false);
if (input.is8Bit())
return Interpreter<LChar>(bytecode, output, input.characters8(), input.length(), start).interpret();
return Interpreter<UChar>(bytecode, output, input.characters16(), input.length(), start).interpret();
@@ -1952,22 +2391,24 @@ unsigned interpret(BytecodePattern* bytecode, const String& input, unsigned star
unsigned interpret(BytecodePattern* bytecode, const LChar* input, unsigned length, unsigned start, unsigned* output)
{
+ SuperSamplerScope superSamplerScope(false);
return Interpreter<LChar>(bytecode, output, input, length, start).interpret();
}
unsigned interpret(BytecodePattern* bytecode, const UChar* input, unsigned length, unsigned start, unsigned* output)
{
+ SuperSamplerScope superSamplerScope(false);
return Interpreter<UChar>(bytecode, output, input, length, start).interpret();
}
// These should be the same for both UChar & LChar.
-COMPILE_ASSERT(sizeof(Interpreter<UChar>::BackTrackInfoPatternCharacter) == (YarrStackSpaceForBackTrackInfoPatternCharacter * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoPatternCharacter);
-COMPILE_ASSERT(sizeof(Interpreter<UChar>::BackTrackInfoCharacterClass) == (YarrStackSpaceForBackTrackInfoCharacterClass * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoCharacterClass);
-COMPILE_ASSERT(sizeof(Interpreter<UChar>::BackTrackInfoBackReference) == (YarrStackSpaceForBackTrackInfoBackReference * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoBackReference);
-COMPILE_ASSERT(sizeof(Interpreter<UChar>::BackTrackInfoAlternative) == (YarrStackSpaceForBackTrackInfoAlternative * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoAlternative);
-COMPILE_ASSERT(sizeof(Interpreter<UChar>::BackTrackInfoParentheticalAssertion) == (YarrStackSpaceForBackTrackInfoParentheticalAssertion * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParentheticalAssertion);
-COMPILE_ASSERT(sizeof(Interpreter<UChar>::BackTrackInfoParenthesesOnce) == (YarrStackSpaceForBackTrackInfoParenthesesOnce * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParenthesesOnce);
-COMPILE_ASSERT(sizeof(Interpreter<UChar>::BackTrackInfoParentheses) == (YarrStackSpaceForBackTrackInfoParentheses * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParentheses);
+COMPILE_ASSERT(sizeof(BackTrackInfoPatternCharacter) == (YarrStackSpaceForBackTrackInfoPatternCharacter * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoPatternCharacter);
+COMPILE_ASSERT(sizeof(BackTrackInfoCharacterClass) == (YarrStackSpaceForBackTrackInfoCharacterClass * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoCharacterClass);
+COMPILE_ASSERT(sizeof(BackTrackInfoBackReference) == (YarrStackSpaceForBackTrackInfoBackReference * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoBackReference);
+COMPILE_ASSERT(sizeof(BackTrackInfoAlternative) == (YarrStackSpaceForBackTrackInfoAlternative * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoAlternative);
+COMPILE_ASSERT(sizeof(BackTrackInfoParentheticalAssertion) == (YarrStackSpaceForBackTrackInfoParentheticalAssertion * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParentheticalAssertion);
+COMPILE_ASSERT(sizeof(BackTrackInfoParenthesesOnce) == (YarrStackSpaceForBackTrackInfoParenthesesOnce * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParenthesesOnce);
+COMPILE_ASSERT(sizeof(Interpreter<UChar>::BackTrackInfoParentheses) <= (YarrStackSpaceForBackTrackInfoParentheses * sizeof(uintptr_t)), CheckYarrStackSpaceForBackTrackInfoParentheses);
} }
diff --git a/src/3rdparty/masm/yarr/YarrInterpreter.h b/src/3rdparty/masm/yarr/YarrInterpreter.h
index 3b44acbd2b..a319cb3461 100644
--- a/src/3rdparty/masm/yarr/YarrInterpreter.h
+++ b/src/3rdparty/masm/yarr/YarrInterpreter.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009, 2010 Apple Inc. All rights reserved.
+ * Copyright (C) 2009, 2010-2012, 2014, 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,12 +23,10 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef YarrInterpreter_h
-#define YarrInterpreter_h
+#pragma once
+#include "ConcurrentJSLock.h"
#include "YarrPattern.h"
-#include <wtf/PassOwnPtr.h>
-#include <wtf/unicode/Unicode.h>
namespace WTF {
class BumpPointerAllocator;
@@ -76,10 +74,10 @@ struct ByteTerm {
union {
struct {
union {
- UChar patternCharacter;
+ UChar32 patternCharacter;
struct {
- UChar lo;
- UChar hi;
+ UChar32 lo;
+ UChar32 hi;
} casedCharacter;
CharacterClass* characterClass;
unsigned subpatternId;
@@ -89,7 +87,8 @@ struct ByteTerm {
unsigned parenthesesWidth;
};
QuantifierType quantityType;
- unsigned quantityCount;
+ unsigned quantityMinCount;
+ unsigned quantityMaxCount;
} atom;
struct {
int next;
@@ -107,11 +106,17 @@ struct ByteTerm {
bool m_invert : 1;
unsigned inputPosition;
- ByteTerm(UChar ch, int inputPos, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
+ ByteTerm(UChar32 ch, unsigned inputPos, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
: frameLocation(frameLocation)
, m_capture(false)
, m_invert(false)
{
+ atom.patternCharacter = ch;
+ atom.quantityType = quantityType;
+ atom.quantityMinCount = quantityCount.unsafeGet();
+ atom.quantityMaxCount = quantityCount.unsafeGet();
+ inputPosition = inputPos;
+
switch (quantityType) {
case QuantifierFixedCount:
type = (quantityCount == 1) ? ByteTerm::TypePatternCharacterOnce : ByteTerm::TypePatternCharacterFixed;
@@ -123,14 +128,9 @@ struct ByteTerm {
type = ByteTerm::TypePatternCharacterNonGreedy;
break;
}
-
- atom.patternCharacter = ch;
- atom.quantityType = quantityType;
- atom.quantityCount = quantityCount.unsafeGet();
- inputPosition = inputPos;
}
- ByteTerm(UChar lo, UChar hi, int inputPos, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
+ ByteTerm(UChar32 lo, UChar32 hi, unsigned inputPos, unsigned frameLocation, Checked<unsigned> quantityCount, QuantifierType quantityType)
: frameLocation(frameLocation)
, m_capture(false)
, m_invert(false)
@@ -150,22 +150,24 @@ struct ByteTerm {
atom.casedCharacter.lo = lo;
atom.casedCharacter.hi = hi;
atom.quantityType = quantityType;
- atom.quantityCount = quantityCount.unsafeGet();
+ atom.quantityMinCount = quantityCount.unsafeGet();
+ atom.quantityMaxCount = quantityCount.unsafeGet();
inputPosition = inputPos;
}
- ByteTerm(CharacterClass* characterClass, bool invert, int inputPos)
+ ByteTerm(CharacterClass* characterClass, bool invert, unsigned inputPos)
: type(ByteTerm::TypeCharacterClass)
, m_capture(false)
, m_invert(invert)
{
atom.characterClass = characterClass;
atom.quantityType = QuantifierFixedCount;
- atom.quantityCount = 1;
+ atom.quantityMinCount = 1;
+ atom.quantityMaxCount = 1;
inputPosition = inputPos;
}
- ByteTerm(Type type, unsigned subpatternId, ByteDisjunction* parenthesesInfo, bool capture, int inputPos)
+ ByteTerm(Type type, unsigned subpatternId, ByteDisjunction* parenthesesInfo, bool capture, unsigned inputPos)
: type(type)
, m_capture(capture)
, m_invert(false)
@@ -173,7 +175,8 @@ struct ByteTerm {
atom.subpatternId = subpatternId;
atom.parenthesesDisjunction = parenthesesInfo;
atom.quantityType = QuantifierFixedCount;
- atom.quantityCount = 1;
+ atom.quantityMinCount = 1;
+ atom.quantityMaxCount = 1;
inputPosition = inputPos;
}
@@ -183,21 +186,23 @@ struct ByteTerm {
, m_invert(invert)
{
atom.quantityType = QuantifierFixedCount;
- atom.quantityCount = 1;
+ atom.quantityMinCount = 1;
+ atom.quantityMaxCount = 1;
}
- ByteTerm(Type type, unsigned subpatternId, bool capture, bool invert, int inputPos)
+ ByteTerm(Type type, unsigned subpatternId, bool capture, bool invert, unsigned inputPos)
: type(type)
, m_capture(capture)
, m_invert(invert)
{
atom.subpatternId = subpatternId;
atom.quantityType = QuantifierFixedCount;
- atom.quantityCount = 1;
+ atom.quantityMinCount = 1;
+ atom.quantityMaxCount = 1;
inputPosition = inputPos;
}
- static ByteTerm BOL(int inputPos)
+ static ByteTerm BOL(unsigned inputPos)
{
ByteTerm term(TypeAssertionBOL);
term.inputPosition = inputPos;
@@ -218,21 +223,21 @@ struct ByteTerm {
return term;
}
- static ByteTerm EOL(int inputPos)
+ static ByteTerm EOL(unsigned inputPos)
{
ByteTerm term(TypeAssertionEOL);
term.inputPosition = inputPos;
return term;
}
- static ByteTerm WordBoundary(bool invert, int inputPos)
+ static ByteTerm WordBoundary(bool invert, unsigned inputPos)
{
ByteTerm term(TypeAssertionWordBoundary, invert);
term.inputPosition = inputPos;
return term;
}
- static ByteTerm BackReference(unsigned subpatternId, int inputPos)
+ static ByteTerm BackReference(unsigned subpatternId, unsigned inputPos)
{
return ByteTerm(TypeBackReference, subpatternId, false, false, inputPos);
}
@@ -329,6 +334,8 @@ public:
{
}
+ size_t estimatedSizeInBytes() const { return terms.capacity() * sizeof(ByteTerm); }
+
Vector<ByteTerm> terms;
unsigned m_numSubpatterns;
unsigned m_frameSize;
@@ -337,16 +344,19 @@ public:
struct BytecodePattern {
WTF_MAKE_FAST_ALLOCATED;
public:
- BytecodePattern(PassOwnPtr<ByteDisjunction> body, Vector<OwnPtr<ByteDisjunction> >& parenthesesInfoToAdopt, YarrPattern& pattern, BumpPointerAllocator* allocator)
- : m_body(body)
- , m_ignoreCase(pattern.m_ignoreCase)
- , m_multiline(pattern.m_multiline)
+ BytecodePattern(std::unique_ptr<ByteDisjunction> body, Vector<std::unique_ptr<ByteDisjunction>>& parenthesesInfoToAdopt, YarrPattern& pattern, BumpPointerAllocator* allocator, ConcurrentJSLock* lock)
+ : m_body(WTFMove(body))
+ , m_flags(pattern.m_flags)
, m_allocator(allocator)
+ , m_lock(lock)
{
m_body->terms.shrinkToFit();
newlineCharacterClass = pattern.newlineCharacterClass();
- wordcharCharacterClass = pattern.wordcharCharacterClass();
+ if (unicode() && ignoreCase())
+ wordcharCharacterClass = pattern.wordUnicodeIgnoreCaseCharCharacterClass();
+ else
+ wordcharCharacterClass = pattern.wordcharCharacterClass();
m_allParenthesesInfo.swap(parenthesesInfoToAdopt);
m_allParenthesesInfo.shrinkToFit();
@@ -355,26 +365,32 @@ public:
m_userCharacterClasses.shrinkToFit();
}
- OwnPtr<ByteDisjunction> m_body;
- bool m_ignoreCase;
- bool m_multiline;
+ size_t estimatedSizeInBytes() const { return m_body->estimatedSizeInBytes(); }
+
+ bool ignoreCase() const { return m_flags & FlagIgnoreCase; }
+ bool multiline() const { return m_flags & FlagMultiline; }
+ bool sticky() const { return m_flags & FlagSticky; }
+ bool unicode() const { return m_flags & FlagUnicode; }
+ bool dotAll() const { return m_flags & FlagDotAll; }
+
+ std::unique_ptr<ByteDisjunction> m_body;
+ RegExpFlags m_flags;
// Each BytecodePattern is associated with a RegExp, each RegExp is associated
- // with a JSGlobalData. Cache a pointer to out JSGlobalData's m_regExpAllocator.
+ // with a VM. Cache a pointer to out VM's m_regExpAllocator.
BumpPointerAllocator* m_allocator;
+ ConcurrentJSLock* m_lock;
CharacterClass* newlineCharacterClass;
CharacterClass* wordcharCharacterClass;
private:
- Vector<OwnPtr<ByteDisjunction> > m_allParenthesesInfo;
- Vector<OwnPtr<CharacterClass> > m_userCharacterClasses;
+ Vector<std::unique_ptr<ByteDisjunction>> m_allParenthesesInfo;
+ Vector<std::unique_ptr<CharacterClass>> m_userCharacterClasses;
};
-JS_EXPORT_PRIVATE PassOwnPtr<BytecodePattern> byteCompile(YarrPattern&, BumpPointerAllocator*);
+JS_EXPORT_PRIVATE std::unique_ptr<BytecodePattern> byteCompile(YarrPattern&, BumpPointerAllocator*, ConcurrentJSLock* = nullptr);
JS_EXPORT_PRIVATE unsigned interpret(BytecodePattern*, const String& input, unsigned start, unsigned* output);
unsigned interpret(BytecodePattern*, const LChar* input, unsigned length, unsigned start, unsigned* output);
unsigned interpret(BytecodePattern*, const UChar* input, unsigned length, unsigned start, unsigned* output);
} } // namespace JSC::Yarr
-
-#endif // YarrInterpreter_h
diff --git a/src/3rdparty/masm/yarr/YarrJIT.cpp b/src/3rdparty/masm/yarr/YarrJIT.cpp
index 71123b7be7..ce7c7163ed 100644
--- a/src/3rdparty/masm/yarr/YarrJIT.cpp
+++ b/src/3rdparty/masm/yarr/YarrJIT.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2009-2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -25,22 +25,23 @@
#include "config.h"
#include "YarrJIT.h"
+
#include <wtf/ASCIICType.h>
+#include "LinkBuffer.h"
#include "Options.h"
+#include "VM.h"
#include "Yarr.h"
-#include "YarrCanonicalizeUCS2.h"
+#include "YarrCanonicalize.h"
#if ENABLE(YARR_JIT)
-#include "LinkBuffer.h"
-
using namespace WTF;
namespace JSC { namespace Yarr {
template<YarrJITCompileMode compileMode>
class YarrGenerator : private DefaultMacroAssembler {
- friend void jitCompile(JSGlobalData*, YarrCodeBlock& jitObject, const String& pattern, unsigned& numSubpatterns, const char*& error, bool ignoreCase, bool multiline);
+ friend void jitCompile(VM*, YarrCodeBlock&, const String& pattern, unsigned& numSubpatterns, const char*& error, bool ignoreCase, bool multiline);
#if CPU(ARM)
static const RegisterID input = ARMRegisters::r0;
@@ -50,20 +51,38 @@ class YarrGenerator : private DefaultMacroAssembler {
static const RegisterID regT0 = ARMRegisters::r4;
static const RegisterID regT1 = ARMRegisters::r5;
+ static const RegisterID initialStart = ARMRegisters::r8;
static const RegisterID returnRegister = ARMRegisters::r0;
static const RegisterID returnRegister2 = ARMRegisters::r1;
+
+#define HAVE_INITIAL_START_REG
#elif CPU(ARM64)
+ // Argument registers
static const RegisterID input = ARM64Registers::x0;
static const RegisterID index = ARM64Registers::x1;
static const RegisterID length = ARM64Registers::x2;
static const RegisterID output = ARM64Registers::x3;
-
- static const RegisterID regT0 = ARM64Registers::x4;
- static const RegisterID regT1 = ARM64Registers::x5;
+ static const RegisterID freelistRegister = ARM64Registers::x4;
+ static const RegisterID freelistSizeRegister = ARM64Registers::x5;
+
+ // Scratch registers
+ static const RegisterID regT0 = ARM64Registers::x6;
+ static const RegisterID regT1 = ARM64Registers::x7;
+ static const RegisterID regT2 = ARM64Registers::x8;
+ static const RegisterID remainingMatchCount = ARM64Registers::x9;
+ static const RegisterID regUnicodeInputAndTrail = ARM64Registers::x10;
+ static const RegisterID initialStart = ARM64Registers::x11;
+ static const RegisterID supplementaryPlanesBase = ARM64Registers::x12;
+ static const RegisterID surrogateTagMask = ARM64Registers::x13;
+ static const RegisterID leadingSurrogateTag = ARM64Registers::x14;
+ static const RegisterID trailingSurrogateTag = ARM64Registers::x15;
static const RegisterID returnRegister = ARM64Registers::x0;
static const RegisterID returnRegister2 = ARM64Registers::x1;
+
+#define HAVE_INITIAL_START_REG
+#define JIT_UNICODE_EXPRESSIONS
#elif CPU(MIPS)
static const RegisterID input = MIPSRegisters::a0;
static const RegisterID index = MIPSRegisters::a1;
@@ -72,20 +91,12 @@ class YarrGenerator : private DefaultMacroAssembler {
static const RegisterID regT0 = MIPSRegisters::t4;
static const RegisterID regT1 = MIPSRegisters::t5;
+ static const RegisterID initialStart = MIPSRegisters::t6;
static const RegisterID returnRegister = MIPSRegisters::v0;
static const RegisterID returnRegister2 = MIPSRegisters::v1;
-#elif CPU(SH4)
- static const RegisterID input = SH4Registers::r4;
- static const RegisterID index = SH4Registers::r5;
- static const RegisterID length = SH4Registers::r6;
- static const RegisterID output = SH4Registers::r7;
- static const RegisterID regT0 = SH4Registers::r0;
- static const RegisterID regT1 = SH4Registers::r1;
-
- static const RegisterID returnRegister = SH4Registers::r0;
- static const RegisterID returnRegister2 = SH4Registers::r1;
+#define HAVE_INITIAL_START_REG
#elif CPU(X86)
static const RegisterID input = X86Registers::eax;
static const RegisterID index = X86Registers::edx;
@@ -99,10 +110,13 @@ class YarrGenerator : private DefaultMacroAssembler {
static const RegisterID returnRegister2 = X86Registers::edx;
#elif CPU(X86_64)
#if !OS(WINDOWS)
+ // Argument registers
static const RegisterID input = X86Registers::edi;
static const RegisterID index = X86Registers::esi;
static const RegisterID length = X86Registers::edx;
static const RegisterID output = X86Registers::ecx;
+ static const RegisterID freelistRegister = X86Registers::r8;
+ static const RegisterID freelistSizeRegister = X86Registers::r9; // Only used during initialization.
#else
// If the return value doesn't fit in 64bits, its destination is pointed by rcx and the parameters are shifted.
// http://msdn.microsoft.com/en-us/library/7572ztz4.aspx
@@ -113,11 +127,186 @@ class YarrGenerator : private DefaultMacroAssembler {
static const RegisterID output = X86Registers::r10;
#endif
+ // Scratch registers
static const RegisterID regT0 = X86Registers::eax;
- static const RegisterID regT1 = X86Registers::ebx;
+#if !OS(WINDOWS)
+ static const RegisterID regT1 = X86Registers::r9;
+ static const RegisterID regT2 = X86Registers::r10;
+#else
+ static const RegisterID regT1 = X86Registers::ecx;
+ static const RegisterID regT2 = X86Registers::edi;
+#endif
+
+ static const RegisterID initialStart = X86Registers::ebx;
+#if !OS(WINDOWS)
+ static const RegisterID remainingMatchCount = X86Registers::r12;
+#else
+ static const RegisterID remainingMatchCount = X86Registers::esi;
+#endif
+ static const RegisterID regUnicodeInputAndTrail = X86Registers::r13;
+ static const RegisterID leadingSurrogateTag = X86Registers::r14;
+ static const RegisterID trailingSurrogateTag = X86Registers::r15;
static const RegisterID returnRegister = X86Registers::eax;
static const RegisterID returnRegister2 = X86Registers::edx;
+
+ const TrustedImm32 supplementaryPlanesBase = TrustedImm32(0x10000);
+ const TrustedImm32 surrogateTagMask = TrustedImm32(0xfffffc00);
+#define HAVE_INITIAL_START_REG
+#define JIT_UNICODE_EXPRESSIONS
+#endif
+
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ struct ParenContextSizes {
+ size_t m_numSubpatterns;
+ size_t m_frameSlots;
+
+ ParenContextSizes(size_t numSubpatterns, size_t frameSlots)
+ : m_numSubpatterns(numSubpatterns)
+ , m_frameSlots(frameSlots)
+ {
+ }
+
+ size_t numSubpatterns() { return m_numSubpatterns; }
+
+ size_t frameSlots() { return m_frameSlots; }
+ };
+
+ struct ParenContext {
+ struct ParenContext* next;
+ uint32_t begin;
+ uint32_t matchAmount;
+ uintptr_t returnAddress;
+ struct Subpatterns {
+ unsigned start;
+ unsigned end;
+ } subpatterns[0];
+ uintptr_t frameSlots[0];
+
+ static size_t sizeFor(ParenContextSizes& parenContextSizes)
+ {
+ return sizeof(ParenContext) + sizeof(Subpatterns) * parenContextSizes.numSubpatterns() + sizeof(uintptr_t) * parenContextSizes.frameSlots();
+ }
+
+ static ptrdiff_t nextOffset()
+ {
+ return offsetof(ParenContext, next);
+ }
+
+ static ptrdiff_t beginOffset()
+ {
+ return offsetof(ParenContext, begin);
+ }
+
+ static ptrdiff_t matchAmountOffset()
+ {
+ return offsetof(ParenContext, matchAmount);
+ }
+
+ static ptrdiff_t returnAddressOffset()
+ {
+ return offsetof(ParenContext, returnAddress);
+ }
+
+ static ptrdiff_t subpatternOffset(size_t subpattern)
+ {
+ return offsetof(ParenContext, subpatterns) + (subpattern - 1) * sizeof(Subpatterns);
+ }
+
+ static ptrdiff_t savedFrameOffset(ParenContextSizes& parenContextSizes)
+ {
+ return offsetof(ParenContext, subpatterns) + (parenContextSizes.numSubpatterns()) * sizeof(Subpatterns);
+ }
+ };
+
+ void initParenContextFreeList()
+ {
+ RegisterID parenContextPointer = regT0;
+ RegisterID nextParenContextPointer = regT2;
+
+ size_t parenContextSize = ParenContext::sizeFor(m_parenContextSizes);
+
+ parenContextSize = WTF::roundUpToMultipleOf<sizeof(uintptr_t)>(parenContextSize);
+
+ // Check that the paren context is a reasonable size.
+ if (parenContextSize > INT16_MAX)
+ m_abortExecution.append(jump());
+
+ Jump emptyFreeList = branchTestPtr(Zero, freelistRegister);
+ move(freelistRegister, parenContextPointer);
+ addPtr(TrustedImm32(parenContextSize), freelistRegister, nextParenContextPointer);
+ addPtr(freelistRegister, freelistSizeRegister);
+ subPtr(TrustedImm32(parenContextSize), freelistSizeRegister);
+
+ Label loopTop(this);
+ Jump initDone = branchPtr(Above, nextParenContextPointer, freelistSizeRegister);
+ storePtr(nextParenContextPointer, Address(parenContextPointer, ParenContext::nextOffset()));
+ move(nextParenContextPointer, parenContextPointer);
+ addPtr(TrustedImm32(parenContextSize), parenContextPointer, nextParenContextPointer);
+ jump(loopTop);
+
+ initDone.link(this);
+ storePtr(TrustedImmPtr(nullptr), Address(parenContextPointer, ParenContext::nextOffset()));
+ emptyFreeList.link(this);
+ }
+
+ void allocateParenContext(RegisterID result)
+ {
+ m_abortExecution.append(branchTestPtr(Zero, freelistRegister));
+ sub32(TrustedImm32(1), remainingMatchCount);
+ m_hitMatchLimit.append(branchTestPtr(Zero, remainingMatchCount));
+ move(freelistRegister, result);
+ loadPtr(Address(freelistRegister, ParenContext::nextOffset()), freelistRegister);
+ }
+
+ void freeParenContext(RegisterID headPtrRegister, RegisterID newHeadPtrRegister)
+ {
+ loadPtr(Address(headPtrRegister, ParenContext::nextOffset()), newHeadPtrRegister);
+ storePtr(freelistRegister, Address(headPtrRegister, ParenContext::nextOffset()));
+ move(headPtrRegister, freelistRegister);
+ }
+
+ void saveParenContext(RegisterID parenContextReg, RegisterID tempReg, unsigned firstSubpattern, unsigned lastSubpattern, unsigned subpatternBaseFrameLocation)
+ {
+ store32(index, Address(parenContextReg, ParenContext::beginOffset()));
+ loadFromFrame(subpatternBaseFrameLocation + BackTrackInfoParentheses::matchAmountIndex(), tempReg);
+ store32(tempReg, Address(parenContextReg, ParenContext::matchAmountOffset()));
+ loadFromFrame(subpatternBaseFrameLocation + BackTrackInfoParentheses::returnAddressIndex(), tempReg);
+ storePtr(tempReg, Address(parenContextReg, ParenContext::returnAddressOffset()));
+ if (compileMode == IncludeSubpatterns) {
+ for (unsigned subpattern = firstSubpattern; subpattern <= lastSubpattern; subpattern++) {
+ loadPtr(Address(output, (subpattern << 1) * sizeof(unsigned)), tempReg);
+ storePtr(tempReg, Address(parenContextReg, ParenContext::subpatternOffset(subpattern)));
+ clearSubpatternStart(subpattern);
+ }
+ }
+ subpatternBaseFrameLocation += YarrStackSpaceForBackTrackInfoParentheses;
+ for (unsigned frameLocation = subpatternBaseFrameLocation; frameLocation < m_parenContextSizes.frameSlots(); frameLocation++) {
+ loadFromFrame(frameLocation, tempReg);
+ storePtr(tempReg, Address(parenContextReg, ParenContext::savedFrameOffset(m_parenContextSizes) + frameLocation * sizeof(uintptr_t)));
+ }
+ }
+
+ void restoreParenContext(RegisterID parenContextReg, RegisterID tempReg, unsigned firstSubpattern, unsigned lastSubpattern, unsigned subpatternBaseFrameLocation)
+ {
+ load32(Address(parenContextReg, ParenContext::beginOffset()), index);
+ storeToFrame(index, subpatternBaseFrameLocation + BackTrackInfoParentheses::beginIndex());
+ load32(Address(parenContextReg, ParenContext::matchAmountOffset()), tempReg);
+ storeToFrame(tempReg, subpatternBaseFrameLocation + BackTrackInfoParentheses::matchAmountIndex());
+ loadPtr(Address(parenContextReg, ParenContext::returnAddressOffset()), tempReg);
+ storeToFrame(tempReg, subpatternBaseFrameLocation + BackTrackInfoParentheses::returnAddressIndex());
+ if (compileMode == IncludeSubpatterns) {
+ for (unsigned subpattern = firstSubpattern; subpattern <= lastSubpattern; subpattern++) {
+ loadPtr(Address(parenContextReg, ParenContext::subpatternOffset(subpattern)), tempReg);
+ storePtr(tempReg, Address(output, (subpattern << 1) * sizeof(unsigned)));
+ }
+ }
+ subpatternBaseFrameLocation += YarrStackSpaceForBackTrackInfoParentheses;
+ for (unsigned frameLocation = subpatternBaseFrameLocation; frameLocation < m_parenContextSizes.frameSlots(); frameLocation++) {
+ loadPtr(Address(parenContextReg, ParenContext::savedFrameOffset(m_parenContextSizes) + frameLocation * sizeof(uintptr_t)), tempReg);
+ storeToFrame(tempReg, frameLocation);
+ }
+ }
#endif
void optimizeAlternative(PatternAlternative* alternative)
@@ -129,8 +318,10 @@ class YarrGenerator : private DefaultMacroAssembler {
PatternTerm& term = alternative->m_terms[i];
PatternTerm& nextTerm = alternative->m_terms[i + 1];
+ // We can move BMP only character classes after fixed character terms.
if ((term.type == PatternTerm::TypeCharacterClass)
&& (term.quantityType == QuantifierFixedCount)
+ && (!m_decodeSurrogatePairs || (!term.characterClass->m_hasNonBMPCharacters && !term.m_invert))
&& (nextTerm.type == PatternTerm::TypePatternCharacter)
&& (nextTerm.quantityType == QuantifierFixedCount)) {
PatternTerm termCopy = term;
@@ -140,7 +331,7 @@ class YarrGenerator : private DefaultMacroAssembler {
}
}
- void matchCharacterClassRange(RegisterID character, JumpList& failures, JumpList& matchDest, const CharacterRange* ranges, unsigned count, unsigned* matchIndex, const UChar* matches, unsigned matchCount)
+ void matchCharacterClassRange(RegisterID character, JumpList& failures, JumpList& matchDest, const CharacterRange* ranges, unsigned count, unsigned* matchIndex, const UChar32* matches, unsigned matchCount)
{
do {
// pick which range we're going to generate
@@ -189,26 +380,28 @@ class YarrGenerator : private DefaultMacroAssembler {
void matchCharacterClass(RegisterID character, JumpList& matchDest, const CharacterClass* charClass)
{
- if (charClass->m_table) {
+ if (charClass->m_table && !m_decodeSurrogatePairs) {
ExtendedAddress tableEntry(character, reinterpret_cast<intptr_t>(charClass->m_table));
matchDest.append(branchTest8(charClass->m_tableInverted ? Zero : NonZero, tableEntry));
return;
}
- Jump unicodeFail;
+ JumpList unicodeFail;
if (charClass->m_matchesUnicode.size() || charClass->m_rangesUnicode.size()) {
- Jump isAscii = branch32(LessThanOrEqual, character, TrustedImm32(0x7f));
+ JumpList isAscii;
+ if (charClass->m_matches.size() || charClass->m_ranges.size())
+ isAscii.append(branch32(LessThanOrEqual, character, TrustedImm32(0x7f)));
if (charClass->m_matchesUnicode.size()) {
for (unsigned i = 0; i < charClass->m_matchesUnicode.size(); ++i) {
- UChar ch = charClass->m_matchesUnicode[i];
+ UChar32 ch = charClass->m_matchesUnicode[i];
matchDest.append(branch32(Equal, character, Imm32(ch)));
}
}
if (charClass->m_rangesUnicode.size()) {
for (unsigned i = 0; i < charClass->m_rangesUnicode.size(); ++i) {
- UChar lo = charClass->m_rangesUnicode[i].begin;
- UChar hi = charClass->m_rangesUnicode[i].end;
+ UChar32 lo = charClass->m_rangesUnicode[i].begin;
+ UChar32 hi = charClass->m_rangesUnicode[i].end;
Jump below = branch32(LessThan, character, Imm32(lo));
matchDest.append(branch32(LessThanOrEqual, character, Imm32(hi)));
@@ -216,18 +409,16 @@ class YarrGenerator : private DefaultMacroAssembler {
}
}
- unicodeFail = jump();
+ if (charClass->m_matches.size() || charClass->m_ranges.size())
+ unicodeFail = jump();
isAscii.link(this);
}
if (charClass->m_ranges.size()) {
unsigned matchIndex = 0;
JumpList failures;
- ASSERT(charClass->m_ranges.size() <= UINT_MAX);
- matchCharacterClassRange(character, failures, matchDest, &charClass->m_ranges[0],
- static_cast<unsigned>(charClass->m_ranges.size()),
- &matchIndex, charClass->m_matches.isEmpty() ? 0 : &charClass->m_matches[0],
- static_cast<unsigned>(charClass->m_matches.size()));
+ matchCharacterClassRange(character, failures, matchDest, charClass->m_ranges.data(), charClass->m_ranges.size(),
+ &matchIndex, charClass->m_matches.data(), charClass->m_matches.size());
while (matchIndex < charClass->m_matches.size())
matchDest.append(branch32(Equal, character, Imm32((unsigned short)charClass->m_matches[matchIndex++])));
@@ -238,7 +429,7 @@ class YarrGenerator : private DefaultMacroAssembler {
for (unsigned i = 0; i < charClass->m_matches.size(); ++i) {
char ch = charClass->m_matches[i];
- if (m_pattern.m_ignoreCase) {
+ if (m_pattern.ignoreCase()) {
if (isASCIILower(ch)) {
matchesAZaz.append(ch);
continue;
@@ -249,8 +440,7 @@ class YarrGenerator : private DefaultMacroAssembler {
matchDest.append(branch32(Equal, character, Imm32((unsigned short)ch)));
}
- ASSERT(matchesAZaz.size() <= UINT_MAX);
- if (unsigned countAZaz = static_cast<int>(matchesAZaz.size())) {
+ if (unsigned countAZaz = matchesAZaz.size()) {
or32(TrustedImm32(32), character);
for (unsigned i = 0; i < countAZaz; ++i)
matchDest.append(branch32(Equal, character, TrustedImm32(matchesAZaz[i])));
@@ -290,29 +480,102 @@ class YarrGenerator : private DefaultMacroAssembler {
return branch32(NotEqual, index, length);
}
- Jump jumpIfCharNotEquals(UChar ch, int inputPosition, RegisterID character)
+ BaseIndex negativeOffsetIndexedAddress(Checked<unsigned> negativeCharacterOffset, RegisterID tempReg, RegisterID indexReg = index)
{
- readCharacter(inputPosition, character);
-
- // For case-insesitive compares, non-ascii characters that have different
- // upper & lower case representations are converted to a character class.
- ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch));
- if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) {
- or32(TrustedImm32(0x20), character);
- ch |= 0x20;
+ RegisterID base = input;
+
+ // BaseIndex() addressing can take a int32_t offset. Given that we can have a regular
+ // expression that has unsigned character offsets, BaseIndex's signed offset is insufficient
+ // for addressing in extreme cases where we might underflow. Therefore we check to see if
+ // negativeCharacterOffset will underflow directly or after converting for 16 bit characters.
+ // If so, we do our own address calculating by adjusting the base, using the result register
+ // as a temp address register.
+ unsigned maximumNegativeOffsetForCharacterSize = m_charSize == Char8 ? 0x7fffffff : 0x3fffffff;
+ unsigned offsetAdjustAmount = 0x40000000;
+ if (negativeCharacterOffset.unsafeGet() > maximumNegativeOffsetForCharacterSize) {
+ base = tempReg;
+ move(input, base);
+ while (negativeCharacterOffset.unsafeGet() > maximumNegativeOffsetForCharacterSize) {
+ subPtr(TrustedImm32(offsetAdjustAmount), base);
+ if (m_charSize != Char8)
+ subPtr(TrustedImm32(offsetAdjustAmount), base);
+ negativeCharacterOffset -= offsetAdjustAmount;
+ }
}
- return branch32(NotEqual, character, Imm32(ch));
+ Checked<int32_t> characterOffset(-static_cast<int32_t>(negativeCharacterOffset.unsafeGet()));
+
+ if (m_charSize == Char8)
+ return BaseIndex(input, indexReg, TimesOne, (characterOffset * static_cast<int32_t>(sizeof(char))).unsafeGet());
+
+ return BaseIndex(input, indexReg, TimesTwo, (characterOffset * static_cast<int32_t>(sizeof(UChar))).unsafeGet());
+ }
+
+#ifdef JIT_UNICODE_EXPRESSIONS
+ void tryReadUnicodeCharImpl(RegisterID resultReg)
+ {
+ ASSERT(m_charSize == Char16);
+
+ JumpList notUnicode;
+ load16Unaligned(regUnicodeInputAndTrail, resultReg);
+ and32(surrogateTagMask, resultReg, regT2);
+ notUnicode.append(branch32(NotEqual, regT2, leadingSurrogateTag));
+ addPtr(TrustedImm32(2), regUnicodeInputAndTrail);
+ getEffectiveAddress(BaseIndex(input, length, TimesTwo), regT2);
+ notUnicode.append(branch32(AboveOrEqual, regUnicodeInputAndTrail, regT2));
+ load16Unaligned(Address(regUnicodeInputAndTrail), regUnicodeInputAndTrail);
+ and32(surrogateTagMask, regUnicodeInputAndTrail, regT2);
+ notUnicode.append(branch32(NotEqual, regT2, trailingSurrogateTag));
+ sub32(leadingSurrogateTag, resultReg);
+ sub32(trailingSurrogateTag, regUnicodeInputAndTrail);
+ lshift32(TrustedImm32(10), resultReg);
+ or32(regUnicodeInputAndTrail, resultReg);
+ add32(supplementaryPlanesBase, resultReg);
+ notUnicode.link(this);
+ }
+
+ void tryReadUnicodeChar(BaseIndex address, RegisterID resultReg)
+ {
+ ASSERT(m_charSize == Char16);
+
+ getEffectiveAddress(address, regUnicodeInputAndTrail);
+
+ if (resultReg == regT0)
+ m_tryReadUnicodeCharacterCalls.append(nearCall());
+ else
+ tryReadUnicodeCharImpl(resultReg);
}
+#endif
- void readCharacter(int inputPosition, RegisterID reg)
+ void readCharacter(Checked<unsigned> negativeCharacterOffset, RegisterID resultReg, RegisterID indexReg = index)
{
+ BaseIndex address = negativeOffsetIndexedAddress(negativeCharacterOffset, resultReg, indexReg);
+
if (m_charSize == Char8)
- load8(BaseIndex(input, index, TimesOne, inputPosition * sizeof(char)), reg);
+ load8(address, resultReg);
+#ifdef JIT_UNICODE_EXPRESSIONS
+ else if (m_decodeSurrogatePairs)
+ tryReadUnicodeChar(address, resultReg);
+#endif
else
- load16(BaseIndex(input, index, TimesTwo, inputPosition * sizeof(UChar)), reg);
+ load16Unaligned(address, resultReg);
}
+ Jump jumpIfCharNotEquals(UChar32 ch, Checked<unsigned> negativeCharacterOffset, RegisterID character)
+ {
+ readCharacter(negativeCharacterOffset, character);
+
+ // For case-insesitive compares, non-ascii characters that have different
+ // upper & lower case representations are converted to a character class.
+ ASSERT(!m_pattern.ignoreCase() || isASCIIAlpha(ch) || isCanonicallyUnique(ch, m_canonicalMode));
+ if (m_pattern.ignoreCase() && isASCIIAlpha(ch)) {
+ or32(TrustedImm32(0x20), character);
+ ch |= 0x20;
+ }
+
+ return branch32(NotEqual, character, Imm32(ch));
+ }
+
void storeToFrame(RegisterID reg, unsigned frameLocation)
{
poke(reg, frameLocation);
@@ -323,9 +586,16 @@ class YarrGenerator : private DefaultMacroAssembler {
poke(imm, frameLocation);
}
+#if CPU(ARM64) || CPU(X86_64)
+ void storeToFrame(TrustedImmPtr imm, unsigned frameLocation)
+ {
+ poke(imm, frameLocation);
+ }
+#endif
+
DataLabelPtr storeToFrameWithPatch(unsigned frameLocation)
{
- return storePtrWithPatch(TrustedImmPtr(0), Address(stackPointerRegister, frameLocation * sizeof(void*)));
+ return storePtrWithPatch(TrustedImmPtr(nullptr), Address(stackPointerRegister, frameLocation * sizeof(void*)));
}
void loadFromFrame(unsigned frameLocation, RegisterID reg)
@@ -340,32 +610,82 @@ class YarrGenerator : private DefaultMacroAssembler {
unsigned alignCallFrameSizeInBytes(unsigned callFrameSize)
{
+ if (!callFrameSize)
+ return 0;
+
callFrameSize *= sizeof(void*);
if (callFrameSize / sizeof(void*) != m_pattern.m_body->m_callFrameSize)
CRASH();
- // Originally, the code was:
-// callFrameSize = (callFrameSize + 0x3f) & ~0x3f;
- // However, 64 bytes is a bit surprising. The biggest "alignment" requirement is on Aarch64, where:
- // "SP mod 16 = 0. The stack must be quad-word aligned." (IHI0055B_aapcs64.pdf)
- callFrameSize = (callFrameSize + 0xf) & ~0xf;
- if (!callFrameSize)
- CRASH();
+ callFrameSize = (callFrameSize + 0x3f) & ~0x3f;
return callFrameSize;
}
void initCallFrame()
{
- unsigned callFrameSize = m_pattern.m_body->m_callFrameSize;
- if (callFrameSize)
- subPtr(Imm32(alignCallFrameSizeInBytes(callFrameSize)), stackPointerRegister);
+ unsigned callFrameSizeInBytes = alignCallFrameSizeInBytes(m_pattern.m_body->m_callFrameSize);
+ if (callFrameSizeInBytes) {
+#if CPU(X86_64) || CPU(ARM64)
+ if (Options::zeroStackFrame()) {
+ // We need to start from the stack pointer, because we could have spilled callee saves
+ move(stackPointerRegister, regT0);
+ subPtr(Imm32(callFrameSizeInBytes), stackPointerRegister);
+ if (callFrameSizeInBytes <= 128) {
+ for (unsigned offset = 0; offset < callFrameSizeInBytes; offset += sizeof(intptr_t))
+ storePtr(TrustedImmPtr(0), Address(regT0, -8 - offset));
+ } else {
+ Label zeroLoop = label();
+ subPtr(TrustedImm32(sizeof(intptr_t) * 2), regT0);
+#if CPU(ARM64)
+ storePair64(ARM64Registers::zr, ARM64Registers::zr, regT0);
+#else
+ storePtr(TrustedImmPtr(0), Address(regT0));
+ storePtr(TrustedImmPtr(0), Address(regT0, sizeof(intptr_t)));
+#endif
+ branchPtr(NotEqual, regT0, stackPointerRegister).linkTo(zeroLoop, this);
+ }
+ } else
+#endif
+ subPtr(Imm32(callFrameSizeInBytes), stackPointerRegister);
+
+ }
}
void removeCallFrame()
{
- unsigned callFrameSize = m_pattern.m_body->m_callFrameSize;
- if (callFrameSize)
- addPtr(Imm32(alignCallFrameSizeInBytes(callFrameSize)), stackPointerRegister);
+ unsigned callFrameSizeInBytes = alignCallFrameSizeInBytes(m_pattern.m_body->m_callFrameSize);
+ if (callFrameSizeInBytes)
+ addPtr(Imm32(callFrameSizeInBytes), stackPointerRegister);
+ }
+
+ void generateFailReturn()
+ {
+ move(TrustedImmPtr((void*)WTF::notFound), returnRegister);
+ move(TrustedImm32(0), returnRegister2);
+ generateReturn();
+ }
+
+ void generateJITFailReturn()
+ {
+ if (m_abortExecution.empty() && m_hitMatchLimit.empty())
+ return;
+
+ JumpList finishExiting;
+ if (!m_abortExecution.empty()) {
+ m_abortExecution.link(this);
+ move(TrustedImmPtr((void*)static_cast<size_t>(-2)), returnRegister);
+ finishExiting.append(jump());
+ }
+
+ if (!m_hitMatchLimit.empty()) {
+ m_hitMatchLimit.link(this);
+ move(TrustedImmPtr((void*)static_cast<size_t>(-1)), returnRegister);
+ }
+
+ finishExiting.link(this);
+ removeCallFrame();
+ move(TrustedImm32(0), returnRegister2);
+ generateReturn();
}
- // Used to record subpatters, should only be called if compileMode is IncludeSubpatterns.
+ // Used to record subpatterns, should only be called if compileMode is IncludeSubpatterns.
void setSubpatternStart(RegisterID reg, unsigned subpattern)
{
ASSERT(subpattern);
@@ -385,6 +705,12 @@ class YarrGenerator : private DefaultMacroAssembler {
store32(TrustedImm32(-1), Address(output, (subpattern << 1) * sizeof(int)));
}
+ void clearMatches(unsigned subpattern, unsigned lastSubpattern)
+ {
+ for (; subpattern <= lastSubpattern; subpattern++)
+ clearSubpatternStart(subpattern);
+ }
+
// We use one of three different strategies to track the start of the current match,
// while matching.
// 1) If the pattern has a fixed size, do nothing! - we calculate the value lazily
@@ -427,18 +753,21 @@ class YarrGenerator : private DefaultMacroAssembler {
OpNestedAlternativeNext,
OpNestedAlternativeEnd,
// Used for alternatives in subpatterns where there is only a single
- // alternative (backtrackingis easier in these cases), or for alternatives
+ // alternative (backtracking is easier in these cases), or for alternatives
// which never need to be backtracked (those in parenthetical assertions,
// terminal subpatterns).
OpSimpleNestedAlternativeBegin,
OpSimpleNestedAlternativeNext,
OpSimpleNestedAlternativeEnd,
- // Used to wrap 'Once' subpattern matches (quantityCount == 1).
+ // Used to wrap 'Once' subpattern matches (quantityMaxCount == 1).
OpParenthesesSubpatternOnceBegin,
OpParenthesesSubpatternOnceEnd,
// Used to wrap 'Terminal' subpattern matches (at the end of the regexp).
OpParenthesesSubpatternTerminalBegin,
OpParenthesesSubpatternTerminalEnd,
+ // Used to wrap generic captured matches
+ OpParenthesesSubpatternBegin,
+ OpParenthesesSubpatternEnd,
// Used to wrap parenthetical assertions.
OpParentheticalAssertionBegin,
OpParentheticalAssertionEnd,
@@ -468,16 +797,16 @@ class YarrGenerator : private DefaultMacroAssembler {
// The operation, as a YarrOpCode, and also a reference to the PatternTerm.
YarrOpCode m_op;
- PatternTerm* m_term = nullptr;
+ PatternTerm* m_term;
// For alternatives, this holds the PatternAlternative and doubly linked
// references to this alternative's siblings. In the case of the
// OpBodyAlternativeEnd node at the end of a section of repeating nodes,
// m_nextOp will reference the OpBodyAlternativeBegin node of the first
// repeating alternative.
- PatternAlternative* m_alternative = nullptr;
- size_t m_previousOp = 0;
- size_t m_nextOp = 0;
+ PatternAlternative* m_alternative;
+ size_t m_previousOp;
+ size_t m_nextOp;
// Used to record a set of Jumps out of the generated code, typically
// used for jumps out to backtracking code, and a single reentry back
@@ -495,9 +824,9 @@ class YarrGenerator : private DefaultMacroAssembler {
bool m_isDeadCode;
// Currently used in the case of some of the more complex management of
- // 'm_checked', to cache the offset used in this alternative, to avoid
+ // 'm_checkedOffset', to cache the offset used in this alternative, to avoid
// recalculating it.
- int m_checkAdjust;
+ Checked<unsigned> m_checkAdjust;
// Used by OpNestedAlternativeNext/End to hold the pointer to the
// value that will be pushed into the pattern's frame to return to,
@@ -599,7 +928,7 @@ class YarrGenerator : private DefaultMacroAssembler {
}
// Called at the end of code generation to link all return addresses.
- void linkDataLabels(LinkBuffer<JSC::DefaultMacroAssembler>& linkBuffer)
+ void linkDataLabels(DefaultLinkBuffer& linkBuffer)
{
ASSERT(isEmpty());
for (unsigned i = 0; i < m_backtrackRecords.size(); ++i)
@@ -642,14 +971,14 @@ class YarrGenerator : private DefaultMacroAssembler {
YarrOp& op = m_ops[opIndex];
PatternTerm* term = op.m_term;
- if (m_pattern.m_multiline) {
+ if (m_pattern.multiline()) {
const RegisterID character = regT0;
JumpList matchDest;
if (!term->inputPosition)
- matchDest.append(branch32(Equal, index, Imm32(m_checked)));
+ matchDest.append(branch32(Equal, index, Imm32(m_checkedOffset.unsafeGet())));
- readCharacter((term->inputPosition - m_checked) - 1, character);
+ readCharacter(m_checkedOffset - term->inputPosition + 1, character);
matchCharacterClass(character, matchDest, m_pattern.newlineCharacterClass());
op.m_jumps.append(jump());
@@ -659,7 +988,7 @@ class YarrGenerator : private DefaultMacroAssembler {
if (term->inputPosition)
op.m_jumps.append(jump());
else
- op.m_jumps.append(branch32(NotEqual, index, Imm32(m_checked)));
+ op.m_jumps.append(branch32(NotEqual, index, Imm32(m_checkedOffset.unsafeGet())));
}
}
void backtrackAssertionBOL(size_t opIndex)
@@ -672,20 +1001,20 @@ class YarrGenerator : private DefaultMacroAssembler {
YarrOp& op = m_ops[opIndex];
PatternTerm* term = op.m_term;
- if (m_pattern.m_multiline) {
+ if (m_pattern.multiline()) {
const RegisterID character = regT0;
JumpList matchDest;
- if (term->inputPosition == m_checked)
+ if (term->inputPosition == m_checkedOffset.unsafeGet())
matchDest.append(atEndOfInput());
- readCharacter(term->inputPosition - m_checked, character);
+ readCharacter(m_checkedOffset - term->inputPosition, character);
matchCharacterClass(character, matchDest, m_pattern.newlineCharacterClass());
op.m_jumps.append(jump());
matchDest.link(this);
} else {
- if (term->inputPosition == m_checked)
+ if (term->inputPosition == m_checkedOffset.unsafeGet())
op.m_jumps.append(notAtEndOfInput());
// Erk, really should poison out these alternatives early. :-/
else
@@ -705,11 +1034,19 @@ class YarrGenerator : private DefaultMacroAssembler {
const RegisterID character = regT0;
- if (term->inputPosition == m_checked)
+ if (term->inputPosition == m_checkedOffset.unsafeGet())
nextIsNotWordChar.append(atEndOfInput());
- readCharacter((term->inputPosition - m_checked), character);
- matchCharacterClass(character, nextIsWordChar, m_pattern.wordcharCharacterClass());
+ readCharacter(m_checkedOffset - term->inputPosition, character);
+
+ CharacterClass* wordcharCharacterClass;
+
+ if (m_unicodeIgnoreCase)
+ wordcharCharacterClass = m_pattern.wordUnicodeIgnoreCaseCharCharacterClass();
+ else
+ wordcharCharacterClass = m_pattern.wordcharCharacterClass();
+
+ matchCharacterClass(character, nextIsWordChar, wordcharCharacterClass);
}
void generateAssertionWordBoundary(size_t opIndex)
@@ -722,9 +1059,17 @@ class YarrGenerator : private DefaultMacroAssembler {
Jump atBegin;
JumpList matchDest;
if (!term->inputPosition)
- atBegin = branch32(Equal, index, Imm32(m_checked));
- readCharacter((term->inputPosition - m_checked) - 1, character);
- matchCharacterClass(character, matchDest, m_pattern.wordcharCharacterClass());
+ atBegin = branch32(Equal, index, Imm32(m_checkedOffset.unsafeGet()));
+ readCharacter(m_checkedOffset - term->inputPosition + 1, character);
+
+ CharacterClass* wordcharCharacterClass;
+
+ if (m_unicodeIgnoreCase)
+ wordcharCharacterClass = m_pattern.wordUnicodeIgnoreCaseCharCharacterClass();
+ else
+ wordcharCharacterClass = m_pattern.wordcharCharacterClass();
+
+ matchCharacterClass(character, matchDest, wordcharCharacterClass);
if (!term->inputPosition)
atBegin.link(this);
@@ -775,7 +1120,7 @@ class YarrGenerator : private DefaultMacroAssembler {
YarrOp* nextOp = &m_ops[opIndex + 1];
PatternTerm* term = op.m_term;
- UChar ch = term->patternCharacter;
+ UChar32 ch = term->patternCharacter;
if ((ch > 0xff) && (m_charSize == Char8)) {
// Have a 16 bit pattern character and an 8 bit string - short circuit
@@ -784,21 +1129,21 @@ class YarrGenerator : private DefaultMacroAssembler {
}
const RegisterID character = regT0;
- int maxCharactersAtOnce = m_charSize == Char8 ? 4 : 2;
+ unsigned maxCharactersAtOnce = m_charSize == Char8 ? 4 : 2;
unsigned ignoreCaseMask = 0;
#if CPU(BIG_ENDIAN)
int allCharacters = ch << (m_charSize == Char8 ? 24 : 16);
#else
int allCharacters = ch;
#endif
- int numberCharacters;
- int startTermPosition = term->inputPosition;
+ unsigned numberCharacters;
+ unsigned startTermPosition = term->inputPosition;
// For case-insesitive compares, non-ascii characters that have different
// upper & lower case representations are converted to a character class.
- ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch));
+ ASSERT(!m_pattern.ignoreCase() || isASCIIAlpha(ch) || isCanonicallyUnique(ch, m_canonicalMode));
- if (m_pattern.m_ignoreCase && isASCIIAlpha(ch))
+ if (m_pattern.ignoreCase() && isASCIIAlpha(ch))
#if CPU(BIG_ENDIAN)
ignoreCaseMask |= 32 << (m_charSize == Char8 ? 24 : 16);
#else
@@ -810,8 +1155,9 @@ class YarrGenerator : private DefaultMacroAssembler {
if (nextTerm->type != PatternTerm::TypePatternCharacter
|| nextTerm->quantityType != QuantifierFixedCount
- || nextTerm->quantityCount != 1
- || nextTerm->inputPosition != (startTermPosition + numberCharacters))
+ || nextTerm->quantityMaxCount != 1
+ || nextTerm->inputPosition != (startTermPosition + numberCharacters)
+ || (U16_LENGTH(nextTerm->patternCharacter) != 1 && m_decodeSurrogatePairs))
break;
nextOp->m_isDeadCode = true;
@@ -822,7 +1168,7 @@ class YarrGenerator : private DefaultMacroAssembler {
int shiftAmount = (m_charSize == Char8 ? 8 : 16) * numberCharacters;
#endif
- UChar currentCharacter = nextTerm->patternCharacter;
+ UChar32 currentCharacter = nextTerm->patternCharacter;
if ((currentCharacter > 0xff) && (m_charSize == Char8)) {
// Have a 16 bit pattern character and an 8 bit string - short circuit
@@ -832,47 +1178,43 @@ class YarrGenerator : private DefaultMacroAssembler {
// For case-insesitive compares, non-ascii characters that have different
// upper & lower case representations are converted to a character class.
- ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(currentCharacter) || isCanonicallyUnique(currentCharacter));
+ ASSERT(!m_pattern.ignoreCase() || isASCIIAlpha(currentCharacter) || isCanonicallyUnique(currentCharacter, m_canonicalMode));
allCharacters |= (currentCharacter << shiftAmount);
- if ((m_pattern.m_ignoreCase) && (isASCIIAlpha(currentCharacter)))
+ if ((m_pattern.ignoreCase()) && (isASCIIAlpha(currentCharacter)))
ignoreCaseMask |= 32 << shiftAmount;
}
if (m_charSize == Char8) {
switch (numberCharacters) {
case 1:
- op.m_jumps.append(jumpIfCharNotEquals(ch, startTermPosition - m_checked, character));
+ op.m_jumps.append(jumpIfCharNotEquals(ch, m_checkedOffset - startTermPosition, character));
return;
case 2: {
- BaseIndex address(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar));
- load16Unaligned(address, character);
+ load16Unaligned(negativeOffsetIndexedAddress(m_checkedOffset - startTermPosition, character), character);
break;
}
case 3: {
- BaseIndex highAddress(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar));
- load16Unaligned(highAddress, character);
+ load16Unaligned(negativeOffsetIndexedAddress(m_checkedOffset - startTermPosition, character), character);
if (ignoreCaseMask)
or32(Imm32(ignoreCaseMask), character);
op.m_jumps.append(branch32(NotEqual, character, Imm32((allCharacters & 0xffff) | ignoreCaseMask)));
- op.m_jumps.append(jumpIfCharNotEquals(allCharacters >> 16, startTermPosition + 2 - m_checked, character));
+ op.m_jumps.append(jumpIfCharNotEquals(allCharacters >> 16, m_checkedOffset - startTermPosition - 2, character));
return;
}
case 4: {
- BaseIndex address(input, index, TimesOne, (startTermPosition - m_checked) * sizeof(LChar));
- load32WithUnalignedHalfWords(address, character);
+ load32WithUnalignedHalfWords(negativeOffsetIndexedAddress(m_checkedOffset- startTermPosition, character), character);
break;
}
}
} else {
switch (numberCharacters) {
case 1:
- op.m_jumps.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character));
+ op.m_jumps.append(jumpIfCharNotEquals(ch, m_checkedOffset - term->inputPosition, character));
return;
case 2:
- BaseIndex address(input, index, TimesTwo, (term->inputPosition - m_checked) * sizeof(UChar));
- load32WithUnalignedHalfWords(address, character);
+ load32WithUnalignedHalfWords(negativeOffsetIndexedAddress(m_checkedOffset- term->inputPosition, character), character);
break;
}
}
@@ -891,32 +1233,33 @@ class YarrGenerator : private DefaultMacroAssembler {
{
YarrOp& op = m_ops[opIndex];
PatternTerm* term = op.m_term;
- UChar ch = term->patternCharacter;
+ UChar32 ch = term->patternCharacter;
const RegisterID character = regT0;
const RegisterID countRegister = regT1;
move(index, countRegister);
- sub32(Imm32(term->quantityCount.unsafeGet()), countRegister);
+ Checked<unsigned> scaledMaxCount = term->quantityMaxCount;
+ scaledMaxCount *= U_IS_BMP(ch) ? 1 : 2;
+ sub32(Imm32(scaledMaxCount.unsafeGet()), countRegister);
Label loop(this);
- BaseIndex address(input, countRegister, m_charScale, (Checked<int>(term->inputPosition - m_checked + Checked<int64_t>(term->quantityCount)) * static_cast<int>(m_charSize == Char8 ? sizeof(char) : sizeof(UChar))).unsafeGet());
-
- if (m_charSize == Char8)
- load8(address, character);
- else
- load16(address, character);
-
+ readCharacter(m_checkedOffset - term->inputPosition - scaledMaxCount, character, countRegister);
// For case-insesitive compares, non-ascii characters that have different
// upper & lower case representations are converted to a character class.
- ASSERT(!m_pattern.m_ignoreCase || isASCIIAlpha(ch) || isCanonicallyUnique(ch));
- if (m_pattern.m_ignoreCase && isASCIIAlpha(ch)) {
+ ASSERT(!m_pattern.ignoreCase() || isASCIIAlpha(ch) || isCanonicallyUnique(ch, m_canonicalMode));
+ if (m_pattern.ignoreCase() && isASCIIAlpha(ch)) {
or32(TrustedImm32(0x20), character);
ch |= 0x20;
}
op.m_jumps.append(branch32(NotEqual, character, Imm32(ch)));
- add32(TrustedImm32(1), countRegister);
+#ifdef JIT_UNICODE_EXPRESSIONS
+ if (m_decodeSurrogatePairs && !U_IS_BMP(ch))
+ add32(TrustedImm32(2), countRegister);
+ else
+#endif
+ add32(TrustedImm32(1), countRegister);
branch32(NotEqual, countRegister, index).linkTo(loop, this);
}
void backtrackPatternCharacterFixed(size_t opIndex)
@@ -928,7 +1271,7 @@ class YarrGenerator : private DefaultMacroAssembler {
{
YarrOp& op = m_ops[opIndex];
PatternTerm* term = op.m_term;
- UChar ch = term->patternCharacter;
+ UChar32 ch = term->patternCharacter;
const RegisterID character = regT0;
const RegisterID countRegister = regT1;
@@ -940,20 +1283,30 @@ class YarrGenerator : private DefaultMacroAssembler {
JumpList failures;
Label loop(this);
failures.append(atEndOfInput());
- failures.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character));
+ failures.append(jumpIfCharNotEquals(ch, m_checkedOffset - term->inputPosition, character));
- add32(TrustedImm32(1), countRegister);
add32(TrustedImm32(1), index);
- if (term->quantityCount == quantifyInfinite)
+#ifdef JIT_UNICODE_EXPRESSIONS
+ if (m_decodeSurrogatePairs && !U_IS_BMP(ch)) {
+ Jump surrogatePairOk = notAtEndOfInput();
+ sub32(TrustedImm32(1), index);
+ failures.append(jump());
+ surrogatePairOk.link(this);
+ add32(TrustedImm32(1), index);
+ }
+#endif
+ add32(TrustedImm32(1), countRegister);
+
+ if (term->quantityMaxCount == quantifyInfinite)
jump(loop);
else
- branch32(NotEqual, countRegister, Imm32(term->quantityCount.unsafeGet())).linkTo(loop, this);
+ branch32(NotEqual, countRegister, Imm32(term->quantityMaxCount.unsafeGet())).linkTo(loop, this);
failures.link(this);
}
op.m_reentry = label();
- storeToFrame(countRegister, term->frameLocation);
+ storeToFrame(countRegister, term->frameLocation + BackTrackInfoPatternCharacter::matchAmountIndex());
}
void backtrackPatternCharacterGreedy(size_t opIndex)
{
@@ -964,10 +1317,13 @@ class YarrGenerator : private DefaultMacroAssembler {
m_backtrackingState.link(this);
- loadFromFrame(term->frameLocation, countRegister);
+ loadFromFrame(term->frameLocation + BackTrackInfoPatternCharacter::matchAmountIndex(), countRegister);
m_backtrackingState.append(branchTest32(Zero, countRegister));
sub32(TrustedImm32(1), countRegister);
- sub32(TrustedImm32(1), index);
+ if (!m_decodeSurrogatePairs || U_IS_BMP(term->patternCharacter))
+ sub32(TrustedImm32(1), index);
+ else
+ sub32(TrustedImm32(2), index);
jump(op.m_reentry);
}
@@ -980,36 +1336,50 @@ class YarrGenerator : private DefaultMacroAssembler {
move(TrustedImm32(0), countRegister);
op.m_reentry = label();
- storeToFrame(countRegister, term->frameLocation);
+ storeToFrame(countRegister, term->frameLocation + BackTrackInfoPatternCharacter::matchAmountIndex());
}
void backtrackPatternCharacterNonGreedy(size_t opIndex)
{
YarrOp& op = m_ops[opIndex];
PatternTerm* term = op.m_term;
- UChar ch = term->patternCharacter;
+ UChar32 ch = term->patternCharacter;
const RegisterID character = regT0;
const RegisterID countRegister = regT1;
m_backtrackingState.link(this);
- loadFromFrame(term->frameLocation, countRegister);
+ loadFromFrame(term->frameLocation + BackTrackInfoPatternCharacter::matchAmountIndex(), countRegister);
// Unless have a 16 bit pattern character and an 8 bit string - short circuit
if (!((ch > 0xff) && (m_charSize == Char8))) {
JumpList nonGreedyFailures;
nonGreedyFailures.append(atEndOfInput());
- if (term->quantityCount != quantifyInfinite)
- nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount.unsafeGet())));
- nonGreedyFailures.append(jumpIfCharNotEquals(ch, term->inputPosition - m_checked, character));
+ if (term->quantityMaxCount != quantifyInfinite)
+ nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityMaxCount.unsafeGet())));
+ nonGreedyFailures.append(jumpIfCharNotEquals(ch, m_checkedOffset - term->inputPosition, character));
- add32(TrustedImm32(1), countRegister);
add32(TrustedImm32(1), index);
+#ifdef JIT_UNICODE_EXPRESSIONS
+ if (m_decodeSurrogatePairs && !U_IS_BMP(ch)) {
+ Jump surrogatePairOk = notAtEndOfInput();
+ sub32(TrustedImm32(1), index);
+ nonGreedyFailures.append(jump());
+ surrogatePairOk.link(this);
+ add32(TrustedImm32(1), index);
+ }
+#endif
+ add32(TrustedImm32(1), countRegister);
jump(op.m_reentry);
nonGreedyFailures.link(this);
}
+ if (m_decodeSurrogatePairs && !U_IS_BMP(ch)) {
+ // subtract countRegister*2 for non-BMP characters
+ lshift32(TrustedImm32(1), countRegister);
+ }
+
sub32(countRegister, index);
m_backtrackingState.fallthrough();
}
@@ -1021,19 +1391,43 @@ class YarrGenerator : private DefaultMacroAssembler {
const RegisterID character = regT0;
+ if (m_decodeSurrogatePairs)
+ storeToFrame(index, term->frameLocation + BackTrackInfoCharacterClass::beginIndex());
+
JumpList matchDest;
- readCharacter(term->inputPosition - m_checked, character);
- matchCharacterClass(character, matchDest, term->characterClass);
+ readCharacter(m_checkedOffset - term->inputPosition, character);
+ // If we are matching the "any character" builtin class we only need to read the
+ // character and don't need to match as it will always succeed.
+ if (term->invert() || !term->characterClass->m_anyCharacter) {
+ matchCharacterClass(character, matchDest, term->characterClass);
- if (term->invert())
- op.m_jumps.append(matchDest);
- else {
- op.m_jumps.append(jump());
- matchDest.link(this);
+ if (term->invert())
+ op.m_jumps.append(matchDest);
+ else {
+ op.m_jumps.append(jump());
+ matchDest.link(this);
+ }
}
+#ifdef JIT_UNICODE_EXPRESSIONS
+ if (m_decodeSurrogatePairs) {
+ Jump isBMPChar = branch32(LessThan, character, supplementaryPlanesBase);
+ add32(TrustedImm32(1), index);
+ isBMPChar.link(this);
+ }
+#endif
}
void backtrackCharacterClassOnce(size_t opIndex)
{
+#ifdef JIT_UNICODE_EXPRESSIONS
+ if (m_decodeSurrogatePairs) {
+ YarrOp& op = m_ops[opIndex];
+ PatternTerm* term = op.m_term;
+
+ m_backtrackingState.link(this);
+ loadFromFrame(term->frameLocation + BackTrackInfoCharacterClass::beginIndex(), index);
+ m_backtrackingState.fallthrough();
+ }
+#endif
backtrackTermDefault(opIndex);
}
@@ -1046,24 +1440,34 @@ class YarrGenerator : private DefaultMacroAssembler {
const RegisterID countRegister = regT1;
move(index, countRegister);
- sub32(Imm32(term->quantityCount.unsafeGet()), countRegister);
+ sub32(Imm32(term->quantityMaxCount.unsafeGet()), countRegister);
Label loop(this);
JumpList matchDest;
- if (m_charSize == Char8)
- load8(BaseIndex(input, countRegister, TimesOne, (Checked<int>(term->inputPosition - m_checked + Checked<int64_t>(term->quantityCount)) * static_cast<int>(sizeof(char))).unsafeGet()), character);
- else
- load16(BaseIndex(input, countRegister, TimesTwo, (Checked<int>(term->inputPosition - m_checked + Checked<int64_t>(term->quantityCount)) * static_cast<int>(sizeof(UChar))).unsafeGet()), character);
- matchCharacterClass(character, matchDest, term->characterClass);
+ readCharacter(m_checkedOffset - term->inputPosition - term->quantityMaxCount, character, countRegister);
+ // If we are matching the "any character" builtin class we only need to read the
+ // character and don't need to match as it will always succeed.
+ if (term->invert() || !term->characterClass->m_anyCharacter) {
+ matchCharacterClass(character, matchDest, term->characterClass);
- if (term->invert())
- op.m_jumps.append(matchDest);
- else {
- op.m_jumps.append(jump());
- matchDest.link(this);
+ if (term->invert())
+ op.m_jumps.append(matchDest);
+ else {
+ op.m_jumps.append(jump());
+ matchDest.link(this);
+ }
}
add32(TrustedImm32(1), countRegister);
+#ifdef JIT_UNICODE_EXPRESSIONS
+ if (m_decodeSurrogatePairs) {
+ Jump isBMPChar = branch32(LessThan, character, supplementaryPlanesBase);
+ op.m_jumps.append(atEndOfInput());
+ add32(TrustedImm32(1), countRegister);
+ add32(TrustedImm32(1), index);
+ isBMPChar.link(this);
+ }
+#endif
branch32(NotEqual, countRegister, index).linkTo(loop, this);
}
void backtrackCharacterClassFixed(size_t opIndex)
@@ -1079,6 +1483,8 @@ class YarrGenerator : private DefaultMacroAssembler {
const RegisterID character = regT0;
const RegisterID countRegister = regT1;
+ if (m_decodeSurrogatePairs)
+ storeToFrame(index, term->frameLocation + BackTrackInfoCharacterClass::beginIndex());
move(TrustedImm32(0), countRegister);
JumpList failures;
@@ -1086,20 +1492,33 @@ class YarrGenerator : private DefaultMacroAssembler {
failures.append(atEndOfInput());
if (term->invert()) {
- readCharacter(term->inputPosition - m_checked, character);
+ readCharacter(m_checkedOffset - term->inputPosition, character);
matchCharacterClass(character, failures, term->characterClass);
} else {
JumpList matchDest;
- readCharacter(term->inputPosition - m_checked, character);
- matchCharacterClass(character, matchDest, term->characterClass);
- failures.append(jump());
+ readCharacter(m_checkedOffset - term->inputPosition, character);
+ // If we are matching the "any character" builtin class we only need to read the
+ // character and don't need to match as it will always succeed.
+ if (!term->characterClass->m_anyCharacter) {
+ matchCharacterClass(character, matchDest, term->characterClass);
+ failures.append(jump());
+ }
matchDest.link(this);
}
- add32(TrustedImm32(1), countRegister);
add32(TrustedImm32(1), index);
- if (term->quantityCount != quantifyInfinite) {
- branch32(NotEqual, countRegister, Imm32(term->quantityCount.unsafeGet())).linkTo(loop, this);
+#ifdef JIT_UNICODE_EXPRESSIONS
+ if (m_decodeSurrogatePairs) {
+ failures.append(atEndOfInput());
+ Jump isBMPChar = branch32(LessThan, character, supplementaryPlanesBase);
+ add32(TrustedImm32(1), index);
+ isBMPChar.link(this);
+ }
+#endif
+ add32(TrustedImm32(1), countRegister);
+
+ if (term->quantityMaxCount != quantifyInfinite) {
+ branch32(NotEqual, countRegister, Imm32(term->quantityMaxCount.unsafeGet())).linkTo(loop, this);
failures.append(jump());
} else
jump(loop);
@@ -1107,7 +1526,7 @@ class YarrGenerator : private DefaultMacroAssembler {
failures.link(this);
op.m_reentry = label();
- storeToFrame(countRegister, term->frameLocation);
+ storeToFrame(countRegister, term->frameLocation + BackTrackInfoCharacterClass::matchAmountIndex());
}
void backtrackCharacterClassGreedy(size_t opIndex)
{
@@ -1118,10 +1537,34 @@ class YarrGenerator : private DefaultMacroAssembler {
m_backtrackingState.link(this);
- loadFromFrame(term->frameLocation, countRegister);
+ loadFromFrame(term->frameLocation + BackTrackInfoCharacterClass::matchAmountIndex(), countRegister);
m_backtrackingState.append(branchTest32(Zero, countRegister));
sub32(TrustedImm32(1), countRegister);
- sub32(TrustedImm32(1), index);
+ if (!m_decodeSurrogatePairs)
+ sub32(TrustedImm32(1), index);
+ else {
+ const RegisterID character = regT0;
+
+ loadFromFrame(term->frameLocation + BackTrackInfoCharacterClass::beginIndex(), index);
+ // Rematch one less
+ storeToFrame(countRegister, term->frameLocation + BackTrackInfoCharacterClass::matchAmountIndex());
+
+ Label rematchLoop(this);
+ readCharacter(m_checkedOffset - term->inputPosition, character);
+
+ sub32(TrustedImm32(1), countRegister);
+ add32(TrustedImm32(1), index);
+
+#ifdef JIT_UNICODE_EXPRESSIONS
+ Jump isBMPChar = branch32(LessThan, character, supplementaryPlanesBase);
+ add32(TrustedImm32(1), index);
+ isBMPChar.link(this);
+#endif
+
+ branchTest32(Zero, countRegister).linkTo(rematchLoop, this);
+
+ loadFromFrame(term->frameLocation + BackTrackInfoCharacterClass::matchAmountIndex(), countRegister);
+ }
jump(op.m_reentry);
}
@@ -1134,8 +1577,11 @@ class YarrGenerator : private DefaultMacroAssembler {
move(TrustedImm32(0), countRegister);
op.m_reentry = label();
- storeToFrame(countRegister, term->frameLocation);
+ if (m_decodeSurrogatePairs)
+ storeToFrame(index, term->frameLocation + BackTrackInfoCharacterClass::beginIndex());
+ storeToFrame(countRegister, term->frameLocation + BackTrackInfoCharacterClass::matchAmountIndex());
}
+
void backtrackCharacterClassNonGreedy(size_t opIndex)
{
YarrOp& op = m_ops[opIndex];
@@ -1148,24 +1594,38 @@ class YarrGenerator : private DefaultMacroAssembler {
m_backtrackingState.link(this);
- loadFromFrame(term->frameLocation, countRegister);
+ if (m_decodeSurrogatePairs)
+ loadFromFrame(term->frameLocation + BackTrackInfoCharacterClass::beginIndex(), index);
+ loadFromFrame(term->frameLocation + BackTrackInfoCharacterClass::matchAmountIndex(), countRegister);
nonGreedyFailures.append(atEndOfInput());
- nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityCount.unsafeGet())));
+ nonGreedyFailures.append(branch32(Equal, countRegister, Imm32(term->quantityMaxCount.unsafeGet())));
JumpList matchDest;
- readCharacter(term->inputPosition - m_checked, character);
- matchCharacterClass(character, matchDest, term->characterClass);
+ readCharacter(m_checkedOffset - term->inputPosition, character);
+ // If we are matching the "any character" builtin class we only need to read the
+ // character and don't need to match as it will always succeed.
+ if (term->invert() || !term->characterClass->m_anyCharacter) {
+ matchCharacterClass(character, matchDest, term->characterClass);
- if (term->invert())
- nonGreedyFailures.append(matchDest);
- else {
- nonGreedyFailures.append(jump());
- matchDest.link(this);
+ if (term->invert())
+ nonGreedyFailures.append(matchDest);
+ else {
+ nonGreedyFailures.append(jump());
+ matchDest.link(this);
+ }
}
- add32(TrustedImm32(1), countRegister);
add32(TrustedImm32(1), index);
+#ifdef JIT_UNICODE_EXPRESSIONS
+ if (m_decodeSurrogatePairs) {
+ nonGreedyFailures.append(atEndOfInput());
+ Jump isBMPChar = branch32(LessThan, character, supplementaryPlanesBase);
+ add32(TrustedImm32(1), index);
+ isBMPChar.link(this);
+ }
+#endif
+ add32(TrustedImm32(1), countRegister);
jump(op.m_reentry);
@@ -1181,15 +1641,28 @@ class YarrGenerator : private DefaultMacroAssembler {
const RegisterID character = regT0;
const RegisterID matchPos = regT1;
+#ifndef HAVE_INITIAL_START_REG
+ const RegisterID initialStart = character;
+#endif
JumpList foundBeginningNewLine;
JumpList saveStartIndex;
JumpList foundEndingNewLine;
+ if (m_pattern.dotAll()) {
+ move(TrustedImm32(0), matchPos);
+ setMatchStart(matchPos);
+ move(length, index);
+ return;
+ }
+
ASSERT(!m_pattern.m_body->m_hasFixedSize);
getMatchStart(matchPos);
- saveStartIndex.append(branchTest32(Zero, matchPos));
+#ifndef HAVE_INITIAL_START_REG
+ loadFromFrame(m_pattern.m_initialStartValueFrameLocation, initialStart);
+#endif
+ saveStartIndex.append(branch32(BelowOrEqual, matchPos, initialStart));
Label findBOLLoop(this);
sub32(TrustedImm32(1), matchPos);
if (m_charSize == Char8)
@@ -1197,14 +1670,18 @@ class YarrGenerator : private DefaultMacroAssembler {
else
load16(BaseIndex(input, matchPos, TimesTwo, 0), character);
matchCharacterClass(character, foundBeginningNewLine, m_pattern.newlineCharacterClass());
- branchTest32(NonZero, matchPos).linkTo(findBOLLoop, this);
+
+#ifndef HAVE_INITIAL_START_REG
+ loadFromFrame(m_pattern.m_initialStartValueFrameLocation, initialStart);
+#endif
+ branch32(Above, matchPos, initialStart).linkTo(findBOLLoop, this);
saveStartIndex.append(jump());
foundBeginningNewLine.link(this);
add32(TrustedImm32(1), matchPos); // Advance past newline
saveStartIndex.link(this);
- if (!m_pattern.m_multiline && term->anchors.bolAnchor)
+ if (!m_pattern.multiline() && term->anchors.bolAnchor)
op.m_jumps.append(branchTest32(NonZero, matchPos));
ASSERT(!m_pattern.m_body->m_hasFixedSize);
@@ -1224,7 +1701,7 @@ class YarrGenerator : private DefaultMacroAssembler {
foundEndingNewLine.link(this);
- if (!m_pattern.m_multiline && term->anchors.eolAnchor)
+ if (!m_pattern.multiline() && term->anchors.eolAnchor)
op.m_jumps.append(branch32(NotEqual, matchPos, length));
move(matchPos, index);
@@ -1247,7 +1724,7 @@ class YarrGenerator : private DefaultMacroAssembler {
case PatternTerm::TypePatternCharacter:
switch (term->quantityType) {
case QuantifierFixedCount:
- if (term->quantityCount == 1)
+ if (term->quantityMaxCount == 1)
generatePatternCharacterOnce(opIndex);
else
generatePatternCharacterFixed(opIndex);
@@ -1264,7 +1741,7 @@ class YarrGenerator : private DefaultMacroAssembler {
case PatternTerm::TypeCharacterClass:
switch (term->quantityType) {
case QuantifierFixedCount:
- if (term->quantityCount == 1)
+ if (term->quantityMaxCount == 1)
generateCharacterClassOnce(opIndex);
else
generateCharacterClassFixed(opIndex);
@@ -1297,7 +1774,7 @@ class YarrGenerator : private DefaultMacroAssembler {
case PatternTerm::TypeParentheticalAssertion:
RELEASE_ASSERT_NOT_REACHED();
case PatternTerm::TypeBackReference:
- m_shouldFallBack = true;
+ m_failureReason = JITFailureReason::BackReference;
break;
case PatternTerm::TypeDotStarEnclosure:
generateDotStarEnclosure(opIndex);
@@ -1313,7 +1790,7 @@ class YarrGenerator : private DefaultMacroAssembler {
case PatternTerm::TypePatternCharacter:
switch (term->quantityType) {
case QuantifierFixedCount:
- if (term->quantityCount == 1)
+ if (term->quantityMaxCount == 1)
backtrackPatternCharacterOnce(opIndex);
else
backtrackPatternCharacterFixed(opIndex);
@@ -1330,7 +1807,7 @@ class YarrGenerator : private DefaultMacroAssembler {
case PatternTerm::TypeCharacterClass:
switch (term->quantityType) {
case QuantifierFixedCount:
- if (term->quantityCount == 1)
+ if (term->quantityMaxCount == 1)
backtrackCharacterClassOnce(opIndex);
else
backtrackCharacterClassFixed(opIndex);
@@ -1368,7 +1845,7 @@ class YarrGenerator : private DefaultMacroAssembler {
break;
case PatternTerm::TypeBackReference:
- m_shouldFallBack = true;
+ m_failureReason = JITFailureReason::BackReference;
break;
}
}
@@ -1419,7 +1896,7 @@ class YarrGenerator : private DefaultMacroAssembler {
// set as appropriate to this alternative.
op.m_reentry = label();
- m_checked += alternative->m_minimumSize;
+ m_checkedOffset += alternative->m_minimumSize;
break;
}
case OpBodyAlternativeNext:
@@ -1472,8 +1949,8 @@ class YarrGenerator : private DefaultMacroAssembler {
}
if (op.m_op == OpBodyAlternativeNext)
- m_checked += alternative->m_minimumSize;
- m_checked -= priorAlternative->m_minimumSize;
+ m_checkedOffset += alternative->m_minimumSize;
+ m_checkedOffset -= priorAlternative->m_minimumSize;
break;
}
@@ -1500,13 +1977,13 @@ class YarrGenerator : private DefaultMacroAssembler {
PatternDisjunction* disjunction = term->parentheses.disjunction;
// Calculate how much input we need to check for, and if non-zero check.
- op.m_checkAdjust = alternative->m_minimumSize;
+ op.m_checkAdjust = Checked<unsigned>(alternative->m_minimumSize);
if ((term->quantityType == QuantifierFixedCount) && (term->type != PatternTerm::TypeParentheticalAssertion))
op.m_checkAdjust -= disjunction->m_minimumSize;
if (op.m_checkAdjust)
- op.m_jumps.append(jumpIfNoAvailableInput(op.m_checkAdjust));
+ op.m_jumps.append(jumpIfNoAvailableInput(op.m_checkAdjust.unsafeGet()));
- m_checked += op.m_checkAdjust;
+ m_checkedOffset += op.m_checkAdjust;
break;
}
case OpSimpleNestedAlternativeNext:
@@ -1518,10 +1995,7 @@ class YarrGenerator : private DefaultMacroAssembler {
// In the non-simple case, store a 'return address' so we can backtrack correctly.
if (op.m_op == OpNestedAlternativeNext) {
unsigned parenthesesFrameLocation = term->frameLocation;
- unsigned alternativeFrameLocation = parenthesesFrameLocation;
- if (term->quantityType != QuantifierFixedCount)
- alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce;
- op.m_returnAddress = storeToFrameWithPatch(alternativeFrameLocation);
+ op.m_returnAddress = storeToFrameWithPatch(parenthesesFrameLocation + BackTrackInfoParentheses::returnAddressIndex());
}
if (term->quantityType != QuantifierFixedCount && !m_ops[op.m_previousOp].m_alternative->m_minimumSize) {
@@ -1554,11 +2028,11 @@ class YarrGenerator : private DefaultMacroAssembler {
if ((term->quantityType == QuantifierFixedCount) && (term->type != PatternTerm::TypeParentheticalAssertion))
op.m_checkAdjust -= disjunction->m_minimumSize;
if (op.m_checkAdjust)
- op.m_jumps.append(jumpIfNoAvailableInput(op.m_checkAdjust));
+ op.m_jumps.append(jumpIfNoAvailableInput(op.m_checkAdjust.unsafeGet()));
YarrOp& lastOp = m_ops[op.m_previousOp];
- m_checked -= lastOp.m_checkAdjust;
- m_checked += op.m_checkAdjust;
+ m_checkedOffset -= lastOp.m_checkAdjust;
+ m_checkedOffset += op.m_checkAdjust;
break;
}
case OpSimpleNestedAlternativeEnd:
@@ -1568,10 +2042,7 @@ class YarrGenerator : private DefaultMacroAssembler {
// In the non-simple case, store a 'return address' so we can backtrack correctly.
if (op.m_op == OpNestedAlternativeEnd) {
unsigned parenthesesFrameLocation = term->frameLocation;
- unsigned alternativeFrameLocation = parenthesesFrameLocation;
- if (term->quantityType != QuantifierFixedCount)
- alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce;
- op.m_returnAddress = storeToFrameWithPatch(alternativeFrameLocation);
+ op.m_returnAddress = storeToFrameWithPatch(parenthesesFrameLocation + BackTrackInfoParentheses::returnAddressIndex());
}
if (term->quantityType != QuantifierFixedCount && !m_ops[op.m_previousOp].m_alternative->m_minimumSize) {
@@ -1587,7 +2058,7 @@ class YarrGenerator : private DefaultMacroAssembler {
op.m_jumps.clear();
YarrOp& lastOp = m_ops[op.m_previousOp];
- m_checked -= lastOp.m_checkAdjust;
+ m_checkedOffset -= lastOp.m_checkAdjust;
break;
}
@@ -1599,7 +2070,7 @@ class YarrGenerator : private DefaultMacroAssembler {
PatternTerm* term = op.m_term;
unsigned parenthesesFrameLocation = term->frameLocation;
const RegisterID indexTemporary = regT0;
- ASSERT(term->quantityCount == 1);
+ ASSERT(term->quantityMaxCount == 1);
// Upon entry to a Greedy quantified set of parenthese store the index.
// We'll use this for two purposes:
@@ -1616,12 +2087,12 @@ class YarrGenerator : private DefaultMacroAssembler {
//
// FIXME: for capturing parens, could use the index in the capture array?
if (term->quantityType == QuantifierGreedy)
- storeToFrame(index, parenthesesFrameLocation);
+ storeToFrame(index, parenthesesFrameLocation + BackTrackInfoParenthesesOnce::beginIndex());
else if (term->quantityType == QuantifierNonGreedy) {
- storeToFrame(TrustedImm32(-1), parenthesesFrameLocation);
+ storeToFrame(TrustedImm32(-1), parenthesesFrameLocation + BackTrackInfoParenthesesOnce::beginIndex());
op.m_jumps.append(jump());
op.m_reentry = label();
- storeToFrame(index, parenthesesFrameLocation);
+ storeToFrame(index, parenthesesFrameLocation + BackTrackInfoParenthesesOnce::beginIndex());
}
// If the parenthese are capturing, store the starting index value to the
@@ -1631,12 +2102,12 @@ class YarrGenerator : private DefaultMacroAssembler {
// offsets only afterwards, at the point the results array is
// being accessed.
if (term->capture() && compileMode == IncludeSubpatterns) {
- int inputOffset = term->inputPosition - m_checked;
+ unsigned inputOffset = (m_checkedOffset - term->inputPosition).unsafeGet();
if (term->quantityType == QuantifierFixedCount)
- inputOffset -= term->parentheses.disjunction->m_minimumSize;
+ inputOffset += term->parentheses.disjunction->m_minimumSize;
if (inputOffset) {
move(index, indexTemporary);
- add32(Imm32(inputOffset), indexTemporary);
+ sub32(Imm32(inputOffset), indexTemporary);
setSubpatternStart(indexTemporary, term->parentheses.subpatternId);
} else
setSubpatternStart(index, term->parentheses.subpatternId);
@@ -1646,18 +2117,16 @@ class YarrGenerator : private DefaultMacroAssembler {
case OpParenthesesSubpatternOnceEnd: {
PatternTerm* term = op.m_term;
const RegisterID indexTemporary = regT0;
- ASSERT(term->quantityCount == 1);
+ ASSERT(term->quantityMaxCount == 1);
-#ifndef NDEBUG
// Runtime ASSERT to make sure that the nested alternative handled the
// "no input consumed" check.
- if (term->quantityType != QuantifierFixedCount && !term->parentheses.disjunction->m_minimumSize) {
+ if (!ASSERT_DISABLED && term->quantityType != QuantifierFixedCount && !term->parentheses.disjunction->m_minimumSize) {
Jump pastBreakpoint;
pastBreakpoint = branch32(NotEqual, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*)));
- breakpoint();
+ // ### abortWithReason(YARRNoInputConsumed);
pastBreakpoint.link(this);
}
-#endif
// If the parenthese are capturing, store the ending index value to the
// captures array, offsetting as necessary.
@@ -1666,10 +2135,10 @@ class YarrGenerator : private DefaultMacroAssembler {
// offsets only afterwards, at the point the results array is
// being accessed.
if (term->capture() && compileMode == IncludeSubpatterns) {
- int inputOffset = term->inputPosition - m_checked;
+ unsigned inputOffset = (m_checkedOffset - term->inputPosition).unsafeGet();
if (inputOffset) {
move(index, indexTemporary);
- add32(Imm32(inputOffset), indexTemporary);
+ sub32(Imm32(inputOffset), indexTemporary);
setSubpatternEnd(indexTemporary, term->parentheses.subpatternId);
} else
setSubpatternEnd(index, term->parentheses.subpatternId);
@@ -1691,7 +2160,7 @@ class YarrGenerator : private DefaultMacroAssembler {
case OpParenthesesSubpatternTerminalBegin: {
PatternTerm* term = op.m_term;
ASSERT(term->quantityType == QuantifierGreedy);
- ASSERT(term->quantityCount == quantifyInfinite);
+ ASSERT(term->quantityMaxCount == quantifyInfinite);
ASSERT(!term->capture());
// Upon entry set a label to loop back to.
@@ -1699,23 +2168,23 @@ class YarrGenerator : private DefaultMacroAssembler {
// Store the start index of the current match; we need to reject zero
// length matches.
- storeToFrame(index, term->frameLocation);
+ storeToFrame(index, term->frameLocation + BackTrackInfoParenthesesTerminal::beginIndex());
break;
}
case OpParenthesesSubpatternTerminalEnd: {
YarrOp& beginOp = m_ops[op.m_previousOp];
-#ifndef NDEBUG
- PatternTerm* term = op.m_term;
-
- // Runtime ASSERT to make sure that the nested alternative handled the
- // "no input consumed" check.
- Jump pastBreakpoint;
- pastBreakpoint = branch32(NotEqual, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*)));
- breakpoint();
- pastBreakpoint.link(this);
-#endif
+ if (!ASSERT_DISABLED) {
+ PatternTerm* term = op.m_term;
+
+ // Runtime ASSERT to make sure that the nested alternative handled the
+ // "no input consumed" check.
+ Jump pastBreakpoint;
+ pastBreakpoint = branch32(NotEqual, index, Address(stackPointerRegister, term->frameLocation * sizeof(void*)));
+ // ### abortWithReason(YARRNoInputConsumed);
+ pastBreakpoint.link(this);
+ }
- // We know that the match is non-zero, we can accept it and
+ // We know that the match is non-zero, we can accept it and
// loop back up to the head of the subpattern.
jump(beginOp.m_reentry);
@@ -1725,6 +2194,131 @@ class YarrGenerator : private DefaultMacroAssembler {
break;
}
+ // OpParenthesesSubpatternBegin/End
+ //
+ // These nodes support generic subpatterns.
+ case OpParenthesesSubpatternBegin: {
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ PatternTerm* term = op.m_term;
+ unsigned parenthesesFrameLocation = term->frameLocation;
+
+ // Upon entry to a Greedy quantified set of parenthese store the index.
+ // We'll use this for two purposes:
+ // - To indicate which iteration we are on of mathing the remainder of
+ // the expression after the parentheses - the first, including the
+ // match within the parentheses, or the second having skipped over them.
+ // - To check for empty matches, which must be rejected.
+ //
+ // At the head of a NonGreedy set of parentheses we'll immediately set the
+ // value on the stack to -1 (indicating a match skipping the subpattern),
+ // and plant a jump to the end. We'll also plant a label to backtrack to
+ // to reenter the subpattern later, with a store to set up index on the
+ // second iteration.
+ //
+ // FIXME: for capturing parens, could use the index in the capture array?
+ if (term->quantityType == QuantifierGreedy || term->quantityType == QuantifierNonGreedy) {
+ storeToFrame(TrustedImm32(0), parenthesesFrameLocation + BackTrackInfoParentheses::matchAmountIndex());
+ storeToFrame(TrustedImmPtr(nullptr), parenthesesFrameLocation + BackTrackInfoParentheses::parenContextHeadIndex());
+
+ if (term->quantityType == QuantifierNonGreedy) {
+ storeToFrame(TrustedImm32(-1), parenthesesFrameLocation + BackTrackInfoParentheses::beginIndex());
+ op.m_jumps.append(jump());
+ }
+
+ op.m_reentry = label();
+ RegisterID currParenContextReg = regT0;
+ RegisterID newParenContextReg = regT1;
+
+ loadFromFrame(parenthesesFrameLocation + BackTrackInfoParentheses::parenContextHeadIndex(), currParenContextReg);
+ allocateParenContext(newParenContextReg);
+ storePtr(currParenContextReg, newParenContextReg);
+ storeToFrame(newParenContextReg, parenthesesFrameLocation + BackTrackInfoParentheses::parenContextHeadIndex());
+ saveParenContext(newParenContextReg, regT2, term->parentheses.subpatternId, term->parentheses.lastSubpatternId, parenthesesFrameLocation);
+ storeToFrame(index, parenthesesFrameLocation + BackTrackInfoParentheses::beginIndex());
+ }
+
+ // If the parenthese are capturing, store the starting index value to the
+ // captures array, offsetting as necessary.
+ //
+ // FIXME: could avoid offsetting this value in JIT code, apply
+ // offsets only afterwards, at the point the results array is
+ // being accessed.
+ if (term->capture() && compileMode == IncludeSubpatterns) {
+ const RegisterID indexTemporary = regT0;
+ unsigned inputOffset = (m_checkedOffset - term->inputPosition).unsafeGet();
+ if (term->quantityType == QuantifierFixedCount)
+ inputOffset += term->parentheses.disjunction->m_minimumSize;
+ if (inputOffset) {
+ move(index, indexTemporary);
+ sub32(Imm32(inputOffset), indexTemporary);
+ setSubpatternStart(indexTemporary, term->parentheses.subpatternId);
+ } else
+ setSubpatternStart(index, term->parentheses.subpatternId);
+ }
+#else // !YARR_JIT_ALL_PARENS_EXPRESSIONS
+ RELEASE_ASSERT_NOT_REACHED();
+#endif
+ break;
+ }
+ case OpParenthesesSubpatternEnd: {
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ PatternTerm* term = op.m_term;
+ unsigned parenthesesFrameLocation = term->frameLocation;
+
+ // Runtime ASSERT to make sure that the nested alternative handled the
+ // "no input consumed" check.
+ if (!ASSERT_DISABLED && term->quantityType != QuantifierFixedCount && !term->parentheses.disjunction->m_minimumSize) {
+ Jump pastBreakpoint;
+ pastBreakpoint = branch32(NotEqual, index, Address(stackPointerRegister, parenthesesFrameLocation * sizeof(void*)));
+ // ### abortWithReason(YARRNoInputConsumed);
+ pastBreakpoint.link(this);
+ }
+
+ const RegisterID countTemporary = regT1;
+
+ YarrOp& beginOp = m_ops[op.m_previousOp];
+ loadFromFrame(parenthesesFrameLocation + BackTrackInfoParentheses::matchAmountIndex(), countTemporary);
+ add32(TrustedImm32(1), countTemporary);
+ storeToFrame(countTemporary, parenthesesFrameLocation + BackTrackInfoParentheses::matchAmountIndex());
+
+ // If the parenthese are capturing, store the ending index value to the
+ // captures array, offsetting as necessary.
+ //
+ // FIXME: could avoid offsetting this value in JIT code, apply
+ // offsets only afterwards, at the point the results array is
+ // being accessed.
+ if (term->capture() && compileMode == IncludeSubpatterns) {
+ const RegisterID indexTemporary = regT0;
+
+ unsigned inputOffset = (m_checkedOffset - term->inputPosition).unsafeGet();
+ if (inputOffset) {
+ move(index, indexTemporary);
+ sub32(Imm32(inputOffset), indexTemporary);
+ setSubpatternEnd(indexTemporary, term->parentheses.subpatternId);
+ } else
+ setSubpatternEnd(index, term->parentheses.subpatternId);
+ }
+
+ // If the parentheses are quantified Greedy then add a label to jump back
+ // to if get a failed match from after the parentheses. For NonGreedy
+ // parentheses, link the jump from before the subpattern to here.
+ if (term->quantityType == QuantifierGreedy) {
+ if (term->quantityMaxCount != quantifyInfinite)
+ branch32(Below, countTemporary, Imm32(term->quantityMaxCount.unsafeGet())).linkTo(beginOp.m_reentry, this);
+ else
+ jump(beginOp.m_reentry);
+
+ op.m_reentry = label();
+ } else if (term->quantityType == QuantifierNonGreedy) {
+ YarrOp& beginOp = m_ops[op.m_previousOp];
+ beginOp.m_jumps.link(this);
+ }
+#else // !YARR_JIT_ALL_PARENS_EXPRESSIONS
+ RELEASE_ASSERT_NOT_REACHED();
+#endif
+ break;
+ }
+
// OpParentheticalAssertionBegin/End
case OpParentheticalAssertionBegin: {
PatternTerm* term = op.m_term;
@@ -1732,14 +2326,14 @@ class YarrGenerator : private DefaultMacroAssembler {
// Store the current index - assertions should not update index, so
// we will need to restore it upon a successful match.
unsigned parenthesesFrameLocation = term->frameLocation;
- storeToFrame(index, parenthesesFrameLocation);
+ storeToFrame(index, parenthesesFrameLocation + BackTrackInfoParentheticalAssertion::beginIndex());
// Check
- op.m_checkAdjust = m_checked - term->inputPosition;
+ op.m_checkAdjust = m_checkedOffset - term->inputPosition;
if (op.m_checkAdjust)
- sub32(Imm32(op.m_checkAdjust), index);
+ sub32(Imm32(op.m_checkAdjust.unsafeGet()), index);
- m_checked -= op.m_checkAdjust;
+ m_checkedOffset -= op.m_checkAdjust;
break;
}
case OpParentheticalAssertionEnd: {
@@ -1747,7 +2341,7 @@ class YarrGenerator : private DefaultMacroAssembler {
// Restore the input index value.
unsigned parenthesesFrameLocation = term->frameLocation;
- loadFromFrame(parenthesesFrameLocation, index);
+ loadFromFrame(parenthesesFrameLocation + BackTrackInfoParentheticalAssertion::beginIndex(), index);
// If inverted, a successful match of the assertion must be treated
// as a failure, so jump to backtracking.
@@ -1757,15 +2351,13 @@ class YarrGenerator : private DefaultMacroAssembler {
}
YarrOp& lastOp = m_ops[op.m_previousOp];
- m_checked += lastOp.m_checkAdjust;
+ m_checkedOffset += lastOp.m_checkAdjust;
break;
}
case OpMatchFailed:
removeCallFrame();
- move(TrustedImmPtr((void*)WTF::notFound), returnRegister);
- move(TrustedImm32(0), returnRegister2);
- generateReturn();
+ generateFailReturn();
break;
}
@@ -1817,9 +2409,9 @@ class YarrGenerator : private DefaultMacroAssembler {
if (op.m_op == OpBodyAlternativeNext) {
PatternAlternative* priorAlternative = m_ops[op.m_previousOp].m_alternative;
- m_checked += priorAlternative->m_minimumSize;
+ m_checkedOffset += priorAlternative->m_minimumSize;
}
- m_checked -= alternative->m_minimumSize;
+ m_checkedOffset -= alternative->m_minimumSize;
// Is this the last alternative? If not, then if we backtrack to this point we just
// need to jump to try to match the next alternative.
@@ -1836,6 +2428,8 @@ class YarrGenerator : private DefaultMacroAssembler {
}
bool onceThrough = endOp.m_nextOp == notFound;
+
+ JumpList lastStickyAlternativeFailures;
// First, generate code to handle cases where we backtrack out of an attempted match
// of the last alternative. If this is a 'once through' set of alternatives then we
@@ -1851,43 +2445,49 @@ class YarrGenerator : private DefaultMacroAssembler {
&& (alternative->m_minimumSize > beginOp->m_alternative->m_minimumSize)
&& (alternative->m_minimumSize - beginOp->m_alternative->m_minimumSize == 1))
m_backtrackingState.linkTo(beginOp->m_reentry, this);
- else {
+ else if (m_pattern.sticky() && m_ops[op.m_nextOp].m_op == OpBodyAlternativeEnd) {
+ // It is a sticky pattern and the last alternative failed, jump to the end.
+ m_backtrackingState.takeBacktracksToJumpList(lastStickyAlternativeFailures, this);
+ } else {
// We need to generate a trampoline of code to execute before looping back
// around to the first alternative.
m_backtrackingState.link(this);
- // If the pattern size is not fixed, then store the start index, for use if we match.
- if (!m_pattern.m_body->m_hasFixedSize) {
- if (alternative->m_minimumSize == 1)
- setMatchStart(index);
- else {
- move(index, regT0);
- if (alternative->m_minimumSize)
- sub32(Imm32(alternative->m_minimumSize - 1), regT0);
- else
- add32(TrustedImm32(1), regT0);
- setMatchStart(regT0);
+ // No need to advance and retry for a sticky pattern.
+ if (!m_pattern.sticky()) {
+ // If the pattern size is not fixed, then store the start index for use if we match.
+ if (!m_pattern.m_body->m_hasFixedSize) {
+ if (alternative->m_minimumSize == 1)
+ setMatchStart(index);
+ else {
+ move(index, regT0);
+ if (alternative->m_minimumSize)
+ sub32(Imm32(alternative->m_minimumSize - 1), regT0);
+ else
+ add32(TrustedImm32(1), regT0);
+ setMatchStart(regT0);
+ }
}
- }
- // Generate code to loop. Check whether the last alternative is longer than the
- // first (e.g. /a|xy/ or /a|xyz/).
- if (alternative->m_minimumSize > beginOp->m_alternative->m_minimumSize) {
- // We want to loop, and increment input position. If the delta is 1, it is
- // already correctly incremented, if more than one then decrement as appropriate.
- unsigned delta = alternative->m_minimumSize - beginOp->m_alternative->m_minimumSize;
- ASSERT(delta);
- if (delta != 1)
- sub32(Imm32(delta - 1), index);
- jump(beginOp->m_reentry);
- } else {
- // If the first alternative has minimum size 0xFFFFFFFFu, then there cannot
- // be sufficent input available to handle this, so just fall through.
- unsigned delta = beginOp->m_alternative->m_minimumSize - alternative->m_minimumSize;
- if (delta != 0xFFFFFFFFu) {
- // We need to check input because we are incrementing the input.
- add32(Imm32(delta + 1), index);
- checkInput().linkTo(beginOp->m_reentry, this);
+ // Generate code to loop. Check whether the last alternative is longer than the
+ // first (e.g. /a|xy/ or /a|xyz/).
+ if (alternative->m_minimumSize > beginOp->m_alternative->m_minimumSize) {
+ // We want to loop, and increment input position. If the delta is 1, it is
+ // already correctly incremented, if more than one then decrement as appropriate.
+ unsigned delta = alternative->m_minimumSize - beginOp->m_alternative->m_minimumSize;
+ ASSERT(delta);
+ if (delta != 1)
+ sub32(Imm32(delta - 1), index);
+ jump(beginOp->m_reentry);
+ } else {
+ // If the first alternative has minimum size 0xFFFFFFFFu, then there cannot
+ // be sufficent input available to handle this, so just fall through.
+ unsigned delta = beginOp->m_alternative->m_minimumSize - alternative->m_minimumSize;
+ if (delta != 0xFFFFFFFFu) {
+ // We need to check input because we are incrementing the input.
+ add32(Imm32(delta + 1), index);
+ checkInput().linkTo(beginOp->m_reentry, this);
+ }
}
}
}
@@ -1896,7 +2496,7 @@ class YarrGenerator : private DefaultMacroAssembler {
// We can reach this point in the code in two ways:
// - Fallthrough from the code above (a repeating alternative backtracked out of its
// last alternative, and did not have sufficent input to run the first).
- // - We will loop back up to the following label when a releating alternative loops,
+ // - We will loop back up to the following label when a repeating alternative loops,
// following a failed input check.
//
// Either way, we have just failed the input check for the first alternative.
@@ -1956,56 +2556,57 @@ class YarrGenerator : private DefaultMacroAssembler {
needsToUpdateMatchStart = false;
}
- // Check whether there is sufficient input to loop. Increment the input position by
- // one, and check. Also add in the minimum disjunction size before checking - there
- // is no point in looping if we're just going to fail all the input checks around
- // the next iteration.
- ASSERT(alternative->m_minimumSize >= m_pattern.m_body->m_minimumSize);
- if (alternative->m_minimumSize == m_pattern.m_body->m_minimumSize) {
- // If the last alternative had the same minimum size as the disjunction,
- // just simply increment input pos by 1, no adjustment based on minimum size.
- add32(TrustedImm32(1), index);
- } else {
- // If the minumum for the last alternative was one greater than than that
- // for the disjunction, we're already progressed by 1, nothing to do!
- unsigned delta = (alternative->m_minimumSize - m_pattern.m_body->m_minimumSize) - 1;
- if (delta)
- sub32(Imm32(delta), index);
- }
- Jump matchFailed = jumpIfNoAvailableInput();
+ if (!m_pattern.sticky()) {
+ // Check whether there is sufficient input to loop. Increment the input position by
+ // one, and check. Also add in the minimum disjunction size before checking - there
+ // is no point in looping if we're just going to fail all the input checks around
+ // the next iteration.
+ ASSERT(alternative->m_minimumSize >= m_pattern.m_body->m_minimumSize);
+ if (alternative->m_minimumSize == m_pattern.m_body->m_minimumSize) {
+ // If the last alternative had the same minimum size as the disjunction,
+ // just simply increment input pos by 1, no adjustment based on minimum size.
+ add32(TrustedImm32(1), index);
+ } else {
+ // If the minumum for the last alternative was one greater than than that
+ // for the disjunction, we're already progressed by 1, nothing to do!
+ unsigned delta = (alternative->m_minimumSize - m_pattern.m_body->m_minimumSize) - 1;
+ if (delta)
+ sub32(Imm32(delta), index);
+ }
+ Jump matchFailed = jumpIfNoAvailableInput();
+
+ if (needsToUpdateMatchStart) {
+ if (!m_pattern.m_body->m_minimumSize)
+ setMatchStart(index);
+ else {
+ move(index, regT0);
+ sub32(Imm32(m_pattern.m_body->m_minimumSize), regT0);
+ setMatchStart(regT0);
+ }
+ }
- if (needsToUpdateMatchStart) {
- if (!m_pattern.m_body->m_minimumSize)
- setMatchStart(index);
+ // Calculate how much more input the first alternative requires than the minimum
+ // for the body as a whole. If no more is needed then we dont need an additional
+ // input check here - jump straight back up to the start of the first alternative.
+ if (beginOp->m_alternative->m_minimumSize == m_pattern.m_body->m_minimumSize)
+ jump(beginOp->m_reentry);
else {
- move(index, regT0);
- sub32(Imm32(m_pattern.m_body->m_minimumSize), regT0);
- setMatchStart(regT0);
+ if (beginOp->m_alternative->m_minimumSize > m_pattern.m_body->m_minimumSize)
+ add32(Imm32(beginOp->m_alternative->m_minimumSize - m_pattern.m_body->m_minimumSize), index);
+ else
+ sub32(Imm32(m_pattern.m_body->m_minimumSize - beginOp->m_alternative->m_minimumSize), index);
+ checkInput().linkTo(beginOp->m_reentry, this);
+ jump(firstInputCheckFailed);
}
- }
- // Calculate how much more input the first alternative requires than the minimum
- // for the body as a whole. If no more is needed then we dont need an additional
- // input check here - jump straight back up to the start of the first alternative.
- if (beginOp->m_alternative->m_minimumSize == m_pattern.m_body->m_minimumSize)
- jump(beginOp->m_reentry);
- else {
- if (beginOp->m_alternative->m_minimumSize > m_pattern.m_body->m_minimumSize)
- add32(Imm32(beginOp->m_alternative->m_minimumSize - m_pattern.m_body->m_minimumSize), index);
- else
- sub32(Imm32(m_pattern.m_body->m_minimumSize - beginOp->m_alternative->m_minimumSize), index);
- checkInput().linkTo(beginOp->m_reentry, this);
- jump(firstInputCheckFailed);
+ // We jump to here if we iterate to the point that there is insufficient input to
+ // run any matches, and need to return a failure state from JIT code.
+ matchFailed.link(this);
}
- // We jump to here if we iterate to the point that there is insufficient input to
- // run any matches, and need to return a failure state from JIT code.
- matchFailed.link(this);
-
+ lastStickyAlternativeFailures.link(this);
removeCallFrame();
- move(TrustedImmPtr((void*)WTF::notFound), returnRegister);
- move(TrustedImm32(0), returnRegister2);
- generateReturn();
+ generateFailReturn();
break;
}
case OpBodyAlternativeEnd: {
@@ -2013,7 +2614,7 @@ class YarrGenerator : private DefaultMacroAssembler {
ASSERT(m_backtrackingState.isEmpty());
PatternAlternative* priorAlternative = m_ops[op.m_previousOp].m_alternative;
- m_checked += priorAlternative->m_minimumSize;
+ m_checkedOffset += priorAlternative->m_minimumSize;
break;
}
@@ -2064,7 +2665,7 @@ class YarrGenerator : private DefaultMacroAssembler {
if (op.m_checkAdjust) {
// Handle the cases where we need to link the backtracks here.
m_backtrackingState.link(this);
- sub32(Imm32(op.m_checkAdjust), index);
+ sub32(Imm32(op.m_checkAdjust.unsafeGet()), index);
if (!isLastAlternative) {
// An alternative that is not the last should jump to its successor.
jump(nextOp.m_reentry);
@@ -2114,9 +2715,9 @@ class YarrGenerator : private DefaultMacroAssembler {
if (!isBegin) {
YarrOp& lastOp = m_ops[op.m_previousOp];
- m_checked += lastOp.m_checkAdjust;
+ m_checkedOffset += lastOp.m_checkAdjust;
}
- m_checked -= op.m_checkAdjust;
+ m_checkedOffset -= op.m_checkAdjust;
break;
}
case OpSimpleNestedAlternativeEnd:
@@ -2136,10 +2737,7 @@ class YarrGenerator : private DefaultMacroAssembler {
// Plant a jump to the return address.
unsigned parenthesesFrameLocation = term->frameLocation;
- unsigned alternativeFrameLocation = parenthesesFrameLocation;
- if (term->quantityType != QuantifierFixedCount)
- alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce;
- loadFromFrameAndJump(alternativeFrameLocation);
+ loadFromFrameAndJump(parenthesesFrameLocation + BackTrackInfoParentheses::returnAddressIndex());
// Link the DataLabelPtr associated with the end of the last
// alternative to this point.
@@ -2147,7 +2745,7 @@ class YarrGenerator : private DefaultMacroAssembler {
}
YarrOp& lastOp = m_ops[op.m_previousOp];
- m_checked += lastOp.m_checkAdjust;
+ m_checkedOffset += lastOp.m_checkAdjust;
break;
}
@@ -2168,9 +2766,9 @@ class YarrGenerator : private DefaultMacroAssembler {
// matching start, depending of whether the match is Greedy or NonGreedy.
case OpParenthesesSubpatternOnceBegin: {
PatternTerm* term = op.m_term;
- ASSERT(term->quantityCount == 1);
+ ASSERT(term->quantityMaxCount == 1);
- // We only need to backtrack to thispoint if capturing or greedy.
+ // We only need to backtrack to this point if capturing or greedy.
if ((term->capture() && compileMode == IncludeSubpatterns) || term->quantityType == QuantifierGreedy) {
m_backtrackingState.link(this);
@@ -2182,7 +2780,7 @@ class YarrGenerator : private DefaultMacroAssembler {
if (term->quantityType == QuantifierGreedy) {
// Clear the flag in the stackframe indicating we ran through the subpattern.
unsigned parenthesesFrameLocation = term->frameLocation;
- storeToFrame(TrustedImm32(-1), parenthesesFrameLocation);
+ storeToFrame(TrustedImm32(-1), parenthesesFrameLocation + BackTrackInfoParenthesesOnce::beginIndex());
// Jump to after the parentheses, skipping the subpattern.
jump(m_ops[op.m_nextOp].m_reentry);
// A backtrack from after the parentheses, when skipping the subpattern,
@@ -2204,7 +2802,7 @@ class YarrGenerator : private DefaultMacroAssembler {
// are currently in a state where we had skipped over the subpattern
// (in which case the flag value on the stack will be -1).
unsigned parenthesesFrameLocation = term->frameLocation;
- Jump hadSkipped = branch32(Equal, Address(stackPointerRegister, parenthesesFrameLocation * sizeof(void*)), TrustedImm32(-1));
+ Jump hadSkipped = branch32(Equal, Address(stackPointerRegister, (parenthesesFrameLocation + BackTrackInfoParenthesesOnce::beginIndex()) * sizeof(void*)), TrustedImm32(-1));
if (term->quantityType == QuantifierGreedy) {
// For Greedy parentheses, we skip after having already tried going
@@ -2248,6 +2846,108 @@ class YarrGenerator : private DefaultMacroAssembler {
m_backtrackingState.append(op.m_jumps);
break;
+ // OpParenthesesSubpatternBegin/End
+ //
+ // When we are backtracking back out of a capturing subpattern we need
+ // to clear the start index in the matches output array, to record that
+ // this subpattern has not been captured.
+ //
+ // When backtracking back out of a Greedy quantified subpattern we need
+ // to catch this, and try running the remainder of the alternative after
+ // the subpattern again, skipping the parentheses.
+ //
+ // Upon backtracking back into a quantified set of parentheses we need to
+ // check whether we were currently skipping the subpattern. If not, we
+ // can backtrack into them, if we were we need to either backtrack back
+ // out of the start of the parentheses, or jump back to the forwards
+ // matching start, depending of whether the match is Greedy or NonGreedy.
+ case OpParenthesesSubpatternBegin: {
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ PatternTerm* term = op.m_term;
+ unsigned parenthesesFrameLocation = term->frameLocation;
+
+ if (term->quantityType != QuantifierFixedCount) {
+ m_backtrackingState.link(this);
+
+ if (term->quantityType == QuantifierGreedy) {
+ RegisterID currParenContextReg = regT0;
+ RegisterID newParenContextReg = regT1;
+
+ loadFromFrame(parenthesesFrameLocation + BackTrackInfoParentheses::parenContextHeadIndex(), currParenContextReg);
+
+ restoreParenContext(currParenContextReg, regT2, term->parentheses.subpatternId, term->parentheses.lastSubpatternId, parenthesesFrameLocation);
+
+ freeParenContext(currParenContextReg, newParenContextReg);
+ storeToFrame(newParenContextReg, parenthesesFrameLocation + BackTrackInfoParentheses::parenContextHeadIndex());
+ const RegisterID countTemporary = regT0;
+ loadFromFrame(parenthesesFrameLocation + BackTrackInfoParentheses::matchAmountIndex(), countTemporary);
+ Jump zeroLengthMatch = branchTest32(Zero, countTemporary);
+
+ sub32(TrustedImm32(1), countTemporary);
+ storeToFrame(countTemporary, parenthesesFrameLocation + BackTrackInfoParentheses::matchAmountIndex());
+
+ jump(m_ops[op.m_nextOp].m_reentry);
+
+ zeroLengthMatch.link(this);
+
+ // Clear the flag in the stackframe indicating we didn't run through the subpattern.
+ storeToFrame(TrustedImm32(-1), parenthesesFrameLocation + BackTrackInfoParentheses::beginIndex());
+
+ jump(m_ops[op.m_nextOp].m_reentry);
+ }
+
+ // If Greedy, jump to the end.
+ if (term->quantityType == QuantifierGreedy) {
+ // A backtrack from after the parentheses, when skipping the subpattern,
+ // will jump back to here.
+ op.m_jumps.link(this);
+ }
+
+ m_backtrackingState.fallthrough();
+ }
+#else // !YARR_JIT_ALL_PARENS_EXPRESSIONS
+ RELEASE_ASSERT_NOT_REACHED();
+#endif
+ break;
+ }
+ case OpParenthesesSubpatternEnd: {
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ PatternTerm* term = op.m_term;
+
+ if (term->quantityType != QuantifierFixedCount) {
+ m_backtrackingState.link(this);
+
+ // Check whether we should backtrack back into the parentheses, or if we
+ // are currently in a state where we had skipped over the subpattern
+ // (in which case the flag value on the stack will be -1).
+ unsigned parenthesesFrameLocation = term->frameLocation;
+ Jump hadSkipped = branch32(Equal, Address(stackPointerRegister, (parenthesesFrameLocation + BackTrackInfoParentheses::beginIndex()) * sizeof(void*)), TrustedImm32(-1));
+
+ if (term->quantityType == QuantifierGreedy) {
+ // For Greedy parentheses, we skip after having already tried going
+ // through the subpattern, so if we get here we're done.
+ YarrOp& beginOp = m_ops[op.m_previousOp];
+ beginOp.m_jumps.append(hadSkipped);
+ } else {
+ // For NonGreedy parentheses, we try skipping the subpattern first,
+ // so if we get here we need to try running through the subpattern
+ // next. Jump back to the start of the parentheses in the forwards
+ // matching path.
+ ASSERT(term->quantityType == QuantifierNonGreedy);
+ YarrOp& beginOp = m_ops[op.m_previousOp];
+ hadSkipped.linkTo(beginOp.m_reentry, this);
+ }
+
+ m_backtrackingState.fallthrough();
+ }
+
+ m_backtrackingState.append(op.m_jumps);
+#else // !YARR_JIT_ALL_PARENS_EXPRESSIONS
+ RELEASE_ASSERT_NOT_REACHED();
+#endif
+ break;
+ }
+
// OpParentheticalAssertionBegin/End
case OpParentheticalAssertionBegin: {
PatternTerm* term = op.m_term;
@@ -2260,7 +2960,7 @@ class YarrGenerator : private DefaultMacroAssembler {
m_backtrackingState.link(this);
if (op.m_checkAdjust)
- add32(Imm32(op.m_checkAdjust), index);
+ add32(Imm32(op.m_checkAdjust.unsafeGet()), index);
// In an inverted assertion failure to match the subpattern
// is treated as a successful match - jump to the end of the
@@ -2277,7 +2977,7 @@ class YarrGenerator : private DefaultMacroAssembler {
// added the failure caused by a successful match to this.
m_backtrackingState.append(endOp.m_jumps);
- m_checked += op.m_checkAdjust;
+ m_checkedOffset += op.m_checkAdjust;
break;
}
case OpParentheticalAssertionEnd: {
@@ -2289,7 +2989,7 @@ class YarrGenerator : private DefaultMacroAssembler {
m_backtrackingState.takeBacktracksToJumpList(op.m_jumps, this);
YarrOp& lastOp = m_ops[op.m_previousOp];
- m_checked -= lastOp.m_checkAdjust;
+ m_checkedOffset -= lastOp.m_checkAdjust;
break;
}
@@ -2307,9 +3007,9 @@ class YarrGenerator : private DefaultMacroAssembler {
// Emits ops for a subpattern (set of parentheses). These consist
// of a set of alternatives wrapped in an outer set of nodes for
// the parentheses.
- // Supported types of parentheses are 'Once' (quantityCount == 1)
- // and 'Terminal' (non-capturing parentheses quantified as greedy
- // and infinite).
+ // Supported types of parentheses are 'Once' (quantityMaxCount == 1),
+ // 'Terminal' (non-capturing parentheses quantified as greedy
+ // and infinite), and 0 based greedy quantified parentheses.
// Alternatives will use the 'Simple' set of ops if either the
// subpattern is terminal (in which case we will never need to
// backtrack), or if the subpattern only contains one alternative.
@@ -2328,7 +3028,10 @@ class YarrGenerator : private DefaultMacroAssembler {
// comes where the subpattern is capturing, in which case we would
// need to restore the capture from the first subpattern upon a
// failure in the second.
- if (term->quantityCount == 1 && !term->parentheses.isCopy) {
+ if (term->quantityMinCount && term->quantityMinCount != term->quantityMaxCount) {
+ m_failureReason = JITFailureReason::VariableCountedParenthesisWithNonZeroMinimum;
+ return;
+ } if (term->quantityMaxCount == 1 && !term->parentheses.isCopy) {
// Select the 'Once' nodes.
parenthesesBeginOpCode = OpParenthesesSubpatternOnceBegin;
parenthesesEndOpCode = OpParenthesesSubpatternOnceEnd;
@@ -2344,9 +3047,31 @@ class YarrGenerator : private DefaultMacroAssembler {
parenthesesBeginOpCode = OpParenthesesSubpatternTerminalBegin;
parenthesesEndOpCode = OpParenthesesSubpatternTerminalEnd;
} else {
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ // We only handle generic parenthesis with greedy counts.
+ if (term->quantityType != QuantifierGreedy) {
+ // This subpattern is not supported by the JIT.
+ m_failureReason = JITFailureReason::NonGreedyParenthesizedSubpattern;
+ return;
+ }
+
+ m_containsNestedSubpatterns = true;
+
+ // Select the 'Generic' nodes.
+ parenthesesBeginOpCode = OpParenthesesSubpatternBegin;
+ parenthesesEndOpCode = OpParenthesesSubpatternEnd;
+
+ // If there is more than one alternative we cannot use the 'simple' nodes.
+ if (term->parentheses.disjunction->m_alternatives.size() != 1) {
+ alternativeBeginOpCode = OpNestedAlternativeBegin;
+ alternativeNextOpCode = OpNestedAlternativeNext;
+ alternativeEndOpCode = OpNestedAlternativeEnd;
+ }
+#else
// This subpattern is not supported by the JIT.
- m_shouldFallBack = true;
+ m_failureReason = JITFailureReason::ParenthesizedSubpattern;
return;
+#endif
}
size_t parenBegin = m_ops.size();
@@ -2355,7 +3080,7 @@ class YarrGenerator : private DefaultMacroAssembler {
m_ops.append(alternativeBeginOpCode);
m_ops.last().m_previousOp = notFound;
m_ops.last().m_term = term;
- Vector<OwnPtr<PatternAlternative> >& alternatives = term->parentheses.disjunction->m_alternatives;
+ Vector<std::unique_ptr<PatternAlternative>>& alternatives = term->parentheses.disjunction->m_alternatives;
for (unsigned i = 0; i < alternatives.size(); ++i) {
size_t lastOpIndex = m_ops.size() - 1;
@@ -2406,7 +3131,7 @@ class YarrGenerator : private DefaultMacroAssembler {
m_ops.append(OpSimpleNestedAlternativeBegin);
m_ops.last().m_previousOp = notFound;
m_ops.last().m_term = term;
- Vector<OwnPtr<PatternAlternative> >& alternatives = term->parentheses.disjunction->m_alternatives;
+ Vector<std::unique_ptr<PatternAlternative>>& alternatives = term->parentheses.disjunction->m_alternatives;
for (unsigned i = 0; i < alternatives.size(); ++i) {
size_t lastOpIndex = m_ops.size() - 1;
@@ -2480,7 +3205,7 @@ class YarrGenerator : private DefaultMacroAssembler {
// to return the failing result.
void opCompileBody(PatternDisjunction* disjunction)
{
- Vector<OwnPtr<PatternAlternative> >& alternatives = disjunction->m_alternatives;
+ Vector<std::unique_ptr<PatternAlternative>>& alternatives = disjunction->m_alternatives;
size_t currentAlternativeIndex = 0;
// Emit the 'once through' alternatives.
@@ -2548,18 +3273,59 @@ class YarrGenerator : private DefaultMacroAssembler {
lastOp.m_nextOp = repeatLoop;
}
+ void generateTryReadUnicodeCharacterHelper()
+ {
+#ifdef JIT_UNICODE_EXPRESSIONS
+ if (m_tryReadUnicodeCharacterCalls.isEmpty())
+ return;
+
+ ASSERT(m_decodeSurrogatePairs);
+
+ m_tryReadUnicodeCharacterEntry = label();
+
+ tryReadUnicodeCharImpl(regT0);
+
+ ret();
+#endif
+ }
+
void generateEnter()
{
#if CPU(X86_64)
push(X86Registers::ebp);
move(stackPointerRegister, X86Registers::ebp);
- push(X86Registers::ebx);
+
+ if (m_pattern.m_saveInitialStartValue)
+ push(X86Registers::ebx);
+
+#if OS(WINDOWS)
+ push(X86Registers::edi);
+#endif
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ if (m_containsNestedSubpatterns) {
+#if OS(WINDOWS)
+ push(X86Registers::esi);
+#endif
+ push(X86Registers::r12);
+ }
+#endif
+
+ if (m_decodeSurrogatePairs) {
+ push(X86Registers::r13);
+ push(X86Registers::r14);
+ push(X86Registers::r15);
+
+ move(TrustedImm32(0xd800), leadingSurrogateTag);
+ move(TrustedImm32(0xdc00), trailingSurrogateTag);
+ }
// The ABI doesn't guarantee the upper bits are zero on unsigned arguments, so clear them ourselves.
zeroExtend32ToPtr(index, index);
zeroExtend32ToPtr(length, length);
#if OS(WINDOWS)
if (compileMode == IncludeSubpatterns)
loadPtr(Address(X86Registers::ebp, 6 * sizeof(void*)), output);
+ // rcx is the pointer to the allocated space for result in x64 Windows.
+ push(X86Registers::ecx);
#endif
#elif CPU(X86)
push(X86Registers::ebp);
@@ -2580,6 +3346,14 @@ class YarrGenerator : private DefaultMacroAssembler {
loadPtr(Address(X86Registers::ebp, 2 * sizeof(void*)), output);
#endif
#elif CPU(ARM64)
+ if (m_decodeSurrogatePairs) {
+ pushPair(framePointerRegister, linkRegister);
+ move(TrustedImm32(0x10000), supplementaryPlanesBase);
+ move(TrustedImm32(0xfffffc00), surrogateTagMask);
+ move(TrustedImm32(0xd800), leadingSurrogateTag);
+ move(TrustedImm32(0xdc00), trailingSurrogateTag);
+ }
+
// The ABI doesn't guarantee the upper bits are zero on unsigned arguments, so clear them ourselves.
zeroExtend32ToPtr(index, index);
zeroExtend32ToPtr(length, length);
@@ -2587,45 +3361,60 @@ class YarrGenerator : private DefaultMacroAssembler {
push(ARMRegisters::r4);
push(ARMRegisters::r5);
push(ARMRegisters::r6);
-#if CPU(ARM_TRADITIONAL)
- push(ARMRegisters::r8); // scratch register
-#endif
- if (compileMode == IncludeSubpatterns)
- move(ARMRegisters::r3, output);
-#elif CPU(SH4)
- push(SH4Registers::r11);
- push(SH4Registers::r13);
+ push(ARMRegisters::r8);
#elif CPU(MIPS)
// Do nothing.
#endif
+
+ store8(TrustedImm32(1), &m_vm->isExecutingInRegExpJIT);
}
void generateReturn()
{
+ store8(TrustedImm32(0), &m_vm->isExecutingInRegExpJIT);
+
#if CPU(X86_64)
#if OS(WINDOWS)
// Store the return value in the allocated space pointed by rcx.
+ pop(X86Registers::ecx);
store64(returnRegister, Address(X86Registers::ecx));
store64(returnRegister2, Address(X86Registers::ecx, sizeof(void*)));
move(X86Registers::ecx, returnRegister);
#endif
- pop(X86Registers::ebx);
+ if (m_decodeSurrogatePairs) {
+ pop(X86Registers::r15);
+ pop(X86Registers::r14);
+ pop(X86Registers::r13);
+ }
+
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ if (m_containsNestedSubpatterns) {
+ pop(X86Registers::r12);
+#if OS(WINDOWS)
+ pop(X86Registers::esi);
+#endif
+ }
+#endif
+#if OS(WINDOWS)
+ pop(X86Registers::edi);
+#endif
+
+ if (m_pattern.m_saveInitialStartValue)
+ pop(X86Registers::ebx);
pop(X86Registers::ebp);
#elif CPU(X86)
pop(X86Registers::esi);
pop(X86Registers::edi);
pop(X86Registers::ebx);
pop(X86Registers::ebp);
+#elif CPU(ARM64)
+ if (m_decodeSurrogatePairs)
+ popPair(framePointerRegister, linkRegister);
#elif CPU(ARM)
-#if CPU(ARM_TRADITIONAL)
- pop(ARMRegisters::r8); // scratch register
-#endif
+ pop(ARMRegisters::r8);
pop(ARMRegisters::r6);
pop(ARMRegisters::r5);
pop(ARMRegisters::r4);
-#elif CPU(SH4)
- pop(SH4Registers::r13);
- pop(SH4Registers::r11);
#elif CPU(MIPS)
// Do nothing
#endif
@@ -2633,25 +3422,57 @@ class YarrGenerator : private DefaultMacroAssembler {
}
public:
- YarrGenerator(YarrPattern& pattern, YarrCharSize charSize)
- : m_pattern(pattern)
+ YarrGenerator(VM* vm, YarrPattern& pattern, YarrCodeBlock& codeBlock, YarrCharSize charSize)
+ : m_vm(vm)
+ , m_pattern(pattern)
+ , m_codeBlock(codeBlock)
, m_charSize(charSize)
- , m_charScale(m_charSize == Char8 ? TimesOne: TimesTwo)
- , m_shouldFallBack(false)
- , m_checked(0)
+ , m_decodeSurrogatePairs(m_charSize == Char16 && m_pattern.unicode())
+ , m_unicodeIgnoreCase(m_pattern.unicode() && m_pattern.ignoreCase())
+ , m_canonicalMode(m_pattern.unicode() ? CanonicalMode::Unicode : CanonicalMode::UCS2)
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ , m_containsNestedSubpatterns(false)
+ , m_parenContextSizes(compileMode == IncludeSubpatterns ? m_pattern.m_numSubpatterns : 0, m_pattern.m_body->m_callFrameSize)
+#endif
{
}
- void compile(JSGlobalData* globalData, YarrCodeBlock& jitObject)
+ void compile()
{
+ YarrCodeBlock& codeBlock = m_codeBlock;
+
+#ifndef JIT_UNICODE_EXPRESSIONS
+ if (m_decodeSurrogatePairs) {
+ codeBlock.setFallBackWithFailureReason(JITFailureReason::DecodeSurrogatePair);
+ return;
+ }
+#endif
+
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ if (m_containsNestedSubpatterns)
+ codeBlock.setUsesPaternContextBuffer();
+#endif
+
+ // We need to compile before generating code since we set flags based on compilation that
+ // are used during generation.
+ opCompileBody(m_pattern.m_body);
+
+ if (m_failureReason) {
+ codeBlock.setFallBackWithFailureReason(*m_failureReason);
+ return;
+ }
+
generateEnter();
Jump hasInput = checkInput();
- move(TrustedImmPtr((void*)WTF::notFound), returnRegister);
- move(TrustedImm32(0), returnRegister2);
- generateReturn();
+ generateFailReturn();
hasInput.link(this);
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ if (m_containsNestedSubpatterns)
+ move(TrustedImm32(matchLimit), remainingMatchCount);
+#endif
+
if (compileMode == IncludeSubpatterns) {
for (unsigned i = 0; i < m_pattern.m_numSubpatterns + 1; ++i)
store32(TrustedImm32(-1), Address(output, (i << 1) * sizeof(int)));
@@ -2662,47 +3483,80 @@ public:
initCallFrame();
- // Compile the pattern to the internal 'YarrOp' representation.
- opCompileBody(m_pattern.m_body);
-
- // If we encountered anything we can't handle in the JIT code
- // (e.g. backreferences) then return early.
- if (m_shouldFallBack) {
- jitObject.setFallBack(true);
- return;
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ if (m_containsNestedSubpatterns)
+ initParenContextFreeList();
+#endif
+
+ if (m_pattern.m_saveInitialStartValue) {
+#ifdef HAVE_INITIAL_START_REG
+ move(index, initialStart);
+#else
+ storeToFrame(index, m_pattern.m_initialStartValueFrameLocation);
+#endif
}
generate();
backtrack();
- // Link & finalize the code.
- LinkBuffer<JSC::DefaultMacroAssembler> linkBuffer(*globalData, this, REGEXP_CODE_ID);
+ generateTryReadUnicodeCharacterHelper();
+
+ generateJITFailReturn();
+
+ JSGlobalData data(m_vm->regExpAllocator);
+ DefaultLinkBuffer linkBuffer(data, this, REGEXP_CODE_ID, JITCompilationCanFail);
+ if (linkBuffer.didFailToAllocate()) {
+ codeBlock.setFallBackWithFailureReason(JITFailureReason::ExecutableMemoryAllocationFailure);
+ return;
+ }
+
+ if (!m_tryReadUnicodeCharacterCalls.isEmpty()) {
+ CodeLocationLabel tryReadUnicodeCharacterHelper = linkBuffer.locationOf(m_tryReadUnicodeCharacterEntry);
+
+ for (auto call : m_tryReadUnicodeCharacterCalls)
+ linkBuffer.link(call, tryReadUnicodeCharacterHelper);
+ }
+
m_backtrackingState.linkDataLabels(linkBuffer);
if (compileMode == MatchOnly) {
if (m_charSize == Char8)
- jitObject.set8BitCodeMatchOnly(FINALIZE_CODE(linkBuffer, ("Match-only 8-bit regular expression")));
+ codeBlock.set8BitCodeMatchOnly(FINALIZE_CODE(linkBuffer, "YarJIT", "Match-only 8-bit regular expression"));
else
- jitObject.set16BitCodeMatchOnly(FINALIZE_CODE(linkBuffer, ("Match-only 16-bit regular expression")));
+ codeBlock.set16BitCodeMatchOnly(FINALIZE_CODE(linkBuffer, "YarJIT", "Match-only 16-bit regular expression"));
} else {
if (m_charSize == Char8)
- jitObject.set8BitCode(FINALIZE_CODE(linkBuffer, ("8-bit regular expression")));
+ codeBlock.set8BitCode(FINALIZE_CODE(linkBuffer, "YarJIT", "8-bit regular expression"));
else
- jitObject.set16BitCode(FINALIZE_CODE(linkBuffer, ("16-bit regular expression")));
+ codeBlock.set16BitCode(FINALIZE_CODE(linkBuffer, "YarJIT", "16-bit regular expression"));
}
- jitObject.setFallBack(m_shouldFallBack);
+ if (m_failureReason)
+ codeBlock.setFallBackWithFailureReason(*m_failureReason);
}
private:
+ VM* m_vm;
+
YarrPattern& m_pattern;
+ YarrCodeBlock& m_codeBlock;
YarrCharSize m_charSize;
- Scale m_charScale;
-
// Used to detect regular expression constructs that are not currently
// supported in the JIT; fall back to the interpreter when this is detected.
- bool m_shouldFallBack;
+ std::optional<JITFailureReason> m_failureReason;
+
+ bool m_decodeSurrogatePairs;
+ bool m_unicodeIgnoreCase;
+ CanonicalMode m_canonicalMode;
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ bool m_containsNestedSubpatterns;
+ ParenContextSizes m_parenContextSizes;
+#endif
+ JumpList m_abortExecution;
+ JumpList m_hitMatchLimit;
+ Vector<Call> m_tryReadUnicodeCharacterCalls;
+ Label m_tryReadUnicodeCharacterEntry;
// The regular expression expressed as a linear sequence of operations.
Vector<YarrOp, 128> m_ops;
@@ -2717,18 +3571,47 @@ private:
// FIXME: This should go away. Rather than tracking this value throughout
// code generation, we should gather this information up front & store it
// on the YarrOp structure.
- int m_checked;
+ Checked<unsigned> m_checkedOffset;
// This class records state whilst generating the backtracking path of code.
BacktrackingState m_backtrackingState;
};
-void jitCompile(YarrPattern& pattern, YarrCharSize charSize, JSGlobalData* globalData, YarrCodeBlock& jitObject, YarrJITCompileMode mode)
+static void dumpCompileFailure(JITFailureReason failure)
+{
+ switch (failure) {
+ case JITFailureReason::DecodeSurrogatePair:
+ dataLog("Can't JIT a pattern decoding surrogate pairs\n");
+ break;
+ case JITFailureReason::BackReference:
+ dataLog("Can't JIT a pattern containing back references\n");
+ break;
+ case JITFailureReason::VariableCountedParenthesisWithNonZeroMinimum:
+ dataLog("Can't JIT a pattern containing a variable counted parenthesis with a non-zero minimum\n");
+ break;
+ case JITFailureReason::ParenthesizedSubpattern:
+ dataLog("Can't JIT a pattern containing parenthesized subpatterns\n");
+ break;
+ case JITFailureReason::NonGreedyParenthesizedSubpattern:
+ dataLog("Can't JIT a pattern containing non-greedy parenthesized subpatterns\n");
+ break;
+ case JITFailureReason::ExecutableMemoryAllocationFailure:
+ dataLog("Can't JIT because of failure of allocation of executable memory\n");
+ break;
+ }
+}
+
+void jitCompile(YarrPattern& pattern, YarrCharSize charSize, VM* vm, YarrCodeBlock& codeBlock, YarrJITCompileMode mode)
{
if (mode == MatchOnly)
- YarrGenerator<MatchOnly>(pattern, charSize).compile(globalData, jitObject);
+ YarrGenerator<MatchOnly>(vm, pattern, codeBlock, charSize).compile();
else
- YarrGenerator<IncludeSubpatterns>(pattern, charSize).compile(globalData, jitObject);
+ YarrGenerator<IncludeSubpatterns>(vm, pattern, codeBlock, charSize).compile();
+
+ if (auto failureReason = codeBlock.failureReason()) {
+ if (Options::dumpCompiledRegExpPatterns())
+ dumpCompileFailure(*failureReason);
+ }
}
}}
diff --git a/src/3rdparty/masm/yarr/YarrJIT.h b/src/3rdparty/masm/yarr/YarrJIT.h
index 139144f2dd..8b6b3a7577 100644
--- a/src/3rdparty/masm/yarr/YarrJIT.h
+++ b/src/3rdparty/masm/yarr/YarrJIT.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009-2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,12 +23,12 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef YarrJIT_h
-#define YarrJIT_h
+#pragma once
+
+#include <Platform.h>
#if ENABLE(YARR_JIT)
-#include "JSGlobalData.h"
#include "MacroAssemblerCodeRef.h"
#include "MatchResult.h"
#include "Yarr.h"
@@ -40,19 +40,39 @@
#define YARR_CALL
#endif
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+constexpr size_t patternContextBufferSize = 8192; // Space caller allocates to save nested parenthesis context
+#endif
+
namespace JSC {
-class JSGlobalData;
+class VM;
class ExecutablePool;
namespace Yarr {
+enum class JITFailureReason : uint8_t {
+ DecodeSurrogatePair,
+ BackReference,
+ VariableCountedParenthesisWithNonZeroMinimum,
+ ParenthesizedSubpattern,
+ NonGreedyParenthesizedSubpattern,
+ ExecutableMemoryAllocationFailure,
+};
+
class YarrCodeBlock {
#if CPU(X86_64) || CPU(ARM64)
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ typedef MatchResult (*YarrJITCode8)(const LChar* input, unsigned start, unsigned length, int* output, void* freeParenContext, unsigned parenContextSize) YARR_CALL;
+ typedef MatchResult (*YarrJITCode16)(const UChar* input, unsigned start, unsigned length, int* output, void* freeParenContext, unsigned parenContextSize) YARR_CALL;
+ typedef MatchResult (*YarrJITCodeMatchOnly8)(const LChar* input, unsigned start, unsigned length, void*, void* freeParenContext, unsigned parenContextSize) YARR_CALL;
+ typedef MatchResult (*YarrJITCodeMatchOnly16)(const UChar* input, unsigned start, unsigned length, void*, void* freeParenContext, unsigned parenContextSize) YARR_CALL;
+#else
typedef MatchResult (*YarrJITCode8)(const LChar* input, unsigned start, unsigned length, int* output) YARR_CALL;
typedef MatchResult (*YarrJITCode16)(const UChar* input, unsigned start, unsigned length, int* output) YARR_CALL;
typedef MatchResult (*YarrJITCodeMatchOnly8)(const LChar* input, unsigned start, unsigned length) YARR_CALL;
typedef MatchResult (*YarrJITCodeMatchOnly16)(const UChar* input, unsigned start, unsigned length) YARR_CALL;
+#endif
#else
typedef EncodedMatchResult (*YarrJITCode8)(const LChar* input, unsigned start, unsigned length, int* output) YARR_CALL;
typedef EncodedMatchResult (*YarrJITCode16)(const UChar* input, unsigned start, unsigned length, int* output) YARR_CALL;
@@ -61,17 +81,10 @@ class YarrCodeBlock {
#endif
public:
- YarrCodeBlock()
- : m_needFallBack(false)
- {
- }
-
- ~YarrCodeBlock()
- {
- }
+ YarrCodeBlock() = default;
- void setFallBack(bool fallback) { m_needFallBack = fallback; }
- bool isFallBack() { return m_needFallBack; }
+ void setFallBackWithFailureReason(JITFailureReason failureReason) { m_failureReason = failureReason; }
+ std::optional<JITFailureReason> failureReason() { return m_failureReason; }
bool has8BitCode() { return m_ref8.size(); }
bool has16BitCode() { return m_ref16.size(); }
@@ -83,6 +96,34 @@ public:
void set8BitCodeMatchOnly(MacroAssemblerCodeRef matchOnly) { m_matchOnly8 = matchOnly; }
void set16BitCodeMatchOnly(MacroAssemblerCodeRef matchOnly) { m_matchOnly16 = matchOnly; }
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ bool usesPatternContextBuffer() { return m_usesPatternContextBuffer; }
+ void setUsesPaternContextBuffer() { m_usesPatternContextBuffer = true; }
+
+ MatchResult execute(const LChar* input, unsigned start, unsigned length, int* output, void* freeParenContext, unsigned parenContextSize)
+ {
+ ASSERT(has8BitCode());
+ return MatchResult(reinterpret_cast<YarrJITCode8>(m_ref8.code().executableAddress())(input, start, length, output, freeParenContext, parenContextSize));
+ }
+
+ MatchResult execute(const UChar* input, unsigned start, unsigned length, int* output, void* freeParenContext, unsigned parenContextSize)
+ {
+ ASSERT(has16BitCode());
+ return MatchResult(reinterpret_cast<YarrJITCode16>(m_ref16.code().executableAddress())(input, start, length, output, freeParenContext, parenContextSize));
+ }
+
+ MatchResult execute(const LChar* input, unsigned start, unsigned length, void* freeParenContext, unsigned parenContextSize)
+ {
+ ASSERT(has8BitCodeMatchOnly());
+ return MatchResult(reinterpret_cast<YarrJITCodeMatchOnly8>(m_matchOnly8.code().executableAddress())(input, start, length, 0, freeParenContext, parenContextSize));
+ }
+
+ MatchResult execute(const UChar* input, unsigned start, unsigned length, void* freeParenContext, unsigned parenContextSize)
+ {
+ ASSERT(has16BitCodeMatchOnly());
+ return MatchResult(reinterpret_cast<YarrJITCodeMatchOnly16>(m_matchOnly16.code().executableAddress())(input, start, length, 0, freeParenContext, parenContextSize));
+ }
+#else
MatchResult execute(const LChar* input, unsigned start, unsigned length, int* output)
{
ASSERT(has8BitCode());
@@ -106,18 +147,54 @@ public:
ASSERT(has16BitCodeMatchOnly());
return MatchResult(reinterpret_cast<YarrJITCodeMatchOnly16>(m_matchOnly16.code().executableAddress())(input, start, length));
}
+#endif
#if ENABLE(REGEXP_TRACING)
- void *getAddr() { return m_ref.code().executableAddress(); }
+ void *get8BitMatchOnlyAddr()
+ {
+ if (!has8BitCodeMatchOnly())
+ return 0;
+
+ return m_matchOnly8.code().executableAddress();
+ }
+
+ void *get16BitMatchOnlyAddr()
+ {
+ if (!has16BitCodeMatchOnly())
+ return 0;
+
+ return m_matchOnly16.code().executableAddress();
+ }
+
+ void *get8BitMatchAddr()
+ {
+ if (!has8BitCode())
+ return 0;
+
+ return m_ref8.code().executableAddress();
+ }
+
+ void *get16BitMatchAddr()
+ {
+ if (!has16BitCode())
+ return 0;
+
+ return m_ref16.code().executableAddress();
+ }
#endif
+ size_t size() const
+ {
+ return m_ref8.size() + m_ref16.size() + m_matchOnly8.size() + m_matchOnly16.size();
+ }
+
void clear()
{
m_ref8 = MacroAssemblerCodeRef();
m_ref16 = MacroAssemblerCodeRef();
m_matchOnly8 = MacroAssemblerCodeRef();
m_matchOnly16 = MacroAssemblerCodeRef();
- m_needFallBack = false;
+ m_failureReason = std::nullopt;
}
private:
@@ -125,17 +202,18 @@ private:
MacroAssemblerCodeRef m_ref16;
MacroAssemblerCodeRef m_matchOnly8;
MacroAssemblerCodeRef m_matchOnly16;
- bool m_needFallBack;
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ bool m_usesPatternContextBuffer;
+#endif
+ std::optional<JITFailureReason> m_failureReason;
};
enum YarrJITCompileMode {
MatchOnly,
IncludeSubpatterns
};
-void jitCompile(YarrPattern&, YarrCharSize, JSGlobalData*, YarrCodeBlock& jitObject, YarrJITCompileMode = IncludeSubpatterns);
+void jitCompile(YarrPattern&, YarrCharSize, VM*, YarrCodeBlock& jitObject, YarrJITCompileMode = IncludeSubpatterns);
} } // namespace JSC::Yarr
#endif
-
-#endif // YarrJIT_h
diff --git a/src/3rdparty/masm/yarr/YarrParser.h b/src/3rdparty/masm/yarr/YarrParser.h
index 13ffd3a1d6..3e5311f1fb 100644
--- a/src/3rdparty/masm/yarr/YarrParser.h
+++ b/src/3rdparty/masm/yarr/YarrParser.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2009, 2014-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,46 +23,25 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef YarrParser_h
-#define YarrParser_h
+#pragma once
#include "Yarr.h"
+#include "YarrPattern.h"
+#include "YarrUnicodeProperties.h"
#include <wtf/ASCIICType.h>
+#include <wtf/HashSet.h>
+#include <wtf/Optional.h>
+#include <wtf/text/StringBuilder.h>
#include <wtf/text/WTFString.h>
-#include <wtf/unicode/Unicode.h>
namespace JSC { namespace Yarr {
-#define REGEXP_ERROR_PREFIX "Invalid regular expression: "
-
-enum BuiltInCharacterClassID {
- DigitClassID,
- SpaceClassID,
- WordClassID,
- NewlineClassID,
-};
-
// The Parser class should not be used directly - only via the Yarr::parse() method.
template<class Delegate, typename CharType>
class Parser {
private:
template<class FriendDelegate>
- friend const char* parse(FriendDelegate&, const String& pattern, unsigned backReferenceLimit);
-
- enum ErrorCode {
- NoError,
- PatternTooLarge,
- QuantifierOutOfOrder,
- QuantifierWithoutAtom,
- QuantifierTooLarge,
- MissingParentheses,
- ParenthesesUnmatched,
- ParenthesesTypeInvalid,
- CharacterClassUnmatched,
- CharacterClassOutOfOrder,
- EscapeUnterminated,
- NumberOfErrorCodes
- };
+ friend ErrorCode parse(FriendDelegate&, const String& pattern, bool isUnicode, unsigned backReferenceLimit);
/*
* CharacterClassParserDelegate:
@@ -77,7 +56,7 @@ private:
public:
CharacterClassParserDelegate(Delegate& delegate, ErrorCode& err)
: m_delegate(delegate)
- , m_err(err)
+ , m_errorCode(err)
, m_state(Empty)
, m_character(0)
{
@@ -102,7 +81,7 @@ private:
* mode we will allow a hypen to be treated as indicating a range (i.e. /[a-z]/
* is different to /[a\-z]/).
*/
- void atomPatternCharacter(UChar ch, bool hyphenIsRange = false)
+ void atomPatternCharacter(UChar32 ch, bool hyphenIsRange = false)
{
switch (m_state) {
case AfterCharacterClass:
@@ -118,7 +97,8 @@ private:
m_state = AfterCharacterClassHyphen;
return;
}
- Q_FALLTHROUGH(); // cached character, so treat this as Empty.
+ // Otherwise just fall through - cached character so treat this as Empty.
+ FALLTHROUGH;
case Empty:
m_character = ch;
@@ -136,7 +116,7 @@ private:
case CachedCharacterHyphen:
if (ch < m_character) {
- m_err = CharacterClassOutOfOrder;
+ m_errorCode = ErrorCode::CharacterClassOutOfOrder;
return;
}
m_delegate.atomCharacterClassRange(m_character, ch);
@@ -168,8 +148,7 @@ private:
case CachedCharacter:
// Flush the currently cached character, then fall through.
m_delegate.atomCharacterClassAtom(m_character);
- Q_FALLTHROUGH();
-
+ FALLTHROUGH;
case Empty:
case AfterCharacterClass:
m_state = AfterCharacterClass;
@@ -187,7 +166,7 @@ private:
case CachedCharacterHyphen:
m_delegate.atomCharacterClassAtom(m_character);
m_delegate.atomCharacterClassAtom('-');
- // fall through
+ FALLTHROUGH;
case AfterCharacterClassHyphen:
m_delegate.atomCharacterClassBuiltIn(classID, invert);
m_state = Empty;
@@ -215,10 +194,11 @@ private:
// invoked with inCharacterClass set.
NO_RETURN_DUE_TO_ASSERT void assertionWordBoundary(bool) { RELEASE_ASSERT_NOT_REACHED(); }
NO_RETURN_DUE_TO_ASSERT void atomBackReference(unsigned) { RELEASE_ASSERT_NOT_REACHED(); }
+ NO_RETURN_DUE_TO_ASSERT void atomNamedBackReference(String) { RELEASE_ASSERT_NOT_REACHED(); }
private:
Delegate& m_delegate;
- ErrorCode& m_err;
+ ErrorCode& m_errorCode;
enum CharacterClassConstructionState {
Empty,
CachedCharacter,
@@ -226,20 +206,31 @@ private:
AfterCharacterClass,
AfterCharacterClassHyphen,
} m_state;
- UChar m_character;
+ UChar32 m_character;
};
- Parser(Delegate& delegate, const String& pattern, unsigned backReferenceLimit)
+ Parser(Delegate& delegate, const String& pattern, bool isUnicode, unsigned backReferenceLimit)
: m_delegate(delegate)
, m_backReferenceLimit(backReferenceLimit)
- , m_err(NoError)
- , m_data(pattern.getCharacters<CharType>())
+ , m_data(pattern.characters<CharType>())
, m_size(pattern.length())
- , m_index(0)
- , m_parenthesesNestingDepth(0)
+ , m_isUnicode(isUnicode)
{
}
+ // The handling of IdentityEscapes is different depending on the unicode flag.
+ // For Unicode patterns, IdentityEscapes only include SyntaxCharacters or '/'.
+ // For non-unicode patterns, most any character can be escaped.
+ bool isIdentityEscapeAnError(int ch)
+ {
+ if (m_isUnicode && !strchr("^$\\.*+?()[]{}|/", ch)) {
+ m_errorCode = ErrorCode::InvalidIdentityEscape;
+ return true;
+ }
+
+ return false;
+ }
+
/*
* parseEscape():
*
@@ -263,12 +254,12 @@ private:
template<bool inCharacterClass, class EscapeDelegate>
bool parseEscape(EscapeDelegate& delegate)
{
- ASSERT(!m_err);
+ ASSERT(!hasError(m_errorCode));
ASSERT(peek() == '\\');
consume();
if (atEndOfPattern()) {
- m_err = EscapeUnterminated;
+ m_errorCode = ErrorCode::EscapeUnterminated;
return false;
}
@@ -276,18 +267,24 @@ private:
// Assertions
case 'b':
consume();
- if (inCharacterClass)
+ if (inCharacterClass) {
+ if (isIdentityEscapeAnError('b'))
+ break;
+
delegate.atomPatternCharacter('\b');
- else {
+ } else {
delegate.assertionWordBoundary(false);
return false;
}
break;
case 'B':
consume();
- if (inCharacterClass)
+ if (inCharacterClass) {
+ if (isIdentityEscapeAnError('B'))
+ break;
+
delegate.atomPatternCharacter('B');
- else {
+ } else {
delegate.assertionWordBoundary(true);
return false;
}
@@ -296,27 +293,27 @@ private:
// CharacterClassEscape
case 'd':
consume();
- delegate.atomBuiltInCharacterClass(DigitClassID, false);
+ delegate.atomBuiltInCharacterClass(BuiltInCharacterClassID::DigitClassID, false);
break;
case 's':
consume();
- delegate.atomBuiltInCharacterClass(SpaceClassID, false);
+ delegate.atomBuiltInCharacterClass(BuiltInCharacterClassID::SpaceClassID, false);
break;
case 'w':
consume();
- delegate.atomBuiltInCharacterClass(WordClassID, false);
+ delegate.atomBuiltInCharacterClass(BuiltInCharacterClassID::WordClassID, false);
break;
case 'D':
consume();
- delegate.atomBuiltInCharacterClass(DigitClassID, true);
+ delegate.atomBuiltInCharacterClass(BuiltInCharacterClassID::DigitClassID, true);
break;
case 'S':
consume();
- delegate.atomBuiltInCharacterClass(SpaceClassID, true);
+ delegate.atomBuiltInCharacterClass(BuiltInCharacterClassID::SpaceClassID, true);
break;
case 'W':
consume();
- delegate.atomBuiltInCharacterClass(WordClassID, true);
+ delegate.atomBuiltInCharacterClass(BuiltInCharacterClassID::WordClassID, true);
break;
// DecimalEscape
@@ -341,15 +338,22 @@ private:
}
restoreState(state);
+
+ if (m_isUnicode) {
+ m_errorCode = ErrorCode::InvalidBackreference;
+ return false;
+ }
}
-
- // Not a backreference, and not octal.
+
+ // Not a backreference, and not octal. Just a number.
if (peek() >= '8') {
- delegate.atomPatternCharacter('\\');
+ delegate.atomPatternCharacter(consume());
break;
}
+
+ // Fall-through to handle this as an octal escape.
+ FALLTHROUGH;
}
- Q_FALLTHROUGH(); // Handle this as an octal escape.
// Octal escape
case '0':
@@ -400,32 +404,161 @@ private:
case 'x': {
consume();
int x = tryConsumeHex(2);
- if (x == -1)
+ if (x == -1) {
+ if (isIdentityEscapeAnError('x'))
+ break;
+
delegate.atomPatternCharacter('x');
- else
+ } else
delegate.atomPatternCharacter(x);
break;
}
+ // Named backreference
+ case 'k': {
+ consume();
+ ParseState state = saveState();
+ if (!atEndOfPattern() && !inCharacterClass) {
+ if (consume() == '<') {
+ auto groupName = tryConsumeGroupName();
+ if (groupName && m_captureGroupNames.contains(groupName.value())) {
+ delegate.atomNamedBackReference(groupName.value());
+ break;
+ }
+ if (m_isUnicode) {
+ m_errorCode = ErrorCode::InvalidBackreference;
+ break;
+ }
+ }
+ }
+ restoreState(state);
+ delegate.atomPatternCharacter('k');
+ break;
+ }
+
+ // Unicode property escapes
+ case 'p':
+ case 'P': {
+ int escapeChar = consume();
+
+ if (!m_isUnicode) {
+ if (isIdentityEscapeAnError(escapeChar))
+ break;
+ delegate.atomPatternCharacter(escapeChar);
+ break;
+ }
+
+ if (!atEndOfPattern() && peek() == '{') {
+ consume();
+ auto optClassID = tryConsumeUnicodePropertyExpression();
+ if (!optClassID) {
+ // tryConsumeUnicodePropertyExpression() will set m_errorCode for a malformed property expression
+ break;
+ }
+ delegate.atomBuiltInCharacterClass(optClassID.value(), escapeChar == 'P');
+ } else
+ m_errorCode = ErrorCode::InvalidUnicodePropertyExpression;
+ break;
+ }
+
// UnicodeEscape
case 'u': {
consume();
+ if (atEndOfPattern()) {
+ if (isIdentityEscapeAnError('u'))
+ break;
+
+ delegate.atomPatternCharacter('u');
+ break;
+ }
+
+ if (m_isUnicode && peek() == '{') {
+ consume();
+ UChar32 codePoint = 0;
+ do {
+ if (atEndOfPattern() || !isASCIIHexDigit(peek())) {
+ m_errorCode = ErrorCode::InvalidUnicodeEscape;
+ break;
+ }
+
+ codePoint = (codePoint << 4) | toASCIIHexValue(consume());
+
+ if (codePoint > UCHAR_MAX_VALUE)
+ m_errorCode = ErrorCode::InvalidUnicodeEscape;
+ } while (!atEndOfPattern() && peek() != '}');
+ if (!atEndOfPattern() && peek() == '}')
+ consume();
+ else if (!hasError(m_errorCode))
+ m_errorCode = ErrorCode::InvalidUnicodeEscape;
+ if (hasError(m_errorCode))
+ return false;
+
+ delegate.atomPatternCharacter(codePoint);
+ break;
+ }
int u = tryConsumeHex(4);
- if (u == -1)
+ if (u == -1) {
+ if (isIdentityEscapeAnError('u'))
+ break;
+
delegate.atomPatternCharacter('u');
- else
+ } else {
+ // If we have the first of a surrogate pair, look for the second.
+ if (U16_IS_LEAD(u) && m_isUnicode && (patternRemaining() >= 6) && peek() == '\\') {
+ ParseState state = saveState();
+ consume();
+
+ if (tryConsume('u')) {
+ int surrogate2 = tryConsumeHex(4);
+ if (U16_IS_TRAIL(surrogate2)) {
+ u = U16_GET_SUPPLEMENTARY(u, surrogate2);
+ delegate.atomPatternCharacter(u);
+ break;
+ }
+ }
+
+ restoreState(state);
+ }
delegate.atomPatternCharacter(u);
+ }
break;
}
// IdentityEscape
default:
+ int ch = peek();
+
+ if (ch == '-' && m_isUnicode && inCharacterClass) {
+ // \- is allowed for ClassEscape with unicode flag.
+ delegate.atomPatternCharacter(consume());
+ break;
+ }
+
+ if (isIdentityEscapeAnError(ch))
+ break;
+
delegate.atomPatternCharacter(consume());
}
return true;
}
+ UChar32 consumePossibleSurrogatePair()
+ {
+ UChar32 ch = consume();
+ if (U16_IS_LEAD(ch) && m_isUnicode && (patternRemaining() > 0)) {
+ ParseState state = saveState();
+
+ UChar32 surrogate2 = consume();
+ if (U16_IS_TRAIL(surrogate2))
+ ch = U16_GET_SUPPLEMENTARY(ch, surrogate2);
+ else
+ restoreState(state);
+ }
+
+ return ch;
+ }
+
/*
* parseAtomEscape(), parseCharacterClassEscape():
*
@@ -449,11 +582,11 @@ private:
*/
void parseCharacterClass()
{
- ASSERT(!m_err);
+ ASSERT(!hasError(m_errorCode));
ASSERT(peek() == '[');
consume();
- CharacterClassParserDelegate characterClassConstructor(m_delegate, m_err);
+ CharacterClassParserDelegate characterClassConstructor(m_delegate, m_errorCode);
characterClassConstructor.begin(tryConsume('^'));
@@ -469,14 +602,14 @@ private:
break;
default:
- characterClassConstructor.atomPatternCharacter(consume(), true);
+ characterClassConstructor.atomPatternCharacter(consumePossibleSurrogatePair(), true);
}
- if (m_err)
+ if (hasError(m_errorCode))
return;
}
- m_err = CharacterClassUnmatched;
+ m_errorCode = ErrorCode::CharacterClassUnmatched;
}
/*
@@ -486,13 +619,13 @@ private:
*/
void parseParenthesesBegin()
{
- ASSERT(!m_err);
+ ASSERT(!hasError(m_errorCode));
ASSERT(peek() == '(');
consume();
if (tryConsume('?')) {
if (atEndOfPattern()) {
- m_err = ParenthesesTypeInvalid;
+ m_errorCode = ErrorCode::ParenthesesTypeInvalid;
return;
}
@@ -508,9 +641,23 @@ private:
case '!':
m_delegate.atomParentheticalAssertionBegin(true);
break;
-
+
+ case '<': {
+ auto groupName = tryConsumeGroupName();
+ if (groupName) {
+ auto setAddResult = m_captureGroupNames.add(groupName.value());
+ if (setAddResult.isNewEntry)
+ m_delegate.atomParenthesesSubpatternBegin(true, groupName);
+ else
+ m_errorCode = ErrorCode::DuplicateGroupName;
+ } else
+ m_errorCode = ErrorCode::InvalidGroupName;
+
+ break;
+ }
+
default:
- m_err = ParenthesesTypeInvalid;
+ m_errorCode = ErrorCode::ParenthesesTypeInvalid;
}
} else
m_delegate.atomParenthesesSubpatternBegin();
@@ -525,14 +672,14 @@ private:
*/
void parseParenthesesEnd()
{
- ASSERT(!m_err);
+ ASSERT(!hasError(m_errorCode));
ASSERT(peek() == ')');
consume();
if (m_parenthesesNestingDepth > 0)
m_delegate.atomParenthesesEnd();
else
- m_err = ParenthesesUnmatched;
+ m_errorCode = ErrorCode::ParenthesesUnmatched;
--m_parenthesesNestingDepth;
}
@@ -544,18 +691,18 @@ private:
*/
void parseQuantifier(bool lastTokenWasAnAtom, unsigned min, unsigned max)
{
- ASSERT(!m_err);
+ ASSERT(!hasError(m_errorCode));
ASSERT(min <= max);
if (min == UINT_MAX) {
- m_err = QuantifierTooLarge;
+ m_errorCode = ErrorCode::QuantifierTooLarge;
return;
}
if (lastTokenWasAnAtom)
m_delegate.quantifyAtom(min, max, !tryConsume('?'));
else
- m_err = QuantifierWithoutAtom;
+ m_errorCode = ErrorCode::QuantifierWithoutAtom;
}
/*
@@ -603,7 +750,7 @@ private:
case '.':
consume();
- m_delegate.atomBuiltInCharacterClass(NewlineClassID, true);
+ m_delegate.atomBuiltInCharacterClass(BuiltInCharacterClassID::DotClassID, false);
lastTokenWasAnAtom = true;
break;
@@ -649,7 +796,7 @@ private:
if (min <= max)
parseQuantifier(lastTokenWasAnAtom, min, max);
else
- m_err = QuantifierOutOfOrder;
+ m_errorCode = ErrorCode::QuantifierOutOfOrder;
lastTokenWasAnAtom = false;
break;
}
@@ -657,51 +804,36 @@ private:
restoreState(state);
}
- Q_FALLTHROUGH(); // if we did not find a complete quantifer, fall through to the default case.
+ // if we did not find a complete quantifer, fall through to the default case.
+ FALLTHROUGH;
default:
- m_delegate.atomPatternCharacter(consume());
+ m_delegate.atomPatternCharacter(consumePossibleSurrogatePair());
lastTokenWasAnAtom = true;
}
- if (m_err)
+ if (hasError(m_errorCode))
return;
}
if (m_parenthesesNestingDepth > 0)
- m_err = MissingParentheses;
+ m_errorCode = ErrorCode::MissingParentheses;
}
/*
* parse():
*
- * This method calls parseTokens() to parse over the input and converts any
- * error code to a const char* for a result.
+ * This method calls parseTokens() to parse over the input and returns error code for a result.
*/
- const char* parse()
+ ErrorCode parse()
{
if (m_size > MAX_PATTERN_SIZE)
- m_err = PatternTooLarge;
+ m_errorCode = ErrorCode::PatternTooLarge;
else
parseTokens();
- ASSERT(atEndOfPattern() || m_err);
-
- // The order of this array must match the ErrorCode enum.
- static const char* errorMessages[NumberOfErrorCodes] = {
- 0, // NoError
- REGEXP_ERROR_PREFIX "regular expression too large",
- REGEXP_ERROR_PREFIX "numbers out of order in {} quantifier",
- REGEXP_ERROR_PREFIX "nothing to repeat",
- REGEXP_ERROR_PREFIX "number too large in {} quantifier",
- REGEXP_ERROR_PREFIX "missing )",
- REGEXP_ERROR_PREFIX "unmatched parentheses",
- REGEXP_ERROR_PREFIX "unrecognized character after (?",
- REGEXP_ERROR_PREFIX "missing terminating ] for character class",
- REGEXP_ERROR_PREFIX "range out of order in character class",
- REGEXP_ERROR_PREFIX "\\ at end of pattern"
- };
-
- return errorMessages[m_err];
+ ASSERT(atEndOfPattern() || hasError(m_errorCode));
+
+ return m_errorCode;
}
// Misc helper functions:
@@ -724,6 +856,12 @@ private:
return m_index == m_size;
}
+ unsigned patternRemaining()
+ {
+ ASSERT(m_index <= m_size);
+ return m_size - m_index;
+ }
+
int peek()
{
ASSERT(m_index < m_size);
@@ -741,6 +879,87 @@ private:
return peek() - '0';
}
+ int tryConsumeUnicodeEscape()
+ {
+ if (!tryConsume('u'))
+ return -1;
+
+ if (m_isUnicode && tryConsume('{')) {
+ int codePoint = 0;
+ do {
+ if (atEndOfPattern() || !isASCIIHexDigit(peek())) {
+ m_errorCode = ErrorCode::InvalidUnicodeEscape;
+ return -1;
+ }
+
+ codePoint = (codePoint << 4) | toASCIIHexValue(consume());
+
+ if (codePoint > UCHAR_MAX_VALUE) {
+ m_errorCode = ErrorCode::InvalidUnicodeEscape;
+ return -1;
+ }
+ } while (!atEndOfPattern() && peek() != '}');
+ if (!atEndOfPattern() && peek() == '}')
+ consume();
+ else if (!hasError(m_errorCode))
+ m_errorCode = ErrorCode::InvalidUnicodeEscape;
+ if (hasError(m_errorCode))
+ return -1;
+
+ return codePoint;
+ }
+
+ int u = tryConsumeHex(4);
+ if (u == -1)
+ return -1;
+
+ // If we have the first of a surrogate pair, look for the second.
+ if (U16_IS_LEAD(u) && m_isUnicode && (patternRemaining() >= 6) && peek() == '\\') {
+ ParseState state = saveState();
+ consume();
+
+ if (tryConsume('u')) {
+ int surrogate2 = tryConsumeHex(4);
+ if (U16_IS_TRAIL(surrogate2)) {
+ u = U16_GET_SUPPLEMENTARY(u, surrogate2);
+ return u;
+ }
+ }
+
+ restoreState(state);
+ }
+
+ return u;
+ }
+
+ int tryConsumeIdentifierCharacter()
+ {
+ int ch = peek();
+
+ if (ch == '\\') {
+ consume();
+ ch = tryConsumeUnicodeEscape();
+ } else
+ consume();
+
+ return ch;
+ }
+
+ bool isIdentifierStart(int ch)
+ {
+ return (WTF::isASCII(ch) && (WTF::isASCIIAlpha(ch) || ch == '_' || ch == '$')) || (U_GET_GC_MASK(ch) & U_GC_L_MASK);
+ }
+
+ bool isIdentifierPart(int ch)
+ {
+ return (WTF::isASCII(ch) && (WTF::isASCIIAlpha(ch) || ch == '_' || ch == '$')) || (U_GET_GC_MASK(ch) & (U_GC_L_MASK | U_GC_MN_MASK | U_GC_MC_MASK | U_GC_ND_MASK | U_GC_PC_MASK)) || ch == 0x200C || ch == 0x200D;
+ }
+
+ bool isUnicodePropertyValueExpressionChar(int ch)
+ {
+ return WTF::isASCIIAlphanumeric(ch) || ch == '_' || ch == '=';
+ }
+
int consume()
{
ASSERT(m_index < m_size);
@@ -755,13 +974,10 @@ private:
unsigned consumeNumber()
{
- unsigned n = consumeDigit();
- // check for overflow.
- for (unsigned newValue; peekIsDigit() && ((newValue = n * 10 + peekDigit()) >= n); ) {
- n = newValue;
- consume();
- }
- return n;
+ Checked<unsigned, RecordOverflow> n = consumeDigit();
+ while (peekIsDigit())
+ n = n * 10 + consumeDigit();
+ return n.hasOverflowed() ? quantifyInfinite : n.unsafeGet();
}
unsigned consumeOctal()
@@ -797,13 +1013,99 @@ private:
return n;
}
+ std::optional<String> tryConsumeGroupName()
+ {
+ if (atEndOfPattern())
+ return std::nullopt;
+
+ ParseState state = saveState();
+
+ int ch = tryConsumeIdentifierCharacter();
+
+ if (isIdentifierStart(ch)) {
+ StringBuilder identifierBuilder;
+ identifierBuilder.append(ch);
+
+ while (!atEndOfPattern()) {
+ ch = tryConsumeIdentifierCharacter();
+ if (ch == '>')
+ return std::optional<String>(identifierBuilder.toString());
+
+ if (!isIdentifierPart(ch))
+ break;
+
+ identifierBuilder.append(ch);
+ }
+ }
+
+ restoreState(state);
+
+ return std::nullopt;
+ }
+
+ std::optional<BuiltInCharacterClassID> tryConsumeUnicodePropertyExpression()
+ {
+ if (atEndOfPattern() || !isUnicodePropertyValueExpressionChar(peek())) {
+ m_errorCode = ErrorCode::InvalidUnicodePropertyExpression;
+ return std::nullopt;
+ }
+
+ StringBuilder expressionBuilder;
+ String unicodePropertyName;
+ bool foundEquals = false;
+ unsigned errors = 0;
+
+ expressionBuilder.append(consume());
+
+ while (!atEndOfPattern()) {
+ int ch = peek();
+ if (ch == '}') {
+ consume();
+ if (errors) {
+ m_errorCode = ErrorCode::InvalidUnicodePropertyExpression;
+ return std::nullopt;
+ }
+
+ if (foundEquals) {
+ auto result = unicodeMatchPropertyValue(unicodePropertyName, expressionBuilder.toString());
+ if (!result)
+ m_errorCode = ErrorCode::InvalidUnicodePropertyExpression;
+ return result;
+ }
+
+ auto result = unicodeMatchProperty(expressionBuilder.toString());
+ if (!result)
+ m_errorCode = ErrorCode::InvalidUnicodePropertyExpression;
+ return result;
+ }
+
+ consume();
+ if (ch == '=') {
+ if (!foundEquals) {
+ foundEquals = true;
+ unicodePropertyName = expressionBuilder.toString();
+ expressionBuilder.clear();
+ } else
+ errors++;
+ } else if (!isUnicodePropertyValueExpressionChar(ch))
+ errors++;
+ else
+ expressionBuilder.append(ch);
+ }
+
+ m_errorCode = ErrorCode::InvalidUnicodePropertyExpression;
+ return std::nullopt;
+ }
+
Delegate& m_delegate;
unsigned m_backReferenceLimit;
- ErrorCode m_err;
+ ErrorCode m_errorCode { ErrorCode::NoError };
const CharType* m_data;
unsigned m_size;
- unsigned m_index;
- unsigned m_parenthesesNestingDepth;
+ unsigned m_index { 0 };
+ bool m_isUnicode;
+ unsigned m_parenthesesNestingDepth { 0 };
+ HashSet<String> m_captureGroupNames;
// Derived by empirical testing of compile time in PCRE and WREC.
static const unsigned MAX_PATTERN_SIZE = 1024 * 1024;
@@ -823,17 +1125,18 @@ private:
* void assertionEOL();
* void assertionWordBoundary(bool invert);
*
- * void atomPatternCharacter(UChar ch);
+ * void atomPatternCharacter(UChar32 ch);
* void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert);
* void atomCharacterClassBegin(bool invert)
- * void atomCharacterClassAtom(UChar ch)
- * void atomCharacterClassRange(UChar begin, UChar end)
+ * void atomCharacterClassAtom(UChar32 ch)
+ * void atomCharacterClassRange(UChar32 begin, UChar32 end)
* void atomCharacterClassBuiltIn(BuiltInCharacterClassID classID, bool invert)
* void atomCharacterClassEnd()
- * void atomParenthesesSubpatternBegin(bool capture = true);
+ * void atomParenthesesSubpatternBegin(bool capture = true, std::optional<String> groupName);
* void atomParentheticalAssertionBegin(bool invert = false);
* void atomParenthesesEnd();
* void atomBackReference(unsigned subpatternId);
+ * void atomNamedBackReference(String subpatternName);
*
* void quantifyAtom(unsigned min, unsigned max, bool greedy);
*
@@ -869,13 +1172,11 @@ private:
*/
template<class Delegate>
-const char* parse(Delegate& delegate, const String& pattern, unsigned backReferenceLimit = quantifyInfinite)
+ErrorCode parse(Delegate& delegate, const String& pattern, bool isUnicode, unsigned backReferenceLimit = quantifyInfinite)
{
if (pattern.is8Bit())
- return Parser<Delegate, LChar>(delegate, pattern, backReferenceLimit).parse();
- return Parser<Delegate, UChar>(delegate, pattern, backReferenceLimit).parse();
+ return Parser<Delegate, LChar>(delegate, pattern, isUnicode, backReferenceLimit).parse();
+ return Parser<Delegate, UChar>(delegate, pattern, isUnicode, backReferenceLimit).parse();
}
} } // namespace JSC::Yarr
-
-#endif // YarrParser_h
diff --git a/src/3rdparty/masm/yarr/YarrPattern.cpp b/src/3rdparty/masm/yarr/YarrPattern.cpp
index c7e5b6b09b..ac66ea1b9a 100644
--- a/src/3rdparty/masm/yarr/YarrPattern.cpp
+++ b/src/3rdparty/masm/yarr/YarrPattern.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2009, 2013-2016 Apple Inc. All rights reserved.
* Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged
*
* Redistribution and use in source and binary forms, with or without
@@ -27,10 +27,15 @@
#include "config.h"
#include "YarrPattern.h"
+#include "Options.h"
#include "Yarr.h"
-#include "YarrCanonicalizeUCS2.h"
+#include "YarrCanonicalize.h"
#include "YarrParser.h"
+#include <wtf/DataLog.h>
+#include <wtf/Optional.h>
+//#include <wtf/Threading.h>
#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
using namespace WTF;
@@ -40,8 +45,11 @@ namespace JSC { namespace Yarr {
class CharacterClassConstructor {
public:
- CharacterClassConstructor(bool isCaseInsensitive = false)
+ CharacterClassConstructor(bool isCaseInsensitive, CanonicalMode canonicalMode)
: m_isCaseInsensitive(isCaseInsensitive)
+ , m_hasNonBMPCharacters(false)
+ , m_anyCharacter(false)
+ , m_canonicalMode(canonicalMode)
{
}
@@ -51,6 +59,8 @@ public:
m_ranges.clear();
m_matchesUnicode.clear();
m_rangesUnicode.clear();
+ m_hasNonBMPCharacters = false;
+ m_anyCharacter = false;
}
void append(const CharacterClass* other)
@@ -65,11 +75,71 @@ public:
addSortedRange(m_rangesUnicode, other->m_rangesUnicode[i].begin, other->m_rangesUnicode[i].end);
}
- void putChar(UChar ch)
+ void appendInverted(const CharacterClass* other)
{
- // Handle ascii cases.
- if (ch <= 0x7f) {
- if (m_isCaseInsensitive && isASCIIAlpha(ch)) {
+ auto addSortedInverted = [&](UChar32 min, UChar32 max,
+ const Vector<UChar32>& srcMatches, const Vector<CharacterRange>& srcRanges,
+ Vector<UChar32>& destMatches, Vector<CharacterRange>& destRanges) {
+
+ auto addSortedMatchOrRange = [&](UChar32 lo, UChar32 hiPlusOne) {
+ if (lo < hiPlusOne) {
+ if (lo + 1 == hiPlusOne)
+ addSorted(destMatches, lo);
+ else
+ addSortedRange(destRanges, lo, hiPlusOne - 1);
+ }
+ };
+
+ UChar32 lo = min;
+ size_t matchesIndex = 0;
+ size_t rangesIndex = 0;
+ bool matchesRemaining = matchesIndex < srcMatches.size();
+ bool rangesRemaining = rangesIndex < srcRanges.size();
+
+ if (!matchesRemaining && !rangesRemaining) {
+ addSortedMatchOrRange(min, max + 1);
+ return;
+ }
+
+ while (matchesRemaining || rangesRemaining) {
+ UChar32 hiPlusOne;
+ UChar32 nextLo;
+
+ if (matchesRemaining
+ && (!rangesRemaining || srcMatches[matchesIndex] < srcRanges[rangesIndex].begin)) {
+ hiPlusOne = srcMatches[matchesIndex];
+ nextLo = hiPlusOne + 1;
+ ++matchesIndex;
+ matchesRemaining = matchesIndex < srcMatches.size();
+ } else {
+ hiPlusOne = srcRanges[rangesIndex].begin;
+ nextLo = srcRanges[rangesIndex].end + 1;
+ ++rangesIndex;
+ rangesRemaining = rangesIndex < srcRanges.size();
+ }
+
+ addSortedMatchOrRange(lo, hiPlusOne);
+
+ lo = nextLo;
+ }
+
+ addSortedMatchOrRange(lo, max + 1);
+ };
+
+ addSortedInverted(0, 0x7f, other->m_matches, other->m_ranges, m_matches, m_ranges);
+ addSortedInverted(0x80, 0x10ffff, other->m_matchesUnicode, other->m_rangesUnicode, m_matchesUnicode, m_rangesUnicode);
+ }
+
+ void putChar(UChar32 ch)
+ {
+ if (!m_isCaseInsensitive) {
+ addSorted(ch);
+ return;
+ }
+
+ if (m_canonicalMode == CanonicalMode::UCS2 && isASCII(ch)) {
+ // Handle ASCII cases.
+ if (isASCIIAlpha(ch)) {
addSorted(m_matches, toASCIIUpper(ch));
addSorted(m_matches, toASCIILower(ch));
} else
@@ -77,40 +147,33 @@ public:
return;
}
- // Simple case, not a case-insensitive match.
- if (!m_isCaseInsensitive) {
- addSorted(m_matchesUnicode, ch);
- return;
- }
-
// Add multiple matches, if necessary.
- UCS2CanonicalizationRange* info = rangeInfoFor(ch);
+ const CanonicalizationRange* info = canonicalRangeInfoFor(ch, m_canonicalMode);
if (info->type == CanonicalizeUnique)
- addSorted(m_matchesUnicode, ch);
+ addSorted(ch);
else
putUnicodeIgnoreCase(ch, info);
}
- void putUnicodeIgnoreCase(UChar ch, UCS2CanonicalizationRange* info)
+ void putUnicodeIgnoreCase(UChar32 ch, const CanonicalizationRange* info)
{
ASSERT(m_isCaseInsensitive);
- ASSERT(ch > 0x7f);
ASSERT(ch >= info->begin && ch <= info->end);
ASSERT(info->type != CanonicalizeUnique);
if (info->type == CanonicalizeSet) {
- for (uint16_t* set = characterSetInfo[info->value]; (ch = *set); ++set)
- addSorted(m_matchesUnicode, ch);
+ for (const UChar32* set = canonicalCharacterSetInfo(info->value, m_canonicalMode); (ch = *set); ++set)
+ addSorted(ch);
} else {
- addSorted(m_matchesUnicode, ch);
- addSorted(m_matchesUnicode, getCanonicalPair(info, ch));
+ addSorted(ch);
+ addSorted(getCanonicalPair(info, ch));
}
}
- void putRange(UChar lo, UChar hi)
+ void putRange(UChar32 lo, UChar32 hi)
{
- if (lo <= 0x7f) {
+ if (isASCII(lo)) {
char asciiLo = lo;
- char asciiHi = std::min(hi, (UChar)0x7f);
+ char asciiHi = std::min(hi, (UChar32)0x7f);
addSortedRange(m_ranges, lo, asciiHi);
if (m_isCaseInsensitive) {
@@ -120,19 +183,19 @@ public:
addSortedRange(m_ranges, std::max(asciiLo, 'a')+('A'-'a'), std::min(asciiHi, 'z')+('A'-'a'));
}
}
- if (hi <= 0x7f)
+ if (isASCII(hi))
return;
- lo = std::max(lo, (UChar)0x80);
+ lo = std::max(lo, (UChar32)0x80);
addSortedRange(m_rangesUnicode, lo, hi);
if (!m_isCaseInsensitive)
return;
- UCS2CanonicalizationRange* info = rangeInfoFor(lo);
+ const CanonicalizationRange* info = canonicalRangeInfoFor(lo, m_canonicalMode);
while (true) {
// Handle the range [lo .. end]
- UChar end = std::min<UChar>(info->end, hi);
+ UChar32 end = std::min<UChar32>(info->end, hi);
switch (info->type) {
case CanonicalizeUnique:
@@ -140,7 +203,7 @@ public:
break;
case CanonicalizeSet: {
UChar ch;
- for (uint16_t* set = characterSetInfo[info->value]; (ch = *set); ++set)
+ for (const UChar32* set = canonicalCharacterSetInfo(info->value, m_canonicalMode); (ch = *set); ++set)
addSorted(m_matchesUnicode, ch);
break;
}
@@ -175,24 +238,38 @@ public:
}
- PassOwnPtr<CharacterClass> charClass()
+ std::unique_ptr<CharacterClass> charClass()
{
- OwnPtr<CharacterClass> characterClass = adoptPtr(new CharacterClass);
+ coalesceTables();
+
+ auto characterClass = std::make_unique<CharacterClass>();
characterClass->m_matches.swap(m_matches);
characterClass->m_ranges.swap(m_ranges);
characterClass->m_matchesUnicode.swap(m_matchesUnicode);
characterClass->m_rangesUnicode.swap(m_rangesUnicode);
+ characterClass->m_hasNonBMPCharacters = hasNonBMPCharacters();
+ characterClass->m_anyCharacter = anyCharacter();
+
+ m_hasNonBMPCharacters = false;
+ m_anyCharacter = false;
- return characterClass.release();
+ return characterClass;
}
private:
- void addSorted(Vector<UChar>& matches, UChar ch)
+ void addSorted(UChar32 ch)
+ {
+ addSorted(isASCII(ch) ? m_matches : m_matchesUnicode, ch);
+ }
+
+ void addSorted(Vector<UChar32>& matches, UChar32 ch)
{
unsigned pos = 0;
- ASSERT(matches.size() <= UINT_MAX);
- unsigned range = static_cast<unsigned>(matches.size());
+ unsigned range = matches.size();
+
+ if (!U_IS_BMP(ch))
+ m_hasNonBMPCharacters = true;
// binary chop, find position to insert char.
while (range) {
@@ -201,9 +278,31 @@ private:
int val = matches[pos+index] - ch;
if (!val)
return;
- else if (val > 0)
+ else if (val > 0) {
+ if (val == 1) {
+ UChar32 lo = ch;
+ UChar32 hi = ch + 1;
+ matches.remove(pos + index);
+ if (pos + index > 0 && matches[pos + index - 1] == ch - 1) {
+ lo = ch - 1;
+ matches.remove(pos + index - 1);
+ }
+ addSortedRange(isASCII(ch) ? m_ranges : m_rangesUnicode, lo, hi);
+ return;
+ }
range = index;
- else {
+ } else {
+ if (val == -1) {
+ UChar32 lo = ch - 1;
+ UChar32 hi = ch;
+ matches.remove(pos + index);
+ if (pos + index + 1 < matches.size() && matches[pos + index + 1] == ch + 1) {
+ hi = ch + 1;
+ matches.remove(pos + index + 1);
+ }
+ addSortedRange(isASCII(ch) ? m_ranges : m_rangesUnicode, lo, hi);
+ return;
+ }
pos += (index+1);
range -= (index+1);
}
@@ -215,17 +314,19 @@ private:
matches.insert(pos, ch);
}
- void addSortedRange(Vector<CharacterRange>& ranges, UChar lo, UChar hi)
+ void addSortedRange(Vector<CharacterRange>& ranges, UChar32 lo, UChar32 hi)
{
- ASSERT(ranges.size() <= UINT_MAX);
- unsigned end = static_cast<unsigned>(ranges.size());
-
+ size_t end = ranges.size();
+
+ if (!U_IS_BMP(hi))
+ m_hasNonBMPCharacters = true;
+
// Simple linear scan - I doubt there are that many ranges anyway...
// feel free to fix this with something faster (eg binary chop).
- for (unsigned i = 0; i < end; ++i) {
+ for (size_t i = 0; i < end; ++i) {
// does the new range fall before the current position in the array
if (hi < ranges[i].begin) {
- // optional optimization: concatenate appending ranges? - may not be worthwhile.
+ // Concatenate appending ranges.
if (hi == (ranges[i].begin - 1)) {
ranges[i].begin = lo;
return;
@@ -233,7 +334,7 @@ private:
ranges.insert(i, CharacterRange(lo, hi));
return;
}
- // Okay, since we didn't hit the last case, the end of the new range is definitely at or after the beginning
+ // Okay, since we didn't hit the last case, the end of the new range is definitely at or after the begining
// If the new range start at or before the end of the last range, then the overlap (if it starts one after the
// end of the last range they concatenate, which is just as good.
if (lo <= (ranges[i].end + 1)) {
@@ -241,18 +342,7 @@ private:
ranges[i].begin = std::min(ranges[i].begin, lo);
ranges[i].end = std::max(ranges[i].end, hi);
- // now check if the new range can subsume any subsequent ranges.
- unsigned next = i+1;
- // each iteration of the loop we will either remove something from the list, or break the loop.
- while (next < ranges.size()) {
- if (ranges[next].begin <= (ranges[i].end + 1)) {
- // the next entry now overlaps / concatenates this one.
- ranges[i].end = std::max(ranges[i].end, ranges[next].end);
- ranges.remove(next);
- } else
- break;
- }
-
+ mergeRangesFrom(ranges, i);
return;
}
}
@@ -261,25 +351,95 @@ private:
ranges.append(CharacterRange(lo, hi));
}
- bool m_isCaseInsensitive;
+ void mergeRangesFrom(Vector<CharacterRange>& ranges, size_t index)
+ {
+ size_t next = index + 1;
+
+ // each iteration of the loop we will either remove something from the list, or break out of the loop.
+ while (next < ranges.size()) {
+ if (ranges[next].begin <= (ranges[index].end + 1)) {
+ // the next entry now overlaps / concatenates with this one.
+ ranges[index].end = std::max(ranges[index].end, ranges[next].end);
+ ranges.remove(next);
+ } else
+ break;
+ }
+
+ }
+
+ void coalesceTables()
+ {
+ auto coalesceMatchesAndRanges = [&](Vector<UChar32>& matches, Vector<CharacterRange>& ranges) {
+
+ size_t matchesIndex = 0;
+ size_t rangesIndex = 0;
+
+ while (matchesIndex < matches.size() && rangesIndex < ranges.size()) {
+ while (matchesIndex < matches.size() && matches[matchesIndex] < ranges[rangesIndex].begin - 1)
+ matchesIndex++;
+
+ if (matchesIndex < matches.size() && matches[matchesIndex] == ranges[rangesIndex].begin - 1) {
+ ranges[rangesIndex].begin = matches[matchesIndex];
+ matches.remove(matchesIndex);
+ }
+
+ while (matchesIndex < matches.size() && matches[matchesIndex] < ranges[rangesIndex].end + 1)
+ matchesIndex++;
+
+ if (matchesIndex < matches.size()) {
+ if (matches[matchesIndex] == ranges[rangesIndex].end + 1) {
+ ranges[rangesIndex].end = matches[matchesIndex];
+ matches.remove(matchesIndex);
+
+ mergeRangesFrom(ranges, rangesIndex);
+ } else
+ matchesIndex++;
+ }
+ }
+ };
+
+ coalesceMatchesAndRanges(m_matches, m_ranges);
+ coalesceMatchesAndRanges(m_matchesUnicode, m_rangesUnicode);
+
+ if (!m_matches.size() && !m_matchesUnicode.size()
+ && m_ranges.size() == 1 && m_rangesUnicode.size() == 1
+ && m_ranges[0].begin == 0 && m_ranges[0].end == 0x7f
+ && m_rangesUnicode[0].begin == 0x80 && m_rangesUnicode[0].end == 0x10ffff)
+ m_anyCharacter = true;
+ }
- Vector<UChar> m_matches;
+ bool hasNonBMPCharacters()
+ {
+ return m_hasNonBMPCharacters;
+ }
+
+ bool anyCharacter()
+ {
+ return m_anyCharacter;
+ }
+
+ bool m_isCaseInsensitive : 1;
+ bool m_hasNonBMPCharacters : 1;
+ bool m_anyCharacter : 1;
+ CanonicalMode m_canonicalMode;
+
+ Vector<UChar32> m_matches;
Vector<CharacterRange> m_ranges;
- Vector<UChar> m_matchesUnicode;
+ Vector<UChar32> m_matchesUnicode;
Vector<CharacterRange> m_rangesUnicode;
};
class YarrPatternConstructor {
public:
- YarrPatternConstructor(YarrPattern& pattern)
+ YarrPatternConstructor(YarrPattern& pattern, void* stackLimit)
: m_pattern(pattern)
- , m_characterClassConstructor(pattern.m_ignoreCase)
- , m_invertParentheticalAssertion(false)
+ , m_characterClassConstructor(pattern.ignoreCase(), pattern.unicode() ? CanonicalMode::Unicode : CanonicalMode::UCS2)
+ , m_stackLimit(stackLimit)
{
- OwnPtr<PatternDisjunction> body = adoptPtr(new PatternDisjunction);
+ auto body = std::make_unique<PatternDisjunction>();
m_pattern.m_body = body.get();
m_alternative = body->addNewAlternative();
- m_pattern.m_disjunctions.append(body.release());
+ m_pattern.m_disjunctions.append(WTFMove(body));
}
~YarrPatternConstructor()
@@ -291,15 +451,15 @@ public:
m_pattern.reset();
m_characterClassConstructor.reset();
- OwnPtr<PatternDisjunction> body = adoptPtr(new PatternDisjunction);
+ auto body = std::make_unique<PatternDisjunction>();
m_pattern.m_body = body.get();
m_alternative = body->addNewAlternative();
- m_pattern.m_disjunctions.append(body.release());
+ m_pattern.m_disjunctions.append(WTFMove(body));
}
void assertionBOL()
{
- if (!m_alternative->m_terms.size() & !m_invertParentheticalAssertion) {
+ if (!m_alternative->m_terms.size() && !m_invertParentheticalAssertion) {
m_alternative->m_startsWithBOL = true;
m_alternative->m_containsBOL = true;
m_pattern.m_containsBOL = true;
@@ -315,41 +475,51 @@ public:
m_alternative->m_terms.append(PatternTerm::WordBoundary(invert));
}
- void atomPatternCharacter(UChar ch)
+ void atomPatternCharacter(UChar32 ch)
{
// We handle case-insensitive checking of unicode characters which do have both
// cases by handling them as if they were defined using a CharacterClass.
- if (!m_pattern.m_ignoreCase || isASCII(ch)) {
+ if (!m_pattern.ignoreCase() || (isASCII(ch) && !m_pattern.unicode())) {
m_alternative->m_terms.append(PatternTerm(ch));
return;
}
- UCS2CanonicalizationRange* info = rangeInfoFor(ch);
+ const CanonicalizationRange* info = canonicalRangeInfoFor(ch, m_pattern.unicode() ? CanonicalMode::Unicode : CanonicalMode::UCS2);
if (info->type == CanonicalizeUnique) {
m_alternative->m_terms.append(PatternTerm(ch));
return;
}
m_characterClassConstructor.putUnicodeIgnoreCase(ch, info);
- OwnPtr<CharacterClass> newCharacterClass = m_characterClassConstructor.charClass();
+ auto newCharacterClass = m_characterClassConstructor.charClass();
m_alternative->m_terms.append(PatternTerm(newCharacterClass.get(), false));
- m_pattern.m_userCharacterClasses.append(newCharacterClass.release());
+ m_pattern.m_userCharacterClasses.append(WTFMove(newCharacterClass));
}
void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert)
{
switch (classID) {
- case DigitClassID:
+ case BuiltInCharacterClassID::DigitClassID:
m_alternative->m_terms.append(PatternTerm(m_pattern.digitsCharacterClass(), invert));
break;
- case SpaceClassID:
+ case BuiltInCharacterClassID::SpaceClassID:
m_alternative->m_terms.append(PatternTerm(m_pattern.spacesCharacterClass(), invert));
break;
- case WordClassID:
- m_alternative->m_terms.append(PatternTerm(m_pattern.wordcharCharacterClass(), invert));
+ case BuiltInCharacterClassID::WordClassID:
+ if (m_pattern.unicode() && m_pattern.ignoreCase())
+ m_alternative->m_terms.append(PatternTerm(m_pattern.wordUnicodeIgnoreCaseCharCharacterClass(), invert));
+ else
+ m_alternative->m_terms.append(PatternTerm(m_pattern.wordcharCharacterClass(), invert));
+ break;
+ case BuiltInCharacterClassID::DotClassID:
+ ASSERT(!invert);
+ if (m_pattern.dotAll())
+ m_alternative->m_terms.append(PatternTerm(m_pattern.anyCharacterClass(), false));
+ else
+ m_alternative->m_terms.append(PatternTerm(m_pattern.newlineCharacterClass(), true));
break;
- case NewlineClassID:
- m_alternative->m_terms.append(PatternTerm(m_pattern.newlineCharacterClass(), invert));
+ default:
+ m_alternative->m_terms.append(PatternTerm(m_pattern.unicodeCharacterClassFor(classID), invert));
break;
}
}
@@ -359,64 +529,83 @@ public:
m_invertCharacterClass = invert;
}
- void atomCharacterClassAtom(UChar ch)
+ void atomCharacterClassAtom(UChar32 ch)
{
m_characterClassConstructor.putChar(ch);
}
- void atomCharacterClassRange(UChar begin, UChar end)
+ void atomCharacterClassRange(UChar32 begin, UChar32 end)
{
m_characterClassConstructor.putRange(begin, end);
}
void atomCharacterClassBuiltIn(BuiltInCharacterClassID classID, bool invert)
{
- ASSERT(classID != NewlineClassID);
+ ASSERT(classID != BuiltInCharacterClassID::DotClassID);
switch (classID) {
- case DigitClassID:
+ case BuiltInCharacterClassID::DigitClassID:
m_characterClassConstructor.append(invert ? m_pattern.nondigitsCharacterClass() : m_pattern.digitsCharacterClass());
break;
- case SpaceClassID:
+ case BuiltInCharacterClassID::SpaceClassID:
m_characterClassConstructor.append(invert ? m_pattern.nonspacesCharacterClass() : m_pattern.spacesCharacterClass());
break;
- case WordClassID:
- m_characterClassConstructor.append(invert ? m_pattern.nonwordcharCharacterClass() : m_pattern.wordcharCharacterClass());
+ case BuiltInCharacterClassID::WordClassID:
+ if (m_pattern.unicode() && m_pattern.ignoreCase())
+ m_characterClassConstructor.append(invert ? m_pattern.nonwordUnicodeIgnoreCaseCharCharacterClass() : m_pattern.wordUnicodeIgnoreCaseCharCharacterClass());
+ else
+ m_characterClassConstructor.append(invert ? m_pattern.nonwordcharCharacterClass() : m_pattern.wordcharCharacterClass());
break;
default:
- RELEASE_ASSERT_NOT_REACHED();
+ if (!invert)
+ m_characterClassConstructor.append(m_pattern.unicodeCharacterClassFor(classID));
+ else
+ m_characterClassConstructor.appendInverted(m_pattern.unicodeCharacterClassFor(classID));
}
}
void atomCharacterClassEnd()
{
- OwnPtr<CharacterClass> newCharacterClass = m_characterClassConstructor.charClass();
+ auto newCharacterClass = m_characterClassConstructor.charClass();
+
+ if (!m_invertCharacterClass && newCharacterClass.get()->m_anyCharacter) {
+ m_alternative->m_terms.append(PatternTerm(m_pattern.anyCharacterClass(), false));
+ return;
+ }
m_alternative->m_terms.append(PatternTerm(newCharacterClass.get(), m_invertCharacterClass));
- m_pattern.m_userCharacterClasses.append(newCharacterClass.release());
+ m_pattern.m_userCharacterClasses.append(WTFMove(newCharacterClass));
}
- void atomParenthesesSubpatternBegin(bool capture = true)
+ void atomParenthesesSubpatternBegin(bool capture = true, std::optional<String> optGroupName = std::nullopt)
{
unsigned subpatternId = m_pattern.m_numSubpatterns + 1;
- if (capture)
+ if (capture) {
m_pattern.m_numSubpatterns++;
+ if (optGroupName) {
+ while (m_pattern.m_captureGroupNames.size() < subpatternId)
+ m_pattern.m_captureGroupNames.append(String());
+ m_pattern.m_captureGroupNames.append(optGroupName.value());
+ m_pattern.m_namedGroupToParenIndex.add(optGroupName.value(), subpatternId);
+ }
+ } else
+ ASSERT(!optGroupName);
- OwnPtr<PatternDisjunction> parenthesesDisjunction = adoptPtr(new PatternDisjunction(m_alternative));
+ auto parenthesesDisjunction = std::make_unique<PatternDisjunction>(m_alternative);
m_alternative->m_terms.append(PatternTerm(PatternTerm::TypeParenthesesSubpattern, subpatternId, parenthesesDisjunction.get(), capture, false));
m_alternative = parenthesesDisjunction->addNewAlternative();
- m_pattern.m_disjunctions.append(parenthesesDisjunction.release());
+ m_pattern.m_disjunctions.append(WTFMove(parenthesesDisjunction));
}
void atomParentheticalAssertionBegin(bool invert = false)
{
- OwnPtr<PatternDisjunction> parenthesesDisjunction = adoptPtr(new PatternDisjunction(m_alternative));
+ auto parenthesesDisjunction = std::make_unique<PatternDisjunction>(m_alternative);
m_alternative->m_terms.append(PatternTerm(PatternTerm::TypeParentheticalAssertion, m_pattern.m_numSubpatterns + 1, parenthesesDisjunction.get(), false, invert));
m_alternative = parenthesesDisjunction->addNewAlternative();
m_invertParentheticalAssertion = invert;
- m_pattern.m_disjunctions.append(parenthesesDisjunction.release());
+ m_pattern.m_disjunctions.append(WTFMove(parenthesesDisjunction));
}
void atomParenthesesEnd()
@@ -429,8 +618,7 @@ public:
PatternTerm& lastTerm = m_alternative->lastTerm();
- ASSERT(parenthesesDisjunction->m_alternatives.size() <= UINT_MAX);
- unsigned numParenAlternatives = static_cast<unsigned>(parenthesesDisjunction->m_alternatives.size());
+ unsigned numParenAlternatives = parenthesesDisjunction->m_alternatives.size();
unsigned numBOLAnchoredAlts = 0;
for (unsigned i = 0; i < numParenAlternatives; i++) {
@@ -478,16 +666,22 @@ public:
m_alternative->m_terms.append(PatternTerm(subpatternId));
}
- // deep copy the argument disjunction. If filterStartsWithBOL is true,
+ void atomNamedBackReference(String subpatternName)
+ {
+ ASSERT(m_pattern.m_namedGroupToParenIndex.find(subpatternName) != m_pattern.m_namedGroupToParenIndex.end());
+ atomBackReference(m_pattern.m_namedGroupToParenIndex.get(subpatternName));
+ }
+
+ // deep copy the argument disjunction. If filterStartsWithBOL is true,
// skip alternatives with m_startsWithBOL set true.
PatternDisjunction* copyDisjunction(PatternDisjunction* disjunction, bool filterStartsWithBOL = false)
{
- OwnPtr<PatternDisjunction> newDisjunction;
+ std::unique_ptr<PatternDisjunction> newDisjunction;
for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) {
PatternAlternative* alternative = disjunction->m_alternatives[alt].get();
if (!filterStartsWithBOL || !alternative->m_startsWithBOL) {
if (!newDisjunction) {
- newDisjunction = adoptPtr(new PatternDisjunction());
+ newDisjunction = std::make_unique<PatternDisjunction>();
newDisjunction->m_parent = disjunction->m_parent;
}
PatternAlternative* newAlternative = newDisjunction->addNewAlternative();
@@ -501,7 +695,7 @@ public:
return 0;
PatternDisjunction* copiedDisjunction = newDisjunction.get();
- m_pattern.m_disjunctions.append(newDisjunction.release());
+ m_pattern.m_disjunctions.append(WTFMove(newDisjunction));
return copiedDisjunction;
}
@@ -512,6 +706,7 @@ public:
PatternTerm termCopy = term;
termCopy.parentheses.disjunction = copyDisjunction(termCopy.parentheses.disjunction, filterStartsWithBOL);
+ m_pattern.m_hasCopiedParenSubexpressions = true;
return termCopy;
}
@@ -527,7 +722,7 @@ public:
PatternTerm& term = m_alternative->lastTerm();
ASSERT(term.type > PatternTerm::TypeAssertionWordBoundary);
- ASSERT((term.quantityCount == 1) && (term.quantityType == QuantifierFixedCount));
+ ASSERT(term.quantityMinCount == 1 && term.quantityMaxCount == 1 && term.quantityType == QuantifierFixedCount);
if (term.type == PatternTerm::TypeParentheticalAssertion) {
// If an assertion is quantified with a minimum count of zero, it can simply be removed.
@@ -549,12 +744,12 @@ public:
return;
}
- if (min == 0)
- term.quantify(max, greedy ? QuantifierGreedy : QuantifierNonGreedy);
- else if (min == max)
- term.quantify(min, QuantifierFixedCount);
+ if (min == max)
+ term.quantify(min, max, QuantifierFixedCount);
+ else if (!min || (term.type == PatternTerm::TypeParenthesesSubpattern && m_pattern.m_hasCopiedParenSubexpressions))
+ term.quantify(min, max, greedy ? QuantifierGreedy : QuantifierNonGreedy);
else {
- term.quantify(min, QuantifierFixedCount);
+ term.quantify(min, min, QuantifierFixedCount);
m_alternative->m_terms.append(copyTerm(term));
// NOTE: this term is interesting from an analysis perspective, in that it can be ignored.....
m_alternative->lastTerm().quantify((max == quantifyInfinite) ? max : max - min, greedy ? QuantifierGreedy : QuantifierNonGreedy);
@@ -568,10 +763,14 @@ public:
m_alternative = m_alternative->m_parent->addNewAlternative();
}
- unsigned setupAlternativeOffsets(PatternAlternative* alternative, unsigned currentCallFrameSize, unsigned initialInputPosition)
+ ErrorCode setupAlternativeOffsets(PatternAlternative* alternative, unsigned currentCallFrameSize, unsigned initialInputPosition, unsigned& newCallFrameSize) WARN_UNUSED_RETURN
{
+ if (UNLIKELY(!isSafeToRecurse()))
+ return ErrorCode::TooManyDisjunctions;
+
+ ErrorCode error = ErrorCode::NoError;
alternative->m_hasFixedSize = true;
- Checked<unsigned> currentInputPosition = initialInputPosition;
+ Checked<unsigned, RecordOverflow> currentInputPosition = initialInputPosition;
for (unsigned i = 0; i < alternative->m_terms.size(); ++i) {
PatternTerm& term = alternative->m_terms[i];
@@ -599,8 +798,14 @@ public:
term.frameLocation = currentCallFrameSize;
currentCallFrameSize += YarrStackSpaceForBackTrackInfoPatternCharacter;
alternative->m_hasFixedSize = false;
+ } else if (m_pattern.unicode()) {
+ Checked<unsigned, RecordOverflow> tempCount = term.quantityMaxCount;
+ tempCount *= U16_LENGTH(term.patternCharacter);
+ if (tempCount.hasOverflowed())
+ return ErrorCode::OffsetTooLarge;
+ currentInputPosition += tempCount;
} else
- currentInputPosition += term.quantityCount;
+ currentInputPosition += term.quantityMaxCount;
break;
case PatternTerm::TypeCharacterClass:
@@ -609,29 +814,39 @@ public:
term.frameLocation = currentCallFrameSize;
currentCallFrameSize += YarrStackSpaceForBackTrackInfoCharacterClass;
alternative->m_hasFixedSize = false;
+ } else if (m_pattern.unicode()) {
+ term.frameLocation = currentCallFrameSize;
+ currentCallFrameSize += YarrStackSpaceForBackTrackInfoCharacterClass;
+ currentInputPosition += term.quantityMaxCount;
+ alternative->m_hasFixedSize = false;
} else
- currentInputPosition += term.quantityCount;
+ currentInputPosition += term.quantityMaxCount;
break;
case PatternTerm::TypeParenthesesSubpattern:
// Note: for fixed once parentheses we will ensure at least the minimum is available; others are on their own.
term.frameLocation = currentCallFrameSize;
- if (term.quantityCount == 1 && !term.parentheses.isCopy) {
- if (term.quantityType != QuantifierFixedCount)
- currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesOnce;
- currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet());
+ if (term.quantityMaxCount == 1 && !term.parentheses.isCopy) {
+ currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesOnce;
+ error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet(), currentCallFrameSize);
+ if (hasError(error))
+ return error;
// If quantity is fixed, then pre-check its minimum size.
if (term.quantityType == QuantifierFixedCount)
currentInputPosition += term.parentheses.disjunction->m_minimumSize;
term.inputPosition = currentInputPosition.unsafeGet();
} else if (term.parentheses.isTerminal) {
currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesTerminal;
- currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet());
+ error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet(), currentCallFrameSize);
+ if (hasError(error))
+ return error;
term.inputPosition = currentInputPosition.unsafeGet();
} else {
term.inputPosition = currentInputPosition.unsafeGet();
- setupDisjunctionOffsets(term.parentheses.disjunction, 0, currentInputPosition.unsafeGet());
currentCallFrameSize += YarrStackSpaceForBackTrackInfoParentheses;
+ error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition.unsafeGet(), currentCallFrameSize);
+ if (hasError(error))
+ return error;
}
// Fixed count of 1 could be accepted, if they have a fixed size *AND* if all alternatives are of the same length.
alternative->m_hasFixedSize = false;
@@ -640,35 +855,53 @@ public:
case PatternTerm::TypeParentheticalAssertion:
term.inputPosition = currentInputPosition.unsafeGet();
term.frameLocation = currentCallFrameSize;
- currentCallFrameSize = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize + YarrStackSpaceForBackTrackInfoParentheticalAssertion, currentInputPosition.unsafeGet());
+ error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize + YarrStackSpaceForBackTrackInfoParentheticalAssertion, currentInputPosition.unsafeGet(), currentCallFrameSize);
+ if (hasError(error))
+ return error;
break;
case PatternTerm::TypeDotStarEnclosure:
+ ASSERT(!m_pattern.m_saveInitialStartValue);
alternative->m_hasFixedSize = false;
term.inputPosition = initialInputPosition;
+ m_pattern.m_initialStartValueFrameLocation = currentCallFrameSize;
+ currentCallFrameSize += YarrStackSpaceForDotStarEnclosure;
+ m_pattern.m_saveInitialStartValue = true;
break;
}
+ if (currentInputPosition.hasOverflowed())
+ return ErrorCode::OffsetTooLarge;
}
alternative->m_minimumSize = (currentInputPosition - initialInputPosition).unsafeGet();
- return currentCallFrameSize;
+ newCallFrameSize = currentCallFrameSize;
+ return error;
}
- unsigned setupDisjunctionOffsets(PatternDisjunction* disjunction, unsigned initialCallFrameSize, unsigned initialInputPosition)
+ ErrorCode setupDisjunctionOffsets(PatternDisjunction* disjunction, unsigned initialCallFrameSize, unsigned initialInputPosition, unsigned& callFrameSize)
{
+ if (UNLIKELY(!isSafeToRecurse()))
+ return ErrorCode::TooManyDisjunctions;
+
if ((disjunction != m_pattern.m_body) && (disjunction->m_alternatives.size() > 1))
initialCallFrameSize += YarrStackSpaceForBackTrackInfoAlternative;
unsigned minimumInputSize = UINT_MAX;
unsigned maximumCallFrameSize = 0;
bool hasFixedSize = true;
+ ErrorCode error = ErrorCode::NoError;
for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) {
PatternAlternative* alternative = disjunction->m_alternatives[alt].get();
- unsigned currentAlternativeCallFrameSize = setupAlternativeOffsets(alternative, initialCallFrameSize, initialInputPosition);
+ unsigned currentAlternativeCallFrameSize;
+ error = setupAlternativeOffsets(alternative, initialCallFrameSize, initialInputPosition, currentAlternativeCallFrameSize);
+ if (hasError(error))
+ return error;
minimumInputSize = std::min(minimumInputSize, alternative->m_minimumSize);
maximumCallFrameSize = std::max(maximumCallFrameSize, currentAlternativeCallFrameSize);
hasFixedSize &= alternative->m_hasFixedSize;
+ if (alternative->m_minimumSize > INT_MAX)
+ m_pattern.m_containsUnsignedLengthPattern = true;
}
ASSERT(minimumInputSize != UINT_MAX);
@@ -677,12 +910,15 @@ public:
disjunction->m_hasFixedSize = hasFixedSize;
disjunction->m_minimumSize = minimumInputSize;
disjunction->m_callFrameSize = maximumCallFrameSize;
- return maximumCallFrameSize;
+ callFrameSize = maximumCallFrameSize;
+ return error;
}
- void setupOffsets()
+ ErrorCode setupOffsets()
{
- setupDisjunctionOffsets(m_pattern.m_body, 0, 0);
+ // FIXME: Yarr should not use the stack to handle subpatterns (rdar://problem/26436314).
+ unsigned ignoredCallFrameSize;
+ return setupDisjunctionOffsets(m_pattern.m_body, 0, 0, ignoredCallFrameSize);
}
// This optimization identifies sets of parentheses that we will never need to backtrack.
@@ -699,14 +935,15 @@ public:
if (m_pattern.m_numSubpatterns)
return;
- Vector<OwnPtr<PatternAlternative> >& alternatives = m_pattern.m_body->m_alternatives;
+ Vector<std::unique_ptr<PatternAlternative>>& alternatives = m_pattern.m_body->m_alternatives;
for (size_t i = 0; i < alternatives.size(); ++i) {
Vector<PatternTerm>& terms = alternatives[i]->m_terms;
if (terms.size()) {
PatternTerm& term = terms.last();
if (term.type == PatternTerm::TypeParenthesesSubpattern
&& term.quantityType == QuantifierGreedy
- && term.quantityCount == quantifyInfinite
+ && term.quantityMinCount == 0
+ && term.quantityMaxCount == quantifyInfinite
&& !term.capture())
term.parentheses.isTerminal = true;
}
@@ -722,7 +959,7 @@ public:
// At this point, this is only valid for non-multiline expressions.
PatternDisjunction* disjunction = m_pattern.m_body;
- if (!m_pattern.m_containsBOL || m_pattern.m_multiline)
+ if (!m_pattern.m_containsBOL || m_pattern.multiline())
return;
PatternDisjunction* loopDisjunction = copyDisjunction(disjunction, true);
@@ -740,11 +977,12 @@ public:
}
}
- bool containsCapturingTerms(PatternAlternative* alternative, size_t firstTermIndex, size_t lastTermIndex)
+ bool containsCapturingTerms(PatternAlternative* alternative, size_t firstTermIndex, size_t endIndex)
{
Vector<PatternTerm>& terms = alternative->m_terms;
- for (size_t termIndex = firstTermIndex; termIndex <= lastTermIndex; ++termIndex) {
+ ASSERT(endIndex <= terms.size());
+ for (size_t termIndex = firstTermIndex; termIndex < endIndex; ++termIndex) {
PatternTerm& term = terms[termIndex];
if (term.m_capture)
@@ -753,7 +991,7 @@ public:
if (term.type == PatternTerm::TypeParenthesesSubpattern) {
PatternDisjunction* nestedDisjunction = term.parentheses.disjunction;
for (unsigned alt = 0; alt < nestedDisjunction->m_alternatives.size(); ++alt) {
- if (containsCapturingTerms(nestedDisjunction->m_alternatives[alt].get(), 0, nestedDisjunction->m_alternatives[alt]->m_terms.size() - 1))
+ if (containsCapturingTerms(nestedDisjunction->m_alternatives[alt].get(), 0, nestedDisjunction->m_alternatives[alt]->m_terms.size()))
return true;
}
}
@@ -769,16 +1007,17 @@ public:
// beginning and the end of the match.
void optimizeDotStarWrappedExpressions()
{
- Vector<OwnPtr<PatternAlternative> >& alternatives = m_pattern.m_body->m_alternatives;
+ Vector<std::unique_ptr<PatternAlternative>>& alternatives = m_pattern.m_body->m_alternatives;
if (alternatives.size() != 1)
return;
+ CharacterClass* dotCharacterClass = m_pattern.dotAll() ? m_pattern.anyCharacterClass() : m_pattern.newlineCharacterClass();
PatternAlternative* alternative = alternatives[0].get();
Vector<PatternTerm>& terms = alternative->m_terms;
if (terms.size() >= 3) {
bool startsWithBOL = false;
bool endsWithEOL = false;
- size_t termIndex, firstExpressionTerm, lastExpressionTerm;
+ size_t termIndex, firstExpressionTerm;
termIndex = 0;
if (terms[termIndex].type == PatternTerm::TypeAssertionBOL) {
@@ -787,7 +1026,10 @@ public:
}
PatternTerm& firstNonAnchorTerm = terms[termIndex];
- if ((firstNonAnchorTerm.type != PatternTerm::TypeCharacterClass) || (firstNonAnchorTerm.characterClass != m_pattern.newlineCharacterClass()) || !((firstNonAnchorTerm.quantityType == QuantifierGreedy) || (firstNonAnchorTerm.quantityType == QuantifierNonGreedy)))
+ if (firstNonAnchorTerm.type != PatternTerm::TypeCharacterClass
+ || firstNonAnchorTerm.characterClass != dotCharacterClass
+ || firstNonAnchorTerm.quantityMinCount
+ || firstNonAnchorTerm.quantityMaxCount != quantifyInfinite)
return;
firstExpressionTerm = termIndex + 1;
@@ -799,16 +1041,19 @@ public:
}
PatternTerm& lastNonAnchorTerm = terms[termIndex];
- if ((lastNonAnchorTerm.type != PatternTerm::TypeCharacterClass) || (lastNonAnchorTerm.characterClass != m_pattern.newlineCharacterClass()) || (lastNonAnchorTerm.quantityType != QuantifierGreedy))
+ if (lastNonAnchorTerm.type != PatternTerm::TypeCharacterClass
+ || lastNonAnchorTerm.characterClass != dotCharacterClass
+ || lastNonAnchorTerm.quantityType != QuantifierGreedy
+ || lastNonAnchorTerm.quantityMinCount
+ || lastNonAnchorTerm.quantityMaxCount != quantifyInfinite)
return;
-
- lastExpressionTerm = termIndex - 1;
- if (firstExpressionTerm > lastExpressionTerm)
+ size_t endIndex = termIndex;
+ if (firstExpressionTerm >= endIndex)
return;
- if (!containsCapturingTerms(alternative, firstExpressionTerm, lastExpressionTerm)) {
- for (termIndex = terms.size() - 1; termIndex > lastExpressionTerm; --termIndex)
+ if (!containsCapturingTerms(alternative, firstExpressionTerm, endIndex)) {
+ for (termIndex = terms.size() - 1; termIndex >= endIndex; --termIndex)
terms.remove(termIndex);
for (termIndex = firstExpressionTerm; termIndex > 0; --termIndex)
@@ -822,62 +1067,445 @@ public:
}
private:
+ bool isSafeToRecurse() const
+ {
+ if (!m_stackLimit)
+ return true;
+ int8_t* curr = reinterpret_cast<int8_t*>(&curr);
+ int8_t* limit = reinterpret_cast<int8_t*>(m_stackLimit);
+ return curr >= limit;
+ }
+
YarrPattern& m_pattern;
PatternAlternative* m_alternative;
CharacterClassConstructor m_characterClassConstructor;
+ void* m_stackLimit;
bool m_invertCharacterClass;
- bool m_invertParentheticalAssertion;
+ bool m_invertParentheticalAssertion { false };
};
-const char* YarrPattern::compile(const String& patternString)
+ErrorCode YarrPattern::compile(const String& patternString, void* stackLimit)
{
- YarrPatternConstructor constructor(*this);
+ YarrPatternConstructor constructor(*this, stackLimit);
- if (const char* error = parse(constructor, patternString))
- return error;
+ if (m_flags == InvalidFlags)
+ return ErrorCode::InvalidRegularExpressionFlags;
+
+ {
+ ErrorCode error = parse(constructor, patternString, unicode());
+ if (hasError(error))
+ return error;
+ }
// If the pattern contains illegal backreferences reset & reparse.
// Quoting Netscape's "What's new in JavaScript 1.2",
// "Note: if the number of left parentheses is less than the number specified
// in \#, the \# is taken as an octal escape as described in the next row."
if (containsIllegalBackReference()) {
+ if (unicode())
+ return ErrorCode::InvalidBackreference;
+
unsigned numSubpatterns = m_numSubpatterns;
constructor.reset();
-#if !ASSERT_DISABLED
- const char* error =
-#endif
- parse(constructor, patternString, numSubpatterns);
-
- ASSERT(!error);
+ ErrorCode error = parse(constructor, patternString, unicode(), numSubpatterns);
+ ASSERT_UNUSED(error, !hasError(error));
ASSERT(numSubpatterns == m_numSubpatterns);
}
constructor.checkForTerminalParentheses();
constructor.optimizeDotStarWrappedExpressions();
constructor.optimizeBOL();
-
- constructor.setupOffsets();
- return 0;
+ {
+ ErrorCode error = constructor.setupOffsets();
+ if (hasError(error))
+ return error;
+ }
+
+ if (Options::dumpCompiledRegExpPatterns())
+ dumpPattern(patternString);
+
+ return ErrorCode::NoError;
}
-YarrPattern::YarrPattern(const String& pattern, bool ignoreCase, bool multiline, const char** error)
- : m_ignoreCase(ignoreCase)
- , m_multiline(multiline)
- , m_containsBackreferences(false)
+YarrPattern::YarrPattern(const String& pattern, RegExpFlags flags, ErrorCode& error, void* stackLimit)
+ : m_containsBackreferences(false)
, m_containsBOL(false)
- , m_numSubpatterns(0)
- , m_maxBackReference(0)
- , newlineCached(0)
- , digitsCached(0)
- , spacesCached(0)
- , wordcharCached(0)
- , nondigitsCached(0)
- , nonspacesCached(0)
- , nonwordcharCached(0)
+ , m_containsUnsignedLengthPattern(false)
+ , m_hasCopiedParenSubexpressions(false)
+ , m_saveInitialStartValue(false)
+ , m_flags(flags)
+{
+ error = compile(pattern, stackLimit);
+}
+
+void indentForNestingLevel(PrintStream& out, unsigned nestingDepth)
+{
+ out.print(" ");
+ for (; nestingDepth; --nestingDepth)
+ out.print(" ");
+}
+
+void dumpUChar32(PrintStream& out, UChar32 c)
+{
+ if (c >= ' '&& c <= 0xff)
+ out.printf("'%c'", static_cast<char>(c));
+ else
+ out.printf("0x%04x", c);
+}
+
+void dumpCharacterClass(PrintStream& out, YarrPattern* pattern, CharacterClass* characterClass)
+{
+ if (characterClass == pattern->anyCharacterClass())
+ out.print("<any character>");
+ else if (characterClass == pattern->newlineCharacterClass())
+ out.print("<newline>");
+ else if (characterClass == pattern->digitsCharacterClass())
+ out.print("<digits>");
+ else if (characterClass == pattern->spacesCharacterClass())
+ out.print("<whitespace>");
+ else if (characterClass == pattern->wordcharCharacterClass())
+ out.print("<word>");
+ else if (characterClass == pattern->wordUnicodeIgnoreCaseCharCharacterClass())
+ out.print("<unicode ignore case>");
+ else if (characterClass == pattern->nondigitsCharacterClass())
+ out.print("<non-digits>");
+ else if (characterClass == pattern->nonspacesCharacterClass())
+ out.print("<non-whitespace>");
+ else if (characterClass == pattern->nonwordcharCharacterClass())
+ out.print("<non-word>");
+ else if (characterClass == pattern->nonwordUnicodeIgnoreCaseCharCharacterClass())
+ out.print("<unicode non-ignore case>");
+ else {
+ bool needMatchesRangesSeperator = false;
+
+ auto dumpMatches = [&] (const char* prefix, Vector<UChar32> matches) {
+ size_t matchesSize = matches.size();
+ if (matchesSize) {
+ if (needMatchesRangesSeperator)
+ out.print(",");
+ needMatchesRangesSeperator = true;
+
+ out.print(prefix, ":(");
+ for (size_t i = 0; i < matchesSize; ++i) {
+ if (i)
+ out.print(",");
+ dumpUChar32(out, matches[i]);
+ }
+ out.print(")");
+ }
+ };
+
+ auto dumpRanges = [&] (const char* prefix, Vector<CharacterRange> ranges) {
+ size_t rangeSize = ranges.size();
+ if (rangeSize) {
+ if (needMatchesRangesSeperator)
+ out.print(",");
+ needMatchesRangesSeperator = true;
+
+ out.print(prefix, " ranges:(");
+ for (size_t i = 0; i < rangeSize; ++i) {
+ if (i)
+ out.print(",");
+ CharacterRange range = ranges[i];
+ out.print("(");
+ dumpUChar32(out, range.begin);
+ out.print("..");
+ dumpUChar32(out, range.end);
+ out.print(")");
+ }
+ out.print(")");
+ }
+ };
+
+ out.print("[");
+ dumpMatches("ASCII", characterClass->m_matches);
+ dumpRanges("ASCII", characterClass->m_ranges);
+ dumpMatches("Unicode", characterClass->m_matchesUnicode);
+ dumpRanges("Unicode", characterClass->m_rangesUnicode);
+ out.print("]");
+ }
+}
+
+void PatternAlternative::dump(PrintStream& out, YarrPattern* thisPattern, unsigned nestingDepth)
+{
+ out.print("minimum size: ", m_minimumSize);
+ if (m_hasFixedSize)
+ out.print(",fixed size");
+ if (m_onceThrough)
+ out.print(",once through");
+ if (m_startsWithBOL)
+ out.print(",starts with ^");
+ if (m_containsBOL)
+ out.print(",contains ^");
+ out.print("\n");
+
+ for (size_t i = 0; i < m_terms.size(); ++i)
+ m_terms[i].dump(out, thisPattern, nestingDepth);
+}
+
+void PatternTerm::dumpQuantifier(PrintStream& out)
+{
+ if (quantityType == QuantifierFixedCount && quantityMinCount == 1 && quantityMaxCount == 1)
+ return;
+ out.print(" {", quantityMinCount.unsafeGet());
+ if (quantityMinCount != quantityMaxCount) {
+ if (quantityMaxCount == UINT_MAX)
+ out.print(",...");
+ else
+ out.print(",", quantityMaxCount.unsafeGet());
+ }
+ out.print("}");
+ if (quantityType == QuantifierGreedy)
+ out.print(" greedy");
+ else if (quantityType == QuantifierNonGreedy)
+ out.print(" non-greedy");
+}
+
+void PatternTerm::dump(PrintStream& out, YarrPattern* thisPattern, unsigned nestingDepth)
+{
+ indentForNestingLevel(out, nestingDepth);
+
+ if (type != TypeParenthesesSubpattern && type != TypeParentheticalAssertion) {
+ if (invert())
+ out.print("not ");
+ }
+
+ switch (type) {
+ case TypeAssertionBOL:
+ out.println("BOL");
+ break;
+ case TypeAssertionEOL:
+ out.println("EOL");
+ break;
+ case TypeAssertionWordBoundary:
+ out.println("word boundary");
+ break;
+ case TypePatternCharacter:
+ out.printf("character ");
+ out.printf("inputPosition %u ", inputPosition);
+ if (thisPattern->ignoreCase() && isASCIIAlpha(patternCharacter)) {
+ dumpUChar32(out, toASCIIUpper(patternCharacter));
+ out.print("/");
+ dumpUChar32(out, toASCIILower(patternCharacter));
+ } else
+ dumpUChar32(out, patternCharacter);
+ dumpQuantifier(out);
+ if (quantityType != QuantifierFixedCount)
+ out.print(",frame location ", frameLocation);
+ out.println();
+ break;
+ case TypeCharacterClass:
+ out.print("character class ");
+ if (characterClass->m_anyCharacter)
+ out.print("<any character>");
+ else if (characterClass == thisPattern->newlineCharacterClass())
+ out.print("<newline>");
+ else if (characterClass == thisPattern->digitsCharacterClass())
+ out.print("<digits>");
+ else if (characterClass == thisPattern->spacesCharacterClass())
+ out.print("<whitespace>");
+ else if (characterClass == thisPattern->wordcharCharacterClass())
+ out.print("<word>");
+ else if (characterClass == thisPattern->wordUnicodeIgnoreCaseCharCharacterClass())
+ out.print("<unicode ignore case>");
+ else if (characterClass == thisPattern->nondigitsCharacterClass())
+ out.print("<non-digits>");
+ else if (characterClass == thisPattern->nonspacesCharacterClass())
+ out.print("<non-whitespace>");
+ else if (characterClass == thisPattern->nonwordcharCharacterClass())
+ out.print("<non-word>");
+ else if (characterClass == thisPattern->nonwordUnicodeIgnoreCaseCharCharacterClass())
+ out.print("<unicode non-ignore case>");
+ else {
+ bool needMatchesRangesSeperator = false;
+
+ auto dumpMatches = [&] (const char* prefix, Vector<UChar32> matches) {
+ size_t matchesSize = matches.size();
+ if (matchesSize) {
+ if (needMatchesRangesSeperator)
+ out.print(",");
+ needMatchesRangesSeperator = true;
+
+ out.print(prefix, ":(");
+ for (size_t i = 0; i < matchesSize; ++i) {
+ if (i)
+ out.print(",");
+ dumpUChar32(out, matches[i]);
+ }
+ out.print(")");
+ }
+ };
+
+ auto dumpRanges = [&] (const char* prefix, Vector<CharacterRange> ranges) {
+ size_t rangeSize = ranges.size();
+ if (rangeSize) {
+ if (needMatchesRangesSeperator)
+ out.print(",");
+ needMatchesRangesSeperator = true;
+
+ out.print(prefix, " ranges:(");
+ for (size_t i = 0; i < rangeSize; ++i) {
+ if (i)
+ out.print(",");
+ CharacterRange range = ranges[i];
+ out.print("(");
+ dumpUChar32(out, range.begin);
+ out.print("..");
+ dumpUChar32(out, range.end);
+ out.print(")");
+ }
+ out.print(")");
+ }
+ };
+
+ out.print("[");
+ dumpMatches("ASCII", characterClass->m_matches);
+ dumpRanges("ASCII", characterClass->m_ranges);
+ dumpMatches("Unicode", characterClass->m_matchesUnicode);
+ dumpRanges("Unicode", characterClass->m_rangesUnicode);
+ out.print("]");
+ }
+ dumpQuantifier(out);
+ if (quantityType != QuantifierFixedCount || thisPattern->unicode())
+ out.print(",frame location ", frameLocation);
+ out.println();
+ break;
+ case TypeBackReference:
+ out.print("back reference to subpattern #", backReferenceSubpatternId);
+ out.println(",frame location ", frameLocation);
+ break;
+ case TypeForwardReference:
+ out.println("forward reference");
+ break;
+ case TypeParenthesesSubpattern:
+ if (m_capture)
+ out.print("captured ");
+ else
+ out.print("non-captured ");
+
+ FALLTHROUGH;
+ case TypeParentheticalAssertion:
+ if (m_invert)
+ out.print("inverted ");
+
+ if (type == TypeParenthesesSubpattern)
+ out.print("subpattern");
+ else if (type == TypeParentheticalAssertion)
+ out.print("assertion");
+
+ if (m_capture)
+ out.print(" #", parentheses.subpatternId);
+
+ dumpQuantifier(out);
+
+ if (parentheses.isCopy)
+ out.print(",copy");
+
+ if (parentheses.isTerminal)
+ out.print(",terminal");
+
+ out.println(",frame location ", frameLocation);
+
+ if (parentheses.disjunction->m_alternatives.size() > 1) {
+ indentForNestingLevel(out, nestingDepth + 1);
+ unsigned alternativeFrameLocation = frameLocation;
+ if (quantityMaxCount == 1 && !parentheses.isCopy)
+ alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesOnce;
+ else if (parentheses.isTerminal)
+ alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParenthesesTerminal;
+ else
+ alternativeFrameLocation += YarrStackSpaceForBackTrackInfoParentheses;
+ out.println("alternative list,frame location ", alternativeFrameLocation);
+ }
+
+ parentheses.disjunction->dump(out, thisPattern, nestingDepth + 1);
+ break;
+ case TypeDotStarEnclosure:
+ out.println(".* enclosure,frame location ", thisPattern->m_initialStartValueFrameLocation);
+ break;
+ }
+}
+
+void PatternDisjunction::dump(PrintStream& out, YarrPattern* thisPattern, unsigned nestingDepth = 0)
+{
+ unsigned alternativeCount = m_alternatives.size();
+ for (unsigned i = 0; i < alternativeCount; ++i) {
+ indentForNestingLevel(out, nestingDepth);
+ if (alternativeCount > 1)
+ out.print("alternative #", i, ": ");
+ m_alternatives[i].get()->dump(out, thisPattern, nestingDepth + (alternativeCount > 1));
+ }
+}
+
+void YarrPattern::dumpPattern(const String& patternString)
+{
+ dumpPattern(WTF::dataFile(), patternString);
+}
+
+void YarrPattern::dumpPattern(PrintStream& out, const String& patternString)
+{
+ out.print("RegExp pattern for /");
+ out.print(patternString);
+ out.print("/");
+ if (global())
+ out.print("g");
+ if (ignoreCase())
+ out.print("i");
+ if (multiline())
+ out.print("m");
+ if (unicode())
+ out.print("u");
+ if (sticky())
+ out.print("y");
+ if (m_flags != NoFlags) {
+ bool printSeperator = false;
+ out.print(" (");
+ if (global()) {
+ out.print("global");
+ printSeperator = true;
+ }
+ if (ignoreCase()) {
+ if (printSeperator)
+ out.print("|");
+ out.print("ignore case");
+ printSeperator = true;
+ }
+ if (multiline()) {
+ if (printSeperator)
+ out.print("|");
+ out.print("multiline");
+ printSeperator = true;
+ }
+ if (unicode()) {
+ if (printSeperator)
+ out.print("|");
+ out.print("unicode");
+ printSeperator = true;
+ }
+ if (sticky()) {
+ if (printSeperator)
+ out.print("|");
+ out.print("sticky");
+ printSeperator = true;
+ }
+ out.print(")");
+ }
+ out.print(":\n");
+ if (m_body->m_callFrameSize)
+ out.print(" callframe size: ", m_body->m_callFrameSize, "\n");
+ m_body->dump(out, this);
+}
+
+std::unique_ptr<CharacterClass> anycharCreate()
{
- *error = compile(pattern);
+ auto characterClass = std::make_unique<CharacterClass>();
+ characterClass->m_ranges.append(CharacterRange(0x00, 0x7f));
+ characterClass->m_rangesUnicode.append(CharacterRange(0x0080, 0x10ffff));
+ characterClass->m_hasNonBMPCharacters = true;
+ characterClass->m_anyCharacter = true;
+ return characterClass;
}
-} }
+} } // namespace JSC::Yarr
diff --git a/src/3rdparty/masm/yarr/YarrPattern.h b/src/3rdparty/masm/yarr/YarrPattern.h
index e7d187c2b3..59decbac46 100644
--- a/src/3rdparty/masm/yarr/YarrPattern.h
+++ b/src/3rdparty/masm/yarr/YarrPattern.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2009, 2013-2017 Apple Inc. All rights reserved.
* Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged
*
* Redistribution and use in source and binary forms, with or without
@@ -24,26 +24,27 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef YarrPattern_h
-#define YarrPattern_h
+#pragma once
+#include "RegExpKey.h"
+#include "YarrErrorCode.h"
+#include "YarrUnicodeProperties.h"
#include <wtf/CheckedArithmetic.h>
-#include <wtf/OwnPtr.h>
-#include <wtf/PassOwnPtr.h>
-#include <wtf/RefCounted.h>
+#include <wtf/HashMap.h>
+#include <wtf/PrintStream.h>
#include <wtf/Vector.h>
#include <wtf/text/WTFString.h>
-#include <wtf/unicode/Unicode.h>
namespace JSC { namespace Yarr {
+struct YarrPattern;
struct PatternDisjunction;
struct CharacterRange {
- UChar begin;
- UChar end;
+ UChar32 begin { 0 };
+ UChar32 end { 0x10ffff };
- CharacterRange(UChar begin, UChar end)
+ CharacterRange(UChar32 begin, UChar32 end)
: begin(begin)
, end(end)
{
@@ -58,20 +59,38 @@ public:
// specified matches and ranges)
CharacterClass()
: m_table(0)
+ , m_hasNonBMPCharacters(false)
+ , m_anyCharacter(false)
{
}
CharacterClass(const char* table, bool inverted)
: m_table(table)
, m_tableInverted(inverted)
+ , m_hasNonBMPCharacters(false)
+ , m_anyCharacter(false)
{
}
- Vector<UChar> m_matches;
+ CharacterClass(std::initializer_list<UChar32> matches, std::initializer_list<CharacterRange> ranges, std::initializer_list<UChar32> matchesUnicode, std::initializer_list<CharacterRange> rangesUnicode)
+ : m_matches(matches)
+ , m_ranges(ranges)
+ , m_matchesUnicode(matchesUnicode)
+ , m_rangesUnicode(rangesUnicode)
+ , m_table(0)
+ , m_tableInverted(false)
+ , m_hasNonBMPCharacters(false)
+ , m_anyCharacter(false)
+ {
+ }
+
+ Vector<UChar32> m_matches;
Vector<CharacterRange> m_ranges;
- Vector<UChar> m_matchesUnicode;
+ Vector<UChar32> m_matchesUnicode;
Vector<CharacterRange> m_rangesUnicode;
const char* m_table;
- bool m_tableInverted;
+ bool m_tableInverted : 1;
+ bool m_hasNonBMPCharacters : 1;
+ bool m_anyCharacter : 1;
};
enum QuantifierType {
@@ -96,7 +115,7 @@ struct PatternTerm {
bool m_capture :1;
bool m_invert :1;
union {
- UChar patternCharacter;
+ UChar32 patternCharacter;
CharacterClass* characterClass;
unsigned backReferenceSubpatternId;
struct {
@@ -112,18 +131,19 @@ struct PatternTerm {
} anchors;
};
QuantifierType quantityType;
- Checked<unsigned> quantityCount;
- int inputPosition;
+ Checked<unsigned> quantityMinCount;
+ Checked<unsigned> quantityMaxCount;
+ unsigned inputPosition;
unsigned frameLocation;
- PatternTerm(UChar ch)
+ PatternTerm(UChar32 ch)
: type(PatternTerm::TypePatternCharacter)
, m_capture(false)
, m_invert(false)
{
patternCharacter = ch;
quantityType = QuantifierFixedCount;
- quantityCount = 1;
+ quantityMinCount = quantityMaxCount = 1;
}
PatternTerm(CharacterClass* charClass, bool invert)
@@ -133,7 +153,7 @@ struct PatternTerm {
{
characterClass = charClass;
quantityType = QuantifierFixedCount;
- quantityCount = 1;
+ quantityMinCount = quantityMaxCount = 1;
}
PatternTerm(Type type, unsigned subpatternId, PatternDisjunction* disjunction, bool capture = false, bool invert = false)
@@ -146,7 +166,7 @@ struct PatternTerm {
parentheses.isCopy = false;
parentheses.isTerminal = false;
quantityType = QuantifierFixedCount;
- quantityCount = 1;
+ quantityMinCount = quantityMaxCount = 1;
}
PatternTerm(Type type, bool invert = false)
@@ -155,7 +175,7 @@ struct PatternTerm {
, m_invert(invert)
{
quantityType = QuantifierFixedCount;
- quantityCount = 1;
+ quantityMinCount = quantityMaxCount = 1;
}
PatternTerm(unsigned spatternId)
@@ -165,7 +185,7 @@ struct PatternTerm {
{
backReferenceSubpatternId = spatternId;
quantityType = QuantifierFixedCount;
- quantityCount = 1;
+ quantityMinCount = quantityMaxCount = 1;
}
PatternTerm(bool bolAnchor, bool eolAnchor)
@@ -176,7 +196,7 @@ struct PatternTerm {
anchors.bolAnchor = bolAnchor;
anchors.eolAnchor = eolAnchor;
quantityType = QuantifierFixedCount;
- quantityCount = 1;
+ quantityMinCount = quantityMaxCount = 1;
}
static PatternTerm ForwardReference()
@@ -208,12 +228,32 @@ struct PatternTerm {
{
return m_capture;
}
-
+
+ bool containsAnyCaptures()
+ {
+ ASSERT(this->type == TypeParenthesesSubpattern);
+ return parentheses.lastSubpatternId >= parentheses.subpatternId;
+ }
+
void quantify(unsigned count, QuantifierType type)
{
- quantityCount = count;
+ quantityMinCount = 0;
+ quantityMaxCount = count;
quantityType = type;
}
+
+ void quantify(unsigned minCount, unsigned maxCount, QuantifierType type)
+ {
+ // Currently only Parentheses can specify a non-zero min with a different max.
+ ASSERT(this->type == TypeParenthesesSubpattern || !minCount || minCount == maxCount);
+ ASSERT(minCount <= maxCount);
+ quantityMinCount = minCount;
+ quantityMaxCount = maxCount;
+ quantityType = type;
+ }
+
+ void dumpQuantifier(PrintStream&);
+ void dump(PrintStream&, YarrPattern*, unsigned);
};
struct PatternAlternative {
@@ -250,6 +290,8 @@ public:
return m_onceThrough;
}
+ void dump(PrintStream&, YarrPattern*, unsigned);
+
Vector<PatternTerm> m_terms;
PatternDisjunction* m_parent;
unsigned m_minimumSize;
@@ -270,12 +312,13 @@ public:
PatternAlternative* addNewAlternative()
{
- PatternAlternative* alternative = new PatternAlternative(this);
- m_alternatives.append(adoptPtr(alternative));
- return alternative;
+ m_alternatives.append(std::make_unique<PatternAlternative>(this));
+ return static_cast<PatternAlternative*>(m_alternatives.last().get());
}
- Vector<OwnPtr<PatternAlternative> > m_alternatives;
+ void dump(PrintStream&, YarrPattern*, unsigned);
+
+ Vector<std::unique_ptr<PatternAlternative>> m_alternatives;
PatternAlternative* m_parent;
unsigned m_minimumSize;
unsigned m_callFrameSize;
@@ -286,13 +329,17 @@ public:
// (please to be calling newlineCharacterClass() et al on your
// friendly neighborhood YarrPattern instance to get nicely
// cached copies).
-CharacterClass* newlineCreate();
-CharacterClass* digitsCreate();
-CharacterClass* spacesCreate();
-CharacterClass* wordcharCreate();
-CharacterClass* nondigitsCreate();
-CharacterClass* nonspacesCreate();
-CharacterClass* nonwordcharCreate();
+
+std::unique_ptr<CharacterClass> anycharCreate();
+std::unique_ptr<CharacterClass> newlineCreate();
+std::unique_ptr<CharacterClass> digitsCreate();
+std::unique_ptr<CharacterClass> spacesCreate();
+std::unique_ptr<CharacterClass> wordcharCreate();
+std::unique_ptr<CharacterClass> wordUnicodeIgnoreCaseCharCreate();
+std::unique_ptr<CharacterClass> nondigitsCreate();
+std::unique_ptr<CharacterClass> nonspacesCreate();
+std::unique_ptr<CharacterClass> nonwordcharCreate();
+std::unique_ptr<CharacterClass> nonwordUnicodeIgnoreCaseCharCreate();
struct TermChain {
TermChain(PatternTerm term)
@@ -303,27 +350,37 @@ struct TermChain {
Vector<TermChain> hotTerms;
};
+
struct YarrPattern {
- JS_EXPORT_PRIVATE YarrPattern(const String& pattern, bool ignoreCase, bool multiline, const char** error);
+ JS_EXPORT_PRIVATE YarrPattern(const String& pattern, RegExpFlags, ErrorCode&, void* stackLimit = nullptr);
void reset()
{
m_numSubpatterns = 0;
m_maxBackReference = 0;
+ m_initialStartValueFrameLocation = 0;
m_containsBackreferences = false;
m_containsBOL = false;
-
- newlineCached = 0;
- digitsCached = 0;
- spacesCached = 0;
- wordcharCached = 0;
- nondigitsCached = 0;
- nonspacesCached = 0;
- nonwordcharCached = 0;
+ m_containsUnsignedLengthPattern = false;
+ m_hasCopiedParenSubexpressions = false;
+ m_saveInitialStartValue = false;
+
+ anycharCached = nullptr;
+ newlineCached = nullptr;
+ digitsCached = nullptr;
+ spacesCached = nullptr;
+ wordcharCached = nullptr;
+ wordUnicodeIgnoreCaseCharCached = nullptr;
+ nondigitsCached = nullptr;
+ nonspacesCached = nullptr;
+ nonwordcharCached = nullptr;
+ nonwordUnicodeIgnoreCasecharCached = nullptr;
+ unicodePropertiesCached.clear();
m_disjunctions.clear();
m_userCharacterClasses.clear();
+ m_captureGroupNames.shrink(0);
}
bool containsIllegalBackReference()
@@ -331,71 +388,212 @@ struct YarrPattern {
return m_maxBackReference > m_numSubpatterns;
}
+ bool containsUnsignedLengthPattern()
+ {
+ return m_containsUnsignedLengthPattern;
+ }
+
+ CharacterClass* anyCharacterClass()
+ {
+ if (!anycharCached) {
+ m_userCharacterClasses.append(anycharCreate());
+ anycharCached = m_userCharacterClasses.last().get();
+ }
+ return anycharCached;
+ }
CharacterClass* newlineCharacterClass()
{
- if (!newlineCached)
- m_userCharacterClasses.append(adoptPtr(newlineCached = newlineCreate()));
+ if (!newlineCached) {
+ m_userCharacterClasses.append(newlineCreate());
+ newlineCached = m_userCharacterClasses.last().get();
+ }
return newlineCached;
}
CharacterClass* digitsCharacterClass()
{
- if (!digitsCached)
- m_userCharacterClasses.append(adoptPtr(digitsCached = digitsCreate()));
+ if (!digitsCached) {
+ m_userCharacterClasses.append(digitsCreate());
+ digitsCached = m_userCharacterClasses.last().get();
+ }
return digitsCached;
}
CharacterClass* spacesCharacterClass()
{
- if (!spacesCached)
- m_userCharacterClasses.append(adoptPtr(spacesCached = spacesCreate()));
+ if (!spacesCached) {
+ m_userCharacterClasses.append(spacesCreate());
+ spacesCached = m_userCharacterClasses.last().get();
+ }
return spacesCached;
}
CharacterClass* wordcharCharacterClass()
{
- if (!wordcharCached)
- m_userCharacterClasses.append(adoptPtr(wordcharCached = wordcharCreate()));
+ if (!wordcharCached) {
+ m_userCharacterClasses.append(wordcharCreate());
+ wordcharCached = m_userCharacterClasses.last().get();
+ }
return wordcharCached;
}
+ CharacterClass* wordUnicodeIgnoreCaseCharCharacterClass()
+ {
+ if (!wordUnicodeIgnoreCaseCharCached) {
+ m_userCharacterClasses.append(wordUnicodeIgnoreCaseCharCreate());
+ wordUnicodeIgnoreCaseCharCached = m_userCharacterClasses.last().get();
+ }
+ return wordUnicodeIgnoreCaseCharCached;
+ }
CharacterClass* nondigitsCharacterClass()
{
- if (!nondigitsCached)
- m_userCharacterClasses.append(adoptPtr(nondigitsCached = nondigitsCreate()));
+ if (!nondigitsCached) {
+ m_userCharacterClasses.append(nondigitsCreate());
+ nondigitsCached = m_userCharacterClasses.last().get();
+ }
return nondigitsCached;
}
CharacterClass* nonspacesCharacterClass()
{
- if (!nonspacesCached)
- m_userCharacterClasses.append(adoptPtr(nonspacesCached = nonspacesCreate()));
+ if (!nonspacesCached) {
+ m_userCharacterClasses.append(nonspacesCreate());
+ nonspacesCached = m_userCharacterClasses.last().get();
+ }
return nonspacesCached;
}
CharacterClass* nonwordcharCharacterClass()
{
- if (!nonwordcharCached)
- m_userCharacterClasses.append(adoptPtr(nonwordcharCached = nonwordcharCreate()));
+ if (!nonwordcharCached) {
+ m_userCharacterClasses.append(nonwordcharCreate());
+ nonwordcharCached = m_userCharacterClasses.last().get();
+ }
return nonwordcharCached;
}
+ CharacterClass* nonwordUnicodeIgnoreCaseCharCharacterClass()
+ {
+ if (!nonwordUnicodeIgnoreCasecharCached) {
+ m_userCharacterClasses.append(nonwordUnicodeIgnoreCaseCharCreate());
+ nonwordUnicodeIgnoreCasecharCached = m_userCharacterClasses.last().get();
+ }
+ return nonwordUnicodeIgnoreCasecharCached;
+ }
+ CharacterClass* unicodeCharacterClassFor(BuiltInCharacterClassID unicodeClassID)
+ {
+ ASSERT(unicodeClassID >= BuiltInCharacterClassID::BaseUnicodePropertyID);
+
+ unsigned classID = static_cast<unsigned>(unicodeClassID);
+
+ if (unicodePropertiesCached.find(classID) == unicodePropertiesCached.end()) {
+ m_userCharacterClasses.append(createUnicodeCharacterClassFor(unicodeClassID));
+ CharacterClass* result = m_userCharacterClasses.last().get();
+ unicodePropertiesCached.add(classID, result);
+ return result;
+ }
+
+ return unicodePropertiesCached.get(classID);
+ }
+
+ void dumpPattern(const String& pattern);
+ void dumpPattern(PrintStream& out, const String& pattern);
+
+ bool global() const { return m_flags & FlagGlobal; }
+ bool ignoreCase() const { return m_flags & FlagIgnoreCase; }
+ bool multiline() const { return m_flags & FlagMultiline; }
+ bool sticky() const { return m_flags & FlagSticky; }
+ bool unicode() const { return m_flags & FlagUnicode; }
+ bool dotAll() const { return m_flags & FlagDotAll; }
- bool m_ignoreCase : 1;
- bool m_multiline : 1;
bool m_containsBackreferences : 1;
bool m_containsBOL : 1;
- unsigned m_numSubpatterns;
- unsigned m_maxBackReference;
+ bool m_containsUnsignedLengthPattern : 1;
+ bool m_hasCopiedParenSubexpressions : 1;
+ bool m_saveInitialStartValue : 1;
+ RegExpFlags m_flags;
+ unsigned m_numSubpatterns { 0 };
+ unsigned m_maxBackReference { 0 };
+ unsigned m_initialStartValueFrameLocation { 0 };
PatternDisjunction* m_body;
- Vector<OwnPtr<PatternDisjunction>, 4> m_disjunctions;
- Vector<OwnPtr<CharacterClass> > m_userCharacterClasses;
+ Vector<std::unique_ptr<PatternDisjunction>, 4> m_disjunctions;
+ Vector<std::unique_ptr<CharacterClass>> m_userCharacterClasses;
+ Vector<String> m_captureGroupNames;
+ HashMap<String, unsigned> m_namedGroupToParenIndex;
private:
- const char* compile(const String& patternString);
-
- CharacterClass* newlineCached;
- CharacterClass* digitsCached;
- CharacterClass* spacesCached;
- CharacterClass* wordcharCached;
- CharacterClass* nondigitsCached;
- CharacterClass* nonspacesCached;
- CharacterClass* nonwordcharCached;
+ ErrorCode compile(const String& patternString, void* stackLimit);
+
+ CharacterClass* anycharCached { nullptr };
+ CharacterClass* newlineCached { nullptr };
+ CharacterClass* digitsCached { nullptr };
+ CharacterClass* spacesCached { nullptr };
+ CharacterClass* wordcharCached { nullptr };
+ CharacterClass* wordUnicodeIgnoreCaseCharCached { nullptr };
+ CharacterClass* nondigitsCached { nullptr };
+ CharacterClass* nonspacesCached { nullptr };
+ CharacterClass* nonwordcharCached { nullptr };
+ CharacterClass* nonwordUnicodeIgnoreCasecharCached { nullptr };
+ HashMap<unsigned, CharacterClass*> unicodePropertiesCached;
};
-} } // namespace JSC::Yarr
+ void indentForNestingLevel(PrintStream&, unsigned);
+ void dumpUChar32(PrintStream&, UChar32);
+ void dumpCharacterClass(PrintStream&, YarrPattern*, CharacterClass*);
+
+ struct BackTrackInfoPatternCharacter {
+ uintptr_t begin; // Only needed for unicode patterns
+ uintptr_t matchAmount;
+
+ static unsigned beginIndex() { return offsetof(BackTrackInfoPatternCharacter, begin) / sizeof(uintptr_t); }
+ static unsigned matchAmountIndex() { return offsetof(BackTrackInfoPatternCharacter, matchAmount) / sizeof(uintptr_t); }
+ };
-#endif // YarrPattern_h
+ struct BackTrackInfoCharacterClass {
+ uintptr_t begin; // Only needed for unicode patterns
+ uintptr_t matchAmount;
+
+ static unsigned beginIndex() { return offsetof(BackTrackInfoCharacterClass, begin) / sizeof(uintptr_t); }
+ static unsigned matchAmountIndex() { return offsetof(BackTrackInfoCharacterClass, matchAmount) / sizeof(uintptr_t); }
+ };
+
+ struct BackTrackInfoBackReference {
+ uintptr_t begin; // Not really needed for greedy quantifiers.
+ uintptr_t matchAmount; // Not really needed for fixed quantifiers.
+
+ unsigned beginIndex() { return offsetof(BackTrackInfoBackReference, begin) / sizeof(uintptr_t); }
+ unsigned matchAmountIndex() { return offsetof(BackTrackInfoBackReference, matchAmount) / sizeof(uintptr_t); }
+ };
+
+ struct BackTrackInfoAlternative {
+ union {
+ uintptr_t offset;
+ };
+ };
+
+ struct BackTrackInfoParentheticalAssertion {
+ uintptr_t begin;
+
+ static unsigned beginIndex() { return offsetof(BackTrackInfoParentheticalAssertion, begin) / sizeof(uintptr_t); }
+ };
+
+ struct BackTrackInfoParenthesesOnce {
+ uintptr_t begin;
+ uintptr_t returnAddress;
+
+ static unsigned beginIndex() { return offsetof(BackTrackInfoParenthesesOnce, begin) / sizeof(uintptr_t); }
+ static unsigned returnAddressIndex() { return offsetof(BackTrackInfoParenthesesOnce, returnAddress) / sizeof(uintptr_t); }
+ };
+
+ struct BackTrackInfoParenthesesTerminal {
+ uintptr_t begin;
+
+ static unsigned beginIndex() { return offsetof(BackTrackInfoParenthesesTerminal, begin) / sizeof(uintptr_t); }
+ };
+
+ struct BackTrackInfoParentheses {
+ uintptr_t begin;
+ uintptr_t returnAddress;
+ uintptr_t matchAmount;
+ uintptr_t parenContextHead;
+
+ static unsigned beginIndex() { return offsetof(BackTrackInfoParentheses, begin) / sizeof(uintptr_t); }
+ static unsigned returnAddressIndex() { return offsetof(BackTrackInfoParentheses, returnAddress) / sizeof(uintptr_t); }
+ static unsigned matchAmountIndex() { return offsetof(BackTrackInfoParentheses, matchAmount) / sizeof(uintptr_t); }
+ static unsigned parenContextHeadIndex() { return offsetof(BackTrackInfoParentheses, parenContextHead) / sizeof(uintptr_t); }
+ };
+
+} } // namespace JSC::Yarr
diff --git a/src/3rdparty/masm/yarr/YarrSyntaxChecker.cpp b/src/3rdparty/masm/yarr/YarrSyntaxChecker.cpp
index aa98c4a354..9f05f22852 100644
--- a/src/3rdparty/masm/yarr/YarrSyntaxChecker.cpp
+++ b/src/3rdparty/masm/yarr/YarrSyntaxChecker.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -27,6 +27,8 @@
#include "YarrSyntaxChecker.h"
#include "YarrParser.h"
+#include <wtf/Optional.h>
+#include <wtf/text/WTFString.h>
namespace JSC { namespace Yarr {
@@ -35,25 +37,26 @@ public:
void assertionBOL() {}
void assertionEOL() {}
void assertionWordBoundary(bool) {}
- void atomPatternCharacter(UChar) {}
+ void atomPatternCharacter(UChar32) {}
void atomBuiltInCharacterClass(BuiltInCharacterClassID, bool) {}
void atomCharacterClassBegin(bool = false) {}
void atomCharacterClassAtom(UChar) {}
void atomCharacterClassRange(UChar, UChar) {}
void atomCharacterClassBuiltIn(BuiltInCharacterClassID, bool) {}
void atomCharacterClassEnd() {}
- void atomParenthesesSubpatternBegin(bool = true) {}
+ void atomParenthesesSubpatternBegin(bool = true, std::optional<String> = std::nullopt) {}
void atomParentheticalAssertionBegin(bool = false) {}
void atomParenthesesEnd() {}
void atomBackReference(unsigned) {}
+ void atomNamedBackReference(String) {}
void quantifyAtom(unsigned, unsigned, bool) {}
void disjunction() {}
};
-const char* checkSyntax(const String& pattern)
+ErrorCode checkSyntax(const String& pattern, const String& flags)
{
SyntaxChecker syntaxChecker;
- return parse(syntaxChecker, pattern);
+ return parse(syntaxChecker, pattern, flags.contains('u'));
}
-}} // JSC::YARR
+}} // JSC::Yarr
diff --git a/src/3rdparty/masm/yarr/YarrSyntaxChecker.h b/src/3rdparty/masm/yarr/YarrSyntaxChecker.h
index 104ced3ab4..86daf38bcb 100644
--- a/src/3rdparty/masm/yarr/YarrSyntaxChecker.h
+++ b/src/3rdparty/masm/yarr/YarrSyntaxChecker.h
@@ -23,16 +23,13 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef YarrSyntaxChecker_h
-#define YarrSyntaxChecker_h
+#pragma once
+#include "YarrErrorCode.h"
#include <wtf/text/WTFString.h>
namespace JSC { namespace Yarr {
-const char* checkSyntax(const String& pattern);
-
-}} // JSC::YARR
-
-#endif // YarrSyntaxChecker_h
+ErrorCode checkSyntax(const String& pattern, const String& flags);
+}} // JSC::Yarr
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerSH4.cpp b/src/3rdparty/masm/yarr/YarrUnicodeProperties.h
index 59de3ff48c..20f6739de3 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerSH4.cpp
+++ b/src/3rdparty/masm/yarr/YarrUnicodeProperties.h
@@ -1,6 +1,5 @@
/*
- * Copyright (C) 2011 STMicroelectronics. All rights reserved.
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -21,32 +20,22 @@
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
-#include "config.h"
+#pragma once
-#if ENABLE(ASSEMBLER) && CPU(SH4)
+#include "Yarr.h"
+#include <wtf/Optional.h>
+#include <wtf/text/WTFString.h>
-#include "MacroAssemblerSH4.h"
+namespace JSC { namespace Yarr {
-namespace JSC {
+struct CharacterClass;
-void MacroAssemblerSH4::linkCall(void* code, Call call, FunctionPtr function)
-{
- SH4Assembler::linkCall(code, call.m_label, function.value());
-}
+JS_EXPORT_PRIVATE std::optional<BuiltInCharacterClassID> unicodeMatchPropertyValue(WTF::String, WTF::String);
+JS_EXPORT_PRIVATE std::optional<BuiltInCharacterClassID> unicodeMatchProperty(WTF::String);
-void MacroAssemblerSH4::repatchCall(CodeLocationCall call, CodeLocationLabel destination)
-{
- SH4Assembler::relinkCall(call.dataLocation(), destination.executableAddress());
-}
+std::unique_ptr<CharacterClass> createUnicodeCharacterClassFor(BuiltInCharacterClassID);
-void MacroAssemblerSH4::repatchCall(CodeLocationCall call, FunctionPtr destination)
-{
- SH4Assembler::relinkCall(call.dataLocation(), destination.executableAddress());
-}
-
-} // namespace JSC
-
-#endif // ENABLE(ASSEMBLER)
+} } // namespace JSC::Yarr
diff --git a/src/3rdparty/masm/create_regex_tables b/src/3rdparty/masm/yarr/create_regex_tables
index 7544b75cd9..4c3dbbe3fb 100644
--- a/src/3rdparty/masm/create_regex_tables
+++ b/src/3rdparty/masm/yarr/create_regex_tables
@@ -1,4 +1,6 @@
-# Copyright (C) 2010, 2013 Apple Inc. All rights reserved.
+#!/usr/bin/env python
+
+# Copyright (C) 2010, 2013-2017 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
@@ -21,16 +23,19 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+import os
import sys
types = {
"wordchar": { "UseTable" : True, "data": ['_', ('0','9'), ('A', 'Z'), ('a','z')]},
- "nonwordchar": { "UseTable" : True, "Inverse": "wordchar", "data": ['`', (0, ord('0') - 1), (ord('9') + 1, ord('A') - 1), (ord('Z') + 1, ord('_') - 1), (ord('z') + 1, 0xffff)]},
+ "wordUnicodeIgnoreCaseChar": { "UseTable" : False, "data": ['_', ('0', '9'), ('A', 'Z'), ('a', 'z'), 0x017f, 0x212a]},
+ "nonwordchar": { "UseTable" : True, "Inverse": "wordchar", "data": ['`', (0, ord('0') - 1), (ord('9') + 1, ord('A') - 1), (ord('Z') + 1, ord('_') - 1), (ord('z') + 1, 0x10ffff)]},
+ "nonwordUnicodeIgnoreCaseChar": { "UseTable" : False, "Inverse": "wordUnicodeIgnoreCaseChar", "data": ['`', (0, ord('0') - 1), (ord('9') + 1, ord('A') - 1), (ord('Z') + 1, ord('_') - 1), (ord('z') + 1, 0x017e), (0x0180, 0x2129), (0x212b, 0x10ffff)]},
"newline": { "UseTable" : False, "data": ['\n', '\r', 0x2028, 0x2029]},
"spaces": { "UseTable" : True, "data": [' ', ('\t', '\r'), 0xa0, 0x1680, 0x180e, 0x2028, 0x2029, 0x202f, 0x205f, 0x3000, (0x2000, 0x200a), 0xfeff]},
- "nonspaces": { "UseTable" : True, "Inverse": "spaces", "data": [(0, ord('\t') - 1), (ord('\r') + 1, ord(' ') - 1), (ord(' ') + 1, 0x009f), (0x00a1, 0x167f), (0x1681, 0x180d), (0x180f, 0x1fff), (0x200b, 0x2027), (0x202a, 0x202e), (0x2030, 0x205e), (0x2060, 0x2fff), (0x3001, 0xfefe), (0xff00, 0xffff)]},
+ "nonspaces": { "UseTable" : True, "Inverse": "spaces", "data": [(0, ord('\t') - 1), (ord('\r') + 1, ord(' ') - 1), (ord(' ') + 1, 0x009f), (0x00a1, 0x167f), (0x1681, 0x180d), (0x180f, 0x1fff), (0x200b, 0x2027), (0x202a, 0x202e), (0x2030, 0x205e), (0x2060, 0x2fff), (0x3001, 0xfefe), (0xff00, 0x10ffff)]},
"digits": { "UseTable" : False, "data": [('0', '9')]},
- "nondigits": { "UseTable" : False, "Inverse": "digits", "data": [(0, ord('0') - 1), (ord('9') + 1, 0xffff)] }
+ "nondigits": { "UseTable" : False, "Inverse": "digits", "data": [(0, ord('0') - 1), (ord('9') + 1, 0x10ffff)] }
}
entriesPerLine = 50
arrays = "";
@@ -86,15 +91,16 @@ for name, classes in types.items():
# Generate createFunction:
function = "";
- function += ("CharacterClass* %sCreate()\n" % name)
+ function += ("std::unique_ptr<CharacterClass> %sCreate()\n" % name)
function += ("{\n")
if emitTables and classes["UseTable"]:
if "Inverse" in classes:
- function += (" CharacterClass* characterClass = new CharacterClass(_%sData, true);\n" % (classes["Inverse"]))
+ function += (" auto characterClass = std::make_unique<CharacterClass>(_%sData, true);\n" % (classes["Inverse"]))
else:
- function += (" CharacterClass* characterClass = new CharacterClass(_%sData, false);\n" % (name))
+ function += (" auto characterClass = std::make_unique<CharacterClass>(_%sData, false);\n" % (name))
else:
- function += (" CharacterClass* characterClass = new CharacterClass;\n")
+ function += (" auto characterClass = std::make_unique<CharacterClass>();\n")
+ hasNonBMPCharacters = False
for (min, max) in ranges:
if (min == max):
if (min > 127):
@@ -106,12 +112,19 @@ for name, classes in types.items():
function += (" characterClass->m_rangesUnicode.append(CharacterRange(0x%04x, 0x%04x));\n" % (min, max))
else:
function += (" characterClass->m_ranges.append(CharacterRange(0x%02x, 0x%02x));\n" % (min, max))
+ if max >= 0x10000:
+ hasNonBMPCharacters = True
+ function += (" characterClass->m_hasNonBMPCharacters = %s;\n" % ("true" if hasNonBMPCharacters else "false"))
function += (" return characterClass;\n")
function += ("}\n\n")
functions += function
if (len(sys.argv) > 1):
- f = open(sys.argv[-1], "w")
+ path = sys.argv[-1]
+ dirname = os.path.dirname(path)
+ if not os.path.isdir(dirname):
+ os.makedirs(dirname)
+ f = open(path, "w")
f.write(arrays)
f.write(functions)
f.close()
diff --git a/src/3rdparty/masm/yarr/generateYarrCanonicalizeUnicode b/src/3rdparty/masm/yarr/generateYarrCanonicalizeUnicode
new file mode 100644
index 0000000000..a103bcdf16
--- /dev/null
+++ b/src/3rdparty/masm/yarr/generateYarrCanonicalizeUnicode
@@ -0,0 +1,204 @@
+#! /usr/bin/env python
+
+# Copyright (C) 2016 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. 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.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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.
+
+# This tool processes the Unicode Character Database file CaseFolding.txt to create
+# canonicalization table as decribed in ECMAScript 6 standard in section
+# "21.2.2.8.2 Runtime Semantics: Canonicalize()", step 2.
+
+import optparse
+import os
+import re
+import sys
+from sets import Set
+
+header = """/*
+* Copyright (C) 2016 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. 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.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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.
+*/
+
+// DO NO EDIT! - This file was generated by generateYarrCanonicalizeUnicode
+
+#include "config.h"
+#include "YarrCanonicalize.h"
+
+namespace JSC { namespace Yarr {
+
+"""
+
+footer = """} } // JSC::Yarr
+"""
+
+MaxUnicode = 0x10ffff
+commonAndSimpleLinesRE = re.compile(r"(?P<code>[0-9A-F]+)\s*;\s*[CS]\s*;\s*(?P<mapping>[0-9A-F]+)", re.IGNORECASE)
+
+def openOrExit(path, mode):
+ try:
+ dirname = os.path.dirname(path)
+ if not os.path.isdir(dirname):
+ os.makedirs(dirname)
+ return open(path, mode)
+ except IOError as e:
+ print "I/O error opening {0}, ({1}): {2}".format(path, e.errno, e.strerror)
+ exit(1)
+
+class Canonicalize:
+ def __init__(self):
+ self.canonicalGroups = {};
+
+ def addMapping(self, code, mapping):
+ if mapping not in self.canonicalGroups:
+ self.canonicalGroups[mapping] = []
+ self.canonicalGroups[mapping].append(code)
+
+ def readCaseFolding(self, file):
+ codesSeen = Set()
+ for line in file:
+ line = line.split('#', 1)[0]
+ line = line.rstrip()
+ if (not len(line)):
+ continue
+
+ fields = commonAndSimpleLinesRE.match(line)
+ if (not fields):
+ continue
+
+ code = int(fields.group('code'), 16)
+ mapping = int(fields.group('mapping'), 16)
+
+ codesSeen.add(code)
+ self.addMapping(code, mapping)
+
+ for i in range(MaxUnicode + 1):
+ if i in codesSeen:
+ continue;
+
+ self.addMapping(i, i)
+
+ def createTables(self, file):
+ typeInfo = [""] * (MaxUnicode + 1)
+ characterSets = []
+
+ for mapping in sorted(self.canonicalGroups.keys()):
+ characters = self.canonicalGroups[mapping]
+ if len(characters) == 1:
+ typeInfo[characters[0]] = "CanonicalizeUnique:0"
+ else:
+ characters.sort()
+ if len(characters) > 2:
+ for ch in characters:
+ typeInfo[ch] = "CanonicalizeSet:%d" % len(characterSets)
+ characterSets.append(characters)
+ else:
+ low = characters[0]
+ high = characters[1]
+ delta = high - low
+ if delta == 1:
+ type = "CanonicalizeAlternatingUnaligned:0" if low & 1 else "CanonicalizeAlternatingAligned:0"
+ typeInfo[low] = type
+ typeInfo[high] = type
+ else:
+ typeInfo[low] = "CanonicalizeRangeLo:%d" % delta
+ typeInfo[high] = "CanonicalizeRangeHi:%d" % delta
+
+ rangeInfo = []
+ end = 0
+ while end <= MaxUnicode:
+ begin = end
+ type = typeInfo[end]
+ while end < MaxUnicode and typeInfo[end + 1] == type:
+ end = end + 1
+ rangeInfo.append({"begin": begin, "end": end, "type": type})
+ end = end + 1
+
+ for i in range(len(characterSets)):
+ characters = ""
+ set = characterSets[i]
+ for ch in set:
+ characters = characters + "0x{character:04x}, ".format(character=ch)
+ file.write("const UChar32 unicodeCharacterSet{index:d}[] = {{ {characters}0 }};\n".format(index=i, characters=characters))
+
+ file.write("\n")
+ file.write("static const size_t UNICODE_CANONICALIZATION_SETS = {setCount:d};\n".format(setCount=len(characterSets)))
+ file.write("const UChar32* const unicodeCharacterSetInfo[UNICODE_CANONICALIZATION_SETS] = {\n")
+
+ for i in range(len(characterSets)):
+ file.write(" unicodeCharacterSet{setNumber:d},\n".format(setNumber=i))
+
+ file.write("};\n")
+ file.write("\n")
+ file.write("const size_t UNICODE_CANONICALIZATION_RANGES = {rangeCount:d};\n".format(rangeCount=len(rangeInfo)))
+ file.write("const CanonicalizationRange unicodeRangeInfo[UNICODE_CANONICALIZATION_RANGES] = {\n")
+
+ for info in rangeInfo:
+ typeAndValue = info["type"].split(":")
+ file.write(" {{ 0x{begin:04x}, 0x{end:04x}, 0x{value:04x}, {type} }},\n".format(begin=info["begin"], end=info["end"], value=int(typeAndValue[1]), type=typeAndValue[0]))
+
+ file.write("};\n")
+ file.write("\n")
+
+
+if __name__ == "__main__":
+ parser = optparse.OptionParser(usage = "usage: %prog <CaseFolding.txt> <YarrCanonicalizeUnicode.h>")
+ (options, args) = parser.parse_args()
+
+ if len(args) != 2:
+ parser.error("<CaseFolding.txt> <YarrCanonicalizeUnicode.h>")
+
+ caseFoldingTxtPath = args[0]
+ canonicalizeHPath = args[1]
+ caseFoldingTxtFile = openOrExit(caseFoldingTxtPath, "r")
+ canonicalizeHFile = openOrExit(canonicalizeHPath, "wb")
+
+ canonicalize = Canonicalize()
+ canonicalize.readCaseFolding(caseFoldingTxtFile)
+
+ canonicalizeHFile.write(header);
+ canonicalize.createTables(canonicalizeHFile)
+ canonicalizeHFile.write(footer);
+
+ caseFoldingTxtFile.close()
+ canonicalizeHFile.close()
+
+ exit(0)
diff --git a/src/3rdparty/masm/yarr/yarr.pri b/src/3rdparty/masm/yarr/yarr.pri
index 7e9b4d3f3b..c8e30990be 100644
--- a/src/3rdparty/masm/yarr/yarr.pri
+++ b/src/3rdparty/masm/yarr/yarr.pri
@@ -8,5 +8,8 @@ SOURCES += \
$$PWD/YarrInterpreter.cpp \
$$PWD/YarrPattern.cpp \
$$PWD/YarrSyntaxChecker.cpp \
- $$PWD/YarrCanonicalizeUCS2.cpp
+ $$PWD/YarrCanonicalizeUCS2.cpp \
+ $$PWD/YarrCanonicalizeUnicode.cpp \
+ $$PWD/YarrErrorCode.cpp \
+ $$PWD/YarrUnicodeProperties.cpp
diff --git a/src/imports/builtins/builtins.qmltypes b/src/imports/builtins/builtins.qmltypes
index 53c5300685..f2179aaf7a 100644
--- a/src/imports/builtins/builtins.qmltypes
+++ b/src/imports/builtins/builtins.qmltypes
@@ -428,7 +428,8 @@ Module {
"WA_AlwaysStackOnTop": 128,
"WA_TabletTracking": 129,
"WA_ContentsMarginsRespectsSafeArea": 130,
- "WA_AttributeCount": 131
+ "WA_StyleSheetTarget": 131,
+ "WA_AttributeCount": 132
}
}
Enum {
@@ -1648,7 +1649,8 @@ Module {
"NoScrollPhase": 0,
"ScrollBegin": 1,
"ScrollUpdate": 2,
- "ScrollEnd": 3
+ "ScrollEnd": 3,
+ "ScrollMomentum": 4
}
}
Enum {
diff --git a/src/imports/folderlistmodel/folderlistmodel.pro b/src/imports/folderlistmodel/folderlistmodel.pro
index 99c54113e4..54ef107e45 100644
--- a/src/imports/folderlistmodel/folderlistmodel.pro
+++ b/src/imports/folderlistmodel/folderlistmodel.pro
@@ -1,7 +1,7 @@
CXX_MODULE = qml
TARGET = qmlfolderlistmodelplugin
TARGETPATH = Qt/labs/folderlistmodel
-IMPORT_VERSION = 2.1
+IMPORT_VERSION = 2.$$QT_MINOR_VERSION
QT = core-private qml-private
diff --git a/src/imports/folderlistmodel/plugin.cpp b/src/imports/folderlistmodel/plugin.cpp
index 5e8b41401f..7089fed4ad 100644
--- a/src/imports/folderlistmodel/plugin.cpp
+++ b/src/imports/folderlistmodel/plugin.cpp
@@ -42,13 +42,6 @@
#include "qquickfolderlistmodel.h"
-static void initResources()
-{
-#ifdef QT_STATIC
- Q_INIT_RESOURCE(qmake_Qt_labs_folderlistmodel);
-#endif
-}
-
QT_BEGIN_NAMESPACE
//![class decl]
@@ -58,7 +51,7 @@ class QmlFolderListModelPlugin : public QQmlExtensionPlugin
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
- QmlFolderListModelPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { initResources(); }
+ QmlFolderListModelPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { }
void registerTypes(const char *uri) override
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.folderlistmodel"));
diff --git a/src/imports/folderlistmodel/plugins.qmltypes b/src/imports/folderlistmodel/plugins.qmltypes
index a336b55022..cfa4765116 100644
--- a/src/imports/folderlistmodel/plugins.qmltypes
+++ b/src/imports/folderlistmodel/plugins.qmltypes
@@ -4,10 +4,10 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable Qt.labs.folderlistmodel 2.11'
+// 'qmlplugindump -nonrelocatable Qt.labs.folderlistmodel 2.12'
Module {
- dependencies: ["QtQuick 2.8"]
+ dependencies: ["QtQuick 2.12"]
Component {
name: "QQuickFolderListModel"
prototype: "QAbstractListModel"
diff --git a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp
index 7b2884df77..66d9e7ae46 100644
--- a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp
+++ b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp
@@ -224,16 +224,16 @@ QString QQuickFolderListModelPrivate::resolvePath(const QUrl &path)
}
/*!
- \qmlmodule Qt.labs.folderlistmodel 2.11
+ \qmlmodule Qt.labs.folderlistmodel 2.\QtMinorVersion
\title Qt Labs FolderListModel QML Types
\ingroup qmlmodules
\brief The FolderListModel provides a model of the contents of a file system folder.
To use this module, import the module with the following line:
- \code
- import Qt.labs.folderlistmodel 2.11
- \endcode
+ \qml \QtMinorVersion
+ import Qt.labs.folderlistmodel 2.\1
+ \endqml
*/
@@ -251,7 +251,9 @@ QString QQuickFolderListModelPrivate::resolvePath(const QUrl &path)
\e{Elements in the Qt.labs module are not guaranteed to remain compatible
in future versions.}
- \b{import Qt.labs.folderlistmodel 2.11}
+ \qml \QtMinorVersion
+ import Qt.labs.folderlistmodel 2.\1
+ \endqml
The \l folder property specifies the folder to access. Information about the
files and directories in the folder is supplied via the model's interface.
@@ -295,9 +297,9 @@ QString QQuickFolderListModelPrivate::resolvePath(const QUrl &path)
The following example shows a FolderListModel being used to provide a list
of QML files in a \l ListView:
- \qml
- import QtQuick 2.0
- import Qt.labs.folderlistmodel 2.11
+ \qml \QtMinorVersion
+ import QtQuick 2.\1
+ import Qt.labs.folderlistmodel 2.\1
ListView {
width: 200; height: 400
diff --git a/src/imports/handlers/handlers.pro b/src/imports/handlers/handlers.pro
deleted file mode 100644
index 0e32644773..0000000000
--- a/src/imports/handlers/handlers.pro
+++ /dev/null
@@ -1,11 +0,0 @@
-CXX_MODULE = qml
-TARGET = handlersplugin
-TARGETPATH = Qt/labs/handlers
-IMPORT_VERSION = 1.0
-
-SOURCES += \
- plugin.cpp
-
-QT += quick-private qml-private
-
-load(qml_plugin)
diff --git a/src/imports/handlers/plugins.qmltypes b/src/imports/handlers/plugins.qmltypes
deleted file mode 100644
index f51e1e4bf3..0000000000
--- a/src/imports/handlers/plugins.qmltypes
+++ /dev/null
@@ -1,308 +0,0 @@
-import QtQuick.tooling 1.2
-
-// This file describes the plugin-supplied types contained in the library.
-// It is used for QML tooling purposes only.
-//
-// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable Qt.labs.handlers 1.0'
-
-Module {
- dependencies: ["QtQuick 2.8"]
- Component {
- name: "QQuickDragAxis"
- prototype: "QObject"
- exports: ["Qt.labs.handlers/DragAxis 1.0"]
- isCreatable: false
- exportMetaObjectRevisions: [0]
- Property { name: "minimum"; type: "double" }
- Property { name: "maximum"; type: "double" }
- Property { name: "enabled"; type: "bool" }
- }
- Component {
- name: "QQuickDragHandler"
- prototype: "QQuickSinglePointHandler"
- exports: ["Qt.labs.handlers/DragHandler 1.0"]
- exportMetaObjectRevisions: [0]
- Property { name: "xAxis"; type: "QQuickDragAxis"; isReadonly: true; isPointer: true }
- Property { name: "yAxis"; type: "QQuickDragAxis"; isReadonly: true; isPointer: true }
- Property { name: "translation"; type: "QVector2D"; isReadonly: true }
- Method { name: "enforceConstraints" }
- }
- Component {
- name: "QQuickEventPoint"
- prototype: "QObject"
- exports: ["Qt.labs.handlers/EventPoint 1.0"]
- isCreatable: false
- exportMetaObjectRevisions: [0]
- Enum {
- name: "States"
- values: {
- "Pressed": 1,
- "Updated": 2,
- "Stationary": 4,
- "Released": 8
- }
- }
- Enum {
- name: "GrabState"
- values: {
- "GrabPassive": 1,
- "UngrabPassive": 2,
- "CancelGrabPassive": 3,
- "OverrideGrabPassive": 4,
- "GrabExclusive": 16,
- "UngrabExclusive": 32,
- "CancelGrabExclusive": 48
- }
- }
- Property { name: "event"; type: "QQuickPointerEvent"; isReadonly: true; isPointer: true }
- Property { name: "position"; type: "QPointF"; isReadonly: true }
- Property { name: "scenePosition"; type: "QPointF"; isReadonly: true }
- Property { name: "scenePressPosition"; type: "QPointF"; isReadonly: true }
- Property { name: "sceneGrabPosition"; type: "QPointF"; isReadonly: true }
- Property { name: "state"; type: "State"; isReadonly: true }
- Property { name: "pointId"; type: "int"; isReadonly: true }
- Property { name: "timeHeld"; type: "double"; isReadonly: true }
- Property { name: "velocity"; type: "QVector2D"; isReadonly: true }
- Property { name: "accepted"; type: "bool" }
- Property { name: "exclusiveGrabber"; type: "QObject"; isPointer: true }
- }
- Component {
- name: "QQuickEventTouchPoint"
- prototype: "QQuickEventPoint"
- exports: ["Qt.labs.handlers/EventTouchPoint 1.0"]
- isCreatable: false
- exportMetaObjectRevisions: [0]
- Property { name: "rotation"; type: "double"; isReadonly: true }
- Property { name: "pressure"; type: "double"; isReadonly: true }
- Property { name: "ellipseDiameters"; type: "QSizeF"; isReadonly: true }
- Property { name: "uniqueId"; type: "QPointingDeviceUniqueId"; isReadonly: true }
- }
- Component {
- name: "QQuickMultiPointHandler"
- prototype: "QQuickPointerDeviceHandler"
- Property { name: "minimumPointCount"; type: "int" }
- Property { name: "maximumPointCount"; type: "int" }
- Property { name: "pointDistanceThreshold"; type: "double" }
- }
- Component {
- name: "QQuickPinchHandler"
- prototype: "QQuickMultiPointHandler"
- exports: ["Qt.labs.handlers/PinchHandler 1.0"]
- exportMetaObjectRevisions: [0]
- Enum {
- name: "PinchOrigin"
- values: {
- "FirstPoint": 0,
- "PinchCenter": 1,
- "TargetCenter": 2
- }
- }
- Property { name: "minimumScale"; type: "double" }
- Property { name: "maximumScale"; type: "double" }
- Property { name: "minimumRotation"; type: "double" }
- Property { name: "maximumRotation"; type: "double" }
- Property { name: "pinchOrigin"; type: "PinchOrigin" }
- Property { name: "centroid"; type: "QPointF"; isReadonly: true }
- Property { name: "centroidVelocity"; type: "QVector2D"; isReadonly: true }
- Property { name: "scale"; type: "double"; isReadonly: true }
- Property { name: "rotation"; type: "double"; isReadonly: true }
- Property { name: "translation"; type: "QVector2D"; isReadonly: true }
- Property { name: "minimumX"; type: "double" }
- Property { name: "maximumX"; type: "double" }
- Property { name: "minimumY"; type: "double" }
- Property { name: "maximumY"; type: "double" }
- Signal { name: "updated" }
- }
- Component {
- name: "QQuickPointHandler"
- prototype: "QQuickSinglePointHandler"
- exports: ["Qt.labs.handlers/PointHandler 1.0"]
- exportMetaObjectRevisions: [0]
- Property { name: "translation"; type: "QVector2D"; isReadonly: true }
- }
- Component {
- name: "QQuickPointerDevice"
- prototype: "QObject"
- exports: ["Qt.labs.handlers/PointerDevice 1.0"]
- isCreatable: false
- exportMetaObjectRevisions: [0]
- Enum {
- name: "DeviceType"
- values: {
- "UnknownDevice": 0,
- "Mouse": 1,
- "TouchScreen": 2,
- "TouchPad": 4,
- "Puck": 8,
- "Stylus": 16,
- "Airbrush": 32,
- "AllDevices": 63
- }
- }
- Enum {
- name: "DeviceTypes"
- values: {
- "UnknownDevice": 0,
- "Mouse": 1,
- "TouchScreen": 2,
- "TouchPad": 4,
- "Puck": 8,
- "Stylus": 16,
- "Airbrush": 32,
- "AllDevices": 63
- }
- }
- Enum {
- name: "PointerType"
- values: {
- "GenericPointer": 1,
- "Finger": 2,
- "Pen": 4,
- "Eraser": 8,
- "Cursor": 16,
- "AllPointerTypes": 31
- }
- }
- Enum {
- name: "PointerTypes"
- values: {
- "GenericPointer": 1,
- "Finger": 2,
- "Pen": 4,
- "Eraser": 8,
- "Cursor": 16,
- "AllPointerTypes": 31
- }
- }
- Enum {
- name: "CapabilityFlag"
- values: {
- "Position": 1,
- "Area": 2,
- "Pressure": 4,
- "Velocity": 8,
- "Scroll": 256,
- "Hover": 512,
- "Rotation": 1024,
- "XTilt": 2048,
- "YTilt": 4096
- }
- }
- Enum {
- name: "Capabilities"
- values: {
- "Position": 1,
- "Area": 2,
- "Pressure": 4,
- "Velocity": 8,
- "Scroll": 256,
- "Hover": 512,
- "Rotation": 1024,
- "XTilt": 2048,
- "YTilt": 4096
- }
- }
- Property { name: "type"; type: "DeviceType"; isReadonly: true }
- Property { name: "pointerType"; type: "PointerType"; isReadonly: true }
- Property { name: "capabilities"; type: "Capabilities"; isReadonly: true }
- Property { name: "maximumTouchPoints"; type: "int"; isReadonly: true }
- Property { name: "buttonCount"; type: "int"; isReadonly: true }
- Property { name: "name"; type: "string"; isReadonly: true }
- Property { name: "uniqueId"; type: "QPointingDeviceUniqueId"; isReadonly: true }
- }
- Component {
- name: "QQuickPointerDeviceHandler"
- prototype: "QQuickPointerHandler"
- Property { name: "acceptedDevices"; type: "QQuickPointerDevice::DeviceTypes" }
- Property { name: "acceptedPointerTypes"; type: "QQuickPointerDevice::PointerTypes" }
- Property { name: "acceptedModifiers"; type: "Qt::KeyboardModifiers" }
- Method {
- name: "setAcceptedDevices"
- Parameter { name: "acceptedDevices"; type: "QQuickPointerDevice::DeviceTypes" }
- }
- Method {
- name: "setAcceptedPointerTypes"
- Parameter { name: "acceptedPointerTypes"; type: "QQuickPointerDevice::PointerTypes" }
- }
- Method {
- name: "setAcceptedModifiers"
- Parameter { name: "acceptedModifiers"; type: "Qt::KeyboardModifiers" }
- }
- }
- Component {
- name: "QQuickPointerEvent"
- prototype: "QObject"
- exports: ["Qt.labs.handlers/PointerEvent 1.0"]
- isCreatable: false
- exportMetaObjectRevisions: [0]
- Property { name: "device"; type: "const QQuickPointerDevice"; isReadonly: true; isPointer: true }
- Property { name: "modifiers"; type: "Qt::KeyboardModifiers"; isReadonly: true }
- Property { name: "button"; type: "Qt::MouseButtons"; isReadonly: true }
- Property { name: "buttons"; type: "Qt::MouseButtons"; isReadonly: true }
- }
- Component {
- name: "QQuickPointerHandler"
- prototype: "QObject"
- exports: ["Qt.labs.handlers/PointerHandler 1.0"]
- isCreatable: false
- exportMetaObjectRevisions: [0]
- Enum {
- name: "GrabPermissions"
- values: {
- "TakeOverForbidden": 0,
- "CanTakeOverFromHandlersOfSameType": 1,
- "CanTakeOverFromHandlersOfDifferentType": 2,
- "CanTakeOverFromItems": 4,
- "CanTakeOverFromAnything": 15,
- "ApprovesTakeOverByHandlersOfSameType": 16,
- "ApprovesTakeOverByHandlersOfDifferentType": 32,
- "ApprovesTakeOverByItems": 64,
- "ApprovesCancellation": 128,
- "ApprovesTakeOverByAnything": 240
- }
- }
- Property { name: "enabled"; type: "bool" }
- Property { name: "active"; type: "bool"; isReadonly: true }
- Property { name: "target"; type: "QQuickItem"; isPointer: true }
- Property { name: "parent"; type: "QQuickItem"; isReadonly: true; isPointer: true }
- Property { name: "grabPermissions"; type: "GrabPermissions" }
- Signal {
- name: "grabChanged"
- Parameter { name: "point"; type: "QQuickEventPoint"; isPointer: true }
- }
- Signal { name: "grabPermissionChanged" }
- Signal {
- name: "canceled"
- Parameter { name: "point"; type: "QQuickEventPoint"; isPointer: true }
- }
- }
- Component {
- name: "QQuickSinglePointHandler"
- prototype: "QQuickPointerDeviceHandler"
- Property { name: "acceptedButtons"; type: "Qt::MouseButtons" }
- Property { name: "point"; type: "QQuickHandlerPoint"; isReadonly: true }
- Signal { name: "singlePointGrabChanged" }
- }
- Component {
- name: "QQuickTapHandler"
- prototype: "QQuickSinglePointHandler"
- exports: ["Qt.labs.handlers/TapHandler 1.0"]
- exportMetaObjectRevisions: [0]
- Enum {
- name: "GesturePolicy"
- values: {
- "DragThreshold": 0,
- "WithinBounds": 1,
- "ReleaseWithinBounds": 2
- }
- }
- Property { name: "pressed"; type: "bool"; isReadonly: true }
- Property { name: "tapCount"; type: "int"; isReadonly: true }
- Property { name: "timeHeld"; type: "double"; isReadonly: true }
- Property { name: "longPressThreshold"; type: "double" }
- Property { name: "gesturePolicy"; type: "GesturePolicy" }
- Signal { name: "tapped" }
- Signal { name: "longPressed" }
- }
-}
diff --git a/src/imports/handlers/qmldir b/src/imports/handlers/qmldir
deleted file mode 100644
index 4896348c2e..0000000000
--- a/src/imports/handlers/qmldir
+++ /dev/null
@@ -1,5 +0,0 @@
-module Qt.labs.handlers
-plugin handlersplugin
-classname QtQuickHandlersPlugin
-typeinfo plugins.qmltypes
-
diff --git a/src/imports/imports.pro b/src/imports/imports.pro
index e188602277..eb3e336c7f 100644
--- a/src/imports/imports.pro
+++ b/src/imports/imports.pro
@@ -1,20 +1,20 @@
TEMPLATE = subdirs
-QT_FOR_CONFIG += quick-private
-
SUBDIRS += \
builtins \
qtqml \
- folderlistmodel \
- models
+ models \
+ labsmodels
+qtConfig(thread): SUBDIRS += folderlistmodel
qtHaveModule(sql): SUBDIRS += localstorage
qtConfig(settings): SUBDIRS += settings
qtConfig(statemachine): SUBDIRS += statemachine
qtHaveModule(quick) {
+ QT_FOR_CONFIG += quick-private
+
SUBDIRS += \
- handlers \
layouts \
qtquick2 \
window
@@ -24,7 +24,7 @@ qtHaveModule(quick) {
qtConfig(quick-particles): \
SUBDIRS += particles
- qtConfig(quick-path): SUBDIRS += shapes
+ qtConfig(quick-path):qtConfig(thread): SUBDIRS += shapes
}
qtHaveModule(xmlpatterns) : SUBDIRS += xmllistmodel
diff --git a/src/imports/labsmodels/labsmodels.pro b/src/imports/labsmodels/labsmodels.pro
new file mode 100644
index 0000000000..1795ae5e43
--- /dev/null
+++ b/src/imports/labsmodels/labsmodels.pro
@@ -0,0 +1,11 @@
+CXX_MODULE = qml
+TARGET = labsmodelsplugin
+TARGETPATH = Qt/labs/qmlmodels
+IMPORT_VERSION = 1.0
+
+SOURCES += \
+ plugin.cpp
+
+QT = qml-private
+
+load(qml_plugin)
diff --git a/src/imports/handlers/plugin.cpp b/src/imports/labsmodels/plugin.cpp
index d1041d6539..e18f08b70b 100644
--- a/src/imports/handlers/plugin.cpp
+++ b/src/imports/labsmodels/plugin.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
@@ -38,30 +38,42 @@
****************************************************************************/
#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqml.h>
-#include <private/qquickhandlersmodule_p.h>
-
-static void initResources()
-{
-#ifdef QT_STATIC
- Q_INIT_RESOURCE(qmake_Qt_labs_handlers);
-#endif
-}
+#include <private/qqmlmodelsmodule_p.h>
QT_BEGIN_NAMESPACE
+/*!
+ \qmlmodule Qt.labs.qmlmodels 1.0
+ \title Qt QML Models experimental QML Types
+ \ingroup qmlmodules
+ \brief Provides QML experimental types for data models
+ \since 5.12
+
+ This QML module contains experimental QML types related to data models.
+
+ To use the types in this module, import the module with the following line:
+
+ \code
+ import Qt.labs.qmlmodels 1.0
+ \endcode
+*/
+
//![class decl]
-class QtQuickHandlersPlugin : public QQmlExtensionPlugin
+class QtQmlLabsModelsPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
- QtQuickHandlersPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { initResources(); }
+ QtQmlLabsModelsPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { }
void registerTypes(const char *uri) override
{
- Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.handlers"));
+ Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.qmlmodels"));
Q_UNUSED(uri);
- QQuickHandlersModule::defineModule();
+ QQmlModelsModule::defineLabsModule();
+
+ qmlRegisterModule(uri, 1, 0);
}
};
//![class decl]
diff --git a/src/imports/labsmodels/plugins.qmltypes b/src/imports/labsmodels/plugins.qmltypes
new file mode 100644
index 0000000000..606b8712da
--- /dev/null
+++ b/src/imports/labsmodels/plugins.qmltypes
@@ -0,0 +1,41 @@
+import QtQuick.tooling 1.2
+
+// This file describes the plugin-supplied types contained in the library.
+// It is used for QML tooling purposes only.
+//
+// This file was auto-generated by:
+// 'qmlplugindump -nonrelocatable Qt.labs.qmlmodels 1.0'
+
+Module {
+ dependencies: ["QtQuick 2.12"]
+ Component {
+ name: "QQmlAbstractDelegateComponent"
+ prototype: "QQmlComponent"
+ exports: ["Qt.labs.qmlmodels/AbstractDelegateComponent 1.0"]
+ isCreatable: false
+ exportMetaObjectRevisions: [0]
+ Signal { name: "delegateChanged" }
+ }
+ Component {
+ name: "QQmlDelegateChoice"
+ defaultProperty: "delegate"
+ prototype: "QObject"
+ exports: ["Qt.labs.qmlmodels/DelegateChoice 1.0"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "roleValue"; type: "QVariant" }
+ Property { name: "row"; type: "int" }
+ Property { name: "index"; type: "int" }
+ Property { name: "column"; type: "int" }
+ Property { name: "delegate"; type: "QQmlComponent"; isPointer: true }
+ Signal { name: "changed" }
+ }
+ Component {
+ name: "QQmlDelegateChooser"
+ defaultProperty: "choices"
+ prototype: "QQmlAbstractDelegateComponent"
+ exports: ["Qt.labs.qmlmodels/DelegateChooser 1.0"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "role"; type: "string" }
+ Property { name: "choices"; type: "QQmlDelegateChoice"; isList: true; isReadonly: true }
+ }
+}
diff --git a/src/imports/labsmodels/qmldir b/src/imports/labsmodels/qmldir
new file mode 100644
index 0000000000..9c735711c4
--- /dev/null
+++ b/src/imports/labsmodels/qmldir
@@ -0,0 +1,3 @@
+module Qt.labs.qmlmodels
+plugin labsmodelsplugin
+classname QtQmlLabsModelsPlugin
diff --git a/src/imports/layouts/plugin.cpp b/src/imports/layouts/plugin.cpp
index c805c9fb43..80799e8859 100644
--- a/src/imports/layouts/plugin.cpp
+++ b/src/imports/layouts/plugin.cpp
@@ -42,13 +42,6 @@
#include "qquicklinearlayout_p.h"
#include "qquickstacklayout_p.h"
-static void initResources()
-{
-#ifdef QT_STATIC
- Q_INIT_RESOURCE(qmake_QtQuick_Layouts);
-#endif
-}
-
QT_BEGIN_NAMESPACE
//![class decl]
@@ -59,7 +52,6 @@ class QtQuickLayoutsPlugin : public QQmlExtensionPlugin
public:
QtQuickLayoutsPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent)
{
- initResources();
}
void registerTypes(const char *uri) override
{
diff --git a/src/imports/layouts/plugins.qmltypes b/src/imports/layouts/plugins.qmltypes
index afb563391d..f8d7b9acfe 100644
--- a/src/imports/layouts/plugins.qmltypes
+++ b/src/imports/layouts/plugins.qmltypes
@@ -4,10 +4,10 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtQuick.Layouts 1.3'
+// 'qmlplugindump -nonrelocatable QtQuick.Layouts 1.12'
Module {
- dependencies: ["QtQuick 2.8"]
+ dependencies: ["QtQuick 2.12"]
Component {
name: "QQuickColumnLayout"
defaultProperty: "data"
diff --git a/src/imports/localstorage/dependencies.json b/src/imports/localstorage/dependencies.json
new file mode 100644
index 0000000000..0d4f101c7a
--- /dev/null
+++ b/src/imports/localstorage/dependencies.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/src/imports/localstorage/localstorage.pro b/src/imports/localstorage/localstorage.pro
index 2fc976d37d..5c8177e238 100644
--- a/src/imports/localstorage/localstorage.pro
+++ b/src/imports/localstorage/localstorage.pro
@@ -1,7 +1,7 @@
CXX_MODULE = qml
TARGET = qmllocalstorageplugin
TARGETPATH = QtQuick/LocalStorage
-IMPORT_VERSION = 2.$${QT_MINOR_VERSION}
+IMPORT_VERSION = 2.$$QT_MINOR_VERSION
QT = sql qml-private core-private
diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp
index 9cca11ac5d..e7e8d130bb 100644
--- a/src/imports/localstorage/plugin.cpp
+++ b/src/imports/localstorage/plugin.cpp
@@ -61,19 +61,12 @@
#include <private/qv4jscall_p.h>
#include <private/qv4objectiterator_p.h>
-static void initResources()
-{
-#ifdef QT_STATIC
- Q_INIT_RESOURCE(qmake_QtQuick_LocalStorage);
-#endif
-}
-
QT_BEGIN_NAMESPACE
#define V4THROW_SQL(error, desc) { \
QV4::ScopedString v(scope, scope.engine->newString(desc)); \
QV4::ScopedObject ex(scope, scope.engine->newErrorObject(v)); \
- ex->put(QV4::ScopedString(scope, scope.engine->newIdentifier(QStringLiteral("code"))).getPointer(), QV4::ScopedValue(scope, Primitive::fromInt32(error))); \
+ ex->put(QV4::ScopedString(scope, scope.engine->newIdentifier(QStringLiteral("code"))).getPointer(), QV4::ScopedValue(scope, Value::fromInt32(error))); \
scope.engine->throwError(ex); \
RETURN_UNDEFINED(); \
}
@@ -81,7 +74,7 @@ QT_BEGIN_NAMESPACE
#define V4THROW_SQL2(error, desc) { \
QV4::ScopedString v(scope, scope.engine->newString(desc)); \
QV4::ScopedObject ex(scope, scope.engine->newErrorObject(v)); \
- ex->put(QV4::ScopedString(scope, scope.engine->newIdentifier(QStringLiteral("code"))).getPointer(), QV4::ScopedValue(scope, Primitive::fromInt32(error))); \
+ ex->put(QV4::ScopedString(scope, scope.engine->newIdentifier(QStringLiteral("code"))).getPointer(), QV4::ScopedValue(scope, Value::fromInt32(error))); \
args->setReturnValue(scope.engine->throwError(ex)); \
return; \
}
@@ -148,13 +141,13 @@ public:
static Heap::QQmlSqlDatabaseWrapper *create(QV4::ExecutionEngine *engine)
{
- return engine->memoryManager->allocObject<QQmlSqlDatabaseWrapper>();
+ return engine->memoryManager->allocate<QQmlSqlDatabaseWrapper>();
}
~QQmlSqlDatabaseWrapper() {
}
- static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty);
+ static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
};
}
@@ -244,12 +237,16 @@ static ReturnedValue qmlsqldatabase_rows_index(const QQmlSqlDatabaseWrapper *r,
}
}
-ReturnedValue QQmlSqlDatabaseWrapper::getIndexed(const Managed *m, uint index, bool *hasProperty)
+ReturnedValue QQmlSqlDatabaseWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
+ if (!id.isArrayIndex())
+ return Object::virtualGet(m, id, receiver, hasProperty);
+
+ uint index = id.asArrayIndex();
Q_ASSERT(m->as<QQmlSqlDatabaseWrapper>());
const QQmlSqlDatabaseWrapper *r = static_cast<const QQmlSqlDatabaseWrapper *>(m);
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Rows)
- return Object::getIndexed(m, index, hasProperty);
+ return Object::virtualGet(m, id, receiver, hasProperty);
return qmlsqldatabase_rows_index(r, r->engine(), index, hasProperty);
}
@@ -294,7 +291,7 @@ static ReturnedValue qmlsqldatabase_executeSql(const FunctionObject *b, const Va
QSqlQuery query(db);
bool err = false;
- ScopedValue result(scope, Primitive::undefinedValue());
+ ScopedValue result(scope, Value::undefinedValue());
if (query.prepare(sql)) {
if (argc > 1) {
@@ -304,11 +301,11 @@ static ReturnedValue qmlsqldatabase_executeSql(const FunctionObject *b, const Va
quint32 size = array->getLength();
QV4::ScopedValue v(scope);
for (quint32 ii = 0; ii < size; ++ii) {
- query.bindValue(ii, toSqlVariant(scope.engine, (v = array->getIndexed(ii))));
+ query.bindValue(ii, toSqlVariant(scope.engine, (v = array->get(ii))));
}
} else if (values->as<Object>()) {
ScopedObject object(scope, values);
- ObjectIterator it(scope, object, ObjectIterator::WithProtoChain|ObjectIterator::EnumerableOnly);
+ ObjectIterator it(scope, object, ObjectIterator::EnumerableOnly);
ScopedValue key(scope);
QV4::ScopedValue val(scope);
while (1) {
@@ -330,7 +327,7 @@ static ReturnedValue qmlsqldatabase_executeSql(const FunctionObject *b, const Va
if (query.exec()) {
QV4::Scoped<QQmlSqlDatabaseWrapper> rows(scope, QQmlSqlDatabaseWrapper::create(scope.engine));
QV4::ScopedObject p(scope, databaseData(scope.engine)->rowsProto.value());
- rows->setPrototype(p.getPointer());
+ rows->setPrototypeUnchecked(p.getPointer());
rows->d()->type = Heap::QQmlSqlDatabaseWrapper::Rows;
*rows->d()->database = db;
*rows->d()->sqlQuery = query;
@@ -340,7 +337,7 @@ static ReturnedValue qmlsqldatabase_executeSql(const FunctionObject *b, const Va
// XXX optimize
ScopedString s(scope);
ScopedValue v(scope);
- resultObject->put((s = scope.engine->newIdentifier("rowsAffected")).getPointer(), (v = Primitive::fromInt32(query.numRowsAffected())));
+ resultObject->put((s = scope.engine->newIdentifier("rowsAffected")).getPointer(), (v = Value::fromInt32(query.numRowsAffected())));
resultObject->put((s = scope.engine->newIdentifier("insertId")).getPointer(), (v = scope.engine->newString(query.lastInsertId().toString())));
resultObject->put((s = scope.engine->newIdentifier("rows")).getPointer(), rows);
} else {
@@ -397,14 +394,14 @@ static ReturnedValue qmlsqldatabase_changeVersion(const FunctionObject *b, const
QSqlDatabase db = *r->d()->database;
QString from_version = argv[0].toQString();
QString to_version = argv[1].toQString();
- ScopedFunctionObject callback(scope, argc > 2 ? argv[2] : Primitive::undefinedValue());
+ ScopedFunctionObject callback(scope, argc > 2 ? argv[2] : Value::undefinedValue());
if (from_version != *r->d()->version)
V4THROW_SQL(SQLEXCEPTION_VERSION_ERR, QQmlEngine::tr("Version mismatch: expected %1, found %2").arg(from_version).arg(*r->d()->version));
Scoped<QQmlSqlDatabaseWrapper> w(scope, QQmlSqlDatabaseWrapper::create(scope.engine));
ScopedObject p(scope, databaseData(scope.engine)->queryProto.value());
- w->setPrototype(p.getPointer());
+ w->setPrototypeUnchecked(p.getPointer());
w->d()->type = Heap::QQmlSqlDatabaseWrapper::Query;
*w->d()->database = db;
*w->d()->version = *r->d()->version;
@@ -456,7 +453,7 @@ static ReturnedValue qmlsqldatabase_transaction_shared(const FunctionObject *b,
Scoped<QQmlSqlDatabaseWrapper> w(scope, QQmlSqlDatabaseWrapper::create(scope.engine));
QV4::ScopedObject p(scope, databaseData(scope.engine)->queryProto.value());
- w->setPrototype(p.getPointer());
+ w->setPrototypeUnchecked(p.getPointer());
w->d()->type = Heap::QQmlSqlDatabaseWrapper::Query;
*w->d()->database = db;
*w->d()->version = *r->d()->version;
@@ -523,7 +520,7 @@ through the data.
/*!
- \qmlmodule QtQuick.LocalStorage 2.11
+ \qmlmodule QtQuick.LocalStorage 2.\QtMinorVersion
\title Qt Quick Local Storage QML Types
\ingroup qmlmodules
\brief Provides a JavaScript object singleton type for accessing a local
@@ -544,16 +541,16 @@ through the data.
To use the types in this module, import the module and call the
relevant functions using the \c LocalStorage type:
- \code
- import QtQuick.LocalStorage 2.0
- import QtQuick 2.0
+ \qml \QtMinorVersion
+ import QtQuick 2.\1
+ import QtQuick.LocalStorage 2.\1
Item {
Component.onCompleted: {
var db = LocalStorage.openDatabaseSync(...)
}
}
- \endcode
+ \endqml
These databases are user-specific and QML-specific, but accessible to all QML applications.
@@ -573,11 +570,13 @@ The \l{Qt Quick Examples - Local Storage}{SQL Local Storage example} demonstrate
using the Offline Storage API.
\section3 Open or Create a Database
-\code
-import QtQuick.LocalStorage 2.0 as Sql
+
+\qml \QtMinorVersion
+import QtQuick.LocalStorage 2.\1 as Sql
db = Sql.openDatabaseSync(identifier, version, description, estimated_size, callback(db))
-\endcode
+\endqml
+
The above code returns the database identified by \e identifier. If the database does not already exist, it
is created, and the function \e callback is called with the database as a parameter. \e identifier is the
name of the physical file (with or without full path) containing the database. \e description and
@@ -780,7 +779,7 @@ void QQuickLocalStorage::openDatabaseSync(QQmlV4Function *args)
QV4::Scoped<QQmlSqlDatabaseWrapper> db(scope, QQmlSqlDatabaseWrapper::create(scope.engine));
QV4::ScopedObject p(scope, databaseData(scope.engine)->databaseProto.value());
- db->setPrototype(p.getPointer());
+ db->setPrototypeUnchecked(p.getPointer());
*db->d()->database = database;
*db->d()->version = version;
@@ -814,7 +813,6 @@ class QQmlLocalStoragePlugin : public QQmlExtensionPlugin
public:
QQmlLocalStoragePlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent)
{
- initResources();
}
void registerTypes(const char *uri) override
{
diff --git a/src/imports/localstorage/plugins.qmltypes b/src/imports/localstorage/plugins.qmltypes
index 412989c001..59944328d6 100644
--- a/src/imports/localstorage/plugins.qmltypes
+++ b/src/imports/localstorage/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtQuick.LocalStorage 2.11'
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQuick.LocalStorage 2.12'
Module {
dependencies: []
diff --git a/src/imports/models/dependencies.json b/src/imports/models/dependencies.json
new file mode 100644
index 0000000000..0d4f101c7a
--- /dev/null
+++ b/src/imports/models/dependencies.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/src/imports/models/plugin.cpp b/src/imports/models/plugin.cpp
index 83f8597408..2b19345f6b 100644
--- a/src/imports/models/plugin.cpp
+++ b/src/imports/models/plugin.cpp
@@ -42,17 +42,10 @@
#include <private/qqmlmodelsmodule_p.h>
-static void initResources()
-{
-#ifdef QT_STATIC
- Q_INIT_RESOURCE(qmake_QtQml_Models_2);
-#endif
-}
-
QT_BEGIN_NAMESPACE
/*!
- \qmlmodule QtQml.Models 2.11
+ \qmlmodule QtQml.Models 2.\QtMinorVersion
\title Qt QML Models QML Types
\ingroup qmlmodules
\brief Provides QML types for data models
@@ -62,9 +55,9 @@ QT_BEGIN_NAMESPACE
To use the types in this module, import the module with the following line:
- \code
- import QtQml.Models 2.11
- \endcode
+ \qml \QtMinorVersion
+ import QtQml.Models 2.\1
+ \endqml
Note that QtQml.Models module started at version 2.1 to match the version
of the parent module, \l{Qt QML}.
@@ -78,7 +71,7 @@ class QtQmlModelsPlugin : public QQmlExtensionPlugin
Q_OBJECT
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
- QtQmlModelsPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { initResources(); }
+ QtQmlModelsPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { }
void registerTypes(const char *uri) override
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQml.Models"));
diff --git a/src/imports/models/plugins.qmltypes b/src/imports/models/plugins.qmltypes
index 60146f51ba..87ec0fee76 100644
--- a/src/imports/models/plugins.qmltypes
+++ b/src/imports/models/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtQml.Models 2.11'
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQml.Models 2.12'
Module {
dependencies: []
diff --git a/src/imports/particles/plugin.cpp b/src/imports/particles/plugin.cpp
index d548f26599..e2b8712599 100644
--- a/src/imports/particles/plugin.cpp
+++ b/src/imports/particles/plugin.cpp
@@ -42,13 +42,6 @@
#include <private/qquickparticlesmodule_p.h>
-static void initResources()
-{
-#ifdef QT_STATIC
- Q_INIT_RESOURCE(qmake_QtQuick_Particles_2);
-#endif
-}
-
QT_BEGIN_NAMESPACE
//![class decl]
@@ -57,7 +50,7 @@ class QtQuick2ParticlesPlugin : public QQmlExtensionPlugin
Q_OBJECT
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
- QtQuick2ParticlesPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { initResources(); }
+ QtQuick2ParticlesPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { }
void registerTypes(const char *uri) override
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Particles"));
diff --git a/src/imports/particles/plugins.qmltypes b/src/imports/particles/plugins.qmltypes
index 6c7c98cc71..b68be6c5da 100644
--- a/src/imports/particles/plugins.qmltypes
+++ b/src/imports/particles/plugins.qmltypes
@@ -4,10 +4,10 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtQuick.Particles 2.0'
+// 'qmlplugindump -nonrelocatable QtQuick.Particles 2.12'
Module {
- dependencies: ["QtQuick 2.8"]
+ dependencies: ["QtQuick 2.12"]
Component {
name: "QQuickAgeAffector"
defaultProperty: "data"
diff --git a/src/imports/qtqml/plugins.qmltypes b/src/imports/qtqml/plugins.qmltypes
index 82333627a0..1ffe4a78ca 100644
--- a/src/imports/qtqml/plugins.qmltypes
+++ b/src/imports/qtqml/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable -noforceqtquick QtQml 2.3'
+// 'qmlplugindump -nonrelocatable -noforceqtquick QtQml 2.12'
Module {
dependencies: []
@@ -96,7 +96,7 @@ Module {
exports: ["QtQml/Connections 2.0", "QtQml/Connections 2.3"]
exportMetaObjectRevisions: [0, 1]
Property { name: "target"; type: "QObject"; isPointer: true }
- Property { name: "enabled"; type: "bool" }
+ Property { name: "enabled"; revision: 1; type: "bool" }
Property { name: "ignoreUnknownSignals"; type: "bool" }
Signal { name: "enabledChanged"; revision: 1 }
}
@@ -198,9 +198,20 @@ Module {
Component {
name: "QQmlLoggingCategory"
prototype: "QObject"
- exports: ["QtQml/LoggingCategory 2.8"]
- exportMetaObjectRevisions: [0]
+ exports: ["QtQml/LoggingCategory 2.12", "QtQml/LoggingCategory 2.8"]
+ exportMetaObjectRevisions: [1, 0]
+ Enum {
+ name: "DefaultLogLevel"
+ values: {
+ "Debug": 0,
+ "Info": 4,
+ "Warning": 1,
+ "Critical": 2,
+ "Fatal": 3
+ }
+ }
Property { name: "name"; type: "string" }
+ Property { name: "defaultLogLevel"; revision: 1; type: "DefaultLogLevel" }
}
Component {
name: "QQmlTimer"
@@ -229,5 +240,6 @@ Module {
Property { name: "wasHeld"; type: "bool"; isReadonly: true }
Property { name: "isClick"; type: "bool"; isReadonly: true }
Property { name: "accepted"; type: "bool" }
+ Property { name: "flags"; revision: 11; type: "int"; isReadonly: true }
}
}
diff --git a/src/imports/qtqml/qtqml.pro b/src/imports/qtqml/qtqml.pro
index 8804c944e7..c00172ddc4 100644
--- a/src/imports/qtqml/qtqml.pro
+++ b/src/imports/qtqml/qtqml.pro
@@ -7,8 +7,6 @@ load(qml_module)
!cross_compile:if(build_pass|!debug_and_release) {
qtPrepareTool(QMLPLUGINDUMP, qmlplugindump)
- # Use QtQml version defined in qmlplugindump source
- # TODO: retrieve the correct version from QtQml
- qmltypes.commands = $$QMLPLUGINDUMP -nonrelocatable QtQml 2.2 > $$PWD/plugins.qmltypes
+ qmltypes.commands = $$QMLPLUGINDUMP -nonrelocatable -noforceqtquick QtQml 2.$$QT_MINOR_VERSION > $$PWD/plugins.qmltypes
QMAKE_EXTRA_TARGETS += qmltypes
}
diff --git a/src/imports/qtquick2/dependencies.json b/src/imports/qtquick2/dependencies.json
new file mode 100644
index 0000000000..0d4f101c7a
--- /dev/null
+++ b/src/imports/qtquick2/dependencies.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/src/imports/qtquick2/plugin.cpp b/src/imports/qtquick2/plugin.cpp
index 147f01e81b..d73a8b3688 100644
--- a/src/imports/qtquick2/plugin.cpp
+++ b/src/imports/qtquick2/plugin.cpp
@@ -41,13 +41,6 @@
#include <private/qtquick2_p.h>
-static void initResources()
-{
-#ifdef QT_STATIC
- Q_INIT_RESOURCE(qmake_QtQuick_2);
-#endif
-}
-
QT_BEGIN_NAMESPACE
//![class decl]
@@ -56,7 +49,7 @@ class QtQuick2Plugin : public QQmlExtensionPlugin
Q_OBJECT
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
- QtQuick2Plugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { initResources(); }
+ QtQuick2Plugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { }
void registerTypes(const char *uri) override
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick"));
diff --git a/src/imports/qtquick2/plugins.qmltypes b/src/imports/qtquick2/plugins.qmltypes
index 6f6f1de8c3..456db6316e 100644
--- a/src/imports/qtquick2/plugins.qmltypes
+++ b/src/imports/qtquick2/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtQuick 2.11'
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQuick 2.12'
Module {
dependencies: []
@@ -742,9 +742,9 @@ Module {
Component {
name: "QQuickAbstractAnimation"
prototype: "QObject"
- exports: ["QtQuick/Animation 2.0"]
+ exports: ["QtQuick/Animation 2.0", "QtQuick/Animation 2.12"]
isCreatable: false
- exportMetaObjectRevisions: [0]
+ exportMetaObjectRevisions: [0, 12]
Enum {
name: "Loops"
values: {
@@ -773,6 +773,7 @@ Module {
name: "loopCountChanged"
Parameter { type: "int" }
}
+ Signal { name: "finished"; revision: 12 }
Method { name: "restart" }
Method { name: "start" }
Method { name: "pause" }
@@ -960,8 +961,8 @@ Module {
name: "QQuickAnimatedSprite"
defaultProperty: "data"
prototype: "QQuickItem"
- exports: ["QtQuick/AnimatedSprite 2.0"]
- exportMetaObjectRevisions: [0]
+ exports: ["QtQuick/AnimatedSprite 2.0", "QtQuick/AnimatedSprite 2.12"]
+ exportMetaObjectRevisions: [0, 12]
Enum {
name: "LoopParameters"
values: {
@@ -1043,6 +1044,7 @@ Module {
name: "currentFrameChanged"
Parameter { name: "arg"; type: "int" }
}
+ Signal { name: "finished"; revision: 12 }
Method { name: "start" }
Method { name: "stop" }
Method { name: "restart" }
@@ -1448,6 +1450,25 @@ Module {
Method { name: "drop"; type: "int" }
}
Component {
+ name: "QQuickDragAxis"
+ prototype: "QObject"
+ exports: ["QtQuick/DragAxis 2.12"]
+ isCreatable: false
+ exportMetaObjectRevisions: [0]
+ Property { name: "minimum"; type: "double" }
+ Property { name: "maximum"; type: "double" }
+ Property { name: "enabled"; type: "bool" }
+ }
+ Component {
+ name: "QQuickDragHandler"
+ prototype: "QQuickMultiPointHandler"
+ exports: ["QtQuick/DragHandler 2.12"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "xAxis"; type: "QQuickDragAxis"; isReadonly: true; isPointer: true }
+ Property { name: "yAxis"; type: "QQuickDragAxis"; isReadonly: true; isPointer: true }
+ Property { name: "translation"; type: "QVector2D"; isReadonly: true }
+ }
+ Component {
name: "QQuickDropArea"
defaultProperty: "data"
prototype: "QQuickItem"
@@ -1525,15 +1546,66 @@ Module {
Property { name: "type"; type: "Qt::EnterKeyType" }
}
Component {
+ name: "QQuickEventPoint"
+ prototype: "QObject"
+ exports: ["QtQuick/EventPoint 2.12"]
+ isCreatable: false
+ exportMetaObjectRevisions: [0]
+ Enum {
+ name: "States"
+ values: {
+ "Pressed": 1,
+ "Updated": 2,
+ "Stationary": 4,
+ "Released": 8
+ }
+ }
+ Enum {
+ name: "GrabTransition"
+ values: {
+ "GrabPassive": 1,
+ "UngrabPassive": 2,
+ "CancelGrabPassive": 3,
+ "OverrideGrabPassive": 4,
+ "GrabExclusive": 16,
+ "UngrabExclusive": 32,
+ "CancelGrabExclusive": 48
+ }
+ }
+ Property { name: "event"; type: "QQuickPointerEvent"; isReadonly: true; isPointer: true }
+ Property { name: "position"; type: "QPointF"; isReadonly: true }
+ Property { name: "scenePosition"; type: "QPointF"; isReadonly: true }
+ Property { name: "scenePressPosition"; type: "QPointF"; isReadonly: true }
+ Property { name: "sceneGrabPosition"; type: "QPointF"; isReadonly: true }
+ Property { name: "state"; type: "State"; isReadonly: true }
+ Property { name: "pointId"; type: "int"; isReadonly: true }
+ Property { name: "timeHeld"; type: "double"; isReadonly: true }
+ Property { name: "velocity"; type: "QVector2D"; isReadonly: true }
+ Property { name: "accepted"; type: "bool" }
+ Property { name: "exclusiveGrabber"; type: "QObject"; isPointer: true }
+ }
+ Component {
+ name: "QQuickEventTouchPoint"
+ prototype: "QQuickEventPoint"
+ exports: ["QtQuick/EventTouchPoint 2.12"]
+ isCreatable: false
+ exportMetaObjectRevisions: [0]
+ Property { name: "rotation"; type: "double"; isReadonly: true }
+ Property { name: "pressure"; type: "double"; isReadonly: true }
+ Property { name: "ellipseDiameters"; type: "QSizeF"; isReadonly: true }
+ Property { name: "uniqueId"; type: "QPointingDeviceUniqueId"; isReadonly: true }
+ }
+ Component {
name: "QQuickFlickable"
defaultProperty: "flickableData"
prototype: "QQuickItem"
exports: [
"QtQuick/Flickable 2.0",
"QtQuick/Flickable 2.10",
+ "QtQuick/Flickable 2.12",
"QtQuick/Flickable 2.9"
]
- exportMetaObjectRevisions: [0, 10, 9]
+ exportMetaObjectRevisions: [0, 10, 12, 9]
Enum {
name: "BoundsBehavior"
values: {
@@ -1600,6 +1672,7 @@ Module {
isPointer: true
}
Property { name: "pixelAligned"; type: "bool" }
+ Property { name: "synchronousDrag"; revision: 12; type: "bool" }
Property { name: "horizontalOvershoot"; revision: 9; type: "double"; isReadonly: true }
Property { name: "verticalOvershoot"; revision: 9; type: "double"; isReadonly: true }
Property { name: "flickableData"; type: "QObject"; isList: true; isReadonly: true }
@@ -1612,8 +1685,13 @@ Module {
Signal { name: "flickEnded" }
Signal { name: "dragStarted" }
Signal { name: "dragEnded" }
+ Signal { name: "synchronousDragChanged"; revision: 12 }
Signal { name: "horizontalOvershootChanged"; revision: 9 }
Signal { name: "verticalOvershootChanged"; revision: 9 }
+ Signal { name: "atXEndChanged"; revision: 12 }
+ Signal { name: "atYEndChanged"; revision: 12 }
+ Signal { name: "atXBeginningChanged"; revision: 12 }
+ Signal { name: "atYBeginningChanged"; revision: 12 }
Method {
name: "resizeContent"
Parameter { name: "w"; type: "double" }
@@ -1836,9 +1914,17 @@ Module {
name: "QQuickGradient"
defaultProperty: "stops"
prototype: "QObject"
- exports: ["QtQuick/Gradient 2.0"]
- exportMetaObjectRevisions: [0]
+ exports: ["QtQuick/Gradient 2.0", "QtQuick/Gradient 2.12"]
+ exportMetaObjectRevisions: [0, 12]
+ Enum {
+ name: "Orientation"
+ values: {
+ "Vertical": 2,
+ "Horizontal": 1
+ }
+ }
Property { name: "stops"; type: "QQuickGradientStop"; isList: true; isReadonly: true }
+ Property { name: "orientation"; revision: 12; type: "Orientation" }
Signal { name: "updated" }
}
Component {
@@ -2017,6 +2103,13 @@ Module {
}
Component { name: "QQuickGridViewAttached"; prototype: "QQuickItemViewAttached" }
Component {
+ name: "QQuickHoverHandler"
+ prototype: "QQuickSinglePointHandler"
+ exports: ["QtQuick/HoverHandler 2.12"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "hovered"; type: "bool"; isReadonly: true }
+ }
+ Component {
name: "QQuickImage"
defaultProperty: "data"
prototype: "QQuickImageBase"
@@ -2890,6 +2983,14 @@ Module {
Signal { name: "pressAndHoldIntervalChanged"; revision: 9 }
}
Component {
+ name: "QQuickMultiPointHandler"
+ prototype: "QQuickPointerDeviceHandler"
+ Property { name: "minimumPointCount"; type: "int" }
+ Property { name: "maximumPointCount"; type: "int" }
+ Property { name: "centroid"; type: "QQuickHandlerPoint"; isReadonly: true }
+ Signal { name: "marginChanged" }
+ }
+ Component {
name: "QQuickMultiPointTouchArea"
defaultProperty: "data"
prototype: "QQuickItem"
@@ -3401,6 +3502,213 @@ Module {
Property { name: "accepted"; type: "bool" }
}
Component {
+ name: "QQuickPinchHandler"
+ prototype: "QQuickMultiPointHandler"
+ exports: ["QtQuick/PinchHandler 2.12"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "minimumScale"; type: "double" }
+ Property { name: "maximumScale"; type: "double" }
+ Property { name: "minimumRotation"; type: "double" }
+ Property { name: "maximumRotation"; type: "double" }
+ Property { name: "scale"; type: "double"; isReadonly: true }
+ Property { name: "activeScale"; type: "double"; isReadonly: true }
+ Property { name: "rotation"; type: "double"; isReadonly: true }
+ Property { name: "translation"; type: "QVector2D"; isReadonly: true }
+ Property { name: "minimumX"; type: "double" }
+ Property { name: "maximumX"; type: "double" }
+ Property { name: "minimumY"; type: "double" }
+ Property { name: "maximumY"; type: "double" }
+ Property { name: "xAxis"; type: "QQuickDragAxis"; isReadonly: true; isPointer: true }
+ Property { name: "yAxis"; type: "QQuickDragAxis"; isReadonly: true; isPointer: true }
+ Signal { name: "updated" }
+ }
+ Component {
+ name: "QQuickPointHandler"
+ prototype: "QQuickSinglePointHandler"
+ exports: ["QtQuick/PointHandler 2.12"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "translation"; type: "QVector2D"; isReadonly: true }
+ }
+ Component {
+ name: "QQuickPointerDevice"
+ prototype: "QObject"
+ exports: ["QtQuick/PointerDevice 2.12"]
+ isCreatable: false
+ exportMetaObjectRevisions: [0]
+ Enum {
+ name: "DeviceType"
+ values: {
+ "UnknownDevice": 0,
+ "Mouse": 1,
+ "TouchScreen": 2,
+ "TouchPad": 4,
+ "Puck": 8,
+ "Stylus": 16,
+ "Airbrush": 32,
+ "AllDevices": 32767
+ }
+ }
+ Enum {
+ name: "DeviceTypes"
+ values: {
+ "UnknownDevice": 0,
+ "Mouse": 1,
+ "TouchScreen": 2,
+ "TouchPad": 4,
+ "Puck": 8,
+ "Stylus": 16,
+ "Airbrush": 32,
+ "AllDevices": 32767
+ }
+ }
+ Enum {
+ name: "PointerType"
+ values: {
+ "GenericPointer": 1,
+ "Finger": 2,
+ "Pen": 4,
+ "Eraser": 8,
+ "Cursor": 16,
+ "AllPointerTypes": 32767
+ }
+ }
+ Enum {
+ name: "PointerTypes"
+ values: {
+ "GenericPointer": 1,
+ "Finger": 2,
+ "Pen": 4,
+ "Eraser": 8,
+ "Cursor": 16,
+ "AllPointerTypes": 32767
+ }
+ }
+ Enum {
+ name: "CapabilityFlag"
+ values: {
+ "Position": 1,
+ "Area": 2,
+ "Pressure": 4,
+ "Velocity": 8,
+ "MouseEmulation": 64,
+ "Scroll": 256,
+ "Hover": 512,
+ "Rotation": 1024,
+ "XTilt": 2048,
+ "YTilt": 4096
+ }
+ }
+ Enum {
+ name: "Capabilities"
+ values: {
+ "Position": 1,
+ "Area": 2,
+ "Pressure": 4,
+ "Velocity": 8,
+ "MouseEmulation": 64,
+ "Scroll": 256,
+ "Hover": 512,
+ "Rotation": 1024,
+ "XTilt": 2048,
+ "YTilt": 4096
+ }
+ }
+ Property { name: "type"; type: "DeviceType"; isReadonly: true }
+ Property { name: "pointerType"; type: "PointerType"; isReadonly: true }
+ Property { name: "capabilities"; type: "Capabilities"; isReadonly: true }
+ Property { name: "maximumTouchPoints"; type: "int"; isReadonly: true }
+ Property { name: "buttonCount"; type: "int"; isReadonly: true }
+ Property { name: "name"; type: "string"; isReadonly: true }
+ Property { name: "uniqueId"; type: "QPointingDeviceUniqueId"; isReadonly: true }
+ }
+ Component {
+ name: "QQuickPointerDeviceHandler"
+ prototype: "QQuickPointerHandler"
+ Property { name: "acceptedDevices"; type: "QQuickPointerDevice::DeviceTypes" }
+ Property { name: "acceptedPointerTypes"; type: "QQuickPointerDevice::PointerTypes" }
+ Property { name: "acceptedButtons"; type: "Qt::MouseButtons" }
+ Property { name: "acceptedModifiers"; type: "Qt::KeyboardModifiers" }
+ Method {
+ name: "setAcceptedDevices"
+ Parameter { name: "acceptedDevices"; type: "QQuickPointerDevice::DeviceTypes" }
+ }
+ Method {
+ name: "setAcceptedPointerTypes"
+ Parameter { name: "acceptedPointerTypes"; type: "QQuickPointerDevice::PointerTypes" }
+ }
+ Method {
+ name: "setAcceptedButtons"
+ Parameter { name: "buttons"; type: "Qt::MouseButtons" }
+ }
+ Method {
+ name: "setAcceptedModifiers"
+ Parameter { name: "acceptedModifiers"; type: "Qt::KeyboardModifiers" }
+ }
+ }
+ Component {
+ name: "QQuickPointerEvent"
+ prototype: "QObject"
+ exports: ["QtQuick/PointerEvent 2.12"]
+ isCreatable: false
+ exportMetaObjectRevisions: [0]
+ Property { name: "device"; type: "QQuickPointerDevice"; isReadonly: true; isPointer: true }
+ Property { name: "modifiers"; type: "Qt::KeyboardModifiers"; isReadonly: true }
+ Property { name: "button"; type: "Qt::MouseButtons"; isReadonly: true }
+ Property { name: "buttons"; type: "Qt::MouseButtons"; isReadonly: true }
+ }
+ Component {
+ name: "QQuickPointerHandler"
+ prototype: "QObject"
+ exports: ["QtQuick/PointerHandler 2.12"]
+ isCreatable: false
+ exportMetaObjectRevisions: [0]
+ Enum {
+ name: "GrabPermissions"
+ values: {
+ "TakeOverForbidden": 0,
+ "CanTakeOverFromHandlersOfSameType": 1,
+ "CanTakeOverFromHandlersOfDifferentType": 2,
+ "CanTakeOverFromItems": 4,
+ "CanTakeOverFromAnything": 15,
+ "ApprovesTakeOverByHandlersOfSameType": 16,
+ "ApprovesTakeOverByHandlersOfDifferentType": 32,
+ "ApprovesTakeOverByItems": 64,
+ "ApprovesCancellation": 128,
+ "ApprovesTakeOverByAnything": 240
+ }
+ }
+ Property { name: "enabled"; type: "bool" }
+ Property { name: "active"; type: "bool"; isReadonly: true }
+ Property { name: "target"; type: "QQuickItem"; isPointer: true }
+ Property { name: "parent"; type: "QQuickItem"; isReadonly: true; isPointer: true }
+ Property { name: "grabPermissions"; type: "GrabPermissions" }
+ Property { name: "margin"; type: "double" }
+ Signal {
+ name: "grabChanged"
+ Parameter { name: "transition"; type: "QQuickEventPoint::GrabTransition" }
+ Parameter { name: "point"; type: "QQuickEventPoint"; isPointer: true }
+ }
+ Signal { name: "grabPermissionChanged" }
+ Signal {
+ name: "canceled"
+ Parameter { name: "point"; type: "QQuickEventPoint"; isPointer: true }
+ }
+ }
+ Component {
+ name: "QQuickPointerMouseEvent"
+ prototype: "QQuickSinglePointEvent"
+ exports: ["QtQuick/PointerMouseEvent 2.12"]
+ isCreatable: false
+ exportMetaObjectRevisions: [0]
+ }
+ Component {
+ name: "QQuickPointerTouchEvent"
+ prototype: "QQuickPointerEvent"
+ exports: ["QtQuick/PointerTouchEvent 2.12"]
+ isCreatable: false
+ exportMetaObjectRevisions: [0]
+ }
+ Component {
name: "QQuickPositionerAttached"
prototype: "QObject"
Property { name: "index"; type: "int"; isReadonly: true }
@@ -3470,7 +3778,7 @@ Module {
exports: ["QtQuick/Rectangle 2.0"]
exportMetaObjectRevisions: [0]
Property { name: "color"; type: "QColor" }
- Property { name: "gradient"; type: "QQuickGradient"; isPointer: true }
+ Property { name: "gradient"; type: "QJSValue" }
Property { name: "border"; type: "QQuickPen"; isReadonly: true; isPointer: true }
Property { name: "radius"; type: "double" }
}
@@ -3580,6 +3888,10 @@ Module {
Property { name: "right"; type: "int" }
Property { name: "bottom"; type: "int" }
Signal { name: "borderChanged" }
+ Signal { name: "leftBorderChanged" }
+ Signal { name: "topBorderChanged" }
+ Signal { name: "rightBorderChanged" }
+ Signal { name: "bottomBorderChanged" }
}
Component {
name: "QQuickScriptAction"
@@ -3704,6 +4016,12 @@ Module {
Signal { name: "activated" }
Signal { name: "activatedAmbiguously" }
}
+ Component { name: "QQuickSinglePointEvent"; prototype: "QQuickPointerEvent" }
+ Component {
+ name: "QQuickSinglePointHandler"
+ prototype: "QQuickPointerDeviceHandler"
+ Property { name: "point"; type: "QQuickHandlerPoint"; isReadonly: true }
+ }
Component {
name: "QQuickSmoothedAnimation"
prototype: "QQuickNumberAnimation"
@@ -4012,18 +4330,84 @@ Module {
Signal { name: "paletteChanged" }
}
Component {
+ name: "QQuickTableView"
+ defaultProperty: "flickableData"
+ prototype: "QQuickFlickable"
+ exports: ["QtQuick/TableView 2.12"]
+ exportMetaObjectRevisions: [0]
+ attachedType: "QQuickTableViewAttached"
+ Property { name: "rows"; type: "int"; isReadonly: true }
+ Property { name: "columns"; type: "int"; isReadonly: true }
+ Property { name: "rowSpacing"; type: "double" }
+ Property { name: "columnSpacing"; type: "double" }
+ Property { name: "topMargin"; type: "double" }
+ Property { name: "bottomMargin"; type: "double" }
+ Property { name: "leftMargin"; type: "double" }
+ Property { name: "rightMargin"; type: "double" }
+ Property { name: "rowHeightProvider"; type: "QJSValue" }
+ Property { name: "columnWidthProvider"; type: "QJSValue" }
+ Property { name: "model"; type: "QVariant" }
+ Property { name: "delegate"; type: "QQmlComponent"; isPointer: true }
+ Property { name: "reuseItems"; type: "bool" }
+ Property { name: "contentWidth"; type: "double" }
+ Property { name: "contentHeight"; type: "double" }
+ Signal { name: "contentWidthOverrideChanged" }
+ Signal { name: "contentHeightOverrideChanged" }
+ Method { name: "forceLayout" }
+ }
+ Component {
+ name: "QQuickTableViewAttached"
+ prototype: "QObject"
+ Property { name: "view"; type: "QQuickTableView"; isReadonly: true; isPointer: true }
+ Signal { name: "pooled" }
+ Signal { name: "reused" }
+ }
+ Component {
+ name: "QQuickTapHandler"
+ prototype: "QQuickSinglePointHandler"
+ exports: ["QtQuick/TapHandler 2.12"]
+ exportMetaObjectRevisions: [0]
+ Enum {
+ name: "GesturePolicy"
+ values: {
+ "DragThreshold": 0,
+ "WithinBounds": 1,
+ "ReleaseWithinBounds": 2
+ }
+ }
+ Property { name: "pressed"; type: "bool"; isReadonly: true }
+ Property { name: "tapCount"; type: "int"; isReadonly: true }
+ Property { name: "timeHeld"; type: "double"; isReadonly: true }
+ Property { name: "longPressThreshold"; type: "double" }
+ Property { name: "gesturePolicy"; type: "GesturePolicy" }
+ Signal {
+ name: "tapped"
+ Parameter { name: "eventPoint"; type: "QQuickEventPoint"; isPointer: true }
+ }
+ Signal {
+ name: "singleTapped"
+ Parameter { name: "eventPoint"; type: "QQuickEventPoint"; isPointer: true }
+ }
+ Signal {
+ name: "doubleTapped"
+ Parameter { name: "eventPoint"; type: "QQuickEventPoint"; isPointer: true }
+ }
+ Signal { name: "longPressed" }
+ }
+ Component {
name: "QQuickText"
defaultProperty: "data"
prototype: "QQuickImplicitSizeItem"
exports: [
"QtQuick/Text 2.0",
"QtQuick/Text 2.10",
+ "QtQuick/Text 2.12",
"QtQuick/Text 2.2",
"QtQuick/Text 2.3",
"QtQuick/Text 2.6",
"QtQuick/Text 2.9"
]
- exportMetaObjectRevisions: [0, 10, 2, 3, 6, 9]
+ exportMetaObjectRevisions: [0, 10, 12, 2, 3, 6, 9]
Enum {
name: "HAlignment"
values: {
@@ -4174,6 +4558,16 @@ Module {
}
Signal { name: "contentSizeChanged" }
Signal {
+ name: "contentWidthChanged"
+ revision: 12
+ Parameter { name: "contentWidth"; type: "double" }
+ }
+ Signal {
+ name: "contentHeightChanged"
+ revision: 12
+ Parameter { name: "contentHeight"; type: "double" }
+ }
+ Signal {
name: "lineHeightChanged"
Parameter { name: "lineHeight"; type: "double" }
}
diff --git a/src/imports/qtquick2/qtquick2.pro b/src/imports/qtquick2/qtquick2.pro
index 01ac034104..744dce4195 100644
--- a/src/imports/qtquick2/qtquick2.pro
+++ b/src/imports/qtquick2/qtquick2.pro
@@ -1,7 +1,7 @@
CXX_MODULE = qml
TARGET = qtquick2plugin
TARGETPATH = QtQuick.2
-IMPORT_VERSION = 2.11
+IMPORT_VERSION = 2.$$QT_MINOR_VERSION
SOURCES += \
plugin.cpp
diff --git a/src/imports/settings/dependencies.json b/src/imports/settings/dependencies.json
new file mode 100644
index 0000000000..0d4f101c7a
--- /dev/null
+++ b/src/imports/settings/dependencies.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/src/imports/settings/plugin.cpp b/src/imports/settings/plugin.cpp
index 65de78a2f1..3ac0ad4653 100644
--- a/src/imports/settings/plugin.cpp
+++ b/src/imports/settings/plugin.cpp
@@ -42,13 +42,6 @@
#include "qqmlsettings_p.h"
-static void initResources()
-{
-#ifdef QT_STATIC
- Q_INIT_RESOURCE(qmake_Qt_labs_settings);
-#endif
-}
-
QT_BEGIN_NAMESPACE
class QmlSettingsPlugin : public QQmlExtensionPlugin
@@ -57,11 +50,12 @@ class QmlSettingsPlugin : public QQmlExtensionPlugin
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
- QmlSettingsPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { initResources(); }
+ QmlSettingsPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { }
void registerTypes(const char *uri) override
{
Q_ASSERT(QByteArray(uri) == QByteArray("Qt.labs.settings"));
qmlRegisterType<QQmlSettings>(uri, 1, 0, "Settings");
+ qmlRegisterType<QQmlSettings,1>(uri, 1, 1, "Settings");
}
};
diff --git a/src/imports/settings/plugins.qmltypes b/src/imports/settings/plugins.qmltypes
index 40d8746525..1d69fcf7e9 100644
--- a/src/imports/settings/plugins.qmltypes
+++ b/src/imports/settings/plugins.qmltypes
@@ -4,15 +4,38 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable -noforceqtquick Qt.labs.settings 1.0'
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json Qt.labs.settings 1.1'
Module {
dependencies: []
Component {
name: "QQmlSettings"
prototype: "QObject"
- exports: ["Qt.labs.settings/Settings 1.0"]
- exportMetaObjectRevisions: [0]
+ exports: [
+ "Qt.labs.settings/Settings 1.0",
+ "Qt.labs.settings/Settings 1.1"
+ ]
+ exportMetaObjectRevisions: [0, 1]
Property { name: "category"; type: "string" }
+ Property { name: "fileName"; revision: 1; type: "string" }
+ Method {
+ name: "value"
+ revision: 1
+ type: "QVariant"
+ Parameter { name: "key"; type: "string" }
+ Parameter { name: "defaultValue"; type: "QVariant" }
+ }
+ Method {
+ name: "value"
+ revision: 1
+ type: "QVariant"
+ Parameter { name: "key"; type: "string" }
+ }
+ Method {
+ name: "setValue"
+ revision: 1
+ Parameter { name: "key"; type: "string" }
+ Parameter { name: "value"; type: "QVariant" }
+ }
}
}
diff --git a/src/imports/settings/qqmlsettings.cpp b/src/imports/settings/qqmlsettings.cpp
index 2271774643..6b3904909a 100644
--- a/src/imports/settings/qqmlsettings.cpp
+++ b/src/imports/settings/qqmlsettings.cpp
@@ -156,7 +156,8 @@ QT_BEGIN_NAMESPACE
Application specific settings are identified by providing application
\l {QCoreApplication::applicationName}{name},
\l {QCoreApplication::organizationName}{organization} and
- \l {QCoreApplication::organizationDomain}{domain}.
+ \l {QCoreApplication::organizationDomain}{domain}, or by specifying
+ \l fileName.
\code
#include <QGuiApplication>
@@ -258,6 +259,7 @@ public:
int timerId = 0;
bool initialized = false;
QString category;
+ QString fileName;
mutable QPointer<QSettings> settings;
QHash<const char *, QVariant> changedProperties;
};
@@ -268,7 +270,7 @@ QSettings *QQmlSettingsPrivate::instance() const
{
if (!settings) {
QQmlSettings *q = const_cast<QQmlSettings*>(q_func());
- settings = new QSettings(q);
+ settings = fileName.isEmpty() ? new QSettings(q) : new QSettings(fileName, QSettings::IniFormat, q);
if (!category.isEmpty())
settings->beginGroup(category);
if (initialized)
@@ -299,6 +301,11 @@ void QQmlSettingsPrivate::load()
const QMetaObject *mo = q->metaObject();
const int offset = mo->propertyOffset();
const int count = mo->propertyCount();
+
+ // don't save built-in properties if there aren't any qml properties
+ if (offset == 1)
+ return;
+
for (int i = offset; i < count; ++i) {
QMetaProperty property = mo->property(i);
@@ -398,6 +405,66 @@ void QQmlSettings::setCategory(const QString &category)
}
}
+/*!
+ \qmlproperty string Settings::fileName
+
+ This property holds the path to the settings file. If the file doesn't
+ already exist, it is created.
+
+ \sa QSettings::fileName, QSettings::IniFormat
+
+ \since Qt.labs.settings 1.1
+*/
+QString QQmlSettings::fileName() const
+{
+ Q_D(const QQmlSettings);
+ return d->fileName;
+}
+
+void QQmlSettings::setFileName(const QString &fileName)
+{
+ Q_D(QQmlSettings);
+ if (d->fileName != fileName) {
+ d->reset();
+ d->fileName = fileName;
+ if (d->initialized)
+ d->load();
+ }
+}
+
+/*!
+ \qmlmethod var Settings::value(string key, var defaultValue)
+
+ Returns the value for setting \a key. If the setting doesn't exist,
+ returns \a defaultValue.
+
+ \sa QSettings::value
+
+ \since Qt.labs.settings 1.1
+*/
+QVariant QQmlSettings::value(const QString &key, const QVariant &defaultValue) const
+{
+ Q_D(const QQmlSettings);
+ return d->instance()->value(key, defaultValue);
+}
+
+/*!
+ \qmlmethod Settings::setValue(string key, var value)
+
+ Sets the value of setting key to value. If the key already exists,
+ the previous value is overwritten.
+
+ \sa QSettings::setValue
+
+ \since Qt.labs.settings 1.1
+*/
+void QQmlSettings::setValue(const QString &key, const QVariant &value)
+{
+ Q_D(const QQmlSettings);
+ d->instance()->setValue(key, value);
+ qCDebug(lcSettings) << "QQmlSettings: setValue" << key << ":" << value;
+}
+
void QQmlSettings::classBegin()
{
}
diff --git a/src/imports/settings/qqmlsettings_p.h b/src/imports/settings/qqmlsettings_p.h
index ce942d7564..f73e595557 100644
--- a/src/imports/settings/qqmlsettings_p.h
+++ b/src/imports/settings/qqmlsettings_p.h
@@ -65,6 +65,7 @@ class QQmlSettings : public QObject, public QQmlParserStatus
Q_OBJECT
Q_INTERFACES(QQmlParserStatus)
Q_PROPERTY(QString category READ category WRITE setCategory FINAL)
+ Q_PROPERTY(QString fileName READ fileName WRITE setFileName FINAL REVISION 1)
public:
explicit QQmlSettings(QObject *parent = 0);
@@ -73,6 +74,12 @@ public:
QString category() const;
void setCategory(const QString &category);
+ QString fileName() const;
+ void setFileName(const QString &fileName);
+
+ Q_REVISION(1) Q_INVOKABLE QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const;
+ Q_REVISION(1) Q_INVOKABLE void setValue(const QString &key, const QVariant &value);
+
protected:
void timerEvent(QTimerEvent *event) override;
diff --git a/src/imports/settings/settings.pro b/src/imports/settings/settings.pro
index 29229f59cb..816a6a9fef 100644
--- a/src/imports/settings/settings.pro
+++ b/src/imports/settings/settings.pro
@@ -1,7 +1,7 @@
CXX_MODULE = qml
TARGET = qmlsettingsplugin
TARGETPATH = Qt/labs/settings
-IMPORT_VERSION = 1.0
+IMPORT_VERSION = 1.1
QT = core qml
diff --git a/src/imports/shapes/plugin.cpp b/src/imports/shapes/plugin.cpp
index f0e66479b6..e3a9017681 100644
--- a/src/imports/shapes/plugin.cpp
+++ b/src/imports/shapes/plugin.cpp
@@ -39,16 +39,7 @@
#include <QtQml/qqmlextensionplugin.h>
#include <QtQml/qqml.h>
-
-#include "qquickshape_p.h"
-
-static void initResources()
-{
-#ifdef QT_STATIC
- Q_INIT_RESOURCE(qmake_QtQuick_Shapes);
-#endif
- Q_INIT_RESOURCE(qtquickshapesplugin);
-}
+#include <QtQuickShapes/private/qquickshape_p.h>
QT_BEGIN_NAMESPACE
@@ -58,7 +49,11 @@ class QmlShapesPlugin : public QQmlExtensionPlugin
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
- QmlShapesPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { initResources(); }
+ QmlShapesPlugin(QObject *parent = nullptr)
+ : QQmlExtensionPlugin(parent)
+ {
+ }
+
void registerTypes(const char *uri) override
{
Q_ASSERT(QByteArray(uri) == QByteArray("QtQuick.Shapes"));
diff --git a/src/imports/shapes/plugins.qmltypes b/src/imports/shapes/plugins.qmltypes
index a140644d6d..a851b20ed2 100644
--- a/src/imports/shapes/plugins.qmltypes
+++ b/src/imports/shapes/plugins.qmltypes
@@ -4,10 +4,10 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtQuick.Shapes 1.11'
+// 'qmlplugindump -nonrelocatable QtQuick.Shapes 1.12'
Module {
- dependencies: ["QtQuick 2.8"]
+ dependencies: ["QtQuick 2.12"]
Component {
name: "QQuickShape"
defaultProperty: "data"
diff --git a/src/imports/shapes/shapes.pro b/src/imports/shapes/shapes.pro
index 4d6e9508af..857fcd7564 100644
--- a/src/imports/shapes/shapes.pro
+++ b/src/imports/shapes/shapes.pro
@@ -1,33 +1,11 @@
CXX_MODULE = qml
TARGET = qmlshapesplugin
TARGETPATH = QtQuick/Shapes
-IMPORT_VERSION = 1.11
+IMPORT_VERSION = 1.$$QT_MINOR_VERSION
-QT = core gui-private qml quick-private
-
-HEADERS += \
- qquickshape_p.h \
- qquickshape_p_p.h \
- qquickshapegenericrenderer_p.h \
- qquickshapesoftwarerenderer_p.h
+QT = core gui-private qml quick-private quickshapes-private
SOURCES += \
plugin.cpp \
- qquickshape.cpp \
- qquickshapegenericrenderer.cpp \
- qquickshapesoftwarerenderer.cpp
-
-qtConfig(opengl) {
- HEADERS += \
- qquicknvprfunctions_p.h \
- qquicknvprfunctions_p_p.h \
- qquickshapenvprrenderer_p.h
-
- SOURCES += \
- qquicknvprfunctions.cpp \
- qquickshapenvprrenderer.cpp
-}
-
-RESOURCES += qtquickshapesplugin.qrc
load(qml_plugin)
diff --git a/src/imports/sharedimage/plugins.qmltypes b/src/imports/sharedimage/plugins.qmltypes
index 46fa24df1a..504186936b 100644
--- a/src/imports/sharedimage/plugins.qmltypes
+++ b/src/imports/sharedimage/plugins.qmltypes
@@ -7,5 +7,5 @@ import QtQuick.tooling 1.2
// 'qmlplugindump -nonrelocatable Qt.labs.sharedimage 1.0'
Module {
- dependencies: ["QtQuick 2.8"]
+ dependencies: ["QtQuick 2.12"]
}
diff --git a/src/imports/statemachine/dependencies.json b/src/imports/statemachine/dependencies.json
new file mode 100644
index 0000000000..0d4f101c7a
--- /dev/null
+++ b/src/imports/statemachine/dependencies.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/src/imports/statemachine/plugin.cpp b/src/imports/statemachine/plugin.cpp
index 93ced6e280..bf7499b31a 100644
--- a/src/imports/statemachine/plugin.cpp
+++ b/src/imports/statemachine/plugin.cpp
@@ -47,13 +47,6 @@
#include <QQmlExtensionPlugin>
#include <qqml.h>
-static void initResources()
-{
-#ifdef QT_STATIC
- Q_INIT_RESOURCE(qmake_QtQml_StateMachine);
-#endif
-}
-
QT_BEGIN_NAMESPACE
class QtQmlStateMachinePlugin : public QQmlExtensionPlugin
@@ -62,7 +55,7 @@ class QtQmlStateMachinePlugin : public QQmlExtensionPlugin
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
- QtQmlStateMachinePlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { initResources(); }
+ QtQmlStateMachinePlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { }
void registerTypes(const char *uri) override
{
qmlRegisterType<State>(uri, 1, 0, "State");
diff --git a/src/imports/statemachine/plugins.qmltypes b/src/imports/statemachine/plugins.qmltypes
index e6ecaa75c8..001efd3847 100644
--- a/src/imports/statemachine/plugins.qmltypes
+++ b/src/imports/statemachine/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtQml.StateMachine 1.11'
+// 'qmlplugindump -nonrelocatable -dependencies dependencies.json QtQml.StateMachine 1.12'
Module {
dependencies: []
diff --git a/src/imports/statemachine/signaltransition.cpp b/src/imports/statemachine/signaltransition.cpp
index ab625788bb..d4ea25cc4b 100644
--- a/src/imports/statemachine/signaltransition.cpp
+++ b/src/imports/statemachine/signaltransition.cpp
@@ -185,12 +185,12 @@ void SignalTransition::connectTriggered()
m_signalExpression = expression;
}
-void SignalTransitionParser::verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props)
+void SignalTransitionParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props)
{
for (int ii = 0; ii < props.count(); ++ii) {
const QV4::CompiledData::Binding *binding = props.at(ii);
- QString propName = qmlUnit->stringAt(binding->propertyNameIndex);
+ QString propName = compilationUnit->stringAt(binding->propertyNameIndex);
if (propName != QLatin1String("onTriggered")) {
error(props.at(ii), SignalTransition::tr("Cannot assign to non-existent property \"%1\"").arg(propName));
@@ -204,7 +204,7 @@ void SignalTransitionParser::verifyBindings(const QV4::CompiledData::Unit *qmlUn
}
}
-void SignalTransitionParser::applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
+void SignalTransitionParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
{
SignalTransition *st = qobject_cast<SignalTransition*>(object);
st->m_compilationUnit = compilationUnit;
diff --git a/src/imports/statemachine/signaltransition.h b/src/imports/statemachine/signaltransition.h
index d7a3b7b618..90e6f96fbc 100644
--- a/src/imports/statemachine/signaltransition.h
+++ b/src/imports/statemachine/signaltransition.h
@@ -97,8 +97,8 @@ private:
class SignalTransitionParser : public QQmlCustomParser
{
public:
- void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props) override;
- void applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
+ void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) override;
+ void applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
};
QT_END_NAMESPACE
diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml
index 77b644d3f2..535e29ee70 100644
--- a/src/imports/testlib/TestCase.qml
+++ b/src/imports/testlib/TestCase.qml
@@ -1084,6 +1084,24 @@ Item {
does not occur, then the test will fail. Similar to
\c{QTest::ignoreMessage(QtWarningMsg, message)} in C++.
+ Since Qt 5.12, \a message can be either a string, or a regular
+ expression providing a pattern of messages to ignore.
+
+ For example, the following snippet will ignore a string warning message:
+ \qml
+ ignoreWarning("Something sort of bad happened")
+ \endqml
+
+ And the following snippet will ignore a regular expression matching a
+ number of possible warning messages:
+ \qml
+ ignoreWarning(new RegExp("[0-9]+ bad things happened"))
+ \endqml
+
+ \note Despite being a JavaScript RegExp object, it will not be
+ interpreted as such; instead, the pattern will be passed to
+ \l QRegularExpression.
+
\sa warn()
*/
function ignoreWarning(msg) {
diff --git a/src/imports/testlib/main.cpp b/src/imports/testlib/main.cpp
index 443229bee9..af15a44012 100644
--- a/src/imports/testlib/main.cpp
+++ b/src/imports/testlib/main.cpp
@@ -54,13 +54,6 @@ QML_DECLARE_TYPE(QuickTestEvent)
#include <QtDebug>
-static void initResources()
-{
-#ifdef QT_STATIC
- Q_INIT_RESOURCE(qmake_QtTest);
-#endif
-}
-
QT_BEGIN_NAMESPACE
class QuickTestUtil : public QObject
@@ -150,7 +143,7 @@ class QTestQmlModule : public QQmlExtensionPlugin
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
- QTestQmlModule(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { initResources(); }
+ QTestQmlModule(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { }
void registerTypes(const char *uri) override
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("QtTest"));
diff --git a/src/imports/testlib/plugins.qmltypes b/src/imports/testlib/plugins.qmltypes
index 7f3140d86b..56b4ecf662 100644
--- a/src/imports/testlib/plugins.qmltypes
+++ b/src/imports/testlib/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable -noforceqtquick QtTest 1.2'
+// 'qmlplugindump -nonrelocatable QtTest 1.12'
Module {
dependencies: ["QtQuick 2.0", "QtQuick.Window 2.0"]
@@ -269,7 +269,7 @@ Module {
}
Method {
name: "ignoreWarning"
- Parameter { name: "message"; type: "string" }
+ Parameter { name: "message"; type: "QJSValue" }
}
Method {
name: "wait"
diff --git a/src/imports/window/plugin.cpp b/src/imports/window/plugin.cpp
index 907eecb060..657b230fa2 100644
--- a/src/imports/window/plugin.cpp
+++ b/src/imports/window/plugin.cpp
@@ -41,17 +41,10 @@
#include <private/qquickwindowmodule_p.h>
-static void initResources()
-{
-#ifdef QT_STATIC
- Q_INIT_RESOURCE(qmake_QtQuick_Window_2);
-#endif
-}
-
QT_BEGIN_NAMESPACE
/*!
- \qmlmodule QtQuick.Window 2.11
+ \qmlmodule QtQuick.Window 2.\QtMinorVersion
\title Qt Quick Window QML Types
\ingroup qmlmodules
\brief Provides QML types for window management
@@ -60,9 +53,9 @@ QT_BEGIN_NAMESPACE
To use the types in this module, import the module with the following line:
- \code
- import QtQuick.Window 2.11
- \endcode
+ \qml \QtMinorVersion
+ import QtQuick.Window 2.\1
+ \endqml
*/
@@ -72,7 +65,7 @@ class QtQuick2WindowPlugin : public QQmlExtensionPlugin
Q_OBJECT
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
- QtQuick2WindowPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { initResources(); }
+ QtQuick2WindowPlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { }
void registerTypes(const char *uri) override
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Window"));
diff --git a/src/imports/window/plugins.qmltypes b/src/imports/window/plugins.qmltypes
index 4815be7262..8ecb09968c 100644
--- a/src/imports/window/plugins.qmltypes
+++ b/src/imports/window/plugins.qmltypes
@@ -4,10 +4,10 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtQuick.Window 2.11'
+// 'qmlplugindump -nonrelocatable QtQuick.Window 2.12'
Module {
- dependencies: ["QtQuick 2.8"]
+ dependencies: ["QtQuick 2.12"]
Component {
name: "QQuickRootItem"
defaultProperty: "data"
diff --git a/src/imports/xmllistmodel/plugin.cpp b/src/imports/xmllistmodel/plugin.cpp
index 82e11eeeb3..c5356b8534 100644
--- a/src/imports/xmllistmodel/plugin.cpp
+++ b/src/imports/xmllistmodel/plugin.cpp
@@ -42,13 +42,6 @@
#include "qqmlxmllistmodel_p.h"
-static void initResources()
-{
-#ifdef QT_STATIC
- Q_INIT_RESOURCE(qmake_QtQuick_XmlListModel);
-#endif
-}
-
QT_BEGIN_NAMESPACE
class QmlXmlListModelPlugin : public QQmlExtensionPlugin
@@ -57,7 +50,7 @@ class QmlXmlListModelPlugin : public QQmlExtensionPlugin
Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
public:
- QmlXmlListModelPlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); }
+ QmlXmlListModelPlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { }
void registerTypes(const char *uri) override
{
Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.XmlListModel"));
diff --git a/src/imports/xmllistmodel/plugins.qmltypes b/src/imports/xmllistmodel/plugins.qmltypes
index cc675d5f83..951d0b6eeb 100644
--- a/src/imports/xmllistmodel/plugins.qmltypes
+++ b/src/imports/xmllistmodel/plugins.qmltypes
@@ -4,10 +4,10 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtQuick.XmlListModel 2.0'
+// 'qmlplugindump -nonrelocatable QtQuick.XmlListModel 2.12'
Module {
- dependencies: ["QtQuick 2.8"]
+ dependencies: ["QtQuick 2.12"]
Component {
name: "QQuickXmlListModel"
defaultProperty: "roles"
diff --git a/src/imports/xmllistmodel/qqmlxmllistmodel.cpp b/src/imports/xmllistmodel/qqmlxmllistmodel.cpp
index 773d9747f0..470b419c1f 100644
--- a/src/imports/xmllistmodel/qqmlxmllistmodel.cpp
+++ b/src/imports/xmllistmodel/qqmlxmllistmodel.cpp
@@ -54,8 +54,10 @@
#include <QXmlResultItems>
#include <QXmlNodeModelIndex>
#include <QBuffer>
+#if QT_CONFIG(qml_network)
#include <QNetworkRequest>
#include <QNetworkReply>
+#endif
#include <QTimer>
#include <QMutex>
@@ -72,7 +74,7 @@ typedef QPair<int, int> QQuickXmlListRange;
#define XMLLISTMODEL_CLEAR_ID 0
/*!
- \qmlmodule QtQuick.XmlListModel 2.11
+ \qmlmodule QtQuick.XmlListModel 2.\QtMinorVersion
\title Qt Quick XmlListModel QML Types
\ingroup qmlmodules
\brief Provides QML types for creating models from XML data
@@ -81,9 +83,9 @@ typedef QPair<int, int> QQuickXmlListRange;
To use the types in this module, import the module with the following line:
- \code
- import QtQuick.XmlListModel 2.11
- \endcode
+ \qml \QtMinorVersion
+ import QtQuick.XmlListModel 2.\1
+ \endqml
*/
/*!
@@ -542,7 +544,10 @@ class QQuickXmlListModelPrivate : public QAbstractItemModelPrivate
public:
QQuickXmlListModelPrivate()
: isComponentComplete(true), size(0), highestRole(Qt::UserRole)
- , reply(0), status(QQuickXmlListModel::Null), progress(0.0)
+#if QT_CONFIG(qml_network)
+ , reply(0)
+#endif
+ , status(QQuickXmlListModel::Null), progress(0.0)
, queryId(-1), roleObjects(), redirectCount(0) {}
@@ -555,6 +560,7 @@ public:
emit q->statusChanged(status);
}
+#if QT_CONFIG(qml_network)
void deleteReply() {
Q_Q(QQuickXmlListModel);
if (reply) {
@@ -563,6 +569,7 @@ public:
reply = 0;
}
}
+#endif
bool isComponentComplete;
QUrl src;
@@ -574,7 +581,9 @@ public:
QStringList roleNames;
int highestRole;
+#if QT_CONFIG(qml_network)
QNetworkReply *reply;
+#endif
QQuickXmlListModel::Status status;
QString errorString;
qreal progress;
@@ -1036,10 +1045,12 @@ void QQuickXmlListModel::reload()
if (d->size < 0)
d->size = 0;
+#if QT_CONFIG(qml_network)
if (d->reply) {
d->reply->abort();
d->deleteReply();
}
+#endif
if (!d->xml.isEmpty()) {
d->queryId = QQuickXmlQueryEngine::instance(qmlEngine(this))->doQuery(d->query, d->namespaces, d->xml.toUtf8(), &d->roleObjects, d->keyRoleResultsCache);
@@ -1050,7 +1061,19 @@ void QQuickXmlListModel::reload()
d->notifyQueryStarted(false);
QTimer::singleShot(0, this, SLOT(dataCleared()));
+ } else if (QQmlFile::isLocalFile(d->src)) {
+ QFile file(QQmlFile::urlToLocalFileOrQrc(d->src));
+ QByteArray data = file.open(QIODevice::ReadOnly) ? file.readAll() : QByteArray();
+ d->notifyQueryStarted(false);
+ if (data.isEmpty()) {
+ d->queryId = XMLLISTMODEL_CLEAR_ID;
+ QTimer::singleShot(0, this, SLOT(dataCleared()));
+ } else {
+ d->queryId = QQuickXmlQueryEngine::instance(qmlEngine(this))->doQuery(
+ d->query, d->namespaces, data, &d->roleObjects, d->keyRoleResultsCache);
+ }
} else {
+#if QT_CONFIG(qml_network)
d->notifyQueryStarted(true);
QNetworkRequest req(d->src);
req.setRawHeader("Accept", "application/xml,*/*");
@@ -1058,11 +1081,17 @@ void QQuickXmlListModel::reload()
QObject::connect(d->reply, SIGNAL(finished()), this, SLOT(requestFinished()));
QObject::connect(d->reply, SIGNAL(downloadProgress(qint64,qint64)),
this, SLOT(requestProgress(qint64,qint64)));
+#else
+ d->queryId = XMLLISTMODEL_CLEAR_ID;
+ d->notifyQueryStarted(false);
+ QTimer::singleShot(0, this, SLOT(dataCleared()));
+#endif
}
}
#define XMLLISTMODEL_MAX_REDIRECT 16
+#if QT_CONFIG(qml_network)
void QQuickXmlListModel::requestFinished()
{
Q_D(QQuickXmlListModel);
@@ -1108,6 +1137,7 @@ void QQuickXmlListModel::requestFinished()
emit progressChanged(d->progress);
}
}
+#endif
void QQuickXmlListModel::requestProgress(qint64 received, qint64 total)
{
diff --git a/src/imports/xmllistmodel/qqmlxmllistmodel_p.h b/src/imports/xmllistmodel/qqmlxmllistmodel_p.h
index e6a0898bb9..65f1299324 100644
--- a/src/imports/xmllistmodel/qqmlxmllistmodel_p.h
+++ b/src/imports/xmllistmodel/qqmlxmllistmodel_p.h
@@ -144,7 +144,9 @@ public Q_SLOTS:
void reload();
private Q_SLOTS:
+#if QT_CONFIG(qml_network)
void requestFinished();
+#endif
void requestProgress(qint64,qint64);
void dataCleared();
void queryCompleted(const QQuickXmlQueryResult &);
diff --git a/src/imports/xmllistmodel/xmllistmodel.pro b/src/imports/xmllistmodel/xmllistmodel.pro
index 5eaaaf6af9..1e61f4d3d9 100644
--- a/src/imports/xmllistmodel/xmllistmodel.pro
+++ b/src/imports/xmllistmodel/xmllistmodel.pro
@@ -3,7 +3,8 @@ TARGET = qmlxmllistmodelplugin
TARGETPATH = QtQuick/XmlListModel
IMPORT_VERSION = 2.$$QT_MINOR_VERSION
-QT = network xmlpatterns qml-private core-private
+QT = xmlpatterns qml-private core-private
+qtConfig(qml-network): QT += network
SOURCES += qqmlxmllistmodel.cpp plugin.cpp
HEADERS += qqmlxmllistmodel_p.h
diff --git a/src/particles/qquickcustomaffector.cpp b/src/particles/qquickcustomaffector.cpp
index 53557e1d0b..ccb00eeba2 100644
--- a/src/particles/qquickcustomaffector.cpp
+++ b/src/particles/qquickcustomaffector.cpp
@@ -154,7 +154,7 @@ void QQuickCustomAffector::affectSystem(qreal dt)
QV4::ScopedArrayObject array(scope, v4->newArrayObject(toAffect.size()));
QV4::ScopedValue v(scope);
for (int i=0; i<toAffect.size(); i++)
- array->putIndexed(i, (v = toAffect[i]->v4Value(m_system)));
+ array->put(i, (v = toAffect[i]->v4Value(m_system)));
if (dt >= simulationCutoff || dt <= simulationDelta) {
affectProperties(toAffect, dt);
diff --git a/src/particles/qquickitemparticle_p.h b/src/particles/qquickitemparticle_p.h
index 9e6c7deaea..7867add4e4 100644
--- a/src/particles/qquickitemparticle_p.h
+++ b/src/particles/qquickitemparticle_p.h
@@ -56,7 +56,6 @@
#include <private/qquickanimation_p_p.h>
QT_BEGIN_NAMESPACE
-class QQuickVisualDataModel;
class QQuickItemParticleAttached;
class QQuickItemParticle : public QQuickParticlePainter
diff --git a/src/particles/qquickparticleemitter.cpp b/src/particles/qquickparticleemitter.cpp
index cd4cdcf3ef..5ec834a463 100644
--- a/src/particles/qquickparticleemitter.cpp
+++ b/src/particles/qquickparticleemitter.cpp
@@ -494,7 +494,7 @@ void QQuickParticleEmitter::emitWindow(int timeStamp)
QV4::ScopedArrayObject array(scope, v4->newArrayObject(toEmit.size()));
QV4::ScopedValue v(scope);
for (int i=0; i<toEmit.size(); i++)
- array->putIndexed(i, (v = toEmit[i]->v4Value(m_system)));
+ array->put(i, (v = toEmit[i]->v4Value(m_system)));
emitParticles(QQmlV4Handle(array));//A chance for arbitrary JS changes
}
diff --git a/src/particles/qquicktrailemitter.cpp b/src/particles/qquicktrailemitter.cpp
index f0a0b297e2..ca3ebbd4ec 100644
--- a/src/particles/qquicktrailemitter.cpp
+++ b/src/particles/qquicktrailemitter.cpp
@@ -273,7 +273,7 @@ void QQuickTrailEmitter::emitWindow(int timeStamp)
QV4::ScopedArrayObject array(scope, v4->newArrayObject(toEmit.size()));
QV4::ScopedValue v(scope);
for (int i=0; i<toEmit.size(); i++)
- array->putIndexed(i, (v = toEmit[i]->v4Value(m_system)));
+ array->put(i, (v = toEmit[i]->v4Value(m_system)));
if (isEmitFollowConnected())
emitFollowParticles(QQmlV4Handle(array), d->v4Value(m_system));//A chance for many arbitrary JS changes
diff --git a/src/particles/qquickv4particledata.cpp b/src/particles/qquickv4particledata.cpp
index eb9d2288d6..42b30f0472 100644
--- a/src/particles/qquickv4particledata.cpp
+++ b/src/particles/qquickv4particledata.cpp
@@ -518,9 +518,9 @@ QQuickV4ParticleData::QQuickV4ParticleData(QV4::ExecutionEngine* v4, QQuickParti
QV4::Scope scope(v4);
QV4ParticleDataDeletable *d = particleV4Data(scope.engine);
- QV4::ScopedObject o(scope, v4->memoryManager->allocObject<QV4ParticleData>(datum, system));
+ QV4::ScopedObject o(scope, v4->memoryManager->allocate<QV4ParticleData>(datum, system));
QV4::ScopedObject p(scope, d->proto.value());
- o->setPrototype(p);
+ o->setPrototypeUnchecked(p);
m_v4Value = o;
}
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
index 0afd71767e..86fdf87650 100644
--- a/src/plugins/plugins.pro
+++ b/src/plugins/plugins.pro
@@ -1,5 +1,5 @@
TEMPLATE = subdirs
QT_FOR_CONFIG += qml
-qtConfig(qml-debug):SUBDIRS += qmltooling
+qtConfig(thread):qtConfig(qml-debug):SUBDIRS += qmltooling
qtHaveModule(quick):SUBDIRS += scenegraph
diff --git a/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp b/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp
index e1d6263e36..3e75e39f86 100644
--- a/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp
+++ b/src/plugins/qmltooling/packetprotocol/qpacketprotocol.cpp
@@ -106,6 +106,9 @@ class QPacketProtocolPrivate : public QObjectPrivate
public:
QPacketProtocolPrivate(QIODevice *dev);
+ bool writeToDevice(const char *bytes, qint64 size);
+ bool readFromDevice(char *buffer, qint64 size);
+
QList<qint32> sendingPackets;
QList<QByteArray> packets;
QByteArray inProgress;
@@ -125,7 +128,6 @@ QPacketProtocol::QPacketProtocol(QIODevice *dev, QObject *parent)
Q_ASSERT(dev);
QObject::connect(dev, &QIODevice::readyRead, this, &QPacketProtocol::readyToRead);
- QObject::connect(dev, &QIODevice::aboutToClose, this, &QPacketProtocol::aboutToClose);
QObject::connect(dev, &QIODevice::bytesWritten, this, &QPacketProtocol::bytesWritten);
}
@@ -143,18 +145,18 @@ void QPacketProtocol::send(const QByteArray &data)
return; // We don't send empty packets
if (data.size() > maxSize) {
- emit invalidPacket();
+ emit error();
return;
}
- qint32 sendSize = data.size() + sizeof(qint32);
+ const qint32 sendSize = data.size() + static_cast<qint32>(sizeof(qint32));
d->sendingPackets.append(sendSize);
+
qint32 sendSizeLE = qToLittleEndian(sendSize);
- qint64 writeBytes = d->dev->write((char *)&sendSizeLE, sizeof(qint32));
- Q_UNUSED(writeBytes);
- Q_ASSERT(writeBytes == sizeof(qint32));
- writeBytes = d->dev->write(data);
- Q_ASSERT(writeBytes == data.size());
+ if (!d->writeToDevice((const char *)&sendSizeLE, sizeof(qint32))
+ || !d->writeToDevice(data.data(), data.size())) {
+ emit error();
+ }
}
/*!
@@ -206,17 +208,6 @@ bool QPacketProtocol::waitForReadyRead(int msecs)
} while (true);
}
-/*!
- Return the QIODevice passed to the QPacketProtocol constructor.
-*/
-void QPacketProtocol::aboutToClose()
-{
- Q_D(QPacketProtocol);
- d->inProgress.clear();
- d->sendingPackets.clear();
- d->inProgressSize = -1;
-}
-
void QPacketProtocol::bytesWritten(qint64 bytes)
{
Q_D(QPacketProtocol);
@@ -240,28 +231,40 @@ void QPacketProtocol::readyToRead()
// Need to get trailing data
if (-1 == d->inProgressSize) {
// We need a size header of sizeof(qint32)
- if (sizeof(qint32) > (uint)d->dev->bytesAvailable())
+ if (static_cast<qint64>(sizeof(qint32)) > d->dev->bytesAvailable())
return;
// Read size header
qint32 inProgressSizeLE;
- const qint64 read = d->dev->read((char *)&inProgressSizeLE, sizeof(qint32));
+ if (!d->readFromDevice((char *)&inProgressSizeLE, sizeof(qint32))) {
+ emit error();
+ return;
+ }
d->inProgressSize = qFromLittleEndian(inProgressSizeLE);
// Check sizing constraints
- if (read != sizeof(qint32) || d->inProgressSize < read) {
+ if (d->inProgressSize < qint32(sizeof(qint32))) {
disconnect(d->dev, &QIODevice::readyRead, this, &QPacketProtocol::readyToRead);
- disconnect(d->dev, &QIODevice::aboutToClose, this, &QPacketProtocol::aboutToClose);
disconnect(d->dev, &QIODevice::bytesWritten, this, &QPacketProtocol::bytesWritten);
d->dev = nullptr;
- emit invalidPacket();
+ emit error();
return;
}
- d->inProgressSize -= read;
+ d->inProgressSize -= sizeof(qint32);
} else {
- d->inProgress.append(d->dev->read(d->inProgressSize - d->inProgress.size()));
+ const int bytesToRead = static_cast<int>(
+ qMin(d->dev->bytesAvailable(),
+ static_cast<qint64>(d->inProgressSize - d->inProgress.size())));
+
+ QByteArray toRead(bytesToRead, Qt::Uninitialized);
+ if (!d->readFromDevice(toRead.data(), toRead.length())) {
+ emit error();
+ return;
+ }
+
+ d->inProgress.append(toRead);
if (d->inProgressSize == d->inProgress.size()) {
// Packet has arrived!
d->packets.append(d->inProgress);
@@ -281,6 +284,30 @@ QPacketProtocolPrivate::QPacketProtocolPrivate(QIODevice *dev) :
{
}
+bool QPacketProtocolPrivate::writeToDevice(const char *bytes, qint64 size)
+{
+ qint64 totalWritten = 0;
+ while (totalWritten < size) {
+ const qint64 chunkSize = dev->write(bytes + totalWritten, size - totalWritten);
+ if (chunkSize < 0)
+ return false;
+ totalWritten += chunkSize;
+ }
+ return totalWritten == size;
+}
+
+bool QPacketProtocolPrivate::readFromDevice(char *buffer, qint64 size)
+{
+ qint64 totalRead = 0;
+ while (totalRead < size) {
+ const qint64 chunkSize = dev->read(buffer + totalRead, size - totalRead);
+ if (chunkSize < 0)
+ return false;
+ totalRead += chunkSize;
+ }
+ return totalRead == size;
+}
+
/*!
\fn void QPacketProtocol::readyRead()
diff --git a/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h b/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h
index 35edb568aa..b401a58437 100644
--- a/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h
+++ b/src/plugins/qmltooling/packetprotocol/qpacketprotocol_p.h
@@ -72,10 +72,9 @@ public:
Q_SIGNALS:
void readyRead();
- void invalidPacket();
+ void error();
private:
- void aboutToClose();
void bytesWritten(qint64 bytes);
void readyToRead();
};
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
index 95e6d5704c..3dfb755936 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
@@ -46,6 +46,7 @@
#include <private/qv4objectiterator_p.h>
#include <private/qv4identifier_p.h>
#include <private/qv4runtime_p.h>
+#include <private/qv4identifiertable_p.h>
#include <private/qqmlcontext_p.h>
#include <private/qqmlengine_p.h>
@@ -96,33 +97,28 @@ int QV4DataCollector::encodeScopeType(QV4::Heap::ExecutionContext::ContextType s
{
switch (scopeType) {
case QV4::Heap::ExecutionContext::Type_GlobalContext:
- return 0;
- case QV4::Heap::ExecutionContext::Type_CatchContext:
- return 4;
+ break;
case QV4::Heap::ExecutionContext::Type_WithContext:
return 2;
case QV4::Heap::ExecutionContext::Type_CallContext:
return 1;
case QV4::Heap::ExecutionContext::Type_QmlContext:
return 3;
- default:
- return -1;
+ case QV4::Heap::ExecutionContext::Type_BlockContext:
+ return 4;
}
+ return 0;
}
QV4DataCollector::QV4DataCollector(QV4::ExecutionEngine *engine)
- : m_engine(engine), m_namesAsObjects(true), m_redundantRefs(true)
+ : m_engine(engine)
{
m_values.set(engine, engine->newArrayObject());
}
-// TODO: Directly call addRef() once we don't need to support redundantRefs anymore
-QV4DataCollector::Ref QV4DataCollector::collect(const QV4::ScopedValue &value)
+QV4DataCollector::Ref QV4DataCollector::addValueRef(const QV4::ScopedValue &value)
{
- Ref ref = addRef(value);
- if (m_redundantRefs)
- m_collectedRefs.append(ref);
- return ref;
+ return addRef(value);
}
const QV4::Object *collectProperty(const QV4::ScopedValue &value, QV4::ExecutionEngine *engine,
@@ -158,15 +154,13 @@ const QV4::Object *collectProperty(const QV4::ScopedValue &value, QV4::Execution
int numProperties = 0;
QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly);
QV4::PropertyAttributes attrs;
- uint index;
QV4::ScopedProperty p(scope);
- QV4::ScopedString name(scope);
+ QV4::ScopedPropertyKey name(scope);
while (true) {
- it.next(name.getRef(), &index, p, &attrs);
- if (attrs.isEmpty())
+ name = it.next(p, &attrs);
+ if (!name->isValid())
break;
- else
- ++numProperties;
+ ++numProperties;
}
dict.insert(valueKey, numProperties);
return o;
@@ -192,61 +186,21 @@ const QV4::Object *collectProperty(const QV4::ScopedValue &value, QV4::Execution
}
}
-QJsonObject QV4DataCollector::lookupRef(Ref ref, bool deep)
+QJsonObject QV4DataCollector::lookupRef(Ref ref)
{
QJsonObject dict;
- if (m_namesAsObjects) {
- if (lookupSpecialRef(ref, &dict))
- return dict;
- }
-
- if (m_redundantRefs)
- deep = true;
-
dict.insert(QStringLiteral("handle"), qint64(ref));
QV4::Scope scope(engine());
QV4::ScopedValue value(scope, getValue(ref));
const QV4::Object *object = collectProperty(value, engine(), dict);
- if (deep && object)
+ if (object)
dict.insert(QStringLiteral("properties"), collectProperties(object));
return dict;
}
-// TODO: Drop this method once we don't need to support namesAsObjects anymore
-QV4DataCollector::Ref QV4DataCollector::addFunctionRef(const QString &functionName)
-{
- Q_ASSERT(m_namesAsObjects);
- Ref ref = addRef(QV4::Primitive::emptyValue(), false);
-
- QJsonObject dict;
- dict.insert(QStringLiteral("handle"), qint64(ref));
- dict.insert(QStringLiteral("type"), QStringLiteral("function"));
- dict.insert(QStringLiteral("name"), functionName);
- m_specialRefs.insert(ref, dict);
- m_collectedRefs.append(ref);
-
- return ref;
-}
-
-// TODO: Drop this method once we don't need to support namesAsObjects anymore
-QV4DataCollector::Ref QV4DataCollector::addScriptRef(const QString &scriptName)
-{
- Q_ASSERT(m_namesAsObjects);
- Ref ref = addRef(QV4::Primitive::emptyValue(), false);
-
- QJsonObject dict;
- dict.insert(QStringLiteral("handle"), qint64(ref));
- dict.insert(QStringLiteral("type"), QStringLiteral("script"));
- dict.insert(QStringLiteral("name"), scriptName);
- m_specialRefs.insert(ref, dict);
- m_collectedRefs.append(ref);
-
- return ref;
-}
-
bool QV4DataCollector::isValidRef(QV4DataCollector::Ref ref) const
{
QV4::Scope scope(engine());
@@ -268,12 +222,12 @@ bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr)
Refs collectedRefs;
QV4::ScopedValue v(scope);
- QV4::InternalClass *ic = ctxt->internalClass();
+ QV4::Heap::InternalClass *ic = ctxt->internalClass();
for (uint i = 0; i < ic->size; ++i) {
- QString name = ic->nameMap[i]->string;
+ QString name = ic->keyAt(i);
names.append(name);
v = static_cast<QV4::Heap::CallContext *>(ctxt->d())->locals[i];
- collectedRefs.append(collect(v));
+ collectedRefs.append(addValueRef(v));
}
Q_ASSERT(names.size() == collectedRefs.size());
@@ -284,13 +238,7 @@ bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr)
}
}
- Ref scopeObjectRef = addRef(scopeObject);
- if (m_redundantRefs) {
- dict->insert(QStringLiteral("ref"), qint64(scopeObjectRef));
- m_collectedRefs.append(scopeObjectRef);
- } else {
- *dict = lookupRef(scopeObjectRef, true);
- }
+ *dict = lookupRef(addRef(scopeObject));
return true;
}
@@ -306,13 +254,8 @@ QJsonObject QV4DataCollector::buildFrame(const QV4::StackFrame &stackFrame, int
QJsonObject frame;
frame[QLatin1String("index")] = frameNr;
frame[QLatin1String("debuggerFrame")] = false;
- if (m_namesAsObjects) {
- frame[QLatin1String("func")] = toRef(addFunctionRef(stackFrame.function));
- frame[QLatin1String("script")] = toRef(addScriptRef(stackFrame.source));
- } else {
- frame[QLatin1String("func")] = stackFrame.function;
- frame[QLatin1String("script")] = stackFrame.source;
- }
+ frame[QLatin1String("func")] = stackFrame.function;
+ frame[QLatin1String("script")] = stackFrame.source;
frame[QLatin1String("line")] = stackFrame.line - 1;
if (stackFrame.column >= 0)
frame[QLatin1String("column")] = stackFrame.column;
@@ -330,7 +273,7 @@ QJsonObject QV4DataCollector::buildFrame(const QV4::StackFrame &stackFrame, int
if (ctxt) {
QV4::ScopedValue o(scope, ctxt->d()->activation);
- frame[QLatin1String("receiver")] = toRef(collect(o));
+ frame[QLatin1String("receiver")] = toRef(addValueRef(o));
}
// Only type and index are used by Qt Creator, so we keep it easy:
@@ -351,30 +294,9 @@ QJsonObject QV4DataCollector::buildFrame(const QV4::StackFrame &stackFrame, int
return frame;
}
-// TODO: Drop this method once we don't need to support redundantRefs anymore
-QJsonArray QV4DataCollector::flushCollectedRefs()
-{
- Q_ASSERT(m_redundantRefs);
- QJsonArray refs;
- std::sort(m_collectedRefs.begin(), m_collectedRefs.end());
- for (int i = 0, ei = m_collectedRefs.size(); i != ei; ++i) {
- QV4DataCollector::Ref ref = m_collectedRefs.at(i);
- if (i > 0 && ref == m_collectedRefs.at(i - 1))
- continue;
- refs.append(lookupRef(ref, true));
- }
-
- m_collectedRefs.clear();
- return refs;
-}
-
void QV4DataCollector::clear()
{
m_values.set(engine(), engine()->newArrayObject());
- m_collectedRefs.clear();
- m_specialRefs.clear();
- m_namesAsObjects = true;
- m_redundantRefs = true;
}
QV4DataCollector::Ref QV4DataCollector::addRef(QV4::Value value, bool deduplicate)
@@ -394,18 +316,18 @@ QV4DataCollector::Ref QV4DataCollector::addRef(QV4::Value value, bool deduplicat
{ std::swap(*hasExceptionLoc, hadException); }
};
- // if we wouldn't do this, the putIndexed won't work.
+ // if we wouldn't do this, the put won't work.
ExceptionStateSaver resetExceptionState(engine());
QV4::Scope scope(engine());
QV4::ScopedObject array(scope, m_values.value());
if (deduplicate) {
for (Ref i = 0; i < array->getLength(); ++i) {
- if (array->getIndexed(i) == value.rawValue() && !m_specialRefs.contains(i))
+ if (array->get(i) == value.rawValue())
return i;
}
}
Ref ref = array->getLength();
- array->putIndexed(ref, value);
+ array->put(ref, value);
Q_ASSERT(array->getLength() - 1 == ref);
return ref;
}
@@ -415,19 +337,7 @@ QV4::ReturnedValue QV4DataCollector::getValue(Ref ref)
QV4::Scope scope(engine());
QV4::ScopedObject array(scope, m_values.value());
Q_ASSERT(ref < array->getLength());
- return array->getIndexed(ref, nullptr);
-}
-
-// TODO: Drop this method once we don't need to support namesAsObjects anymore
-bool QV4DataCollector::lookupSpecialRef(Ref ref, QJsonObject *dict)
-{
- Q_ASSERT(m_namesAsObjects);
- SpecialRefs::const_iterator it = m_specialRefs.constFind(ref);
- if (it == m_specialRefs.cend())
- return false;
-
- *dict = it.value();
- return true;
+ return array->get(ref, nullptr);
}
QJsonArray QV4DataCollector::collectProperties(const QV4::Object *object)
@@ -459,8 +369,6 @@ QJsonObject QV4DataCollector::collectAsJson(const QString &name, const QV4::Scop
if (value->isManaged() && !value->isString()) {
Ref ref = addRef(value);
dict.insert(QStringLiteral("ref"), qint64(ref));
- if (m_redundantRefs)
- m_collectedRefs.append(ref);
}
collectProperty(value, engine(), dict);
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h
index 5494e10e9a..bc178fa2db 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h
@@ -67,42 +67,27 @@ public:
QV4DataCollector(QV4::ExecutionEngine *engine);
- Ref collect(const QV4::ScopedValue &value); // only for redundantRefs
- Ref addFunctionRef(const QString &functionName); // only for namesAsObjects
- Ref addScriptRef(const QString &scriptName); // only for namesAsObjects
-
- void setNamesAsObjects(bool namesAsObjects) { m_namesAsObjects = namesAsObjects; }
- bool namesAsObjects() const { return m_namesAsObjects; }
-
- void setRedundantRefs(bool redundantRefs) { m_redundantRefs = redundantRefs; }
- bool redundantRefs() const { return m_redundantRefs; }
+ Ref addValueRef(const QV4::ScopedValue &value);
bool isValidRef(Ref ref) const;
- QJsonObject lookupRef(Ref ref, bool deep);
+ QJsonObject lookupRef(Ref ref);
bool collectScope(QJsonObject *dict, int frameNr, int scopeNr);
QJsonObject buildFrame(const QV4::StackFrame &stackFrame, int frameNr);
QV4::ExecutionEngine *engine() const { return m_engine; }
- QJsonArray flushCollectedRefs(); // only for redundantRefs
void clear();
private:
Ref addRef(QV4::Value value, bool deduplicate = true);
QV4::ReturnedValue getValue(Ref ref);
- bool lookupSpecialRef(Ref ref, QJsonObject *dict); // only for namesAsObjects
QJsonArray collectProperties(const QV4::Object *object);
QJsonObject collectAsJson(const QString &name, const QV4::ScopedValue &value);
void collectArgumentsInContext();
QV4::ExecutionEngine *m_engine;
- Refs m_collectedRefs; // only for redundantRefs
QV4::PersistentValue m_values;
- typedef QHash<Ref, QJsonObject> SpecialRefs; // only for namesAsObjects
- SpecialRefs m_specialRefs; // only for namesAsObjects
- bool m_namesAsObjects;
- bool m_redundantRefs;
};
QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp
index a1ed211a55..3170ee0c1b 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp
@@ -70,7 +70,7 @@ QV4Debugger::QV4Debugger(QV4::ExecutionEngine *engine)
, m_pauseRequested(false)
, m_haveBreakPoints(false)
, m_breakOnThrow(false)
- , m_returnedValue(engine, QV4::Primitive::undefinedValue())
+ , m_returnedValue(engine, QV4::Value::undefinedValue())
, m_gatherSources(nullptr)
, m_runningJob(nullptr)
, m_collector(engine)
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp
index 7950d21612..b424ef9f6c 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp
@@ -65,7 +65,7 @@ void JavaScriptJob::run()
QV4::Scope scope(engine);
QV4::ScopedContext ctx(scope, engine->currentStackFrame ? engine->currentContext()
- : engine->rootContext());
+ : engine->scriptContext());
QObject scopeObject;
QV4::CppStackFrame *frame = engine->currentStackFrame;
@@ -103,7 +103,7 @@ void JavaScriptJob::run()
}
}
- QV4::Script script(ctx, QV4::Compiler::EvalCode, this->script);
+ QV4::Script script(ctx, QV4::Compiler::ContextType::Eval, this->script);
if (const QV4::Function *function = frame ? frame->v4Function : engine->globalCode)
script.strictMode = function->isStrict();
@@ -150,7 +150,6 @@ void BacktraceJob::run()
result.insert(QStringLiteral("toFrame"), fromFrame + frameArray.size());
result.insert(QStringLiteral("frames"), frameArray);
}
- flushRedundantRefs();
}
FrameJob::FrameJob(QV4DataCollector *collector, int frameNr) :
@@ -165,7 +164,6 @@ void FrameJob::run()
success = false;
} else {
result = collector->buildFrame(frames[frameNr], frameNr);
- flushRedundantRefs();
success = true;
}
}
@@ -195,7 +193,6 @@ void ScopeJob::run()
result[QLatin1String("index")] = scopeNr;
result[QLatin1String("frameIndex")] = frameNr;
result[QLatin1String("object")] = object;
- flushRedundantRefs();
}
bool ScopeJob::wasSuccessful() const
@@ -228,9 +225,8 @@ void ValueLookupJob::run()
exception = QString::fromLatin1("Invalid Ref: %1").arg(ref);
break;
}
- result[QString::number(ref)] = collector->lookupRef(ref, true);
+ result[QString::number(ref)] = collector->lookupRef(ref);
}
- flushRedundantRefs();
}
const QString &ValueLookupJob::exceptionMessage() const
@@ -249,9 +245,7 @@ void ExpressionEvalJob::handleResult(QV4::ScopedValue &value)
{
if (hasExeption())
exception = value->toQStringNoThrow();
- result = collector->lookupRef(collector->collect(value), true);
- if (collector->redundantRefs())
- collectedRefs = collector->flushCollectedRefs();
+ result = collector->lookupRef(collector->addValueRef(value));
}
const QString &ExpressionEvalJob::exceptionMessage() const
@@ -264,13 +258,6 @@ const QJsonObject &ExpressionEvalJob::returnValue() const
return result;
}
-// TODO: Drop this method once we don't need to support redundantRefs anymore
-const QJsonArray &ExpressionEvalJob::refs() const
-{
- Q_ASSERT(collector->redundantRefs());
- return collectedRefs;
-}
-
GatherSourcesJob::GatherSourcesJob(QV4::ExecutionEngine *engine)
: engine(engine)
{}
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h
index eca8710e15..d1c7495863 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h
@@ -78,24 +78,10 @@ class CollectJob : public QV4DebugJob
protected:
QV4DataCollector *collector;
QJsonObject result;
- QJsonArray collectedRefs; // only for redundantRefs
-
- void flushRedundantRefs()
- {
- if (collector->redundantRefs())
- collectedRefs = collector->flushCollectedRefs();
- }
public:
CollectJob(QV4DataCollector *collector) : collector(collector) {}
const QJsonObject &returnValue() const { return result; }
-
- // TODO: Drop this method once we don't need to support redundantRefs anymore
- const QJsonArray &refs() const
- {
- Q_ASSERT(collector->redundantRefs());
- return collectedRefs;
- }
};
class BacktraceJob: public CollectJob
@@ -146,7 +132,6 @@ class ExpressionEvalJob: public JavaScriptJob
QV4DataCollector *collector;
QString exception;
QJsonObject result;
- QJsonArray collectedRefs; // only for redundantRefs
public:
ExpressionEvalJob(QV4::ExecutionEngine *engine, int frameNr, int context,
@@ -154,7 +139,6 @@ public:
void handleResult(QV4::ScopedValue &value) override;
const QString &exceptionMessage() const;
const QJsonObject &returnValue() const;
- const QJsonArray &refs() const; // only for redundantRefs
};
class GatherSourcesJob: public QV4DebugJob
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
index 32de8e9027..5866163ca6 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp
@@ -67,21 +67,21 @@ const char *const V4_PAUSE = "interrupt";
QT_BEGIN_NAMESPACE
-class V8CommandHandler;
-class UnknownV8CommandHandler;
+class V4CommandHandler;
+class UnknownV4CommandHandler;
using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>;
int QV4DebugServiceImpl::sequence = 0;
-class V8CommandHandler
+class V4CommandHandler
{
public:
- V8CommandHandler(const QString &command)
+ V4CommandHandler(const QString &command)
: cmd(command)
{}
- virtual ~V8CommandHandler()
+ virtual ~V4CommandHandler()
{}
QString command() const { return cmd; }
@@ -122,21 +122,6 @@ protected:
response.insert(QStringLiteral("running"), debugService->debuggerAgent.isRunning());
}
- QV4DataCollector *saneCollector(QV4Debugger *debugger)
- {
- QV4DataCollector *collector = debugger->collector();
- collector->setNamesAsObjects(debugService->clientRequiresNamesAsObjects());
- collector->setRedundantRefs(debugService->clientRequiresRedundantRefs());
- return collector;
- }
-
- // TODO: drop this method once we don't need to support redundantRefs anymore.
- void addRefs(const QJsonArray &refs)
- {
- Q_ASSERT(debugService->clientRequiresRedundantRefs());
- response.insert(QStringLiteral("refs"), refs);
- }
-
void createErrorResponse(const QString &msg)
{
QJsonValue command = req.value(QLatin1String("command"));
@@ -158,10 +143,10 @@ protected:
QJsonObject response;
};
-class UnknownV8CommandHandler: public V8CommandHandler
+class UnknownV4CommandHandler: public V4CommandHandler
{
public:
- UnknownV8CommandHandler(): V8CommandHandler(QString()) {}
+ UnknownV4CommandHandler(): V4CommandHandler(QString()) {}
void handleRequest() override
{
@@ -173,10 +158,10 @@ public:
};
namespace {
-class V8VersionRequest: public V8CommandHandler
+class V4VersionRequest: public V4CommandHandler
{
public:
- V8VersionRequest(): V8CommandHandler(QStringLiteral("version")) {}
+ V4VersionRequest(): V4CommandHandler(QStringLiteral("version")) {}
void handleRequest() override
{
@@ -189,98 +174,137 @@ public:
QLatin1String("this is not V8, this is V4 in Qt " QT_VERSION_STR));
body.insert(QStringLiteral("UnpausedEvaluate"), true);
body.insert(QStringLiteral("ContextEvaluate"), true);
+ body.insert(QStringLiteral("ChangeBreakpoint"), true);
addBody(body);
}
};
-class V8SetBreakPointRequest: public V8CommandHandler
+class V4BreakPointRequest: public V4CommandHandler
{
public:
- V8SetBreakPointRequest(): V8CommandHandler(QStringLiteral("setbreakpoint")) {}
+ V4BreakPointRequest(const QString &name): V4CommandHandler(name) {}
- void handleRequest() override
+ void handleRequest() final
{
+ // Other types are currently not supported
+ m_type = QStringLiteral("scriptRegExp");
+
// decypher the payload:
- QJsonObject args = req.value(QLatin1String("arguments")).toObject();
- if (args.isEmpty())
+ m_args = req.value(QLatin1String("arguments")).toObject();
+ if (m_args.isEmpty()) {
+ createErrorResponse(QStringLiteral("breakpoint request with empty arguments object"));
return;
+ }
- QString type = args.value(QLatin1String("type")).toString();
+ const int id = handleBreakPointRequest();
+ if (id < 0) {
+ createErrorResponse(m_error);
+ } else {
+ // response:
+ addCommand();
+ addRequestSequence();
+ addSuccess(true);
+ addRunning();
+ QJsonObject body;
+ body.insert(QStringLiteral("type"), m_type);
+ body.insert(QStringLiteral("breakpoint"), id);
+ addBody(body);
+ }
+ }
+
+protected:
+ virtual int handleBreakPointRequest() = 0;
+
+ QJsonObject m_args;
+ QString m_type;
+ QString m_error;
+};
+
+class V4SetBreakPointRequest: public V4BreakPointRequest
+{
+public:
+ V4SetBreakPointRequest(): V4BreakPointRequest(QStringLiteral("setbreakpoint")) {}
+
+ int handleBreakPointRequest() final
+ {
+ // decypher the payload:
+ const QString type = m_args.value(QLatin1String("type")).toString();
if (type != QLatin1String("scriptRegExp")) {
- createErrorResponse(QStringLiteral("breakpoint type \"%1\" is not implemented").arg(type));
- return;
+ m_error = QStringLiteral("breakpoint type \"%1\" is not implemented").arg(type);
+ return -1;
}
- QString fileName = args.value(QLatin1String("target")).toString();
+ const QString fileName = m_args.value(QLatin1String("target")).toString();
if (fileName.isEmpty()) {
- createErrorResponse(QStringLiteral("breakpoint has no file name"));
- return;
+ m_error = QStringLiteral("breakpoint has no file name");
+ return -1;
}
- int line = args.value(QLatin1String("line")).toInt(-1);
+
+ const int line = m_args.value(QLatin1String("line")).toInt(-1);
if (line < 0) {
- createErrorResponse(QStringLiteral("breakpoint has an invalid line number"));
- return;
+ m_error = QStringLiteral("breakpoint has an invalid line number");
+ return -1;
}
- bool enabled = args.value(QStringLiteral("enabled")).toBool(true);
- QString condition = args.value(QStringLiteral("condition")).toString();
+ const bool enabled = m_args.value(QStringLiteral("enabled")).toBool(true);
+ const QString condition = m_args.value(QStringLiteral("condition")).toString();
// set the break point:
- int id = debugService->debuggerAgent.addBreakPoint(fileName, line + 1, enabled, condition);
+ return debugService->debuggerAgent.addBreakPoint(fileName, line + 1, enabled, condition);
- // response:
- addCommand();
- addRequestSequence();
- addSuccess(true);
- addRunning();
- QJsonObject body;
- body.insert(QStringLiteral("type"), type);
- body.insert(QStringLiteral("breakpoint"), id);
// It's undocumented, but V8 sends back an actual_locations array too. However, our
// Debugger currently doesn't tell us when it resolved a breakpoint, so we'll leave them
// pending until the breakpoint is hit for the first time.
- addBody(body);
}
};
-class V8ClearBreakPointRequest: public V8CommandHandler
+class V4ClearBreakPointRequest: public V4BreakPointRequest
{
public:
- V8ClearBreakPointRequest(): V8CommandHandler(QStringLiteral("clearbreakpoint")) {}
+ V4ClearBreakPointRequest(): V4BreakPointRequest(QStringLiteral("clearbreakpoint")) {}
- void handleRequest() override
+ int handleBreakPointRequest() final
{
- // decypher the payload:
- QJsonObject args = req.value(QLatin1String("arguments")).toObject();
- if (args.isEmpty())
- return;
+ const int id = m_args.value(QLatin1String("breakpoint")).toInt(-1);
+ if (id < 0)
+ m_error = QStringLiteral("breakpoint has an invalid number");
+ else // remove the break point:
+ debugService->debuggerAgent.removeBreakPoint(id);
- int id = args.value(QLatin1String("breakpoint")).toInt(-1);
+ return id;
+ }
+};
+
+class V4ChangeBreakPointRequest: public V4BreakPointRequest
+{
+public:
+ V4ChangeBreakPointRequest(): V4BreakPointRequest(QStringLiteral("changebreakpoint")) {}
+
+ int handleBreakPointRequest() final
+ {
+ const int id = m_args.value(QLatin1String("breakpoint")).toInt(-1);
if (id < 0) {
- createErrorResponse(QStringLiteral("breakpoint has an invalid number"));
- return;
+ m_error = QStringLiteral("breakpoint has an invalid number");
+ return id;
}
- // remove the break point:
- debugService->debuggerAgent.removeBreakPoint(id);
+ const QJsonValue enabled = m_args.value(QLatin1String("enabled"));
+ if (!enabled.isBool()) {
+ m_error = QStringLiteral("missing bool \"enabled\" in breakpoint change request");
+ return -1;
+ }
- // response:
- addCommand();
- addRequestSequence();
- addSuccess(true);
- addRunning();
- QJsonObject body;
- body.insert(QStringLiteral("type"), QStringLiteral("scriptRegExp"));
- body.insert(QStringLiteral("breakpoint"), id);
- addBody(body);
+ // enable or disable the break point:
+ debugService->debuggerAgent.enableBreakPoint(id, enabled.toBool());
+ return id;
}
};
-class V8BacktraceRequest: public V8CommandHandler
+class V4BacktraceRequest: public V4CommandHandler
{
public:
- V8BacktraceRequest(): V8CommandHandler(QStringLiteral("backtrace")) {}
+ V4BacktraceRequest(): V4CommandHandler(QStringLiteral("backtrace")) {}
void handleRequest() override
{
@@ -297,7 +321,7 @@ public:
return;
}
- BacktraceJob job(saneCollector(debugger), fromFrame, toFrame);
+ BacktraceJob job(debugger->collector(), fromFrame, toFrame);
debugger->runInEngine(&job);
// response:
@@ -306,15 +330,13 @@ public:
addSuccess(true);
addRunning();
addBody(job.returnValue());
- if (debugService->clientRequiresRedundantRefs())
- addRefs(job.refs());
}
};
-class V8FrameRequest: public V8CommandHandler
+class V4FrameRequest: public V4CommandHandler
{
public:
- V8FrameRequest(): V8CommandHandler(QStringLiteral("frame")) {}
+ V4FrameRequest(): V4CommandHandler(QStringLiteral("frame")) {}
void handleRequest() override
{
@@ -334,7 +356,7 @@ public:
return;
}
- FrameJob job(saneCollector(debugger), frameNr);
+ FrameJob job(debugger->collector(), frameNr);
debugger->runInEngine(&job);
if (!job.wasSuccessful()) {
createErrorResponse(QStringLiteral("frame retrieval failed"));
@@ -349,15 +371,13 @@ public:
addSuccess(true);
addRunning();
addBody(job.returnValue());
- if (debugService->clientRequiresRedundantRefs())
- addRefs(job.refs());
}
};
-class V8ScopeRequest: public V8CommandHandler
+class V4ScopeRequest: public V4CommandHandler
{
public:
- V8ScopeRequest(): V8CommandHandler(QStringLiteral("scope")) {}
+ V4ScopeRequest(): V4CommandHandler(QStringLiteral("scope")) {}
void handleRequest() override
{
@@ -382,7 +402,7 @@ public:
return;
}
- ScopeJob job(saneCollector(debugger), frameNr, scopeNr);
+ ScopeJob job(debugger->collector(), frameNr, scopeNr);
debugger->runInEngine(&job);
if (!job.wasSuccessful()) {
createErrorResponse(QStringLiteral("scope retrieval failed"));
@@ -395,15 +415,13 @@ public:
addSuccess(true);
addRunning();
addBody(job.returnValue());
- if (debugService->clientRequiresRedundantRefs())
- addRefs(job.refs());
}
};
-class V8LookupRequest: public V8CommandHandler
+class V4LookupRequest: public V4CommandHandler
{
public:
- V8LookupRequest(): V8CommandHandler(QStringLiteral("lookup")) {}
+ V4LookupRequest(): V4CommandHandler(QStringLiteral("lookup")) {}
void handleRequest() override
{
@@ -424,7 +442,7 @@ public:
debugger = debuggers.first();
}
- ValueLookupJob job(handles, saneCollector(debugger));
+ ValueLookupJob job(handles, debugger->collector());
debugger->runInEngine(&job);
if (!job.exceptionMessage().isEmpty()) {
createErrorResponse(job.exceptionMessage());
@@ -435,16 +453,14 @@ public:
addSuccess(true);
addRunning();
addBody(job.returnValue());
- if (debugService->clientRequiresRedundantRefs())
- addRefs(job.refs());
}
}
};
-class V8ContinueRequest: public V8CommandHandler
+class V4ContinueRequest: public V4CommandHandler
{
public:
- V8ContinueRequest(): V8CommandHandler(QStringLiteral("continue")) {}
+ V4ContinueRequest(): V4CommandHandler(QStringLiteral("continue")) {}
void handleRequest() override
{
@@ -487,10 +503,10 @@ public:
}
};
-class V8DisconnectRequest: public V8CommandHandler
+class V4DisconnectRequest: public V4CommandHandler
{
public:
- V8DisconnectRequest(): V8CommandHandler(QStringLiteral("disconnect")) {}
+ V4DisconnectRequest(): V4CommandHandler(QStringLiteral("disconnect")) {}
void handleRequest() override
{
@@ -505,10 +521,10 @@ public:
}
};
-class V8SetExceptionBreakRequest: public V8CommandHandler
+class V4SetExceptionBreakRequest: public V4CommandHandler
{
public:
- V8SetExceptionBreakRequest(): V8CommandHandler(QStringLiteral("setexceptionbreak")) {}
+ V4SetExceptionBreakRequest(): V4CommandHandler(QStringLiteral("setexceptionbreak")) {}
void handleRequest() override
{
@@ -545,10 +561,10 @@ public:
}
};
-class V8ScriptsRequest: public V8CommandHandler
+class V4ScriptsRequest: public V4CommandHandler
{
public:
- V8ScriptsRequest(): V8CommandHandler(QStringLiteral("scripts")) {}
+ V4ScriptsRequest(): V4CommandHandler(QStringLiteral("scripts")) {}
void handleRequest() override
{
@@ -617,10 +633,10 @@ public:
// }
//
// The "value" key in "body" is the result of evaluating the expression in the request.
-class V8EvaluateRequest: public V8CommandHandler
+class V4EvaluateRequest: public V4CommandHandler
{
public:
- V8EvaluateRequest(): V8CommandHandler(QStringLiteral("evaluate")) {}
+ V4EvaluateRequest(): V4CommandHandler(QStringLiteral("evaluate")) {}
void handleRequest() override
{
@@ -645,7 +661,7 @@ public:
}
ExpressionEvalJob job(debugger->engine(), frame, context, expression,
- saneCollector(debugger));
+ debugger->collector());
debugger->runInEngine(&job);
if (job.hasExeption()) {
createErrorResponse(job.exceptionMessage());
@@ -655,44 +671,43 @@ public:
addSuccess(true);
addRunning();
addBody(job.returnValue());
- if (debugService->clientRequiresRedundantRefs())
- addRefs(job.refs());
}
}
};
} // anonymous namespace
-void QV4DebugServiceImpl::addHandler(V8CommandHandler* handler)
+void QV4DebugServiceImpl::addHandler(V4CommandHandler* handler)
{
handlers[handler->command()] = handler;
}
-V8CommandHandler *QV4DebugServiceImpl::v8CommandHandler(const QString &command) const
+V4CommandHandler *QV4DebugServiceImpl::v4CommandHandler(const QString &command) const
{
- V8CommandHandler *handler = handlers.value(command, 0);
+ V4CommandHandler *handler = handlers.value(command, 0);
if (handler)
return handler;
else
- return unknownV8CommandHandler.data();
+ return unknownV4CommandHandler.data();
}
QV4DebugServiceImpl::QV4DebugServiceImpl(QObject *parent) :
QQmlConfigurableDebugService<QV4DebugService>(1, parent),
- debuggerAgent(this), theSelectedFrame(0), redundantRefs(true), namesAsObjects(true),
- unknownV8CommandHandler(new UnknownV8CommandHandler)
+ debuggerAgent(this), theSelectedFrame(0),
+ unknownV4CommandHandler(new UnknownV4CommandHandler)
{
- addHandler(new V8VersionRequest);
- addHandler(new V8SetBreakPointRequest);
- addHandler(new V8ClearBreakPointRequest);
- addHandler(new V8BacktraceRequest);
- addHandler(new V8FrameRequest);
- addHandler(new V8ScopeRequest);
- addHandler(new V8LookupRequest);
- addHandler(new V8ContinueRequest);
- addHandler(new V8DisconnectRequest);
- addHandler(new V8SetExceptionBreakRequest);
- addHandler(new V8ScriptsRequest);
- addHandler(new V8EvaluateRequest);
+ addHandler(new V4VersionRequest);
+ addHandler(new V4SetBreakPointRequest);
+ addHandler(new V4ClearBreakPointRequest);
+ addHandler(new V4ChangeBreakPointRequest);
+ addHandler(new V4BacktraceRequest);
+ addHandler(new V4FrameRequest);
+ addHandler(new V4ScopeRequest);
+ addHandler(new V4LookupRequest);
+ addHandler(new V4ContinueRequest);
+ addHandler(new V4DisconnectRequest);
+ addHandler(new V4SetExceptionBreakRequest);
+ addHandler(new V4ScriptsRequest);
+ addHandler(new V4EvaluateRequest);
}
QV4DebugServiceImpl::~QV4DebugServiceImpl()
@@ -782,12 +797,7 @@ void QV4DebugServiceImpl::messageReceived(const QByteArray &message)
if (type == V4_CONNECT) {
QJsonObject parameters = QJsonDocument::fromJson(payload).object();
- namesAsObjects = true;
- redundantRefs = true;
- if (parameters.contains("namesAsObjects"))
- namesAsObjects = parameters.value("namesAsObjects").toBool();
- if (parameters.contains("redundantRefs"))
- redundantRefs = parameters.value("redundantRefs").toBool();
+ Q_UNUSED(parameters); // For future protocol changes
emit messageToClient(name(), packMessage(type));
stopWaiting();
@@ -805,10 +815,10 @@ void QV4DebugServiceImpl::messageReceived(const QByteArray &message)
else
breakOnSignals.removeOne(signalName);
} else if (type == "v8request") {
- handleV8Request(payload);
+ handleV4Request(payload);
} else if (type == V4_DISCONNECT) {
TRACE_PROTOCOL(qDebug() << "... payload:" << payload.constData());
- handleV8Request(payload);
+ handleV4Request(payload);
} else {
sendSomethingToSomebody(type, 0);
}
@@ -823,7 +833,7 @@ void QV4DebugServiceImpl::sendSomethingToSomebody(const char *type, int magicNum
emit messageToClient(name(), packMessage(type, rs.data()));
}
-void QV4DebugServiceImpl::handleV8Request(const QByteArray &payload)
+void QV4DebugServiceImpl::handleV4Request(const QByteArray &payload)
{
TRACE_PROTOCOL(qDebug() << "v8request, payload:" << payload.constData());
@@ -832,7 +842,7 @@ void QV4DebugServiceImpl::handleV8Request(const QByteArray &payload)
QJsonValue type = o.value(QLatin1String("type"));
if (type.toString() == QLatin1String("request")) {
QJsonValue command = o.value(QLatin1String("command"));
- V8CommandHandler *h = v8CommandHandler(command.toString());
+ V4CommandHandler *h = v4CommandHandler(command.toString());
if (h)
h->handle(o, this);
}
@@ -846,11 +856,11 @@ QByteArray QV4DebugServiceImpl::packMessage(const QByteArray &command, const QBy
return rs.data();
}
-void QV4DebugServiceImpl::send(QJsonObject v8Payload)
+void QV4DebugServiceImpl::send(QJsonObject v4Payload)
{
- v8Payload[QLatin1String("seq")] = sequence++;
+ v4Payload[QLatin1String("seq")] = sequence++;
QJsonDocument doc;
- doc.setObject(v8Payload);
+ doc.setObject(v4Payload);
#ifdef NO_PROTOCOL_TRACING
QByteArray responseData = doc.toJson(QJsonDocument::Compact);
#else
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h
index 5401956994..d0b104dfad 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h
@@ -64,15 +64,15 @@ QT_BEGIN_NAMESPACE
namespace QV4 { struct ExecutionEngine; }
class VariableCollector;
-class V8CommandHandler;
-class UnknownV8CommandHandler;
+class V4CommandHandler;
+class UnknownV4CommandHandler;
class QV4DebugServiceImpl;
class QV4DebugServiceImpl : public QQmlConfigurableDebugService<QV4DebugService>
{
Q_OBJECT
public:
- explicit QV4DebugServiceImpl(QObject *parent = 0);
+ explicit QV4DebugServiceImpl(QObject *parent = nullptr);
~QV4DebugServiceImpl() override;
void engineAdded(QJSEngine *engine) override;
@@ -81,14 +81,11 @@ public:
void stateAboutToBeChanged(State state) override;
void signalEmitted(const QString &signal) override;
- void send(QJsonObject v8Payload);
+ void send(QJsonObject v4Payload);
int selectedFrame() const;
void selectFrame(int frameNr);
- bool clientRequiresRedundantRefs() const { return redundantRefs; }
- bool clientRequiresNamesAsObjects() const { return namesAsObjects; }
-
QV4DebuggerAgent debuggerAgent;
protected:
@@ -98,22 +95,19 @@ protected:
private:
friend class QQmlDebuggerServiceFactory;
- void handleV8Request(const QByteArray &payload);
+ void handleV4Request(const QByteArray &payload);
static QByteArray packMessage(const QByteArray &command,
const QByteArray &message = QByteArray());
void processCommand(const QByteArray &command, const QByteArray &data);
- V8CommandHandler *v8CommandHandler(const QString &command) const;
+ V4CommandHandler *v4CommandHandler(const QString &command) const;
QStringList breakOnSignals;
static int sequence;
int theSelectedFrame;
- bool redundantRefs;
- bool namesAsObjects;
-
- void addHandler(V8CommandHandler* handler);
- QHash<QString, V8CommandHandler*> handlers;
- QScopedPointer<UnknownV8CommandHandler> unknownV8CommandHandler;
+ void addHandler(V4CommandHandler* handler);
+ QHash<QString, V4CommandHandler*> handlers;
+ QScopedPointer<UnknownV4CommandHandler> unknownV4CommandHandler;
};
QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
index b19115aa60..ceaa8a8de1 100644
--- a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
@@ -51,6 +51,7 @@
#include <private/qv4runtime_p.h>
#include <private/qversionedpacket_p.h>
#include <private/qqmldebugserviceinterfaces_p.h>
+#include <private/qv4identifiertable_p.h>
#include <QtQml/qjsengine.h>
#include <QtCore/qjsonarray.h>
@@ -252,9 +253,9 @@ QV4::ReturnedValue NativeDebugger::evaluateExpression(const QString &expression)
m_runningJob = true;
QV4::ExecutionContext *ctx = m_engine->currentStackFrame ? m_engine->currentContext()
- : m_engine->rootContext();
+ : m_engine->scriptContext();
- QV4::Script script(ctx, QV4::Compiler::EvalCode, expression);
+ QV4::Script script(ctx, QV4::Compiler::ContextType::Eval, expression);
if (const QV4::Function *function = m_engine->currentStackFrame
? m_engine->currentStackFrame->v4Function : m_engine->globalCode)
script.strictMode = function->isStrict();
@@ -276,7 +277,7 @@ QV4::ReturnedValue NativeDebugger::evaluateExpression(const QString &expression)
}
NativeDebugger::NativeDebugger(QQmlNativeDebugServiceImpl *service, QV4::ExecutionEngine *engine)
- : m_returnedValue(engine, QV4::Primitive::undefinedValue())
+ : m_returnedValue(engine, QV4::Value::undefinedValue())
{
m_stepping = NotStepping;
m_pauseRequested = false;
@@ -414,7 +415,7 @@ void Collector::collect(QJsonArray *out, const QString &parentIName, const QStri
if (isExpanded(iname)) {
QJsonArray children;
for (uint i = 0; i < n; ++i) {
- QV4::ReturnedValue v = array->getIndexed(i);
+ QV4::ReturnedValue v = array->get(i);
QV4::ScopedValue sval(scope, v);
collect(&children, iname, QString::number(i), *sval);
}
@@ -426,20 +427,17 @@ void Collector::collect(QJsonArray *out, const QString &parentIName, const QStri
qint64 numProperties = 0;
QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly);
QV4::ScopedProperty p(scope);
- QV4::ScopedString name(scope);
+ QV4::ScopedPropertyKey name(scope);
while (true) {
QV4::PropertyAttributes attrs;
- uint index;
- it.next(name.getRef(), &index, p, &attrs);
- if (attrs.isEmpty())
+ name = it.next(p, &attrs);
+ if (!name->isValid())
break;
- if (name.getPointer()) {
+ if (name->isStringOrSymbol()) {
++numProperties;
if (expanded) {
- if (name.getPointer()) {
- QV4::Value v = p.property->value;
- collect(&children, iname, name->toQStringNoThrow(), v);
- }
+ QV4::Value v = p.property->value;
+ collect(&children, iname, name->toQString(), v);
}
}
}
@@ -493,10 +491,10 @@ void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &a
collector.collect(&output, QString(), QStringLiteral("this"), thisObject);
QV4::Scoped<QV4::CallContext> callContext(scope, frame->callContext());
if (callContext) {
- QV4::InternalClass *ic = callContext->internalClass();
+ QV4::Heap::InternalClass *ic = callContext->internalClass();
QV4::ScopedValue v(scope);
for (uint i = 0; i < ic->size; ++i) {
- QString name = ic->nameMap[i]->string;
+ QString name = ic->keyAt(i);
v = callContext->d()->locals[i];
collector.collect(&output, QString(), name, v);
}
diff --git a/src/plugins/qmltooling/qmldbg_preview/qmldbg_preview.pro b/src/plugins/qmltooling/qmldbg_preview/qmldbg_preview.pro
new file mode 100644
index 0000000000..08686a43e3
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qmldbg_preview.pro
@@ -0,0 +1,29 @@
+QT += core-private qml-private packetprotocol-private network quick-private gui-private
+
+TARGET = qmldbg_preview
+
+SOURCES += \
+ $$PWD/qqmlpreviewblacklist.cpp \
+ $$PWD/qqmlpreviewfileengine.cpp \
+ $$PWD/qqmlpreviewfileloader.cpp \
+ $$PWD/qqmlpreviewhandler.cpp \
+ $$PWD/qqmlpreviewposition.cpp \
+ $$PWD/qqmlpreviewservice.cpp \
+ $$PWD/qqmlpreviewservicefactory.cpp
+
+HEADERS += \
+ $$PWD/qqmlpreviewblacklist.h \
+ $$PWD/qqmlpreviewfileengine.h \
+ $$PWD/qqmlpreviewfileloader.h \
+ $$PWD/qqmlpreviewhandler.h \
+ $$PWD/qqmlpreviewposition.h \
+ $$PWD/qqmlpreviewservice.h \
+ $$PWD/qqmlpreviewservicefactory.h
+
+OTHER_FILES += \
+ $$PWD/qqmlpreviewservice.json
+
+PLUGIN_TYPE = qmltooling
+PLUGIN_CLASS_NAME = QQmlPreviewServiceFactory
+
+load(qt_plugin)
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp
new file mode 100644
index 0000000000..77fe69821c
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.cpp
@@ -0,0 +1,213 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $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 "qqmlpreviewblacklist.h"
+
+QT_BEGIN_NAMESPACE
+
+void QQmlPreviewBlacklist::blacklist(const QString &path)
+{
+ if (!path.isEmpty())
+ m_root.insert(path, 0);
+}
+
+void QQmlPreviewBlacklist::whitelist(const QString &path)
+{
+ if (!path.isEmpty())
+ m_root.remove(path, 0);
+}
+
+bool QQmlPreviewBlacklist::isBlacklisted(const QString &path) const
+{
+ return path.isEmpty() ? true : m_root.containedPrefixLeaf(path, 0) > 0;
+}
+
+void QQmlPreviewBlacklist::clear()
+{
+ m_root = Node();
+}
+
+QQmlPreviewBlacklist::Node::Node()
+{
+}
+
+QQmlPreviewBlacklist::Node::Node(const QQmlPreviewBlacklist::Node &other) :
+ m_mine(other.m_mine), m_isLeaf(other.m_isLeaf)
+{
+ for (auto it = other.m_next.begin(), end = other.m_next.end(); it != end; ++it)
+ m_next.insert(it.key(), new Node(**it));
+}
+
+QQmlPreviewBlacklist::Node::Node(QQmlPreviewBlacklist::Node &&other) Q_DECL_NOEXCEPT
+{
+ m_mine.swap(other.m_mine);
+ m_next.swap(other.m_next);
+ m_isLeaf = other.m_isLeaf;
+}
+
+QQmlPreviewBlacklist::Node::~Node()
+{
+ qDeleteAll(m_next);
+}
+
+QQmlPreviewBlacklist::Node &QQmlPreviewBlacklist::Node::operator=(
+ const QQmlPreviewBlacklist::Node &other)
+{
+ if (&other != this) {
+ m_mine = other.m_mine;
+ for (auto it = other.m_next.begin(), end = other.m_next.end(); it != end; ++it)
+ m_next.insert(it.key(), new Node(**it));
+ m_isLeaf = other.m_isLeaf;
+ }
+ return *this;
+}
+
+QQmlPreviewBlacklist::Node &QQmlPreviewBlacklist::Node::operator=(
+ QQmlPreviewBlacklist::Node &&other) Q_DECL_NOEXCEPT
+{
+ if (&other != this) {
+ m_mine.swap(other.m_mine);
+ m_next.swap(other.m_next);
+ m_isLeaf = other.m_isLeaf;
+ }
+ return *this;
+}
+
+void QQmlPreviewBlacklist::Node::split(QString::iterator it, QString::iterator end)
+{
+ QString existing;
+ existing.resize(end - it - 1);
+ std::copy(it + 1, end, existing.begin());
+
+ Node *node = new Node(existing, m_next, m_isLeaf);
+ m_next.clear();
+ m_next.insert(*it, node);
+ m_mine.resize(it - m_mine.begin());
+ m_isLeaf = false;
+}
+
+void QQmlPreviewBlacklist::Node::insert(const QString &path, int offset)
+{
+ for (auto it = m_mine.begin(), end = m_mine.end(); it != end; ++it) {
+ if (offset == path.size()) {
+ split(it, end);
+ m_isLeaf = true;
+ return;
+ }
+
+ if (path.at(offset) != *it) {
+ split(it, end);
+
+ QString inserted;
+ inserted.resize(path.size() - offset - 1);
+ std::copy(path.begin() + offset + 1, path.end(), inserted.begin());
+ m_next.insert(path.at(offset), new Node(inserted));
+ return;
+ }
+
+ ++offset;
+ }
+
+ if (offset == path.size()) {
+ m_isLeaf = true;
+ return;
+ }
+
+ Node *&node = m_next[path.at(offset++)];
+ if (node == nullptr) {
+ QString inserted;
+ inserted.resize(path.size() - offset);
+ std::copy(path.begin() + offset, path.end(), inserted.begin());
+ node = new Node(inserted);
+ } else {
+ node->insert(path, offset);
+ }
+}
+
+void QQmlPreviewBlacklist::Node::remove(const QString &path, int offset)
+{
+ for (auto it = m_mine.begin(), end = m_mine.end(); it != end; ++it) {
+ if (offset == path.size() || path.at(offset) != *it) {
+ split(it, end);
+ return;
+ }
+ ++offset;
+ }
+
+ m_isLeaf = false;
+ if (offset == path.size())
+ return;
+
+ auto it = m_next.find(path.at(offset));
+ if (it != m_next.end())
+ (*it)->remove(path, ++offset);
+}
+
+int QQmlPreviewBlacklist::Node::containedPrefixLeaf(const QString &path, int offset) const
+{
+ if (offset == path.size())
+ return (m_mine.isEmpty() && m_isLeaf) ? offset : -1;
+
+ for (auto it = m_mine.begin(), end = m_mine.end(); it != end; ++it) {
+ if (path.at(offset) != *it)
+ return -1;
+
+ if (++offset == path.size())
+ return (++it == end && m_isLeaf) ? offset : -1;
+ }
+
+ const QChar c = path.at(offset);
+ if (m_isLeaf && c == '/')
+ return offset;
+
+ auto it = m_next.find(c);
+ if (it == m_next.end())
+ return -1;
+
+ return (*it)->containedPrefixLeaf(path, ++offset);
+}
+
+QQmlPreviewBlacklist::Node::Node(const QString &mine,
+ const QHash<QChar, QQmlPreviewBlacklist::Node *> &next,
+ bool isLeaf)
+ : m_mine(mine), m_next(next), m_isLeaf(isLeaf)
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.h
new file mode 100644
index 0000000000..2f743ca7a6
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewblacklist.h
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $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 QQMLPREVIEWBLACKLIST_H
+#define QQMLPREVIEWBLACKLIST_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/qhash.h>
+#include <QtCore/qchar.h>
+#include <QtCore/qstring.h>
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlPreviewBlacklist
+{
+public:
+ void blacklist(const QString &path);
+ void whitelist(const QString &path);
+ bool isBlacklisted(const QString &path) const;
+ void clear();
+
+private:
+ class Node {
+ public:
+ Node();
+ Node(const Node &other);
+ Node(Node &&other) Q_DECL_NOEXCEPT;
+
+ ~Node();
+
+ Node &operator=(const Node &other);
+ Node &operator=(Node &&other) Q_DECL_NOEXCEPT;
+
+ void split(QString::iterator it, QString::iterator end);
+ void insert(const QString &path, int offset);
+ void remove(const QString &path, int offset);
+ int containedPrefixLeaf(const QString &path, int offset) const;
+
+ private:
+ Node(const QString &mine, const QHash<QChar, Node *> &next = QHash<QChar, Node *>(),
+ bool isLeaf = true);
+
+ QString m_mine;
+ QHash<QChar, Node *> m_next;
+ bool m_isLeaf = false;
+ };
+
+ Node m_root;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPREVIEWBLACKLIST_H
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp
new file mode 100644
index 0000000000..72de52bbe1
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.cpp
@@ -0,0 +1,443 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $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 "qqmlpreviewfileengine.h"
+#include "qqmlpreviewservice.h"
+
+#include <QtCore/qlibraryinfo.h>
+#include <QtCore/qthread.h>
+#include <QtCore/qwaitcondition.h>
+
+#include <cstring>
+
+QT_BEGIN_NAMESPACE
+
+static bool isRelative(const QString &path)
+{
+ if (path.isEmpty())
+ return true;
+ if (path.at(0) == '/')
+ return false;
+ if (path.at(0) == ':' && path.length() >= 2 && path.at(1) == '/')
+ return false;
+#ifdef Q_OS_WIN
+ if (path.length() >= 2 && path.at(1) == ':')
+ return false;
+#endif
+ return true;
+}
+
+static QString absolutePath(const QString &path)
+{
+ return QDir::cleanPath(isRelative(path) ? (QDir::currentPath() + '/' + path) : path);
+}
+
+bool isRootPath(const QString &path)
+{
+ return QFileSystemEntry::isRootPath(path);
+}
+
+class QQmlPreviewFileEngineIterator : public QAbstractFileEngineIterator
+{
+public:
+ QQmlPreviewFileEngineIterator(QDir::Filters filters, const QStringList &filterNames,
+ const QStringList &m_entries);
+ ~QQmlPreviewFileEngineIterator();
+
+ QString next() override;
+ bool hasNext() const override;
+ QString currentFileName() const override;
+
+private:
+ const QStringList m_entries;
+ int m_index;
+};
+
+QQmlPreviewFileEngineIterator::QQmlPreviewFileEngineIterator(QDir::Filters filters,
+ const QStringList &filterNames,
+ const QStringList &entries)
+ : QAbstractFileEngineIterator(filters, filterNames), m_entries(entries), m_index(0)
+{
+}
+
+QQmlPreviewFileEngineIterator::~QQmlPreviewFileEngineIterator()
+{
+}
+
+QString QQmlPreviewFileEngineIterator::next()
+{
+ if (!hasNext())
+ return QString();
+ ++m_index;
+ return currentFilePath();
+}
+
+bool QQmlPreviewFileEngineIterator::hasNext() const
+{
+ return m_index < m_entries.size();
+}
+
+QString QQmlPreviewFileEngineIterator::currentFileName() const
+{
+ if (m_index == 0 || m_index > m_entries.size())
+ return QString();
+ return m_entries.at(m_index - 1);
+}
+
+QQmlPreviewFileEngine::QQmlPreviewFileEngine(const QString &file, const QString &absolute,
+ QQmlPreviewFileLoader *loader) :
+ m_name(file), m_absolute(absolute), m_loader(loader)
+{
+ load();
+}
+
+void QQmlPreviewFileEngine::setFileName(const QString &file)
+{
+ m_name = file;
+ m_absolute = absolutePath(file);
+ m_fallback.reset();
+ m_contents.close();
+ m_contents.setData(QByteArray());
+ m_entries.clear();
+ load();
+}
+
+bool QQmlPreviewFileEngine::open(QIODevice::OpenMode flags)
+{
+ switch (m_result) {
+ case QQmlPreviewFileLoader::File:
+ return m_contents.open(flags);
+ case QQmlPreviewFileLoader::Directory:
+ return false;
+ case QQmlPreviewFileLoader::Fallback:
+ return m_fallback->open(flags);
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+}
+
+bool QQmlPreviewFileEngine::close()
+{
+ switch (m_result) {
+ case QQmlPreviewFileLoader::Fallback:
+ return m_fallback->close();
+ case QQmlPreviewFileLoader::File:
+ m_contents.close();
+ return true;
+ case QQmlPreviewFileLoader::Directory:
+ return false;
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+}
+
+qint64 QQmlPreviewFileEngine::size() const
+{
+ return m_fallback ? m_fallback->size() : m_contents.size();
+}
+
+qint64 QQmlPreviewFileEngine::pos() const
+{
+ return m_fallback ? m_fallback->pos() : m_contents.pos();
+}
+
+bool QQmlPreviewFileEngine::seek(qint64 newPos)
+{
+ return m_fallback? m_fallback->seek(newPos) : m_contents.seek(newPos);
+}
+
+qint64 QQmlPreviewFileEngine::read(char *data, qint64 maxlen)
+{
+ return m_fallback ? m_fallback->read(data, maxlen) : m_contents.read(data, maxlen);
+}
+
+QAbstractFileEngine::FileFlags QQmlPreviewFileEngine::fileFlags(
+ QAbstractFileEngine::FileFlags type) const
+{
+ if (m_fallback)
+ return m_fallback->fileFlags(type);
+
+ QAbstractFileEngine::FileFlags ret = 0;
+
+ if (type & PermsMask) {
+ ret |= QAbstractFileEngine::FileFlags(
+ ReadOwnerPerm | ReadUserPerm | ReadGroupPerm | ReadOtherPerm);
+ }
+
+ if (type & TypesMask) {
+ if (m_result == QQmlPreviewFileLoader::Directory)
+ ret |= DirectoryType;
+ else
+ ret |= FileType;
+ }
+
+ if (type & FlagsMask) {
+ ret |= ExistsFlag;
+ if (isRootPath(m_name))
+ ret |= RootFlag;
+ }
+
+ return ret;
+}
+
+QString QQmlPreviewFileEngine::fileName(QAbstractFileEngine::FileName file) const
+{
+ if (m_fallback)
+ return m_fallback->fileName(file);
+
+ if (file == BaseName) {
+ int slashPos = m_name.lastIndexOf('/');
+ if (slashPos == -1)
+ return m_name;
+ return m_name.mid(slashPos + 1);
+ } else if (file == PathName || file == AbsolutePathName) {
+ const QString path = (file == AbsolutePathName) ? m_absolute : m_name;
+ const int slashPos = path.lastIndexOf('/');
+ if (slashPos == -1)
+ return QString();
+ else if (slashPos == 0)
+ return "/";
+ return path.left(slashPos);
+ } else if (file == CanonicalName || file == CanonicalPathName) {
+ if (file == CanonicalPathName) {
+ const int slashPos = m_absolute.lastIndexOf('/');
+ if (slashPos != -1)
+ return m_absolute.left(slashPos);
+ }
+ return m_absolute;
+ }
+ return m_name;
+}
+
+uint QQmlPreviewFileEngine::ownerId(QAbstractFileEngine::FileOwner owner) const
+{
+ return m_fallback ? m_fallback->ownerId(owner) : static_cast<uint>(-2);
+}
+
+QAbstractFileEngine::Iterator *QQmlPreviewFileEngine::beginEntryList(QDir::Filters filters,
+ const QStringList &filterNames)
+{
+ return m_fallback ? m_fallback->beginEntryList(filters, filterNames)
+ : new QQmlPreviewFileEngineIterator(filters, filterNames, m_entries);
+}
+
+QAbstractFileEngine::Iterator *QQmlPreviewFileEngine::endEntryList()
+{
+ return m_fallback ? m_fallback->endEntryList() : nullptr;
+}
+
+bool QQmlPreviewFileEngine::flush()
+{
+ return m_fallback ? m_fallback->flush() : true;
+}
+
+bool QQmlPreviewFileEngine::syncToDisk()
+{
+ return m_fallback ? m_fallback->syncToDisk() : false;
+}
+
+bool QQmlPreviewFileEngine::isSequential() const
+{
+ return m_fallback ? m_fallback->isSequential() : m_contents.isSequential();
+}
+
+bool QQmlPreviewFileEngine::remove()
+{
+ return m_fallback ? m_fallback->remove() : false;
+}
+
+bool QQmlPreviewFileEngine::copy(const QString &newName)
+{
+ return m_fallback ? m_fallback->copy(newName) : false;
+}
+
+bool QQmlPreviewFileEngine::rename(const QString &newName)
+{
+ return m_fallback ? m_fallback->rename(newName) : false;
+}
+
+bool QQmlPreviewFileEngine::renameOverwrite(const QString &newName)
+{
+ return m_fallback ? m_fallback->renameOverwrite(newName) : false;
+}
+
+bool QQmlPreviewFileEngine::link(const QString &newName)
+{
+ return m_fallback ? m_fallback->link(newName) : false;
+}
+
+bool QQmlPreviewFileEngine::mkdir(const QString &dirName, bool createParentDirectories) const
+{
+ return m_fallback ? m_fallback->mkdir(dirName, createParentDirectories) : false;
+}
+
+bool QQmlPreviewFileEngine::rmdir(const QString &dirName, bool recurseParentDirectories) const
+{
+ return m_fallback ? m_fallback->rmdir(dirName, recurseParentDirectories) : false;
+}
+
+bool QQmlPreviewFileEngine::setSize(qint64 size)
+{
+ switch (m_result) {
+ case QQmlPreviewFileLoader::Fallback:
+ return m_fallback->setSize(size);
+ case QQmlPreviewFileLoader::File:
+ if (size < 0 || size > std::numeric_limits<int>::max())
+ return false;
+ m_contents.buffer().resize(static_cast<int>(size));
+ return true;
+ case QQmlPreviewFileLoader::Directory:
+ return false;
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+}
+
+bool QQmlPreviewFileEngine::caseSensitive() const
+{
+ return m_fallback ? m_fallback->caseSensitive() : true;
+}
+
+bool QQmlPreviewFileEngine::isRelativePath() const
+{
+ return m_fallback ? m_fallback->isRelativePath() : isRelative(m_name);
+}
+
+QStringList QQmlPreviewFileEngine::entryList(QDir::Filters filters,
+ const QStringList &filterNames) const
+{
+ return m_fallback ? m_fallback->entryList(filters, filterNames)
+ : QAbstractFileEngine::entryList(filters, filterNames);
+}
+
+bool QQmlPreviewFileEngine::setPermissions(uint perms)
+{
+ return m_fallback ? m_fallback->setPermissions(perms) : false;
+}
+
+QByteArray QQmlPreviewFileEngine::id() const
+{
+ return m_fallback ? m_fallback->id() : QByteArray();
+}
+
+QString QQmlPreviewFileEngine::owner(FileOwner owner) const
+{
+ return m_fallback ? m_fallback->owner(owner) : QString();
+}
+
+QDateTime QQmlPreviewFileEngine::fileTime(FileTime time) const
+{
+ // Files we replace are always newer than the ones we had before. This makes the QML engine
+ // actually recompile them, rather than pick them from the cache.
+ return m_fallback ? m_fallback->fileTime(time) : QDateTime::currentDateTime();
+}
+
+int QQmlPreviewFileEngine::handle() const
+{
+ return m_fallback ? m_fallback->handle() : -1;
+}
+
+qint64 QQmlPreviewFileEngine::readLine(char *data, qint64 maxlen)
+{
+ return m_fallback ? m_fallback->readLine(data, maxlen) : m_contents.readLine(data, maxlen);
+}
+
+qint64 QQmlPreviewFileEngine::write(const char *data, qint64 len)
+{
+ return m_fallback ? m_fallback->write(data, len) : m_contents.write(data, len);
+}
+
+bool QQmlPreviewFileEngine::extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output)
+{
+ return m_fallback ? m_fallback->extension(extension, option, output) : false;
+}
+
+bool QQmlPreviewFileEngine::supportsExtension(Extension extension) const
+{
+ return m_fallback ? m_fallback->supportsExtension(extension) : false;
+}
+
+void QQmlPreviewFileEngine::load() const
+{
+ m_result = m_loader->load(m_absolute);
+ switch (m_result) {
+ case QQmlPreviewFileLoader::File:
+ m_contents.setData(m_loader->contents());
+ break;
+ case QQmlPreviewFileLoader::Directory:
+ m_entries = m_loader->entries();
+ break;
+ case QQmlPreviewFileLoader::Fallback:
+ m_fallback.reset(QAbstractFileEngine::create(m_name));
+ break;
+ case QQmlPreviewFileLoader::Unknown:
+ Q_UNREACHABLE();
+ break;
+ }
+}
+
+QQmlPreviewFileEngineHandler::QQmlPreviewFileEngineHandler(QQmlPreviewFileLoader *loader)
+ : m_loader(loader)
+{
+}
+
+QAbstractFileEngine *QQmlPreviewFileEngineHandler::create(const QString &fileName) const
+{
+ // Don't load compiled QML/JS over the network
+ if (fileName.endsWith(".qmlc") || fileName.endsWith(".jsc") || isRootPath(fileName)) {
+ return nullptr;
+ }
+
+ QString relative = fileName;
+ while (relative.endsWith('/'))
+ relative.chop(1);
+
+ if (relative.isEmpty() || relative == ":")
+ return nullptr;
+
+ const QString absolute = relative.startsWith(':') ? relative : absolutePath(relative);
+
+ return m_loader->isBlacklisted(absolute)
+ ? nullptr : new QQmlPreviewFileEngine(relative, absolute, m_loader.data());
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.h
new file mode 100644
index 0000000000..9a40b6360c
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileengine.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $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 QQMLPREVIEWFILEENGINE_H
+#define QQMLPREVIEWFILEENGINE_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 "qqmlpreviewfileloader.h"
+
+#include <private/qabstractfileengine_p.h>
+#include <private/qfsfileengine_p.h>
+#include <QtCore/qbuffer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlPreviewFileEngine : public QAbstractFileEngine
+{
+public:
+ QQmlPreviewFileEngine(const QString &file, const QString &absolute,
+ QQmlPreviewFileLoader *loader);
+
+ void setFileName(const QString &file) override;
+
+ bool open(QIODevice::OpenMode flags) override ;
+ bool close() override;
+ qint64 size() const override;
+ qint64 pos() const override;
+ bool seek(qint64) override;
+ qint64 read(char *data, qint64 maxlen) override;
+
+ FileFlags fileFlags(FileFlags type) const override;
+ QString fileName(QAbstractFileEngine::FileName file) const override;
+ uint ownerId(FileOwner) const override;
+
+ Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override;
+ Iterator *endEntryList() override;
+
+ // Forwarding to fallback if exists
+ bool flush() override;
+ bool syncToDisk() override;
+ bool isSequential() const override;
+ bool remove() override;
+ bool copy(const QString &newName) override;
+ bool rename(const QString &newName) override;
+ bool renameOverwrite(const QString &newName) override;
+ bool link(const QString &newName) override;
+ bool mkdir(const QString &dirName, bool createParentDirectories) const override;
+ bool rmdir(const QString &dirName, bool recurseParentDirectories) const override;
+ bool setSize(qint64 size) override;
+ bool caseSensitive() const override;
+ bool isRelativePath() const override;
+ QStringList entryList(QDir::Filters filters, const QStringList &filterNames) const override;
+ bool setPermissions(uint perms) override;
+ QByteArray id() const override;
+ QString owner(FileOwner) const override;
+ QDateTime fileTime(FileTime time) const override;
+ int handle() const override;
+ qint64 readLine(char *data, qint64 maxlen) override;
+ qint64 write(const char *data, qint64 len) override;
+ bool extension(Extension extension, const ExtensionOption *option, ExtensionReturn *output) override;
+ bool supportsExtension(Extension extension) const override;
+
+private:
+ void load() const;
+
+ QString m_name;
+ QString m_absolute;
+ QPointer<QQmlPreviewFileLoader> m_loader;
+
+ mutable QBuffer m_contents;
+ mutable QStringList m_entries;
+ mutable QScopedPointer<QAbstractFileEngine> m_fallback;
+ mutable QQmlPreviewFileLoader::Result m_result = QQmlPreviewFileLoader::Unknown;
+};
+
+class QQmlPreviewFileEngineHandler : public QAbstractFileEngineHandler
+{
+public:
+ QQmlPreviewFileEngineHandler(QQmlPreviewFileLoader *loader);
+ QAbstractFileEngine *create(const QString &fileName) const override;
+
+private:
+ QPointer<QQmlPreviewFileLoader> m_loader;
+};
+
+
+
+QT_END_NAMESPACE
+
+#endif // QQMLPREVIEWFILEENGINE_H
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp
new file mode 100644
index 0000000000..9927089e5e
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.cpp
@@ -0,0 +1,197 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $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 "qqmlpreviewfileloader.h"
+#include "qqmlpreviewservice.h"
+
+#include <QtCore/qlibraryinfo.h>
+#include <QtCore/qstandardpaths.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlPreviewFileLoader::QQmlPreviewFileLoader(QQmlPreviewServiceImpl *service) : m_service(service)
+{
+ // Exclude some resource paths used by Qt itself. There is no point in loading those from the
+ // client as the client will not have the files (or even worse, it may have different ones).
+ m_blacklist.blacklist(":/qt-project.org");
+ m_blacklist.blacklist(":/QtQuick/Controls/Styles");
+ m_blacklist.blacklist(":/ExtrasImports/QtQuick/Controls/Styles");
+ m_blacklist.blacklist(":/qgradient");
+
+ // Target specific configuration should not replaced with files from the host.
+ m_blacklist.blacklist("/etc");
+
+ for (int loc = QLibraryInfo::PrefixPath; loc < QLibraryInfo::TestsPath; ++loc) {
+ m_blacklist.blacklist(QLibraryInfo::location(
+ static_cast<QLibraryInfo::LibraryLocation>(loc)));
+ }
+ m_blacklist.blacklist(QLibraryInfo::location(QLibraryInfo::SettingsPath));
+
+ static const QStandardPaths::StandardLocation blackListLocations[] = {
+ QStandardPaths::DataLocation,
+ QStandardPaths::CacheLocation,
+ QStandardPaths::GenericDataLocation,
+ QStandardPaths::ConfigLocation,
+ QStandardPaths::GenericCacheLocation,
+ QStandardPaths::GenericConfigLocation,
+ QStandardPaths::AppDataLocation,
+ QStandardPaths::AppConfigLocation
+ };
+
+ for (auto locationType : blackListLocations) {
+ const QStringList locations = QStandardPaths::standardLocations(locationType);
+ for (const QString &location : locations)
+ m_blacklist.blacklist(location);
+ }
+
+ m_blacklist.whitelist(QLibraryInfo::location(QLibraryInfo::TestsPath));
+
+ connect(this, &QQmlPreviewFileLoader::request, service, &QQmlPreviewServiceImpl::forwardRequest,
+ Qt::DirectConnection);
+ connect(service, &QQmlPreviewServiceImpl::directory, this, &QQmlPreviewFileLoader::directory);
+ connect(service, &QQmlPreviewServiceImpl::file, this, &QQmlPreviewFileLoader::file);
+ connect(service, &QQmlPreviewServiceImpl::error, this, &QQmlPreviewFileLoader::error);
+ connect(service, &QQmlPreviewServiceImpl::clearCache, this, &QQmlPreviewFileLoader::clearCache);
+ moveToThread(&m_thread);
+ m_thread.start();
+}
+
+QQmlPreviewFileLoader::~QQmlPreviewFileLoader() {
+ m_thread.quit();
+ m_thread.wait();
+}
+
+QQmlPreviewFileLoader::Result QQmlPreviewFileLoader::load(const QString &path)
+{
+ QMutexLocker locker(&m_mutex);
+ m_path = path;
+
+ auto fileIterator = m_fileCache.constFind(path);
+ if (fileIterator != m_fileCache.constEnd()) {
+ m_result = File;
+ m_contents = *fileIterator;
+ m_entries.clear();
+ return m_result;
+ }
+
+ auto dirIterator = m_directoryCache.constFind(path);
+ if (dirIterator != m_directoryCache.constEnd()) {
+ m_result = Directory;
+ m_contents.clear();
+ m_entries = *dirIterator;
+ return m_result;
+ }
+
+ m_result = Unknown;
+ m_entries.clear();
+ m_contents.clear();
+ emit request(path);
+ m_waitCondition.wait(&m_mutex);
+ return m_result;
+}
+
+QByteArray QQmlPreviewFileLoader::contents()
+{
+ QMutexLocker locker(&m_mutex);
+ return m_contents;
+}
+
+QStringList QQmlPreviewFileLoader::entries()
+{
+ QMutexLocker locker(&m_mutex);
+ return m_entries;
+}
+
+void QQmlPreviewFileLoader::whitelist(const QUrl &url)
+{
+ const QString path = QQmlFile::urlToLocalFileOrQrc(url);
+ if (!path.isEmpty()) {
+ QMutexLocker locker(&m_mutex);
+ m_blacklist.whitelist(path);
+ }
+}
+
+bool QQmlPreviewFileLoader::isBlacklisted(const QString &path)
+{
+ QMutexLocker locker(&m_mutex);
+ return m_blacklist.isBlacklisted(path);
+}
+
+void QQmlPreviewFileLoader::file(const QString &path, const QByteArray &contents)
+{
+ QMutexLocker locker(&m_mutex);
+ m_blacklist.whitelist(path);
+ m_fileCache[path] = contents;
+ if (path == m_path) {
+ m_contents = contents;
+ m_result = File;
+ m_waitCondition.wakeOne();
+ }
+}
+
+void QQmlPreviewFileLoader::directory(const QString &path, const QStringList &entries)
+{
+ QMutexLocker locker(&m_mutex);
+ m_blacklist.whitelist(path);
+ m_directoryCache[path] = entries;
+ if (path == m_path) {
+ m_entries = entries;
+ m_result = Directory;
+ m_waitCondition.wakeOne();
+ }
+}
+
+void QQmlPreviewFileLoader::error(const QString &path)
+{
+ QMutexLocker locker(&m_mutex);
+ m_blacklist.blacklist(path);
+ if (path == m_path) {
+ m_result = Fallback;
+ m_waitCondition.wakeOne();
+ }
+}
+
+void QQmlPreviewFileLoader::clearCache()
+{
+ QMutexLocker locker(&m_mutex);
+ m_fileCache.clear();
+ m_directoryCache.clear();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h
new file mode 100644
index 0000000000..0c55c48c4a
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewfileloader.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $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 QQMLPREVIEWFILELOADER_H
+#define QQMLPREVIEWFILELOADER_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 "qqmlpreviewblacklist.h"
+
+#include <QtCore/qobject.h>
+#include <QtCore/qthread.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qwaitcondition.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qset.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlPreviewServiceImpl;
+class QQmlPreviewFileLoader : public QObject
+{
+ Q_OBJECT
+public:
+ enum Result {
+ File,
+ Directory,
+ Fallback,
+ Unknown
+ };
+
+ QQmlPreviewFileLoader(QQmlPreviewServiceImpl *service);
+ ~QQmlPreviewFileLoader();
+
+ Result load(const QString &file);
+ QByteArray contents();
+ QStringList entries();
+
+ void whitelist(const QUrl &url);
+ bool isBlacklisted(const QString &file);
+
+signals:
+ void request(const QString &file);
+
+private:
+ QMutex m_mutex;
+ QWaitCondition m_waitCondition;
+
+ QThread m_thread;
+ QPointer<QQmlPreviewServiceImpl> m_service;
+
+ QString m_path;
+ QByteArray m_contents;
+ QStringList m_entries;
+ Result m_result;
+
+ QQmlPreviewBlacklist m_blacklist;
+ QHash<QString, QByteArray> m_fileCache;
+ QHash<QString, QStringList> m_directoryCache;
+
+ void file(const QString &file, const QByteArray &contents);
+ void directory(const QString &file, const QStringList &entries);
+ void error(const QString &file);
+ void clearCache();
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPREVIEWFILELOADER_H
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp
new file mode 100644
index 0000000000..5bd96af582
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.cpp
@@ -0,0 +1,472 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $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 "qqmlpreviewhandler.h"
+
+#include <QtCore/qtimer.h>
+#include <QtCore/qsettings.h>
+#include <QtCore/qlibraryinfo.h>
+#include <QtCore/qtranslator.h>
+
+#include <QtGui/qwindow.h>
+#include <QtGui/qguiapplication.h>
+#include <QtQuick/qquickwindow.h>
+#include <QtQuick/qquickitem.h>
+#include <QtQml/qqmlcomponent.h>
+
+#include <private/qquickpixmapcache_p.h>
+#include <private/qquickview_p.h>
+#include <private/qhighdpiscaling_p.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QuitLockDisabler
+{
+ const bool quitLockEnabled;
+
+ QuitLockDisabler() : quitLockEnabled(QCoreApplication::isQuitLockEnabled())
+ {
+ QCoreApplication::setQuitLockEnabled(false);
+ }
+
+ ~QuitLockDisabler()
+ {
+ QCoreApplication::setQuitLockEnabled(quitLockEnabled);
+ }
+};
+
+QQmlPreviewHandler::QQmlPreviewHandler(QObject *parent) : QObject(parent)
+{
+ m_dummyItem.reset(new QQuickItem);
+
+ // TODO: Is there a better way to determine this? We want to keep the window alive when possible
+ // as otherwise it will reappear in a different place when (re)loading a file. However,
+ // the file we load might create another window, in which case the eglfs plugin (and
+ // others?) will do a qFatal as it only supports a single window.
+ const QString platformName = QGuiApplication::platformName();
+ m_supportsMultipleWindows = (platformName == QStringLiteral("windows")
+ || platformName == QStringLiteral("cocoa")
+ || platformName == QStringLiteral("xcb")
+ || platformName == QStringLiteral("wayland"));
+
+ QCoreApplication::instance()->installEventFilter(this);
+
+ m_fpsTimer.setInterval(1000);
+ connect(&m_fpsTimer, &QTimer::timeout, this, &QQmlPreviewHandler::fpsTimerHit);
+}
+
+QQmlPreviewHandler::~QQmlPreviewHandler()
+{
+ removeTranslators();
+ clear();
+}
+
+static void closeAllWindows()
+{
+ const QWindowList windows = QGuiApplication::allWindows();
+ for (QWindow *window : windows)
+ window->close();
+}
+
+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) &&
+ 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));
+ }
+
+ return QObject::eventFilter(obj, event);
+}
+
+void QQmlPreviewHandler::addEngine(QQmlEngine *qmlEngine)
+{
+ m_engines.append(qmlEngine);
+}
+
+void QQmlPreviewHandler::removeEngine(QQmlEngine *qmlEngine)
+{
+ const bool found = m_engines.removeOne(qmlEngine);
+ Q_ASSERT(found);
+ for (QObject *obj : m_createdObjects)
+ if (obj && QtQml::qmlEngine(obj) == qmlEngine)
+ delete obj;
+ m_createdObjects.removeAll(nullptr);
+}
+
+void QQmlPreviewHandler::loadUrl(const QUrl &url)
+{
+ QSharedPointer<QuitLockDisabler> disabler(new QuitLockDisabler);
+
+ clear();
+ m_component.reset(nullptr);
+ QQuickPixmap::purgeCache();
+
+ const int numEngines = m_engines.count();
+ if (numEngines > 1) {
+ emit error(QString::fromLatin1("%1 QML engines available. We cannot decide which one "
+ "should load the component.").arg(numEngines));
+ return;
+ } else if (numEngines == 0) {
+ emit error(QLatin1String("No QML engines found."));
+ return;
+ }
+ m_lastPosition.loadWindowPositionSettings(url);
+
+ QQmlEngine *engine = m_engines.front();
+ engine->clearComponentCache();
+ m_component.reset(new QQmlComponent(engine, url, this));
+
+ auto onStatusChanged = [disabler, this](QQmlComponent::Status status) {
+ switch (status) {
+ case QQmlComponent::Null:
+ case QQmlComponent::Loading:
+ return true; // try again later
+ case QQmlComponent::Ready:
+ tryCreateObject();
+ break;
+ case QQmlComponent::Error:
+ emit error(m_component->errorString());
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+
+ disconnect(m_component.data(), &QQmlComponent::statusChanged, this, nullptr);
+ return false; // we're done
+ };
+
+ if (onStatusChanged(m_component->status()))
+ connect(m_component.data(), &QQmlComponent::statusChanged, this, onStatusChanged);
+}
+
+void QQmlPreviewHandler::rerun()
+{
+ if (m_component.isNull() || !m_component->isReady())
+ emit error(QLatin1String("Component is not ready."));
+
+ QuitLockDisabler disabler;
+ Q_UNUSED(disabler);
+ clear();
+ tryCreateObject();
+}
+
+void QQmlPreviewHandler::zoom(qreal newFactor)
+{
+ if (!m_currentWindow)
+ return;
+ if (qFuzzyIsNull(newFactor)) {
+ emit error(QString::fromLatin1("Zooming with factor: %1 will result in nothing " \
+ "so it will be ignored.").arg(newFactor));
+ return;
+ }
+ QString errorMessage;
+ bool resetZoom = false;
+
+ if (newFactor < 0) {
+ resetZoom = true;
+ newFactor = 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);
+ }
+ }
+
+ 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);
+ }
+}
+
+void QQmlPreviewHandler::removeTranslators()
+{
+ if (!m_qtTranslator.isNull()) {
+ QCoreApplication::removeTranslator(m_qtTranslator.get());
+ m_qtTranslator.reset();
+ }
+
+ if (m_qmlTranslator.isNull()) {
+ QCoreApplication::removeTranslator(m_qmlTranslator.get());
+ m_qmlTranslator.reset();
+ }
+}
+
+void QQmlPreviewHandler::language(const QUrl &context, const QLocale &locale)
+{
+ removeTranslators();
+
+ m_qtTranslator.reset(new QTranslator(this));
+ if (m_qtTranslator->load(locale, QLatin1String("qt"), QLatin1String("_"),
+ QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
+ QCoreApplication::installTranslator(m_qtTranslator.get());
+ }
+
+ m_qmlTranslator.reset(new QTranslator(this));
+ if (m_qmlTranslator->load(locale, QLatin1String("qml"), QLatin1String("_"),
+ context.toLocalFile() + QLatin1String("/i18n"))) {
+ QCoreApplication::installTranslator(m_qmlTranslator.get());
+ }
+
+ for (QQmlEngine *engine : qAsConst(m_engines))
+ engine->retranslate();
+}
+
+void QQmlPreviewHandler::clear()
+{
+ qDeleteAll(m_createdObjects);
+ m_createdObjects.clear();
+ setCurrentWindow(nullptr);
+}
+
+Qt::WindowFlags fixFlags(Qt::WindowFlags flags)
+{
+ // If only the type flag is given, some other window flags are automatically assumed. When we
+ // add a flag, we need to make those explicit.
+ switch (flags) {
+ case Qt::Window:
+ return flags | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint
+ | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint;
+ case Qt::Dialog:
+ case Qt::Tool:
+ return flags | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint;
+ default:
+ return flags;
+ }
+}
+
+void QQmlPreviewHandler::showObject(QObject *object)
+{
+ if (QWindow *window = qobject_cast<QWindow *>(object)) {
+ setCurrentWindow(qobject_cast<QQuickWindow *>(window));
+ for (QWindow *otherWindow : QGuiApplication::allWindows()) {
+ if (QQuickWindow *quickWindow = qobject_cast<QQuickWindow *>(otherWindow)) {
+ if (quickWindow == m_currentWindow)
+ continue;
+ quickWindow->setVisible(false);
+ quickWindow->setFlags(quickWindow->flags() & ~Qt::WindowStaysOnTopHint);
+ }
+ }
+ } else if (QQuickItem *item = qobject_cast<QQuickItem *>(object)) {
+ setCurrentWindow(nullptr);
+ for (QWindow *window : QGuiApplication::allWindows()) {
+ if (QQuickWindow *quickWindow = qobject_cast<QQuickWindow *>(window)) {
+ if (m_currentWindow != nullptr) {
+ emit error(QLatin1String("Multiple QQuickWindows available. We cannot "
+ "decide which one to use."));
+ return;
+ }
+ setCurrentWindow(quickWindow);
+ } else {
+ window->setVisible(false);
+ window->setFlag(Qt::WindowStaysOnTopHint, false);
+ }
+ }
+
+ if (m_currentWindow == nullptr) {
+ setCurrentWindow(new QQuickWindow);
+ m_createdObjects.append(m_currentWindow.data());
+ }
+
+ for (QQuickItem *oldItem : m_currentWindow->contentItem()->childItems())
+ oldItem->setParentItem(m_dummyItem.data());
+
+ // Special case for QQuickView, as that keeps a "root" pointer around, and uses it to
+ // automatically resize the window or the item.
+ if (QQuickView *view = qobject_cast<QQuickView *>(m_currentWindow))
+ QQuickViewPrivate::get(view)->setRootObject(item);
+ else
+ item->setParentItem(m_currentWindow->contentItem());
+
+ m_currentWindow->resize(item->size().toSize());
+ } else {
+ emit error(QLatin1String("Created object is neither a QWindow nor a QQuickItem."));
+ }
+
+ if (m_currentWindow) {
+ m_lastPosition.initLastSavedWindowPosition(m_currentWindow);
+ m_currentWindow->setFlags(fixFlags(m_currentWindow->flags()) | Qt::WindowStaysOnTopHint);
+ m_currentWindow->setVisible(true);
+ }
+}
+
+void QQmlPreviewHandler::setCurrentWindow(QQuickWindow *window)
+{
+ if (window == m_currentWindow.data())
+ return;
+
+ if (m_currentWindow) {
+ disconnect(m_currentWindow.data(), &QQuickWindow::beforeSynchronizing,
+ this, &QQmlPreviewHandler::beforeSynchronizing);
+ disconnect(m_currentWindow.data(), &QQuickWindow::afterSynchronizing,
+ this, &QQmlPreviewHandler::afterSynchronizing);
+ disconnect(m_currentWindow.data(), &QQuickWindow::beforeRendering,
+ this, &QQmlPreviewHandler::beforeRendering);
+ disconnect(m_currentWindow.data(), &QQuickWindow::frameSwapped,
+ this, &QQmlPreviewHandler::frameSwapped);
+ m_fpsTimer.stop();
+ m_rendering = FrameTime();
+ m_synchronizing = FrameTime();
+ }
+
+ m_currentWindow = window;
+
+ if (m_currentWindow) {
+ connect(m_currentWindow.data(), &QQuickWindow::beforeSynchronizing,
+ this, &QQmlPreviewHandler::beforeSynchronizing, Qt::DirectConnection);
+ connect(m_currentWindow.data(), &QQuickWindow::afterSynchronizing,
+ this, &QQmlPreviewHandler::afterSynchronizing, Qt::DirectConnection);
+ connect(m_currentWindow.data(), &QQuickWindow::beforeRendering,
+ this, &QQmlPreviewHandler::beforeRendering, Qt::DirectConnection);
+ connect(m_currentWindow.data(), &QQuickWindow::frameSwapped,
+ this, &QQmlPreviewHandler::frameSwapped, Qt::DirectConnection);
+ m_fpsTimer.start();
+ }
+}
+
+void QQmlPreviewHandler::beforeSynchronizing()
+{
+ m_synchronizing.beginFrame();
+}
+
+void QQmlPreviewHandler::afterSynchronizing()
+{
+
+ if (m_rendering.elapsed >= 0)
+ m_rendering.endFrame();
+ m_synchronizing.recordFrame();
+ m_synchronizing.endFrame();
+}
+
+void QQmlPreviewHandler::beforeRendering()
+{
+ m_rendering.beginFrame();
+}
+
+void QQmlPreviewHandler::frameSwapped()
+{
+ m_rendering.recordFrame();
+}
+
+void QQmlPreviewHandler::FrameTime::beginFrame()
+{
+ timer.start();
+}
+
+void QQmlPreviewHandler::FrameTime::recordFrame()
+{
+ elapsed = timer.elapsed();
+}
+
+void QQmlPreviewHandler::FrameTime::endFrame()
+{
+ if (elapsed < min)
+ min = static_cast<quint16>(qMax(0ll, elapsed));
+ if (elapsed > max)
+ max = static_cast<quint16>(qMin(qint64(std::numeric_limits<quint16>::max()), elapsed));
+ total = static_cast<quint16>(qBound(0ll, qint64(std::numeric_limits<quint16>::max()),
+ elapsed + total));
+ ++number;
+ elapsed = -1;
+}
+
+void QQmlPreviewHandler::FrameTime::reset()
+{
+ min = std::numeric_limits<quint16>::max();
+ max = 0;
+ total = 0;
+ number = 0;
+}
+
+void QQmlPreviewHandler::fpsTimerHit()
+{
+ const FpsInfo info = {
+ m_synchronizing.number,
+ m_synchronizing.min,
+ m_synchronizing.max,
+ m_synchronizing.total,
+
+ m_rendering.number,
+ m_rendering.min,
+ m_rendering.max,
+ m_rendering.total
+ };
+
+ emit fps(info);
+
+ m_rendering.reset();
+ m_synchronizing.reset();
+}
+
+void QQmlPreviewHandler::tryCreateObject()
+{
+ if (!m_supportsMultipleWindows)
+ closeAllWindows();
+ QObject *object = m_component->create();
+ m_createdObjects.append(object);
+ showObject(object);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h
new file mode 100644
index 0000000000..21ea672580
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewhandler.h
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $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 QQMLPREVIEWHANDLER_H
+#define QQMLPREVIEWHANDLER_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 "qqmlpreviewposition.h"
+
+#include <QtCore/qobject.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qrect.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qelapsedtimer.h>
+#include <QtQml/qqmlengine.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlEngine;
+class QQuickItem;
+class QQmlPreviewUrlInterceptor;
+class QQuickWindow;
+class QTranslator;
+
+class QQmlPreviewHandler : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QQmlPreviewHandler(QObject *parent = nullptr);
+ ~QQmlPreviewHandler();
+
+ void addEngine(QQmlEngine *engine);
+ void removeEngine(QQmlEngine *engine);
+
+ void loadUrl(const QUrl &url);
+ void rerun();
+ void zoom(qreal newFactor);
+ void language(const QUrl &context, const QLocale &locale);
+
+ void clear();
+
+ struct FpsInfo {
+ quint16 numSyncs;
+ quint16 minSync;
+ quint16 maxSync;
+ quint16 totalSync;
+
+ quint16 numRenders;
+ quint16 minRender;
+ quint16 maxRender;
+ quint16 totalRender;
+ };
+
+signals:
+ void error(const QString &message);
+ void fps(const FpsInfo &info);
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *event);
+private:
+ void tryCreateObject();
+ void showObject(QObject *object);
+ void setCurrentWindow(QQuickWindow *window);
+
+ void beforeSynchronizing();
+ void afterSynchronizing();
+ void beforeRendering();
+ void frameSwapped();
+
+ void fpsTimerHit();
+ void removeTranslators();
+
+ QScopedPointer<QQuickItem> m_dummyItem;
+ QList<QQmlEngine *> m_engines;
+ QVector<QPointer<QObject>> m_createdObjects;
+ QScopedPointer<QQmlComponent> m_component;
+ QPointer<QQuickWindow> m_currentWindow;
+ bool m_supportsMultipleWindows;
+ QQmlPreviewPosition m_lastPosition;
+
+ QTimer m_fpsTimer;
+
+ struct FrameTime {
+ void beginFrame();
+ void recordFrame();
+ void endFrame();
+ void reset();
+
+ QElapsedTimer timer;
+ qint64 elapsed = -1;
+ quint16 min = std::numeric_limits<quint16>::max();
+ quint16 max = 0;
+ quint16 total = 0;
+ quint16 number = 0;
+ };
+
+ FrameTime m_rendering;
+ FrameTime m_synchronizing;
+
+ QScopedPointer<QTranslator> m_qtTranslator;
+ QScopedPointer<QTranslator> m_qmlTranslator;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QQmlPreviewHandler::FpsInfo)
+
+#endif // QQMLPREVIEWHANDLER_H
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp
new file mode 100644
index 0000000000..3edcbac0a9
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.cpp
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $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 "qqmlpreviewposition.h"
+
+#include <QtGui/qwindow.h>
+#include <QtGui/qscreen.h>
+#include <QtGui/qguiapplication.h>
+
+QT_BEGIN_NAMESPACE
+
+static const QSize availableScreenSize(const QPoint &point)
+{
+ if (const QScreen *screen = QGuiApplication::screenAt(point))
+ return screen->availableGeometry().size();
+ return QSize();
+}
+
+QQmlPreviewPosition::QQmlPreviewPosition()
+ : m_settings("QtProject", "QtQmlPreview")
+{
+ m_savePositionTimer.setSingleShot(true);
+ m_savePositionTimer.setInterval(500);
+ QObject::connect(&m_savePositionTimer, &QTimer::timeout, [this]() {
+ saveWindowPosition();
+ });
+}
+
+void QQmlPreviewPosition::setPosition(const QPoint &point)
+{
+ m_hasPosition = true;
+ m_lastWindowPosition = point;
+ m_savePositionTimer.start();
+}
+
+void QQmlPreviewPosition::saveWindowPosition()
+{
+ if (m_hasPosition) {
+ if (!m_settingsKey.isNull())
+ m_settings.setValue(m_settingsKey, m_lastWindowPosition);
+
+ m_settings.setValue(QLatin1String("global_lastpostion"), m_lastWindowPosition);
+ }
+}
+
+void QQmlPreviewPosition::loadWindowPositionSettings(const QUrl &url)
+{
+ m_settingsKey = url.toString(QUrl::PreferLocalFile) + QLatin1String("_lastpostion");
+
+ if (m_settings.contains(m_settingsKey)) {
+ m_hasPosition = true;
+ m_lastWindowPosition = m_settings.value(m_settingsKey).toPoint();
+ }
+}
+
+void QQmlPreviewPosition::initLastSavedWindowPosition(QWindow *window)
+{
+ if (m_positionedWindows.contains(window))
+ return;
+ 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();
+ }
+ if (QGuiApplication::screenAt(m_lastWindowPosition))
+ window->setFramePosition(m_lastWindowPosition);
+
+ m_positionedWindows.append(window);
+}
+
+const QSize QQmlPreviewPosition::currentScreenSize(QWindow *window)
+{
+ return availableScreenSize(window->position());
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/compressedtexture/qsgtexturefilehandler_p.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.h
index 8b831aebb9..3d4ca9dc67 100644
--- a/src/quick/scenegraph/compressedtexture/qsgtexturefilehandler_p.h
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewposition.h
@@ -1,9 +1,9 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
-** This file is part of the QtQuick module of the Qt Toolkit.
+** This file is part of the QML preview debug service.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QSGTEXTUREFILEHANDLER_P_H
-#define QSGTEXTUREFILEHANDLER_P_H
+#ifndef QQMLPREVIEWPOSITION_H
+#define QQMLPREVIEWPOSITION_H
//
// W A R N I N G
@@ -51,33 +51,37 @@
// We mean it.
//
-#include <QLoggingCategory>
+#include <QtCore/qvector.h>
+#include <QtCore/qpoint.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qsettings.h>
QT_BEGIN_NAMESPACE
-Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TEXTUREIO)
+class QWindow;
-class QQuickTextureFactory;
-
-class QSGTextureFileHandler
+class QQmlPreviewPosition
{
public:
- QSGTextureFileHandler(QIODevice *device, const QByteArray &logName = QByteArray())
- : m_device(device)
- {
- m_logName = !logName.isEmpty() ? logName : QByteArrayLiteral("(unknown)");
- }
- virtual ~QSGTextureFileHandler() {}
+ QQmlPreviewPosition();
- virtual QQuickTextureFactory *read() = 0;
- QIODevice *device() const { return m_device; }
- QByteArray logName() const { return m_logName; }
+ void setPosition(const QPoint &point);
+ void saveWindowPosition();
+ void loadWindowPositionSettings(const QUrl &url);
+ void initLastSavedWindowPosition(QWindow *window);
+ static const QSize currentScreenSize(QWindow *window);
private:
- QIODevice *m_device = nullptr;
- QByteArray m_logName;
+ bool m_hasPosition = false;
+ QPoint m_lastWindowPosition;
+ QSettings m_settings;
+ QString m_settingsKey;
+ QTimer m_savePositionTimer;
+ QVector<QWindow *> m_positionedWindows;
};
+
QT_END_NAMESPACE
-#endif // QSGTEXTUREFILEHANDLER_P_H
+#endif // QQMLPREVIEWPOSITION_H
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp
new file mode 100644
index 0000000000..2e2224df47
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.cpp
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $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 "qqmlpreviewservice.h"
+
+#include <QtCore/qpointer.h>
+#include <QtQml/qqmlengine.h>
+#include <QtQml/qqmlcomponent.h>
+#include <QtQuick/qquickwindow.h>
+#include <QtQuick/qquickitem.h>
+#include <QtGui/qguiapplication.h>
+
+#include <private/qquickpixmapcache_p.h>
+#include <private/qqmldebugconnector_p.h>
+#include <private/qversionedpacket_p.h>
+
+QT_BEGIN_NAMESPACE
+
+const QString QQmlPreviewServiceImpl::s_key = QStringLiteral("QmlPreview");
+using QQmlDebugPacket = QVersionedPacket<QQmlDebugConnector>;
+
+QQmlPreviewServiceImpl::QQmlPreviewServiceImpl(QObject *parent) :
+ QQmlDebugService(s_key, 1.0f, parent)
+{
+ m_loader.reset(new QQmlPreviewFileLoader(this));
+ connect(this, &QQmlPreviewServiceImpl::load,
+ m_loader.data(), &QQmlPreviewFileLoader::whitelist, Qt::DirectConnection);
+ connect(this, &QQmlPreviewServiceImpl::load, &m_handler, &QQmlPreviewHandler::loadUrl);
+ connect(this, &QQmlPreviewServiceImpl::rerun, &m_handler, &QQmlPreviewHandler::rerun);
+ connect(this, &QQmlPreviewServiceImpl::zoom, &m_handler, &QQmlPreviewHandler::zoom);
+ connect(this, &QQmlPreviewServiceImpl::language, &m_handler, &QQmlPreviewHandler::language);
+ connect(&m_handler, &QQmlPreviewHandler::error, this, &QQmlPreviewServiceImpl::forwardError,
+ Qt::DirectConnection);
+ connect(&m_handler, &QQmlPreviewHandler::fps, this, &QQmlPreviewServiceImpl::forwardFps,
+ Qt::DirectConnection);
+}
+
+QQmlPreviewServiceImpl::~QQmlPreviewServiceImpl()
+{
+}
+
+void QQmlPreviewServiceImpl::messageReceived(const QByteArray &data)
+{
+ QQmlDebugPacket packet(data);
+ qint8 command;
+
+ packet >> command;
+ switch (command) {
+ case File: {
+ QString path;
+ QByteArray contents;
+ packet >> path >> contents;
+ emit file(path, contents);
+
+ // Replace the whole scene with the first file successfully loaded over the debug
+ // connection. This is an OK approximation of the root component, and if the client wants
+ // something specific, it will send an explicit Load anyway.
+ if (m_currentUrl.isEmpty() && path.endsWith(".qml")) {
+ if (path.startsWith(':'))
+ m_currentUrl = QUrl("qrc" + path);
+ else
+ m_currentUrl = QUrl::fromLocalFile(path);
+ emit load(m_currentUrl);
+ }
+ break;
+ }
+ case Directory: {
+ QString path;
+ QStringList entries;
+ packet >> path >> entries;
+ emit directory(path, entries);
+ break;
+ }
+ case Load: {
+ QUrl url;
+ packet >> url;
+ if (url.isEmpty())
+ url = m_currentUrl;
+ else
+ m_currentUrl = url;
+ emit load(url);
+ break;
+ }
+ case Error: {
+ QString file;
+ packet >> file;
+ emit error(file);
+ break;
+ }
+ case Rerun:
+ emit rerun();
+ break;
+ case ClearCache:
+ emit clearCache();
+ break;
+ case Zoom: {
+ float factor;
+ packet >> factor;
+ emit zoom(static_cast<qreal>(factor));
+ break;
+ }
+ case Language: {
+ QUrl context;
+ QString locale;
+ packet >> context >> locale;
+ emit language(context.isEmpty() ? m_currentUrl : context,
+ locale.isEmpty() ? QLocale() : QLocale(locale));
+ break;
+ }
+ default:
+ forwardError(QString::fromLatin1("Invalid command: %1").arg(command));
+ break;
+ }
+}
+
+void QQmlPreviewServiceImpl::engineAboutToBeAdded(QJSEngine *engine)
+{
+ if (QQmlEngine *qmlEngine = qobject_cast<QQmlEngine *>(engine))
+ m_handler.addEngine(qmlEngine);
+ emit attachedToEngine(engine);
+}
+
+void QQmlPreviewServiceImpl::engineAboutToBeRemoved(QJSEngine *engine)
+{
+ if (QQmlEngine *qmlEngine = qobject_cast<QQmlEngine *>(engine))
+ m_handler.removeEngine(qmlEngine);
+ emit detachedFromEngine(engine);
+}
+
+void QQmlPreviewServiceImpl::stateChanged(QQmlDebugService::State state)
+{
+ m_fileEngine.reset(state == Enabled ? new QQmlPreviewFileEngineHandler(m_loader.data())
+ : nullptr);
+}
+
+void QQmlPreviewServiceImpl::forwardRequest(const QString &file)
+{
+ QQmlDebugPacket packet;
+ packet << static_cast<qint8>(Request) << file;
+ emit messageToClient(name(), packet.data());
+}
+
+void QQmlPreviewServiceImpl::forwardError(const QString &error)
+{
+ QQmlDebugPacket packet;
+ packet << static_cast<qint8>(Error) << error;
+ emit messageToClient(name(), packet.data());
+}
+
+void QQmlPreviewServiceImpl::forwardFps(const QQmlPreviewHandler::FpsInfo &frames)
+{
+ QQmlDebugPacket packet;
+ packet << static_cast<qint8>(Fps)
+ << frames.numSyncs << frames.minSync << frames.maxSync << frames.totalSync
+ << frames.numRenders << frames.minRender << frames.maxRender << frames.totalRender;
+ emit messageToClient(name(), packet.data());
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h
new file mode 100644
index 0000000000..7bdc87ec59
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $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 QQMLPREVIEWSERVICE_H
+#define QQMLPREVIEWSERVICE_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 "qqmlpreviewhandler.h"
+#include "qqmlpreviewfileengine.h"
+#include <private/qqmldebugserviceinterfaces_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlPreviewFileEngineHandler;
+class QQmlPreviewHandler;
+class QQmlPreviewServiceImpl : public QQmlDebugService
+{
+ Q_OBJECT
+
+public:
+ enum Command {
+ File,
+ Load,
+ Request,
+ Error,
+ Rerun,
+ Directory,
+ ClearCache,
+ Zoom,
+ Fps,
+ Language
+ };
+
+ static const QString s_key;
+
+ QQmlPreviewServiceImpl(QObject *parent = nullptr);
+ virtual ~QQmlPreviewServiceImpl();
+
+ void messageReceived(const QByteArray &message) override;
+ void engineAboutToBeAdded(QJSEngine *engine) override;
+ void engineAboutToBeRemoved(QJSEngine *engine) override;
+ void stateChanged(State state) override;
+
+ void forwardRequest(const QString &file);
+ void forwardError(const QString &error);
+ void forwardFps(const QQmlPreviewHandler::FpsInfo &frames);
+
+signals:
+ void error(const QString &file);
+ void file(const QString &file, const QByteArray &contents);
+ void directory(const QString &file, const QStringList &entries);
+ void load(const QUrl &url);
+ void rerun();
+ void clearCache();
+ void zoom(qreal factor);
+ void language(const QUrl &context, const QLocale &locale);
+
+private:
+ QScopedPointer<QQmlPreviewFileEngineHandler> m_fileEngine;
+ QScopedPointer<QQmlPreviewFileLoader> m_loader;
+ QQmlPreviewHandler m_handler;
+ QUrl m_currentUrl;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPREVIEWSERVICE_H
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.json b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.json
new file mode 100644
index 0000000000..d7e1ef1f10
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservice.json
@@ -0,0 +1,3 @@
+{
+ "Keys" : [ "QmlPreview" ]
+}
diff --git a/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp
new file mode 100644
index 0000000000..f0aa3226c8
--- /dev/null
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.cpp
@@ -0,0 +1,50 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QML preview debug service.
+**
+** $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 "qqmlpreviewservicefactory.h"
+#include "qqmlpreviewservice.h"
+
+QT_BEGIN_NAMESPACE
+
+QQmlDebugService *QQmlPreviewServiceFactory::create(const QString &key)
+{
+ return key == QQmlPreviewServiceImpl::s_key ? new QQmlPreviewServiceImpl(this) : nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/compressedtexture/qsgktxhandler_p.h b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.h
index 22f4db65b2..0ceadf24f5 100644
--- a/src/quick/scenegraph/compressedtexture/qsgktxhandler_p.h
+++ b/src/plugins/qmltooling/qmldbg_preview/qqmlpreviewservicefactory.h
@@ -1,9 +1,9 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
-** This file is part of the QtQuick module of the Qt Toolkit.
+** This file is part of the QML preview debug service.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QSGKTXHANDLER_H
-#define QSGKTXHANDLER_H
+#ifndef QQMLPREVIEWSERVCIEFACTORY_H
+#define QQMLPREVIEWSERVCIEFACTORY_H
//
// W A R N I N G
@@ -51,28 +51,19 @@
// We mean it.
//
-#include "qsgtexturefilehandler_p.h"
+#include <private/qqmldebugservicefactory_p.h>
QT_BEGIN_NAMESPACE
-struct KTXHeader;
-
-class QSGKtxHandler : public QSGTextureFileHandler
+class QQmlPreviewServiceFactory : public QQmlDebugServiceFactory
{
-public:
- using QSGTextureFileHandler::QSGTextureFileHandler;
-
- static bool canRead(const QByteArray &suffix, const QByteArray &block);
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlDebugServiceFactory_iid FILE "qqmlpreviewservice.json")
- QQuickTextureFactory *read() override;
-
-private:
- bool checkHeader(const KTXHeader &header);
- quint32 decode(quint32 val);
-
- bool inverseEndian = false;
+public:
+ QQmlDebugService *create(const QString &key) override;
};
QT_END_NAMESPACE
-#endif // QSGKTXHANDLER_H
+#endif // QQMLPREVIEWSERVCIEFACTORY_H
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
index 19104927f2..a688e98b3f 100644
--- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
@@ -80,8 +80,7 @@ void QQmlProfilerAdapter::init(QQmlProfilerService *service, QQmlProfiler *profi
// convert to QByteArrays that can be sent to the debug client
static void qQmlProfilerDataToByteArrays(const QQmlProfilerData &d,
QQmlProfiler::LocationHash &locations,
- QList<QByteArray> &messages,
- bool trackLocations)
+ QList<QByteArray> &messages)
{
QQmlDebugPacket ds;
Q_ASSERT_X((d.messageType & (1 << 31)) == 0, Q_FUNC_INFO,
@@ -96,7 +95,7 @@ static void qQmlProfilerDataToByteArrays(const QQmlProfilerData &d,
if (decodedMessageType == QQmlProfilerDefinitions::RangeEnd
|| decodedMessageType == QQmlProfilerDefinitions::RangeStart) {
ds << d.time << decodedMessageType << static_cast<quint32>(d.detailType);
- if (trackLocations && d.locationId != 0)
+ if (d.locationId != 0)
ds << static_cast<qint64>(d.locationId);
} else {
auto i = locations.find(d.locationId);
@@ -107,8 +106,7 @@ static void qQmlProfilerDataToByteArrays(const QQmlProfilerData &d,
<< static_cast<qint32>(i->location.column);
if (d.messageType & (1 << QQmlProfilerDefinitions::RangeData)) {
// Send both, location and data ...
- if (trackLocations)
- ds << static_cast<qint64>(d.locationId);
+ ds << static_cast<qint64>(d.locationId);
messages.append(ds.squeezedData());
ds.clear();
ds << d.time << int(QQmlProfilerDefinitions::RangeData)
@@ -116,10 +114,8 @@ static void qQmlProfilerDataToByteArrays(const QQmlProfilerData &d,
<< (i->location.sourceFile.isEmpty() ? i->url.toString() :
i->location.sourceFile);
}
- if (trackLocations) {
- ds << static_cast<qint64>(d.locationId);
- locations.erase(i); // ... so that we can erase here without missing anything.
- }
+ ds << static_cast<qint64>(d.locationId);
+ locations.erase(i); // ... so that we can erase here without missing anything.
} else {
// Skip RangeData and RangeLocation: We've already sent them
continue;
@@ -130,14 +126,13 @@ static void qQmlProfilerDataToByteArrays(const QQmlProfilerData &d,
}
}
-qint64 QQmlProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages,
- bool trackLocations)
+qint64 QQmlProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages)
{
while (next != data.length()) {
const QQmlProfilerData &nextData = data.at(next);
if (nextData.time > until || messages.length() > s_numMessagesPerBatch)
return nextData.time;
- qQmlProfilerDataToByteArrays(nextData, locations, messages, trackLocations);
+ qQmlProfilerDataToByteArrays(nextData, locations, messages);
++next;
}
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h
index b14b72d254..12544a19c2 100644
--- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.h
@@ -61,8 +61,7 @@ class QQmlProfilerAdapter : public QQmlAbstractProfilerAdapter {
public:
QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEnginePrivate *engine);
QQmlProfilerAdapter(QQmlProfilerService *service, QQmlTypeLoader *loader);
- qint64 sendMessages(qint64 until, QList<QByteArray> &messages,
- bool trackLocations) override;
+ qint64 sendMessages(qint64 until, QList<QByteArray> &messages) override;
void receiveData(const QVector<QQmlProfilerData> &new_data,
const QQmlProfiler::LocationHash &locations);
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp
index 21a5663f59..462401a093 100644
--- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.cpp
@@ -58,7 +58,7 @@ Q_QML_DEBUG_PLUGIN_LOADER(QQmlAbstractProfilerAdapter)
QQmlProfilerServiceImpl::QQmlProfilerServiceImpl(QObject *parent) :
QQmlConfigurableDebugService<QQmlProfilerService>(1, parent),
- m_waitingForStop(false), m_useMessageTypes(false), m_globalEnabled(false), m_globalFeatures(0)
+ m_waitingForStop(false), m_globalEnabled(false), m_globalFeatures(0)
{
m_timer.start();
QQmlAbstractProfilerAdapter *quickAdapter =
@@ -332,7 +332,7 @@ void QQmlProfilerServiceImpl::stopProfiling(QJSEngine *engine)
m_waitingForStop = true;
for (QQmlAbstractProfilerAdapter *profiler : qAsConst(reporting))
- profiler->reportData(m_useMessageTypes);
+ profiler->reportData();
for (QQmlAbstractProfilerAdapter *profiler : qAsConst(stopping))
profiler->stopProfiling();
@@ -367,8 +367,7 @@ void QQmlProfilerServiceImpl::sendMessages()
m_startTimes.erase(m_startTimes.begin());
qint64 next = first->sendMessages(m_startTimes.isEmpty() ?
std::numeric_limits<qint64>::max() :
- m_startTimes.begin().key(), messages,
- m_useMessageTypes);
+ m_startTimes.begin().key(), messages);
if (next != -1)
m_startTimes.insert(next, first);
@@ -454,13 +453,15 @@ void QQmlProfilerServiceImpl::messageReceived(const QByteArray &message)
&m_flushTimer, &QTimer::stop);
}
}
+
+ bool useMessageTypes = false;
if (!stream.atEnd())
- stream >> m_useMessageTypes;
+ stream >> useMessageTypes;
// If engineId == -1 objectForId() and then the cast will return 0.
- if (enabled)
+ if (enabled && useMessageTypes) // If the client doesn't support message types don't profile.
startProfiling(qobject_cast<QJSEngine *>(objectForId(engineId)), features);
- else
+ else if (!enabled) // On stopProfiling the client doesn't repeat useMessageTypes.
stopProfiling(qobject_cast<QJSEngine *>(objectForId(engineId)));
stopWaiting();
@@ -486,7 +487,7 @@ void QQmlProfilerServiceImpl::flush()
}
for (QQmlAbstractProfilerAdapter *profiler : qAsConst(reporting))
- profiler->reportData(m_useMessageTypes);
+ profiler->reportData();
}
QT_END_NAMESPACE
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h
index 2b92a478c1..3791ab29ae 100644
--- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofilerservice.h
@@ -117,7 +117,6 @@ private:
QElapsedTimer m_timer;
QTimer m_flushTimer;
bool m_waitingForStop;
- bool m_useMessageTypes;
bool m_globalEnabled;
quint64 m_globalFeatures;
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp
index f1ac8ef998..e4f2f556fc 100644
--- a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp
+++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp
@@ -104,8 +104,7 @@ qint64 QV4ProfilerAdapter::finalizeMessages(qint64 until, QList<QByteArray> &mes
return callNext == -1 ? memoryNext : qMin(callNext, memoryNext);
}
-qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages,
- bool trackLocations)
+qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages)
{
QQmlDebugPacket d;
@@ -134,24 +133,17 @@ qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &message
appendMemoryEvents(props.start, messages, d);
auto location = m_functionLocations.find(props.id);
- d << props.start << int(RangeStart) << int(Javascript);
- if (trackLocations)
- d << static_cast<qint64>(props.id);
+ d << props.start << int(RangeStart) << int(Javascript) << static_cast<qint64>(props.id);
if (location != m_functionLocations.end()) {
messages.push_back(d.squeezedData());
d.clear();
d << props.start << int(RangeLocation) << int(Javascript) << location->file << location->line
- << location->column;
- if (trackLocations)
- d << static_cast<qint64>(props.id);
+ << location->column << static_cast<qint64>(props.id);
messages.push_back(d.squeezedData());
d.clear();
- d << props.start << int(RangeData) << int(Javascript) << location->name;
-
- if (trackLocations) {
- d << static_cast<qint64>(props.id);
- m_functionLocations.erase(location);
- }
+ d << props.start << int(RangeData) << int(Javascript) << location->name
+ << static_cast<qint64>(props.id);
+ m_functionLocations.erase(location);
}
messages.push_back(d.squeezedData());
d.clear();
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h
index 2211c82fc5..c4ca38d9b0 100644
--- a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h
+++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.h
@@ -68,8 +68,7 @@ class QV4ProfilerAdapter : public QQmlAbstractProfilerAdapter {
public:
QV4ProfilerAdapter(QQmlProfilerService *service, QV4::ExecutionEngine *engine);
- virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages,
- bool trackLocations) override;
+ virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages) override;
void receiveData(const QV4::Profiling::FunctionLocationHash &,
const QVector<QV4::Profiling::FunctionCallProperties> &,
diff --git a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp
index 2c152e4cd5..79a1c82411 100644
--- a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp
+++ b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp
@@ -152,10 +152,8 @@ static void qQuickProfilerDataToByteArrays(const QQuickProfilerData &data,
}
}
-qint64 QQuickProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages,
- bool trackLocations)
+qint64 QQuickProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages)
{
- Q_UNUSED(trackLocations);
while (next < m_data.size()) {
if (m_data[next].time <= until && messages.length() <= s_numMessagesPerBatch)
qQuickProfilerDataToByteArrays(m_data[next++], messages);
diff --git a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h
index 1ad020afd6..1f3467c1d0 100644
--- a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h
+++ b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.h
@@ -61,7 +61,7 @@ class QQuickProfilerAdapter : public QQmlAbstractProfilerAdapter {
public:
QQuickProfilerAdapter(QObject *parent = 0);
~QQuickProfilerAdapter();
- qint64 sendMessages(qint64 until, QList<QByteArray> &messages, bool trackLocations) override;
+ qint64 sendMessages(qint64 until, QList<QByteArray> &messages) override;
void receiveData(const QVector<QQuickProfilerData> &new_data);
private:
diff --git a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp
index c1e86f0b3c..8293e88038 100644
--- a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp
+++ b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp
@@ -177,14 +177,13 @@ private:
void changeServiceState(const QString &serviceName, QQmlDebugService::State state);
void removeThread();
void receiveMessage();
- void invalidPacket();
+ void protocolError();
QQmlDebugServerConnection *m_connection;
QHash<QString, QQmlDebugService *> m_plugins;
QStringList m_clientPlugins;
bool m_gotHello;
bool m_blockingMode;
- bool m_clientSupportsMultiPackets;
QHash<QJSEngine *, EngineCondition> m_engineConditions;
@@ -277,8 +276,7 @@ static void cleanupOnShutdown()
QQmlDebugServerImpl::QQmlDebugServerImpl() :
m_connection(nullptr),
m_gotHello(false),
- m_blockingMode(false),
- m_clientSupportsMultiPackets(false)
+ m_blockingMode(false)
{
static bool postRoutineAdded = false;
if (!postRoutineAdded) {
@@ -464,10 +462,9 @@ void QQmlDebugServerImpl::receiveMessage()
s_dataStreamVersion = QDataStream::Qt_DefaultCompiledVersion;
}
+ bool clientSupportsMultiPackets = false;
if (!in.atEnd())
- in >> m_clientSupportsMultiPackets;
- else
- m_clientSupportsMultiPackets = false;
+ in >> clientSupportsMultiPackets;
// Send the hello answer immediately, since it needs to arrive before
// the plugins below start sending messages.
@@ -475,13 +472,15 @@ void QQmlDebugServerImpl::receiveMessage()
QQmlDebugPacket out;
QStringList pluginNames;
QList<float> pluginVersions;
- const int count = m_plugins.count();
- pluginNames.reserve(count);
- pluginVersions.reserve(count);
- for (QHash<QString, QQmlDebugService *>::ConstIterator i = m_plugins.constBegin();
- i != m_plugins.constEnd(); ++i) {
- pluginNames << i.key();
- pluginVersions << i.value()->version();
+ if (clientSupportsMultiPackets) { // otherwise, disable all plugins
+ const int count = m_plugins.count();
+ pluginNames.reserve(count);
+ pluginVersions.reserve(count);
+ for (QHash<QString, QQmlDebugService *>::ConstIterator i = m_plugins.constBegin();
+ i != m_plugins.constEnd(); ++i) {
+ pluginNames << i.key();
+ pluginVersions << i.value()->version();
+ }
}
out << QString(QStringLiteral("QDeclarativeDebugClient")) << 0 << protocolVersion
@@ -523,7 +522,7 @@ void QQmlDebugServerImpl::receiveMessage()
} else {
qWarning("QML Debugger: Invalid control message %d.", op);
- invalidPacket();
+ protocolError();
return;
}
@@ -701,16 +700,11 @@ void QQmlDebugServerImpl::sendMessage(const QString &name, const QByteArray &mes
void QQmlDebugServerImpl::sendMessages(const QString &name, const QList<QByteArray> &messages)
{
if (canSendMessage(name)) {
- if (m_clientSupportsMultiPackets) {
- QQmlDebugPacket out;
- out << name;
- for (const QByteArray &message : messages)
- out << message;
- m_protocol->send(out.data());
- } else {
- for (const QByteArray &message : messages)
- doSendMessage(name, message);
- }
+ QQmlDebugPacket out;
+ out << name;
+ for (const QByteArray &message : messages)
+ out << message;
+ m_protocol->send(out.data());
m_connection->flush();
}
}
@@ -743,16 +737,16 @@ void QQmlDebugServerImpl::setDevice(QIODevice *socket)
m_protocol = new QPacketProtocol(socket, this);
QObject::connect(m_protocol, &QPacketProtocol::readyRead,
this, &QQmlDebugServerImpl::receiveMessage);
- QObject::connect(m_protocol, &QPacketProtocol::invalidPacket,
- this, &QQmlDebugServerImpl::invalidPacket);
+ QObject::connect(m_protocol, &QPacketProtocol::error,
+ this, &QQmlDebugServerImpl::protocolError);
if (blockingMode())
m_protocol->waitForReadyRead(-1);
}
-void QQmlDebugServerImpl::invalidPacket()
+void QQmlDebugServerImpl::protocolError()
{
- qWarning("QML Debugger: Received a corrupted packet! Giving up ...");
+ qWarning("QML Debugger: A protocol error has occurred! Giving up ...");
m_connection->disconnect();
// protocol might still be processing packages at this point
m_protocol->deleteLater();
diff --git a/src/plugins/qmltooling/qmltooling.pro b/src/plugins/qmltooling/qmltooling.pro
index 119415372b..30097be77b 100644
--- a/src/plugins/qmltooling/qmltooling.pro
+++ b/src/plugins/qmltooling/qmltooling.pro
@@ -6,9 +6,8 @@ SUBDIRS += \
packetprotocol
# Connectors
-SUBDIRS += \
- qmldbg_native \
- qmldbg_server
+SUBDIRS += qmldbg_native
+qtConfig(thread): SUBDIRS += qmldbg_server
qmldbg_native.depends = packetprotocol
qmldbg_server.depends = packetprotocol
@@ -36,6 +35,10 @@ qtHaveModule(quick) {
SUBDIRS += \
qmldbg_inspector \
qmldbg_quickprofiler
+
+ qtConfig(qml-network): SUBDIRS += qmldbg_preview
+
qmldbg_inspector.depends = packetprotocol
qmldbg_quickprofiler.depends = packetprotocol
+ qmldbg_preview.depends = packetprotocol
}
diff --git a/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.cpp b/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.cpp
index 0bd51cbf46..d728686248 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.cpp
+++ b/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.cpp
@@ -176,6 +176,12 @@ void QSGOpenVGInternalRectangleNode::setGradientStops(const QGradientStops &stop
m_fillDirty = true;
}
+void QSGOpenVGInternalRectangleNode::setGradientVertical(bool vertical)
+{
+ m_vertical = vertical;
+ m_fillDirty = true;
+}
+
void QSGOpenVGInternalRectangleNode::setRadius(qreal radius)
{
m_radius = radius;
@@ -242,13 +248,13 @@ void QSGOpenVGInternalRectangleNode::render()
} else {
// Linear Gradient
vgSetParameteri(m_rectanglePaint, VG_PAINT_TYPE, VG_PAINT_TYPE_LINEAR_GRADIENT);
- const VGfloat verticalLinearGradient[] = {
- 0.0f,
+ const VGfloat linearGradient[] = {
0.0f,
0.0f,
- static_cast<VGfloat>(m_rect.height())
+ m_vertical ? 0.0f : static_cast<VGfloat>(m_rect.width()),
+ m_vertical ? static_cast<VGfloat>(m_rect.height()) : 0.0f
};
- vgSetParameterfv(m_rectanglePaint, VG_PAINT_LINEAR_GRADIENT, 4, verticalLinearGradient);
+ vgSetParameterfv(m_rectanglePaint, VG_PAINT_LINEAR_GRADIENT, 4, linearGradient);
vgSetParameteri(m_rectanglePaint, VG_PAINT_COLOR_RAMP_SPREAD_MODE, VG_COLOR_RAMP_SPREAD_PAD);
vgSetParameteri(m_rectanglePaint, VG_PAINT_COLOR_RAMP_PREMULTIPLIED, false);
diff --git a/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.h b/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.h
index e8d25c94f8..86d2c3318c 100644
--- a/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.h
+++ b/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.h
@@ -59,6 +59,7 @@ public:
void setPenColor(const QColor &color) override;
void setPenWidth(qreal width) override;
void setGradientStops(const QGradientStops &stops) override;
+ void setGradientVertical(bool vertical) override;
void setRadius(qreal radius) override;
void setAligned(bool aligned) override;
void update() override;
@@ -85,6 +86,7 @@ private:
qreal m_penWidth = 0.0;
qreal m_radius = 0.0;
bool m_aligned = false;
+ bool m_vertical = true;
QGradientStops m_gradientStops;
VGPath m_rectanglePath;
diff --git a/src/qml/animations/qabstractanimationjob.cpp b/src/qml/animations/qabstractanimationjob.cpp
index 3f33e7e81b..ece2f0d692 100644
--- a/src/qml/animations/qabstractanimationjob.cpp
+++ b/src/qml/animations/qabstractanimationjob.cpp
@@ -70,17 +70,12 @@ QQmlAnimationTimer::QQmlAnimationTimer() :
QQmlAnimationTimer *QQmlAnimationTimer::instance(bool create)
{
QQmlAnimationTimer *inst;
-#ifndef QT_NO_THREAD
if (create && !animationTimer()->hasLocalData()) {
inst = new QQmlAnimationTimer;
animationTimer()->setLocalData(inst);
} else {
inst = animationTimer() ? animationTimer()->localData() : 0;
}
-#else
- static QAnimationTimer unifiedTimer;
- inst = &unifiedTimer;
-#endif
return inst;
}
diff --git a/src/qml/animations/qabstractanimationjob_p.h b/src/qml/animations/qabstractanimationjob_p.h
index 63fd4b0dac..0be6ca96ea 100644
--- a/src/qml/animations/qabstractanimationjob_p.h
+++ b/src/qml/animations/qabstractanimationjob_p.h
@@ -56,6 +56,8 @@
#include <QtCore/private/qabstractanimation_p.h>
#include <vector>
+QT_REQUIRE_CONFIG(qml_animation);
+
QT_BEGIN_NAMESPACE
class QAnimationGroupJob;
diff --git a/src/qml/animations/qanimationgroupjob_p.h b/src/qml/animations/qanimationgroupjob_p.h
index fb567dc019..b01b2f3b36 100644
--- a/src/qml/animations/qanimationgroupjob_p.h
+++ b/src/qml/animations/qanimationgroupjob_p.h
@@ -54,6 +54,8 @@
#include "private/qabstractanimationjob_p.h"
#include <QtCore/qdebug.h>
+QT_REQUIRE_CONFIG(qml_animation);
+
QT_BEGIN_NAMESPACE
class Q_QML_PRIVATE_EXPORT QAnimationGroupJob : public QAbstractAnimationJob
diff --git a/src/qml/animations/qanimationjobutil_p.h b/src/qml/animations/qanimationjobutil_p.h
index 0bb9e83b2d..e3d6fe9178 100644
--- a/src/qml/animations/qanimationjobutil_p.h
+++ b/src/qml/animations/qanimationjobutil_p.h
@@ -51,6 +51,8 @@
// We mean it.
//
+QT_REQUIRE_CONFIG(qml_animation);
+
#define RETURN_IF_DELETED(func) \
{ \
bool *prevWasDeleted = m_wasDeleted; \
diff --git a/src/qml/animations/qcontinuinganimationgroupjob_p.h b/src/qml/animations/qcontinuinganimationgroupjob_p.h
index baf4ff1ae5..c67b8d39ad 100644
--- a/src/qml/animations/qcontinuinganimationgroupjob_p.h
+++ b/src/qml/animations/qcontinuinganimationgroupjob_p.h
@@ -53,6 +53,8 @@
#include "private/qanimationgroupjob_p.h"
+QT_REQUIRE_CONFIG(qml_animation);
+
QT_BEGIN_NAMESPACE
class Q_QML_PRIVATE_EXPORT QContinuingAnimationGroupJob : public QAnimationGroupJob
diff --git a/src/qml/animations/qparallelanimationgroupjob_p.h b/src/qml/animations/qparallelanimationgroupjob_p.h
index 67ba626247..0265fe3274 100644
--- a/src/qml/animations/qparallelanimationgroupjob_p.h
+++ b/src/qml/animations/qparallelanimationgroupjob_p.h
@@ -53,6 +53,8 @@
#include "private/qanimationgroupjob_p.h"
+QT_REQUIRE_CONFIG(qml_animation);
+
QT_BEGIN_NAMESPACE
class Q_QML_PRIVATE_EXPORT QParallelAnimationGroupJob : public QAnimationGroupJob
diff --git a/src/qml/animations/qpauseanimationjob_p.h b/src/qml/animations/qpauseanimationjob_p.h
index d0e8d57fc7..6c9bbf0dab 100644
--- a/src/qml/animations/qpauseanimationjob_p.h
+++ b/src/qml/animations/qpauseanimationjob_p.h
@@ -53,6 +53,8 @@
#include <private/qanimationgroupjob_p.h>
+QT_REQUIRE_CONFIG(qml_animation);
+
QT_BEGIN_NAMESPACE
class Q_QML_PRIVATE_EXPORT QPauseAnimationJob : public QAbstractAnimationJob
diff --git a/src/qml/animations/qsequentialanimationgroupjob_p.h b/src/qml/animations/qsequentialanimationgroupjob_p.h
index 800f0c3b90..13f9806be1 100644
--- a/src/qml/animations/qsequentialanimationgroupjob_p.h
+++ b/src/qml/animations/qsequentialanimationgroupjob_p.h
@@ -53,6 +53,8 @@
#include <private/qanimationgroupjob_p.h>
+QT_REQUIRE_CONFIG(qml_animation);
+
QT_BEGIN_NAMESPACE
class QPauseAnimationJob;
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri
index 95096db51d..da3c173545 100644
--- a/src/qml/compiler/compiler.pri
+++ b/src/qml/compiler/compiler.pri
@@ -11,7 +11,8 @@ HEADERS += \
$$PWD/qv4codegen_p.h \
$$PWD/qqmlirbuilder_p.h \
$$PWD/qqmltypecompiler_p.h \
- $$PWD/qv4instr_moth_p.h
+ $$PWD/qv4instr_moth_p.h \
+ $$PWD/qv4bytecodehandler_p.h
SOURCES += \
$$PWD/qv4bytecodegenerator.cpp \
@@ -21,7 +22,8 @@ SOURCES += \
$$PWD/qv4compilerscanfunctions.cpp \
$$PWD/qv4codegen.cpp \
$$PWD/qqmlirbuilder.cpp \
- $$PWD/qv4instr_moth.cpp
+ $$PWD/qv4instr_moth.cpp \
+ $$PWD/qv4bytecodehandler.cpp
!qmldevtools_build {
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index 8a1b3744ee..883b21ab07 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -96,23 +96,24 @@ void Object::init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, cons
declarationsOverride = nullptr;
}
-QString Object::sanityCheckFunctionNames(const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation)
+QString IRBuilder::sanityCheckFunctionNames(Object *obj, const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation)
{
QSet<int> functionNames;
- for (Function *f = functions->first; f; f = f->next) {
- QQmlJS::AST::FunctionDeclaration *function = f->functionDeclaration;
- Q_ASSERT(function);
- *errorLocation = function->identifierToken;
+ for (auto functionit = obj->functionsBegin(); functionit != obj->functionsEnd(); ++functionit) {
+ Function *f = functionit.ptr;
+ errorLocation->startLine = f->location.line;
+ errorLocation->startColumn = f->location.column;
if (functionNames.contains(f->nameIndex))
return tr("Duplicate method name");
functionNames.insert(f->nameIndex);
- for (QmlIR::Signal *s = qmlSignals->first; s; s = s->next) {
+ for (auto signalit = obj->signalsBegin(); signalit != obj->signalsEnd(); ++signalit) {
+ QmlIR::Signal *s = signalit.ptr;
if (s->nameIndex == f->nameIndex)
return tr("Duplicate method name");
}
- const QString name = function->name.toString();
+ const QString name = stringAt(f->nameIndex);
if (name.at(0).isUpper())
return tr("Method names cannot begin with an upper case letter");
if (illegalNames.contains(name))
@@ -497,7 +498,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiObjectBinding *node)
bool IRBuilder::visit(QQmlJS::AST::UiScriptBinding *node)
{
- appendBinding(node->qualifiedId, node->statement);
+ appendBinding(node->qualifiedId, node->statement, node);
return false;
}
@@ -601,7 +602,7 @@ bool IRBuilder::defineQMLObject(int *objectIndex, QQmlJS::AST::UiQualifiedId *qu
return false;
QQmlJS::AST::SourceLocation loc;
- QString error = obj->sanityCheckFunctionNames(illegalNames, &loc);
+ QString error = sanityCheckFunctionNames(obj, illegalNames, &loc);
if (!error.isEmpty()) {
recordError(loc, error);
return false;
@@ -618,7 +619,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiImport *node)
if (!node->fileName.isNull()) {
uri = node->fileName.toString();
- if (uri.endsWith(QLatin1String(".js"))) {
+ if (uri.endsWith(QLatin1String(".js")) || uri.endsWith(QLatin1String(".mjs"))) {
import->type = QV4::CompiledData::Import::ImportScript;
} else {
import->type = QV4::CompiledData::Import::ImportFile;
@@ -690,9 +691,9 @@ bool IRBuilder::visit(QQmlJS::AST::UiPragma *node)
Pragma *pragma = New<Pragma>();
// For now the only valid pragma is Singleton, so lets validate the input
- if (!node->pragmaType->name.isNull())
+ if (!node->name.isNull())
{
- if (QLatin1String("Singleton") == node->pragmaType->name)
+ if (QLatin1String("Singleton") == node->name)
{
pragma->type = Pragma::PragmaSingleton;
} else {
@@ -957,7 +958,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiPublicMember *node)
QQmlJS::AST::Node::accept(node->binding, this);
} else if (node->statement) {
if (!isRedundantNullInitializerForPropertyDeclaration(_propertyDeclaration, node->statement))
- appendBinding(node->identifierToken, node->identifierToken, _propertyDeclaration->nameIndex, node->statement);
+ appendBinding(node->identifierToken, node->identifierToken, _propertyDeclaration->nameIndex, node->statement, node);
}
qSwap(_propertyDeclaration, property);
}
@@ -971,26 +972,27 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node)
if (QQmlJS::AST::FunctionDeclaration *funDecl = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration *>(node->sourceElement)) {
CompiledFunctionOrExpression *foe = New<CompiledFunctionOrExpression>();
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>();
- f->functionDeclaration = funDecl;
QQmlJS::AST::SourceLocation loc = funDecl->identifierToken;
f->location.line = loc.startLine;
f->location.column = loc.startColumn;
f->index = index;
f->nameIndex = registerString(funDecl->name.toString());
- int formalsCount = 0;
- for (QQmlJS::AST::FormalParameterList *it = funDecl->formals; it; it = it->next)
- ++formalsCount;
+ const QStringList formals = funDecl->formals ? funDecl->formals->formals() : QStringList();
+ int formalsCount = formals.size();
f->formals.allocate(pool, formalsCount);
int i = 0;
- for (QQmlJS::AST::FormalParameterList *it = funDecl->formals; it; it = it->next, ++i)
- f->formals[i] = registerString(it->name.toString());
+ for (const QString &arg : formals) {
+ f->formals[i] = registerString(arg);
+ ++i;
+ }
_object->appendFunction(f);
} else {
@@ -1044,7 +1046,7 @@ QStringRef IRBuilder::textRefAt(const QQmlJS::AST::SourceLocation &first, const
return QStringRef(&sourceCode, first.offset, last.offset + last.length - first.offset);
}
-void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement)
+void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, QQmlJS::AST::Node *parentNode)
{
QQmlJS::AST::SourceLocation loc = statement->firstSourceLocation();
binding->valueLocation.line = loc.startLine;
@@ -1067,7 +1069,7 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST
binding->value.b = false;
} else if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(expr)) {
binding->type = QV4::CompiledData::Binding::Type_Number;
- binding->setNumberValueInternal(lit->value);
+ binding->value.constantValueIndex = jsGenerator->registerConstant(QV4::Encode(lit->value));
} else if (QQmlJS::AST::CallExpression *call = QQmlJS::AST::cast<QQmlJS::AST::CallExpression *>(expr)) {
if (QQmlJS::AST::IdentifierExpression *base = QQmlJS::AST::cast<QQmlJS::AST::IdentifierExpression *>(call->base)) {
tryGeneratingTranslationBinding(base->name, call->arguments, binding);
@@ -1079,7 +1081,7 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST
} else if (QQmlJS::AST::UnaryMinusExpression *unaryMinus = QQmlJS::AST::cast<QQmlJS::AST::UnaryMinusExpression *>(expr)) {
if (QQmlJS::AST::NumericLiteral *lit = QQmlJS::AST::cast<QQmlJS::AST::NumericLiteral *>(unaryMinus->expression)) {
binding->type = QV4::CompiledData::Binding::Type_Number;
- binding->setNumberValueInternal(-lit->value);
+ binding->value.constantValueIndex = jsGenerator->registerConstant(QV4::Encode(-lit->value));
}
}
}
@@ -1090,6 +1092,7 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST
CompiledFunctionOrExpression *expr = New<CompiledFunctionOrExpression>();
expr->node = statement;
+ expr->parentNode = parentNode;
expr->nameIndex = registerString(QLatin1String("expression for ")
+ stringAt(binding->propertyNameIndex));
expr->disableAcceleratedLookups = false;
@@ -1117,6 +1120,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg
} else {
return; // first argument is not a string, stop
}
+ translationData.stringIndex = jsGenerator->registerString(translation.toString());
args = args->next;
@@ -1141,8 +1145,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg
return; // too many arguments, stop
binding->type = QV4::CompiledData::Binding::Type_Translation;
- binding->stringIndex = jsGenerator->registerString(translation.toString());
- binding->value.translationData = translationData;
+ binding->value.translationDataIndex = jsGenerator->registerTranslation(translationData);
} else if (base == QLatin1String("qsTrId")) {
QV4::CompiledData::TranslationData translationData;
translationData.number = -1;
@@ -1157,6 +1160,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg
} else {
return; // first argument is not a string, stop
}
+ translationData.stringIndex = jsGenerator->registerString(id.toString());
args = args->next;
@@ -1173,8 +1177,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg
return; // too many arguments, stop
binding->type = QV4::CompiledData::Binding::Type_TranslationById;
- binding->stringIndex = jsGenerator->registerString(id.toString());
- binding->value.translationData = translationData;
+ binding->value.translationDataIndex = jsGenerator->registerTranslation(translationData);
} else if (base == QLatin1String("QT_TR_NOOP") || base == QLatin1String("QT_TRID_NOOP")) {
if (!args || !args->expression)
return; // no arguments, stop
@@ -1216,7 +1219,7 @@ void IRBuilder::tryGeneratingTranslationBinding(const QStringRef &base, AST::Arg
}
}
-void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value)
+void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value, QQmlJS::AST::Node *parentNode)
{
const QQmlJS::AST::SourceLocation qualifiedNameLocation = name->identifierToken;
Object *object = nullptr;
@@ -1227,7 +1230,7 @@ void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Sta
return;
}
qSwap(_object, object);
- appendBinding(qualifiedNameLocation, name->identifierToken, registerString(name->name.toString()), value);
+ appendBinding(qualifiedNameLocation, name->identifierToken, registerString(name->name.toString()), value, parentNode);
qSwap(_object, object);
}
@@ -1242,7 +1245,8 @@ void IRBuilder::appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex,
qSwap(_object, object);
}
-void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value)
+void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex,
+ QQmlJS::AST::Statement *value, QQmlJS::AST::Node *parentNode)
{
Binding *binding = New<Binding>();
binding->propertyNameIndex = propertyNameIndex;
@@ -1250,7 +1254,7 @@ void IRBuilder::appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLo
binding->location.line = nameLocation.startLine;
binding->location.column = nameLocation.startColumn;
binding->flags = 0;
- setBindingValue(binding, value);
+ setBindingValue(binding, value, parentNode);
QString error = bindingsTarget()->appendBinding(binding, /*isListBinding*/false);
if (!error.isEmpty()) {
recordError(qualifiedNameLocation, error);
@@ -1547,68 +1551,105 @@ bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *prope
return QQmlJS::AST::cast<QQmlJS::AST::NullExpression *>(expr);
}
-QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
+void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher)
{
+ output.jsGenerator.stringTable.registerString(output.jsModule.fileName);
+ output.jsGenerator.stringTable.registerString(output.jsModule.finalUrl);
+
QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = output.javaScriptCompilationUnit;
- QV4::CompiledData::Unit *jsUnit = compilationUnit->createUnitData(&output);
- const uint unitSize = jsUnit->unitSize;
- const int importSize = sizeof(QV4::CompiledData::Import) * output.imports.count();
- const int objectOffsetTableSize = output.objects.count() * sizeof(quint32);
+ const QV4::CompiledData::Unit *jsUnit = nullptr;
+ std::function<QV4::CompiledData::QmlUnit *(QV4::CompiledData::QmlUnit *, uint)> unitFinalizer
+ = [](QV4::CompiledData::QmlUnit *unit, uint) {
+ return unit;
+ };
+
+ // We may already have unit data if we're loading an ahead-of-time generated cache file.
+ if (compilationUnit->data) {
+ jsUnit = compilationUnit->data;
+#ifndef V4_BOOTSTRAP
+ output.javaScriptCompilationUnit->dynamicStrings = output.jsGenerator.stringTable.allStrings();
+#endif
+ } else {
+ QV4::CompiledData::Unit *createdUnit;
+ jsUnit = createdUnit = output.jsGenerator.generateUnit();
+
+ // enable flag if we encountered pragma Singleton
+ for (Pragma *p : qAsConst(output.pragmas)) {
+ if (p->type == Pragma::PragmaSingleton) {
+ createdUnit->flags |= QV4::CompiledData::Unit::IsSingleton;
+ break;
+ }
+ }
+ // This unit's memory was allocated with malloc on the heap, so it's
+ // definitely not suitable for StaticData access.
+ createdUnit->flags &= ~QV4::CompiledData::Unit::StaticData;
+
+#ifndef V4_BOOTSTRAP
+ if (dependencyHasher) {
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ if (dependencyHasher(&hash)) {
+ QByteArray checksum = hash.result();
+ Q_ASSERT(checksum.size() == sizeof(createdUnit->dependencyMD5Checksum));
+ memcpy(createdUnit->dependencyMD5Checksum, checksum.constData(), sizeof(createdUnit->dependencyMD5Checksum));
+ }
+ }
+#else
+ Q_UNUSED(dependencyHasher);
+#endif
+ createdUnit->sourceFileIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.fileName);
+ createdUnit->finalUrlIndex = output.jsGenerator.stringTable.getStringId(output.jsModule.finalUrl);
+
+ // Combine the qml data into the general unit data.
+ unitFinalizer = [&jsUnit](QV4::CompiledData::QmlUnit *qmlUnit, uint qmlDataSize) {
+ void *ptr = const_cast<QV4::CompiledData::Unit*>(jsUnit);
+ QV4::CompiledData::Unit *newUnit = (QV4::CompiledData::Unit *)realloc(ptr, jsUnit->unitSize + qmlDataSize);
+ jsUnit = newUnit;
+ newUnit->offsetToQmlUnit = newUnit->unitSize;
+ newUnit->unitSize += qmlDataSize;
+ memcpy(const_cast<QV4::CompiledData::QmlUnit *>(newUnit->qmlUnit()), qmlUnit, qmlDataSize);
+ free(const_cast<QV4::CompiledData::QmlUnit*>(qmlUnit));
+ qmlUnit = nullptr;
+ newUnit->generateChecksum();
+ return const_cast<QV4::CompiledData::QmlUnit*>(newUnit->qmlUnit());
+ };
+ }
+
+ // No more new strings after this point, we're calculating offsets.
+ output.jsGenerator.stringTable.freeze();
+
+ const uint importSize = sizeof(QV4::CompiledData::Import) * output.imports.count();
+ const uint objectOffsetTableSize = output.objects.count() * sizeof(quint32);
QHash<const Object*, quint32> objectOffsets;
- int objectsSize = 0;
+ const unsigned int objectOffset = sizeof(QV4::CompiledData::QmlUnit) + importSize;
+ uint nextOffset = objectOffset + objectOffsetTableSize;
for (Object *o : qAsConst(output.objects)) {
- objectOffsets.insert(o, unitSize + importSize + objectOffsetTableSize + objectsSize);
- objectsSize += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count);
+ objectOffsets.insert(o, nextOffset);
+ nextOffset += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count);
int signalTableSize = 0;
for (const Signal *s = o->firstSignal(); s; s = s->next)
signalTableSize += QV4::CompiledData::Signal::calculateSize(s->parameters->count);
- objectsSize += signalTableSize;
+ nextOffset += signalTableSize;
int enumTableSize = 0;
for (const Enum *e = o->firstEnum(); e; e = e->next)
enumTableSize += QV4::CompiledData::Enum::calculateSize(e->enumValues->count);
- objectsSize += enumTableSize;
+ nextOffset += enumTableSize;
}
- const int totalSize = unitSize + importSize + objectOffsetTableSize + objectsSize + output.jsGenerator.stringTable.sizeOfTableAndData();
+ const uint totalSize = nextOffset;
char *data = (char*)malloc(totalSize);
- memcpy(data, jsUnit, unitSize);
- memset(data + unitSize, 0, totalSize - unitSize);
- if (jsUnit != compilationUnit->data)
- free(jsUnit);
- jsUnit = nullptr;
-
- QV4::CompiledData::Unit *qmlUnit = reinterpret_cast<QV4::CompiledData::Unit *>(data);
- qmlUnit->unitSize = totalSize;
- qmlUnit->flags |= QV4::CompiledData::Unit::IsQml;
- // This unit's memory was allocated with malloc on the heap, so it's
- // definitely not suitable for StaticData access.
- qmlUnit->flags &= ~QV4::CompiledData::Unit::StaticData;
- qmlUnit->offsetToImports = unitSize;
+ memset(data, 0, totalSize);
+ QV4::CompiledData::QmlUnit *qmlUnit = reinterpret_cast<QV4::CompiledData::QmlUnit *>(data);
+ qmlUnit->offsetToImports = sizeof(*qmlUnit);
qmlUnit->nImports = output.imports.count();
- qmlUnit->offsetToObjects = unitSize + importSize;
+ qmlUnit->offsetToObjects = objectOffset;
qmlUnit->nObjects = output.objects.count();
- qmlUnit->offsetToStringTable = totalSize - output.jsGenerator.stringTable.sizeOfTableAndData();
- qmlUnit->stringTableSize = output.jsGenerator.stringTable.stringCount();
-
-#ifndef V4_BOOTSTRAP
- if (dependencyHasher) {
- QCryptographicHash hash(QCryptographicHash::Md5);
- if (dependencyHasher(&hash)) {
- QByteArray checksum = hash.result();
- Q_ASSERT(checksum.size() == sizeof(qmlUnit->dependencyMD5Checksum));
- memcpy(qmlUnit->dependencyMD5Checksum, checksum.constData(), sizeof(qmlUnit->dependencyMD5Checksum));
- }
- }
-#else
- Q_UNUSED(dependencyHasher);
-#endif
// write imports
char *importPtr = data + qmlUnit->offsetToImports;
@@ -1620,9 +1661,9 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4:
// write objects
quint32_le *objectTable = reinterpret_cast<quint32_le*>(data + qmlUnit->offsetToObjects);
- char *objectPtr = data + qmlUnit->offsetToObjects + objectOffsetTableSize;
for (int i = 0; i < output.objects.count(); ++i) {
const Object *o = output.objects.at(i);
+ char * const objectPtr = data + objectOffsets.value(o);
*objectTable++ = objectOffsets.value(o);
QV4::CompiledData::Object *objectToWrite = reinterpret_cast<QV4::CompiledData::Object*>(objectPtr);
@@ -1736,25 +1777,33 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4:
for (int i = 0; i < o->namedObjectsInComponent.count; ++i) {
*namedObjectInComponentPtr++ = o->namedObjectsInComponent.at(i);
}
-
- objectPtr += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count);
- objectPtr += signalTableSize;
- objectPtr += enumTableSize;
}
- // enable flag if we encountered pragma Singleton
- for (Pragma *p : qAsConst(output.pragmas)) {
- if (p->type == Pragma::PragmaSingleton) {
- qmlUnit->flags |= QV4::CompiledData::Unit::IsSingleton;
- break;
- }
- }
+ qmlUnit = unitFinalizer(qmlUnit, totalSize);
- output.jsGenerator.stringTable.serialize(qmlUnit);
-
- qmlUnit->generateChecksum();
+ static const bool showStats = qEnvironmentVariableIsSet("QML_SHOW_UNIT_STATS");
+ if (showStats) {
+ qDebug() << "Generated QML unit that is" << totalSize << "bytes big contains:";
+ qDebug() << " " << jsUnit->functionTableSize << "functions";
+ qDebug() << " " << jsUnit->unitSize << "for JS unit";
+ qDebug() << " " << importSize << "for imports";
+ qDebug() << " " << nextOffset - objectOffset - objectOffsetTableSize << "for" << qmlUnit->nObjects << "objects";
+ quint32 totalBindingCount = 0;
+ for (quint32 i = 0; i < qmlUnit->nObjects; ++i)
+ totalBindingCount += qmlUnit->objectAt(i)->nBindings;
+ qDebug() << " " << totalBindingCount << "bindings";
+ quint32 totalCodeSize = 0;
+ for (quint32 i = 0; i < jsUnit->functionTableSize; ++i)
+ totalCodeSize += jsUnit->functionAt(i)->codeSize;
+ qDebug() << " " << totalCodeSize << "bytes total byte code";
+ qDebug() << " " << jsUnit->stringTableSize << "strings";
+ quint32 totalStringSize = 0;
+ for (quint32 i = 0; i < jsUnit->stringTableSize; ++i)
+ totalStringSize += QV4::CompiledData::String::calculateSize(jsUnit->stringAtInternal(i));
+ qDebug() << " " << totalStringSize << "bytes total strings";
+ }
- return qmlUnit;
+ compilationUnit->setUnitData(jsUnit, qmlUnit, output.jsModule.fileName, output.jsModule.finalUrl);
}
char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, BindingFilter filter) const
@@ -1806,24 +1855,36 @@ void JSCodeGen::beginObjectScope(QQmlPropertyCache *scopeObject)
QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<CompiledFunctionOrExpression> &functions)
{
+ auto qmlName = [&](const CompiledFunctionOrExpression &c) {
+ if (c.nameIndex != 0)
+ return stringPool->stringForIndex(c.nameIndex);
+ else
+ return QStringLiteral("%qml-expression-entry");
+ };
QVector<int> runtimeFunctionIndices(functions.size());
- QV4::Compiler::ScanFunctions scan(this, sourceCode, QV4::Compiler::GlobalCode);
- scan.enterGlobalEnvironment(QV4::Compiler::QmlBinding);
+ QV4::Compiler::ScanFunctions scan(this, sourceCode, QV4::Compiler::ContextType::Global);
+ scan.enterGlobalEnvironment(QV4::Compiler::ContextType::Binding);
for (const CompiledFunctionOrExpression &f : functions) {
Q_ASSERT(f.node != qmlRoot);
+ Q_ASSERT(f.parentNode && f.parentNode != qmlRoot);
QQmlJS::AST::FunctionDeclaration *function = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(f.node);
- if (function)
+ if (function) {
scan.enterQmlFunction(function);
- else
- scan.enterEnvironment(f.node, QV4::Compiler::QmlBinding);
+ } else {
+ Q_ASSERT(f.node != f.parentNode);
+ scan.enterEnvironment(f.parentNode, QV4::Compiler::ContextType::Binding, qmlName(f));
+ }
scan(function ? function->body : f.node);
scan.leaveEnvironment();
}
scan.leaveEnvironment();
+ if (hasError)
+ return QVector<int>();
+
_context = nullptr;
for (int i = 0; i < functions.count(); ++i) {
@@ -1836,15 +1897,13 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
QString name;
if (function)
name = function->name.toString();
- else if (qmlFunction.nameIndex != 0)
- name = stringPool->stringForIndex(qmlFunction.nameIndex);
else
- name = QStringLiteral("%qml-expression-entry");
+ name = qmlName(qmlFunction);
- QQmlJS::AST::SourceElements *body;
- if (function)
- body = function->body ? function->body->elements : nullptr;
- else {
+ QQmlJS::AST::StatementList *body;
+ if (function) {
+ body = function->body;
+ } else {
// Synthesize source elements.
QQmlJS::MemoryPool *pool = jsEngine->pool();
@@ -1854,13 +1913,12 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
QQmlJS::AST::ExpressionNode *expr = node->expressionCast();
stmt = new (pool) QQmlJS::AST::ExpressionStatement(expr);
}
- QQmlJS::AST::SourceElement *element = new (pool) QQmlJS::AST::StatementSourceElement(stmt);
- body = new (pool) QQmlJS::AST::SourceElements(element);
+ body = new (pool) QQmlJS::AST::StatementList(stmt);
body = body->finish();
}
_disableAcceleratedLookups = qmlFunction.disableAcceleratedLookups;
- int idx = defineFunction(name, node,
+ int idx = defineFunction(name, function ? function : qmlFunction.parentNode,
function ? function->formals : nullptr,
body);
runtimeFunctionIndices[i] = idx;
@@ -1869,7 +1927,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil
return runtimeFunctionIndices;
}
-int JSCodeGen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::SourceElements *body)
+int JSCodeGen::defineFunction(const QString &name, AST::Node *ast, AST::FormalParameterList *formals, AST::StatementList *body)
{
int qmlContextTemp = -1;
int importedScriptsTemp = -1;
@@ -2190,7 +2248,7 @@ QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &n
// Look for IDs first.
for (const IdMapping &mapping : qAsConst(_idObjects)) {
if (name == mapping.name) {
- if (_context->compilationMode == QV4::Compiler::QmlBinding)
+ if (_context->contextType == QV4::Compiler::ContextType::Binding)
_context->idObjectDependencies.insert(mapping.idIndex);
Instruction::LoadIdObject load;
@@ -2221,42 +2279,38 @@ QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &n
if (_scopeObject) {
QQmlPropertyData *data = lookupQmlCompliantProperty(_scopeObject, name);
- if (!data)
- 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 (data) {
+ 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)
- 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);
+ if (data) {
+ 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);
+ }
}
- if (m_globalNames.contains(name)) {
- Reference r = Reference::fromName(this, name);
+ Reference r = Reference::fromName(this, name);
+ if (m_globalNames.contains(name))
r.global = true;
- return r;
- }
+ return r;
#else
Q_UNUSED(name)
-#endif // V4_BOOTSTRAP
- // fall back to name lookup at run-time.
return Reference();
+#endif // V4_BOOTSTRAP
}
#ifndef V4_BOOTSTRAP
@@ -2317,12 +2371,12 @@ IRLoader::IRLoader(const QV4::CompiledData::Unit *qmlData, QmlIR::Document *outp
void IRLoader::load()
{
- output->jsGenerator.stringTable.clear();
- for (uint i = 0; i < unit->stringTableSize; ++i)
- output->jsGenerator.stringTable.registerString(unit->stringAt(i));
+ output->jsGenerator.stringTable.initializeFromBackingUnit(unit);
+
+ const QV4::CompiledData::QmlUnit *qmlUnit = unit->qmlUnit();
- for (quint32 i = 0; i < unit->nImports; ++i)
- output->imports << unit->importAt(i);
+ for (quint32 i = 0; i < qmlUnit->nImports; ++i)
+ output->imports << qmlUnit->importAt(i);
if (unit->flags & QV4::CompiledData::Unit::IsSingleton) {
QmlIR::Pragma *p = New<QmlIR::Pragma>();
@@ -2331,8 +2385,8 @@ void IRLoader::load()
output->pragmas << p;
}
- for (uint i = 0; i < unit->nObjects; ++i) {
- const QV4::CompiledData::Object *serializedObject = unit->objectAt(i);
+ for (uint i = 0; i < qmlUnit->nObjects; ++i) {
+ const QV4::CompiledData::Object *serializedObject = qmlUnit->objectAt(i);
QmlIR::Object *object = loadObject(serializedObject);
output->objects.append(object);
}
@@ -2446,8 +2500,6 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO
}
}
- QQmlJS::Engine *jsParserEngine = &output->jsParserEngine;
-
const quint32_le *functionIdx = serializedObject->functionOffsetTable();
for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) {
QmlIR::Function *f = pool->New<QmlIR::Function>();
@@ -2458,26 +2510,10 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO
f->location = compiledFunction->location;
f->nameIndex = compiledFunction->nameIndex;
- QQmlJS::AST::FormalParameterList *paramList = nullptr;
- const quint32_le *formalNameIdx = compiledFunction->formalsTable();
- for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) {
- const QString formal = unit->stringAt(*formalNameIdx);
- QStringRef paramNameRef = jsParserEngine->newStringRef(formal);
-
- if (paramList)
- paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, paramNameRef);
- else
- paramList = new (pool) QQmlJS::AST::FormalParameterList(paramNameRef);
- }
-
- if (paramList)
- paramList = paramList->finish();
-
- const QString name = unit->stringAt(compiledFunction->nameIndex);
- f->functionDeclaration = new(pool) QQmlJS::AST::FunctionDeclaration(jsParserEngine->newStringRef(name), paramList, /*body*/nullptr);
+ const QString name = unit->stringAtInternal(compiledFunction->nameIndex);
f->formals.allocate(pool, int(compiledFunction->nFormals));
- formalNameIdx = compiledFunction->formalsTable();
+ const quint32_le *formalNameIdx = compiledFunction->formalsTable();
for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx)
f->formals[i] = *formalNameIdx;
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index 689b232b1c..d1e2b17bb7 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -57,7 +57,6 @@
#include <private/qqmljsmemorypool_p.h>
#include <private/qv4codegen_p.h>
#include <private/qv4compiler_p.h>
-#include <private/qqmljslexer_p.h>
#include <QTextStream>
#include <QCoreApplication>
@@ -325,7 +324,6 @@ struct Alias : public QV4::CompiledData::Alias
struct Function
{
- QQmlJS::AST::FunctionDeclaration *functionDeclaration;
QV4::CompiledData::Location location;
int nameIndex;
quint32 index; // index in parsedQML::functions
@@ -342,12 +340,9 @@ struct Function
struct Q_QML_PRIVATE_EXPORT CompiledFunctionOrExpression
{
CompiledFunctionOrExpression()
-
{}
- CompiledFunctionOrExpression(QQmlJS::AST::Node *n)
- : node(n)
- {}
+ QQmlJS::AST::Node *parentNode = nullptr; // FunctionDeclaration, Statement or Expression
QQmlJS::AST::Node *node = nullptr; // FunctionDeclaration, Statement or Expression
quint32 nameIndex = 0;
bool disableAcceleratedLookups = false;
@@ -400,8 +395,6 @@ public:
void init(QQmlJS::MemoryPool *pool, int typeNameIndex, int idIndex, const QQmlJS::AST::SourceLocation &location = QQmlJS::AST::SourceLocation());
- QString sanityCheckFunctionNames(const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation);
-
QString appendEnum(Enum *enumeration);
QString appendSignal(Signal *signal);
QString appendProperty(Property *prop, const QString &propertyName, bool isDefaultProperty, const QQmlJS::AST::SourceLocation &defaultToken, QQmlJS::AST::SourceLocation *errorLocation);
@@ -520,12 +513,12 @@ public:
QStringRef textRefAt(const QQmlJS::AST::SourceLocation &first,
const QQmlJS::AST::SourceLocation &last) const;
- void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement);
+ void setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST::Statement *statement, AST::Node *parentNode);
void tryGeneratingTranslationBinding(const QStringRef &base, QQmlJS::AST::ArgumentList *args, QV4::CompiledData::Binding *binding);
- void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value);
+ void appendBinding(QQmlJS::AST::UiQualifiedId *name, QQmlJS::AST::Statement *value, AST::Node *parentNode);
void appendBinding(QQmlJS::AST::UiQualifiedId *name, int objectIndex, bool isOnAssignment = false);
- void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value);
+ void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, QQmlJS::AST::Statement *value, AST::Node *parentNode);
void appendBinding(const QQmlJS::AST::SourceLocation &qualifiedNameLocation, const QQmlJS::AST::SourceLocation &nameLocation, quint32 propertyNameIndex, int objectIndex, bool isListItem = false, bool isOnAssignment = false);
bool appendAlias(QQmlJS::AST::UiPublicMember *node);
@@ -548,6 +541,8 @@ public:
static bool isStatementNodeScript(QQmlJS::AST::Statement *statement);
static bool isRedundantNullInitializerForPropertyDeclaration(Property *property, QQmlJS::AST::Statement *statement);
+ QString sanityCheckFunctionNames(Object *obj, const QSet<QString> &illegalNames, QQmlJS::AST::SourceLocation *errorLocation);
+
QList<QQmlJS::DiagnosticMessage> errors;
QSet<QString> illegalNames;
@@ -568,7 +563,7 @@ public:
struct Q_QML_PRIVATE_EXPORT QmlUnitGenerator
{
- QV4::CompiledData::Unit *generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher = QV4::CompiledData::DependentTypesHasher());
+ void generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher = QV4::CompiledData::DependentTypesHasher());
private:
typedef bool (Binding::*BindingFilter)() const;
@@ -578,7 +573,7 @@ private:
#ifndef V4_BOOTSTRAP
struct Q_QML_EXPORT PropertyResolver
{
- PropertyResolver(const QQmlPropertyCache *cache)
+ PropertyResolver(const QQmlRefPointer<QQmlPropertyCache> &cache)
: cache(cache)
{}
@@ -597,7 +592,7 @@ struct Q_QML_EXPORT PropertyResolver
// This code must match the semantics of QQmlPropertyPrivate::findSignalByName
QQmlPropertyData *signal(const QString &name, bool *notInRevision) const;
- const QQmlPropertyCache *cache;
+ QQmlRefPointer<QQmlPropertyCache> cache;
};
#endif
@@ -623,7 +618,7 @@ struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen
int defineFunction(const QString &name, AST::Node *ast,
AST::FormalParameterList *formals,
- AST::SourceElements *body) override;
+ AST::StatementList *body) override;
protected:
void beginFunctionBodyHook() override;
diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h
index 8bbc8291b4..02517ea6bb 100644
--- a/src/qml/compiler/qqmlpropertycachecreator_p.h
+++ b/src/qml/compiler/qqmlpropertycachecreator_p.h
@@ -99,8 +99,8 @@ public:
protected:
QQmlCompileError buildMetaObjectRecursively(int objectIndex, const QQmlBindingInstantiationContext &context);
- QQmlPropertyCache *propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const;
- QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, QQmlPropertyCache *baseTypeCache);
+ QQmlRefPointer<QQmlPropertyCache> propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const;
+ QQmlCompileError createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache);
QString stringAt(int index) const { return objectContainer->stringAt(index); }
@@ -152,7 +152,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje
const CompiledObject *obj = objectContainer->objectAt(context.referencingObjectIndex);
auto *typeRef = objectContainer->resolvedTypes.value(obj->inheritedTypeNameIndex);
Q_ASSERT(typeRef);
- QQmlPropertyCache *baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
+ QQmlRefPointer<QQmlPropertyCache> baseTypeCache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
QQmlCompileError error = createMetaObject(context.referencingObjectIndex, obj, baseTypeCache);
if (error.isSet())
return error;
@@ -166,7 +166,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje
}
}
- QQmlPropertyCache *baseTypeCache;
+ QQmlRefPointer<QQmlPropertyCache> baseTypeCache;
{
QQmlCompileError error;
baseTypeCache = propertyCacheForObject(obj, context, &error);
@@ -209,7 +209,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::buildMetaObje
}
template <typename ObjectContainer>
-inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const
+inline QQmlRefPointer<QQmlPropertyCache> QQmlPropertyCacheCreator<ObjectContainer>::propertyCacheForObject(const CompiledObject *obj, const QQmlBindingInstantiationContext &context, QQmlCompileError *error) const
{
if (context.instantiatingProperty) {
return context.instantiatingPropertyCache(enginePrivate);
@@ -241,14 +241,12 @@ inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCac
QString propertyName = stringAt(context.instantiatingBinding->propertyNameIndex);
if (imports->resolveType(propertyName, &qmltype, nullptr, nullptr, nullptr)) {
if (qmltype.isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
auto compilationUnit = tdata->compilationUnit();
qmltype = QQmlMetaType::qmlType(compilationUnit->metaTypeId);
-
- tdata->release();
}
}
}
@@ -264,7 +262,7 @@ inline QQmlPropertyCache *QQmlPropertyCacheCreator<ObjectContainer>::propertyCac
}
template <typename ObjectContainer>
-inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, QQmlPropertyCache *baseTypeCache)
+inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObject(int objectIndex, const CompiledObject *obj, const QQmlRefPointer<QQmlPropertyCache> &baseTypeCache)
{
QQmlRefPointer<QQmlPropertyCache> cache;
cache.adopt(baseTypeCache->copyAndReserve(obj->propertyCount() + obj->aliasCount(),
@@ -314,7 +312,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
}
}
if (newClassName.isEmpty()) {
- newClassName = QQmlMetaObject(baseTypeCache).className();
+ newClassName = QQmlMetaObject(baseTypeCache.data()).className();
newClassName.append("_QML_");
newClassName.append(QByteArray::number(classIndexCounter.fetchAndAddRelaxed(1)));
}
@@ -354,7 +352,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
// and throw an error if there is a signal/method defined as an override.
QSet<QString> seenSignals;
seenSignals << QStringLiteral("destroyed") << QStringLiteral("parentChanged") << QStringLiteral("objectNameChanged");
- QQmlPropertyCache *parentCache = cache;
+ QQmlPropertyCache *parentCache = cache.data();
while ((parentCache = parentCache->parent())) {
if (int pSigCount = parentCache->signalCount()) {
int pSigOffset = parentCache->signalOffset();
@@ -440,15 +438,13 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
return QQmlCompileError(s->location, QQmlPropertyCacheCreatorBase::tr("Invalid signal parameter type: %1").arg(customTypeName));
if (qmltype.isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
auto compilationUnit = tdata->compilationUnit();
paramTypes[i + 1] = compilationUnit->metaTypeId;
-
- tdata->release();
} else {
paramTypes[i + 1] = qmltype.typeId();
}
@@ -523,7 +519,7 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
Q_ASSERT(qmltype.isValid());
if (qmltype.isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(qmltype.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
@@ -534,8 +530,6 @@ inline QQmlCompileError QQmlPropertyCacheCreator<ObjectContainer>::createMetaObj
} else {
propertyType = compilationUnit->listMetaTypeId;
}
-
- tdata->release();
} else {
if (p->type == QV4::CompiledData::Property::Custom) {
propertyType = qmltype.typeId();
diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp
index ffd3b5975a..4cb922ca8d 100644
--- a/src/qml/compiler/qqmlpropertyvalidator.cpp
+++ b/src/qml/compiler/qqmlpropertyvalidator.cpp
@@ -45,15 +45,16 @@
QT_BEGIN_NAMESPACE
-QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit)
+QQmlPropertyValidator::QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
: enginePrivate(enginePrivate)
+ , compilationUnit(compilationUnit)
, imports(imports)
- , qmlUnit(compilationUnit->data)
+ , qmlUnit(compilationUnit->unitData())
, resolvedTypes(compilationUnit->resolvedTypes)
, propertyCaches(compilationUnit->propertyCaches)
, bindingPropertyDataPerObject(&compilationUnit->bindingPropertyDataPerObject)
{
- bindingPropertyDataPerObject->resize(qmlUnit->nObjects);
+ bindingPropertyDataPerObject->resize(compilationUnit->objectCount());
}
QVector<QQmlCompileError> QQmlPropertyValidator::validate()
@@ -81,7 +82,7 @@ struct BindingFinder
QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex, const QV4::CompiledData::Binding *instantiatingBinding, bool populatingValueTypeGroupProperty) const
{
- const QV4::CompiledData::Object *obj = qmlUnit->objectAt(objectIndex);
+ const QV4::CompiledData::Object *obj = compilationUnit->objectAt(objectIndex);
if (obj->flags & QV4::CompiledData::Object::IsComponent) {
Q_ASSERT(obj->nBindings == 1);
@@ -305,7 +306,7 @@ QVector<QQmlCompileError> QQmlPropertyValidator::validateObject(int objectIndex,
customParser->validator = this;
customParser->engine = enginePrivate;
customParser->imports = &imports;
- customParser->verifyBindings(qmlUnit, customBindings);
+ customParser->verifyBindings(compilationUnit, customBindings);
customParser->validator = nullptr;
customParser->engine = nullptr;
customParser->imports = (QQmlImports*)nullptr;
@@ -332,7 +333,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
if (binding->flags & QV4::CompiledData::Binding::IsResolvedEnum)
return noError;
- QString value = binding->valueAsString(qmlUnit);
+ QString value = binding->valueAsString(compilationUnit.data());
QMetaProperty p = propertyCache->firstCppMetaObject()->property(property->coreIndex());
bool ok;
if (p.isFlagType()) {
@@ -375,7 +376,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::UInt: {
if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- double d = binding->valueAsNumber();
+ double d = binding->valueAsNumber(compilationUnit->constants);
if (double(uint(d)) == d)
return noError;
}
@@ -384,7 +385,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::Int: {
if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- double d = binding->valueAsNumber();
+ double d = binding->valueAsNumber(compilationUnit->constants);
if (double(int(d)) == d)
return noError;
}
@@ -405,7 +406,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::Color: {
bool ok = false;
- QQmlStringConverters::rgbaFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::rgbaFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: color expected"));
}
@@ -414,7 +415,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
#if QT_CONFIG(datestring)
case QVariant::Date: {
bool ok = false;
- QQmlStringConverters::dateFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::dateFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: date expected"));
}
@@ -422,7 +423,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::Time: {
bool ok = false;
- QQmlStringConverters::timeFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::timeFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: time expected"));
}
@@ -430,7 +431,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::DateTime: {
bool ok = false;
- QQmlStringConverters::dateTimeFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::dateTimeFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: datetime expected"));
}
@@ -439,7 +440,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
#endif // datestring
case QVariant::Point: {
bool ok = false;
- QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected"));
}
@@ -447,7 +448,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::PointF: {
bool ok = false;
- QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected"));
}
@@ -455,7 +456,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::Size: {
bool ok = false;
- QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: size expected"));
}
@@ -463,7 +464,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::SizeF: {
bool ok = false;
- QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: size expected"));
}
@@ -471,7 +472,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::Rect: {
bool ok = false;
- QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: rect expected"));
}
@@ -479,7 +480,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
break;
case QVariant::RectF: {
bool ok = false;
- QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok);
+ QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok);
if (!ok) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected"));
}
@@ -496,7 +497,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
float xp;
float yp;
} vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
+ if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 2D vector expected"));
}
}
@@ -507,7 +508,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
float yp;
float zy;
} vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
+ if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 3D vector expected"));
}
}
@@ -519,7 +520,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
float zy;
float wp;
} vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
+ if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 4D vector expected"));
}
}
@@ -531,7 +532,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
float yp;
float zp;
} vec;
- if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(qmlUnit), &vec, sizeof(vec))) {
+ if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: quaternion expected"));
}
}
@@ -548,7 +549,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache
} else if (property->propType() == qMetaTypeId<QList<int> >()) {
bool ok = (binding->type == QV4::CompiledData::Binding::Type_Number);
if (ok) {
- double n = binding->valueAsNumber();
+ double n = binding->valueAsNumber(compilationUnit->constants);
if (double(int(n)) != n)
ok = false;
}
@@ -627,9 +628,9 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
bool isValueSource = false;
bool isPropertyInterceptor = false;
- const QV4::CompiledData::Object *targetObject = qmlUnit->objectAt(binding->value.objectIndex);
+ const QV4::CompiledData::Object *targetObject = compilationUnit->objectAt(binding->value.objectIndex);
if (auto *typeRef = resolvedTypes.value(targetObject->inheritedTypeNameIndex)) {
- QQmlPropertyCache *cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
+ QQmlRefPointer<QQmlPropertyCache> cache = typeRef->createPropertyCache(QQmlEnginePrivate::get(enginePrivate));
const QMetaObject *mo = cache->firstCppMetaObject();
QQmlType qmlType;
while (mo && !qmlType.isValid()) {
@@ -665,12 +666,12 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
}
}
return noError;
- } else if (qmlUnit->objectAt(binding->value.objectIndex)->flags & QV4::CompiledData::Object::IsComponent) {
+ } else if (compilationUnit->objectAt(binding->value.objectIndex)->flags & QV4::CompiledData::Object::IsComponent) {
return noError;
} 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"));
+ return QQmlCompileError(binding->location, tr("Unexpected object assignment for property \"%1\"").arg(propertyName));
} else if (property->propType() == qMetaTypeId<QQmlScriptString>()) {
return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: script expected"));
} else {
@@ -680,7 +681,7 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
// Using -1 for the minor version ensures that we get the raw metaObject.
QQmlPropertyCache *propertyMetaObject = enginePrivate->rawPropertyCacheForType(property->propType(), -1);
- // Will be true if the assgned type inherits propertyMetaObject
+ // Will be true if the assigned type inherits propertyMetaObject
bool isAssignable = false;
// Determine isAssignable value
if (propertyMetaObject) {
@@ -692,7 +693,8 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData *
}
if (!isAssignable) {
- return QQmlCompileError(binding->valueLocation, tr("Cannot assign object to property"));
+ return QQmlCompileError(binding->valueLocation, tr("Cannot assign object of type \"%1\" to property of type \"%2\" as the former is neither the same as the latter nor a sub-class of it.")
+ .arg(stringAt(compilationUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex)).arg(QLatin1String(QMetaType::typeName(property->propType()))));
}
}
return noError;
diff --git a/src/qml/compiler/qqmlpropertyvalidator_p.h b/src/qml/compiler/qqmlpropertyvalidator_p.h
index e37b8141f4..67a2f989af 100644
--- a/src/qml/compiler/qqmlpropertyvalidator_p.h
+++ b/src/qml/compiler/qqmlpropertyvalidator_p.h
@@ -58,7 +58,7 @@ class QQmlPropertyValidator
{
Q_DECLARE_TR_FUNCTIONS(QQmlPropertyValidator)
public:
- QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, QV4::CompiledData::CompilationUnit *compilationUnit);
+ QQmlPropertyValidator(QQmlEnginePrivate *enginePrivate, const QQmlImports &imports, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
QVector<QQmlCompileError> validate();
@@ -71,9 +71,10 @@ private:
Q_REQUIRED_RESULT QVector<QQmlCompileError> recordError(const QV4::CompiledData::Location &location, const QString &description) const;
Q_REQUIRED_RESULT QVector<QQmlCompileError> recordError(const QQmlCompileError &error) const;
- QString stringAt(int index) const { return qmlUnit->stringAt(index); }
+ QString stringAt(int index) const { return compilationUnit->stringAt(index); }
QQmlEnginePrivate *enginePrivate;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
const QQmlImports &imports;
const QV4::CompiledData::Unit *qmlUnit;
const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypes;
diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp
index a896745b3f..a03f69576d 100644
--- a/src/qml/compiler/qqmltypecompiler.cpp
+++ b/src/qml/compiler/qqmltypecompiler.cpp
@@ -44,8 +44,7 @@
#include <private/qqmlcustomparser_p.h>
#include <private/qqmlvmemetaobject_p.h>
#include <private/qqmlcomponent_p.h>
-
-//#include "qv4jssimplifier_p.h"
+#include <private/qqmldelegatecomponent_p.h>
#define COMPILE_EXCEPTION(token, desc) \
{ \
@@ -67,7 +66,7 @@ QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *type
{
}
-QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile()
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> QQmlTypeCompiler::compile()
{
// Build property caches and VME meta object data
@@ -135,8 +134,8 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile()
return nullptr;
}
- // Compile JS binding expressions and signal handlers
if (!document->javaScriptCompilationUnit) {
+ // Compile JS binding expressions and signal handlers if necessary
{
// We can compile script strings ahead of time, but they must be compiled
// without type optimizations as their scope is always entirely dynamic.
@@ -147,7 +146,7 @@ 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, &document->jsGenerator.stringTable, engine->v8engine()->illegalNames());
+ document->program, typeNameCache.data(), &document->jsGenerator.stringTable, engine->v8engine()->illegalNames());
v4CodeGenerator.setUseFastLookups(false);
QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator);
if (!jsCodeGen.generateCodeForComponents())
@@ -159,18 +158,14 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile()
// Generate QML compiled type data structures
QmlIR::QmlUnitGenerator qmlGenerator;
- QV4::CompiledData::Unit *qmlUnit = qmlGenerator.generate(*document, dependencyHasher);
-
- Q_ASSERT(document->javaScriptCompilationUnit);
- // The js unit owns the data and will free the qml unit.
- document->javaScriptCompilationUnit->data = qmlUnit;
+ qmlGenerator.generate(*document, dependencyHasher);
- QV4::CompiledData::CompilationUnit *compilationUnit = document->javaScriptCompilationUnit;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = document->javaScriptCompilationUnit;
compilationUnit = document->javaScriptCompilationUnit;
compilationUnit->typeNameCache = typeNameCache;
compilationUnit->resolvedTypes = resolvedTypes;
compilationUnit->propertyCaches = std::move(m_propertyCaches);
- Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast<int>(compilationUnit->data->nObjects));
+ Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast<int>(compilationUnit->objectCount()));
if (errors.isEmpty())
return compilationUnit;
@@ -209,9 +204,14 @@ int QQmlTypeCompiler::registerString(const QString &str)
return document->jsGenerator.registerString(str);
}
+int QQmlTypeCompiler::registerConstant(QV4::ReturnedValue v)
+{
+ return document->jsGenerator.registerConstant(v);
+}
+
const QV4::CompiledData::Unit *QQmlTypeCompiler::qmlUnit() const
{
- return document->javaScriptCompilationUnit->data;
+ return document->javaScriptCompilationUnit->unitData();
}
const QQmlImports *QQmlTypeCompiler::imports() const
@@ -339,14 +339,12 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
if (!type.isValid()) {
if (imports->resolveType(propertyName, &type, nullptr, nullptr, nullptr)) {
if (type.isComposite()) {
- QQmlTypeData *tdata = enginePrivate->typeLoader.getType(type.sourceUrl());
+ QQmlRefPointer<QQmlTypeData> tdata = enginePrivate->typeLoader.getType(type.sourceUrl());
Q_ASSERT(tdata);
Q_ASSERT(tdata->isComplete());
auto compilationUnit = tdata->compilationUnit();
type = QQmlMetaType::qmlType(compilationUnit->metaTypeId);
-
- tdata->release();
}
}
}
@@ -466,14 +464,12 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
for (const QString &param : qAsConst(parameters)) {
QStringRef paramNameRef = compiler->newStringRef(param);
- if (paramList)
- paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, paramNameRef);
- else
- paramList = new (pool) QQmlJS::AST::FormalParameterList(paramNameRef);
+ QQmlJS::AST::PatternElement *b = new (pool) QQmlJS::AST::PatternElement(paramNameRef, nullptr);
+ paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, b);
}
if (paramList)
- paramList = paramList->finish();
+ paramList = paramList->finish(pool);
QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex);
QQmlJS::AST::FunctionDeclaration *functionDeclaration = nullptr;
@@ -490,11 +486,8 @@ bool SignalHandlerConverter::convertSignalHandlerExpressionsToFunctionDeclaratio
}
if (!functionDeclaration) {
QQmlJS::AST::Statement *statement = static_cast<QQmlJS::AST::Statement*>(foe->node);
- QQmlJS::AST::SourceElement *sourceElement = new (pool) QQmlJS::AST::StatementSourceElement(statement);
- QQmlJS::AST::SourceElements *elements = new (pool) QQmlJS::AST::SourceElements(sourceElement);
- elements = elements->finish();
-
- QQmlJS::AST::FunctionBody *body = new (pool) QQmlJS::AST::FunctionBody(elements);
+ QQmlJS::AST::StatementList *body = new (pool) QQmlJS::AST::StatementList(statement);
+ body = body->finish();
functionDeclaration = new (pool) QQmlJS::AST::FunctionDeclaration(compiler->newStringRef(stringAt(binding->propertyNameIndex)), paramList, body);
functionDeclaration->lbraceToken = functionDeclaration->functionToken
@@ -564,7 +557,8 @@ bool QQmlEnumTypeResolver::assignEnumToBinding(QmlIR::Binding *binding, const QS
COMPILE_EXCEPTION(binding, tr("Invalid property assignment: Enum value \"%1\" cannot start with a lowercase letter").arg(enumName.toString()));
}
binding->type = QV4::CompiledData::Binding::Type_Number;
- binding->setNumberValueInternal((double)enumValue);
+ binding->value.constantValueIndex = compiler->registerConstant(QV4::Encode((double)enumValue));
+// binding->setNumberValueInternal((double)enumValue);
binding->flags |= QV4::CompiledData::Binding::IsResolvedEnum;
return true;
}
@@ -811,13 +805,21 @@ void QQmlComponentAndAliasResolver::findAndRegisterImplicitComponents(const QmlI
const QmlIR::Object *targetObject = qmlObjects->at(binding->value.objectIndex);
auto *tr = resolvedTypes->value(targetObject->inheritedTypeNameIndex);
Q_ASSERT(tr);
- if (tr->type.isValid()) {
- if (tr->type.metaObject() == &QQmlComponent::staticMetaObject)
- continue;
- } else if (tr->compilationUnit) {
- if (tr->compilationUnit->rootPropertyCache()->firstCppMetaObject() == &QQmlComponent::staticMetaObject)
- continue;
- }
+
+ const QMetaObject *firstMetaObject = nullptr;
+ if (tr->type.isValid())
+ firstMetaObject = tr->type.metaObject();
+ else if (tr->compilationUnit)
+ firstMetaObject = tr->compilationUnit->rootPropertyCache()->firstCppMetaObject();
+ // 1: test for QQmlComponent
+ if (firstMetaObject && firstMetaObject == &QQmlComponent::staticMetaObject)
+ 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
QQmlPropertyData *pd = nullptr;
if (binding->propertyNameIndex != quint32(0)) {
diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h
index b8eddcb9b2..ffe04eb090 100644
--- a/src/qml/compiler/qqmltypecompiler_p.h
+++ b/src/qml/compiler/qqmltypecompiler_p.h
@@ -92,7 +92,7 @@ public:
QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypes;
// ---
- QV4::CompiledData::CompilationUnit *compile();
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compile();
QList<QQmlError> compilationErrors() const { return errors; }
void recordError(QQmlError error);
@@ -100,6 +100,7 @@ public:
void recordError(const QQmlCompileError &error);
int registerString(const QString &str);
+ int registerConstant(QV4::ReturnedValue v);
const QV4::CompiledData::Unit *qmlUnit() const;
diff --git a/src/qml/compiler/qv4bytecodegenerator.cpp b/src/qml/compiler/qv4bytecodegenerator.cpp
index 4d50654d27..d0bca69b56 100644
--- a/src/qml/compiler/qv4bytecodegenerator.cpp
+++ b/src/qml/compiler/qv4bytecodegenerator.cpp
@@ -40,7 +40,6 @@
#include <private/qv4bytecodegenerator_p.h>
#include <private/qv4compilercontext_p.h>
#include <private/qqmljsastfwd_p.h>
-#include <private/qv4compileddata_p.h>
QT_USE_NAMESPACE
using namespace QV4;
@@ -70,14 +69,14 @@ int BytecodeGenerator::newRegisterArray(int n)
void BytecodeGenerator::packInstruction(I &i)
{
- uchar type = *reinterpret_cast<uchar *>(i.packed);
- Q_ASSERT(type >= MOTH_NUM_INSTRUCTIONS());
- if (type >= MOTH_NUM_INSTRUCTIONS())
- type -= MOTH_NUM_INSTRUCTIONS();
+ Instr::Type type = Instr::unpack(i.packed);
+ Q_ASSERT(int(type) < MOTH_NUM_INSTRUCTIONS());
+ type = Instr::narrowInstructionType(type);
int instructionsAsInts[sizeof(Instr)/sizeof(int)] = {};
int nMembers = Moth::InstrInfo::argumentCount[static_cast<int>(i.type)];
+ uchar *code = i.packed + Instr::encodedLength(type);
for (int j = 0; j < nMembers; ++j) {
- instructionsAsInts[j] = qFromLittleEndian<qint32>(i.packed + 1 + j * sizeof(int));
+ instructionsAsInts[j] = qFromLittleEndian<qint32>(code + j * sizeof(int));
}
enum {
Normal,
@@ -89,11 +88,10 @@ void BytecodeGenerator::packInstruction(I &i)
break;
}
}
- char *code = i.packed;
+ code = i.packed;
switch (width) {
case Normal:
- *reinterpret_cast<uchar *>(code) = type;
- ++code;
+ code = Instr::pack(code, type);
for (int n = 0; n < nMembers; ++n) {
qint8 v = static_cast<qint8>(instructionsAsInts[n]);
memcpy(code, &v, 1);
@@ -121,8 +119,8 @@ void BytecodeGenerator::adjustJumpOffsets()
int jumpOffset = linkedInstruction.position - (i.position + i.size);
// qDebug() << "adjusting jump offset for instruction" << index << i.position << i.size << "offsetForJump" << i.offsetForJump << "target"
// << labels.at(i.linkedLabel) << linkedInstruction.position << "jumpOffset" << jumpOffset;
- uchar type = *reinterpret_cast<const uchar *>(i.packed);
- if (type >= MOTH_NUM_INSTRUCTIONS()) {
+ Instr::Type type = Instr::unpack(i.packed);
+ if (Instr::isWide(type)) {
Q_ASSERT(i.offsetForJump == i.size - 4);
qToLittleEndian<qint32>(jumpOffset, c);
} else {
@@ -177,7 +175,7 @@ void BytecodeGenerator::finalize(Compiler::Context *context)
entry.line = currentLine;
lineNumbers.append(entry);
}
- code.append(i.packed, i.size);
+ code.append(reinterpret_cast<const char *>(i.packed), i.size);
}
context->code = code;
@@ -185,6 +183,25 @@ void BytecodeGenerator::finalize(Compiler::Context *context)
}
int BytecodeGenerator::addInstructionHelper(Instr::Type type, const Instr &i, int offsetOfOffset) {
+ if (lastInstrType == int(Instr::Type::StoreReg)) {
+ if (type == Instr::Type::LoadReg) {
+ if (i.LoadReg.reg == lastInstr.StoreReg.reg) {
+ // value is already in the accumulator
+ return -1;
+ }
+ }
+ if (type == Instr::Type::MoveReg) {
+ if (i.MoveReg.srcReg == lastInstr.StoreReg.reg) {
+ Instruction::StoreReg store;
+ store.reg = i.MoveReg.destReg;
+ addInstruction(store);
+ return -1;
+ }
+ }
+ }
+ lastInstrType = int(type);
+ lastInstr = i;
+
#if QT_CONFIG(qml_debug)
if (debugMode && type != Instr::Type::Debug) {
QT_WARNING_PUSH
@@ -207,12 +224,10 @@ QT_WARNING_POP
const int argCount = Moth::InstrInfo::argumentCount[static_cast<int>(type)];
int s = argCount*sizeof(int);
if (offsetOfOffset != -1)
- offsetOfOffset += 1;
- I instr{type, static_cast<short>(s + 1), 0, currentLine, offsetOfOffset, -1, "\0\0" };
- char *code = instr.packed;
- *reinterpret_cast<uchar *>(code) = static_cast<uchar>(MOTH_NUM_INSTRUCTIONS() + static_cast<int>(type));
- ++code;
- Q_ASSERT(MOTH_NUM_INSTRUCTIONS() + static_cast<int>(type) < 256);
+ offsetOfOffset += Instr::encodedLength(type);
+ I instr{type, static_cast<short>(s + Instr::encodedLength(type)), 0, currentLine, offsetOfOffset, -1, "\0\0" };
+ uchar *code = instr.packed;
+ code = Instr::pack(code, Instr::wideInstructionType(type));
for (int j = 0; j < argCount; ++j) {
qToLittleEndian<qint32>(i.argumentsAsInts[j], code);
diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h
index e69f2cd310..4f3dc27acc 100644
--- a/src/qml/compiler/qv4bytecodegenerator_p.h
+++ b/src/qml/compiler/qv4bytecodegenerator_p.h
@@ -77,22 +77,18 @@ public:
Label(BytecodeGenerator *generator, LinkMode mode = LinkNow)
: generator(generator),
index(generator->labels.size()) {
- generator->labels.append(mode == LinkNow ? generator->instructions.size() : -1);
- }
- static Label returnLabel() {
- Label l;
- l.index = INT_MAX;
- return l;
- }
- bool isReturn() const {
- return index == INT_MAX;
+ generator->labels.append(-1);
+ if (mode == LinkNow)
+ link();
}
void link() {
Q_ASSERT(index >= 0);
Q_ASSERT(generator->labels[index] == -1);
generator->labels[index] = generator->instructions.size();
+ generator->clearLastInstruction();
}
+ bool isValid() const { return generator != nullptr; }
BytecodeGenerator *generator = nullptr;
int index = -1;
@@ -133,14 +129,16 @@ public:
};
struct ExceptionHandler : public Label {
+ ExceptionHandler() = default;
ExceptionHandler(BytecodeGenerator *generator)
: Label(generator, LinkLater)
{
}
~ExceptionHandler()
{
- Q_ASSERT(generator->currentExceptionHandler != this);
+ Q_ASSERT(!generator || generator->currentExceptionHandler != this);
}
+ bool isValid() const { return generator != nullptr; }
};
Label label() {
@@ -165,8 +163,11 @@ public:
Q_REQUIRED_RESULT Jump jump()
{
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // broken gcc warns about Instruction::Debug()
Instruction::Jump data;
return addJumpInstruction(data);
+QT_WARNING_POP
}
Q_REQUIRED_RESULT Jump jumpTrue()
@@ -181,6 +182,18 @@ public:
return addJumpInstruction(data);
}
+ Q_REQUIRED_RESULT Jump jumpNotUndefined()
+ {
+ Instruction::JumpNotUndefined data;
+ return addJumpInstruction(data);
+ }
+
+ Q_REQUIRED_RESULT Jump jumpNoException()
+ {
+ Instruction::JumpNoException data;
+ return addJumpInstruction(data);
+ }
+
void jumpStrictEqual(const StackSlot &lhs, const Label &target)
{
Instruction::CmpStrictEqual cmp;
@@ -197,26 +210,10 @@ public:
addJumpInstruction(Instruction::JumpTrue()).link(target);
}
- Q_REQUIRED_RESULT Jump jumpStrictEqualStackSlotInt(const StackSlot &lhs, int rhs)
- {
- Instruction::JumpStrictEqualStackSlotInt data;
- data.lhs = lhs;
- data.rhs = rhs;
- return addJumpInstruction(data);
- }
-
- Q_REQUIRED_RESULT Jump jumpStrictNotEqualStackSlotInt(const StackSlot &lhs, int rhs)
- {
- Instruction::JumpStrictNotEqualStackSlotInt data;
- data.lhs = lhs;
- data.rhs = rhs;
- return addJumpInstruction(data);
- }
-
- void setExceptionHandler(ExceptionHandler *handler)
+ void setUnwindHandler(ExceptionHandler *handler)
{
currentExceptionHandler = handler;
- Instruction::SetExceptionHandler data;
+ Instruction::SetUnwindHandler data;
data.offset = 0;
if (!handler)
addInstruction(data);
@@ -224,6 +221,19 @@ public:
addJumpInstruction(data).link(*handler);
}
+ void unwindToLabel(int level, const Label &target)
+ {
+ if (level) {
+ Instruction::UnwindToLabel unwind;
+ unwind.level = level;
+ addJumpInstruction(unwind).link(target);
+ } else {
+ jump().link(target);
+ }
+ }
+
+
+
void setLocation(const QQmlJS::AST::SourceLocation &loc);
ExceptionHandler *exceptionHandler() const {
@@ -233,6 +243,7 @@ public:
int newRegister();
int newRegisterArray(int n);
int registerCount() const { return regCount; }
+ int currentRegister() const { return currentReg; }
void finalize(Compiler::Context *context);
@@ -252,6 +263,11 @@ public:
addJumpInstruction(Instruction::JumpTrue()).link(*trueLabel);
}
+ void clearLastInstruction()
+ {
+ lastInstrType = -1;
+ }
+
private:
friend struct Jump;
friend struct Label;
@@ -266,7 +282,7 @@ private:
int line;
int offsetForJump;
int linkedLabel;
- char packed[sizeof(Instr) + 2]; // 2 for instruction and prefix
+ unsigned char packed[sizeof(Instr) + 2]; // 2 for instruction type
};
void compressInstructions();
@@ -275,7 +291,7 @@ private:
QVector<I> instructions;
QVector<int> labels;
- ExceptionHandler *currentExceptionHandler;
+ ExceptionHandler *currentExceptionHandler = nullptr;
int regCount = 0;
public:
int currentReg = 0;
@@ -283,6 +299,9 @@ private:
int startLine = 0;
int currentLine = 0;
bool debugMode = false;
+
+ int lastInstrType = -1;
+ Moth::Instr lastInstr;
};
}
diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp
new file mode 100644
index 0000000000..9cf96d27f3
--- /dev/null
+++ b/src/qml/compiler/qv4bytecodehandler.cpp
@@ -0,0 +1,554 @@
+/****************************************************************************
+**
+** 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 <private/qv4bytecodehandler_p.h>
+
+QT_USE_NAMESPACE
+using namespace QV4;
+using namespace Moth;
+
+ByteCodeHandler::~ByteCodeHandler()
+{
+}
+
+#define DISPATCH_INSTRUCTION(name, nargs, ...) \
+ generate_##name( \
+ __VA_ARGS__ \
+ );
+
+#define DECODE_AND_DISPATCH(instr) \
+ { \
+ INSTR_##instr(MOTH_DECODE_WITH_BASE) \
+ Q_UNUSED(base_ptr); \
+ _currentOffset = _nextOffset; \
+ _nextOffset = code - start; \
+ startInstruction(Instr::Type::instr); \
+ INSTR_##instr(DISPATCH) \
+ endInstruction(Instr::Type::instr); \
+ continue; \
+ }
+
+void ByteCodeHandler::decode(const char *code, uint len)
+{
+ MOTH_JUMP_TABLE;
+
+ const char *start = code;
+ const char *end = code + len;
+ while (code < end) {
+ MOTH_DISPATCH()
+
+ FOR_EACH_MOTH_INSTR(DECODE_AND_DISPATCH)
+ }
+}
+
+#undef DECODE_AND_DISPATCH
+#undef DISPATCH_INSTRUCTION
+
+#define MOTH_UNUSED_ARGS0()
+#define MOTH_UNUSED_ARGS1(arg) \
+ Q_UNUSED(arg);
+#define MOTH_UNUSED_ARGS2(arg1, arg2) \
+ Q_UNUSED(arg1); \
+ Q_UNUSED(arg2);
+#define MOTH_UNUSED_ARGS3(arg1, arg2, arg3) \
+ Q_UNUSED(arg1); \
+ Q_UNUSED(arg2); \
+ Q_UNUSED(arg3);
+#define MOTH_UNUSED_ARGS4(arg1, arg2, arg3, arg4) \
+ Q_UNUSED(arg1); \
+ Q_UNUSED(arg2); \
+ Q_UNUSED(arg3); \
+ Q_UNUSED(arg4);
+
+#define MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, ...) \
+ MOTH_EXPAND_FOR_MSVC(MOTH_UNUSED_ARGS##nargs(__VA_ARGS__))
+
+#define MOTH_MARK_ARGS_UNUSED_INSTRUCTION(name, nargs, ...) \
+ MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, __VA_ARGS__)
+
+#define COLLECTOR_BEGIN_INSTR(instr) \
+ { \
+ INSTR_##instr(MOTH_DECODE_WITH_BASE) \
+ INSTR_##instr(MOTH_MARK_ARGS_UNUSED) \
+ Q_UNUSED(base_ptr);
+
+#define COLLECTOR_END_INSTR(instr) \
+ continue; \
+ }
+
+std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint len)
+{
+ MOTH_JUMP_TABLE;
+
+ std::vector<int> labels;
+
+ const auto addLabel = [&labels,len](int offset) {
+ Q_ASSERT(offset >= 0 && offset < static_cast<int>(len));
+ labels.push_back(offset);
+ };
+
+ const char *start = code;
+ const char *end = code + len;
+ while (code < end) {
+ MOTH_DISPATCH()
+ Q_UNREACHABLE();
+
+ COLLECTOR_BEGIN_INSTR(LoadReg)
+ COLLECTOR_END_INSTR(LoadReg)
+
+ COLLECTOR_BEGIN_INSTR(StoreReg)
+ COLLECTOR_END_INSTR(StoreReg)
+
+ COLLECTOR_BEGIN_INSTR(MoveReg)
+ COLLECTOR_END_INSTR(MoveReg)
+
+ COLLECTOR_BEGIN_INSTR(LoadConst)
+ COLLECTOR_END_INSTR(LoadConst)
+
+ COLLECTOR_BEGIN_INSTR(LoadNull)
+ COLLECTOR_END_INSTR(LoadNull)
+
+ COLLECTOR_BEGIN_INSTR(LoadZero)
+ COLLECTOR_END_INSTR(LoadZero)
+
+ COLLECTOR_BEGIN_INSTR(LoadTrue)
+ COLLECTOR_END_INSTR(LoadTrue)
+
+ COLLECTOR_BEGIN_INSTR(LoadFalse)
+ COLLECTOR_END_INSTR(LoadFalse)
+
+ COLLECTOR_BEGIN_INSTR(LoadUndefined)
+ COLLECTOR_END_INSTR(LoadUndefined)
+
+ COLLECTOR_BEGIN_INSTR(LoadInt)
+ COLLECTOR_END_INSTR(LoadInt)
+
+ COLLECTOR_BEGIN_INSTR(MoveConst)
+ COLLECTOR_END_INSTR(MoveConst)
+
+ COLLECTOR_BEGIN_INSTR(LoadImport)
+ COLLECTOR_END_INSTR(LoadImport)
+
+ COLLECTOR_BEGIN_INSTR(LoadLocal)
+ COLLECTOR_END_INSTR(LoadLocal)
+
+ COLLECTOR_BEGIN_INSTR(StoreLocal)
+ COLLECTOR_END_INSTR(StoreLocal)
+
+ COLLECTOR_BEGIN_INSTR(LoadScopedLocal)
+ COLLECTOR_END_INSTR(LoadScopedLocal)
+
+ COLLECTOR_BEGIN_INSTR(StoreScopedLocal)
+ COLLECTOR_END_INSTR(StoreScopedLocal)
+
+ COLLECTOR_BEGIN_INSTR(LoadRuntimeString)
+ COLLECTOR_END_INSTR(LoadRuntimeString)
+
+ COLLECTOR_BEGIN_INSTR(MoveRegExp)
+ COLLECTOR_END_INSTR(MoveRegExp)
+
+ COLLECTOR_BEGIN_INSTR(LoadClosure)
+ COLLECTOR_END_INSTR(LoadClosure)
+
+ COLLECTOR_BEGIN_INSTR(LoadName)
+ COLLECTOR_END_INSTR(LoadName)
+
+ COLLECTOR_BEGIN_INSTR(LoadGlobalLookup)
+ COLLECTOR_END_INSTR(LoadGlobalLookup)
+
+ COLLECTOR_BEGIN_INSTR(StoreNameSloppy)
+ COLLECTOR_END_INSTR(StoreNameSloppy)
+
+ COLLECTOR_BEGIN_INSTR(StoreNameStrict)
+ COLLECTOR_END_INSTR(StoreNameStrict)
+
+ COLLECTOR_BEGIN_INSTR(LoadElement)
+ COLLECTOR_END_INSTR(LoadElement)
+
+ COLLECTOR_BEGIN_INSTR(StoreElement)
+ COLLECTOR_END_INSTR(StoreElement)
+
+ COLLECTOR_BEGIN_INSTR(LoadProperty)
+ COLLECTOR_END_INSTR(LoadProperty)
+
+ COLLECTOR_BEGIN_INSTR(GetLookup)
+ COLLECTOR_END_INSTR(GetLookup)
+
+ COLLECTOR_BEGIN_INSTR(StoreProperty)
+ COLLECTOR_END_INSTR(StoreProperty)
+
+ COLLECTOR_BEGIN_INSTR(SetLookup)
+ COLLECTOR_END_INSTR(SetLookup)
+
+ COLLECTOR_BEGIN_INSTR(LoadSuperProperty)
+ COLLECTOR_END_INSTR(LoadSuperProperty)
+
+ COLLECTOR_BEGIN_INSTR(StoreSuperProperty)
+ COLLECTOR_END_INSTR(StoreSuperProperty)
+
+ COLLECTOR_BEGIN_INSTR(StoreScopeObjectProperty)
+ COLLECTOR_END_INSTR(StoreScopeObjectProperty)
+
+ COLLECTOR_BEGIN_INSTR(LoadScopeObjectProperty)
+ COLLECTOR_END_INSTR(LoadScopeObjectProperty)
+
+ COLLECTOR_BEGIN_INSTR(StoreContextObjectProperty)
+ COLLECTOR_END_INSTR(StoreContextObjectProperty)
+
+ COLLECTOR_BEGIN_INSTR(LoadContextObjectProperty)
+ COLLECTOR_END_INSTR(LoadContextObjectProperty)
+
+ COLLECTOR_BEGIN_INSTR(LoadIdObject)
+ COLLECTOR_END_INSTR(LoadIdObject)
+
+ COLLECTOR_BEGIN_INSTR(Yield)
+ COLLECTOR_END_INSTR(Yield)
+
+ COLLECTOR_BEGIN_INSTR(YieldStar)
+ COLLECTOR_END_INSTR(YieldStar)
+
+ COLLECTOR_BEGIN_INSTR(Resume)
+ COLLECTOR_END_INSTR(Resume)
+
+ COLLECTOR_BEGIN_INSTR(CallValue)
+ COLLECTOR_END_INSTR(CallValue)
+
+ COLLECTOR_BEGIN_INSTR(CallWithReceiver)
+ COLLECTOR_END_INSTR(CallWithReceiver)
+
+ COLLECTOR_BEGIN_INSTR(CallProperty)
+ COLLECTOR_END_INSTR(CallProperty)
+
+ COLLECTOR_BEGIN_INSTR(CallPropertyLookup)
+ COLLECTOR_END_INSTR(CallPropertyLookup)
+
+ COLLECTOR_BEGIN_INSTR(CallElement)
+ COLLECTOR_END_INSTR(CallElement)
+
+ COLLECTOR_BEGIN_INSTR(CallName)
+ COLLECTOR_END_INSTR(CallName)
+
+ COLLECTOR_BEGIN_INSTR(CallPossiblyDirectEval)
+ COLLECTOR_END_INSTR(CallPossiblyDirectEval)
+
+ COLLECTOR_BEGIN_INSTR(CallGlobalLookup)
+ COLLECTOR_END_INSTR(CallGlobalLookup)
+
+ COLLECTOR_BEGIN_INSTR(CallScopeObjectProperty)
+ COLLECTOR_END_INSTR(CallScopeObjectProperty)
+
+ COLLECTOR_BEGIN_INSTR(CallContextObjectProperty)
+ COLLECTOR_END_INSTR(CallContextObjectProperty)
+
+ COLLECTOR_BEGIN_INSTR(CallWithSpread)
+ COLLECTOR_END_INSTR(CallWithSpread)
+
+ COLLECTOR_BEGIN_INSTR(Construct)
+ COLLECTOR_END_INSTR(Construct)
+
+ COLLECTOR_BEGIN_INSTR(ConstructWithSpread)
+ COLLECTOR_END_INSTR(ConstructWithSpread)
+
+ COLLECTOR_BEGIN_INSTR(SetUnwindHandler)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(SetUnwindHandler)
+
+ COLLECTOR_BEGIN_INSTR(UnwindDispatch)
+ COLLECTOR_END_INSTR(UnwindDispatch)
+
+ COLLECTOR_BEGIN_INSTR(UnwindToLabel)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(UnwindToLabel)
+
+ COLLECTOR_BEGIN_INSTR(DeadTemporalZoneCheck)
+ COLLECTOR_END_INSTR(DeadTemporalZoneCheck)
+
+ COLLECTOR_BEGIN_INSTR(ThrowException)
+ COLLECTOR_END_INSTR(ThrowException)
+
+ COLLECTOR_BEGIN_INSTR(GetException)
+ COLLECTOR_END_INSTR(HasException)
+
+ COLLECTOR_BEGIN_INSTR(SetException)
+ COLLECTOR_END_INSTR(SetExceptionFlag)
+
+ COLLECTOR_BEGIN_INSTR(CreateCallContext)
+ COLLECTOR_END_INSTR(CreateCallContext)
+
+ COLLECTOR_BEGIN_INSTR(PushCatchContext)
+ COLLECTOR_END_INSTR(PushCatchContext)
+
+ COLLECTOR_BEGIN_INSTR(PushWithContext)
+ COLLECTOR_END_INSTR(PushWithContext)
+
+ COLLECTOR_BEGIN_INSTR(PushBlockContext)
+ COLLECTOR_END_INSTR(PushBlockContext)
+
+ COLLECTOR_BEGIN_INSTR(CloneBlockContext)
+ COLLECTOR_END_INSTR(CloneBlockContext)
+
+ COLLECTOR_BEGIN_INSTR(PushScriptContext)
+ COLLECTOR_END_INSTR(PushScriptContext)
+
+ COLLECTOR_BEGIN_INSTR(PopScriptContext)
+ COLLECTOR_END_INSTR(PopScriptContext)
+
+ COLLECTOR_BEGIN_INSTR(PopContext)
+ COLLECTOR_END_INSTR(PopContext)
+
+ COLLECTOR_BEGIN_INSTR(GetIterator)
+ COLLECTOR_END_INSTR(GetIterator)
+
+ COLLECTOR_BEGIN_INSTR(IteratorNext)
+ COLLECTOR_END_INSTR(IteratorNext)
+
+ COLLECTOR_BEGIN_INSTR(IteratorNextForYieldStar)
+ COLLECTOR_END_INSTR(IteratorNextForYieldStar)
+
+ COLLECTOR_BEGIN_INSTR(IteratorClose)
+ COLLECTOR_END_INSTR(IteratorClose)
+
+ COLLECTOR_BEGIN_INSTR(DestructureRestElement)
+ COLLECTOR_END_INSTR(DestructureRestElement)
+
+ COLLECTOR_BEGIN_INSTR(DeleteProperty)
+ COLLECTOR_END_INSTR(DeleteProperty)
+
+ COLLECTOR_BEGIN_INSTR(DeleteName)
+ COLLECTOR_END_INSTR(DeleteName)
+
+ COLLECTOR_BEGIN_INSTR(TypeofName)
+ COLLECTOR_END_INSTR(TypeofName)
+
+ COLLECTOR_BEGIN_INSTR(TypeofValue)
+ COLLECTOR_END_INSTR(TypeofValue)
+
+ COLLECTOR_BEGIN_INSTR(DeclareVar)
+ COLLECTOR_END_INSTR(DeclareVar)
+
+ COLLECTOR_BEGIN_INSTR(DefineArray)
+ COLLECTOR_END_INSTR(DefineArray)
+
+ COLLECTOR_BEGIN_INSTR(DefineObjectLiteral)
+ COLLECTOR_END_INSTR(DefineObjectLiteral)
+
+ COLLECTOR_BEGIN_INSTR(CreateClass)
+ COLLECTOR_END_INSTR(CreateClass)
+
+ COLLECTOR_BEGIN_INSTR(CreateMappedArgumentsObject)
+ COLLECTOR_END_INSTR(CreateMappedArgumentsObject)
+
+ COLLECTOR_BEGIN_INSTR(CreateUnmappedArgumentsObject)
+ COLLECTOR_END_INSTR(CreateUnmappedArgumentsObject)
+
+ COLLECTOR_BEGIN_INSTR(CreateRestParameter)
+ COLLECTOR_END_INSTR(CreateRestParameter)
+
+ COLLECTOR_BEGIN_INSTR(ConvertThisToObject)
+ COLLECTOR_END_INSTR(ConvertThisToObject)
+
+ COLLECTOR_BEGIN_INSTR(LoadSuperConstructor)
+ COLLECTOR_END_INSTR(LoadSuperConstructor)
+
+ COLLECTOR_BEGIN_INSTR(ToObject)
+ COLLECTOR_END_INSTR(ToObject)
+
+ COLLECTOR_BEGIN_INSTR(Jump)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(Jump)
+
+ COLLECTOR_BEGIN_INSTR(JumpTrue)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(JumpTrue)
+
+ COLLECTOR_BEGIN_INSTR(JumpFalse)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(JumpFalse)
+
+ COLLECTOR_BEGIN_INSTR(JumpNoException)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(JumpNoException)
+
+ COLLECTOR_BEGIN_INSTR(JumpNotUndefined)
+ addLabel(code - start + offset);
+ COLLECTOR_END_INSTR(JumpNotUndefined)
+
+ COLLECTOR_BEGIN_INSTR(CmpEqNull)
+ COLLECTOR_END_INSTR(CmpEqNull)
+
+ COLLECTOR_BEGIN_INSTR(CmpNeNull)
+ COLLECTOR_END_INSTR(CmpNeNull)
+
+ COLLECTOR_BEGIN_INSTR(CmpEqInt)
+ COLLECTOR_END_INSTR(CmpEq)
+
+ COLLECTOR_BEGIN_INSTR(CmpNeInt)
+ COLLECTOR_END_INSTR(CmpNeInt)
+
+ COLLECTOR_BEGIN_INSTR(CmpEq)
+ COLLECTOR_END_INSTR(CmpEq)
+
+ COLLECTOR_BEGIN_INSTR(CmpNe)
+ COLLECTOR_END_INSTR(CmpNe)
+
+ COLLECTOR_BEGIN_INSTR(CmpGt)
+ COLLECTOR_END_INSTR(CmpGt)
+
+ COLLECTOR_BEGIN_INSTR(CmpGe)
+ COLLECTOR_END_INSTR(CmpGe)
+
+ COLLECTOR_BEGIN_INSTR(CmpLt)
+ COLLECTOR_END_INSTR(CmpLt)
+
+ COLLECTOR_BEGIN_INSTR(CmpLe)
+ COLLECTOR_END_INSTR(CmpLe)
+
+ COLLECTOR_BEGIN_INSTR(CmpStrictEqual)
+ COLLECTOR_END_INSTR(CmpStrictEqual)
+
+ COLLECTOR_BEGIN_INSTR(CmpStrictNotEqual)
+ COLLECTOR_END_INSTR(CmpStrictNotEqual)
+
+ COLLECTOR_BEGIN_INSTR(CmpIn)
+ COLLECTOR_END_INSTR(CmpIn)
+
+ COLLECTOR_BEGIN_INSTR(CmpInstanceOf)
+ COLLECTOR_END_INSTR(CmpInstanceOf)
+
+ COLLECTOR_BEGIN_INSTR(UNot)
+ COLLECTOR_END_INSTR(UNot)
+
+ COLLECTOR_BEGIN_INSTR(UPlus)
+ COLLECTOR_END_INSTR(UPlus)
+
+ COLLECTOR_BEGIN_INSTR(UMinus)
+ COLLECTOR_END_INSTR(UMinus)
+
+ COLLECTOR_BEGIN_INSTR(UCompl)
+ COLLECTOR_END_INSTR(UCompl)
+
+ COLLECTOR_BEGIN_INSTR(Increment)
+ COLLECTOR_END_INSTR(PreIncrement)
+
+ COLLECTOR_BEGIN_INSTR(Decrement)
+ COLLECTOR_END_INSTR(PreDecrement)
+
+ COLLECTOR_BEGIN_INSTR(Add)
+ COLLECTOR_END_INSTR(Add)
+
+ COLLECTOR_BEGIN_INSTR(BitAnd)
+ COLLECTOR_END_INSTR(BitAnd)
+
+ COLLECTOR_BEGIN_INSTR(BitOr)
+ COLLECTOR_END_INSTR(BitOr)
+
+ COLLECTOR_BEGIN_INSTR(BitXor)
+ COLLECTOR_END_INSTR(BitXor)
+
+ COLLECTOR_BEGIN_INSTR(UShr)
+ COLLECTOR_END_INSTR(UShr)
+
+ COLLECTOR_BEGIN_INSTR(Shr)
+ COLLECTOR_END_INSTR(Shr)
+
+ COLLECTOR_BEGIN_INSTR(Shl)
+ COLLECTOR_END_INSTR(Shl)
+
+ COLLECTOR_BEGIN_INSTR(BitAndConst)
+ COLLECTOR_END_INSTR(BitAndConst)
+
+ COLLECTOR_BEGIN_INSTR(BitOrConst)
+ COLLECTOR_END_INSTR(BitOr)
+
+ COLLECTOR_BEGIN_INSTR(BitXorConst)
+ COLLECTOR_END_INSTR(BitXor)
+
+ COLLECTOR_BEGIN_INSTR(UShrConst)
+ COLLECTOR_END_INSTR(UShrConst)
+
+ COLLECTOR_BEGIN_INSTR(ShrConst)
+ COLLECTOR_END_INSTR(ShrConst)
+
+ COLLECTOR_BEGIN_INSTR(ShlConst)
+ COLLECTOR_END_INSTR(ShlConst)
+
+ COLLECTOR_BEGIN_INSTR(Exp)
+ COLLECTOR_END_INSTR(Exp)
+
+ COLLECTOR_BEGIN_INSTR(Mul)
+ COLLECTOR_END_INSTR(Mul)
+
+ COLLECTOR_BEGIN_INSTR(Div)
+ COLLECTOR_END_INSTR(Div)
+
+ COLLECTOR_BEGIN_INSTR(Mod)
+ COLLECTOR_END_INSTR(Mod)
+
+ COLLECTOR_BEGIN_INSTR(Sub)
+ COLLECTOR_END_INSTR(Sub)
+
+ COLLECTOR_BEGIN_INSTR(Ret)
+ COLLECTOR_END_INSTR(Ret)
+
+#ifndef QT_NO_QML_DEBUGGER
+ COLLECTOR_BEGIN_INSTR(Debug)
+ COLLECTOR_END_INSTR(Debug)
+#endif // QT_NO_QML_DEBUGGER
+
+ COLLECTOR_BEGIN_INSTR(InitializeBlockDeadTemporalZone)
+ COLLECTOR_END_INSTR(InitializeBlockDeadTemporalZone)
+
+ COLLECTOR_BEGIN_INSTR(ThrowOnNullOrUndefined)
+ COLLECTOR_END_INSTR(ThrowOnNullOrUndefined)
+
+ COLLECTOR_BEGIN_INSTR(LoadQmlContext)
+ COLLECTOR_END_INSTR(LoadQmlContext)
+
+ COLLECTOR_BEGIN_INSTR(LoadQmlImportedScripts)
+ COLLECTOR_END_INSTR(LoadQmlImportedScripts)
+
+ COLLECTOR_BEGIN_INSTR(TailCall)
+ COLLECTOR_END_INSTR(TailCall)
+ }
+
+ return labels;
+}
+
+#undef COLLECTOR_BEGIN_INSTR
+#undef COLLECTOR_END_INSTR
diff --git a/src/qml/compiler/qv4bytecodehandler_p.h b/src/qml/compiler/qv4bytecodehandler_p.h
new file mode 100644
index 0000000000..ca6abf3dc3
--- /dev/null
+++ b/src/qml/compiler/qv4bytecodehandler_p.h
@@ -0,0 +1,115 @@
+/****************************************************************************
+**
+** 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 QV4BYTECODEHANDLER_P_H
+#define QV4BYTECODEHANDLER_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/qv4instr_moth_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace Moth {
+
+#define BYTECODE_HANDLER_DEFINE_ARGS(nargs, ...) \
+ MOTH_EXPAND_FOR_MSVC(BYTECODE_HANDLER_DEFINE_ARGS##nargs(__VA_ARGS__))
+
+#define BYTECODE_HANDLER_DEFINE_ARGS0()
+#define BYTECODE_HANDLER_DEFINE_ARGS1(arg) \
+ int arg
+#define BYTECODE_HANDLER_DEFINE_ARGS2(arg1, arg2) \
+ int arg1, \
+ int arg2
+#define BYTECODE_HANDLER_DEFINE_ARGS3(arg1, arg2, arg3) \
+ int arg1, \
+ int arg2, \
+ int arg3
+#define BYTECODE_HANDLER_DEFINE_ARGS4(arg1, arg2, arg3, arg4) \
+ int arg1, \
+ int arg2, \
+ int arg3, \
+ int arg4
+
+#define BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER_INSTRUCTION(name, nargs, ...) \
+ virtual void generate_##name( \
+ BYTECODE_HANDLER_DEFINE_ARGS(nargs, __VA_ARGS__) \
+ ) = 0;
+
+#define BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER(instr) \
+ INSTR_##instr(BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER)
+
+class ByteCodeHandler
+{
+public:
+ virtual ~ByteCodeHandler();
+
+ void decode(const char *code, uint len);
+
+ int currentInstructionOffset() const { return _currentOffset; }
+ int nextInstructionOffset() const { return _nextOffset; }
+
+ static std::vector<int> collectLabelsInBytecode(const char *code, uint len);
+
+protected:
+ FOR_EACH_MOTH_INSTR(BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER)
+
+ virtual void startInstruction(Moth::Instr::Type instr) = 0;
+ virtual void endInstruction(Moth::Instr::Type instr) = 0;
+
+private:
+ int _currentOffset = 0;
+ int _nextOffset = 0;
+};
+
+} // Moth namespace
+} // QV4 namespace
+
+QT_END_NAMESPACE
+
+#endif // QV4BYTECODEHANDLER_P_H
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index e831fd48f5..321cf21c54 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -42,11 +42,8 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QStringList>
-#include <QtCore/QSet>
-#include <QtCore/QBuffer>
-#include <QtCore/QBitArray>
-#include <QtCore/QLinkedList>
#include <QtCore/QStack>
+#include <QScopeGuard>
#include <private/qqmljsast_p.h>
#include <private/qv4string_p.h>
#include <private/qv4value_p.h>
@@ -55,9 +52,15 @@
#include <private/qv4bytecodegenerator_p.h>
#include <private/qv4compilerscanfunctions_p.h>
+#ifndef V4_BOOTSTRAP
+# include <qqmlerror.h>
+#endif
+
#include <cmath>
#include <iostream>
+static const bool disable_lookups = false;
+
#ifdef CONST
#undef CONST
#endif
@@ -77,8 +80,6 @@ static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGene
case Statement::Kind_ForEachStatement:
case Statement::Kind_ForStatement:
case Statement::Kind_IfStatement:
- case Statement::Kind_LocalForEachStatement:
- case Statement::Kind_LocalForStatement:
case Statement::Kind_WhileStatement:
bytecodeGenerator->setLocation(fallback);
break;
@@ -90,7 +91,7 @@ static inline void setJumpOutLocation(QV4::Moth::BytecodeGenerator *bytecodeGene
Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict)
: _module(nullptr)
- , _returnAddress(0)
+ , _returnAddress(-1)
, _context(nullptr)
, _labelledStatement(nullptr)
, jsUnitGenerator(jsUnitGenerator)
@@ -106,7 +107,31 @@ void Codegen::generateFromProgram(const QString &fileName,
const QString &sourceCode,
Program *node,
Module *module,
- CompilationMode mode)
+ ContextType contextType)
+{
+ Q_ASSERT(node);
+
+ _module = module;
+ _context = nullptr;
+
+ // ### should be set on the module outside of this method
+ _module->fileName = fileName;
+ _module->finalUrl = finalUrl;
+
+ ScanFunctions scan(this, sourceCode, contextType);
+ scan(node);
+
+ if (hasError)
+ return;
+
+ defineFunction(QStringLiteral("%entry"), node, nullptr, node->statements);
+}
+
+void Codegen::generateFromModule(const QString &fileName,
+ const QString &finalUrl,
+ const QString &sourceCode,
+ ESModule *node,
+ Module *module)
{
Q_ASSERT(node);
@@ -117,10 +142,35 @@ void Codegen::generateFromProgram(const QString &fileName,
_module->fileName = fileName;
_module->finalUrl = finalUrl;
- ScanFunctions scan(this, sourceCode, mode);
+ ScanFunctions scan(this, sourceCode, ContextType::ESModule);
scan(node);
- defineFunction(QStringLiteral("%entry"), node, nullptr, node->elements);
+ if (hasError)
+ return;
+
+ {
+ Compiler::Context *moduleContext = _module->contextMap.value(node);
+ for (const auto &entry: moduleContext->exportEntries) {
+ if (entry.moduleRequest.isEmpty()) {
+ // ### check against imported bound names
+ _module->localExportEntries << entry;
+ } else if (entry.importName == QLatin1Char('*')) {
+ _module->starExportEntries << entry;
+ } else {
+ _module->indirectExportEntries << entry;
+ }
+ }
+ _module->importEntries = moduleContext->importEntries;
+
+ _module->moduleRequests = std::move(moduleContext->moduleRequests);
+ _module->moduleRequests.removeDuplicates();
+ }
+
+ std::sort(_module->localExportEntries.begin(), _module->localExportEntries.end(), ExportEntry::lessThan);
+ std::sort(_module->starExportEntries.begin(), _module->starExportEntries.end(), ExportEntry::lessThan);
+ std::sort(_module->indirectExportEntries.begin(), _module->indirectExportEntries.end(), ExportEntry::lessThan);
+
+ defineFunction(QStringLiteral("%entry"), node, nullptr, node->body);
}
void Codegen::enterContext(Node *node)
@@ -132,19 +182,23 @@ void Codegen::enterContext(Node *node)
int Codegen::leaveContext()
{
Q_ASSERT(_context);
- Q_ASSERT(!_context->controlFlow);
int functionIndex = _context->functionIndex;
_context = _context->parent;
return functionIndex;
}
+Context *Codegen::enterBlock(Node *node)
+{
+ enterContext(node);
+ return _context;
+}
+
Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
{
if (hasError)
return _expr.result();
-#ifndef V4_BOOTSTRAP
- if (expr.isConst()) {
+ if (expr.isConstant()) {
auto v = Value::fromReturnedValue(expr.constant);
if (v.isNumber()) {
switch (op) {
@@ -161,7 +215,6 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
}
}
}
-#endif // V4_BOOTSTRAP
switch (op) {
case UMinus: {
@@ -267,9 +320,9 @@ void Codegen::statement(Statement *ast)
bytecodeGenerator->setLocation(ast->firstSourceLocation());
VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast);
- qSwap(_volataleMemoryLocations, vLocs);
+ qSwap(_volatileMemoryLocations, vLocs);
accept(ast);
- qSwap(_volataleMemoryLocations, vLocs);
+ qSwap(_volatileMemoryLocations, vLocs);
}
void Codegen::statement(ExpressionNode *ast)
@@ -282,11 +335,11 @@ void Codegen::statement(ExpressionNode *ast)
Result r(nx);
qSwap(_expr, r);
VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast);
- qSwap(_volataleMemoryLocations, vLocs);
+ qSwap(_volatileMemoryLocations, vLocs);
accept(ast);
- qSwap(_volataleMemoryLocations, vLocs);
+ qSwap(_volatileMemoryLocations, vLocs);
qSwap(_expr, r);
if (hasError)
@@ -337,60 +390,103 @@ Codegen::Reference Codegen::expression(ExpressionNode *ast)
return r.result();
}
-Codegen::Result Codegen::sourceElement(SourceElement *ast)
+void Codegen::program(Program *ast)
{
- Result r(nx);
if (ast) {
- qSwap(_expr, r);
- accept(ast);
- qSwap(_expr, r);
+ statementList(ast->statements);
}
- return r;
}
-void Codegen::functionBody(FunctionBody *ast)
-{
- if (ast)
- sourceElements(ast->elements);
-}
+enum class CompletionState {
+ Empty,
+ EmptyAbrupt,
+ NonEmpty
+};
-void Codegen::program(Program *ast)
-{
- if (ast) {
- sourceElements(ast->elements);
+static CompletionState completionState(StatementList *list)
+{
+ for (StatementList *it = list; it; it = it->next) {
+ if (it->statement->kind == Statement::Kind_BreakStatement ||
+ it->statement->kind == Statement::Kind_ContinueStatement)
+ return CompletionState::EmptyAbrupt;
+ if (it->statement->kind == Statement::Kind_EmptyStatement ||
+ it->statement->kind == Statement::Kind_VariableDeclaration ||
+ it->statement->kind == Statement::Kind_FunctionDeclaration)
+ continue;
+ if (it->statement->kind == Statement::Kind_Block) {
+ CompletionState subState = completionState(static_cast<Block *>(it->statement)->statements);
+ if (subState != CompletionState::Empty)
+ return subState;
+ continue;
+ }
+ return CompletionState::NonEmpty;
}
+ return CompletionState::Empty;
}
-void Codegen::sourceElements(SourceElements *ast)
+static Node *completionStatement(StatementList *list)
{
- bool _requiresReturnValue = false;
- qSwap(_requiresReturnValue, requiresReturnValue);
- for (SourceElements *it = ast; it; it = it->next) {
- if (!it->next)
- qSwap(_requiresReturnValue, requiresReturnValue);
- sourceElement(it->element);
- if (hasError)
- return;
- if (StatementSourceElement *sse = AST::cast<StatementSourceElement *>(it->element)) {
- if (AST::cast<ThrowStatement *>(sse->statement) ||
- AST::cast<ReturnStatement *>(sse->statement))
- return;
+ Node *completionStatement = nullptr;
+ for (StatementList *it = list; it; it = it->next) {
+ if (it->statement->kind == Statement::Kind_BreakStatement ||
+ it->statement->kind == Statement::Kind_ContinueStatement)
+ return completionStatement;
+ if (it->statement->kind == Statement::Kind_ThrowStatement ||
+ it->statement->kind == Statement::Kind_ReturnStatement)
+ return it->statement;
+ if (it->statement->kind == Statement::Kind_EmptyStatement ||
+ it->statement->kind == Statement::Kind_VariableStatement ||
+ it->statement->kind == Statement::Kind_FunctionDeclaration)
+ continue;
+ if (it->statement->kind == Statement::Kind_Block) {
+ CompletionState state = completionState(static_cast<Block *>(it->statement)->statements);
+ switch (state) {
+ case CompletionState::Empty:
+ continue;
+ case CompletionState::EmptyAbrupt:
+ return it->statement;
+ case CompletionState::NonEmpty:
+ break;
+ }
}
+ completionStatement = it->statement;
}
+ return completionStatement;
}
void Codegen::statementList(StatementList *ast)
{
+ if (!ast)
+ return;
+
bool _requiresReturnValue = requiresReturnValue;
- requiresReturnValue = false;
- for (StatementList *it = ast; it; it = it->next) {
- if (!it->next ||
- it->next->statement->kind == Statement::Kind_BreakStatement ||
- it->next->statement->kind == Statement::Kind_ContinueStatement ||
- it->next->statement->kind == Statement::Kind_ReturnStatement)
- requiresReturnValue = _requiresReturnValue;
- statement(it->statement);
+ // ### the next line is pessimizing a bit too much, as there are many cases, where the complietion from the break
+ // statement will not be used, but it's at least spec compliant
+ if (!controlFlow || !controlFlow->hasLoop())
requiresReturnValue = false;
+
+ Node *needsCompletion = nullptr;
+
+ if (_requiresReturnValue && !requiresReturnValue)
+ needsCompletion = completionStatement(ast);
+
+ if (requiresReturnValue && !needsCompletion && !insideSwitch) {
+ // break or continue is the first real statement, set the return value to undefined
+ Reference::fromConst(this, Encode::undefined()).storeOnStack(_returnAddress);
+ }
+
+ bool _insideSwitch = insideSwitch;
+ insideSwitch = false;
+
+ for (StatementList *it = ast; it; it = it->next) {
+ if (it->statement == needsCompletion)
+ requiresReturnValue = true;
+ if (Statement *s = it->statement->statementCast())
+ statement(s);
+ else
+ statement(static_cast<ExpressionNode *>(it->statement));
+ if (it->statement == needsCompletion)
+ requiresReturnValue = false;
if (it->statement->kind == Statement::Kind_ThrowStatement ||
it->statement->kind == Statement::Kind_BreakStatement ||
it->statement->kind == Statement::Kind_ContinueStatement ||
@@ -399,22 +495,23 @@ void Codegen::statementList(StatementList *ast)
break;
}
requiresReturnValue = _requiresReturnValue;
+ insideSwitch = _insideSwitch;
}
-void Codegen::variableDeclaration(VariableDeclaration *ast)
+void Codegen::variableDeclaration(PatternElement *ast)
{
+ TailCallBlocker blockTailCalls(this);
RegisterScope scope(this);
- if (!ast->expression)
- return;
- Reference rhs = expression(ast->expression);
- if (hasError)
+ if (!ast->initializer) {
+ if (ast->isLexicallyScoped()) {
+ Reference::fromConst(this, Encode::undefined()).loadInAccumulator();
+ Reference varToStore = targetForPatternElement(ast);
+ varToStore.storeConsumeAccumulator();
+ }
return;
-
- Reference lhs = referenceForName(ast->name.toString(), true);
- //### if lhs is a temp, this won't generate a temp-to-temp move. Same for when rhs is a const
- rhs.loadInAccumulator();
- lhs.storeConsumeAccumulator();
+ }
+ initializeAndDestructureBindingElement(ast, Reference(), /*isDefinition*/ true);
}
void Codegen::variableDeclarationList(VariableDeclarationList *ast)
@@ -424,6 +521,203 @@ void Codegen::variableDeclarationList(VariableDeclarationList *ast)
}
}
+Codegen::Reference Codegen::targetForPatternElement(AST::PatternElement *p)
+{
+ if (!p->bindingIdentifier.isNull())
+ return referenceForName(p->bindingIdentifier.toString(), true, p->firstSourceLocation());
+ if (!p->bindingTarget || p->destructuringPattern())
+ return Codegen::Reference::fromStackSlot(this);
+ Reference lhs = expression(p->bindingTarget);
+ if (hasError)
+ return lhs;
+ lhs = lhs.asLValue();
+ return lhs;
+}
+
+void Codegen::initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &base, bool isDefinition)
+{
+ Q_ASSERT(e->type == AST::PatternElement::Binding || e->type == AST::PatternElement::RestElement);
+ RegisterScope scope(this);
+ Reference baseRef = (base.isAccumulator()) ? base.storeOnStack() : base;
+ Reference varToStore = targetForPatternElement(e);
+ if (isDefinition)
+ varToStore.isReferenceToConst = false;
+ if (hasError)
+ return;
+
+ if (e->initializer) {
+ if (!baseRef.isValid()) {
+ // assignment
+ Reference expr = expression(e->initializer);
+ if (hasError)
+ return;
+ expr.loadInAccumulator();
+ varToStore.storeConsumeAccumulator();
+ } else if (baseRef == varToStore) {
+ baseRef.loadInAccumulator();
+ BytecodeGenerator::Jump jump = bytecodeGenerator->jumpNotUndefined();
+ Reference expr = expression(e->initializer);
+ if (hasError) {
+ jump.link();
+ return;
+ }
+ expr.loadInAccumulator();
+ varToStore.storeConsumeAccumulator();
+ jump.link();
+ } else {
+ baseRef.loadInAccumulator();
+ BytecodeGenerator::Jump jump = bytecodeGenerator->jumpNotUndefined();
+ Reference expr = expression(e->initializer);
+ if (hasError) {
+ jump.link();
+ return;
+ }
+ expr.loadInAccumulator();
+ jump.link();
+ varToStore.storeConsumeAccumulator();
+ }
+ } else if (baseRef != varToStore && baseRef.isValid()) {
+ baseRef.loadInAccumulator();
+ varToStore.storeConsumeAccumulator();
+ }
+ Pattern *p = e->destructuringPattern();
+ if (!p)
+ return;
+
+ if (!varToStore.isStackSlot())
+ varToStore = varToStore.storeOnStack();
+ if (PatternElementList *l = e->elementList()) {
+ destructureElementList(varToStore, l, isDefinition);
+ } else if (PatternPropertyList *p = e->propertyList()) {
+ destructurePropertyList(varToStore, p, isDefinition);
+ } else if (e->bindingTarget) {
+ // empty binding pattern. For spec compatibility, try to coerce the argument to an object
+ varToStore.loadInAccumulator();
+ Instruction::ToObject toObject;
+ bytecodeGenerator->addInstruction(toObject);
+ return;
+ }
+}
+
+Codegen::Reference Codegen::referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name)
+{
+ AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(name);
+ Reference property;
+ if (cname) {
+ Reference computedName = expression(cname->expression);
+ if (hasError)
+ return Reference();
+ computedName = computedName.storeOnStack();
+ property = Reference::fromSubscript(object, computedName).asLValue();
+ } else {
+ QString propertyName = name->asString();
+ property = Reference::fromMember(object, propertyName);
+ }
+ return property;
+}
+
+void Codegen::destructurePropertyList(const Codegen::Reference &object, PatternPropertyList *bindingList, bool isDefinition)
+{
+ RegisterScope scope(this);
+
+ object.loadInAccumulator();
+ Instruction::ThrowOnNullOrUndefined t;
+ bytecodeGenerator->addInstruction(t);
+
+ for (PatternPropertyList *it = bindingList; it; it = it->next) {
+ PatternProperty *p = it->property;
+ RegisterScope scope(this);
+ Reference property = referenceForPropertyName(object, p->name);
+ if (hasError)
+ return;
+ initializeAndDestructureBindingElement(p, property, isDefinition);
+ if (hasError)
+ return;
+ }
+}
+
+void Codegen::destructureElementList(const Codegen::Reference &array, PatternElementList *bindingList, bool isDefinition)
+{
+ RegisterScope scope(this);
+
+ Reference iterator = Reference::fromStackSlot(this);
+ Reference iteratorValue = Reference::fromStackSlot(this);
+ Reference iteratorDone = Reference::fromStackSlot(this);
+ Reference::storeConstOnStack(this, Encode(false), iteratorDone.stackSlot());
+
+ array.loadInAccumulator();
+ Instruction::GetIterator iteratorObjInstr;
+ iteratorObjInstr.iterator = static_cast<int>(AST::ForEachType::Of);
+ bytecodeGenerator->addInstruction(iteratorObjInstr);
+ iterator.storeConsumeAccumulator();
+
+ bool hasRest = false;
+
+ BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
+ {
+ auto cleanup = [this, iterator, iteratorDone]() {
+ iterator.loadInAccumulator();
+ Instruction::IteratorClose close;
+ close.done = iteratorDone.stackSlot();
+ bytecodeGenerator->addInstruction(close);
+ };
+
+ ControlFlowUnwindCleanup flow(this, cleanup);
+
+ for (PatternElementList *p = bindingList; p; p = p->next) {
+ PatternElement *e = p->element;
+ for (Elision *elision = p->elision; elision; elision = elision->next) {
+ iterator.loadInAccumulator();
+ Instruction::IteratorNext next;
+ next.value = iteratorValue.stackSlot();
+ next.done = iteratorDone.stackSlot();
+ bytecodeGenerator->addInstruction(next);
+ }
+
+ if (!e)
+ continue;
+
+ RegisterScope scope(this);
+ iterator.loadInAccumulator();
+
+ if (e->type == PatternElement::RestElement) {
+ Reference::fromConst(this, Encode(true)).storeOnStack(iteratorDone.stackSlot());
+ bytecodeGenerator->addInstruction(Instruction::DestructureRestElement());
+ initializeAndDestructureBindingElement(e, Reference::fromAccumulator(this), isDefinition);
+ hasRest = true;
+ } else {
+ Instruction::IteratorNext next;
+ next.value = iteratorValue.stackSlot();
+ next.done = iteratorDone.stackSlot();
+ bytecodeGenerator->addInstruction(next);
+ initializeAndDestructureBindingElement(e, iteratorValue, isDefinition);
+ if (hasError) {
+ end.link();
+ return;
+ }
+ }
+ }
+
+ if (hasRest)
+ // no need to close the iterator
+ bytecodeGenerator->jump().link(end);
+ }
+
+
+ end.link();
+}
+
+void Codegen::destructurePattern(Pattern *p, const Reference &rhs)
+{
+ RegisterScope scope(this);
+ if (auto *o = AST::cast<ObjectPattern *>(p))
+ destructurePropertyList(rhs, o->properties);
+ else if (auto *a = AST::cast<ArrayPattern *>(p))
+ destructureElementList(rhs, a->elements);
+ else
+ Q_UNREACHABLE();
+}
+
bool Codegen::visit(ArgumentList *)
{
@@ -461,12 +755,6 @@ bool Codegen::visit(DefaultClause *)
return false;
}
-bool Codegen::visit(ElementList *)
-{
- Q_UNREACHABLE();
- return false;
-}
-
bool Codegen::visit(Elision *)
{
Q_UNREACHABLE();
@@ -485,39 +773,67 @@ bool Codegen::visit(FormalParameterList *)
return false;
}
-bool Codegen::visit(FunctionBody *)
+bool Codegen::visit(Program *)
{
Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(Program *)
+bool Codegen::visit(PatternElement *)
{
Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(PropertyAssignmentList *)
+bool Codegen::visit(PatternElementList *)
{
Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(PropertyNameAndValue *)
+bool Codegen::visit(PatternProperty *)
{
Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(PropertyGetterSetter *)
+bool Codegen::visit(PatternPropertyList *)
{
Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(SourceElements *)
+bool Codegen::visit(ExportDeclaration *ast)
{
- Q_UNREACHABLE();
+ if (!ast->exportDefault)
+ return true;
+
+ TailCallBlocker blockTailCalls(this);
+ Reference exportedValue;
+
+ if (auto *fdecl = AST::cast<FunctionDeclaration*>(ast->variableStatementOrDeclaration)) {
+ Result r;
+ qSwap(_expr, r);
+ visit(static_cast<FunctionExpression*>(fdecl));
+ qSwap(_expr, r);
+ exportedValue = r.result();
+ } else if (auto *classDecl = AST::cast<ClassDeclaration*>(ast->variableStatementOrDeclaration)) {
+ Result r;
+ qSwap(_expr, r);
+ visit(static_cast<ClassExpression*>(classDecl));
+ qSwap(_expr, r);
+ exportedValue = r.result();
+ } else if (ExpressionNode *expr = ast->variableStatementOrDeclaration->expressionCast()) {
+ exportedValue = expression(expr);
+ }
+
+ exportedValue.loadInAccumulator();
+
+ const int defaultExportIndex = _context->locals.indexOf(_context->localNameForDefaultExport);
+ Q_ASSERT(defaultExportIndex != -1);
+ Reference defaultExportSlot = Reference::fromScopedLocal(this, defaultExportIndex, /*scope*/0);
+ defaultExportSlot.storeConsumeAccumulator();
+
return false;
}
@@ -581,21 +897,124 @@ bool Codegen::visit(UiQualifiedId *)
return false;
}
-bool Codegen::visit(UiQualifiedPragmaId *)
+bool Codegen::visit(VariableDeclarationList *)
{
Q_UNREACHABLE();
return false;
}
-bool Codegen::visit(VariableDeclaration *)
+bool Codegen::visit(ClassExpression *ast)
{
- Q_UNREACHABLE();
+ TailCallBlocker blockTailCalls(this);
+
+ Compiler::Class jsClass;
+ jsClass.nameIndex = registerString(ast->name.toString());
+
+ ClassElementList *constructor = nullptr;
+ int nComputedNames = 0;
+ int nStaticComputedNames = 0;
+
+ RegisterScope scope(this);
+ ControlFlowBlock controlFlow(this, ast);
+
+ for (auto *member = ast->elements; member; member = member->next) {
+ PatternProperty *p = member->property;
+ FunctionExpression *f = p->initializer->asFunctionDefinition();
+ Q_ASSERT(f);
+ AST::ComputedPropertyName *cname = AST::cast<ComputedPropertyName *>(p->name);
+ if (cname) {
+ ++nComputedNames;
+ if (member->isStatic)
+ ++nStaticComputedNames;
+ }
+ QString name = p->name->asString();
+ uint nameIndex = cname ? UINT_MAX : registerString(name);
+ Compiler::Class::Method::Type type = Compiler::Class::Method::Regular;
+ if (p->type == PatternProperty::Getter)
+ type = Compiler::Class::Method::Getter;
+ else if (p->type == PatternProperty::Setter)
+ type = Compiler::Class::Method::Setter;
+ Compiler::Class::Method m{ nameIndex, type, static_cast<uint>(defineFunction(name, f, f->formals, f->body)) };
+
+ if (member->isStatic) {
+ if (name == QStringLiteral("prototype")) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a static method named 'prototype'."));
+ return false;
+ }
+ jsClass.staticMethods << m;
+ } else {
+ if (name == QStringLiteral("constructor")) {
+ if (constructor) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a multiple constructors in a class."));
+ return false;
+ }
+ if (m.type != Compiler::Class::Method::Regular) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("Cannot declare a getter or setter named 'constructor'."));
+ return false;
+ }
+ constructor = member;
+ jsClass.constructorIndex = m.functionIndex;
+ continue;
+ }
+
+ jsClass.methods << m;
+ }
+ }
+
+ int classIndex = _module->classes.size();
+ _module->classes.append(jsClass);
+
+ Reference heritage = Reference::fromStackSlot(this);
+ if (ast->heritage) {
+ bytecodeGenerator->setLocation(ast->heritage->firstSourceLocation());
+ Reference r = expression(ast->heritage);
+ if (hasError)
+ return false;
+ r.storeOnStack(heritage.stackSlot());
+ } else {
+ Reference::fromConst(this, Value::emptyValue().asReturnedValue()).loadInAccumulator();
+ heritage.storeConsumeAccumulator();
+ }
+
+ int computedNames = nComputedNames ? bytecodeGenerator->newRegisterArray(nComputedNames) : 0;
+ int currentStaticName = computedNames;
+ int currentNonStaticName = computedNames + nStaticComputedNames;
+
+ for (auto *member = ast->elements; member; member = member->next) {
+ AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(member->property->name);
+ if (!cname)
+ continue;
+ RegisterScope scope(this);
+ bytecodeGenerator->setLocation(cname->firstSourceLocation());
+ Reference computedName = expression(cname->expression);
+ if (hasError)
+ return false;
+ computedName.storeOnStack(member->isStatic ? currentStaticName++ : currentNonStaticName++);
+ }
+
+ Instruction::CreateClass createClass;
+ createClass.classIndex = classIndex;
+ createClass.heritage = heritage.stackSlot();
+ createClass.computedNames = computedNames;
+
+ bytecodeGenerator->addInstruction(createClass);
+
+ if (!ast->name.isEmpty()) {
+ Reference ctor = referenceForName(ast->name.toString(), true);
+ ctor.isReferenceToConst = false; // this is the definition
+ (void) ctor.storeRetainAccumulator();
+ }
+
+ _expr.setResult(Reference::fromAccumulator(this));
return false;
}
-bool Codegen::visit(VariableDeclarationList *)
+bool Codegen::visit(ClassDeclaration *ast)
{
- Q_UNREACHABLE();
+ TailCallBlocker blockTailCalls(this);
+ Reference outerVar = referenceForName(ast->name.toString(), true);
+ visit(static_cast<ClassExpression *>(ast));
+ (void) outerVar.storeRetainAccumulator();
return false;
}
@@ -604,55 +1023,171 @@ bool Codegen::visit(Expression *ast)
if (hasError)
return false;
+ TailCallBlocker blockTailCalls(this);
statement(ast->left);
+ blockTailCalls.unblock();
accept(ast->right);
return false;
}
-bool Codegen::visit(ArrayLiteral *ast)
+bool Codegen::visit(ArrayPattern *ast)
{
if (hasError)
return false;
- RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
+
+ PatternElementList *it = ast->elements;
int argc = 0;
- int args = -1;
- auto push = [this, &argc, &args](AST::ExpressionNode *arg) {
- int temp = bytecodeGenerator->newRegister();
- if (args == -1)
- args = temp;
- if (!arg) {
- auto c = Reference::fromConst(this, Primitive::emptyValue().asReturnedValue());
- (void) c.storeOnStack(temp);
- } else {
- RegisterScope scope(this);
- (void) expression(arg).storeOnStack(temp);
+ {
+ RegisterScope scope(this);
+
+ int args = -1;
+ auto push = [this, &argc, &args](AST::ExpressionNode *arg) {
+ int temp = bytecodeGenerator->newRegister();
+ if (args == -1)
+ args = temp;
+ if (!arg) {
+ auto c = Reference::fromConst(this, Value::emptyValue().asReturnedValue());
+ (void) c.storeOnStack(temp);
+ } else {
+ RegisterScope scope(this);
+ Reference r = expression(arg);
+ if (hasError)
+ return;
+ (void) r.storeOnStack(temp);
+ }
+ ++argc;
+ };
+
+ for (; it; it = it->next) {
+ PatternElement *e = it->element;
+ if (e && e->type == PatternElement::SpreadElement)
+ break;
+ for (Elision *elision = it->elision; elision; elision = elision->next)
+ push(nullptr);
+
+ if (!e)
+ continue;
+
+ push(e->initializer);
+ if (hasError)
+ return false;
}
- ++argc;
- };
- for (ElementList *it = ast->elements; it; it = it->next) {
+ if (args == -1) {
+ Q_ASSERT(argc == 0);
+ args = 0;
+ }
- for (Elision *elision = it->elision; elision; elision = elision->next)
- push(nullptr);
+ Instruction::DefineArray call;
+ call.argc = argc;
+ call.args = Moth::StackSlot::createRegister(args);
+ bytecodeGenerator->addInstruction(call);
+ }
- push(it->expression);
- if (hasError)
- return false;
+ if (!it) {
+ _expr.setResult(Reference::fromAccumulator(this));
+ return false;
}
- for (Elision *elision = ast->elision; elision; elision = elision->next)
- push(nullptr);
+ Q_ASSERT(it->element && it->element->type == PatternElement::SpreadElement);
- if (args == -1) {
- Q_ASSERT(argc == 0);
- args = 0;
+ RegisterScope scope(this);
+ Reference array = Reference::fromStackSlot(this);
+ array.storeConsumeAccumulator();
+ Reference index = Reference::storeConstOnStack(this, Encode(argc));
+
+ auto pushAccumulator = [&]() {
+ Reference slot = Reference::fromSubscript(array, index);
+ slot.storeConsumeAccumulator();
+
+ index.loadInAccumulator();
+ Instruction::Increment inc;
+ bytecodeGenerator->addInstruction(inc);
+ index.storeConsumeAccumulator();
+ };
+
+ while (it) {
+ for (Elision *elision = it->elision; elision; elision = elision->next) {
+ Reference::fromConst(this, Value::emptyValue().asReturnedValue()).loadInAccumulator();
+ pushAccumulator();
+ }
+
+ if (!it->element) {
+ it = it->next;
+ continue;
+ }
+
+ // handle spread element
+ if (it->element->type == PatternElement::SpreadElement) {
+ RegisterScope scope(this);
+
+ Reference iterator = Reference::fromStackSlot(this);
+ Reference iteratorDone = Reference::fromConst(this, Encode(false)).storeOnStack();
+ Reference lhsValue = Reference::fromStackSlot(this);
+
+ // There should be a temporal block, so that variables declared in lhs shadow outside vars.
+ // This block should define a temporal dead zone for those variables, which is not yet implemented.
+ {
+ RegisterScope innerScope(this);
+ Reference expr = expression(it->element->initializer);
+ if (hasError)
+ return false;
+
+ expr.loadInAccumulator();
+ Instruction::GetIterator iteratorObjInstr;
+ iteratorObjInstr.iterator = static_cast<int>(AST::ForEachType::Of);
+ bytecodeGenerator->addInstruction(iteratorObjInstr);
+ iterator.storeConsumeAccumulator();
+ }
+
+ BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label done = bytecodeGenerator->newLabel();
+
+ {
+ auto cleanup = [this, iterator, iteratorDone]() {
+ iterator.loadInAccumulator();
+ Instruction::IteratorClose close;
+ close.done = iteratorDone.stackSlot();
+ bytecodeGenerator->addInstruction(close);
+ };
+ ControlFlowLoop flow(this, &end, &in, cleanup);
+ bytecodeGenerator->jump().link(in);
+
+ BytecodeGenerator::Label body = bytecodeGenerator->label();
+
+ lhsValue.loadInAccumulator();
+ pushAccumulator();
+
+ in.link();
+ iterator.loadInAccumulator();
+ Instruction::IteratorNext next;
+ next.value = lhsValue.stackSlot();
+ next.done = iteratorDone.stackSlot();
+ bytecodeGenerator->addInstruction(next);
+ bytecodeGenerator->addJumpInstruction(Instruction::JumpFalse()).link(body);
+ bytecodeGenerator->jump().link(done);
+
+ end.link();
+ }
+
+ done.link();
+ } else {
+ RegisterScope innerScope(this);
+ Reference expr = expression(it->element->initializer);
+ if (hasError)
+ return false;
+
+ expr.loadInAccumulator();
+ pushAccumulator();
+ }
+
+ it = it->next;
}
- Instruction::DefineArray call;
- call.argc = argc;
- call.args = Moth::StackSlot::createRegister(args);
- bytecodeGenerator->addInstruction(call);
+ array.loadInAccumulator();
_expr.setResult(Reference::fromAccumulator(this));
return false;
@@ -663,10 +1198,18 @@ bool Codegen::visit(ArrayMemberExpression *ast)
if (hasError)
return false;
+ TailCallBlocker blockTailCalls(this);
Reference base = expression(ast->base);
if (hasError)
return false;
+ if (base.isSuper()) {
+ Reference index = expression(ast->expression).storeOnStack();
+ _expr.setResult(Reference::fromSuperProperty(index));
+ return false;
+ }
base = base.storeOnStack();
+ if (hasError)
+ return false;
if (AST::StringLiteral *str = AST::cast<AST::StringLiteral *>(ast->expression)) {
QString s = str->value.toString();
uint arrayIndex = QV4::String::toArrayIndex(s);
@@ -692,6 +1235,7 @@ static QSOperator::Op baseOp(int op)
case QSOperator::InplaceAdd: return QSOperator::Add;
case QSOperator::InplaceLeftShift: return QSOperator::LShift;
case QSOperator::InplaceMod: return QSOperator::Mod;
+ case QSOperator::InplaceExp: return QSOperator::Exp;
case QSOperator::InplaceMul: return QSOperator::Mul;
case QSOperator::InplaceOr: return QSOperator::BitOr;
case QSOperator::InplaceRightShift: return QSOperator::RShift;
@@ -706,11 +1250,14 @@ bool Codegen::visit(BinaryExpression *ast)
if (hasError)
return false;
+ TailCallBlocker blockTailCalls(this);
+
if (ast->op == QSOperator::And) {
if (_expr.accept(cx)) {
auto iftrue = bytecodeGenerator->newLabel();
condition(ast->left, &iftrue, _expr.iffalse(), true);
iftrue.link();
+ blockTailCalls.unblock();
condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition());
} else {
auto iftrue = bytecodeGenerator->newLabel();
@@ -725,6 +1272,7 @@ bool Codegen::visit(BinaryExpression *ast)
bytecodeGenerator->jumpFalse().link(endif);
iftrue.link();
+ blockTailCalls.unblock();
Reference right = expression(ast->right);
if (hasError)
return false;
@@ -754,6 +1302,7 @@ bool Codegen::visit(BinaryExpression *ast)
bytecodeGenerator->jumpTrue().link(endif);
iffalse.link();
+ blockTailCalls.unblock();
Reference right = expression(ast->right);
if (hasError)
return false;
@@ -764,19 +1313,21 @@ bool Codegen::visit(BinaryExpression *ast)
_expr.setResult(Reference::fromAccumulator(this));
}
return false;
- }
-
- Reference left = expression(ast->left);
- if (hasError)
- return false;
-
- switch (ast->op) {
- case QSOperator::Or:
- case QSOperator::And:
- Q_UNREACHABLE(); // handled separately above
- break;
+ } else if (ast->op == QSOperator::Assign) {
+ if (AST::Pattern *p = ast->left->patternCast()) {
+ RegisterScope scope(this);
+ Reference right = expression(ast->right).storeOnStack();
+ destructurePattern(p, right);
+ if (!_expr.accept(nx)) {
+ right.loadInAccumulator();
+ _expr.setResult(Reference::fromAccumulator(this));
+ }
+ return false;
+ }
+ Reference left = expression(ast->left);
+ if (hasError)
+ return false;
- case QSOperator::Assign: {
if (!left.isLValue()) {
throwReferenceError(ast->operatorToken, QStringLiteral("left-hand side of assignment operator is not an lvalue"));
return false;
@@ -784,6 +1335,7 @@ bool Codegen::visit(BinaryExpression *ast)
left = left.asLValue();
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(left, ast->left->lastSourceLocation()))
return false;
+ blockTailCalls.unblock();
Reference r = expression(ast->right);
if (hasError)
return false;
@@ -792,15 +1344,27 @@ bool Codegen::visit(BinaryExpression *ast)
_expr.setResult(left.storeConsumeAccumulator());
else
_expr.setResult(left.storeRetainAccumulator());
- break;
+ return false;
}
+ Reference left = expression(ast->left);
+ if (hasError)
+ return false;
+
+ switch (ast->op) {
+ case QSOperator::Or:
+ case QSOperator::And:
+ case QSOperator::Assign:
+ Q_UNREACHABLE(); // handled separately above
+ break;
+
case QSOperator::InplaceAnd:
case QSOperator::InplaceSub:
case QSOperator::InplaceDiv:
case QSOperator::InplaceAdd:
case QSOperator::InplaceLeftShift:
case QSOperator::InplaceMod:
+ case QSOperator::InplaceExp:
case QSOperator::InplaceMul:
case QSOperator::InplaceOr:
case QSOperator::InplaceRightShift:
@@ -830,7 +1394,7 @@ bool Codegen::visit(BinaryExpression *ast)
case QSOperator::BitAnd:
case QSOperator::BitOr:
case QSOperator::BitXor:
- if (left.isConst()) {
+ if (left.isConstant()) {
Reference right = expression(ast->right);
if (hasError)
return false;
@@ -850,6 +1414,7 @@ bool Codegen::visit(BinaryExpression *ast)
case QSOperator::StrictNotEqual:
case QSOperator::Add:
case QSOperator::Div:
+ case QSOperator::Exp:
case QSOperator::Mod:
case QSOperator::Mul:
case QSOperator::Sub:
@@ -890,7 +1455,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Sub: {
- if (right.isConst() && right.constant == Encode(int(1))) {
+ if (right.isConstant() && right.constant == Encode(int(1))) {
left.loadInAccumulator();
bytecodeGenerator->addInstruction(Instruction::Decrement());
} else {
@@ -902,6 +1467,14 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
}
break;
}
+ case QSOperator::Exp: {
+ left = left.storeOnStack();
+ right.loadInAccumulator();
+ Instruction::Exp exp;
+ exp.lhs = left.stackSlot();
+ bytecodeGenerator->addInstruction(exp);
+ break;
+ }
case QSOperator::Mul: {
left = left.storeOnStack();
right.loadInAccumulator();
@@ -927,10 +1500,10 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::BitAnd:
- if (right.isConst()) {
- int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32();
- if (left.isConst()) {
- int result = Primitive::fromReturnedValue(left.constant).toInt32() & rightAsInt;
+ if (right.isConstant()) {
+ int rightAsInt = Value::fromReturnedValue(right.constant).toInt32();
+ if (left.isConstant()) {
+ int result = Value::fromReturnedValue(left.constant).toInt32() & rightAsInt;
return Reference::fromConst(this, Encode(result));
}
left.loadInAccumulator();
@@ -945,10 +1518,10 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
}
break;
case QSOperator::BitOr:
- if (right.isConst()) {
- int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32();
- if (left.isConst()) {
- int result = Primitive::fromReturnedValue(left.constant).toInt32() | rightAsInt;
+ if (right.isConstant()) {
+ int rightAsInt = Value::fromReturnedValue(right.constant).toInt32();
+ if (left.isConstant()) {
+ int result = Value::fromReturnedValue(left.constant).toInt32() | rightAsInt;
return Reference::fromConst(this, Encode(result));
}
left.loadInAccumulator();
@@ -963,10 +1536,10 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
}
break;
case QSOperator::BitXor:
- if (right.isConst()) {
- int rightAsInt = Primitive::fromReturnedValue(right.constant).toInt32();
- if (left.isConst()) {
- int result = Primitive::fromReturnedValue(left.constant).toInt32() ^ rightAsInt;
+ if (right.isConstant()) {
+ int rightAsInt = Value::fromReturnedValue(right.constant).toInt32();
+ if (left.isConstant()) {
+ int result = Value::fromReturnedValue(left.constant).toInt32() ^ rightAsInt;
return Reference::fromConst(this, Encode(result));
}
left.loadInAccumulator();
@@ -981,10 +1554,10 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
}
break;
case QSOperator::URShift:
- if (right.isConst()) {
+ if (right.isConstant()) {
left.loadInAccumulator();
Instruction::UShrConst ushr;
- ushr.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f;
+ ushr.rhs = Value::fromReturnedValue(right.constant).toInt32() & 0x1f;
bytecodeGenerator->addInstruction(ushr);
} else {
right.loadInAccumulator();
@@ -994,10 +1567,10 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
}
break;
case QSOperator::RShift:
- if (right.isConst()) {
+ if (right.isConstant()) {
left.loadInAccumulator();
Instruction::ShrConst shr;
- shr.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f;
+ shr.rhs = Value::fromReturnedValue(right.constant).toInt32() & 0x1f;
bytecodeGenerator->addInstruction(shr);
} else {
right.loadInAccumulator();
@@ -1007,10 +1580,10 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
}
break;
case QSOperator::LShift:
- if (right.isConst()) {
+ if (right.isConstant()) {
left.loadInAccumulator();
Instruction::ShlConst shl;
- shl.rhs = Primitive::fromReturnedValue(right.constant).toInt32() & 0x1f;
+ shl.rhs = Value::fromReturnedValue(right.constant).toInt32() & 0x1f;
bytecodeGenerator->addInstruction(shl);
} else {
right.loadInAccumulator();
@@ -1146,13 +1719,13 @@ static QSOperator::Op operatorForSwappedOperands(QSOperator::Op oper)
Codegen::Reference Codegen::jumpBinop(QSOperator::Op oper, Reference &left, Reference &right)
{
- if (left.isConst()) {
+ if (left.isConstant()) {
oper = operatorForSwappedOperands(oper);
qSwap(left, right);
}
- if (right.isConst() && (oper == QSOperator::Equal || oper == QSOperator::NotEqual)) {
- Value c = Primitive::fromReturnedValue(right.constant);
+ 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) {
@@ -1257,8 +1830,10 @@ bool Codegen::visit(CallExpression *ast)
return false;
RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
Reference base = expression(ast->base);
+
if (hasError)
return false;
switch (base.type) {
@@ -1270,15 +1845,62 @@ bool Codegen::visit(CallExpression *ast)
break;
case Reference::Name:
break;
+ case Reference::Super:
+ handleConstruct(base, ast->arguments);
+ return false;
+ case Reference::SuperProperty:
+ break;
default:
base = base.storeOnStack();
break;
}
+ int thisObject = bytecodeGenerator->newRegister();
+ int functionObject = bytecodeGenerator->newRegister();
+
auto calldata = pushArgs(ast->arguments);
if (hasError)
return false;
+ blockTailCalls.unblock();
+ if (calldata.hasSpread || _tailCallsAreAllowed) {
+ Reference baseObject = base.baseObject();
+ if (!baseObject.isStackSlot()) {
+ baseObject.storeOnStack(thisObject);
+ baseObject = Reference::fromStackSlot(this, thisObject);
+ }
+ if (!base.isStackSlot()) {
+ base.storeOnStack(functionObject);
+ base = Reference::fromStackSlot(this, functionObject);
+ }
+
+ if (calldata.hasSpread) {
+ Instruction::CallWithSpread call;
+ call.func = base.stackSlot();
+ call.thisObject = baseObject.stackSlot();
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(call);
+ } else {
+ Instruction::TailCall call;
+ call.func = base.stackSlot();
+ call.thisObject = baseObject.stackSlot();
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(call);
+ }
+
+ _expr.setResult(Reference::fromAccumulator(this));
+ return false;
+
+ }
+
+ handleCall(base, calldata, functionObject, thisObject);
+ return false;
+}
+
+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;
@@ -1295,7 +1917,7 @@ bool Codegen::visit(CallExpression *ast)
call.argv = calldata.argv;
bytecodeGenerator->addInstruction(call);
} else if (base.type == Reference::Member) {
- if (useFastLookups) {
+ if (!disable_lookups && useFastLookups) {
Instruction::CallPropertyLookup call;
call.base = base.propertyBase.stackSlot();
call.lookupIndex = registerGetterLookup(base.propertyNameIndex);
@@ -1323,7 +1945,7 @@ bool Codegen::visit(CallExpression *ast)
call.argc = calldata.argc;
call.argv = calldata.argv;
bytecodeGenerator->addInstruction(call);
- } else if (useFastLookups && base.global) {
+ } else if (!disable_lookups && useFastLookups && base.global) {
Instruction::CallGlobalLookup call;
call.index = registerGlobalGetterLookup(base.nameAsIndex());
call.argc = calldata.argc;
@@ -1336,6 +1958,22 @@ bool Codegen::visit(CallExpression *ast)
call.argv = calldata.argv;
bytecodeGenerator->addInstruction(call);
}
+ } else if (base.type == Reference::SuperProperty) {
+ Reference receiver = base.baseObject();
+ if (!base.isStackSlot()) {
+ base.storeOnStack(slotForFunction);
+ base = Reference::fromStackSlot(this, slotForFunction);
+ }
+ if (!receiver.isStackSlot()) {
+ receiver.storeOnStack(slotForThisObject);
+ receiver = Reference::fromStackSlot(this, slotForThisObject);
+ }
+ Instruction::CallWithReceiver call;
+ call.name = base.stackSlot();
+ call.thisObject = receiver.stackSlot();
+ call.argc = calldata.argc;
+ call.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(call);
} else {
Q_ASSERT(base.isStackSlot());
Instruction::CallValue call;
@@ -1346,36 +1984,69 @@ bool Codegen::visit(CallExpression *ast)
}
_expr.setResult(Reference::fromAccumulator(this));
- return false;
}
Codegen::Arguments Codegen::pushArgs(ArgumentList *args)
{
+ bool hasSpread = false;
int argc = 0;
- for (ArgumentList *it = args; it; it = it->next)
+ for (ArgumentList *it = args; it; it = it->next) {
+ if (it->isSpreadElement) {
+ hasSpread = true;
+ ++argc;
+ }
++argc;
+ }
if (!argc)
- return { 0, 0 };
+ return { 0, 0, false };
int calldata = bytecodeGenerator->newRegisterArray(argc);
argc = 0;
for (ArgumentList *it = args; it; it = it->next) {
+ if (it->isSpreadElement) {
+ Reference::fromConst(this, Value::emptyValue().asReturnedValue()).storeOnStack(calldata + argc);
+ ++argc;
+ }
RegisterScope scope(this);
Reference e = expression(it->expression);
if (hasError)
break;
- if (!argc && !it->next) {
+ if (!argc && !it->next && !hasSpread) {
// avoid copy for functions taking a single argument
if (e.isStackSlot())
- return { 1, e.stackSlot() };
+ return { 1, e.stackSlot(), hasSpread };
}
(void) e.storeOnStack(calldata + argc);
++argc;
}
- return { argc, calldata };
+ return { argc, calldata, hasSpread };
+}
+
+Codegen::Arguments Codegen::pushTemplateArgs(TemplateLiteral *args)
+{
+ int argc = 0;
+ for (TemplateLiteral *it = args; it; it = it->next)
+ ++argc;
+
+ if (!argc)
+ return { 0, 0, false };
+
+ int calldata = bytecodeGenerator->newRegisterArray(argc);
+
+ argc = 0;
+ for (TemplateLiteral *it = args; it && it->expression; it = it->next) {
+ RegisterScope scope(this);
+ Reference e = expression(it->expression);
+ if (hasError)
+ break;
+ (void) e.storeOnStack(calldata + argc);
+ ++argc;
+ }
+
+ return { argc, calldata, false };
}
bool Codegen::visit(ConditionalExpression *ast)
@@ -1384,11 +2055,14 @@ bool Codegen::visit(ConditionalExpression *ast)
return true;
RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
BytecodeGenerator::Label iftrue = bytecodeGenerator->newLabel();
BytecodeGenerator::Label iffalse = bytecodeGenerator->newLabel();
condition(ast->expression, &iftrue, &iffalse, true);
+ blockTailCalls.unblock();
+
iftrue.link();
Reference ok = expression(ast->ok);
if (hasError)
@@ -1413,11 +2087,16 @@ bool Codegen::visit(DeleteExpression *ast)
if (hasError)
return false;
+ RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
Reference expr = expression(ast->expression);
if (hasError)
return false;
switch (expr.type) {
+ case Reference::SuperProperty:
+ // ### this should throw a reference error at runtime.
+ return false;
case Reference::StackSlot:
if (!expr.stackSlotIsLocalOrArgument)
break;
@@ -1444,9 +2123,14 @@ bool Codegen::visit(DeleteExpression *ast)
case Reference::Member: {
//### maybe add a variant where the base can be in the accumulator?
expr = expr.asLValue();
- Instruction::DeleteMember del;
+ Instruction::LoadRuntimeString instr;
+ instr.stringId = expr.propertyNameIndex;
+ bytecodeGenerator->addInstruction(instr);
+ Reference index = Reference::fromStackSlot(this);
+ index.storeConsumeAccumulator();
+ Instruction::DeleteProperty del;
del.base = expr.propertyBase.stackSlot();
- del.member = expr.propertyNameIndex;
+ del.index = index.stackSlot();
bytecodeGenerator->addInstruction(del);
_expr.setResult(Reference::fromAccumulator(this));
return false;
@@ -1454,7 +2138,7 @@ bool Codegen::visit(DeleteExpression *ast)
case Reference::Subscript: {
//### maybe add a variant where the index can be in the accumulator?
expr = expr.asLValue();
- Instruction::DeleteSubscript del;
+ Instruction::DeleteProperty del;
del.base = expr.elementBase;
del.index = expr.elementSubscript.stackSlot();
bytecodeGenerator->addInstruction(del);
@@ -1478,109 +2162,190 @@ bool Codegen::visit(FalseLiteral *)
return false;
}
+bool Codegen::visit(SuperLiteral *)
+{
+ if (hasError)
+ return false;
+
+ _expr.setResult(Reference::fromSuper(this));
+ return false;
+}
+
bool Codegen::visit(FieldMemberExpression *ast)
{
if (hasError)
return false;
+ TailCallBlocker blockTailCalls(this);
+ if (AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(ast->base)) {
+ if (id->name == QLatin1String("new")) {
+ // new.target
+ Q_ASSERT(ast->name == QLatin1String("target"));
+
+ if (_context->isArrowFunction || _context->contextType == ContextType::Eval) {
+ Reference r = referenceForName(QStringLiteral("new.target"), false);
+ r.isReadonly = true;
+ _expr.setResult(r);
+ return false;
+ }
+
+ Reference r = Reference::fromStackSlot(this, CallData::NewTarget);
+ _expr.setResult(r);
+ return false;
+ }
+ }
+
Reference base = expression(ast->base);
if (hasError)
return false;
+ if (base.isSuper()) {
+ Instruction::LoadRuntimeString load;
+ load.stringId = registerString(ast->name.toString());
+ bytecodeGenerator->addInstruction(load);
+ Reference property = Reference::fromAccumulator(this).storeOnStack();
+ _expr.setResult(Reference::fromSuperProperty(property));
+ return false;
+ }
_expr.setResult(Reference::fromMember(base, ast->name.toString()));
return false;
}
-bool Codegen::visit(FunctionExpression *ast)
+bool Codegen::visit(TaggedTemplate *ast)
{
if (hasError)
return false;
RegisterScope scope(this);
- int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : nullptr);
- loadClosure(function);
- _expr.setResult(Reference::fromAccumulator(this));
+ int functionObject = -1, thisObject = -1;
+
+ Reference base = expression(ast->base);
+ if (hasError)
+ return false;
+ switch (base.type) {
+ case Reference::Member:
+ case Reference::Subscript:
+ base = base.asLValue();
+ break;
+ case Reference::Name:
+ break;
+ case Reference::SuperProperty:
+ thisObject = bytecodeGenerator->newRegister();
+ functionObject = bytecodeGenerator->newRegister();
+ break;
+ default:
+ base = base.storeOnStack();
+ break;
+ }
+
+ int arrayTemp = createTemplateArray(ast->templateLiteral);
+ Q_UNUSED(arrayTemp);
+ auto calldata = pushTemplateArgs(ast->templateLiteral);
+ if (hasError)
+ return false;
+ ++calldata.argc;
+ Q_ASSERT(calldata.argv == arrayTemp + 1);
+ --calldata.argv;
+
+ handleCall(base, calldata, functionObject, thisObject);
return false;
}
-Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs)
+int Codegen::createTemplateArray(TemplateLiteral *t)
{
- int scope = 0;
- Context *c = _context;
+ int arrayTemp = bytecodeGenerator->newRegister();
- // skip the innermost context if it's simple (as the runtime won't
- // create a context for it
- if (c->canUseSimpleCall()) {
- Context::Member m = c->findMember(name);
- if (m.type != Context::UndefinedMember) {
- Q_ASSERT((!m.canEscape));
- Reference r = Reference::fromStackSlot(this, m.index, true /*isLocal*/);
- if (name == QLatin1String("arguments") || name == QLatin1String("eval")) {
- r.isArgOrEval = true;
- if (isLhs && c->isStrict)
- // ### add correct source location
- throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode"));
- }
- return r;
- }
- const int argIdx = c->findArgument(name);
- if (argIdx != -1) {
- Q_ASSERT(!c->argumentsCanEscape && (c->usesArgumentsObject != Context::ArgumentsObjectUsed || c->isStrict));
- return Reference::fromArgument(this, argIdx, _volataleMemoryLocations.isVolatile(name));
- }
- c = c->parent;
- }
+ RegisterScope scope(this);
- while (c->parent) {
- if (c->forceLookupByName())
- goto loadByName;
+ int argc = 0;
+ int args = -1;
+ auto push = [this, &argc, &args](const QStringRef &arg) {
+ int temp = bytecodeGenerator->newRegister();
+ if (args == -1)
+ args = temp;
+ Instruction::LoadRuntimeString instr;
+ instr.stringId = registerString(arg.toString());
+ bytecodeGenerator->addInstruction(instr);
+ Instruction::StoreReg store;
+ store.reg = temp;
+ bytecodeGenerator->addInstruction(store);
- Context::Member m = c->findMember(name);
- if (m.type != Context::UndefinedMember) {
- Reference r = m.canEscape ? Reference::fromScopedLocal(this, m.index, scope)
- : Reference::fromStackSlot(this, m.index, true /*isLocal*/);
- if (name == QLatin1String("arguments") || name == QLatin1String("eval")) {
- r.isArgOrEval = true;
- if (isLhs && c->isStrict)
- // ### add correct source location
- throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode"));
- }
- return r;
- }
- const int argIdx = c->findArgument(name);
- if (argIdx != -1) {
- if (c->argumentsCanEscape || c->usesArgumentsObject == Context::ArgumentsObjectUsed) {
- int idx = argIdx + c->locals.size();
- return Reference::fromScopedLocal(this, idx, scope);
- } else {
- Q_ASSERT(scope == 0);
- return Reference::fromArgument(this, argIdx, _volataleMemoryLocations.isVolatile(name));
- }
- }
+ ++argc;
+ };
- if (!c->isStrict && c->hasDirectEval)
- goto loadByName;
+ for (TemplateLiteral *it = t; it; it = it->next)
+ push(it->value);
- ++scope;
- c = c->parent;
+ if (args == -1) {
+ Q_ASSERT(argc == 0);
+ args = 0;
}
- {
- // This hook allows implementing QML lookup semantics
- Reference fallback = fallbackNameLookup(name);
- if (fallback.type != Reference::Invalid)
- return fallback;
- }
+ Instruction::DefineArray call;
+ call.argc = argc;
+ call.args = Moth::StackSlot::createRegister(args);
+ bytecodeGenerator->addInstruction(call);
+
+ Instruction::StoreReg store;
+ store.reg = arrayTemp;
+ bytecodeGenerator->addInstruction(store);
+
+ return arrayTemp;
+}
+
+bool Codegen::visit(FunctionExpression *ast)
+{
+ if (hasError)
+ return false;
+
+ TailCallBlocker blockTailCalls(this);
- if (!c->parent && !c->forceLookupByName() && _context->compilationMode != EvalCode && c->compilationMode != QmlBinding) {
- Reference r = Reference::fromName(this, name);
- r.global = true;
+ RegisterScope scope(this);
+
+ int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body);
+ if (hasError)
+ return false;
+ loadClosure(function);
+ _expr.setResult(Reference::fromAccumulator(this));
+ return false;
+}
+
+Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs, const SourceLocation &accessLocation)
+{
+ Context::ResolvedName resolved = _context->resolveName(name, accessLocation);
+
+ if (resolved.type == Context::ResolvedName::Local || resolved.type == Context::ResolvedName::Stack
+ || resolved.type == Context::ResolvedName::Import) {
+ if (resolved.isArgOrEval && isLhs)
+ // ### add correct source location
+ throwSyntaxError(SourceLocation(), QStringLiteral("Variable name may not be eval or arguments in strict mode"));
+ Reference r;
+ switch (resolved.type) {
+ case Context::ResolvedName::Local:
+ r = Reference::fromScopedLocal(this, resolved.index, resolved.scope); break;
+ case Context::ResolvedName::Stack:
+ r = Reference::fromStackSlot(this, resolved.index, true /*isLocal*/); break;
+ case Context::ResolvedName::Import:
+ r = Reference::fromImport(this, resolved.index); break;
+ default: Q_UNREACHABLE();
+ }
+ if (r.isStackSlot() && _volatileMemoryLocations.isVolatile(name))
+ r.isVolatile = true;
+ r.isArgOrEval = resolved.isArgOrEval;
+ r.isReferenceToConst = resolved.isConst;
+ r.requiresTDZCheck = resolved.requiresTDZCheck;
+ r.name = name; // used to show correct name at run-time when TDZ check fails.
return r;
}
- // global context or with. Lookup by name
- loadByName:
- return Reference::fromName(this, name);
+ // 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);
+ return r;
}
void Codegen::loadClosure(int closureId)
@@ -1605,7 +2370,7 @@ bool Codegen::visit(IdentifierExpression *ast)
if (hasError)
return false;
- _expr.setResult(referenceForName(ast->name.toString(), false));
+ _expr.setResult(referenceForName(ast->name.toString(), false, ast->firstSourceLocation()));
return false;
}
@@ -1618,25 +2383,63 @@ bool Codegen::visit(NestedExpression *ast)
return false;
}
+void Codegen::handleConstruct(const Reference &base, ArgumentList *arguments)
+{
+ Reference constructor;
+ if (base.isSuper()) {
+ Instruction::LoadSuperConstructor super;
+ bytecodeGenerator->addInstruction(super);
+ constructor = Reference::fromAccumulator(this).storeOnStack();
+ } else {
+ constructor = base.storeOnStack();
+ }
+
+ auto calldata = pushArgs(arguments);
+ if (hasError)
+ return;
+
+ if (base.isSuper())
+ Reference::fromStackSlot(this, CallData::NewTarget).loadInAccumulator();
+ else
+ constructor.loadInAccumulator();
+
+ if (calldata.hasSpread) {
+ Instruction::ConstructWithSpread create;
+ create.func = constructor.stackSlot();
+ create.argc = calldata.argc;
+ create.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(create);
+ } else {
+ Instruction::Construct create;
+ create.func = constructor.stackSlot();
+ create.argc = calldata.argc;
+ create.argv = calldata.argv;
+ bytecodeGenerator->addInstruction(create);
+ }
+ if (base.isSuper())
+ // set the result up as the thisObject
+ Reference::fromAccumulator(this).storeOnStack(CallData::This);
+
+ _expr.setResult(Reference::fromAccumulator(this));
+}
+
bool Codegen::visit(NewExpression *ast)
{
if (hasError)
return false;
RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
Reference base = expression(ast->expression);
if (hasError)
return false;
- //### Maybe create a ConstructA that takes an accumulator?
- base = base.storeOnStack();
+ if (base.isSuper()) {
+ throwSyntaxError(ast->expression->firstSourceLocation(), QStringLiteral("Cannot use new with super."));
+ return false;
+ }
- Instruction::Construct create;
- create.func = base.stackSlot();
- create.argc = 0;
- create.argv = 0;
- bytecodeGenerator->addInstruction(create);
- _expr.setResult(Reference::fromAccumulator(this));
+ handleConstruct(base, nullptr);
return false;
}
@@ -1646,22 +2449,17 @@ bool Codegen::visit(NewMemberExpression *ast)
return false;
RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
Reference base = expression(ast->base);
if (hasError)
return false;
- base = base.storeOnStack();
-
- auto calldata = pushArgs(ast->arguments);
- if (hasError)
+ if (base.isSuper()) {
+ throwSyntaxError(ast->base->firstSourceLocation(), QStringLiteral("Cannot use new with super."));
return false;
+ }
- Instruction::Construct create;
- create.func = base.stackSlot();
- create.argc = calldata.argc;
- create.argv = calldata.argv;
- bytecodeGenerator->addInstruction(create);
- _expr.setResult(Reference::fromAccumulator(this));
+ handleConstruct(base, ast->arguments);
return false;
}
@@ -1670,6 +2468,7 @@ bool Codegen::visit(NotExpression *ast)
if (hasError)
return false;
+ TailCallBlocker blockTailCalls(this);
_expr.setResult(unop(Not, expression(ast->expression)));
return false;
}
@@ -1696,133 +2495,119 @@ bool Codegen::visit(NumericLiteral *ast)
return false;
}
-bool Codegen::visit(ObjectLiteral *ast)
+bool Codegen::visit(ObjectPattern *ast)
{
if (hasError)
return false;
+ TailCallBlocker blockTailCalls(this);
+
+ QVector<QPair<Reference, ObjectPropertyValue>> computedProperties;
QMap<QString, ObjectPropertyValue> valueMap;
RegisterScope scope(this);
- for (PropertyAssignmentList *it = ast->properties; it; it = it->next) {
- QString name = it->assignment->name->asString();
- if (PropertyNameAndValue *nv = AST::cast<AST::PropertyNameAndValue *>(it->assignment)) {
- Reference value = expression(nv->value);
- if (hasError)
- return false;
-
- ObjectPropertyValue &v = valueMap[name];
- if (v.hasGetter() || v.hasSetter() || (_context->isStrict && v.rvalue.isValid())) {
- throwSyntaxError(nv->lastSourceLocation(),
- QStringLiteral("Illegal duplicate key '%1' in object literal").arg(name));
- return false;
- }
+ QStringList members;
- v.rvalue = value.storeOnStack();
- } else if (PropertyGetterSetter *gs = AST::cast<AST::PropertyGetterSetter *>(it->assignment)) {
- const int function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : nullptr);
- ObjectPropertyValue &v = valueMap[name];
- if (v.rvalue.isValid() ||
- (gs->type == PropertyGetterSetter::Getter && v.hasGetter()) ||
- (gs->type == PropertyGetterSetter::Setter && v.hasSetter())) {
- throwSyntaxError(gs->lastSourceLocation(),
- QStringLiteral("Illegal duplicate key '%1' in object literal").arg(name));
- return false;
- }
- if (gs->type == PropertyGetterSetter::Getter)
- v.getter = function;
- else
- v.setter = function;
- } else {
- Q_UNREACHABLE();
- }
- }
-
- QVector<QString> nonArrayKey, arrayKeyWithValue, arrayKeyWithGetterSetter;
- bool needSparseArray = false; // set to true if any array index is bigger than 16
-
- for (QMap<QString, ObjectPropertyValue>::iterator it = valueMap.begin(), eit = valueMap.end();
- it != eit; ++it) {
- QString name = it.key();
- uint keyAsIndex = QV4::String::toArrayIndex(name);
- if (keyAsIndex != std::numeric_limits<uint>::max()) {
- it->keyAsIndex = keyAsIndex;
- if (keyAsIndex > 16)
- needSparseArray = true;
- if (it->hasSetter() || it->hasGetter())
- arrayKeyWithGetterSetter.append(name);
- else
- arrayKeyWithValue.append(name);
- } else {
- nonArrayKey.append(name);
- }
- }
-
- int args = -1;
- auto push = [this, &args](const Reference &arg) {
+ int argc = 0;
+ int args = 0;
+ auto push = [this, &args, &argc](const Reference &arg) {
int temp = bytecodeGenerator->newRegister();
- if (args == -1)
+ if (argc == 0)
args = temp;
(void) arg.storeOnStack(temp);
+ ++argc;
};
- QVector<QV4::Compiler::JSUnitGenerator::MemberInfo> members;
+ PatternPropertyList *it = ast->properties;
+ for (; it; it = it->next) {
+ PatternProperty *p = it->property;
+ AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(p->name);
+ if (cname || p->type != PatternProperty::Literal)
+ break;
+ QString name = p->name->asString();
+ uint arrayIndex = QV4::String::toArrayIndex(name);
+ if (arrayIndex != UINT_MAX)
+ break;
+ if (members.contains(name))
+ break;
+ members.append(name);
- Reference acc = Reference::fromAccumulator(this);
- // generate the key/value pairs
- for (const QString &key : qAsConst(nonArrayKey)) {
- const ObjectPropertyValue &prop = valueMap[key];
-
- if (prop.hasGetter() || prop.hasSetter()) {
- Q_ASSERT(!prop.rvalue.isValid());
- loadClosure(prop.getter);
- push(acc);
- loadClosure(prop.setter);
- push(acc);
- members.append({ key, true });
- } else {
- Q_ASSERT(prop.rvalue.isValid());
- push(prop.rvalue);
- members.append({ key, false });
+ {
+ RegisterScope innerScope(this);
+ Reference value = expression(p->initializer);
+ if (hasError)
+ return false;
+ value.loadInAccumulator();
}
- }
-
- // generate array entries with values
- for (const QString &key : qAsConst(arrayKeyWithValue)) {
- const ObjectPropertyValue &prop = valueMap[key];
- Q_ASSERT(!prop.hasGetter() && !prop.hasSetter());
- push(Reference::fromConst(this, Encode(prop.keyAsIndex)));
- push(prop.rvalue);
- }
-
- // generate array entries with both a value and a setter
- for (const QString &key : qAsConst(arrayKeyWithGetterSetter)) {
- const ObjectPropertyValue &prop = valueMap[key];
- Q_ASSERT(!prop.rvalue.isValid());
- push(Reference::fromConst(this, Encode(prop.keyAsIndex)));
- loadClosure(prop.getter);
- push(acc);
- loadClosure(prop.setter);
- push(acc);
+ push(Reference::fromAccumulator(this));
}
int classId = jsUnitGenerator->registerJSClass(members);
- uint arrayGetterSetterCountAndFlags = arrayKeyWithGetterSetter.size();
- arrayGetterSetterCountAndFlags |= needSparseArray << 30;
-
- if (args == -1)
- args = 0;
+ // handle complex property setters
+ for (; it; it = it->next) {
+ PatternProperty *p = it->property;
+ AST::ComputedPropertyName *cname = AST::cast<AST::ComputedPropertyName *>(p->name);
+ ObjectLiteralArgument argType = ObjectLiteralArgument::Value;
+ if (p->type == PatternProperty::Method)
+ argType = ObjectLiteralArgument::Method;
+ else if (p->type == PatternProperty::Getter)
+ argType = ObjectLiteralArgument::Getter;
+ else if (p->type == PatternProperty::Setter)
+ argType = ObjectLiteralArgument::Setter;
+
+ Reference::fromConst(this, Encode(int(argType))).loadInAccumulator();
+ push(Reference::fromAccumulator(this));
+
+ if (cname) {
+ RegisterScope innerScope(this);
+ Reference name = expression(cname->expression);
+ if (hasError)
+ return false;
+ name.loadInAccumulator();
+ } else {
+ QString name = p->name->asString();
+#if 0
+ uint arrayIndex = QV4::String::toArrayIndex(name);
+ if (arrayIndex != UINT_MAX) {
+ Reference::fromConst(this, Encode(arrayIndex)).loadInAccumulator();
+ } else
+#endif
+ {
+ Instruction::LoadRuntimeString instr;
+ instr.stringId = registerString(name);
+ bytecodeGenerator->addInstruction(instr);
+ }
+ }
+ push(Reference::fromAccumulator(this));
+ {
+ RegisterScope innerScope(this);
+ if (p->type != PatternProperty::Literal) {
+ // need to get the closure id for the method
+ FunctionExpression *f = p->initializer->asFunctionDefinition();
+ Q_ASSERT(f);
+ int function = defineFunction(f->name.toString(), f, f->formals, f->body);
+ if (hasError)
+ return false;
+ Reference::fromConst(this, Encode(function)).loadInAccumulator();
+ } else {
+ Reference value = expression(p->initializer);
+ if (hasError)
+ return false;
+ value.loadInAccumulator();
+ }
+ }
+ push(Reference::fromAccumulator(this));
+ }
Instruction::DefineObjectLiteral call;
call.internalClassId = classId;
- call.arrayValueCount = arrayKeyWithValue.size();
- call.arrayGetterSetterCountAndFlags = arrayGetterSetterCountAndFlags;
+ call.argc = argc;
call.args = Moth::StackSlot::createRegister(args);
bytecodeGenerator->addInstruction(call);
-
- _expr.setResult(Reference::fromAccumulator(this));
+ Reference result = Reference::fromAccumulator(this);
+ _expr.setResult(result);
return false;
}
@@ -1933,11 +2718,62 @@ bool Codegen::visit(StringLiteral *ast)
return false;
}
+bool Codegen::visit(TemplateLiteral *ast)
+{
+ if (hasError)
+ return false;
+
+ TailCallBlocker blockTailCalls(this);
+
+ Instruction::LoadRuntimeString instr;
+ instr.stringId = registerString(ast->value.toString());
+ bytecodeGenerator->addInstruction(instr);
+
+ if (ast->expression) {
+ RegisterScope scope(this);
+ int temp = bytecodeGenerator->newRegister();
+ Instruction::StoreReg store;
+ store.reg = temp;
+ bytecodeGenerator->addInstruction(store);
+
+ Reference expr = expression(ast->expression);
+
+ if (ast->next) {
+ int temp2 = bytecodeGenerator->newRegister();
+ expr.storeOnStack(temp2);
+ visit(ast->next);
+
+ Instruction::Add instr;
+ instr.lhs = temp2;
+ bytecodeGenerator->addInstruction(instr);
+ } else {
+ expr.loadInAccumulator();
+ }
+
+ Instruction::Add instr;
+ instr.lhs = temp;
+ bytecodeGenerator->addInstruction(instr);
+ }
+
+ auto r = Reference::fromAccumulator(this);
+ r.isReadonly = true;
+
+ _expr.setResult(r);
+ return false;
+
+}
+
bool Codegen::visit(ThisExpression *)
{
if (hasError)
return false;
+ if (_context->isArrowFunction) {
+ Reference r = referenceForName(QStringLiteral("this"), false);
+ r.isReadonly = true;
+ _expr.setResult(r);
+ return false;
+ }
_expr.setResult(Reference::fromThis(this));
return false;
}
@@ -1947,6 +2783,7 @@ bool Codegen::visit(TildeExpression *ast)
if (hasError)
return false;
+ TailCallBlocker blockTailCalls(this);
_expr.setResult(unop(Compl, expression(ast->expression)));
return false;
}
@@ -1966,6 +2803,7 @@ bool Codegen::visit(TypeOfExpression *ast)
return false;
RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
Reference expr = expression(ast->expression);
if (hasError)
@@ -1991,6 +2829,7 @@ bool Codegen::visit(UnaryMinusExpression *ast)
if (hasError)
return false;
+ TailCallBlocker blockTailCalls(this);
_expr.setResult(unop(UMinus, expression(ast->expression)));
return false;
}
@@ -2000,6 +2839,7 @@ bool Codegen::visit(UnaryPlusExpression *ast)
if (hasError)
return false;
+ TailCallBlocker blockTailCalls(this);
_expr.setResult(unop(UPlus, expression(ast->expression)));
return false;
}
@@ -2010,6 +2850,7 @@ bool Codegen::visit(VoidExpression *ast)
return false;
RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
statement(ast->expression);
_expr.setResult(Reference::fromConst(this, Encode::undefined()));
@@ -2021,15 +2862,83 @@ bool Codegen::visit(FunctionDeclaration * ast)
if (hasError)
return false;
+ // no need to block tail calls: the function body isn't visited here.
RegisterScope scope(this);
- if (_context->compilationMode == QmlBinding)
- Reference::fromName(this, ast->name.toString()).loadInAccumulator();
+ if (_functionContext->contextType == ContextType::Binding)
+ referenceForName(ast->name.toString(), true).loadInAccumulator();
_expr.accept(nx);
return false;
}
-static bool endsWithReturn(Node *node)
+bool Codegen::visit(YieldExpression *ast)
+{
+ if (inFormalParameterList) {
+ throwSyntaxError(ast->firstSourceLocation(), QLatin1String("yield is not allowed inside parameter lists"));
+ return false;
+ }
+
+ RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
+ Reference expr = ast->expression ? expression(ast->expression) : Reference::fromConst(this, Encode::undefined());
+ if (hasError)
+ return false;
+
+ Reference acc = Reference::fromAccumulator(this);
+
+ if (ast->isYieldStar) {
+ Reference iterator = Reference::fromStackSlot(this);
+ Reference lhsValue = Reference::fromConst(this, Encode::undefined()).storeOnStack();
+
+ expr.loadInAccumulator();
+ Instruction::GetIterator getIterator;
+ getIterator.iterator = static_cast<int>(AST::ForEachType::Of);
+ bytecodeGenerator->addInstruction(getIterator);
+ iterator.storeConsumeAccumulator();
+ Instruction::LoadUndefined load;
+ bytecodeGenerator->addInstruction(load);
+
+ BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
+ bytecodeGenerator->jump().link(in);
+
+ BytecodeGenerator::Label loop = bytecodeGenerator->label();
+
+ lhsValue.loadInAccumulator();
+ Instruction::YieldStar yield;
+ bytecodeGenerator->addInstruction(yield);
+
+ in.link();
+
+ Instruction::IteratorNextForYieldStar next;
+ next.object = lhsValue.stackSlot();
+ next.iterator = iterator.stackSlot();
+ bytecodeGenerator->addInstruction(next);
+
+ BytecodeGenerator::Jump done = bytecodeGenerator->jumpTrue();
+ bytecodeGenerator->jumpNotUndefined().link(loop);
+ lhsValue.loadInAccumulator();
+ emitReturn(acc);
+
+
+ done.link();
+
+ lhsValue.loadInAccumulator();
+ _expr.setResult(acc);
+ return false;
+ }
+
+ expr.loadInAccumulator();
+ Instruction::Yield yield;
+ bytecodeGenerator->addInstruction(yield);
+ Instruction::Resume resume;
+ BytecodeGenerator::Jump jump = bytecodeGenerator->addJumpInstruction(resume);
+ emitReturn(acc);
+ jump.link();
+ _expr.setResult(acc);
+ return false;
+}
+
+static bool endsWithReturn(Module *module, Node *node)
{
if (!node)
return false;
@@ -2038,32 +2947,29 @@ static bool endsWithReturn(Node *node)
if (AST::cast<ThrowStatement *>(node))
return true;
if (Program *p = AST::cast<Program *>(node))
- return endsWithReturn(p->elements);
- if (SourceElements *se = AST::cast<SourceElements *>(node)) {
- while (se->next)
- se = se->next;
- return endsWithReturn(se->element);
- }
- if (StatementSourceElement *sse = AST::cast<StatementSourceElement *>(node))
- return endsWithReturn(sse->statement);
+ return endsWithReturn(module, p->statements);
if (StatementList *sl = AST::cast<StatementList *>(node)) {
while (sl->next)
sl = sl->next;
- return endsWithReturn(sl->statement);
+ return endsWithReturn(module, sl->statement);
+ }
+ if (Block *b = AST::cast<Block *>(node)) {
+ Context *blockContext = module->contextMap.value(node);
+ if (blockContext->requiresExecutionContext)
+ // we need to emit a return statement here, because of the
+ // unwind handler
+ return false;
+ return endsWithReturn(module, b->statements);
}
- if (Block *b = AST::cast<Block *>(node))
- return endsWithReturn(b->statements);
if (IfStatement *is = AST::cast<IfStatement *>(node))
- return is->ko && endsWithReturn(is->ok) && endsWithReturn(is->ko);
+ return is->ko && endsWithReturn(module, is->ok) && endsWithReturn(module, is->ko);
return false;
}
int Codegen::defineFunction(const QString &name, AST::Node *ast,
AST::FormalParameterList *formals,
- AST::SourceElements *body)
+ AST::StatementList *body)
{
- Q_UNUSED(formals);
-
enterContext(ast);
if (_context->functionIndex >= 0)
@@ -2074,7 +2980,15 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
_module->functions.append(_context);
_context->functionIndex = _module->functions.count() - 1;
- _context->hasDirectEval |= (_context->compilationMode == EvalCode || _context->compilationMode == GlobalCode || _module->debugMode); // Conditional breakpoints are like eval in the function
+ Context *savedFunctionContext = _functionContext;
+ _functionContext = _context;
+ ControlFlow *savedControlFlow = controlFlow;
+ controlFlow = nullptr;
+
+ if (_context->contextType == ContextType::Global) {
+ _module->blocks.append(_context);
+ _context->blockIndex = _module->blocks.count() - 1;
+ }
if (_module->debugMode) // allow the debugger to see overwritten arguments
_context->argumentsCanEscape = true;
@@ -2084,132 +2998,109 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
// at all, because if the onSignal is a signal handler, the user is actually making it explicit
// that the binding is a function, so we should execute that. However, we don't know that during
// AOT compilation, so mark the surrounding function as only-returning-a-closure.
- _context->returnsClosure = cast<ExpressionStatement *>(ast) && cast<FunctionExpression *>(cast<ExpressionStatement *>(ast)->expression);
+ _context->returnsClosure = body && body->statement && cast<ExpressionStatement *>(body->statement) && cast<FunctionExpression *>(cast<ExpressionStatement *>(body->statement)->expression);
BytecodeGenerator bytecode(_context->line, _module->debugMode);
BytecodeGenerator *savedBytecodeGenerator;
savedBytecodeGenerator = bytecodeGenerator;
bytecodeGenerator = &bytecode;
bytecodeGenerator->setLocation(ast->firstSourceLocation());
+ BytecodeGenerator::Label *savedReturnLabel = _returnLabel;
+ _returnLabel = nullptr;
+
+ bool savedFunctionEndsWithReturn = functionEndsWithReturn;
+ functionEndsWithReturn = endsWithReturn(_module, body);
// reserve the js stack frame (Context & js Function & accumulator)
bytecodeGenerator->newRegisterArray(sizeof(CallData)/sizeof(Value) - 1 + _context->arguments.size());
+ bool _inFormalParameterList = false;
+ qSwap(_inFormalParameterList, inFormalParameterList);
+
int returnAddress = -1;
- bool _requiresReturnValue = (_context->compilationMode == QmlBinding || _context->compilationMode == EvalCode || _context->compilationMode == GlobalCode);
+ bool _requiresReturnValue = _context->requiresImplicitReturnValue();
qSwap(requiresReturnValue, _requiresReturnValue);
- if (requiresReturnValue)
- returnAddress = bytecodeGenerator->newRegister();
- if (!_context->parent || _context->usesArgumentsObject == Context::ArgumentsObjectUnknown)
- _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
- if (_context->usesArgumentsObject == Context::ArgumentsObjectUsed)
- _context->addLocalVar(QStringLiteral("arguments"), Context::VariableDeclaration, AST::VariableDeclaration::FunctionScope);
-
- bool allVarsEscape = _context->hasWith || _context->hasTry || _context->hasDirectEval;
- bool needsCallContext = false;
- const QLatin1String exprForOn("expression for on");
- if (!_context->canUseSimpleCall() && _context->compilationMode != GlobalCode && (_context->compilationMode != EvalCode || _context->isStrict))
- needsCallContext = true;
- else if (_context->compilationMode == QmlBinding && name.length() > exprForOn.size() && name.startsWith(exprForOn) && name.at(exprForOn.size()).isUpper())
- // we don't really need this for bindings, but we do for signal handlers, and we don't know if the code is a signal handler or not.
- needsCallContext = true;
- if (needsCallContext) {
- Instruction::CreateCallContext createContext;
- bytecodeGenerator->addInstruction(createContext);
- }
-
- // variables in global code are properties of the global context object, not locals as with other functions.
- if (_context->compilationMode == FunctionCode || _context->compilationMode == QmlBinding) {
- for (Context::MemberMap::iterator it = _context->members.begin(), end = _context->members.end(); it != end; ++it) {
- const QString &local = it.key();
- if (allVarsEscape)
- it->canEscape = true;
- if (it->canEscape) {
- it->index = _context->locals.size();
- _context->locals.append(local);
- if (it->type == Context::ThisFunctionName) {
- // move the name from the stack to the call context
- Instruction::LoadReg load;
- load.reg = CallData::Function;
- bytecodeGenerator->addInstruction(load);
- Instruction::StoreLocal store;
- store.index = it->index;
- bytecodeGenerator->addInstruction(store);
- }
- } else {
- if (it->type == Context::ThisFunctionName)
- it->index = CallData::Function;
- else
- it->index = bytecodeGenerator->newRegister();
- }
- }
- } else {
- for (Context::MemberMap::const_iterator it = _context->members.constBegin(), cend = _context->members.constEnd(); it != cend; ++it) {
- const QString &local = it.key();
+ returnAddress = bytecodeGenerator->newRegister();
+ qSwap(_returnAddress, returnAddress);
- Instruction::DeclareVar declareVar;
- declareVar.isDeletable = false;
- declareVar.varName = registerString(local);
- bytecodeGenerator->addInstruction(declareVar);
- }
+ // register the lexical scope for global code
+ if (!_context->parent && _context->requiresExecutionContext) {
+ _module->blocks.append(_context);
+ _context->blockIndex = _module->blocks.count() - 1;
}
- qSwap(_returnAddress, returnAddress);
- for (const Context::Member &member : qAsConst(_context->members)) {
- if (member.function) {
- const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals,
- member.function->body ? member.function->body->elements : nullptr);
- loadClosure(function);
- if (! _context->parent) {
- Reference::fromName(this, member.function->name.toString()).storeConsumeAccumulator();
+ TailCallBlocker maybeBlockTailCalls(this, _context->canHaveTailCalls());
+
+ RegisterScope registerScope(this);
+ _context->emitBlockHeader(this);
+
+ {
+ QScopedValueRollback<bool> inFormals(inFormalParameterList, true);
+ TailCallBlocker blockTailCalls(this); // we're not in the FunctionBody or ConciseBody yet
+
+ int argc = 0;
+ while (formals) {
+ PatternElement *e = formals->element;
+ if (!e) {
+ if (!formals->next)
+ // trailing comma
+ break;
+ Q_UNREACHABLE();
+ }
+
+ Reference arg = referenceForName(e->bindingIdentifier.toString(), true);
+ if (e->type == PatternElement::RestElement) {
+ Q_ASSERT(!formals->next);
+ Instruction::CreateRestParameter rest;
+ rest.argIndex = argc;
+ bytecodeGenerator->addInstruction(rest);
+ arg.storeConsumeAccumulator();
} else {
- Q_ASSERT(member.index >= 0);
- Reference local = member.canEscape ? Reference::fromScopedLocal(this, member.index, 0)
- : Reference::fromStackSlot(this, member.index, true);
- local.storeConsumeAccumulator();
+ if (e->bindingTarget || e->initializer) {
+ initializeAndDestructureBindingElement(e, arg);
+ if (hasError)
+ break;
+ }
}
+ formals = formals->next;
+ ++argc;
}
}
- if (_context->usesArgumentsObject == Context::ArgumentsObjectUsed) {
- if (_context->isStrict) {
- Instruction::CreateUnmappedArgumentsObject setup;
- bytecodeGenerator->addInstruction(setup);
- } else {
- Instruction::CreateMappedArgumentsObject setup;
- bytecodeGenerator->addInstruction(setup);
- }
- referenceForName(QStringLiteral("arguments"), false).storeConsumeAccumulator();
- }
- if (_context->usesThis && !_context->isStrict) {
- // make sure we convert this to an object
- Instruction::ConvertThisToObject convert;
- bytecodeGenerator->addInstruction(convert);
+
+ if (_context->isGenerator) {
+ Instruction::Yield yield;
+ bytecodeGenerator->addInstruction(yield);
}
beginFunctionBodyHook();
- sourceElements(body);
+ statementList(body);
- if (hasError || !endsWithReturn(body)) {
- bytecodeGenerator->setLocation(ast->lastSourceLocation());
- if (requiresReturnValue) {
- if (_returnAddress >= 0) {
- Instruction::LoadReg load;
- load.reg = Moth::StackSlot::createRegister(_returnAddress);
- bytecodeGenerator->addInstruction(load);
- }
+ bytecodeGenerator->setLocation(ast->lastSourceLocation());
+ _context->emitBlockFooter(this);
+
+ if (_returnLabel || hasError || !functionEndsWithReturn) {
+ if (_returnLabel)
+ _returnLabel->link();
+
+ if (_returnLabel || requiresReturnValue) {
+ Instruction::LoadReg load;
+ load.reg = Moth::StackSlot::createRegister(_returnAddress);
+ bytecodeGenerator->addInstruction(load);
} else {
Reference::fromConst(this, Encode::undefined()).loadInAccumulator();
}
+
bytecodeGenerator->addInstruction(Instruction::Ret());
}
+ Q_ASSERT(_context == _functionContext);
bytecodeGenerator->finalize(_context);
- _context->registerCount = bytecodeGenerator->registerCount();
+ _context->registerCountInFunction = bytecodeGenerator->registerCount();
static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
if (showCode) {
qDebug() << "=== Bytecode for" << _context->name << "strict mode" << _context->isStrict
- << "register count" << _context->registerCount;
+ << "register count" << _context->registerCountInFunction << "implicit return" << requiresReturnValue;
QV4::Moth::dumpBytecode(_context->code, _context->locals.size(), _context->arguments.size(),
_context->line, _context->lineNumberMapping);
qDebug();
@@ -2217,29 +3108,17 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast,
qSwap(_returnAddress, returnAddress);
qSwap(requiresReturnValue, _requiresReturnValue);
+ qSwap(_inFormalParameterList, inFormalParameterList);
bytecodeGenerator = savedBytecodeGenerator;
+ delete _returnLabel;
+ _returnLabel = savedReturnLabel;
+ controlFlow = savedControlFlow;
+ functionEndsWithReturn = savedFunctionEndsWithReturn;
+ _functionContext = savedFunctionContext;
return leaveContext();
}
-bool Codegen::visit(FunctionSourceElement *ast)
-{
- if (hasError)
- return false;
-
- statement(ast->declaration);
- return false;
-}
-
-bool Codegen::visit(StatementSourceElement *ast)
-{
- if (hasError)
- return false;
-
- statement(ast->statement);
- return false;
-}
-
bool Codegen::visit(Block *ast)
{
if (hasError)
@@ -2247,6 +3126,7 @@ bool Codegen::visit(Block *ast)
RegisterScope scope(this);
+ ControlFlowBlock controlFlow(this, ast);
statementList(ast->statements);
return false;
}
@@ -2256,13 +3136,14 @@ bool Codegen::visit(BreakStatement *ast)
if (hasError)
return false;
- if (!_context->controlFlow) {
+ // no need to block tail calls here: children aren't visited
+ if (!controlFlow) {
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop"));
return false;
}
- ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Break, ast->label.toString());
- if (h.type == ControlFlow::Invalid) {
+ ControlFlow::UnwindTarget target = controlFlow->unwindTarget(ControlFlow::Break, ast->label.toString());
+ if (!target.linkLabel.isValid()) {
if (ast->label.isEmpty())
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop"));
else
@@ -2270,7 +3151,7 @@ bool Codegen::visit(BreakStatement *ast)
return false;
}
- _context->controlFlow->jumpToHandler(h);
+ bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
return false;
}
@@ -2280,15 +3161,16 @@ bool Codegen::visit(ContinueStatement *ast)
if (hasError)
return false;
+ // no need to block tail calls here: children aren't visited
RegisterScope scope(this);
- if (!_context->controlFlow) {
+ if (!controlFlow) {
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Continue outside of loop"));
return false;
}
- ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Continue, ast->label.toString());
- if (h.type == ControlFlow::Invalid) {
+ ControlFlow::UnwindTarget target = controlFlow->unwindTarget(ControlFlow::Continue, ast->label.toString());
+ if (!target.linkLabel.isValid()) {
if (ast->label.isEmpty())
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Undefined label '%1'").arg(ast->label.toString()));
else
@@ -2296,7 +3178,7 @@ bool Codegen::visit(ContinueStatement *ast)
return false;
}
- _context->controlFlow->jumpToHandler(h);
+ bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
return false;
}
@@ -2325,6 +3207,7 @@ bool Codegen::visit(DoWhileStatement *ast)
cond.link();
+ TailCallBlocker blockTailCalls(this);
if (!AST::cast<FalseLiteral *>(ast->expression)) {
if (AST::cast<TrueLiteral *>(ast->expression))
bytecodeGenerator->jump().link(body);
@@ -2351,6 +3234,7 @@ bool Codegen::visit(ExpressionStatement *ast)
return true;
RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
if (requiresReturnValue) {
Reference e = expression(ast->expression);
@@ -2369,46 +3253,90 @@ bool Codegen::visit(ForEachStatement *ast)
return true;
RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
- Reference nextIterObj = Reference::fromStackSlot(this);
- Reference iterObj = Reference::fromStackSlot(this);
- Reference expr = expression(ast->expression);
- if (hasError)
- return true;
+ Reference iterator = Reference::fromStackSlot(this);
+ Reference iteratorDone = Reference::fromConst(this, Encode(false)).storeOnStack();
+ Reference lhsValue = Reference::fromStackSlot(this);
- expr.loadInAccumulator();
- Instruction::ForeachIteratorObject iteratorObjInstr;
- bytecodeGenerator->addInstruction(iteratorObjInstr);
- iterObj.storeConsumeAccumulator();
+ // There should be a temporal block, so that variables declared in lhs shadow outside vars.
+ // This block should define a temporal dead zone for those variables.
+ {
+ RegisterScope innerScope(this);
+ ControlFlowBlock controlFlow(this, ast);
+ Reference expr = expression(ast->expression);
+ if (hasError)
+ return true;
- Reference lhs = expression(ast->initialiser).asLValue();
+ expr.loadInAccumulator();
+ Instruction::GetIterator iteratorObjInstr;
+ iteratorObjInstr.iterator = static_cast<int>(ast->type);
+ bytecodeGenerator->addInstruction(iteratorObjInstr);
+ iterator.storeConsumeAccumulator();
+ }
BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
+ BytecodeGenerator::Label done = bytecodeGenerator->newLabel();
- bytecodeGenerator->jump().link(in);
-
- ControlFlowLoop flow(this, &end, &in);
-
- BytecodeGenerator::Label body = bytecodeGenerator->label();
-
- nextIterObj.loadInAccumulator();
- lhs.storeConsumeAccumulator();
-
- statement(ast->statement);
- setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
+ {
+ auto cleanup = [ast, iterator, iteratorDone, this]() {
+ if (ast->type == ForEachType::Of) {
+ iterator.loadInAccumulator();
+ Instruction::IteratorClose close;
+ close.done = iteratorDone.stackSlot();
+ bytecodeGenerator->addInstruction(close);
+ }
+ };
+ ControlFlowLoop flow(this, &end, &in, cleanup);
+ bytecodeGenerator->jump().link(in);
+
+ BytecodeGenerator::Label body = bytecodeGenerator->label();
+
+ // each iteration gets it's own context, as per spec
+ {
+ RegisterScope innerScope(this);
+ ControlFlowBlock controlFlow(this, ast);
+
+ if (ExpressionNode *e = ast->lhs->expressionCast()) {
+ if (AST::Pattern *p = e->patternCast()) {
+ RegisterScope scope(this);
+ destructurePattern(p, lhsValue);
+ } else {
+ Reference lhs = expression(e);
+ if (hasError)
+ goto error;
+ lhs = lhs.asLValue();
+ lhsValue.loadInAccumulator();
+ lhs.storeConsumeAccumulator();
+ }
+ } else if (PatternElement *p = AST::cast<PatternElement *>(ast->lhs)) {
+ initializeAndDestructureBindingElement(p, lhsValue, /*isDefinition =*/ true);
+ if (hasError)
+ goto error;
+ } else {
+ Q_UNREACHABLE();
+ }
- in.link();
+ statement(ast->statement);
+ setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
- iterObj.loadInAccumulator();
- Instruction::ForeachNextPropertyName nextPropInstr;
- bytecodeGenerator->addInstruction(nextPropInstr);
- nextIterObj.storeConsumeAccumulator();
+ }
- Reference::fromConst(this, QV4::Encode::null()).loadInAccumulator();
- bytecodeGenerator->jumpStrictNotEqual(nextIterObj.stackSlot(), body);
+ error:
+ in.link();
+ iterator.loadInAccumulator();
+ Instruction::IteratorNext next;
+ next.value = lhsValue.stackSlot();
+ next.done = iteratorDone.stackSlot();
+ bytecodeGenerator->addInstruction(next);
+ bytecodeGenerator->addJumpInstruction(Instruction::JumpFalse()).link(body);
+ bytecodeGenerator->jump().link(done);
+
+ end.link();
+ }
- end.link();
+ done.link();
return false;
}
@@ -2419,8 +3347,14 @@ bool Codegen::visit(ForStatement *ast)
return true;
RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
+
+ ControlFlowBlock controlFlow(this, ast);
- statement(ast->initialiser);
+ if (ast->initialiser)
+ statement(ast->initialiser);
+ else if (ast->declarations)
+ variableDeclarationList(ast->declarations);
BytecodeGenerator::Label cond = bytecodeGenerator->label();
BytecodeGenerator::Label body = bytecodeGenerator->newLabel();
@@ -2432,10 +3366,16 @@ bool Codegen::visit(ForStatement *ast)
condition(ast->condition, &body, &end, true);
body.link();
+ blockTailCalls.unblock();
statement(ast->statement);
+ blockTailCalls.reblock();
setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
step.link();
+ if (_context->requiresExecutionContext) {
+ Instruction::CloneBlockContext clone;
+ bytecodeGenerator->addInstruction(clone);
+ }
statement(ast->expression);
bytecodeGenerator->jump().link(cond);
@@ -2450,15 +3390,17 @@ bool Codegen::visit(IfStatement *ast)
return true;
RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
BytecodeGenerator::Label trueLabel = bytecodeGenerator->newLabel();
BytecodeGenerator::Label falseLabel = bytecodeGenerator->newLabel();
condition(ast->expression, &trueLabel, &falseLabel, true);
+ blockTailCalls.unblock();
trueLabel.link();
statement(ast->ok);
if (ast->ko) {
- if (endsWithReturn(ast)) {
+ if (endsWithReturn(_module, ast)) {
falseLabel.link();
statement(ast->ko);
} else {
@@ -2482,7 +3424,7 @@ bool Codegen::visit(LabelledStatement *ast)
RegisterScope scope(this);
// check that no outer loop contains the label
- ControlFlow *l = _context->controlFlow;
+ ControlFlow *l = controlFlow;
while (l) {
if (l->label() == ast->label) {
QString error = QString(QStringLiteral("Label '%1' has already been declared")).arg(ast->label.toString());
@@ -2497,9 +3439,7 @@ bool Codegen::visit(LabelledStatement *ast)
AST::cast<AST::WhileStatement *>(ast->statement) ||
AST::cast<AST::DoWhileStatement *>(ast->statement) ||
AST::cast<AST::ForStatement *>(ast->statement) ||
- AST::cast<AST::ForEachStatement *>(ast->statement) ||
- AST::cast<AST::LocalForStatement *>(ast->statement) ||
- AST::cast<AST::LocalForEachStatement *>(ast->statement)) {
+ AST::cast<AST::ForEachStatement *>(ast->statement)) {
statement(ast->statement); // labelledStatement will be associated with the ast->statement's loop.
} else {
BytecodeGenerator::Label breakLabel = bytecodeGenerator->newLabel();
@@ -2511,85 +3451,17 @@ bool Codegen::visit(LabelledStatement *ast)
return false;
}
-bool Codegen::visit(LocalForEachStatement *ast)
+void Codegen::emitReturn(const Reference &expr)
{
- if (hasError)
- return true;
-
- RegisterScope scope(this);
-
- Reference nextIterObj = Reference::fromStackSlot(this);
- Reference iterObj = Reference::fromStackSlot(this);
- Reference expr = expression(ast->expression);
- if (hasError)
- return true;
-
- variableDeclaration(ast->declaration);
-
- expr.loadInAccumulator();
- Instruction::ForeachIteratorObject iteratorObjInstr;
- bytecodeGenerator->addInstruction(iteratorObjInstr);
- iterObj.storeConsumeAccumulator();
-
- BytecodeGenerator::Label in = bytecodeGenerator->newLabel();
- BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
-
- bytecodeGenerator->jump().link(in);
- ControlFlowLoop flow(this, &end, &in);
-
- BytecodeGenerator::Label body = bytecodeGenerator->label();
-
- Reference it = referenceForName(ast->declaration->name.toString(), true).asLValue();
-
- nextIterObj.loadInAccumulator();
- it.storeConsumeAccumulator();
-
- statement(ast->statement);
- setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
-
- in.link();
-
- iterObj.loadInAccumulator();
- Instruction::ForeachNextPropertyName nextPropInstr;
- bytecodeGenerator->addInstruction(nextPropInstr);
- nextIterObj.storeConsumeAccumulator();
-
- Reference::fromConst(this, QV4::Encode::null()).loadInAccumulator();
- bytecodeGenerator->jumpStrictNotEqual(nextIterObj.stackSlot(), body);
-
- end.link();
-
- return false;
-}
-
-bool Codegen::visit(LocalForStatement *ast)
-{
- if (hasError)
- return true;
-
- RegisterScope scope(this);
-
- variableDeclarationList(ast->declarations);
-
- BytecodeGenerator::Label cond = bytecodeGenerator->label();
- BytecodeGenerator::Label body = bytecodeGenerator->newLabel();
- BytecodeGenerator::Label step = bytecodeGenerator->newLabel();
- BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
-
- ControlFlowLoop flow(this, &end, &step);
-
- condition(ast->condition, &body, &end, true);
-
- body.link();
- statement(ast->statement);
- setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken);
-
- step.link();
- statement(ast->expression);
- bytecodeGenerator->jump().link(cond);
- end.link();
-
- return false;
+ ControlFlow::UnwindTarget target = controlFlow ? controlFlow->unwindTarget(ControlFlow::Return) : ControlFlow::UnwindTarget();
+ if (target.linkLabel.isValid() && target.unwindLevel) {
+ Q_ASSERT(_returnAddress >= 0);
+ (void) expr.storeOnStack(_returnAddress);
+ bytecodeGenerator->unwindToLabel(target.unwindLevel, target.linkLabel);
+ } else {
+ expr.loadInAccumulator();
+ bytecodeGenerator->addInstruction(Instruction::Ret());
+ }
}
bool Codegen::visit(ReturnStatement *ast)
@@ -2597,7 +3469,7 @@ bool Codegen::visit(ReturnStatement *ast)
if (hasError)
return true;
- if (_context->compilationMode != FunctionCode && _context->compilationMode != QmlBinding) {
+ if (_functionContext->contextType != ContextType::Function && _functionContext->contextType != ContextType::Binding) {
throwSyntaxError(ast->returnToken, QStringLiteral("Return statement outside of function"));
return false;
}
@@ -2610,17 +3482,8 @@ bool Codegen::visit(ReturnStatement *ast)
expr = Reference::fromConst(this, Encode::undefined());
}
- if (_context->controlFlow && _context->controlFlow->returnRequiresUnwind()) {
- if (_returnAddress >= 0)
- (void) expr.storeOnStack(_returnAddress);
- else
- expr.loadInAccumulator();
- ControlFlow::Handler h = _context->controlFlow->getHandler(ControlFlow::Return);
- _context->controlFlow->jumpToHandler(h);
- } else {
- expr.loadInAccumulator();
- bytecodeGenerator->addInstruction(Instruction::Ret());
- }
+ emitReturn(expr);
+
return false;
}
@@ -2629,7 +3492,11 @@ bool Codegen::visit(SwitchStatement *ast)
if (hasError)
return true;
+ if (requiresReturnValue)
+ Reference::fromConst(this, Encode::undefined()).storeOnStack(_returnAddress);
+
RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
if (ast->block) {
BytecodeGenerator::Label switchEnd = bytecodeGenerator->newLabel();
@@ -2639,6 +3506,8 @@ bool Codegen::visit(SwitchStatement *ast)
return false;
lhs = lhs.storeOnStack();
+ ControlFlowBlock controlFlow(this, ast->block);
+
// set up labels for all clauses
QHash<Node *, BytecodeGenerator::Label> blockMap;
for (CaseClauses *it = ast->block->clauses; it; it = it->next)
@@ -2674,6 +3543,8 @@ bool Codegen::visit(SwitchStatement *ast)
ControlFlowLoop flow(this, &switchEnd);
+ insideSwitch = true;
+ blockTailCalls.unblock();
for (CaseClauses *it = ast->block->clauses; it; it = it->next) {
CaseClause *clause = it->clause;
blockMap[clause].link();
@@ -2694,6 +3565,7 @@ bool Codegen::visit(SwitchStatement *ast)
statementList(clause->statements);
}
+ insideSwitch = false;
switchEnd.link();
@@ -2708,35 +3580,27 @@ bool Codegen::visit(ThrowStatement *ast)
return false;
RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
Reference expr = expression(ast->expression);
if (hasError)
return false;
- if (_context->controlFlow) {
- _context->controlFlow->handleThrow(expr);
- } else {
- expr.loadInAccumulator();
- Instruction::ThrowException instr;
- bytecodeGenerator->addInstruction(instr);
- }
+ expr.loadInAccumulator();
+ Instruction::ThrowException instr;
+ bytecodeGenerator->addInstruction(instr);
return false;
}
void Codegen::handleTryCatch(TryStatement *ast)
{
Q_ASSERT(ast);
- if (_context->isStrict &&
- (ast->catchExpression->name == QLatin1String("eval") || ast->catchExpression->name == QLatin1String("arguments"))) {
- throwSyntaxError(ast->catchExpression->identifierToken, QStringLiteral("Catch variable name may not be eval or arguments in strict mode"));
- return;
- }
-
RegisterScope scope(this);
BytecodeGenerator::Label noException = bytecodeGenerator->newLabel();
{
ControlFlowCatch catchFlow(this, ast->catchExpression);
RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this); // IMPORTANT: destruction will unblock tail calls before catch is generated
statement(ast->statement);
bytecodeGenerator->jump().link(noException);
}
@@ -2747,6 +3611,7 @@ void Codegen::handleTryFinally(TryStatement *ast)
{
RegisterScope scope(this);
ControlFlowFinally finally(this, ast->finallyExpression);
+ TailCallBlocker blockTailCalls(this); // IMPORTANT: destruction will unblock tail calls before finally is generated
if (ast->catchExpression) {
handleTryCatch(ast);
@@ -2761,8 +3626,6 @@ bool Codegen::visit(TryStatement *ast)
if (hasError)
return true;
- Q_ASSERT(_context->hasTry);
-
RegisterScope scope(this);
if (ast->finallyExpression && ast->finallyExpression->statement) {
@@ -2792,6 +3655,7 @@ bool Codegen::visit(WhileStatement *ast)
return false;
RegisterScope scope(this);
+ TailCallBlocker blockTailCalls(this);
BytecodeGenerator::Label start = bytecodeGenerator->newLabel();
BytecodeGenerator::Label end = bytecodeGenerator->newLabel();
@@ -2800,6 +3664,7 @@ bool Codegen::visit(WhileStatement *ast)
if (!AST::cast<TrueLiteral *>(ast->expression))
condition(ast->expression, &start, &end, true);
+ blockTailCalls.unblock();
start.link();
statement(ast->statement);
@@ -2816,8 +3681,7 @@ bool Codegen::visit(WithStatement *ast)
return true;
RegisterScope scope(this);
-
- _context->hasWith = true;
+ TailCallBlocker blockTailCalls(this);
Reference src = expression(ast->expression);
if (hasError)
@@ -2825,9 +3689,13 @@ bool Codegen::visit(WithStatement *ast)
src = src.storeOnStack(); // trigger load before we setup the exception handler, so exceptions here go to the right place
src.loadInAccumulator();
- ControlFlowWith flow(this);
-
- statement(ast->statement);
+ enterContext(ast);
+ {
+ blockTailCalls.unblock();
+ ControlFlowWith flow(this);
+ statement(ast->statement);
+ }
+ leaveContext();
return false;
}
@@ -2917,9 +3785,10 @@ QList<QQmlJS::DiagnosticMessage> Codegen::errors() const
QQmlRefPointer<CompiledData::CompilationUnit> Codegen::generateCompilationUnit(bool generateUnitData)
{
- CompiledData::CompilationUnit *compilationUnit = new CompiledData::CompilationUnit;
+ CompiledData::Unit *unitData = nullptr;
if (generateUnitData)
- compilationUnit->data = jsUnitGenerator->generateUnit();
+ unitData = jsUnitGenerator->generateUnit();
+ CompiledData::CompilationUnit *compilationUnit = new CompiledData::CompilationUnit(unitData);
QQmlRefPointer<CompiledData::CompilationUnit> unit;
unit.adopt(compilationUnit);
@@ -3080,57 +3949,20 @@ Codegen::RValue Codegen::RValue::storeOnStack() const
}
}
-Codegen::Reference::Reference(const Codegen::Reference &other)
-{
- *this = other;
-}
-
-Codegen::Reference &Codegen::Reference::operator =(const Reference &other)
+void Codegen::RValue::loadInAccumulator() const
{
- type = other.type;
-
switch (type) {
- case Invalid:
case Accumulator:
- break;
+ // nothing to do
+ return;
case StackSlot:
- theStackSlot = other.theStackSlot;
- break;
- case ScopedLocal:
- index = other.index;
- scope = other.scope;
- break;
- case Name:
- name = other.name;
- break;
- case Member:
- propertyBase = other.propertyBase;
- propertyNameIndex = other.propertyNameIndex;
- break;
- case Subscript:
- elementBase = other.elementBase;
- elementSubscript = other.elementSubscript;
- break;
+ return Reference::fromStackSlot(codegen, theStackSlot).loadInAccumulator();
case Const:
- constant = other.constant;
- break;
- case QmlScopeObject:
- case QmlContextObject:
- qmlBase = other.qmlBase;
- qmlCoreIndex = other.qmlCoreIndex;
- qmlNotifyIndex = other.qmlNotifyIndex;
- capturePolicy = other.capturePolicy;
- break;
+ return Reference::fromConst(codegen, constant).loadInAccumulator();
+ default:
+ Q_UNREACHABLE();
}
- // keep loaded reference
- isArgOrEval = other.isArgOrEval;
- codegen = other.codegen;
- isReadonly = other.isReadonly;
- stackSlotIsLocalOrArgument = other.stackSlotIsLocalOrArgument;
- isVolatile = other.isVolatile;
- global = other.global;
- return *this;
}
bool Codegen::Reference::operator==(const Codegen::Reference &other) const
@@ -3141,6 +3973,10 @@ bool Codegen::Reference::operator==(const Codegen::Reference &other) const
case Invalid:
case Accumulator:
break;
+ case Super:
+ return true;
+ case SuperProperty:
+ return property == other.property;
case StackSlot:
return theStackSlot == other.theStackSlot;
case ScopedLocal:
@@ -3151,6 +3987,8 @@ bool Codegen::Reference::operator==(const Codegen::Reference &other) const
return propertyBase == other.propertyBase && propertyNameIndex == other.propertyNameIndex;
case Subscript:
return elementBase == other.elementBase && elementSubscript == other.elementSubscript;
+ case Import:
+ return index == other.index;
case Const:
return constant == other.constant;
case QmlScopeObject:
@@ -3184,6 +4022,9 @@ Codegen::Reference Codegen::Reference::asLValue() const
case Invalid:
case Accumulator:
Q_UNREACHABLE();
+ case Super:
+ codegen->throwSyntaxError(AST::SourceLocation(), QStringLiteral("Super lvalues not implemented."));
+ return *this;
case Member:
if (!propertyBase.isStackSlot()) {
Reference r = *this;
@@ -3209,6 +4050,30 @@ Codegen::Reference Codegen::Reference::storeConsumeAccumulator() const
return Reference();
}
+Codegen::Reference Codegen::Reference::baseObject() const
+{
+ if (type == Reference::QmlScopeObject || type == Reference::QmlContextObject) {
+ return Reference::fromStackSlot(codegen, qmlBase.stackSlot());
+ } else if (type == Reference::Member) {
+ RValue rval = propertyBase;
+ if (!rval.isValid())
+ return Reference::fromConst(codegen, Encode::undefined());
+ if (rval.isAccumulator())
+ return Reference::fromAccumulator(codegen);
+ if (rval.isStackSlot())
+ return Reference::fromStackSlot(codegen, rval.stackSlot());
+ if (rval.isConst())
+ return Reference::fromConst(codegen, rval.constantValue());
+ Q_UNREACHABLE();
+ } else if (type == Reference::Subscript) {
+ return Reference::fromStackSlot(codegen, elementBase.stackSlot());
+ } else if (type == Reference::SuperProperty) {
+ return Reference::fromStackSlot(codegen, CallData::This);
+ } else {
+ return Reference::fromConst(codegen, Encode::undefined());
+ }
+}
+
Codegen::Reference Codegen::Reference::storeOnStack() const
{ return doStoreOnStack(-1); }
@@ -3217,10 +4082,10 @@ void Codegen::Reference::storeOnStack(int slotIndex) const
Codegen::Reference Codegen::Reference::doStoreOnStack(int slotIndex) const
{
- if (isStackSlot() && slotIndex == -1 && !(stackSlotIsLocalOrArgument && isVolatile))
+ if (isStackSlot() && slotIndex == -1 && !(stackSlotIsLocalOrArgument && isVolatile) && !requiresTDZCheck)
return *this;
- if (isStackSlot()) { // temp-to-temp move
+ if (isStackSlot() && !requiresTDZCheck) { // temp-to-temp move
Reference dest = Reference::fromStackSlot(codegen, slotIndex);
Instruction::MoveReg move;
move.srcReg = stackSlot();
@@ -3230,7 +4095,7 @@ Codegen::Reference Codegen::Reference::doStoreOnStack(int slotIndex) const
}
Reference slot = Reference::fromStackSlot(codegen, slotIndex);
- if (isConst()) {
+ if (isConstant()) {
Instruction::MoveConst move;
move.constIndex = codegen->registerConstant(constant);
move.destTemp = slot.stackSlot();
@@ -3280,7 +4145,29 @@ bool Codegen::Reference::storeWipesAccumulator() const
void Codegen::Reference::storeAccumulator() const
{
+ if (isReferenceToConst) {
+ // throw a type error
+ RegisterScope scope(codegen);
+ Reference r = codegen->referenceForName(QStringLiteral("TypeError"), false);
+ r = r.storeOnStack();
+ Instruction::Construct construct;
+ construct.func = r.stackSlot();
+ construct.argc = 0;
+ construct.argv = 0;
+ codegen->bytecodeGenerator->addInstruction(construct);
+ Instruction::ThrowException throwException;
+ codegen->bytecodeGenerator->addInstruction(throwException);
+ return;
+ }
switch (type) {
+ case Super:
+ Q_UNREACHABLE();
+ return;
+ case SuperProperty:
+ Instruction::StoreSuperProperty store;
+ store.property = property.stackSlot();
+ codegen->bytecodeGenerator->addInstruction(store);
+ return;
case StackSlot: {
Instruction::StoreReg store;
store.reg = theStackSlot;
@@ -3313,7 +4200,7 @@ void Codegen::Reference::storeAccumulator() const
}
} return;
case Member:
- if (codegen->useFastLookups) {
+ if (!disable_lookups && codegen->useFastLookups) {
Instruction::SetLookup store;
store.base = propertyBase.stackSlot();
store.index = codegen->registerSetterLookup(propertyNameIndex);
@@ -3346,6 +4233,7 @@ void Codegen::Reference::storeAccumulator() const
case Invalid:
case Accumulator:
case Const:
+ case Import:
break;
}
@@ -3355,9 +4243,34 @@ void Codegen::Reference::storeAccumulator() const
void Codegen::Reference::loadInAccumulator() const
{
+ auto tdzCheck = [this](bool requiresCheck){
+ if (!requiresCheck)
+ return;
+ Instruction::DeadTemporalZoneCheck check;
+ check.name = codegen->registerString(name);
+ codegen->bytecodeGenerator->addInstruction(check);
+ };
+ auto tdzCheckStackSlot = [this, tdzCheck](Moth::StackSlot slot, bool requiresCheck){
+ if (!requiresCheck)
+ return;
+ Instruction::LoadReg load;
+ load.reg = slot;
+ codegen->bytecodeGenerator->addInstruction(load);
+ tdzCheck(true);
+ };
+
switch (type) {
case Accumulator:
return;
+ case Super:
+ Q_UNREACHABLE();
+ return;
+ case SuperProperty:
+ tdzCheckStackSlot(property, subscriptRequiresTDZCheck);
+ Instruction::LoadSuperProperty load;
+ load.property = property.stackSlot();
+ codegen->bytecodeGenerator->addInstruction(load);
+ return;
case Const: {
QT_WARNING_PUSH
QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty structs.
@@ -3374,7 +4287,7 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty str
Instruction::LoadUndefined load;
codegen->bytecodeGenerator->addInstruction(load);
} else {
- Value p = Primitive::fromReturnedValue(constant);
+ Value p = Value::fromReturnedValue(constant);
if (p.isNumber()) {
double d = p.asDouble();
int i = static_cast<int>(d);
@@ -3385,7 +4298,7 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty str
return;
}
Instruction::LoadInt load;
- load.value = Primitive::fromReturnedValue(constant).toInt32();
+ load.value = Value::fromReturnedValue(constant).toInt32();
codegen->bytecodeGenerator->addInstruction(load);
return;
}
@@ -3400,6 +4313,7 @@ QT_WARNING_POP
Instruction::LoadReg load;
load.reg = stackSlot();
codegen->bytecodeGenerator->addInstruction(load);
+ tdzCheck(requiresTDZCheck);
} return;
case ScopedLocal: {
if (!scope) {
@@ -3412,6 +4326,7 @@ QT_WARNING_POP
load.scope = scope;
codegen->bytecodeGenerator->addInstruction(load);
}
+ tdzCheck(requiresTDZCheck);
return;
}
case Name:
@@ -3429,7 +4344,7 @@ QT_WARNING_POP
return;
}
}
- if (codegen->useFastLookups && global) {
+ if (!disable_lookups && global) {
Instruction::LoadGlobalLookup load;
load.index = codegen->registerGlobalGetterLookup(nameAsIndex());
codegen->bytecodeGenerator->addInstruction(load);
@@ -3440,46 +4355,31 @@ QT_WARNING_POP
}
return;
case Member:
- if (codegen->useFastLookups) {
- if (propertyBase.isAccumulator()) {
- Instruction::GetLookupA load;
- load.index = codegen->registerGetterLookup(propertyNameIndex);
- codegen->bytecodeGenerator->addInstruction(load);
- } else {
- Instruction::GetLookup load;
- load.base = propertyBase.storeOnStack().stackSlot();
- load.index = codegen->registerGetterLookup(propertyNameIndex);
- codegen->bytecodeGenerator->addInstruction(load);
- }
- } else {
- if (propertyBase.isAccumulator()) {
- Instruction::LoadPropertyA load;
- load.name = propertyNameIndex;
- codegen->bytecodeGenerator->addInstruction(load);
- } else {
- Instruction::LoadProperty load;
- load.base = propertyBase.storeOnStack().stackSlot();
- load.name = propertyNameIndex;
- codegen->bytecodeGenerator->addInstruction(load);
- }
- }
- return;
- case Subscript: {
- if (elementSubscript.isAccumulator()) {
- Instruction::LoadElementA load;
- load.base = elementBase;
- codegen->bytecodeGenerator->addInstruction(load);
- } else if (elementSubscript.isConst()) {
- Reference::fromConst(codegen, elementSubscript.constantValue()).loadInAccumulator();
- Instruction::LoadElementA load;
- load.base = elementBase;
+ propertyBase.loadInAccumulator();
+ tdzCheck(requiresTDZCheck);
+ if (!disable_lookups && codegen->useFastLookups) {
+ Instruction::GetLookup load;
+ load.index = codegen->registerGetterLookup(propertyNameIndex);
codegen->bytecodeGenerator->addInstruction(load);
} else {
- Instruction::LoadElement load;
- load.base = elementBase;
- load.index = elementSubscript.storeOnStack().stackSlot();
+ Instruction::LoadProperty load;
+ load.name = propertyNameIndex;
codegen->bytecodeGenerator->addInstruction(load);
}
+ return;
+ case Import: {
+ Instruction::LoadImport load;
+ load.index = index;
+ codegen->bytecodeGenerator->addInstruction(load);
+ tdzCheck(requiresTDZCheck);
+ } return;
+ case Subscript: {
+ tdzCheckStackSlot(elementBase, requiresTDZCheck);
+ elementSubscript.loadInAccumulator();
+ tdzCheck(subscriptRequiresTDZCheck);
+ Instruction::LoadElement load;
+ load.base = elementBase;
+ codegen->bytecodeGenerator->addInstruction(load);
} return;
case QmlScopeObject: {
Instruction::LoadScopeObjectProperty load;
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index 1db788154e..49607256f9 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -53,19 +53,11 @@
#include "private/qv4global_p.h"
#include <private/qqmljsastvisitor_p.h>
#include <private/qqmljsast_p.h>
-#include <private/qqmljsengine_p.h>
-#include <private/qv4instr_moth_p.h>
#include <private/qv4compiler_p.h>
#include <private/qv4compilercontext_p.h>
-#include <private/qqmlrefcount_p.h>
-#include <QtCore/QStringList>
-#include <QtCore/QDateTime>
-#include <QStack>
-#ifndef V4_BOOTSTRAP
-#include <qqmlerror.h>
-#endif
#include <private/qv4util_p.h>
#include <private/qv4bytecodegenerator_p.h>
+#include <private/qv4stackframe_p.h>
QT_BEGIN_NAMESPACE
@@ -101,7 +93,13 @@ public:
const QString &sourceCode,
AST::Program *ast,
Module *module,
- CompilationMode mode = GlobalCode);
+ ContextType contextType = ContextType::Global);
+
+ void generateFromModule(const QString &fileName,
+ const QString &finalUrl,
+ const QString &sourceCode,
+ AST::ESModule *ast,
+ Module *module);
public:
class VolatileMemoryLocationScanner;
@@ -172,16 +170,20 @@ public:
}
Q_REQUIRED_RESULT RValue storeOnStack() const;
+ void loadInAccumulator() const;
};
struct Reference {
enum Type {
Invalid,
Accumulator,
+ Super,
+ SuperProperty,
StackSlot,
ScopedLocal,
Name,
Member,
Subscript,
+ Import,
QmlScopeObject,
QmlContextObject,
LastLValue = QmlContextObject,
@@ -192,11 +194,14 @@ public:
Reference(Codegen *cg, Type type = Invalid) : type(type), codegen(cg) {}
Reference() {}
- Reference(const Reference &other);
-
- Reference &operator =(const Reference &other);
+ Reference(const Reference &) = default;
+ Reference(Reference &&) = default;
+ Reference &operator =(const Reference &) = default;
+ Reference &operator =(Reference &&) = default;
bool operator==(const Reference &other) const;
+ bool operator!=(const Reference &other) const
+ { return !(*this == other); }
bool isValid() const { return type != Invalid; }
bool loadTriggersSideEffect() const {
@@ -208,13 +213,16 @@ public:
case Name:
case Member:
case Subscript:
+ case SuperProperty:
return true;
default:
- return false;
+ return requiresTDZCheck;
}
}
- bool isConst() const { return type == Const; }
+ bool isConstant() const { return type == Const; }
bool isAccumulator() const { return type == Accumulator; }
+ bool isSuper() const { return type == Super; }
+ bool isSuperProperty() const { return type == SuperProperty; }
bool isStackSlot() const { return type == StackSlot; }
bool isRegister() const {
return isStackSlot();
@@ -245,6 +253,9 @@ public:
static Reference fromAccumulator(Codegen *cg) {
return Reference(cg, Accumulator);
}
+ static Reference fromSuper(Codegen *cg) {
+ return Reference(cg, Super);
+ }
static Reference fromStackSlot(Codegen *cg, int tempIndex = -1, bool isLocal = false) {
Reference r(cg, StackSlot);
if (tempIndex == -1)
@@ -266,6 +277,11 @@ public:
r.scope = scope;
return r;
}
+ static Reference fromImport(Codegen *cg, int index) {
+ Reference r(cg, Import);
+ r.index = index;
+ return r;
+ }
static Reference fromName(Codegen *cg, const QString &name) {
Reference r(cg, Name);
r.name = name;
@@ -275,6 +291,14 @@ public:
Reference r(baseRef.codegen, Member);
r.propertyBase = baseRef.asRValue();
r.propertyNameIndex = r.codegen->registerString(name);
+ r.requiresTDZCheck = baseRef.requiresTDZCheck;
+ return r;
+ }
+ static Reference fromSuperProperty(const Reference &property) {
+ Q_ASSERT(property.isStackSlot());
+ Reference r(property.codegen, SuperProperty);
+ r.property = property.stackSlot();
+ r.subscriptRequiresTDZCheck = property.requiresTDZCheck;
return r;
}
static Reference fromSubscript(const Reference &baseRef, const Reference &subscript) {
@@ -282,6 +306,8 @@ public:
Reference r(baseRef.codegen, Subscript);
r.elementBase = baseRef.stackSlot();
r.elementSubscript = subscript.asRValue();
+ r.requiresTDZCheck = baseRef.requiresTDZCheck;
+ r.subscriptRequiresTDZCheck = subscript.requiresTDZCheck;
return r;
}
static Reference fromConst(Codegen *cg, QV4::ReturnedValue constant) {
@@ -309,6 +335,9 @@ public:
static Reference fromThis(Codegen *cg) {
Reference r = fromStackSlot(cg, CallData::This);
r.isReadonly = true;
+ // ### Optimize this. Functions that are not derived constructors or arrow functions can't have an
+ // empty this object
+ r.requiresTDZCheck = true;
return r;
}
@@ -326,6 +355,8 @@ public:
Q_REQUIRED_RESULT Reference storeRetainAccumulator() const;
Reference storeConsumeAccumulator() const;
+ Q_REQUIRED_RESULT Reference baseObject() const;
+
bool storeWipesAccumulator() const;
void loadInAccumulator() const;
@@ -361,10 +392,14 @@ public:
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;
@@ -468,7 +503,10 @@ protected:
void enterContext(AST::Node *node);
int leaveContext();
-
+public:
+ Context *enterBlock(AST::Node *node);
+ int leaveBlock() { return leaveContext(); }
+protected:
void leaveLoop();
enum UnaryOperation {
@@ -486,6 +524,7 @@ protected:
void addCJump();
+public:
int registerString(const QString &name) {
return jsUnitGenerator->registerString(name);
}
@@ -497,33 +536,37 @@ protected:
// Returns index in _module->functions
virtual int defineFunction(const QString &name, AST::Node *ast,
AST::FormalParameterList *formals,
- AST::SourceElements *body);
+ AST::StatementList *body);
+protected:
void statement(AST::Statement *ast);
void statement(AST::ExpressionNode *ast);
void condition(AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue,
const BytecodeGenerator::Label *iffalse,
bool trueBlockFollowsCondition);
Reference expression(AST::ExpressionNode *ast);
- Result sourceElement(AST::SourceElement *ast);
void accept(AST::Node *node);
- void functionBody(AST::FunctionBody *ast);
void program(AST::Program *ast);
- void sourceElements(AST::SourceElements *ast);
void statementList(AST::StatementList *ast);
- void variableDeclaration(AST::VariableDeclaration *ast);
+ void variableDeclaration(AST::PatternElement *ast);
void variableDeclarationList(AST::VariableDeclarationList *ast);
- Reference referenceForName(const QString &name, bool lhs);
+ Reference targetForPatternElement(AST::PatternElement *p);
+ void initializeAndDestructureBindingElement(AST::PatternElement *e, const Reference &baseRef = Reference(), bool isDefinition = false);
+ void destructurePropertyList(const Reference &object, AST::PatternPropertyList *bindingList, bool isDefinition = false);
+ void destructureElementList(const Reference &array, AST::PatternElementList *bindingList, bool isDefinition = false);
+ void destructurePattern(AST::Pattern *p, const Reference &rhs);
- void loadClosure(int index);
+ Reference referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name);
// Hook provided to implement QML lookup semantics
virtual Reference fallbackNameLookup(const QString &name);
virtual void beginFunctionBodyHook() {}
+ void emitReturn(const Reference &expr);
+
// nodes
bool visit(AST::ArgumentList *ast) override;
bool visit(AST::CaseBlock *ast) override;
@@ -531,16 +574,10 @@ protected:
bool visit(AST::CaseClauses *ast) override;
bool visit(AST::Catch *ast) override;
bool visit(AST::DefaultClause *ast) override;
- bool visit(AST::ElementList *ast) override;
bool visit(AST::Elision *ast) override;
bool visit(AST::Finally *ast) override;
bool visit(AST::FormalParameterList *ast) override;
- bool visit(AST::FunctionBody *ast) override;
bool visit(AST::Program *ast) override;
- bool visit(AST::PropertyNameAndValue *ast) override;
- bool visit(AST::PropertyAssignmentList *ast) override;
- bool visit(AST::PropertyGetterSetter *ast) override;
- bool visit(AST::SourceElements *ast) override;
bool visit(AST::StatementList *ast) override;
bool visit(AST::UiArrayMemberList *ast) override;
bool visit(AST::UiImport *ast) override;
@@ -551,20 +588,27 @@ protected:
bool visit(AST::UiParameterList *ast) override;
bool visit(AST::UiProgram *ast) override;
bool visit(AST::UiQualifiedId *ast) override;
- bool visit(AST::UiQualifiedPragmaId *ast) override;
- bool visit(AST::VariableDeclaration *ast) override;
bool visit(AST::VariableDeclarationList *ast) override;
+ bool visit(AST::PatternElement *ast) override;
+ bool visit(AST::PatternElementList *ast) override;
+ bool visit(AST::PatternProperty *ast) override;
+ bool visit(AST::PatternPropertyList *ast) override;
+
+ bool visit(AST::ExportDeclaration *ast) override;
+
// expressions
bool visit(AST::Expression *ast) override;
- bool visit(AST::ArrayLiteral *ast) override;
+ bool visit(AST::ArrayPattern *ast) override;
bool visit(AST::ArrayMemberExpression *ast) override;
bool visit(AST::BinaryExpression *ast) override;
bool visit(AST::CallExpression *ast) override;
bool visit(AST::ConditionalExpression *ast) override;
bool visit(AST::DeleteExpression *ast) override;
bool visit(AST::FalseLiteral *ast) override;
+ bool visit(AST::SuperLiteral *ast) override;
bool visit(AST::FieldMemberExpression *ast) override;
+ bool visit(AST::TaggedTemplate *ast) override;
bool visit(AST::FunctionExpression *ast) override;
bool visit(AST::IdentifierExpression *ast) override;
bool visit(AST::NestedExpression *ast) override;
@@ -573,13 +617,14 @@ protected:
bool visit(AST::NotExpression *ast) override;
bool visit(AST::NullExpression *ast) override;
bool visit(AST::NumericLiteral *ast) override;
- bool visit(AST::ObjectLiteral *ast) override;
+ bool visit(AST::ObjectPattern *ast) override;
bool visit(AST::PostDecrementExpression *ast) override;
bool visit(AST::PostIncrementExpression *ast) override;
bool visit(AST::PreDecrementExpression *ast) override;
bool visit(AST::PreIncrementExpression *ast) override;
bool visit(AST::RegExpLiteral *ast) override;
bool visit(AST::StringLiteral *ast) override;
+ bool visit(AST::TemplateLiteral *ast) override;
bool visit(AST::ThisExpression *ast) override;
bool visit(AST::TildeExpression *ast) override;
bool visit(AST::TrueLiteral *ast) override;
@@ -588,10 +633,9 @@ protected:
bool visit(AST::UnaryPlusExpression *ast) override;
bool visit(AST::VoidExpression *ast) override;
bool visit(AST::FunctionDeclaration *ast) override;
-
- // source elements
- bool visit(AST::FunctionSourceElement *ast) override;
- bool visit(AST::StatementSourceElement *ast) override;
+ bool visit(AST::YieldExpression *ast) override;
+ bool visit(AST::ClassExpression *ast) override;
+ bool visit(AST::ClassDeclaration *ast) override;
// statements
bool visit(AST::Block *ast) override;
@@ -605,8 +649,6 @@ protected:
bool visit(AST::ForStatement *ast) override;
bool visit(AST::IfStatement *ast) override;
bool visit(AST::LabelledStatement *ast) override;
- bool visit(AST::LocalForEachStatement *ast) override;
- bool visit(AST::LocalForStatement *ast) override;
bool visit(AST::ReturnStatement *ast) override;
bool visit(AST::SwitchStatement *ast) override;
bool visit(AST::ThrowStatement *ast) override;
@@ -635,18 +677,36 @@ public:
Reference binopHelper(QSOperator::Op oper, Reference &left, Reference &right);
Reference jumpBinop(QSOperator::Op oper, Reference &left, Reference &right);
- struct Arguments { int argc; int argv; };
+ struct Arguments { int argc; int argv; bool hasSpread; };
Arguments pushArgs(AST::ArgumentList *args);
+ void handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject);
+
+ Arguments pushTemplateArgs(AST::TemplateLiteral *args);
+ int createTemplateArray(AST::TemplateLiteral *t);
void setUseFastLookups(bool b) { useFastLookups = b; }
void handleTryCatch(AST::TryStatement *ast);
void handleTryFinally(AST::TryStatement *ast);
+
+ Reference referenceForName(const QString &name, bool lhs, const QQmlJS::AST::SourceLocation &accessLocation = QQmlJS::AST::SourceLocation());
+
QQmlRefPointer<QV4::CompiledData::CompilationUnit> generateCompilationUnit(bool generateUnitData = true);
static QQmlRefPointer<QV4::CompiledData::CompilationUnit> createUnitForLoading();
Context *currentContext() const { return _context; }
+ BytecodeGenerator *generator() const { return bytecodeGenerator; }
+
+ void loadClosure(int index);
+
+ Module *module() const { return _module; }
+
+ BytecodeGenerator::Label returnLabel() {
+ if (!_returnLabel)
+ _returnLabel = new BytecodeGenerator::Label(bytecodeGenerator->newLabel());
+ return *_returnLabel;
+ }
protected:
friend class ScanFunctions;
@@ -654,23 +714,56 @@ protected:
friend struct ControlFlowCatch;
friend struct ControlFlowFinally;
Result _expr;
- VolatileMemoryLocations _volataleMemoryLocations;
+ VolatileMemoryLocations _volatileMemoryLocations;
Module *_module;
int _returnAddress;
Context *_context;
+ Context *_functionContext = nullptr;
AST::LabelledStatement *_labelledStatement;
QV4::Compiler::JSUnitGenerator *jsUnitGenerator;
BytecodeGenerator *bytecodeGenerator = nullptr;
+ Moth::BytecodeGenerator::Label *_returnLabel = nullptr;
bool _strictMode;
bool useFastLookups = true;
bool requiresReturnValue = false;
+ bool insideSwitch = false;
+ bool inFormalParameterList = false;
+ bool functionEndsWithReturn = false;
+ bool _tailCallsAreAllowed = true;
+
+ ControlFlow *controlFlow = nullptr;
bool _fileNameIsUrl;
bool hasError;
QList<QQmlJS::DiagnosticMessage> _errors;
+ class TailCallBlocker
+ {
+ public:
+ TailCallBlocker(Codegen *cg, bool onoff = false)
+ : _cg(cg)
+ , _saved(_cg->_tailCallsAreAllowed)
+ , _onoff(onoff)
+ { _cg->_tailCallsAreAllowed = onoff; }
+
+ ~TailCallBlocker()
+ { _cg->_tailCallsAreAllowed = _saved; }
+
+ void unblock() const
+ { _cg->_tailCallsAreAllowed = _saved; }
+
+ void reblock() const
+ { _cg->_tailCallsAreAllowed = _onoff; }
+
+ private:
+ Codegen *_cg;
+ bool _saved;
+ bool _onoff;
+ };
+
private:
VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast) const;
+ void handleConstruct(const Reference &base, AST::ArgumentList *args);
};
}
diff --git a/src/qml/compiler/qv4compilationunitmapper_unix.cpp b/src/qml/compiler/qv4compilationunitmapper_unix.cpp
index 8348613888..6768bc9596 100644
--- a/src/qml/compiler/qv4compilationunitmapper_unix.cpp
+++ b/src/qml/compiler/qv4compilationunitmapper_unix.cpp
@@ -42,7 +42,7 @@
#include <sys/mman.h>
#include <functional>
#include <private/qcore_unix_p.h>
-#include <private/qdeferredcleanup_p.h>
+#include <QScopeGuard>
#include <QDateTime>
#include "qv4compileddata_p.h"
@@ -61,7 +61,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
return nullptr;
}
- QDeferredCleanup cleanup([fd]{
+ auto cleanup = qScopeGuard([fd]{
qt_safe_close(fd) ;
});
@@ -92,8 +92,16 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
void CompilationUnitMapper::close()
{
- if (dataPtr != nullptr)
- munmap(dataPtr, length);
+ // Do not unmap the data here.
+ if (dataPtr != nullptr) {
+ // Do not unmap cache files that are built with the StaticData flag. That's the majority of
+ // them and it's necessary to benefit from the QString literal optimization. There might
+ // still be QString instances around that point into that memory area. The memory is backed
+ // on the disk, so the kernel is free to release the pages and all that remains is the
+ // address space allocation.
+ if (!(reinterpret_cast<CompiledData::Unit*>(dataPtr)->flags & CompiledData::Unit::StaticData))
+ munmap(dataPtr, length);
+ }
dataPtr = nullptr;
}
diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/compiler/qv4compilationunitmapper_win.cpp
index 8b000021f8..779c1288fe 100644
--- a/src/qml/compiler/qv4compilationunitmapper_win.cpp
+++ b/src/qml/compiler/qv4compilationunitmapper_win.cpp
@@ -40,7 +40,7 @@
#include "qv4compilationunitmapper_p.h"
#include "qv4compileddata_p.h"
-#include <private/qdeferredcleanup_p.h>
+#include <QScopeGuard>
#include <QFileInfo>
#include <QDateTime>
#include <qt_windows.h>
@@ -71,7 +71,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
return nullptr;
}
- QDeferredCleanup fileHandleCleanup([handle]{
+ auto fileHandleCleanup = qScopeGuard([handle]{
CloseHandle(handle);
});
@@ -90,24 +90,19 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
if (!header.verifyHeader(sourceTimeStamp, errorString))
return nullptr;
- const uint mappingFlags = header.flags & QV4::CompiledData::Unit::ContainsMachineCode
- ? PAGE_EXECUTE_READ : PAGE_READONLY;
- const uint viewFlags = header.flags & QV4::CompiledData::Unit::ContainsMachineCode
- ? (FILE_MAP_READ | FILE_MAP_EXECUTE) : FILE_MAP_READ;
-
// Data structure and qt version matched, so now we can access the rest of the file safely.
- HANDLE fileMappingHandle = CreateFileMapping(handle, 0, mappingFlags, 0, 0, 0);
+ HANDLE fileMappingHandle = CreateFileMapping(handle, 0, PAGE_READONLY, 0, 0, 0);
if (!fileMappingHandle) {
*errorString = qt_error_string(GetLastError());
return nullptr;
}
- QDeferredCleanup mappingCleanup([fileMappingHandle]{
+ auto mappingCleanup = qScopeGuard([fileMappingHandle]{
CloseHandle(fileMappingHandle);
});
- dataPtr = MapViewOfFile(fileMappingHandle, viewFlags, 0, 0, 0);
+ dataPtr = MapViewOfFile(fileMappingHandle, FILE_MAP_READ, 0, 0, 0);
if (!dataPtr) {
*errorString = qt_error_string(GetLastError());
return nullptr;
@@ -118,8 +113,15 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co
void CompilationUnitMapper::close()
{
- if (dataPtr != nullptr)
- UnmapViewOfFile(dataPtr);
+ if (dataPtr != nullptr) {
+ // Do not unmap cache files that are built with the StaticData flag. That's the majority of
+ // them and it's necessary to benefit from the QString literal optimization. There might
+ // still be QString instances around that point into that memory area. The memory is backed
+ // on the disk, so the kernel is free to release the pages and all that remains is the
+ // address space allocation.
+ if (!(reinterpret_cast<CompiledData::Unit*>(dataPtr)->flags & CompiledData::Unit::StaticData))
+ UnmapViewOfFile(dataPtr);
+ }
dataPtr = nullptr;
}
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index 8dcc068a06..c82f3d1bc8 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -50,6 +50,7 @@
#include <private/qqmltypeloader_p.h>
#include <private/qqmlengine_p.h>
#include <private/qv4vme_moth_p.h>
+#include <private/qv4module_p.h>
#include "qv4compilationunitmapper_p.h"
#include <QQmlPropertyMap>
#include <QDateTime>
@@ -64,6 +65,7 @@
#include <QCoreApplication>
#include <QCryptographicHash>
#include <QSaveFile>
+#include <QScopeGuard>
// generated by qmake:
#include "qml_compile_hash_p.h"
@@ -76,36 +78,56 @@ namespace QV4 {
namespace CompiledData {
+#if defined(QML_COMPILE_HASH)
+# ifdef Q_OS_LINUX
+// Place on a separate section on Linux so it's easier to check from outside
+// what the hash version is.
+__attribute__((section(".qml_compile_hash")))
+# endif
+const char qml_compile_hash[48 + 1] = QML_COMPILE_HASH;
static_assert(sizeof(Unit::libraryVersionHash) >= QML_COMPILE_HASH_LENGTH + 1, "Compile hash length exceeds reserved size in data structure. Please adjust and bump the format version");
+#else
+# error "QML_COMPILE_HASH must be defined for the build of QtDeclarative to ensure version checking for cache files"
+#endif
-#if !defined(V4_BOOTSTRAP)
-static QString cacheFilePath(const QUrl &url)
+
+CompilationUnit::CompilationUnit(const Unit *unitData, const QString &fileName, const QString &finalUrlString)
{
- const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
- const QString localCachePath = localSourcePath + QLatin1Char('c');
-#ifndef Q_OS_ANDROID
- if (QFile::exists(localCachePath) || QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable())
- return localCachePath;
-#endif
- QCryptographicHash fileNameHash(QCryptographicHash::Sha1);
- fileNameHash.addData(localSourcePath.toUtf8());
- QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/");
- QDir::root().mkpath(directory);
- return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + QFileInfo(localCachePath).completeSuffix();
+ setUnitData(unitData, nullptr, fileName, finalUrlString);
}
-#endif
-
-CompilationUnit::CompilationUnit(const Unit *unitData)
- : data(unitData)
-{}
#ifndef V4_BOOTSTRAP
CompilationUnit::~CompilationUnit()
{
unlink();
- if (data && !(data->flags & QV4::CompiledData::Unit::StaticData))
- free(const_cast<Unit *>(data));
+
+ if (data) {
+ if (data->qmlUnit() != qmlData)
+ free(const_cast<QmlUnit *>(qmlData));
+ qmlData = nullptr;
+
+ if (!(data->flags & QV4::CompiledData::Unit::StaticData))
+ free(const_cast<Unit *>(data));
+ }
data = nullptr;
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ delete [] constants;
+ constants = nullptr;
+#endif
+
+ delete [] imports;
+ imports = nullptr;
+}
+
+QString CompilationUnit::localCacheFilePath(const QUrl &url)
+{
+ const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
+ const QString cacheFileSuffix = QFileInfo(localSourcePath + QLatin1Char('c')).completeSuffix();
+ QCryptographicHash fileNameHash(QCryptographicHash::Sha1);
+ fileNameHash.addData(localSourcePath.toUtf8());
+ QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/");
+ QDir::root().mkpath(directory);
+ return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + cacheFileSuffix;
}
QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
@@ -115,27 +137,21 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
Q_ASSERT(!runtimeStrings);
Q_ASSERT(data);
- runtimeStrings = (QV4::Heap::String **)malloc(data->stringTableSize * sizeof(QV4::Heap::String*));
+ const quint32 stringCount = totalStringCount();
+ runtimeStrings = (QV4::Heap::String **)malloc(stringCount * sizeof(QV4::Heap::String*));
// memset the strings to 0 in case a GC run happens while we're within the loop below
- memset(runtimeStrings, 0, data->stringTableSize * sizeof(QV4::Heap::String*));
- for (uint i = 0; i < data->stringTableSize; ++i)
- runtimeStrings[i] = engine->newString(data->stringAt(i));
+ memset(runtimeStrings, 0, stringCount * sizeof(QV4::Heap::String*));
+ for (uint i = 0; i < stringCount; ++i)
+ runtimeStrings[i] = engine->newString(stringAt(i));
runtimeRegularExpressions = new QV4::Value[data->regexpTableSize];
// memset the regexps to 0 in case a GC run happens while we're within the loop below
memset(runtimeRegularExpressions, 0, data->regexpTableSize * sizeof(QV4::Value));
for (uint i = 0; i < data->regexpTableSize; ++i) {
const CompiledData::RegExp *re = data->regexpAt(i);
- bool global = false;
- bool multiline = false;
- bool ignoreCase = false;
- if (re->flags & CompiledData::RegExp::RegExp_Global)
- global = true;
- if (re->flags & CompiledData::RegExp::RegExp_IgnoreCase)
- ignoreCase = true;
- if (re->flags & CompiledData::RegExp::RegExp_Multiline)
- multiline = true;
- runtimeRegularExpressions[i] = QV4::RegExp::create(engine, data->stringAt(re->stringIndex), ignoreCase, multiline, global);
+ uint f = re->flags;
+ const CompiledData::RegExp::Flags flags = static_cast<CompiledData::RegExp::Flags>(f);
+ runtimeRegularExpressions[i] = QV4::RegExp::create(engine, stringAt(re->stringIndex), flags);
}
if (data->lookupTableSize) {
@@ -157,36 +173,45 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
}
if (data->jsClassTableSize) {
- runtimeClasses = (QV4::InternalClass**)malloc(data->jsClassTableSize * sizeof(QV4::InternalClass*));
+ runtimeClasses = (QV4::Heap::InternalClass **)malloc(data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *));
+ // memset the regexps to 0 in case a GC run happens while we're within the loop below
+ memset(runtimeClasses, 0, data->jsClassTableSize * sizeof(QV4::Heap::InternalClass *));
for (uint i = 0; i < data->jsClassTableSize; ++i) {
int memberCount = 0;
const CompiledData::JSClassMember *member = data->jsClassAt(i, &memberCount);
- QV4::InternalClass *klass = engine->internalClasses[QV4::ExecutionEngine::Class_Object];
+ runtimeClasses[i] = engine->internalClasses(QV4::ExecutionEngine::Class_Object);
for (int j = 0; j < memberCount; ++j, ++member)
- klass = klass->addMember(engine->identifierTable->identifier(runtimeStrings[member->nameOffset]), member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data);
-
- runtimeClasses[i] = klass;
+ runtimeClasses[i] = runtimeClasses[i]->addMember(engine->identifierTable->asPropertyKey(runtimeStrings[member->nameOffset]), member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data);
}
}
-#if Q_BYTE_ORDER == Q_BIG_ENDIAN
- Value *bigEndianConstants = new Value[data->constantTableSize];
- const quint64_le *littleEndianConstants = data->constants();
- for (uint i = 0; i < data->constantTableSize; ++i)
- bigEndianConstants[i] = Value::fromReturnedValue(littleEndianConstants[i]);
- constants = bigEndianConstants;
-#else
- constants = reinterpret_cast<const Value*>(data->constants());
-#endif
+ runtimeFunctions.resize(data->functionTableSize);
+ for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
+ const QV4::CompiledData::Function *compiledFunction = data->functionAt(i);
+ runtimeFunctions[i] = new QV4::Function(engine, this, compiledFunction);
+ }
+
+ Scope scope(engine);
+ Scoped<InternalClass> ic(scope);
- linkBackendToEngine(engine);
+ runtimeBlocks.resize(data->blockTableSize);
+ for (int i = 0 ;i < runtimeBlocks.size(); ++i) {
+ const QV4::CompiledData::Block *compiledBlock = data->blockAt(i);
+ ic = engine->internalClasses(EngineBase::Class_CallContext);
+
+ // first locals
+ const quint32_le *localsIndices = compiledBlock->localsTable();
+ for (quint32 i = 0; i < compiledBlock->nLocals; ++i)
+ ic = ic->addMember(engine->identifierTable->asPropertyKey(runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
+ runtimeBlocks[i] = ic->d();
+ }
static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
if (showCode) {
qDebug() << "=== Constant table";
Moth::dumpConstantTable(constants, data->constantTableSize);
qDebug() << "=== String table";
- for (uint i = 0; i < data->stringTableSize; ++i)
+ for (uint i = 0, end = totalStringCount(); i < end; ++i)
qDebug() << " " << i << ":" << runtimeStrings[i]->toQString();
qDebug() << "=== Closure table";
for (uint i = 0; i < data->functionTableSize; ++i)
@@ -215,8 +240,6 @@ void CompilationUnit::unlink()
propertyCaches.clear();
- for (int ii = 0; ii < dependentScripts.count(); ++ii)
- dependentScripts.at(ii)->release();
dependentScripts.clear();
typeNameCache = nullptr;
@@ -236,37 +259,50 @@ void CompilationUnit::unlink()
runtimeClasses = nullptr;
qDeleteAll(runtimeFunctions);
runtimeFunctions.clear();
-#if Q_BYTE_ORDER == Q_BIG_ENDIAN
- delete [] constants;
- constants = nullptr;
-#endif
}
void CompilationUnit::markObjects(QV4::MarkStack *markStack)
{
- for (uint i = 0; i < data->stringTableSize; ++i)
- if (runtimeStrings[i])
- runtimeStrings[i]->mark(markStack);
+ if (runtimeStrings) {
+ for (uint i = 0, end = totalStringCount(); i < end; ++i)
+ if (runtimeStrings[i])
+ runtimeStrings[i]->mark(markStack);
+ }
if (runtimeRegularExpressions) {
for (uint i = 0; i < data->regexpTableSize; ++i)
runtimeRegularExpressions[i].mark(markStack);
}
+ if (runtimeClasses) {
+ for (uint i = 0; i < data->jsClassTableSize; ++i)
+ if (runtimeClasses[i])
+ runtimeClasses[i]->mark(markStack);
+ }
+ for (QV4::Function *f : qAsConst(runtimeFunctions))
+ if (f && f->internalClass)
+ f->internalClass->mark(markStack);
+ for (QV4::Heap::InternalClass *c : qAsConst(runtimeBlocks))
+ if (c)
+ c->mark(markStack);
+
+ if (runtimeLookups) {
+ for (uint i = 0; i < data->lookupTableSize; ++i)
+ runtimeLookups[i].markObjects(markStack);
+ }
+
+ if (m_module)
+ m_module->mark(markStack);
}
-IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjectIndex)
+IdentifierHash CompilationUnit::createNamedObjectsPerComponent(int componentObjectIndex)
{
- auto it = namedObjectsPerComponentCache.find(componentObjectIndex);
- if (it == namedObjectsPerComponentCache.end()) {
- IdentifierHash namedObjectCache(engine);
- const CompiledData::Object *component = data->objectAt(componentObjectIndex);
- const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable();
- for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) {
- const CompiledData::Object *namedObject = data->objectAt(*namedObjectIndexPtr);
- namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id);
- }
- it = namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache);
- }
- return *it;
+ IdentifierHash namedObjectCache(engine);
+ const CompiledData::Object *component = objectAt(componentObjectIndex);
+ const quint32_le *namedObjectIndexPtr = component->namedObjectsInComponentTable();
+ for (quint32 i = 0; i < component->nNamedObjectsInComponent; ++i, ++namedObjectIndexPtr) {
+ const CompiledData::Object *namedObject = objectAt(*namedObjectIndexPtr);
+ namedObjectCache.add(runtimeStrings[namedObject->idNameIndex], namedObject->id);
+ }
+ return *namedObjectsPerComponentCache.insert(componentObjectIndex, namedObjectCache);
}
void CompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine)
@@ -294,8 +330,8 @@ void CompilationUnit::finalizeCompositeType(QQmlEnginePrivate *qmlEngine)
int bindingCount = 0;
int parserStatusCount = 0;
int objectCount = 0;
- for (quint32 i = 0; i < data->nObjects; ++i) {
- const QV4::CompiledData::Object *obj = data->objectAt(i);
+ for (quint32 i = 0, count = this->objectCount(); i < count; ++i) {
+ const QV4::CompiledData::Object *obj = objectAt(i);
bindingCount += obj->nBindings;
if (auto *typeRef = resolvedTypes.value(obj->inheritedTypeNameIndex)) {
if (typeRef->type.isValid()) {
@@ -334,6 +370,219 @@ bool CompilationUnit::verifyChecksum(const DependentTypesHasher &dependencyHashe
sizeof(data->dependencyMD5Checksum)) == 0;
}
+QStringList CompilationUnit::moduleRequests() const
+{
+ QStringList requests;
+ requests.reserve(data->moduleRequestTableSize);
+ for (uint i = 0; i < data->moduleRequestTableSize; ++i)
+ requests << stringAt(data->moduleRequestTable()[i]);
+ return requests;
+}
+
+Heap::Module *CompilationUnit::instantiate(ExecutionEngine *engine)
+{
+ if (m_module)
+ return m_module;
+
+ if (!this->engine)
+ linkToEngine(engine);
+
+ m_module = engine->memoryManager->allocate<Module>(engine, this);
+
+ for (const QString &request: moduleRequests()) {
+ auto dependentModuleUnit = engine->loadModule(QUrl(request), this);
+ if (engine->hasException)
+ return nullptr;
+ dependentModuleUnit->instantiate(engine);
+ }
+
+ Scope scope(engine);
+ ScopedString importName(scope);
+
+ const uint importCount = data->importEntryTableSize;
+ 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);
+ importName = runtimeStrings[entry.importName];
+ const Value *valuePtr = dependentModuleUnit->resolveExport(importName);
+ if (!valuePtr) {
+ QString referenceErrorMessage = QStringLiteral("Unable to resolve import reference ");
+ referenceErrorMessage += importName->toQString();
+ engine->throwReferenceError(referenceErrorMessage, fileName(), /*### line*/1, /*### column*/1);
+ return nullptr;
+ }
+ imports[i] = valuePtr;
+ }
+
+ for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) {
+ const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i];
+ auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this);
+ if (!dependentModuleUnit)
+ return nullptr;
+
+ ScopedString importName(scope, runtimeStrings[entry.importName]);
+ if (!dependentModuleUnit->resolveExport(importName)) {
+ QString referenceErrorMessage = QStringLiteral("Unable to resolve re-export reference ");
+ referenceErrorMessage += importName->toQString();
+ engine->throwReferenceError(referenceErrorMessage, fileName(), /*### line*/1, /*### column*/1);
+ return nullptr;
+ }
+ }
+
+ return m_module;
+}
+
+const Value *CompilationUnit::resolveExport(QV4::String *exportName)
+{
+ QVector<ResolveSetEntry> resolveSet;
+ return resolveExportRecursively(exportName, &resolveSet);
+}
+
+QStringList CompilationUnit::exportedNames() const
+{
+ QStringList names;
+ QVector<const CompiledData::CompilationUnit*> exportNameSet;
+ getExportedNamesRecursively(&names, &exportNameSet);
+ names.sort();
+ auto last = std::unique(names.begin(), names.end());
+ names.erase(last, names.end());
+ return names;
+}
+
+const Value *CompilationUnit::resolveExportRecursively(QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet)
+{
+ if (!m_module)
+ return nullptr;
+
+ for (const auto &entry: *resolveSet)
+ if (entry.module == this && entry.exportName->isEqualTo(exportName))
+ return nullptr;
+
+ (*resolveSet) << ResolveSetEntry(this, exportName);
+
+ if (exportName->toQString() == QLatin1String("*"))
+ return &m_module->self;
+
+ Scope scope(engine);
+
+ if (auto localExport = lookupNameInExportTable(data->localExportEntryTable(), data->localExportEntryTableSize, exportName)) {
+ ScopedString localName(scope, runtimeStrings[localExport->localName]);
+ uint index = m_module->scope->internalClass->indexOfValueOrGetter(localName->toPropertyKey());
+ if (index == UINT_MAX)
+ return nullptr;
+ if (index >= m_module->scope->locals.size)
+ return imports[index - m_module->scope->locals.size];
+ return &m_module->scope->locals[index];
+ }
+
+ if (auto indirectExport = lookupNameInExportTable(data->indirectExportEntryTable(), data->indirectExportEntryTableSize, exportName)) {
+ auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(indirectExport->moduleRequest)), this);
+ if (!dependentModuleUnit)
+ return nullptr;
+ ScopedString importName(scope, runtimeStrings[indirectExport->importName]);
+ return dependentModuleUnit->resolveExportRecursively(importName, resolveSet);
+ }
+
+
+ if (exportName->toQString() == QLatin1String("default"))
+ return nullptr;
+
+ const Value *starResolution = nullptr;
+
+ for (uint i = 0; i < data->starExportEntryTableSize; ++i) {
+ const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i];
+ auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this);
+ if (!dependentModuleUnit)
+ return nullptr;
+
+ const Value *resolution = dependentModuleUnit->resolveExportRecursively(exportName, resolveSet);
+ // ### handle ambiguous
+ if (resolution) {
+ if (!starResolution) {
+ starResolution = resolution;
+ continue;
+ }
+ if (resolution != starResolution)
+ return nullptr;
+ }
+ }
+
+ return starResolution;
+}
+
+const ExportEntry *CompilationUnit::lookupNameInExportTable(const ExportEntry *firstExportEntry, int tableSize, QV4::String *name) const
+{
+ const CompiledData::ExportEntry *lastExportEntry = firstExportEntry + tableSize;
+ auto matchingExport = std::lower_bound(firstExportEntry, lastExportEntry, name, [this](const CompiledData::ExportEntry &lhs, QV4::String *name) {
+ return stringAt(lhs.exportName) < name->toQString();
+ });
+ if (matchingExport == lastExportEntry || stringAt(matchingExport->exportName) != name->toQString())
+ return nullptr;
+ return matchingExport;
+}
+
+void CompilationUnit::getExportedNamesRecursively(QStringList *names, QVector<const CompilationUnit*> *exportNameSet, bool includeDefaultExport) const
+{
+ if (exportNameSet->contains(this))
+ return;
+ exportNameSet->append(this);
+
+ const auto append = [names, includeDefaultExport](const QString &name) {
+ if (!includeDefaultExport && name == QLatin1String("default"))
+ return;
+ names->append(name);
+ };
+
+ for (uint i = 0; i < data->localExportEntryTableSize; ++i) {
+ const CompiledData::ExportEntry &entry = data->localExportEntryTable()[i];
+ append(stringAt(entry.exportName));
+ }
+
+ for (uint i = 0; i < data->indirectExportEntryTableSize; ++i) {
+ const CompiledData::ExportEntry &entry = data->indirectExportEntryTable()[i];
+ append(stringAt(entry.exportName));
+ }
+
+ for (uint i = 0; i < data->starExportEntryTableSize; ++i) {
+ const CompiledData::ExportEntry &entry = data->starExportEntryTable()[i];
+ auto dependentModuleUnit = engine->loadModule(QUrl(stringAt(entry.moduleRequest)), this);
+ if (!dependentModuleUnit)
+ return;
+ dependentModuleUnit->getExportedNamesRecursively(names, exportNameSet, /*includeDefaultExport*/false);
+ }
+}
+
+void CompilationUnit::evaluate()
+{
+ if (m_moduleEvaluated)
+ return;
+ m_moduleEvaluated = true;
+
+ for (const QString &request: moduleRequests()) {
+ auto dependentModuleUnit = engine->loadModule(QUrl(request), this);
+ if (engine->hasException)
+ return;
+ dependentModuleUnit->evaluate();
+ if (engine->hasException)
+ return;
+ }
+
+ QV4::Function *moduleFunction = runtimeFunctions[data->indexOfRootFunction];
+ CppStackFrame frame;
+ frame.init(engine, moduleFunction, nullptr, 0);
+ frame.setupJSFrame(engine->jsStackTop, Value::undefinedValue(), m_module->scope,
+ Value::undefinedValue(), Value::undefinedValue());
+
+ frame.push();
+ engine->jsStackTop += frame.requiredJSStackFrameSize();
+ auto frameCleanup = qScopeGuard([&frame]() {
+ frame.pop();
+ });
+ Moth::VME::exec(&frame, engine);
+}
+
bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString)
{
if (!QQmlFile::isLocalFile(url)) {
@@ -344,31 +593,31 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeS
const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url);
QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper());
- CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourceTimeStamp, errorString);
- if (!mappedUnit)
- return false;
-
- const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr;
- QScopedValueRollback<const Unit *> dataPtrChange(data, mappedUnit);
+ const QStringList cachePaths = { sourcePath + QLatin1Char('c'), localCacheFilePath(url) };
+ for (const QString &cachePath : cachePaths) {
+ CompiledData::Unit *mappedUnit = cacheFile->open(cachePath, sourceTimeStamp, errorString);
+ if (!mappedUnit)
+ continue;
+
+ const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr;
+ const Unit *oldData = data;
+ auto dataPtrRevert = qScopeGuard([this, oldData](){
+ setUnitData(oldData);
+ });
+ setUnitData(mappedUnit);
+
+ if (data->sourceFileIndex != 0 && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) {
+ *errorString = QStringLiteral("QML source file has moved to a different location.");
+ continue;
+ }
- if (data->sourceFileIndex != 0 && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) {
- *errorString = QStringLiteral("QML source file has moved to a different location.");
- return false;
+ dataPtrRevert.dismiss();
+ free(const_cast<Unit*>(oldDataPtr));
+ backingFile.reset(cacheFile.take());
+ return true;
}
- dataPtrChange.commit();
- free(const_cast<Unit*>(oldDataPtr));
- backingFile.reset(cacheFile.take());
- return true;
-}
-
-void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine)
-{
- runtimeFunctions.resize(data->functionTableSize);
- for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
- const QV4::CompiledData::Function *compiledFunction = data->functionAt(i);
- runtimeFunctions[i] = new QV4::Function(engine, this, compiledFunction, &Moth::VME::exec);
- }
+ return false;
}
#endif // V4_BOOTSTRAP
@@ -391,7 +640,7 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
*errorString = QStringLiteral("File has to be a local file.");
return false;
}
- const QString outputFileName = cacheFilePath(unitUrl);
+ const QString outputFileName = localCacheFilePath(unitUrl);
#endif
#if QT_CONFIG(temporaryfile)
@@ -429,109 +678,38 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString)
#endif // QT_CONFIG(temporaryfile)
}
-Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument)
+void CompilationUnit::setUnitData(const Unit *unitData, const QmlUnit *qmlUnit,
+ const QString &fileName, const QString &finalUrlString)
{
- if (!irDocument->javaScriptCompilationUnit->data)
- return irDocument->jsGenerator.generateUnit(QV4::Compiler::JSUnitGenerator::GenerateWithoutStringTable);
-
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = irDocument->javaScriptCompilationUnit;
- QV4::CompiledData::Unit *jsUnit = const_cast<QV4::CompiledData::Unit*>(compilationUnit->data);
- auto ensureWritableUnit = [&jsUnit, &compilationUnit]() {
- if (jsUnit == compilationUnit->data) {
- char *unitCopy = (char*)malloc(jsUnit->unitSize);
- memcpy(unitCopy, jsUnit, jsUnit->unitSize);
- jsUnit = reinterpret_cast<QV4::CompiledData::Unit*>(unitCopy);
- }
- };
-
- QV4::Compiler::StringTableGenerator &stringTable = irDocument->jsGenerator.stringTable;
-
- if (jsUnit->sourceFileIndex == quint32(0) || jsUnit->stringAt(jsUnit->sourceFileIndex) != irDocument->jsModule.fileName) {
- ensureWritableUnit();
- jsUnit->sourceFileIndex = stringTable.registerString(irDocument->jsModule.fileName);
- jsUnit->finalUrlIndex = stringTable.registerString(irDocument->jsModule.finalUrl);
- }
-
- // Collect signals that have had a change in signature (from onClicked to onClicked(mouse) for example)
- // and now need fixing in the QV4::CompiledData. Also register strings at the same time, to finalize
- // the string table.
- QVector<quint32> changedSignals;
- QVector<QQmlJS::AST::FormalParameterList*> changedSignalParameters;
- for (QmlIR::Object *o: qAsConst(irDocument->objects)) {
- for (QmlIR::Binding *binding = o->firstBinding(); binding; binding = binding->next) {
- if (!(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression))
- continue;
-
- quint32 functionIndex = binding->value.compiledScriptIndex;
- QmlIR::CompiledFunctionOrExpression *foe = o->functionsAndExpressions->slowAt(functionIndex);
- if (!foe)
- continue;
-
- // save absolute index
- changedSignals << o->runtimeFunctionIndices.at(functionIndex);
-
- Q_ASSERT(foe->node);
- Q_ASSERT(QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(foe->node));
-
- QQmlJS::AST::FormalParameterList *parameters = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(foe->node)->formals;
- changedSignalParameters << parameters;
-
- for (; parameters; parameters = parameters->next)
- stringTable.registerString(parameters->name.toString());
- }
- }
-
- QVector<quint32> signalParameterNameTable;
- quint32 signalParameterNameTableOffset = jsUnit->unitSize;
-
- // Update signal signatures
- if (!changedSignals.isEmpty()) {
- if (jsUnit == compilationUnit->data) {
- char *unitCopy = (char*)malloc(jsUnit->unitSize);
- memcpy(unitCopy, jsUnit, jsUnit->unitSize);
- jsUnit = reinterpret_cast<QV4::CompiledData::Unit*>(unitCopy);
- }
-
- for (int i = 0; i < changedSignals.count(); ++i) {
- const uint functionIndex = changedSignals.at(i);
- // The data is now read-write due to the copy above, so the const_cast is ok.
- QV4::CompiledData::Function *function = const_cast<QV4::CompiledData::Function *>(jsUnit->functionAt(functionIndex));
- Q_ASSERT(function->nFormals == quint32(0));
-
- function->formalsOffset = signalParameterNameTableOffset - jsUnit->functionOffsetTable()[functionIndex];
-
- for (QQmlJS::AST::FormalParameterList *parameters = changedSignalParameters.at(i);
- parameters; parameters = parameters->next) {
- signalParameterNameTable.append(stringTable.getStringId(parameters->name.toString()));
- function->nFormals = function->nFormals + 1;
- }
-
- // Hack to ensure an activation is created.
- function->flags |= QV4::CompiledData::Function::HasCatchOrWith | QV4::CompiledData::Function::HasDirectEval;
-
- signalParameterNameTableOffset += function->nFormals * sizeof(quint32);
- }
- }
+ data = unitData;
+ qmlData = nullptr;
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ delete [] constants;
+#endif
+ constants = nullptr;
+ m_fileName.clear();
+ m_finalUrlString.clear();
+ if (!data)
+ return;
- if (!signalParameterNameTable.isEmpty()) {
- ensureWritableUnit();
- Q_ASSERT(jsUnit != compilationUnit->data);
- const uint signalParameterTableSize = signalParameterNameTable.count() * sizeof(quint32);
- uint newSize = jsUnit->unitSize + signalParameterTableSize;
- const uint oldSize = jsUnit->unitSize;
- char *unitWithSignalParameters = (char*)realloc(jsUnit, newSize);
- memcpy(unitWithSignalParameters + oldSize, signalParameterNameTable.constData(), signalParameterTableSize);
- jsUnit = reinterpret_cast<QV4::CompiledData::Unit*>(unitWithSignalParameters);
- jsUnit->unitSize = newSize;
- }
+ qmlData = qmlUnit ? qmlUnit : data->qmlUnit();
- if (jsUnit != compilationUnit->data)
- jsUnit->flags &= ~QV4::CompiledData::Unit::StaticData;
+#if Q_BYTE_ORDER == Q_BIG_ENDIAN
+ Value *bigEndianConstants = new Value[data->constantTableSize];
+ const quint64_le *littleEndianConstants = data->constants();
+ for (uint i = 0; i < data->constantTableSize; ++i)
+ bigEndianConstants[i] = Value::fromReturnedValue(littleEndianConstants[i]);
+ constants = bigEndianConstants;
+#else
+ constants = reinterpret_cast<const Value*>(data->constants());
+#endif
- return jsUnit;
+ m_fileName = !fileName.isEmpty() ? fileName : stringAt(data->sourceFileIndex);
+ m_finalUrlString = !finalUrlString.isEmpty() ? finalUrlString : stringAt(data->finalUrlIndex);
}
-QString Binding::valueAsString(const Unit *unit) const
+#ifndef V4_BOOTSTRAP
+QString Binding::valueAsString(const CompilationUnit *unit) const
{
switch (type) {
case Type_Script:
@@ -540,29 +718,31 @@ QString Binding::valueAsString(const Unit *unit) const
case Type_Boolean:
return value.b ? QStringLiteral("true") : QStringLiteral("false");
case Type_Number:
- return QString::number(valueAsNumber());
+ return QString::number(valueAsNumber(unit->constants));
case Type_Invalid:
return QString();
#if !QT_CONFIG(translation)
case Type_TranslationById:
case Type_Translation:
- return unit->stringAt(stringIndex);
+ return unit->stringAt(unit->unitData->translations()[value.translationDataIndex].stringIndex);
#else
case Type_TranslationById: {
- QByteArray id = unit->stringAt(stringIndex).toUtf8();
- return qtTrId(id.constData(), value.translationData.number);
+ const TranslationData &translation = unit->unitData()->translations()[value.translationDataIndex];
+ QByteArray id = unit->stringAt(translation.stringIndex).toUtf8();
+ return qtTrId(id.constData(), translation.number);
}
case Type_Translation: {
+ const TranslationData &translation = unit->unitData()->translations()[value.translationDataIndex];
// This code must match that in the qsTr() implementation
- const QString &path = unit->stringAt(unit->sourceFileIndex);
+ const QString &path = unit->stringAt(unit->unitData()->sourceFileIndex);
int lastSlash = path.lastIndexOf(QLatin1Char('/'));
QStringRef context = (lastSlash > -1) ? path.midRef(lastSlash + 1, path.length() - lastSlash - 5)
: QStringRef();
QByteArray contextUtf8 = context.toUtf8();
- QByteArray comment = unit->stringAt(value.translationData.commentIndex).toUtf8();
- QByteArray text = unit->stringAt(stringIndex).toUtf8();
+ QByteArray comment = unit->stringAt(translation.commentIndex).toUtf8();
+ QByteArray text = unit->stringAt(translation.stringIndex).toUtf8();
return QCoreApplication::translate(contextUtf8.constData(), text.constData(),
- comment.constData(), value.translationData.number);
+ comment.constData(), translation.number);
}
#endif
default:
@@ -614,7 +794,7 @@ QString Binding::escapedString(const QString &string)
return tmp;
}
-QString Binding::valueAsScriptString(const Unit *unit) const
+QString Binding::valueAsScriptString(const CompilationUnit *unit) const
{
if (type == Type_String)
return escapedString(unit->stringAt(stringIndex));
@@ -622,11 +802,10 @@ QString Binding::valueAsScriptString(const Unit *unit) const
return valueAsString(unit);
}
-#ifndef V4_BOOTSTRAP
/*!
Returns the property cache, if one alread exists. The cache is not referenced.
*/
-QQmlPropertyCache *ResolvedTypeReference::propertyCache() const
+QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::propertyCache() const
{
if (type.isValid())
return typePropertyCache;
@@ -637,7 +816,7 @@ QQmlPropertyCache *ResolvedTypeReference::propertyCache() const
/*!
Returns the property cache, creating one if it doesn't already exist. The cache is not referenced.
*/
-QQmlPropertyCache *ResolvedTypeReference::createPropertyCache(QQmlEngine *engine)
+QQmlRefPointer<QQmlPropertyCache> ResolvedTypeReference::createPropertyCache(QQmlEngine *engine)
{
if (typePropertyCache) {
return typePropertyCache;
@@ -656,7 +835,7 @@ bool ResolvedTypeReference::addToHash(QCryptographicHash *hash, QQmlEngine *engi
hash->addData(createPropertyCache(engine)->checksum(&ok));
return ok;
}
- hash->addData(compilationUnit->data->md5Checksum, sizeof(compilationUnit->data->md5Checksum));
+ hash->addData(compilationUnit->unitData()->md5Checksum, sizeof(compilationUnit->unitData()->md5Checksum));
return true;
}
@@ -754,7 +933,7 @@ bool Unit::verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString)
}
#if defined(QML_COMPILE_HASH)
- if (qstrcmp(QML_COMPILE_HASH, libraryVersionHash) != 0) {
+ if (qstrcmp(CompiledData::qml_compile_hash, libraryVersionHash) != 0) {
*errorString = QStringLiteral("QML library version mismatch. Expected compile hash does not match");
return false;
}
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h
index 1df9d6794f..1f6c591ada 100644
--- a/src/qml/compiler/qv4compileddata_p.h
+++ b/src/qml/compiler/qv4compileddata_p.h
@@ -88,6 +88,10 @@ struct Document;
namespace QV4 {
+namespace Heap {
+struct Module;
+};
+
struct Function;
class EvalISelFactory;
class CompilationUnitMapper;
@@ -133,14 +137,17 @@ static_assert(sizeof(Location) == 4, "Location structure needs to have the expec
struct RegExp
{
enum Flags : unsigned int {
+ RegExp_NoFlags = 0x0,
RegExp_Global = 0x01,
RegExp_IgnoreCase = 0x02,
- RegExp_Multiline = 0x04
+ RegExp_Multiline = 0x04,
+ RegExp_Unicode = 0x08,
+ RegExp_Sticky = 0x10
};
union {
quint32 _dummy;
- quint32_le_bitfield<0, 4> flags;
- quint32_le_bitfield<4, 28> stringIndex;
+ quint32_le_bitfield<0, 5> flags;
+ quint32_le_bitfield<5, 27> stringIndex;
};
RegExp() : _dummy(0) { }
@@ -186,21 +193,67 @@ struct JSClass
};
static_assert(sizeof(JSClass) == 4, "JSClass structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+// This data structure is intended to be binary compatible with QStringData/QStaticStringData on
+// 64-bit and 32-bit little-endian architectures, in all directions. So the same structure mapped
+// from a file must be castable to a QStringData regardless of the pointer size. With the first
+// few fields that's easy, they're always 32-bit. However the offset field of QArrayData is a
+// ptrdiff_t and thus variable in size.
+// On 64-bit systems compilers enforce an 8-byte alignment and thus place it at offset 16, while
+// on 32-bit systems offset 12 is sufficient. Therefore the two values don't overlap and contain
+// the same value.
struct String
{
+ qint32_le refcount; // -1
qint32_le size;
+ quint32_le allocAndCapacityReservedFlag; // 0
+ quint32_le offsetOn32Bit;
+ quint64_le offsetOn64Bit;
// uint16 strdata[]
static int calculateSize(const QString &str) {
- return (sizeof(String) + str.length() * sizeof(quint16) + 7) & ~0x7;
+ return (sizeof(String) + (str.length() + 1) * sizeof(quint16) + 7) & ~0x7;
}
};
-static_assert(sizeof(String) == 4, "String 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(String) == 24, "String structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+// Ensure compatibility with QString
+static_assert(offsetof(QArrayData, ref) == offsetof(String, refcount), "refcount must be at the same location");
+static_assert(offsetof(QArrayData, size) == offsetof(String, size), "size must be at the same location");
+static_assert(offsetof(String, offsetOn64Bit) == 16, "offset must be at 8-byte aligned location");
+static_assert(offsetof(String, offsetOn32Bit) == 12, "offset must be at 4-byte aligned location");
+#if QT_POINTER_SIZE == 8
+static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn64Bit), "offset must be at the same location");
+#else
+static_assert(offsetof(QArrayData, offset) == offsetof(String, offsetOn32Bit), "offset must be at the same location");
+#endif
struct CodeOffsetToLine {
quint32_le codeOffset;
quint32_le line;
};
+static_assert(sizeof(CodeOffsetToLine) == 8, "CodeOffsetToLine structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+struct Block
+{
+ quint32_le nLocals;
+ quint32_le localsOffset;
+ quint16_le sizeOfLocalTemporalDeadZone;
+ quint16_le padding;
+
+ const quint32_le *localsTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + localsOffset); }
+
+ static int calculateSize(int nLocals) {
+ int trailingData = nLocals*sizeof (quint32);
+ size_t size = align(align(sizeof(Block)) + size_t(trailingData));
+ Q_ASSERT(size < INT_MAX);
+ return int(size);
+ }
+
+ static size_t align(size_t a) {
+ return (a + 7) & ~size_t(7);
+ }
+};
+static_assert(sizeof(Block) == 12, "Block structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
// Function is aligned on an 8-byte boundary to make sure there are no bus errors or penalties
// for unaligned access. The ordering of the fields is also from largest to smallest.
@@ -208,10 +261,8 @@ struct Function
{
enum Flags : unsigned int {
IsStrict = 0x1,
- HasDirectEval = 0x2,
- UsesArgumentsObject = 0x4,
-// Unused = 0x8,
- HasCatchOrWith = 0x10
+ IsArrowFunction = 0x2,
+ IsGenerator = 0x4
};
// Absolute offset into file where the code for this function is located.
@@ -219,48 +270,52 @@ struct Function
quint32_le codeSize;
quint32_le nameIndex;
- quint32_le nFormals;
- quint32_le formalsOffset;
- quint32_le nLocals;
+ quint16_le length;
+ quint16_le nFormals;
+ quint32_le formalsOffset; // Can't turn this into a calculated offset because of the mutation in CompilationUnit::createUnitData.
quint32_le localsOffset;
- quint32_le nLineNumbers;
- quint32_le lineNumberOffset;
+ quint16_le nLocals;
+ quint16_le nLineNumbers;
+ size_t lineNumberOffset() const { return localsOffset + nLocals * sizeof(quint32); }
quint32_le nestedFunctionIndex; // for functions that only return a single closure, used in signal handlers
- quint32_le nRegisters;
+ quint16_le sizeOfLocalTemporalDeadZone;
+ quint16_le firstTemporalDeadZoneRegister;
+ quint16_le sizeOfRegisterTemporalDeadZone;
+ quint16_le nRegisters;
Location location;
// Qml Extensions Begin
- quint32_le nDependingIdObjects;
- quint32_le dependingIdObjectsOffset; // Array of resolved ID objects
- quint32_le nDependingContextProperties;
- quint32_le dependingContextPropertiesOffset; // Array of int pairs (property index and notify index)
- quint32_le nDependingScopeProperties;
- quint32_le dependingScopePropertiesOffset; // Array of int pairs (property index and notify index)
+ // 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 formalsIndex[nFormals]
-// quint32 localsIndex[nLocals]
-// quint32 offsetForInnerFunctions[nInnerFunctions]
-// Function[nInnerFunctions]
-
// Keep all unaligned data at the end
quint8 flags;
quint8 padding1;
- quint16_le padding2;
+
+ // quint32 formalsIndex[nFormals]
+ // quint32 localsIndex[nLocals]
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); }
+ 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(); }
const quint32_le *formalsEnd() const { return formalsTable() + nFormals; }
// ---
- const uchar *code() const { return reinterpret_cast<const uchar *>(this) + codeOffset; }
+ const char *code() const { return reinterpret_cast<const char *>(this) + codeOffset; }
inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; }
@@ -276,15 +331,73 @@ struct Function
return (a + 7) & ~size_t(7);
}
};
-static_assert(sizeof(Function) == 76, "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 {
+ Regular,
+ Getter,
+ Setter
+ };
+
+ quint32_le name;
+ quint32_le type;
+ quint32_le function;
+};
+static_assert(sizeof(Method) == 12, "Method structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+struct Class
+{
+ quint32_le nameIndex;
+ quint32_le scopeIndex;
+ quint32_le constructorFunction;
+ quint32_le nStaticMethods;
+ quint32_le nMethods;
+ quint32_le methodTableOffset;
+
+ const Method *methodTable() const { return reinterpret_cast<const Method *>(reinterpret_cast<const char *>(this) + methodTableOffset); }
+
+ static int calculateSize(int nStaticMethods, int nMethods) {
+ int trailingData = (nStaticMethods + nMethods) * sizeof(Method);
+ size_t size = align(sizeof(Class) + trailingData);
+ Q_ASSERT(size < INT_MAX);
+ return int(size);
+ }
+
+ static size_t align(size_t a) {
+ return (a + 7) & ~size_t(7);
+ }
+};
+static_assert(sizeof(Class) == 24, "Class structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+struct ExportEntry
+{
+ quint32_le exportName;
+ quint32_le moduleRequest;
+ quint32_le importName;
+ quint32_le localName;
+};
+static_assert(sizeof(ExportEntry) == 16, "ExportEntry structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+struct ImportEntry
+{
+ quint32_le moduleRequest;
+ quint32_le importName;
+ quint32_le localName;
+ quint32_le padding;
+};
+static_assert(sizeof(ImportEntry) == 16, "ImportEntry structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
// Qml data structures
-struct Q_QML_EXPORT TranslationData {
+struct Q_QML_EXPORT TranslationData
+{
+ quint32_le stringIndex;
quint32_le commentIndex;
qint32_le number;
+ quint32_le padding;
};
-static_assert(sizeof(TranslationData) == 8, "TranslationData 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(TranslationData) == 16, "TranslationData structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Q_QML_PRIVATE_EXPORT Binding
{
@@ -322,18 +435,16 @@ struct Q_QML_PRIVATE_EXPORT Binding
};
union {
bool b;
- quint64 doubleValue; // do not access directly, needs endian protected access
+ quint32_le constantValueIndex;
quint32_le compiledScriptIndex; // used when Type_Script
quint32_le objectIndex;
- TranslationData translationData; // used when Type_Translation
+ quint32_le translationDataIndex; // used when Type_Translation
} value;
- quint32_le stringIndex; // Set for Type_String, Type_Translation and Type_Script (the latter because of script strings)
+ quint32_le stringIndex; // Set for Type_String and Type_Script (the latter because of script strings)
Location location;
Location valueLocation;
- quint32_le padding;
-
bool isValueBinding() const
{
if (type == Type_AttachedProperty
@@ -385,25 +496,18 @@ struct Q_QML_PRIVATE_EXPORT Binding
static QString escapedString(const QString &string);
- bool containsTranslations() const { return type == Type_Translation || type == Type_TranslationById; }
- bool evaluatesToString() const { return type == Type_String || containsTranslations(); }
+ bool isTranslationBinding() const { return type == Type_Translation || type == Type_TranslationById; }
+ bool evaluatesToString() const { return type == Type_String || isTranslationBinding(); }
- QString valueAsString(const Unit *unit) const;
- QString valueAsScriptString(const Unit *unit) const;
- double valueAsNumber() const
+#ifndef V4_BOOTSTRAP
+ QString valueAsString(const CompilationUnit *unit) const;
+ QString valueAsScriptString(const CompilationUnit *unit) const;
+#endif
+ double valueAsNumber(const Value *constantTable) const
{
if (type != Type_Number)
return 0.0;
- quint64 intval = qFromLittleEndian<quint64>(value.doubleValue);
- double d;
- memcpy(&d, &intval, sizeof(double));
- return d;
- }
- void setNumberValueInternal(double d)
- {
- quint64 intval;
- memcpy(&intval, &d, sizeof(double));
- value.doubleValue = qToLittleEndian<quint64>(intval);
+ return constantTable[value.constantValueIndex].doubleValue();
}
bool valueAsBoolean() const
@@ -415,7 +519,7 @@ struct Q_QML_PRIVATE_EXPORT Binding
};
-static_assert(sizeof(Binding) == 32, "Binding 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(Binding) == 24, "Binding structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct EnumValue
{
@@ -423,6 +527,7 @@ struct EnumValue
qint32_le value;
Location location;
};
+static_assert(sizeof(EnumValue) == 12, "EnumValue structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Enum
{
@@ -446,6 +551,7 @@ struct Enum
int enumValueCount() const { return nEnumValues; }
// ---
};
+static_assert(sizeof(Enum) == 12, "Enum structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Parameter
{
@@ -552,17 +658,17 @@ struct Object
qint32_le_bitfield<16, 16> id;
};
qint32_le indexOfDefaultPropertyOrAlias; // -1 means no default property declared in this object
- quint32_le nFunctions;
+ quint16_le nFunctions;
+ quint16_le nProperties;
quint32_le offsetToFunctions;
- quint32_le nProperties;
quint32_le offsetToProperties;
- quint32_le nAliases;
quint32_le offsetToAliases;
- quint32_le nEnums;
+ quint16_le nAliases;
+ quint16_le nEnums;
quint32_le offsetToEnums; // which in turn will be a table with offsets to variable-sized Enum objects
- quint32_le nSignals;
quint32_le offsetToSignals; // which in turn will be a table with offsets to variable-sized Signal objects
- quint32_le nBindings;
+ quint16_le nSignals;
+ quint16_le nBindings;
quint32_le offsetToBindings;
quint32_le nNamedObjectsInComponent;
quint32_le offsetToNamedObjectsInComponent;
@@ -653,7 +759,7 @@ struct Object
int namedObjectsInComponentCount() const { return nNamedObjectsInComponent; }
// ---
};
-static_assert(sizeof(Object) == 80, "Object 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(Object) == 68, "Object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct Import
{
@@ -676,7 +782,28 @@ struct Import
};
static_assert(sizeof(Import) == 24, "Import structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+struct QmlUnit
+{
+ quint32_le nImports;
+ quint32_le offsetToImports;
+ quint32_le nObjects;
+ quint32_le offsetToObjects;
+
+ const Import *importAt(int idx) const {
+ return reinterpret_cast<const Import*>((reinterpret_cast<const char *>(this)) + offsetToImports + idx * sizeof(Import));
+ }
+
+ const Object *objectAt(int idx) const {
+ const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToObjects);
+ const quint32_le offset = offsetTable[idx];
+ return reinterpret_cast<const Object*>(reinterpret_cast<const char*>(this) + offset);
+ }
+};
+static_assert(sizeof(QmlUnit) == 16, "QmlUnit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
+
+enum { QmlCompileHashSpace = 48 };
static const char magic_str[] = "qv4cdata";
+extern const char qml_compile_hash[QmlCompileHashSpace + 1];
struct Unit
{
@@ -688,7 +815,7 @@ struct Unit
quint32_le unitSize; // Size of the Unit and any depending data.
// END DO NOT CHANGE THESE FIELDS EVER
- char libraryVersionHash[48];
+ char libraryVersionHash[QmlCompileHashSpace];
char md5Checksum[16]; // checksum of all bytes following this field.
void generateChecksum();
@@ -697,18 +824,21 @@ struct Unit
enum : unsigned int {
IsJavascript = 0x1,
- IsQml = 0x2,
- StaticData = 0x4, // Unit data persistent in memory?
- IsSingleton = 0x8,
- IsSharedLibrary = 0x10, // .pragma shared?
- ContainsMachineCode = 0x20, // used to determine if we need to mmap with execute permissions
- PendingTypeCompilation = 0x40 // the QML data structures present are incomplete and require type compilation
+ StaticData = 0x2, // Unit data persistent in memory?
+ IsSingleton = 0x4,
+ IsSharedLibrary = 0x8, // .pragma shared?
+ IsESModule = 0x10,
+ PendingTypeCompilation = 0x20 // the QML data structures present are incomplete and require type compilation
};
quint32_le flags;
quint32_le stringTableSize;
quint32_le offsetToStringTable;
quint32_le functionTableSize;
quint32_le offsetToFunctionTable;
+ quint32_le classTableSize;
+ quint32_le offsetToClassTable;
+ quint32_le blockTableSize;
+ quint32_le offsetToBlockTable;
quint32_le lookupTableSize;
quint32_le offsetToLookupTable;
quint32_le regexpTableSize;
@@ -717,28 +847,30 @@ struct Unit
quint32_le offsetToConstantTable;
quint32_le jsClassTableSize;
quint32_le offsetToJSClassTable;
+ quint32_le translationTableSize;
+ quint32_le offsetToTranslationTable;
+ quint32_le localExportEntryTableSize;
+ quint32_le offsetToLocalExportEntryTable;
+ quint32_le indirectExportEntryTableSize;
+ quint32_le offsetToIndirectExportEntryTable;
+ quint32_le starExportEntryTableSize;
+ quint32_le offsetToStarExportEntryTable;
+ quint32_le importEntryTableSize;
+ quint32_le offsetToImportEntryTable;
+ quint32_le moduleRequestTableSize;
+ quint32_le offsetToModuleRequestTable;
qint32_le indexOfRootFunction;
quint32_le sourceFileIndex;
quint32_le finalUrlIndex;
- /* QML specific fields */
- quint32_le nImports;
- quint32_le offsetToImports;
- quint32_le nObjects;
- quint32_le offsetToObjects;
-
- quint32_le padding;
+ quint32_le offsetToQmlUnit;
bool verifyHeader(QDateTime expectedSourceTimeStamp, QString *errorString) const;
- const Import *importAt(int idx) const {
- return reinterpret_cast<const Import*>((reinterpret_cast<const char *>(this)) + offsetToImports + idx * sizeof(Import));
- }
+ /* QML specific fields */
- const Object *objectAt(int idx) const {
- const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToObjects);
- const quint32_le offset = offsetTable[idx];
- return reinterpret_cast<const Object*>(reinterpret_cast<const char*>(this) + offset);
+ const QmlUnit *qmlUnit() const {
+ return reinterpret_cast<const QmlUnit *>(reinterpret_cast<const char *>(this) + offsetToQmlUnit);
}
bool isSingleton() const {
@@ -746,18 +878,19 @@ struct Unit
}
/* end QML specific fields*/
- QString stringAt(int idx) const {
+ QString stringAtInternal(int idx) const {
+ Q_ASSERT(idx < int(stringTableSize));
const quint32_le *offsetTable = reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToStringTable);
const quint32_le offset = offsetTable[idx];
const String *str = reinterpret_cast<const String*>(reinterpret_cast<const char *>(this) + offset);
if (str->size == 0)
return QString();
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ if (flags & StaticData) {
+ const QStringDataPtr holder = { const_cast<QStringData *>(reinterpret_cast<const QStringData*>(str)) };
+ return QString(holder);
+ }
const QChar *characters = reinterpret_cast<const QChar *>(str + 1);
- // Too risky to do this while we unmap disk backed compilation but keep pointers to string
- // data in the identifier tables.
- // if (flags & StaticData)
- // return QString::fromRawData(characters, str->size);
return QString(characters, str->size);
#else
const quint16_le *characters = reinterpret_cast<const quint16_le *>(str + 1);
@@ -770,6 +903,8 @@ struct Unit
}
const quint32_le *functionOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); }
+ const quint32_le *classOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToClassTable); }
+ const quint32_le *blockOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToBlockTable); }
const Function *functionAt(int idx) const {
const quint32_le *offsetTable = functionOffsetTable();
@@ -777,6 +912,18 @@ struct Unit
return reinterpret_cast<const Function*>(reinterpret_cast<const char *>(this) + offset);
}
+ const Class *classAt(int idx) const {
+ const quint32_le *offsetTable = classOffsetTable();
+ const quint32_le offset = offsetTable[idx];
+ return reinterpret_cast<const Class *>(reinterpret_cast<const char *>(this) + offset);
+ }
+
+ const Block *blockAt(int idx) const {
+ const quint32_le *offsetTable = blockOffsetTable();
+ const quint32_le offset = offsetTable[idx];
+ return reinterpret_cast<const Block *>(reinterpret_cast<const char *>(this) + offset);
+ }
+
const Lookup *lookupTable() const { return reinterpret_cast<const Lookup*>(reinterpret_cast<const char *>(this) + offsetToLookupTable); }
const RegExp *regexpAt(int index) const {
return reinterpret_cast<const RegExp*>(reinterpret_cast<const char *>(this) + offsetToRegexpTable + index * sizeof(RegExp));
@@ -793,9 +940,20 @@ struct Unit
*nMembers = klass->nMembers;
return reinterpret_cast<const JSClassMember*>(ptr + sizeof(JSClass));
}
+
+ const TranslationData *translations() const {
+ return reinterpret_cast<const TranslationData *>(reinterpret_cast<const char *>(this) + offsetToTranslationTable);
+ }
+
+ const ImportEntry *importEntryTable() const { return reinterpret_cast<const ImportEntry *>(reinterpret_cast<const char *>(this) + offsetToImportEntryTable); }
+ const ExportEntry *localExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToLocalExportEntryTable); }
+ const ExportEntry *indirectExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToIndirectExportEntryTable); }
+ const ExportEntry *starExportEntryTable() const { return reinterpret_cast<const ExportEntry *>(reinterpret_cast<const char *>(this) + offsetToStarExportEntryTable); }
+
+ const quint32_le *moduleRequestTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToModuleRequestTable); }
};
-static_assert(sizeof(Unit) == 192, "Unit 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(Unit) == 240, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target");
struct TypeReference
{
@@ -832,9 +990,7 @@ struct TypeReferenceMap : QHash<int, TypeReference>
auto propEnd = obj->propertiesEnd();
for ( ; prop != propEnd; ++prop) {
if (prop->type >= QV4::CompiledData::Property::Custom) {
- // ### FIXME: We could report the more accurate location here by using prop->location, but the old
- // compiler can't and the tests expect it to be the object location right now.
- TypeReference &r = this->add(prop->customTypeNameIndex, obj->location);
+ TypeReference &r = this->add(prop->customTypeNameIndex, prop->location);
r.errorWhenNotFound = true;
}
}
@@ -881,17 +1037,23 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnitBase
QV4::Heap::String **runtimeStrings = nullptr; // Array
const Value* constants = nullptr;
QV4::Value *runtimeRegularExpressions = nullptr;
+ QV4::Heap::InternalClass **runtimeClasses = nullptr;
+ const Value** imports = nullptr;
};
Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value);
Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeStrings) == 0);
Q_STATIC_ASSERT(offsetof(CompilationUnitBase, constants) == sizeof(QV4::Heap::String **));
Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeRegularExpressions) == offsetof(CompilationUnitBase, constants) + sizeof(const Value *));
+Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeClasses) == offsetof(CompilationUnitBase, runtimeRegularExpressions) + sizeof(const Value *));
+Q_STATIC_ASSERT(offsetof(CompilationUnitBase, imports) == offsetof(CompilationUnitBase, runtimeClasses) + sizeof(const Value *));
struct Q_QML_PRIVATE_EXPORT CompilationUnit final : public CompilationUnitBase
{
+ const Unit *data = nullptr;
+ const QmlUnit *qmlData = nullptr;
public:
- CompilationUnit(const Unit *unitData = nullptr);
+ CompilationUnit(const Unit *unitData = nullptr, const QString &fileName = QString(), const QString &finalUrlString = QString());
#ifdef V4_BOOTSTRAP
~CompilationUnit() {}
#else
@@ -915,10 +1077,9 @@ public:
return refCount.load();
}
- const Unit *data = nullptr;
-
- // Called only when building QML, when we build the header for JS first and append QML data
- QV4::CompiledData::Unit *createUnitData(QmlIR::Document *irDocument);
+ const Unit *unitData() const { return data; }
+ void setUnitData(const Unit *unitData, const QmlUnit *qmlUnit = nullptr,
+ const QString &fileName = QString(), const QString &finalUrlString = QString());
#ifndef V4_BOOTSTRAP
QIntrusiveListNode nextCompilationUnit;
@@ -932,8 +1093,8 @@ public:
// finalUrl() and finalUrlString() shall be used to resolve further URLs referred to in the code
// They are _not_ intercepted and thus represent the "logical" name for the code.
- QString fileName() const { return data->stringAt(data->sourceFileIndex); }
- QString finalUrlString() const { return data->stringAt(data->finalUrlIndex); }
+ QString fileName() const { return m_fileName; }
+ QString finalUrlString() const { return m_finalUrlString; }
QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; }
QUrl finalUrl() const
{
@@ -943,14 +1104,14 @@ public:
}
QV4::Lookup *runtimeLookups = nullptr;
- QV4::InternalClass **runtimeClasses = nullptr;
QVector<QV4::Function *> runtimeFunctions;
+ QVector<QV4::Heap::InternalClass *> runtimeBlocks;
mutable QQmlNullableValue<QUrl> m_url;
mutable QQmlNullableValue<QUrl> m_finalUrl;
// QML specific fields
QQmlPropertyCacheVector propertyCaches;
- QQmlPropertyCache *rootPropertyCache() const { return propertyCaches.at(/*root object*/0); }
+ QQmlRefPointer<QQmlPropertyCache> rootPropertyCache() const { return propertyCaches.at(/*root object*/0); }
QQmlRefPointer<QQmlTypeNameCache> typeNameCache;
@@ -962,7 +1123,7 @@ public:
// mapping from component object index (CompiledData::Unit object index that points to component) to identifier hash of named objects
// this is initialized on-demand by QQmlContextData
QHash<int, IdentifierHash> namedObjectsPerComponentCache;
- IdentifierHash namedObjectsPerComponent(int componentObjectIndex);
+ inline IdentifierHash namedObjectsPerComponent(int componentObjectIndex);
void finalizeCompositeType(QQmlEnginePrivate *qmlEngine);
@@ -970,7 +1131,7 @@ public:
int totalParserStatusCount = 0; // Number of instantiated types that are QQmlParserStatus subclasses
int totalObjectCount = 0; // Number of objects explicitly instantiated
- QVector<QQmlScriptData *> dependentScripts;
+ QVector<QQmlRefPointer<QQmlScriptData>> dependentScripts;
ResolvedTypeReferenceMap resolvedTypes;
bool verifyChecksum(const DependentTypesHasher &dependencyHasher) const;
@@ -980,12 +1141,20 @@ public:
bool isRegisteredWithEngine = false;
QScopedPointer<CompilationUnitMapper> backingFile;
+ QStringList dynamicStrings;
// --- interface for QQmlPropertyCacheCreator
typedef Object CompiledObject;
- int objectCount() const { return data->nObjects; }
- const Object *objectAt(int index) const { return data->objectAt(index); }
- QString stringAt(int index) const { return data->stringAt(index); }
+ int objectCount() const { return qmlData->nObjects; }
+ const Object *objectAt(int index) const { return qmlData->objectAt(index); }
+ int importCount() const { return qmlData->nImports; }
+ const Import *importAt(int index) const { return qmlData->importAt(index); }
+ QString stringAt(int index) const
+ {
+ if (uint(index) >= data->stringTableSize)
+ return dynamicStrings.at(index - data->stringTableSize);
+ return data->stringAtInternal(index);
+ }
struct FunctionIterator
{
@@ -1003,6 +1172,12 @@ public:
FunctionIterator objectFunctionsEnd(const Object *object) const { return FunctionIterator(data, object, object->nFunctions); }
// ---
+ QStringList moduleRequests() const;
+ Heap::Module *instantiate(ExecutionEngine *engine);
+ const Value *resolveExport(QV4::String *exportName);
+ QStringList exportedNames() const;
+ void evaluate();
+
QV4::Function *linkToEngine(QV4::ExecutionEngine *engine);
void unlink();
@@ -1010,15 +1185,42 @@ public:
bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, QString *errorString);
+ static QString localCacheFilePath(const QUrl &url);
+
protected:
- void linkBackendToEngine(QV4::ExecutionEngine *engine);
+ quint32 totalStringCount() const
+ { return data->stringTableSize; }
+
+#else // V4_BOOTSTRAP
+ QString stringAt(int index) const { return data->stringAtInternal(index); }
#endif // V4_BOOTSTRAP
private:
void destroy();
+ struct ResolveSetEntry
+ {
+ ResolveSetEntry() {}
+ ResolveSetEntry(CompilationUnit *module, QV4::String *exportName)
+ : module(module), exportName(exportName) {}
+ CompilationUnit *module = nullptr;
+ QV4::String *exportName = nullptr;
+ };
+
+ const Value *resolveExportRecursively(QV4::String *exportName, QVector<ResolveSetEntry> *resolveSet);
+ const ExportEntry *lookupNameInExportTable(const ExportEntry *firstExportEntry, int tableSize, QV4::String *name) const;
+ void getExportedNamesRecursively(QStringList *names, QVector<const CompilationUnit *> *exportNameSet, bool includeDefaultExport = true) const;
+
+ QString m_fileName; // initialized from data->sourceFileIndex
+ QString m_finalUrlString; // initialized from data->finalUrlIndex
+
QAtomicInt refCount = 1;
+ Q_NEVER_INLINE IdentifierHash createNamedObjectsPerComponent(int componentObjectIndex);
+
+ Heap::Module *m_module = nullptr;
+ bool m_moduleEvaluated = false;
+
public:
#if defined(V4_BOOTSTRAP)
bool saveToDisk(const QString &outputFileName, QString *errorString);
@@ -1046,17 +1248,24 @@ struct ResolvedTypeReference
// therefore cannot have a property cache installed when instantiated.
bool isFullyDynamicType;
- QQmlPropertyCache *propertyCache() const;
- QQmlPropertyCache *createPropertyCache(QQmlEngine *);
+ QQmlRefPointer<QQmlPropertyCache> propertyCache() const;
+ QQmlRefPointer<QQmlPropertyCache> createPropertyCache(QQmlEngine *);
bool addToHash(QCryptographicHash *hash, QQmlEngine *engine);
void doDynamicTypeCheck();
};
-#endif
+IdentifierHash CompilationUnit::namedObjectsPerComponent(int componentObjectIndex)
+{
+ auto it = namedObjectsPerComponentCache.find(componentObjectIndex);
+ if (Q_UNLIKELY(it == namedObjectsPerComponentCache.end()))
+ return createNamedObjectsPerComponent(componentObjectIndex);
+ return *it;
}
+#endif // V4_BOOTSTRAP
-}
+} // CompiledData namespace
+} // QV4 namespace
Q_DECLARE_TYPEINFO(QV4::CompiledData::JSClassMember, Q_PRIMITIVE_TYPE);
diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp
index c9e535c93f..f2e3aaedb5 100644
--- a/src/qml/compiler/qv4compiler.cpp
+++ b/src/qml/compiler/qv4compiler.cpp
@@ -48,9 +48,6 @@
#include <wtf/MathExtras.h>
#include <QCryptographicHash>
-// generated by qmake:
-#include "qml_compile_hash_p.h"
-
QV4::Compiler::StringTableGenerator::StringTableGenerator()
{
clear();
@@ -58,6 +55,7 @@ QV4::Compiler::StringTableGenerator::StringTableGenerator()
int QV4::Compiler::StringTableGenerator::registerString(const QString &str)
{
+ Q_ASSERT(!frozen);
QHash<QString, int>::ConstIterator it = stringToId.constFind(str);
if (it != stringToId.cend())
return *it;
@@ -78,26 +76,44 @@ void QV4::Compiler::StringTableGenerator::clear()
strings.clear();
stringToId.clear();
stringDataSize = 0;
+ frozen = false;
+}
+
+void QV4::Compiler::StringTableGenerator::initializeFromBackingUnit(const QV4::CompiledData::Unit *unit)
+{
+ clear();
+ for (uint i = 0; i < unit->stringTableSize; ++i)
+ registerString(unit->stringAtInternal(i));
+ backingUnitTableSize = unit->stringTableSize;
+ stringDataSize = 0;
}
void QV4::Compiler::StringTableGenerator::serialize(CompiledData::Unit *unit)
{
char *dataStart = reinterpret_cast<char *>(unit);
quint32_le *stringTable = reinterpret_cast<quint32_le *>(dataStart + unit->offsetToStringTable);
- char *stringData = dataStart + unit->offsetToStringTable + unit->stringTableSize * sizeof(uint);
- for (int i = 0; i < strings.size(); ++i) {
- stringTable[i] = stringData - dataStart;
+ char *stringData = reinterpret_cast<char *>(stringTable) + WTF::roundUpToMultipleOf(8, unit->stringTableSize * sizeof(uint));
+ for (int i = backingUnitTableSize ; i < strings.size(); ++i) {
+ const int index = i - backingUnitTableSize;
+ stringTable[index] = stringData - dataStart;
const QString &qstr = strings.at(i);
QV4::CompiledData::String *s = reinterpret_cast<QV4::CompiledData::String *>(stringData);
+ Q_ASSERT(reinterpret_cast<uintptr_t>(s) % alignof(QV4::CompiledData::String) == 0);
+ s->refcount = -1;
s->size = qstr.length();
+ s->allocAndCapacityReservedFlag = 0;
+ s->offsetOn32Bit = sizeof(QV4::CompiledData::String);
+ s->offsetOn64Bit = sizeof(QV4::CompiledData::String);
+
+ ushort *uc = reinterpret_cast<ushort *>(reinterpret_cast<char *>(s) + sizeof(*s));
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
- memcpy(s + 1, qstr.constData(), qstr.length()*sizeof(ushort));
+ memcpy(uc, qstr.constData(), s->size * sizeof(ushort));
#else
- ushort *uc = reinterpret_cast<ushort *>(s + 1);
- for (int i = 0; i < qstr.length(); ++i)
+ for (int i = 0; i < s->size; ++i)
uc[i] = qToLittleEndian<ushort>(qstr.at(i).unicode());
#endif
+ uc[s->size] = 0;
stringData += QV4::CompiledData::String::calculateSize(qstr);
}
@@ -164,6 +180,10 @@ int QV4::Compiler::JSUnitGenerator::registerRegExp(QQmlJS::AST::RegExpLiteral *r
re.flags |= CompiledData::RegExp::RegExp_IgnoreCase;
if (regexp->flags & QQmlJS::Lexer::RegExp_Multiline)
re.flags |= CompiledData::RegExp::RegExp_Multiline;
+ if (regexp->flags & QQmlJS::Lexer::RegExp_Unicode)
+ re.flags |= CompiledData::RegExp::RegExp_Unicode;
+ if (regexp->flags & QQmlJS::Lexer::RegExp_Sticky)
+ re.flags |= CompiledData::RegExp::RegExp_Sticky;
regexps.append(re);
return regexps.size() - 1;
@@ -183,7 +203,7 @@ QV4::ReturnedValue QV4::Compiler::JSUnitGenerator::constant(int idx)
return constants.at(idx);
}
-int QV4::Compiler::JSUnitGenerator::registerJSClass(const QVector<MemberInfo> &members)
+int QV4::Compiler::JSUnitGenerator::registerJSClass(const QStringList &members)
{
// ### re-use existing class definitions.
@@ -197,29 +217,19 @@ int QV4::Compiler::JSUnitGenerator::registerJSClass(const QVector<MemberInfo> &m
jsClass->nMembers = members.size();
CompiledData::JSClassMember *member = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1);
- for (const MemberInfo &memberInfo : members) {
- member->nameOffset = registerString(memberInfo.name);
- member->isAccessor = memberInfo.isAccessor;
+ for (const auto &name : members) {
+ member->nameOffset = registerString(name);
+ member->isAccessor = false;
++member;
}
return jsClassOffsets.size() - 1;
}
-int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, CompiledData::JSClassMember *members)
+int QV4::Compiler::JSUnitGenerator::registerTranslation(const QV4::CompiledData::TranslationData &translation)
{
- const int size = CompiledData::JSClass::calculateSize(count);
- jsClassOffsets.append(jsClassData.size());
- const int oldSize = jsClassData.size();
- jsClassData.resize(jsClassData.size() + size);
- memset(jsClassData.data() + oldSize, 0, size);
-
- CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize);
- jsClass->nMembers = count;
- CompiledData::JSClassMember *jsClassMembers = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1);
- memcpy(jsClassMembers, members, sizeof(CompiledData::JSClassMember)*count);
-
- return jsClassOffsets.size() - 1;
+ translations.append(translation);
+ return translations.size() - 1;
}
QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorOption option)
@@ -233,28 +243,67 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
for (int i = 0; i < f->locals.size(); ++i)
registerString(f->locals.at(i));
}
+ for (Context *c : qAsConst(module->blocks)) {
+ for (int i = 0; i < c->locals.size(); ++i)
+ registerString(c->locals.at(i));
+ }
+ {
+ const auto registerExportEntry = [this](const Compiler::ExportEntry &entry) {
+ registerString(entry.exportName);
+ registerString(entry.moduleRequest);
+ registerString(entry.importName);
+ registerString(entry.localName);
+ };
+ std::for_each(module->localExportEntries.constBegin(), module->localExportEntries.constEnd(), registerExportEntry);
+ std::for_each(module->indirectExportEntries.constBegin(), module->indirectExportEntries.constEnd(), registerExportEntry);
+ std::for_each(module->starExportEntries.constBegin(), module->starExportEntries.constEnd(), registerExportEntry);
+ }
+ {
+ for (const auto &entry: module->importEntries) {
+ registerString(entry.moduleRequest);
+ registerString(entry.importName);
+ registerString(entry.localName);
+ }
+
+ for (const QString &request: module->moduleRequests)
+ registerString(request);
+ }
- Q_ALLOCA_VAR(quint32_le, functionOffsets, module->functions.size() * sizeof(quint32_le));
+ Q_ALLOCA_VAR(quint32_le, blockClassAndFunctionOffsets, (module->functions.size() + module->classes.size() + module->blocks.size()) * sizeof(quint32_le));
uint jsClassDataOffset = 0;
char *dataPtr;
CompiledData::Unit *unit;
{
- QV4::CompiledData::Unit tempHeader = generateHeader(option, functionOffsets, &jsClassDataOffset);
+ QV4::CompiledData::Unit tempHeader = generateHeader(option, blockClassAndFunctionOffsets, &jsClassDataOffset);
dataPtr = reinterpret_cast<char *>(malloc(tempHeader.unitSize));
memset(dataPtr, 0, tempHeader.unitSize);
memcpy(&unit, &dataPtr, sizeof(CompiledData::Unit*));
memcpy(unit, &tempHeader, sizeof(tempHeader));
}
- memcpy(dataPtr + unit->offsetToFunctionTable, functionOffsets, unit->functionTableSize * sizeof(quint32_le));
+ memcpy(dataPtr + unit->offsetToFunctionTable, blockClassAndFunctionOffsets, unit->functionTableSize * sizeof(quint32_le));
+ memcpy(dataPtr + unit->offsetToClassTable, blockClassAndFunctionOffsets + unit->functionTableSize, unit->classTableSize * sizeof(quint32_le));
+ memcpy(dataPtr + unit->offsetToBlockTable, blockClassAndFunctionOffsets + unit->functionTableSize + unit->classTableSize, unit->blockTableSize * sizeof(quint32_le));
for (int i = 0; i < module->functions.size(); ++i) {
Context *function = module->functions.at(i);
if (function == module->rootContext)
unit->indexOfRootFunction = i;
- writeFunction(dataPtr + functionOffsets[i], function);
+ writeFunction(dataPtr + blockClassAndFunctionOffsets[i], function);
+ }
+
+ for (int i = 0; i < module->classes.size(); ++i) {
+ const Class &c = module->classes.at(i);
+
+ writeClass(dataPtr + blockClassAndFunctionOffsets[i + module->functions.size()], c);
+ }
+
+ for (int i = 0; i < module->blocks.size(); ++i) {
+ Context *block = module->blocks.at(i);
+
+ writeBlock(dataPtr + blockClassAndFunctionOffsets[i + module->classes.size() + module->functions.size()], block);
}
CompiledData::Lookup *lookupsToWrite = reinterpret_cast<CompiledData::Lookup*>(dataPtr + unit->offsetToLookupTable);
@@ -282,6 +331,43 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO
jsClassOffsetTable[i] = jsClassDataOffset + jsClassOffsets.at(i);
}
+
+ memcpy(dataPtr + unit->offsetToTranslationTable, translations.constData(), translations.count() * sizeof(CompiledData::TranslationData));
+
+ {
+ const auto populateExportEntryTable = [this, dataPtr](const QVector<Compiler::ExportEntry> &table, quint32_le offset) {
+ CompiledData::ExportEntry *entryToWrite = reinterpret_cast<CompiledData::ExportEntry *>(dataPtr + offset);
+ for (const Compiler::ExportEntry &entry: table) {
+ entryToWrite->exportName = getStringId(entry.exportName);
+ entryToWrite->moduleRequest = getStringId(entry.moduleRequest);
+ entryToWrite->importName = getStringId(entry.importName);
+ entryToWrite->localName = getStringId(entry.localName);
+ entryToWrite++;
+ }
+ };
+ populateExportEntryTable(module->localExportEntries, unit->offsetToLocalExportEntryTable);
+ populateExportEntryTable(module->indirectExportEntries, unit->offsetToIndirectExportEntryTable);
+ populateExportEntryTable(module->starExportEntries, unit->offsetToStarExportEntryTable);
+ }
+
+ {
+ CompiledData::ImportEntry *entryToWrite = reinterpret_cast<CompiledData::ImportEntry *>(dataPtr + unit->offsetToImportEntryTable);
+ for (const Compiler::ImportEntry &entry: module->importEntries) {
+ entryToWrite->moduleRequest = getStringId(entry.moduleRequest);
+ entryToWrite->importName = getStringId(entry.importName);
+ entryToWrite->localName = getStringId(entry.localName);
+ entryToWrite++;
+ }
+ }
+
+ {
+ quint32_le *moduleRequestEntryToWrite = reinterpret_cast<quint32_le *>(dataPtr + unit->offsetToModuleRequestTable);
+ for (const QString &moduleRequest: module->moduleRequests) {
+ *moduleRequestEntryToWrite = getStringId(moduleRequest);
+ moduleRequestEntryToWrite++;
+ }
+ }
+
// write strings and string table
if (option == GenerateWithStringTable)
stringTable.serialize(unit);
@@ -295,36 +381,38 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
{
QV4::CompiledData::Function *function = (QV4::CompiledData::Function *)f;
- quint32 currentOffset = sizeof(QV4::CompiledData::Function);
- currentOffset = (currentOffset + 7) & ~quint32(0x7);
+ quint32 currentOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, sizeof(*function)));
function->nameIndex = getStringId(irFunction->name);
function->flags = 0;
- if (irFunction->hasDirectEval)
- function->flags |= CompiledData::Function::HasDirectEval;
- if (irFunction->usesArgumentsObject)
- function->flags |= CompiledData::Function::UsesArgumentsObject;
if (irFunction->isStrict)
function->flags |= CompiledData::Function::IsStrict;
- if (irFunction->hasTry || irFunction->hasWith)
- function->flags |= CompiledData::Function::HasCatchOrWith;
+ if (irFunction->isArrowFunction)
+ function->flags |= CompiledData::Function::IsArrowFunction;
+ if (irFunction->isGenerator)
+ function->flags |= CompiledData::Function::IsGenerator;
function->nestedFunctionIndex =
irFunction->returnsClosure ? quint32(module->functions.indexOf(irFunction->nestedContexts.first()))
: std::numeric_limits<uint32_t>::max();
+ function->length = irFunction->formals ? irFunction->formals->length() : 0;
function->nFormals = irFunction->arguments.size();
function->formalsOffset = currentOffset;
currentOffset += function->nFormals * sizeof(quint32);
+ function->sizeOfLocalTemporalDeadZone = irFunction->sizeOfLocalTemporalDeadZone;
+ function->sizeOfRegisterTemporalDeadZone = irFunction->sizeOfRegisterTemporalDeadZone;
+ function->firstTemporalDeadZoneRegister = irFunction->firstTemporalDeadZoneRegister;
+
function->nLocals = irFunction->locals.size();
function->localsOffset = currentOffset;
currentOffset += function->nLocals * sizeof(quint32);
function->nLineNumbers = irFunction->lineNumberMapping.size();
- function->lineNumberOffset = currentOffset;
+ Q_ASSERT(function->lineNumberOffset() == currentOffset);
currentOffset += function->nLineNumbers * sizeof(CompiledData::CodeOffsetToLine);
- function->nRegisters = irFunction->registerCount;
+ function->nRegisters = irFunction->registerCountInFunction;
function->nDependingIdObjects = 0;
function->nDependingContextProperties = 0;
@@ -332,19 +420,19 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
if (!irFunction->idObjectDependencies.isEmpty()) {
function->nDependingIdObjects = irFunction->idObjectDependencies.count();
- function->dependingIdObjectsOffset = currentOffset;
+ Q_ASSERT(function->dependingIdObjectsOffset() == currentOffset);
currentOffset += function->nDependingIdObjects * sizeof(quint32);
}
if (!irFunction->contextObjectPropertyDependencies.isEmpty()) {
function->nDependingContextProperties = irFunction->contextObjectPropertyDependencies.count();
- function->dependingContextPropertiesOffset = currentOffset;
+ Q_ASSERT(function->dependingContextPropertiesOffset() == currentOffset);
currentOffset += function->nDependingContextProperties * sizeof(quint32) * 2;
}
if (!irFunction->scopeObjectPropertyDependencies.isEmpty()) {
function->nDependingScopeProperties = irFunction->scopeObjectPropertyDependencies.count();
- function->dependingScopePropertiesOffset = currentOffset;
+ Q_ASSERT(function->dependingScopePropertiesOffset() == currentOffset);
currentOffset += function->nDependingScopeProperties * sizeof(quint32) * 2;
}
@@ -365,22 +453,22 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
locals[i] = getStringId(irFunction->locals.at(i));
// write line numbers
- memcpy(f + function->lineNumberOffset, irFunction->lineNumberMapping.constData(), irFunction->lineNumberMapping.size()*sizeof(CompiledData::CodeOffsetToLine));
+ memcpy(f + function->lineNumberOffset(), irFunction->lineNumberMapping.constData(), irFunction->lineNumberMapping.size()*sizeof(CompiledData::CodeOffsetToLine));
// write QML dependencies
- quint32_le *writtenDeps = (quint32_le *)(f + function->dependingIdObjectsOffset);
+ 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);
+ 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);
+ writtenDeps = (quint32_le *)(f + function->dependingScopePropertiesOffset());
for (auto property : irFunction->scopeObjectPropertyDependencies) {
*writtenDeps++ = property.key(); // property index
*writtenDeps++ = property.value(); // notify index
@@ -390,7 +478,84 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte
memcpy(f + function->codeOffset, irFunction->code.constData(), irFunction->code.size());
}
-QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, quint32_le *functionOffsets, uint *jsClassDataOffset)
+static_assert(int(QV4::Compiler::Class::Method::Regular) == int(QV4::CompiledData::Method::Regular), "Incompatible layout");
+static_assert(int(QV4::Compiler::Class::Method::Getter) == int(QV4::CompiledData::Method::Getter), "Incompatible layout");
+static_assert(int(QV4::Compiler::Class::Method::Setter) == int(QV4::CompiledData::Method::Setter), "Incompatible layout");
+
+void QV4::Compiler::JSUnitGenerator::writeClass(char *b, const QV4::Compiler::Class &c)
+{
+ QV4::CompiledData::Class *cls = reinterpret_cast<QV4::CompiledData::Class *>(b);
+
+ quint32 currentOffset = sizeof(QV4::CompiledData::Class);
+
+ QVector<Class::Method> allMethods = c.staticMethods;
+ allMethods += c.methods;
+
+ cls->constructorFunction = c.constructorIndex;
+ cls->nameIndex = c.nameIndex;
+ cls->nMethods = c.methods.size();
+ cls->nStaticMethods = c.staticMethods.size();
+ cls->methodTableOffset = currentOffset;
+ CompiledData::Method *method = reinterpret_cast<CompiledData::Method *>(b + currentOffset);
+
+ // write methods
+ for (int i = 0; i < allMethods.size(); ++i) {
+ method->name = allMethods.at(i).nameIndex;
+ method->type = allMethods.at(i).type;
+ method->function = allMethods.at(i).functionIndex;
+ ++method;
+ }
+
+ static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
+ if (showCode) {
+ qDebug() << "=== Class " << stringForIndex(cls->nameIndex) << "static methods" << cls->nStaticMethods << "methods" << cls->nMethods;
+ qDebug() << " constructor:" << cls->constructorFunction;
+ const char *staticString = ": static ";
+ for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) {
+ if (i == cls->nStaticMethods)
+ staticString = ": ";
+ const char *type;
+ switch (cls->methodTable()[i].type) {
+ case CompiledData::Method::Getter:
+ type = "get "; break;
+ case CompiledData::Method::Setter:
+ type = "set "; break;
+ default:
+ type = "";
+
+ }
+ qDebug() << " " << i << staticString << type << stringForIndex(cls->methodTable()[i].name) << cls->methodTable()[i].function;
+ }
+ qDebug();
+ }
+}
+
+void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context *irBlock) const
+{
+ QV4::CompiledData::Block *block = reinterpret_cast<QV4::CompiledData::Block *>(b);
+
+ quint32 currentOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, sizeof(*block)));
+
+ block->sizeOfLocalTemporalDeadZone = irBlock->sizeOfLocalTemporalDeadZone;
+ block->nLocals = irBlock->locals.size();
+ block->localsOffset = currentOffset;
+ currentOffset += block->nLocals * sizeof(quint32);
+
+ // write locals
+ quint32_le *locals = (quint32_le *)(b + block->localsOffset);
+ for (int i = 0; i < irBlock->locals.size(); ++i)
+ locals[i] = getStringId(irBlock->locals.at(i));
+
+ static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE");
+ if (showCode) {
+ qDebug() << "=== Variables for block" << irBlock->blockIndex;
+ for (int i = 0; i < irBlock->locals.size(); ++i)
+ qDebug() << " " << i << ":" << locals[i];
+ qDebug();
+ }
+}
+
+QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Compiler::JSUnitGenerator::GeneratorOption option, quint32_le *blockAndFunctionOffsets, uint *jsClassDataOffset)
{
CompiledData::Unit unit;
memset(&unit, 0, sizeof(unit));
@@ -399,7 +564,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
unit.flags |= module->unitFlags;
unit.version = QV4_DATA_STRUCTURE_VERSION;
unit.qtVersion = QT_VERSION;
- qstrcpy(unit.libraryVersionHash, QML_COMPILE_HASH);
+ qstrcpy(unit.libraryVersionHash, CompiledData::qml_compile_hash);
memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum));
memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum));
@@ -409,6 +574,14 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
unit.offsetToFunctionTable = nextOffset;
nextOffset += unit.functionTableSize * sizeof(uint);
+ unit.classTableSize = module->classes.size();
+ unit.offsetToClassTable = nextOffset;
+ nextOffset += unit.classTableSize * sizeof(uint);
+
+ unit.blockTableSize = module->blocks.size();
+ unit.offsetToBlockTable = nextOffset;
+ nextOffset += unit.blockTableSize * sizeof(uint);
+
unit.lookupTableSize = lookups.count();
unit.offsetToLookupTable = nextOffset;
nextOffset += unit.lookupTableSize * sizeof(CompiledData::Lookup);
@@ -431,20 +604,68 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
*jsClassDataOffset = nextOffset;
nextOffset += jsClassData.size();
- nextOffset = (nextOffset + 7) & ~quint32(0x7);
+ nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset));
+
+ unit.translationTableSize = translations.count();
+ unit.offsetToTranslationTable = nextOffset;
+ nextOffset += unit.translationTableSize * sizeof(CompiledData::TranslationData);
+
+ nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset));
+
+ const auto reserveExportTable = [&nextOffset](int count, quint32_le *tableSizePtr, quint32_le *offsetPtr) {
+ *tableSizePtr = count;
+ *offsetPtr = nextOffset;
+ nextOffset += count * sizeof(CompiledData::ExportEntry);
+ nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset));
+ };
+ reserveExportTable(module->localExportEntries.count(), &unit.localExportEntryTableSize, &unit.offsetToLocalExportEntryTable);
+ reserveExportTable(module->indirectExportEntries.count(), &unit.indirectExportEntryTableSize, &unit.offsetToIndirectExportEntryTable);
+ reserveExportTable(module->starExportEntries.count(), &unit.starExportEntryTableSize, &unit.offsetToStarExportEntryTable);
+
+ unit.importEntryTableSize = module->importEntries.count();
+ unit.offsetToImportEntryTable = nextOffset;
+ nextOffset += unit.importEntryTableSize * sizeof(CompiledData::ImportEntry);
+ nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset));
+
+ unit.moduleRequestTableSize = module->moduleRequests.count();
+ unit.offsetToModuleRequestTable = nextOffset;
+ nextOffset += unit.moduleRequestTableSize * sizeof(uint);
+ nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset));
+
+ quint32 functionSize = 0;
for (int i = 0; i < module->functions.size(); ++i) {
Context *f = module->functions.at(i);
- functionOffsets[i] = nextOffset;
+ blockAndFunctionOffsets[i] = nextOffset;
const int qmlIdDepsCount = f->idObjectDependencies.count();
const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count();
- nextOffset += QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->lineNumberMapping.size(), f->nestedContexts.size(),
+ quint32 size = QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->lineNumberMapping.size(), f->nestedContexts.size(),
qmlIdDepsCount, qmlPropertyDepsCount, f->code.size());
+ functionSize += size - f->code.size();
+ nextOffset += size;
+ }
+
+ blockAndFunctionOffsets += module->functions.size();
+
+ for (int i = 0; i < module->classes.size(); ++i) {
+ const Class &c = module->classes.at(i);
+ blockAndFunctionOffsets[i] = nextOffset;
+
+ nextOffset += QV4::CompiledData::Class::calculateSize(c.staticMethods.size(), c.methods.size());
+ }
+ blockAndFunctionOffsets += module->classes.size();
+
+ for (int i = 0; i < module->blocks.size(); ++i) {
+ Context *c = module->blocks.at(i);
+ blockAndFunctionOffsets[i] = nextOffset;
+
+ nextOffset += QV4::CompiledData::Block::calculateSize(c->locals.size());
}
if (option == GenerateWithStringTable) {
unit.stringTableSize = stringTable.stringCount();
+ nextOffset = static_cast<quint32>(WTF::roundUpToMultipleOf(8, nextOffset));
unit.offsetToStringTable = nextOffset;
nextOffset += stringTable.sizeOfTableAndData();
} else {
@@ -455,12 +676,16 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp
unit.sourceFileIndex = getStringId(module->fileName);
unit.finalUrlIndex = getStringId(module->finalUrl);
unit.sourceTimeStamp = module->sourceTimeStamp.isValid() ? module->sourceTimeStamp.toMSecsSinceEpoch() : 0;
- unit.nImports = 0;
- unit.offsetToImports = 0;
- unit.nObjects = 0;
- unit.offsetToObjects = 0;
+ unit.offsetToQmlUnit = 0;
unit.unitSize = nextOffset;
+ static const bool showStats = qEnvironmentVariableIsSet("QML_SHOW_UNIT_STATS");
+ if (showStats) {
+ qDebug() << "Generated JS unit that is" << unit.unitSize << "bytes contains:";
+ qDebug() << " " << functionSize << "bytes for non-code function data for" << unit.functionTableSize << "functions";
+ qDebug() << " " << translations.count() * sizeof(CompiledData::TranslationData) << "bytes for" << translations.count() << "translations";
+ }
+
return unit;
}
diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h
index 360af6540f..c4c886ffad 100644
--- a/src/qml/compiler/qv4compiler_p.h
+++ b/src/qml/compiler/qv4compiler_p.h
@@ -72,24 +72,33 @@ struct JSClassMember;
namespace Compiler {
+struct Class;
+
struct Q_QML_PRIVATE_EXPORT StringTableGenerator {
StringTableGenerator();
int registerString(const QString &str);
int getStringId(const QString &string) const;
QString stringForIndex(int index) const { return strings.at(index); }
- uint stringCount() const { return strings.size(); }
+ uint stringCount() const { return strings.size() - backingUnitTableSize; }
+
+ uint sizeOfTableAndData() const { return stringDataSize + ((stringCount() * sizeof(uint) + 7) & ~7); }
- uint sizeOfTableAndData() const { return stringDataSize + strings.count() * sizeof(uint); }
+ void freeze() { frozen = true; }
void clear();
+ void initializeFromBackingUnit(const CompiledData::Unit *unit);
+
void serialize(CompiledData::Unit *unit);
+ QStringList allStrings() const { return strings.mid(backingUnitTableSize); }
private:
QHash<QString, int> stringToId;
QStringList strings;
uint stringDataSize;
+ uint backingUnitTableSize = 0;
+ bool frozen = false;
};
struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
@@ -116,8 +125,9 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
int registerConstant(ReturnedValue v);
ReturnedValue constant(int idx);
- int registerJSClass(const QVector<MemberInfo> &members);
- int registerJSClass(int count, CompiledData::JSClassMember *members);
+ int registerJSClass(const QStringList &members);
+
+ int registerTranslation(const CompiledData::TranslationData &translation);
enum GeneratorOption {
GenerateWithStringTable,
@@ -125,8 +135,9 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator {
};
QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable);
- // Returns bytes written
void writeFunction(char *f, Context *irFunction) const;
+ void writeClass(char *f, const Class &c);
+ void writeBlock(char *f, Context *irBlock) const;
StringTableGenerator stringTable;
QString codeGeneratorName;
@@ -140,6 +151,7 @@ private:
QVector<ReturnedValue> constants;
QByteArray jsClassData;
QVector<int> jsClassOffsets;
+ QVector<CompiledData::TranslationData> translations;
};
}
diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp
index 0a9d3d8efe..b423dcf0b7 100644
--- a/src/qml/compiler/qv4compilercontext.cpp
+++ b/src/qml/compiler/qv4compilercontext.cpp
@@ -39,6 +39,7 @@
#include "qv4compilercontext_p.h"
#include "qv4compilercontrolflow_p.h"
+#include "qv4bytecodegenerator_p.h"
QT_USE_NAMESPACE
using namespace QV4;
@@ -47,11 +48,11 @@ using namespace QQmlJS::AST;
QT_BEGIN_NAMESPACE
-Context *Module::newContext(Node *node, Context *parent, CompilationMode compilationMode)
+Context *Module::newContext(Node *node, Context *parent, ContextType contextType)
{
Q_ASSERT(!contextMap.contains(node));
- Context *c = new Context(parent, compilationMode);
+ Context *c = new Context(parent, contextType);
if (node) {
SourceLocation loc = node->firstSourceLocation();
c->line = loc.startLine;
@@ -70,15 +71,339 @@ Context *Module::newContext(Node *node, Context *parent, CompilationMode compila
return c;
}
-bool Context::forceLookupByName()
+bool Context::Member::requiresTDZCheck(const SourceLocation &accessLocation, bool accessAcrossContextBoundaries) const
{
- ControlFlow *flow = controlFlow;
- while (flow) {
- if (flow->needsLookupByName)
+ if (!isLexicallyScoped())
+ return false;
+
+ if (accessAcrossContextBoundaries)
+ return true;
+
+ if (!accessLocation.isValid() || !endOfInitializerLocation.isValid())
+ return true;
+
+ return accessLocation.begin() < endOfInitializerLocation.end();
+}
+
+bool Context::addLocalVar(const QString &name, Context::MemberType type, VariableScope scope, FunctionExpression *function,
+ const QQmlJS::AST::SourceLocation &endOfInitializer)
+{
+ // ### can this happen?
+ if (name.isEmpty())
+ return true;
+
+ if (type != FunctionDefinition) {
+ if (formals && formals->containsName(name))
+ return (scope == VariableScope::Var);
+ }
+ if (!isCatchBlock || name != caughtVariable) {
+ MemberMap::iterator it = members.find(name);
+ if (it != members.end()) {
+ if (scope != VariableScope::Var || (*it).scope != VariableScope::Var)
+ return false;
+ if ((*it).type <= type) {
+ (*it).type = type;
+ (*it).function = function;
+ }
return true;
- flow = flow->parent;
+ }
}
- return false;
+
+ // hoist var declarations to the function level
+ if (contextType == ContextType::Block && (scope == VariableScope::Var && type != MemberType::FunctionDefinition))
+ return parent->addLocalVar(name, type, scope, function, endOfInitializer);
+
+ Member m;
+ m.type = type;
+ m.function = function;
+ m.scope = scope;
+ m.endOfInitializerLocation = endOfInitializer;
+ members.insert(name, m);
+ return true;
+}
+
+Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::AST::SourceLocation &accessLocation)
+{
+ int scope = 0;
+ Context *c = this;
+
+ ResolvedName result;
+
+ while (c) {
+ if (c->isWithBlock)
+ return result;
+
+ Context::Member m = c->findMember(name);
+ if (!c->parent && m.index < 0)
+ break;
+
+ if (m.type != Context::UndefinedMember) {
+ result.type = m.canEscape ? ResolvedName::Local : ResolvedName::Stack;
+ result.scope = scope;
+ result.index = m.index;
+ result.isConst = (m.scope == VariableScope::Const);
+ result.requiresTDZCheck = m.requiresTDZCheck(accessLocation, c != this);
+ if (c->isStrict && (name == QLatin1String("arguments") || name == QLatin1String("eval")))
+ result.isArgOrEval = true;
+ return result;
+ }
+ const int argIdx = c->findArgument(name);
+ if (argIdx != -1) {
+ if (c->argumentsCanEscape) {
+ result.index = argIdx + c->locals.size();
+ result.scope = scope;
+ result.type = ResolvedName::Local;
+ result.isConst = false;
+ return result;
+ } else {
+ result.index = argIdx + sizeof(CallData)/sizeof(Value) - 1;
+ result.scope = 0;
+ result.type = ResolvedName::Stack;
+ result.isConst = false;
+ return result;
+ }
+ }
+ if (c->hasDirectEval) {
+ Q_ASSERT(!c->isStrict && c->contextType != ContextType::Block);
+ return result;
+ }
+
+ if (c->requiresExecutionContext)
+ ++scope;
+ c = c->parent;
+ }
+
+ if (c && c->contextType == ContextType::ESModule) {
+ for (int i = 0; i < c->importEntries.count(); ++i) {
+ if (c->importEntries.at(i).localName == name) {
+ result.index = i;
+ result.type = ResolvedName::Import;
+ result.isConst = true;
+ // We don't know at compile time whether the imported value is let/const or not.
+ result.requiresTDZCheck = true;
+ return result;
+ }
+ }
+ }
+
+ // ### can we relax the restrictions here?
+ if (contextType == ContextType::Eval || c->contextType == ContextType::Binding)
+ return result;
+
+ result.type = ResolvedName::Global;
+ return result;
+}
+
+void Context::emitBlockHeader(Codegen *codegen)
+{
+ using Instruction = Moth::Instruction;
+ Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator();
+
+ setupFunctionIndices(bytecodeGenerator);
+
+ if (requiresExecutionContext) {
+ if (blockIndex < 0) {
+ codegen->module()->blocks.append(this);
+ blockIndex = codegen->module()->blocks.count() - 1;
+ }
+
+ if (contextType == ContextType::Global) {
+ Instruction::PushScriptContext scriptContext;
+ scriptContext.index = blockIndex;
+ bytecodeGenerator->addInstruction(scriptContext);
+ } else if (contextType == ContextType::Block || (contextType == ContextType::Eval && !isStrict)) {
+ if (isCatchBlock) {
+ Instruction::PushCatchContext catchContext;
+ catchContext.index = blockIndex;
+ catchContext.name = codegen->registerString(caughtVariable);
+ bytecodeGenerator->addInstruction(catchContext);
+ } else {
+ Instruction::PushBlockContext blockContext;
+ blockContext.index = blockIndex;
+ bytecodeGenerator->addInstruction(blockContext);
+ }
+ } else if (contextType != ContextType::ESModule) {
+ Instruction::CreateCallContext createContext;
+ bytecodeGenerator->addInstruction(createContext);
+ }
+ }
+
+ if (contextType == ContextType::Block && sizeOfRegisterTemporalDeadZone > 0) {
+ Instruction::InitializeBlockDeadTemporalZone tdzInit;
+ tdzInit.firstReg = registerOffset + nRegisters - sizeOfRegisterTemporalDeadZone;
+ tdzInit.count = sizeOfRegisterTemporalDeadZone;
+ bytecodeGenerator->addInstruction(tdzInit);
+ }
+
+ if (usesThis) {
+ Q_ASSERT(!isStrict);
+ // make sure we convert this to an object
+ Instruction::ConvertThisToObject convert;
+ bytecodeGenerator->addInstruction(convert);
+ }
+ if (innerFunctionAccessesThis) {
+ Instruction::LoadReg load;
+ load.reg = CallData::This;
+ bytecodeGenerator->addInstruction(load);
+ Codegen::Reference r = codegen->referenceForName(QStringLiteral("this"), true);
+ r.storeConsumeAccumulator();
+ }
+ if (innerFunctionAccessesNewTarget) {
+ Instruction::LoadReg load;
+ load.reg = CallData::NewTarget;
+ bytecodeGenerator->addInstruction(load);
+ Codegen::Reference r = codegen->referenceForName(QStringLiteral("new.target"), true);
+ r.storeConsumeAccumulator();
+ }
+
+ if (contextType == ContextType::Global || (contextType == ContextType::Eval && !isStrict)) {
+ // variables in global code are properties of the global context object, not locals as with other functions.
+ for (Context::MemberMap::const_iterator it = members.constBegin(), cend = members.constEnd(); it != cend; ++it) {
+ if (it->isLexicallyScoped())
+ continue;
+ const QString &local = it.key();
+
+ Instruction::DeclareVar declareVar;
+ declareVar.isDeletable = (contextType == ContextType::Eval);
+ declareVar.varName = codegen->registerString(local);
+ bytecodeGenerator->addInstruction(declareVar);
+ }
+ }
+
+ if (contextType == ContextType::Function || contextType == ContextType::Binding || contextType == ContextType::ESModule) {
+ for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
+ if (it->canEscape && it->type == Context::ThisFunctionName) {
+ // move the function from the stack to the call context
+ Instruction::LoadReg load;
+ load.reg = CallData::Function;
+ bytecodeGenerator->addInstruction(load);
+ Instruction::StoreLocal store;
+ store.index = it->index;
+ bytecodeGenerator->addInstruction(store);
+ }
+ }
+ }
+
+ if (usesArgumentsObject == Context::ArgumentsObjectUsed) {
+ Q_ASSERT(contextType != ContextType::Block);
+ if (isStrict || (formals && !formals->isSimpleParameterList())) {
+ Instruction::CreateUnmappedArgumentsObject setup;
+ bytecodeGenerator->addInstruction(setup);
+ } else {
+ Instruction::CreateMappedArgumentsObject setup;
+ bytecodeGenerator->addInstruction(setup);
+ }
+ codegen->referenceForName(QStringLiteral("arguments"), false).storeConsumeAccumulator();
+ }
+
+ for (const Context::Member &member : qAsConst(members)) {
+ if (member.function) {
+ const int function = codegen->defineFunction(member.function->name.toString(), member.function, member.function->formals, member.function->body);
+ codegen->loadClosure(function);
+ Codegen::Reference r = codegen->referenceForName(member.function->name.toString(), true);
+ r.storeConsumeAccumulator();
+ }
+ }
+}
+
+void Context::emitBlockFooter(Codegen *codegen)
+{
+ using Instruction = Moth::Instruction;
+ Moth::BytecodeGenerator *bytecodeGenerator = codegen->generator();
+
+ if (!requiresExecutionContext)
+ return;
+
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized") // the loads below are empty structs.
+ if (contextType == ContextType::Global)
+ bytecodeGenerator->addInstruction(Instruction::PopScriptContext());
+ else if (contextType != ContextType::ESModule)
+ bytecodeGenerator->addInstruction(Instruction::PopContext());
+QT_WARNING_POP
+}
+
+void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator)
+{
+ if (registerOffset != -1) {
+ // already computed, check for consistency
+ Q_ASSERT(registerOffset == bytecodeGenerator->currentRegister());
+ bytecodeGenerator->newRegisterArray(nRegisters);
+ return;
+ }
+ Q_ASSERT(locals.size() == 0);
+ Q_ASSERT(nRegisters == 0);
+ registerOffset = bytecodeGenerator->currentRegister();
+
+ QVector<Context::MemberMap::Iterator> localsInTDZ;
+ const auto registerLocal = [this, &localsInTDZ](Context::MemberMap::iterator member) {
+ if (member->isLexicallyScoped()) {
+ localsInTDZ << member;
+ } else {
+ member->index = locals.size();
+ locals.append(member.key());
+ }
+ };
+
+ QVector<Context::MemberMap::Iterator> registersInTDZ;
+ const auto allocateRegister = [bytecodeGenerator, &registersInTDZ](Context::MemberMap::iterator member) {
+ if (member->isLexicallyScoped())
+ registersInTDZ << member;
+ else
+ member->index = bytecodeGenerator->newRegister();
+ };
+
+ switch (contextType) {
+ case ContextType::ESModule:
+ case ContextType::Block:
+ case ContextType::Function:
+ case ContextType::Binding: {
+ for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
+ if (it->canEscape) {
+ registerLocal(it);
+ } else {
+ if (it->type == Context::ThisFunctionName)
+ it->index = CallData::Function;
+ else
+ allocateRegister(it);
+ }
+ }
+ break;
+ }
+ case ContextType::Global:
+ case ContextType::Eval:
+ for (Context::MemberMap::iterator it = members.begin(), end = members.end(); it != end; ++it) {
+ if (!it->isLexicallyScoped() && (contextType == ContextType::Global || !isStrict))
+ continue;
+ if (it->canEscape)
+ registerLocal(it);
+ else
+ allocateRegister(it);
+ }
+ break;
+ }
+
+ sizeOfLocalTemporalDeadZone = localsInTDZ.count();
+ for (auto &member: qAsConst(localsInTDZ)) {
+ member->index = locals.size();
+ locals.append(member.key());
+ }
+
+ if (contextType == ContextType::ESModule && !localNameForDefaultExport.isEmpty()) {
+ if (!members.contains(localNameForDefaultExport)) {
+ // allocate a local slot for the default export, to be used in
+ // CodeGen::visit(ExportDeclaration*).
+ locals.append(localNameForDefaultExport);
+ ++sizeOfLocalTemporalDeadZone;
+ }
+ }
+
+ sizeOfRegisterTemporalDeadZone = registersInTDZ.count();
+ firstTemporalDeadZoneRegister = bytecodeGenerator->currentRegister();
+ for (auto &member: qAsConst(registersInTDZ))
+ member->index = bytecodeGenerator->newRegister();
+
+ nRegisters = bytecodeGenerator->currentRegister() - registerOffset;
}
QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h
index 455a76c729..a29f4ea4cb 100644
--- a/src/qml/compiler/qv4compilercontext_p.h
+++ b/src/qml/compiler/qv4compilercontext_p.h
@@ -66,18 +66,56 @@ namespace Compiler {
struct ControlFlow;
-enum CompilationMode {
- GlobalCode,
- EvalCode,
- FunctionCode,
- QmlBinding // This is almost the same as EvalCode, except:
+enum class ContextType {
+ Global,
+ Function,
+ Eval,
+ Binding, // This is almost the same as Eval, except:
// * function declarations are moved to the return address when encountered
// * return statements are allowed everywhere (like in FunctionCode)
// * variable declarations are treated as true locals (like in FunctionCode)
+ Block,
+ ESModule
};
struct Context;
+struct Class {
+ struct Method {
+ enum Type {
+ Regular,
+ Getter,
+ Setter
+ };
+ uint nameIndex;
+ Type type;
+ uint functionIndex;
+ };
+
+ uint nameIndex;
+ uint constructorIndex = UINT_MAX;
+ QVector<Method> staticMethods;
+ QVector<Method> methods;
+};
+
+struct ExportEntry
+{
+ QString exportName;
+ QString moduleRequest;
+ QString importName;
+ QString localName;
+
+ static bool lessThan(const ExportEntry &lhs, const ExportEntry &rhs)
+ { return lhs.exportName < rhs.exportName; }
+};
+
+struct ImportEntry
+{
+ QString moduleRequest;
+ QString importName;
+ QString localName;
+};
+
struct Module {
Module(bool debugMode)
: debugMode(debugMode)
@@ -86,16 +124,23 @@ struct Module {
qDeleteAll(contextMap);
}
- Context *newContext(QQmlJS::AST::Node *node, Context *parent, CompilationMode compilationMode);
+ Context *newContext(QQmlJS::AST::Node *node, Context *parent, ContextType compilationMode);
QHash<QQmlJS::AST::Node *, Context *> contextMap;
QList<Context *> functions;
+ QList<Context *> blocks;
+ QVector<Class> classes;
Context *rootContext;
QString fileName;
QString finalUrl;
QDateTime sourceTimeStamp;
uint unitFlags = 0; // flags merged into CompiledData::Unit::flags
bool debugMode = false;
+ QVector<ExportEntry> localExportEntries;
+ QVector<ExportEntry> indirectExportEntries;
+ QVector<ExportEntry> starExportEntries;
+ QVector<ImportEntry> importEntries;
+ QStringList moduleRequests;
};
@@ -104,8 +149,9 @@ struct Context {
QString name;
int line = 0;
int column = 0;
- int registerCount = 0;
+ int registerCountInFunction = 0;
int functionIndex = -1;
+ int blockIndex = -1;
enum MemberType {
UndefinedMember,
@@ -118,11 +164,13 @@ struct Context {
struct Member {
MemberType type = UndefinedMember;
int index = -1;
- QQmlJS::AST::VariableDeclaration::VariableScope scope = QQmlJS::AST::VariableDeclaration::FunctionScope;
+ QQmlJS::AST::VariableScope scope = QQmlJS::AST::VariableScope::Var;
mutable bool canEscape = false;
QQmlJS::AST::FunctionExpression *function = nullptr;
+ QQmlJS::AST::SourceLocation endOfInitializerLocation;
- bool isLexicallyScoped() const { return this->scope != QQmlJS::AST::VariableDeclaration::FunctionScope; }
+ bool isLexicallyScoped() const { return this->scope != QQmlJS::AST::VariableScope::Var; }
+ bool requiresTDZCheck(const QQmlJS::AST::SourceLocation &accessLocation, bool accessAcrossContextBoundaries) const;
};
typedef QMap<QString, Member> MemberMap;
@@ -131,21 +179,38 @@ struct Context {
QQmlJS::AST::FormalParameterList *formals = nullptr;
QStringList arguments;
QStringList locals;
+ QStringList moduleRequests;
+ QVector<ImportEntry> importEntries;
+ QVector<ExportEntry> exportEntries;
+ QString localNameForDefaultExport;
QVector<Context *> nestedContexts;
ControlFlow *controlFlow = nullptr;
QByteArray code;
QVector<CompiledData::CodeOffsetToLine> lineNumberMapping;
- int maxNumberOfArguments = 0;
+ int nRegisters = 0;
+ int registerOffset = -1;
+ int sizeOfLocalTemporalDeadZone = 0;
+ int firstTemporalDeadZoneRegister = 0;
+ int sizeOfRegisterTemporalDeadZone = 0;
bool hasDirectEval = false;
+ bool allVarsEscape = false;
bool hasNestedFunctions = false;
bool isStrict = false;
+ bool isArrowFunction = false;
+ bool isGenerator = false;
bool usesThis = false;
+ bool innerFunctionAccessesThis = false;
+ bool innerFunctionAccessesNewTarget = false;
bool hasTry = false;
- bool hasWith = false;
bool returnsClosure = false;
mutable bool argumentsCanEscape = false;
+ bool requiresExecutionContext = false;
+ bool isWithBlock = false;
+ bool isCatchBlock = false;
+ QString caughtVariable;
+ QQmlJS::AST::SourceLocation lastBlockInitializerLocation;
enum UsesArgumentsObject {
ArgumentsObjectUnknown,
@@ -155,7 +220,7 @@ struct Context {
UsesArgumentsObject usesArgumentsObject = ArgumentsObjectUnknown;
- CompilationMode compilationMode;
+ ContextType contextType;
template <typename T>
class SmallSet: public QVarLengthArray<T, 8>
@@ -204,25 +269,15 @@ struct Context {
PropertyDependencyMap contextObjectPropertyDependencies;
PropertyDependencyMap scopeObjectPropertyDependencies;
- Context(Context *parent, CompilationMode mode)
+ Context(Context *parent, ContextType type)
: parent(parent)
- , compilationMode(mode)
+ , contextType(type)
{
if (parent && parent->isStrict)
isStrict = true;
}
- bool forceLookupByName();
-
-
- bool canUseSimpleCall() const {
- return nestedContexts.isEmpty() &&
- locals.isEmpty() &&
- !hasTry && !hasWith &&
- (usesArgumentsObject == ArgumentsObjectNotUsed || isStrict) && !hasDirectEval;
- }
-
- int findArgument(const QString &name)
+ int findArgument(const QString &name) const
{
// search backwards to handle duplicate argument names correctly
for (int i = arguments.size() - 1; i >= 0; --i) {
@@ -253,36 +308,51 @@ struct Context {
return true;
}
+ bool requiresImplicitReturnValue() const {
+ return contextType == ContextType::Binding ||
+ contextType == ContextType::Eval ||
+ contextType == ContextType::Global;
+ }
+
void addUsedVariable(const QString &name) {
usedVariables.insert(name);
}
- bool addLocalVar(const QString &name, MemberType type, QQmlJS::AST::VariableDeclaration::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr)
- {
- if (name.isEmpty())
- return true;
+ bool addLocalVar(const QString &name, MemberType contextType, QQmlJS::AST::VariableScope scope, QQmlJS::AST::FunctionExpression *function = nullptr,
+ const QQmlJS::AST::SourceLocation &endOfInitializer = QQmlJS::AST::SourceLocation());
+
+ struct ResolvedName {
+ enum Type {
+ Unresolved,
+ Global,
+ Local,
+ Stack,
+ Import
+ };
+ Type type = Unresolved;
+ bool isArgOrEval = false;
+ bool isConst = false;
+ bool requiresTDZCheck = false;
+ int scope = -1;
+ int index = -1;
+ QQmlJS::AST::SourceLocation endOfDeclarationLocation;
+ bool isValid() const { return type != Unresolved; }
+ };
+ ResolvedName resolveName(const QString &name, const QQmlJS::AST::SourceLocation &accessLocation);
+ void emitBlockHeader(Compiler::Codegen *codegen);
+ void emitBlockFooter(Compiler::Codegen *codegen);
- if (type != FunctionDefinition) {
- for (QQmlJS::AST::FormalParameterList *it = formals; it; it = it->next)
- if (it->name == name)
- return (scope == QQmlJS::AST::VariableDeclaration::FunctionScope);
- }
- MemberMap::iterator it = members.find(name);
- if (it != members.end()) {
- if (scope != QQmlJS::AST::VariableDeclaration::FunctionScope || (*it).scope != QQmlJS::AST::VariableDeclaration::FunctionScope)
- return false;
- if ((*it).type <= type) {
- (*it).type = type;
- (*it).function = function;
- }
- return true;
- }
- Member m;
- m.type = type;
- m.function = function;
- m.scope = scope;
- members.insert(name, m);
- return true;
+ void setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator);
+
+ bool canHaveTailCalls() const
+ {
+ if (!isStrict)
+ return false;
+ if (contextType == ContextType::Function)
+ return !isGenerator;
+ if (contextType == ContextType::Block && parent)
+ return parent->canHaveTailCalls();
+ return false;
}
};
diff --git a/src/qml/compiler/qv4compilercontrolflow_p.h b/src/qml/compiler/qv4compilercontrolflow_p.h
index 9bda20905a..5b622e81d8 100644
--- a/src/qml/compiler/qv4compilercontrolflow_p.h
+++ b/src/qml/compiler/qv4compilercontrolflow_p.h
@@ -53,6 +53,7 @@
#include <private/qv4global_p.h>
#include <private/qv4codegen_p.h>
#include <private/qqmljsast_p.h>
+#include <private/qv4bytecodegenerator_p.h>
QT_BEGIN_NAMESPACE
@@ -68,117 +69,84 @@ struct ControlFlow {
enum Type {
Loop,
With,
+ Block,
Finally,
Catch
};
- enum HandlerType {
- Invalid,
+ enum UnwindType {
Break,
Continue,
- Return,
- Throw
+ Return
};
- struct Handler {
- HandlerType type;
- QString label;
+ struct UnwindTarget {
BytecodeGenerator::Label linkLabel;
- int tempIndex;
- int value;
+ int unwindLevel;
};
Codegen *cg;
ControlFlow *parent;
Type type;
- bool needsLookupByName = false;
ControlFlow(Codegen *cg, Type type)
- : cg(cg), parent(cg->_context->controlFlow), type(type)
+ : cg(cg), parent(cg->controlFlow), type(type)
{
- cg->_context->controlFlow = this;
+ cg->controlFlow = this;
}
virtual ~ControlFlow() {
- cg->_context->controlFlow = parent;
+ cg->controlFlow = parent;
}
- void emitReturnStatement() const {
- if (cg->_returnAddress >= 0) {
- Instruction::LoadReg load;
- load.reg = Moth::StackSlot::createRegister(cg->_returnAddress);
- generator()->addInstruction(load);
+ UnwindTarget unwindTarget(UnwindType type, const QString &label = QString())
+ {
+ Q_ASSERT(type == Break || type == Continue || type == Return);
+ ControlFlow *flow = this;
+ int level = 0;
+ while (flow) {
+ BytecodeGenerator::Label l = flow->getUnwindTarget(type, label);
+ if (l.isValid())
+ return UnwindTarget{l, level};
+ if (flow->requiresUnwind())
+ ++level;
+ flow = flow->parent;
}
- Instruction::Ret ret;
- cg->bytecodeGenerator->addInstruction(ret);
+ if (type == Return)
+ return UnwindTarget{ cg->returnLabel(), level };
+ return UnwindTarget();
}
- void jumpToHandler(const Handler &h) {
- if (h.linkLabel.isReturn()) {
- emitReturnStatement();
- } else {
- if (h.tempIndex >= 0)
- Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex);
- cg->bytecodeGenerator->jump().link(h.linkLabel);
- }
- }
+ virtual QString label() const { return QString(); }
- bool returnRequiresUnwind() const {
- const ControlFlow *f = this;
- while (f) {
- if (f->type == Finally)
+ bool hasLoop() const {
+ const ControlFlow *flow = this;
+ while (flow) {
+ if (flow->type == Loop)
return true;
- f = f->parent;
+ flow = flow->parent;
}
return false;
}
- virtual QString label() const { return QString(); }
-
- bool isSimple() const {
- return type == Loop;
+protected:
+ virtual BytecodeGenerator::Label getUnwindTarget(UnwindType, const QString & = QString()) {
+ return BytecodeGenerator::Label();
}
-
- Handler getParentHandler(HandlerType type, const QString &label = QString()) {
- if (parent)
- return parent->getHandler(type, label);
- switch (type) {
- case Break:
- case Continue:
- return { Invalid, QString(), {}, -1, 0 };
- case Return:
- case Throw:
- return { type, QString(), BytecodeGenerator::Label::returnLabel(), -1, 0 };
- case Invalid:
- break;
- }
- Q_ASSERT(false);
- Q_UNREACHABLE();
+ virtual bool requiresUnwind() {
+ return false;
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) = 0;
-
- BytecodeGenerator::ExceptionHandler *parentExceptionHandler() {
- return parent ? parent->exceptionHandler() : nullptr;
+public:
+ BytecodeGenerator::ExceptionHandler *parentUnwindHandler() {
+ return parent ? parent->unwindHandler() : nullptr;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return parentExceptionHandler();
+ virtual BytecodeGenerator::ExceptionHandler *unwindHandler() {
+ return parentUnwindHandler();
}
- virtual void handleThrow(const Reference &expr) {
- Reference e = expr;
- Handler h = getHandler(ControlFlow::Throw);
- if (h.tempIndex >= 0) {
- e = e.storeOnStack();
- Reference::storeConstOnStack(cg, QV4::Encode(h.value), h.tempIndex);
- }
- e.loadInAccumulator();
- Instruction::ThrowException instr;
- generator()->addInstruction(instr);
- }
-
protected:
QString loopLabel() const {
QString label;
@@ -193,136 +161,155 @@ protected:
}
};
-struct ControlFlowLoop : public ControlFlow
+struct ControlFlowUnwind : public ControlFlow
{
- QString loopLabel;
- BytecodeGenerator::Label *breakLabel = nullptr;
- BytecodeGenerator::Label *continueLabel = nullptr;
+ BytecodeGenerator::ExceptionHandler unwindLabel;
+
+ ControlFlowUnwind(Codegen *cg, Type type)
+ : ControlFlow(cg, type)
+ {
+ }
- ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = nullptr)
- : ControlFlow(cg, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel)
+ void setupUnwindHandler()
{
+ unwindLabel = generator()->newExceptionHandler();
}
- virtual QString label() const { return loopLabel; }
+ void emitUnwindHandler()
+ {
+ Q_ASSERT(requiresUnwind());
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- switch (type) {
- case Break:
- if (breakLabel && (label.isEmpty() || label == loopLabel))
- return { type, loopLabel, *breakLabel, -1, 0 };
- break;
- case Continue:
- if (continueLabel && (label.isEmpty() || label == loopLabel))
- return { type, loopLabel, *continueLabel, -1, 0 };
- break;
- case Return:
- case Throw:
- break;
- case Invalid:
- Q_ASSERT(false);
- Q_UNREACHABLE();
- }
- return getParentHandler(type, label);
+ Instruction::UnwindDispatch dispatch;
+ generator()->addInstruction(dispatch);
}
+ virtual BytecodeGenerator::ExceptionHandler *unwindHandler() override {
+ return unwindLabel.isValid() ? &unwindLabel : parentUnwindHandler();
+ }
};
-struct ControlFlowUnwind : public ControlFlow
+struct ControlFlowUnwindCleanup : public ControlFlowUnwind
{
- BytecodeGenerator::ExceptionHandler unwindLabel;
- int controlFlowTemp;
- QVector<Handler> handlers;
+ std::function<void()> cleanup = nullptr;
- ControlFlowUnwind(Codegen *cg, Type type)
- : ControlFlow(cg, type), unwindLabel(generator()->newExceptionHandler())
+ ControlFlowUnwindCleanup(Codegen *cg, std::function<void()> cleanup, Type type = Block)
+ : ControlFlowUnwind(cg, type), cleanup(cleanup)
{
- Q_ASSERT(type != Loop);
- controlFlowTemp = static_cast<int>(generator()->newRegister());
- Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp);
- // we'll need at least a handler for throw
- getHandler(Throw);
+ if (cleanup) {
+ setupUnwindHandler();
+ generator()->setUnwindHandler(&unwindLabel);
+ }
}
- void emitUnwindHandler()
- {
- Q_ASSERT(!isSimple());
-
- Reference temp = Reference::fromStackSlot(cg, controlFlowTemp);
- for (const auto &h : qAsConst(handlers)) {
- Handler parentHandler = getParentHandler(h.type, h.label);
-
- if (h.type == Throw || parentHandler.tempIndex >= 0) {
- BytecodeGenerator::Label skip = generator()->newLabel();
- generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip);
- if (h.type == Throw)
- emitForThrowHandling();
- jumpToHandler(parentHandler);
- skip.link();
- } else {
- if (parentHandler.linkLabel.isReturn()) {
- BytecodeGenerator::Label skip = generator()->newLabel();
- generator()->jumpStrictNotEqualStackSlotInt(temp.stackSlot(), h.value).link(skip);
- emitReturnStatement();
- skip.link();
- } else {
- generator()->jumpStrictEqualStackSlotInt(temp.stackSlot(), h.value).link(parentHandler.linkLabel);
- }
- }
+ ~ControlFlowUnwindCleanup() {
+ if (cleanup) {
+ unwindLabel.link();
+ generator()->setUnwindHandler(parentUnwindHandler());
+ cleanup();
+ emitUnwindHandler();
}
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- for (const auto &h : qAsConst(handlers)) {
- if (h.type == type && h.label == label)
- return h;
- }
- Handler h = {
- type,
- label,
- unwindLabel,
- controlFlowTemp,
- handlers.size()
- };
- handlers.append(h);
- return h;
+ bool requiresUnwind() override {
+ return cleanup != nullptr;
}
+};
+
+struct ControlFlowLoop : public ControlFlowUnwindCleanup
+{
+ QString loopLabel;
+ BytecodeGenerator::Label *breakLabel = nullptr;
+ BytecodeGenerator::Label *continueLabel = nullptr;
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return &unwindLabel;
+ ControlFlowLoop(Codegen *cg, BytecodeGenerator::Label *breakLabel, BytecodeGenerator::Label *continueLabel = nullptr, std::function<void()> cleanup = nullptr)
+ : ControlFlowUnwindCleanup(cg, cleanup, Loop), loopLabel(ControlFlow::loopLabel()), breakLabel(breakLabel), continueLabel(continueLabel)
+ {
+ }
+
+ BytecodeGenerator::Label getUnwindTarget(UnwindType type, const QString &label) override {
+ switch (type) {
+ case Break:
+ if (breakLabel && (label.isEmpty() || label == loopLabel))
+ return *breakLabel;
+ break;
+ case Continue:
+ if (continueLabel && (label.isEmpty() || label == loopLabel))
+ return *continueLabel;
+ break;
+ default:
+ break;
+ }
+ return BytecodeGenerator::Label();
}
- virtual void emitForThrowHandling() { }
+ QString label() const override { return loopLabel; }
};
+
struct ControlFlowWith : public ControlFlowUnwind
{
ControlFlowWith(Codegen *cg)
: ControlFlowUnwind(cg, With)
{
- needsLookupByName = true;
-
- savedContextRegister = Moth::StackSlot::createRegister(generator()->newRegister());
+ setupUnwindHandler();
// assumes the with object is in the accumulator
Instruction::PushWithContext pushScope;
- pushScope.reg = savedContextRegister;
generator()->addInstruction(pushScope);
- generator()->setExceptionHandler(&unwindLabel);
+ generator()->setUnwindHandler(&unwindLabel);
}
- virtual ~ControlFlowWith() {
+ ~ControlFlowWith() {
// emit code for unwinding
unwindLabel.link();
- generator()->setExceptionHandler(parentExceptionHandler());
+ generator()->setUnwindHandler(parentUnwindHandler());
Instruction::PopContext pop;
- pop.reg = savedContextRegister;
generator()->addInstruction(pop);
emitUnwindHandler();
}
- Moth::StackSlot savedContextRegister;
+
+ bool requiresUnwind() override {
+ return true;
+ }
+
+
+};
+
+struct ControlFlowBlock : public ControlFlowUnwind
+{
+ ControlFlowBlock(Codegen *cg, AST::Node *ast)
+ : ControlFlowUnwind(cg, Block)
+ {
+ block = cg->enterBlock(ast);
+ block->emitBlockHeader(cg);
+
+ if (block->requiresExecutionContext) {
+ setupUnwindHandler();
+ generator()->setUnwindHandler(&unwindLabel);
+ }
+ }
+
+ virtual ~ControlFlowBlock() {
+ // emit code for unwinding
+ if (block->requiresExecutionContext) {
+ unwindLabel.link();
+ generator()->setUnwindHandler(parentUnwindHandler());
+ }
+
+ block->emitBlockFooter(cg);
+
+ if (block->requiresExecutionContext )
+ emitUnwindHandler();
+ cg->leaveBlock();
+ }
+
+ virtual bool requiresUnwind() override {
+ return block->requiresExecutionContext;
+ }
+
+ Context *block;
};
struct ControlFlowCatch : public ControlFlowUnwind
@@ -330,71 +317,56 @@ struct ControlFlowCatch : public ControlFlowUnwind
AST::Catch *catchExpression;
bool insideCatch = false;
BytecodeGenerator::ExceptionHandler exceptionLabel;
- BytecodeGenerator::ExceptionHandler catchUnwindLabel;
ControlFlowCatch(Codegen *cg, AST::Catch *catchExpression)
: ControlFlowUnwind(cg, Catch), catchExpression(catchExpression),
- exceptionLabel(generator()->newExceptionHandler()),
- catchUnwindLabel(generator()->newExceptionHandler())
+ exceptionLabel(generator()->newExceptionHandler())
{
- generator()->setExceptionHandler(&exceptionLabel);
+ generator()->setUnwindHandler(&exceptionLabel);
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- Handler h = getParentHandler(type, label);
- if (h.type == Invalid)
- return h;
- h = ControlFlowUnwind::getHandler(type, label);
- if (insideCatch)
- // if we're inside the catch block, we need to jump to the pop scope
- // instruction at the end of the catch block, not the unwind handler
- h.linkLabel = catchUnwindLabel;
- else if (type == Throw)
- // if we're inside the try block, we need to jump to the catch block,
- // not the unwind handler
- h.linkLabel = exceptionLabel;
- return h;
+ virtual bool requiresUnwind() override {
+ return true;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return insideCatch ? &catchUnwindLabel : &exceptionLabel;
+ BytecodeGenerator::ExceptionHandler *unwindHandler() override {
+ return insideCatch ? &unwindLabel : &exceptionLabel;
}
~ControlFlowCatch() {
// emit code for unwinding
-
- needsLookupByName = true;
insideCatch = true;
+ setupUnwindHandler();
Codegen::RegisterScope scope(cg);
// exceptions inside the try block go here
exceptionLabel.link();
- Moth::StackSlot savedContextReg = Moth::StackSlot::createRegister(generator()->newRegister());
- Instruction::PushCatchContext pushCatch;
- pushCatch.name = cg->registerString(catchExpression->name.toString());
- pushCatch.reg = savedContextReg;
- generator()->addInstruction(pushCatch);
- // clear the unwind temp for exceptions, we want to resume normal code flow afterwards
- Reference::storeConstOnStack(cg, QV4::Encode::undefined(), controlFlowTemp);
- generator()->setExceptionHandler(&catchUnwindLabel);
+ BytecodeGenerator::Jump noException = generator()->jumpNoException();
- cg->statement(catchExpression->statement);
+ Context *block = cg->enterBlock(catchExpression);
- insideCatch = false;
- needsLookupByName = false;
+ block->emitBlockHeader(cg);
- // exceptions inside catch and break/return statements go here
- catchUnwindLabel.link();
- Instruction::PopContext pop;
- pop.reg = savedContextReg;
- generator()->addInstruction(pop);
+ generator()->setUnwindHandler(&unwindLabel);
+
+ if (catchExpression->patternElement->bindingIdentifier.isEmpty())
+ // destructuring pattern
+ cg->initializeAndDestructureBindingElement(catchExpression->patternElement, Reference::fromName(cg, QStringLiteral("@caught")));
+ // skip the additional block
+ cg->statementList(catchExpression->statement->statements);
- // break/continue/return statements in try go here
+ // exceptions inside catch and break/return statements go here
unwindLabel.link();
- generator()->setExceptionHandler(parentExceptionHandler());
+ block->emitBlockFooter(cg);
+
+ cg->leaveBlock();
+
+ noException.link();
+ generator()->setUnwindHandler(parentUnwindHandler());
emitUnwindHandler();
+ insideCatch = false;
}
};
@@ -402,25 +374,21 @@ struct ControlFlowFinally : public ControlFlowUnwind
{
AST::Finally *finally;
bool insideFinally = false;
- int exceptionTemp = -1;
ControlFlowFinally(Codegen *cg, AST::Finally *finally)
: ControlFlowUnwind(cg, Finally), finally(finally)
{
Q_ASSERT(finally != nullptr);
- generator()->setExceptionHandler(&unwindLabel);
+ setupUnwindHandler();
+ generator()->setUnwindHandler(&unwindLabel);
}
- virtual Handler getHandler(HandlerType type, const QString &label = QString()) {
- // if we're inside the finally block, any exceptions etc. should
- // go directly to the parent handler
- if (insideFinally)
- return getParentHandler(type, label);
- return ControlFlowUnwind::getHandler(type, label);
+ virtual bool requiresUnwind() override {
+ return !insideFinally;
}
- virtual BytecodeGenerator::ExceptionHandler *exceptionHandler() {
- return insideFinally ? parentExceptionHandler() : ControlFlowUnwind::exceptionHandler();
+ BytecodeGenerator::ExceptionHandler *unwindHandler() override {
+ return insideFinally ? parentUnwindHandler() : ControlFlowUnwind::unwindHandler();
}
~ControlFlowFinally() {
@@ -429,34 +397,35 @@ struct ControlFlowFinally : public ControlFlowUnwind
Codegen::RegisterScope scope(cg);
- Moth::StackSlot retVal = Moth::StackSlot::createRegister(generator()->newRegister());
- Instruction::StoreReg storeRetVal;
- storeRetVal.reg = retVal;
- generator()->addInstruction(storeRetVal);
-
insideFinally = true;
- exceptionTemp = generator()->newRegister();
+ int returnValueTemp = -1;
+ if (cg->requiresReturnValue) {
+ returnValueTemp = generator()->newRegister();
+ Instruction::MoveReg move;
+ move.srcReg = cg->_returnAddress;
+ move.destReg = returnValueTemp;
+ generator()->addInstruction(move);
+ }
+ int exceptionTemp = generator()->newRegister();
Instruction::GetException instr;
generator()->addInstruction(instr);
Reference::fromStackSlot(cg, exceptionTemp).storeConsumeAccumulator();
- generator()->setExceptionHandler(parentExceptionHandler());
+ generator()->setUnwindHandler(parentUnwindHandler());
cg->statement(finally->statement);
insideFinally = false;
- Instruction::LoadReg loadRetVal;
- loadRetVal.reg = retVal;
- generator()->addInstruction(loadRetVal);
-
- emitUnwindHandler();
- }
-
- virtual void emitForThrowHandling() {
- // reset the exception flag, that got cleared before executing the statements in finally
+ if (cg->requiresReturnValue) {
+ Instruction::MoveReg move;
+ move.srcReg = returnValueTemp;
+ move.destReg = cg->_returnAddress;
+ generator()->addInstruction(move);
+ }
Reference::fromStackSlot(cg, exceptionTemp).loadInAccumulator();
Instruction::SetException setException;
- Q_ASSERT(exceptionTemp != -1);
generator()->addInstruction(setException);
+
+ emitUnwindHandler();
}
};
diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp
index 84ee452332..4e18af2227 100644
--- a/src/qml/compiler/qv4compilerscanfunctions.cpp
+++ b/src/qml/compiler/qv4compilerscanfunctions.cpp
@@ -56,12 +56,12 @@ using namespace QV4;
using namespace QV4::Compiler;
using namespace QQmlJS::AST;
-ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode)
+ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType)
: _cg(cg)
, _sourceCode(sourceCode)
, _context(nullptr)
, _allowFuncDecls(true)
- , defaultProgramMode(defaultProgramMode)
+ , defaultProgramType(defaultProgramType)
{
}
@@ -73,18 +73,19 @@ void ScanFunctions::operator()(Node *node)
calcEscapingVariables();
}
-void ScanFunctions::enterGlobalEnvironment(CompilationMode compilationMode)
+void ScanFunctions::enterGlobalEnvironment(ContextType compilationMode)
{
- enterEnvironment(astNodeForGlobalEnvironment, compilationMode);
+ enterEnvironment(astNodeForGlobalEnvironment, compilationMode, QStringLiteral("%GlobalCode"));
}
-void ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode)
+void ScanFunctions::enterEnvironment(Node *node, ContextType compilationMode, const QString &name)
{
Context *c = _cg->_module->contextMap.value(node);
if (!c)
c = _cg->_module->newContext(node, _context, compilationMode);
if (!c->isStrict)
c->isStrict = _cg->_strictMode;
+ c->name = name;
_contextStack.append(c);
_context = c;
}
@@ -92,28 +93,26 @@ void ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode
void ScanFunctions::leaveEnvironment()
{
_contextStack.pop();
- _context = _contextStack.isEmpty() ? 0 : _contextStack.top();
-}
-
-void ScanFunctions::checkDirectivePrologue(SourceElements *ast)
-{
- for (SourceElements *it = ast; it; it = it->next) {
- if (StatementSourceElement *stmt = cast<StatementSourceElement *>(it->element)) {
- if (ExpressionStatement *expr = cast<ExpressionStatement *>(stmt->statement)) {
- if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) {
- // Use the source code, because the StringLiteral's
- // value might have escape sequences in it, which is not
- // allowed.
- if (strLit->literalToken.length < 2)
- continue;
- QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2);
- if (str == QLatin1String("use strict")) {
- _context->isStrict = true;
- } else {
- // TODO: give a warning.
- }
+ _context = _contextStack.isEmpty() ? nullptr : _contextStack.top();
+}
+
+void ScanFunctions::checkDirectivePrologue(StatementList *ast)
+{
+ for (StatementList *it = ast; it; it = it->next) {
+ if (ExpressionStatement *expr = cast<ExpressionStatement *>(it->statement)) {
+ if (StringLiteral *strLit = cast<StringLiteral *>(expr->expression)) {
+ // Use the source code, because the StringLiteral's
+ // value might have escape sequences in it, which is not
+ // allowed.
+ if (strLit->literalToken.length < 2)
continue;
+ QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2);
+ if (str == QLatin1String("use strict")) {
+ _context->isStrict = true;
+ } else {
+ // TODO: give a warning.
}
+ continue;
}
}
@@ -138,31 +137,162 @@ void ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc)
}
}
-bool ScanFunctions::formalsContainName(AST::FormalParameterList *parameters, const QString &name)
+bool ScanFunctions::visit(Program *ast)
{
- while (parameters) {
- if (parameters->name == name)
- return true;
- parameters = parameters->next;
- }
- return false;
+ enterEnvironment(ast, defaultProgramType, QStringLiteral("%ProgramCode"));
+ checkDirectivePrologue(ast->statements);
+ return true;
}
-bool ScanFunctions::visit(Program *ast)
+void ScanFunctions::endVisit(Program *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(ESModule *ast)
{
- enterEnvironment(ast, defaultProgramMode);
- checkDirectivePrologue(ast->elements);
+ enterEnvironment(ast, defaultProgramType, QStringLiteral("%ModuleCode"));
+ _context->isStrict = true;
return true;
}
-void ScanFunctions::endVisit(Program *)
+void ScanFunctions::endVisit(ESModule *)
{
leaveEnvironment();
}
+bool ScanFunctions::visit(ExportDeclaration *declaration)
+{
+ QString module;
+ if (declaration->fromClause) {
+ module = declaration->fromClause->moduleSpecifier.toString();
+ if (!module.isEmpty())
+ _context->moduleRequests << module;
+ }
+
+ QString localNameForDefaultExport = QStringLiteral("*default*");
+
+ if (declaration->exportAll) {
+ Compiler::ExportEntry entry;
+ entry.moduleRequest = declaration->fromClause->moduleSpecifier.toString();
+ entry.importName = QStringLiteral("*");
+ _context->exportEntries << entry;
+ } else if (declaration->exportClause) {
+ for (ExportsList *it = declaration->exportClause->exportsList; it; it = it->next) {
+ ExportSpecifier *spec = it->exportSpecifier;
+ Compiler::ExportEntry entry;
+ if (module.isEmpty())
+ entry.localName = spec->identifier.toString();
+ else
+ entry.importName = spec->identifier.toString();
+
+ entry.moduleRequest = module;
+ entry.exportName = spec->exportedIdentifier.toString();
+
+ _context->exportEntries << entry;
+ }
+ } else if (auto *vstmt = AST::cast<AST::VariableStatement*>(declaration->variableStatementOrDeclaration)) {
+ QStringList boundNames;
+ for (VariableDeclarationList *it = vstmt->declarations; it; it = it->next) {
+ if (!it->declaration)
+ continue;
+ it->declaration->boundNames(&boundNames);
+ }
+ for (const QString &name: boundNames) {
+ Compiler::ExportEntry entry;
+ entry.localName = name;
+ entry.exportName = name;
+ _context->exportEntries << entry;
+ }
+ } else if (auto *classDecl = AST::cast<AST::ClassDeclaration*>(declaration->variableStatementOrDeclaration)) {
+ QString name = classDecl->name.toString();
+ if (!name.isEmpty()) {
+ Compiler::ExportEntry entry;
+ entry.localName = name;
+ entry.exportName = name;
+ _context->exportEntries << entry;
+ if (declaration->exportDefault)
+ localNameForDefaultExport = entry.localName;
+ }
+ } else if (auto *fdef = declaration->variableStatementOrDeclaration->asFunctionDefinition()) {
+ QString functionName;
+
+ // Only function definitions for which we enter their name into the local environment
+ // can result in exports. Nested expressions such as (function foo() {}) are not accessible
+ // as locals and can only be exported as default exports (further down).
+ auto ast = declaration->variableStatementOrDeclaration;
+ if (AST::cast<AST::ExpressionStatement*>(ast) || AST::cast<AST::FunctionDeclaration*>(ast))
+ functionName = fdef->name.toString();
+
+ if (!functionName.isEmpty()) {
+ Compiler::ExportEntry entry;
+ entry.localName = functionName;
+ entry.exportName = functionName;
+ _context->exportEntries << entry;
+ if (declaration->exportDefault)
+ localNameForDefaultExport = entry.localName;
+ }
+ }
+
+ if (declaration->exportDefault) {
+ Compiler::ExportEntry entry;
+ entry.localName = localNameForDefaultExport;
+ _context->localNameForDefaultExport = localNameForDefaultExport;
+ entry.exportName = QStringLiteral("default");
+ _context->exportEntries << entry;
+ }
+
+ return true; // scan through potential assignment expression code, etc.
+}
+
+bool ScanFunctions::visit(ImportDeclaration *declaration)
+{
+ QString module;
+ if (declaration->fromClause) {
+ module = declaration->fromClause->moduleSpecifier.toString();
+ if (!module.isEmpty())
+ _context->moduleRequests << module;
+ }
+
+ if (!declaration->moduleSpecifier.isEmpty())
+ _context->moduleRequests << declaration->moduleSpecifier.toString();
+
+ if (ImportClause *import = declaration->importClause) {
+ if (!import->importedDefaultBinding.isEmpty()) {
+ Compiler::ImportEntry entry;
+ entry.moduleRequest = module;
+ entry.importName = QStringLiteral("default");
+ entry.localName = import->importedDefaultBinding.toString();
+ _context->importEntries << entry;
+ }
+
+ if (import->nameSpaceImport) {
+ Compiler::ImportEntry entry;
+ entry.moduleRequest = module;
+ entry.importName = QStringLiteral("*");
+ entry.localName = import->nameSpaceImport->importedBinding.toString();
+ _context->importEntries << entry;
+ }
+
+ if (import->namedImports) {
+ for (ImportsList *it = import->namedImports->importsList; it; it = it->next) {
+ Compiler::ImportEntry entry;
+ entry.moduleRequest = module;
+ entry.localName = it->importSpecifier->importedBinding.toString();
+ if (!it->importSpecifier->identifier.isEmpty())
+ entry.importName = it->importSpecifier->identifier.toString();
+ else
+ entry.importName = entry.localName;
+ _context->importEntries << entry;
+ }
+ }
+ }
+ return false;
+}
+
bool ScanFunctions::visit(CallExpression *ast)
{
- if (! _context->hasDirectEval) {
+ if (!_context->hasDirectEval) {
if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) {
if (id->name == QLatin1String("eval")) {
if (_context->usesArgumentsObject == Context::ArgumentsObjectUnknown)
@@ -171,53 +301,36 @@ bool ScanFunctions::visit(CallExpression *ast)
}
}
}
- int argc = 0;
- for (ArgumentList *it = ast->arguments; it; it = it->next)
- ++argc;
- _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc);
return true;
}
-bool ScanFunctions::visit(NewMemberExpression *ast)
+bool ScanFunctions::visit(PatternElement *ast)
{
- int argc = 0;
- for (ArgumentList *it = ast->arguments; it; it = it->next)
- ++argc;
- _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc);
- return true;
-}
+ if (!ast->isVariableDeclaration())
+ return true;
-bool ScanFunctions::visit(ArrayLiteral *ast)
-{
- int index = 0;
- for (ElementList *it = ast->elements; it; it = it->next) {
- for (Elision *elision = it->elision; elision; elision = elision->next)
- ++index;
- ++index;
- }
- if (ast->elision) {
- for (Elision *elision = ast->elision->next; elision; elision = elision->next)
- ++index;
- }
- _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, index);
- return true;
-}
+ QStringList names;
+ ast->boundNames(&names);
-bool ScanFunctions::visit(VariableDeclaration *ast)
-{
- if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode"));
- checkName(ast->name, ast->identifierToken);
- if (ast->name == QLatin1String("arguments"))
- _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
- if (ast->scope == AST::VariableDeclaration::VariableScope::ReadOnlyBlockScope && !ast->expression) {
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration"));
- return false;
- }
- QString name = ast->name.toString();
- if (!_context->addLocalVar(ast->name.toString(), ast->expression ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope)) {
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name));
- return false;
+ QQmlJS::AST::SourceLocation lastInitializerLocation = ast->lastSourceLocation();
+ if (_context->lastBlockInitializerLocation.isValid())
+ lastInitializerLocation = _context->lastBlockInitializerLocation;
+
+ for (const QString &name : qAsConst(names)) {
+ if (_context->isStrict && (name == QLatin1String("eval") || name == QLatin1String("arguments")))
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode"));
+ checkName(QStringRef(&name), ast->identifierToken);
+ if (name == QLatin1String("arguments"))
+ _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ if (ast->scope == VariableScope::Const && !ast->initializer && !ast->isForDeclaration && !ast->destructuringPattern()) {
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration"));
+ return false;
+ }
+ if (!_context->addLocalVar(name, ast->initializer ? Context::VariableDefinition : Context::VariableDeclaration, ast->scope,
+ /*function*/nullptr, lastInitializerLocation)) {
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name));
+ return false;
+ }
}
return true;
}
@@ -237,7 +350,8 @@ bool ScanFunctions::visit(ExpressionStatement *ast)
if (!_allowFuncDecls)
_cg->throwSyntaxError(expr->functionToken, QStringLiteral("conditional function or closure declaration"));
- enterFunction(expr, /*enterName*/ true);
+ if (!enterFunction(expr, /*enterName*/ true))
+ return false;
Node::accept(expr->formals, this);
Node::accept(expr->body, this);
leaveEnvironment();
@@ -253,79 +367,137 @@ bool ScanFunctions::visit(ExpressionStatement *ast)
bool ScanFunctions::visit(FunctionExpression *ast)
{
- enterFunction(ast, /*enterName*/ false);
+ return enterFunction(ast, /*enterName*/ false);
+}
+
+bool ScanFunctions::visit(ClassExpression *ast)
+{
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%Class"));
+ _context->isStrict = true;
+ _context->hasNestedFunctions = true;
+ if (!ast->name.isEmpty())
+ _context->addLocalVar(ast->name.toString(), Context::VariableDefinition, AST::VariableScope::Const);
return true;
}
-void ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName)
+void ScanFunctions::endVisit(ClassExpression *)
{
- if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
- _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode"));
- enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : nullptr);
+ leaveEnvironment();
}
-void ScanFunctions::endVisit(FunctionExpression *)
+bool ScanFunctions::visit(ClassDeclaration *ast)
+{
+ if (!ast->name.isEmpty())
+ _context->addLocalVar(ast->name.toString(), Context::VariableDeclaration, AST::VariableScope::Let);
+
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%Class"));
+ _context->isStrict = true;
+ _context->hasNestedFunctions = true;
+ if (!ast->name.isEmpty())
+ _context->addLocalVar(ast->name.toString(), Context::VariableDefinition, AST::VariableScope::Const);
+ return true;
+}
+
+void ScanFunctions::endVisit(ClassDeclaration *)
{
leaveEnvironment();
}
-bool ScanFunctions::visit(ObjectLiteral *ast)
+bool ScanFunctions::visit(TemplateLiteral *ast)
{
- int argc = 0;
- for (PropertyAssignmentList *it = ast->properties; it; it = it->next) {
- QString key = it->assignment->name->asString();
- if (QV4::String::toArrayIndex(key) != UINT_MAX)
- ++argc;
- ++argc;
- if (AST::cast<AST::PropertyGetterSetter *>(it->assignment))
- ++argc;
+ while (ast) {
+ if (ast->expression)
+ Node::accept(ast->expression, this);
+ ast = ast->next;
}
- _context->maxNumberOfArguments = qMax(_context->maxNumberOfArguments, argc);
+ return true;
+
+}
+
+bool ScanFunctions::visit(SuperLiteral *)
+{
+ Context *c = _context;
+ bool needContext = false;
+ while (c && (c->contextType == ContextType::Block || c->isArrowFunction)) {
+ needContext |= c->isArrowFunction;
+ c = c->parent;
+ }
+
+ c->requiresExecutionContext |= needContext;
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
- Node::accept(ast->properties, this);
return false;
}
-bool ScanFunctions::visit(PropertyGetterSetter *ast)
+bool ScanFunctions::visit(FieldMemberExpression *ast)
{
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
- enterFunction(ast, QString(), ast->formals, ast->functionBody, /*FunctionExpression*/nullptr);
+ if (AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(ast->base)) {
+ if (id->name == QLatin1String("new")) {
+ // new.target
+ if (ast->name != QLatin1String("target")) {
+ _cg->throwSyntaxError(ast->identifierToken, QLatin1String("Expected 'target' after 'new.'."));
+ return false;
+ }
+ Context *c = _context;
+ bool needContext = false;
+ while (c->contextType == ContextType::Block || c->isArrowFunction) {
+ needContext |= c->isArrowFunction;
+ c = c->parent;
+ }
+ c->requiresExecutionContext |= needContext;
+ c->innerFunctionAccessesNewTarget |= needContext;
+
+ return false;
+ }
+ }
+
return true;
}
-void ScanFunctions::endVisit(PropertyGetterSetter *)
+bool ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName)
{
- leaveEnvironment();
+ if (_context->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments")))
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode"));
+ return enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName);
}
-bool ScanFunctions::visit(FunctionDeclaration *ast)
+void ScanFunctions::endVisit(FunctionExpression *)
{
- enterFunction(ast, /*enterName*/ true);
- return true;
+ leaveEnvironment();
}
-void ScanFunctions::endVisit(FunctionDeclaration *)
+bool ScanFunctions::visit(ObjectPattern *ast)
{
- leaveEnvironment();
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
+ Node::accept(ast->properties, this);
+ return false;
}
-bool ScanFunctions::visit(TryStatement *)
+bool ScanFunctions::visit(PatternProperty *ast)
{
- // ### should limit to catch(), as try{} finally{} should be ok without
- _context->hasTry = true;
+ Q_UNUSED(ast);
+ // ### Shouldn't be required anymore
+// if (ast->type == PatternProperty::Getter || ast->type == PatternProperty::Setter) {
+// TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true);
+// return enterFunction(ast, QString(), ast->formals, ast->functionBody, /*enterName */ false);
+// }
return true;
}
-bool ScanFunctions::visit(WithStatement *ast)
+void ScanFunctions::endVisit(PatternProperty *)
{
- if (_context->isStrict) {
- _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode"));
- return false;
- }
+ // ###
+// if (ast->type == PatternProperty::Getter || ast->type == PatternProperty::Setter)
+// leaveEnvironment();
+}
- _context->hasWith = true;
- return true;
+bool ScanFunctions::visit(FunctionDeclaration *ast)
+{
+ return enterFunction(ast, /*enterName*/ true);
+}
+
+void ScanFunctions::endVisit(FunctionDeclaration *)
+{
+ leaveEnvironment();
}
bool ScanFunctions::visit(DoWhileStatement *ast) {
@@ -338,7 +510,9 @@ bool ScanFunctions::visit(DoWhileStatement *ast) {
}
bool ScanFunctions::visit(ForStatement *ast) {
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%For"));
Node::accept(ast->initialiser, this);
+ Node::accept(ast->declarations, this);
Node::accept(ast->condition, this);
Node::accept(ast->expression, this);
@@ -348,9 +522,16 @@ bool ScanFunctions::visit(ForStatement *ast) {
return false;
}
-bool ScanFunctions::visit(LocalForStatement *ast) {
- Node::accept(ast->declarations, this);
- Node::accept(ast->condition, this);
+void ScanFunctions::endVisit(ForStatement *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(ForEachStatement *ast) {
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%Foreach"));
+ if (ast->expression)
+ _context->lastBlockInitializerLocation = ast->expression->lastSourceLocation();
+ Node::accept(ast->lhs, this);
Node::accept(ast->expression, this);
TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
@@ -359,90 +540,154 @@ bool ScanFunctions::visit(LocalForStatement *ast) {
return false;
}
-bool ScanFunctions::visit(ForEachStatement *ast) {
- Node::accept(ast->initialiser, this);
- Node::accept(ast->expression, this);
+void ScanFunctions::endVisit(ForEachStatement *)
+{
+ leaveEnvironment();
+}
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
- Node::accept(ast->statement, this);
+bool ScanFunctions::visit(ThisExpression *)
+{
+ _context->usesThis = true;
+ return false;
+}
+bool ScanFunctions::visit(Block *ast)
+{
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls);
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%Block"));
+ Node::accept(ast->statements, this);
return false;
}
-bool ScanFunctions::visit(LocalForEachStatement *ast) {
- Node::accept(ast->declaration, this);
- Node::accept(ast->expression, this);
+void ScanFunctions::endVisit(Block *)
+{
+ leaveEnvironment();
+}
- TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_context->isStrict);
- Node::accept(ast->statement, this);
+bool ScanFunctions::visit(CaseBlock *ast)
+{
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%CaseBlock"));
+ return true;
+}
- return false;
+void ScanFunctions::endVisit(CaseBlock *)
+{
+ leaveEnvironment();
}
-bool ScanFunctions::visit(ThisExpression *)
+bool ScanFunctions::visit(Catch *ast)
{
- _context->usesThis = true;
+ TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls);
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%CatchBlock"));
+ _context->isCatchBlock = true;
+ QString caughtVar = ast->patternElement->bindingIdentifier.toString();
+ if (caughtVar.isEmpty())
+ caughtVar = QStringLiteral("@caught");
+ _context->addLocalVar(caughtVar, Context::MemberType::VariableDefinition, VariableScope::Let);
+
+ _context->caughtVariable = caughtVar;
+ if (_context->isStrict &&
+ (caughtVar == QLatin1String("eval") || caughtVar == QLatin1String("arguments"))) {
+ _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Catch variable name may not be eval or arguments in strict mode"));
+ return false;
+ }
+ Node::accept(ast->patternElement, this);
+ // skip the block statement
+ Node::accept(ast->statement->statements, this);
return false;
}
-bool ScanFunctions::visit(Block *ast) {
+void ScanFunctions::endVisit(Catch *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::visit(WithStatement *ast)
+{
+ Node::accept(ast->expression, this);
+
TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _context->isStrict ? false : _allowFuncDecls);
- Node::accept(ast->statements, this);
+ enterEnvironment(ast, ContextType::Block, QStringLiteral("%WithBlock"));
+ _context->isWithBlock = true;
+
+ if (_context->isStrict) {
+ _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode"));
+ return false;
+ }
+ Node::accept(ast->statement, this);
+
return false;
}
-void ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr)
+void ScanFunctions::endVisit(WithStatement *)
+{
+ leaveEnvironment();
+}
+
+bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, StatementList *body, bool enterName)
{
Context *outerContext = _context;
- enterEnvironment(ast, FunctionCode);
+ enterEnvironment(ast, ContextType::Function, name);
+ FunctionExpression *expr = AST::cast<FunctionExpression *>(ast);
+ if (!expr)
+ expr = AST::cast<FunctionDeclaration *>(ast);
if (outerContext) {
outerContext->hasNestedFunctions = true;
// The identifier of a function expression cannot be referenced from the enclosing environment.
- if (expr) {
- if (!outerContext->addLocalVar(name, Context::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr)) {
+ if (enterName) {
+ if (!outerContext->addLocalVar(name, Context::FunctionDefinition, VariableScope::Var, expr)) {
_cg->throwSyntaxError(ast->firstSourceLocation(), QStringLiteral("Identifier %1 has already been declared").arg(name));
- return;
+ return false;
}
+ outerContext->addLocalVar(name, Context::FunctionDefinition, VariableScope::Var, expr);
}
if (name == QLatin1String("arguments"))
outerContext->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
}
- if (formalsContainName(formals, QStringLiteral("arguments")))
+ _context->name = name;
+ if (formals && formals->containsName(QStringLiteral("arguments")))
_context->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ if (expr) {
+ if (expr->isArrowFunction)
+ _context->isArrowFunction = true;
+ else if (expr->isGenerator)
+ _context->isGenerator = true;
+ }
- if (!name.isEmpty() && !formalsContainName(formals, name))
- _context->addLocalVar(name, Context::ThisFunctionName, QQmlJS::AST::VariableDeclaration::FunctionScope);
+ if (!enterName && (!name.isEmpty() && (!formals || !formals->containsName(name))))
+ _context->addLocalVar(name, Context::ThisFunctionName, VariableScope::Var);
_context->formals = formals;
if (body && !_context->isStrict)
- checkDirectivePrologue(body->elements);
-
- for (FormalParameterList *it = formals; it; it = it->next) {
- QString arg = it->name.toString();
- int duplicateIndex = _context->arguments.indexOf(arg);
- if (duplicateIndex != -1) {
- if (_context->isStrict) {
- _cg->throwSyntaxError(it->identifierToken, QStringLiteral("Duplicate parameter name '%1' is not allowed in strict mode").arg(arg));
- return;
- } else {
- // change the name of the earlier argument to enforce the specified lookup semantics
- QString modified = arg;
- while (_context->arguments.contains(modified))
- modified += QString(0xfffe);
- _context->arguments[duplicateIndex] = modified;
+ checkDirectivePrologue(body);
+
+ bool isSimpleParameterList = formals && formals->isSimpleParameterList();
+
+ _context->arguments = formals ? formals->formals() : QStringList();
+
+ const QStringList boundNames = formals ? formals->boundNames() : QStringList();
+ for (int i = 0; i < boundNames.size(); ++i) {
+ const QString &arg = boundNames.at(i);
+ if (_context->isStrict || !isSimpleParameterList) {
+ bool duplicate = (boundNames.indexOf(arg, i + 1) != -1);
+ if (duplicate) {
+ _cg->throwSyntaxError(formals->firstSourceLocation(), QStringLiteral("Duplicate parameter name '%1' is not allowed.").arg(arg));
+ return false;
}
}
if (_context->isStrict) {
if (arg == QLatin1String("eval") || arg == QLatin1String("arguments")) {
- _cg->throwSyntaxError(it->identifierToken, QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg));
- return;
+ _cg->throwSyntaxError(formals->firstSourceLocation(), QStringLiteral("'%1' cannot be used as parameter name in strict mode").arg(arg));
+ return false;
}
}
- _context->arguments += arg;
+ if (!_context->arguments.contains(arg))
+ _context->addLocalVar(arg, Context::VariableDefinition, VariableScope::Var);
}
+ return true;
}
void ScanFunctions::calcEscapingVariables()
@@ -450,27 +695,150 @@ void ScanFunctions::calcEscapingVariables()
Module *m = _cg->_module;
for (Context *inner : qAsConst(m->contextMap)) {
+ if (inner->usesArgumentsObject != Context::ArgumentsObjectUsed)
+ continue;
+ if (inner->contextType != ContextType::Block && !inner->isArrowFunction)
+ continue;
+ Context *c = inner->parent;
+ while (c && (c->contextType == ContextType::Block || c->isArrowFunction))
+ c = c->parent;
+ if (c)
+ c->usesArgumentsObject = Context::ArgumentsObjectUsed;
+ inner->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ }
+ for (Context *inner : qAsConst(m->contextMap)) {
+ if (!inner->parent || inner->usesArgumentsObject == Context::ArgumentsObjectUnknown)
+ inner->usesArgumentsObject = Context::ArgumentsObjectNotUsed;
+ if (inner->usesArgumentsObject == Context::ArgumentsObjectUsed) {
+ QString arguments = QStringLiteral("arguments");
+ inner->addLocalVar(arguments, Context::VariableDeclaration, AST::VariableScope::Var);
+ if (!inner->isStrict) {
+ inner->argumentsCanEscape = true;
+ inner->requiresExecutionContext = true;
+ }
+ }
+ }
+
+ for (Context *c : qAsConst(m->contextMap)) {
+ 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;
+ }
+ break;
+ }
+
+ for (Context *inner : qAsConst(m->contextMap)) {
for (const QString &var : qAsConst(inner->usedVariables)) {
Context *c = inner;
while (c) {
+ Context *current = c;
+ c = c->parent;
+ if (current->isWithBlock || current->contextType != ContextType::Block)
+ break;
+ }
+ Q_ASSERT(c != inner);
+ while (c) {
Context::MemberMap::const_iterator it = c->members.find(var);
if (it != c->members.end()) {
- if (c != inner)
+ if (c->parent || it->isLexicallyScoped()) {
+ it->canEscape = true;
+ c->requiresExecutionContext = true;
+ } else if (c->contextType == ContextType::ESModule) {
+ // Module instantiation provides a context, but vars used from inner
+ // scopes need to be stored in its locals[].
it->canEscape = true;
+ }
break;
}
if (c->findArgument(var) != -1) {
- if (c != inner)
- c->argumentsCanEscape = true;
+ c->argumentsCanEscape = true;
+ c->requiresExecutionContext = true;
break;
}
c = c->parent;
}
}
- Context *c = inner->parent;
- while (c) {
- c->hasDirectEval |= inner->hasDirectEval;
- c = c->parent;
+ if (inner->hasDirectEval) {
+ inner->hasDirectEval = false;
+ inner->innerFunctionAccessesNewTarget = true;
+ if (!inner->isStrict) {
+ Context *c = inner;
+ while (c->contextType == ContextType::Block) {
+ c = c->parent;
+ }
+ Q_ASSERT(c);
+ c->hasDirectEval = true;
+ c->innerFunctionAccessesThis = true;
+ }
+ Context *c = inner;
+ while (c) {
+ c->allVarsEscape = true;
+ c = c->parent;
+ }
+ }
+ if (inner->usesThis) {
+ inner->usesThis = false;
+ bool innerFunctionAccessesThis = false;
+ Context *c = inner;
+ while (c->contextType == ContextType::Block || c->isArrowFunction) {
+ innerFunctionAccessesThis |= c->isArrowFunction;
+ c = c->parent;
+ }
+ Q_ASSERT(c);
+ if (!inner->isStrict)
+ c->usesThis = true;
+ c->innerFunctionAccessesThis |= innerFunctionAccessesThis;
+ }
+ }
+ for (Context *c : qAsConst(m->contextMap)) {
+ if (c->innerFunctionAccessesThis) {
+ // 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;
+ }
+ 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;
+ }
+ if (c->allVarsEscape && c->contextType == ContextType::Block && c->members.isEmpty())
+ c->allVarsEscape = false;
+ if (c->contextType == ContextType::Global || (!c->isStrict && c->contextType == ContextType::Eval) || m->debugMode)
+ c->allVarsEscape = true;
+ if (c->allVarsEscape) {
+ if (c->parent) {
+ c->requiresExecutionContext = true;
+ c->argumentsCanEscape = true;
+ } else {
+ for (const auto &m : qAsConst(c->members)) {
+ if (m.isLexicallyScoped()) {
+ c->requiresExecutionContext = true;
+ break;
+ }
+ }
+ }
+ }
+ if (c->contextType == ContextType::Block && c->isCatchBlock) {
+ c->requiresExecutionContext = true;
+ auto m = c->members.find(c->caughtVariable);
+ m->canEscape = true;
+ }
+ const QLatin1String exprForOn("expression for on");
+ if (c->contextType == ContextType::Binding && c->name.length() > exprForOn.size() &&
+ c->name.startsWith(exprForOn) && c->name.at(exprForOn.size()).isUpper())
+ // we don't really need this for bindings, but we do for signal handlers, and in this case,
+ // we don't know if the code is a signal handler or not.
+ c->requiresExecutionContext = true;
+ if (c->allVarsEscape) {
+ for (auto &m : c->members)
+ m.canEscape = true;
}
}
@@ -478,10 +846,13 @@ void ScanFunctions::calcEscapingVariables()
if (showEscapingVars) {
qDebug() << "==== escaping variables ====";
for (Context *c : qAsConst(m->contextMap)) {
- qDebug() << "Context" << c->name << ":";
- qDebug() << " Arguments escape" << c->argumentsCanEscape;
+ qDebug() << "Context" << c << c->name << "requiresExecutionContext" << c->requiresExecutionContext << "isStrict" << c->isStrict;
+ qDebug() << " isArrowFunction" << c->isArrowFunction << "innerFunctionAccessesThis" << c->innerFunctionAccessesThis;
+ qDebug() << " parent:" << c->parent;
+ if (c->argumentsCanEscape)
+ qDebug() << " Arguments escape";
for (auto it = c->members.constBegin(); it != c->members.constEnd(); ++it) {
- qDebug() << " " << it.key() << it.value().canEscape;
+ qDebug() << " " << it.key() << it.value().index << it.value().canEscape << "isLexicallyScoped:" << it.value().isLexicallyScoped();
}
}
}
diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h
index 87b7210879..bb07540ec9 100644
--- a/src/qml/compiler/qv4compilerscanfunctions_p.h
+++ b/src/qml/compiler/qv4compilerscanfunctions_p.h
@@ -58,6 +58,7 @@
#include <private/qv4util_p.h>
#include <QtCore/QStringList>
#include <QStack>
+#include <QScopedValueRollback>
QT_BEGIN_NAMESPACE
@@ -79,13 +80,13 @@ class Codegen;
class ScanFunctions: protected QQmlJS::AST::Visitor
{
- typedef QV4::TemporaryAssignment<bool> TemporaryBoolAssignment;
+ typedef QScopedValueRollback<bool> TemporaryBoolAssignment;
public:
- ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode);
+ ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType);
void operator()(AST::Node *node);
- void enterGlobalEnvironment(CompilationMode compilationMode);
- void enterEnvironment(AST::Node *node, CompilationMode compilationMode);
+ void enterGlobalEnvironment(ContextType compilationMode);
+ void enterEnvironment(AST::Node *node, ContextType compilationMode, const QString &name);
void leaveEnvironment();
void enterQmlFunction(AST::FunctionDeclaration *ast)
@@ -95,48 +96,68 @@ protected:
using Visitor::visit;
using Visitor::endVisit;
- void checkDirectivePrologue(AST::SourceElements *ast);
+ void checkDirectivePrologue(AST::StatementList *ast);
void checkName(const QStringRef &name, const AST::SourceLocation &loc);
- bool formalsContainName(AST::FormalParameterList *parameters, const QString &name);
bool visit(AST::Program *ast) override;
void endVisit(AST::Program *) override;
+ bool visit(AST::ESModule *ast) override;
+ void endVisit(AST::ESModule *) override;
+
+ bool visit(AST::ExportDeclaration *declaration) override;
+ bool visit(AST::ImportDeclaration *declaration) override;
+
bool visit(AST::CallExpression *ast) override;
- bool visit(AST::NewMemberExpression *ast) override;
- bool visit(AST::ArrayLiteral *ast) override;
- bool visit(AST::VariableDeclaration *ast) override;
+ bool visit(AST::PatternElement *ast) override;
bool visit(AST::IdentifierExpression *ast) override;
bool visit(AST::ExpressionStatement *ast) override;
bool visit(AST::FunctionExpression *ast) override;
+ bool visit(AST::TemplateLiteral *ast) override;
+ bool visit(AST::SuperLiteral *) override;
+ bool visit(AST::FieldMemberExpression *) override;
- void enterFunction(AST::FunctionExpression *ast, bool enterName);
+ bool enterFunction(AST::FunctionExpression *ast, bool enterName);
void endVisit(AST::FunctionExpression *) override;
- bool visit(AST::ObjectLiteral *ast) override;
+ bool visit(AST::ObjectPattern *ast) override;
- bool visit(AST::PropertyGetterSetter *ast) override;
- void endVisit(AST::PropertyGetterSetter *) override;
+ bool visit(AST::PatternProperty *ast) override;
+ void endVisit(AST::PatternProperty *) override;
bool visit(AST::FunctionDeclaration *ast) override;
void endVisit(AST::FunctionDeclaration *) override;
- bool visit(AST::TryStatement *ast) override;
- bool visit(AST::WithStatement *ast) override;
+ bool visit(AST::ClassExpression *ast) override;
+ void endVisit(AST::ClassExpression *) override;
+
+ bool visit(AST::ClassDeclaration *ast) override;
+ void endVisit(AST::ClassDeclaration *) override;
bool visit(AST::DoWhileStatement *ast) override;
bool visit(AST::ForStatement *ast) override;
- bool visit(AST::LocalForStatement *ast) override;
+ void endVisit(AST::ForStatement *) override;
bool visit(AST::ForEachStatement *ast) override;
- bool visit(AST::LocalForEachStatement *ast) override;
+ void endVisit(AST::ForEachStatement *) override;
+
bool visit(AST::ThisExpression *ast) override;
bool visit(AST::Block *ast) override;
+ void endVisit(AST::Block *ast) override;
+
+ bool visit(AST::CaseBlock *ast) override;
+ void endVisit(AST::CaseBlock *ast) override;
+
+ bool visit(AST::Catch *ast) override;
+ void endVisit(AST::Catch *ast) override;
+
+ bool visit(AST::WithStatement *ast) override;
+ void endVisit(AST::WithStatement *ast) override;
protected:
- void enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::FunctionBody *body, AST::FunctionExpression *expr);
+ bool enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::StatementList *body, bool enterName);
void calcEscapingVariables();
// fields:
@@ -146,7 +167,7 @@ protected:
QStack<Context *> _contextStack;
bool _allowFuncDecls;
- CompilationMode defaultProgramMode;
+ ContextType defaultProgramType;
private:
static constexpr AST::Node *astNodeForGlobalEnvironment = nullptr;
diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp
index 450fa50528..eed8ffe6b8 100644
--- a/src/qml/compiler/qv4instr_moth.cpp
+++ b/src/qml/compiler/qv4instr_moth.cpp
@@ -39,15 +39,16 @@
#include "qv4instr_moth_p.h"
#include <private/qv4compileddata_p.h>
+#include <private/qv4stackframe_p.h>
using namespace QV4;
using namespace QV4::Moth;
int InstrInfo::size(Instr::Type type)
{
-#define MOTH_RETURN_INSTR_SIZE(I) case Instr::Type::I: return InstrMeta<int(Instr::Type::I)>::Size;
+#define MOTH_RETURN_INSTR_SIZE(I) case Instr::Type::I: case Instr::Type::I##_Wide: return InstrMeta<int(Instr::Type::I)>::Size;
switch (type) {
- FOR_EACH_MOTH_INSTR(MOTH_RETURN_INSTR_SIZE)
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_RETURN_INSTR_SIZE)
}
#undef MOTH_RETURN_INSTR_SIZE
Q_UNREACHABLE();
@@ -110,6 +111,8 @@ static QString toString(QV4::ReturnedValue v)
QDebug d = qDebug(); \
d.noquote(); \
d.nospace(); \
+ if (static_cast<int>(Instr::Type::instr) >= 0x100) \
+ --base_ptr; \
d << alignedLineNumber(line) << alignedNumber(codeOffset).constData() << ": " \
<< rawBytes(base_ptr, int(code - base_ptr)) << #instr << " ";
@@ -122,7 +125,7 @@ namespace QV4 {
namespace Moth {
const int InstrInfo::argumentCount[] = {
- FOR_EACH_MOTH_INSTR(MOTH_COLLECT_NARGS)
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_COLLECT_NARGS)
};
@@ -147,11 +150,13 @@ QString dumpRegister(int reg, int nFormals)
return QStringLiteral("(context)");
else if (reg == CallData::Accumulator)
return QStringLiteral("(accumulator)");
+ else if (reg == CallData::NewTarget)
+ return QStringLiteral("(new.target)");
else if (reg == CallData::This)
return QStringLiteral("(this)");
else if (reg == CallData::Argc)
return QStringLiteral("(argc)");
- reg -= 4;
+ reg -= CallData::OffsetCount;
if (reg <= nFormals)
return QStringLiteral("a%1").arg(reg);
reg -= nFormals;
@@ -202,6 +207,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(destReg, nFormals) << ", " << dumpRegister(srcReg, nFormals);
MOTH_END_INSTR(MoveReg)
+ MOTH_BEGIN_INSTR(LoadImport)
+ d << "i" << index;
+ MOTH_END_INSTR(LoadImport)
+
MOTH_BEGIN_INSTR(LoadConst)
d << "C" << index;
MOTH_END_INSTR(LoadConst)
@@ -286,10 +295,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(StoreNameStrict)
MOTH_BEGIN_INSTR(LoadElement)
- d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]";
- MOTH_END_INSTR(LoadElement)
-
- MOTH_BEGIN_INSTR(LoadElementA)
d << dumpRegister(base, nFormals) << "[acc]";
MOTH_END_INSTR(LoadElement)
@@ -298,20 +303,12 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(StoreElement)
MOTH_BEGIN_INSTR(LoadProperty)
- d << dumpRegister(base, nFormals) << "[" << name << "]";
- MOTH_END_INSTR(LoadProperty)
-
- MOTH_BEGIN_INSTR(LoadPropertyA)
d << "acc[" << name << "]";
- MOTH_END_INSTR(LoadElementA)
+ MOTH_END_INSTR(LoadProperty)
MOTH_BEGIN_INSTR(GetLookup)
- d << dumpRegister(base, nFormals) << "(" << index << ")";
- MOTH_END_INSTR(GetLookup)
-
- MOTH_BEGIN_INSTR(GetLookupA)
d << "acc(" << index << ")";
- MOTH_END_INSTR(GetLookupA)
+ MOTH_END_INSTR(GetLookup)
MOTH_BEGIN_INSTR(StoreProperty)
d << dumpRegister(base, nFormals) << "[" << name<< "]";
@@ -321,6 +318,14 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(base, nFormals) << "(" << index << ")";
MOTH_END_INSTR(SetLookup)
+ MOTH_BEGIN_INSTR(LoadSuperProperty)
+ d << dumpRegister(property, nFormals);
+ MOTH_END_INSTR(LoadSuperProperty)
+
+ MOTH_BEGIN_INSTR(StoreSuperProperty)
+ d << dumpRegister(property, nFormals);
+ MOTH_END_INSTR(StoreSuperProperty)
+
MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]";
MOTH_END_INSTR(StoreScopeObjectProperty)
@@ -341,10 +346,24 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(base, nFormals) << "[" << index << "]";
MOTH_END_INSTR(LoadIdObject)
+ MOTH_BEGIN_INSTR(Yield)
+ MOTH_END_INSTR(Yield)
+
+ MOTH_BEGIN_INSTR(YieldStar)
+ MOTH_END_INSTR(YieldStar)
+
+ MOTH_BEGIN_INSTR(Resume)
+ d << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(Resume)
+
MOTH_BEGIN_INSTR(CallValue)
d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallValue)
+ MOTH_BEGIN_INSTR(CallWithReceiver)
+ d << dumpRegister(name, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(CallWithReceiver)
+
MOTH_BEGIN_INSTR(CallProperty)
d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallProperty)
@@ -377,12 +396,35 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals);
MOTH_END_INSTR(CallContextObjectProperty)
- MOTH_BEGIN_INSTR(SetExceptionHandler)
+ MOTH_BEGIN_INSTR(CallWithSpread)
+ d << "new" << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(CallWithSpread)
+
+ MOTH_BEGIN_INSTR(Construct)
+ d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(Construct)
+
+ MOTH_BEGIN_INSTR(ConstructWithSpread)
+ d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals);
+ MOTH_END_INSTR(ConstructWithSpread)
+
+ MOTH_BEGIN_INSTR(SetUnwindHandler)
if (offset)
d << ABSOLUTE_OFFSET();
else
d << "<null>";
- MOTH_END_INSTR(SetExceptionHandler)
+ MOTH_END_INSTR(SetUnwindHandler)
+
+ MOTH_BEGIN_INSTR(UnwindDispatch)
+ MOTH_END_INSTR(UnwindDispatch)
+
+ MOTH_BEGIN_INSTR(UnwindToLabel)
+ d << "(" << level << ") " << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(UnwindToLabel)
+
+ MOTH_BEGIN_INSTR(DeadTemporalZoneCheck)
+ d << name;
+ MOTH_END_INSTR(DeadTemporalZoneCheck)
MOTH_BEGIN_INSTR(ThrowException)
MOTH_END_INSTR(ThrowException)
@@ -397,30 +439,51 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(CreateCallContext)
MOTH_BEGIN_INSTR(PushCatchContext)
- d << dumpRegister(reg, nFormals) << ", " << name;
+ d << index << ", " << name;
MOTH_END_INSTR(PushCatchContext)
MOTH_BEGIN_INSTR(PushWithContext)
- d << dumpRegister(reg, nFormals);
MOTH_END_INSTR(PushWithContext)
+ MOTH_BEGIN_INSTR(PushBlockContext)
+ d << index;
+ MOTH_END_INSTR(PushBlockContext)
+
+ MOTH_BEGIN_INSTR(CloneBlockContext)
+ MOTH_END_INSTR(CloneBlockContext)
+
+ MOTH_BEGIN_INSTR(PushScriptContext)
+ d << index;
+ MOTH_END_INSTR(PushScriptContext)
+
+ MOTH_BEGIN_INSTR(PopScriptContext)
+ MOTH_END_INSTR(PopScriptContext)
+
MOTH_BEGIN_INSTR(PopContext)
- d << dumpRegister(reg, nFormals);
MOTH_END_INSTR(PopContext)
- MOTH_BEGIN_INSTR(ForeachIteratorObject)
- MOTH_END_INSTR(ForeachIteratorObject)
+ MOTH_BEGIN_INSTR(GetIterator)
+ d << iterator;
+ MOTH_END_INSTR(GetIterator)
+
+ MOTH_BEGIN_INSTR(IteratorNext)
+ d << dumpRegister(value, nFormals) << ", " << dumpRegister(done, nFormals);
+ MOTH_END_INSTR(IteratorNext)
- MOTH_BEGIN_INSTR(ForeachNextPropertyName)
- MOTH_END_INSTR(ForeachNextPropertyName)
+ MOTH_BEGIN_INSTR(IteratorNextForYieldStar)
+ d << dumpRegister(iterator, nFormals) << ", " << dumpRegister(object, nFormals);
+ MOTH_END_INSTR(IteratorNextForYieldStar)
- MOTH_BEGIN_INSTR(DeleteMember)
- d << dumpRegister(base, nFormals) << "[" << member << "]";
- MOTH_END_INSTR(DeleteMember)
+ MOTH_BEGIN_INSTR(IteratorClose)
+ d << dumpRegister(done, nFormals);
+ MOTH_END_INSTR(IteratorClose)
- MOTH_BEGIN_INSTR(DeleteSubscript)
+ MOTH_BEGIN_INSTR(DestructureRestElement)
+ MOTH_END_INSTR(DestructureRestElement)
+
+ MOTH_BEGIN_INSTR(DeleteProperty)
d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]";
- MOTH_END_INSTR(DeleteSubscript)
+ MOTH_END_INSTR(DeleteProperty)
MOTH_BEGIN_INSTR(DeleteName)
d << name;
@@ -442,24 +505,35 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_END_INSTR(DefineArray)
MOTH_BEGIN_INSTR(DefineObjectLiteral)
- d << dumpRegister(args, nFormals)
- << ", " << internalClassId
- << ", " << arrayValueCount
- << ", " << arrayGetterSetterCountAndFlags;
+ d << internalClassId
+ << ", " << argc
+ << ", " << dumpRegister(args, nFormals);
MOTH_END_INSTR(DefineObjectLiteral)
+ MOTH_BEGIN_INSTR(CreateClass)
+ d << classIndex
+ << ", " << dumpRegister(heritage, nFormals)
+ << ", " << dumpRegister(computedNames, nFormals);
+ MOTH_END_INSTR(CreateClass)
+
MOTH_BEGIN_INSTR(CreateMappedArgumentsObject)
MOTH_END_INSTR(CreateMappedArgumentsObject)
MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject)
MOTH_END_INSTR(CreateUnmappedArgumentsObject)
+ MOTH_BEGIN_INSTR(CreateRestParameter)
+ d << argIndex;
+ MOTH_END_INSTR(CreateRestParameter)
+
MOTH_BEGIN_INSTR(ConvertThisToObject)
MOTH_END_INSTR(ConvertThisToObject)
- MOTH_BEGIN_INSTR(Construct)
- d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals);
- MOTH_END_INSTR(Construct)
+ MOTH_BEGIN_INSTR(LoadSuperConstructor)
+ MOTH_END_INSTR(LoadSuperConstructor)
+
+ MOTH_BEGIN_INSTR(ToObject)
+ MOTH_END_INSTR(ToObject)
MOTH_BEGIN_INSTR(Jump)
d << ABSOLUTE_OFFSET();
@@ -473,6 +547,14 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << ABSOLUTE_OFFSET();
MOTH_END_INSTR(JumpFalse)
+ MOTH_BEGIN_INSTR(JumpNotUndefined)
+ d << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(JumpNotUndefined)
+
+ MOTH_BEGIN_INSTR(JumpNoException)
+ d << ABSOLUTE_OFFSET();
+ MOTH_END_INSTR(JumpNoException)
+
MOTH_BEGIN_INSTR(CmpEqNull)
MOTH_END_INSTR(CmpEqNull)
@@ -519,14 +601,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << dumpRegister(lhs, nFormals);
MOTH_END_INSTR(CmpStrictNotEqual)
- MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt)
- d << dumpRegister(lhs, nFormals) << ", " << rhs << " " << ABSOLUTE_OFFSET();
- MOTH_END_INSTR(JumpStrictEqualStackSlotInt)
-
- MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt)
- d << dumpRegister(lhs, nFormals) << ", " << rhs << " " << ABSOLUTE_OFFSET();
- MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
MOTH_BEGIN_INSTR(UNot)
MOTH_END_INSTR(UNot)
@@ -597,6 +671,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
d << "acc, " << rhs;
MOTH_END_INSTR(ShlConst)
+ MOTH_BEGIN_INSTR(Exp)
+ d << dumpRegister(lhs, nFormals) << ", acc";
+ MOTH_END_INSTR(Exp)
+
MOTH_BEGIN_INSTR(Mul)
d << dumpRegister(lhs, nFormals) << ", acc";
MOTH_END_INSTR(Mul)
@@ -627,6 +705,13 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
MOTH_BEGIN_INSTR(Debug)
MOTH_END_INSTR(Debug)
+ MOTH_BEGIN_INSTR(InitializeBlockDeadTemporalZone)
+ d << dumpRegister(firstReg, nFormals) << ", " << count;
+ MOTH_END_INSTR(InitializeBlockDeadTemporalZone)
+
+ MOTH_BEGIN_INSTR(ThrowOnNullOrUndefined)
+ MOTH_END_INSTR(ThrowOnNullOrUndefined)
+
MOTH_BEGIN_INSTR(LoadQmlContext)
d << dumpRegister(result, nFormals);
MOTH_END_INSTR(LoadQmlContext)
@@ -634,6 +719,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st
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 df9182e924..2b1660ee58 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -53,6 +53,8 @@
#include <private/qv4global_p.h>
#include <private/qv4value_p.h>
#include <private/qv4runtime_p.h>
+#include <private/qv4compileddata_p.h> // for CompiledData::CodeOffsetToLine used by the dumper
+#include <qendian.h>
QT_BEGIN_NAMESPACE
@@ -60,6 +62,7 @@ QT_BEGIN_NAMESPACE
op##_INSTRUCTION(name, nargs, __VA_ARGS__)
/* for all jump instructions, the offset has to come last, to simplify the job of the bytecode generator */
+#define INSTR_Nop(op) INSTRUCTION(op, Nop, 0)
#define INSTR_Ret(op) INSTRUCTION(op, Ret, 0)
#define INSTR_Debug(op) INSTRUCTION(op, Debug, 0)
#define INSTR_LoadConst(op) INSTRUCTION(op, LoadConst, 1, index)
@@ -73,6 +76,7 @@ QT_BEGIN_NAMESPACE
#define INSTR_LoadReg(op) INSTRUCTION(op, LoadReg, 1, reg)
#define INSTR_StoreReg(op) INSTRUCTION(op, StoreReg, 1, reg)
#define INSTR_MoveReg(op) INSTRUCTION(op, MoveReg, 2, srcReg, destReg)
+#define INSTR_LoadImport(op) INSTRUCTION(op, LoadImport, 1, index)
#define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 1, index)
#define INSTR_StoreLocal(op) INSTRUCTION(op, StoreLocal, 1, index)
#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 2, scope, index)
@@ -84,21 +88,25 @@ QT_BEGIN_NAMESPACE
#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 1, index)
#define INSTR_StoreNameSloppy(op) INSTRUCTION(op, StoreNameSloppy, 1, name)
#define INSTR_StoreNameStrict(op) INSTRUCTION(op, StoreNameStrict, 1, name)
-#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 2, name, base)
-#define INSTR_LoadPropertyA(op) INSTRUCTION(op, LoadPropertyA, 1, name)
-#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, base)
-#define INSTR_GetLookupA(op) INSTRUCTION(op, GetLookupA, 1, index)
+#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 1, name)
+#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 1, index)
#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)
+#define INSTR_Resume(op) INSTRUCTION(op, Resume, 1, offset)
+#define INSTR_IteratorNextForYieldStar(op) INSTRUCTION(op, IteratorNextForYieldStar, 2, iterator, object)
#define INSTR_StoreProperty(op) INSTRUCTION(op, StoreProperty, 2, name, base)
#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, index)
-#define INSTR_LoadElementA(op) INSTRUCTION(op, LoadElementA, 1, base)
+#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 1, base)
#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 2, base, index)
#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 3, name, argc, argv)
+#define INSTR_CallWithReceiver(op) INSTRUCTION(op, CallWithReceiver, 4, name, thisObject, argc, argv)
#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 4, name, base, argc, argv)
#define INSTR_CallPropertyLookup(op) INSTRUCTION(op, CallPropertyLookup, 4, lookupIndex, base, argc, argv)
#define INSTR_CallElement(op) INSTRUCTION(op, CallElement, 4, base, index, argc, argv)
@@ -107,32 +115,47 @@ QT_BEGIN_NAMESPACE
#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 3, index, argc, argv)
#define INSTR_CallScopeObjectProperty(op) INSTRUCTION(op, CallScopeObjectProperty, 4, name, base, argc, argv)
#define INSTR_CallContextObjectProperty(op) INSTRUCTION(op, CallContextObjectProperty, 4, name, base, argc, argv)
-#define INSTR_SetExceptionHandler(op) INSTRUCTION(op, SetExceptionHandler, 1, offset)
+#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 4, func, thisObject, argc, argv)
+#define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv)
+#define INSTR_ConstructWithSpread(op) INSTRUCTION(op, ConstructWithSpread, 3, func, argc, argv)
+#define INSTR_SetUnwindHandler(op) INSTRUCTION(op, SetUnwindHandler, 1, offset)
+#define INSTR_UnwindDispatch(op) INSTRUCTION(op, UnwindDispatch, 0)
+#define INSTR_UnwindToLabel(op) INSTRUCTION(op, UnwindToLabel, 2, level, offset)
+#define INSTR_DeadTemporalZoneCheck(op) INSTRUCTION(op, DeadTemporalZoneCheck, 1, name)
#define INSTR_ThrowException(op) INSTRUCTION(op, ThrowException, 0)
#define INSTR_GetException(op) INSTRUCTION(op, GetException, 0)
#define INSTR_SetException(op) INSTRUCTION(op, SetException, 0)
#define INSTR_CreateCallContext(op) INSTRUCTION(op, CreateCallContext, 0)
-#define INSTR_PushCatchContext(op) INSTRUCTION(op, PushCatchContext, 2, name, reg)
-#define INSTR_PushWithContext(op) INSTRUCTION(op, PushWithContext, 1, reg)
-#define INSTR_PopContext(op) INSTRUCTION(op, PopContext, 1, reg)
-#define INSTR_ForeachIteratorObject(op) INSTRUCTION(op, ForeachIteratorObject, 0)
-#define INSTR_ForeachNextPropertyName(op) INSTRUCTION(op, ForeachNextPropertyName, 0)
-#define INSTR_DeleteMember(op) INSTRUCTION(op, DeleteMember, 2, member, base)
-#define INSTR_DeleteSubscript(op) INSTRUCTION(op, DeleteSubscript, 2, base, index)
+#define INSTR_PushCatchContext(op) INSTRUCTION(op, PushCatchContext, 2, index, name)
+#define INSTR_PushWithContext(op) INSTRUCTION(op, PushWithContext, 0)
+#define INSTR_PushBlockContext(op) INSTRUCTION(op, PushBlockContext, 1, index)
+#define INSTR_CloneBlockContext(op) INSTRUCTION(op, CloneBlockContext, 0)
+#define INSTR_PushScriptContext(op) INSTRUCTION(op, PushScriptContext, 1, index)
+#define INSTR_PopScriptContext(op) INSTRUCTION(op, PopScriptContext, 0)
+#define INSTR_PopContext(op) INSTRUCTION(op, PopContext, 0)
+#define INSTR_GetIterator(op) INSTRUCTION(op, GetIterator, 1, iterator)
+#define INSTR_IteratorNext(op) INSTRUCTION(op, IteratorNext, 2, value, done)
+#define INSTR_IteratorClose(op) INSTRUCTION(op, IteratorClose, 1, done)
+#define INSTR_DestructureRestElement(op) INSTRUCTION(op, DestructureRestElement, 0)
+#define INSTR_DeleteProperty(op) INSTRUCTION(op, DeleteProperty, 2, base, index)
#define INSTR_DeleteName(op) INSTRUCTION(op, DeleteName, 1, name)
#define INSTR_TypeofName(op) INSTRUCTION(op, TypeofName, 1, name)
#define INSTR_TypeofValue(op) INSTRUCTION(op, TypeofValue, 0)
#define INSTR_DeclareVar(op) INSTRUCTION(op, DeclareVar, 2, varName, isDeletable)
#define INSTR_DefineArray(op) INSTRUCTION(op, DefineArray, 2, argc, args)
-// arrayGetterSetterCountAndFlags contains 30 bits for count, 1 bit for needsSparseArray boolean
-#define INSTR_DefineObjectLiteral(op) INSTRUCTION(op, DefineObjectLiteral, 4, internalClassId, arrayValueCount, arrayGetterSetterCountAndFlags, args)
+#define INSTR_DefineObjectLiteral(op) INSTRUCTION(op, DefineObjectLiteral, 3, internalClassId, argc, args)
+#define INSTR_CreateClass(op) INSTRUCTION(op, CreateClass, 3, classIndex, heritage, computedNames)
#define INSTR_CreateMappedArgumentsObject(op) INSTRUCTION(op, CreateMappedArgumentsObject, 0)
#define INSTR_CreateUnmappedArgumentsObject(op) INSTRUCTION(op, CreateUnmappedArgumentsObject, 0)
+#define INSTR_CreateRestParameter(op) INSTRUCTION(op, CreateRestParameter, 1, argIndex)
#define INSTR_ConvertThisToObject(op) INSTRUCTION(op, ConvertThisToObject, 0)
-#define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv)
+#define INSTR_LoadSuperConstructor(op) INSTRUCTION(op, LoadSuperConstructor, 0)
+#define INSTR_ToObject(op) INSTRUCTION(op, ToObject, 0)
#define INSTR_Jump(op) INSTRUCTION(op, Jump, 1, offset)
#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 1, offset)
#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 1, offset)
+#define INSTR_JumpNotUndefined(op) INSTRUCTION(op, JumpNotUndefined, 1, offset)
+#define INSTR_JumpNoException(op) INSTRUCTION(op, JumpNoException, 1, offset)
#define INSTR_CmpEqNull(op) INSTRUCTION(op, CmpEqNull, 0)
#define INSTR_CmpNeNull(op) INSTRUCTION(op, CmpNeNull, 0)
#define INSTR_CmpEqInt(op) INSTRUCTION(op, CmpEqInt, 1, lhs)
@@ -147,8 +170,6 @@ QT_BEGIN_NAMESPACE
#define INSTR_CmpStrictNotEqual(op) INSTRUCTION(op, CmpStrictNotEqual, 1, lhs)
#define INSTR_CmpIn(op) INSTRUCTION(op, CmpIn, 1, lhs)
#define INSTR_CmpInstanceOf(op) INSTRUCTION(op, CmpInstanceOf, 1, lhs)
-#define INSTR_JumpStrictEqualStackSlotInt(op) INSTRUCTION(op, JumpStrictEqualStackSlotInt, 3, lhs, rhs, offset)
-#define INSTR_JumpStrictNotEqualStackSlotInt(op) INSTRUCTION(op, JumpStrictNotEqualStackSlotInt, 3, lhs, rhs, offset)
#define INSTR_UNot(op) INSTRUCTION(op, UNot, 0)
#define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 0)
#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 0)
@@ -168,17 +189,23 @@ QT_BEGIN_NAMESPACE
#define INSTR_UShrConst(op) INSTRUCTION(op, UShrConst, 1, rhs)
#define INSTR_ShrConst(op) INSTRUCTION(op, ShrConst, 1, rhs)
#define INSTR_ShlConst(op) INSTRUCTION(op, ShlConst, 1, rhs)
+#define INSTR_Exp(op) INSTRUCTION(op, Exp, 1, lhs)
#define INSTR_Mul(op) INSTRUCTION(op, Mul, 1, lhs)
#define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs)
#define INSTR_Mod(op) INSTRUCTION(op, Mod, 1, lhs)
#define INSTR_Sub(op) INSTRUCTION(op, Sub, 1, lhs)
#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)
+#define INSTR_TailCall(op) INSTRUCTION(op, TailCall, 4, func, thisObject, argc, argv)
+#define FOR_EACH_MOTH_INSTR_ALL(F) \
+ F(Nop) \
+ FOR_EACH_MOTH_INSTR(F)
#define FOR_EACH_MOTH_INSTR(F) \
F(Ret) \
- F(Debug) \
F(LoadConst) \
F(LoadZero) \
F(LoadTrue) \
@@ -186,15 +213,16 @@ QT_BEGIN_NAMESPACE
F(LoadNull) \
F(LoadUndefined) \
F(LoadInt) \
+ F(LoadRuntimeString) \
F(MoveConst) \
F(LoadReg) \
F(StoreReg) \
F(MoveReg) \
+ F(LoadImport) \
F(LoadLocal) \
F(StoreLocal) \
F(LoadScopedLocal) \
F(StoreScopedLocal) \
- F(LoadRuntimeString) \
F(MoveRegExp) \
F(LoadClosure) \
F(LoadName) \
@@ -202,53 +230,25 @@ QT_BEGIN_NAMESPACE
F(StoreNameSloppy) \
F(StoreNameStrict) \
F(LoadElement) \
- F(LoadElementA) \
F(StoreElement) \
F(LoadProperty) \
- F(LoadPropertyA) \
F(GetLookup) \
- F(GetLookupA) \
F(StoreProperty) \
F(SetLookup) \
+ F(LoadSuperProperty) \
+ F(StoreSuperProperty) \
F(StoreScopeObjectProperty) \
F(StoreContextObjectProperty) \
F(LoadScopeObjectProperty) \
F(LoadContextObjectProperty) \
F(LoadIdObject) \
- F(CallValue) \
- F(CallProperty) \
- F(CallPropertyLookup) \
- F(CallElement) \
- F(CallName) \
- F(CallPossiblyDirectEval) \
- F(CallGlobalLookup) \
- F(CallScopeObjectProperty) \
- F(CallContextObjectProperty) \
- F(SetExceptionHandler) \
- F(ThrowException) \
- F(GetException) \
- F(SetException) \
- F(CreateCallContext) \
- F(PushCatchContext) \
- F(PushWithContext) \
- F(PopContext) \
- F(ForeachIteratorObject) \
- F(ForeachNextPropertyName) \
- F(DeleteMember) \
- F(DeleteSubscript) \
- F(DeleteName) \
- F(TypeofName) \
- F(TypeofValue) \
- F(DeclareVar) \
- F(DefineArray) \
- F(DefineObjectLiteral) \
- F(CreateMappedArgumentsObject) \
- F(CreateUnmappedArgumentsObject) \
F(ConvertThisToObject) \
- F(Construct) \
+ F(ToObject) \
F(Jump) \
F(JumpTrue) \
F(JumpFalse) \
+ F(JumpNoException) \
+ F(JumpNotUndefined) \
F(CmpEqNull) \
F(CmpNeNull) \
F(CmpEqInt) \
@@ -263,8 +263,6 @@ QT_BEGIN_NAMESPACE
F(CmpStrictNotEqual) \
F(CmpIn) \
F(CmpInstanceOf) \
- F(JumpStrictEqualStackSlotInt) \
- F(JumpStrictNotEqualStackSlotInt) \
F(UNot) \
F(UPlus) \
F(UMinus) \
@@ -284,13 +282,67 @@ QT_BEGIN_NAMESPACE
F(UShrConst) \
F(ShrConst) \
F(ShlConst) \
+ F(Exp) \
F(Mul) \
F(Div) \
F(Mod) \
F(Sub) \
+ F(CallValue) \
+ F(CallWithReceiver) \
+ F(CallProperty) \
+ F(CallPropertyLookup) \
+ F(CallElement) \
+ F(CallName) \
+ F(CallPossiblyDirectEval) \
+ F(CallGlobalLookup) \
+ F(CallScopeObjectProperty) \
+ F(CallContextObjectProperty) \
+ F(CallWithSpread) \
+ F(Construct) \
+ F(ConstructWithSpread) \
+ F(SetUnwindHandler) \
+ F(UnwindDispatch) \
+ F(UnwindToLabel) \
+ F(DeadTemporalZoneCheck) \
+ F(ThrowException) \
+ F(GetException) \
+ F(SetException) \
+ F(CreateCallContext) \
+ F(PushCatchContext) \
+ F(PushWithContext) \
+ F(PushBlockContext) \
+ F(CloneBlockContext) \
+ F(PopContext) \
+ F(GetIterator) \
+ F(IteratorNext) \
+ F(IteratorClose) \
+ F(DestructureRestElement) \
+ F(DeleteProperty) \
+ F(DeleteName) \
+ F(TypeofName) \
+ F(TypeofValue) \
+ F(DeclareVar) \
+ F(DefineArray) \
+ F(DefineObjectLiteral) \
+ F(CreateMappedArgumentsObject) \
+ F(CreateUnmappedArgumentsObject) \
+ F(CreateRestParameter) \
F(LoadQmlContext) \
- F(LoadQmlImportedScripts)
-#define MOTH_NUM_INSTRUCTIONS() (static_cast<int>(Moth::Instr::Type::LoadQmlImportedScripts) + 1)
+ F(LoadQmlImportedScripts) \
+ F(Yield) \
+ F(YieldStar) \
+ F(Resume) \
+ F(IteratorNextForYieldStar) \
+ F(CreateClass) \
+ F(LoadSuperConstructor) \
+ F(PushScriptContext) \
+ F(PopScriptContext) \
+ F(InitializeBlockDeadTemporalZone) \
+ F(ThrowOnNullOrUndefined) \
+ F(TailCall) \
+ F(Debug) \
+
+#define MOTH_NUM_INSTRUCTIONS() (static_cast<int>(Moth::Instr::Type::Debug_Wide) + 1)
#if defined(Q_CC_GNU) && !defined(Q_CC_INTEL)
// icc before version 1200 doesn't support computed goto, and at least up to version 18.0.0 the
@@ -301,7 +353,7 @@ QT_BEGIN_NAMESPACE
#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QV4::Moth::Instr) - 1)
-#define MOTH_INSTR_ENUM(I) I,
+#define MOTH_INSTR_ENUM(I) I, I##_Wide,
#define MOTH_INSTR_SIZE(I) (sizeof(QV4::Moth::Instr::instr_##I))
#define MOTH_EXPAND_FOR_MSVC(x) x
@@ -344,7 +396,7 @@ QT_BEGIN_NAMESPACE
#define MOTH_COLLECT_NARGS(instr) \
INSTR_##instr(MOTH_COLLECT_ARG_COUNT)
#define MOTH_COLLECT_ARG_COUNT_INSTRUCTION(name, nargs, ...) \
- nargs,
+ nargs, nargs,
#define MOTH_DECODE_ARG(arg, type, nargs, offset) \
arg = qFromLittleEndian<type>(qFromUnaligned<type>(reinterpret_cast<const type *>(code) - nargs + offset));
@@ -397,38 +449,53 @@ QT_BEGIN_NAMESPACE
#ifdef MOTH_COMPUTED_GOTO
/* collect jump labels */
#define COLLECT_LABELS(instr) \
- INSTR_##instr(GET_LABEL)
+ INSTR_##instr(GET_LABEL) \
+ INSTR_##instr(GET_LABEL_WIDE)
#define GET_LABEL_INSTRUCTION(name, ...) \
&&op_byte_##name,
-#define COLLECT_LABELS_WIDE(instr) \
- INSTR_##instr(GET_LABEL_WIDE)
#define GET_LABEL_WIDE_INSTRUCTION(name, ...) \
&&op_int_##name,
#define MOTH_JUMP_TABLE \
static const void *jumpTable[] = { \
- FOR_EACH_MOTH_INSTR(COLLECT_LABELS) \
- FOR_EACH_MOTH_INSTR(COLLECT_LABELS_WIDE) \
+ FOR_EACH_MOTH_INSTR_ALL(COLLECT_LABELS) \
};
-#define MOTH_DISPATCH() \
+#define MOTH_DISPATCH_SINGLE() \
goto *jumpTable[*reinterpret_cast<const uchar *>(code)];
+
+#define MOTH_DISPATCH() \
+ MOTH_DISPATCH_SINGLE() \
+ op_byte_Nop: \
+ ++code; \
+ MOTH_DISPATCH_SINGLE() \
+ op_int_Nop: /* wide prefix */ \
+ ++code; \
+ goto *jumpTable[0x100 | *reinterpret_cast<const uchar *>(code)];
#else
#define MOTH_JUMP_TABLE
#define MOTH_INSTR_CASE_AND_JUMP(instr) \
- INSTR_##instr(GET_CASE_AND_JUMP)
-#define GET_CASE_AND_JUMP_INSTRUCTION(name, ...) \
- case static_cast<uchar>(Instr::Type::name): goto op_byte_##name;
-#define MOTH_INSTR_CASE_AND_JUMP_WIDE(instr) \
+ INSTR_##instr(GET_CASE_AND_JUMP) \
INSTR_##instr(GET_CASE_AND_JUMP_WIDE)
+#define GET_CASE_AND_JUMP_INSTRUCTION(name, ...) \
+ case Instr::Type::name: goto op_byte_##name;
#define GET_CASE_AND_JUMP_WIDE_INSTRUCTION(name, ...) \
- case (static_cast<uchar>(Instr::Type::name) + MOTH_NUM_INSTRUCTIONS()): goto op_int_##name;
+ case Instr::Type::name##_Wide: goto op_int_##name;
#define MOTH_DISPATCH() \
- switch (static_cast<uchar>(*code)) { \
+ Instr::Type type = Instr::Type(static_cast<uchar>(*code)); \
+ dispatch: \
+ switch (type) { \
+ case Instr::Type::Nop: \
+ ++code; \
+ type = Instr::Type(static_cast<uchar>(*code)); \
+ goto dispatch; \
+ case Instr::Type::Nop_Wide: /* wide prefix */ \
+ ++code; \
+ type = Instr::Type(0x100 | static_cast<uchar>(*code)); \
+ goto dispatch; \
FOR_EACH_MOTH_INSTR(MOTH_INSTR_CASE_AND_JUMP) \
- FOR_EACH_MOTH_INSTR(MOTH_INSTR_CASE_AND_JUMP_WIDE) \
}
#endif
@@ -471,12 +538,29 @@ inline void dumpBytecode(const QByteArray &bytecode, int nLocals, int nFormals,
union Instr
{
enum class Type {
- FOR_EACH_MOTH_INSTR(MOTH_INSTR_ENUM)
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_INSTR_ENUM)
};
- FOR_EACH_MOTH_INSTR(MOTH_EMIT_STRUCTS)
+ static Type wideInstructionType(Type t) { return Type(int(t) | 1); }
+ static Type narrowInstructionType(Type t) { return Type(int(t) & ~1); }
+ static bool isWide(Type t) { return int(t) & 1; }
+ static bool isNarrow(Type t) { return !(int(t) & 1); }
+ static int encodedLength(Type t) { return int(t) >= 256 ? 2 : 1; }
+
+ static Type unpack(const uchar *c) { if (c[0] == 0x1) return Type(0x100 + c[1]); return Type(c[0]); }
+ static uchar *pack(uchar *c, Type t) {
+ if (uint(t) >= 256) {
+ c[0] = 0x1;
+ c[1] = uint(t) &0xff;
+ return c + 2;
+ }
+ c[0] = uchar(uint(t));
+ return c + 1;
+ }
+
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_EMIT_STRUCTS)
- FOR_EACH_MOTH_INSTR(MOTH_EMIT_INSTR_MEMBERS)
+ FOR_EACH_MOTH_INSTR_ALL(MOTH_EMIT_INSTR_MEMBERS)
int argumentsAsInts[4];
};
@@ -487,8 +571,6 @@ struct InstrInfo
static int size(Instr::Type type);
};
-Q_STATIC_ASSERT(MOTH_NUM_INSTRUCTIONS() < 128);
-
template<int N>
struct InstrMeta {
};
@@ -506,7 +588,7 @@ QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
reinterpret_cast<const char *>(&v), \
Size); } \
};
-FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE);
+FOR_EACH_MOTH_INSTR_ALL(MOTH_INSTR_META_TEMPLATE);
#undef MOTH_INSTR_META_TEMPLATE
QT_WARNING_POP
@@ -517,7 +599,7 @@ class InstrData : public InstrMeta<InstrType>::DataType
struct Instruction {
#define MOTH_INSTR_DATA_TYPEDEF(I) typedef InstrData<int(Instr::Type::I)> I;
-FOR_EACH_MOTH_INSTR(MOTH_INSTR_DATA_TYPEDEF)
+FOR_EACH_MOTH_INSTR_ALL(MOTH_INSTR_DATA_TYPEDEF)
#undef MOTH_INSTR_DATA_TYPEDEF
private:
Instruction();
diff --git a/src/qml/compiler/qv4jssimplifier.cpp b/src/qml/compiler/qv4jssimplifier.cpp
deleted file mode 100644
index 7d09218fe6..0000000000
--- a/src/qml/compiler/qv4jssimplifier.cpp
+++ /dev/null
@@ -1,384 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qv4jssimplifier_p.h"
-
-QT_BEGIN_NAMESPACE
-
-QQmlJavaScriptBindingExpressionSimplificationPass::QQmlJavaScriptBindingExpressionSimplificationPass(const QVector<QmlIR::Object*> &qmlObjects, QV4::IR::Module *jsModule, QV4::Compiler::JSUnitGenerator *unitGenerator)
- : qmlObjects(qmlObjects)
- , jsModule(jsModule)
- , unitGenerator(unitGenerator)
-{
-
-}
-
-void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings()
-{
- for (int i = 0; i < qmlObjects.count(); ++i)
- reduceTranslationBindings(i);
- if (!irFunctionsToRemove.isEmpty()) {
- QQmlIRFunctionCleanser cleanser(jsModule, qmlObjects, irFunctionsToRemove);
- cleanser.clean();
- }
-}
-
-void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings(int objectIndex)
-{
- const QmlIR::Object *obj = qmlObjects.at(objectIndex);
-
- for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) {
- if (binding->type != QV4::CompiledData::Binding::Type_Script)
- continue;
-
- const int irFunctionIndex = obj->runtimeFunctionIndices.at(binding->value.compiledScriptIndex);
- QV4::IR::Function *irFunction = jsModule->functions.at(irFunctionIndex);
- if (simplifyBinding(irFunction, binding)) {
- irFunctionsToRemove.append(irFunctionIndex);
- jsModule->functions[irFunctionIndex] = 0;
- delete irFunction;
- }
- }
-}
-
-void QQmlJavaScriptBindingExpressionSimplificationPass::visitMove(QV4::IR::Move *move)
-{
- QV4::IR::Temp *target = move->target->asTemp();
- if (!target || target->kind != QV4::IR::Temp::VirtualRegister) {
- discard();
- return;
- }
-
- if (QV4::IR::Call *call = move->source->asCall()) {
- if (QV4::IR::Name *n = call->base->asName()) {
- if (n->builtin == QV4::IR::Name::builtin_invalid) {
- visitFunctionCall(n->id, call->args, target);
- return;
- }
- }
- discard();
- return;
- }
-
- if (QV4::IR::Name *n = move->source->asName()) {
- if (n->builtin == QV4::IR::Name::builtin_qml_context
- || n->builtin == QV4::IR::Name::builtin_qml_imported_scripts_object) {
- // these are free of side-effects
- return;
- }
- discard();
- return;
- }
-
- if (!move->source->asTemp() && !move->source->asString() && !move->source->asConst()) {
- discard();
- return;
- }
-
- _temps[target->index] = move->source;
-}
-
-void QQmlJavaScriptBindingExpressionSimplificationPass::visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target)
-{
- // more than one function call?
- if (_nameOfFunctionCalled) {
- discard();
- return;
- }
-
- _nameOfFunctionCalled = name;
-
- _functionParameters.clear();
- while (args) {
- int slot;
- if (QV4::IR::Temp *param = args->expr->asTemp()) {
- if (param->kind != QV4::IR::Temp::VirtualRegister) {
- discard();
- return;
- }
- slot = param->index;
- _functionParameters.append(slot);
- } else if (QV4::IR::Const *param = args->expr->asConst()) {
- slot = --_synthesizedConsts;
- Q_ASSERT(!_temps.contains(slot));
- _temps[slot] = param;
- _functionParameters.append(slot);
- }
- args = args->next;
- }
-
- _functionCallReturnValue = target->index;
-}
-
-void QQmlJavaScriptBindingExpressionSimplificationPass::visitRet(QV4::IR::Ret *ret)
-{
- // nothing initialized earlier?
- if (_returnValueOfBindingExpression != -1) {
- discard();
- return;
- }
- QV4::IR::Temp *target = ret->expr->asTemp();
- if (!target || target->kind != QV4::IR::Temp::VirtualRegister) {
- discard();
- return;
- }
- _returnValueOfBindingExpression = target->index;
-}
-
-bool QQmlJavaScriptBindingExpressionSimplificationPass::simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding)
-{
- _canSimplify = true;
- _nameOfFunctionCalled = 0;
- _functionParameters.clear();
- _functionCallReturnValue = -1;
- _temps.clear();
- _returnValueOfBindingExpression = -1;
- _synthesizedConsts = 0;
-
- // It would seem unlikely that function with some many basic blocks (after optimization)
- // consists merely of a qsTr call or a constant value return ;-)
- if (function->basicBlockCount() > 10)
- return false;
-
- for (QV4::IR::BasicBlock *bb : function->basicBlocks()) {
- for (QV4::IR::Stmt *s : bb->statements()) {
- visit(s);
- if (!_canSimplify)
- return false;
- }
- }
-
- if (_returnValueOfBindingExpression == -1)
- return false;
-
- if (_nameOfFunctionCalled) {
- if (_functionCallReturnValue != _returnValueOfBindingExpression)
- return false;
- return detectTranslationCallAndConvertBinding(binding);
- }
-
- return false;
-}
-
-bool QQmlJavaScriptBindingExpressionSimplificationPass::detectTranslationCallAndConvertBinding(QmlIR::Binding *binding)
-{
- if (*_nameOfFunctionCalled == QLatin1String("qsTr")) {
- QString translation;
- QV4::CompiledData::TranslationData translationData;
- translationData.number = -1;
- translationData.commentIndex = 0; // empty string
-
- QVector<int>::ConstIterator param = _functionParameters.constBegin();
- QVector<int>::ConstIterator end = _functionParameters.constEnd();
- if (param == end)
- return false;
-
- QV4::IR::String *stringParam = _temps[*param]->asString();
- if (!stringParam)
- return false;
-
- translation = *stringParam->value;
-
- ++param;
- if (param != end) {
- stringParam = _temps[*param]->asString();
- if (!stringParam)
- return false;
- translationData.commentIndex = unitGenerator->registerString(*stringParam->value);
- ++param;
-
- if (param != end) {
- QV4::IR::Const *constParam = _temps[*param]->asConst();
- if (!constParam || constParam->type != QV4::IR::SInt32Type)
- return false;
-
- translationData.number = int(constParam->value);
- ++param;
- }
- }
-
- if (param != end)
- return false;
-
- binding->type = QV4::CompiledData::Binding::Type_Translation;
- binding->stringIndex = unitGenerator->registerString(translation);
- binding->value.translationData = translationData;
- return true;
- } else if (*_nameOfFunctionCalled == QLatin1String("qsTrId")) {
- QString id;
- QV4::CompiledData::TranslationData translationData;
- translationData.number = -1;
- translationData.commentIndex = 0; // empty string, but unused
-
- QVector<int>::ConstIterator param = _functionParameters.constBegin();
- QVector<int>::ConstIterator end = _functionParameters.constEnd();
- if (param == end)
- return false;
-
- QV4::IR::String *stringParam = _temps[*param]->asString();
- if (!stringParam)
- return false;
-
- id = *stringParam->value;
-
- ++param;
- if (param != end) {
- QV4::IR::Const *constParam = _temps[*param]->asConst();
- if (!constParam || constParam->type != QV4::IR::SInt32Type)
- return false;
-
- translationData.number = int(constParam->value);
- ++param;
- }
-
- if (param != end)
- return false;
-
- binding->type = QV4::CompiledData::Binding::Type_TranslationById;
- binding->stringIndex = unitGenerator->registerString(id);
- binding->value.translationData = translationData;
- return true;
- } else if (*_nameOfFunctionCalled == QLatin1String("QT_TR_NOOP") || *_nameOfFunctionCalled == QLatin1String("QT_TRID_NOOP")) {
- QVector<int>::ConstIterator param = _functionParameters.constBegin();
- QVector<int>::ConstIterator end = _functionParameters.constEnd();
- if (param == end)
- return false;
-
- QV4::IR::String *stringParam = _temps[*param]->asString();
- if (!stringParam)
- return false;
-
- ++param;
- if (param != end)
- return false;
-
- binding->type = QV4::CompiledData::Binding::Type_String;
- binding->stringIndex = unitGenerator->registerString(*stringParam->value);
- return true;
- } else if (*_nameOfFunctionCalled == QLatin1String("QT_TRANSLATE_NOOP")) {
- QVector<int>::ConstIterator param = _functionParameters.constBegin();
- QVector<int>::ConstIterator end = _functionParameters.constEnd();
- if (param == end)
- return false;
-
- ++param;
- if (param == end)
- return false;
-
- QV4::IR::String *stringParam = _temps[*param]->asString();
- if (!stringParam)
- return false;
-
- ++param;
- if (param != end)
- return false;
-
- binding->type = QV4::CompiledData::Binding::Type_String;
- binding->stringIndex = unitGenerator->registerString(*stringParam->value);
- return true;
- }
- return false;
-}
-
-QQmlIRFunctionCleanser::QQmlIRFunctionCleanser(QV4::IR::Module *module, const QVector<QmlIR::Object *> &qmlObjects, const QVector<int> &functionsToRemove)
- : module(module)
- , qmlObjects(qmlObjects)
- , functionsToRemove(functionsToRemove)
-{
-}
-
-void QQmlIRFunctionCleanser::clean()
-{
- QVector<QV4::IR::Function*> newFunctions;
- newFunctions.reserve(module->functions.count() - functionsToRemove.count());
-
- newFunctionIndices.resize(module->functions.count());
-
- for (int i = 0; i < module->functions.count(); ++i) {
- QV4::IR::Function *f = module->functions.at(i);
- Q_ASSERT(f || functionsToRemove.contains(i));
- if (f) {
- newFunctionIndices[i] = newFunctions.count();
- newFunctions << f;
- }
- }
-
- module->functions = newFunctions;
-
- for (QV4::IR::Function *function : qAsConst(module->functions)) {
- for (QV4::IR::BasicBlock *block : function->basicBlocks()) {
- for (QV4::IR::Stmt *s : block->statements()) {
- visit(s);
- }
- }
- }
-
- for (QmlIR::Object *obj : qmlObjects) {
- for (int i = 0; i < obj->runtimeFunctionIndices.count; ++i)
- obj->runtimeFunctionIndices[i] = newFunctionIndices[obj->runtimeFunctionIndices.at(i)];
- }
-}
-
-void QQmlIRFunctionCleanser::visit(QV4::IR::Stmt *s)
-{
-
- switch (s->stmtKind) {
- case QV4::IR::Stmt::PhiStmt:
- // nothing to do
- break;
- default:
- STMT_VISIT_ALL_KINDS(s);
- break;
- }
-}
-
-void QQmlIRFunctionCleanser::visit(QV4::IR::Expr *e)
-{
- switch (e->exprKind) {
- case QV4::IR::Expr::ClosureExpr: {
- auto closure = e->asClosure();
- closure->value = newFunctionIndices.at(closure->value);
- } break;
- default:
- EXPR_VISIT_ALL_KINDS(e);
- break;
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/qml/compiler/qv4jssimplifier_p.h b/src/qml/compiler/qv4jssimplifier_p.h
deleted file mode 100644
index ae8d74135c..0000000000
--- a/src/qml/compiler/qv4jssimplifier_p.h
+++ /dev/null
@@ -1,154 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the tools applications of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#ifndef QV4JSSIMPLIFIER
-#define QV4JSSIMPLIFIER
-
-//
-// 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 "qqmlirbuilder_p.h"
-
-QT_BEGIN_NAMESPACE
-
-namespace QmlIR {
-struct Document;
-}
-
-namespace QV4 {
-namespace CompiledData {
-struct QmlUnit;
-struct Location;
-}
-}
-
-class QQmlJavaScriptBindingExpressionSimplificationPass
-{
-public:
- QQmlJavaScriptBindingExpressionSimplificationPass(const QVector<QmlIR::Object*> &qmlObjects, QV4::IR::Module *jsModule, QV4::Compiler::JSUnitGenerator *unitGenerator);
-
- void reduceTranslationBindings();
-
-private:
- void reduceTranslationBindings(int objectIndex);
-
- void visit(QV4::IR::Stmt *s)
- {
- switch (s->stmtKind) {
- case QV4::IR::Stmt::MoveStmt:
- visitMove(s->asMove());
- break;
- case QV4::IR::Stmt::RetStmt:
- visitRet(s->asRet());
- break;
- case QV4::IR::Stmt::CJumpStmt:
- discard();
- break;
- case QV4::IR::Stmt::ExpStmt:
- discard();
- break;
- case QV4::IR::Stmt::JumpStmt:
- break;
- case QV4::IR::Stmt::PhiStmt:
- break;
- }
- }
-
- void visitMove(QV4::IR::Move *move);
- void visitRet(QV4::IR::Ret *ret);
-
- void visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target);
-
- void discard() { _canSimplify = false; }
-
- bool simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding);
- bool detectTranslationCallAndConvertBinding(QmlIR::Binding *binding);
-
- const QVector<QmlIR::Object*> &qmlObjects;
- QV4::IR::Module *jsModule;
- QV4::Compiler::JSUnitGenerator *unitGenerator;
-
- bool _canSimplify;
- const QString *_nameOfFunctionCalled;
- QVector<int> _functionParameters;
- int _functionCallReturnValue;
-
- QHash<int, QV4::IR::Expr*> _temps;
- int _returnValueOfBindingExpression;
- int _synthesizedConsts;
-
- QVector<int> irFunctionsToRemove;
-};
-
-class QQmlIRFunctionCleanser
-{
-public:
- QQmlIRFunctionCleanser(QV4::IR::Module *module, const QVector<QmlIR::Object*> &qmlObjects, const QVector<int> &functionsToRemove);
-
- void clean();
-
-private:
- virtual void visitMove(QV4::IR::Move *s) {
- visit(s->source);
- visit(s->target);
- }
-
- void visit(QV4::IR::Stmt *s);
- void visit(QV4::IR::Expr *e);
-
-private:
- QV4::IR::Module *module;
- const QVector<QmlIR::Object*> &qmlObjects;
- const QVector<int> &functionsToRemove;
-
- QVector<int> newFunctionIndices;
-};
-
-QT_END_NAMESPACE
-
-#endif // QV4JSSIMPLIFIER
diff --git a/src/qml/configure.json b/src/qml/configure.json
index 681cecea99..9d30dd7be2 100644
--- a/src/qml/configure.json
+++ b/src/qml/configure.json
@@ -32,19 +32,77 @@
"section": "QML",
"condition": [
"features.commandlineparser",
- "features.localserver",
- "features.process",
"features.qml-debug",
- "features.qml-network",
+ "features.qml-network && features.localserver",
"features.xmlstreamwriter"
],
"output": [ "privateFeature" ]
},
+ "qml-preview": {
+ "label": "Command line QML Preview tool",
+ "purpose": "Updates QML documents in your application live as you change them on disk",
+ "section": "QML",
+ "condition": [
+ "features.commandlineparser",
+ "features.qml-network && features.localserver",
+ "features.process",
+ "features.qml-debug"
+ ],
+ "output": [ "privateFeature" ]
+ },
+ "qml-devtools": {
+ "label": "QML Development Tools",
+ "purpose": "Provides the QmlDevtools library and various utilities.",
+ "section": "QML",
+ "output": [ "privateFeature" ]
+ },
+ "qml-sequence-object": {
+ "label": "QML sequence object",
+ "purpose": "Supports mapping sequence types into QML.",
+ "section": "QML",
+ "output": [ "privateFeature" ]
+ },
+ "qml-list-model": {
+ "label": "QML list model",
+ "purpose": "Provides the ListModel QML type.",
+ "section": "QML",
+ "output": [ "privateFeature" ]
+ },
+ "qml-xml-http-request": {
+ "label": "QML XML http request",
+ "purpose": "Provides support for sending XML http requests.",
+ "section": "QML",
+ "condition": [
+ "features.xmlstreamreader",
+ "features.qml-network"
+ ],
+ "output": [ "privateFeature" ]
+ },
+ "qml-locale": {
+ "label": "QML Locale",
+ "purpose": "Provides support for locales in QML.",
+ "section": "QML",
+ "output": [ "privateFeature" ]
+ },
+ "qml-animation": {
+ "label": "QML Animations",
+ "purpose": "Provides support for animations and timers in QML.",
+ "section": "QML",
+ "condition": "features.animation",
+ "output": [ "privateFeature" ]
+ },
"qml-delegate-model": {
"label": "QML delegate model",
"purpose": "Provides the DelegateModel QML type.",
"section": "QML",
"output": [ "privateFeature" ]
+ },
+ "qml-worker-script": {
+ "label": "QML WorkerScript",
+ "purpose": "Enables the use of threads in QML.",
+ "section": "QML",
+ "condition": "features.thread",
+ "output": [ "privateFeature" ]
}
},
@@ -54,6 +112,10 @@
"entries": [
"qml-network",
"qml-debug",
+ "qml-sequence-object",
+ "qml-list-model",
+ "qml-xml-http-request",
+ "qml-locale",
"qml-delegate-model"
]
}
diff --git a/src/qml/debugger/qqmlabstractprofileradapter_p.h b/src/qml/debugger/qqmlabstractprofileradapter_p.h
index c63e694c7e..f39f8fccd2 100644
--- a/src/qml/debugger/qqmlabstractprofileradapter_p.h
+++ b/src/qml/debugger/qqmlabstractprofileradapter_p.h
@@ -73,13 +73,13 @@ public:
~QQmlAbstractProfilerAdapter() override {}
void setService(QQmlProfilerService *new_service) { service = new_service; }
- virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages, bool trackLocations) = 0;
+ virtual qint64 sendMessages(qint64 until, QList<QByteArray> &messages) = 0;
void startProfiling(quint64 features);
void stopProfiling();
- void reportData(bool trackLocations) { emit dataRequested(trackLocations); }
+ void reportData() { emit dataRequested(); }
void stopWaiting() { waiting = false; }
void startWaiting() { waiting = true; }
@@ -96,7 +96,7 @@ signals:
void profilingDisabled();
void profilingDisabledWhileWaiting();
- void dataRequested(bool trackLocations);
+ void dataRequested();
void referenceTimeKnown(const QElapsedTimer &timer);
protected:
diff --git a/src/qml/debugger/qqmldebugconnector.cpp b/src/qml/debugger/qqmldebugconnector.cpp
index d9f51ce09f..0ef40d6911 100644
--- a/src/qml/debugger/qqmldebugconnector.cpp
+++ b/src/qml/debugger/qqmldebugconnector.cpp
@@ -82,7 +82,7 @@ Q_GLOBAL_STATIC(QQmlDebugConnectorParams, qmlDebugConnectorParams)
void QQmlDebugConnector::setPluginKey(const QString &key)
{
QQmlDebugConnectorParams *params = qmlDebugConnectorParams();
- if (params) {
+ if (params && params->pluginKey != key) {
if (params->instance)
qWarning() << "QML debugger: Cannot set plugin key after loading the plugin.";
else
diff --git a/src/qml/debugger/qqmlprofiler.cpp b/src/qml/debugger/qqmlprofiler.cpp
index 8c0bd73822..da0b14dd85 100644
--- a/src/qml/debugger/qqmlprofiler.cpp
+++ b/src/qml/debugger/qqmlprofiler.cpp
@@ -59,19 +59,18 @@ void QQmlProfiler::startProfiling(quint64 features)
void QQmlProfiler::stopProfiling()
{
featuresEnabled = false;
- reportData(true);
+ reportData();
m_locations.clear();
}
-void QQmlProfiler::reportData(bool trackLocations)
+void QQmlProfiler::reportData()
{
LocationHash resolved;
resolved.reserve(m_locations.size());
for (auto it = m_locations.begin(), end = m_locations.end(); it != end; ++it) {
- if (!trackLocations || !it->sent) {
+ if (!it->sent) {
resolved.insert(it.key(), it.value());
- if (trackLocations)
- it->sent = true;
+ it->sent = true;
}
}
diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h
index 287c53ea05..d01e2bc429 100644
--- a/src/qml/debugger/qqmlprofiler_p.h
+++ b/src/qml/debugger/qqmlprofiler_p.h
@@ -383,7 +383,7 @@ public:
void startProfiling(quint64 features);
void stopProfiling();
- void reportData(bool trackLocations);
+ void reportData();
void setTimer(const QElapsedTimer &timer) { m_timer = timer; }
signals:
diff --git a/src/qml/doc/images/cpp-qml-integration-flowchart.odg b/src/qml/doc/images/cpp-qml-integration-flowchart.odg
index 8482bd4bf4..f24021635e 100644
--- a/src/qml/doc/images/cpp-qml-integration-flowchart.odg
+++ b/src/qml/doc/images/cpp-qml-integration-flowchart.odg
Binary files differ
diff --git a/src/qml/doc/images/cpp-qml-integration-flowchart.png b/src/qml/doc/images/cpp-qml-integration-flowchart.png
index 56a33205fd..3649ff9e41 100644
--- a/src/qml/doc/images/cpp-qml-integration-flowchart.png
+++ b/src/qml/doc/images/cpp-qml-integration-flowchart.png
Binary files differ
diff --git a/src/qml/doc/images/visualitemmodel.png b/src/qml/doc/images/objectmodel.png
index 5e6d1325b2..5e6d1325b2 100644
--- a/src/qml/doc/images/visualitemmodel.png
+++ b/src/qml/doc/images/objectmodel.png
Binary files differ
diff --git a/src/qml/doc/snippets/delegatemodel/visualdatamodel.qml b/src/qml/doc/snippets/delegatemodel/delegatemodel.qml
index 1a7baa6b1e..1a7baa6b1e 100644
--- a/src/qml/doc/snippets/delegatemodel/visualdatamodel.qml
+++ b/src/qml/doc/snippets/delegatemodel/delegatemodel.qml
diff --git a/src/qml/doc/snippets/delegatemodel/visualdatamodel_rootindex/main.cpp b/src/qml/doc/snippets/delegatemodel/delegatemodel_rootindex/main.cpp
index a56eb69616..a56eb69616 100644
--- a/src/qml/doc/snippets/delegatemodel/visualdatamodel_rootindex/main.cpp
+++ b/src/qml/doc/snippets/delegatemodel/delegatemodel_rootindex/main.cpp
diff --git a/src/qml/doc/snippets/delegatemodel/visualdatamodel_rootindex/view.qml b/src/qml/doc/snippets/delegatemodel/delegatemodel_rootindex/view.qml
index 2e17eed8f0..2e17eed8f0 100644
--- a/src/qml/doc/snippets/delegatemodel/visualdatamodel_rootindex/view.qml
+++ b/src/qml/doc/snippets/delegatemodel/delegatemodel_rootindex/view.qml
diff --git a/src/qml/doc/snippets/delegatemodel/visualdatagroup.qml b/src/qml/doc/snippets/delegatemodel/delegatemodelgroup.qml
index 8562deeeda..8562deeeda 100644
--- a/src/qml/doc/snippets/delegatemodel/visualdatagroup.qml
+++ b/src/qml/doc/snippets/delegatemodel/delegatemodelgroup.qml
diff --git a/src/qml/doc/snippets/qml/workerscript/script.js b/src/qml/doc/snippets/qml/workerscript/script.mjs
index f55dee3507..f55dee3507 100644
--- a/src/qml/doc/snippets/qml/workerscript/script.js
+++ b/src/qml/doc/snippets/qml/workerscript/script.mjs
diff --git a/src/qml/doc/snippets/qml/workerscript/workerscript.qml b/src/qml/doc/snippets/qml/workerscript/workerscript.qml
index e3fe3c92e6..cc637d34cf 100644
--- a/src/qml/doc/snippets/qml/workerscript/workerscript.qml
+++ b/src/qml/doc/snippets/qml/workerscript/workerscript.qml
@@ -61,7 +61,7 @@ Rectangle {
WorkerScript {
id: myWorker
- source: "script.js"
+ source: "script.mjs"
onMessage: myText.text = messageObject.reply
}
diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc
index 8a606d672a..6159ffe20b 100644
--- a/src/qml/doc/src/cppintegration/data.qdoc
+++ b/src/qml/doc/src/cppintegration/data.qdoc
@@ -262,8 +262,8 @@ method.
\section2 Sequence Type to JavaScript Array
-Certain C++ sequence types are supported transparently in QML as JavaScript
-\c Array types.
+Certain C++ sequence types are supported transparently in QML to behave like
+JavaScript \c Array types.
In particular, QML currently supports:
\list
@@ -420,6 +420,47 @@ To use an enum as a \l {QFlags}{flags} type in QML, see \l Q_FLAG().
\note The names of enum values must begin with a capital letter in order to
be accessible from QML.
+\code
+...
+enum class Status {
+ Ready,
+ Loading,
+ Error
+}
+Q_ENUM(Status)
+...
+\endcode
+
+Enum classes are registered in QML as scoped and unscoped properties.
+The \c Ready value will be registered at \c Message.Status.Ready and \c Message.Ready .
+
+When using enum classes, there can be multiple enums using the same identifiers.
+The unscoped registration will be overwriten by the last registered enum. For classes
+that contain such name conficts it is possible to disable the unscoped registration by
+annotating your class with a special Q_CLASSINFO macro.
+Use the name \c RegisterEnumClassesUnscoped with the value \c false to prevent scoped
+enums from being merged into the same name space.
+
+\code
+class Message : public QObject
+ {
+ Q_OBJECT
+ Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
+ Q_ENUM(ScopedEnum)
+ Q_ENUM(OtherValue)
+
+ public:
+ enum class ScopedEnum {
+ Value1,
+ Value2,
+ OtherValue
+ };
+ enum class OtherValue {
+ Value1,
+ Value2
+ };
+ };
+\endcode
\section2 Enumeration Types as Signal and Method Parameters
diff --git a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc
index aefdfd9401..52534b4a62 100644
--- a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc
+++ b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc
@@ -255,9 +255,6 @@ type, and so cannot provide the necessary QML property characteristics
through the Qt meta object system, such as signal notifications when a list
is modified.
-QQmlListProperty is a template class that can be conveniently constructed from
-a QList value.
-
For example, the \c MessageBoard class below has a \c messages property of
type QQmlListProperty that stores a list of \c Message instances:
diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc
index 55ca040af6..62c0f5d81b 100644
--- a/src/qml/doc/src/qmlfunctions.qdoc
+++ b/src/qml/doc/src/qmlfunctions.qdoc
@@ -538,30 +538,6 @@
}
\endqml
- Since singleton types do not have an associated QQmlContext object, then within the functions of a QObject-derived
- type that is registered as a singleton type implementation the QML context and engine information is not available.
- The QQmlEngine::contextForObject() function returns NULL when supplied with a pointer to an QObject that
- implements a singleton type.
-
- Extending the above example:
-
- \code
- class SingletonTypeExample : public QObject
- {
- ...
-
- Q_INVOKABLE void doSomethingElse()
- {
- // QML Engine/Context information is not accessible here:
- Q_ASSERT(QQmlEngine::contextForObject(this) == 0);
- Q_ASSERT(qmlContext(this) == 0);
- Q_ASSERT(qmlEngine(this) == 0);
- }
-
- ...
- }
- \endcode
-
\sa {Choosing the Correct Integration Method Between C++ and QML}
*/
@@ -662,3 +638,25 @@
are registered for that version. This is particularly useful for keeping the
versions of related modules in sync.
*/
+
+/*!
+ \since 5.12
+ \fn int qmlTypeId(const char* uri, int versionMajor, int versionMinor, const char *qmlName);
+ \relates QQmlEngine
+
+ Returns the QML type id of a type that was registered with the
+ name \a qmlName in a particular \a uri and a version specified in \a
+ versionMajor and \a versionMinor.
+
+ This function returns the same value as the QML type registration functions
+ such as qmlRegisterType() and qmlRegisterSingletonType().
+
+ If \a qmlName, \a uri and \a versionMajor match a registered type, but the
+ specified minor version in \a versionMinor is higher, then the id of the type
+ with the closest minor version is returned.
+
+ Returns -1 if no matching type was found or one of the given parameters
+ was invalid.
+
+ \sa qmlRegisterType(), qmlRegisterSingletonType()
+*/
diff --git a/src/qml/doc/src/qmltypereference.qdoc b/src/qml/doc/src/qmltypereference.qdoc
index 960ea116c9..79bfdc7042 100644
--- a/src/qml/doc/src/qmltypereference.qdoc
+++ b/src/qml/doc/src/qmltypereference.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
-\qmlmodule QtQml 2.11
+\qmlmodule QtQml 2.\QtMinorVersion
\title Qt QML QML Types
\ingroup qmlmodules
\brief List of QML types provided by the Qt QML module
@@ -43,19 +43,19 @@ The types provided by the \c QtQml module are only available in a QML document
if that document imports the \c QtQml namespace (or if the document imports the
\c QtQuick namespace, as noted below).
-The current version of the \c QtQml module is version 2.11, and thus it may be
-imported via the following statement:
+The current version of the \c QtQml module is version 2.\QtMinorVersion, and
+thus it may be imported via the following statement:
-\qml
-import QtQml 2.11
+\qml \QtMinorVersion
+import QtQml 2.\1
\endqml
Most clients will never need to use the \c QtQml import, as all of the types
are also provided by the \c QtQuick namespace which may be imported as
follows:
-\qml
-import QtQuick 2.11
+\qml \QtMinorVersion
+import QtQuick 2.\1
\endqml
See the \l{Qt Quick} module documentation for more information about the \c
diff --git a/src/qml/doc/src/qtqml.qdoc b/src/qml/doc/src/qtqml.qdoc
index b4bc9a0774..68d2b66950 100644
--- a/src/qml/doc/src/qtqml.qdoc
+++ b/src/qml/doc/src/qtqml.qdoc
@@ -55,11 +55,12 @@ following directive:
#include <QtQml>
\endcode
-The QML types in Qt QML are available through the \c QtQML import. To use the
+The QML types in Qt QML are available through the \c QtQml import. To use the
types, add the following import statement to your .qml file:
-\code
-import QtQml 2.11
-\endcode
+
+\qml \QtMinorVersion
+import QtQml 2.\1
+\endqml
To link against the module, add this line to your \l qmake \c
diff --git a/src/qml/doc/src/statemachine.qdoc b/src/qml/doc/src/statemachine.qdoc
index 59170a223f..6986f1baa0 100644
--- a/src/qml/doc/src/statemachine.qdoc
+++ b/src/qml/doc/src/statemachine.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
- \qmlmodule QtQml.StateMachine 1.11
+ \qmlmodule QtQml.StateMachine 1.\QtMinorVersion
\title Declarative State Machine QML Types
\brief Provides QML types to create and execute state graphs.
@@ -68,9 +68,9 @@
\e{QtQml.StateMachine} \e{last}. This way, the \e{State} type is provided
by the Declarative State Machine Framework and not by \l{QtQuick}:
- \qml
- import QtQuick 2.0
- import QtQml.StateMachine 1.0
+ \qml \QtMinorVersion
+ import QtQuick 2.\1
+ import QtQml.StateMachine 1.\1
StateMachine {
State {
@@ -82,9 +82,9 @@
Alternatively, you can import \e{QtQml.StateMachine} into a separate
namespace to avoid any ambiguity with QtQuick's \e{State} item:
- \qml
- import QtQuick 2.0
- import QtQml.StateMachine 1.0 as DSM
+ \qml \QtMinorVersion
+ import QtQuick 2.\1
+ import QtQml.StateMachine 1.\1 as DSM
DSM.StateMachine {
DSM.State {
diff --git a/src/qml/jit/jit.pri b/src/qml/jit/jit.pri
index 2080844d93..2c664af188 100644
--- a/src/qml/jit/jit.pri
+++ b/src/qml/jit/jit.pri
@@ -2,9 +2,13 @@ INCLUDEPATH += $$PWD
INCLUDEPATH += $$OUT_PWD
SOURCES += \
- $$PWD/qv4jit.cpp \
- $$PWD/qv4assembler.cpp
+ $$PWD/qv4jithelpers.cpp \
+ $$PWD/qv4baselinejit.cpp \
+ $$PWD/qv4baselineassembler.cpp \
+ $$PWD/qv4assemblercommon.cpp
HEADERS += \
- $$PWD/qv4jit_p.h \
- $$PWD/qv4assembler_p.h
+ $$PWD/qv4jithelpers_p.h \
+ $$PWD/qv4baselinejit_p.h \
+ $$PWD/qv4baselineassembler_p.h \
+ $$PWD/qv4assemblercommon_p.h
diff --git a/src/qml/jit/qv4assemblercommon.cpp b/src/qml/jit/qv4assemblercommon.cpp
new file mode 100644
index 0000000000..fadb9dce08
--- /dev/null
+++ b/src/qml/jit/qv4assemblercommon.cpp
@@ -0,0 +1,386 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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 <QBuffer>
+#include <QFile>
+
+#include "qv4engine_p.h"
+#include "qv4assemblercommon_p.h"
+#include <private/qv4function_p.h>
+#include <private/qv4runtime_p.h>
+
+#include <assembler/MacroAssemblerCodeRef.h>
+#include <assembler/LinkBuffer.h>
+#include <WTFStubs.h>
+
+#undef ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES
+
+#ifdef V4_ENABLE_JIT
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace JIT {
+
+namespace {
+class QIODevicePrintStream: public FilePrintStream
+{
+ Q_DISABLE_COPY(QIODevicePrintStream)
+
+public:
+ explicit QIODevicePrintStream(QIODevice *dest)
+ : FilePrintStream(nullptr)
+ , dest(dest)
+ , buf(4096, '0')
+ {
+ Q_ASSERT(dest);
+ }
+
+ ~QIODevicePrintStream()
+ {}
+
+ void vprintf(const char* format, va_list argList) WTF_ATTRIBUTE_PRINTF(2, 0)
+ {
+ const int written = qvsnprintf(buf.data(), buf.size(), format, argList);
+ if (written > 0)
+ dest->write(buf.constData(), written);
+ memset(buf.data(), 0, qMin(written, buf.size()));
+ }
+
+ void flush()
+ {}
+
+private:
+ QIODevice *dest;
+ QByteArray buf;
+};
+} // anonymous namespace
+
+static void printDisassembledOutputWithCalls(QByteArray processedOutput,
+ const QHash<const void*, const char*>& functions)
+{
+ for (QHash<const void*, const char*>::ConstIterator it = functions.begin(), end = functions.end();
+ it != end; ++it) {
+ const QByteArray ptrString = "0x" + QByteArray::number(quintptr(it.key()), 16);
+ int idx = 0;
+ while (idx >= 0) {
+ idx = processedOutput.indexOf(ptrString, idx);
+ if (idx < 0)
+ break;
+ idx = processedOutput.indexOf('\n', idx);
+ if (idx < 0)
+ break;
+ processedOutput = processedOutput.insert(idx, QByteArrayLiteral(" ; ") + it.value());
+ }
+ }
+
+ qDebug("%s", processedOutput.constData());
+}
+
+static QByteArray functionName(Function *function)
+{
+ QByteArray name = function->name()->toQString().toUtf8();
+ if (name.isEmpty()) {
+ name = QByteArray::number(reinterpret_cast<quintptr>(function), 16);
+ name.prepend("QV4::Function(0x");
+ name.append(')');
+ }
+ return name;
+}
+
+JIT::PlatformAssemblerCommon::~PlatformAssemblerCommon()
+{}
+
+void PlatformAssemblerCommon::link(Function *function, const char *jitKind)
+{
+ for (const auto &jumpTarget : jumpsToLink)
+ jumpTarget.jump.linkTo(labelForOffset[jumpTarget.offset], this);
+
+ JSC::JSGlobalData dummy(function->internalClass->engine->executableAllocator);
+ JSC::LinkBuffer<MacroAssembler> linkBuffer(dummy, this, nullptr);
+
+ for (const auto &ehTarget : ehTargets) {
+ auto targetLabel = labelForOffset.value(ehTarget.offset);
+ linkBuffer.patch(ehTarget.label, linkBuffer.locationOf(targetLabel));
+ }
+
+ JSC::MacroAssemblerCodeRef codeRef;
+
+ static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM");
+ if (showCode) {
+ QBuffer buf;
+ buf.open(QIODevice::WriteOnly);
+ WTF::setDataFile(new QIODevicePrintStream(&buf));
+
+ QByteArray name = functionName(function);
+ codeRef = linkBuffer.finalizeCodeWithDisassembly(jitKind, "%s", name.constData());
+
+ WTF::setDataFile(stderr);
+ printDisassembledOutputWithCalls(buf.data(), functions);
+ } else {
+ codeRef = linkBuffer.finalizeCodeWithoutDisassembly();
+ }
+
+ function->codeRef = new JSC::MacroAssemblerCodeRef(codeRef);
+ function->jittedCode = reinterpret_cast<Function::JittedCode>(function->codeRef->code().executableAddress());
+
+ // This implements writing of JIT'd addresses so that perf can find the
+ // symbol names.
+ //
+ // Perf expects the mapping to be in a certain place and have certain
+ // content, for more information, see:
+ // https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt
+ static bool doProfile = !qEnvironmentVariableIsEmpty("QV4_PROFILE_WRITE_PERF_MAP");
+ if (Q_UNLIKELY(doProfile)) {
+ static QFile perfMapFile(QString::fromLatin1("/tmp/perf-%1.map")
+ .arg(QCoreApplication::applicationPid()));
+ static const bool isOpen = perfMapFile.open(QIODevice::WriteOnly);
+ if (!isOpen) {
+ qWarning("QV4::JIT::Assembler: Cannot write perf map file.");
+ doProfile = false;
+ } else {
+ perfMapFile.write(QByteArray::number(reinterpret_cast<quintptr>(
+ codeRef.code().executableAddress()), 16));
+ perfMapFile.putChar(' ');
+ perfMapFile.write(QByteArray::number(static_cast<qsizetype>(codeRef.size()), 16));
+ perfMapFile.putChar(' ');
+ perfMapFile.write(functionName(function));
+ perfMapFile.putChar('\n');
+ perfMapFile.flush();
+ }
+ }
+}
+
+void PlatformAssemblerCommon::prepareCallWithArgCount(int argc)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(remainingArgcForCall == NoCall);
+ remainingArgcForCall = argc;
+#endif
+
+ if (argc > ArgInRegCount) {
+ argcOnStackForCall = int(WTF::roundUpToMultipleOf(16, size_t(argc - ArgInRegCount) * PointerSize));
+ subPtr(TrustedImm32(argcOnStackForCall), StackPointerRegister);
+ }
+}
+
+void PlatformAssemblerCommon::storeInstructionPointer(int instructionOffset)
+{
+ Address addr(CppStackFrameRegister, offsetof(QV4::CppStackFrame, instructionPointer));
+ store32(TrustedImm32(instructionOffset), addr);
+}
+
+PlatformAssemblerCommon::Address PlatformAssemblerCommon::argStackAddress(int arg)
+{
+ int offset = arg - ArgInRegCount;
+ Q_ASSERT(offset >= 0);
+ return Address(StackPointerRegister, offset * PointerSize);
+}
+
+JSC::MacroAssemblerBase::Address PlatformAssemblerCommon::inArgStackAddress(int arg)
+{
+ int offset = arg - ArgInRegCount;
+ Q_ASSERT(offset >= 0);
+ return Address(FramePointerRegister, -(offset + 1) * PointerSize);
+}
+
+void PlatformAssemblerCommon::passAccumulatorAsArg(int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
+
+ passAccumulatorAsArg_internal(arg, false);
+}
+
+void JIT::PlatformAssemblerCommon::pushAccumulatorAsArg(int arg)
+{
+ passAccumulatorAsArg_internal(arg, true);
+}
+
+void PlatformAssemblerCommon::passAccumulatorAsArg_internal(int arg, bool doPush)
+{
+ if (arg < ArgInRegCount) {
+ addPtr(TrustedImm32(offsetof(CallData, accumulator)), JSStackFrameRegister, registerForArg(arg));
+ } else {
+ addPtr(TrustedImm32(offsetof(CallData, accumulator)), JSStackFrameRegister, ScratchRegister);
+ if (doPush)
+ push(ScratchRegister);
+ else
+ storePtr(ScratchRegister, argStackAddress(arg));
+ }
+}
+
+void PlatformAssemblerCommon::passFunctionAsArg(int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
+
+ if (arg < ArgInRegCount) {
+ loadFunctionPtr(registerForArg(arg));
+ } else {
+ loadFunctionPtr(ScratchRegister);
+ storePtr(ScratchRegister, argStackAddress(arg));
+ }
+}
+
+void PlatformAssemblerCommon::passEngineAsArg(int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
+
+ if (arg < ArgInRegCount) {
+ move(EngineRegister, registerForArg(arg));
+ } else {
+ storePtr(EngineRegister, argStackAddress(arg));
+ }
+}
+
+void PlatformAssemblerCommon::passJSSlotAsArg(int reg, int arg)
+{
+ Address addr(JSStackFrameRegister, reg * int(sizeof(QV4::Value)));
+ passAddressAsArg(addr, arg);
+}
+
+void JIT::PlatformAssemblerCommon::passAddressAsArg(Address addr, int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
+
+ if (arg < ArgInRegCount) {
+ addPtr(TrustedImm32(addr.offset), addr.base, registerForArg(arg));
+ } else {
+ addPtr(TrustedImm32(addr.offset), addr.base, ScratchRegister);
+ storePtr(ScratchRegister, argStackAddress(arg));
+ }
+}
+
+void PlatformAssemblerCommon::passCppFrameAsArg(int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
+
+ if (arg < ArgInRegCount)
+ move(CppStackFrameRegister, registerForArg(arg));
+ else
+ store32(CppStackFrameRegister, argStackAddress(arg));
+}
+
+void PlatformAssemblerCommon::passInt32AsArg(int value, int arg)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(arg < remainingArgcForCall);
+ --remainingArgcForCall;
+#endif
+
+ if (arg < ArgInRegCount)
+ move(TrustedImm32(value), registerForArg(arg));
+ else
+ store32(TrustedImm32(value), argStackAddress(arg));
+}
+
+void PlatformAssemblerCommon::callRuntime(const char *functionName, const void *funcPtr)
+{
+#ifndef QT_NO_DEBUG
+ Q_ASSERT(remainingArgcForCall == 0);
+ remainingArgcForCall = NoCall;
+#endif
+ callRuntimeUnchecked(functionName, funcPtr);
+ if (argcOnStackForCall > 0) {
+ addPtr(TrustedImm32(argcOnStackForCall), StackPointerRegister);
+ argcOnStackForCall = 0;
+ }
+}
+
+void PlatformAssemblerCommon::callRuntimeUnchecked(const char *functionName, const void *funcPtr)
+{
+ functions.insert(funcPtr, functionName);
+ callAbsolute(funcPtr);
+}
+
+void PlatformAssemblerCommon::tailCallRuntime(const char *functionName, const void *funcPtr)
+{
+ functions.insert(funcPtr, functionName);
+ setTailCallArg(EngineRegister, 1);
+ setTailCallArg(CppStackFrameRegister, 0);
+ freeStackSpace();
+ generatePlatformFunctionExit(/*tailCall =*/ true);
+ jumpAbsolute(funcPtr);
+}
+
+void PlatformAssemblerCommon::setTailCallArg(RegisterID src, int arg)
+{
+ if (arg < ArgInRegCount)
+ move(src, registerForArg(arg));
+ else
+ storePtr(src, inArgStackAddress(arg));
+}
+
+JSC::MacroAssemblerBase::Address PlatformAssemblerCommon::jsAlloca(int slotCount)
+{
+ Address jsStackTopAddr(EngineRegister, offsetof(EngineBase, jsStackTop));
+ RegisterID jsStackTop = AccumulatorRegisterValue;
+ loadPtr(jsStackTopAddr, jsStackTop);
+ addPtr(TrustedImm32(sizeof(Value) * slotCount), jsStackTop);
+ storePtr(jsStackTop, jsStackTopAddr);
+ return Address(jsStackTop, 0);
+}
+
+void PlatformAssemblerCommon::storeInt32AsValue(int srcInt, Address destAddr)
+{
+ store32(TrustedImm32(srcInt),
+ Address(destAddr.base, destAddr.offset + QV4::Value::valueOffset()));
+ store32(TrustedImm32(int(QV4::Value::ValueTypeInternal::Integer)),
+ Address(destAddr.base, destAddr.offset + QV4::Value::tagOffset()));
+}
+
+} // JIT namespace
+} // QV4 namepsace
+
+QT_END_NAMESPACE
+
+#endif // V4_ENABLE_JIT
diff --git a/src/qml/jit/qv4assemblercommon_p.h b/src/qml/jit/qv4assemblercommon_p.h
new file mode 100644
index 0000000000..cbbd6464d9
--- /dev/null
+++ b/src/qml/jit/qv4assemblercommon_p.h
@@ -0,0 +1,736 @@
+/****************************************************************************
+**
+** 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 QV4PLATFORMASSEMBLER_P_H
+#define QV4PLATFORMASSEMBLER_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/qv4engine_p.h>
+#include <private/qv4global_p.h>
+#include <private/qv4function_p.h>
+#include <QHash>
+#include <wtf/Vector.h>
+#include <assembler/MacroAssembler.h>
+
+#ifdef V4_ENABLE_JIT
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+namespace JIT {
+
+#if defined(Q_PROCESSOR_X86_64) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
+#if defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD) || defined(Q_OS_DARWIN)
+
+class PlatformAssembler_X86_64_SysV : public JSC::MacroAssembler<JSC::MacroAssemblerX86_64>
+{
+public:
+ static constexpr int NativeStackAlignment = 16;
+
+ static const RegisterID NoRegister = RegisterID(-1);
+
+ static const RegisterID ReturnValueRegister = RegisterID::eax;
+ static const RegisterID ReturnValueRegisterValue = ReturnValueRegister;
+ static const RegisterID AccumulatorRegister = RegisterID::eax;
+ static const RegisterID AccumulatorRegisterValue = AccumulatorRegister;
+ static const RegisterID ScratchRegister = RegisterID::r10;
+ static const RegisterID ScratchRegister2 = RegisterID::r9; // Note: overlaps with Arg5Reg, so do not use while setting up a call!
+ static const RegisterID JSStackFrameRegister = RegisterID::r12;
+ static const RegisterID CppStackFrameRegister = RegisterID::r13;
+ static const RegisterID EngineRegister = RegisterID::r14;
+ static const RegisterID StackPointerRegister = RegisterID::esp;
+ static const RegisterID FramePointerRegister = RegisterID::ebp;
+ static const FPRegisterID FPScratchRegister = FPRegisterID::xmm1;
+ static const FPRegisterID FPScratchRegister2 = FPRegisterID::xmm2;
+
+ static const RegisterID Arg0Reg = RegisterID::edi;
+ static const RegisterID Arg1Reg = RegisterID::esi;
+ static const RegisterID Arg2Reg = RegisterID::edx;
+ static const RegisterID Arg3Reg = RegisterID::ecx;
+ static const RegisterID Arg4Reg = RegisterID::r8;
+ static const RegisterID Arg5Reg = RegisterID::r9;
+ static const RegisterID Arg6Reg = NoRegister;
+ static const RegisterID Arg7Reg = NoRegister;
+ static const int ArgInRegCount = 6;
+
+ void popValue()
+ {
+ addPtr(TrustedImmPtr(sizeof(ReturnedValue)), StackPointerRegister);
+ }
+
+ void generatePlatformFunctionEntry()
+ {
+ push(FramePointerRegister);
+ move(StackPointerRegister, FramePointerRegister);
+ move(TrustedImmPtr(nullptr), AccumulatorRegister); push(AccumulatorRegister); // exceptionHandler
+ push(JSStackFrameRegister);
+ push(CppStackFrameRegister);
+ push(EngineRegister);
+ move(Arg0Reg, CppStackFrameRegister);
+ move(Arg1Reg, EngineRegister);
+ }
+
+ void generatePlatformFunctionExit(bool tailCall = false)
+ {
+ pop(EngineRegister);
+ pop(CppStackFrameRegister);
+ pop(JSStackFrameRegister);
+ pop(); // exceptionHandler
+ pop(FramePointerRegister);
+ if (!tailCall)
+ ret();
+ }
+
+ void callAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ call(ScratchRegister);
+ }
+
+ void jumpAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ jump(ScratchRegister);
+ }
+
+ void pushAligned(RegisterID reg)
+ {
+ subPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ push(reg);
+ }
+
+ void popAligned(RegisterID reg)
+ {
+ pop(reg);
+ addPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ }
+};
+
+typedef PlatformAssembler_X86_64_SysV PlatformAssemblerBase;
+
+#endif
+#if defined(Q_OS_WIN)
+
+class PlatformAssembler_Win64 : public JSC::MacroAssembler<JSC::MacroAssemblerX86_64>
+{
+public:
+ static const RegisterID NoRegister = RegisterID(-1);
+
+ static const RegisterID ReturnValueRegister = RegisterID::eax;
+ static const RegisterID ReturnValueRegisterValue = ReturnValueRegister;
+ static const RegisterID AccumulatorRegister = RegisterID::eax;
+ static const RegisterID AccumulatorRegisterValue = AccumulatorRegister;
+ static const RegisterID ScratchRegister = RegisterID::r10;
+ static const RegisterID ScratchRegister2 = RegisterID::r9; // Note: overlaps with Arg3Reg, so do not use while setting up a call!
+ static const RegisterID JSStackFrameRegister = RegisterID::r12;
+ static const RegisterID CppStackFrameRegister = RegisterID::r13;
+ static const RegisterID EngineRegister = RegisterID::r14;
+ static const RegisterID StackPointerRegister = RegisterID::esp;
+ static const RegisterID FramePointerRegister = RegisterID::ebp;
+ static const FPRegisterID FPScratchRegister = FPRegisterID::xmm1;
+
+ static const RegisterID Arg0Reg = RegisterID::ecx;
+ static const RegisterID Arg1Reg = RegisterID::edx;
+ static const RegisterID Arg2Reg = RegisterID::r8;
+ static const RegisterID Arg3Reg = RegisterID::r9;
+ static const RegisterID Arg4Reg = NoRegister;
+ static const RegisterID Arg5Reg = NoRegister;
+ static const RegisterID Arg6Reg = NoRegister;
+ static const RegisterID Arg7Reg = NoRegister;
+ static const int ArgInRegCount = 4;
+
+ void popValue()
+ {
+ addPtr(TrustedImmPtr(sizeof(ReturnedValue)), StackPointerRegister);
+ }
+
+ void generatePlatformFunctionEntry()
+ {
+ push(FramePointerRegister);
+ move(StackPointerRegister, FramePointerRegister);
+ move(TrustedImmPtr(nullptr), AccumulatorRegister); push(AccumulatorRegister); // exceptionHandler
+ push(JSStackFrameRegister);
+ push(CppStackFrameRegister);
+ push(EngineRegister);
+ move(Arg0Reg, CppStackFrameRegister);
+ move(Arg1Reg, EngineRegister);
+ }
+
+ void generatePlatformFunctionExit(bool tailCall = false)
+ {
+ pop(EngineRegister);
+ pop(CppStackFrameRegister);
+ pop(JSStackFrameRegister);
+ pop(); // exceptionHandler
+ pop(FramePointerRegister);
+ if (!tailCall)
+ ret();
+ }
+
+ void callAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ subPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
+ call(ScratchRegister);
+ addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
+ }
+
+ void jumpAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ jump(ScratchRegister);
+ }
+
+ void pushAligned(RegisterID reg)
+ {
+ subPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ push(reg);
+ }
+
+ void popAligned(RegisterID reg)
+ {
+ pop(reg);
+ addPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ }
+};
+
+typedef PlatformAssembler_Win64 PlatformAssemblerBase;
+
+#endif
+#endif
+
+#if (defined(Q_PROCESSOR_X86) && !defined(Q_PROCESSOR_X86_64)) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
+
+class PlatformAssembler_X86_All : public JSC::MacroAssembler<JSC::MacroAssemblerX86>
+{
+public:
+ static const RegisterID NoRegister = RegisterID(-1);
+
+ static const RegisterID ReturnValueRegisterValue = RegisterID::eax;
+ static const RegisterID ReturnValueRegisterTag = RegisterID::edx;
+ static const RegisterID ScratchRegister = RegisterID::ecx;
+ static const RegisterID AccumulatorRegisterValue = ReturnValueRegisterValue;
+ static const RegisterID AccumulatorRegisterTag = ReturnValueRegisterTag;
+ static const RegisterID JSStackFrameRegister = RegisterID::ebx;
+ static const RegisterID CppStackFrameRegister = RegisterID::esi;
+ static const RegisterID EngineRegister = RegisterID::edi;
+ static const RegisterID StackPointerRegister = RegisterID::esp;
+ static const RegisterID FramePointerRegister = RegisterID::ebp;
+ static const FPRegisterID FPScratchRegister = FPRegisterID::xmm1;
+
+ static const RegisterID Arg0Reg = NoRegister;
+ static const RegisterID Arg1Reg = NoRegister;
+ static const RegisterID Arg2Reg = NoRegister;
+ static const RegisterID Arg3Reg = NoRegister;
+ static const RegisterID Arg4Reg = NoRegister;
+ static const RegisterID Arg5Reg = NoRegister;
+ static const RegisterID Arg6Reg = NoRegister;
+ static const RegisterID Arg7Reg = NoRegister;
+ static const int ArgInRegCount = 0;
+
+ void popValue()
+ {
+ addPtr(TrustedImmPtr(sizeof(ReturnedValue)), StackPointerRegister);
+ }
+
+ void generatePlatformFunctionEntry()
+ {
+ push(RegisterID::ebp);
+ move(RegisterID::esp, RegisterID::ebp);
+ move(TrustedImmPtr(nullptr), AccumulatorRegisterValue); push(AccumulatorRegisterValue); // exceptionHandler
+ push(JSStackFrameRegister);
+ push(CppStackFrameRegister);
+ push(EngineRegister);
+ // Ensure the stack is 16-byte aligned in order for compiler generated aligned SSE2
+ // instructions to be able to target the stack.
+ subPtr(TrustedImm32(8), StackPointerRegister);
+ loadPtr(Address(FramePointerRegister, 2 * PointerSize), CppStackFrameRegister);
+ loadPtr(Address(FramePointerRegister, 3 * PointerSize), EngineRegister);
+ }
+
+ void generatePlatformFunctionExit(bool tailCall = false)
+ {
+ addPtr(TrustedImm32(8), StackPointerRegister);
+ pop(EngineRegister);
+ pop(CppStackFrameRegister);
+ pop(JSStackFrameRegister);
+ pop(); // exceptionHandler
+ pop(RegisterID::ebp);
+ if (!tailCall)
+ ret();
+ }
+
+ void callAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ call(ScratchRegister);
+ }
+
+ void jumpAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ jump(ScratchRegister);
+ }
+
+ void pushAligned(RegisterID reg)
+ {
+ subPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ push(reg);
+ }
+
+ void popAligned(RegisterID reg)
+ {
+ pop(reg);
+ addPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ }
+};
+
+typedef PlatformAssembler_X86_All PlatformAssemblerBase;
+
+#endif
+
+#if defined(Q_PROCESSOR_ARM_64) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
+
+class PlatformAssembler_ARM64 : public JSC::MacroAssembler<JSC::MacroAssemblerARM64>
+{
+public:
+ static const RegisterID NoRegister = RegisterID(-1);
+
+ static const RegisterID ReturnValueRegister = JSC::ARM64Registers::x0;
+ static const RegisterID ReturnValueRegisterValue = ReturnValueRegister;
+ static const RegisterID AccumulatorRegister = JSC::ARM64Registers::x9;
+ static const RegisterID AccumulatorRegisterValue = AccumulatorRegister;
+ static const RegisterID ScratchRegister = JSC::ARM64Registers::x10;
+ static const RegisterID ScratchRegister2 = JSC::ARM64Registers::x7; // Note: overlaps with Arg7Reg, so do not use while setting up a call!
+ static const RegisterID JSStackFrameRegister = JSC::ARM64Registers::x19;
+ static const RegisterID CppStackFrameRegister = JSC::ARM64Registers::x20;
+ static const RegisterID EngineRegister = JSC::ARM64Registers::x21;
+ static const RegisterID StackPointerRegister = JSC::ARM64Registers::sp;
+ static const RegisterID FramePointerRegister = JSC::ARM64Registers::fp;
+ static const FPRegisterID FPScratchRegister = JSC::ARM64Registers::q1;
+
+ static const RegisterID Arg0Reg = JSC::ARM64Registers::x0;
+ static const RegisterID Arg1Reg = JSC::ARM64Registers::x1;
+ static const RegisterID Arg2Reg = JSC::ARM64Registers::x2;
+ static const RegisterID Arg3Reg = JSC::ARM64Registers::x3;
+ static const RegisterID Arg4Reg = JSC::ARM64Registers::x4;
+ static const RegisterID Arg5Reg = JSC::ARM64Registers::x5;
+ static const RegisterID Arg6Reg = JSC::ARM64Registers::x6;
+ static const RegisterID Arg7Reg = JSC::ARM64Registers::x7;
+ static const int ArgInRegCount = 8;
+
+ void push(RegisterID src)
+ {
+ pushToSave(src);
+ }
+
+ void pop(RegisterID dest)
+ {
+ popToRestore(dest);
+ }
+
+ void pop()
+ {
+ add64(TrustedImm32(16), stackPointerRegister);
+ }
+
+ void popValue()
+ {
+ pop();
+ }
+
+ void generatePlatformFunctionEntry()
+ {
+ pushPair(JSC::ARM64Registers::fp, JSC::ARM64Registers::lr);
+ move(RegisterID::sp, RegisterID::fp);
+ move(TrustedImmPtr(nullptr), AccumulatorRegister); // exceptionHandler
+ pushPair(JSStackFrameRegister, AccumulatorRegister);
+ pushPair(EngineRegister, CppStackFrameRegister);
+ move(Arg0Reg, CppStackFrameRegister);
+ move(Arg1Reg, EngineRegister);
+ }
+
+ void generatePlatformFunctionExit(bool tailCall = false)
+ {
+ move(AccumulatorRegister, ReturnValueRegister);
+ popPair(EngineRegister, CppStackFrameRegister);
+ popPair(JSStackFrameRegister, AccumulatorRegister);
+ popPair(JSC::ARM64Registers::fp, JSC::ARM64Registers::lr);
+ if (!tailCall)
+ ret();
+ }
+
+ void callAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ call(ScratchRegister);
+ }
+
+ void jumpAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), ScratchRegister);
+ jump(ScratchRegister);
+ }
+
+ void pushAligned(RegisterID reg)
+ {
+ pushToSave(reg);
+ }
+
+ void popAligned(RegisterID reg)
+ {
+ popToRestore(reg);
+ }
+};
+
+typedef PlatformAssembler_ARM64 PlatformAssemblerBase;
+
+#endif
+
+#if defined(Q_PROCESSOR_ARM_32) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
+
+class PlatformAssembler_ARM32 : public JSC::MacroAssembler<JSC::MacroAssemblerARMv7>
+{
+public:
+ static const RegisterID NoRegister = RegisterID(-1);
+
+ static const RegisterID ReturnValueRegisterValue = JSC::ARMRegisters::r0;
+ static const RegisterID ReturnValueRegisterTag = JSC::ARMRegisters::r1;
+ static const RegisterID ScratchRegister = JSC::ARMRegisters::r2;
+ static const RegisterID AccumulatorRegisterValue = JSC::ARMRegisters::r4;
+ static const RegisterID AccumulatorRegisterTag = JSC::ARMRegisters::r5;
+ // r6 is used by MacroAssemblerARMv7
+ static const RegisterID JSStackFrameRegister = JSC::ARMRegisters::r8;
+ static const RegisterID CppStackFrameRegister = JSC::ARMRegisters::r10;
+#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP)
+ static const RegisterID FramePointerRegister = JSC::ARMRegisters::r7;
+ static const RegisterID EngineRegister = JSC::ARMRegisters::r11;
+#else // Thumbs down
+ static const RegisterID FramePointerRegister = JSC::ARMRegisters::r11;
+ static const RegisterID EngineRegister = JSC::ARMRegisters::r7;
+#endif
+ static const RegisterID StackPointerRegister = JSC::ARMRegisters::r13;
+ static const FPRegisterID FPScratchRegister = JSC::ARMRegisters::d1;
+
+ static const RegisterID Arg0Reg = JSC::ARMRegisters::r0;
+ static const RegisterID Arg1Reg = JSC::ARMRegisters::r1;
+ static const RegisterID Arg2Reg = JSC::ARMRegisters::r2;
+ static const RegisterID Arg3Reg = JSC::ARMRegisters::r3;
+ static const RegisterID Arg4Reg = NoRegister;
+ static const RegisterID Arg5Reg = NoRegister;
+ static const RegisterID Arg6Reg = NoRegister;
+ static const RegisterID Arg7Reg = NoRegister;
+ static const int ArgInRegCount = 4;
+
+ void popValue()
+ {
+ addPtr(TrustedImm32(sizeof(ReturnedValue)), StackPointerRegister);
+ }
+
+ void generatePlatformFunctionEntry()
+ {
+ push(JSC::ARMRegisters::lr);
+ push(FramePointerRegister);
+ move(StackPointerRegister, FramePointerRegister);
+ push(TrustedImm32(0)); // exceptionHandler
+ push(AccumulatorRegisterValue);
+ push(AccumulatorRegisterTag);
+ push(addressTempRegister);
+ push(JSStackFrameRegister);
+ push(CppStackFrameRegister);
+ push(EngineRegister);
+ subPtr(TrustedImm32(4), StackPointerRegister); // stack alignment
+ move(Arg0Reg, CppStackFrameRegister);
+ move(Arg1Reg, EngineRegister);
+ }
+
+ void generatePlatformFunctionExit(bool tailCall = false)
+ {
+ move(AccumulatorRegisterValue, ReturnValueRegisterValue);
+ move(AccumulatorRegisterTag, ReturnValueRegisterTag);
+ addPtr(TrustedImm32(4), StackPointerRegister); // stack alignment
+ pop(EngineRegister);
+ pop(CppStackFrameRegister);
+ pop(JSStackFrameRegister);
+ pop(addressTempRegister);
+ pop(AccumulatorRegisterTag);
+ pop(AccumulatorRegisterValue);
+ pop(); // exceptionHandler
+ pop(FramePointerRegister);
+ pop(JSC::ARMRegisters::lr);
+ if (!tailCall)
+ ret();
+ }
+
+ void callAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), dataTempRegister);
+ call(dataTempRegister);
+ }
+
+ void jumpAbsolute(const void *funcPtr)
+ {
+ move(TrustedImmPtr(funcPtr), dataTempRegister);
+ jump(dataTempRegister);
+ }
+
+ void pushAligned(RegisterID reg)
+ {
+ subPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ push(reg);
+ }
+
+ void popAligned(RegisterID reg)
+ {
+ pop(reg);
+ addPtr(TrustedImm32(PointerSize), StackPointerRegister);
+ }
+};
+
+typedef PlatformAssembler_ARM32 PlatformAssemblerBase;
+#endif
+
+class PlatformAssemblerCommon : public JIT::PlatformAssemblerBase
+{
+public:
+ PlatformAssemblerCommon(const Value *constantTable)
+ : constantTable(constantTable)
+ {}
+
+ virtual ~PlatformAssemblerCommon();
+
+ Address exceptionHandlerAddress() const
+ {
+ return Address(FramePointerRegister, -1 * PointerSize);
+ }
+
+ Address contextAddress() const
+ {
+ return Address(JSStackFrameRegister, offsetof(CallData, context));
+ }
+
+ RegisterID registerForArg(int arg) const
+ {
+ Q_ASSERT(arg >= 0);
+ Q_ASSERT(arg < ArgInRegCount);
+ switch (arg) {
+ case 0: return Arg0Reg;
+ case 1: return Arg1Reg;
+ case 2: return Arg2Reg;
+ case 3: return Arg3Reg;
+ case 4: return Arg4Reg;
+ case 5: return Arg5Reg;
+ case 6: return Arg6Reg;
+ case 7: return Arg7Reg;
+ default:
+ Q_UNIMPLEMENTED();
+ Q_UNREACHABLE();
+ }
+ }
+
+ Address loadFunctionPtr(RegisterID target)
+ {
+ Address addr(CppStackFrameRegister, offsetof(CppStackFrame, v4Function));
+ loadPtr(addr, target);
+ return Address(target);
+ }
+
+ Address loadCompilationUnitPtr(RegisterID target)
+ {
+ Address addr = loadFunctionPtr(target);
+ addr.offset = offsetof(QV4::Function, compilationUnit);
+ loadPtr(addr, target);
+ return Address(target);
+ }
+
+ Address loadConstAddress(int constIndex, RegisterID baseReg = ScratchRegister)
+ {
+ Address addr = loadCompilationUnitPtr(baseReg);
+ addr.offset = offsetof(QV4::CompiledData::CompilationUnitBase, constants);
+ loadPtr(addr, baseReg);
+ addr.offset = constIndex * int(sizeof(QV4::Value));
+ return addr;
+ }
+
+ Address loadStringAddress(int stringId)
+ {
+ Address addr = loadCompilationUnitPtr(ScratchRegister);
+ addr.offset = offsetof(QV4::CompiledData::CompilationUnitBase, runtimeStrings);
+ loadPtr(addr, ScratchRegister);
+ return Address(ScratchRegister, stringId * PointerSize);
+ }
+
+ void passAsArg(RegisterID src, int arg)
+ {
+ move(src, registerForArg(arg));
+ }
+
+ void generateCatchTrampoline(std::function<void()> loadUndefined)
+ {
+ for (Jump j : catchyJumps)
+ j.link(this);
+
+ loadPtr(exceptionHandlerAddress(), ScratchRegister);
+ Jump exitFunction = branchPtr(Equal, ScratchRegister, TrustedImmPtr(0));
+ jump(ScratchRegister);
+ exitFunction.link(this);
+ loadUndefined();
+
+ if (functionExit.isSet())
+ jump(functionExit);
+ else
+ generateFunctionExit();
+ }
+
+ void checkException()
+ {
+ addCatchyJump(
+ branch32(NotEqual,
+ Address(EngineRegister, offsetof(EngineBase, hasException)),
+ TrustedImm32(0)));
+ }
+
+ void addCatchyJump(Jump j)
+ {
+ Q_ASSERT(j.isSet());
+ catchyJumps.push_back(j);
+ }
+
+ void generateFunctionEntry()
+ {
+ generatePlatformFunctionEntry();
+ loadPtr(Address(CppStackFrameRegister, offsetof(CppStackFrame, jsFrame)), JSStackFrameRegister);
+ allocateStackSpace();
+ }
+
+ virtual void allocateStackSpace() {}
+
+ void generateFunctionExit()
+ {
+ if (functionExit.isSet()) {
+ jump(functionExit);
+ return;
+ }
+
+ functionExit = label();
+ freeStackSpace();
+ generatePlatformFunctionExit();
+ }
+
+ virtual void freeStackSpace() {}
+
+ void addLabelForOffset(int offset)
+ {
+ labelForOffset.insert(offset, label());
+ }
+
+ void addJumpToOffset(const Jump &jump, int offset)
+ {
+ jumpsToLink.push_back({ jump, offset });
+ }
+
+ void addEHTarget(const DataLabelPtr &label, int offset)
+ {
+ ehTargets.push_back({ label, offset });
+ }
+
+ void link(Function *function, const char *jitKind);
+
+ Value constant(int idx) const
+ { return constantTable[idx]; }
+
+ // stuff for runtime calls
+ void prepareCallWithArgCount(int argc);
+ void storeInstructionPointer(int instructionOffset);
+ void passAccumulatorAsArg(int arg);
+ void pushAccumulatorAsArg(int arg);
+ void passFunctionAsArg(int arg);
+ void passEngineAsArg(int arg);
+ void passJSSlotAsArg(int reg, int arg);
+ void passAddressAsArg(Address addr, int arg);
+ void passCppFrameAsArg(int arg);
+ void passInt32AsArg(int value, int arg);
+ void callRuntime(const char *functionName, const void *funcPtr);
+ void callRuntimeUnchecked(const char *functionName, const void *funcPtr);
+ void tailCallRuntime(const char *functionName, const void *funcPtr);
+ void setTailCallArg(RegisterID src, int arg);
+ Address jsAlloca(int slotCount);
+ void storeInt32AsValue(int srcInt, Address destAddr);
+
+private:
+ void passAccumulatorAsArg_internal(int arg, bool doPush);
+ static Address argStackAddress(int arg);
+ static Address inArgStackAddress(int arg);
+
+private:
+ const Value* constantTable;
+ struct JumpTarget { JSC::MacroAssemblerBase::Jump jump; int offset; };
+ std::vector<JumpTarget> jumpsToLink;
+ struct ExceptionHanlderTarget { JSC::MacroAssemblerBase::DataLabelPtr label; int offset; };
+ std::vector<ExceptionHanlderTarget> ehTargets;
+ QHash<int, JSC::MacroAssemblerBase::Label> labelForOffset;
+ QHash<const void *, const char *> functions;
+ std::vector<Jump> catchyJumps;
+ Label functionExit;
+
+#ifndef QT_NO_DEBUG
+ enum { NoCall = -1 };
+ int remainingArgcForCall = NoCall;
+#endif
+ int argcOnStackForCall = 0;
+};
+
+} // JIT namespace
+} // QV4 namespace
+
+QT_END_NAMESPACE
+
+#endif // V4_ENABLE_JIT
+
+#endif // QV4PLATFORMASSEMBLER_P_H
diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4baselineassembler.cpp
index 9786293e4c..f6b745632b 100644
--- a/src/qml/jit/qv4assembler.cpp
+++ b/src/qml/jit/qv4baselineassembler.cpp
@@ -41,9 +41,11 @@
#include <QFile>
#include "qv4engine_p.h"
-#include "qv4assembler_p.h"
+#include "qv4baselineassembler_p.h"
+#include "qv4assemblercommon_p.h"
#include <private/qv4function_p.h>
#include <private/qv4runtime_p.h>
+#include <private/qv4stackframe_p.h>
#include <wtf/Vector.h>
#include <assembler/MacroAssembler.h>
@@ -59,7 +61,9 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
namespace JIT {
-#define callHelper(x) PlatformAssemblerCommon::callRuntime(#x, reinterpret_cast<void *>(&x))
+#define ASM_GENERATE_RUNTIME_CALL(function, destination) \
+ pasm()->GENERATE_RUNTIME_CALL(function, destination)
+#define callHelper(x) PlatformAssemblerCommon::callRuntimeUnchecked(#x, reinterpret_cast<void *>(&x))
const QV4::Value::ValueTypeInternal IntegerTag = QV4::Value::ValueTypeInternal::Integer;
@@ -73,568 +77,19 @@ static ReturnedValue toInt32Helper(ReturnedValue v)
return Encode(Value::fromReturnedValue(v).toInt32());
}
-#if defined(Q_PROCESSOR_X86_64) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
-#if defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD) || defined(Q_OS_DARWIN)
-
-struct PlatformAssembler_X86_64_SysV : JSC::MacroAssembler<JSC::MacroAssemblerX86_64>
-{
- static const RegisterID NoRegister = RegisterID(-1);
-
- static const RegisterID ReturnValueRegister = RegisterID::eax;
- static const RegisterID ReturnValueRegisterValue = ReturnValueRegister;
- static const RegisterID AccumulatorRegister = RegisterID::eax;
- static const RegisterID AccumulatorRegisterValue = AccumulatorRegister;
- static const RegisterID ScratchRegister = RegisterID::r10;
- static const RegisterID ScratchRegister2 = RegisterID::r9; // Note: overlaps with Arg5Reg, so do not use while setting up a call!
- static const RegisterID JSStackFrameRegister = RegisterID::r12;
- static const RegisterID CppStackFrameRegister = RegisterID::r13;
- static const RegisterID EngineRegister = RegisterID::r14;
- static const RegisterID StackPointerRegister = RegisterID::esp;
- static const RegisterID FramePointerRegister = RegisterID::ebp;
- static const FPRegisterID FPScratchRegister = FPRegisterID::xmm1;
-
- static const RegisterID Arg0Reg = RegisterID::edi;
- static const RegisterID Arg1Reg = RegisterID::esi;
- static const RegisterID Arg2Reg = RegisterID::edx;
- static const RegisterID Arg3Reg = RegisterID::ecx;
- static const RegisterID Arg4Reg = RegisterID::r8;
- static const RegisterID Arg5Reg = RegisterID::r9;
- static const RegisterID Arg6Reg = NoRegister;
- static const RegisterID Arg7Reg = NoRegister;
- static const int ArgInRegCount = 6;
-
- void popValue()
- {
- addPtr(TrustedImmPtr(sizeof(ReturnedValue)), StackPointerRegister);
- }
-
- void generatePlatformFunctionEntry()
- {
- push(RegisterID::ebp);
- move(RegisterID::esp, RegisterID::ebp);
- move(TrustedImmPtr(nullptr), AccumulatorRegister); push(AccumulatorRegister); // exceptionHandler
- push(JSStackFrameRegister);
- push(CppStackFrameRegister);
- push(EngineRegister);
- move(Arg0Reg, CppStackFrameRegister);
- move(Arg1Reg, EngineRegister);
- }
-
- void generatePlatformFunctionExit()
- {
- pop(EngineRegister);
- pop(CppStackFrameRegister);
- pop(JSStackFrameRegister);
- pop(); // exceptionHandler
- pop(RegisterID::ebp);
- ret();
- }
-
- void callAbsolute(const void *funcPtr)
- {
- move(TrustedImmPtr(funcPtr), ScratchRegister);
- call(ScratchRegister);
- }
-
- void pushAligned(RegisterID reg)
- {
- subPtr(TrustedImm32(PointerSize), StackPointerRegister);
- push(reg);
- }
-
- void popAligned(RegisterID reg)
- {
- pop(reg);
- addPtr(TrustedImm32(PointerSize), StackPointerRegister);
- }
-};
-
-typedef PlatformAssembler_X86_64_SysV PlatformAssemblerBase;
-
-#endif
-#if defined(Q_OS_WIN)
-
-struct PlatformAssembler_Win64 : JSC::MacroAssembler<JSC::MacroAssemblerX86_64>
-{
- static const RegisterID NoRegister = RegisterID(-1);
-
- static const RegisterID ReturnValueRegister = RegisterID::eax;
- static const RegisterID ReturnValueRegisterValue = ReturnValueRegister;
- static const RegisterID AccumulatorRegister = RegisterID::eax;
- static const RegisterID AccumulatorRegisterValue = AccumulatorRegister;
- static const RegisterID ScratchRegister = RegisterID::r10;
- static const RegisterID ScratchRegister2 = RegisterID::r9; // Note: overlaps with Arg3Reg, so do not use while setting up a call!
- static const RegisterID JSStackFrameRegister = RegisterID::r12;
- static const RegisterID CppStackFrameRegister = RegisterID::r13;
- static const RegisterID EngineRegister = RegisterID::r14;
- static const RegisterID StackPointerRegister = RegisterID::esp;
- static const RegisterID FramePointerRegister = RegisterID::ebp;
- static const FPRegisterID FPScratchRegister = FPRegisterID::xmm1;
-
- static const RegisterID Arg0Reg = RegisterID::ecx;
- static const RegisterID Arg1Reg = RegisterID::edx;
- static const RegisterID Arg2Reg = RegisterID::r8;
- static const RegisterID Arg3Reg = RegisterID::r9;
- static const RegisterID Arg4Reg = NoRegister;
- static const RegisterID Arg5Reg = NoRegister;
- static const RegisterID Arg6Reg = NoRegister;
- static const RegisterID Arg7Reg = NoRegister;
- static const int ArgInRegCount = 4;
-
- void popValue()
- {
- addPtr(TrustedImmPtr(sizeof(ReturnedValue)), StackPointerRegister);
- }
-
- void generatePlatformFunctionEntry()
- {
- push(RegisterID::ebp);
- move(RegisterID::esp, RegisterID::ebp);
- move(TrustedImmPtr(nullptr), AccumulatorRegister); push(AccumulatorRegister); // exceptionHandler
- push(JSStackFrameRegister);
- push(CppStackFrameRegister);
- push(EngineRegister);
- move(Arg0Reg, CppStackFrameRegister);
- move(Arg1Reg, EngineRegister);
- }
-
- void generatePlatformFunctionExit()
- {
- pop(EngineRegister);
- pop(CppStackFrameRegister);
- pop(JSStackFrameRegister);
- pop(); // exceptionHandler
- pop(RegisterID::ebp);
- ret();
- }
-
- void callAbsolute(const void *funcPtr)
- {
- move(TrustedImmPtr(funcPtr), ScratchRegister);
- subPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
- call(ScratchRegister);
- addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister);
- }
-
- void pushAligned(RegisterID reg)
- {
- subPtr(TrustedImm32(PointerSize), StackPointerRegister);
- push(reg);
- }
-
- void popAligned(RegisterID reg)
- {
- pop(reg);
- addPtr(TrustedImm32(PointerSize), StackPointerRegister);
- }
-};
-
-typedef PlatformAssembler_Win64 PlatformAssemblerBase;
-
-#endif
-#endif
-
-#if (defined(Q_PROCESSOR_X86) && !defined(Q_PROCESSOR_X86_64)) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
-
-struct PlatformAssembler_X86_All : JSC::MacroAssembler<JSC::MacroAssemblerX86>
-{
- static const RegisterID NoRegister = RegisterID(-1);
-
- static const RegisterID ReturnValueRegisterValue = RegisterID::eax;
- static const RegisterID ReturnValueRegisterTag = RegisterID::edx;
- static const RegisterID ScratchRegister = RegisterID::ecx;
- static const RegisterID AccumulatorRegisterValue = ReturnValueRegisterValue;
- static const RegisterID AccumulatorRegisterTag = ReturnValueRegisterTag;
- static const RegisterID JSStackFrameRegister = RegisterID::ebx;
- static const RegisterID CppStackFrameRegister = RegisterID::esi;
- static const RegisterID EngineRegister = RegisterID::edi;
- static const RegisterID StackPointerRegister = RegisterID::esp;
- static const RegisterID FramePointerRegister = RegisterID::ebp;
- static const FPRegisterID FPScratchRegister = FPRegisterID::xmm1;
-
- static const RegisterID Arg0Reg = NoRegister;
- static const RegisterID Arg1Reg = NoRegister;
- static const RegisterID Arg2Reg = NoRegister;
- static const RegisterID Arg3Reg = NoRegister;
- static const RegisterID Arg4Reg = NoRegister;
- static const RegisterID Arg5Reg = NoRegister;
- static const RegisterID Arg6Reg = NoRegister;
- static const RegisterID Arg7Reg = NoRegister;
- static const int ArgInRegCount = 0;
-
- void popValue()
- {
- addPtr(TrustedImmPtr(sizeof(ReturnedValue)), StackPointerRegister);
- }
-
- void generatePlatformFunctionEntry()
- {
- push(RegisterID::ebp);
- move(RegisterID::esp, RegisterID::ebp);
- move(TrustedImmPtr(nullptr), AccumulatorRegisterValue); push(AccumulatorRegisterValue); // exceptionHandler
- push(JSStackFrameRegister);
- push(CppStackFrameRegister);
- push(EngineRegister);
- // Ensure the stack is 16-byte aligned in order for compiler generated aligned SSE2
- // instructions to be able to target the stack.
- subPtr(TrustedImm32(8), StackPointerRegister);
- loadPtr(Address(FramePointerRegister, 2 * PointerSize), CppStackFrameRegister);
- loadPtr(Address(FramePointerRegister, 3 * PointerSize), EngineRegister);
- }
-
- void generatePlatformFunctionExit()
- {
- addPtr(TrustedImm32(8), StackPointerRegister);
- pop(EngineRegister);
- pop(CppStackFrameRegister);
- pop(JSStackFrameRegister);
- pop(); // exceptionHandler
- pop(RegisterID::ebp);
- ret();
- }
-
- void callAbsolute(const void *funcPtr)
- {
- move(TrustedImmPtr(funcPtr), ScratchRegister);
- call(ScratchRegister);
- }
-
- void pushAligned(RegisterID reg)
- {
- subPtr(TrustedImm32(PointerSize), StackPointerRegister);
- push(reg);
- }
-
- void popAligned(RegisterID reg)
- {
- pop(reg);
- addPtr(TrustedImm32(PointerSize), StackPointerRegister);
- }
-};
-
-typedef PlatformAssembler_X86_All PlatformAssemblerBase;
-
-#endif
-
-#if defined(Q_PROCESSOR_ARM_64) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
-
-struct PlatformAssembler_ARM64 : JSC::MacroAssembler<JSC::MacroAssemblerARM64>
-{
- static const RegisterID NoRegister = RegisterID(-1);
-
- static const RegisterID ReturnValueRegister = JSC::ARM64Registers::x0;
- static const RegisterID ReturnValueRegisterValue = ReturnValueRegister;
- static const RegisterID AccumulatorRegister = JSC::ARM64Registers::x9;
- static const RegisterID AccumulatorRegisterValue = AccumulatorRegister;
- static const RegisterID ScratchRegister = JSC::ARM64Registers::x10;
- static const RegisterID ScratchRegister2 = JSC::ARM64Registers::x7; // Note: overlaps with Arg7Reg, so do not use while setting up a call!
- static const RegisterID JSStackFrameRegister = JSC::ARM64Registers::x19;
- static const RegisterID CppStackFrameRegister = JSC::ARM64Registers::x20;
- static const RegisterID EngineRegister = JSC::ARM64Registers::x21;
- static const RegisterID StackPointerRegister = JSC::ARM64Registers::sp;
- static const RegisterID FramePointerRegister = JSC::ARM64Registers::fp;
- static const FPRegisterID FPScratchRegister = JSC::ARM64Registers::q1;
-
- static const RegisterID Arg0Reg = JSC::ARM64Registers::x0;
- static const RegisterID Arg1Reg = JSC::ARM64Registers::x1;
- static const RegisterID Arg2Reg = JSC::ARM64Registers::x2;
- static const RegisterID Arg3Reg = JSC::ARM64Registers::x3;
- static const RegisterID Arg4Reg = JSC::ARM64Registers::x4;
- static const RegisterID Arg5Reg = JSC::ARM64Registers::x5;
- static const RegisterID Arg6Reg = JSC::ARM64Registers::x6;
- static const RegisterID Arg7Reg = JSC::ARM64Registers::x7;
- static const int ArgInRegCount = 8;
-
- void push(RegisterID src)
- {
- pushToSave(src);
- }
-
- void pop(RegisterID dest)
- {
- popToRestore(dest);
- }
-
- void pop()
- {
- add64(TrustedImm32(16), stackPointerRegister);
- }
-
- void popValue()
- {
- pop();
- }
-
- void generatePlatformFunctionEntry()
- {
- pushPair(JSC::ARM64Registers::fp, JSC::ARM64Registers::lr);
- move(RegisterID::sp, RegisterID::fp);
- move(TrustedImmPtr(nullptr), AccumulatorRegister); // exceptionHandler
- pushPair(JSStackFrameRegister, AccumulatorRegister);
- pushPair(EngineRegister, CppStackFrameRegister);
- move(Arg0Reg, CppStackFrameRegister);
- move(Arg1Reg, EngineRegister);
- }
-
- void generatePlatformFunctionExit()
- {
- move(AccumulatorRegister, ReturnValueRegister);
- popPair(EngineRegister, CppStackFrameRegister);
- popPair(JSStackFrameRegister, AccumulatorRegister);
- popPair(JSC::ARM64Registers::fp, JSC::ARM64Registers::lr);
- ret();
- }
-
- void callAbsolute(const void *funcPtr)
- {
- move(TrustedImmPtr(funcPtr), ScratchRegister);
- call(ScratchRegister);
- }
-
- void pushAligned(RegisterID reg)
- {
- pushToSave(reg);
- }
-
- void popAligned(RegisterID reg)
- {
- popToRestore(reg);
- }
-};
-
-typedef PlatformAssembler_ARM64 PlatformAssemblerBase;
-
-#endif
-
-#if defined(Q_PROCESSOR_ARM_32) || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
-
-struct PlatformAssembler_ARM32 : JSC::MacroAssembler<JSC::MacroAssemblerARMv7>
-{
- static const RegisterID NoRegister = RegisterID(-1);
-
- static const RegisterID ReturnValueRegisterValue = JSC::ARMRegisters::r0;
- static const RegisterID ReturnValueRegisterTag = JSC::ARMRegisters::r1;
- static const RegisterID ScratchRegister = JSC::ARMRegisters::r2;
- static const RegisterID AccumulatorRegisterValue = JSC::ARMRegisters::r4;
- static const RegisterID AccumulatorRegisterTag = JSC::ARMRegisters::r5;
- // r6 is used by MacroAssemblerARMv7
- static const RegisterID JSStackFrameRegister = JSC::ARMRegisters::r8;
- static const RegisterID CppStackFrameRegister = JSC::ARMRegisters::r10;
-#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP)
- static const RegisterID FramePointerRegister = JSC::ARMRegisters::r7;
- static const RegisterID EngineRegister = JSC::ARMRegisters::r11;
-#else // Thumbs down
- static const RegisterID FramePointerRegister = JSC::ARMRegisters::r11;
- static const RegisterID EngineRegister = JSC::ARMRegisters::r7;
-#endif
- static const RegisterID StackPointerRegister = JSC::ARMRegisters::r13;
- static const FPRegisterID FPScratchRegister = JSC::ARMRegisters::d1;
-
- static const RegisterID Arg0Reg = JSC::ARMRegisters::r0;
- static const RegisterID Arg1Reg = JSC::ARMRegisters::r1;
- static const RegisterID Arg2Reg = JSC::ARMRegisters::r2;
- static const RegisterID Arg3Reg = JSC::ARMRegisters::r3;
- static const RegisterID Arg4Reg = NoRegister;
- static const RegisterID Arg5Reg = NoRegister;
- static const RegisterID Arg6Reg = NoRegister;
- static const RegisterID Arg7Reg = NoRegister;
- static const int ArgInRegCount = 4;
-
- void popValue()
- {
- addPtr(TrustedImm32(sizeof(ReturnedValue)), StackPointerRegister);
- }
-
- void generatePlatformFunctionEntry()
- {
- push(JSC::ARMRegisters::lr);
- push(FramePointerRegister);
- move(StackPointerRegister, FramePointerRegister);
- push(TrustedImm32(0)); // exceptionHandler
- push(AccumulatorRegisterValue);
- push(AccumulatorRegisterTag);
- push(addressTempRegister);
- push(JSStackFrameRegister);
- push(CppStackFrameRegister);
- push(EngineRegister);
- subPtr(TrustedImm32(4), StackPointerRegister); // stack alignment
- move(Arg0Reg, CppStackFrameRegister);
- move(Arg1Reg, EngineRegister);
- }
-
- void generatePlatformFunctionExit()
- {
- move(AccumulatorRegisterValue, ReturnValueRegisterValue);
- move(AccumulatorRegisterTag, ReturnValueRegisterTag);
- addPtr(TrustedImm32(4), StackPointerRegister); // stack alignment
- pop(EngineRegister);
- pop(CppStackFrameRegister);
- pop(JSStackFrameRegister);
- pop(addressTempRegister);
- pop(AccumulatorRegisterTag);
- pop(AccumulatorRegisterValue);
- pop(); // exceptionHandler
- pop(FramePointerRegister);
- pop(JSC::ARMRegisters::lr);
- ret();
- }
-
- void callAbsolute(const void *funcPtr)
- {
- move(TrustedImmPtr(funcPtr), dataTempRegister);
- call(dataTempRegister);
- }
-
- void pushAligned(RegisterID reg)
- {
- subPtr(TrustedImm32(PointerSize), StackPointerRegister);
- push(reg);
- }
-
- void popAligned(RegisterID reg)
- {
- pop(reg);
- addPtr(TrustedImm32(PointerSize), StackPointerRegister);
- }
-};
-
-typedef PlatformAssembler_ARM32 PlatformAssemblerBase;
-
-#endif
-
-struct PlatformAssemblerCommon : PlatformAssemblerBase
-{
- const Value* constantTable;
- struct JumpTarget { JSC::MacroAssemblerBase::Jump jump; int offset; };
- std::vector<JumpTarget> patches;
- struct ExceptionHanlderTarget { JSC::MacroAssemblerBase::DataLabelPtr label; int offset; };
- std::vector<ExceptionHanlderTarget> ehTargets;
- QHash<int, JSC::MacroAssemblerBase::Label> labelsByOffset;
- QHash<const void *, const char *> functions;
- std::vector<Jump> catchyJumps;
- Label functionExit;
-
- Address exceptionHandlerAddress() const
- {
- return Address(FramePointerRegister, -1 * PointerSize);
- }
-
- Address contextAddress() const
- {
- return Address(JSStackFrameRegister, offsetof(CallData, context));
- }
-
- RegisterID registerForArg(int arg) const
- {
- Q_ASSERT(arg >= 0);
- Q_ASSERT(arg < ArgInRegCount);
- switch (arg) {
- case 0: return Arg0Reg;
- case 1: return Arg1Reg;
- case 2: return Arg2Reg;
- case 3: return Arg3Reg;
- case 4: return Arg4Reg;
- case 5: return Arg5Reg;
- case 6: return Arg6Reg;
- case 7: return Arg7Reg;
- default:
- Q_UNIMPLEMENTED();
- Q_UNREACHABLE();
- }
- }
-
- void callRuntime(const char *functionName, const void *funcPtr)
- {
- functions.insert(funcPtr, functionName);
- callAbsolute(funcPtr);
- }
-
- Address loadFunctionPtr(RegisterID target)
- {
- Address addr(CppStackFrameRegister, offsetof(CppStackFrame, v4Function));
- loadPtr(addr, target);
- return Address(target);
- }
-
- Address loadCompilationUnitPtr(RegisterID target)
- {
- Address addr = loadFunctionPtr(target);
- addr.offset = offsetof(QV4::Function, compilationUnit);
- loadPtr(addr, target);
- return Address(target);
- }
-
- Address loadConstAddress(int constIndex, RegisterID baseReg = ScratchRegister)
- {
- Address addr = loadCompilationUnitPtr(baseReg);
- addr.offset = offsetof(QV4::CompiledData::CompilationUnitBase, constants);
- loadPtr(addr, baseReg);
- addr.offset = constIndex * int(sizeof(QV4::Value));
- return addr;
- }
-
- Address loadStringAddress(int stringId)
- {
- Address addr = loadCompilationUnitPtr(ScratchRegister);
- addr.offset = offsetof(QV4::CompiledData::CompilationUnitBase, runtimeStrings);
- loadPtr(addr, ScratchRegister);
- return Address(ScratchRegister, stringId * PointerSize);
- }
-
- void passAsArg(RegisterID src, int arg)
- {
- move(src, registerForArg(arg));
- }
-
- void generateCatchTrampoline(std::function<void()> loadUndefined)
- {
- for (Jump j : catchyJumps)
- j.link(this);
-
- loadPtr(exceptionHandlerAddress(), ScratchRegister);
- Jump exitFunction = branchPtr(Equal, ScratchRegister, TrustedImmPtr(0));
- jump(ScratchRegister);
- exitFunction.link(this);
- loadUndefined();
-
- if (functionExit.isSet())
- jump(functionExit);
- else
- generateFunctionExit();
- }
-
- void addCatchyJump(Jump j)
- {
- Q_ASSERT(j.isSet());
- catchyJumps.push_back(j);
- }
-
- void generateFunctionEntry()
- {
- generatePlatformFunctionEntry();
- loadPtr(Address(CppStackFrameRegister, offsetof(CppStackFrame, jsFrame)), JSStackFrameRegister);
- }
-
- void generateFunctionExit()
- {
- if (functionExit.isSet()) {
- jump(functionExit);
- return;
- }
-
- functionExit = label();
- generatePlatformFunctionExit();
- }
-};
-
#if QT_POINTER_SIZE == 8 || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
-struct PlatformAssembler64 : PlatformAssemblerCommon
+class PlatformAssembler64 : public PlatformAssemblerCommon
{
+public:
+ PlatformAssembler64(const Value *constantTable)
+ : PlatformAssemblerCommon(constantTable)
+ {}
+
void callRuntime(const char *functionName, const void *funcPtr,
- Assembler::CallResultDestination dest)
+ CallResultDestination dest)
{
PlatformAssemblerCommon::callRuntime(functionName, funcPtr);
- if (dest == Assembler::ResultInAccumulator)
+ if (dest == CallResultDestination::InAccumulator)
saveReturnValueInAccumulator();
}
@@ -651,7 +106,7 @@ struct PlatformAssembler64 : PlatformAssemblerCommon
void copyConst(int constIndex, Address dest)
{
//###
- if (constantTable[constIndex].isUndefined()) {
+ if (constant(constIndex).isUndefined()) {
loadUndefined(ScratchRegister);
} else {
load64(loadConstAddress(constIndex, ScratchRegister), ScratchRegister);
@@ -680,6 +135,12 @@ struct PlatformAssembler64 : PlatformAssemblerCommon
store64(AccumulatorRegister, addr);
}
+ void moveReg(Address sourceRegAddress, Address destRegAddress)
+ {
+ load64(sourceRegAddress, ScratchRegister);
+ store64(ScratchRegister, destRegAddress);
+ }
+
void loadString(int stringId)
{
loadAccumulator(loadStringAddress(stringId));
@@ -700,6 +161,22 @@ struct PlatformAssembler64 : PlatformAssemblerCommon
PlatformAssemblerCommon::generateCatchTrampoline([this](){loadUndefined();});
}
+ void jumpNotUndefined(int offset)
+ {
+ auto jump = branch64(NotEqual, AccumulatorRegister, TrustedImm64(0));
+ addJumpToOffset(jump, offset);
+ }
+
+ Jump jumpEmpty()
+ {
+ return branch64(Equal, AccumulatorRegister, TrustedImm64(Value::emptyValue().asReturnedValue()));
+ }
+
+ Jump jumpNotEmpty()
+ {
+ return branch64(NotEqual, AccumulatorRegister, TrustedImm64(Value::emptyValue().asReturnedValue()));
+ }
+
void toBoolean(std::function<void(RegisterID)> continuation)
{
urshift64(AccumulatorRegister, TrustedImm32(Value::IsIntegerConvertible_Shift), ScratchRegister);
@@ -762,8 +239,8 @@ struct PlatformAssembler64 : PlatformAssemblerCommon
auto isInt = branch32(Equal, TrustedImm32(Value::QT_Int), ScratchRegister2);
move(AccumulatorRegister, registerForArg(0));
- callRuntime("toInt32Helper", reinterpret_cast<void *>(&toInt32Helper),
- Assembler::ResultInAccumulator);
+ callRuntimeUnchecked("toInt32Helper", reinterpret_cast<void *>(&toInt32Helper));
+ saveReturnValueInAccumulator();
isInt.link(this);
}
@@ -809,7 +286,7 @@ struct PlatformAssembler64 : PlatformAssemblerCommon
load64(lhsAddr, ScratchRegister);
Jump isUndef = branch64(Equal, ScratchRegister, TrustedImm64(0));
Jump equal = branch32(Equal, TrustedImm32(rhs), ScratchRegister);
- patches.push_back({ equal, offset });
+ addJumpToOffset(equal, offset);
isUndef.link(this);
}
@@ -818,9 +295,9 @@ struct PlatformAssembler64 : PlatformAssemblerCommon
Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
load64(lhsAddr, ScratchRegister);
Jump isUndef = branch64(Equal, ScratchRegister, TrustedImm64(0));
- patches.push_back({ isUndef, offset });
+ addJumpToOffset(isUndef, offset);
Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister);
- patches.push_back({ notEqual, offset });
+ addJumpToOffset(notEqual, offset);
}
void setAccumulatorTag(QV4::Value::ValueTypeInternal tag, RegisterID sourceReg = NoRegister)
@@ -898,13 +375,18 @@ typedef PlatformAssembler64 PlatformAssembler;
#endif
#if QT_POINTER_SIZE == 4 || defined(ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES)
-struct PlatformAssembler32 : PlatformAssemblerCommon
+class PlatformAssembler32 : public PlatformAssemblerCommon
{
+public:
+ PlatformAssembler32(const Value *constantTable)
+ : PlatformAssemblerCommon(constantTable)
+ {}
+
void callRuntime(const char *functionName, const void *funcPtr,
- Assembler::CallResultDestination dest)
+ CallResultDestination dest)
{
PlatformAssemblerCommon::callRuntime(functionName, funcPtr);
- if (dest == Assembler::ResultInAccumulator)
+ if (dest == CallResultDestination::InAccumulator)
saveReturnValueInAccumulator();
}
@@ -923,7 +405,7 @@ struct PlatformAssembler32 : PlatformAssemblerCommon
void copyConst(int constIndex, Address destRegAddr)
{
//###
- if (constantTable[constIndex].isUndefined()) {
+ if (constant(constIndex).isUndefined()) {
move(TrustedImm32(0), ScratchRegister);
store32(ScratchRegister, destRegAddr);
destRegAddr.offset += 4;
@@ -960,6 +442,16 @@ struct PlatformAssembler32 : PlatformAssemblerCommon
store32(AccumulatorRegisterTag, addr);
}
+ void moveReg(Address sourceRegAddress, Address destRegAddress)
+ {
+ load32(sourceRegAddress, ReturnValueRegisterValue);
+ sourceRegAddress.offset += 4;
+ load32(sourceRegAddress, ReturnValueRegisterTag);
+ store32(ReturnValueRegisterValue, destRegAddress);
+ destRegAddress.offset += 4;
+ store32(ReturnValueRegisterTag, destRegAddress);
+ }
+
void loadString(int stringId)
{
load32(loadStringAddress(stringId), AccumulatorRegisterValue);
@@ -997,8 +489,7 @@ struct PlatformAssembler32 : PlatformAssemblerCommon
move(AccumulatorRegisterValue, registerForArg(0));
move(AccumulatorRegisterTag, registerForArg(1));
}
- callRuntime("toNumberHelper", reinterpret_cast<void *>(&toNumberHelper),
- Assembler::ResultInAccumulator);
+ callRuntimeUnchecked("toNumberHelper", reinterpret_cast<void *>(&toNumberHelper));
saveReturnValueInAccumulator();
if (ArgInRegCount < 2)
addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
@@ -1050,8 +541,8 @@ struct PlatformAssembler32 : PlatformAssemblerCommon
move(AccumulatorRegisterValue, registerForArg(0));
move(AccumulatorRegisterTag, registerForArg(1));
}
- callRuntime("toInt32Helper", reinterpret_cast<void *>(&toInt32Helper),
- Assembler::ResultInAccumulator);
+ callRuntimeUnchecked("toInt32Helper", reinterpret_cast<void *>(&toInt32Helper));
+ saveReturnValueInAccumulator();
if (ArgInRegCount < 2)
addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
popAligned(lhsTarget);
@@ -1071,8 +562,8 @@ struct PlatformAssembler32 : PlatformAssemblerCommon
move(AccumulatorRegisterValue, registerForArg(0));
move(AccumulatorRegisterTag, registerForArg(1));
}
- callRuntime("toInt32Helper", reinterpret_cast<void *>(&toInt32Helper),
- Assembler::ResultInAccumulator);
+ callRuntimeUnchecked("toInt32Helper", reinterpret_cast<void *>(&toInt32Helper));
+ saveReturnValueInAccumulator();
if (ArgInRegCount < 2)
addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister);
@@ -1135,6 +626,24 @@ struct PlatformAssembler32 : PlatformAssemblerCommon
push(TrustedImm32(v));
}
+ void jumpNotUndefined(int offset)
+ {
+ move(AccumulatorRegisterTag, ScratchRegister);
+ or32(AccumulatorRegisterValue, ScratchRegister);
+ auto jump = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
+ addJumpToOffset(jump, offset);
+ }
+
+ Jump jumpEmpty()
+ {
+ return branch32(Equal, AccumulatorRegisterTag, TrustedImm32(Value::emptyValue().asReturnedValue() >> 32));
+ }
+
+ Jump jumpNotEmpty()
+ {
+ return branch32(NotEqual, AccumulatorRegisterTag, TrustedImm32(Value::emptyValue().asReturnedValue() >> 32));
+ }
+
void toBoolean(std::function<void(RegisterID)> continuation)
{
urshift32(AccumulatorRegisterTag, TrustedImm32(Value::IsIntegerConvertible_Shift - 32),
@@ -1180,11 +689,11 @@ struct PlatformAssembler32 : PlatformAssemblerCommon
load32(lhsAddr, ScratchRegister);
Jump notEqInt = branch32(NotEqual, ScratchRegister, TrustedImm32(rhs));
Jump notEqUndefVal = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
- patches.push_back({ notEqUndefVal, offset });
+ addJumpToOffset(notEqUndefVal, offset);
lhsAddr.offset += 4;
load32(lhsAddr, ScratchRegister);
Jump notEqUndefTag = branch32(NotEqual, ScratchRegister, TrustedImm32(0));
- patches.push_back({ notEqUndefTag, offset });
+ addJumpToOffset(notEqUndefTag, offset);
notEqInt.link(this);
}
@@ -1193,12 +702,12 @@ struct PlatformAssembler32 : PlatformAssemblerCommon
Address lhsAddr(JSStackFrameRegister, lhs * int(sizeof(Value)));
load32(lhsAddr, ScratchRegister);
Jump notEqual = branch32(NotEqual, TrustedImm32(rhs), ScratchRegister);
- patches.push_back({ notEqual, offset });
+ addJumpToOffset(notEqual, offset);
Jump notUndefValue = branch32(NotEqual, TrustedImm32(0), ScratchRegister);
lhsAddr.offset += 4;
load32(lhsAddr, ScratchRegister);
Jump equalUndef = branch32(Equal, TrustedImm32(0), ScratchRegister);
- patches.push_back({ equalUndef, offset });
+ addJumpToOffset(equalUndef, offset);
notUndefValue.link(this);
}
@@ -1281,6 +790,8 @@ struct PlatformAssembler32 : PlatformAssemblerCommon
typedef PlatformAssembler32 PlatformAssembler;
#endif
+#define pasm() reinterpret_cast<PlatformAssembler *>(this->d)
+
typedef PlatformAssembler::TrustedImmPtr TrustedImmPtr;
typedef PlatformAssembler::TrustedImm32 TrustedImm32;
typedef PlatformAssembler::TrustedImm64 TrustedImm64;
@@ -1288,193 +799,72 @@ typedef PlatformAssembler::Address Address;
typedef PlatformAssembler::RegisterID RegisterID;
typedef PlatformAssembler::FPRegisterID FPRegisterID;
-#define pasm() reinterpret_cast<PlatformAssembler *>(this->d)
-
static Address regAddr(int reg)
{
return Address(PlatformAssembler::JSStackFrameRegister, reg * int(sizeof(QV4::Value)));
}
-Assembler::Assembler(const Value *constantTable)
- : d(new PlatformAssembler)
+BaselineAssembler::BaselineAssembler(const Value *constantTable)
+ : d(new PlatformAssembler(constantTable))
{
- pasm()->constantTable = constantTable;
}
-Assembler::~Assembler()
+BaselineAssembler::~BaselineAssembler()
{
delete pasm();
}
-void Assembler::generatePrologue()
+void BaselineAssembler::generatePrologue()
{
pasm()->generateFunctionEntry();
}
-void Assembler::generateEpilogue()
+void BaselineAssembler::generateEpilogue()
{
pasm()->generateCatchTrampoline();
}
-namespace {
-class QIODevicePrintStream: public FilePrintStream
-{
- Q_DISABLE_COPY(QIODevicePrintStream)
-
-public:
- explicit QIODevicePrintStream(QIODevice *dest)
- : FilePrintStream(nullptr)
- , dest(dest)
- , buf(4096, '0')
- {
- Q_ASSERT(dest);
- }
-
- ~QIODevicePrintStream()
- {}
-
- void vprintf(const char* format, va_list argList) WTF_ATTRIBUTE_PRINTF(2, 0)
- {
- const int written = qvsnprintf(buf.data(), buf.size(), format, argList);
- if (written > 0)
- dest->write(buf.constData(), written);
- memset(buf.data(), 0, qMin(written, buf.size()));
- }
-
- void flush()
- {}
-
-private:
- QIODevice *dest;
- QByteArray buf;
-};
-} // anonymous namespace
-
-static void printDisassembledOutputWithCalls(QByteArray processedOutput,
- const QHash<const void*, const char*>& functions)
-{
- for (QHash<const void*, const char*>::ConstIterator it = functions.begin(), end = functions.end();
- it != end; ++it) {
- const QByteArray ptrString = "0x" + QByteArray::number(quintptr(it.key()), 16);
- int idx = 0;
- while (idx >= 0) {
- idx = processedOutput.indexOf(ptrString, idx);
- if (idx < 0)
- break;
- idx = processedOutput.indexOf('\n', idx);
- if (idx < 0)
- break;
- processedOutput = processedOutput.insert(idx, QByteArrayLiteral(" ; ") + it.value());
- }
- }
-
- qDebug("%s", processedOutput.constData());
-}
-
-static QByteArray functionName(Function *function)
-{
- QByteArray name = function->name()->toQString().toUtf8();
- if (name.isEmpty()) {
- name = QByteArray::number(reinterpret_cast<quintptr>(function), 16);
- name.prepend("QV4::Function(0x");
- name.append(')');
- }
- return name;
-}
-
-void Assembler::link(Function *function)
+void BaselineAssembler::link(Function *function)
{
- for (const auto &jumpTarget : pasm()->patches)
- jumpTarget.jump.linkTo(pasm()->labelsByOffset[jumpTarget.offset], pasm());
-
- JSC::JSGlobalData dummy(function->internalClass->engine->executableAllocator);
- JSC::LinkBuffer<PlatformAssembler::MacroAssembler> linkBuffer(dummy, pasm(), nullptr);
-
- for (const auto &ehTarget : pasm()->ehTargets) {
- auto targetLabel = pasm()->labelsByOffset.value(ehTarget.offset);
- linkBuffer.patch(ehTarget.label, linkBuffer.locationOf(targetLabel));
- }
-
- JSC::MacroAssemblerCodeRef codeRef;
-
- static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM");
- if (showCode) {
- QBuffer buf;
- buf.open(QIODevice::WriteOnly);
- WTF::setDataFile(new QIODevicePrintStream(&buf));
-
- QByteArray name = functionName(function);
- codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data());
-
- WTF::setDataFile(stderr);
- printDisassembledOutputWithCalls(buf.data(), pasm()->functions);
- } else {
- codeRef = linkBuffer.finalizeCodeWithoutDisassembly();
- }
-
- function->codeRef = new JSC::MacroAssemblerCodeRef(codeRef);
- function->jittedCode = reinterpret_cast<Function::JittedCode>(function->codeRef->code().executableAddress());
-
-#if defined(Q_OS_LINUX)
- // This implements writing of JIT'd addresses so that perf can find the
- // symbol names.
- //
- // Perf expects the mapping to be in a certain place and have certain
- // content, for more information, see:
- // https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt
- static bool doProfile = !qEnvironmentVariableIsEmpty("QV4_PROFILE_WRITE_PERF_MAP");
- if (doProfile) {
- static QFile perfMapFile(QString::fromLatin1("/tmp/perf-%1.map")
- .arg(QCoreApplication::applicationPid()));
- static const bool isOpen = perfMapFile.open(QIODevice::WriteOnly);
- if (!isOpen) {
- qWarning("QV4::JIT::Assembler: Cannot write perf map file.");
- doProfile = false;
- } else {
- perfMapFile.write(QByteArray::number(reinterpret_cast<quintptr>(
- codeRef.code().executableAddress()), 16));
- perfMapFile.putChar(' ');
- perfMapFile.write(QByteArray::number(static_cast<qsizetype>(codeRef.size()), 16));
- perfMapFile.putChar(' ');
- perfMapFile.write(functionName(function));
- perfMapFile.putChar('\n');
- perfMapFile.flush();
- }
- }
-#endif
+ pasm()->link(function, "BaselineJIT");
}
-void Assembler::addLabel(int offset)
+void BaselineAssembler::addLabel(int offset)
{
- pasm()->labelsByOffset[offset] = pasm()->label();
+ pasm()->addLabelForOffset(offset);
}
-void Assembler::loadConst(int constIndex)
+void BaselineAssembler::loadConst(int constIndex)
{
//###
- if (pasm()->constantTable[constIndex].isUndefined()) {
+ if (pasm()->constant(constIndex).isUndefined()) {
pasm()->loadUndefined();
} else {
pasm()->loadAccumulator(pasm()->loadConstAddress(constIndex));
}
}
-void Assembler::copyConst(int constIndex, int destReg)
+void BaselineAssembler::copyConst(int constIndex, int destReg)
{
pasm()->copyConst(constIndex, regAddr(destReg));
}
-void Assembler::loadReg(int reg)
+void BaselineAssembler::loadReg(int reg)
{
pasm()->loadAccumulator(regAddr(reg));
}
-void Assembler::storeReg(int reg)
+void JIT::BaselineAssembler::moveReg(int sourceReg, int destReg)
+{
+ pasm()->moveReg(regAddr(sourceReg), regAddr(destReg));
+}
+
+void BaselineAssembler::storeReg(int reg)
{
pasm()->storeAccumulator(regAddr(reg));
}
-void Assembler::loadLocal(int index, int level)
+void BaselineAssembler::loadLocal(int index, int level)
{
Heap::CallContext ctx;
Q_UNUSED(ctx)
@@ -1486,7 +876,7 @@ void Assembler::loadLocal(int index, int level)
pasm()->loadAccumulator(Address(PlatformAssembler::ScratchRegister, ctx.locals.offset + offsetof(ValueArray<0>, values) + sizeof(Value)*index));
}
-void Assembler::storeLocal(int index, int level)
+void BaselineAssembler::storeLocal(int index, int level)
{
Heap::CallContext ctx;
Q_UNUSED(ctx)
@@ -1498,36 +888,46 @@ void Assembler::storeLocal(int index, int level)
pasm()->storeAccumulator(Address(PlatformAssembler::ScratchRegister, ctx.locals.offset + offsetof(ValueArray<0>, values) + sizeof(Value)*index));
}
-void Assembler::loadString(int stringId)
+void BaselineAssembler::loadString(int stringId)
{
pasm()->loadString(stringId);
}
-void Assembler::loadValue(ReturnedValue value)
+void BaselineAssembler::loadValue(ReturnedValue value)
{
pasm()->loadValue(value);
}
-void JIT::Assembler::storeHeapObject(int reg)
+void BaselineAssembler::storeHeapObject(int reg)
{
pasm()->storeHeapObject(PlatformAssembler::ReturnValueRegisterValue, regAddr(reg));
}
-void Assembler::toNumber()
+void BaselineAssembler::loadImport(int index)
+{
+ Address addr = pasm()->loadCompilationUnitPtr(PlatformAssembler::ScratchRegister);
+ addr.offset = offsetof(QV4::CompiledData::CompilationUnitBase, imports);
+ pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister);
+ addr.offset = index * int(sizeof(QV4::Value*));
+ pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister);
+ pasm()->loadAccumulator(Address(PlatformAssembler::ScratchRegister));
+}
+
+void BaselineAssembler::toNumber()
{
pasm()->toNumber();
}
-void Assembler::uminus()
+void BaselineAssembler::uminus()
{
saveAccumulatorInFrame();
- prepareCallWithArgCount(1);
- passAccumulatorAsArg(0);
- IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_uMinus, ResultInAccumulator);
+ pasm()->prepareCallWithArgCount(1);
+ pasm()->passAccumulatorAsArg(0);
+ ASM_GENERATE_RUNTIME_CALL(Runtime::method_uMinus, CallResultDestination::InAccumulator);
checkException();
}
-void Assembler::ucompl()
+void BaselineAssembler::ucompl()
{
pasm()->toInt32();
pasm()->xor32(TrustedImm32(-1), PlatformAssembler::AccumulatorRegisterValue);
@@ -1544,7 +944,7 @@ static ReturnedValue incHelper(const Value v)
return Encode(d + 1.);
}
-void Assembler::inc()
+void BaselineAssembler::inc()
{
auto done = pasm()->unopIntPath([this](){
auto overflowed = pasm()->branchAdd32(PlatformAssembler::Overflow,
@@ -1576,7 +976,7 @@ static ReturnedValue decHelper(const Value v)
return Encode(d - 1.);
}
-void Assembler::dec()
+void BaselineAssembler::dec()
{
auto done = pasm()->unopIntPath([this](){
auto overflowed = pasm()->branchSub32(PlatformAssembler::Overflow,
@@ -1598,7 +998,7 @@ void Assembler::dec()
done.link(pasm());
}
-void Assembler::unot()
+void BaselineAssembler::unot()
{
pasm()->toBoolean([this](PlatformAssembler::RegisterID resultReg){
pasm()->compare32(PlatformAssembler::Equal, resultReg,
@@ -1607,7 +1007,7 @@ void Assembler::unot()
});
}
-void Assembler::add(int lhs)
+void BaselineAssembler::add(int lhs)
{
auto done = pasm()->binopBothIntPath(regAddr(lhs), [this](){
auto overflowed = pasm()->branchAdd32(PlatformAssembler::Overflow,
@@ -1620,18 +1020,18 @@ void Assembler::add(int lhs)
// slow path:
saveAccumulatorInFrame();
- prepareCallWithArgCount(3);
- passAccumulatorAsArg(2);
- passRegAsArg(lhs, 1);
- passEngineAsArg(0);
- IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_add, ResultInAccumulator);
+ pasm()->prepareCallWithArgCount(3);
+ pasm()->passAccumulatorAsArg(2);
+ pasm()->passJSSlotAsArg(lhs, 1);
+ pasm()->passEngineAsArg(0);
+ ASM_GENERATE_RUNTIME_CALL(Runtime::method_add, CallResultDestination::InAccumulator);
checkException();
// done.
done.link(pasm());
}
-void Assembler::bitAnd(int lhs)
+void BaselineAssembler::bitAnd(int lhs)
{
PlatformAssembler::Address lhsAddr = regAddr(lhs);
pasm()->toInt32LhsAcc(lhsAddr, PlatformAssembler::ScratchRegister);
@@ -1639,7 +1039,7 @@ void Assembler::bitAnd(int lhs)
pasm()->setAccumulatorTag(IntegerTag);
}
-void Assembler::bitOr(int lhs)
+void BaselineAssembler::bitOr(int lhs)
{
PlatformAssembler::Address lhsAddr = regAddr(lhs);
pasm()->toInt32LhsAcc(lhsAddr, PlatformAssembler::ScratchRegister);
@@ -1647,7 +1047,7 @@ void Assembler::bitOr(int lhs)
pasm()->setAccumulatorTag(IntegerTag);
}
-void Assembler::bitXor(int lhs)
+void BaselineAssembler::bitXor(int lhs)
{
PlatformAssembler::Address lhsAddr = regAddr(lhs);
pasm()->toInt32LhsAcc(lhsAddr, PlatformAssembler::ScratchRegister);
@@ -1655,7 +1055,7 @@ void Assembler::bitXor(int lhs)
pasm()->setAccumulatorTag(IntegerTag);
}
-void Assembler::ushr(int lhs)
+void BaselineAssembler::ushr(int lhs)
{
PlatformAssembler::Address lhsAddr = regAddr(lhs);
pasm()->toInt32LhsAcc(lhsAddr, PlatformAssembler::ScratchRegister);
@@ -1676,7 +1076,7 @@ void Assembler::ushr(int lhs)
done.link(pasm());
}
-void Assembler::shr(int lhs)
+void BaselineAssembler::shr(int lhs)
{
PlatformAssembler::Address lhsAddr = regAddr(lhs);
pasm()->toInt32LhsAcc(lhsAddr, PlatformAssembler::ScratchRegister);
@@ -1686,7 +1086,7 @@ void Assembler::shr(int lhs)
pasm()->setAccumulatorTag(IntegerTag);
}
-void Assembler::shl(int lhs)
+void BaselineAssembler::shl(int lhs)
{
PlatformAssembler::Address lhsAddr = regAddr(lhs);
pasm()->toInt32LhsAcc(lhsAddr, PlatformAssembler::ScratchRegister);
@@ -1696,28 +1096,28 @@ void Assembler::shl(int lhs)
pasm()->setAccumulatorTag(IntegerTag);
}
-void Assembler::bitAndConst(int rhs)
+void BaselineAssembler::bitAndConst(int rhs)
{
pasm()->toInt32();
pasm()->and32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(IntegerTag);
}
-void Assembler::bitOrConst(int rhs)
+void BaselineAssembler::bitOrConst(int rhs)
{
pasm()->toInt32();
pasm()->or32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(IntegerTag);
}
-void Assembler::bitXorConst(int rhs)
+void BaselineAssembler::bitXorConst(int rhs)
{
pasm()->toInt32();
pasm()->xor32(TrustedImm32(rhs), PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(IntegerTag);
}
-void Assembler::ushrConst(int rhs)
+void BaselineAssembler::ushrConst(int rhs)
{
rhs &= 0x1f;
pasm()->toInt32();
@@ -1742,7 +1142,7 @@ void Assembler::ushrConst(int rhs)
}
}
-void Assembler::shrConst(int rhs)
+void BaselineAssembler::shrConst(int rhs)
{
rhs &= 0x1f;
pasm()->toInt32();
@@ -1751,7 +1151,7 @@ void Assembler::shrConst(int rhs)
pasm()->setAccumulatorTag(IntegerTag);
}
-void Assembler::shlConst(int rhs)
+void BaselineAssembler::shlConst(int rhs)
{
rhs &= 0x1f;
pasm()->toInt32();
@@ -1760,7 +1160,7 @@ void Assembler::shlConst(int rhs)
pasm()->setAccumulatorTag(IntegerTag);
}
-void Assembler::mul(int lhs)
+void BaselineAssembler::mul(int lhs)
{
auto done = pasm()->binopBothIntPath(regAddr(lhs), [this](){
auto overflowed = pasm()->branchMul32(PlatformAssembler::Overflow,
@@ -1773,37 +1173,37 @@ void Assembler::mul(int lhs)
// slow path:
saveAccumulatorInFrame();
- prepareCallWithArgCount(2);
- passAccumulatorAsArg(1);
- passRegAsArg(lhs, 0);
- IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_mul, ResultInAccumulator);
+ pasm()->prepareCallWithArgCount(2);
+ pasm()->passAccumulatorAsArg(1);
+ pasm()->passJSSlotAsArg(lhs, 0);
+ ASM_GENERATE_RUNTIME_CALL(Runtime::method_mul, CallResultDestination::InAccumulator);
checkException();
// done.
done.link(pasm());
}
-void Assembler::div(int lhs)
+void BaselineAssembler::div(int lhs)
{
saveAccumulatorInFrame();
- prepareCallWithArgCount(2);
- passAccumulatorAsArg(1);
- passRegAsArg(lhs, 0);
- IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_div, ResultInAccumulator);
+ pasm()->prepareCallWithArgCount(2);
+ pasm()->passAccumulatorAsArg(1);
+ pasm()->passJSSlotAsArg(lhs, 0);
+ ASM_GENERATE_RUNTIME_CALL(Runtime::method_div, CallResultDestination::InAccumulator);
checkException();
}
-void Assembler::mod(int lhs)
+void BaselineAssembler::mod(int lhs)
{
saveAccumulatorInFrame();
- prepareCallWithArgCount(2);
- passAccumulatorAsArg(1);
- passRegAsArg(lhs, 0);
- IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_mod, ResultInAccumulator);
+ pasm()->prepareCallWithArgCount(2);
+ pasm()->passAccumulatorAsArg(1);
+ pasm()->passJSSlotAsArg(lhs, 0);
+ ASM_GENERATE_RUNTIME_CALL(Runtime::method_mod, CallResultDestination::InAccumulator);
checkException();
}
-void Assembler::sub(int lhs)
+void BaselineAssembler::sub(int lhs)
{
auto done = pasm()->binopBothIntPath(regAddr(lhs), [this](){
auto overflowed = pasm()->branchSub32(PlatformAssembler::Overflow,
@@ -1816,30 +1216,30 @@ void Assembler::sub(int lhs)
// slow path:
saveAccumulatorInFrame();
- prepareCallWithArgCount(2);
- passAccumulatorAsArg(1);
- passRegAsArg(lhs, 0);
- IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_sub, ResultInAccumulator);
+ pasm()->prepareCallWithArgCount(2);
+ pasm()->passAccumulatorAsArg(1);
+ pasm()->passJSSlotAsArg(lhs, 0);
+ ASM_GENERATE_RUNTIME_CALL(Runtime::method_sub, CallResultDestination::InAccumulator);
checkException();
// done.
done.link(pasm());
}
-void Assembler::cmpeqNull()
+void BaselineAssembler::cmpeqNull()
{
pasm()->isNullOrUndefined();
pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
}
-void Assembler::cmpneNull()
+void BaselineAssembler::cmpneNull()
{
pasm()->isNullOrUndefined();
pasm()->xor32(TrustedImm32(1), PlatformAssembler::AccumulatorRegisterValue);
pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
}
-void Assembler::cmpeqInt(int lhs)
+void BaselineAssembler::cmpeqInt(int lhs)
{
auto isIntOrBool = pasm()->isIntOrBool();
saveAccumulatorInFrame();
@@ -1848,8 +1248,9 @@ void Assembler::cmpeqInt(int lhs)
pasm()->push(PlatformAssembler::StackPointerRegister);
else
pasm()->move(PlatformAssembler::StackPointerRegister, pasm()->registerForArg(1));
- passAccumulatorAsArg_internal(0, true);
- pasm()->callRuntime("Runtime::method_equal", (void*)Runtime::method_equal, ResultInAccumulator);
+ pasm()->pushAccumulatorAsArg(0);
+ pasm()->callRuntimeUnchecked("Runtime::method_equal", (void*)Runtime::method_equal);
+ pasm()->saveReturnValueInAccumulator();
if (PlatformAssembler::ArgInRegCount < 2)
pasm()->addPtr(TrustedImm32(2 * PlatformAssembler::PointerSize), PlatformAssembler::StackPointerRegister);
pasm()->popValueAligned();
@@ -1862,7 +1263,7 @@ void Assembler::cmpeqInt(int lhs)
done.link(pasm());
}
-void Assembler::cmpneInt(int lhs)
+void BaselineAssembler::cmpneInt(int lhs)
{
auto isIntOrBool = pasm()->isIntOrBool();
saveAccumulatorInFrame();
@@ -1871,8 +1272,9 @@ void Assembler::cmpneInt(int lhs)
pasm()->push(PlatformAssembler::StackPointerRegister);
else
pasm()->move(PlatformAssembler::StackPointerRegister, pasm()->registerForArg(1));
- passAccumulatorAsArg_internal(0, true);
- pasm()->callRuntime("Runtime::method_notEqual", (void*)Runtime::method_notEqual, ResultInAccumulator);
+ pasm()->pushAccumulatorAsArg(0);
+ pasm()->callRuntimeUnchecked("Runtime::method_notEqual", (void*)Runtime::method_notEqual);
+ pasm()->saveReturnValueInAccumulator();
if (PlatformAssembler::ArgInRegCount < 2)
pasm()->addPtr(TrustedImm32(2 * PlatformAssembler::PointerSize), PlatformAssembler::StackPointerRegister);
pasm()->popValueAligned();
@@ -1885,7 +1287,7 @@ void Assembler::cmpneInt(int lhs)
done.link(pasm());
}
-void Assembler::cmp(int cond, CmpFunc function, const char *functionName, int lhs)
+void BaselineAssembler::cmp(int cond, CmpFunc function, const char *functionName, int lhs)
{
auto c = static_cast<PlatformAssembler::RelationalCondition>(cond);
auto done = pasm()->binopBothIntPath(regAddr(lhs), [this, c](){
@@ -1898,11 +1300,11 @@ void Assembler::cmp(int cond, CmpFunc function, const char *functionName, int lh
// slow path:
saveAccumulatorInFrame();
- prepareCallWithArgCount(2);
- passAccumulatorAsArg(1);
- passRegAsArg(lhs, 0);
+ pasm()->prepareCallWithArgCount(2);
+ pasm()->passAccumulatorAsArg(1);
+ pasm()->passJSSlotAsArg(lhs, 0);
- callRuntime(functionName, reinterpret_cast<void*>(function), ResultInAccumulator);
+ callRuntime(functionName, reinterpret_cast<void*>(function), CallResultDestination::InAccumulator);
checkException();
pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
@@ -1910,49 +1312,49 @@ void Assembler::cmp(int cond, CmpFunc function, const char *functionName, int lh
done.link(pasm());
}
-void Assembler::cmpeq(int lhs)
+void BaselineAssembler::cmpeq(int lhs)
{
cmp(PlatformAssembler::Equal, &Runtime::method_compareEqual,
"Runtime::method_compareEqual", lhs);
}
-void Assembler::cmpne(int lhs)
+void BaselineAssembler::cmpne(int lhs)
{
cmp(PlatformAssembler::NotEqual, &Runtime::method_compareNotEqual,
"Runtime::method_compareNotEqual", lhs);
}
-void Assembler::cmpgt(int lhs)
+void BaselineAssembler::cmpgt(int lhs)
{
cmp(PlatformAssembler::GreaterThan, &Runtime::method_compareGreaterThan,
"Runtime::method_compareGreaterThan", lhs);
}
-void Assembler::cmpge(int lhs)
+void BaselineAssembler::cmpge(int lhs)
{
cmp(PlatformAssembler::GreaterThanOrEqual, &Runtime::method_compareGreaterEqual,
"Runtime::method_compareGreaterEqual", lhs);
}
-void Assembler::cmplt(int lhs)
+void BaselineAssembler::cmplt(int lhs)
{
cmp(PlatformAssembler::LessThan, &Runtime::method_compareLessThan,
"Runtime::method_compareLessThan", lhs);
}
-void Assembler::cmple(int lhs)
+void BaselineAssembler::cmple(int lhs)
{
cmp(PlatformAssembler::LessThanOrEqual, &Runtime::method_compareLessEqual,
"Runtime::method_compareLessEqual", lhs);
}
-void Assembler::cmpStrictEqual(int lhs)
+void BaselineAssembler::cmpStrictEqual(int lhs)
{
cmp(PlatformAssembler::Equal, &RuntimeHelpers::strictEqual,
"RuntimeHelpers::strictEqual", lhs);
}
-void Assembler::cmpStrictNotEqual(int lhs)
+void BaselineAssembler::cmpStrictNotEqual(int lhs)
{
cmp(PlatformAssembler::Equal, &RuntimeHelpers::strictEqual,
"RuntimeHelpers::strictEqual", lhs);
@@ -1960,206 +1362,126 @@ void Assembler::cmpStrictNotEqual(int lhs)
pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean);
}
-void Assembler::jump(int offset)
+void BaselineAssembler::jump(int offset)
{
- pasm()->patches.push_back({ pasm()->jump(), offset });
+ pasm()->addJumpToOffset(pasm()->jump(), offset);
}
-void Assembler::jumpTrue(int offset)
+void BaselineAssembler::jumpTrue(int offset)
{
pasm()->toBoolean([this, offset](PlatformAssembler::RegisterID resultReg) {
auto jump = pasm()->branch32(PlatformAssembler::NotEqual, TrustedImm32(0), resultReg);
- pasm()->patches.push_back({ jump, offset });
+ pasm()->addJumpToOffset(jump, offset);
});
}
-void Assembler::jumpFalse(int offset)
+void BaselineAssembler::jumpFalse(int offset)
{
pasm()->toBoolean([this, offset](PlatformAssembler::RegisterID resultReg) {
auto jump = pasm()->branch32(PlatformAssembler::Equal, TrustedImm32(0), resultReg);
- pasm()->patches.push_back({ jump, offset });
+ pasm()->addJumpToOffset(jump, offset);
});
}
-void Assembler::jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
+void BaselineAssembler::jumpNoException(int offset)
{
- pasm()->jumpStrictEqualStackSlotInt(lhs, rhs, offset);
+ auto jump = pasm()->branch32(
+ PlatformAssembler::Equal,
+ PlatformAssembler::Address(PlatformAssembler::EngineRegister,
+ offsetof(EngineBase, hasException)),
+ TrustedImm32(0));
+ pasm()->addJumpToOffset(jump, offset);
}
-void Assembler::jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
+void BaselineAssembler::jumpNotUndefined(int offset)
{
- pasm()->jumpStrictNotEqualStackSlotInt(lhs, rhs, offset);
+ pasm()->jumpNotUndefined(offset);
}
-void Assembler::prepareCallWithArgCount(int argc)
+void BaselineAssembler::prepareCallWithArgCount(int argc)
{
-#ifndef QT_NO_DEBUG
- Q_ASSERT(remainingArgcForCall == NoCall);
- remainingArgcForCall = argc;
-#endif
-
- if (argc > PlatformAssembler::ArgInRegCount) {
- argcOnStackForCall = int(WTF::roundUpToMultipleOf(16, size_t(argc - PlatformAssembler::ArgInRegCount) * PlatformAssembler::PointerSize));
- pasm()->subPtr(TrustedImm32(argcOnStackForCall), PlatformAssembler::StackPointerRegister);
- }
+ pasm()->prepareCallWithArgCount(argc);
}
-void Assembler::storeInstructionPointer(int instructionOffset)
+void BaselineAssembler::storeInstructionPointer(int instructionOffset)
{
- PlatformAssembler::Address addr(PlatformAssembler::CppStackFrameRegister,
- offsetof(QV4::CppStackFrame, instructionPointer));
- pasm()->store32(TrustedImm32(instructionOffset), addr);
+ pasm()->storeInstructionPointer(instructionOffset);
}
-Address argStackAddress(int arg)
+void BaselineAssembler::passAccumulatorAsArg(int arg)
{
- int offset = arg - PlatformAssembler::ArgInRegCount;
- Q_ASSERT(offset >= 0);
- return Address(PlatformAssembler::StackPointerRegister, offset * PlatformAssembler::PointerSize);
+ pasm()->passAccumulatorAsArg(arg);
}
-void Assembler::passAccumulatorAsArg(int arg)
+void BaselineAssembler::passFunctionAsArg(int arg)
{
-#ifndef QT_NO_DEBUG
- Q_ASSERT(arg < remainingArgcForCall);
- --remainingArgcForCall;
-#endif
-
- passAccumulatorAsArg_internal(arg, false);
+ pasm()->passFunctionAsArg(arg);
}
-void Assembler::passAccumulatorAsArg_internal(int arg, bool push)
+void BaselineAssembler::passEngineAsArg(int arg)
{
- if (arg < PlatformAssembler::ArgInRegCount) {
- pasm()->addPtr(TrustedImm32(offsetof(CallData, accumulator)),
- PlatformAssembler::JSStackFrameRegister,
- pasm()->registerForArg(arg));
- } else {
- pasm()->addPtr(TrustedImm32(offsetof(CallData, accumulator)),
- PlatformAssembler::JSStackFrameRegister,
- PlatformAssembler::ScratchRegister);
- if (push)
- pasm()->push(PlatformAssembler::ScratchRegister);
- else
- pasm()->storePtr(PlatformAssembler::ScratchRegister,
- argStackAddress(arg));
- }
+ pasm()->passEngineAsArg(arg);
}
-void Assembler::passFunctionAsArg(int arg)
+void BaselineAssembler::passJSSlotAsArg(int reg, int arg)
{
-#ifndef QT_NO_DEBUG
- Q_ASSERT(arg < remainingArgcForCall);
- --remainingArgcForCall;
-#endif
-
- if (arg < PlatformAssembler::ArgInRegCount) {
- pasm()->loadFunctionPtr(pasm()->registerForArg(arg));
- } else {
- pasm()->loadFunctionPtr(PlatformAssembler::ScratchRegister);
- pasm()->storePtr(PlatformAssembler::ScratchRegister,
- argStackAddress(arg));
- }
+ pasm()->passJSSlotAsArg(reg, arg);
}
-void Assembler::passEngineAsArg(int arg)
+void BaselineAssembler::passCppFrameAsArg(int arg)
{
-#ifndef QT_NO_DEBUG
- Q_ASSERT(arg < remainingArgcForCall);
- --remainingArgcForCall;
-#endif
-
- if (arg < PlatformAssembler::ArgInRegCount) {
- pasm()->move(PlatformAssembler::EngineRegister, pasm()->registerForArg(arg));
- } else {
- pasm()->storePtr(PlatformAssembler::EngineRegister, argStackAddress(arg));
- }
+ pasm()->passCppFrameAsArg(arg);
}
-void Assembler::passRegAsArg(int reg, int arg)
+void BaselineAssembler::passInt32AsArg(int value, int arg)
{
-#ifndef QT_NO_DEBUG
- Q_ASSERT(arg < remainingArgcForCall);
- --remainingArgcForCall;
-#endif
-
- if (arg < PlatformAssembler::ArgInRegCount) {
- pasm()->addPtr(TrustedImm32(reg * int(sizeof(QV4::Value))),
- PlatformAssembler::JSStackFrameRegister,
- pasm()->registerForArg(arg));
- } else {
- pasm()->addPtr(TrustedImm32(reg * int(sizeof(QV4::Value))),
- PlatformAssembler::JSStackFrameRegister,
- PlatformAssembler::ScratchRegister);
- pasm()->storePtr(PlatformAssembler::ScratchRegister,
- argStackAddress(arg));
- }
+ pasm()->passInt32AsArg(value, arg);
}
-void JIT::Assembler::passCppFrameAsArg(int arg)
+void BaselineAssembler::callRuntime(const char *functionName, const void *funcPtr, CallResultDestination dest)
{
-#ifndef QT_NO_DEBUG
- Q_ASSERT(arg < remainingArgcForCall);
- --remainingArgcForCall;
-#endif
-
- if (arg < PlatformAssembler::ArgInRegCount) {
- pasm()->move(PlatformAssembler::CppStackFrameRegister, pasm()->registerForArg(arg));
- } else {
- pasm()->store32(PlatformAssembler::CppStackFrameRegister, argStackAddress(arg));
- }
+ pasm()->callRuntime(functionName, funcPtr, dest);
}
-void Assembler::passInt32AsArg(int value, int arg)
+void BaselineAssembler::saveAccumulatorInFrame()
{
-#ifndef QT_NO_DEBUG
- Q_ASSERT(arg < remainingArgcForCall);
- --remainingArgcForCall;
-#endif
-
- if (arg < PlatformAssembler::ArgInRegCount) {
- pasm()->move(TrustedImm32(value), pasm()->registerForArg(arg));
- } else {
- pasm()->store32(TrustedImm32(value), argStackAddress(arg));
- }
+ pasm()->storeAccumulator(PlatformAssembler::Address(PlatformAssembler::JSStackFrameRegister,
+ offsetof(CallData, accumulator)));
}
-void Assembler::callRuntime(const char *functionName, const void *funcPtr,
- Assembler::CallResultDestination dest)
+static ReturnedValue TheJitIs__Tail_Calling__ToTheRuntimeSoTheJitFrameIsMissing(CppStackFrame *frame, ExecutionEngine *engine)
{
-#ifndef QT_NO_DEBUG
- Q_ASSERT(remainingArgcForCall == 0);
- remainingArgcForCall = NoCall;
-#endif
- pasm()->callRuntime(functionName, funcPtr, dest);
- if (argcOnStackForCall > 0) {
- pasm()->addPtr(TrustedImm32(argcOnStackForCall), PlatformAssembler::StackPointerRegister);
- argcOnStackForCall = 0;
- }
+ return Runtime::method_tailCall(frame, engine);
}
-void Assembler::saveAccumulatorInFrame()
+void BaselineAssembler::jsTailCall(int func, int thisObject, int argc, int argv)
{
- pasm()->storeAccumulator(PlatformAssembler::Address(PlatformAssembler::JSStackFrameRegister,
- offsetof(CallData, accumulator)));
+ Address tos = pasm()->jsAlloca(4);
+
+ int32_t argcOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_argc;
+ int32_t argvOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_argv;
+ int32_t thisOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_thisObject;
+ int32_t funcOffset = tos.offset + int32_t(sizeof(Value)) * Runtime::StackOffsets::tailCall_function;
+
+ pasm()->storeInt32AsValue(argc, Address(tos.base, argcOffset));
+ pasm()->storeInt32AsValue(argv, Address(tos.base, argvOffset));
+ pasm()->moveReg(regAddr(thisObject), Address(tos.base, thisOffset));
+ pasm()->moveReg(regAddr(func), Address(tos.base, funcOffset));
+ pasm()->tailCallRuntime("TheJitIs__Tail_Calling__ToTheRuntimeSoTheJitFrameIsMissing",
+ reinterpret_cast<void *>(TheJitIs__Tail_Calling__ToTheRuntimeSoTheJitFrameIsMissing));
}
-void Assembler::checkException()
+void BaselineAssembler::checkException()
{
- pasm()->addCatchyJump(
- pasm()->branch32(
- PlatformAssembler::NotEqual,
- PlatformAssembler::Address(PlatformAssembler::EngineRegister,
- offsetof(EngineBase, hasException)),
- TrustedImm32(0)));
+ pasm()->checkException();
}
-void Assembler::gotoCatchException()
+void BaselineAssembler::gotoCatchException()
{
pasm()->addCatchyJump(pasm()->jump());
}
-void Assembler::getException()
+void BaselineAssembler::getException()
{
Q_STATIC_ASSERT(sizeof(QV4::EngineBase::hasException) == 1);
@@ -2175,49 +1497,93 @@ void Assembler::getException()
pasm()->store8(TrustedImm32(0), hasExceptionAddr);
auto done = pasm()->jump();
nope.link(pasm());
- pasm()->loadValue(Primitive::emptyValue().asReturnedValue());
+ pasm()->loadValue(Value::emptyValue().asReturnedValue());
done.link(pasm());
}
-void Assembler::setException()
+void BaselineAssembler::setException()
{
+ auto noException = pasm()->jumpEmpty();
Address addr(PlatformAssembler::EngineRegister, offsetof(EngineBase, exceptionValue));
pasm()->loadPtr(addr, PlatformAssembler::ScratchRegister);
pasm()->storeAccumulator(Address(PlatformAssembler::ScratchRegister));
addr.offset = offsetof(EngineBase, hasException);
Q_STATIC_ASSERT(sizeof(QV4::EngineBase::hasException) == 1);
pasm()->store8(TrustedImm32(1), addr);
+ noException.link(pasm());
}
-void Assembler::setExceptionHandler(int offset)
+void BaselineAssembler::setUnwindHandler(int offset)
{
auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress());
- pasm()->ehTargets.push_back({ l, offset });
+ pasm()->addEHTarget(l, offset);
}
-void Assembler::clearExceptionHandler()
+void BaselineAssembler::clearUnwindHandler()
{
pasm()->storePtr(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress());
}
-void Assembler::pushCatchContext(int name, int reg)
+void JIT::BaselineAssembler::unwindDispatch()
{
- pasm()->copyReg(pasm()->contextAddress(), regAddr(reg));
- prepareCallWithArgCount(2);
- passInt32AsArg(name, 1);
- passRegAsArg(CallData::Context, 0);
- IN_JIT_GENERATE_RUNTIME_CALL(Runtime::method_createCatchContext, ResultInAccumulator);
+ checkException();
+ pasm()->load32(Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel)), PlatformAssembler::ScratchRegister);
+ auto noUnwind = pasm()->branch32(PlatformAssembler::Equal, PlatformAssembler::ScratchRegister, TrustedImm32(0));
+ pasm()->sub32(TrustedImm32(1), PlatformAssembler::ScratchRegister);
+ pasm()->store32(PlatformAssembler::ScratchRegister, Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel)));
+ auto jump = pasm()->branch32(PlatformAssembler::Equal, PlatformAssembler::ScratchRegister, TrustedImm32(0));
+ gotoCatchException();
+ jump.link(pasm());
+
+ pasm()->loadPtr(Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLabel)), PlatformAssembler::ScratchRegister);
+ pasm()->jump(PlatformAssembler::ScratchRegister);
+
+ noUnwind.link(pasm());
+}
+
+void JIT::BaselineAssembler::unwindToLabel(int level, int offset)
+{
+ auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLabel)));
+ pasm()->addEHTarget(l, offset);
+ pasm()->store32(TrustedImm32(level), Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel)));
+ gotoCatchException();
+}
+
+void BaselineAssembler::pushCatchContext(int index, int name)
+{
+ pasm()->prepareCallWithArgCount(3);
+ pasm()->passInt32AsArg(name, 2);
+ pasm()->passInt32AsArg(index, 1);
+ pasm()->passJSSlotAsArg(CallData::Context, 0);
+ ASM_GENERATE_RUNTIME_CALL(Runtime::method_createCatchContext, CallResultDestination::InAccumulator);
pasm()->storeAccumulator(pasm()->contextAddress());
}
-void Assembler::popContext(int reg)
+void BaselineAssembler::popContext()
+{
+ Heap::CallContext ctx;
+ Q_UNUSED(ctx)
+ pasm()->loadPointerFromValue(regAddr(CallData::Context), PlatformAssembler::ScratchRegister);
+ pasm()->loadPtr(Address(PlatformAssembler::ScratchRegister, ctx.outer.offset), PlatformAssembler::ScratchRegister);
+ pasm()->storeHeapObject(PlatformAssembler::ScratchRegister, regAddr(CallData::Context));
+}
+
+void BaselineAssembler::deadTemporalZoneCheck(int offsetForSavedIP, int variableName)
{
- pasm()->copyReg(regAddr(reg), pasm()->contextAddress());
+ auto valueIsAliveJump = pasm()->jumpNotEmpty();
+ storeInstructionPointer(offsetForSavedIP);
+ saveAccumulatorInFrame();
+ prepareCallWithArgCount(2);
+ passInt32AsArg(variableName, 1);
+ passEngineAsArg(0);
+ ASM_GENERATE_RUNTIME_CALL(Runtime::method_throwReferenceError, CallResultDestination::Ignore);
+ gotoCatchException();
+ valueIsAliveJump.link(pasm());
}
-void Assembler::ret()
+void BaselineAssembler::ret()
{
pasm()->generateFunctionExit();
}
diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4baselineassembler_p.h
index 37d4232a17..0aa508ae71 100644
--- a/src/qml/jit/qv4assembler_p.h
+++ b/src/qml/jit/qv4baselineassembler_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** 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.
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QV4ASSEMBLER_P_H
-#define QV4ASSEMBLER_P_H
+#ifndef QV4BASELINEASSEMBLER_P_H
+#define QV4BASELINEASSEMBLER_P_H
//
// W A R N I N G
@@ -63,22 +63,18 @@ namespace JIT {
#define JIT_STRINGIFYx(s) #s
#define JIT_STRINGIFY(s) JIT_STRINGIFYx(s)
-#define IN_JIT_GENERATE_RUNTIME_CALL(function, destination) \
+#define GENERATE_RUNTIME_CALL(function, destination) \
callRuntime(JIT_STRINGIFY(function), \
reinterpret_cast<void *>(&function), \
destination)
-#define JIT_GENERATE_RUNTIME_CALL(function, destination) \
- as->IN_JIT_GENERATE_RUNTIME_CALL(function, destination)
+#define GENERATE_TAIL_CALL(function) \
+ tailCallRuntime(JIT_STRINGIFY(function), \
+ reinterpret_cast<void *>(&function))
-class Assembler {
+class BaselineAssembler {
public:
- enum CallResultDestination {
- IgnoreResult,
- ResultInAccumulator,
- };
-
- Assembler(const Value* constantTable);
- ~Assembler();
+ BaselineAssembler(const Value* constantTable);
+ ~BaselineAssembler();
// codegen infrastructure
void generatePrologue();
@@ -90,12 +86,14 @@ public:
void loadConst(int constIndex);
void copyConst(int constIndex, int destReg);
void loadReg(int reg);
+ void moveReg(int sourceReg, int destReg);
void storeReg(int reg);
void loadLocal(int index, int level = 0);
void storeLocal(int index, int level = 0);
void loadString(int stringId);
void loadValue(ReturnedValue value);
void storeHeapObject(int reg);
+ void loadImport(int index);
// numeric ops
void unot();
@@ -140,8 +138,8 @@ public:
void jump(int offset);
void jumpTrue(int offset);
void jumpFalse(int offset);
- void jumpStrictEqualStackSlotInt(int lhs, int rhs, int offset);
- void jumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset);
+ void jumpNoException(int offset);
+ void jumpNotUndefined(int offset);
// stuff for runtime calls
void prepareCallWithArgCount(int argc);
@@ -149,21 +147,25 @@ public:
void passAccumulatorAsArg(int arg);
void passFunctionAsArg(int arg);
void passEngineAsArg(int arg);
- void passRegAsArg(int reg, int arg);
+ void passJSSlotAsArg(int reg, int arg);
void passCppFrameAsArg(int arg);
void passInt32AsArg(int value, int arg);
- void callRuntime(const char *functionName, const void *funcPtr, Assembler::CallResultDestination dest);
+ void callRuntime(const char *functionName, const void *funcPtr, CallResultDestination dest);
void saveAccumulatorInFrame();
+ void jsTailCall(int func, int thisObject, int argc, int argv);
// exception/context stuff
void checkException();
void gotoCatchException();
void getException();
void setException();
- void setExceptionHandler(int offset);
- void clearExceptionHandler();
- void pushCatchContext(int name, int reg);
- void popContext(int reg);
+ void setUnwindHandler(int offset);
+ void clearUnwindHandler();
+ void unwindDispatch();
+ void unwindToLabel(int level, int offset);
+ void pushCatchContext(int index, int name);
+ void popContext();
+ void deadTemporalZoneCheck(int offsetForSavedIP, int variableName);
// other stuff
void ret();
@@ -171,16 +173,9 @@ public:
protected:
void *d;
-#ifndef QT_NO_DEBUG
- enum { NoCall = -1 };
- int remainingArgcForCall = NoCall;
-#endif
- int argcOnStackForCall = 0;
-
private:
typedef unsigned(*CmpFunc)(const Value&,const Value&);
void cmp(int cond, CmpFunc function, const char *functionName, int lhs);
- void passAccumulatorAsArg_internal(int arg, bool push);
};
} // namespace JIT
@@ -188,4 +183,4 @@ private:
QT_END_NAMESPACE
-#endif // QV4ASSEMBLER_P_H
+#endif // QV4BASELINEASSEMBLER_P_H
diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp
new file mode 100644
index 0000000000..bbbe0c69c5
--- /dev/null
+++ b/src/qml/jit/qv4baselinejit.cpp
@@ -0,0 +1,1007 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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 "qv4baselinejit_p.h"
+#include "qv4jithelpers_p.h"
+#include "qv4baselineassembler_p.h"
+#include <private/qv4lookup_p.h>
+#include <private/qv4generatorobject_p.h>
+
+#ifdef V4_ENABLE_JIT
+
+QT_USE_NAMESPACE
+using namespace QV4;
+using namespace QV4::JIT;
+using namespace QV4::Moth;
+
+BaselineJIT::BaselineJIT(Function *function)
+ : function(function)
+ , as(new BaselineAssembler(function->compilationUnit->constants))
+{}
+
+BaselineJIT::~BaselineJIT()
+{}
+
+void BaselineJIT::generate()
+{
+// qDebug()<<"jitting" << function->name()->toQString();
+ const char *code = function->codeData;
+ uint len = function->compiledFunction->codeSize;
+ labels = collectLabelsInBytecode(code, len);
+
+ as->generatePrologue();
+ decode(code, len);
+ as->generateEpilogue();
+
+ as->link(function);
+// qDebug()<<"done";
+}
+
+#define STORE_IP() as->storeInstructionPointer(nextInstructionOffset())
+#define STORE_ACC() as->saveAccumulatorInFrame()
+#define BASELINEJIT_GENERATE_RUNTIME_CALL(function, destination) \
+ as->GENERATE_RUNTIME_CALL(function, destination)
+#define BASELINEJIT_GENERATE_TAIL_CALL(function) \
+ as->GENERATE_TAIL_CALL(function)
+
+void BaselineJIT::generate_Ret()
+{
+ as->ret();
+}
+
+void BaselineJIT::generate_Debug() { Q_UNREACHABLE(); }
+
+void BaselineJIT::generate_LoadConst(int index)
+{
+ as->loadConst(index);
+}
+
+void BaselineJIT::generate_LoadZero()
+{
+ as->loadValue(Encode(int(0)));
+}
+
+void BaselineJIT::generate_LoadTrue()
+{
+ as->loadValue(Encode(true));
+}
+
+void BaselineJIT::generate_LoadFalse()
+{
+ as->loadValue(Encode(false));
+}
+
+void BaselineJIT::generate_LoadNull()
+{
+ as->loadValue(Encode::null());
+}
+
+void BaselineJIT::generate_LoadUndefined()
+{
+ as->loadValue(Encode::undefined());
+}
+
+void BaselineJIT::generate_LoadInt(int value)
+{
+ //###
+ as->loadValue(Encode(value));
+}
+
+void BaselineJIT::generate_MoveConst(int constIndex, int destTemp)
+{
+ as->copyConst(constIndex, destTemp);
+}
+
+void BaselineJIT::generate_LoadReg(int reg)
+{
+ as->loadReg(reg);
+}
+
+void BaselineJIT::generate_StoreReg(int reg)
+{
+ as->storeReg(reg);
+}
+
+void BaselineJIT::generate_MoveReg(int srcReg, int destReg)
+{
+ // Don't clobber the accumulator.
+ as->moveReg(srcReg, destReg);
+}
+
+void BaselineJIT::generate_LoadImport(int index)
+{
+ as->loadImport(index);
+}
+
+void BaselineJIT::generate_LoadLocal(int index)
+{
+ as->loadLocal(index);
+}
+
+void BaselineJIT::generate_StoreLocal(int index)
+{
+ as->checkException();
+ as->storeLocal(index);
+}
+
+void BaselineJIT::generate_LoadScopedLocal(int scope, int index)
+{
+ as->loadLocal(index, scope);
+}
+
+void BaselineJIT::generate_StoreScopedLocal(int scope, int index)
+{
+ as->checkException();
+ as->storeLocal(index, scope);
+}
+
+void BaselineJIT::generate_LoadRuntimeString(int stringId)
+{
+ as->loadString(stringId);
+}
+
+void BaselineJIT::generate_MoveRegExp(int regExpId, int destReg)
+{
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(regExpId, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_regexpLiteral, CallResultDestination::InAccumulator);
+ as->storeReg(destReg);
+}
+
+void BaselineJIT::generate_LoadClosure(int value)
+{
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(value, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_closure, CallResultDestination::InAccumulator);
+}
+
+void BaselineJIT::generate_LoadName(int name)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(name, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadName, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_LoadGlobalLookup(int index)
+{
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(index, 2);
+ as->passEngineAsArg(1);
+ as->passFunctionAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::loadGlobalLookup, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_StoreNameSloppy(int name)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(3);
+ as->passAccumulatorAsArg(2);
+ as->passInt32AsArg(name, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeNameSloppy, CallResultDestination::Ignore);
+ as->checkException();
+}
+
+void BaselineJIT::generate_StoreNameStrict(int name)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(3);
+ as->passAccumulatorAsArg(2);
+ as->passInt32AsArg(name, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeNameStrict, CallResultDestination::Ignore);
+ as->checkException();
+}
+
+void BaselineJIT::generate_LoadElement(int base)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(3);
+ as->passAccumulatorAsArg(2);
+ as->passJSSlotAsArg(base, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadElement, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_StoreElement(int base, int index)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(4);
+ as->passAccumulatorAsArg(3);
+ as->passJSSlotAsArg(index, 2);
+ as->passJSSlotAsArg(base, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeElement, CallResultDestination::Ignore);
+ as->checkException();
+}
+
+void BaselineJIT::generate_LoadProperty(int name)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(name, 2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadProperty, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_GetLookup(int index)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(4);
+ as->passInt32AsArg(index, 3);
+ as->passAccumulatorAsArg(2);
+ as->passEngineAsArg(1);
+ as->passFunctionAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::getLookup, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_StoreProperty(int name, int base)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(4);
+ as->passAccumulatorAsArg(3);
+ as->passInt32AsArg(name, 2);
+ as->passJSSlotAsArg(base, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeProperty, CallResultDestination::Ignore);
+ as->checkException();
+}
+
+void BaselineJIT::generate_SetLookup(int index, int base)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(4);
+ as->passAccumulatorAsArg(3);
+ as->passJSSlotAsArg(base, 2);
+ as->passInt32AsArg(index, 1);
+ as->passFunctionAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL((function->isStrict() ? Helpers::setLookupStrict : Helpers::setLookupSloppy),
+ CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_LoadSuperProperty(int property)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(2);
+ as->passJSSlotAsArg(property, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadSuperProperty, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_StoreSuperProperty(int property)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(3);
+ as->passAccumulatorAsArg(2);
+ as->passJSSlotAsArg(property, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeSuperProperty, CallResultDestination::Ignore);
+ as->checkException();
+}
+
+
+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(Runtime::method_storeQmlScopeObjectProperty, CallResultDestination::Ignore);
+ as->checkException();
+}
+
+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(Runtime::method_storeQmlContextObjectProperty, CallResultDestination::Ignore);
+ as->checkException();
+}
+
+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(Runtime::method_loadQmlScopeObjectProperty, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+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(Runtime::method_loadQmlContextObjectProperty, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+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(Runtime::method_loadQmlIdObject, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_Yield()
+{
+ // #####
+ Q_UNREACHABLE();
+}
+
+void BaselineJIT::generate_YieldStar()
+{
+ // #####
+ Q_UNREACHABLE();
+}
+
+void BaselineJIT::generate_Resume(int)
+{
+ // #####
+ Q_UNREACHABLE();
+}
+
+void BaselineJIT::generate_CallValue(int name, int argc, int argv)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(4);
+ as->passInt32AsArg(argc, 3);
+ as->passJSSlotAsArg(argv, 2);
+ as->passJSSlotAsArg(name, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callValue, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, int argv)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(5);
+ as->passInt32AsArg(argc, 4);
+ as->passJSSlotAsArg(argv, 3);
+ as->passJSSlotAsArg(thisObject, 2);
+ as->passJSSlotAsArg(name, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callWithReceiver, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(5);
+ as->passInt32AsArg(argc, 4);
+ as->passJSSlotAsArg(argv, 3);
+ as->passInt32AsArg(name, 2);
+ as->passJSSlotAsArg(base, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callProperty, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(5);
+ as->passInt32AsArg(argc, 4);
+ as->passJSSlotAsArg(argv, 3);
+ as->passInt32AsArg(lookupIndex, 2);
+ as->passJSSlotAsArg(base, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callPropertyLookup, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(5);
+ as->passInt32AsArg(argc, 4);
+ as->passJSSlotAsArg(argv, 3);
+ as->passJSSlotAsArg(index, 2);
+ as->passJSSlotAsArg(base, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callElement, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_CallName(int name, int argc, int argv)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(4);
+ as->passInt32AsArg(argc, 3);
+ as->passJSSlotAsArg(argv, 2);
+ as->passInt32AsArg(name, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callName, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(argc, 2);
+ as->passJSSlotAsArg(argv, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callPossiblyDirectEval, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(4);
+ as->passInt32AsArg(argc, 3);
+ as->passJSSlotAsArg(argv, 2);
+ as->passInt32AsArg(index, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callGlobalLookup, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv)
+{
+ 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(Runtime::method_callQmlScopeObjectProperty, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv)
+{
+ 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(Runtime::method_callQmlContextObjectProperty, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+
+void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(5);
+ as->passInt32AsArg(argc, 4);
+ as->passJSSlotAsArg(argv, 3);
+ as->passJSSlotAsArg(thisObject, 2);
+ as->passJSSlotAsArg(func, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callWithSpread, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_TailCall(int func, int thisObject, int argc, int argv)
+{
+ STORE_IP();
+ as->jsTailCall(func, thisObject, argc, argv);
+}
+
+void BaselineJIT::generate_Construct(int func, int argc, int argv)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(5);
+ as->passInt32AsArg(argc, 4);
+ as->passJSSlotAsArg(argv, 3);
+ as->passAccumulatorAsArg(2);
+ as->passJSSlotAsArg(func, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_construct, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_ConstructWithSpread(int func, int argc, int argv)
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(5);
+ as->passInt32AsArg(argc, 4);
+ as->passJSSlotAsArg(argv, 3);
+ as->passAccumulatorAsArg(2);
+ as->passJSSlotAsArg(func, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_constructWithSpread, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_SetUnwindHandler(int offset)
+{
+ if (offset)
+ as->setUnwindHandler(absoluteOffsetForJump(offset));
+ else
+ as->clearUnwindHandler();
+}
+
+void BaselineJIT::generate_UnwindDispatch()
+{
+ as->unwindDispatch();
+}
+
+void BaselineJIT::generate_UnwindToLabel(int level, int offset)
+{
+ as->unwindToLabel(level, absoluteOffsetForJump(offset));
+}
+
+void BaselineJIT::generate_DeadTemporalZoneCheck(int name)
+{
+ as->deadTemporalZoneCheck(nextInstructionOffset(), name);
+}
+
+void BaselineJIT::generate_ThrowException()
+{
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_throwException, CallResultDestination::Ignore);
+ as->gotoCatchException();
+}
+
+void BaselineJIT::generate_GetException() { as->getException(); }
+void BaselineJIT::generate_SetException() { as->setException(); }
+
+void BaselineJIT::generate_CreateCallContext()
+{
+ as->prepareCallWithArgCount(1);
+ as->passCppFrameAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(ExecutionContext::newCallContext, CallResultDestination::Ignore); // keeps result in return value register
+ as->storeHeapObject(CallData::Context);
+}
+
+void BaselineJIT::generate_PushCatchContext(int index, int name) { as->pushCatchContext(index, name); }
+
+void BaselineJIT::generate_PushWithContext()
+{
+ STORE_IP();
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(2);
+ as->passJSSlotAsArg(0, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createWithContext, CallResultDestination::Ignore); // keeps result in return value register
+ as->checkException();
+ as->storeHeapObject(CallData::Context);
+}
+
+void BaselineJIT::generate_PushBlockContext(int index)
+{
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(index, 1);
+ as->passJSSlotAsArg(0, 0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::pushBlockContext, CallResultDestination::Ignore);
+}
+
+void BaselineJIT::generate_CloneBlockContext()
+{
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(1);
+ as->passJSSlotAsArg(CallData::Context, 0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::cloneBlockContext, CallResultDestination::Ignore);
+}
+
+void BaselineJIT::generate_PushScriptContext(int index)
+{
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(index, 2);
+ as->passEngineAsArg(1);
+ as->passJSSlotAsArg(0, 0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::pushScriptContext, CallResultDestination::Ignore);
+}
+
+void BaselineJIT::generate_PopScriptContext()
+{
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(2);
+ as->passEngineAsArg(1);
+ as->passJSSlotAsArg(0, 0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::popScriptContext, CallResultDestination::Ignore);
+}
+
+void BaselineJIT::generate_PopContext() { as->popContext(); }
+
+void BaselineJIT::generate_GetIterator(int iterator)
+{
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(iterator, 2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_getIterator, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_IteratorNext(int value, int done)
+{
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(3);
+ as->passJSSlotAsArg(value, 2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_iteratorNext, CallResultDestination::InAccumulator);
+ as->storeReg(done);
+ as->checkException();
+}
+
+void BaselineJIT::generate_IteratorNextForYieldStar(int iterator, int object)
+{
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(4);
+ as->passJSSlotAsArg(object, 3);
+ as->passJSSlotAsArg(iterator, 2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_iteratorNextForYieldStar, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_IteratorClose(int done)
+{
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(3);
+ as->passJSSlotAsArg(done, 2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_iteratorClose, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_DestructureRestElement()
+{
+ as->saveAccumulatorInFrame();
+ as->prepareCallWithArgCount(2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_destructureRestElement, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_DeleteProperty(int base, int index)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(3);
+ as->passJSSlotAsArg(index, 2);
+ as->passJSSlotAsArg(base, 1);
+ as->passFunctionAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::deleteProperty, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_DeleteName(int name)
+{
+ STORE_IP();
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(name, 1);
+ as->passFunctionAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::deleteName, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_TypeofName(int name)
+{
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(name, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_typeofName, CallResultDestination::InAccumulator);
+}
+
+void BaselineJIT::generate_TypeofValue()
+{
+ STORE_ACC();
+ as->prepareCallWithArgCount(2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_typeofValue, CallResultDestination::InAccumulator);
+}
+
+void BaselineJIT::generate_DeclareVar(int varName, int isDeletable)
+{
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(varName, 2);
+ as->passInt32AsArg(isDeletable, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_declareVar, CallResultDestination::Ignore);
+}
+
+void BaselineJIT::generate_DefineArray(int argc, int args)
+{
+ as->prepareCallWithArgCount(3);
+ as->passInt32AsArg(argc, 2);
+ as->passJSSlotAsArg(args, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_arrayLiteral, CallResultDestination::InAccumulator);
+}
+
+void BaselineJIT::generate_DefineObjectLiteral(int internalClassId, int argc, int args)
+{
+ as->prepareCallWithArgCount(4);
+ as->passJSSlotAsArg(args, 3);
+ as->passInt32AsArg(argc, 2);
+ as->passInt32AsArg(internalClassId, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_objectLiteral, CallResultDestination::InAccumulator);
+}
+
+void BaselineJIT::generate_CreateClass(int classIndex, int heritage, int computedNames)
+{
+ as->prepareCallWithArgCount(4);
+ as->passJSSlotAsArg(computedNames, 3);
+ as->passJSSlotAsArg(heritage, 2);
+ as->passInt32AsArg(classIndex, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createClass, CallResultDestination::InAccumulator);
+}
+
+void BaselineJIT::generate_CreateMappedArgumentsObject()
+{
+ as->prepareCallWithArgCount(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createMappedArgumentsObject,
+ CallResultDestination::InAccumulator);
+}
+
+void BaselineJIT::generate_CreateUnmappedArgumentsObject()
+{
+ as->prepareCallWithArgCount(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createUnmappedArgumentsObject,
+ CallResultDestination::InAccumulator);
+}
+
+void BaselineJIT::generate_CreateRestParameter(int argIndex)
+{
+ as->prepareCallWithArgCount(2);
+ as->passInt32AsArg(argIndex, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_createRestParameter, CallResultDestination::InAccumulator);
+}
+
+void BaselineJIT::generate_ConvertThisToObject()
+{
+ as->prepareCallWithArgCount(2);
+ as->passJSSlotAsArg(CallData::This, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::convertThisToObject, CallResultDestination::Ignore);
+ as->checkException();
+}
+
+void BaselineJIT::generate_LoadSuperConstructor()
+{
+ as->prepareCallWithArgCount(2);
+ as->passJSSlotAsArg(CallData::Function, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadSuperConstructor, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_ToObject()
+{
+ STORE_ACC();
+ as->prepareCallWithArgCount(2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::toObject, CallResultDestination::InAccumulator);
+ as->checkException();
+
+}
+
+void BaselineJIT::generate_Jump(int offset) { as->jump(absoluteOffsetForJump(offset)); }
+void BaselineJIT::generate_JumpTrue(int offset) { as->jumpTrue(absoluteOffsetForJump(offset)); }
+void BaselineJIT::generate_JumpFalse(int offset) { as->jumpFalse(absoluteOffsetForJump(offset)); }
+void BaselineJIT::generate_JumpNoException(int offset) { as->jumpNoException(absoluteOffsetForJump(offset)); }
+void BaselineJIT::generate_JumpNotUndefined(int offset) { as->jumpNotUndefined(absoluteOffsetForJump(offset)); }
+
+void BaselineJIT::generate_CmpEqNull() { as->cmpeqNull(); }
+void BaselineJIT::generate_CmpNeNull() { as->cmpneNull(); }
+void BaselineJIT::generate_CmpEqInt(int lhs) { as->cmpeqInt(lhs); }
+void BaselineJIT::generate_CmpNeInt(int lhs) { as->cmpneInt(lhs); }
+void BaselineJIT::generate_CmpEq(int lhs) { as->cmpeq(lhs); }
+void BaselineJIT::generate_CmpNe(int lhs) { as->cmpne(lhs); }
+void BaselineJIT::generate_CmpGt(int lhs) { as->cmpgt(lhs); }
+void BaselineJIT::generate_CmpGe(int lhs) { as->cmpge(lhs); }
+void BaselineJIT::generate_CmpLt(int lhs) { as->cmplt(lhs); }
+void BaselineJIT::generate_CmpLe(int lhs) { as->cmple(lhs); }
+void BaselineJIT::generate_CmpStrictEqual(int lhs) { as->cmpStrictEqual(lhs); }
+void BaselineJIT::generate_CmpStrictNotEqual(int lhs) { as->cmpStrictNotEqual(lhs); }
+
+void BaselineJIT::generate_CmpIn(int lhs)
+{
+ STORE_ACC();
+ as->prepareCallWithArgCount(3);
+ as->passAccumulatorAsArg(2);
+ as->passJSSlotAsArg(lhs, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_in, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_CmpInstanceOf(int lhs)
+{
+ STORE_ACC();
+ as->prepareCallWithArgCount(3);
+ as->passAccumulatorAsArg(2);
+ as->passJSSlotAsArg(lhs, 1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_instanceof, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+
+void BaselineJIT::generate_UNot() { as->unot(); }
+void BaselineJIT::generate_UPlus() { as->toNumber(); }
+void BaselineJIT::generate_UMinus() { as->uminus(); }
+void BaselineJIT::generate_UCompl() { as->ucompl(); }
+void BaselineJIT::generate_Increment() { as->inc(); }
+void BaselineJIT::generate_Decrement() { as->dec(); }
+void BaselineJIT::generate_Add(int lhs) { as->add(lhs); }
+
+void BaselineJIT::generate_BitAnd(int lhs) { as->bitAnd(lhs); }
+void BaselineJIT::generate_BitOr(int lhs) { as->bitOr(lhs); }
+void BaselineJIT::generate_BitXor(int lhs) { as->bitXor(lhs); }
+void BaselineJIT::generate_UShr(int lhs) { as->ushr(lhs); }
+void BaselineJIT::generate_Shr(int lhs) { as->shr(lhs); }
+void BaselineJIT::generate_Shl(int lhs) { as->shl(lhs); }
+
+void BaselineJIT::generate_BitAndConst(int rhs) { as->bitAndConst(rhs); }
+void BaselineJIT::generate_BitOrConst(int rhs) { as->bitOrConst(rhs); }
+void BaselineJIT::generate_BitXorConst(int rhs) { as->bitXorConst(rhs); }
+void BaselineJIT::generate_UShrConst(int rhs) { as->ushrConst(rhs); }
+void BaselineJIT::generate_ShrConst(int rhs) { as->shrConst(rhs); }
+void BaselineJIT::generate_ShlConst(int rhs) { as->shlConst(rhs); }
+
+void BaselineJIT::generate_Exp(int lhs) {
+ STORE_IP();
+ STORE_ACC();
+ as->prepareCallWithArgCount(2);
+ as->passAccumulatorAsArg(1);
+ as->passJSSlotAsArg(lhs, 0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::exp, CallResultDestination::InAccumulator);
+ as->checkException();
+}
+void BaselineJIT::generate_Mul(int lhs) { as->mul(lhs); }
+void BaselineJIT::generate_Div(int lhs) { as->div(lhs); }
+void BaselineJIT::generate_Mod(int lhs) { as->mod(lhs); }
+void BaselineJIT::generate_Sub(int lhs) { as->sub(lhs); }
+
+//void BaselineJIT::generate_BinopContext(int alu, int lhs)
+//{
+// auto engine = function->internalClass->engine;
+// void *op = engine->runtime.runtimeMethods[alu];
+// STORE_ACC();
+// as->passAccumulatorAsArg(2);
+// as->passRegAsArg(lhs, 1);
+// as->passEngineAsArg(0);
+// as->callRuntime("binopContext", op, CallResultDestination::InAccumulator);
+// as->checkException();
+//}
+
+void BaselineJIT::generate_LoadQmlContext(int result)
+{
+ as->prepareCallWithArgCount(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlContext, CallResultDestination::InAccumulator);
+ as->storeReg(result);
+}
+
+void BaselineJIT::generate_LoadQmlImportedScripts(int result)
+{
+ as->prepareCallWithArgCount(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlImportedScripts, CallResultDestination::InAccumulator);
+ as->storeReg(result);
+}
+
+void BaselineJIT::generate_InitializeBlockDeadTemporalZone(int firstReg, int count)
+{
+ as->loadValue(Value::emptyValue().rawValue());
+ for (int i = firstReg, end = firstReg + count; i < end; ++i)
+ as->storeReg(i);
+}
+
+void BaselineJIT::generate_ThrowOnNullOrUndefined()
+{
+ STORE_ACC();
+ as->prepareCallWithArgCount(2);
+ as->passAccumulatorAsArg(1);
+ as->passEngineAsArg(0);
+ BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::throwOnNullOrUndefined, CallResultDestination::Ignore);
+ as->checkException();
+}
+
+
+void BaselineJIT::startInstruction(Instr::Type /*instr*/)
+{
+ if (hasLabel())
+ as->addLabel(currentInstructionOffset());
+}
+
+void BaselineJIT::endInstruction(Instr::Type instr)
+{
+ Q_UNUSED(instr);
+}
+
+#endif // V4_ENABLE_JIT
diff --git a/src/qml/jit/qv4jit_p.h b/src/qml/jit/qv4baselinejit_p.h
index 5aebf78a8d..47ad274d23 100644
--- a/src/qml/jit/qv4jit_p.h
+++ b/src/qml/jit/qv4baselinejit_p.h
@@ -54,68 +54,23 @@
#include <private/qv4global_p.h>
#include <private/qv4function_p.h>
#include <private/qv4instr_moth_p.h>
+#include <private/qv4bytecodehandler_p.h>
//QT_REQUIRE_CONFIG(qml_jit);
-#define JIT_DEFINE_ARGS(nargs, ...) \
- MOTH_EXPAND_FOR_MSVC(JIT_DEFINE_ARGS##nargs(__VA_ARGS__))
-
-#define JIT_DEFINE_ARGS0()
-#define JIT_DEFINE_ARGS1(arg) \
- int arg
-#define JIT_DEFINE_ARGS2(arg1, arg2) \
- int arg1, \
- int arg2
-#define JIT_DEFINE_ARGS3(arg1, arg2, arg3) \
- int arg1, \
- int arg2, \
- int arg3
-#define JIT_DEFINE_ARGS4(arg1, arg2, arg3, arg4) \
- int arg1, \
- int arg2, \
- int arg3, \
- int arg4
-
-#define JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER_INSTRUCTION(name, nargs, ...) \
- virtual void generate_##name( \
- JIT_DEFINE_ARGS(nargs, __VA_ARGS__) \
- ) = 0;
-
-#define JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER(instr) \
- INSTR_##instr(JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER)
-
QT_BEGIN_NAMESPACE
namespace QV4 {
namespace JIT {
-class Assembler;
-
-class ByteCodeHandler
-{
-public:
- virtual ~ByteCodeHandler();
-
- void decode(const char *code, uint len);
-
- int instructionOffset() const { return _offset; }
-
-protected:
- FOR_EACH_MOTH_INSTR(JIT_DEFINE_VIRTUAL_BYTECODE_HANDLER)
-
- virtual void startInstruction(Moth::Instr::Type instr) = 0;
- virtual void endInstruction(Moth::Instr::Type instr) = 0;
-
-private:
- int _offset = 0;
-};
+class BaselineAssembler;
#ifdef V4_ENABLE_JIT
-class BaselineJIT final: public ByteCodeHandler
+class BaselineJIT final: public Moth::ByteCodeHandler
{
public:
BaselineJIT(QV4::Function *);
- virtual ~BaselineJIT();
+ virtual ~BaselineJIT() Q_DECL_OVERRIDE;
void generate();
@@ -132,6 +87,7 @@ public:
void generate_LoadReg(int reg) override;
void generate_StoreReg(int reg) override;
void generate_MoveReg(int srcReg, int destReg) override;
+ void generate_LoadImport(int index) override;
void generate_LoadLocal(int index) override;
void generate_StoreLocal(int index) override;
void generate_LoadScopedLocal(int scope, int index) override;
@@ -143,15 +99,14 @@ public:
void generate_LoadGlobalLookup(int index) override;
void generate_StoreNameSloppy(int name) override;
void generate_StoreNameStrict(int name) override;
- void generate_LoadElement(int base, int index) override;
- void generate_LoadElementA(int base) override;
+ void generate_LoadElement(int base) override;
void generate_StoreElement(int base, int index) override;
- void generate_LoadProperty(int name, int base) override;
- void generate_LoadPropertyA(int name) override;
- void generate_GetLookup(int index, int base) override;
- void generate_GetLookupA(int index) override;
+ void generate_LoadProperty(int name) override;
+ void generate_GetLookup(int index) override;
void generate_StoreProperty(int name, int base) override;
void generate_SetLookup(int index, int base) override;
+ void generate_LoadSuperProperty(int property) override;
+ void generate_StoreSuperProperty(int property) override;
void generate_StoreScopeObjectProperty(int base,
int propertyIndex) override;
void generate_StoreContextObjectProperty(int base,
@@ -161,7 +116,12 @@ public:
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;
+
void generate_CallValue(int name, int argc, int argv) override;
+ void generate_CallWithReceiver(int name, int thisObject, int argc, int argv) override;
void generate_CallProperty(int name, int base, int argc, int argv) override;
void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) override;
void generate_CallElement(int base, int index, int argc, int argv) override;
@@ -170,33 +130,49 @@ public:
void generate_CallGlobalLookup(int index, int argc, int argv) override;
void generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv) override;
void generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv) override;
- void generate_SetExceptionHandler(int offset) override;
+ void generate_CallWithSpread(int func, int thisObject, int argc, int argv) override;
+ void generate_TailCall(int func, int thisObject, int argc, int argv) override;
+ void generate_Construct(int func, int argc, int argv) override;
+ void generate_ConstructWithSpread(int func, int argc, int argv) override;
+ void generate_SetUnwindHandler(int offset) override;
+ void generate_UnwindDispatch() override;
+ void generate_UnwindToLabel(int level, int offset) override;
+ void generate_DeadTemporalZoneCheck(int name) override;
void generate_ThrowException() override;
void generate_GetException() override;
void generate_SetException() override;
void generate_CreateCallContext() override;
- void generate_PushCatchContext(int name, int reg) override;
- void generate_PushWithContext(int reg) override;
- void generate_PopContext(int reg) override;
- void generate_ForeachIteratorObject() override;
- void generate_ForeachNextPropertyName() override;
- void generate_DeleteMember(int member, int base) override;
- void generate_DeleteSubscript(int base, int index) override;
+ void generate_PushCatchContext(int index, int name) override;
+ void generate_PushWithContext() override;
+ void generate_PushBlockContext(int index) override;
+ void generate_CloneBlockContext() override;
+ void generate_PushScriptContext(int index) override;
+ void generate_PopScriptContext() override;
+ void generate_PopContext() override;
+ void generate_GetIterator(int iterator) override;
+ void generate_IteratorNext(int value, int done) override;
+ void generate_IteratorNextForYieldStar(int iterator, int object) override;
+ void generate_IteratorClose(int done) override;
+ void generate_DestructureRestElement() override;
+ void generate_DeleteProperty(int base, int index) override;
void generate_DeleteName(int name) override;
void generate_TypeofName(int name) override;
void generate_TypeofValue() override;
void generate_DeclareVar(int varName, int isDeletable) override;
void generate_DefineArray(int argc, int args) override;
- void generate_DefineObjectLiteral(int internalClassId, int arrayValueCount,
- int arrayGetterSetterCountAndFlags,
- int args) override;
+ void generate_DefineObjectLiteral(int internalClassId, int argc, int args) override;
+ void generate_CreateClass(int classIndex, int heritage, int computedNames) override;
void generate_CreateMappedArgumentsObject() override;
void generate_CreateUnmappedArgumentsObject() override;
+ void generate_CreateRestParameter(int argIndex) override;
void generate_ConvertThisToObject() override;
- void generate_Construct(int func, int argc, int argv) override;
+ void generate_LoadSuperConstructor() override;
+ void generate_ToObject() override;
void generate_Jump(int offset) override;
void generate_JumpTrue(int offset) override;
void generate_JumpFalse(int offset) override;
+ void generate_JumpNoException(int offset) override;
+ void generate_JumpNotUndefined(int offset) override;
void generate_CmpEqNull() override;
void generate_CmpNeNull() override;
void generate_CmpEqInt(int lhs) override;
@@ -211,10 +187,6 @@ public:
void generate_CmpStrictNotEqual(int lhs) override;
void generate_CmpIn(int lhs) override;
void generate_CmpInstanceOf(int lhs) override;
- void generate_JumpStrictEqualStackSlotInt(int lhs, int rhs,
- int offset) override;
- void generate_JumpStrictNotEqualStackSlotInt(int lhs, int rhs,
- int offset) override;
void generate_UNot() override;
void generate_UPlus() override;
void generate_UMinus() override;
@@ -234,26 +206,29 @@ public:
void generate_UShrConst(int rhs) override;
void generate_ShrConst(int rhs) override;
void generate_ShlConst(int rhs) override;
+ void generate_Exp(int lhs) override;
void generate_Mul(int lhs) override;
void generate_Div(int lhs) override;
void generate_Mod(int lhs) override;
void generate_Sub(int lhs) 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 startInstruction(Moth::Instr::Type instr) override;
void endInstruction(Moth::Instr::Type instr) override;
protected:
bool hasLabel() const
- { return std::find(labels.cbegin(), labels.cend(), instructionOffset()) != labels.cend(); }
+ { return std::find(labels.cbegin(), labels.cend(), currentInstructionOffset()) != labels.cend(); }
-private:
- void collectLabelsInBytecode();
+ int absoluteOffsetForJump(int relativeOffset) const
+ { return nextInstructionOffset() + relativeOffset; }
private:
QV4::Function *function;
- QScopedPointer<Assembler> as;
+ QScopedPointer<BaselineAssembler> as;
std::vector<int> labels;
};
#endif // V4_ENABLE_JIT
diff --git a/src/qml/jit/qv4jit.cpp b/src/qml/jit/qv4jit.cpp
deleted file mode 100644
index bc46c0ca1d..0000000000
--- a/src/qml/jit/qv4jit.cpp
+++ /dev/null
@@ -1,1328 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 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 "qv4jit_p.h"
-#include "qv4assembler_p.h"
-#include <private/qv4lookup_p.h>
-
-#ifdef V4_ENABLE_JIT
-
-QT_USE_NAMESPACE
-using namespace QV4;
-using namespace QV4::JIT;
-using namespace QV4::Moth;
-
-ByteCodeHandler::~ByteCodeHandler()
-{
-}
-
-#define DISPATCH_INSTRUCTION(name, nargs, ...) \
- generate_##name( \
- __VA_ARGS__ \
- );
-
-#define DECODE_AND_DISPATCH(instr) \
- { \
- INSTR_##instr(MOTH_DECODE_WITH_BASE) \
- Q_UNUSED(base_ptr); \
- startInstruction(Instr::Type::instr); \
- _offset = code - start; \
- INSTR_##instr(DISPATCH) \
- endInstruction(Instr::Type::instr); \
- continue; \
- }
-
-void ByteCodeHandler::decode(const char *code, uint len)
-{
- MOTH_JUMP_TABLE;
-
- const char *start = code;
- const char *end = code + len;
- while (code < end) {
- MOTH_DISPATCH()
-
- FOR_EACH_MOTH_INSTR(DECODE_AND_DISPATCH)
- }
-}
-
-#undef DECODE_AND_DISPATCH
-#undef DISPATCH_INSTRUCTION
-
-BaselineJIT::BaselineJIT(Function *function)
- : function(function)
- , as(new Assembler(function->compilationUnit->constants))
-{}
-
-BaselineJIT::~BaselineJIT()
-{}
-
-void BaselineJIT::generate()
-{
-// qDebug()<<"jitting" << function->name()->toQString();
- collectLabelsInBytecode();
-
- as->generatePrologue();
- decode(reinterpret_cast<const char *>(function->codeData), function->compiledFunction->codeSize);
- as->generateEpilogue();
-
- as->link(function);
-// qDebug()<<"done";
-}
-
-#define STORE_IP() as->storeInstructionPointer(instructionOffset())
-#define STORE_ACC() as->saveAccumulatorInFrame()
-
-void BaselineJIT::generate_Ret()
-{
- as->ret();
-}
-
-void BaselineJIT::generate_Debug() { Q_UNREACHABLE(); }
-
-void BaselineJIT::generate_LoadConst(int index)
-{
- as->loadConst(index);
-}
-
-void BaselineJIT::generate_LoadZero()
-{
- as->loadValue(Encode(int(0)));
-}
-
-void BaselineJIT::generate_LoadTrue()
-{
- as->loadValue(Encode(true));
-}
-
-void BaselineJIT::generate_LoadFalse()
-{
- as->loadValue(Encode(false));
-}
-
-void BaselineJIT::generate_LoadNull()
-{
- as->loadValue(Encode::null());
-}
-
-void BaselineJIT::generate_LoadUndefined()
-{
- as->loadValue(Encode::undefined());
-}
-
-void BaselineJIT::generate_LoadInt(int value)
-{
- //###
- as->loadValue(Encode(value));
-}
-
-void BaselineJIT::generate_MoveConst(int constIndex, int destTemp)
-{
- as->copyConst(constIndex, destTemp);
-}
-
-void BaselineJIT::generate_LoadReg(int reg)
-{
- as->loadReg(reg);
-}
-
-void BaselineJIT::generate_StoreReg(int reg)
-{
- as->storeReg(reg);
-}
-
-void BaselineJIT::generate_MoveReg(int srcReg, int destReg)
-{
- as->loadReg(srcReg);
- as->storeReg(destReg);
-}
-
-void BaselineJIT::generate_LoadLocal(int index)
-{
- as->loadLocal(index);
-}
-
-void BaselineJIT::generate_StoreLocal(int index)
-{
- as->checkException();
- as->storeLocal(index);
-}
-
-void BaselineJIT::generate_LoadScopedLocal(int scope, int index)
-{
- as->loadLocal(index, scope);
-}
-
-void BaselineJIT::generate_StoreScopedLocal(int scope, int index)
-{
- as->checkException();
- as->storeLocal(index, scope);
-}
-
-void BaselineJIT::generate_LoadRuntimeString(int stringId)
-{
- as->loadString(stringId);
-}
-
-void BaselineJIT::generate_MoveRegExp(int regExpId, int destReg)
-{
- as->prepareCallWithArgCount(2);
- as->passInt32AsArg(regExpId, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_regexpLiteral, Assembler::ResultInAccumulator);
- as->storeReg(destReg);
-}
-
-void BaselineJIT::generate_LoadClosure(int value)
-{
- as->prepareCallWithArgCount(2);
- as->passInt32AsArg(value, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_closure, Assembler::ResultInAccumulator);
-}
-
-void BaselineJIT::generate_LoadName(int name)
-{
- STORE_IP();
- as->prepareCallWithArgCount(2);
- as->passInt32AsArg(name, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadName, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-static ReturnedValue loadGlobalLookupHelper(ExecutionEngine *engine, QV4::Function *f, int index)
-{
- QV4::Lookup *l = f->compilationUnit->runtimeLookups + index;
- return l->globalGetter(l, engine);
-}
-
-void BaselineJIT::generate_LoadGlobalLookup(int index)
-{
- as->prepareCallWithArgCount(3);
- as->passInt32AsArg(index, 2);
- as->passFunctionAsArg(1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(loadGlobalLookupHelper, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_StoreNameSloppy(int name)
-{
- STORE_IP();
- STORE_ACC();
- as->prepareCallWithArgCount(3);
- as->passAccumulatorAsArg(2);
- as->passInt32AsArg(name, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeNameSloppy, Assembler::IgnoreResult);
- as->checkException();
-}
-
-void BaselineJIT::generate_StoreNameStrict(int name)
-{
- STORE_IP();
- STORE_ACC();
- as->prepareCallWithArgCount(3);
- as->passAccumulatorAsArg(2);
- as->passInt32AsArg(name, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeNameStrict, Assembler::IgnoreResult);
- as->checkException();
-}
-
-void BaselineJIT::generate_LoadElement(int base, int index)
-{
- STORE_IP();
- as->prepareCallWithArgCount(3);
- as->passRegAsArg(index, 2);
- as->passRegAsArg(base, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadElement, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_LoadElementA(int base)
-{
- STORE_IP();
- STORE_ACC();
- as->prepareCallWithArgCount(3);
- as->passAccumulatorAsArg(2);
- as->passRegAsArg(base, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadElement, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-static void storeElementHelper(QV4::Function *f, const Value &base, const Value &index, const Value &value)
-{
- auto engine = f->internalClass->engine;
- if (!Runtime::method_storeElement(engine, base, index, value) && f->isStrict())
- engine->throwTypeError();
-}
-
-void BaselineJIT::generate_StoreElement(int base, int index)
-{
- STORE_IP();
- STORE_ACC();
- as->prepareCallWithArgCount(4);
- as->passAccumulatorAsArg(3);
- as->passRegAsArg(index, 2);
- as->passRegAsArg(base, 1);
- as->passFunctionAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(storeElementHelper, Assembler::IgnoreResult);
- as->checkException();
-}
-
-void BaselineJIT::generate_LoadProperty(int name, int base)
-{
- STORE_IP();
- as->prepareCallWithArgCount(3);
- as->passInt32AsArg(name, 2);
- as->passRegAsArg(base, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadProperty, Assembler::ResultInAccumulator);
- as->checkException();
-}
-void BaselineJIT::generate_LoadPropertyA(int name)
-{
- STORE_IP();
- STORE_ACC();
- as->prepareCallWithArgCount(3);
- as->passInt32AsArg(name, 2);
- as->passAccumulatorAsArg(1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadProperty, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-static ReturnedValue getLookupHelper(ExecutionEngine *engine, QV4::Function *f, int index, const QV4::Value &base)
-{
- QV4::Lookup *l = f->compilationUnit->runtimeLookups + index;
- return l->getter(l, engine, base);
-}
-
-void BaselineJIT::generate_GetLookup(int index, int base)
-{
- STORE_IP();
- as->prepareCallWithArgCount(4);
- as->passRegAsArg(base, 3);
- as->passInt32AsArg(index, 2);
- as->passFunctionAsArg(1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(getLookupHelper, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_GetLookupA(int index)
-{
- STORE_IP();
- STORE_ACC();
- as->prepareCallWithArgCount(4);
- as->passAccumulatorAsArg(3);
- as->passInt32AsArg(index, 2);
- as->passFunctionAsArg(1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(getLookupHelper, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-static void storePropertyHelper(QV4::Function *f, const Value &base, int name, const Value &value)
-{
- auto engine = f->internalClass->engine;
- if (!Runtime::method_storeProperty(engine, base, name, value) && f->isStrict())
- engine->throwTypeError();
-}
-
-void BaselineJIT::generate_StoreProperty(int name, int base)
-{
- STORE_IP();
- STORE_ACC();
- as->prepareCallWithArgCount(4);
- as->passAccumulatorAsArg(3);
- as->passInt32AsArg(name, 2);
- as->passRegAsArg(base, 1);
- as->passFunctionAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(storePropertyHelper, Assembler::IgnoreResult);
- as->checkException();
-}
-
-static void setLookupHelper(QV4::Function *f, int index, QV4::Value &base, const QV4::Value &value)
-{
- ExecutionEngine *engine = f->internalClass->engine;
- QV4::Lookup *l = f->compilationUnit->runtimeLookups + index;
- if (!l->setter(l, engine, base, value) && f->isStrict())
- engine->throwTypeError();
-}
-
-void BaselineJIT::generate_SetLookup(int index, int base)
-{
- STORE_IP();
- STORE_ACC();
- as->prepareCallWithArgCount(4);
- as->passAccumulatorAsArg(3);
- as->passRegAsArg(base, 2);
- as->passInt32AsArg(index, 1);
- as->passFunctionAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(setLookupHelper, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_StoreScopeObjectProperty(int base, int propertyIndex)
-{
- STORE_ACC();
- as->prepareCallWithArgCount(4);
- as->passAccumulatorAsArg(3);
- as->passInt32AsArg(propertyIndex, 2);
- as->passRegAsArg(base, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeQmlScopeObjectProperty, Assembler::IgnoreResult);
- as->checkException();
-}
-
-void BaselineJIT::generate_StoreContextObjectProperty(int base, int propertyIndex)
-{
- STORE_ACC();
- as->prepareCallWithArgCount(4);
- as->passAccumulatorAsArg(3);
- as->passInt32AsArg(propertyIndex, 2);
- as->passRegAsArg(base, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_storeQmlContextObjectProperty, Assembler::IgnoreResult);
- as->checkException();
-}
-
-void BaselineJIT::generate_LoadScopeObjectProperty(int propertyIndex, int base, int captureRequired)
-{
- STORE_IP();
- as->prepareCallWithArgCount(4);
- as->passInt32AsArg(captureRequired, 3);
- as->passInt32AsArg(propertyIndex, 2);
- as->passRegAsArg(base, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlScopeObjectProperty, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_LoadContextObjectProperty(int propertyIndex, int base, int captureRequired)
-{
- STORE_IP();
- as->prepareCallWithArgCount(4);
- as->passInt32AsArg(captureRequired, 3);
- as->passInt32AsArg(propertyIndex, 2);
- as->passRegAsArg(base, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlContextObjectProperty, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_LoadIdObject(int index, int base)
-{
- STORE_IP();
- as->prepareCallWithArgCount(3);
- as->passInt32AsArg(index, 2);
- as->passRegAsArg(base, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlIdObject, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_CallValue(int name, int argc, int argv)
-{
- STORE_IP();
- as->prepareCallWithArgCount(4);
- as->passInt32AsArg(argc, 3);
- as->passRegAsArg(argv, 2);
- as->passRegAsArg(name, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_callValue, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv)
-{
- STORE_IP();
- as->prepareCallWithArgCount(5);
- as->passInt32AsArg(argc, 4);
- as->passRegAsArg(argv, 3);
- as->passInt32AsArg(name, 2);
- as->passRegAsArg(base, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_callProperty, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv)
-{
- STORE_IP();
- as->prepareCallWithArgCount(5);
- as->passInt32AsArg(argc, 4);
- as->passRegAsArg(argv, 3);
- as->passInt32AsArg(lookupIndex, 2);
- as->passRegAsArg(base, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_callPropertyLookup, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv)
-{
- STORE_IP();
- as->prepareCallWithArgCount(5);
- as->passInt32AsArg(argc, 4);
- as->passRegAsArg(argv, 3);
- as->passRegAsArg(index, 2);
- as->passRegAsArg(base, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_callElement, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_CallName(int name, int argc, int argv)
-{
- STORE_IP();
- as->prepareCallWithArgCount(4);
- as->passInt32AsArg(argc, 3);
- as->passRegAsArg(argv, 2);
- as->passInt32AsArg(name, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_callName, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv)
-{
- STORE_IP();
- as->prepareCallWithArgCount(3);
- as->passInt32AsArg(argc, 2);
- as->passRegAsArg(argv, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_callPossiblyDirectEval, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv)
-{
- STORE_IP();
- as->prepareCallWithArgCount(4);
- as->passInt32AsArg(argc, 3);
- as->passRegAsArg(argv, 2);
- as->passInt32AsArg(index, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_callGlobalLookup, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv)
-{
- STORE_IP();
- as->prepareCallWithArgCount(5);
- as->passInt32AsArg(argc, 4);
- as->passRegAsArg(argv, 3);
- as->passInt32AsArg(propIdx, 2);
- as->passRegAsArg(base, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_callQmlScopeObjectProperty, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv)
-{
- STORE_IP();
- as->prepareCallWithArgCount(5);
- as->passInt32AsArg(argc, 4);
- as->passRegAsArg(argv, 3);
- as->passInt32AsArg(propIdx, 2);
- as->passRegAsArg(base, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_callQmlContextObjectProperty, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_SetExceptionHandler(int offset)
-{
- if (offset)
- as->setExceptionHandler(instructionOffset() + offset);
- else
- as->clearExceptionHandler();
-}
-
-void BaselineJIT::generate_ThrowException()
-{
- STORE_IP();
- STORE_ACC();
- as->prepareCallWithArgCount(2);
- as->passAccumulatorAsArg(1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_throwException, Assembler::IgnoreResult);
- as->gotoCatchException();
-}
-
-void BaselineJIT::generate_GetException() { as->getException(); }
-void BaselineJIT::generate_SetException() { as->setException(); }
-
-void BaselineJIT::generate_CreateCallContext()
-{
- as->prepareCallWithArgCount(1);
- as->passCppFrameAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(ExecutionContext::newCallContext, Assembler::IgnoreResult); // keeps result in return value register
- as->storeHeapObject(CallData::Context);
-}
-
-void BaselineJIT::generate_PushCatchContext(int name, int reg) { as->pushCatchContext(name, reg); }
-
-static void pushWithContextHelper(ExecutionEngine *engine, QV4::Value *stack, int reg)
-{
- QV4::Value &accumulator = stack[CallData::Accumulator];
- accumulator = accumulator.toObject(engine);
- if (engine->hasException)
- return;
- stack[reg] = stack[CallData::Context];
- ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
- stack[CallData::Context] = Runtime::method_createWithContext(c, accumulator);
-}
-
-void BaselineJIT::generate_PushWithContext(int reg)
-{
- STORE_IP();
- as->saveAccumulatorInFrame();
- as->prepareCallWithArgCount(3);
- as->passInt32AsArg(reg, 2);
- as->passRegAsArg(0, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(pushWithContextHelper, Assembler::IgnoreResult);
- as->checkException();
-}
-
-void BaselineJIT::generate_PopContext(int reg) { as->popContext(reg); }
-
-void BaselineJIT::generate_ForeachIteratorObject()
-{
- as->saveAccumulatorInFrame();
- as->prepareCallWithArgCount(2);
- as->passAccumulatorAsArg(1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_foreachIterator, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_ForeachNextPropertyName()
-{
- as->saveAccumulatorInFrame();
- as->prepareCallWithArgCount(1);
- as->passAccumulatorAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_foreachNextPropertyName,
- Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-static ReturnedValue deleteMemberHelper(QV4::Function *function, const QV4::Value &base, int member)
-{
- auto engine = function->internalClass->engine;
- if (!Runtime::method_deleteMember(engine, base, member)) {
- if (function->isStrict())
- engine->throwTypeError();
- return Encode(false);
- } else {
- return Encode(true);
- }
-}
-
-void BaselineJIT::generate_DeleteMember(int member, int base)
-{
- STORE_IP();
- as->prepareCallWithArgCount(3);
- as->passInt32AsArg(member, 2);
- as->passRegAsArg(base, 1);
- as->passFunctionAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(deleteMemberHelper, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-static ReturnedValue deleteSubscriptHelper(QV4::Function *function, const QV4::Value &base, const QV4::Value &index)
-{
- auto engine = function->internalClass->engine;
- if (!Runtime::method_deleteElement(engine, base, index)) {
- if (function->isStrict())
- engine->throwTypeError();
- return Encode(false);
- } else {
- return Encode(true);
- }
-}
-
-void BaselineJIT::generate_DeleteSubscript(int base, int index)
-{
- STORE_IP();
- as->prepareCallWithArgCount(3);
- as->passRegAsArg(index, 2);
- as->passRegAsArg(base, 1);
- as->passFunctionAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(deleteSubscriptHelper, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-static ReturnedValue deleteNameHelper(QV4::Function *function, int name)
-{
- auto engine = function->internalClass->engine;
- if (!Runtime::method_deleteName(engine, name)) {
- if (function->isStrict())
- engine->throwTypeError();
- return Encode(false);
- } else {
- return Encode(true);
- }
-}
-
-void BaselineJIT::generate_DeleteName(int name)
-{
- STORE_IP();
- as->prepareCallWithArgCount(2);
- as->passInt32AsArg(name, 1);
- as->passFunctionAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(deleteNameHelper, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_TypeofName(int name)
-{
- as->prepareCallWithArgCount(2);
- as->passInt32AsArg(name, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_typeofName, Assembler::ResultInAccumulator);
-}
-
-void BaselineJIT::generate_TypeofValue()
-{
- STORE_ACC();
- as->prepareCallWithArgCount(2);
- as->passAccumulatorAsArg(1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_typeofValue, Assembler::ResultInAccumulator);
-}
-
-void BaselineJIT::generate_DeclareVar(int varName, int isDeletable)
-{
- as->prepareCallWithArgCount(3);
- as->passInt32AsArg(varName, 2);
- as->passInt32AsArg(isDeletable, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_declareVar, Assembler::IgnoreResult);
-}
-
-void BaselineJIT::generate_DefineArray(int argc, int args)
-{
- as->prepareCallWithArgCount(3);
- as->passInt32AsArg(argc, 2);
- as->passRegAsArg(args, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_arrayLiteral, Assembler::ResultInAccumulator);
-}
-
-void BaselineJIT::generate_DefineObjectLiteral(int internalClassId, int arrayValueCount,
- int arrayGetterSetterCountAndFlags, int args)
-{
- as->prepareCallWithArgCount(5);
- as->passInt32AsArg(arrayGetterSetterCountAndFlags, 4);
- as->passInt32AsArg(arrayValueCount, 3);
- as->passInt32AsArg(internalClassId, 2);
- as->passRegAsArg(args, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_objectLiteral, Assembler::ResultInAccumulator);
-}
-void BaselineJIT::generate_CreateMappedArgumentsObject()
-{
- as->prepareCallWithArgCount(1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_createMappedArgumentsObject,
- Assembler::ResultInAccumulator);
-}
-
-void BaselineJIT::generate_CreateUnmappedArgumentsObject()
-{
- as->prepareCallWithArgCount(1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_createUnmappedArgumentsObject,
- Assembler::ResultInAccumulator);
-}
-
-static void convertThisToObjectHelper(ExecutionEngine *engine, Value *t)
-{
- if (!t->isObject()) {
- if (t->isNullOrUndefined()) {
- *t = engine->globalObject->asReturnedValue();
- } else {
- *t = t->toObject(engine)->asReturnedValue();
- }
- }
-}
-
-void BaselineJIT::generate_ConvertThisToObject()
-{
- as->prepareCallWithArgCount(2);
- as->passRegAsArg(CallData::This, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(convertThisToObjectHelper, Assembler::IgnoreResult);
- as->checkException();
-}
-
-void BaselineJIT::generate_Construct(int func, int argc, int argv)
-{
- STORE_IP();
- as->prepareCallWithArgCount(4);
- as->passInt32AsArg(argc, 3);
- as->passRegAsArg(argv, 2);
- as->passRegAsArg(func, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_construct, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_Jump(int offset) { as->jump(instructionOffset() + offset); }
-void BaselineJIT::generate_JumpTrue(int offset) { as->jumpTrue(instructionOffset() + offset); }
-void BaselineJIT::generate_JumpFalse(int offset) { as->jumpFalse(instructionOffset() + offset); }
-
-void BaselineJIT::generate_CmpEqNull() { as->cmpeqNull(); }
-void BaselineJIT::generate_CmpNeNull() { as->cmpneNull(); }
-void BaselineJIT::generate_CmpEqInt(int lhs) { as->cmpeqInt(lhs); }
-void BaselineJIT::generate_CmpNeInt(int lhs) { as->cmpneInt(lhs); }
-void BaselineJIT::generate_CmpEq(int lhs) { as->cmpeq(lhs); }
-void BaselineJIT::generate_CmpNe(int lhs) { as->cmpne(lhs); }
-void BaselineJIT::generate_CmpGt(int lhs) { as->cmpgt(lhs); }
-void BaselineJIT::generate_CmpGe(int lhs) { as->cmpge(lhs); }
-void BaselineJIT::generate_CmpLt(int lhs) { as->cmplt(lhs); }
-void BaselineJIT::generate_CmpLe(int lhs) { as->cmple(lhs); }
-void BaselineJIT::generate_CmpStrictEqual(int lhs) { as->cmpStrictEqual(lhs); }
-void BaselineJIT::generate_CmpStrictNotEqual(int lhs) { as->cmpStrictNotEqual(lhs); }
-
-void BaselineJIT::generate_CmpIn(int lhs)
-{
- STORE_ACC();
- as->prepareCallWithArgCount(3);
- as->passAccumulatorAsArg(2);
- as->passRegAsArg(lhs, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_in, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_CmpInstanceOf(int lhs)
-{
- STORE_ACC();
- as->prepareCallWithArgCount(3);
- as->passAccumulatorAsArg(2);
- as->passRegAsArg(lhs, 1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_instanceof, Assembler::ResultInAccumulator);
- as->checkException();
-}
-
-void BaselineJIT::generate_JumpStrictEqualStackSlotInt(int lhs, int rhs, int offset)
-{
- as->jumpStrictEqualStackSlotInt(lhs, rhs, instructionOffset() + offset);
-}
-
-void BaselineJIT::generate_JumpStrictNotEqualStackSlotInt(int lhs, int rhs, int offset)
-{
- as->jumpStrictNotEqualStackSlotInt(lhs, rhs, instructionOffset() + offset);
-}
-
-void BaselineJIT::generate_UNot() { as->unot(); }
-void BaselineJIT::generate_UPlus() { as->toNumber(); }
-void BaselineJIT::generate_UMinus() { as->uminus(); }
-void BaselineJIT::generate_UCompl() { as->ucompl(); }
-void BaselineJIT::generate_Increment() { as->inc(); }
-void BaselineJIT::generate_Decrement() { as->dec(); }
-void BaselineJIT::generate_Add(int lhs) { as->add(lhs); }
-
-void BaselineJIT::generate_BitAnd(int lhs) { as->bitAnd(lhs); }
-void BaselineJIT::generate_BitOr(int lhs) { as->bitOr(lhs); }
-void BaselineJIT::generate_BitXor(int lhs) { as->bitXor(lhs); }
-void BaselineJIT::generate_UShr(int lhs) { as->ushr(lhs); }
-void BaselineJIT::generate_Shr(int lhs) { as->shr(lhs); }
-void BaselineJIT::generate_Shl(int lhs) { as->shl(lhs); }
-
-void BaselineJIT::generate_BitAndConst(int rhs) { as->bitAndConst(rhs); }
-void BaselineJIT::generate_BitOrConst(int rhs) { as->bitOrConst(rhs); }
-void BaselineJIT::generate_BitXorConst(int rhs) { as->bitXorConst(rhs); }
-void BaselineJIT::generate_UShrConst(int rhs) { as->ushrConst(rhs); }
-void BaselineJIT::generate_ShrConst(int rhs) { as->shrConst(rhs); }
-void BaselineJIT::generate_ShlConst(int rhs) { as->shlConst(rhs); }
-
-void BaselineJIT::generate_Mul(int lhs) { as->mul(lhs); }
-void BaselineJIT::generate_Div(int lhs) { as->div(lhs); }
-void BaselineJIT::generate_Mod(int lhs) { as->mod(lhs); }
-void BaselineJIT::generate_Sub(int lhs) { as->sub(lhs); }
-
-//void BaselineJIT::generate_BinopContext(int alu, int lhs)
-//{
-// auto engine = function->internalClass->engine;
-// void *op = engine->runtime.runtimeMethods[alu];
-// STORE_ACC();
-// as->passAccumulatorAsArg(2);
-// as->passRegAsArg(lhs, 1);
-// as->passEngineAsArg(0);
-// as->callRuntime("binopContext", op, Assembler::ResultInAccumulator);
-// as->checkException();
-//}
-
-void BaselineJIT::generate_LoadQmlContext(int result)
-{
- as->prepareCallWithArgCount(1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlContext, Assembler::ResultInAccumulator);
- as->storeReg(result);
-}
-
-void BaselineJIT::generate_LoadQmlImportedScripts(int result)
-{
- as->prepareCallWithArgCount(1);
- as->passEngineAsArg(0);
- JIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlImportedScripts, Assembler::ResultInAccumulator);
- as->storeReg(result);
-}
-
-void BaselineJIT::startInstruction(Instr::Type /*instr*/)
-{
- if (hasLabel())
- as->addLabel(instructionOffset());
-}
-
-void BaselineJIT::endInstruction(Instr::Type instr)
-{
- Q_UNUSED(instr);
-}
-
-#define MOTH_UNUSED_ARGS0()
-#define MOTH_UNUSED_ARGS1(arg) \
- Q_UNUSED(arg);
-#define MOTH_UNUSED_ARGS2(arg1, arg2) \
- Q_UNUSED(arg1); \
- Q_UNUSED(arg2);
-#define MOTH_UNUSED_ARGS3(arg1, arg2, arg3) \
- Q_UNUSED(arg1); \
- Q_UNUSED(arg2); \
- Q_UNUSED(arg3);
-#define MOTH_UNUSED_ARGS4(arg1, arg2, arg3, arg4) \
- Q_UNUSED(arg1); \
- Q_UNUSED(arg2); \
- Q_UNUSED(arg3); \
- Q_UNUSED(arg4);
-
-#define MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, ...) \
- MOTH_EXPAND_FOR_MSVC(MOTH_UNUSED_ARGS##nargs(__VA_ARGS__))
-
-#define MOTH_MARK_ARGS_UNUSED_INSTRUCTION(name, nargs, ...) \
- MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, __VA_ARGS__)
-
-#define MOTH_BEGIN_INSTR(instr) \
- { \
- INSTR_##instr(MOTH_DECODE_WITH_BASE) \
- INSTR_##instr(MOTH_MARK_ARGS_UNUSED) \
- Q_UNUSED(base_ptr);
-
-#define MOTH_END_INSTR(instr) \
- continue; \
- }
-
-void BaselineJIT::collectLabelsInBytecode()
-{
- MOTH_JUMP_TABLE;
-
- const auto addLabel = [&](int offset) {
- Q_ASSERT(offset >= 0 && offset < static_cast<int>(function->compiledFunction->codeSize));
- labels.push_back(offset);
- };
-
- const char *code = reinterpret_cast<const char *>(function->codeData);
- const char *start = code;
- const char *end = code + function->compiledFunction->codeSize;
- while (code < end) {
- MOTH_DISPATCH()
- Q_UNREACHABLE();
-
- MOTH_BEGIN_INSTR(LoadReg)
- MOTH_END_INSTR(LoadReg)
-
- MOTH_BEGIN_INSTR(StoreReg)
- MOTH_END_INSTR(StoreReg)
-
- MOTH_BEGIN_INSTR(MoveReg)
- MOTH_END_INSTR(MoveReg)
-
- MOTH_BEGIN_INSTR(LoadConst)
- MOTH_END_INSTR(LoadConst)
-
- MOTH_BEGIN_INSTR(LoadNull)
- MOTH_END_INSTR(LoadNull)
-
- MOTH_BEGIN_INSTR(LoadZero)
- MOTH_END_INSTR(LoadZero)
-
- MOTH_BEGIN_INSTR(LoadTrue)
- MOTH_END_INSTR(LoadTrue)
-
- MOTH_BEGIN_INSTR(LoadFalse)
- MOTH_END_INSTR(LoadFalse)
-
- MOTH_BEGIN_INSTR(LoadUndefined)
- MOTH_END_INSTR(LoadUndefined)
-
- MOTH_BEGIN_INSTR(LoadInt)
- MOTH_END_INSTR(LoadInt)
-
- MOTH_BEGIN_INSTR(MoveConst)
- MOTH_END_INSTR(MoveConst)
-
- MOTH_BEGIN_INSTR(LoadLocal)
- MOTH_END_INSTR(LoadLocal)
-
- MOTH_BEGIN_INSTR(StoreLocal)
- MOTH_END_INSTR(StoreLocal)
-
- MOTH_BEGIN_INSTR(LoadScopedLocal)
- MOTH_END_INSTR(LoadScopedLocal)
-
- MOTH_BEGIN_INSTR(StoreScopedLocal)
- MOTH_END_INSTR(StoreScopedLocal)
-
- MOTH_BEGIN_INSTR(LoadRuntimeString)
- MOTH_END_INSTR(LoadRuntimeString)
-
- MOTH_BEGIN_INSTR(MoveRegExp)
- MOTH_END_INSTR(MoveRegExp)
-
- MOTH_BEGIN_INSTR(LoadClosure)
- MOTH_END_INSTR(LoadClosure)
-
- MOTH_BEGIN_INSTR(LoadName)
- MOTH_END_INSTR(LoadName)
-
- MOTH_BEGIN_INSTR(LoadGlobalLookup)
- MOTH_END_INSTR(LoadGlobalLookup)
-
- MOTH_BEGIN_INSTR(StoreNameSloppy)
- MOTH_END_INSTR(StoreNameSloppy)
-
- MOTH_BEGIN_INSTR(StoreNameStrict)
- MOTH_END_INSTR(StoreNameStrict)
-
- MOTH_BEGIN_INSTR(LoadElement)
- MOTH_END_INSTR(LoadElement)
-
- MOTH_BEGIN_INSTR(LoadElementA)
- MOTH_END_INSTR(LoadElement)
-
- MOTH_BEGIN_INSTR(StoreElement)
- MOTH_END_INSTR(StoreElement)
-
- MOTH_BEGIN_INSTR(LoadProperty)
- MOTH_END_INSTR(LoadProperty)
-
- MOTH_BEGIN_INSTR(LoadPropertyA)
- MOTH_END_INSTR(LoadElementA)
-
- MOTH_BEGIN_INSTR(GetLookup)
- MOTH_END_INSTR(GetLookup)
-
- MOTH_BEGIN_INSTR(GetLookupA)
- MOTH_END_INSTR(GetLookupA)
-
- MOTH_BEGIN_INSTR(StoreProperty)
- MOTH_END_INSTR(StoreProperty)
-
- MOTH_BEGIN_INSTR(SetLookup)
- MOTH_END_INSTR(SetLookup)
-
- MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
- MOTH_END_INSTR(StoreScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadScopeObjectProperty)
- MOTH_END_INSTR(LoadScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(StoreContextObjectProperty)
- MOTH_END_INSTR(StoreContextObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadContextObjectProperty)
- MOTH_END_INSTR(LoadContextObjectProperty)
-
- MOTH_BEGIN_INSTR(LoadIdObject)
- MOTH_END_INSTR(LoadIdObject)
-
- MOTH_BEGIN_INSTR(CallValue)
- MOTH_END_INSTR(CallValue)
-
- MOTH_BEGIN_INSTR(CallProperty)
- MOTH_END_INSTR(CallProperty)
-
- MOTH_BEGIN_INSTR(CallPropertyLookup)
- MOTH_END_INSTR(CallPropertyLookup)
-
- MOTH_BEGIN_INSTR(CallElement)
- MOTH_END_INSTR(CallElement)
-
- MOTH_BEGIN_INSTR(CallName)
- MOTH_END_INSTR(CallName)
-
- MOTH_BEGIN_INSTR(CallPossiblyDirectEval)
- MOTH_END_INSTR(CallPossiblyDirectEval)
-
- MOTH_BEGIN_INSTR(CallGlobalLookup)
- MOTH_END_INSTR(CallGlobalLookup)
-
- MOTH_BEGIN_INSTR(CallScopeObjectProperty)
- MOTH_END_INSTR(CallScopeObjectProperty)
-
- MOTH_BEGIN_INSTR(CallContextObjectProperty)
- MOTH_END_INSTR(CallContextObjectProperty)
-
- MOTH_BEGIN_INSTR(SetExceptionHandler)
- addLabel(code - start + offset);
- MOTH_END_INSTR(SetExceptionHandler)
-
- MOTH_BEGIN_INSTR(ThrowException)
- MOTH_END_INSTR(ThrowException)
-
- MOTH_BEGIN_INSTR(GetException)
- MOTH_END_INSTR(HasException)
-
- MOTH_BEGIN_INSTR(SetException)
- MOTH_END_INSTR(SetExceptionFlag)
-
- MOTH_BEGIN_INSTR(CreateCallContext)
- MOTH_END_INSTR(CreateCallContext)
-
- MOTH_BEGIN_INSTR(PushCatchContext)
- MOTH_END_INSTR(PushCatchContext)
-
- MOTH_BEGIN_INSTR(PushWithContext)
- MOTH_END_INSTR(PushWithContext)
-
- MOTH_BEGIN_INSTR(PopContext)
- MOTH_END_INSTR(PopContext)
-
- MOTH_BEGIN_INSTR(ForeachIteratorObject)
- MOTH_END_INSTR(ForeachIteratorObject)
-
- MOTH_BEGIN_INSTR(ForeachNextPropertyName)
- MOTH_END_INSTR(ForeachNextPropertyName)
-
- MOTH_BEGIN_INSTR(DeleteMember)
- MOTH_END_INSTR(DeleteMember)
-
- MOTH_BEGIN_INSTR(DeleteSubscript)
- MOTH_END_INSTR(DeleteSubscript)
-
- MOTH_BEGIN_INSTR(DeleteName)
- MOTH_END_INSTR(DeleteName)
-
- MOTH_BEGIN_INSTR(TypeofName)
- MOTH_END_INSTR(TypeofName)
-
- MOTH_BEGIN_INSTR(TypeofValue)
- MOTH_END_INSTR(TypeofValue)
-
- MOTH_BEGIN_INSTR(DeclareVar)
- MOTH_END_INSTR(DeclareVar)
-
- MOTH_BEGIN_INSTR(DefineArray)
- MOTH_END_INSTR(DefineArray)
-
- MOTH_BEGIN_INSTR(DefineObjectLiteral)
- MOTH_END_INSTR(DefineObjectLiteral)
-
- MOTH_BEGIN_INSTR(CreateMappedArgumentsObject)
- MOTH_END_INSTR(CreateMappedArgumentsObject)
-
- MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject)
- MOTH_END_INSTR(CreateUnmappedArgumentsObject)
-
- MOTH_BEGIN_INSTR(ConvertThisToObject)
- MOTH_END_INSTR(ConvertThisToObject)
-
- MOTH_BEGIN_INSTR(Construct)
- MOTH_END_INSTR(Construct)
-
- MOTH_BEGIN_INSTR(Jump)
- addLabel(code - start + offset);
- MOTH_END_INSTR(Jump)
-
- MOTH_BEGIN_INSTR(JumpTrue)
- addLabel(code - start + offset);
- MOTH_END_INSTR(JumpTrue)
-
- MOTH_BEGIN_INSTR(JumpFalse)
- addLabel(code - start + offset);
- MOTH_END_INSTR(JumpFalse)
-
- MOTH_BEGIN_INSTR(CmpEqNull)
- MOTH_END_INSTR(CmpEqNull)
-
- MOTH_BEGIN_INSTR(CmpNeNull)
- MOTH_END_INSTR(CmpNeNull)
-
- MOTH_BEGIN_INSTR(CmpEqInt)
- MOTH_END_INSTR(CmpEq)
-
- MOTH_BEGIN_INSTR(CmpNeInt)
- MOTH_END_INSTR(CmpNeInt)
-
- MOTH_BEGIN_INSTR(CmpEq)
- MOTH_END_INSTR(CmpEq)
-
- MOTH_BEGIN_INSTR(CmpNe)
- MOTH_END_INSTR(CmpNe)
-
- MOTH_BEGIN_INSTR(CmpGt)
- MOTH_END_INSTR(CmpGt)
-
- MOTH_BEGIN_INSTR(CmpGe)
- MOTH_END_INSTR(CmpGe)
-
- MOTH_BEGIN_INSTR(CmpLt)
- MOTH_END_INSTR(CmpLt)
-
- MOTH_BEGIN_INSTR(CmpLe)
- MOTH_END_INSTR(CmpLe)
-
- MOTH_BEGIN_INSTR(CmpStrictEqual)
- MOTH_END_INSTR(CmpStrictEqual)
-
- MOTH_BEGIN_INSTR(CmpStrictNotEqual)
- MOTH_END_INSTR(CmpStrictNotEqual)
-
- MOTH_BEGIN_INSTR(CmpIn)
- MOTH_END_INSTR(CmpIn)
-
- MOTH_BEGIN_INSTR(CmpInstanceOf)
- MOTH_END_INSTR(CmpInstanceOf)
-
- MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt)
- addLabel(code - start + offset);
- MOTH_END_INSTR(JumpStrictEqualStackSlotInt)
-
- MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt)
- addLabel(code - start + offset);
- MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
- MOTH_BEGIN_INSTR(UNot)
- MOTH_END_INSTR(UNot)
-
- MOTH_BEGIN_INSTR(UPlus)
- MOTH_END_INSTR(UPlus)
-
- MOTH_BEGIN_INSTR(UMinus)
- MOTH_END_INSTR(UMinus)
-
- MOTH_BEGIN_INSTR(UCompl)
- MOTH_END_INSTR(UCompl)
-
- MOTH_BEGIN_INSTR(Increment)
- MOTH_END_INSTR(PreIncrement)
-
- MOTH_BEGIN_INSTR(Decrement)
- MOTH_END_INSTR(PreDecrement)
-
- MOTH_BEGIN_INSTR(Add)
- MOTH_END_INSTR(Add)
-
- MOTH_BEGIN_INSTR(BitAnd)
- MOTH_END_INSTR(BitAnd)
-
- MOTH_BEGIN_INSTR(BitOr)
- MOTH_END_INSTR(BitOr)
-
- MOTH_BEGIN_INSTR(BitXor)
- MOTH_END_INSTR(BitXor)
-
- MOTH_BEGIN_INSTR(UShr)
- MOTH_END_INSTR(UShr)
-
- MOTH_BEGIN_INSTR(Shr)
- MOTH_END_INSTR(Shr)
-
- MOTH_BEGIN_INSTR(Shl)
- MOTH_END_INSTR(Shl)
-
- MOTH_BEGIN_INSTR(BitAndConst)
- MOTH_END_INSTR(BitAndConst)
-
- MOTH_BEGIN_INSTR(BitOrConst)
- MOTH_END_INSTR(BitOr)
-
- MOTH_BEGIN_INSTR(BitXorConst)
- MOTH_END_INSTR(BitXor)
-
- MOTH_BEGIN_INSTR(UShrConst)
- MOTH_END_INSTR(UShrConst)
-
- MOTH_BEGIN_INSTR(ShrConst)
- MOTH_END_INSTR(ShrConst)
-
- MOTH_BEGIN_INSTR(ShlConst)
- MOTH_END_INSTR(ShlConst)
-
- MOTH_BEGIN_INSTR(Mul)
- MOTH_END_INSTR(Mul)
-
- MOTH_BEGIN_INSTR(Div)
- MOTH_END_INSTR(Div)
-
- MOTH_BEGIN_INSTR(Mod)
- MOTH_END_INSTR(Mod)
-
- MOTH_BEGIN_INSTR(Sub)
- MOTH_END_INSTR(Sub)
-
- MOTH_BEGIN_INSTR(Ret)
- MOTH_END_INSTR(Ret)
-
- MOTH_BEGIN_INSTR(Debug)
- MOTH_END_INSTR(Debug)
-
- MOTH_BEGIN_INSTR(LoadQmlContext)
- MOTH_END_INSTR(LoadQmlContext)
-
- MOTH_BEGIN_INSTR(LoadQmlImportedScripts)
- MOTH_END_INSTR(LoadQmlImportedScripts)
- }
-}
-#undef MOTH_BEGIN_INSTR
-#undef MOTH_END_INSTR
-
-#endif // V4_ENABLE_JIT
diff --git a/src/qml/jit/qv4jithelpers.cpp b/src/qml/jit/qv4jithelpers.cpp
new file mode 100644
index 0000000000..f43f37ad70
--- /dev/null
+++ b/src/qml/jit/qv4jithelpers.cpp
@@ -0,0 +1,168 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4jithelpers_p.h"
+#include "qv4engine_p.h"
+#include "qv4function_p.h"
+#include "qv4value_p.h"
+#include "qv4object_p.h"
+#include "qv4functionobject_p.h"
+#include "qv4lookup_p.h"
+#include <QtCore/private/qnumeric_p.h>
+
+#ifdef V4_ENABLE_JIT
+
+QT_BEGIN_NAMESPACE
+namespace QV4 {
+namespace JIT {
+namespace Helpers {
+
+void convertThisToObject(ExecutionEngine *engine, Value *t)
+{
+ if (!t->isObject()) {
+ if (t->isNullOrUndefined()) {
+ *t = engine->globalObject->asReturnedValue();
+ } else {
+ *t = t->toObject(engine)->asReturnedValue();
+ }
+ }
+}
+
+ReturnedValue loadGlobalLookup(Function *f, ExecutionEngine *engine, int index)
+{
+ Lookup *l = f->compilationUnit->runtimeLookups + index;
+ return l->globalGetter(l, engine);
+}
+
+ReturnedValue toObject(ExecutionEngine *engine, const Value &obj)
+{
+ if (obj.isObject())
+ return obj.asReturnedValue();
+
+ return obj.toObject(engine)->asReturnedValue();
+}
+
+ReturnedValue exp(const Value &base, const Value &exp)
+{
+ double b = base.toNumber();
+ double e = exp.toNumber();
+ if (qt_is_inf(e) && (b == 1 || b == -1))
+ return Encode(qt_snan());
+ return Encode(pow(b,e));
+}
+
+ReturnedValue getLookup(Function *f, ExecutionEngine *engine, const Value &base, int index)
+{
+ Lookup *l = f->compilationUnit->runtimeLookups + index;
+ return l->getter(l, engine, base);
+}
+
+void setLookupSloppy(Function *f, int index, Value &base, const Value &value)
+{
+ ExecutionEngine *engine = f->internalClass->engine;
+ QV4::Lookup *l = f->compilationUnit->runtimeLookups + index;
+ l->setter(l, engine, base, value);
+}
+
+void setLookupStrict(Function *f, int index, Value &base, const Value &value)
+{
+ ExecutionEngine *engine = f->internalClass->engine;
+ QV4::Lookup *l = f->compilationUnit->runtimeLookups + index;
+ if (!l->setter(l, engine, base, value))
+ engine->throwTypeError();
+}
+
+
+void pushBlockContext(Value *stack, int index)
+{
+ ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
+ stack[CallData::Context] = Runtime::method_createBlockContext(c, index);
+}
+
+void cloneBlockContext(Value *contextSlot)
+{
+ *contextSlot = Runtime::method_cloneBlockContext(static_cast<QV4::ExecutionContext *>(contextSlot));
+}
+
+void pushScriptContext(Value *stack, ExecutionEngine *engine, int index)
+{
+ stack[CallData::Context] = Runtime::method_createScriptContext(engine, index);
+}
+
+void popScriptContext(Value *stack, ExecutionEngine *engine)
+{
+ stack[CallData::Context] = Runtime::method_popScriptContext(engine);
+}
+
+ReturnedValue deleteProperty(QV4::Function *function, const QV4::Value &base, const QV4::Value &index)
+{
+ auto engine = function->internalClass->engine;
+ if (!Runtime::method_deleteProperty(engine, base, index)) {
+ if (function->isStrict())
+ engine->throwTypeError();
+ return Encode(false);
+ } else {
+ return Encode(true);
+ }
+}
+
+ReturnedValue deleteName(Function *function, int name)
+{
+ auto engine = function->internalClass->engine;
+ if (!Runtime::method_deleteName(engine, name)) {
+ if (function->isStrict())
+ engine->throwTypeError();
+ return Encode(false);
+ } else {
+ return Encode(true);
+ }
+}
+
+void throwOnNullOrUndefined(ExecutionEngine *engine, const Value &v)
+{
+ if (v.isNullOrUndefined())
+ engine->throwTypeError();
+}
+
+} // Helpers namespace
+} // JIT namespace
+} // QV4 namespace
+QT_END_NAMESPACE
+
+#endif // V4_ENABLE_JIT
diff --git a/src/qml/jit/qv4jithelpers_p.h b/src/qml/jit/qv4jithelpers_p.h
new file mode 100644
index 0000000000..bd5f65034d
--- /dev/null
+++ b/src/qml/jit/qv4jithelpers_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** 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 TEMPLATE_H
+#define TEMPLATE_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>
+
+//QT_REQUIRE_CONFIG(qml_jit);
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+#ifdef V4_ENABLE_JIT
+
+namespace JIT {
+namespace Helpers {
+
+void convertThisToObject(ExecutionEngine *engine, Value *t);
+ReturnedValue loadGlobalLookup(Function *f, ExecutionEngine *engine, int index);
+ReturnedValue toObject(ExecutionEngine *engine, const Value &obj);
+ReturnedValue exp(const Value &base, const Value &exp);
+ReturnedValue getLookup(Function *f, ExecutionEngine *engine, const Value &base, int index);
+void setLookupStrict(Function *f, int index, Value &base, const Value &value);
+void setLookupSloppy(Function *f, int index, Value &base, const Value &value);
+void pushBlockContext(Value *stack, int index);
+void cloneBlockContext(Value *contextSlot);
+void pushScriptContext(Value *stack, ExecutionEngine *engine, int index);
+void popScriptContext(Value *stack, ExecutionEngine *engine);
+ReturnedValue deleteProperty(QV4::Function *function, const QV4::Value &base, const QV4::Value &index);
+ReturnedValue deleteName(Function *function, int name);
+void throwOnNullOrUndefined(ExecutionEngine *engine, const Value &v);
+
+} // Helpers namespace
+} // JIT namespace
+
+#endif // V4_ENABLE_JIT
+
+} // QV4 namespace
+
+QT_END_NAMESPACE
+
+#endif // TEMPLATE_H
diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp
index c483af638b..b5eadf483a 100644
--- a/src/qml/jsapi/qjsengine.cpp
+++ b/src/qml/jsapi/qjsengine.cpp
@@ -51,6 +51,8 @@
#include <private/qqmlbuiltinfunctions_p.h>
#include <private/qqmldebugconnector_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4stackframe_p.h>
+#include <private/qv4module_p.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qmetaobject.h>
@@ -113,6 +115,45 @@ Q_DECLARE_METATYPE(QList<int>)
argument is a general-purpose string that is stored in the \c Error
object for debugging purposes.
+ For larger pieces of functionality, you may want to encapsulate
+ your code and data into modules. A module is a file that contains
+ script code, variables, etc., and uses export statements to describe
+ its interface towards the rest of the application. With the help of
+ import statements, a module can refer to functionality from other modules.
+ This allows building a scripted application from smaller connected building blocks
+ in a safe way. In contrast, the approach of using evaluate() carries the risk
+ that internal variables or functions from one evaluate() call accidentally pollute the
+ global object and affect subsequent evaluations.
+
+ The following example provides a module that can add numbers:
+
+ \code
+ export function sum(left, right)
+ {
+ return left + right
+ }
+ \endcode
+
+ This module can be loaded with QJSEngine::import() if it is saved under
+ the name \c{math.mjs}:
+
+ \code
+ QJSvalue module = myEngine.importModule("./math.mjs");
+ QJSValue sumFunction = module.property("sum");
+ QJSValue result = sumFunction.call(args);
+ \endcode
+
+ Modules can also use functionality from other modules using import
+ statements:
+
+ \code
+ import { sum } from "./math.mjs";
+ export function addTwice(left, right)
+ {
+ return sum(left, right) * 2;
+ }
+ \endcode
+
\section1 Engine Configuration
The globalObject() function returns the \b {Global Object}
@@ -304,9 +345,9 @@ QJSEngine::QJSEngine()
QJSEngine::QJSEngine(QObject *parent)
: QObject(*new QJSEnginePrivate, parent)
- , m_v4Engine(new QV4::ExecutionEngine)
+ , m_v4Engine(new QV4::ExecutionEngine(this))
{
- m_v4Engine->v8Engine = new QV8Engine(this, m_v4Engine);
+ m_v4Engine->v8Engine = new QV8Engine(m_v4Engine);
checkForApplicationInstance();
QJSEnginePrivate::addToDebugServer(this);
@@ -317,9 +358,9 @@ QJSEngine::QJSEngine(QObject *parent)
*/
QJSEngine::QJSEngine(QJSEnginePrivate &dd, QObject *parent)
: QObject(dd, parent)
- , m_v4Engine(new QV4::ExecutionEngine)
+ , m_v4Engine(new QV4::ExecutionEngine(this))
{
- m_v4Engine->v8Engine = new QV8Engine(this, m_v4Engine);
+ m_v4Engine->v8Engine = new QV8Engine(m_v4Engine);
checkForApplicationInstance();
}
@@ -462,7 +503,7 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in
QV4::Scope scope(v4);
QV4::ScopedValue result(scope);
- QV4::Script script(v4->rootContext(), QV4::Compiler::GlobalCode, program, fileName, lineNumber);
+ QV4::Script script(v4->rootContext(), QV4::Compiler::ContextType::Global, program, fileName, lineNumber);
script.strictMode = false;
if (v4->currentStackFrame)
script.strictMode = v4->currentStackFrame->v4Function->isStrict();
@@ -480,6 +521,54 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in
return retval;
}
+static QUrl moduleUrlForFileName(const QString &fileName)
+{
+ QString absolutePath = QFileInfo(fileName).canonicalFilePath();
+ if (!absolutePath.startsWith(QLatin1Char(':')))
+ return QUrl::fromLocalFile(absolutePath);
+
+ absolutePath.remove(0, 1);
+ QUrl url;
+ url.setPath(absolutePath);
+ url.setScheme(QLatin1String("qrc"));
+ return url;
+}
+
+/*!
+ Imports the module located at \a fileName and returns a module namespace object that
+ contains all exported variables, constants and functions as properties.
+
+ If this is the first time the module is imported in the engine, the file is loaded
+ from the specified location in either the local file system or the Qt resource system
+ and evaluated as an ECMAScript module. The file is expected to be encoded in UTF-8 text.
+
+ Subsequent imports of the same module will return the previously imported instance. Modules
+ are singletons and remain around until the engine is destroyed.
+
+ The specified \a fileName will internally be normalized using \l QFileInfo::canonicalFilePath().
+ That means that multiple imports of the same file on disk using different relative paths will
+ load the file only once.
+
+ \note If an exception is thrown during the loading of the module, the return value
+ will be the exception (typically an \c{Error} object; see QJSValue::isError()).
+
+ \since 5.12
+ */
+QJSValue QJSEngine::importModule(const QString &fileName)
+{
+ const QUrl url = moduleUrlForFileName(fileName);
+ auto moduleUnit = m_v4Engine->loadModule(url);
+ if (m_v4Engine->hasException)
+ return QJSValue(m_v4Engine, m_v4Engine->catchException());
+
+ QV4::Scope scope(m_v4Engine);
+ QV4::Scoped<QV4::Module> moduleNamespace(scope, moduleUnit->instantiate(m_v4Engine));
+ if (m_v4Engine->hasException)
+ return QJSValue(m_v4Engine, m_v4Engine->catchException());
+ moduleUnit->evaluate();
+ return QJSValue(m_v4Engine, moduleNamespace->asReturnedValue());
+}
+
/*!
Creates a JavaScript object of class Object.
@@ -637,16 +726,16 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr)
double d = QV4::RuntimeHelpers::stringToNumber(string);
switch (type) {
case QMetaType::Int:
- *reinterpret_cast<int*>(ptr) = QV4::Primitive::toInt32(d);
+ *reinterpret_cast<int*>(ptr) = QV4::Value::toInt32(d);
return true;
case QMetaType::UInt:
- *reinterpret_cast<uint*>(ptr) = QV4::Primitive::toUInt32(d);
+ *reinterpret_cast<uint*>(ptr) = QV4::Value::toUInt32(d);
return true;
case QMetaType::LongLong:
- *reinterpret_cast<qlonglong*>(ptr) = QV4::Primitive::toInteger(d);
+ *reinterpret_cast<qlonglong*>(ptr) = QV4::Value::toInteger(d);
return true;
case QMetaType::ULongLong:
- *reinterpret_cast<qulonglong*>(ptr) = QV4::Primitive::toInteger(d);
+ *reinterpret_cast<qulonglong*>(ptr) = QV4::Value::toInteger(d);
return true;
case QMetaType::Double:
*reinterpret_cast<double*>(ptr) = d;
@@ -655,19 +744,19 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr)
*reinterpret_cast<float*>(ptr) = d;
return true;
case QMetaType::Short:
- *reinterpret_cast<short*>(ptr) = QV4::Primitive::toInt32(d);
+ *reinterpret_cast<short*>(ptr) = QV4::Value::toInt32(d);
return true;
case QMetaType::UShort:
- *reinterpret_cast<unsigned short*>(ptr) = QV4::Primitive::toUInt32(d);
+ *reinterpret_cast<unsigned short*>(ptr) = QV4::Value::toUInt32(d);
return true;
case QMetaType::Char:
- *reinterpret_cast<char*>(ptr) = QV4::Primitive::toInt32(d);
+ *reinterpret_cast<char*>(ptr) = QV4::Value::toInt32(d);
return true;
case QMetaType::UChar:
- *reinterpret_cast<unsigned char*>(ptr) = QV4::Primitive::toUInt32(d);
+ *reinterpret_cast<unsigned char*>(ptr) = QV4::Value::toUInt32(d);
return true;
case QMetaType::QChar:
- *reinterpret_cast<QChar*>(ptr) = QV4::Primitive::toUInt32(d);
+ *reinterpret_cast<QChar*>(ptr) = QV4::Value::toUInt32(d);
return true;
default:
return false;
@@ -738,6 +827,74 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr)
\sa toScriptValue()
*/
+/*!
+ Throws a run-time error (exception) with the given \a message.
+
+ This method is the C++ counterpart of a \c throw() expression in
+ JavaScript. It enables C++ code to report run-time errors to QJSEngine.
+ Therefore it should only be called from C++ code that was invoked by a
+ JavaScript function through QJSEngine.
+
+ When returning from C++, the engine will interrupt the normal flow of
+ execution and call the the next pre-registered exception handler with
+ an error object that contains the given \a message. The error object
+ will point to the location of the top-most context on the JavaScript
+ caller stack; specifically, it will have properties \c lineNumber,
+ \c fileName and \c stack. These properties are described in
+ \l{Script Exceptions}.
+
+ In the following example a C++ method in \e FileAccess.cpp throws an error
+ in \e qmlFile.qml at the position where \c readFileAsText() is called:
+
+ \code
+ // qmlFile.qml
+ function someFunction() {
+ ...
+ var text = FileAccess.readFileAsText("/path/to/file.txt");
+ }
+ \endcode
+
+ \code
+ // FileAccess.cpp
+ // Assuming that FileAccess is a QObject-derived class that has been
+ // registered as a singleton type and provides an invokable method
+ // readFileAsText()
+
+ QJSValue FileAccess::readFileAsText(const QString & filePath) {
+ QFile file(filePath);
+
+ if (!file.open(QIODevice::ReadOnly)) {
+ jsEngine->throwError(file.errorString());
+ return QString();
+ }
+
+ ...
+ return content;
+ }
+ \endcode
+
+ It is also possible to catch the thrown error in JavaScript:
+ \code
+ // qmlFile.qml
+ function someFunction() {
+ ...
+ var text;
+ try {
+ text = FileAccess.readFileAsText("/path/to/file.txt");
+ } catch (error) {
+ console.warn("In " + error.fileName + ":" + "error.lineNumber" +
+ ": " + error.message);
+ }
+ }
+ \endcode
+
+ \since Qt 5.12
+ \sa {Script Exceptions}
+*/
+void QJSEngine::throwError(const QString &message)
+{
+ m_v4Engine->throwError(message);
+}
QJSEnginePrivate *QJSEnginePrivate::get(QV4::ExecutionEngine *e)
{
diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h
index 3ba2b52e89..5c8613ffd6 100644
--- a/src/qml/jsapi/qjsengine.h
+++ b/src/qml/jsapi/qjsengine.h
@@ -69,6 +69,8 @@ public:
QJSValue evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1);
+ QJSValue importModule(const QString &fileName);
+
QJSValue newObject();
QJSValue newArray(uint length = 0);
@@ -111,6 +113,8 @@ public:
QV4::ExecutionEngine *handle() const { return m_v4Engine; }
+ void throwError(const QString &message);
+
private:
QJSValue create(int type, const void *ptr);
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp
index 348ddb25d9..9671e82187 100644
--- a/src/qml/jsapi/qjsvalue.cpp
+++ b/src/qml/jsapi/qjsvalue.cpp
@@ -569,7 +569,7 @@ qint32 QJSValue::toInt() const
if (!val) {
QVariant *variant = QJSValuePrivate::getVariant(this);
if (variant->userType() == QMetaType::QString)
- return QV4::Primitive::toInt32(RuntimeHelpers::stringToNumber(variant->toString()));
+ return QV4::Value::toInt32(RuntimeHelpers::stringToNumber(variant->toString()));
else
return variant->toInt();
}
@@ -603,7 +603,7 @@ quint32 QJSValue::toUInt() const
if (!val) {
QVariant *variant = QJSValuePrivate::getVariant(this);
if (variant->userType() == QMetaType::QString)
- return QV4::Primitive::toUInt32(RuntimeHelpers::stringToNumber(variant->toString()));
+ return QV4::Value::toUInt32(RuntimeHelpers::stringToNumber(variant->toString()));
else
return variant->toUInt();
}
@@ -852,7 +852,7 @@ QJSValue QJSValue::prototype() const
ScopedObject o(scope, QJSValuePrivate::getValue(this)->as<QV4::Object>());
if (!o)
return QJSValue();
- ScopedObject p(scope, o->prototype());
+ ScopedObject p(scope, o->getPrototypeOf());
if (!p)
return QJSValue(NullValue);
return QJSValue(o->internalClass()->engine, p.asReturnedValue());
@@ -884,7 +884,7 @@ void QJSValue::setPrototype(const QJSValue& prototype)
if (!val)
return;
if (val->isNull()) {
- o->setPrototype(nullptr);
+ o->setPrototypeOf(nullptr);
return;
}
@@ -895,7 +895,7 @@ void QJSValue::setPrototype(const QJSValue& prototype)
qWarning("QJSValue::setPrototype() failed: cannot set a prototype created in a different engine");
return;
}
- if (!o->setPrototype(p))
+ if (!o->setPrototypeOf(p))
qWarning("QJSValue::setPrototype() failed: cyclic prototype value");
}
@@ -1058,12 +1058,7 @@ QJSValue QJSValue::property(const QString& name) const
return QJSValue();
ScopedString s(scope, engine->newString(name));
- uint idx = s->asArrayIndex();
- if (idx < UINT_MAX)
- return property(idx);
-
- s->makeIdentifier();
- QV4::ScopedValue result(scope, o->get(s));
+ QV4::ScopedValue result(scope, o->get(s->toPropertyKey()));
if (engine->hasException)
result = engine->catchException();
@@ -1110,7 +1105,7 @@ QJSValue QJSValue::property(quint32 arrayIndex) const
if (!o)
return QJSValue();
- QV4::ScopedValue result(scope, arrayIndex == UINT_MAX ? o->get(engine->id_uintMax()) : o->getIndexed(arrayIndex));
+ QV4::ScopedValue result(scope, arrayIndex == UINT_MAX ? o->get(engine->id_uintMax()) : o->get(arrayIndex));
if (engine->hasException)
engine->catchException();
return QJSValue(engine, result->asReturnedValue());
@@ -1148,15 +1143,8 @@ void QJSValue::setProperty(const QString& name, const QJSValue& value)
}
ScopedString s(scope, engine->newString(name));
- uint idx = s->asArrayIndex();
- if (idx < UINT_MAX) {
- setProperty(idx, value);
- return;
- }
-
- s->makeIdentifier();
QV4::ScopedValue v(scope, QJSValuePrivate::convertedToValue(engine, value));
- o->put(s, v);
+ o->put(s->toPropertyKey(), v);
if (engine->hasException)
engine->catchException();
}
@@ -1209,10 +1197,8 @@ void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value)
}
QV4::ScopedValue v(scope, QJSValuePrivate::convertedToValue(engine, value));
- if (arrayIndex != UINT_MAX)
- o->putIndexed(arrayIndex, v);
- else
- o->put(engine->id_uintMax(), v);
+ PropertyKey id = arrayIndex != UINT_MAX ? PropertyKey::fromArrayIndex(arrayIndex) : engine->id_uintMax()->propertyKey();
+ o->put(id, v);
if (engine->hasException)
engine->catchException();
}
@@ -1249,7 +1235,7 @@ bool QJSValue::deleteProperty(const QString &name)
return false;
ScopedString s(scope, engine->newString(name));
- return o->deleteProperty(s);
+ return o->deleteProperty(s->toPropertyKey());
}
/*!
@@ -1269,8 +1255,8 @@ bool QJSValue::hasProperty(const QString &name) const
if (!o)
return false;
- ScopedString s(scope, engine->newIdentifier(name));
- return o->hasProperty(s);
+ ScopedString s(scope, engine->newString(name));
+ return o->hasProperty(s->toPropertyKey());
}
/*!
@@ -1291,7 +1277,7 @@ bool QJSValue::hasOwnProperty(const QString &name) const
return false;
ScopedString s(scope, engine->newIdentifier(name));
- return o->hasOwnProperty(s);
+ return o->getOwnProperty(s->propertyKey()) != Attr_Invalid;
}
/*!
diff --git a/src/qml/jsapi/qjsvalue_p.h b/src/qml/jsapi/qjsvalue_p.h
index 62e09f72be..bcf0a9d12d 100644
--- a/src/qml/jsapi/qjsvalue_p.h
+++ b/src/qml/jsapi/qjsvalue_p.h
@@ -62,7 +62,7 @@
QT_BEGIN_NAMESPACE
-class QJSValuePrivate
+class Q_AUTOTEST_EXPORT QJSValuePrivate
{
public:
static inline QV4::Value *getValue(const QJSValue *jsval)
diff --git a/src/qml/jsapi/qjsvalueiterator.cpp b/src/qml/jsapi/qjsvalueiterator.cpp
index ce472ce7e5..076b90c5f2 100644
--- a/src/qml/jsapi/qjsvalueiterator.cpp
+++ b/src/qml/jsapi/qjsvalueiterator.cpp
@@ -47,19 +47,50 @@
QT_BEGIN_NAMESPACE
QJSValueIteratorPrivate::QJSValueIteratorPrivate(const QJSValue &v)
- : value(v)
- , currentIndex(UINT_MAX)
- , nextIndex(UINT_MAX)
{
+ init(v);
+}
+
+void QJSValueIteratorPrivate::init(const QJSValue &v)
+{
+ engine = nullptr;
+
QV4::ExecutionEngine *e = QJSValuePrivate::engine(&v);
if (!e)
return;
+ QV4::Object *o = QJSValuePrivate::getValue(&v)->objectValue();
+ if (!o)
+ return;
- QV4::Scope scope(e);
- QV4::ScopedObject o(scope, QJSValuePrivate::getValue(&v));
- iterator.set(e, e->newForEachIteratorObject(o));
+ engine = e;
+ object = o;
+ iterator.reset(o->ownPropertyKeys(object.valueRef()));
+ next();
}
+void QJSValueIteratorPrivate::next()
+{
+ QV4::Object *o = object.as<QV4::Object>();
+ if (!o || !iterator)
+ return;
+
+ QV4::PropertyKey key;
+ while (1) {
+ key = iterator->next(o);
+ if (!key.isSymbol())
+ break;
+ }
+ currentKey = nextKey;
+ nextKey.set(engine, key.id());
+}
+
+bool QJSValueIteratorPrivate::isValid() const
+{
+ if (!engine || !iterator)
+ return false;
+ QV4::Value *val = object.valueRef();
+ return (val && val->isObject());
+}
/*!
\class QJSValueIterator
@@ -98,17 +129,6 @@ QJSValueIteratorPrivate::QJSValueIteratorPrivate(const QJSValue &v)
QJSValueIterator::QJSValueIterator(const QJSValue& object)
: d_ptr(new QJSValueIteratorPrivate(object))
{
- QV4::ExecutionEngine *v4 = d_ptr->iterator.engine();
- if (!v4)
- return;
- QV4::Scope scope(v4);
- QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value());
- it->d()->it().flags = QV4::ObjectIterator::NoFlags;
- QV4::ScopedString nm(scope);
- QV4::Property nextProperty;
- QV4::PropertyAttributes nextAttributes;
- it->d()->it().next(nm.getRef(), &d_ptr->nextIndex, &nextProperty, &nextAttributes);
- d_ptr->nextName.set(v4, nm.asReturnedValue());
}
/*!
@@ -127,10 +147,9 @@ QJSValueIterator::~QJSValueIterator()
*/
bool QJSValueIterator::hasNext() const
{
- QV4::Value *val = QJSValuePrivate::getValue(&d_ptr->value);
- if (!val || !val->isObject())
+ if (!d_ptr->isValid())
return false;
- return d_ptr->nextName.as<QV4::String>() || d_ptr->nextIndex != UINT_MAX;
+ return QV4::PropertyKey::fromId(d_ptr->nextKey.value()).isValid();
}
/*!
@@ -143,23 +162,10 @@ bool QJSValueIterator::hasNext() const
*/
bool QJSValueIterator::next()
{
- QV4::Value *val = QJSValuePrivate::getValue(&d_ptr->value);
- if (!val || !val->isObject())
- return false;
- d_ptr->currentName = d_ptr->nextName;
- d_ptr->currentIndex = d_ptr->nextIndex;
-
- QV4::ExecutionEngine *v4 = d_ptr->iterator.engine();
- if (!v4)
+ if (!d_ptr->isValid())
return false;
- QV4::Scope scope(v4);
- QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value());
- QV4::ScopedString nm(scope);
- QV4::Property nextProperty;
- QV4::PropertyAttributes nextAttributes;
- it->d()->it().next(nm.getRef(), &d_ptr->nextIndex, &nextProperty, &nextAttributes);
- d_ptr->nextName.set(v4, nm.asReturnedValue());
- return d_ptr->currentName.as<QV4::String>() || d_ptr->currentIndex != UINT_MAX;
+ d_ptr->next();
+ return QV4::PropertyKey::fromId(d_ptr->currentKey.value()).isValid();
}
/*!
@@ -170,14 +176,14 @@ bool QJSValueIterator::next()
*/
QString QJSValueIterator::name() const
{
- QV4::Value *val = QJSValuePrivate::getValue(&d_ptr->value);
- if (!val || !val->isObject())
+ if (!d_ptr->isValid())
+ return QString();
+ QV4::Scope scope(d_ptr->engine);
+ QV4::ScopedPropertyKey key(scope, QV4::PropertyKey::fromId(d_ptr->currentKey.value()));
+ if (!key->isValid())
return QString();
- if (QV4::String *s = d_ptr->currentName.as<QV4::String>())
- return s->toQString();
- if (d_ptr->currentIndex < UINT_MAX)
- return QString::number(d_ptr->currentIndex);
- return QString();
+ Q_ASSERT(!key->isSymbol());
+ return key->toStringOrSymbol(d_ptr->engine)->toQString();
}
@@ -189,23 +195,21 @@ QString QJSValueIterator::name() const
*/
QJSValue QJSValueIterator::value() const
{
- QV4::ExecutionEngine *engine = d_ptr->iterator.engine();
- if (!engine)
+ if (!d_ptr->isValid())
return QJSValue();
- QV4::Scope scope(engine);
- QV4::ScopedObject obj(scope, QJSValuePrivate::getValue(&d_ptr->value));
- if (!obj)
+ QV4::Scope scope(d_ptr->engine);
+ QV4::ScopedPropertyKey key(scope, QV4::PropertyKey::fromId(d_ptr->currentKey.value()));
+ if (!key->isValid())
return QJSValue();
- if (!d_ptr->currentName.as<QV4::String>() && d_ptr->currentIndex == UINT_MAX)
- return QJSValue();
+ QV4::ScopedObject obj(scope, d_ptr->object.asManaged());
+ QV4::ScopedValue val(scope, obj->get(key));
- QV4::ScopedValue v(scope, d_ptr->currentIndex == UINT_MAX ? obj->get(d_ptr->currentName.as<QV4::String>()) : obj->getIndexed(d_ptr->currentIndex));
if (scope.hasException()) {
- engine->catchException();
+ scope.engine->catchException();
return QJSValue();
}
- return QJSValue(engine, v->asReturnedValue());
+ return QJSValue(scope.engine, val->asReturnedValue());
}
@@ -216,27 +220,7 @@ QJSValue QJSValueIterator::value() const
*/
QJSValueIterator& QJSValueIterator::operator=(QJSValue& object)
{
- d_ptr->value = object;
- d_ptr->currentIndex = UINT_MAX;
- d_ptr->nextIndex = UINT_MAX;
- d_ptr->currentName.clear();
- d_ptr->nextName.clear();
- QV4::ExecutionEngine *v4 = d_ptr->iterator.engine();
- if (!v4) {
- d_ptr->iterator.clear();
- return *this;
- }
-
- QV4::Scope scope(v4);
- QV4::ScopedObject o(scope, QJSValuePrivate::getValue(&object));
- d_ptr->iterator.set(v4, v4->newForEachIteratorObject(o));
- QV4::Scoped<QV4::ForEachIteratorObject> it(scope, d_ptr->iterator.value());
- it->d()->it().flags = QV4::ObjectIterator::NoFlags;
- QV4::ScopedString nm(scope);
- QV4::Property nextProperty;
- QV4::PropertyAttributes nextAttributes;
- it->d()->it().next(nm.getRef(), &d_ptr->nextIndex, &nextProperty, &nextAttributes);
- d_ptr->nextName.set(v4, nm.asReturnedValue());
+ d_ptr->init(object);
return *this;
}
diff --git a/src/qml/jsapi/qjsvalueiterator_p.h b/src/qml/jsapi/qjsvalueiterator_p.h
index 474a98b9fa..a870850c11 100644
--- a/src/qml/jsapi/qjsvalueiterator_p.h
+++ b/src/qml/jsapi/qjsvalueiterator_p.h
@@ -61,12 +61,16 @@ class QJSValueIteratorPrivate
public:
QJSValueIteratorPrivate(const QJSValue &v);
- QJSValue value;
- QV4::PersistentValue iterator;
- QV4::PersistentValue currentName;
- uint currentIndex;
- QV4::PersistentValue nextName;
- uint nextIndex;
+ void init(const QJSValue &v);
+ bool isValid() const;
+
+ void next();
+
+ QV4::ExecutionEngine *engine = nullptr;
+ QV4::PersistentValue object;
+ QScopedPointer<QV4::OwnPropertyKeyIterator> iterator;
+ QV4::PersistentValue currentKey;
+ QV4::PersistentValue nextKey;
};
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri
index 4bc877bd9d..2a338c6792 100644
--- a/src/qml/jsruntime/jsruntime.pri
+++ b/src/qml/jsruntime/jsruntime.pri
@@ -3,7 +3,6 @@ INCLUDEPATH += $$OUT_PWD
!qmldevtools_build {
SOURCES += \
- $$PWD/qv4engine.cpp \
$$PWD/qv4context.cpp \
$$PWD/qv4persistent.cpp \
$$PWD/qv4lookup.cpp \
@@ -12,23 +11,32 @@ SOURCES += \
$$PWD/qv4managed.cpp \
$$PWD/qv4internalclass.cpp \
$$PWD/qv4sparsearray.cpp \
+ $$PWD/qv4atomics.cpp \
$$PWD/qv4arraydata.cpp \
$$PWD/qv4arrayobject.cpp \
+ $$PWD/qv4arrayiterator.cpp \
$$PWD/qv4argumentsobject.cpp \
$$PWD/qv4booleanobject.cpp \
$$PWD/qv4dateobject.cpp \
$$PWD/qv4errorobject.cpp \
$$PWD/qv4function.cpp \
$$PWD/qv4functionobject.cpp \
+ $$PWD/qv4generatorobject.cpp \
$$PWD/qv4globalobject.cpp \
+ $$PWD/qv4iterator.cpp \
$$PWD/qv4jsonobject.cpp \
$$PWD/qv4mathobject.cpp \
$$PWD/qv4memberdata.cpp \
$$PWD/qv4numberobject.cpp \
$$PWD/qv4object.cpp \
$$PWD/qv4objectproto.cpp \
+ $$PWD/qv4propertykey.cpp \
+ $$PWD/qv4proxy.cpp \
$$PWD/qv4qmlcontext.cpp \
+ $$PWD/qv4reflect.cpp \
$$PWD/qv4regexpobject.cpp \
+ $$PWD/qv4stackframe.cpp \
+ $$PWD/qv4stringiterator.cpp \
$$PWD/qv4stringobject.cpp \
$$PWD/qv4variantobject.cpp \
$$PWD/qv4objectiterator.cpp \
@@ -36,13 +44,19 @@ SOURCES += \
$$PWD/qv4runtimecodegen.cpp \
$$PWD/qv4serialize.cpp \
$$PWD/qv4script.cpp \
- $$PWD/qv4sequenceobject.cpp \
+ $$PWD/qv4symbol.cpp \
+ $$PWD/qv4setobject.cpp \
+ $$PWD/qv4setiterator.cpp \
$$PWD/qv4include.cpp \
$$PWD/qv4qobjectwrapper.cpp \
$$PWD/qv4arraybuffer.cpp \
$$PWD/qv4typedarray.cpp \
$$PWD/qv4dataview.cpp \
- $$PWD/qv4vme_moth.cpp
+ $$PWD/qv4vme_moth.cpp \
+ $$PWD/qv4mapobject.cpp \
+ $$PWD/qv4mapiterator.cpp \
+ $$PWD/qv4estable.cpp \
+ $$PWD/qv4module.cpp
qtConfig(qml-debug): SOURCES += $$PWD/qv4profiling.cpp
@@ -62,24 +76,33 @@ HEADERS += \
$$PWD/qv4internalclass_p.h \
$$PWD/qv4jscall_p.h \
$$PWD/qv4sparsearray_p.h \
+ $$PWD/qv4atomics_p.h \
$$PWD/qv4arraydata_p.h \
$$PWD/qv4arrayobject_p.h \
+ $$PWD/qv4arrayiterator_p.h \
$$PWD/qv4argumentsobject_p.h \
$$PWD/qv4booleanobject_p.h \
$$PWD/qv4dateobject_p.h \
$$PWD/qv4errorobject_p.h \
$$PWD/qv4function_p.h \
$$PWD/qv4functionobject_p.h \
+ $$PWD/qv4generatorobject_p.h \
$$PWD/qv4globalobject_p.h \
+ $$PWD/qv4iterator_p.h \
$$PWD/qv4jsonobject_p.h \
$$PWD/qv4mathobject_p.h \
$$PWD/qv4memberdata_p.h \
$$PWD/qv4numberobject_p.h \
$$PWD/qv4object_p.h \
$$PWD/qv4objectproto_p.h \
+ $$PWD/qv4propertykey_p.h \
+ $$PWD/qv4proxy_p.h \
$$PWD/qv4qmlcontext_p.h \
+ $$PWD/qv4reflect_p.h \
$$PWD/qv4regexpobject_p.h \
$$PWD/qv4runtimecodegen_p.h \
+ $$PWD/qv4stackframe_p.h \
+ $$PWD/qv4stringiterator_p.h \
$$PWD/qv4stringobject_p.h \
$$PWD/qv4variantobject_p.h \
$$PWD/qv4property_p.h \
@@ -87,16 +110,31 @@ HEADERS += \
$$PWD/qv4regexp_p.h \
$$PWD/qv4serialize_p.h \
$$PWD/qv4script_p.h \
+ $$PWD/qv4symbol_p.h \
+ $$PWD/qv4setobject_p.h \
+ $$PWD/qv4setiterator_p.h \
$$PWD/qv4scopedvalue_p.h \
$$PWD/qv4executableallocator_p.h \
- $$PWD/qv4sequenceobject_p.h \
$$PWD/qv4include_p.h \
$$PWD/qv4qobjectwrapper_p.h \
$$PWD/qv4profiling_p.h \
$$PWD/qv4arraybuffer_p.h \
$$PWD/qv4typedarray_p.h \
$$PWD/qv4dataview_p.h \
- $$PWD/qv4vme_moth_p.h
+ $$PWD/qv4vme_moth_p.h \
+ $$PWD/qv4mapobject_p.h \
+ $$PWD/qv4mapiterator_p.h \
+ $$PWD/qv4estable_p.h \
+ $$PWD/qv4vtable_p.h \
+ $$PWD/qv4module_p.h
+
+qtConfig(qml-sequence-object) {
+ HEADERS += \
+ $$PWD/qv4sequenceobject_p.h
+
+ SOURCES += \
+ $$PWD/qv4sequenceobject.cpp
+}
}
@@ -110,6 +148,7 @@ HEADERS += \
$$PWD/qv4value_p.h
SOURCES += \
+ $$PWD/qv4engine.cpp \
$$PWD/qv4runtime.cpp \
$$PWD/qv4string.cpp \
$$PWD/qv4value.cpp \
diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp
index 075e7afd8a..4a21f62cf2 100644
--- a/src/qml/jsruntime/qv4argumentsobject.cpp
+++ b/src/qml/jsruntime/qv4argumentsobject.cpp
@@ -37,218 +37,187 @@
**
****************************************************************************/
#include <qv4argumentsobject_p.h>
+#include <qv4arrayobject_p.h>
#include <qv4alloca_p.h>
#include <qv4scopedvalue_p.h>
#include <qv4string_p.h>
#include <qv4function_p.h>
#include <qv4jscall_p.h>
+#include <qv4symbol_p.h>
using namespace QV4;
DEFINE_OBJECT_VTABLE(ArgumentsObject);
DEFINE_OBJECT_VTABLE(StrictArgumentsObject);
-void Heap::ArgumentsObject::init(QV4::CppStackFrame *frame)
-{
- ExecutionEngine *v4 = internalClass->engine;
-
- int nFormals = frame->v4Function->nFormals;
- QV4::CallContext *context = static_cast<QV4::CallContext *>(frame->context());
-
- Object::init();
- fullyCreated = false;
- this->nFormals = nFormals;
- this->context.set(v4, context->d());
- Q_ASSERT(vtable() == QV4::ArgumentsObject::staticVTable());
-
- Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee()));
- setProperty(v4, CalleePropertyIndex, context->d()->function);
- Q_ASSERT(LengthPropertyIndex == internalClass->find(v4->id_length()));
- setProperty(v4, LengthPropertyIndex, Primitive::fromInt32(context->argc()));
-}
-
void Heap::StrictArgumentsObject::init(QV4::CppStackFrame *frame)
+
{
Q_ASSERT(vtable() == QV4::StrictArgumentsObject::staticVTable());
ExecutionEngine *v4 = internalClass->engine;
Object::init();
- Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee()));
- Q_ASSERT(CallerPropertyIndex == internalClass->find(v4->id_caller()));
- setProperty(v4, CalleePropertyIndex + QV4::Object::GetterOffset, *v4->thrower());
- setProperty(v4, CalleePropertyIndex + QV4::Object::SetterOffset, *v4->thrower());
- setProperty(v4, CallerPropertyIndex + QV4::Object::GetterOffset, *v4->thrower());
- setProperty(v4, CallerPropertyIndex + QV4::Object::SetterOffset, *v4->thrower());
+ Q_ASSERT(internalClass->verifyIndex(v4->id_callee()->propertyKey(), CalleePropertyIndex));
+ Q_ASSERT(internalClass->findValueOrSetter(v4->id_callee()->propertyKey()).index == CalleeSetterPropertyIndex);
+ Q_ASSERT(internalClass->verifyIndex(v4->symbol_iterator()->propertyKey(), SymbolIteratorPropertyIndex));
+ setProperty(v4, SymbolIteratorPropertyIndex, *v4->arrayProtoValues());
+ setProperty(v4, CalleePropertyIndex, *v4->thrower());
+ setProperty(v4, CalleeSetterPropertyIndex, *v4->thrower());
Scope scope(v4);
Scoped<QV4::StrictArgumentsObject> args(scope, this);
args->arrayReserve(frame->originalArgumentsCount);
args->arrayPut(0, frame->originalArguments, frame->originalArgumentsCount);
- Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(v4->id_length()));
- setProperty(v4, LengthPropertyIndex, Primitive::fromInt32(frame->originalArgumentsCount));
+ Q_ASSERT(args->internalClass()->verifyIndex(v4->id_length()->propertyKey(), LengthPropertyIndex));
+ setProperty(v4, LengthPropertyIndex, Value::fromInt32(frame->originalArgumentsCount));
+}
+
+void Heap::ArgumentsObject::init(QV4::CppStackFrame *frame)
+{
+ ExecutionEngine *v4 = internalClass->engine;
+
+ QV4::CallContext *context = static_cast<QV4::CallContext *>(frame->context());
+
+ Object::init();
+ this->context.set(v4, context->d());
+ Q_ASSERT(vtable() == QV4::ArgumentsObject::staticVTable());
+
+ Q_ASSERT(internalClass->verifyIndex(v4->id_callee()->propertyKey(), CalleePropertyIndex));
+ setProperty(v4, CalleePropertyIndex, context->d()->function);
+ Q_ASSERT(internalClass->verifyIndex(v4->id_length()->propertyKey(), LengthPropertyIndex));
+ setProperty(v4, LengthPropertyIndex, Value::fromInt32(context->argc()));
+ Q_ASSERT(internalClass->verifyIndex(v4->symbol_iterator()->propertyKey(), SymbolIteratorPropertyIndex));
+ setProperty(v4, SymbolIteratorPropertyIndex, *v4->arrayProtoValues());
+
+ fullyCreated = false;
+ argCount = frame->originalArgumentsCount;
+ uint nFormals = frame->v4Function->nFormals;
+ mapped = nFormals > 63 ? std::numeric_limits<quint64>::max() : (1ull << nFormals) - 1;
}
void ArgumentsObject::fullyCreate()
{
- if (fullyCreated())
+ if (d()->fullyCreated)
return;
Scope scope(engine());
- int argCount = context()->argc();
- uint numAccessors = qMin(d()->nFormals, argCount);
- ArrayData::realloc(this, Heap::ArrayData::Sparse, argCount, true);
- scope.engine->requireArgumentsAccessors(numAccessors);
-
- Scoped<MemberData> md(scope, d()->mappedArguments);
- if (numAccessors) {
- d()->mappedArguments.set(scope.engine, md->allocate(scope.engine, numAccessors));
- for (uint i = 0; i < numAccessors; ++i) {
- d()->mappedArguments->values.set(scope.engine, i, context()->args()[i]);
- arraySet(i, scope.engine->argumentsAccessors + i, Attr_Accessor);
- }
- }
- arrayPut(numAccessors, context()->args() + numAccessors, argCount - numAccessors);
- for (int i = int(numAccessors); i < argCount; ++i)
- setArrayAttributes(i, Attr_Data);
+ arrayReserve(d()->argCount);
+ arrayPut(0, context()->args(), d()->argCount);
+ // Use a sparse array, so that method_getElement() doesn't shortcut
+ initSparseArray();
d()->fullyCreated = true;
}
-bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, const Property *desc, PropertyAttributes attrs)
+bool ArgumentsObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *desc, PropertyAttributes attrs)
{
- fullyCreate();
-
- Scope scope(engine);
- ScopedProperty map(scope);
- PropertyAttributes mapAttrs;
- uint numAccessors = qMin(d()->nFormals, context()->argc());
- bool isMapped = false;
- if (arrayData() && index < numAccessors &&
- arrayData()->attributes(index).isAccessor() &&
- arrayData()->get(index) == scope.engine->argumentsAccessors[index].getter()->asReturnedValue())
- isMapped = true;
-
- if (isMapped) {
- Q_ASSERT(arrayData());
- mapAttrs = arrayData()->attributes(index);
- arrayData()->getProperty(index, map, &mapAttrs);
- setArrayAttributes(index, Attr_Data);
- ArrayData::Index arrayIndex{ arrayData(), arrayData()->mappedIndex(index) };
- arrayIndex.set(scope.engine, d()->mappedArguments->values[index]);
+ ArgumentsObject *args = static_cast<ArgumentsObject *>(m);
+ args->fullyCreate();
+ uint index = id.asArrayIndex();
+
+ if (!args->isMapped(index))
+ return Object::virtualDefineOwnProperty(m, id, desc, attrs);
+
+ Scope scope(args);
+ PropertyAttributes cAttrs = attrs;
+ ScopedProperty cDesc(scope);
+ cDesc->copy(desc, attrs);
+
+ if (attrs.isData() && desc->value.isEmpty() && attrs.hasWritable() && !attrs.isWritable()) {
+ cDesc->value = args->context()->args()[index];
+ cAttrs.setType(PropertyAttributes::Data);
}
- bool result = Object::defineOwnProperty2(scope.engine, index, desc, attrs);
- if (!result) {
+ bool allowed = Object::virtualDefineOwnProperty(m, id, cDesc, cAttrs);
+ if (!allowed)
return false;
- }
- if (isMapped && attrs.isData()) {
- Q_ASSERT(arrayData());
- ScopedFunctionObject setter(scope, map->setter());
- JSCallData jsCallData(scope, 1);
- *jsCallData->thisObject = this->asReturnedValue();
- jsCallData->args[0] = desc->value;
- setter->call(jsCallData);
-
- if (attrs.isWritable()) {
- setArrayAttributes(index, mapAttrs);
- arrayData()->setProperty(engine, index, map);
- }
+ if (attrs.isAccessor()) {
+ args->removeMapping(index);
+ } else {
+ if (!desc->value.isEmpty())
+ args->context()->setArg(index, desc->value);
+ if (attrs.hasWritable() && !attrs.isWritable())
+ args->removeMapping(index);
}
-
- return result;
+ return true;
}
-ReturnedValue ArgumentsObject::getIndexed(const Managed *m, uint index, bool *hasProperty)
+ReturnedValue ArgumentsObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m);
- if (args->fullyCreated())
- return Object::getIndexed(m, index, hasProperty);
-
- if (index < static_cast<uint>(args->context()->argc())) {
+ 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))
+ return Object::virtualGet(m, id, receiver, hasProperty);
+ Q_ASSERT(index < static_cast<uint>(args->context()->function->formalParameterCount()));
if (hasProperty)
- *hasProperty = false;
- return Encode::undefined();
+ *hasProperty = true;
+ return args->context()->args()[index].asReturnedValue();
}
-bool ArgumentsObject::putIndexed(Managed *m, uint index, const Value &value)
+bool ArgumentsObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
ArgumentsObject *args = static_cast<ArgumentsObject *>(m);
- if (!args->fullyCreated() && index >= static_cast<uint>(args->context()->argc()))
- args->fullyCreate();
+ uint index = id.asArrayIndex();
- if (args->fullyCreated())
- return Object::putIndexed(m, index, value);
+ if (args == receiver && index < args->d()->argCount && !args->d()->fullyCreated) {
+ args->context()->setArg(index, value);
+ return true;
+ }
- args->context()->setArg(index, value);
- return true;
+ bool isMapped = (args == receiver && args->isMapped(index));
+ if (isMapped)
+ args->context()->setArg(index, value);
+
+ return Object::virtualPut(m, id, value, receiver);
}
-bool ArgumentsObject::deleteIndexedProperty(Managed *m, uint index)
+bool ArgumentsObject::virtualDeleteProperty(Managed *m, PropertyKey id)
{
ArgumentsObject *args = static_cast<ArgumentsObject *>(m);
- if (!args->fullyCreated())
- args->fullyCreate();
- return Object::deleteIndexedProperty(m, index);
+ args->fullyCreate();
+ bool result = Object::virtualDeleteProperty(m, id);
+ if (result)
+ args->removeMapping(id.asArrayIndex());
+ return result;
}
-PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index)
+PropertyAttributes ArgumentsObject::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
{
const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m);
- if (args->fullyCreated())
- return Object::queryIndexed(m, index);
-
- uint numAccessors = qMin(args->d()->nFormals, args->context()->argc());
- uint argCount = args->context()->argc();
- if (index >= argCount)
- return PropertyAttributes();
- if (index >= numAccessors)
+ uint index = id.asArrayIndex();
+ if (index < args->d()->argCount && !args->d()->fullyCreated) {
+ p->value = args->context()->args()[index];
return Attr_Data;
- return Attr_Accessor;
-}
+ }
-DEFINE_OBJECT_VTABLE(ArgumentsGetterFunction);
+ PropertyAttributes attrs = Object::virtualGetOwnProperty(m, id, p);
+ if (attrs.isEmpty() || !args->isMapped(index))
+ return attrs;
-ReturnedValue ArgumentsGetterFunction::call(const FunctionObject *getter, const Value *thisObject, const Value *, int)
-{
- ExecutionEngine *v4 = getter->engine();
- Scope scope(v4);
- const ArgumentsGetterFunction *g = static_cast<const ArgumentsGetterFunction *>(getter);
- Scoped<ArgumentsObject> o(scope, thisObject->as<ArgumentsObject>());
- if (!o)
- return v4->throwTypeError();
-
- Q_ASSERT(g->index() < static_cast<unsigned>(o->context()->argc()));
- return o->context()->args()[g->index()].asReturnedValue();
+ Q_ASSERT(index < static_cast<uint>(args->context()->function->formalParameterCount()));
+ if (p)
+ p->value = args->context()->args()[index];
+ return attrs;
}
-DEFINE_OBJECT_VTABLE(ArgumentsSetterFunction);
-
-ReturnedValue ArgumentsSetterFunction::call(const FunctionObject *setter, const Value *thisObject, const Value *argv, int argc)
+qint64 ArgumentsObject::virtualGetLength(const Managed *m)
{
- ExecutionEngine *v4 = setter->engine();
- Scope scope(v4);
- const ArgumentsSetterFunction *s = static_cast<const ArgumentsSetterFunction *>(setter);
- Scoped<ArgumentsObject> o(scope, thisObject->as<ArgumentsObject>());
- if (!o)
- return v4->throwTypeError();
-
- Q_ASSERT(s->index() < static_cast<unsigned>(o->context()->argc()));
- o->context()->setArg(s->index(), argc ? argv[0] : Primitive::undefinedValue());
- return Encode::undefined();
+ const ArgumentsObject *a = static_cast<const ArgumentsObject *>(m);
+ return a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->toLength();
}
-uint ArgumentsObject::getLength(const Managed *m)
+OwnPropertyKeyIterator *ArgumentsObject::virtualOwnPropertyKeys(const Object *m, Value *target)
{
- const ArgumentsObject *a = static_cast<const ArgumentsObject *>(m);
- if (a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->isInteger())
- return a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->integerValue();
- return Primitive::toUInt32(a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->doubleValue());
+ static_cast<ArgumentsObject *>(const_cast<Object *>(m))->fullyCreate();
+ return Object::virtualOwnPropertyKeys(m, target);
}
diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h
index ac281f555a..f0e2192c7e 100644
--- a/src/qml/jsruntime/qv4argumentsobject_p.h
+++ b/src/qml/jsruntime/qv4argumentsobject_p.h
@@ -59,33 +59,18 @@ namespace QV4 {
namespace Heap {
-#define ArgumentsGetterFunctionMembers(class, Member) \
- Member(class, NoMark, uint, index)
-
-DECLARE_HEAP_OBJECT(ArgumentsGetterFunction, FunctionObject) {
- DECLARE_MARKOBJECTS(ArgumentsGetterFunction);
- inline void init(QV4::ExecutionContext *scope, uint index);
-};
-
-#define ArgumentsSetterFunctionMembers(class, Member) \
- Member(class, NoMark, uint, index)
-
-DECLARE_HEAP_OBJECT(ArgumentsSetterFunction, FunctionObject) {
- DECLARE_MARKOBJECTS(ArgumentsSetterFunction);
- inline void init(QV4::ExecutionContext *scope, uint index);
-};
-
#define ArgumentsObjectMembers(class, Member) \
Member(class, Pointer, CallContext *, context) \
- Member(class, Pointer, MemberData *, mappedArguments) \
Member(class, NoMark, bool, fullyCreated) \
- Member(class, NoMark, int, nFormals)
+ Member(class, NoMark, uint, argCount) \
+ Member(class, NoMark, quint64, mapped)
DECLARE_HEAP_OBJECT(ArgumentsObject, Object) {
DECLARE_MARKOBJECTS(ArgumentsObject);
enum {
LengthPropertyIndex = 0,
- CalleePropertyIndex = 1
+ SymbolIteratorPropertyIndex = 1,
+ CalleePropertyIndex = 2
};
void init(CppStackFrame *frame);
};
@@ -95,45 +80,15 @@ DECLARE_HEAP_OBJECT(ArgumentsObject, Object) {
DECLARE_HEAP_OBJECT(StrictArgumentsObject, Object) {
enum {
LengthPropertyIndex = 0,
- CalleePropertyIndex = 1,
- CallerPropertyIndex = 3
+ SymbolIteratorPropertyIndex = 1,
+ CalleePropertyIndex = 2,
+ CalleeSetterPropertyIndex = 3
};
void init(CppStackFrame *frame);
};
}
-struct ArgumentsGetterFunction: FunctionObject
-{
- V4_OBJECT2(ArgumentsGetterFunction, FunctionObject)
-
- uint index() const { return d()->index; }
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
-};
-
-inline void
-Heap::ArgumentsGetterFunction::init(QV4::ExecutionContext *scope, uint index)
-{
- Heap::FunctionObject::init(scope);
- this->index = index;
-}
-
-struct ArgumentsSetterFunction: FunctionObject
-{
- V4_OBJECT2(ArgumentsSetterFunction, FunctionObject)
-
- uint index() const { return d()->index; }
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
-};
-
-inline void
-Heap::ArgumentsSetterFunction::init(QV4::ExecutionContext *scope, uint index)
-{
- Heap::FunctionObject::init(scope);
- this->index = index;
-}
-
-
struct ArgumentsObject: Object {
V4_OBJECT2(ArgumentsObject, Object)
Q_MANAGED_TYPE(ArgumentsObject)
@@ -142,18 +97,30 @@ struct ArgumentsObject: Object {
bool fullyCreated() const { return d()->fullyCreated; }
static bool isNonStrictArgumentsObject(Managed *m) {
- return m->d()->vtable() == staticVTable();
+ return m->vtable() == staticVTable();
}
- bool defineOwnProperty(ExecutionEngine *engine, uint index, const Property *desc, PropertyAttributes attrs);
- static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty);
- static bool putIndexed(Managed *m, uint index, const Value &value);
- static bool deleteIndexedProperty(Managed *m, uint index);
- static PropertyAttributes queryIndexed(const Managed *m, uint index);
- static uint getLength(const Managed *m);
+ static bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *desc, PropertyAttributes attrs);
+ 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 bool virtualDeleteProperty(Managed *m, PropertyKey id);
+ static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p);
+ static qint64 virtualGetLength(const Managed *m);
+ static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
void fullyCreate();
+ // There's a slight hack here, as this limits the amount of mapped arguments to 64, but that should be
+ // more than enough for all practical uses of arguments
+ bool isMapped(uint arg) const {
+ return arg < 64 && (d()->mapped & (1ull << arg));
+ }
+
+ void removeMapping(uint arg) {
+ if (arg < 64)
+ (d()->mapped &= ~(1ull << arg));
+ }
+
};
struct StrictArgumentsObject : Object {
diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp
index 59a2b9d913..a99ec16943 100644
--- a/src/qml/jsruntime/qv4arraybuffer.cpp
+++ b/src/qml/jsruntime/qv4arraybuffer.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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.
@@ -41,23 +41,56 @@
#include "qv4dataview_p.h"
#include "qv4string_p.h"
#include "qv4jscall_p.h"
+#include "qv4symbol_p.h"
using namespace QV4;
+DEFINE_OBJECT_VTABLE(SharedArrayBufferCtor);
DEFINE_OBJECT_VTABLE(ArrayBufferCtor);
+DEFINE_OBJECT_VTABLE(SharedArrayBuffer);
DEFINE_OBJECT_VTABLE(ArrayBuffer);
+void Heap::SharedArrayBufferCtor::init(QV4::ExecutionContext *scope)
+{
+ Heap::FunctionObject::init(scope, QStringLiteral("SharedArrayBuffer"));
+}
+
void Heap::ArrayBufferCtor::init(QV4::ExecutionContext *scope)
{
Heap::FunctionObject::init(scope, QStringLiteral("ArrayBuffer"));
}
-ReturnedValue ArrayBufferCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue SharedArrayBufferCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
+{
+ Scope scope(f);
+ if (newTarget->isUndefined())
+ return scope.engine->throwTypeError();
+
+ qint64 len = argc ? argv[0].toIndex() : 0;
+ if (scope.engine->hasException)
+ return Encode::undefined();
+ if (len < 0 || len >= INT_MAX)
+ return scope.engine->throwRangeError(QStringLiteral("SharedArrayBuffer: Invalid length."));
+
+ Scoped<SharedArrayBuffer> a(scope, scope.engine->memoryManager->allocate<SharedArrayBuffer>(len));
+ if (scope.engine->hasException)
+ return Encode::undefined();
+
+ return a->asReturnedValue();
+}
+
+ReturnedValue SharedArrayBufferCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
+{
+ return f->engine()->throwTypeError();
+}
+
+
+ReturnedValue ArrayBufferCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
{
ExecutionEngine *v4 = f->engine();
Scope scope(v4);
- ScopedValue l(scope, argc ? argv[0] : Primitive::undefinedValue());
+ ScopedValue l(scope, argc ? argv[0] : Value::undefinedValue());
double dl = l->toInteger();
if (v4->hasException)
return Encode::undefined();
@@ -66,18 +99,18 @@ ReturnedValue ArrayBufferCtor::callAsConstructor(const FunctionObject *f, const
return v4->throwRangeError(QLatin1String("ArrayBuffer constructor: invalid length"));
Scoped<ArrayBuffer> a(scope, v4->newArrayBuffer(len));
+ if (newTarget->heapObject() != f->heapObject() && newTarget->isFunctionObject()) {
+ const FunctionObject *nt = static_cast<const FunctionObject *>(newTarget);
+ ScopedObject o(scope, nt->protoProperty());
+ if (o)
+ a->setPrototypeOf(o);
+ }
if (scope.engine->hasException)
return Encode::undefined();
return a->asReturnedValue();
}
-
-ReturnedValue ArrayBufferCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
-{
- return callAsConstructor(f, argv, argc);
-}
-
ReturnedValue ArrayBufferCtor::method_isView(const FunctionObject *, const Value *, const Value *argv, int argc)
{
if (argc < 1)
@@ -91,26 +124,29 @@ ReturnedValue ArrayBufferCtor::method_isView(const FunctionObject *, const Value
}
-void Heap::ArrayBuffer::init(size_t length)
+void Heap::SharedArrayBuffer::init(size_t length)
{
Object::init();
- data = QTypedArrayData<char>::allocate(length + 1);
+ if (length < UINT_MAX)
+ data = QTypedArrayData<char>::allocate(length + 1);
if (!data) {
internalClass->engine->throwRangeError(QStringLiteral("ArrayBuffer: out of memory"));
return;
}
data->size = int(length);
memset(data->data(), 0, length + 1);
+ isShared = true;
}
-void Heap::ArrayBuffer::init(const QByteArray& array)
+void Heap::SharedArrayBuffer::init(const QByteArray& array)
{
Object::init();
data = const_cast<QByteArray&>(array).data_ptr();
data->ref.ref();
+ isShared = true;
}
-void Heap::ArrayBuffer::destroy()
+void Heap::SharedArrayBuffer::destroy()
{
if (data && !data->ref.deref())
QTypedArrayData<char>::deallocate(data);
@@ -143,57 +179,98 @@ void ArrayBuffer::detach() {
}
-void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor)
+void SharedArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor)
{
Scope scope(engine);
ScopedObject o(scope);
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
- ctor->defineDefaultProperty(QStringLiteral("isView"), ArrayBufferCtor::method_isView, 1);
+ ctor->addSymbolSpecies();
+
defineDefaultProperty(engine->id_constructor(), (o = ctor));
defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
defineDefaultProperty(QStringLiteral("slice"), method_slice, 2);
- defineDefaultProperty(QStringLiteral("toString"), method_toString, 0);
+ ScopedString name(scope, engine->newString(QStringLiteral("SharedArrayBuffer")));
+ defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name);
}
-ReturnedValue ArrayBufferPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
+ReturnedValue SharedArrayBufferPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- const ArrayBuffer *a = thisObject->as<ArrayBuffer>();
- if (!a)
+ const SharedArrayBuffer *a = thisObject->as<SharedArrayBuffer>();
+ if (!a || a->isDetachedBuffer() || !a->isSharedArrayBuffer())
return b->engine()->throwTypeError();
return Encode(a->d()->data->size);
}
-ReturnedValue ArrayBufferPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue SharedArrayBufferPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
- ExecutionEngine *v4 = b->engine();
- const ArrayBuffer *a = thisObject->as<ArrayBuffer>();
- if (!a)
- return v4->throwTypeError();
+ return slice(b, thisObject, argv, argc, true);
+}
+
+ReturnedValue SharedArrayBufferPrototype::slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc, bool shared)
+{
+ Scope scope(b);
+ const SharedArrayBuffer *a = thisObject->as<SharedArrayBuffer>();
+ if (!a || a->isDetachedBuffer() || (a->isSharedArrayBuffer() != shared))
+ return scope.engine->throwTypeError();
double start = argc > 0 ? argv[0].toInteger() : 0;
double end = (argc < 2 || argv[1].isUndefined()) ?
a->d()->data->size : argv[1].toInteger();
- if (v4->hasException)
+ if (scope.hasException())
return QV4::Encode::undefined();
double first = (start < 0) ? qMax(a->d()->data->size + start, 0.) : qMin(start, (double)a->d()->data->size);
double final = (end < 0) ? qMax(a->d()->data->size + end, 0.) : qMin(end, (double)a->d()->data->size);
- Scope scope(v4);
- ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor()));
+ const FunctionObject *constructor = a->speciesConstructor(scope, shared ? scope.engine->sharedArrayBufferCtor() : scope.engine->arrayBufferCtor());
if (!constructor)
- return v4->throwTypeError();
+ return scope.engine->throwTypeError();
double newLen = qMax(final - first, 0.);
ScopedValue argument(scope, QV4::Encode(newLen));
- QV4::Scoped<ArrayBuffer> newBuffer(scope, constructor->callAsConstructor(argument, 1));
- if (!newBuffer || newBuffer->d()->data->size < (int)newLen)
- return v4->throwTypeError();
+ QV4::Scoped<SharedArrayBuffer> newBuffer(scope, constructor->callAsConstructor(argument, 1));
+ if (!newBuffer || newBuffer->d()->data->size < (int)newLen ||
+ newBuffer->isDetachedBuffer() || (newBuffer->isSharedArrayBuffer() != shared) ||
+ newBuffer->sameValue(*a) ||
+ a->isDetachedBuffer())
+ return scope.engine->throwTypeError();
memcpy(newBuffer->d()->data->data(), a->d()->data->data() + (uint)first, newLen);
- return Encode::undefined();
+ return newBuffer->asReturnedValue();
+}
+
+
+void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor)
+{
+ Scope scope(engine);
+ ScopedObject o(scope);
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
+ ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
+ ctor->defineDefaultProperty(QStringLiteral("isView"), ArrayBufferCtor::method_isView, 1);
+ ctor->addSymbolSpecies();
+
+ defineDefaultProperty(engine->id_constructor(), (o = ctor));
+ defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
+ defineDefaultProperty(QStringLiteral("slice"), method_slice, 2);
+ defineDefaultProperty(QStringLiteral("toString"), method_toString, 0);
+ ScopedString name(scope, engine->newString(QStringLiteral("ArrayBuffer")));
+ defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name);
+}
+
+ReturnedValue ArrayBufferPrototype::method_get_byteLength(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ const ArrayBuffer *a = thisObject->as<ArrayBuffer>();
+ if (!a || a->isDetachedBuffer() || a->isSharedArrayBuffer())
+ return f->engine()->throwTypeError();
+
+ return Encode(a->d()->data->size);
+}
+
+ReturnedValue ArrayBufferPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ return slice(b, thisObject, argv, argc, false);
}
ReturnedValue ArrayBufferPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h
index 59e78ee85f..8344fa2554 100644
--- a/src/qml/jsruntime/qv4arraybuffer_p.h
+++ b/src/qml/jsruntime/qv4arraybuffer_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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.
@@ -59,48 +59,107 @@ namespace QV4 {
namespace Heap {
-struct ArrayBufferCtor : FunctionObject {
+struct SharedArrayBufferCtor : FunctionObject {
void init(QV4::ExecutionContext *scope);
};
-struct Q_QML_PRIVATE_EXPORT ArrayBuffer : Object {
+struct ArrayBufferCtor : SharedArrayBufferCtor {
+ void init(QV4::ExecutionContext *scope);
+};
+
+struct Q_QML_PRIVATE_EXPORT SharedArrayBuffer : Object {
void init(size_t length);
void init(const QByteArray& array);
void destroy();
QTypedArrayData<char> *data;
+ bool isShared;
+
+ uint byteLength() const { return data ? data->size : 0; }
- uint byteLength() const { return data->size; }
+ bool isDetachedBuffer() const { return !data; }
+ bool isSharedArrayBuffer() const { return isShared; }
};
+struct Q_QML_PRIVATE_EXPORT ArrayBuffer : SharedArrayBuffer {
+ void init(size_t length) {
+ SharedArrayBuffer::init(length);
+ isShared = false;
+ }
+ void init(const QByteArray& array) {
+ SharedArrayBuffer::init(array);
+ isShared = false;
+ }
+ void detachArrayBuffer() {
+ if (data && !data->ref.deref())
+ QTypedArrayData<char>::deallocate(data);
+ data = nullptr;
+ }
+};
+
+
}
-struct ArrayBufferCtor: FunctionObject
+struct SharedArrayBufferCtor : FunctionObject
{
- V4_OBJECT2(ArrayBufferCtor, FunctionObject)
+ V4_OBJECT2(SharedArrayBufferCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct ArrayBufferCtor : SharedArrayBufferCtor
+{
+ V4_OBJECT2(ArrayBufferCtor, SharedArrayBufferCtor)
+
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
static ReturnedValue method_isView(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct Q_QML_PRIVATE_EXPORT SharedArrayBuffer : Object
+{
+ V4_OBJECT2(SharedArrayBuffer, Object)
+ V4_NEEDS_DESTROY
+ V4_PROTOTYPE(sharedArrayBufferPrototype)
+ QByteArray asByteArray() const;
+ uint byteLength() const { return d()->byteLength(); }
+ char *data() { Q_ASSERT(d()->data); return d()->data->data(); }
+ const char *constData() { Q_ASSERT(d()->data); return d()->data->data(); }
+
+ bool isShared() { return d()->data->ref.isShared(); }
+ bool isDetachedBuffer() const { return !d()->data; }
+ bool isSharedArrayBuffer() const { return d()->isShared; }
};
-struct Q_QML_PRIVATE_EXPORT ArrayBuffer : Object
+struct Q_QML_PRIVATE_EXPORT ArrayBuffer : SharedArrayBuffer
{
- V4_OBJECT2(ArrayBuffer, Object)
+ V4_OBJECT2(ArrayBuffer, SharedArrayBuffer)
V4_NEEDS_DESTROY
V4_PROTOTYPE(arrayBufferPrototype)
QByteArray asByteArray() const;
uint byteLength() const { return d()->byteLength(); }
char *data() { detach(); return d()->data ? d()->data->data() : nullptr; }
+ // ### is that detach needed?
const char *constData() { detach(); return d()->data ? d()->data->data() : nullptr; }
-private:
+ bool isShared() { return d()->data && d()->data->ref.isShared(); }
void detach();
+ void detachArrayBuffer() { d()->detachArrayBuffer(); }
+};
+
+struct SharedArrayBufferPrototype : Object
+{
+ void init(ExecutionEngine *engine, Object *ctor);
+
+ static ReturnedValue method_get_byteLength(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_slice(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc, bool shared);
};
-struct ArrayBufferPrototype: Object
+struct ArrayBufferPrototype : SharedArrayBufferPrototype
{
void init(ExecutionEngine *engine, Object *ctor);
diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp
index 855407e6f7..654d33b8d1 100644
--- a/src/qml/jsruntime/qv4arraydata.cpp
+++ b/src/qml/jsruntime/qv4arraydata.cpp
@@ -47,25 +47,7 @@
using namespace QV4;
-QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON
-
-const QV4::VTable QV4::ArrayData::static_vtbl = {
- nullptr,
- 0,
- 0,
- QV4::ArrayData::IsExecutionContext,
- QV4::ArrayData::IsString,
- QV4::ArrayData::IsObject,
- QV4::ArrayData::IsFunctionObject,
- QV4::ArrayData::IsErrorObject,
- QV4::ArrayData::IsArrayData,
- 0,
- QV4::ArrayData::MyType,
- "ArrayData",
- Q_VTABLE_FUNCTION(QV4::ArrayData, destroy),
- ArrayData::Data::markObjects,
- isEqualTo
-};
+DEFINE_MANAGED_VTABLE(ArrayData);
const ArrayVTable SimpleArrayData::static_vtbl =
{
@@ -99,18 +81,9 @@ const ArrayVTable SparseArrayData::static_vtbl =
SparseArrayData::length
};
-QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF
-
Q_STATIC_ASSERT(sizeof(Heap::ArrayData) == sizeof(Heap::SimpleArrayData));
Q_STATIC_ASSERT(sizeof(Heap::ArrayData) == sizeof(Heap::SparseArrayData));
-static Q_ALWAYS_INLINE void storeValue(ReturnedValue *target, uint value)
-{
- Value v;
- v.setEmpty(value);
- *target = v.asReturnedValue();
-}
-
void Heap::ArrayData::markObjects(Heap::Base *base, MarkStack *stack)
{
ArrayData *a = static_cast<ArrayData *>(base);
@@ -145,8 +118,6 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt
if (d->type() > newType)
newType = d->type();
}
- if (enforceAttributes && newType == Heap::ArrayData::Simple)
- newType = Heap::ArrayData::Complex;
while (alloc < requested)
alloc *= 2;
@@ -195,7 +166,7 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt
Heap::SparseArrayData *sparse = static_cast<Heap::SparseArrayData *>(newData->d());
- ReturnedValue *lastFree;
+ Value *lastFree;
if (d && d->type() == Heap::ArrayData::Sparse) {
Heap::SparseArrayData *old = static_cast<Heap::SparseArrayData *>(d->d());
sparse->sparse = old->sparse;
@@ -204,29 +175,29 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt
} else {
sparse->sparse = new SparseArray;
lastFree = &sparse->sparse->freeList;
- storeValue(lastFree, 0);
+ *lastFree = Encode(0);
for (uint i = 0; i < toCopy; ++i) {
if (!sparse->values[i].isEmpty()) {
SparseArrayNode *n = sparse->sparse->insert(i);
n->value = i;
} else {
- storeValue(lastFree, i);
+ *lastFree = Encode(i);
sparse->values.values[i].setEmpty();
- lastFree = &sparse->values.values[i].rawValueRef();
+ lastFree = &sparse->values.values[i];
}
}
}
if (toCopy < sparse->values.alloc) {
for (uint i = toCopy; i < sparse->values.alloc; ++i) {
- storeValue(lastFree, i);
+ *lastFree = Encode(i);
sparse->values.values[i].setEmpty();
- lastFree = &sparse->values.values[i].rawValueRef();
+ lastFree = &sparse->values.values[i];
}
}
- storeValue(lastFree, UINT_MAX);
+ *lastFree = Encode(-1);
- Q_ASSERT(Value::fromReturnedValue(sparse->sparse->freeList).isEmpty());
+ Q_ASSERT(sparse->sparse->freeList.isInteger());
// ### Could explicitly free the old data
}
@@ -248,7 +219,7 @@ ReturnedValue SimpleArrayData::get(const Heap::ArrayData *d, uint index)
{
const Heap::SimpleArrayData *dd = static_cast<const Heap::SimpleArrayData *>(d);
if (index >= dd->values.size)
- return Primitive::emptyValue().asReturnedValue();
+ return Value::emptyValue().asReturnedValue();
return dd->data(index).asReturnedValue();
}
@@ -273,7 +244,7 @@ bool SimpleArrayData::del(Object *o, uint index)
return true;
if (!dd->attrs || dd->attrs[index].isConfigurable()) {
- dd->setData(o->engine(), index, Primitive::emptyValue());
+ dd->setData(o->engine(), index, Value::emptyValue());
if (dd->attrs)
dd->attrs[index] = Attr_Data;
return true;
@@ -355,7 +326,7 @@ bool SimpleArrayData::putArray(Object *o, uint index, const Value *values, uint
}
QV4::ExecutionEngine *e = o->engine();
for (uint i = dd->values.size; i < index; ++i)
- dd->setData(e, i, Primitive::emptyValue());
+ dd->setData(e, i, Value::emptyValue());
for (uint i = 0; i < n; ++i)
dd->setData(e, index + i, values[i]);
dd->values.size = qMax(dd->values.size, index + n);
@@ -368,12 +339,12 @@ void SparseArrayData::free(Heap::ArrayData *d, uint idx)
Value *v = d->values.values + idx;
if (d->attrs && d->attrs[idx].isAccessor()) {
// double slot, free both. Order is important, so we have a double slot for allocation again afterwards.
- v[1].setEmpty(Value::fromReturnedValue(d->sparse->freeList).emptyValue());
- v[0].setEmpty(idx + 1);
+ v[1] = d->sparse->freeList;
+ v[0] = Encode(idx + 1);
} else {
- v->setEmpty(Value::fromReturnedValue(d->sparse->freeList).emptyValue());
+ *v = d->sparse->freeList;
}
- d->sparse->freeList = Primitive::emptyValue(idx).asReturnedValue();
+ d->sparse->freeList = Encode(idx);
if (d->attrs)
d->attrs[idx].clear();
}
@@ -390,36 +361,34 @@ uint SparseArrayData::allocate(Object *o, bool doubleSlot)
Q_ASSERT(o->d()->arrayData->type == Heap::ArrayData::Sparse);
Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>();
if (doubleSlot) {
- ReturnedValue *last = &dd->sparse->freeList;
+ Value *last = &dd->sparse->freeList;
while (1) {
- if (Value::fromReturnedValue(*last).value() == UINT_MAX) {
+ if (last->int_32() == -1) {
reallocate(o, dd->values.alloc + 2, true);
dd = o->d()->arrayData.cast<Heap::SimpleArrayData>();
last = &dd->sparse->freeList;
- Q_ASSERT(Value::fromReturnedValue(*last).value() != UINT_MAX);
+ Q_ASSERT(last->int_32() != -1);
}
- Q_ASSERT(dd->values[Value::fromReturnedValue(*last).value()].value() != Value::fromReturnedValue(*last).value());
- if (dd->values[Value::fromReturnedValue(*last).value()].value() == (Value::fromReturnedValue(*last).value() + 1)) {
+ Q_ASSERT(dd->values[static_cast<uint>(last->int_32())].int_32() != last->int_32());
+ if (dd->values[static_cast<uint>(last->int_32())].int_32() == last->int_32() + 1) {
// found two slots in a row
- uint idx = Value::fromReturnedValue(*last).emptyValue();
- Value lastV = Value::fromReturnedValue(*last);
- lastV.setEmpty(dd->values[lastV.emptyValue() + 1].value());
- *last = lastV.rawValue();
+ uint idx = static_cast<uint>(last->int_32());
+ *last = Encode(dd->values[static_cast<uint>(last->int_32()) + 1].int_32());
dd->attrs[idx] = Attr_Accessor;
return idx;
}
- last = &dd->values.values[Value::fromReturnedValue(*last).value()].rawValueRef();
+ last = &dd->values.values[last->int_32()];
}
} else {
- if (Value::fromReturnedValue(dd->sparse->freeList).value() == UINT_MAX) {
+ if (dd->sparse->freeList.int_32() == -1) {
reallocate(o, dd->values.alloc + 1, false);
dd = o->d()->arrayData.cast<Heap::SimpleArrayData>();
}
- uint idx = Value::fromReturnedValue(dd->sparse->freeList).value();
- Q_ASSERT(idx != UINT_MAX);
- dd->sparse->freeList = dd->values[idx].asReturnedValue();
- Q_ASSERT(Value::fromReturnedValue(dd->sparse->freeList).isEmpty());
+ Q_ASSERT(dd->sparse->freeList.int_32() != -1);
+ uint idx = static_cast<uint>(dd->sparse->freeList.int_32());
+ dd->sparse->freeList = dd->values[idx];
+ Q_ASSERT(dd->sparse->freeList.isInteger());
if (dd->attrs)
dd->attrs[idx] = Attr_Data;
return idx;
@@ -431,7 +400,7 @@ ReturnedValue SparseArrayData::get(const Heap::ArrayData *d, uint index)
const Heap::SparseArrayData *s = static_cast<const Heap::SparseArrayData *>(d);
index = s->mappedIndex(index);
if (index == UINT_MAX)
- return Primitive::emptyValue().asReturnedValue();
+ return Value::emptyValue().asReturnedValue();
return s->values[index].asReturnedValue();
}
@@ -474,14 +443,14 @@ bool SparseArrayData::del(Object *o, uint index)
if (isAccessor) {
// free up both indices
- dd->values.values[pidx + 1].setEmpty(Value::fromReturnedValue(dd->sparse->freeList).emptyValue());
- dd->values.values[pidx].setEmpty(pidx + 1);
+ dd->values.values[pidx + 1] = dd->sparse->freeList;
+ dd->values.values[pidx] = Encode(pidx + 1);
} else {
Q_ASSERT(dd->type == Heap::ArrayData::Sparse);
- dd->values.values[pidx].setEmpty(Value::fromReturnedValue(dd->sparse->freeList).emptyValue());
+ dd->values.values[pidx] = dd->sparse->freeList;
}
- dd->sparse->freeList = Primitive::emptyValue(pidx).asReturnedValue();
+ dd->sparse->freeList = Encode(pidx);
dd->sparse->erase(n);
return true;
}
@@ -593,7 +562,7 @@ uint ArrayData::append(Object *obj, ArrayObject *otherObj, uint n)
if (!other || ArgumentsObject::isNonStrictArgumentsObject(otherObj)) {
ScopedValue v(scope);
for (uint i = 0; i < n; ++i)
- obj->arraySet(oldSize + i, (v = otherObj->getIndexed(i)));
+ obj->arraySet(oldSize + i, (v = otherObj->get(i)));
} else if (other && other->isSparse()) {
Heap::SparseArrayData *os = static_cast<Heap::SparseArrayData *>(other->d());
if (other->hasAttributes()) {
@@ -635,7 +604,7 @@ void ArrayData::insert(Object *o, uint index, const Value *v, bool isAccessor)
if (index >= d->values.size) {
// mark possible hole in the array
for (uint i = d->values.size; i < index; ++i)
- d->setData(o->engine(), i, Primitive::emptyValue());
+ d->setData(o->engine(), i, Value::emptyValue());
d->values.size = index + 1;
}
d->setData(o->engine(), index, *v);
@@ -828,7 +797,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c
break;
Q_ASSERT(!d->attrs || !d->attrs[len].isAccessor());
d->setData(engine, i, d->data(len));
- d->setData(engine, len, Primitive::emptyValue());
+ d->setData(engine, len, Value::emptyValue());
}
}
diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h
index 7ec060f9c6..64b2161ef5 100644
--- a/src/qml/jsruntime/qv4arraydata_p.h
+++ b/src/qml/jsruntime/qv4arraydata_p.h
@@ -101,34 +101,22 @@ namespace Heap {
DECLARE_HEAP_OBJECT(ArrayData, Base) {
static void markObjects(Heap::Base *base, MarkStack *stack);
- enum Type { Simple = 0, Complex = 1, Sparse = 2, Custom = 3 };
-
- struct Index {
- Heap::ArrayData *arrayData;
- uint index;
-
- void set(EngineBase *e, Value newVal) {
- arrayData->values.set(e, index, newVal);
- }
- const Value *operator->() const { return &arrayData->values[index]; }
- const Value &operator*() const { return arrayData->values[index]; }
- bool isNull() const { return !arrayData; }
- };
+ enum Type { Simple = 0, Sparse = 1, Custom = 2 };
bool isSparse() const { return type == Sparse; }
- const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(Base::vtable()); }
+ const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(internalClass->vtable); }
inline ReturnedValue get(uint i) const {
return vtable()->get(this, i);
}
inline bool getProperty(uint index, Property *p, PropertyAttributes *attrs);
inline void setProperty(EngineBase *e, uint index, const Property *p);
- inline Index getValueOrSetter(uint index, PropertyAttributes *attrs);
+ inline PropertyIndex getValueOrSetter(uint index, PropertyAttributes *attrs);
inline PropertyAttributes attributes(uint i) const;
bool isEmpty(uint i) const {
- return get(i) == Primitive::emptyValue().asReturnedValue();
+ return get(i) == Value::emptyValue().asReturnedValue();
}
inline uint length() const {
@@ -187,8 +175,6 @@ struct Q_QML_EXPORT ArrayData : public Managed
IsArrayData = true
};
- typedef Heap::ArrayData::Index Index;
-
uint alloc() const { return d()->values.alloc; }
uint &alloc() { return d()->values.alloc; }
void setAlloc(uint a) { d()->values.alloc = a; }
@@ -303,9 +289,9 @@ bool ArrayData::getProperty(uint index, Property *p, PropertyAttributes *attrs)
*attrs = attributes(index);
if (p) {
- p->value = *(Index{ this, mapped });
+ p->value = *(PropertyIndex{ this, values.values + mapped });
if (attrs->isAccessor())
- p->set = *(Index{ this, mapped + 1 /*Object::SetterOffset*/ });
+ p->set = *(PropertyIndex{ this, values.values + mapped + 1 /*Object::SetterOffset*/ });
}
return true;
}
@@ -326,16 +312,18 @@ inline PropertyAttributes ArrayData::attributes(uint i) const
return static_cast<const SimpleArrayData *>(this)->attributes(i);
}
-ArrayData::Index ArrayData::getValueOrSetter(uint index, PropertyAttributes *attrs)
+PropertyIndex ArrayData::getValueOrSetter(uint index, PropertyAttributes *attrs)
{
uint idx = mappedIndex(index);
if (idx == UINT_MAX) {
*attrs = Attr_Invalid;
- return { nullptr, 0 };
+ return { nullptr, nullptr };
}
*attrs = attributes(index);
- return { this, attrs->isAccessor() ? idx + 1 /* QV4::Object::SetterOffset*/ : idx };
+ if (attrs->isAccessor())
+ ++idx;
+ return { this, values.values + idx };
}
diff --git a/src/qml/jsruntime/qv4arrayiterator.cpp b/src/qml/jsruntime/qv4arrayiterator.cpp
new file mode 100644
index 0000000000..199b1a728a
--- /dev/null
+++ b/src/qml/jsruntime/qv4arrayiterator.cpp
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Crimson AS <info@crimson.no>
+** 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 <private/qv4iterator_p.h>
+#include <private/qv4arrayiterator_p.h>
+#include <private/qv4typedarray_p.h>
+#include <private/qv4symbol_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(ArrayIteratorObject);
+
+void ArrayIteratorPrototype::init(ExecutionEngine *e)
+{
+ defineDefaultProperty(QStringLiteral("next"), method_next, 0);
+
+ Scope scope(e);
+ ScopedString val(scope, e->newString(QLatin1String("Array Iterator")));
+ defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val);
+}
+
+ReturnedValue ArrayIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int)
+{
+ Scope scope(b);
+ const ArrayIteratorObject *thisObject = that->as<ArrayIteratorObject>();
+ if (!thisObject)
+ return scope.engine->throwTypeError(QLatin1String("Not an Array Iterator instance"));
+
+ ScopedObject a(scope, thisObject->d()->iteratedObject);
+ if (!a) {
+ QV4::Value undefined = Value::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+ }
+
+ quint32 index = thisObject->d()->nextIndex;
+ IteratorKind itemKind = thisObject->d()->iterationKind;
+
+ Scoped<TypedArray> ta(scope, a->as<TypedArray>());
+ quint32 len = a->getLength();
+
+ if (index >= len) {
+ thisObject->d()->iteratedObject.set(scope.engine, nullptr);
+ QV4::Value undefined = Value::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+ }
+
+ thisObject->d()->nextIndex = index + 1;
+ if (itemKind == KeyIteratorKind) {
+ return IteratorPrototype::createIterResultObject(scope.engine, Value::fromInt32(index), false);
+ }
+
+ ReturnedValue elementValue = a->get(index);
+ CHECK_EXCEPTION();
+
+ if (itemKind == ValueIteratorKind) {
+ return IteratorPrototype::createIterResultObject(scope.engine, Value::fromReturnedValue(elementValue), false);
+ } else {
+ Q_ASSERT(itemKind == KeyValueIteratorKind);
+
+ ScopedArrayObject resultArray(scope, scope.engine->newArrayObject());
+ resultArray->arrayReserve(2);
+ resultArray->arrayPut(0, Value::fromInt32(index));
+ resultArray->arrayPut(1, Value::fromReturnedValue(elementValue));
+ resultArray->setArrayLengthUnchecked(2);
+
+ return IteratorPrototype::createIterResultObject(scope.engine, resultArray, false);
+ }
+}
+
diff --git a/src/qml/jsruntime/qv4arrayiterator_p.h b/src/qml/jsruntime/qv4arrayiterator_p.h
new file mode 100644
index 0000000000..6d6bb466f1
--- /dev/null
+++ b/src/qml/jsruntime/qv4arrayiterator_p.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Crimson AS <info@crimson.no>
+** 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 QV4ARRAYITERATOR_P_H
+#define QV4ARRAYITERATOR_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 "qv4object_p.h"
+#include "qv4iterator_p.h"
+#include "qv4arraydata_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+namespace QV4 {
+
+namespace Heap {
+
+#define ArrayIteratorObjectMembers(class, Member) \
+ Member(class, Pointer, Object *, iteratedObject) \
+ Member(class, NoMark, IteratorKind, iterationKind) \
+ Member(class, NoMark, quint32, nextIndex)
+
+DECLARE_HEAP_OBJECT(ArrayIteratorObject, Object) {
+ DECLARE_MARKOBJECTS(ArrayIteratorObject);
+ void init(Object *obj, QV4::ExecutionEngine *engine)
+ {
+ Object::init();
+ this->iteratedObject.set(engine, obj);
+ this->nextIndex = 0;
+ }
+};
+
+}
+
+struct ArrayIteratorPrototype : Object
+{
+ V4_PROTOTYPE(iteratorPrototype)
+ void init(ExecutionEngine *engine);
+
+ static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct ArrayIteratorObject : Object
+{
+ V4_OBJECT2(ArrayIteratorObject, Object)
+ Q_MANAGED_TYPE(ArrayIteratorObject)
+ V4_PROTOTYPE(arrayIteratorPrototype)
+
+ void init(ExecutionEngine *engine);
+};
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4ARRAYITERATOR_P_H
+
diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp
index bd019d3bcb..2e7c994550 100644
--- a/src/qml/jsruntime/qv4arrayobject.cpp
+++ b/src/qml/jsruntime/qv4arrayobject.cpp
@@ -1,5 +1,6 @@
/****************************************************************************
**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
@@ -38,13 +39,17 @@
****************************************************************************/
#include "qv4arrayobject_p.h"
+#include "qv4objectiterator_p.h"
+#include "qv4arrayiterator_p.h"
#include "qv4sparsearray_p.h"
#include "qv4objectproto_p.h"
#include "qv4jscall_p.h"
#include "qv4argumentsobject_p.h"
#include "qv4runtime_p.h"
#include "qv4string_p.h"
+#include "qv4symbol_p.h"
#include <QtCore/qscopedvaluerollback.h>
+#include "qv4proxy_p.h"
using namespace QV4;
@@ -55,7 +60,7 @@ void Heap::ArrayCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("Array"));
}
-ReturnedValue ArrayCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue ArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
{
ExecutionEngine *v4 = static_cast<const ArrayCtor *>(f)->engine();
Scope scope(v4);
@@ -80,25 +85,50 @@ ReturnedValue ArrayCtor::callAsConstructor(const FunctionObject *f, const Value
return a.asReturnedValue();
}
-ReturnedValue ArrayCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
+ReturnedValue ArrayCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
- return callAsConstructor(f, argv, argc);
+ return virtualCallAsConstructor(f, argv, argc, f);
}
void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor)
{
Scope scope(engine);
ScopedObject o(scope);
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
ctor->defineDefaultProperty(QStringLiteral("isArray"), method_isArray, 1);
+ ctor->defineDefaultProperty(QStringLiteral("of"), method_of, 0);
+ ctor->defineDefaultProperty(QStringLiteral("from"), method_from, 1);
+ ctor->addSymbolSpecies();
+
+ ScopedObject unscopables(scope, engine->newObject(engine->classes[EngineBase::Class_Empty]->changeVTable(QV4::Object::staticVTable())));
+ ScopedString name(scope);
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(engine->id_toString(), method_toString, 0);
- defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString, 0);
+ defineDefaultProperty(engine->id_toLocaleString(), method_toLocaleString, 0);
defineDefaultProperty(QStringLiteral("concat"), method_concat, 1);
- defineDefaultProperty(QStringLiteral("find"), method_find, 1);
- defineDefaultProperty(QStringLiteral("findIndex"), method_findIndex, 1);
+ name = engine->newIdentifier(QStringLiteral("copyWithin"));
+ unscopables->put(name, Value::fromBoolean(true));
+ defineDefaultProperty(name, method_copyWithin, 2);
+ name = engine->newIdentifier(QStringLiteral("entries"));
+ unscopables->put(name, Value::fromBoolean(true));
+ defineDefaultProperty(name, method_entries, 0);
+ name = engine->newIdentifier(QStringLiteral("fill"));
+ unscopables->put(name, Value::fromBoolean(true));
+ defineDefaultProperty(name, method_fill, 1);
+ name = engine->newIdentifier(QStringLiteral("find"));
+ unscopables->put(name, Value::fromBoolean(true));
+ defineDefaultProperty(name, method_find, 1);
+ name = engine->newIdentifier(QStringLiteral("findIndex"));
+ unscopables->put(name, Value::fromBoolean(true));
+ defineDefaultProperty(name, method_findIndex, 1);
+ name = engine->newIdentifier(QStringLiteral("includes"));
+ unscopables->put(name, Value::fromBoolean(true));
+ defineDefaultProperty(name, method_includes, 1);
defineDefaultProperty(QStringLiteral("join"), method_join, 1);
+ name = engine->newIdentifier(QStringLiteral("keys"));
+ unscopables->put(name, Value::fromBoolean(true));
+ defineDefaultProperty(name, method_keys, 0);
defineDefaultProperty(QStringLiteral("pop"), method_pop, 0);
defineDefaultProperty(QStringLiteral("push"), method_push, 1);
defineDefaultProperty(QStringLiteral("reverse"), method_reverse, 0);
@@ -116,12 +146,205 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(QStringLiteral("filter"), method_filter, 1);
defineDefaultProperty(QStringLiteral("reduce"), method_reduce, 1);
defineDefaultProperty(QStringLiteral("reduceRight"), method_reduceRight, 1);
+ ScopedString valuesString(scope, engine->newIdentifier(QStringLiteral("values")));
+ ScopedObject values(scope, FunctionObject::createBuiltinFunction(engine, valuesString, method_values, 0));
+ engine->jsObjects[ExecutionEngine::ArrayProtoValues] = values;
+ unscopables->put(valuesString, Value::fromBoolean(true));
+ defineDefaultProperty(valuesString, values);
+ defineDefaultProperty(engine->symbol_iterator(), values);
+
+ defineReadonlyConfigurableProperty(engine->symbol_unscopables(), unscopables);
}
ReturnedValue ArrayPrototype::method_isArray(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- bool isArray = argc && argv[0].as<ArrayObject>();
- return Encode(isArray);
+ if (!argc || !argv->objectValue())
+ return Encode(false);
+ return Encode(argv->objectValue()->isArray());
+}
+
+static ScopedObject createObjectFromCtorOrArray(Scope &scope, ScopedFunctionObject ctor, bool useLen, int len)
+{
+ ScopedObject a(scope, Value::undefinedValue());
+
+ if (ctor && ctor->isConstructor()) {
+ // this isn't completely kosher. for instance:
+ // Array.from.call(Object, []).constructor == Object
+ // is expected by the tests, but naturally, we get Number.
+ ScopedValue argument(scope, useLen ? QV4::Encode(len) : Value::undefinedValue());
+ a = ctor->callAsConstructor(argument, useLen ? 1 : 0);
+ } else {
+ a = scope.engine->newArrayObject(len);
+ }
+
+ return a;
+}
+
+ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(builtin);
+ ScopedFunctionObject thatCtor(scope, thisObject);
+ ScopedObject itemsObject(scope, argv[0]);
+ bool usingIterator = false;
+
+ if (itemsObject) {
+ // If the object claims to support iterators, then let's try use them.
+ ScopedValue it(scope, itemsObject->get(scope.engine->symbol_iterator()));
+ if (!it->isNullOrUndefined()) {
+ ScopedFunctionObject itfunc(scope, it);
+ if (!itfunc)
+ return scope.engine->throwTypeError();
+ usingIterator = true;
+ }
+ }
+
+ ScopedFunctionObject mapfn(scope, Value::undefinedValue());
+ Value *mapArguments = nullptr;
+ if (argc > 1) {
+ mapfn = ScopedFunctionObject(scope, argv[1]);
+ if (!mapfn)
+ return scope.engine->throwTypeError(QString::fromLatin1("%1 is not a function").arg(argv[1].toQStringNoThrow()));
+ mapArguments = scope.alloc(2);
+ }
+
+ ScopedValue thisArg(scope);
+ if (argc > 2)
+ thisArg = argv[2];
+
+ if (usingIterator) {
+ // Item iteration supported, so let's go ahead and try use that.
+ ScopedObject a(createObjectFromCtorOrArray(scope, thatCtor, false, 0));
+ CHECK_EXCEPTION();
+ ScopedObject iterator(scope, Runtime::method_getIterator(scope.engine, itemsObject, true));
+ CHECK_EXCEPTION(); // symbol_iterator threw; whoops.
+ if (!iterator) {
+ return scope.engine->throwTypeError(); // symbol_iterator wasn't an object.
+ }
+
+ qint64 k = 0;
+ ScopedValue mappedValue(scope);
+ Value *nextValue = scope.alloc(1);
+ ScopedValue done(scope);
+
+ // The loop below pulls out all the properties using the iterator, and
+ // sets them into the created array.
+ forever {
+ if (k > (static_cast<qint64>(1) << 53) - 1) {
+ ScopedValue falsey(scope, Encode(false));
+ ScopedValue error(scope, scope.engine->throwTypeError());
+ return Runtime::method_iteratorClose(scope.engine, iterator, falsey);
+ }
+
+ // Retrieve the next value. If the iteration ends, we're done here.
+ done = Value::fromReturnedValue(Runtime::method_iteratorNext(scope.engine, iterator, nextValue));
+ CHECK_EXCEPTION();
+ if (done->toBoolean()) {
+ if (ArrayObject *ao = a->as<ArrayObject>()) {
+ ao->setArrayLengthUnchecked(k);
+ } else {
+ a->set(scope.engine->id_length(), Value::fromDouble(k), QV4::Object::DoThrowOnRejection);
+ CHECK_EXCEPTION();
+ }
+ return a.asReturnedValue();
+ }
+
+ if (mapfn) {
+ mapArguments[0] = *nextValue;
+ mapArguments[1] = Value::fromDouble(k);
+ mappedValue = mapfn->call(thisArg, mapArguments, 2);
+ if (scope.engine->hasException)
+ return Runtime::method_iteratorClose(scope.engine, iterator, Value::fromBoolean(false));
+ } else {
+ mappedValue = *nextValue;
+ }
+
+ if (a->getOwnProperty(PropertyKey::fromArrayIndex(k)) == Attr_Invalid) {
+ a->arraySet(k, mappedValue);
+ } else {
+ // Don't return: we need to close the iterator.
+ scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k));
+ }
+
+ if (scope.engine->hasException) {
+ ScopedValue falsey(scope, Encode(false));
+ return Runtime::method_iteratorClose(scope.engine, iterator, falsey);
+ }
+
+ k++;
+ }
+
+ // the return is hidden up in the loop above, when iteration finishes.
+ } else {
+ // Array-like fallback. We request properties by index, and set them on
+ // the return object.
+ ScopedObject arrayLike(scope, argv[0].toObject(scope.engine));
+ if (!arrayLike)
+ return scope.engine->throwTypeError(QString::fromLatin1("Cannot convert %1 to object").arg(argv[0].toQStringNoThrow()));
+ qint64 len = arrayLike->getLength();
+ ScopedObject a(createObjectFromCtorOrArray(scope, thatCtor, true, len));
+ CHECK_EXCEPTION();
+
+ qint64 k = 0;
+ ScopedValue mappedValue(scope, Value::undefinedValue());
+ ScopedValue kValue(scope);
+ while (k < len) {
+ kValue = arrayLike->get(k);
+ CHECK_EXCEPTION();
+
+ if (mapfn) {
+ mapArguments[0] = kValue;
+ mapArguments[1] = Value::fromDouble(k);
+ mappedValue = mapfn->call(thisArg, mapArguments, 2);
+ CHECK_EXCEPTION();
+ } else {
+ mappedValue = kValue;
+ }
+
+ if (a->getOwnProperty(PropertyKey::fromArrayIndex(k)) != Attr_Invalid)
+ return scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k));
+
+ a->arraySet(k, mappedValue);
+ CHECK_EXCEPTION();
+
+ k++;
+ }
+
+ if (ArrayObject *ao = a->as<ArrayObject>()) {
+ ao->setArrayLengthUnchecked(k);
+ } else {
+ a->set(scope.engine->id_length(), Value::fromDouble(k), QV4::Object::DoThrowOnRejection);
+ CHECK_EXCEPTION();
+ }
+ return a.asReturnedValue();
+ }
+
+}
+
+ReturnedValue ArrayPrototype::method_of(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(builtin);
+ ScopedFunctionObject that(scope, thisObject);
+ ScopedObject a(createObjectFromCtorOrArray(scope, that, true, argc));
+ CHECK_EXCEPTION();
+
+ int k = 0;
+ while (k < argc) {
+ if (a->getOwnProperty(PropertyKey::fromArrayIndex(k)) != Attr_Invalid) {
+ return scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k));
+ }
+ a->arraySet(k, argv[k]);
+ CHECK_EXCEPTION();
+
+ k++;
+ }
+
+ // ArrayObject updates its own length, and will throw if we try touch it.
+ if (!a->as<ArrayObject>()) {
+ a->set(scope.engine->id_length(), Value::fromDouble(argc), QV4::Object::DoThrowOnRejection);
+ CHECK_EXCEPTION();
+ }
+
+ return a.asReturnedValue();
}
ReturnedValue ArrayPrototype::method_toString(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
@@ -138,9 +361,36 @@ ReturnedValue ArrayPrototype::method_toString(const FunctionObject *builtin, con
return ObjectPrototype::method_toString(builtin, that, argv, argc);
}
-ReturnedValue ArrayPrototype::method_toLocaleString(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue ArrayPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- return method_toString(builtin, thisObject, argv, argc);
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject);
+ if (!instance)
+ return scope.engine->throwTypeError();
+
+ uint len = instance->getLength();
+ const QString separator = QStringLiteral(",");
+
+ QString R;
+
+ ScopedValue v(scope);
+ ScopedString s(scope);
+
+ for (uint k = 0; k < len; ++k) {
+ if (k)
+ R += separator;
+
+ v = instance->get(k);
+ if (v->isNullOrUndefined())
+ continue;
+ v = Runtime::method_callElement(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0);
+ s = v->toString(scope.engine);
+ if (scope.hasException())
+ return Encode::undefined();
+
+ R += s->toQString();
+ }
+ return scope.engine->newString(R)->asReturnedValue();
}
ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value *that, const Value *argv, int argc)
@@ -152,37 +402,128 @@ ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value
ScopedArrayObject result(scope, scope.engine->newArrayObject());
- if (thisObject->isArrayObject()) {
- result->copyArrayData(thisObject);
- } else {
- result->arraySet(0, thisObject);
- }
-
ScopedArrayObject elt(scope);
ScopedObject eltAsObj(scope);
ScopedValue entry(scope);
- for (int i = 0, ei = argc; i < ei; ++i) {
- eltAsObj = argv[i];
- elt = argv[i];
+ for (int i = -1; i < argc; ++i) {
+ const Value *v = i == -1 ? thisObject.getPointer() : argv + i;
+ eltAsObj = *v;
+ elt = *v;
if (elt) {
uint n = elt->getLength();
uint newLen = ArrayData::append(result, elt, n);
result->setArrayLengthUnchecked(newLen);
+ } else if (eltAsObj && eltAsObj->isConcatSpreadable()) {
+ const uint startIndex = result->getLength();
+ const uint len = eltAsObj->getLength();
+ if (scope.engine->hasException)
+ return Encode::undefined();
+
+ for (uint i = 0; i < len; ++i) {
+ bool hasProperty = false;
+ entry = eltAsObj->get(i, &hasProperty);
+ if (hasProperty) {
+ if (!result->put(startIndex + i, entry))
+ return scope.engine->throwTypeError();
+ }
+ }
} else if (eltAsObj && eltAsObj->isListType()) {
const uint startIndex = result->getLength();
for (int i = 0, len = eltAsObj->getLength(); i < len; ++i) {
- entry = eltAsObj->getIndexed(i);
+ entry = eltAsObj->get(i);
// spec says not to throw if this fails
- result->putIndexed(startIndex + i, entry);
+ result->put(startIndex + i, entry);
}
} else {
- result->arraySet(result->getLength(), argv[i]);
+ result->arraySet(result->getLength(), *v);
}
}
return result.asReturnedValue();
}
+ReturnedValue ArrayPrototype::method_copyWithin(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
+ if (!instance)
+ RETURN_UNDEFINED();
+
+ double len = instance->getLength();
+ double target = argv[0].toInteger();
+ double start = argc > 1 ? argv[1].toInteger() : 0;
+ double end = len;
+
+ if (argc > 2 && !argv[2].isUndefined()) {
+ end = argv[2].toInteger();
+ }
+
+ double relativeTarget = target;
+ double relativeStart = start;
+ double relativeEnd = end;
+ double from = 0;
+ double to = 0;
+
+ if (relativeTarget < 0) {
+ to = std::max(len+relativeTarget, 0.0);
+ } else {
+ to = std::min(relativeTarget, len);
+ }
+ if (relativeStart < 0) {
+ from = std::max(len+relativeStart, 0.0);
+ } else {
+ from = std::min(relativeStart, len);
+ }
+
+ double fin = 0;
+ if (relativeEnd < 0) {
+ fin = std::max(len+relativeEnd, 0.0);
+ } else {
+ fin = std::min(relativeEnd, len);
+ }
+ double count = std::min(fin-from, len-to);
+ double direction = 1;
+ if (from < to && to < from+count) {
+ direction = -1;
+ from = from + count - 1;
+ to = to + count - 1;
+ }
+
+ while (count > 0) {
+ bool fromPresent = false;
+ ScopedValue fromVal(scope, instance->get(from, &fromPresent));
+
+ if (fromPresent) {
+ instance->setIndexed(to, fromVal, QV4::Object::DoThrowOnRejection);
+ CHECK_EXCEPTION();
+ } else {
+ bool didDelete = instance->deleteProperty(PropertyKey::fromArrayIndex(to));
+ CHECK_EXCEPTION();
+ if (!didDelete) {
+ return scope.engine->throwTypeError();
+ }
+ }
+
+ from = from + direction;
+ to = to + direction;
+ count = count - 1;
+ }
+
+ return instance.asReturnedValue();
+}
+
+ReturnedValue ArrayPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ ScopedObject O(scope, thisObject->toObject(scope.engine));
+ if (!O)
+ RETURN_UNDEFINED();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
+ ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
ReturnedValue ArrayPrototype::method_find(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
@@ -199,13 +540,13 @@ ReturnedValue ArrayPrototype::method_find(const FunctionObject *b, const Value *
ScopedValue result(scope);
Value *arguments = scope.alloc(3);
- ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
for (uint k = 0; k < len; ++k) {
- arguments[0] = instance->getIndexed(k);
+ arguments[0] = instance->get(k);
CHECK_EXCEPTION();
- arguments[1] = Primitive::fromDouble(k);
+ arguments[1] = Value::fromDouble(k);
arguments[2] = instance;
result = callback->call(that, arguments, 3);
@@ -233,13 +574,13 @@ ReturnedValue ArrayPrototype::method_findIndex(const FunctionObject *b, const Va
ScopedValue result(scope);
Value *arguments = scope.alloc(3);
- ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
for (uint k = 0; k < len; ++k) {
- arguments[0] = instance->getIndexed(k);
+ arguments[0] = instance->get(k);
CHECK_EXCEPTION();
- arguments[1] = Primitive::fromDouble(k);
+ arguments[1] = Value::fromDouble(k);
arguments[2] = instance;
result = callback->call(that, arguments, 3);
@@ -259,7 +600,7 @@ ReturnedValue ArrayPrototype::method_join(const FunctionObject *b, const Value *
if (!instance)
return Encode(scope.engine->newString());
- ScopedValue arg(scope, argc ? argv[0] : Primitive::undefinedValue());
+ ScopedValue arg(scope, argc ? argv[0] : Value::undefinedValue());
QString r4;
if (arg->isUndefined())
@@ -282,7 +623,7 @@ ReturnedValue ArrayPrototype::method_join(const FunctionObject *b, const Value *
if (i)
R += r4;
- e = a->getIndexed(i);
+ e = a->get(i);
CHECK_EXCEPTION();
if (!e->isNullOrUndefined())
R += e->toQString();
@@ -300,7 +641,7 @@ ReturnedValue ArrayPrototype::method_join(const FunctionObject *b, const Value *
for (quint32 k = 1; k < r2; ++k) {
R += r4;
- name = Primitive::fromDouble(k).toString(scope.engine);
+ name = Value::fromDouble(k).toString(scope.engine);
r12 = instance->get(name);
CHECK_EXCEPTION();
@@ -323,20 +664,20 @@ ReturnedValue ArrayPrototype::method_pop(const FunctionObject *b, const Value *t
if (!len) {
if (!instance->isArrayObject())
- instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromInt32(0)));
+ instance->put(scope.engine->id_length(), ScopedValue(scope, Value::fromInt32(0)));
RETURN_UNDEFINED();
}
- ScopedValue result(scope, instance->getIndexed(len - 1));
+ ScopedValue result(scope, instance->get(len - 1));
CHECK_EXCEPTION();
- if (!instance->deleteIndexedProperty(len - 1))
+ if (!instance->deleteProperty(PropertyKey::fromArrayIndex(len - 1)))
return scope.engine->throwTypeError();
if (instance->isArrayObject())
instance->setArrayLength(len - 1);
else {
- if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1))))
+ if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Value::fromDouble(len - 1))))
return scope.engine->throwTypeError();
}
return result->asReturnedValue();
@@ -352,20 +693,20 @@ ReturnedValue ArrayPrototype::method_push(const FunctionObject *b, const Value *
instance->arrayCreate();
Q_ASSERT(instance->arrayData());
- uint len = instance->getLength();
+ qint64 len = instance->getLength();
- if (len + argc < len) {
+ if (len + quint64(argc) >= UINT_MAX) {
// ughh... this goes beyond UINT_MAX
double l = len;
ScopedString s(scope);
for (int i = 0, ei = argc; i < ei; ++i) {
- s = Primitive::fromDouble(l + i).toString(scope.engine);
+ s = Value::fromDouble(l + i).toString(scope.engine);
if (!instance->put(s, argv[i]))
return scope.engine->throwTypeError();
}
double newLen = l + argc;
if (!instance->isArrayObject()) {
- if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen))))
+ if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Value::fromDouble(newLen))))
return scope.engine->throwTypeError();
} else {
ScopedString str(scope, scope.engine->newString(QStringLiteral("Array.prototype.push: Overflow")));
@@ -381,7 +722,7 @@ ReturnedValue ArrayPrototype::method_push(const FunctionObject *b, const Value *
len = instance->arrayData()->length();
} else {
for (int i = 0, ei = argc; i < ei; ++i) {
- if (!instance->putIndexed(len + i, argv[i]))
+ if (!instance->put(len + i, argv[i]))
return scope.engine->throwTypeError();
}
len += argc;
@@ -389,11 +730,11 @@ ReturnedValue ArrayPrototype::method_push(const FunctionObject *b, const Value *
if (instance->isArrayObject())
instance->setArrayLengthUnchecked(len);
else {
- if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len))))
+ if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Value::fromDouble(len))))
return scope.engine->throwTypeError();
}
- return Encode(len);
+ return Encode(uint(len));
}
ReturnedValue ArrayPrototype::method_reverse(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -403,7 +744,10 @@ ReturnedValue ArrayPrototype::method_reverse(const FunctionObject *b, const Valu
if (!instance)
RETURN_UNDEFINED();
- uint length = instance->getLength();
+ qint64 length = instance->getLength();
+ // ### FIXME
+ if (length >= UINT_MAX)
+ return scope.engine->throwRangeError(QLatin1String("Array.prototype.reverse: Length out of range."));
int lo = 0, hi = length - 1;
@@ -411,19 +755,19 @@ ReturnedValue ArrayPrototype::method_reverse(const FunctionObject *b, const Valu
ScopedValue hval(scope);
for (; lo < hi; ++lo, --hi) {
bool loExists, hiExists;
- lval = instance->getIndexed(lo, &loExists);
- hval = instance->getIndexed(hi, &hiExists);
+ lval = instance->get(lo, &loExists);
+ hval = instance->get(hi, &hiExists);
CHECK_EXCEPTION();
bool ok;
if (hiExists)
- ok = instance->putIndexed(lo, hval);
+ ok = instance->put(lo, hval);
else
- ok = instance->deleteIndexedProperty(lo);
+ ok = instance->deleteProperty(PropertyKey::fromArrayIndex(lo));
if (ok) {
if (loExists)
- ok = instance->putIndexed(hi, lval);
+ ok = instance->put(hi, lval);
else
- ok = instance->deleteIndexedProperty(hi);
+ ok = instance->deleteProperty(PropertyKey::fromArrayIndex(hi));
}
if (!ok)
return scope.engine->throwTypeError();
@@ -445,7 +789,7 @@ ReturnedValue ArrayPrototype::method_shift(const FunctionObject *b, const Value
if (!len) {
if (!instance->isArrayObject())
- if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromInt32(0))))
+ if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Value::fromInt32(0))))
return scope.engine->throwTypeError();
RETURN_UNDEFINED();
}
@@ -454,23 +798,23 @@ ReturnedValue ArrayPrototype::method_shift(const FunctionObject *b, const Value
if (!instance->protoHasArray() && !instance->arrayData()->attrs && instance->arrayData()->length() <= len && instance->arrayData()->type != Heap::ArrayData::Custom) {
result = instance->arrayData()->vtable()->pop_front(instance);
} else {
- result = instance->getIndexed(0);
+ result = instance->get(uint(0));
CHECK_EXCEPTION();
ScopedValue v(scope);
// do it the slow way
for (uint k = 1; k < len; ++k) {
bool exists;
- v = instance->getIndexed(k, &exists);
+ v = instance->get(k, &exists);
CHECK_EXCEPTION();
bool ok;
if (exists)
- ok = instance->putIndexed(k - 1, v);
+ ok = instance->put(k - 1, v);
else
- ok = instance->deleteIndexedProperty(k - 1);
+ ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k - 1));
if (!ok)
return scope.engine->throwTypeError();
}
- bool ok = instance->deleteIndexedProperty(len - 1);
+ bool ok = instance->deleteProperty(PropertyKey::fromArrayIndex(len - 1));
if (!ok)
return scope.engine->throwTypeError();
}
@@ -478,7 +822,7 @@ ReturnedValue ArrayPrototype::method_shift(const FunctionObject *b, const Value
if (instance->isArrayObject())
instance->setArrayLengthUnchecked(len - 1);
else {
- bool ok = instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1)));
+ bool ok = instance->put(scope.engine->id_length(), ScopedValue(scope, Value::fromDouble(len - 1)));
if (!ok)
return scope.engine->throwTypeError();
}
@@ -495,7 +839,7 @@ ReturnedValue ArrayPrototype::method_slice(const FunctionObject *b, const Value
ScopedArrayObject result(scope, scope.engine->newArrayObject());
uint len = o->getLength();
- double s = (argc ? argv[0] : Primitive::undefinedValue()).toInteger();
+ double s = (argc ? argv[0] : Value::undefinedValue()).toInteger();
uint start;
if (s < 0)
start = (uint)qMax(len + s, 0.);
@@ -518,7 +862,7 @@ ReturnedValue ArrayPrototype::method_slice(const FunctionObject *b, const Value
uint n = 0;
for (uint i = start; i < end; ++i) {
bool exists;
- v = o->getIndexed(i, &exists);
+ v = o->get(i, &exists);
CHECK_EXCEPTION();
if (exists)
result->arraySet(n, v);
@@ -536,7 +880,7 @@ ReturnedValue ArrayPrototype::method_sort(const FunctionObject *b, const Value *
uint len = instance->getLength();
- ScopedValue comparefn(scope, argc ? argv[0] : Primitive::undefinedValue());
+ ScopedValue comparefn(scope, argc ? argv[0] : Value::undefinedValue());
ArrayData::sort(scope.engine, instance, comparefn, len);
return thisObject->asReturnedValue();
}
@@ -548,60 +892,71 @@ ReturnedValue ArrayPrototype::method_splice(const FunctionObject *b, const Value
if (!instance)
RETURN_UNDEFINED();
- uint len = instance->getLength();
-
- ScopedArrayObject newArray(scope, scope.engine->newArrayObject());
+ qint64 len = instance->getLength();
- double rs = (argc ? argv[0] : Primitive::undefinedValue()).toInteger();
- uint start;
+ double rs = (argc ? argv[0] : Value::undefinedValue()).toInteger();
+ qint64 start;
if (rs < 0)
- start = (uint) qMax(0., len + rs);
+ start = static_cast<qint64>(qMax(0., len + rs));
else
- start = (uint) qMin(rs, (double)len);
+ start = static_cast<qint64>(qMin(rs, static_cast<double>(len)));
+
+ qint64 deleteCount = 0;
+ qint64 itemCount = 0;
+ if (argc == 1) {
+ deleteCount = len - start;
+ } else if (argc > 1){
+ itemCount = argc - 2;
+ double dc = argv[1].toInteger();
+ deleteCount = static_cast<qint64>(qMin(qMax(dc, 0.), double(len - start)));
+ }
- uint deleteCount = (uint)qMin(qMax((argc > 1 ? argv[1] : Primitive::undefinedValue()).toInteger(), 0.), (double)(len - start));
+ if (len + itemCount - deleteCount > /*(static_cast<qint64>(1) << 53) - 1*/ UINT_MAX - 1)
+ return scope.engine->throwTypeError();
+ if (deleteCount > /*(static_cast<qint64>(1) << 53) - 1*/ UINT_MAX - 1)
+ return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range."));
+ ScopedArrayObject newArray(scope, scope.engine->newArrayObject());
newArray->arrayReserve(deleteCount);
ScopedValue v(scope);
for (uint i = 0; i < deleteCount; ++i) {
bool exists;
- v = instance->getIndexed(start + i, &exists);
+ v = instance->get(start + i, &exists);
CHECK_EXCEPTION();
if (exists)
newArray->arrayPut(i, v);
}
newArray->setArrayLengthUnchecked(deleteCount);
- uint itemCount = argc < 2 ? 0 : argc - 2;
if (itemCount < deleteCount) {
for (uint k = start; k < len - deleteCount; ++k) {
bool exists;
- v = instance->getIndexed(k + deleteCount, &exists);
+ v = instance->get(k + deleteCount, &exists);
CHECK_EXCEPTION();
bool ok;
if (exists)
- ok = instance->putIndexed(k + itemCount, v);
+ ok = instance->put(k + itemCount, v);
else
- ok = instance->deleteIndexedProperty(k + itemCount);
+ ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k + itemCount));
if (!ok)
return scope.engine->throwTypeError();
}
for (uint k = len; k > len - deleteCount + itemCount; --k) {
- if (!instance->deleteIndexedProperty(k - 1))
+ if (!instance->deleteProperty(PropertyKey::fromArrayIndex(k - 1)))
return scope.engine->throwTypeError();
}
} else if (itemCount > deleteCount) {
uint k = len - deleteCount;
while (k > start) {
bool exists;
- v = instance->getIndexed(k + deleteCount - 1, &exists);
+ v = instance->get(k + deleteCount - 1, &exists);
CHECK_EXCEPTION();
bool ok;
if (exists)
- ok = instance->putIndexed(k + itemCount - 1, v);
+ ok = instance->put(k + itemCount - 1, v);
else
- ok = instance->deleteIndexedProperty(k + itemCount - 1);
+ ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k + itemCount - 1));
if (!ok)
return scope.engine->throwTypeError();
--k;
@@ -609,9 +964,9 @@ ReturnedValue ArrayPrototype::method_splice(const FunctionObject *b, const Value
}
for (uint i = 0; i < itemCount; ++i)
- instance->putIndexed(start + i, argv[i + 2]);
+ instance->put(start + i, argv[i + 2]);
- if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - deleteCount + itemCount))))
+ if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Value::fromDouble(len - deleteCount + itemCount))))
return scope.engine->throwTypeError();
return newArray->asReturnedValue();
@@ -636,17 +991,17 @@ ReturnedValue ArrayPrototype::method_unshift(const FunctionObject *b, const Valu
ScopedValue v(scope);
for (uint k = len; k > 0; --k) {
bool exists;
- v = instance->getIndexed(k - 1, &exists);
+ v = instance->get(k - 1, &exists);
bool ok;
if (exists)
- ok = instance->putIndexed(k + argc - 1, v);
+ ok = instance->put(k + argc - 1, v);
else
- ok = instance->deleteIndexedProperty(k + argc - 1);
+ ok = instance->deleteProperty(PropertyKey::fromArrayIndex(k + argc - 1));
if (!ok)
return scope.engine->throwTypeError();
}
for (int i = 0, ei = argc; i < ei; ++i) {
- bool ok = instance->putIndexed(i, argv[i]);
+ bool ok = instance->put(i, argv[i]);
if (!ok)
return scope.engine->throwTypeError();
}
@@ -656,13 +1011,51 @@ ReturnedValue ArrayPrototype::method_unshift(const FunctionObject *b, const Valu
if (instance->isArrayObject())
instance->setArrayLengthUnchecked(newLen);
else {
- if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen))))
+ if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Value::fromDouble(newLen))))
return scope.engine->throwTypeError();
}
return Encode(newLen);
}
+ReturnedValue ArrayPrototype::method_includes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
+ if (!instance)
+ RETURN_UNDEFINED();
+
+ qint64 len = instance->getLength();
+ if (len == 0) {
+ return Encode(false);
+ }
+
+ double n = 0;
+ if (argc > 1 && !argv[1].isUndefined()) {
+ n = argv[1].toInteger();
+ }
+
+ double k = 0;
+ if (n >= 0) {
+ k = n;
+ } else {
+ k = len + n;
+ if (k < 0) {
+ k = 0;
+ }
+ }
+
+ while (k < len) {
+ ScopedValue val(scope, instance->get(k));
+ if (val->sameValueZero(argv[0])) {
+ return Encode(true);
+ }
+ k++;
+ }
+
+ return Encode(false);
+}
+
ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
@@ -674,7 +1067,7 @@ ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Valu
if (!len)
return Encode(-1);
- ScopedValue searchValue(scope, argc ? argv[0] : Primitive::undefinedValue());
+ ScopedValue searchValue(scope, argc ? argv[0] : Value::undefinedValue());
uint fromIndex = 0;
if (argc >= 2) {
@@ -691,7 +1084,7 @@ ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Valu
ScopedValue v(scope);
for (uint k = fromIndex; k < len; ++k) {
bool exists;
- v = instance->getIndexed(k, &exists);
+ v = instance->get(k, &exists);
if (exists && RuntimeHelpers::strictEqual(v, searchValue))
return Encode(k);
}
@@ -705,7 +1098,7 @@ ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Valu
// lets be safe and slow
for (uint i = fromIndex; i < len; ++i) {
bool exists;
- value = instance->getIndexed(i, &exists);
+ value = instance->get(i, &exists);
CHECK_EXCEPTION();
if (exists && RuntimeHelpers::strictEqual(value, searchValue))
return Encode(i);
@@ -713,7 +1106,7 @@ ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Valu
} else if (!instance->arrayData()) {
return Encode(-1);
} else {
- Q_ASSERT(instance->arrayType() == Heap::ArrayData::Simple || instance->arrayType() == Heap::ArrayData::Complex);
+ Q_ASSERT(instance->arrayType() == Heap::ArrayData::Simple);
Heap::SimpleArrayData *sa = instance->d()->arrayData.cast<Heap::SimpleArrayData>();
if (len > sa->values.size)
len = sa->values.size;
@@ -729,6 +1122,18 @@ ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Valu
return Encode(-1);
}
+ReturnedValue ArrayPrototype::method_keys(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ Scope scope(f);
+ ScopedObject O(scope, thisObject->toObject(scope.engine));
+ if (!O)
+ RETURN_UNDEFINED();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
+ ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
+ return ao->asReturnedValue();
+}
+
ReturnedValue ArrayPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
@@ -746,7 +1151,7 @@ ReturnedValue ArrayPrototype::method_lastIndexOf(const FunctionObject *b, const
if (argc >= 1)
searchValue = argv[0];
else
- searchValue = Primitive::undefinedValue();
+ searchValue = Value::undefinedValue();
if (argc >= 2) {
double f = argv[1].toInteger();
@@ -765,7 +1170,7 @@ ReturnedValue ArrayPrototype::method_lastIndexOf(const FunctionObject *b, const
for (uint k = fromIndex; k > 0;) {
--k;
bool exists;
- v = instance->getIndexed(k, &exists);
+ v = instance->get(k, &exists);
CHECK_EXCEPTION();
if (exists && RuntimeHelpers::strictEqual(v, searchValue))
return Encode(k);
@@ -786,18 +1191,18 @@ ReturnedValue ArrayPrototype::method_every(const FunctionObject *b, const Value
THROW_TYPE_ERROR();
const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
- ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
ScopedValue r(scope);
Value *arguments = scope.alloc(3);
bool ok = true;
for (uint k = 0; ok && k < len; ++k) {
bool exists;
- arguments[0] = instance->getIndexed(k, &exists);
+ arguments[0] = instance->get(k, &exists);
if (!exists)
continue;
- arguments[1] = Primitive::fromDouble(k);
+ arguments[1] = Value::fromDouble(k);
arguments[2] = instance;
r = callback->call(that, arguments, 3);
ok = r->toBoolean();
@@ -805,6 +1210,42 @@ ReturnedValue ArrayPrototype::method_every(const FunctionObject *b, const Value
return Encode(ok);
}
+ReturnedValue ArrayPrototype::method_fill(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
+ if (!instance)
+ RETURN_UNDEFINED();
+
+ uint len = instance->getLength();
+ int relativeStart = argc > 1 ? argv[1].toInteger() : 0;
+ int relativeEnd = len;
+ if (argc > 2 && !argv[2].isUndefined()) {
+ relativeEnd = argv[2].toInteger();
+ }
+ uint k = 0;
+ uint fin = 0;
+
+ if (relativeStart < 0) {
+ k = std::max(len+relativeStart, uint(0));
+ } else {
+ k = std::min(uint(relativeStart), len);
+ }
+
+ if (relativeEnd < 0) {
+ fin = std::max(len + relativeEnd, uint(0));
+ } else {
+ fin = std::min(uint(relativeEnd), len);
+ }
+
+ while (k < fin) {
+ instance->setIndexed(k, argv[0], QV4::Object::DoThrowOnRejection);
+ k++;
+ }
+
+ return instance.asReturnedValue();
+}
+
ReturnedValue ArrayPrototype::method_some(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
@@ -818,17 +1259,17 @@ ReturnedValue ArrayPrototype::method_some(const FunctionObject *b, const Value *
THROW_TYPE_ERROR();
const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
- ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
ScopedValue result(scope);
Value *arguments = scope.alloc(3);
for (uint k = 0; k < len; ++k) {
bool exists;
- arguments[0] = instance->getIndexed(k, &exists);
+ arguments[0] = instance->get(k, &exists);
if (!exists)
continue;
- arguments[1] = Primitive::fromDouble(k);
+ arguments[1] = Value::fromDouble(k);
arguments[2] = instance;
result = callback->call(that, arguments, 3);
if (result->toBoolean())
@@ -850,16 +1291,16 @@ ReturnedValue ArrayPrototype::method_forEach(const FunctionObject *b, const Valu
THROW_TYPE_ERROR();
const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
- ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
Value *arguments = scope.alloc(3);
for (uint k = 0; k < len; ++k) {
bool exists;
- arguments[0] = instance->getIndexed(k, &exists);
+ arguments[0] = instance->get(k, &exists);
if (!exists)
continue;
- arguments[1] = Primitive::fromDouble(k);
+ arguments[1] = Value::fromDouble(k);
arguments[2] = instance;
callback->call(that, arguments, 3);
}
@@ -873,28 +1314,31 @@ ReturnedValue ArrayPrototype::method_map(const FunctionObject *b, const Value *t
if (!instance)
RETURN_UNDEFINED();
- uint len = instance->getLength();
+ qint64 len = instance->getLength();
if (!argc || !argv->isFunctionObject())
THROW_TYPE_ERROR();
const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
+ if (len > UINT_MAX - 1)
+ return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range."));
+
ScopedArrayObject a(scope, scope.engine->newArrayObject());
a->arrayReserve(len);
a->setArrayLengthUnchecked(len);
ScopedValue v(scope);
ScopedValue mapped(scope);
- ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
Value *arguments = scope.alloc(3);
for (uint k = 0; k < len; ++k) {
bool exists;
- arguments[0] = instance->getIndexed(k, &exists);
+ arguments[0] = instance->get(k, &exists);
if (!exists)
continue;
- arguments[1] = Primitive::fromDouble(k);
+ arguments[1] = Value::fromDouble(k);
arguments[2] = instance;
mapped = callback->call(that, arguments, 3);
a->arraySet(k, mapped);
@@ -919,17 +1363,17 @@ ReturnedValue ArrayPrototype::method_filter(const FunctionObject *b, const Value
a->arrayReserve(len);
ScopedValue selected(scope);
- ScopedValue that(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
Value *arguments = scope.alloc(3);
uint to = 0;
for (uint k = 0; k < len; ++k) {
bool exists;
- arguments[0] = instance->getIndexed(k, &exists);
+ arguments[0] = instance->get(k, &exists);
if (!exists)
continue;
- arguments[1] = Primitive::fromDouble(k);
+ arguments[1] = Value::fromDouble(k);
arguments[2] = instance;
selected = callback->call(that, arguments, 3);
if (selected->toBoolean()) {
@@ -962,7 +1406,7 @@ ReturnedValue ArrayPrototype::method_reduce(const FunctionObject *b, const Value
} else {
bool kPresent = false;
while (k < len && !kPresent) {
- v = instance->getIndexed(k, &kPresent);
+ v = instance->get(k, &kPresent);
if (kPresent)
acc = v;
++k;
@@ -975,11 +1419,11 @@ ReturnedValue ArrayPrototype::method_reduce(const FunctionObject *b, const Value
while (k < len) {
bool kPresent;
- v = instance->getIndexed(k, &kPresent);
+ v = instance->get(k, &kPresent);
if (kPresent) {
arguments[0] = acc;
arguments[1] = v;
- arguments[2] = Primitive::fromDouble(k);
+ arguments[2] = Value::fromDouble(k);
arguments[3] = instance;
acc = callback->call(nullptr, arguments, 4);
}
@@ -1015,7 +1459,7 @@ ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const
} else {
bool kPresent = false;
while (k > 0 && !kPresent) {
- v = instance->getIndexed(k - 1, &kPresent);
+ v = instance->get(k - 1, &kPresent);
if (kPresent)
acc = v;
--k;
@@ -1028,11 +1472,11 @@ ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const
while (k > 0) {
bool kPresent;
- v = instance->getIndexed(k - 1, &kPresent);
+ v = instance->get(k - 1, &kPresent);
if (kPresent) {
arguments[0] = acc;
arguments[1] = v;
- arguments[2] = Primitive::fromDouble(k - 1);
+ arguments[2] = Value::fromDouble(k - 1);
arguments[3] = instance;
acc = callback->call(nullptr, arguments, 4);
}
@@ -1041,3 +1485,20 @@ ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const
return acc->asReturnedValue();
}
+ReturnedValue ArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ ScopedObject O(scope, thisObject->toObject(scope.engine));
+ if (!O)
+ RETURN_UNDEFINED();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
+ ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue ArrayPrototype::method_get_species(const FunctionObject *, const Value *thisObject, const Value *, int)
+{
+ return thisObject->asReturnedValue();
+}
+
diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h
index 3825a600a2..04ec7e1607 100644
--- a/src/qml/jsruntime/qv4arrayobject_p.h
+++ b/src/qml/jsruntime/qv4arrayobject_p.h
@@ -70,8 +70,8 @@ struct ArrayCtor: FunctionObject
{
V4_OBJECT2(ArrayCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct ArrayPrototype: ArrayObject
@@ -79,9 +79,13 @@ struct ArrayPrototype: ArrayObject
void init(ExecutionEngine *engine, Object *ctor);
static ReturnedValue method_isArray(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_from(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_of(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_concat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_copyWithin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_find(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_findIndex(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_join(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
@@ -93,15 +97,23 @@ struct ArrayPrototype: ArrayObject
static ReturnedValue method_sort(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_splice(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_unshift(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_includes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_indexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_lastIndexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_every(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_fill(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_some(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_map(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_filter(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_reduce(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_reduceRight(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ // while this function is implemented here, it's the same for many other JS classes, so the corresponding JS function
+ // is instantiated in the engine, and it can be added to any JS object through Object::addSymbolSpecies()
+ static ReturnedValue method_get_species(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
diff --git a/src/qml/jsruntime/qv4atomics.cpp b/src/qml/jsruntime/qv4atomics.cpp
new file mode 100644
index 0000000000..4299aef859
--- /dev/null
+++ b/src/qml/jsruntime/qv4atomics.cpp
@@ -0,0 +1,257 @@
+/****************************************************************************
+**
+** 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 "qv4arraybuffer_p.h"
+#include "qv4typedarray_p.h"
+#include "qv4atomics_p.h"
+#include "qv4symbol_p.h"
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(Atomics);
+
+void Heap::Atomics::init()
+{
+ Object::init();
+ Scope scope(internalClass->engine);
+ ScopedObject m(scope, this);
+
+ m->defineDefaultProperty(QStringLiteral("add"), QV4::Atomics::method_add, 3);
+ m->defineDefaultProperty(QStringLiteral("and"), QV4::Atomics::method_and, 3);
+ m->defineDefaultProperty(QStringLiteral("compareExchange"), QV4::Atomics::method_compareExchange, 4);
+ m->defineDefaultProperty(QStringLiteral("exchange"), QV4::Atomics::method_exchange, 3);
+ m->defineDefaultProperty(QStringLiteral("isLockFree"), QV4::Atomics::method_isLockFree, 1);
+ m->defineDefaultProperty(QStringLiteral("load"), QV4::Atomics::method_load, 2);
+ m->defineDefaultProperty(QStringLiteral("or"), QV4::Atomics::method_or, 3);
+ m->defineDefaultProperty(QStringLiteral("store"), QV4::Atomics::method_store, 3);
+ m->defineDefaultProperty(QStringLiteral("sub"), QV4::Atomics::method_sub, 3);
+ m->defineDefaultProperty(QStringLiteral("wait"), QV4::Atomics::method_wait, 4);
+ m->defineDefaultProperty(QStringLiteral("wake"), QV4::Atomics::method_wake, 3);
+ m->defineDefaultProperty(QStringLiteral("xor"), QV4::Atomics::method_xor, 3);
+
+ ScopedString name(scope, scope.engine->newString(QStringLiteral("Atomics")));
+ m->defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name);
+}
+
+static SharedArrayBuffer *validateSharedIntegerTypedArray(Scope &scope, const Value &typedArray, bool onlyInt32 = false)
+{
+ const TypedArray *a = typedArray.as<TypedArray>();
+ if (!a) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+
+ TypedArrayType t(a->arrayType());
+ if (!a->d()->type->atomicLoad || (onlyInt32 && t != TypedArrayType::Int32Array)) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+
+ Scoped<SharedArrayBuffer> buffer(scope, a->d()->buffer);
+ if (!buffer->isSharedArrayBuffer()) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+ Q_ASSERT(!buffer->isDetachedBuffer());
+ return buffer;
+}
+
+static int validateAtomicAccess(Scope &scope, const TypedArray &typedArray, const Value &index)
+{
+ const TypedArray &a = static_cast<const TypedArray &>(typedArray);
+ qint64 idx = index.toIndex();
+ if (scope.hasException())
+ return -1;
+ if (idx < 0 || idx >= a.length()) {
+ scope.engine->throwRangeError(QStringLiteral("index out of range."));
+ return -1;
+ }
+ return static_cast<int>(idx);
+}
+
+ReturnedValue atomicReadModifyWrite(const FunctionObject *f, const Value *argv, int argc, AtomicModifyOps modify)
+{
+ Scope scope(f);
+ if (!argc)
+ return scope.engine->throwTypeError();
+
+ SharedArrayBuffer *buffer = validateSharedIntegerTypedArray(scope, argv[0]);
+ if (!buffer)
+ return Encode::undefined();
+ const TypedArray &a = static_cast<const TypedArray &>(argv[0]);
+ int index = validateAtomicAccess(scope, a, argc > 1 ? argv[1] : Value::undefinedValue());
+ if (index < 0)
+ return Encode::undefined();
+
+ Value v = Value::fromReturnedValue((argc > 2 ? argv[2] : Value::undefinedValue()).convertedToNumber());
+ if (scope.hasException())
+ return Encode::undefined();
+
+ int bytesPerElement = a.d()->type->bytesPerElement;
+ int byteOffset = a.d()->byteOffset + index * bytesPerElement;
+
+ return a.d()->type->atomicModifyOps[modify](buffer->data() + byteOffset, v);
+}
+
+ReturnedValue Atomics::method_add(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ return atomicReadModifyWrite(f, argv, argc, AtomicAdd);
+}
+
+ReturnedValue Atomics::method_and(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ return atomicReadModifyWrite(f, argv, argc, AtomicAnd);
+}
+
+ReturnedValue Atomics::method_compareExchange(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc)
+ return scope.engine->throwTypeError();
+
+ SharedArrayBuffer *buffer = validateSharedIntegerTypedArray(scope, argv[0]);
+ if (!buffer)
+ return Encode::undefined();
+ const TypedArray &a = static_cast<const TypedArray &>(argv[0]);
+ int index = validateAtomicAccess(scope, a, argc > 1 ? argv[1] : Value::undefinedValue());
+ if (index < 0)
+ return Encode::undefined();
+
+ Value expected = Value::fromReturnedValue((argc > 2 ? argv[2] : Value::undefinedValue()).convertedToNumber());
+ if (scope.hasException())
+ return Encode::undefined();
+ Value v = Value::fromReturnedValue((argc > 3 ? argv[3] : Value::undefinedValue()).convertedToNumber());
+ if (scope.hasException())
+ return Encode::undefined();
+
+ int bytesPerElement = a.d()->type->bytesPerElement;
+ int byteOffset = a.d()->byteOffset + index * bytesPerElement;
+
+ return a.d()->type->atomicCompareExchange(buffer->data() + byteOffset, expected, v);
+}
+
+ReturnedValue Atomics::method_exchange(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ return atomicReadModifyWrite(f, argv, argc, AtomicExchange);
+}
+
+ReturnedValue Atomics::method_isLockFree(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ if (!argc)
+ return Encode(false);
+ double n = argv[0].toInteger();
+ if (n == 4.)
+ return Encode(true);
+ if (n == 2.)
+ return Encode(QAtomicOps<unsigned short>::isTestAndSetNative());
+#ifdef Q_ATOMIC_INT8_IS_SUPPORTED
+ if (n == 1.)
+ return Encode(QAtomicOps<unsigned char>::isTestAndSetNative());
+#endif
+ return Encode(false);
+}
+
+ReturnedValue Atomics::method_load(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc)
+ return scope.engine->throwTypeError();
+
+ SharedArrayBuffer *buffer = validateSharedIntegerTypedArray(scope, argv[0]);
+ if (!buffer)
+ return Encode::undefined();
+ const TypedArray &a = static_cast<const TypedArray &>(argv[0]);
+ int index = validateAtomicAccess(scope, a, argc > 1 ? argv[1] : Value::undefinedValue());
+ if (index < 0)
+ return Encode::undefined();
+
+ int bytesPerElement = a.d()->type->bytesPerElement;
+ int byteOffset = a.d()->byteOffset + index * bytesPerElement;
+
+ return a.d()->type->atomicLoad(buffer->data() + byteOffset);
+}
+
+ReturnedValue Atomics::method_or(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ return atomicReadModifyWrite(f, argv, argc, AtomicOr);
+}
+
+ReturnedValue Atomics::method_store(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc)
+ return scope.engine->throwTypeError();
+
+ SharedArrayBuffer *buffer = validateSharedIntegerTypedArray(scope, argv[0]);
+ if (!buffer)
+ return Encode::undefined();
+ const TypedArray &a = static_cast<const TypedArray &>(argv[0]);
+ int index = validateAtomicAccess(scope, a, argc > 1 ? argv[1] : Value::undefinedValue());
+ if (index < 0)
+ return Encode::undefined();
+
+ Value v = Value::fromReturnedValue((argc > 2 ? argv[2] : Value::undefinedValue()).convertedToNumber());
+ if (scope.hasException())
+ return Encode::undefined();
+
+ int bytesPerElement = a.d()->type->bytesPerElement;
+ int byteOffset = a.d()->byteOffset + index * bytesPerElement;
+
+ return a.d()->type->atomicStore(buffer->data() + byteOffset, v);
+}
+
+ReturnedValue Atomics::method_sub(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ return atomicReadModifyWrite(f, argv, argc, AtomicSub);
+}
+
+ReturnedValue Atomics::method_wait(const FunctionObject *f, const Value *, const Value *, int)
+{
+ return f->engine()->throwTypeError();
+}
+
+ReturnedValue Atomics::method_wake(const FunctionObject *f, const Value *, const Value *, int)
+{
+ return f->engine()->throwTypeError();
+}
+
+ReturnedValue Atomics::method_xor(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ return atomicReadModifyWrite(f, argv, argc, AtomicXor);
+
+}
diff --git a/src/qml/jsruntime/qv4atomics_p.h b/src/qml/jsruntime/qv4atomics_p.h
new file mode 100644
index 0000000000..35b64bf4fe
--- /dev/null
+++ b/src/qml/jsruntime/qv4atomics_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** 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 QV4ATOMICS_H
+#define QV4ATOMICS_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 "qv4object_p.h"
+#include "qv4functionobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Heap {
+
+struct Atomics : Object {
+ void init();
+};
+
+}
+
+struct Atomics : Object
+{
+ V4_OBJECT2(Atomics, Object)
+
+ static ReturnedValue method_add(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_and(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_compareExchange(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_exchange(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isLockFree(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_load(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_or(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_store(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_sub(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_wait(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_wake(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_xor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp
index eb83f902db..075ee1657e 100644
--- a/src/qml/jsruntime/qv4booleanobject.cpp
+++ b/src/qml/jsruntime/qv4booleanobject.cpp
@@ -50,13 +50,13 @@ void Heap::BooleanCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("Boolean"));
}
-ReturnedValue BooleanCtor::callAsConstructor(const FunctionObject *that, const Value *argv, int argc)
+ReturnedValue BooleanCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *)
{
bool n = argc ? argv[0].toBoolean() : false;
return Encode(that->engine()->newBooleanObject(n));
}
-ReturnedValue BooleanCtor::call(const FunctionObject *, const Value *, const Value *argv, int argc)
+ReturnedValue BooleanCtor::virtualCall(const FunctionObject *, const Value *, const Value *argv, int argc)
{
bool value = argc ? argv[0].toBoolean() : 0;
return Encode(value);
@@ -66,7 +66,7 @@ void BooleanPrototype::init(ExecutionEngine *engine, Object *ctor)
{
Scope scope(engine);
ScopedObject o(scope);
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(engine->id_toString(), method_toString);
diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h
index 3cf09b2667..276ec8393b 100644
--- a/src/qml/jsruntime/qv4booleanobject_p.h
+++ b/src/qml/jsruntime/qv4booleanobject_p.h
@@ -70,8 +70,8 @@ struct BooleanCtor: FunctionObject
{
V4_OBJECT2(BooleanCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct BooleanPrototype: BooleanObject
diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp
index 020e519e74..94b1a9fb73 100644
--- a/src/qml/jsruntime/qv4context.cpp
+++ b/src/qml/jsruntime/qv4context.cpp
@@ -48,12 +48,51 @@
#include "qv4errorobject_p.h"
#include "qv4string_p.h"
#include "qv4qmlcontext_p.h"
+#include "qv4stackframe_p.h"
+#include "qv4symbol_p.h"
using namespace QV4;
DEFINE_MANAGED_VTABLE(ExecutionContext);
DEFINE_MANAGED_VTABLE(CallContext);
-DEFINE_MANAGED_VTABLE(CatchContext);
+
+Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int blockIndex)
+{
+ Function *function = frame->v4Function;
+
+ Heap::InternalClass *ic = function->compilationUnit->runtimeBlocks.at(blockIndex);
+ uint nLocals = ic->size;
+ size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals;
+
+ ExecutionEngine *v4 = function->internalClass->engine;
+ Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, ic);
+ c->init();
+ c->type = Heap::ExecutionContext::Type_BlockContext;
+
+ Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m());
+ c->outer.set(v4, outer);
+ c->function.set(v4, static_cast<Heap::FunctionObject *>(frame->jsFrame->function.m()));
+
+ c->locals.size = nLocals;
+ c->locals.alloc = nLocals;
+
+ c->setupLocalTemporalDeadZone(function->compilationUnit->unitData()->blockAt(blockIndex));
+
+ return c;
+}
+
+Heap::CallContext *ExecutionContext::cloneBlockContext(Heap::CallContext *context)
+{
+ uint nLocals = context->locals.alloc;
+ size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals;
+
+ ExecutionEngine *v4 = context->internalClass->engine;
+ Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, context->internalClass);
+ memcpy(c, context, requiredMemory);
+
+ return c;
+
+}
Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame)
{
@@ -75,8 +114,10 @@ Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame)
uint nLocals = compiledFunction->nLocals;
c->locals.size = nLocals;
c->locals.alloc = localsAndFormals;
- // memory allocated from the JS heap is 0 initialized, so check if undefined is 0
- Q_ASSERT(Primitive::undefinedValue().asReturnedValue() == 0);
+ // memory allocated from the JS heap is 0 initialized, so check if empty is 0
+ Q_ASSERT(Value::undefinedValue().asReturnedValue() == 0);
+
+ c->setupLocalTemporalDeadZone(compiledFunction);
Value *args = c->locals.values + nLocals;
::memcpy(args, frame->originalArguments, frame->originalArgumentsCount * sizeof(Value));
@@ -96,11 +137,14 @@ Heap::ExecutionContext *ExecutionContext::newWithContext(Heap::Object *with)
return c;
}
-Heap::CatchContext *ExecutionContext::newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue)
+Heap::ExecutionContext *ExecutionContext::newCatchContext(CppStackFrame *frame, int blockIndex, Heap::String *exceptionVarName)
{
- Scope scope(this);
- ScopedValue e(scope, exceptionValue);
- return engine()->memoryManager->alloc<CatchContext>(d(), exceptionVarName, e);
+ Scope scope(frame->context());
+ ScopedString name(scope, exceptionVarName);
+ ScopedValue val(scope, scope.engine->catchException(nullptr));
+ ScopedContext ctx(scope, newBlockContext(frame, blockIndex));
+ ctx->setProperty(name, val);
+ return ctx->d();
}
void ExecutionContext::createMutableBinding(String *name, bool deletable)
@@ -132,59 +176,77 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable)
activation = ctx->d()->activation;
break;
}
+ case Heap::ExecutionContext::Type_BlockContext:
+ // never create activation records on block contexts
default:
break;
}
ctx = ctx->d()->outer;
}
- if (activation->hasOwnProperty(name))
+ PropertyKey id = name->toPropertyKey();
+ if (activation->getOwnProperty(id) != Attr_Invalid)
return;
ScopedProperty desc(scope);
PropertyAttributes attrs(Attr_Data);
attrs.setConfigurable(deletable);
- activation->__defineOwnProperty__(scope.engine, name, desc, attrs);
+ if (!activation->defineOwnProperty(id, desc, attrs))
+ scope.engine->throwTypeError();
}
-void Heap::CatchContext::init(ExecutionContext *outerContext, String *exceptionVarName,
- const Value &exceptionValue)
+static bool unscopable(ExecutionEngine *engine, Heap::Object *withObject, PropertyKey id)
{
- Heap::ExecutionContext::init(Heap::ExecutionContext::Type_CatchContext);
- outer.set(internalClass->engine, outerContext);
-
- this->exceptionVarName.set(internalClass->engine, exceptionVarName);
- this->exceptionValue.set(internalClass->engine, exceptionValue);
+ if (!withObject)
+ return false;
+ Scope scope(engine);
+ ScopedObject w(scope, withObject);
+ ScopedObject o(scope, w->get(scope.engine->symbol_unscopables()));
+ if (o) {
+ ScopedValue blocked(scope, o->get(id));
+ return blocked->toBoolean();
+ }
+ return false;
}
bool ExecutionContext::deleteProperty(String *name)
{
- name->makeIdentifier();
- Identifier *id = name->identifier();
+ PropertyKey id = name->toPropertyKey();
Heap::ExecutionContext *ctx = d();
+ ExecutionEngine *engine = ctx->internalClass->engine;
+
for (; ctx; ctx = ctx->outer) {
switch (ctx->type) {
- case Heap::ExecutionContext::Type_CatchContext: {
- Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx);
- if (c->exceptionVarName->isEqualTo(name->d()))
- return false;
- break;
- }
+ case Heap::ExecutionContext::Type_BlockContext:
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
- uint index = c->internalClass->find(id);
+ uint index = c->internalClass->indexOfValueOrGetter(id);
if (index < UINT_MAX)
// ### throw in strict mode?
return false;
Q_FALLTHROUGH();
}
- case Heap::ExecutionContext::Type_WithContext:
+ case Heap::ExecutionContext::Type_WithContext: {
+ if (ctx->activation) {
+ Scope scope(this);
+ ScopedObject object(scope, ctx->activation);
+ if (object && object->hasProperty(id)) {
+ bool u = ::unscopable(engine, ctx->activation, id);
+ if (engine->hasException)
+ return false;
+ if (u)
+ break;
+ return object->deleteProperty(id);
+ }
+ }
+ break;
+ }
case Heap::ExecutionContext::Type_GlobalContext: {
if (ctx->activation) {
Scope scope(this);
ScopedObject object(scope, ctx->activation);
- if (object && object->hasProperty(name))
- return object->deleteProperty(name);
+ if (object && object->hasProperty(id))
+ return object->deleteProperty(id);
}
break;
}
@@ -194,60 +256,57 @@ bool ExecutionContext::deleteProperty(String *name)
}
}
- return !engine()->currentStackFrame->v4Function->isStrict();
+ return !engine->currentStackFrame->v4Function->isStrict();
}
ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value &value)
{
- name->makeIdentifier();
- Identifier *id = name->identifier();
+ PropertyKey id = name->toPropertyKey();
- QV4::ExecutionEngine *v4 = engine();
Heap::ExecutionContext *ctx = d();
+ QV4::ExecutionEngine *engine = ctx->internalClass->engine;
for (; ctx; ctx = ctx->outer) {
switch (ctx->type) {
- case Heap::ExecutionContext::Type_CatchContext: {
- Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx);
- if (c->exceptionVarName->isEqualTo(name->d())) {
- c->exceptionValue.set(v4, value);
- return NoError;
- }
- break;
- }
case Heap::ExecutionContext::Type_WithContext: {
- Scope scope(v4);
+ Scope scope(engine);
ScopedObject w(scope, ctx->activation);
- if (w->hasProperty(name)) {
+ if (w->hasProperty(id)) {
+ bool u = ::unscopable(engine, ctx->activation, id);
+ if (engine->hasException)
+ return TypeError;
+ if (u)
+ break;
if (!w->put(name, value))
return TypeError;
return NoError;
}
break;
}
+ case Heap::ExecutionContext::Type_BlockContext:
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
- uint index = c->internalClass->find(id);
+ uint index = c->internalClass->indexOfValueOrGetter(id);
if (index < UINT_MAX) {
- static_cast<Heap::CallContext *>(c)->locals.set(v4, index, value);
+ static_cast<Heap::CallContext *>(c)->locals.set(engine, index, value);
return NoError;
}
}
Q_FALLTHROUGH();
case Heap::ExecutionContext::Type_GlobalContext:
if (ctx->activation) {
- uint member = ctx->activation->internalClass->find(id);
- if (member < UINT_MAX) {
- Scope scope(v4);
+ auto member = ctx->activation->internalClass->findValueOrSetter(id);
+ if (member.index < UINT_MAX) {
+ Scope scope(engine);
ScopedObject a(scope, ctx->activation);
- if (!a->putValue(member, value))
+ if (!a->putValue(member.index, member.attrs, value))
return TypeError;
return NoError;
}
}
break;
case Heap::ExecutionContext::Type_QmlContext: {
- Scope scope(v4);
+ Scope scope(engine);
ScopedObject activation(scope, ctx->activation);
if (!activation->put(name, value))
return TypeError;
@@ -262,34 +321,43 @@ ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value
ReturnedValue ExecutionContext::getProperty(String *name)
{
- name->makeIdentifier();
+ PropertyKey id = name->toPropertyKey();
Heap::ExecutionContext *ctx = d();
+ QV4::ExecutionEngine *engine = ctx->internalClass->engine;
+
for (; ctx; ctx = ctx->outer) {
switch (ctx->type) {
- case Heap::ExecutionContext::Type_CatchContext: {
- Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx);
- if (c->exceptionVarName->isEqualTo(name->d()))
- return c->exceptionValue.asReturnedValue();
- break;
- }
+ case Heap::ExecutionContext::Type_BlockContext:
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
- Identifier *id = name->identifier();
- uint index = c->internalClass->find(id);
+ uint index = c->internalClass->indexOfValueOrGetter(id);
if (index < UINT_MAX)
return c->locals[index].asReturnedValue();
Q_FALLTHROUGH();
}
case Heap::ExecutionContext::Type_WithContext:
+ if (ctx->activation) {
+ Scope scope(this);
+ ScopedObject activation(scope, ctx->activation);
+ if (activation->hasProperty(id)) {
+ bool u = ::unscopable(engine, ctx->activation, id);
+ if (engine->hasException)
+ return false;
+ if (u)
+ break;
+ return activation->get(id);
+ }
+ }
+ break;
case Heap::ExecutionContext::Type_GlobalContext:
case Heap::ExecutionContext::Type_QmlContext: {
if (ctx->activation) {
Scope scope(this);
ScopedObject activation(scope, ctx->activation);
bool hasProperty = false;
- ReturnedValue v = activation->get(name, &hasProperty);
+ ReturnedValue v = activation->get(id, nullptr, &hasProperty);
if (hasProperty)
return v;
}
@@ -297,29 +365,24 @@ ReturnedValue ExecutionContext::getProperty(String *name)
}
}
}
- return engine()->throwReferenceError(*name);
+ return engine->throwReferenceError(*name);
}
ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base)
{
base->setM(nullptr);
- name->makeIdentifier();
+ PropertyKey id = name->toPropertyKey();
Heap::ExecutionContext *ctx = d();
+ QV4::ExecutionEngine *engine = ctx->internalClass->engine;
+
for (; ctx; ctx = ctx->outer) {
switch (ctx->type) {
- case Heap::ExecutionContext::Type_CatchContext: {
- Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx);
- if (c->exceptionVarName->isEqualTo(name->d()))
- return c->exceptionValue.asReturnedValue();
- break;
- }
+ case Heap::ExecutionContext::Type_BlockContext:
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
- name->makeIdentifier();
- Identifier *id = name->identifier();
- uint index = c->internalClass->find(id);
+ uint index = c->internalClass->indexOfValueOrGetter(id);
if (index < UINT_MAX)
return c->locals[index].asReturnedValue();
Q_FALLTHROUGH();
@@ -336,11 +399,25 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base)
break;
}
case Heap::ExecutionContext::Type_WithContext:
+ if (ctx->activation) {
+ Scope scope(this);
+ ScopedObject activation(scope, ctx->activation);
+ if (activation->hasProperty(id)) {
+ bool u = ::unscopable(engine, ctx->activation, id);
+ if (engine->hasException)
+ return false;
+ if (u)
+ break;
+ base->setM(activation->d());
+ return activation->get(id);
+ }
+ }
+ break;
case Heap::ExecutionContext::Type_QmlContext: {
Scope scope(this);
ScopedObject o(scope, ctx->activation);
bool hasProperty = false;
- ReturnedValue v = o->get(name, &hasProperty);
+ ReturnedValue v = o->get(id, nullptr, &hasProperty);
if (hasProperty) {
base->setM(o->d());
return v;
@@ -349,7 +426,7 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base)
}
}
}
- return engine()->throwReferenceError(*name);
+ return engine->throwReferenceError(*name);
}
void Heap::CallContext::setArg(uint index, Value v)
diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h
index 512bfa06d8..5cd2f9ddf0 100644
--- a/src/qml/jsruntime/qv4context_p.h
+++ b/src/qml/jsruntime/qv4context_p.h
@@ -55,66 +55,11 @@
QT_BEGIN_NAMESPACE
-class QObject;
-class QQmlContextData;
-
namespace QV4 {
-namespace CompiledData {
-struct CompilationUnitBase;
-struct Function;
-}
-
-struct Function;
-struct Identifier;
-struct CallContext;
-struct CatchContext;
-struct QmlContext;
-struct QQmlContextWrapper;
-
-struct CallData
-{
- enum Offsets {
- Function = 0,
- Context = 1,
- Accumulator = 2,
- This = 3,
- Argc = 4
- };
-
- Value function;
- Value context;
- Value accumulator;
- Value thisObject;
- Value _argc;
-
- int argc() const {
- Q_ASSERT(_argc.isInteger());
- return _argc.int_32();
- }
-
- void setArgc(int argc) {
- Q_ASSERT(argc >= 0);
- _argc.setInt_32(argc);
- }
-
- inline ReturnedValue argument(int i) const {
- return i < argc() ? args[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue();
- }
-
- Value args[1];
-
- static Q_DECL_CONSTEXPR int HeaderSize() { return offsetof(CallData, args) / sizeof(QV4::Value); }
-};
-
-Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value);
-Q_STATIC_ASSERT(offsetof(CallData, thisObject) == CallData::This*sizeof(Value));
-Q_STATIC_ASSERT(offsetof(CallData, args) == 5*sizeof(Value));
namespace Heap {
-struct QmlContext;
-
#define ExecutionContextMembers(class, Member) \
Member(class, Pointer, ExecutionContext *, outer) \
Member(class, Pointer, Object *, activation)
@@ -124,9 +69,9 @@ DECLARE_HEAP_OBJECT(ExecutionContext, Base) {
enum ContextType {
Type_GlobalContext = 0x1,
- Type_CatchContext = 0x2,
- Type_WithContext = 0x3,
- Type_QmlContext = 0x4,
+ Type_WithContext = 0x2,
+ Type_QmlContext = 0x3,
+ Type_BlockContext = 0x4,
Type_CallContext = 0x5
};
@@ -137,6 +82,10 @@ DECLARE_HEAP_OBJECT(ExecutionContext, Base) {
type = t;
}
+ const VTable *vtable() const {
+ return internalClass->vtable;
+ }
+
quint32 type : 8;
quint32 nArgs : 24;
#if QT_POINTER_SIZE == 8
@@ -169,6 +118,12 @@ DECLARE_HEAP_OBJECT(CallContext, ExecutionContext) {
return locals.data() + locals.size;
}
void setArg(uint index, Value v);
+
+ template <typename BlockOrFunction>
+ void setupLocalTemporalDeadZone(BlockOrFunction *bof) {
+ for (uint i = bof->nLocals - bof->sizeOfLocalTemporalDeadZone; i < bof->nLocals; ++i)
+ locals.values[i] = Value::emptyValue();
+ }
};
Q_STATIC_ASSERT(std::is_trivial< CallContext >::value);
Q_STATIC_ASSERT(std::is_standard_layout<CallContextData>::value);
@@ -181,16 +136,6 @@ Q_STATIC_ASSERT(offsetof(CallContextData, function) == 0);
//Q_STATIC_ASSERT(sizeof(CallContext) == sizeof(ExecutionContext) + sizeof(CallContextData));
//#endif
-#define CatchContextMembers(class, Member) \
- Member(class, Pointer, String *, exceptionVarName) \
- Member(class, HeapValue, HeapValue, exceptionValue)
-
-DECLARE_HEAP_OBJECT(CatchContext, ExecutionContext) {
- DECLARE_MARKOBJECTS(CatchContext);
-
- void init(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue);
-};
-Q_STATIC_ASSERT(std::is_trivial< CatchContext >::value);
}
@@ -204,9 +149,11 @@ struct Q_QML_EXPORT ExecutionContext : public Managed
Q_MANAGED_TYPE(ExecutionContext)
V4_INTERNALCLASS(ExecutionContext)
+ static Heap::CallContext *newBlockContext(QV4::CppStackFrame *frame, int blockIndex);
+ static Heap::CallContext *cloneBlockContext(Heap::CallContext *context);
static Heap::CallContext *newCallContext(QV4::CppStackFrame *frame);
Heap::ExecutionContext *newWithContext(Heap::Object *with);
- Heap::CatchContext *newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue);
+ static Heap::ExecutionContext *newCatchContext(CppStackFrame *frame, int blockIndex, Heap::String *exceptionVarName);
void createMutableBinding(String *name, bool deletable);
@@ -224,6 +171,12 @@ struct Q_QML_EXPORT ExecutionContext : public Managed
inline CallContext *asCallContext();
inline const CallContext *asCallContext() const;
+
+protected:
+ // vtable method required for compilation
+ static bool virtualDeleteProperty(Managed *, PropertyKey) {
+ Q_UNREACHABLE();
+ }
};
struct Q_QML_EXPORT CallContext : public ExecutionContext
@@ -239,11 +192,6 @@ struct Q_QML_EXPORT CallContext : public ExecutionContext
}
};
-struct CatchContext : public ExecutionContext
-{
- V4_MANAGED(CatchContext, ExecutionContext)
-};
-
inline CallContext *ExecutionContext::asCallContext()
{
return d()->type == Heap::ExecutionContext::Type_CallContext ? static_cast<CallContext *>(this) : nullptr;
diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp
index d894d909ff..5ab8cf2dcb 100644
--- a/src/qml/jsruntime/qv4dataview.cpp
+++ b/src/qml/jsruntime/qv4dataview.cpp
@@ -40,6 +40,7 @@
#include "qv4dataview_p.h"
#include "qv4arraybuffer_p.h"
#include "qv4string_p.h"
+#include "qv4symbol_p.h"
#include <QtCore/private/qnumeric_p.h>
#include "qendian.h"
@@ -54,69 +55,97 @@ void Heap::DataViewCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("DataView"));
}
-ReturnedValue DataViewCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+static uint toIndex(ExecutionEngine *e, const Value &v)
+{
+ if (v.isUndefined())
+ return 0;
+ double index = v.toInteger();
+ if (index < 0) {
+ e->throwRangeError(QStringLiteral("index out of range"));
+ return 0;
+ }
+ uint idx = static_cast<uint>(index);
+ if (idx != index) {
+ e->throwRangeError(QStringLiteral("index out of range"));
+ return 0;
+ }
+ return idx;
+}
+
+ReturnedValue DataViewCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
{
Scope scope(f->engine());
- Scoped<ArrayBuffer> buffer(scope, argc ? argv[0] : Primitive::undefinedValue());
- if (!buffer)
+ Scoped<SharedArrayBuffer> buffer(scope, argc ? argv[0] : Value::undefinedValue());
+ if (!newTarget || !buffer)
+ return scope.engine->throwTypeError();
+
+ uint offset = ::toIndex(scope.engine, argc > 1 ? argv[1]: Value::undefinedValue());
+ if (scope.hasException())
+ return Encode::undefined();
+ if (buffer->isDetachedBuffer())
return scope.engine->throwTypeError();
- double bo = argc > 1 ? argv[1].toNumber() : 0;
- uint byteOffset = (uint)bo;
uint bufferLength = buffer->d()->data->size;
- double bl = argc < 3 || argv[2].isUndefined() ? (bufferLength - bo) : argv[2].toNumber();
- uint byteLength = (uint)bl;
- if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength)
+ if (offset > bufferLength)
+ return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range"));
+
+ uint byteLength = (argc < 3 || argv[2].isUndefined()) ? (bufferLength - offset) : ::toIndex(scope.engine, argv[2]);
+ if (scope.hasException())
+ return Encode::undefined();
+ if (offset + byteLength > bufferLength)
return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range"));
- Scoped<DataView> a(scope, scope.engine->memoryManager->allocObject<DataView>());
+ Scoped<DataView> a(scope, scope.engine->memoryManager->allocate<DataView>());
a->d()->buffer.set(scope.engine, buffer->d());
a->d()->byteLength = byteLength;
- a->d()->byteOffset = byteOffset;
+ a->d()->byteOffset = offset;
return a.asReturnedValue();
}
-ReturnedValue DataViewCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
+ReturnedValue DataViewCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
{
- return callAsConstructor(f, argv, argc);
+ return f->engine()->throwTypeError();
}
void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor)
{
Scope scope(engine);
ScopedObject o(scope);
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(3));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
defineDefaultProperty(engine->id_constructor(), (o = ctor));
defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr);
defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr);
- defineDefaultProperty(QStringLiteral("getInt8"), method_getChar<signed char>, 0);
- defineDefaultProperty(QStringLiteral("getUint8"), method_getChar<unsigned char>, 0);
- defineDefaultProperty(QStringLiteral("getInt16"), method_get<short>, 0);
- defineDefaultProperty(QStringLiteral("getUint16"), method_get<unsigned short>, 0);
- defineDefaultProperty(QStringLiteral("getInt32"), method_get<int>, 0);
- defineDefaultProperty(QStringLiteral("getUint32"), method_get<unsigned int>, 0);
- defineDefaultProperty(QStringLiteral("getFloat32"), method_getFloat<float>, 0);
- defineDefaultProperty(QStringLiteral("getFloat64"), method_getFloat<double>, 0);
-
- defineDefaultProperty(QStringLiteral("setInt8"), method_setChar<signed char>, 0);
- defineDefaultProperty(QStringLiteral("setUint8"), method_setChar<unsigned char>, 0);
- defineDefaultProperty(QStringLiteral("setInt16"), method_set<short>, 0);
- defineDefaultProperty(QStringLiteral("setUint16"), method_set<unsigned short>, 0);
- defineDefaultProperty(QStringLiteral("setInt32"), method_set<int>, 0);
- defineDefaultProperty(QStringLiteral("setUint32"), method_set<unsigned int>, 0);
- defineDefaultProperty(QStringLiteral("setFloat32"), method_setFloat<float>, 0);
- defineDefaultProperty(QStringLiteral("setFloat64"), method_setFloat<double>, 0);
+ defineDefaultProperty(QStringLiteral("getInt8"), method_getChar<signed char>, 1);
+ defineDefaultProperty(QStringLiteral("getUint8"), method_getChar<unsigned char>, 1);
+ defineDefaultProperty(QStringLiteral("getInt16"), method_get<short>, 1);
+ defineDefaultProperty(QStringLiteral("getUint16"), method_get<unsigned short>, 1);
+ defineDefaultProperty(QStringLiteral("getInt32"), method_get<int>, 1);
+ defineDefaultProperty(QStringLiteral("getUint32"), method_get<unsigned int>, 1);
+ defineDefaultProperty(QStringLiteral("getFloat32"), method_getFloat<float>, 1);
+ defineDefaultProperty(QStringLiteral("getFloat64"), method_getFloat<double>, 1);
+
+ defineDefaultProperty(QStringLiteral("setInt8"), method_setChar<signed char>, 2);
+ defineDefaultProperty(QStringLiteral("setUint8"), method_setChar<unsigned char>, 2);
+ defineDefaultProperty(QStringLiteral("setInt16"), method_set<short>, 2);
+ defineDefaultProperty(QStringLiteral("setUint16"), method_set<unsigned short>, 2);
+ defineDefaultProperty(QStringLiteral("setInt32"), method_set<int>, 2);
+ defineDefaultProperty(QStringLiteral("setUint32"), method_set<unsigned int>, 2);
+ defineDefaultProperty(QStringLiteral("setFloat32"), method_setFloat<float>, 2);
+ defineDefaultProperty(QStringLiteral("setFloat64"), method_setFloat<double>, 2);
+
+ ScopedString name(scope, engine->newString(QStringLiteral("DataView")));
+ defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name);
// For backword compatibility
- defineDefaultProperty(QStringLiteral("getUInt8"), method_getChar<unsigned char>, 0);
- defineDefaultProperty(QStringLiteral("getUInt16"), method_get<unsigned short>, 0);
- defineDefaultProperty(QStringLiteral("getUInt32"), method_get<unsigned int>, 0);
- defineDefaultProperty(QStringLiteral("setUInt8"), method_setChar<unsigned char>, 0);
- defineDefaultProperty(QStringLiteral("setUInt16"), method_set<unsigned short>, 0);
- defineDefaultProperty(QStringLiteral("setUInt32"), method_set<unsigned int>, 0);
+ defineDefaultProperty(QStringLiteral("getUInt8"), method_getChar<unsigned char>, 1);
+ defineDefaultProperty(QStringLiteral("getUInt16"), method_get<unsigned short>, 1);
+ defineDefaultProperty(QStringLiteral("getUInt32"), method_get<unsigned int>, 1);
+ defineDefaultProperty(QStringLiteral("setUInt8"), method_setChar<unsigned char>, 1);
+ defineDefaultProperty(QStringLiteral("setUInt16"), method_set<unsigned short>, 1);
+ defineDefaultProperty(QStringLiteral("setUInt32"), method_set<unsigned int>, 1);
}
ReturnedValue DataViewPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -134,6 +163,9 @@ ReturnedValue DataViewPrototype::method_get_byteLength(const FunctionObject *b,
if (!v)
return b->engine()->throwTypeError();
+ if (v->d()->buffer->isDetachedBuffer())
+ return b->engine()->throwTypeError();
+
return Encode(v->d()->byteLength);
}
@@ -143,19 +175,26 @@ ReturnedValue DataViewPrototype::method_get_byteOffset(const FunctionObject *b,
if (!v)
return b->engine()->throwTypeError();
+ if (v->d()->buffer->isDetachedBuffer())
+ return b->engine()->throwTypeError();
+
return Encode(v->d()->byteOffset);
}
template <typename T>
ReturnedValue DataViewPrototype::method_getChar(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
+ ExecutionEngine *e = b->engine();
const DataView *v = thisObject->as<DataView>();
- if (!v || argc < 1)
- return b->engine()->throwTypeError();
- double l = argv[0].toNumber();
- uint idx = (uint)l;
- if (l != idx || idx + sizeof(T) > v->d()->byteLength)
- return b->engine()->throwTypeError();
+ if (!v)
+ return e->throwTypeError();
+ uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue());
+ if (e->hasException)
+ return Encode::undefined();
+ if (v->d()->buffer->isDetachedBuffer())
+ return e->throwTypeError();
+ if (idx + sizeof(T) > v->d()->byteLength)
+ return e->throwRangeError(QStringLiteral("index out of range"));
idx += v->d()->byteOffset;
T t = T(v->d()->buffer->data->data()[idx]);
@@ -166,13 +205,17 @@ ReturnedValue DataViewPrototype::method_getChar(const FunctionObject *b, const V
template <typename T>
ReturnedValue DataViewPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
+ ExecutionEngine *e = b->engine();
const DataView *v = thisObject->as<DataView>();
- if (!v || argc < 1)
- return b->engine()->throwTypeError();
- double l = argv[0].toNumber();
- uint idx = (uint)l;
- if (l != idx || idx + sizeof(T) > v->d()->byteLength)
- return b->engine()->throwTypeError();
+ if (!v)
+ return e->throwTypeError();
+ uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue());
+ if (e->hasException)
+ return Encode::undefined();
+ if (v->d()->buffer->isDetachedBuffer())
+ return e->throwTypeError();
+ if (idx + sizeof(T) > v->d()->byteLength)
+ return e->throwRangeError(QStringLiteral("index out of range"));
idx += v->d()->byteOffset;
bool littleEndian = argc < 2 ? false : argv[1].toBoolean();
@@ -187,13 +230,17 @@ ReturnedValue DataViewPrototype::method_get(const FunctionObject *b, const Value
template <typename T>
ReturnedValue DataViewPrototype::method_getFloat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
+ ExecutionEngine *e = b->engine();
const DataView *v = thisObject->as<DataView>();
- if (!v || argc < 1)
- return b->engine()->throwTypeError();
- double l = argv[0].toNumber();
- uint idx = (uint)l;
- if (l != idx || idx + sizeof(T) > v->d()->byteLength)
- return b->engine()->throwTypeError();
+ if (!v)
+ return e->throwTypeError();
+ uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue());
+ if (e->hasException)
+ return Encode::undefined();
+ if (v->d()->buffer->isDetachedBuffer())
+ return e->throwTypeError();
+ if (idx + sizeof(T) > v->d()->byteLength)
+ return e->throwRangeError(QStringLiteral("index out of range"));
idx += v->d()->byteOffset;
bool littleEndian = argc < 2 ? false : argv[1].toBoolean();
@@ -224,16 +271,23 @@ ReturnedValue DataViewPrototype::method_getFloat(const FunctionObject *b, const
template <typename T>
ReturnedValue DataViewPrototype::method_setChar(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
+ ExecutionEngine *e = b->engine();
const DataView *v = thisObject->as<DataView>();
- if (!v || argc < 1)
- return b->engine()->throwTypeError();
- double l = argv[0].toNumber();
- uint idx = (uint)l;
- if (l != idx || idx + sizeof(T) > v->d()->byteLength)
- return b->engine()->throwTypeError();
- idx += v->d()->byteOffset;
+ if (!v)
+ return e->throwTypeError();
+ uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue());
+ if (e->hasException)
+ return Encode::undefined();
int val = argc >= 2 ? argv[1].toInt32() : 0;
+
+ if (v->d()->buffer->isDetachedBuffer())
+ return e->throwTypeError();
+
+ if (idx + sizeof(T) > v->d()->byteLength)
+ return e->throwRangeError(QStringLiteral("index out of range"));
+ idx += v->d()->byteOffset;
+
v->d()->buffer->data->data()[idx] = (char)val;
RETURN_UNDEFINED();
@@ -242,19 +296,25 @@ ReturnedValue DataViewPrototype::method_setChar(const FunctionObject *b, const V
template <typename T>
ReturnedValue DataViewPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
+ ExecutionEngine *e = b->engine();
const DataView *v = thisObject->as<DataView>();
- if (!v || argc < 1)
- return b->engine()->throwTypeError();
- double l = argv[0].toNumber();
- uint idx = (uint)l;
- if (l != idx || idx + sizeof(T) > v->d()->byteLength)
- return b->engine()->throwTypeError();
- idx += v->d()->byteOffset;
+ if (!v)
+ return e->throwTypeError();
+ uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue());
+ if (e->hasException)
+ return Encode::undefined();
int val = argc >= 2 ? argv[1].toInt32() : 0;
-
bool littleEndian = argc < 3 ? false : argv[2].toBoolean();
+ if (v->d()->buffer->isDetachedBuffer())
+ return e->throwTypeError();
+
+ if (idx + sizeof(T) > v->d()->byteLength)
+ return e->throwRangeError(QStringLiteral("index out of range"));
+ idx += v->d()->byteOffset;
+
+
if (littleEndian)
qToLittleEndian<T>(val, (uchar *)v->d()->buffer->data->data() + idx);
else
@@ -266,18 +326,24 @@ ReturnedValue DataViewPrototype::method_set(const FunctionObject *b, const Value
template <typename T>
ReturnedValue DataViewPrototype::method_setFloat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
+ ExecutionEngine *e = b->engine();
const DataView *v = thisObject->as<DataView>();
- if (!v || argc < 1)
- return b->engine()->throwTypeError();
- double l = argv[0].toNumber();
- uint idx = (uint)l;
- if (l != idx || idx + sizeof(T) > v->d()->byteLength)
- return b->engine()->throwTypeError();
- idx += v->d()->byteOffset;
+ if (!v)
+ return e->throwTypeError();
+ uint idx = ::toIndex(e, argc ? argv[0] : Value::undefinedValue());
+ if (e->hasException)
+ return Encode::undefined();
double val = argc >= 2 ? argv[1].toNumber() : qt_qnan();
bool littleEndian = argc < 3 ? false : argv[2].toBoolean();
+ if (v->d()->buffer->isDetachedBuffer())
+ return e->throwTypeError();
+
+ if (idx + sizeof(T) > v->d()->byteLength)
+ return e->throwRangeError(QStringLiteral("index out of range"));
+ idx += v->d()->byteOffset;
+
if (sizeof(T) == 4) {
// float
union {
diff --git a/src/qml/jsruntime/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h
index 1e07d85118..199a6f9f80 100644
--- a/src/qml/jsruntime/qv4dataview_p.h
+++ b/src/qml/jsruntime/qv4dataview_p.h
@@ -64,7 +64,7 @@ struct DataViewCtor : FunctionObject {
};
#define DataViewMembers(class, Member) \
- Member(class, Pointer, ArrayBuffer *, buffer) \
+ Member(class, Pointer, SharedArrayBuffer *, buffer) \
Member(class, NoMark, uint, byteLength) \
Member(class, NoMark, uint, byteOffset)
@@ -79,8 +79,8 @@ struct DataViewCtor: FunctionObject
{
V4_OBJECT2(DataViewCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct DataView : Object
diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp
index 5bbe312146..df3bb37e9c 100644
--- a/src/qml/jsruntime/qv4dateobject.cpp
+++ b/src/qml/jsruntime/qv4dateobject.cpp
@@ -44,6 +44,7 @@
#include "qv4runtime_p.h"
#include "qv4string_p.h"
#include "qv4jscall_p.h"
+#include "qv4symbol_p.h"
#include <QtCore/QDebug>
#include <QtCore/QDateTime>
@@ -89,9 +90,6 @@ static const double msPerMinute = 60000.0;
static const double msPerHour = 3600000.0;
static const double msPerDay = 86400000.0;
-// The current *standard* time offset, regardless of DST:
-static double LocalTZA = 0.0; // initialized at startup
-
static inline double TimeWithinDay(double t)
{
double r = ::fmod(t, msPerDay);
@@ -224,7 +222,7 @@ static inline double MonthFromTime(double t)
static inline double DateFromTime(double t)
{
- int m = (int) Primitive::toInteger(MonthFromTime(t));
+ int m = (int) QV4::Value::toInteger(MonthFromTime(t));
double d = DayWithinYear(t);
double l = InLeapYear(t);
@@ -255,6 +253,12 @@ static inline double WeekDay(double t)
static inline double MakeTime(double hour, double min, double sec, double ms)
{
+ if (!qIsFinite(hour) || !qIsFinite(min) || !qIsFinite(sec) || !qIsFinite(ms))
+ return qQNaN();
+ hour = QV4::Value::toInteger(hour);
+ min = QV4::Value::toInteger(min);
+ sec = QV4::Value::toInteger(sec);
+ ms = QV4::Value::toInteger(ms);
return ((hour * MinutesPerHour + min) * SecondsPerMinute + sec) * msPerSecond + ms;
}
@@ -280,6 +284,12 @@ static inline double DayFromMonth(double month, double leap)
static double MakeDay(double year, double month, double day)
{
+ if (!qIsFinite(year) || !qIsFinite(month) || !qIsFinite(day))
+ return qQNaN();
+ year = QV4::Value::toInteger(year);
+ month = QV4::Value::toInteger(month);
+ day = QV4::Value::toInteger(day);
+
year += ::floor(month / 12.0);
month = ::fmod(month, 12.0);
@@ -318,14 +328,14 @@ static inline double MakeDate(double day, double time)
against the ECMAScript spec is https://github.com/tc39/ecma262/issues/725
*/
-static inline double DaylightSavingTA(double t) // t is a UTC time
+static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC time
{
return QTimeZone::systemTimeZone().offsetFromUtc(
- QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC)) * 1e3 - LocalTZA;
+ QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC)) * 1e3 - localTZA;
}
#else
// This implementation fails to take account of past changes in standard offset.
-static inline double DaylightSavingTA(double t)
+static inline double DaylightSavingTA(double t, double /*localTZA*/)
{
struct tm tmtm;
#if defined(Q_CC_MSVC)
@@ -348,19 +358,19 @@ static inline double DaylightSavingTA(double t)
}
#endif // USE_QTZ_SYSTEM_ZONE
-static inline double LocalTime(double t)
+static inline double LocalTime(double t, double localTZA)
{
// Flawed, yet verbatim from the spec:
- return t + LocalTZA + DaylightSavingTA(t);
+ return t + localTZA + DaylightSavingTA(t, localTZA);
}
// The spec does note [*] that UTC and LocalTime are not quite mutually inverse.
// [*] http://www.ecma-international.org/ecma-262/7.0/index.html#sec-utc-t
-static inline double UTC(double t)
+static inline double UTC(double t, double localTZA)
{
// Flawed, yet verbatim from the spec:
- return t - LocalTZA - DaylightSavingTA(t - LocalTZA);
+ return t - localTZA - DaylightSavingTA(t - localTZA, localTZA);
}
static inline double currentTime()
@@ -374,10 +384,10 @@ static inline double TimeClip(double t)
return qt_qnan();
// +0 looks weird, but is correct. See ES6 20.3.1.15. We must not return -0.
- return Primitive::toInteger(t) + 0;
+ return QV4::Value::toInteger(t) + 0;
}
-static inline double ParseString(const QString &s)
+static inline double ParseString(const QString &s, double localTZA)
{
/*
First, try the format defined in ECMA 262's "Date Time String Format";
@@ -533,9 +543,9 @@ static inline double ParseString(const QString &s)
if (seenZ)
t -= offset * offsetSign * 60 * 1000;
else if (seenT) // No zone specified, treat date-time as local time
- t = UTC(t);
+ t = UTC(t, localTZA);
// else: treat plain date as already in UTC
- return t;
+ return TimeClip(t);
}
QDateTime dt = QDateTime::fromString(s, Qt::TextDate);
@@ -605,7 +615,7 @@ static inline double ParseString(const QString &s)
}
if (!dt.isValid())
return qt_qnan();
- return dt.toMSecsSinceEpoch();
+ return TimeClip(dt.toMSecsSinceEpoch());
}
/*!
@@ -621,12 +631,12 @@ static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec)
return QDateTime::fromMSecsSinceEpoch(t, Qt::UTC).toTimeSpec(spec);
}
-static inline QString ToString(double t)
+static inline QString ToString(double t, double localTZA)
{
if (std::isnan(t))
return QStringLiteral("Invalid Date");
QString str = ToDateTime(t, Qt::LocalTime).toString() + QLatin1String(" GMT");
- double tzoffset = LocalTZA + DaylightSavingTA(t);
+ double tzoffset = localTZA + DaylightSavingTA(t, localTZA);
if (tzoffset) {
int hours = static_cast<int>(::fabs(tzoffset) / 1000 / 60 / 60);
int mins = int(::fabs(tzoffset) / 1000 / 60) % 60;
@@ -705,7 +715,7 @@ DEFINE_OBJECT_VTABLE(DateObject);
void Heap::DateObject::init(const QDateTime &date)
{
Object::init();
- this->date = date.isValid() ? date.toMSecsSinceEpoch() : qt_qnan();
+ this->date = date.isValid() ? TimeClip(date.toMSecsSinceEpoch()) : qt_qnan();
}
void Heap::DateObject::init(const QTime &time)
@@ -730,7 +740,7 @@ void Heap::DateObject::init(const QTime &time)
*/
static const double d = MakeDay(1925, 5, 8);
double t = MakeTime(time.hour(), time.minute(), time.second(), time.msec());
- date = TimeClip(UTC(MakeDate(d, t)));
+ date = TimeClip(UTC(MakeDate(d, t), internalClass->engine->localTZA));
}
QDateTime DateObject::toQDateTime() const
@@ -745,15 +755,16 @@ void Heap::DateCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("Date"));
}
-ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Value *argv, int argc)
+ReturnedValue DateCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *)
{
+ ExecutionEngine *e = that->engine();
double t = 0;
if (argc == 0)
t = currentTime();
else if (argc == 1) {
- Scope scope(that->engine());
+ Scope scope(e);
ScopedValue arg(scope, argv[0]);
if (DateObject *d = arg->as<DateObject>()) {
t = d->date();
@@ -761,7 +772,7 @@ ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Valu
arg = RuntimeHelpers::toPrimitive(arg, PREFERREDTYPE_HINT);
if (String *s = arg->stringValue())
- t = ParseString(s->toQString());
+ t = ParseString(s->toQString(), e->localTZA);
else
t = TimeClip(arg->toNumber());
}
@@ -778,16 +789,17 @@ ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Valu
if (year >= 0 && year <= 99)
year += 1900;
t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms));
- t = TimeClip(UTC(t));
+ t = TimeClip(UTC(t, e->localTZA));
}
- return Encode(that->engine()->newDateObject(Primitive::fromDouble(t)));
+ return Encode(e->newDateObject(Value::fromDouble(t)));
}
-ReturnedValue DateCtor::call(const FunctionObject *m, const Value *, const Value *, int)
+ReturnedValue DateCtor::virtualCall(const FunctionObject *m, const Value *, const Value *, int)
{
+ ExecutionEngine *e = m->engine();
double t = currentTime();
- return m->engine()->newString(ToString(t))->asReturnedValue();
+ return e->newString(ToString(t, e->localTZA))->asReturnedValue();
}
void DatePrototype::init(ExecutionEngine *engine, Object *ctor)
@@ -795,8 +807,8 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor)
Scope scope(engine);
ScopedObject o(scope);
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
- ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(7));
- LocalTZA = getLocalTZA();
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(7));
+ engine->localTZA = getLocalTZA();
ctor->defineDefaultProperty(QStringLiteral("parse"), method_parse, 1);
ctor->defineDefaultProperty(QStringLiteral("UTC"), method_UTC, 7);
@@ -806,7 +818,7 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(engine->id_toString(), method_toString, 0);
defineDefaultProperty(QStringLiteral("toDateString"), method_toDateString, 0);
defineDefaultProperty(QStringLiteral("toTimeString"), method_toTimeString, 0);
- defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString, 0);
+ defineDefaultProperty(engine->id_toLocaleString(), method_toLocaleString, 0);
defineDefaultProperty(QStringLiteral("toLocaleDateString"), method_toLocaleDateString, 0);
defineDefaultProperty(QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString, 0);
defineDefaultProperty(engine->id_valueOf(), method_valueOf, 0);
@@ -853,15 +865,14 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor)
QString toGmtString(QStringLiteral("toGMTString"));
ScopedString us(scope, engine->newIdentifier(toUtcString));
ScopedString gs(scope, engine->newIdentifier(toGmtString));
- ExecutionContext *global = engine->rootContext();
- ScopedFunctionObject toUtcGmtStringFn(scope, FunctionObject::createBuiltinFunction(global, us, method_toUTCString));
- toUtcGmtStringFn->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0));
+ ScopedFunctionObject toUtcGmtStringFn(scope, FunctionObject::createBuiltinFunction(engine, us, method_toUTCString, 0));
defineDefaultProperty(us, toUtcGmtStringFn);
defineDefaultProperty(gs, toUtcGmtStringFn);
}
defineDefaultProperty(QStringLiteral("toISOString"), method_toISOString, 0);
defineDefaultProperty(QStringLiteral("toJSON"), method_toJSON, 1);
+ defineDefaultProperty(engine->symbol_toPrimitive(), method_symbolToPrimitive, 1, Attr_ReadOnly_ButConfigurable);
}
double DatePrototype::getThisDate(ExecutionEngine *v4, const Value *thisObject)
@@ -872,32 +883,47 @@ double DatePrototype::getThisDate(ExecutionEngine *v4, const Value *thisObject)
return 0;
}
-ReturnedValue DatePrototype::method_parse(const FunctionObject *, const Value *, const Value *argv, int argc)
+ReturnedValue DatePrototype::method_parse(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
if (!argc)
return Encode(qt_qnan());
else
- return Encode(ParseString(argv[0].toQString()));
+ return Encode(ParseString(argv[0].toQString(), f->engine()->localTZA));
}
-ReturnedValue DatePrototype::method_UTC(const FunctionObject *, const Value *, const Value *argv, int argc)
+ReturnedValue DatePrototype::method_UTC(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
const int numArgs = argc;
- if (numArgs >= 2) {
- double year = argv[0].toNumber();
- double month = argv[1].toNumber();
- double day = numArgs >= 3 ? argv[2].toNumber() : 1;
- double hours = numArgs >= 4 ? argv[3].toNumber() : 0;
- double mins = numArgs >= 5 ? argv[4].toNumber() : 0;
- double secs = numArgs >= 6 ? argv[5].toNumber() : 0;
- double ms = numArgs >= 7 ? argv[6].toNumber() : 0;
- if (year >= 0 && year <= 99)
- year += 1900;
- double t = MakeDate(MakeDay(year, month, day),
- MakeTime(hours, mins, secs, ms));
- return Encode(TimeClip(t));
- }
- RETURN_UNDEFINED();
+ if (numArgs < 1)
+ return Encode(qQNaN());
+ ExecutionEngine *e = f->engine();
+ double year = argv[0].toNumber();
+ if (e->hasException)
+ return Encode::undefined();
+ double month = numArgs >= 2 ? argv[1].toNumber() : 0;
+ if (e->hasException)
+ return Encode::undefined();
+ double day = numArgs >= 3 ? argv[2].toNumber() : 1;
+ if (e->hasException)
+ return Encode::undefined();
+ double hours = numArgs >= 4 ? argv[3].toNumber() : 0;
+ if (e->hasException)
+ return Encode::undefined();
+ double mins = numArgs >= 5 ? argv[4].toNumber() : 0;
+ if (e->hasException)
+ return Encode::undefined();
+ double secs = numArgs >= 6 ? argv[5].toNumber() : 0;
+ if (e->hasException)
+ return Encode::undefined();
+ double ms = numArgs >= 7 ? argv[6].toNumber() : 0;
+ if (e->hasException)
+ return Encode::undefined();
+ double iyear = QV4::Value::toInteger(year);
+ if (!qIsNaN(year) && iyear >= 0 && iyear <= 99)
+ year = 1900 + iyear;
+ double t = MakeDate(MakeDay(year, month, day),
+ MakeTime(hours, mins, secs, ms));
+ return Encode(TimeClip(t));
}
ReturnedValue DatePrototype::method_now(const FunctionObject *, const Value *, const Value *, int)
@@ -909,7 +935,7 @@ ReturnedValue DatePrototype::method_toString(const FunctionObject *b, const Valu
{
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
- return Encode(v4->newString(ToString(t)));
+ return Encode(v4->newString(ToString(t, v4->localTZA)));
}
ReturnedValue DatePrototype::method_toDateString(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -966,7 +992,7 @@ ReturnedValue DatePrototype::method_getYear(const FunctionObject *b, const Value
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = YearFromTime(LocalTime(t)) - 1900;
+ t = YearFromTime(LocalTime(t, v4->localTZA)) - 1900;
return Encode(t);
}
@@ -975,7 +1001,7 @@ ReturnedValue DatePrototype::method_getFullYear(const FunctionObject *b, const V
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = YearFromTime(LocalTime(t));
+ t = YearFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -993,7 +1019,7 @@ ReturnedValue DatePrototype::method_getMonth(const FunctionObject *b, const Valu
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = MonthFromTime(LocalTime(t));
+ t = MonthFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1011,7 +1037,7 @@ ReturnedValue DatePrototype::method_getDate(const FunctionObject *b, const Value
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = DateFromTime(LocalTime(t));
+ t = DateFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1029,7 +1055,7 @@ ReturnedValue DatePrototype::method_getDay(const FunctionObject *b, const Value
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = WeekDay(LocalTime(t));
+ t = WeekDay(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1047,7 +1073,7 @@ ReturnedValue DatePrototype::method_getHours(const FunctionObject *b, const Valu
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = HourFromTime(LocalTime(t));
+ t = HourFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1065,7 +1091,7 @@ ReturnedValue DatePrototype::method_getMinutes(const FunctionObject *b, const Va
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = MinFromTime(LocalTime(t));
+ t = MinFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1083,7 +1109,7 @@ ReturnedValue DatePrototype::method_getSeconds(const FunctionObject *b, const Va
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = SecFromTime(LocalTime(t));
+ t = SecFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1101,7 +1127,7 @@ ReturnedValue DatePrototype::method_getMilliseconds(const FunctionObject *b, con
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = msFromTime(LocalTime(t));
+ t = msFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1119,7 +1145,7 @@ ReturnedValue DatePrototype::method_getTimezoneOffset(const FunctionObject *b, c
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = (t - LocalTime(t)) / msPerMinute;
+ t = (t - LocalTime(t, v4->localTZA)) / msPerMinute;
return Encode(t);
}
@@ -1144,13 +1170,13 @@ ReturnedValue DatePrototype::method_setMilliseconds(const FunctionObject *b, con
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
double ms = argc ? argv[0].toNumber() : qt_qnan();
if (v4->hasException)
return QV4::Encode::undefined();
- self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))));
+ self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)), v4->localTZA)));
return Encode(self->date());
}
@@ -1178,7 +1204,7 @@ ReturnedValue DatePrototype::method_setSeconds(const FunctionObject *b, const Va
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
double sec = argc ? argv[0].toNumber() : qt_qnan();
@@ -1187,7 +1213,7 @@ ReturnedValue DatePrototype::method_setSeconds(const FunctionObject *b, const Va
double ms = (argc < 2) ? msFromTime(t) : argv[1].toNumber();
if (v4->hasException)
return QV4::Encode::undefined();
- t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms))));
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)), v4->localTZA));
self->setDate(t);
return Encode(self->date());
}
@@ -1214,7 +1240,7 @@ ReturnedValue DatePrototype::method_setMinutes(const FunctionObject *b, const Va
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
double min = argc ? argv[0].toNumber() : qt_qnan();
@@ -1226,7 +1252,7 @@ ReturnedValue DatePrototype::method_setMinutes(const FunctionObject *b, const Va
double ms = (argc < 3) ? msFromTime(t) : argv[2].toNumber();
if (v4->hasException)
return QV4::Encode::undefined();
- t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms))));
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)), v4->localTZA));
self->setDate(t);
return Encode(self->date());
}
@@ -1254,7 +1280,7 @@ ReturnedValue DatePrototype::method_setHours(const FunctionObject *b, const Valu
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
double hour = argc ? argv[0].toNumber() : qt_qnan();
@@ -1269,7 +1295,7 @@ ReturnedValue DatePrototype::method_setHours(const FunctionObject *b, const Valu
double ms = (argc < 4) ? msFromTime(t) : argv[3].toNumber();
if (v4->hasException)
return QV4::Encode::undefined();
- t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms))));
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)), v4->localTZA));
self->setDate(t);
return Encode(self->date());
}
@@ -1298,13 +1324,13 @@ ReturnedValue DatePrototype::method_setDate(const FunctionObject *b, const Value
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
double date = argc ? argv[0].toNumber() : qt_qnan();
if (v4->hasException)
return QV4::Encode::undefined();
- t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t))));
+ t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)), v4->localTZA));
self->setDate(t);
return Encode(self->date());
}
@@ -1334,7 +1360,7 @@ ReturnedValue DatePrototype::method_setMonth(const FunctionObject *b, const Valu
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
double month = argc ? argv[0].toNumber() : qt_qnan();
@@ -1343,7 +1369,7 @@ ReturnedValue DatePrototype::method_setMonth(const FunctionObject *b, const Valu
double date = (argc < 2) ? DateFromTime(t) : argv[1].toNumber();
if (v4->hasException)
return QV4::Encode::undefined();
- t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t))));
+ t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)), v4->localTZA));
self->setDate(t);
return Encode(self->date());
}
@@ -1374,16 +1400,16 @@ ReturnedValue DatePrototype::method_setYear(const FunctionObject *b, const Value
if (std::isnan(t))
t = 0;
else
- t = LocalTime(t);
+ t = LocalTime(t, v4->localTZA);
double year = argc ? argv[0].toNumber() : qt_qnan();
double r;
if (std::isnan(year)) {
r = qt_qnan();
} else {
- if ((Primitive::toInteger(year) >= 0) && (Primitive::toInteger(year) <= 99))
+ if ((QV4::Value::toInteger(year) >= 0) && (QV4::Value::toInteger(year) <= 99))
year += 1900;
r = MakeDay(year, MonthFromTime(t), DateFromTime(t));
- r = UTC(MakeDate(r, TimeWithinDay(t)));
+ r = UTC(MakeDate(r, TimeWithinDay(t)), v4->localTZA);
r = TimeClip(r);
}
self->setDate(r);
@@ -1413,7 +1439,7 @@ ReturnedValue DatePrototype::method_setFullYear(const FunctionObject *b, const V
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
if (std::isnan(t))
@@ -1427,7 +1453,7 @@ ReturnedValue DatePrototype::method_setFullYear(const FunctionObject *b, const V
double date = (argc < 3) ? DateFromTime(t) : argv[2].toNumber();
if (v4->hasException)
return QV4::Encode::undefined();
- t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t))));
+ t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)), v4->localTZA));
self->setDate(t);
return Encode(self->date());
}
@@ -1471,7 +1497,7 @@ ReturnedValue DatePrototype::method_toISOString(const FunctionObject *b, const V
int year = (int)YearFromTime(t);
if (year < 0 || year > 9999) {
if (qAbs(year) >= 1000000)
- RETURN_RESULT(v4->newString(QStringLiteral("Invalid Date")));
+ RETURN_RESULT(v4->throwRangeError(*thisObject));
result += year < 0 ? QLatin1Char('-') : QLatin1Char('+');
year = qAbs(year);
addZeroPrefixedInt(result, year, 6);
@@ -1518,7 +1544,23 @@ ReturnedValue DatePrototype::method_toJSON(const FunctionObject *b, const Value
return toIso->call(O, nullptr, 0);
}
-void DatePrototype::timezoneUpdated()
+ReturnedValue DatePrototype::method_symbolToPrimitive(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *e = f->engine();
+ if (!thisObject->isObject() || !argc || !argv->isString())
+ return e->throwTypeError();
+
+ String *hint = argv->stringValue();
+ PropertyKey id = hint->toPropertyKey();
+ if (id == e->id_default()->propertyKey())
+ hint = e->id_string();
+ else if (id != e->id_string()->propertyKey() && id != e->id_number()->propertyKey())
+ return e->throwTypeError();
+
+ return RuntimeHelpers::ordinaryToPrimitive(e, static_cast<const Object *>(thisObject), hint);
+}
+
+void DatePrototype::timezoneUpdated(ExecutionEngine *e)
{
- LocalTZA = getLocalTZA();
+ e->localTZA = getLocalTZA();
}
diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h
index 2b9a580288..5b9934282c 100644
--- a/src/qml/jsruntime/qv4dateobject_p.h
+++ b/src/qml/jsruntime/qv4dateobject_p.h
@@ -101,15 +101,15 @@ struct DateObject: Object {
template<>
inline const DateObject *Value::as() const {
- return isManaged() && m()->vtable()->type == Managed::Type_DateObject ? static_cast<const DateObject *>(this) : nullptr;
+ return isManaged() && m()->internalClass->vtable->type == Managed::Type_DateObject ? static_cast<const DateObject *>(this) : nullptr;
}
struct DateCtor: FunctionObject
{
V4_OBJECT2(DateCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int);
};
struct DatePrototype: Object
@@ -169,8 +169,9 @@ struct DatePrototype: Object
static ReturnedValue method_toUTCString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toISOString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toJSON(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_symbolToPrimitive(const FunctionObject *f, const Value *thisObject, const Value *, int);
- static void timezoneUpdated();
+ static void timezoneUpdated(ExecutionEngine *e);
};
}
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 0ed0df89a9..8f64c84eae 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -37,11 +37,30 @@
**
****************************************************************************/
#include <qv4engine_p.h>
+
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsparser_p.h>
+#include <private/qqmljsast_p.h>
+#include <private/qv4compileddata_p.h>
+#include <private/qv4compiler_p.h>
+#include <private/qv4compilercontext_p.h>
+#include <private/qv4codegen_p.h>
+
+#include <QtCore/QTextStream>
+#include <QDateTime>
+#include <QDir>
+#include <QFileInfo>
+
+#ifndef V4_BOOTSTRAP
+
#include <qv4qmlcontext_p.h>
#include <qv4value_p.h>
#include <qv4object_p.h>
#include <qv4objectproto_p.h>
#include <qv4objectiterator_p.h>
+#include <qv4setiterator_p.h>
+#include <qv4mapiterator_p.h>
+#include <qv4arrayiterator_p.h>
#include <qv4arrayobject_p.h>
#include <qv4booleanobject_p.h>
#include <qv4globalobject_p.h>
@@ -52,6 +71,9 @@
#include <qv4numberobject_p.h>
#include <qv4regexpobject_p.h>
#include <qv4regexp_p.h>
+#include "qv4symbol_p.h"
+#include "qv4setobject_p.h"
+#include "qv4mapobject_p.h"
#include <qv4variantobject_p.h>
#include <qv4runtime_p.h>
#include <private/qv4mm_p.h>
@@ -63,7 +85,18 @@
#include "qv4debugging_p.h"
#include "qv4profiling_p.h"
#include "qv4executableallocator_p.h"
+#include "qv4iterator_p.h"
+#include "qv4stringiterator_p.h"
+#include "qv4generatorobject_p.h"
+#include "qv4reflect_p.h"
+#include "qv4proxy_p.h"
+#include "qv4stackframe_p.h"
+#include "qv4atomics_p.h"
+
+#if QT_CONFIG(qml_sequence_object)
#include "qv4sequenceobject_p.h"
+#endif
+
#include "qv4qobjectwrapper_p.h"
#include "qv4memberdata_p.h"
#include "qv4arraybuffer_p.h"
@@ -76,10 +109,11 @@
#include <private/qqmlvaluetype_p.h>
#include <private/qqmllistwrapper_p.h>
#include <private/qqmllist_p.h>
+#include <private/qqmltypeloader_p.h>
+#if QT_CONFIG(qml_locale)
#include <private/qqmllocale_p.h>
-
-#include <QtCore/QTextStream>
-#include <QDateTime>
+#endif
+#include <qqmlfile.h>
#if USE(PTHREADS)
# include <pthread.h>
@@ -95,10 +129,14 @@
#include <valgrind/memcheck.h>
#endif
+#endif // #ifndef V4_BOOTSTRAP
+
QT_BEGIN_NAMESPACE
using namespace QV4;
+#ifndef V4_BOOTSTRAP
+
static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1);
ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
@@ -106,22 +144,9 @@ ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const
return b->engine()->throwTypeError();
}
-
-#ifdef V4_BOOTSTRAP
-QJSEngine *ExecutionEngine::jsEngine() const
-{
- return v8Engine->publicEngine();
-}
-
-QQmlEngine *ExecutionEngine::qmlEngine() const
-{
- return v8Engine->engine();
-}
-#endif // V4_BOOTSTRAP
-
qint32 ExecutionEngine::maxCallDepth = -1;
-ExecutionEngine::ExecutionEngine()
+ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
: executableAllocator(new QV4::ExecutableAllocator)
, regExpAllocator(new QV4::ExecutableAllocator)
, bumperPointerAllocator(new WTF::BumpPointerAllocator)
@@ -129,8 +154,7 @@ ExecutionEngine::ExecutionEngine()
, gcStack(new WTF::PageAllocation)
, globalCode(nullptr)
, v8Engine(nullptr)
- , argumentsAccessors(nullptr)
- , nArgumentsAccessors(0)
+ , publicEngine(jsEngine)
, m_engineId(engineSerial.fetchAndAddOrdered(1))
, regExpCache(nullptr)
, m_multiplyWrappedQObjects(nullptr)
@@ -181,27 +205,44 @@ ExecutionEngine::ExecutionEngine()
}
exceptionValue = jsAlloca(1);
+ *exceptionValue = Encode::undefined();
globalObject = static_cast<Object *>(jsAlloca(1));
jsObjects = jsAlloca(NJSObjects);
typedArrayPrototype = static_cast<Object *>(jsAlloca(NTypedArrayTypes));
typedArrayCtors = static_cast<FunctionObject *>(jsAlloca(NTypedArrayTypes));
jsStrings = jsAlloca(NJSStrings);
+ jsSymbols = jsAlloca(NJSSymbols);
// set up stack limits
jsStackLimit = jsStackBase + JSStackLimit/sizeof(Value);
identifierTable = new IdentifierTable(this);
- classPool = new InternalClassPool;
+ memset(classes, 0, sizeof(classes));
+ classes[Class_Empty] = memoryManager->allocIC<InternalClass>();
+ classes[Class_Empty]->init(this);
+
+ classes[Class_MemberData] = classes[Class_Empty]->changeVTable(QV4::MemberData::staticVTable());
+ classes[Class_SimpleArrayData] = classes[Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable());
+ classes[Class_SparseArrayData] = classes[Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable());
+ classes[Class_ExecutionContext] = classes[Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable());
+ classes[Class_CallContext] = classes[Class_Empty]->changeVTable(QV4::CallContext::staticVTable());
+ classes[Class_QmlContext] = classes[Class_Empty]->changeVTable(QV4::QmlContext::staticVTable());
+
+ Scope scope(this);
+ Scoped<InternalClass> ic(scope);
+ ic = classes[Class_Empty]->changeVTable(QV4::Object::staticVTable());
+ jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(ic->d());
+ classes[Class_Object] = ic->changePrototype(objectPrototype()->d());
+ classes[Class_QmlContextWrapper] = classes[Class_Object]->changeVTable(QV4::QQmlContextWrapper::staticVTable());
+
+ ic = newInternalClass(QV4::StringObject::staticVTable(), objectPrototype());
+ jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(ic->d(), /*init =*/ false);
+ classes[Class_String] = classes[Class_Empty]->changeVTable(QV4::String::staticVTable())->changePrototype(stringPrototype()->d());
+ Q_ASSERT(stringPrototype()->d() && classes[Class_String]->prototype);
- internalClasses[Class_Empty] = new (classPool) InternalClass(this);
- internalClasses[Class_String] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::String::staticVTable());
- internalClasses[Class_MemberData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::MemberData::staticVTable());
- internalClasses[Class_SimpleArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable());
- internalClasses[Class_SparseArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable());
- internalClasses[Class_ExecutionContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable());
- internalClasses[Class_QmlContext] = internalClasses[EngineBase::Class_ExecutionContext]->changeVTable(QV4::QmlContext::staticVTable());
- internalClasses[Class_CallContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::CallContext::staticVTable());
+ jsObjects[SymbolProto] = memoryManager->allocate<SymbolPrototype>();
+ classes[Class_Symbol] = classes[EngineBase::Class_Empty]->changeVTable(QV4::Symbol::staticVTable())->changePrototype(symbolPrototype()->d());
jsStrings[String_Empty] = newIdentifier(QString());
jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined"));
@@ -211,6 +252,8 @@ ExecutionEngine::ExecutionEngine()
jsStrings[String_boolean] = newIdentifier(QStringLiteral("boolean"));
jsStrings[String_number] = newIdentifier(QStringLiteral("number"));
jsStrings[String_string] = newIdentifier(QStringLiteral("string"));
+ jsStrings[String_default] = newIdentifier(QStringLiteral("default"));
+ jsStrings[String_symbol] = newIdentifier(QStringLiteral("symbol"));
jsStrings[String_object] = newIdentifier(QStringLiteral("object"));
jsStrings[String_function] = newIdentifier(QStringLiteral("function"));
jsStrings[String_length] = newIdentifier(QStringLiteral("length"));
@@ -233,147 +276,212 @@ ExecutionEngine::ExecutionEngine()
jsStrings[String_index] = newIdentifier(QStringLiteral("index"));
jsStrings[String_input] = newIdentifier(QStringLiteral("input"));
jsStrings[String_toString] = newIdentifier(QStringLiteral("toString"));
+ jsStrings[String_toLocaleString] = newIdentifier(QStringLiteral("toLocaleString"));
jsStrings[String_destroy] = newIdentifier(QStringLiteral("destroy"));
jsStrings[String_valueOf] = newIdentifier(QStringLiteral("valueOf"));
jsStrings[String_byteLength] = newIdentifier(QStringLiteral("byteLength"));
jsStrings[String_byteOffset] = newIdentifier(QStringLiteral("byteOffset"));
jsStrings[String_buffer] = newIdentifier(QStringLiteral("buffer"));
jsStrings[String_lastIndex] = newIdentifier(QStringLiteral("lastIndex"));
-
- InternalClass *ic = internalClasses[Class_Empty]->changeVTable(QV4::Object::staticVTable());
- jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(ic);
- internalClasses[Class_Object] = ic->changePrototype(objectPrototype()->d());
- internalClasses[EngineBase::Class_QmlContextWrapper] = internalClasses[Class_Object]->changeVTable(QV4::QQmlContextWrapper::staticVTable());
+ jsStrings[String_next] = newIdentifier(QStringLiteral("next"));
+ jsStrings[String_done] = newIdentifier(QStringLiteral("done"));
+ jsStrings[String_return] = newIdentifier(QStringLiteral("return"));
+ jsStrings[String_throw] = newIdentifier(QStringLiteral("throw"));
+ jsStrings[String_global] = newIdentifier(QStringLiteral("global"));
+ jsStrings[String_ignoreCase] = newIdentifier(QStringLiteral("ignoreCase"));
+ jsStrings[String_multiline] = newIdentifier(QStringLiteral("multiline"));
+ jsStrings[String_unicode] = newIdentifier(QStringLiteral("unicode"));
+ jsStrings[String_sticky] = newIdentifier(QStringLiteral("sticky"));
+ jsStrings[String_source] = newIdentifier(QStringLiteral("source"));
+ jsStrings[String_flags] = newIdentifier(QStringLiteral("flags"));
+
+ jsSymbols[Symbol_hasInstance] = Symbol::create(this, QStringLiteral("@Symbol.hasInstance"));
+ jsSymbols[Symbol_isConcatSpreadable] = Symbol::create(this, QStringLiteral("@Symbol.isConcatSpreadable"));
+ jsSymbols[Symbol_iterator] = Symbol::create(this, QStringLiteral("@Symbol.iterator"));
+ jsSymbols[Symbol_match] = Symbol::create(this, QStringLiteral("@Symbol.match"));
+ jsSymbols[Symbol_replace] = Symbol::create(this, QStringLiteral("@Symbol.replace"));
+ jsSymbols[Symbol_search] = Symbol::create(this, QStringLiteral("@Symbol.search"));
+ jsSymbols[Symbol_species] = Symbol::create(this, QStringLiteral("@Symbol.species"));
+ jsSymbols[Symbol_split] = Symbol::create(this, QStringLiteral("@Symbol.split"));
+ jsSymbols[Symbol_toPrimitive] = Symbol::create(this, QStringLiteral("@Symbol.toPrimitive"));
+ jsSymbols[Symbol_toStringTag] = Symbol::create(this, QStringLiteral("@Symbol.toStringTag"));
+ jsSymbols[Symbol_unscopables] = Symbol::create(this, QStringLiteral("@Symbol.unscopables"));
+ jsSymbols[Symbol_revokableProxy] = Symbol::create(this, QStringLiteral("@Proxy.revokableProxy"));
ic = newInternalClass(ArrayPrototype::staticVTable(), objectPrototype());
- Q_ASSERT(ic->prototype);
- ic = ic->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable);
- Q_ASSERT(ic->prototype);
- jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(ic, objectPrototype());
- internalClasses[Class_ArrayObject] = ic->changePrototype(arrayPrototype()->d());
- jsObjects[PropertyListProto] = memoryManager->allocObject<PropertyListPrototype>();
-
- InternalClass *argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype());
- argsClass = argsClass->addMember(id_length(), Attr_NotEnumerable);
- internalClasses[EngineBase::Class_ArgumentsObject] = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable);
+ Q_ASSERT(ic->d()->prototype);
+ ic = ic->addMember(id_length()->propertyKey(), Attr_NotConfigurable|Attr_NotEnumerable);
+ Q_ASSERT(ic->d()->prototype);
+ jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(ic->d());
+ classes[Class_ArrayObject] = ic->changePrototype(arrayPrototype()->d());
+ jsObjects[PropertyListProto] = memoryManager->allocate<PropertyListPrototype>();
+
+ Scoped<InternalClass> argsClass(scope);
+ argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype());
+ argsClass = argsClass->addMember(id_length()->propertyKey(), Attr_NotEnumerable);
+ argsClass = argsClass->addMember(symbol_iterator()->propertyKey(), Attr_Data|Attr_NotEnumerable);
+ classes[Class_ArgumentsObject] = argsClass->addMember(id_callee()->propertyKey(), Attr_Data|Attr_NotEnumerable);
argsClass = newInternalClass(StrictArgumentsObject::staticVTable(), objectPrototype());
- argsClass = argsClass->addMember(id_length(), Attr_NotEnumerable);
- argsClass = argsClass->addMember(id_callee(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
- internalClasses[EngineBase::Class_StrictArgumentsObject] = argsClass->addMember(id_caller(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
+ argsClass = argsClass->addMember(id_length()->propertyKey(), Attr_NotEnumerable);
+ argsClass = argsClass->addMember(symbol_iterator()->propertyKey(), Attr_Data|Attr_NotEnumerable);
+ classes[Class_StrictArgumentsObject] = argsClass->addMember(id_callee()->propertyKey(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
*static_cast<Value *>(globalObject) = newObject();
Q_ASSERT(globalObject->d()->vtable());
initRootContext();
ic = newInternalClass(QV4::StringObject::staticVTable(), objectPrototype());
- ic = ic->addMember(id_length(), Attr_ReadOnly);
- jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(ic);
- internalClasses[Class_StringObject] = ic->changePrototype(stringPrototype()->d());
- Q_ASSERT(internalClasses[EngineBase::Class_StringObject]->find(id_length()) == Heap::StringObject::LengthPropertyIndex);
+ ic = ic->addMember(id_length()->propertyKey(), Attr_ReadOnly);
+ classes[Class_StringObject] = ic->changePrototype(stringPrototype()->d());
+ Q_ASSERT(classes[Class_StringObject]->verifyIndex(id_length()->propertyKey(), Heap::StringObject::LengthPropertyIndex));
- jsObjects[NumberProto] = memoryManager->allocObject<NumberPrototype>();
- jsObjects[BooleanProto] = memoryManager->allocObject<BooleanPrototype>();
- jsObjects[DateProto] = memoryManager->allocObject<DatePrototype>();
+ classes[Class_SymbolObject] = newInternalClass(QV4::SymbolObject::staticVTable(), symbolPrototype());
- uint index;
+ jsObjects[NumberProto] = memoryManager->allocate<NumberPrototype>();
+ jsObjects[BooleanProto] = memoryManager->allocate<BooleanPrototype>();
+ jsObjects[DateProto] = memoryManager->allocate<DatePrototype>();
+
+#ifdef QT_NO_DEBUG
+ InternalClassEntry *index = nullptr;
+#else
+ InternalClassEntry _index;
+ auto *index = &_index;
+#endif
ic = newInternalClass(QV4::FunctionPrototype::staticVTable(), objectPrototype());
- ic = ic->addMember(id_prototype(), Attr_NotEnumerable, &index);
- Q_ASSERT(index == Heap::FunctionObject::Index_Prototype);
- jsObjects[FunctionProto] = memoryManager->allocObject<FunctionPrototype>(ic, objectPrototype());
+ auto addProtoHasInstance = [&] {
+ // Add an invalid prototype slot, so that all function objects have the same layout
+ // This helps speed up instanceof operations and other things where we need to query
+ // prototype property (as we always know it's location)
+ ic = ic->addMember(id_prototype()->propertyKey(), Attr_Invalid, index);
+ Q_ASSERT(index->index == Heap::FunctionObject::Index_Prototype);
+ // add an invalid @hasInstance slot, so that we can quickly track whether the
+ // hasInstance method has been reimplemented. This is required for a fast
+ // instanceof implementation
+ ic = ic->addMember(symbol_hasInstance()->propertyKey(), Attr_Invalid, index);
+ Q_ASSERT(index->index == Heap::FunctionObject::Index_HasInstance);
+ };
+ addProtoHasInstance();
+ jsObjects[FunctionProto] = memoryManager->allocObject<FunctionPrototype>(ic->d());
ic = newInternalClass(FunctionObject::staticVTable(), functionPrototype());
- ic = ic->addMember(id_prototype(), Attr_NotEnumerable|Attr_NotConfigurable, &index);
- Q_ASSERT(index == Heap::FunctionObject::Index_Prototype);
- internalClasses[EngineBase::Class_FunctionObject] = ic;
- ic = ic->addMember(id_name(), Attr_ReadOnly, &index);
- Q_ASSERT(index == Heap::ScriptFunction::Index_Name);
+ addProtoHasInstance();
+ classes[Class_FunctionObject] = ic->d();
+ ic = ic->addMember(id_name()->propertyKey(), Attr_ReadOnly, index);
+ Q_ASSERT(index->index == Heap::ArrowFunction::Index_Name);
+ ic = ic->addMember(id_length()->propertyKey(), Attr_ReadOnly_ButConfigurable, index);
+ Q_ASSERT(index->index == Heap::ArrowFunction::Index_Length);
+ classes[Class_ArrowFunction] = ic->changeVTable(ArrowFunction::staticVTable());
+ ic = ic->changeVTable(MemberFunction::staticVTable());
+ classes[Class_MemberFunction] = ic->d();
+ ic = ic->changeVTable(GeneratorFunction::staticVTable());
+ classes[Class_GeneratorFunction] = ic->d();
+ ic = ic->changeVTable(MemberGeneratorFunction::staticVTable());
+ classes[Class_MemberGeneratorFunction] = ic->d();
+
+ ic = ic->changeMember(id_prototype()->propertyKey(), Attr_NotConfigurable|Attr_NotEnumerable);
ic = ic->changeVTable(ScriptFunction::staticVTable());
- internalClasses[EngineBase::Class_ScriptFunction] = ic->addMember(id_length(), Attr_ReadOnly, &index);
- Q_ASSERT(index == Heap::ScriptFunction::Index_Length);
- internalClasses[EngineBase::Class_ObjectProto] = internalClasses[Class_Object]->addMember(id_constructor(), Attr_NotEnumerable, &index);
- Q_ASSERT(index == Heap::FunctionObject::Index_ProtoConstructor);
+ classes[Class_ScriptFunction] = ic->d();
+ ic = ic->changeVTable(ConstructorFunction::staticVTable());
+ classes[Class_ConstructorFunction] = ic->d();
+
+ classes[Class_ObjectProto] = classes[Class_Object]->addMember(id_constructor()->propertyKey(), Attr_NotEnumerable, index);
+ Q_ASSERT(index->index == Heap::FunctionObject::Index_ProtoConstructor);
+
+ jsObjects[GeneratorProto] = memoryManager->allocObject<GeneratorPrototype>(classes[Class_Object]);
+ classes[Class_GeneratorObject] = newInternalClass(QV4::GeneratorObject::staticVTable(), generatorPrototype());
- Scope scope(this);
ScopedString str(scope);
- internalClasses[Class_RegExp] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::RegExp::staticVTable());
+ classes[Class_RegExp] = classes[Class_Empty]->changeVTable(QV4::RegExp::staticVTable());
ic = newInternalClass(QV4::RegExpObject::staticVTable(), objectPrototype());
- ic = ic->addMember(id_lastIndex(), Attr_NotEnumerable|Attr_NotConfigurable, &index);
- Q_ASSERT(index == RegExpObject::Index_LastIndex);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("source"))), Attr_ReadOnly, &index);
- Q_ASSERT(index == RegExpObject::Index_Source);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("global"))), Attr_ReadOnly, &index);
- Q_ASSERT(index == RegExpObject::Index_Global);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("ignoreCase"))), Attr_ReadOnly, &index);
- Q_ASSERT(index == RegExpObject::Index_IgnoreCase);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("multiline"))), Attr_ReadOnly, &index);
- Q_ASSERT(index == RegExpObject::Index_Multiline);
- jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(ic, objectPrototype());
- internalClasses[Class_RegExpObject] = ic->changePrototype(regExpPrototype()->d());
-
- ic = internalClasses[Class_ArrayObject]->addMember(id_index(), Attr_Data, &index);
- Q_ASSERT(index == RegExpObject::Index_ArrayIndex);
- internalClasses[EngineBase::Class_RegExpExecArray] = ic->addMember(id_input(), Attr_Data, &index);
- Q_ASSERT(index == RegExpObject::Index_ArrayInput);
+ ic = ic->addMember(id_lastIndex()->propertyKey(), Attr_NotEnumerable|Attr_NotConfigurable, index);
+ Q_ASSERT(index->index == RegExpObject::Index_LastIndex);
+ jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(classes[Class_Object]);
+ classes[Class_RegExpObject] = ic->changePrototype(regExpPrototype()->d());
- ic = newInternalClass(ErrorObject::staticVTable(), nullptr);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("stack"))), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index);
- Q_ASSERT(index == ErrorObject::Index_Stack);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("fileName"))), Attr_Data|Attr_NotEnumerable, &index);
- Q_ASSERT(index == ErrorObject::Index_FileName);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("lineNumber"))), Attr_Data|Attr_NotEnumerable, &index);
- internalClasses[EngineBase::Class_ErrorObject] = ic;
- Q_ASSERT(index == ErrorObject::Index_LineNumber);
- internalClasses[EngineBase::Class_ErrorObjectWithMessage] = ic->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index);
- Q_ASSERT(index == ErrorObject::Index_Message);
- ic = newInternalClass(ErrorObject::staticVTable(), objectPrototype());
- ic = ic->addMember(id_constructor(), Attr_Data|Attr_NotEnumerable, &index);
- Q_ASSERT(index == ErrorPrototype::Index_Constructor);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index);
- Q_ASSERT(index == ErrorPrototype::Index_Message);
- internalClasses[EngineBase::Class_ErrorProto] = ic->addMember(id_name(), Attr_Data|Attr_NotEnumerable, &index);
- Q_ASSERT(index == ErrorPrototype::Index_Name);
-
- jsObjects[GetStack_Function] = FunctionObject::createBuiltinFunction(rootContext(), str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack);
- getStackFunction()->defineReadonlyProperty(id_length(), Primitive::fromInt32(0));
-
- jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto], objectPrototype());
- jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
- jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
- jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
- jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
- jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
- jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
-
- jsObjects[VariantProto] = memoryManager->allocObject<VariantPrototype>();
- Q_ASSERT(variantPrototype()->prototype() == objectPrototype()->d());
+ ic = classes[Class_ArrayObject]->addMember(id_index()->propertyKey(), Attr_Data, index);
+ Q_ASSERT(index->index == RegExpObject::Index_ArrayIndex);
+ classes[Class_RegExpExecArray] = ic->addMember(id_input()->propertyKey(), Attr_Data, index);
+ Q_ASSERT(index->index == RegExpObject::Index_ArrayInput);
+ ic = newInternalClass(ErrorObject::staticVTable(), nullptr);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("stack")))->propertyKey(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, index);
+ Q_ASSERT(index->index == ErrorObject::Index_Stack);
+ Q_ASSERT(index->setterIndex == ErrorObject::Index_StackSetter);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("fileName")))->propertyKey(), Attr_Data|Attr_NotEnumerable, index);
+ Q_ASSERT(index->index == ErrorObject::Index_FileName);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("lineNumber")))->propertyKey(), Attr_Data|Attr_NotEnumerable, index);
+ classes[Class_ErrorObject] = ic->d();
+ Q_ASSERT(index->index == ErrorObject::Index_LineNumber);
+ classes[Class_ErrorObjectWithMessage] = ic->addMember((str = newIdentifier(QStringLiteral("message")))->propertyKey(), Attr_Data|Attr_NotEnumerable, index);
+ Q_ASSERT(index->index == ErrorObject::Index_Message);
+ ic = newInternalClass(Object::staticVTable(), objectPrototype());
+ ic = ic->addMember(id_constructor()->propertyKey(), Attr_Data|Attr_NotEnumerable, index);
+ Q_ASSERT(index->index == ErrorPrototype::Index_Constructor);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("message")))->propertyKey(), Attr_Data|Attr_NotEnumerable, index);
+ Q_ASSERT(index->index == ErrorPrototype::Index_Message);
+ classes[Class_ErrorProto] = ic->addMember(id_name()->propertyKey(), Attr_Data|Attr_NotEnumerable, index);
+ Q_ASSERT(index->index == ErrorPrototype::Index_Name);
+
+ classes[Class_ProxyObject] = classes[Class_Empty]->changeVTable(ProxyObject::staticVTable());
+ classes[Class_ProxyFunctionObject] = classes[Class_Empty]->changeVTable(ProxyFunctionObject::staticVTable());
+
+ jsObjects[GetStack_Function] = FunctionObject::createBuiltinFunction(this, str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack, 0);
+
+ jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(classes[Class_ErrorProto]);
+ ic = classes[Class_ErrorProto]->changePrototype(errorPrototype()->d());
+ jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(ic->d());
+ jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(ic->d());
+ jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(ic->d());
+ jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(ic->d());
+ jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(ic->d());
+ jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(ic->d());
+
+ jsObjects[VariantProto] = memoryManager->allocate<VariantPrototype>();
+ Q_ASSERT(variantPrototype()->getPrototypeOf() == objectPrototype()->d());
+
+#if QT_CONFIG(qml_sequence_object)
ic = newInternalClass(SequencePrototype::staticVTable(), SequencePrototype::defaultPrototype(this));
- jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic, SequencePrototype::defaultPrototype(this)));
+ jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic->d()));
+#endif
ExecutionContext *global = rootContext();
- jsObjects[Object_Ctor] = memoryManager->allocObject<ObjectCtor>(global);
- jsObjects[String_Ctor] = memoryManager->allocObject<StringCtor>(global);
- jsObjects[Number_Ctor] = memoryManager->allocObject<NumberCtor>(global);
- jsObjects[Boolean_Ctor] = memoryManager->allocObject<BooleanCtor>(global);
- jsObjects[Array_Ctor] = memoryManager->allocObject<ArrayCtor>(global);
- jsObjects[Function_Ctor] = memoryManager->allocObject<FunctionCtor>(global);
- jsObjects[Date_Ctor] = memoryManager->allocObject<DateCtor>(global);
- jsObjects[RegExp_Ctor] = memoryManager->allocObject<RegExpCtor>(global);
- jsObjects[Error_Ctor] = memoryManager->allocObject<ErrorCtor>(global);
- jsObjects[EvalError_Ctor] = memoryManager->allocObject<EvalErrorCtor>(global);
- jsObjects[RangeError_Ctor] = memoryManager->allocObject<RangeErrorCtor>(global);
- jsObjects[ReferenceError_Ctor] = memoryManager->allocObject<ReferenceErrorCtor>(global);
- jsObjects[SyntaxError_Ctor] = memoryManager->allocObject<SyntaxErrorCtor>(global);
- jsObjects[TypeError_Ctor] = memoryManager->allocObject<TypeErrorCtor>(global);
- jsObjects[URIError_Ctor] = memoryManager->allocObject<URIErrorCtor>(global);
+
+ jsObjects[Object_Ctor] = memoryManager->allocate<ObjectCtor>(global);
+ jsObjects[String_Ctor] = memoryManager->allocate<StringCtor>(global);
+ jsObjects[Symbol_Ctor] = memoryManager->allocate<SymbolCtor>(global);
+ jsObjects[Number_Ctor] = memoryManager->allocate<NumberCtor>(global);
+ jsObjects[Boolean_Ctor] = memoryManager->allocate<BooleanCtor>(global);
+ jsObjects[Array_Ctor] = memoryManager->allocate<ArrayCtor>(global);
+ jsObjects[Function_Ctor] = memoryManager->allocate<FunctionCtor>(global);
+ jsObjects[GeneratorFunction_Ctor] = memoryManager->allocate<GeneratorFunctionCtor>(global);
+ jsObjects[Date_Ctor] = memoryManager->allocate<DateCtor>(global);
+ jsObjects[RegExp_Ctor] = memoryManager->allocate<RegExpCtor>(global);
+ jsObjects[Error_Ctor] = memoryManager->allocate<ErrorCtor>(global);
+ jsObjects[EvalError_Ctor] = memoryManager->allocate<EvalErrorCtor>(global);
+ jsObjects[RangeError_Ctor] = memoryManager->allocate<RangeErrorCtor>(global);
+ jsObjects[ReferenceError_Ctor] = memoryManager->allocate<ReferenceErrorCtor>(global);
+ jsObjects[SyntaxError_Ctor] = memoryManager->allocate<SyntaxErrorCtor>(global);
+ 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()));
+
+ str = newString(QStringLiteral("get [Symbol.species]"));
+ jsObjects[GetSymbolSpecies] = FunctionObject::createBuiltinFunction(this, str, ArrayPrototype::method_get_species, 0);
static_cast<ObjectPrototype *>(objectPrototype())->init(this, objectCtor());
static_cast<StringPrototype *>(stringPrototype())->init(this, stringCtor());
+ static_cast<SymbolPrototype *>(symbolPrototype())->init(this, symbolCtor());
static_cast<NumberPrototype *>(numberPrototype())->init(this, numberCtor());
static_cast<BooleanPrototype *>(booleanPrototype())->init(this, booleanCtor());
static_cast<ArrayPrototype *>(arrayPrototype())->init(this, arrayCtor());
static_cast<PropertyListPrototype *>(propertyListPrototype())->init(this);
static_cast<DatePrototype *>(datePrototype())->init(this, dateCtor());
static_cast<FunctionPrototype *>(functionPrototype())->init(this, functionCtor());
+ static_cast<GeneratorPrototype *>(generatorPrototype())->init(this, generatorFunctionCtor());
static_cast<RegExpPrototype *>(regExpPrototype())->init(this, regExpCtor());
static_cast<ErrorPrototype *>(errorPrototype())->init(this, errorCtor());
static_cast<EvalErrorPrototype *>(evalErrorPrototype())->init(this, evalErrorCtor());
@@ -383,25 +491,59 @@ ExecutionEngine::ExecutionEngine()
static_cast<TypeErrorPrototype *>(typeErrorPrototype())->init(this, typeErrorCtor());
static_cast<URIErrorPrototype *>(uRIErrorPrototype())->init(this, uRIErrorCtor());
+ static_cast<IteratorPrototype *>(iteratorPrototype())->init(this);
+ static_cast<ForInIteratorPrototype *>(forInIteratorPrototype())->init(this);
+ static_cast<MapIteratorPrototype *>(mapIteratorPrototype())->init(this);
+ static_cast<SetIteratorPrototype *>(setIteratorPrototype())->init(this);
+ static_cast<ArrayIteratorPrototype *>(arrayIteratorPrototype())->init(this);
+ static_cast<StringIteratorPrototype *>(stringIteratorPrototype())->init(this);
+
static_cast<VariantPrototype *>(variantPrototype())->init();
+
+#if QT_CONFIG(qml_sequence_object)
sequencePrototype()->cast<SequencePrototype>()->init();
+#endif
+
+ jsObjects[WeakMap_Ctor] = memoryManager->allocate<WeakMapCtor>(global);
+ jsObjects[WeakMapProto] = memoryManager->allocate<WeakMapPrototype>();
+ static_cast<WeakMapPrototype *>(weakMapPrototype())->init(this, weakMapCtor());
+ jsObjects[Map_Ctor] = memoryManager->allocate<MapCtor>(global);
+ jsObjects[MapProto] = memoryManager->allocate<MapPrototype>();
+ static_cast<MapPrototype *>(mapPrototype())->init(this, mapCtor());
+
+ jsObjects[WeakSet_Ctor] = memoryManager->allocate<WeakSetCtor>(global);
+ jsObjects[WeakSetProto] = memoryManager->allocate<WeakSetPrototype>();
+ static_cast<WeakSetPrototype *>(weakSetPrototype())->init(this, weakSetCtor());
+
+ jsObjects[Set_Ctor] = memoryManager->allocate<SetCtor>(global);
+ jsObjects[SetProto] = memoryManager->allocate<SetPrototype>();
+ static_cast<SetPrototype *>(setPrototype())->init(this, setCtor());
// typed arrays
- jsObjects[ArrayBuffer_Ctor] = memoryManager->allocObject<ArrayBufferCtor>(global);
- jsObjects[ArrayBufferProto] = memoryManager->allocObject<ArrayBufferPrototype>();
+ jsObjects[SharedArrayBuffer_Ctor] = memoryManager->allocate<SharedArrayBufferCtor>(global);
+ jsObjects[SharedArrayBufferProto] = memoryManager->allocate<SharedArrayBufferPrototype>();
+ static_cast<SharedArrayBufferPrototype *>(sharedArrayBufferPrototype())->init(this, sharedArrayBufferCtor());
+
+ jsObjects[ArrayBuffer_Ctor] = memoryManager->allocate<ArrayBufferCtor>(global);
+ jsObjects[ArrayBufferProto] = memoryManager->allocate<ArrayBufferPrototype>();
static_cast<ArrayBufferPrototype *>(arrayBufferPrototype())->init(this, arrayBufferCtor());
- jsObjects[DataView_Ctor] = memoryManager->allocObject<DataViewCtor>(global);
- jsObjects[DataViewProto] = memoryManager->allocObject<DataViewPrototype>();
+ jsObjects[DataView_Ctor] = memoryManager->allocate<DataViewCtor>(global);
+ jsObjects[DataViewProto] = memoryManager->allocate<DataViewPrototype>();
static_cast<DataViewPrototype *>(dataViewPrototype())->init(this, dataViewCtor());
jsObjects[ValueTypeProto] = (Heap::Base *) nullptr;
jsObjects[SignalHandlerProto] = (Heap::Base *) nullptr;
- for (int i = 0; i < Heap::TypedArray::NTypes; ++i) {
- static_cast<Value &>(typedArrayCtors[i]) = memoryManager->allocObject<TypedArrayCtor>(global, Heap::TypedArray::Type(i));
- static_cast<Value &>(typedArrayPrototype[i]) = memoryManager->allocObject<TypedArrayPrototype>(Heap::TypedArray::Type(i));
+ jsObjects[IntrinsicTypedArray_Ctor] = memoryManager->allocate<IntrinsicTypedArrayCtor>(global);
+ jsObjects[IntrinsicTypedArrayProto] = memoryManager->allocate<IntrinsicTypedArrayPrototype>();
+ static_cast<IntrinsicTypedArrayPrototype *>(intrinsicTypedArrayPrototype())
+ ->init(this, static_cast<IntrinsicTypedArrayCtor *>(intrinsicTypedArrayCtor()));
+
+ for (int i = 0; i < NTypedArrayTypes; ++i) {
+ static_cast<Value &>(typedArrayCtors[i]) = memoryManager->allocate<TypedArrayCtor>(global, Heap::TypedArray::Type(i));
+ static_cast<Value &>(typedArrayPrototype[i]) = memoryManager->allocate<TypedArrayPrototype>(Heap::TypedArray::Type(i));
typedArrayPrototype[i].as<TypedArrayPrototype>()->init(this, static_cast<TypedArrayCtor *>(typedArrayCtors[i].as<Object>()));
}
@@ -413,6 +555,7 @@ ExecutionEngine::ExecutionEngine()
globalObject->defineDefaultProperty(QStringLiteral("Object"), *objectCtor());
globalObject->defineDefaultProperty(QStringLiteral("String"), *stringCtor());
+ globalObject->defineDefaultProperty(QStringLiteral("Symbol"), *symbolCtor());
FunctionObject *numberObject = numberCtor();
globalObject->defineDefaultProperty(QStringLiteral("Number"), *numberObject);
globalObject->defineDefaultProperty(QStringLiteral("Boolean"), *booleanCtor());
@@ -428,20 +571,29 @@ ExecutionEngine::ExecutionEngine()
globalObject->defineDefaultProperty(QStringLiteral("TypeError"), *typeErrorCtor());
globalObject->defineDefaultProperty(QStringLiteral("URIError"), *uRIErrorCtor());
+ globalObject->defineDefaultProperty(QStringLiteral("SharedArrayBuffer"), *sharedArrayBufferCtor());
globalObject->defineDefaultProperty(QStringLiteral("ArrayBuffer"), *arrayBufferCtor());
globalObject->defineDefaultProperty(QStringLiteral("DataView"), *dataViewCtor());
- for (int i = 0; i < Heap::TypedArray::NTypes; ++i)
- globalObject->defineDefaultProperty((str = typedArrayCtors[i].as<FunctionObject>()->name())->toQString(), typedArrayCtors[i]);
+ globalObject->defineDefaultProperty(QStringLiteral("WeakSet"), *weakSetCtor());
+ globalObject->defineDefaultProperty(QStringLiteral("Set"), *setCtor());
+ globalObject->defineDefaultProperty(QStringLiteral("WeakMap"), *weakMapCtor());
+ globalObject->defineDefaultProperty(QStringLiteral("Map"), *mapCtor());
+
+ for (int i = 0; i < NTypedArrayTypes; ++i)
+ globalObject->defineDefaultProperty((str = typedArrayCtors[i].as<FunctionObject>()->name()), typedArrayCtors[i]);
ScopedObject o(scope);
- globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->allocObject<MathObject>()));
- globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->allocObject<JsonObject>()));
+ globalObject->defineDefaultProperty(QStringLiteral("Atomics"), (o = memoryManager->allocate<Atomics>()));
+ globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->allocate<MathObject>()));
+ globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->allocate<JsonObject>()));
+ globalObject->defineDefaultProperty(QStringLiteral("Reflect"), (o = memoryManager->allocate<Reflect>()));
+ globalObject->defineDefaultProperty(QStringLiteral("Proxy"), (o = memoryManager->allocate<Proxy>(rootContext())));
- globalObject->defineReadonlyProperty(QStringLiteral("undefined"), Primitive::undefinedValue());
- globalObject->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(std::numeric_limits<double>::quiet_NaN()));
- globalObject->defineReadonlyProperty(QStringLiteral("Infinity"), Primitive::fromDouble(Q_INFINITY));
+ globalObject->defineReadonlyProperty(QStringLiteral("undefined"), Value::undefinedValue());
+ globalObject->defineReadonlyProperty(QStringLiteral("NaN"), Value::fromDouble(std::numeric_limits<double>::quiet_NaN()));
+ globalObject->defineReadonlyProperty(QStringLiteral("Infinity"), Value::fromDouble(Q_INFINITY));
- jsObjects[Eval_Function] = memoryManager->allocObject<EvalFunction>(global);
+ jsObjects[Eval_Function] = memoryManager->allocate<EvalFunction>(global);
globalObject->defineDefaultProperty(QStringLiteral("eval"), *evalFunction());
// ES6: 20.1.2.12 & 20.1.2.13:
@@ -453,11 +605,8 @@ ExecutionEngine::ExecutionEngine()
Scope scope(this);
ScopedString pi(scope, newIdentifier(piString));
ScopedString pf(scope, newIdentifier(pfString));
- ExecutionContext *global = rootContext();
- ScopedFunctionObject parseIntFn(scope, FunctionObject::createBuiltinFunction(global, pi, GlobalFunctions::method_parseInt));
- ScopedFunctionObject parseFloatFn(scope, FunctionObject::createBuiltinFunction(global, pf, GlobalFunctions::method_parseFloat));
- parseIntFn->defineReadonlyConfigurableProperty(id_length(), Primitive::fromInt32(2));
- parseFloatFn->defineReadonlyConfigurableProperty(id_length(), Primitive::fromInt32(1));
+ ScopedFunctionObject parseIntFn(scope, FunctionObject::createBuiltinFunction(this, pi, GlobalFunctions::method_parseInt, 2));
+ ScopedFunctionObject parseFloatFn(scope, FunctionObject::createBuiltinFunction(this, pf, GlobalFunctions::method_parseFloat, 1));
globalObject->defineDefaultProperty(piString, parseIntFn);
globalObject->defineDefaultProperty(pfString, parseFloatFn);
numberObject->defineDefaultProperty(piString, parseIntFn);
@@ -473,12 +622,21 @@ ExecutionEngine::ExecutionEngine()
globalObject->defineDefaultProperty(QStringLiteral("escape"), GlobalFunctions::method_escape, 1);
globalObject->defineDefaultProperty(QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1);
- ScopedString name(scope, newString(QStringLiteral("thrower")));
- jsObjects[ThrowerObject] = FunctionObject::createBuiltinFunction(global, name, ::throwTypeError);
+ ScopedFunctionObject t(scope, memoryManager->allocate<FunctionObject>(rootContext(), nullptr, ::throwTypeError));
+ t->defineReadonlyProperty(id_length(), Value::fromInt32(0));
+ t->setInternalClass(t->internalClass()->frozen());
+ jsObjects[ThrowerObject] = t;
+
+ ScopedProperty pd(scope);
+ pd->value = thrower();
+ pd->set = thrower();
+ functionPrototype()->insertMember(id_caller(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable);
+ functionPrototype()->insertMember(id_arguments(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable);
}
ExecutionEngine::~ExecutionEngine()
{
+ modules.clear();
delete m_multiplyWrappedQObjects;
m_multiplyWrappedQObjects = nullptr;
delete identifierTable;
@@ -487,8 +645,6 @@ ExecutionEngine::~ExecutionEngine()
while (!compilationUnits.isEmpty())
(*compilationUnits.begin())->unlink();
- internalClasses[Class_Empty]->destroy();
- delete classPool;
delete bumperPointerAllocator;
delete regExpCache;
delete regExpAllocator;
@@ -497,7 +653,11 @@ ExecutionEngine::~ExecutionEngine()
delete jsStack;
gcStack->deallocate();
delete gcStack;
- delete [] argumentsAccessors;
+}
+
+ExecutionContext *ExecutionEngine::currentContext() const
+{
+ return static_cast<ExecutionContext *>(&currentStackFrame->jsFrame->context);
}
#if QT_CONFIG(qml_debug)
@@ -521,59 +681,71 @@ void ExecutionEngine::initRootContext()
r->d_unchecked()->init(Heap::ExecutionContext::Type_GlobalContext);
r->d()->activation.set(this, globalObject->d());
jsObjects[RootContext] = r;
+ jsObjects[ScriptContext] = r;
jsObjects[IntegerNull] = Encode((int)0);
}
-InternalClass *ExecutionEngine::newClass(const InternalClass &other)
+Heap::InternalClass *ExecutionEngine::newClass(Heap::InternalClass *other)
{
- return new (classPool) InternalClass(other);
+ Heap::InternalClass *ic = memoryManager->allocIC<InternalClass>();
+ ic->init(other);
+ return ic;
}
-InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype)
+Heap::InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype)
{
- return internalClasses[EngineBase::Class_Empty]->changeVTable(vtable)->changePrototype(prototype ? prototype->d() : nullptr);
+ Scope scope(this);
+ Scoped<InternalClass> ic(scope, internalClasses(Class_Empty)->changeVTable(vtable));
+ return ic->changePrototype(prototype ? prototype->d() : nullptr);
}
Heap::Object *ExecutionEngine::newObject()
{
- return memoryManager->allocObject<Object>();
+ return memoryManager->allocate<Object>();
}
-Heap::Object *ExecutionEngine::newObject(InternalClass *internalClass, QV4::Object *prototype)
+Heap::Object *ExecutionEngine::newObject(Heap::InternalClass *internalClass)
{
- return memoryManager->allocObject<Object>(internalClass, prototype);
+ return memoryManager->allocObject<Object>(internalClass);
}
Heap::String *ExecutionEngine::newString(const QString &s)
{
- Scope scope(this);
- return ScopedString(scope, memoryManager->allocWithStringData<String>(s.length() * sizeof(QChar), s))->d();
+ return memoryManager->allocWithStringData<String>(s.length() * sizeof(QChar), s);
}
Heap::String *ExecutionEngine::newIdentifier(const QString &text)
{
- return identifierTable->insertString(text);
+ Scope scope(this);
+ ScopedString s(scope, memoryManager->allocWithStringData<String>(text.length() * sizeof(QChar), text));
+ s->toPropertyKey();
+ return s->d();
}
Heap::Object *ExecutionEngine::newStringObject(const String *string)
{
- return memoryManager->allocObject<StringObject>(string);
+ return memoryManager->allocate<StringObject>(string);
+}
+
+Heap::Object *ExecutionEngine::newSymbolObject(const Symbol *symbol)
+{
+ return memoryManager->allocObject<SymbolObject>(classes[Class_SymbolObject], symbol);
}
Heap::Object *ExecutionEngine::newNumberObject(double value)
{
- return memoryManager->allocObject<NumberObject>(value);
+ return memoryManager->allocate<NumberObject>(value);
}
Heap::Object *ExecutionEngine::newBooleanObject(bool b)
{
- return memoryManager->allocObject<BooleanObject>(b);
+ return memoryManager->allocate<BooleanObject>(b);
}
Heap::ArrayObject *ExecutionEngine::newArrayObject(int count)
{
Scope scope(this);
- ScopedArrayObject object(scope, memoryManager->allocObject<ArrayObject>());
+ ScopedArrayObject object(scope, memoryManager->allocate<ArrayObject>());
if (count) {
if (count < 0x1000)
@@ -586,7 +758,7 @@ Heap::ArrayObject *ExecutionEngine::newArrayObject(int count)
Heap::ArrayObject *ExecutionEngine::newArrayObject(const Value *values, int length)
{
Scope scope(this);
- ScopedArrayObject a(scope, memoryManager->allocObject<ArrayObject>());
+ ScopedArrayObject a(scope, memoryManager->allocate<ArrayObject>());
if (length) {
size_t size = sizeof(Heap::ArrayData) + (length-1)*sizeof(Value);
@@ -607,72 +779,64 @@ Heap::ArrayObject *ExecutionEngine::newArrayObject(const Value *values, int leng
Heap::ArrayObject *ExecutionEngine::newArrayObject(const QStringList &list)
{
- Scope scope(this);
- ScopedArrayObject object(scope, memoryManager->allocObject<ArrayObject>(list));
- return object->d();
+ return memoryManager->allocate<ArrayObject>(list);
}
-Heap::ArrayObject *ExecutionEngine::newArrayObject(InternalClass *internalClass, Object *prototype)
+Heap::ArrayObject *ExecutionEngine::newArrayObject(Heap::InternalClass *internalClass)
{
- Scope scope(this);
- ScopedArrayObject object(scope, memoryManager->allocObject<ArrayObject>(internalClass, prototype));
- return object->d();
+ return memoryManager->allocObject<ArrayObject>(internalClass);
}
Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(const QByteArray &array)
{
- return memoryManager->allocObject<ArrayBuffer>(array);
+ return memoryManager->allocate<ArrayBuffer>(array);
}
Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(size_t length)
{
- return memoryManager->allocObject<ArrayBuffer>(length);
+ return memoryManager->allocate<ArrayBuffer>(length);
}
Heap::DateObject *ExecutionEngine::newDateObject(const Value &value)
{
- return memoryManager->allocObject<DateObject>(value);
+ return memoryManager->allocate<DateObject>(value);
}
Heap::DateObject *ExecutionEngine::newDateObject(const QDateTime &dt)
{
Scope scope(this);
- Scoped<DateObject> object(scope, memoryManager->allocObject<DateObject>(dt));
+ Scoped<DateObject> object(scope, memoryManager->allocate<DateObject>(dt));
return object->d();
}
Heap::DateObject *ExecutionEngine::newDateObjectFromTime(const QTime &t)
{
Scope scope(this);
- Scoped<DateObject> object(scope, memoryManager->allocObject<DateObject>(t));
+ Scoped<DateObject> object(scope, memoryManager->allocate<DateObject>(t));
return object->d();
}
Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags)
{
- bool global = (flags & QV4::CompiledData::RegExp::RegExp_Global);
- bool ignoreCase = (flags & QV4::CompiledData::RegExp::RegExp_IgnoreCase);
- bool multiline = (flags & QV4::CompiledData::RegExp::RegExp_Multiline);
-
Scope scope(this);
- Scoped<RegExp> re(scope, RegExp::create(this, pattern, ignoreCase, multiline, global));
+ Scoped<RegExp> re(scope, RegExp::create(this, pattern, static_cast<CompiledData::RegExp::Flags>(flags)));
return newRegExpObject(re);
}
Heap::RegExpObject *ExecutionEngine::newRegExpObject(RegExp *re)
{
- return memoryManager->allocObject<RegExpObject>(re);
+ return memoryManager->allocate<RegExpObject>(re);
}
Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re)
{
- return memoryManager->allocObject<RegExpObject>(re);
+ return memoryManager->allocate<RegExpObject>(re);
}
Heap::Object *ExecutionEngine::newErrorObject(const Value &value)
{
- return ErrorObject::create<ErrorObject>(this, value);
+ return ErrorObject::create<ErrorObject>(this, value, errorCtor());
}
Heap::Object *ExecutionEngine::newSyntaxErrorObject(const QString &message)
@@ -709,21 +873,36 @@ Heap::Object *ExecutionEngine::newRangeErrorObject(const QString &message)
Heap::Object *ExecutionEngine::newURIErrorObject(const Value &message)
{
- return ErrorObject::create<URIErrorObject>(this, message);
+ return ErrorObject::create<URIErrorObject>(this, message, uRIErrorCtor());
}
Heap::Object *ExecutionEngine::newVariantObject(const QVariant &v)
{
- return memoryManager->allocObject<VariantObject>(v);
+ return memoryManager->allocate<VariantObject>(v);
}
-Heap::Object *ExecutionEngine::newForEachIteratorObject(Object *o)
+Heap::Object *ExecutionEngine::newForInIteratorObject(Object *o)
{
Scope scope(this);
- ScopedObject obj(scope, memoryManager->allocObject<ForEachIteratorObject>(o));
+ ScopedObject obj(scope, memoryManager->allocate<ForInIteratorObject>(o));
return obj->d();
}
+Heap::Object *ExecutionEngine::newMapIteratorObject(Object *o)
+{
+ return memoryManager->allocate<MapIteratorObject>(o->d(), this);
+}
+
+Heap::Object *ExecutionEngine::newSetIteratorObject(Object *o)
+{
+ return memoryManager->allocate<SetIteratorObject>(o->d(), this);
+}
+
+Heap::Object *ExecutionEngine::newArrayIteratorObject(Object *o)
+{
+ return memoryManager->allocate<ArrayIteratorObject>(o->d(), this);
+}
+
Heap::QmlContext *ExecutionEngine::qmlContext() const
{
if (!currentStackFrame)
@@ -761,37 +940,6 @@ QQmlContextData *ExecutionEngine::callingQmlContext() const
return ctx->qml()->context->contextData();
}
-QString CppStackFrame::source() const
-{
- return v4Function ? v4Function->sourceFile() : QString();
-}
-
-QString CppStackFrame::function() const
-{
- return v4Function ? v4Function->name()->toQString() : QString();
-}
-
-int CppStackFrame::lineNumber() const
-{
- if (!v4Function)
- return -1;
-
- auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) {
- return entry.codeOffset < offset;
- };
-
- const QV4::CompiledData::Function *cf = v4Function->compiledFunction;
- uint offset = instructionPointer;
- const CompiledData::CodeOffsetToLine *lineNumbers = cf->lineNumberTable();
- uint nLineNumbers = cf->nLineNumbers;
- const CompiledData::CodeOffsetToLine *line = std::lower_bound(lineNumbers, lineNumbers + nLineNumbers, offset, findLine) - 1;
- return line->line;
-}
-
-ReturnedValue CppStackFrame::thisObject() const {
- return jsFrame->thisObject.asReturnedValue();
-}
-
StackTrace ExecutionEngine::stackTrace(int frameLimit) const
{
Scope scope(const_cast<ExecutionEngine *>(this));
@@ -871,47 +1019,15 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file)
return base.resolved(src);
}
-void ExecutionEngine::requireArgumentsAccessors(int n)
-{
- if (n <= nArgumentsAccessors)
- return;
-
- Scope scope(this);
- ScopedFunctionObject get(scope);
- ScopedFunctionObject set(scope);
-
- if (n >= nArgumentsAccessors) {
- Property *oldAccessors = argumentsAccessors;
- int oldSize = nArgumentsAccessors;
- nArgumentsAccessors = qMax(8, n);
- argumentsAccessors = new Property[nArgumentsAccessors];
- if (oldAccessors) {
- memcpy(static_cast<void *>(argumentsAccessors), static_cast<const void *>(oldAccessors), oldSize*sizeof(Property));
- delete [] oldAccessors;
- }
- ExecutionContext *global = rootContext();
- for (int i = oldSize; i < nArgumentsAccessors; ++i) {
- argumentsAccessors[i].value = ScopedValue(scope, memoryManager->allocObject<ArgumentsGetterFunction>(global, i));
- argumentsAccessors[i].set = ScopedValue(scope, memoryManager->allocObject<ArgumentsSetterFunction>(global, i));
- }
- }
-}
-
void ExecutionEngine::markObjects(MarkStack *markStack)
{
- identifierTable->mark(markStack);
-
- for (int i = 0; i < nArgumentsAccessors; ++i) {
- const Property &pd = argumentsAccessors[i];
- if (Heap::FunctionObject *getter = pd.getter())
- getter->mark(markStack);
- if (Heap::FunctionObject *setter = pd.setter())
- setter->mark(markStack);
- }
-
- classPool->markObjects(markStack);
+ for (int i = 0; i < NClasses; ++i)
+ if (classes[i])
+ classes[i]->mark(markStack);
markStack->drain();
+ identifierTable->markObjects(markStack);
+
for (auto compilationUnit: compilationUnits) {
compilationUnit->markObjects(markStack);
markStack->drain();
@@ -950,7 +1066,7 @@ ReturnedValue ExecutionEngine::catchException(StackTrace *trace)
exceptionStackTrace.clear();
hasException = false;
ReturnedValue res = exceptionValue->asReturnedValue();
- *exceptionValue = Primitive::emptyValue();
+ *exceptionValue = Value::emptyValue();
return res;
}
@@ -1053,12 +1169,7 @@ QQmlError ExecutionEngine::catchExceptionAsQmlError()
error.setColumn(frame.column);
}
QV4::Scoped<QV4::ErrorObject> errorObj(scope, exception);
- if (!!errorObj && errorObj->asSyntaxError()) {
- QV4::ScopedString m(scope, newString(QStringLiteral("message")));
- QV4::ScopedValue v(scope, errorObj->get(m));
- error.setDescription(v->toQStringNoThrow());
- } else
- error.setDescription(exception->toQStringNoThrow());
+ error.setDescription(exception->toQStringNoThrow());
return error;
}
@@ -1117,8 +1228,11 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
return v->toVariant();
} else if (QV4::QmlListWrapper *l = object->as<QV4::QmlListWrapper>()) {
return l->toVariant();
- } else if (object->isListType())
+#if QT_CONFIG(qml_sequence_object)
+ } else if (object->isListType()) {
return QV4::SequencePrototype::toVariant(object);
+#endif
+ }
}
if (value.as<ArrayObject>()) {
@@ -1128,7 +1242,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
uint length = a->getLength();
QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope);
for (uint ii = 0; ii < length; ++ii) {
- qobjectWrapper = a->getIndexed(ii);
+ qobjectWrapper = a->get(ii);
if (!!qobjectWrapper) {
list << qobjectWrapper->object();
} else {
@@ -1141,10 +1255,12 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
return QVariant::fromValue(QV4::JsonObject::toJsonArray(a));
}
+#if QT_CONFIG(qml_sequence_object)
bool succeeded = false;
QVariant retn = QV4::SequencePrototype::toVariant(value, typeHint, &succeeded);
if (succeeded)
return retn;
+#endif
}
if (value.isUndefined())
@@ -1164,8 +1280,10 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
return str.at(0);
return str;
}
+#if QT_CONFIG(qml_locale)
if (const QV4::QQmlLocaleData *ld = value.as<QV4::QQmlLocaleData>())
return *ld->d()->locale;
+#endif
if (const QV4::DateObject *d = value.as<DateObject>())
return d->toQDateTime();
if (const ArrayBuffer *d = value.as<ArrayBuffer>())
@@ -1211,7 +1329,7 @@ static QVariant objectToVariant(QV4::ExecutionEngine *e, const QV4::Object *o, V
int length = a->getLength();
for (int ii = 0; ii < length; ++ii) {
- v = a->getIndexed(ii);
+ v = a->get(ii);
list << ::toVariant(e, v, -1, /*createJSValueForObjects*/false, visitedObjects);
}
@@ -1260,9 +1378,6 @@ static QV4::ReturnedValue objectFromVariantMap(QV4::ExecutionEngine *e, const QV
QV4::ScopedValue v(scope);
for (QVariantMap::const_iterator iter = map.begin(), cend = map.end(); iter != cend; ++iter) {
s = e->newString(iter.key());
- uint idx = s->asArrayIndex();
- if (idx > 16 && (!o->arrayData() || idx > o->arrayData()->length() * 2))
- o->initSparseArray();
o->put(s, (v = e->fromVariant(iter.value())));
}
return o.asReturnedValue();
@@ -1321,6 +1436,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(ptr)));
case QMetaType::QObjectStar:
return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr));
+#if QT_CONFIG(qml_sequence_object)
case QMetaType::QStringList:
{
bool succeeded = false;
@@ -1330,6 +1446,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
return retn->asReturnedValue();
return QV4::Encode(newArrayObject(*reinterpret_cast<const QStringList *>(ptr)));
}
+#endif
case QMetaType::QVariantList:
return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr));
case QMetaType::QVariantMap:
@@ -1340,8 +1457,10 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
return QV4::JsonObject::fromJsonObject(this, *reinterpret_cast<const QJsonObject *>(ptr));
case QMetaType::QJsonArray:
return QV4::JsonObject::fromJsonArray(this, *reinterpret_cast<const QJsonArray *>(ptr));
+#if QT_CONFIG(qml_locale)
case QMetaType::QLocale:
return QQmlLocale::wrap(this, *reinterpret_cast<const QLocale*>(ptr));
+#endif
default:
break;
}
@@ -1381,10 +1500,12 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
if (objOk)
return QV4::QObjectWrapper::wrap(this, obj);
+#if QT_CONFIG(qml_sequence_object)
bool succeeded = false;
QV4::ScopedValue retn(scope, QV4::SequencePrototype::fromVariant(this, variant, &succeeded));
if (succeeded)
return retn->asReturnedValue();
+#endif
if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(type))
return QV4::QQmlValueTypeWrapper::create(this, variant, vtmo, type);
@@ -1428,11 +1549,13 @@ static QV4::ReturnedValue variantMapToJS(QV4::ExecutionEngine *v4, const QVarian
QV4::Scope scope(v4);
QV4::ScopedObject o(scope, v4->newObject());
QV4::ScopedString s(scope);
+ QV4::ScopedPropertyKey key(scope);
QV4::ScopedValue v(scope);
for (QVariantMap::const_iterator it = vmap.constBegin(), cend = vmap.constEnd(); it != cend; ++it) {
s = v4->newIdentifier(it.key());
+ key = s->propertyKey();
v = variantToJS(v4, it.value());
- uint idx = s->asArrayIndex();
+ uint idx = key->asArrayIndex();
if (idx < UINT_MAX)
o->arraySet(idx, v);
else
@@ -1538,6 +1661,128 @@ ReturnedValue ExecutionEngine::global()
return globalObject->asReturnedValue();
}
+QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::compileModule(const QUrl &url)
+{
+ QFile f(QQmlFile::urlToLocalFileOrQrc(url));
+ if (!f.open(QIODevice::ReadOnly)) {
+ throwError(QStringLiteral("Could not open module %1 for reading").arg(url.toString()));
+ return nullptr;
+ }
+
+ const QDateTime timeStamp = QFileInfo(f).lastModified();
+
+ const QString sourceCode = QString::fromUtf8(f.readAll());
+ f.close();
+
+ return compileModule(url, sourceCode, timeStamp);
+}
+
+
+QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::compileModule(const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp)
+{
+ QList<QQmlJS::DiagnosticMessage> diagnostics;
+ auto unit = compileModule(/*debugMode*/debugger() != nullptr, url.toString(), sourceCode, sourceTimeStamp, &diagnostics);
+ for (const QQmlJS::DiagnosticMessage &m : diagnostics) {
+ if (m.isError()) {
+ throwSyntaxError(m.message, url.toString(), m.loc.startLine, m.loc.startColumn);
+ return nullptr;
+ } else {
+ qWarning() << url << ':' << m.loc.startLine << ':' << m.loc.startColumn
+ << ": warning: " << m.message;
+ }
+ }
+ return unit;
+}
+
+#endif // ifndef V4_BOOTSTRAP
+
+QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::compileModule(bool debugMode, const QString &url, const QString &sourceCode,
+ const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics)
+{
+ QQmlJS::Engine ee;
+ QQmlJS::Lexer lexer(&ee);
+ lexer.setCode(sourceCode, /*line*/1, /*qml mode*/false);
+ QQmlJS::Parser parser(&ee);
+
+ const bool parsed = parser.parseModule();
+
+ if (diagnostics)
+ *diagnostics = parser.diagnosticMessages();
+
+ if (!parsed)
+ return nullptr;
+
+ QQmlJS::AST::ESModule *moduleNode = QQmlJS::AST::cast<QQmlJS::AST::ESModule*>(parser.rootNode());
+ if (!moduleNode) {
+ // if parsing was successful, and we have no module, then
+ // the file was empty.
+ if (diagnostics)
+ diagnostics->clear();
+ return nullptr;
+ }
+
+ using namespace QV4::Compiler;
+ Compiler::Module compilerModule(debugMode);
+ compilerModule.unitFlags |= CompiledData::Unit::IsESModule;
+ compilerModule.sourceTimeStamp = sourceTimeStamp;
+ JSUnitGenerator jsGenerator(&compilerModule);
+ Codegen cg(&jsGenerator, /*strictMode*/true);
+ cg.generateFromModule(url, url, sourceCode, moduleNode, &compilerModule);
+ auto errors = cg.errors();
+ if (diagnostics)
+ *diagnostics << errors;
+
+ if (!errors.isEmpty())
+ return nullptr;
+
+ return cg.generateCompilationUnit();
+}
+
+#ifndef V4_BOOTSTRAP
+
+void ExecutionEngine::injectModule(const QQmlRefPointer<CompiledData::CompilationUnit> &moduleUnit)
+{
+ // Injection can happen from the QML type loader thread for example, but instantiation and
+ // evaluation must be limited to the ExecutionEngine's thread.
+ QMutexLocker moduleGuard(&moduleMutex);
+ modules.insert(moduleUnit->finalUrl(), moduleUnit);
+}
+
+QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::moduleForUrl(const QUrl &_url, const CompiledData::CompilationUnit *referrer) const
+{
+ QUrl url = QQmlTypeLoader::normalize(_url);
+ if (referrer)
+ url = referrer->finalUrl().resolved(url);
+
+ QMutexLocker moduleGuard(&moduleMutex);
+ auto existingModule = modules.find(url);
+ if (existingModule == modules.end())
+ return nullptr;
+ return *existingModule;
+}
+
+QQmlRefPointer<CompiledData::CompilationUnit> ExecutionEngine::loadModule(const QUrl &_url, const CompiledData::CompilationUnit *referrer)
+{
+ QUrl url = QQmlTypeLoader::normalize(_url);
+ if (referrer)
+ url = referrer->finalUrl().resolved(url);
+
+ QMutexLocker moduleGuard(&moduleMutex);
+ auto existingModule = modules.find(url);
+ if (existingModule != modules.end())
+ return *existingModule;
+
+ moduleGuard.unlock();
+
+ auto newModule = compileModule(url);
+ if (newModule) {
+ moduleGuard.relock();
+ modules.insert(url, newModule);
+ }
+
+ return newModule;
+}
+
// Converts a JS value to a meta-type.
// data must point to a place that can store a value of the given type.
// Returns true if conversion succeeded, false otherwise.
@@ -1707,7 +1952,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data)
} else if (Object *o = value->objectValue()) {
// Look in the prototype chain.
QV4::Scope scope(this);
- QV4::ScopedObject proto(scope, o->prototype());
+ QV4::ScopedObject proto(scope, o->getPrototypeOf());
while (proto) {
bool canCast = false;
if (QV4::VariantObject *vo = proto->as<QV4::VariantObject>()) {
@@ -1728,7 +1973,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data)
*reinterpret_cast<void* *>(data) = var.data();
return true;
}
- proto = proto->prototype();
+ proto = proto->getPrototypeOf();
}
}
} else if (value->isNull() && name.endsWith('*')) {
@@ -1777,5 +2022,6 @@ static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const QV4::Value &v
return wrapper->object();
}
+#endif // ifndef V4_BOOTSTRAP
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index c7fb743088..276efe6e13 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -55,7 +55,8 @@
#include "qv4context_p.h"
#include <private/qintrusivelist_p.h>
#include "qv4enginebase_p.h"
-
+#include <private/qqmlrefcount_p.h>
+#include <private/qqmljsengine_p.h>
#ifndef V4_BOOTSTRAP
# include "qv4function_p.h"
@@ -87,34 +88,11 @@ namespace CompiledData {
struct CompilationUnit;
}
-struct Function;
-struct InternalClass;
-struct InternalClassPool;
-
-struct Q_QML_EXPORT CppStackFrame {
- CppStackFrame *parent;
- Function *v4Function;
- CallData *jsFrame;
- const Value *originalArguments;
- int originalArgumentsCount;
- int instructionPointer;
-
- QString source() const;
- QString function() const;
- inline QV4::ExecutionContext *context() const {
- return static_cast<ExecutionContext *>(&jsFrame->context);
- }
- int lineNumber() const;
-
- inline QV4::Heap::CallContext *callContext() const {
- Heap::ExecutionContext *ctx = static_cast<ExecutionContext &>(jsFrame->context).d();\
- while (ctx->type != Heap::ExecutionContext::Type_CallContext)
- ctx = ctx->outer;
- return static_cast<Heap::CallContext *>(ctx);
- }
- ReturnedValue thisObject() const;
+namespace Heap {
+struct Module;
};
+struct Function;
struct Q_QML_EXPORT ExecutionEngine : public EngineBase
@@ -142,8 +120,6 @@ public:
QML_NEARLY_ALWAYS_INLINE Value *jsAlloca(int nValues) {
Value *ptr = jsStackTop;
jsStackTop = ptr + nValues;
- for (int i = 0; i < nValues; ++i)
- ptr[i] = Primitive::undefinedValue();
return ptr;
}
@@ -153,22 +129,27 @@ public:
QJSEngine *jsEngine() const;
QQmlEngine *qmlEngine() const;
#else // !V4_BOOTSTRAP
- QJSEngine *jsEngine() const { return v8Engine->publicEngine(); }
+ QJSEngine *jsEngine() const { return publicEngine; }
QQmlEngine *qmlEngine() const { return v8Engine ? v8Engine->engine() : nullptr; }
#endif // V4_BOOTSTRAP
QV8Engine *v8Engine;
+ QJSEngine *publicEngine;
enum JSObjects {
RootContext,
+ ScriptContext,
IntegerNull, // Has to come after the RootContext to make the context stack safe
ObjectProto,
+ SymbolProto,
ArrayProto,
+ ArrayProtoValues,
PropertyListProto,
StringProto,
NumberProto,
BooleanProto,
DateProto,
FunctionProto,
+ GeneratorProto,
RegExpProto,
ErrorProto,
EvalErrorProto,
@@ -178,18 +159,34 @@ public:
TypeErrorProto,
URIErrorProto,
VariantProto,
+#if QT_CONFIG(qml_sequence_object)
SequenceProto,
+#endif
+ SharedArrayBufferProto,
ArrayBufferProto,
DataViewProto,
+ WeakSetProto,
+ SetProto,
+ WeakMapProto,
+ MapProto,
+ IntrinsicTypedArrayProto,
ValueTypeProto,
SignalHandlerProto,
+ IteratorProto,
+ ForInIteratorProto,
+ SetIteratorProto,
+ MapIteratorProto,
+ ArrayIteratorProto,
+ StringIteratorProto,
Object_Ctor,
String_Ctor,
+ Symbol_Ctor,
Number_Ctor,
Boolean_Ctor,
Array_Ctor,
Function_Ctor,
+ GeneratorFunction_Ctor,
Date_Ctor,
RegExp_Ctor,
Error_Ctor,
@@ -199,8 +196,16 @@ public:
SyntaxError_Ctor,
TypeError_Ctor,
URIError_Ctor,
+ SharedArrayBuffer_Ctor,
ArrayBuffer_Ctor,
DataView_Ctor,
+ WeakSet_Ctor,
+ Set_Ctor,
+ WeakMap_Ctor,
+ Map_Ctor,
+ IntrinsicTypedArray_Ctor,
+
+ GetSymbolSpecies,
Eval_Function,
GetStack_Function,
@@ -211,12 +216,16 @@ public:
enum { NTypedArrayTypes = 9 }; // == TypedArray::NValues, avoid header dependency
ExecutionContext *rootContext() const { return reinterpret_cast<ExecutionContext *>(jsObjects + RootContext); }
+ ExecutionContext *scriptContext() const { return reinterpret_cast<ExecutionContext *>(jsObjects + ScriptContext); }
+ void setScriptContext(ReturnedValue c) { jsObjects[ScriptContext] = c; }
FunctionObject *objectCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Object_Ctor); }
FunctionObject *stringCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + String_Ctor); }
+ FunctionObject *symbolCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Symbol_Ctor); }
FunctionObject *numberCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Number_Ctor); }
FunctionObject *booleanCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Boolean_Ctor); }
FunctionObject *arrayCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Array_Ctor); }
FunctionObject *functionCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Function_Ctor); }
+ FunctionObject *generatorFunctionCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + GeneratorFunction_Ctor); }
FunctionObject *dateCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Date_Ctor); }
FunctionObject *regExpCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + RegExp_Ctor); }
FunctionObject *errorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Error_Ctor); }
@@ -226,18 +235,29 @@ public:
FunctionObject *syntaxErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + SyntaxError_Ctor); }
FunctionObject *typeErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + TypeError_Ctor); }
FunctionObject *uRIErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + URIError_Ctor); }
+ FunctionObject *sharedArrayBufferCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + SharedArrayBuffer_Ctor); }
FunctionObject *arrayBufferCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + ArrayBuffer_Ctor); }
FunctionObject *dataViewCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + DataView_Ctor); }
+ FunctionObject *weakSetCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + WeakSet_Ctor); }
+ FunctionObject *setCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Set_Ctor); }
+ FunctionObject *weakMapCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + WeakMap_Ctor); }
+ FunctionObject *mapCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Map_Ctor); }
+ FunctionObject *intrinsicTypedArrayCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + IntrinsicTypedArray_Ctor); }
FunctionObject *typedArrayCtors;
+ FunctionObject *getSymbolSpecies() const { return reinterpret_cast<FunctionObject *>(jsObjects + GetSymbolSpecies); }
+
Object *objectPrototype() const { return reinterpret_cast<Object *>(jsObjects + ObjectProto); }
+ Object *symbolPrototype() const { return reinterpret_cast<Object *>(jsObjects + SymbolProto); }
Object *arrayPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayProto); }
+ Object *arrayProtoValues() const { return reinterpret_cast<Object *>(jsObjects + ArrayProtoValues); }
Object *propertyListPrototype() const { return reinterpret_cast<Object *>(jsObjects + PropertyListProto); }
Object *stringPrototype() const { return reinterpret_cast<Object *>(jsObjects + StringProto); }
Object *numberPrototype() const { return reinterpret_cast<Object *>(jsObjects + NumberProto); }
Object *booleanPrototype() const { return reinterpret_cast<Object *>(jsObjects + BooleanProto); }
Object *datePrototype() const { return reinterpret_cast<Object *>(jsObjects + DateProto); }
Object *functionPrototype() const { return reinterpret_cast<Object *>(jsObjects + FunctionProto); }
+ Object *generatorPrototype() const { return reinterpret_cast<Object *>(jsObjects + GeneratorProto); }
Object *regExpPrototype() const { return reinterpret_cast<Object *>(jsObjects + RegExpProto); }
Object *errorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ErrorProto); }
Object *evalErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + EvalErrorProto); }
@@ -247,23 +267,33 @@ public:
Object *typeErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + TypeErrorProto); }
Object *uRIErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + URIErrorProto); }
Object *variantPrototype() const { return reinterpret_cast<Object *>(jsObjects + VariantProto); }
+#if QT_CONFIG(qml_sequence_object)
Object *sequencePrototype() const { return reinterpret_cast<Object *>(jsObjects + SequenceProto); }
+#endif
+ Object *sharedArrayBufferPrototype() const { return reinterpret_cast<Object *>(jsObjects + SharedArrayBufferProto); }
Object *arrayBufferPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayBufferProto); }
Object *dataViewPrototype() const { return reinterpret_cast<Object *>(jsObjects + DataViewProto); }
+ Object *weakSetPrototype() const { return reinterpret_cast<Object *>(jsObjects + WeakSetProto); }
+ Object *setPrototype() const { return reinterpret_cast<Object *>(jsObjects + SetProto); }
+ Object *weakMapPrototype() const { return reinterpret_cast<Object *>(jsObjects + WeakMapProto); }
+ Object *mapPrototype() const { return reinterpret_cast<Object *>(jsObjects + MapProto); }
+ Object *intrinsicTypedArrayPrototype() const { return reinterpret_cast<Object *>(jsObjects + IntrinsicTypedArrayProto); }
Object *typedArrayPrototype;
Object *valueTypeWrapperPrototype() const { return reinterpret_cast<Object *>(jsObjects + ValueTypeProto); }
Object *signalHandlerPrototype() const { return reinterpret_cast<Object *>(jsObjects + SignalHandlerProto); }
+ Object *iteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + IteratorProto); }
+ Object *forInIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ForInIteratorProto); }
+ Object *setIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + SetIteratorProto); }
+ Object *mapIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + MapIteratorProto); }
+ Object *arrayIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayIteratorProto); }
+ Object *stringIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + StringIteratorProto); }
- InternalClassPool *classPool;
EvalFunction *evalFunction() const { return reinterpret_cast<EvalFunction *>(jsObjects + Eval_Function); }
FunctionObject *getStackFunction() const { return reinterpret_cast<FunctionObject *>(jsObjects + GetStack_Function); }
FunctionObject *thrower() const { return reinterpret_cast<FunctionObject *>(jsObjects + ThrowerObject); }
- Property *argumentsAccessors;
- int nArgumentsAccessors;
-
enum JSStrings {
String_Empty,
String_undefined,
@@ -273,6 +303,8 @@ public:
String_boolean,
String_number,
String_string,
+ String_default,
+ String_symbol,
String_object,
String_function,
String_length,
@@ -295,16 +327,46 @@ public:
String_index,
String_input,
String_toString,
+ String_toLocaleString,
String_destroy,
String_valueOf,
String_byteLength,
String_byteOffset,
String_buffer,
String_lastIndex,
+ String_next,
+ String_done,
+ String_return,
+ String_throw,
+ String_global,
+ String_ignoreCase,
+ String_multiline,
+ String_unicode,
+ String_sticky,
+ String_source,
+ String_flags,
+
NJSStrings
};
Value *jsStrings;
+ enum JSSymbols {
+ Symbol_hasInstance,
+ Symbol_isConcatSpreadable,
+ Symbol_iterator,
+ Symbol_match,
+ Symbol_replace,
+ Symbol_search,
+ Symbol_species,
+ Symbol_split,
+ Symbol_toPrimitive,
+ Symbol_toStringTag,
+ Symbol_unscopables,
+ Symbol_revokableProxy,
+ NJSSymbols
+ };
+ Value *jsSymbols;
+
String *id_empty() const { return reinterpret_cast<String *>(jsStrings + String_Empty); }
String *id_undefined() const { return reinterpret_cast<String *>(jsStrings + String_undefined); }
String *id_null() const { return reinterpret_cast<String *>(jsStrings + String_null); }
@@ -313,6 +375,8 @@ public:
String *id_boolean() const { return reinterpret_cast<String *>(jsStrings + String_boolean); }
String *id_number() const { return reinterpret_cast<String *>(jsStrings + String_number); }
String *id_string() const { return reinterpret_cast<String *>(jsStrings + String_string); }
+ String *id_default() const { return reinterpret_cast<String *>(jsStrings + String_default); }
+ String *id_symbol() const { return reinterpret_cast<String *>(jsStrings + String_symbol); }
String *id_object() const { return reinterpret_cast<String *>(jsStrings + String_object); }
String *id_function() const { return reinterpret_cast<String *>(jsStrings + String_function); }
String *id_length() const { return reinterpret_cast<String *>(jsStrings + String_length); }
@@ -335,12 +399,37 @@ public:
String *id_index() const { return reinterpret_cast<String *>(jsStrings + String_index); }
String *id_input() const { return reinterpret_cast<String *>(jsStrings + String_input); }
String *id_toString() const { return reinterpret_cast<String *>(jsStrings + String_toString); }
+ String *id_toLocaleString() const { return reinterpret_cast<String *>(jsStrings + String_toLocaleString); }
String *id_destroy() const { return reinterpret_cast<String *>(jsStrings + String_destroy); }
String *id_valueOf() const { return reinterpret_cast<String *>(jsStrings + String_valueOf); }
String *id_byteLength() const { return reinterpret_cast<String *>(jsStrings + String_byteLength); }
String *id_byteOffset() const { return reinterpret_cast<String *>(jsStrings + String_byteOffset); }
String *id_buffer() const { return reinterpret_cast<String *>(jsStrings + String_buffer); }
String *id_lastIndex() const { return reinterpret_cast<String *>(jsStrings + String_lastIndex); }
+ String *id_next() const { return reinterpret_cast<String *>(jsStrings + String_next); }
+ String *id_done() const { return reinterpret_cast<String *>(jsStrings + String_done); }
+ String *id_return() const { return reinterpret_cast<String *>(jsStrings + String_return); }
+ String *id_throw() const { return reinterpret_cast<String *>(jsStrings + String_throw); }
+ String *id_global() const { return reinterpret_cast<String *>(jsStrings + String_global); }
+ String *id_ignoreCase() const { return reinterpret_cast<String *>(jsStrings + String_ignoreCase); }
+ String *id_multiline() const { return reinterpret_cast<String *>(jsStrings + String_multiline); }
+ String *id_unicode() const { return reinterpret_cast<String *>(jsStrings + String_unicode); }
+ String *id_sticky() const { return reinterpret_cast<String *>(jsStrings + String_sticky); }
+ String *id_source() const { return reinterpret_cast<String *>(jsStrings + String_source); }
+ String *id_flags() const { return reinterpret_cast<String *>(jsStrings + String_flags); }
+
+ Symbol *symbol_hasInstance() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_hasInstance); }
+ Symbol *symbol_isConcatSpreadable() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_isConcatSpreadable); }
+ Symbol *symbol_iterator() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_iterator); }
+ Symbol *symbol_match() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_match); }
+ Symbol *symbol_replace() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_replace); }
+ Symbol *symbol_search() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_search); }
+ Symbol *symbol_species() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_species); }
+ Symbol *symbol_split() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_split); }
+ Symbol *symbol_toPrimitive() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_toPrimitive); }
+ Symbol *symbol_toStringTag() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_toStringTag); }
+ Symbol *symbol_unscopables() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_unscopables); }
+ Symbol *symbol_revokableProxy() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_revokableProxy); }
#ifndef V4_BOOTSTRAP
QIntrusiveList<CompiledData::CompilationUnit, &CompiledData::CompilationUnit::nextCompilationUnit> compilationUnits;
@@ -372,9 +461,9 @@ public:
const bool m_canAllocateExecutableMemory;
#endif
- int internalClassIdCount = 0;
+ quintptr protoIdCount = 1;
- ExecutionEngine();
+ ExecutionEngine(QJSEngine *jsEngine = nullptr);
~ExecutionEngine();
#if !QT_CONFIG(qml_debug)
@@ -391,29 +480,28 @@ public:
void setProfiler(Profiling::Profiler *profiler);
#endif // QT_CONFIG(qml_debug)
- void setCurrentContext(Heap::ExecutionContext *context);
- ExecutionContext *currentContext() const {
- return static_cast<ExecutionContext *>(&currentStackFrame->jsFrame->context);
- }
+ ExecutionContext *currentContext() const;
- int newInternalClassId() { return ++internalClassIdCount; }
+ // ensure we always get odd prototype IDs. This helps make marking in QV4::Lookup fast
+ quintptr newProtoId() { return (protoIdCount += 2); }
- InternalClass *newInternalClass(const VTable *vtable, Object *prototype);
+ Heap::InternalClass *newInternalClass(const VTable *vtable, Object *prototype);
Heap::Object *newObject();
- Heap::Object *newObject(InternalClass *internalClass, Object *prototype);
+ Heap::Object *newObject(Heap::InternalClass *internalClass);
Heap::String *newString(const QString &s = QString());
Heap::String *newIdentifier(const QString &text);
Heap::Object *newStringObject(const String *string);
+ Heap::Object *newSymbolObject(const Symbol *symbol);
Heap::Object *newNumberObject(double value);
Heap::Object *newBooleanObject(bool b);
Heap::ArrayObject *newArrayObject(int count = 0);
Heap::ArrayObject *newArrayObject(const Value *values, int length);
Heap::ArrayObject *newArrayObject(const QStringList &list);
- Heap::ArrayObject *newArrayObject(InternalClass *ic, Object *prototype);
+ Heap::ArrayObject *newArrayObject(Heap::InternalClass *ic);
Heap::ArrayBuffer *newArrayBuffer(const QByteArray &array);
Heap::ArrayBuffer *newArrayBuffer(size_t length);
@@ -437,7 +525,10 @@ public:
Heap::Object *newVariantObject(const QVariant &v);
- Heap::Object *newForEachIteratorObject(Object *o);
+ Heap::Object *newForInIteratorObject(Object *o);
+ Heap::Object *newSetIteratorObject(Object *o);
+ Heap::Object *newMapIteratorObject(Object *o);
+ Heap::Object *newArrayIteratorObject(Object *o);
Heap::QmlContext *qmlContext() const;
QObject *qmlScopeObject() const;
@@ -447,13 +538,11 @@ public:
StackTrace stackTrace(int frameLimit = -1) const;
QUrl resolvedUrl(const QString &file);
- void requireArgumentsAccessors(int n);
-
void markObjects(MarkStack *markStack);
void initRootContext();
- InternalClass *newClass(const InternalClass &other);
+ Heap::InternalClass *newClass(Heap::InternalClass *other);
StackTrace exceptionStackTrace;
@@ -492,7 +581,7 @@ public:
if (!m_canAllocateExecutableMemory)
return false;
if (f)
- return f->interpreterCallCount >= jitCallCountThreshold;
+ return !f->isGenerator() && f->interpreterCallCount >= jitCallCountThreshold;
return true;
#else
Q_UNUSED(f);
@@ -502,6 +591,20 @@ public:
QV4::ReturnedValue global();
+ double localTZA = 0.0; // local timezone, initialized at startup
+
+ static QQmlRefPointer<CompiledData::CompilationUnit> compileModule(bool debugMode, const QString &url, const QString &sourceCode, const QDateTime &sourceTimeStamp, QList<QQmlJS::DiagnosticMessage> *diagnostics);
+#ifndef V4_BOOTSTRAP
+ QQmlRefPointer<CompiledData::CompilationUnit> compileModule(const QUrl &url);
+ QQmlRefPointer<CompiledData::CompilationUnit> compileModule(const QUrl &url, const QString &sourceCode, const QDateTime &sourceTimeStamp);
+
+ mutable QMutex moduleMutex;
+ QHash<QUrl, QQmlRefPointer<CompiledData::CompilationUnit>> modules;
+ void injectModule(const QQmlRefPointer<CompiledData::CompilationUnit> &moduleUnit);
+ QQmlRefPointer<CompiledData::CompilationUnit> moduleForUrl(const QUrl &_url, const CompiledData::CompilationUnit *referrer = nullptr) const;
+ QQmlRefPointer<CompiledData::CompilationUnit> loadModule(const QUrl &_url, const CompiledData::CompilationUnit *referrer = nullptr);
+#endif
+
private:
#if QT_CONFIG(qml_debug)
QScopedPointer<QV4::Debugging::Debugger> m_debugger;
@@ -521,11 +624,6 @@ struct NoThrowEngine;
#endif
-inline void ExecutionEngine::setCurrentContext(Heap::ExecutionContext *context)
-{
- currentStackFrame->jsFrame->context = context;
-}
-
#define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \
ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4);
diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h
index 59fb4a564a..b5cfea8863 100644
--- a/src/qml/jsruntime/qv4enginebase_p.h
+++ b/src/qml/jsruntime/qv4enginebase_p.h
@@ -72,9 +72,8 @@ struct Q_QML_EXPORT EngineBase {
quint8 hasException = false;
quint8 writeBarrierActive = false;
quint16 unused = 0;
-#if QT_POINTER_SIZE == 8
- quint8 padding[4];
-#endif
+ quint8 isExecutingInRegExpJIT = false;
+ quint8 padding[3];
MemoryManager *memoryManager = nullptr;
Runtime runtime;
@@ -88,7 +87,7 @@ struct Q_QML_EXPORT EngineBase {
// Exception handling
Value *exceptionValue = nullptr;
- enum {
+ enum InternalClassType {
Class_Empty,
Class_String,
Class_MemberData,
@@ -96,11 +95,19 @@ struct Q_QML_EXPORT EngineBase {
Class_SparseArrayData,
Class_ExecutionContext,
Class_CallContext,
+ Class_QmlContext,
Class_Object,
Class_ArrayObject,
Class_FunctionObject,
+ Class_ArrowFunction,
+ Class_GeneratorFunction,
+ Class_GeneratorObject,
Class_StringObject,
+ Class_SymbolObject,
Class_ScriptFunction,
+ Class_ConstructorFunction,
+ Class_MemberFunction,
+ Class_MemberGeneratorFunction,
Class_ObjectProto,
Class_RegExp,
Class_RegExpObject,
@@ -111,10 +118,13 @@ struct Q_QML_EXPORT EngineBase {
Class_ErrorObjectWithMessage,
Class_ErrorProto,
Class_QmlContextWrapper,
- Class_QmlContext,
+ Class_ProxyObject,
+ Class_ProxyFunctionObject,
+ Class_Symbol,
NClasses
};
- InternalClass *internalClasses[NClasses];
+ Heap::InternalClass *classes[NClasses];
+ Heap::InternalClass *internalClasses(InternalClassType icType) { return classes[icType]; }
};
#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
#pragma pack(pop)
@@ -124,7 +134,7 @@ Q_STATIC_ASSERT(std::is_standard_layout<EngineBase>::value);
Q_STATIC_ASSERT(offsetof(EngineBase, currentStackFrame) == 0);
Q_STATIC_ASSERT(offsetof(EngineBase, jsStackTop) == offsetof(EngineBase, currentStackFrame) + QT_POINTER_SIZE);
Q_STATIC_ASSERT(offsetof(EngineBase, hasException) == offsetof(EngineBase, jsStackTop) + QT_POINTER_SIZE);
-Q_STATIC_ASSERT(offsetof(EngineBase, memoryManager) == offsetof(EngineBase, hasException) + QT_POINTER_SIZE);
+Q_STATIC_ASSERT(offsetof(EngineBase, memoryManager) == offsetof(EngineBase, hasException) + 8);
Q_STATIC_ASSERT(offsetof(EngineBase, runtime) == offsetof(EngineBase, memoryManager) + QT_POINTER_SIZE);
}
diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp
index 90e158ba37..c6d6c77d11 100644
--- a/src/qml/jsruntime/qv4errorobject.cpp
+++ b/src/qml/jsruntime/qv4errorobject.cpp
@@ -47,10 +47,6 @@
#include "qv4string_p.h"
#include <private/qv4mm_p.h>
-#include <private/qqmljsengine_p.h>
-#include <private/qqmljslexer_p.h>
-#include <private/qqmljsparser_p.h>
-#include <private/qqmljsast_p.h>
#include <qv4codegen_p.h>
#ifndef Q_OS_WIN
@@ -74,13 +70,13 @@ void Heap::ErrorObject::init()
Scope scope(internalClass->engine);
Scoped<QV4::ErrorObject> e(scope, this);
- if (internalClass == scope.engine->internalClasses[EngineBase::Class_ErrorProto])
+ if (internalClass == scope.engine->internalClasses(EngineBase::Class_ErrorProto))
return;
setProperty(scope.engine, QV4::ErrorObject::Index_Stack, scope.engine->getStackFunction()->d());
- setProperty(scope.engine, QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset, Primitive::undefinedValue());
- setProperty(scope.engine, QV4::ErrorObject::Index_FileName, Primitive::undefinedValue());
- setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Primitive::undefinedValue());
+ setProperty(scope.engine, QV4::ErrorObject::Index_StackSetter, Value::undefinedValue());
+ setProperty(scope.engine, QV4::ErrorObject::Index_FileName, Value::undefinedValue());
+ setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Value::undefinedValue());
}
void Heap::ErrorObject::init(const Value &message, ErrorType t)
@@ -92,12 +88,12 @@ void Heap::ErrorObject::init(const Value &message, ErrorType t)
Scoped<QV4::ErrorObject> e(scope, this);
setProperty(scope.engine, QV4::ErrorObject::Index_Stack, scope.engine->getStackFunction()->d());
- setProperty(scope.engine, QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset, Primitive::undefinedValue());
+ setProperty(scope.engine, QV4::ErrorObject::Index_StackSetter, Value::undefinedValue());
e->d()->stackTrace = new StackTrace(scope.engine->stackTrace());
if (!e->d()->stackTrace->isEmpty()) {
setProperty(scope.engine, QV4::ErrorObject::Index_FileName, scope.engine->newString(e->d()->stackTrace->at(0).source));
- setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Primitive::fromInt32(e->d()->stackTrace->at(0).line));
+ setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Value::fromInt32(e->d()->stackTrace->at(0).line));
}
if (!message.isUndefined())
@@ -114,7 +110,7 @@ void Heap::ErrorObject::init(const Value &message, const QString &fileName, int
Scoped<QV4::ErrorObject> e(scope, this);
setProperty(scope.engine, QV4::ErrorObject::Index_Stack, scope.engine->getStackFunction()->d());
- setProperty(scope.engine, QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset, Primitive::undefinedValue());
+ setProperty(scope.engine, QV4::ErrorObject::Index_StackSetter, Value::undefinedValue());
e->d()->stackTrace = new StackTrace(scope.engine->stackTrace());
StackFrame frame;
@@ -125,7 +121,7 @@ void Heap::ErrorObject::init(const Value &message, const QString &fileName, int
Q_ASSERT(!e->d()->stackTrace->isEmpty());
setProperty(scope.engine, QV4::ErrorObject::Index_FileName, scope.engine->newString(e->d()->stackTrace->at(0).source));
- setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Primitive::fromInt32(e->d()->stackTrace->at(0).line));
+ setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Value::fromInt32(e->d()->stackTrace->at(0).line));
if (!message.isUndefined())
setProperty(scope.engine, QV4::ErrorObject::Index_Message, message);
@@ -233,81 +229,81 @@ void Heap::ErrorCtor::init(QV4::ExecutionContext *scope, const QString &name)
Heap::FunctionObject::init(scope, name);
}
-ReturnedValue ErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue ErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
{
- Value v = argc ? *argv : Primitive::undefinedValue();
- return ErrorObject::create<ErrorObject>(f->engine(), v)->asReturnedValue();
+ Value v = argc ? *argv : Value::undefinedValue();
+ return ErrorObject::create<ErrorObject>(f->engine(), v, newTarget)->asReturnedValue();
}
-ReturnedValue ErrorCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
+ReturnedValue ErrorCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
return f->callAsConstructor(argv, argc);
}
void Heap::EvalErrorCtor::init(QV4::ExecutionContext *scope)
{
- Heap::ErrorCtor::init(scope, QStringLiteral("EvalError"));
+ Heap::FunctionObject::init(scope, QStringLiteral("EvalError"));
}
-ReturnedValue EvalErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue EvalErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
{
- Value v = argc ? *argv : Primitive::undefinedValue();
- return ErrorObject::create<EvalErrorObject>(f->engine(), v)->asReturnedValue();
+ Value v = argc ? *argv : Value::undefinedValue();
+ return ErrorObject::create<EvalErrorObject>(f->engine(), v, newTarget)->asReturnedValue();
}
void Heap::RangeErrorCtor::init(QV4::ExecutionContext *scope)
{
- Heap::ErrorCtor::init(scope, QStringLiteral("RangeError"));
+ Heap::FunctionObject::init(scope, QStringLiteral("RangeError"));
}
-ReturnedValue RangeErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue RangeErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
{
- Value v = argc ? *argv : Primitive::undefinedValue();
- return ErrorObject::create<RangeErrorObject>(f->engine(), v)->asReturnedValue();
+ Value v = argc ? *argv : Value::undefinedValue();
+ return ErrorObject::create<RangeErrorObject>(f->engine(), v, newTarget)->asReturnedValue();
}
void Heap::ReferenceErrorCtor::init(QV4::ExecutionContext *scope)
{
- Heap::ErrorCtor::init(scope, QStringLiteral("ReferenceError"));
+ Heap::FunctionObject::init(scope, QStringLiteral("ReferenceError"));
}
-ReturnedValue ReferenceErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue ReferenceErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
{
- Value v = argc ? *argv : Primitive::undefinedValue();
- return ErrorObject::create<ReferenceErrorObject>(f->engine(), v)->asReturnedValue();
+ Value v = argc ? *argv : Value::undefinedValue();
+ return ErrorObject::create<ReferenceErrorObject>(f->engine(), v, newTarget)->asReturnedValue();
}
void Heap::SyntaxErrorCtor::init(QV4::ExecutionContext *scope)
{
- Heap::ErrorCtor::init(scope, QStringLiteral("SyntaxError"));
+ Heap::FunctionObject::init(scope, QStringLiteral("SyntaxError"));
}
-ReturnedValue SyntaxErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue SyntaxErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
{
- Value v = argc ? *argv : Primitive::undefinedValue();
- return ErrorObject::create<SyntaxErrorObject>(f->engine(), v)->asReturnedValue();
+ Value v = argc ? *argv : Value::undefinedValue();
+ return ErrorObject::create<SyntaxErrorObject>(f->engine(), v, newTarget)->asReturnedValue();
}
void Heap::TypeErrorCtor::init(QV4::ExecutionContext *scope)
{
- Heap::ErrorCtor::init(scope, QStringLiteral("TypeError"));
+ Heap::FunctionObject::init(scope, QStringLiteral("TypeError"));
}
-ReturnedValue TypeErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue TypeErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
{
- Value v = argc ? *argv : Primitive::undefinedValue();
- return ErrorObject::create<TypeErrorObject>(f->engine(), v)->asReturnedValue();
+ Value v = argc ? *argv : Value::undefinedValue();
+ return ErrorObject::create<TypeErrorObject>(f->engine(), v, newTarget)->asReturnedValue();
}
void Heap::URIErrorCtor::init(QV4::ExecutionContext *scope)
{
- Heap::ErrorCtor::init(scope, QStringLiteral("URIError"));
+ Heap::FunctionObject::init(scope, QStringLiteral("URIError"));
}
-ReturnedValue URIErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue URIErrorCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
{
- Value v = argc ? *argv : Primitive::undefinedValue();
- return ErrorObject::create<URIErrorObject>(f->engine(), v)->asReturnedValue();
+ Value v = argc ? *argv : Value::undefinedValue();
+ return ErrorObject::create<URIErrorObject>(f->engine(), v, newTarget)->asReturnedValue();
}
void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, Heap::ErrorObject::ErrorType t)
@@ -316,7 +312,7 @@ void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, He
ScopedString s(scope);
ScopedObject o(scope);
ctor->defineReadonlyProperty(engine->id_prototype(), (o = obj));
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
obj->setProperty(Index_Constructor, ctor->d());
obj->setProperty(Index_Message, engine->id_empty()->d());
obj->setProperty(Index_Name, engine->newString(QString::fromLatin1(ErrorObject::className(t))));
diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h
index 6b578e8c38..139bcc9754 100644
--- a/src/qml/jsruntime/qv4errorobject_p.h
+++ b/src/qml/jsruntime/qv4errorobject_p.h
@@ -115,7 +115,7 @@ struct URIErrorObject : ErrorObject {
void init(const Value &message);
};
-struct ErrorCtor : Heap::FunctionObject {
+struct ErrorCtor : FunctionObject {
void init(QV4::ExecutionContext *scope);
void init(QV4::ExecutionContext *scope, const QString &name);
};
@@ -153,6 +153,7 @@ struct ErrorObject: Object {
enum {
Index_Stack = 0, // Accessor Property
+ Index_StackSetter = 1, // Accessor Property
Index_FileName = 2,
Index_LineNumber = 3,
Index_Message = 4
@@ -165,7 +166,7 @@ struct ErrorObject: Object {
V4_NEEDS_DESTROY
template <typename T>
- static Heap::Object *create(ExecutionEngine *e, const Value &message);
+ static Heap::Object *create(ExecutionEngine *e, const Value &message, const Value *newTarget);
template <typename T>
static Heap::Object *create(ExecutionEngine *e, const QString &message);
template <typename T>
@@ -180,7 +181,7 @@ struct ErrorObject: Object {
template<>
inline const ErrorObject *Value::as() const {
- return isManaged() && m()->vtable()->isErrorObject ? reinterpret_cast<const ErrorObject *>(this) : nullptr;
+ return isManaged() && m()->internalClass->vtable->isErrorObject ? reinterpret_cast<const ErrorObject *>(this) : nullptr;
}
struct EvalErrorObject: ErrorObject {
@@ -229,54 +230,60 @@ struct ErrorCtor: FunctionObject
{
V4_OBJECT2(ErrorCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct EvalErrorCtor: ErrorCtor
{
- V4_OBJECT2(EvalErrorCtor, ErrorCtor)
+ V4_OBJECT2(EvalErrorCtor, FunctionObject)
+ V4_PROTOTYPE(errorCtor)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
};
struct RangeErrorCtor: ErrorCtor
{
- V4_OBJECT2(RangeErrorCtor, ErrorCtor)
+ V4_OBJECT2(RangeErrorCtor, FunctionObject)
+ V4_PROTOTYPE(errorCtor)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
};
struct ReferenceErrorCtor: ErrorCtor
{
- V4_OBJECT2(ReferenceErrorCtor, ErrorCtor)
+ V4_OBJECT2(ReferenceErrorCtor, FunctionObject)
+ V4_PROTOTYPE(errorCtor)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
};
struct SyntaxErrorCtor: ErrorCtor
{
- V4_OBJECT2(SyntaxErrorCtor, ErrorCtor)
+ V4_OBJECT2(SyntaxErrorCtor, FunctionObject)
+ V4_PROTOTYPE(errorCtor)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
};
struct TypeErrorCtor: ErrorCtor
{
- V4_OBJECT2(TypeErrorCtor, ErrorCtor)
+ V4_OBJECT2(TypeErrorCtor, FunctionObject)
+ V4_PROTOTYPE(errorCtor)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
};
struct URIErrorCtor: ErrorCtor
{
- V4_OBJECT2(URIErrorCtor, ErrorCtor)
+ V4_OBJECT2(URIErrorCtor, FunctionObject)
+ V4_PROTOTYPE(errorCtor)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
};
-struct ErrorPrototype : ErrorObject
+struct ErrorPrototype : Object
{
enum {
Index_Constructor = 0,
@@ -289,32 +296,32 @@ struct ErrorPrototype : ErrorObject
static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
-struct EvalErrorPrototype : ErrorObject
+struct EvalErrorPrototype : Object
{
void init(ExecutionEngine *engine, Object *ctor) { ErrorPrototype::init(engine, ctor, this, Heap::ErrorObject::EvalError); }
};
-struct RangeErrorPrototype : ErrorObject
+struct RangeErrorPrototype : Object
{
void init(ExecutionEngine *engine, Object *ctor) { ErrorPrototype::init(engine, ctor, this, Heap::ErrorObject::RangeError); }
};
-struct ReferenceErrorPrototype : ErrorObject
+struct ReferenceErrorPrototype : Object
{
void init(ExecutionEngine *engine, Object *ctor) { ErrorPrototype::init(engine, ctor, this, Heap::ErrorObject::ReferenceError); }
};
-struct SyntaxErrorPrototype : ErrorObject
+struct SyntaxErrorPrototype : Object
{
void init(ExecutionEngine *engine, Object *ctor) { ErrorPrototype::init(engine, ctor, this, Heap::ErrorObject::SyntaxError); }
};
-struct TypeErrorPrototype : ErrorObject
+struct TypeErrorPrototype : Object
{
void init(ExecutionEngine *engine, Object *ctor) { ErrorPrototype::init(engine, ctor, this, Heap::ErrorObject::TypeError); }
};
-struct URIErrorPrototype : ErrorObject
+struct URIErrorPrototype : Object
{
void init(ExecutionEngine *engine, Object *ctor) { ErrorPrototype::init(engine, ctor, this, Heap::ErrorObject::URIError); }
};
@@ -327,26 +334,28 @@ inline SyntaxErrorObject *ErrorObject::asSyntaxError()
template <typename T>
-Heap::Object *ErrorObject::create(ExecutionEngine *e, const Value &message) {
- InternalClass *ic = e->internalClasses[message.isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage];
- ic = ic->changePrototype(T::defaultPrototype(e)->d());
- return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), message);
+Heap::Object *ErrorObject::create(ExecutionEngine *e, const Value &message, const Value *newTarget) {
+ EngineBase::InternalClassType klass = message.isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage;
+ Scope scope(e);
+ ScopedObject proto(scope, static_cast<const Object *>(newTarget)->get(scope.engine->id_prototype()));
+ Scoped<InternalClass> ic(scope, e->internalClasses(klass)->changePrototype(proto->d()));
+ return e->memoryManager->allocObject<T>(ic->d(), message);
}
template <typename T>
Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message) {
Scope scope(e);
ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue());
- InternalClass *ic = e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage];
- ic = ic->changePrototype(T::defaultPrototype(e)->d());
- return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), v);
+ EngineBase::InternalClassType klass = v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage;
+ Scoped<InternalClass> ic(scope, e->internalClasses(klass)->changePrototype(T::defaultPrototype(e)->d()));
+ return e->memoryManager->allocObject<T>(ic->d(), v);
}
template <typename T>
Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message, const QString &filename, int line, int column) {
Scope scope(e);
ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue());
- InternalClass *ic = e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage];
- ic = ic->changePrototype(T::defaultPrototype(e)->d());
- return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), v, filename, line, column);
+ EngineBase::InternalClassType klass = v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage;
+ Scoped<InternalClass> ic(scope, e->internalClasses(klass)->changePrototype(T::defaultPrototype(e)->d()));
+ return e->memoryManager->allocObject<T>(ic->d(), v, filename, line, column);
}
diff --git a/src/qml/jsruntime/qv4estable.cpp b/src/qml/jsruntime/qv4estable.cpp
new file mode 100644
index 0000000000..99f6bf6aa0
--- /dev/null
+++ b/src/qml/jsruntime/qv4estable.cpp
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** 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 "qv4estable_p.h"
+#include "qv4object_p.h"
+
+using namespace QV4;
+
+// The ES spec requires that Map/Set be implemented using a data structure that
+// is a little different from most; it requires nonlinear access, and must also
+// preserve the order of insertion of items in a deterministic way.
+//
+// This class implements those requirements, except for fast access: that
+// will be addressed in a followup patch.
+
+ESTable::ESTable()
+ : m_capacity(8)
+{
+ m_keys = (Value*)malloc(m_capacity * sizeof(Value));
+ m_values = (Value*)malloc(m_capacity * sizeof(Value));
+ memset(m_keys, 0, m_capacity);
+ memset(m_values, 0, m_capacity);
+}
+
+ESTable::~ESTable()
+{
+ free(m_keys);
+ free(m_values);
+ m_size = 0;
+ m_capacity = 0;
+ m_keys = nullptr;
+ m_values = nullptr;
+}
+
+void ESTable::markObjects(MarkStack *s, bool isWeakMap)
+{
+ for (uint i = 0; i < m_size; ++i) {
+ if (!isWeakMap)
+ m_keys[i].mark(s);
+ m_values[i].mark(s);
+ }
+}
+
+// Pretends that there's nothing in the table. Doesn't actually free memory, as
+// it will almost certainly be reused again anyway.
+void ESTable::clear()
+{
+ m_size = 0;
+}
+
+// Update the table to contain \a value for a given \a key. The key is
+// normalized, as required by the ES spec.
+void ESTable::set(const Value &key, const Value &value)
+{
+ for (uint i = 0; i < m_size; ++i) {
+ if (m_keys[i].sameValueZero(key)) {
+ m_values[i] = value;
+ return;
+ }
+ }
+
+ if (m_capacity == m_size) {
+ uint oldCap = m_capacity;
+ m_capacity *= 2;
+ m_keys = (Value*)realloc(m_keys, m_capacity * sizeof(Value));
+ m_values = (Value*)realloc(m_values, m_capacity * sizeof(Value));
+ memset(m_keys + oldCap, 0, m_capacity - oldCap);
+ memset(m_values + oldCap, 0, m_capacity - oldCap);
+ }
+
+ Value nk = key;
+ if (nk.isDouble()) {
+ if (nk.doubleValue() == 0 && std::signbit(nk.doubleValue()))
+ nk = Value::fromDouble(+0);
+ }
+
+ m_keys[m_size] = nk;
+ m_values[m_size] = value;
+
+ m_size++;
+}
+
+// Returns true if the table contains \a key, false otherwise.
+bool ESTable::has(const Value &key) const
+{
+ for (uint i = 0; i < m_size; ++i) {
+ if (m_keys[i].sameValueZero(key))
+ return true;
+ }
+
+ return false;
+}
+
+// Fetches the value for the given \a key, and if \a hasValue is passed in,
+// it is set depending on whether or not the given key was found.
+ReturnedValue ESTable::get(const Value &key, bool *hasValue) const
+{
+ for (uint i = 0; i < m_size; ++i) {
+ if (m_keys[i].sameValueZero(key)) {
+ if (hasValue)
+ *hasValue = true;
+ return m_values[i].asReturnedValue();
+ }
+ }
+
+ if (hasValue)
+ *hasValue = false;
+ return Encode::undefined();
+}
+
+// Removes the given \a key from the table
+bool ESTable::remove(const Value &key)
+{
+ bool found = false;
+ uint idx = 0;
+ for (; idx < m_size; ++idx) {
+ if (m_keys[idx].sameValueZero(key)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found == true) {
+ memmove(m_keys + idx, m_keys + idx + 1, (m_size - idx)*sizeof(Value));
+ memmove(m_values + idx, m_values + idx + 1, (m_size - idx)*sizeof(Value));
+ m_size--;
+ }
+ return found;
+}
+
+// Returns the size of the table. Note that the size may not match the underlying allocation.
+uint ESTable::size() const
+{
+ return m_size;
+}
+
+// Retrieves a key and value for a given \a idx, and places them in \a key and
+// \a value. They must be valid pointers.
+void ESTable::iterate(uint idx, Value *key, Value *value)
+{
+ Q_ASSERT(idx < m_size);
+ Q_ASSERT(key);
+ Q_ASSERT(value);
+ *key = m_keys[idx];
+ *value = m_values[idx];
+}
+
+void ESTable::removeUnmarkedKeys()
+{
+ uint idx = 0;
+ uint toIdx = 0;
+ for (; idx < m_size; ++idx) {
+ Q_ASSERT(m_keys[idx].isObject());
+ Object &o = static_cast<Object &>(m_keys[idx]);
+ if (o.d()->isMarked()) {
+ m_keys[toIdx] = m_keys[idx];
+ m_values[toIdx] = m_values[idx];
+ ++toIdx;
+ }
+ }
+ m_size = toIdx;
+}
+
diff --git a/src/qml/jsruntime/qv4estable_p.h b/src/qml/jsruntime/qv4estable_p.h
new file mode 100644
index 0000000000..f54fc37a7b
--- /dev/null
+++ b/src/qml/jsruntime/qv4estable_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** 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$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QV4ESTABLE_P_H
+#define QV4ESTABLE_P_H
+
+#include "qv4value_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4
+{
+
+class ESTable
+{
+public:
+ ESTable();
+ ~ESTable();
+
+ void markObjects(MarkStack *s, bool isWeakMap);
+ void clear();
+ void set(const Value &k, const Value &v);
+ bool has(const Value &k) const;
+ ReturnedValue get(const Value &k, bool *hasValue = nullptr) const;
+ bool remove(const Value &k);
+ uint size() const;
+ void iterate(uint idx, Value *k, Value *v);
+
+ void removeUnmarkedKeys();
+
+private:
+ Value *m_keys = nullptr;
+ Value *m_values = nullptr;
+ uint m_size = 0;
+ uint m_capacity = 0;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp
index 6fca9ecd45..941c37de5b 100644
--- a/src/qml/jsruntime/qv4function.cpp
+++ b/src/qml/jsruntime/qv4function.cpp
@@ -47,32 +47,51 @@
#include <private/qv4mm_p.h>
#include <private/qv4identifiertable_p.h>
#include <assembler/MacroAssemblerCodeRef.h>
+#include <private/qv4vme_moth_p.h>
+#include <private/qqmlglobal_p.h>
QT_BEGIN_NAMESPACE
using namespace QV4;
-Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, Code codePtr)
- : compiledFunction(function)
- , compilationUnit(unit)
- , code(codePtr)
- , codeData(function->code())
+ReturnedValue Function::call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) {
+ ExecutionEngine *engine = context->engine();
+ CppStackFrame frame;
+ frame.init(engine, this, argv, argc);
+ frame.setupJSFrame(engine->jsStackTop, Value::undefinedValue(), context->d(),
+ thisObject ? *thisObject : Value::undefinedValue(),
+ Value::undefinedValue());
+
+ frame.push();
+ engine->jsStackTop += frame.requiredJSStackFrameSize();
+
+ ReturnedValue result = Moth::VME::exec(&frame, engine);
+
+ frame.pop();
+
+ return result;
+}
+
+Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function)
+ : compiledFunction(function)
+ , compilationUnit(unit)
+ , codeData(function->code())
, jittedCode(nullptr)
, codeRef(nullptr)
, hasQmlDependencies(function->hasQmlDependencies())
{
- Q_UNUSED(engine);
-
- internalClass = engine->internalClasses[EngineBase::Class_CallContext];
+ Scope scope(engine);
+ Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext));
// first locals
const quint32_le *localsIndices = compiledFunction->localsTable();
for (quint32 i = 0; i < compiledFunction->nLocals; ++i)
- internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
+ ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
const quint32_le *formalsIndices = compiledFunction->formalsTable();
for (quint32 i = 0; i < compiledFunction->nFormals; ++i)
- internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable);
+ ic = ic->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable);
+ internalClass = ic->d();
nFormals = compiledFunction->nFormals;
}
@@ -110,20 +129,25 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr
}
- internalClass = engine->internalClasses[EngineBase::Class_CallContext];
+ internalClass = engine->internalClasses(EngineBase::Class_CallContext);
// first locals
const quint32_le *localsIndices = compiledFunction->localsTable();
for (quint32 i = 0; i < compiledFunction->nLocals; ++i)
- internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
+ internalClass = internalClass->addMember(engine->identifierTable->asPropertyKey(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
Scope scope(engine);
ScopedString arg(scope);
for (const QString &parameterName : parameterNames) {
- arg = engine->newString(parameterName);
- internalClass = internalClass->addMember(arg, Attr_NotConfigurable);
+ arg = engine->newIdentifier(parameterName);
+ internalClass = internalClass->addMember(arg->propertyKey(), Attr_NotConfigurable);
}
nFormals = parameters.size();
}
+QQmlSourceLocation Function::sourceLocation() const
+{
+ return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column);
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h
index 59a94e5dde..029dd7786b 100644
--- a/src/qml/jsruntime/qv4function_p.h
+++ b/src/qml/jsruntime/qv4function_p.h
@@ -51,10 +51,8 @@
//
#include "qv4global_p.h"
-#include <private/qqmlglobal_p.h>
#include <private/qv4compileddata_p.h>
#include <private/qv4context_p.h>
-#include <private/qv4vme_moth_p.h>
namespace JSC {
class MacroAssemblerCodeRef;
@@ -62,31 +60,30 @@ class MacroAssemblerCodeRef;
QT_BEGIN_NAMESPACE
+struct QQmlSourceLocation;
+
namespace QV4 {
struct Q_QML_EXPORT Function {
const CompiledData::Function *compiledFunction;
CompiledData::CompilationUnit *compilationUnit;
- ReturnedValue call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) {
- return Moth::VME::exec(this, thisObject, argv, argc, context);
- }
+ ReturnedValue call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context);
- typedef ReturnedValue (*Code)(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc);
- Code code;
- const uchar *codeData;
+ const char *codeData;
typedef ReturnedValue (*JittedCode)(CppStackFrame *, ExecutionEngine *);
JittedCode jittedCode;
JSC::MacroAssemblerCodeRef *codeRef;
// first nArguments names in internalClass are the actual arguments
- InternalClass *internalClass;
+ Heap::InternalClass *internalClass;
uint nFormals;
int interpreterCallCount = 0;
bool hasQmlDependencies;
+ bool isEval = false;
- Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, Code codePtr);
+ Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function);
~Function();
// used when dynamically assigning signal handlers (QQmlConnection)
@@ -98,13 +95,11 @@ struct Q_QML_EXPORT Function {
inline QString sourceFile() const { return compilationUnit->fileName(); }
inline QUrl finalUrl() const { return compilationUnit->finalUrl(); }
- inline bool usesArgumentsObject() const { return compiledFunction->flags & CompiledData::Function::UsesArgumentsObject; }
inline bool isStrict() const { return compiledFunction->flags & CompiledData::Function::IsStrict; }
+ inline bool isArrowFunction() const { return compiledFunction->flags & CompiledData::Function::IsArrowFunction; }
+ inline bool isGenerator() const { return compiledFunction->flags & CompiledData::Function::IsGenerator; }
- QQmlSourceLocation sourceLocation() const
- {
- return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column);
- }
+ QQmlSourceLocation sourceLocation() const;
Function *nestedFunction() const
{
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index 83608070ec..93cc55f8ad 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -41,6 +41,7 @@
#include "qv4objectproto_p.h"
#include "qv4stringobject_p.h"
#include "qv4function_p.h"
+#include "qv4symbol_p.h"
#include <private/qv4mm_p.h>
#include "qv4arrayobject_p.h"
@@ -57,6 +58,7 @@
#include "private/qlocale_tools_p.h"
#include "private/qqmlbuiltinfunctions_p.h"
#include <private/qv4jscall_p.h>
+#include <private/qv4vme_moth_p.h>
#include <QtCore/QDebug>
#include <algorithm>
@@ -68,61 +70,65 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(FunctionObject);
-void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name,
- ReturnedValue (*code)(const QV4::FunctionObject *, const Value *thisObject, const Value *argv, int argc))
+void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, VTable::Call call)
{
- jsCall = code;
- jsConstruct = QV4::FunctionObject::callAsConstructor;
+ jsCall = call;
+ jsConstruct = nullptr;
Object::init();
this->scope.set(scope->engine(), scope->d());
Scope s(scope->engine());
ScopedFunctionObject f(s, this);
- f->init(name, false);
+ if (name)
+ f->setName(name);
}
-void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, bool createProto)
+void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name)
{
- jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call;
- jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor;
+ ExecutionEngine *e = scope->engine();
+
+ jsCall = vtable()->call;
+ jsConstruct = vtable()->callAsConstructor;
Object::init();
this->scope.set(scope->engine(), scope->d());
- Scope s(scope->engine());
+ Scope s(e);
ScopedFunctionObject f(s, this);
- f->init(name, createProto);
+ if (name)
+ f->setName(name);
}
-void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function, bool createProto)
+
+
+void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function, QV4::String *n)
{
- jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call;
- jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor;
+ jsCall = vtable()->call;
+ jsConstruct = vtable()->callAsConstructor;
Object::init();
setFunction(function);
this->scope.set(scope->engine(), scope->d());
Scope s(scope->engine());
- ScopedString name(s, function->name());
+ ScopedString name(s, n ? n->d() : function->name());
ScopedFunctionObject f(s, this);
- f->init(name, createProto);
+ if (name)
+ f->setName(name);
}
-void Heap::FunctionObject::init(QV4::ExecutionContext *scope, const QString &name, bool createProto)
+void Heap::FunctionObject::init(QV4::ExecutionContext *scope, const QString &name)
{
Scope valueScope(scope);
ScopedString s(valueScope, valueScope.engine->newString(name));
- init(scope, s, createProto);
+ init(scope, s);
}
void Heap::FunctionObject::init()
{
- jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call;
- jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor;
+ jsCall = vtable()->call;
+ jsConstruct = vtable()->callAsConstructor;
Object::init();
this->scope.set(internalClass->engine, internalClass->engine->rootContext()->d());
- Q_ASSERT(internalClass && internalClass->find(internalClass->engine->id_prototype()) == Index_Prototype);
- setProperty(internalClass->engine, Index_Prototype, Primitive::undefinedValue());
}
void Heap::FunctionObject::setFunction(Function *f)
@@ -139,23 +145,15 @@ void Heap::FunctionObject::destroy()
Object::destroy();
}
-void FunctionObject::init(String *n, bool createProto)
+void FunctionObject::createDefaultPrototypeProperty(uint protoConstructorSlot)
{
- Scope s(internalClass()->engine);
- ScopedValue protectThis(s, this);
+ Scope s(this);
- Q_ASSERT(internalClass() && internalClass()->find(s.engine->id_prototype()) == Heap::FunctionObject::Index_Prototype);
- if (createProto) {
- ScopedObject proto(s, s.engine->newObject(s.engine->internalClasses[EngineBase::Class_ObjectProto], s.engine->objectPrototype()));
- Q_ASSERT(s.engine->internalClasses[EngineBase::Class_ObjectProto]->find(s.engine->id_constructor()) == Heap::FunctionObject::Index_ProtoConstructor);
- proto->setProperty(Heap::FunctionObject::Index_ProtoConstructor, d());
- setProperty(Heap::FunctionObject::Index_Prototype, proto);
- } else {
- setProperty(Heap::FunctionObject::Index_Prototype, Primitive::undefinedValue());
- }
+ Q_ASSERT(s.engine->internalClasses(EngineBase::Class_ObjectProto)->verifyIndex(s.engine->id_constructor()->propertyKey(), protoConstructorSlot));
- if (n)
- defineReadonlyConfigurableProperty(s.engine->id_name(), *n);
+ ScopedObject proto(s, s.engine->newObject(s.engine->internalClasses(EngineBase::Class_ObjectProto)));
+ proto->setProperty(protoConstructorSlot, d());
+ defineDefaultProperty(s.engine->id_prototype(), proto, Attr_NotEnumerable|Attr_NotConfigurable);
}
ReturnedValue FunctionObject::name() const
@@ -163,29 +161,59 @@ ReturnedValue FunctionObject::name() const
return get(scope()->internalClass->engine->id_name());
}
-ReturnedValue FunctionObject::callAsConstructor(const FunctionObject *f, const Value *, int)
+ReturnedValue FunctionObject::virtualCall(const FunctionObject *, const Value *, const Value *, int)
{
- return f->engine()->throwTypeError();
+ return Encode::undefined();
}
-ReturnedValue FunctionObject::call(const FunctionObject *, const Value *, const Value *, int)
+Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function)
{
- return Encode::undefined();
+ if (function->isArrowFunction())
+ return scope->engine()->memoryManager->allocate<ArrowFunction>(scope, function);
+ return scope->engine()->memoryManager->allocate<ScriptFunction>(scope, function);
}
-Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function)
+Heap::FunctionObject *FunctionObject::createConstructorFunction(ExecutionContext *scope, Function *function, Object *homeObject, bool isDerivedConstructor)
+{
+ if (!function) {
+ Heap::DefaultClassConstructorFunction *c = scope->engine()->memoryManager->allocate<DefaultClassConstructorFunction>(scope);
+ c->isDerivedConstructor = isDerivedConstructor;
+ return c;
+ }
+ Heap::ConstructorFunction *c = scope->engine()->memoryManager->allocate<ConstructorFunction>(scope, function);
+ c->homeObject.set(scope->engine(), homeObject->d());
+ c->isDerivedConstructor = isDerivedConstructor;
+ return c;
+}
+
+Heap::FunctionObject *FunctionObject::createMemberFunction(ExecutionContext *scope, Function *function, Object *homeObject, QV4::String *name)
{
- return scope->engine()->memoryManager->allocObject<ScriptFunction>(scope, function);
+ Heap::MemberFunction *m = scope->engine()->memoryManager->allocate<MemberFunction>(scope, function, name);
+ m->homeObject.set(scope->engine(), homeObject->d());
+ return m;
}
-bool FunctionObject::isBinding() const
+Heap::FunctionObject *FunctionObject::createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, VTable::Call code, int argumentCount)
{
- return d()->vtable() == QQmlBindingFunction::staticVTable();
+ Scope scope(engine);
+ ScopedString name(scope, nameOrSymbol);
+ if (!name)
+ name = engine->newString(QChar::fromLatin1('[') + nameOrSymbol->toQString().midRef(1) + QChar::fromLatin1(']'));
+
+ ScopedFunctionObject function(scope, engine->memoryManager->allocate<FunctionObject>(engine->rootContext(), name, code));
+ function->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(argumentCount));
+ return function->d();
}
-bool FunctionObject::isBoundFunction() const
+ReturnedValue FunctionObject::getHomeObject() const
{
- return d()->vtable() == BoundFunction::staticVTable();
+ const MemberFunction *m = as<MemberFunction>();
+ if (m)
+ return m->d()->homeObject->asReturnedValue();
+ const ConstructorFunction *c = as<ConstructorFunction>();
+ if (c)
+ return c->d()->homeObject->asReturnedValue();
+ return Encode::undefined();
}
QQmlSourceLocation FunctionObject::sourceLocation() const
@@ -201,10 +229,8 @@ void Heap::FunctionCtor::init(QV4::ExecutionContext *scope)
}
// 15.3.2
-ReturnedValue FunctionCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+QQmlRefPointer<CompiledData::CompilationUnit> FunctionCtor::parse(ExecutionEngine *engine, const Value *argv, int argc, Type t)
{
- Scope scope(f->engine());
-
QString arguments;
QString body;
if (argc > 0) {
@@ -215,42 +241,58 @@ ReturnedValue FunctionCtor::callAsConstructor(const FunctionObject *f, const Val
}
body = argv[argc - 1].toQString();
}
- if (scope.engine->hasException)
- return Encode::undefined();
+ if (engine->hasException)
+ return nullptr;
- QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1Char('}');
+ QString function = (t == Type_Function ? QLatin1String("function anonymous(") : QLatin1String("function* anonymous(")) + arguments + QLatin1String("\n){") + body + QLatin1String("\n}");
- QQmlJS::Engine ee, *engine = &ee;
- QQmlJS::Lexer lexer(engine);
+ QQmlJS::Engine ee;
+ QQmlJS::Lexer lexer(&ee);
lexer.setCode(function, 1, false);
- QQmlJS::Parser parser(engine);
+ QQmlJS::Parser parser(&ee);
const bool parsed = parser.parseExpression();
- if (!parsed)
- return scope.engine->throwSyntaxError(QLatin1String("Parse error"));
+ if (!parsed) {
+ engine->throwSyntaxError(QLatin1String("Parse error"));
+ return nullptr;
+ }
QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(parser.rootNode());
- if (!fe)
- return scope.engine->throwSyntaxError(QLatin1String("Parse error"));
+ if (!fe) {
+ engine->throwSyntaxError(QLatin1String("Parse error"));
+ return nullptr;
+ }
- Compiler::Module module(scope.engine->debugger() != nullptr);
+ Compiler::Module module(engine->debugger() != nullptr);
Compiler::JSUnitGenerator jsGenerator(&module);
- RuntimeCodegen cg(scope.engine, &jsGenerator, false);
+ RuntimeCodegen cg(engine, &jsGenerator, false);
cg.generateFromFunctionExpression(QString(), function, fe, &module);
- QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = cg.generateCompilationUnit();
- Function *vmf = compilationUnit->linkToEngine(scope.engine);
+ if (engine->hasException)
+ return nullptr;
+
+ return cg.generateCompilationUnit();
+}
+
+ReturnedValue FunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
+{
+ ExecutionEngine *engine = f->engine();
+
+ QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Function);
+ if (engine->hasException)
+ return Encode::undefined();
- ExecutionContext *global = scope.engine->rootContext();
+ Function *vmf = compilationUnit->linkToEngine(engine);
+ ExecutionContext *global = engine->scriptContext();
return Encode(FunctionObject::createScriptFunction(global, vmf));
}
// 15.3.1: This is equivalent to new Function(...)
-ReturnedValue FunctionCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
+ReturnedValue FunctionCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
- return callAsConstructor(f, argv, argc);
+ return virtualCallAsConstructor(f, argv, argc, f);
}
DEFINE_OBJECT_VTABLE(FunctionPrototype);
@@ -265,16 +307,17 @@ void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor)
Scope scope(engine);
ScopedObject o(scope);
- ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
- defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0));
+ defineReadonlyConfigurableProperty(engine->id_name(), *engine->id_empty());
+ defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(0));
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(engine->id_toString(), method_toString, 0);
defineDefaultProperty(QStringLiteral("apply"), method_apply, 2);
defineDefaultProperty(QStringLiteral("call"), method_call, 1);
defineDefaultProperty(QStringLiteral("bind"), method_bind, 1);
-
+ defineDefaultProperty(engine->symbol_hasInstance(), method_hasInstance, 1, Attr_ReadOnly);
}
ReturnedValue FunctionPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -284,7 +327,19 @@ ReturnedValue FunctionPrototype::method_toString(const FunctionObject *b, const
if (!fun)
return v4->throwTypeError();
- return Encode(v4->newString(QStringLiteral("function() { [code] }")));
+ const Scope scope(fun->engine());
+ const ScopedString scopedFunctionName(scope, fun->name());
+ const QString functionName(scopedFunctionName ? scopedFunctionName->toQString() : QString());
+ QString functionAsString = QStringLiteral("function");
+
+ // If fun->name() is empty, then there is no function name
+ // to append because the function is anonymous.
+ if (!functionName.isEmpty())
+ functionAsString.append(QLatin1Char(' ') + functionName);
+
+ functionAsString.append(QStringLiteral("() { [native code] }"));
+
+ return Encode(v4->newString(functionAsString));
}
ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
@@ -304,14 +359,14 @@ ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, cons
uint len = arr->getLength();
Scope scope(v4);
- Value *arguments = v4->jsAlloca(len);
+ Value *arguments = scope.alloc<Scope::Uninitialized>(len);
if (len) {
if (ArgumentsObject::isNonStrictArgumentsObject(arr) && !arr->cast<ArgumentsObject>()->fullyCreated()) {
QV4::ArgumentsObject *a = arr->cast<ArgumentsObject>();
int l = qMin(len, (uint)a->d()->context->argc());
memcpy(arguments, a->d()->context->args(), l*sizeof(Value));
for (quint32 i = l; i < len; ++i)
- arguments[i] = Primitive::undefinedValue();
+ arguments[i] = Value::undefinedValue();
} else if (arr->arrayType() == Heap::ArrayData::Simple && !arr->protoHasArray()) {
auto sad = static_cast<Heap::SimpleArrayData *>(arr->arrayData());
uint alen = sad ? sad->values.size : 0;
@@ -320,10 +375,12 @@ ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, cons
for (uint i = 0; i < alen; ++i)
arguments[i] = sad->data(i);
for (quint32 i = alen; i < len; ++i)
- arguments[i] = Primitive::undefinedValue();
+ arguments[i] = Value::undefinedValue();
} else {
+ // need to init the arguments array, as the get() calls below can have side effects
+ memset(arguments, 0, len*sizeof(Value));
for (quint32 i = 0; i < len; ++i)
- arguments[i] = arr->getIndexed(i);
+ arguments[i] = arr->get(i);
}
}
@@ -352,7 +409,7 @@ ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Valu
if (!target || target->isBinding())
return scope.engine->throwTypeError();
- ScopedValue boundThis(scope, argc ? argv[0] : Primitive::undefinedValue());
+ ScopedValue boundThis(scope, argc ? argv[0] : Value::undefinedValue());
Scoped<MemberData> boundArgs(scope, (Heap::MemberData *)nullptr);
int nArgs = (argc - 1 >= 0) ? argc - 1 : 0;
@@ -383,18 +440,50 @@ ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Valu
return bound->asReturnedValue();
}
+ReturnedValue FunctionPrototype::method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc)
+{
+ if (!argc)
+ return Encode(false);
+ const Object *o = thisObject->as<Object>();
+ if (!o)
+ return Encode(false);
+
+ return Object::virtualInstanceOf(o, argv[0]);
+}
+
DEFINE_OBJECT_VTABLE(ScriptFunction);
-ReturnedValue ScriptFunction::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc)
+ReturnedValue ScriptFunction::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *newTarget)
{
ExecutionEngine *v4 = fo->engine();
const ScriptFunction *f = static_cast<const ScriptFunction *>(fo);
+ Q_ASSERT(newTarget->isFunctionObject());
+ const FunctionObject *nt = static_cast<const FunctionObject *>(newTarget);
Scope scope(v4);
- InternalClass *ic = f->classForConstructor();
+ Scoped<InternalClass> ic(scope);
+ if (nt->d() == f->d()) {
+ ic = f->classForConstructor();
+ } else {
+ ScopedObject o(scope, nt->protoProperty());
+ ic = scope.engine->internalClasses(EngineBase::Class_Object);
+ if (o)
+ ic = ic->changePrototype(o->d());
+ }
ScopedValue thisObject(scope, v4->memoryManager->allocObject<Object>(ic));
- ReturnedValue result = Moth::VME::exec(fo, thisObject, argv, argc);
+ CppStackFrame frame;
+ frame.init(v4, f->function(), argv, argc);
+ frame.setupJSFrame(v4->jsStackTop, *f, f->scope(),
+ thisObject,
+ newTarget ? *newTarget : Value::undefinedValue());
+
+ frame.push();
+ v4->jsStackTop += frame.requiredJSStackFrameSize();
+
+ ReturnedValue result = Moth::VME::exec(&frame, v4);
+
+ frame.pop();
if (Q_UNLIKELY(v4->hasException))
return Encode::undefined();
@@ -403,50 +492,175 @@ ReturnedValue ScriptFunction::callAsConstructor(const FunctionObject *fo, const
return result;
}
-ReturnedValue ScriptFunction::call(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc)
+DEFINE_OBJECT_VTABLE(ArrowFunction);
+
+ReturnedValue ArrowFunction::virtualCall(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc)
{
- return Moth::VME::exec(fo, thisObject, argv, argc);
+ ExecutionEngine *engine = fo->engine();
+ CppStackFrame frame;
+ frame.init(engine, fo->function(), argv, argc, true);
+ frame.setupJSFrame(engine->jsStackTop, *fo, fo->scope(),
+ thisObject ? *thisObject : Value::undefinedValue(),
+ Value::undefinedValue());
+
+ frame.push();
+ engine->jsStackTop += frame.requiredJSStackFrameSize();
+
+ ReturnedValue result;
+
+ do {
+ frame.pendingTailCall = false;
+ result = Moth::VME::exec(&frame, engine);
+ } while (frame.pendingTailCall);
+
+ frame.pop();
+
+ return result;
}
-void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function)
+void Heap::ArrowFunction::init(QV4::ExecutionContext *scope, Function *function, QV4::String *n)
{
FunctionObject::init();
this->scope.set(scope->engine(), scope->d());
setFunction(function);
Q_ASSERT(function);
- Q_ASSERT(function->code);
Scope s(scope);
ScopedFunctionObject f(s, this);
- ScopedString name(s, function->name());
- f->init(name, true);
- Q_ASSERT(internalClass && internalClass->find(s.engine->id_length()) == Index_Length);
- setProperty(s.engine, Index_Length, Primitive::fromInt32(f->formalParameterCount()));
-
- if (function->isStrict()) {
- ScopedProperty pd(s);
- pd->value = s.engine->thrower();
- pd->set = s.engine->thrower();
- f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
- f->insertMember(s.engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
+ ScopedString name(s, n ? n->d() : function->name());
+ if (name)
+ f->setName(name);
+
+ Q_ASSERT(internalClass && internalClass->verifyIndex(s.engine->id_length()->propertyKey(), Index_Length));
+ setProperty(s.engine, Index_Length, Value::fromInt32(int(function->compiledFunction->length)));
+ canBeTailCalled = true;
+}
+
+void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function)
+{
+ ArrowFunction::init(scope, function);
+ Q_ASSERT(!function->isArrowFunction());
+
+ Scope s(scope);
+ ScopedFunctionObject f(s, this);
+ f->createDefaultPrototypeProperty(Heap::FunctionObject::Index_ProtoConstructor);
+}
+
+Heap::InternalClass *ScriptFunction::classForConstructor() const
+{
+ Scope scope(engine());
+ ScopedValue o(scope, protoProperty());
+ if (d()->cachedClassForConstructor && d()->cachedClassForConstructor->prototype == o->heapObject())
+ return d()->cachedClassForConstructor;
+
+ Scoped<InternalClass> ic(scope, engine()->internalClasses(EngineBase::Class_Object));
+ ScopedObject p(scope, o);
+ if (p)
+ ic = ic->changePrototype(p->d());
+ d()->cachedClassForConstructor.set(scope.engine, ic->d());
+
+ return ic->d();
+}
+
+DEFINE_OBJECT_VTABLE(ConstructorFunction);
+
+ReturnedValue ConstructorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
+{
+ const ConstructorFunction *c = static_cast<const ConstructorFunction *>(f);
+ if (!c->d()->isDerivedConstructor)
+ return ScriptFunction::virtualCallAsConstructor(f, argv, argc, newTarget);
+
+ ExecutionEngine *v4 = f->engine();
+
+ CppStackFrame frame;
+ frame.init(v4, f->function(), argv, argc);
+ frame.setupJSFrame(v4->jsStackTop, *f, f->scope(),
+ Value::emptyValue(),
+ newTarget ? *newTarget : Value::undefinedValue());
+
+ frame.push();
+ v4->jsStackTop += frame.requiredJSStackFrameSize();
+
+ ReturnedValue result = Moth::VME::exec(&frame, v4);
+ ReturnedValue thisObject = frame.jsFrame->thisObject.asReturnedValue();
+
+ frame.pop();
+
+ if (Q_UNLIKELY(v4->hasException))
+ return Encode::undefined();
+ else if (Value::fromReturnedValue(result).isObject())
+ return result;
+ else if (!Value::fromReturnedValue(result).isUndefined())
+ return v4->throwTypeError();
+ else if (Value::fromReturnedValue(thisObject).isEmpty()) {
+ Scope scope(v4);
+ ScopedString s(scope, v4->newString(QStringLiteral("this")));
+ return v4->throwReferenceError(s);
}
+ return thisObject;
+}
+
+ReturnedValue ConstructorFunction::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
+{
+ return f->engine()->throwTypeError(QStringLiteral("Cannot call a class constructor without |new|"));
}
-InternalClass *ScriptFunction::classForConstructor() const
+DEFINE_OBJECT_VTABLE(MemberFunction);
+
+DEFINE_OBJECT_VTABLE(DefaultClassConstructorFunction);
+
+ReturnedValue DefaultClassConstructorFunction::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
{
- const Object *o = d()->protoProperty();
- InternalClass *ic = d()->cachedClassForConstructor;
- if (ic && ic->prototype == o->d())
- return ic;
+ const DefaultClassConstructorFunction *c = static_cast<const DefaultClassConstructorFunction *>(f);
+ ExecutionEngine *v4 = f->engine();
+
+ Scope scope(v4);
+
+ if (!c->d()->isDerivedConstructor) {
+ ScopedObject proto(scope, static_cast<const Object *>(newTarget)->get(scope.engine->id_prototype()));
+ ScopedObject c(scope, scope.engine->newObject());
+ c->setPrototypeUnchecked(proto);
+ return c->asReturnedValue();
+ }
+
+ ScopedFunctionObject super(scope, f->getPrototypeOf());
+ Q_ASSERT(super->isFunctionObject());
+
+ CppStackFrame frame;
+ frame.init(v4, nullptr, argv, argc);
+ frame.setupJSFrame(v4->jsStackTop, *f, f->scope(),
+ Value::undefinedValue(),
+ newTarget ? *newTarget : Value::undefinedValue(), argc, argc);
- ic = engine()->internalClasses[EngineBase::Class_Object];
- if (o)
- ic = ic->changePrototype(o->d());
- d()->cachedClassForConstructor = ic;
+ frame.push();
+ v4->jsStackTop += frame.requiredJSStackFrameSize(argc);
- return ic;
+ // Do a super call
+ ReturnedValue result = super->callAsConstructor(argv, argc, newTarget);
+ ReturnedValue thisObject = frame.jsFrame->thisObject.asReturnedValue();
+
+ frame.pop();
+
+ if (Q_UNLIKELY(v4->hasException))
+ return Encode::undefined();
+ else if (Value::fromReturnedValue(result).isObject())
+ return result;
+ else if (!Value::fromReturnedValue(result).isUndefined())
+ return v4->throwTypeError();
+ else if (Value::fromReturnedValue(thisObject).isEmpty()) {
+ Scope scope(v4);
+ ScopedString s(scope, v4->newString(QStringLiteral("this")));
+ return v4->throwReferenceError(s);
+ }
+
+ return thisObject;
+}
+
+ReturnedValue DefaultClassConstructorFunction::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
+{
+ return f->engine()->throwTypeError(QStringLiteral("Cannot call a class constructor without |new|"));
}
DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction);
@@ -462,6 +676,9 @@ void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject
this->boundArgs.set(s.engine, boundArgs ? boundArgs->d() : nullptr);
this->boundThis.set(scope->engine(), boundThis);
+ if (!target->isConstructor())
+ jsConstruct = nullptr;
+
ScopedObject f(s, this);
ScopedValue l(s, target->get(s.engine->id_length()));
@@ -470,7 +687,7 @@ void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject
len -= boundArgs->size();
if (len < 0)
len = 0;
- f->defineReadonlyConfigurableProperty(s.engine->id_length(), Primitive::fromInt32(len));
+ f->defineReadonlyConfigurableProperty(s.engine->id_length(), Value::fromInt32(len));
ScopedProperty pd(s);
pd->value = s.engine->thrower();
@@ -479,7 +696,7 @@ void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject
f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
}
-ReturnedValue BoundFunction::call(const FunctionObject *fo, const Value *, const Value *argv, int argc)
+ReturnedValue BoundFunction::virtualCall(const FunctionObject *fo, const Value *, const Value *argv, int argc)
{
const BoundFunction *f = static_cast<const BoundFunction *>(fo);
Scope scope(f->engine());
@@ -500,7 +717,7 @@ ReturnedValue BoundFunction::call(const FunctionObject *fo, const Value *, const
return target->call(jsCallData);
}
-ReturnedValue BoundFunction::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc)
+ReturnedValue BoundFunction::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *)
{
const BoundFunction *f = static_cast<const BoundFunction *>(fo);
Scope scope(f->engine());
diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h
index 32e71a175b..e03d49c74d 100644
--- a/src/qml/jsruntime/qv4functionobject_p.h
+++ b/src/qml/jsruntime/qv4functionobject_p.h
@@ -64,29 +64,32 @@ namespace QV4 {
struct IndexedBuiltinFunction;
struct JSCallData;
-typedef ReturnedValue (*jsCallFunction)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
-typedef ReturnedValue (*jsConstructFunction)(const FunctionObject *, const Value *argv, int argc);
-
namespace Heap {
#define FunctionObjectMembers(class, Member) \
Member(class, Pointer, ExecutionContext *, scope) \
Member(class, NoMark, Function *, function) \
- Member(class, NoMark, jsCallFunction, jsCall) \
- Member(class, NoMark, jsConstructFunction, jsConstruct)
+ Member(class, NoMark, VTable::Call, jsCall) \
+ Member(class, NoMark, VTable::CallAsConstructor, jsConstruct) \
+ Member(class, NoMark, bool, canBeTailCalled)
DECLARE_HEAP_OBJECT(FunctionObject, Object) {
DECLARE_MARKOBJECTS(FunctionObject);
enum {
+ Index_ProtoConstructor = 0,
Index_Prototype = 0,
- Index_ProtoConstructor = 0
+ Index_HasInstance = 1,
};
- Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(const QV4::FunctionObject *, const Value *thisObject, const Value *argv, int argc));
- void init(QV4::ExecutionContext *scope, QV4::String *name = nullptr, bool createProto = false);
- void init(QV4::ExecutionContext *scope, QV4::Function *function, bool createProto = false);
- void init(QV4::ExecutionContext *scope, const QString &name, bool createProto = false);
+ bool isConstructor() const {
+ return jsConstruct != nullptr;
+ }
+
+ Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name, VTable::Call call);
+ void init(QV4::ExecutionContext *scope, QV4::String *name = nullptr);
+ void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr);
+ void init(QV4::ExecutionContext *scope, const QString &name);
void init();
void destroy();
@@ -94,8 +97,6 @@ DECLARE_HEAP_OBJECT(FunctionObject, Object) {
unsigned int formalParameterCount() { return function ? function->nFormals : 0; }
unsigned int varCount() { return function ? function->compiledFunction->nLocals : 0; }
-
- const QV4::Object *protoProperty() const { return propertyData(Index_Prototype)->as<QV4::Object>(); }
};
struct FunctionCtor : FunctionObject {
@@ -107,18 +108,48 @@ struct FunctionPrototype : FunctionObject {
};
struct IndexedBuiltinFunction : FunctionObject {
- inline void init(QV4::ExecutionContext *scope, uint index, ReturnedValue (*code)(const QV4::FunctionObject *, const Value *, const Value *, int));
+ inline void init(QV4::ExecutionContext *scope, uint index, VTable::Call call);
uint index;
};
-struct ScriptFunction : FunctionObject {
+struct ArrowFunction : FunctionObject {
enum {
- Index_Name = FunctionObject::Index_Prototype + 1,
+ Index_Name = Index_HasInstance + 1,
Index_Length
};
+ void init(QV4::ExecutionContext *scope, Function *function, QV4::String *name = nullptr);
+};
+
+#define ScriptFunctionMembers(class, Member) \
+ Member(class, Pointer, InternalClass *, cachedClassForConstructor)
+
+DECLARE_HEAP_OBJECT(ScriptFunction, ArrowFunction) {
+ DECLARE_MARKOBJECTS(ScriptFunction)
void init(QV4::ExecutionContext *scope, Function *function);
+};
+
+#define MemberFunctionMembers(class, Member) \
+ Member(class, Pointer, Object *, homeObject)
+
+DECLARE_HEAP_OBJECT(MemberFunction, ArrowFunction) {
+ DECLARE_MARKOBJECTS(MemberFunction)
- QV4::InternalClass *cachedClassForConstructor;
+ void init(QV4::ExecutionContext *scope, Function *function, QV4::String *name = nullptr) {
+ ArrowFunction::init(scope, function, name);
+ }
+};
+
+#define ConstructorFunctionMembers(class, Member) \
+ Member(class, Pointer, Object *, homeObject)
+
+DECLARE_HEAP_OBJECT(ConstructorFunction, ScriptFunction) {
+ DECLARE_MARKOBJECTS(ConstructorFunction)
+ bool isDerivedConstructor;
+};
+
+struct DefaultClassConstructorFunction : FunctionObject
+{
+ bool isDerivedConstructor;
};
#define BoundFunctionMembers(class, Member) \
@@ -145,6 +176,7 @@ struct Q_QML_EXPORT FunctionObject: Object {
V4_NEEDS_DESTROY
enum { NInlineProperties = 1 };
+ bool canBeTailCalled() const { return d()->canBeTailCalled; }
Heap::ExecutionContext *scope() const { return d()->scope; }
Function *function() const { return d()->function; }
@@ -152,36 +184,52 @@ struct Q_QML_EXPORT FunctionObject: Object {
unsigned int formalParameterCount() const { return d()->formalParameterCount(); }
unsigned int varCount() const { return d()->varCount(); }
- void init(String *name, bool createProto);
+ void setName(String *name) {
+ defineReadonlyConfigurableProperty(engine()->id_name(), *name);
+ }
+ void createDefaultPrototypeProperty(uint protoConstructorSlot);
inline ReturnedValue callAsConstructor(const JSCallData &data) const;
- ReturnedValue callAsConstructor(const Value *argv, int argc) const {
- return d()->jsConstruct(this, argv, argc);
+ ReturnedValue callAsConstructor(const Value *argv, int argc, const Value *newTarget = nullptr) const {
+ if (!d()->jsConstruct)
+ return engine()->throwTypeError(QStringLiteral("Function is not a constructor."));
+ return d()->jsConstruct(this, argv, argc, newTarget ? newTarget : this);
}
inline ReturnedValue call(const JSCallData &data) const;
ReturnedValue call(const Value *thisObject, const Value *argv, int argc) const {
+ if (!d()->jsCall)
+ return engine()->throwTypeError(QStringLiteral("Function can only be called with |new|."));
return d()->jsCall(this, thisObject, argv, argc);
}
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function);
- static Heap::FunctionObject *createBuiltinFunction(ExecutionContext *scope, String *name,
- ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc))
- {
- return scope->engine()->memoryManager->allocObject<FunctionObject>(scope, name, code);
- }
+ static Heap::FunctionObject *createConstructorFunction(ExecutionContext *scope, Function *function, Object *homeObject, bool isDerivedConstructor);
+ static Heap::FunctionObject *createMemberFunction(ExecutionContext *scope, Function *function, Object *homeObject, String *name);
+ static Heap::FunctionObject *createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, VTable::Call code, int argumentCount);
bool strictMode() const { return d()->function ? d()->function->isStrict() : false; }
bool isBinding() const;
bool isBoundFunction() const;
+ bool isConstructor() const {
+ return d()->isConstructor();
+ }
+
+ ReturnedValue getHomeObject() const;
+
+ ReturnedValue protoProperty() const {
+ return getValueByIndex(Heap::FunctionObject::Index_Prototype);
+ }
+ bool hasHasInstanceProperty() const {
+ return !internalClass()->propertyData.at(Heap::FunctionObject::Index_HasInstance).isEmpty();
+ }
QQmlSourceLocation sourceLocation() const;
};
template<>
inline const FunctionObject *Value::as() const {
- return isManaged() && m()->vtable()->isFunctionObject ? reinterpret_cast<const FunctionObject *>(this) : nullptr;
+ return isManaged() && m()->internalClass->vtable->isFunctionObject ? reinterpret_cast<const FunctionObject *>(this) : nullptr;
}
@@ -189,8 +237,14 @@ struct FunctionCtor: FunctionObject
{
V4_OBJECT2(FunctionCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+protected:
+ enum Type {
+ Type_Function,
+ Type_Generator
+ };
+ static QQmlRefPointer<CompiledData::CompilationUnit> parse(ExecutionEngine *engine, const Value *argv, int argc, Type t = Type_Function);
};
struct FunctionPrototype: FunctionObject
@@ -203,6 +257,7 @@ struct FunctionPrototype: FunctionObject
static ReturnedValue method_apply(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_bind(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
struct IndexedBuiltinFunction : FunctionObject
@@ -210,43 +265,70 @@ struct IndexedBuiltinFunction : FunctionObject
V4_OBJECT2(IndexedBuiltinFunction, FunctionObject)
};
-void Heap::IndexedBuiltinFunction::init(QV4::ExecutionContext *scope, uint index,
- ReturnedValue (*code)(const QV4::FunctionObject *, const Value *thisObject, const Value *argv, int argc))
+void Heap::IndexedBuiltinFunction::init(QV4::ExecutionContext *scope, uint index, VTable::Call call)
{
Heap::FunctionObject::init(scope);
- this->jsCall = code;
+ this->jsCall = call;
this->index = index;
}
+struct ArrowFunction : FunctionObject {
+ V4_OBJECT2(ArrowFunction, FunctionObject)
+ V4_INTERNALCLASS(ArrowFunction)
+ enum { NInlineProperties = 3 };
-struct ScriptFunction : FunctionObject {
- V4_OBJECT2(ScriptFunction, FunctionObject)
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct ScriptFunction : ArrowFunction {
+ V4_OBJECT2(ScriptFunction, ArrowFunction)
V4_INTERNALCLASS(ScriptFunction)
- enum { NInlineProperties = 3 };
- static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *);
- InternalClass *classForConstructor() const;
+ Heap::InternalClass *classForConstructor() const;
};
+struct MemberFunction : ArrowFunction {
+ V4_OBJECT2(MemberFunction, ArrowFunction)
+ V4_INTERNALCLASS(MemberFunction)
+};
+
+struct ConstructorFunction : ScriptFunction {
+ V4_OBJECT2(ConstructorFunction, ScriptFunction)
+ V4_INTERNALCLASS(ConstructorFunction)
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct DefaultClassConstructorFunction : FunctionObject {
+ V4_OBJECT2(DefaultClassConstructorFunction, FunctionObject)
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
struct BoundFunction: FunctionObject {
V4_OBJECT2(BoundFunction, FunctionObject)
static Heap::BoundFunction *create(ExecutionContext *scope, FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs)
{
- return scope->engine()->memoryManager->allocObject<BoundFunction>(scope, target, boundThis, boundArgs);
+ return scope->engine()->memoryManager->allocate<BoundFunction>(scope, target, boundThis, boundArgs);
}
Heap::FunctionObject *target() const { return d()->target; }
Value boundThis() const { return d()->boundThis; }
Heap::MemberData *boundArgs() const { return d()->boundArgs; }
- static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
+inline bool FunctionObject::isBoundFunction() const
+{
+ return d()->vtable() == BoundFunction::staticVTable();
+}
+
+
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp
new file mode 100644
index 0000000000..da87127e08
--- /dev/null
+++ b/src/qml/jsruntime/qv4generatorobject.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 <qv4generatorobject_p.h>
+#include <qv4symbol_p.h>
+#include <qv4iterator_p.h>
+#include <qv4jscall_p.h>
+#include <qv4vme_moth_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(GeneratorFunctionCtor);
+DEFINE_OBJECT_VTABLE(GeneratorFunction);
+DEFINE_OBJECT_VTABLE(GeneratorObject);
+
+void Heap::GeneratorFunctionCtor::init(QV4::ExecutionContext *scope)
+{
+ Heap::FunctionObject::init(scope, QStringLiteral("GeneratorFunction"));
+}
+
+ReturnedValue GeneratorFunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
+{
+ ExecutionEngine *engine = f->engine();
+
+ QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Generator);
+ if (engine->hasException)
+ return Encode::undefined();
+
+ Function *vmf = compilationUnit->linkToEngine(engine);
+ ExecutionContext *global = engine->scriptContext();
+ return Encode(GeneratorFunction::create(global, vmf));
+}
+
+// 15.3.1: This is equivalent to new Function(...)
+ReturnedValue GeneratorFunctionCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ return virtualCallAsConstructor(f, argv, argc, f);
+}
+
+Heap::FunctionObject *GeneratorFunction::create(ExecutionContext *context, Function *function)
+{
+ Scope scope(context);
+ Scoped<GeneratorFunction> g(scope, context->engine()->memoryManager->allocate<GeneratorFunction>(context, function));
+ ScopedObject proto(scope, scope.engine->newObject());
+ proto->setPrototypeOf(scope.engine->generatorPrototype());
+ g->defineDefaultProperty(scope.engine->id_prototype(), proto, Attr_NotConfigurable|Attr_NotEnumerable);
+ g->setPrototypeOf(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype())));
+ return g->d();
+}
+
+ReturnedValue GeneratorFunction::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ const GeneratorFunction *gf = static_cast<const GeneratorFunction *>(f);
+ Function *function = gf->function();
+ ExecutionEngine *engine = gf->engine();
+
+ // We need to set up a separate stack for the generator, as it's being re-entered
+ uint stackSize = argc // space for the original arguments
+ + CppStackFrame::requiredJSStackFrameSize(function); // space for the JS stack frame
+
+ size_t requiredMemory = sizeof(GeneratorObject::Data) - sizeof(Value) + sizeof(Value) * stackSize;
+
+ Scope scope(gf);
+ Scoped<GeneratorObject> g(scope, scope.engine->memoryManager->allocManaged<GeneratorObject>(requiredMemory, scope.engine->classes[EngineBase::Class_GeneratorObject]));
+ g->setPrototypeOf(ScopedObject(scope, gf->get(scope.engine->id_prototype())));
+
+ Heap::GeneratorObject *gp = g->d();
+ gp->stack.size = stackSize;
+ gp->stack.alloc = stackSize;
+
+ // copy original arguments
+ memcpy(gp->stack.values, argv, argc*sizeof(Value));
+ gp->cppFrame.init(engine, function, gp->stack.values, argc);
+ gp->cppFrame.setupJSFrame(&gp->stack.values[argc], *gf, gf->scope(),
+ thisObject ? *thisObject : Value::undefinedValue(),
+ Value::undefinedValue());
+
+ gp->cppFrame.push();
+
+ Moth::VME::interpret(&gp->cppFrame, engine, function->codeData);
+ gp->state = GeneratorState::SuspendedStart;
+
+ gp->cppFrame.pop();
+ return g->asReturnedValue();
+}
+
+
+void Heap::GeneratorPrototype::init()
+{
+ Heap::FunctionObject::init();
+}
+
+
+void GeneratorPrototype::init(ExecutionEngine *engine, Object *ctor)
+{
+ Scope scope(engine);
+ ScopedValue v(scope);
+
+ ScopedObject ctorProto(scope, engine->newObject(engine->newInternalClass(Object::staticVTable(), engine->functionPrototype())));
+
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
+ ctor->defineReadonlyProperty(engine->id_prototype(), ctorProto);
+
+ ctorProto->defineDefaultProperty(QStringLiteral("constructor"), (v = ctor), Attr_ReadOnly_ButConfigurable);
+ ctorProto->defineDefaultProperty(engine->symbol_toStringTag(), (v = engine->newIdentifier(QStringLiteral("GeneratorFunction"))), Attr_ReadOnly_ButConfigurable);
+ ctorProto->defineDefaultProperty(engine->id_prototype(), (v = this), Attr_ReadOnly_ButConfigurable);
+
+ setPrototypeOf(engine->iteratorPrototype());
+ defineDefaultProperty(QStringLiteral("constructor"), ctorProto, Attr_ReadOnly_ButConfigurable);
+ defineDefaultProperty(QStringLiteral("next"), method_next, 1);
+ defineDefaultProperty(QStringLiteral("return"), method_return, 1);
+ defineDefaultProperty(QStringLiteral("throw"), method_throw, 1);
+ defineDefaultProperty(engine->symbol_toStringTag(), (v = engine->newString(QStringLiteral("Generator"))), Attr_ReadOnly_ButConfigurable);
+}
+
+ReturnedValue GeneratorPrototype::method_next(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *engine = f->engine();
+ const GeneratorObject *g = thisObject->as<GeneratorObject>();
+ if (!g || g->d()->state == GeneratorState::Executing)
+ return engine->throwTypeError();
+ Heap::GeneratorObject *gp = g->d();
+
+ if (gp->state == GeneratorState::Completed)
+ return IteratorPrototype::createIterResultObject(engine, Value::undefinedValue(), true);
+
+ return g->resume(engine, argc ? argv[0] : Value::undefinedValue());
+}
+
+ReturnedValue GeneratorPrototype::method_return(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *engine = f->engine();
+ const GeneratorObject *g = thisObject->as<GeneratorObject>();
+ if (!g || g->d()->state == GeneratorState::Executing)
+ return engine->throwTypeError();
+
+ Heap::GeneratorObject *gp = g->d();
+
+ if (gp->state == GeneratorState::SuspendedStart)
+ gp->state = GeneratorState::Completed;
+
+ if (gp->state == GeneratorState::Completed)
+ return IteratorPrototype::createIterResultObject(engine, argc ? argv[0] : Value::undefinedValue(), true);
+
+ // the bytecode interpreter interprets an exception with empty value as
+ // a yield called with return()
+ engine->throwError(Value::emptyValue());
+
+ return g->resume(engine, argc ? argv[0]: Value::undefinedValue());
+}
+
+ReturnedValue GeneratorPrototype::method_throw(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *engine = f->engine();
+ const GeneratorObject *g = thisObject->as<GeneratorObject>();
+ if (!g || g->d()->state == GeneratorState::Executing)
+ return engine->throwTypeError();
+
+ Heap::GeneratorObject *gp = g->d();
+
+ engine->throwError(argc ? argv[0]: Value::undefinedValue());
+
+ if (gp->state == GeneratorState::SuspendedStart || gp->state == GeneratorState::Completed) {
+ gp->state = GeneratorState::Completed;
+ return Encode::undefined();
+ }
+
+ return g->resume(engine, Value::undefinedValue());
+}
+
+ReturnedValue GeneratorObject::resume(ExecutionEngine *engine, const Value &arg) const
+{
+ Heap::GeneratorObject *gp = d();
+ gp->state = GeneratorState::Executing;
+ gp->cppFrame.parent = engine->currentStackFrame;
+ engine->currentStackFrame = &gp->cppFrame;
+
+ Q_ASSERT(gp->cppFrame.yield != nullptr);
+ const char *code = gp->cppFrame.yield;
+ gp->cppFrame.yield = nullptr;
+ gp->cppFrame.jsFrame->accumulator = arg;
+ gp->cppFrame.yieldIsIterator = false;
+
+ Scope scope(engine);
+ ScopedValue result(scope, Moth::VME::interpret(&gp->cppFrame, engine, code));
+
+ engine->currentStackFrame = gp->cppFrame.parent;
+
+ bool done = (gp->cppFrame.yield == nullptr);
+ gp->state = done ? GeneratorState::Completed : GeneratorState::SuspendedYield;
+ if (engine->hasException)
+ return Encode::undefined();
+ if (gp->cppFrame.yieldIsIterator)
+ return result->asReturnedValue();
+ return IteratorPrototype::createIterResultObject(engine, result, done);
+}
+
+DEFINE_OBJECT_VTABLE(MemberGeneratorFunction);
+
+Heap::FunctionObject *MemberGeneratorFunction::create(ExecutionContext *context, Function *function, Object *homeObject, String *name)
+{
+ Scope scope(context);
+ Scoped<MemberGeneratorFunction> g(scope, context->engine()->memoryManager->allocate<MemberGeneratorFunction>(context, function, name));
+ g->d()->homeObject.set(scope.engine, homeObject->d());
+ ScopedObject proto(scope, scope.engine->newObject());
+ proto->setPrototypeOf(scope.engine->generatorPrototype());
+ g->defineDefaultProperty(scope.engine->id_prototype(), proto, Attr_NotConfigurable|Attr_NotEnumerable);
+ g->setPrototypeOf(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype())));
+ return g->d();
+}
+
+ReturnedValue MemberGeneratorFunction::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ return GeneratorFunction::virtualCall(f, thisObject, argv, argc);
+}
diff --git a/src/qml/jsruntime/qv4generatorobject_p.h b/src/qml/jsruntime/qv4generatorobject_p.h
new file mode 100644
index 0000000000..366319723d
--- /dev/null
+++ b/src/qml/jsruntime/qv4generatorobject_p.h
@@ -0,0 +1,151 @@
+/****************************************************************************
+**
+** 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 QV4GENERATOROBJECT_P_H
+#define QV4GENERATOROBJECT_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 "qv4functionobject_p.h"
+#include "qv4stackframe_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+enum class GeneratorState {
+ Undefined,
+ SuspendedStart,
+ SuspendedYield,
+ Executing,
+ Completed
+};
+
+namespace Heap {
+
+struct GeneratorFunctionCtor : FunctionObject {
+ void init(QV4::ExecutionContext *scope);
+};
+
+struct GeneratorFunction : ArrowFunction {
+ void init(QV4::ExecutionContext *scope, Function *function, QV4::String *name = nullptr) {
+ ArrowFunction::init(scope, function, name);
+ }
+};
+
+struct MemberGeneratorFunction : MemberFunction {
+};
+
+struct GeneratorPrototype : FunctionObject {
+ void init();
+};
+
+#define GeneratorObjectMembers(class, Member) \
+ Member(class, Pointer, ExecutionContext *, context) \
+ Member(class, Pointer, GeneratorFunction *, function) \
+ Member(class, NoMark, GeneratorState, state) \
+ Member(class, NoMark, CppStackFrame, cppFrame) \
+ Member(class, ValueArray, ValueArray, stack)
+
+DECLARE_HEAP_OBJECT(GeneratorObject, Object) {
+ DECLARE_MARKOBJECTS(GeneratorObject);
+};
+
+}
+
+struct GeneratorFunctionCtor : FunctionCtor
+{
+ V4_OBJECT2(GeneratorFunctionCtor, FunctionCtor)
+
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct GeneratorFunction : ArrowFunction
+{
+ V4_OBJECT2(GeneratorFunction, ArrowFunction)
+ V4_INTERNALCLASS(GeneratorFunction)
+
+ static Heap::FunctionObject *create(ExecutionContext *scope, Function *function);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct MemberGeneratorFunction : MemberFunction
+{
+ V4_OBJECT2(MemberGeneratorFunction, MemberFunction)
+ V4_INTERNALCLASS(MemberGeneratorFunction)
+
+ static Heap::FunctionObject *create(ExecutionContext *scope, Function *function, Object *homeObject, String *name);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct GeneratorPrototype : Object
+{
+ void init(ExecutionEngine *engine, Object *ctor);
+
+ static ReturnedValue method_next(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_return(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_throw(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+
+struct GeneratorObject : Object {
+ V4_OBJECT2(GeneratorObject, Object)
+ Q_MANAGED_TYPE(GeneratorObject)
+ V4_INTERNALCLASS(GeneratorObject)
+ V4_PROTOTYPE(generatorPrototype)
+
+ ReturnedValue resume(ExecutionEngine *engine, const Value &arg) const;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4GENERATORFUNCTION_P_H
+
diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h
index 1fa4bae049..eab519720f 100644
--- a/src/qml/jsruntime/qv4global_p.h
+++ b/src/qml/jsruntime/qv4global_p.h
@@ -153,6 +153,11 @@ namespace Compiler {
struct Module;
struct Context;
struct JSUnitGenerator;
+ class Codegen;
+}
+
+namespace Moth {
+ class BytecodeGenerator;
}
namespace Heap {
@@ -160,13 +165,17 @@ namespace Heap {
struct MemberData;
struct ArrayData;
+ struct StringOrSymbol;
struct String;
+ struct Symbol;
struct Object;
struct ObjectPrototype;
struct ExecutionContext;
struct CallContext;
+ struct QmlContext;
struct ScriptFunction;
+ struct InternalClass;
struct BooleanObject;
struct NumberObject;
@@ -181,21 +190,30 @@ namespace Heap {
struct RegExp;
struct EvalFunction;
+ struct SharedArrayBuffer;
struct ArrayBuffer;
struct DataView;
struct TypedArray;
+ struct MapObject;
+ struct SetObject;
+
template <typename T, size_t> struct Pointer;
}
+struct CppStackFrame;
class MemoryManager;
class ExecutableAllocator;
+struct PropertyKey;
+struct StringOrSymbol;
struct String;
+struct Symbol;
struct Object;
struct ObjectPrototype;
struct ObjectIterator;
struct ExecutionContext;
struct CallContext;
+struct QmlContext;
struct ScriptFunction;
struct InternalClass;
struct Property;
@@ -222,10 +240,14 @@ struct RegExpObject;
struct RegExp;
struct EvalFunction;
+struct SharedArrayBuffer;
struct ArrayBuffer;
struct DataView;
struct TypedArray;
+struct MapObject;
+struct SetMapObject;
+
// ReturnedValue is used to return values from runtime methods
// the type has to be a primitive type (no struct or union), so that the compiler
// will return it in a register on all platforms.
@@ -236,6 +258,7 @@ struct Scope;
struct ScopedValue;
template<typename T> struct Scoped;
typedef Scoped<String> ScopedString;
+typedef Scoped<StringOrSymbol> ScopedStringOrSymbol;
typedef Scoped<Object> ScopedObject;
typedef Scoped<ArrayObject> ScopedArrayObject;
typedef Scoped<FunctionObject> ScopedFunctionObject;
@@ -244,6 +267,7 @@ typedef Scoped<ExecutionContext> ScopedContext;
struct PersistentValueStorage;
class PersistentValue;
class WeakValue;
+struct MarkStack;
struct IdentifierTable;
class RegExpCache;
@@ -340,6 +364,7 @@ struct PropertyAttributes
bool isEmpty() const { return !m_all; }
uint flags() const { return m_flags; }
+ uint all() const { return m_all; }
bool operator==(PropertyAttributes other) {
return m_all == other.m_all;
@@ -357,7 +382,23 @@ struct Q_QML_EXPORT StackFrame {
};
typedef QVector<StackFrame> StackTrace;
-}
+enum class ObjectLiteralArgument {
+ Value,
+ Method,
+ Getter,
+ Setter
+};
+
+namespace JIT {
+
+enum class CallResultDestination {
+ Ignore,
+ InAccumulator,
+};
+
+} // JIT namespace
+
+} // QV4 namespace
Q_DECLARE_TYPEINFO(QV4::PropertyAttributes, Q_PRIMITIVE_TYPE);
diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp
index f419ab53fe..becdc3bc55 100644
--- a/src/qml/jsruntime/qv4globalobject.cpp
+++ b/src/qml/jsruntime/qv4globalobject.cpp
@@ -335,7 +335,7 @@ void Heap::EvalFunction::init(QV4::ExecutionContext *scope)
Scope s(scope);
Heap::FunctionObject::init(scope, s.engine->id_eval());
ScopedFunctionObject f(s, this);
- f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(1));
+ f->defineReadonlyConfigurableProperty(s.engine->id_length(), Value::fromInt32(1));
}
ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc, bool directCall) const
@@ -351,7 +351,7 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc,
if (!directCall) {
// the context for eval should be the global scope
- ctx = v4->rootContext();
+ ctx = v4->scriptContext();
}
String *scode = argv[0].stringValue();
@@ -361,7 +361,7 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc,
const QString code = scode->toQString();
bool inheritContext = !isStrict;
- Script script(ctx, QV4::Compiler::EvalCode, code, QStringLiteral("eval code"));
+ Script script(ctx, QV4::Compiler::ContextType::Eval, code, QStringLiteral("eval code"));
script.strictMode = (directCall && isStrict);
script.inheritContext = inheritContext;
script.parse();
@@ -371,6 +371,7 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc,
Function *function = script.function();
if (!function)
return Encode::undefined();
+ function->isEval = true;
if (function->isStrict() || isStrict) {
ScopedFunctionObject e(scope, FunctionObject::createScriptFunction(ctx, function));
@@ -384,7 +385,7 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc,
}
-ReturnedValue EvalFunction::call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue EvalFunction::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
{
// indirect call
return static_cast<const EvalFunction *>(f)->evalCall(thisObject, argv, argc, false);
@@ -411,8 +412,8 @@ static inline int toInt(const QChar &qc, int R)
ReturnedValue GlobalFunctions::method_parseInt(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
Scope scope(b);
- ScopedValue inputString(scope, argc ? argv[0] : Primitive::undefinedValue());
- ScopedValue radix(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+ ScopedValue inputString(scope, argc ? argv[0] : Value::undefinedValue());
+ ScopedValue radix(scope, argc > 1 ? argv[1] : Value::undefinedValue());
int R = radix->isUndefined() ? 0 : radix->toInt32();
// [15.1.2.2] step by step:
@@ -493,7 +494,7 @@ ReturnedValue GlobalFunctions::method_parseFloat(const FunctionObject *b, const
{
Scope scope(b);
// [15.1.2.3] step by step:
- ScopedString inputString(scope, argc ? argv[0] : Primitive::undefinedValue(), ScopedString::Convert);
+ ScopedString inputString(scope, argc ? argv[0] : Value::undefinedValue(), ScopedString::Convert);
CHECK_EXCEPTION();
QString trimmed = inputString->toQString().trimmed(); // 2
diff --git a/src/qml/jsruntime/qv4globalobject_p.h b/src/qml/jsruntime/qv4globalobject_p.h
index fd1820c23c..021b445955 100644
--- a/src/qml/jsruntime/qv4globalobject_p.h
+++ b/src/qml/jsruntime/qv4globalobject_p.h
@@ -71,7 +71,7 @@ struct Q_QML_EXPORT EvalFunction : FunctionObject
ReturnedValue evalCall(const Value *thisObject, const Value *argv, int argc, bool directCall) const;
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
struct GlobalFunctions
diff --git a/src/qml/jsruntime/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp
index c122bcb51a..5db5bd46ec 100644
--- a/src/qml/jsruntime/qv4identifier.cpp
+++ b/src/qml/jsruntime/qv4identifier.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qv4identifier_p.h"
#include "qv4identifiertable_p.h"
+#include "qv4string_p.h"
QT_BEGIN_NAMESPACE
@@ -54,14 +55,16 @@ static inline int primeForNumBits(int numBits)
}
-IdentifierHashData::IdentifierHashData(int numBits)
+IdentifierHashData::IdentifierHashData(IdentifierTable *table, int numBits)
: size(0)
, numBits(numBits)
+ , identifierTable(table)
{
refCount.store(1);
alloc = primeForNumBits(numBits);
entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry));
memset(entries, 0, alloc*sizeof(IdentifierHashEntry));
+ identifierTable->addIdentifierHash(this);
}
IdentifierHashData::IdentifierHashData(IdentifierHashData *other)
@@ -73,12 +76,18 @@ IdentifierHashData::IdentifierHashData(IdentifierHashData *other)
alloc = other->alloc;
entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry));
memcpy(entries, other->entries, alloc*sizeof(IdentifierHashEntry));
+ identifierTable->addIdentifierHash(this);
+}
+
+IdentifierHashData::~IdentifierHashData() {
+ free(entries);
+ if (identifierTable)
+ identifierTable->removeIdentifierHash(this);
}
IdentifierHash::IdentifierHash(ExecutionEngine *engine)
{
- d = new IdentifierHashData(3);
- d->identifierTable = engine->identifierTable;
+ d = new IdentifierHashData(engine->identifierTable, 3);
}
void IdentifierHash::detach()
@@ -92,8 +101,10 @@ void IdentifierHash::detach()
}
-IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier)
+IdentifierHashEntry *IdentifierHash::addEntry(PropertyKey identifier)
{
+ Q_ASSERT(identifier.isStringOrSymbol());
+
// fill up to max 50%
bool grow = (d->alloc <= d->size*2);
@@ -104,10 +115,10 @@ IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier)
memset(newEntries, 0, newAlloc*sizeof(IdentifierHashEntry));
for (int i = 0; i < d->alloc; ++i) {
const IdentifierHashEntry &e = d->entries[i];
- if (!e.identifier)
+ if (!e.identifier.isValid())
continue;
- uint idx = e.identifier->hashValue % newAlloc;
- while (newEntries[idx].identifier) {
+ uint idx = e.identifier.id() % newAlloc;
+ while (newEntries[idx].identifier.isValid()) {
++idx;
idx %= newAlloc;
}
@@ -118,8 +129,8 @@ IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier)
d->alloc = newAlloc;
}
- uint idx = identifier->hashValue % d->alloc;
- while (d->entries[idx].identifier) {
+ uint idx = identifier.id() % d->alloc;
+ while (d->entries[idx].identifier.isValid()) {
Q_ASSERT(d->entries[idx].identifier != identifier);
++idx;
idx %= d->alloc;
@@ -129,15 +140,15 @@ IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier)
return d->entries + idx;
}
-const IdentifierHashEntry *IdentifierHash::lookup(const Identifier *identifier) const
+const IdentifierHashEntry *IdentifierHash::lookup(PropertyKey identifier) const
{
- if (!d)
+ if (!d || !identifier.isStringOrSymbol())
return nullptr;
Q_ASSERT(d->entries);
- uint idx = identifier->hashValue % d->alloc;
+ uint idx = identifier.id() % d->alloc;
while (1) {
- if (!d->entries[idx].identifier)
+ if (!d->entries[idx].identifier.isValid())
return nullptr;
if (d->entries[idx].identifier == identifier)
return d->entries + idx;
@@ -150,39 +161,54 @@ const IdentifierHashEntry *IdentifierHash::lookup(const QString &str) const
{
if (!d)
return nullptr;
- Q_ASSERT(d->entries);
- uint hash = String::createHashValue(str.constData(), str.length(), nullptr);
- uint idx = hash % d->alloc;
- while (1) {
- if (!d->entries[idx].identifier)
- return nullptr;
- if (d->entries[idx].identifier->string == str)
- return d->entries + idx;
- ++idx;
- idx %= d->alloc;
- }
+ PropertyKey id = d->identifierTable->asPropertyKey(str);
+ return lookup(id);
}
const IdentifierHashEntry *IdentifierHash::lookup(String *str) const
{
if (!d)
return nullptr;
- if (str->d()->identifier)
- return lookup(str->d()->identifier);
+ PropertyKey id = d->identifierTable->asPropertyKey(str);
+ if (id.isValid())
+ return lookup(id);
return lookup(str->toQString());
}
-const Identifier *IdentifierHash::toIdentifier(const QString &str) const
+const PropertyKey IdentifierHash::toIdentifier(const QString &str) const
{
Q_ASSERT(d);
- return d->identifierTable->identifier(str);
+ return d->identifierTable->asPropertyKey(str);
}
-const Identifier *IdentifierHash::toIdentifier(Heap::String *str) const
+const PropertyKey IdentifierHash::toIdentifier(Heap::String *str) const
{
Q_ASSERT(d);
- return d->identifierTable->identifier(str);
+ return d->identifierTable->asPropertyKey(str);
+}
+
+QString QV4::IdentifierHash::findId(int value) const
+{
+ IdentifierHashEntry *e = d->entries;
+ IdentifierHashEntry *end = e + d->alloc;
+ while (e < end) {
+ if (e->identifier.isValid() && e->value == value)
+ return e->identifier.toQString();
+ ++e;
+ }
+ return QString();
+}
+
+void IdentifierHashData::markObjects(MarkStack *markStack) const
+{
+ IdentifierHashEntry *e = entries;
+ IdentifierHashEntry *end = e + alloc;
+ while (e < end) {
+ if (Heap::Base *o = e->identifier.asStringOrSymbol())
+ o->mark(markStack);
+ ++e;
+ }
}
diff --git a/src/qml/jsruntime/qv4identifier_p.h b/src/qml/jsruntime/qv4identifier_p.h
index 82346d5f68..32de8b7c8d 100644
--- a/src/qml/jsruntime/qv4identifier_p.h
+++ b/src/qml/jsruntime/qv4identifier_p.h
@@ -51,38 +51,24 @@
//
#include <qstring.h>
+#include <private/qv4global_p.h>
+#include <private/qv4propertykey_p.h>
QT_BEGIN_NAMESPACE
namespace QV4 {
-namespace Heap {
- struct String;
-}
-
-struct String;
-struct IdentifierTable;
-struct ExecutionEngine;
-
-struct Identifier
-{
- QString string;
- uint hashValue;
-};
-
-
struct IdentifierHashEntry {
- const Identifier *identifier;
+ PropertyKey identifier;
int value;
};
struct IdentifierHashData
{
- IdentifierHashData(int numBits);
+ IdentifierHashData(IdentifierTable *table, int numBits);
explicit IdentifierHashData(IdentifierHashData *other);
- ~IdentifierHashData() {
- free(entries);
- }
+ ~IdentifierHashData();
+ void markObjects(MarkStack *markStack) const;
QBasicAtomicInt refCount;
int alloc;
@@ -117,12 +103,12 @@ struct IdentifierHash
QString findId(int value) const;
protected:
- IdentifierHashEntry *addEntry(const Identifier *i);
- const IdentifierHashEntry *lookup(const Identifier *identifier) const;
+ IdentifierHashEntry *addEntry(PropertyKey i);
+ const IdentifierHashEntry *lookup(PropertyKey identifier) const;
const IdentifierHashEntry *lookup(const QString &str) const;
const IdentifierHashEntry *lookup(String *str) const;
- const Identifier *toIdentifier(const QString &str) const;
- const Identifier *toIdentifier(Heap::String *str) const;
+ const PropertyKey toIdentifier(const QString &str) const;
+ const PropertyKey toIdentifier(Heap::String *str) const;
};
@@ -180,20 +166,6 @@ inline int IdentifierHash::value(String *str) const
return e ? e->value : -1;
}
-
-inline
-QString IdentifierHash::findId(int value) const
-{
- IdentifierHashEntry *e = d->entries;
- IdentifierHashEntry *end = e + d->alloc;
- while (e < end) {
- if (e->identifier && e->value == value)
- return e->identifier->string;
- ++e;
- }
- return QString();
-}
-
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp
index b77f9478d3..e476baa886 100644
--- a/src/qml/jsruntime/qv4identifiertable.cpp
+++ b/src/qml/jsruntime/qv4identifiertable.cpp
@@ -37,6 +37,7 @@
**
****************************************************************************/
#include "qv4identifiertable_p.h"
+#include "qv4symbol_p.h"
QT_BEGIN_NAMESPACE
@@ -53,44 +54,44 @@ static inline int primeForNumBits(int numBits)
}
-IdentifierTable::IdentifierTable(ExecutionEngine *engine)
+IdentifierTable::IdentifierTable(ExecutionEngine *engine, int numBits)
: engine(engine)
, size(0)
- , numBits(8)
+ , numBits(numBits)
{
alloc = primeForNumBits(numBits);
- entries = (Heap::String **)malloc(alloc*sizeof(Heap::String *));
- memset(entries, 0, alloc*sizeof(Heap::String *));
+ entriesByHash = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *));
+ entriesById = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *));
+ memset(entriesByHash, 0, alloc*sizeof(Heap::String *));
+ memset(entriesById, 0, alloc*sizeof(Heap::String *));
}
IdentifierTable::~IdentifierTable()
{
- for (int i = 0; i < alloc; ++i)
- if (entries[i])
- delete entries[i]->identifier;
- free(entries);
+ free(entriesByHash);
+ free(entriesById);
+ for (auto &h : idHashes)
+ h->identifierTable = nullptr;
}
-void IdentifierTable::addEntry(Heap::String *str)
+void IdentifierTable::addEntry(Heap::StringOrSymbol *str)
{
uint hash = str->hashValue();
if (str->subtype == Heap::String::StringType_ArrayIndex)
return;
- str->identifier = new Identifier;
- str->identifier->string = str->toQString();
- str->identifier->hashValue = hash;
+ str->identifier = PropertyKey::fromStringOrSymbol(str);
bool grow = (alloc <= size*2);
if (grow) {
++numBits;
int newAlloc = primeForNumBits(numBits);
- Heap::String **newEntries = (Heap::String **)malloc(newAlloc*sizeof(Heap::String *));
- memset(newEntries, 0, newAlloc*sizeof(Heap::String *));
- for (int i = 0; i < alloc; ++i) {
- Heap::String *e = entries[i];
+ Heap::StringOrSymbol **newEntries = (Heap::StringOrSymbol **)malloc(newAlloc*sizeof(Heap::String *));
+ memset(newEntries, 0, newAlloc*sizeof(Heap::StringOrSymbol *));
+ for (uint i = 0; i < alloc; ++i) {
+ Heap::StringOrSymbol *e = entriesByHash[i];
if (!e)
continue;
uint idx = e->stringHash % newAlloc;
@@ -100,17 +101,42 @@ void IdentifierTable::addEntry(Heap::String *str)
}
newEntries[idx] = e;
}
- free(entries);
- entries = newEntries;
+ free(entriesByHash);
+ entriesByHash = newEntries;
+
+ newEntries = (Heap::StringOrSymbol **)malloc(newAlloc*sizeof(Heap::String *));
+ memset(newEntries, 0, newAlloc*sizeof(Heap::StringOrSymbol *));
+ for (uint i = 0; i < alloc; ++i) {
+ Heap::StringOrSymbol *e = entriesById[i];
+ if (!e)
+ continue;
+ uint idx = e->identifier.id() % newAlloc;
+ while (newEntries[idx]) {
+ ++idx;
+ idx %= newAlloc;
+ }
+ newEntries[idx] = e;
+ }
+ free(entriesById);
+ entriesById = newEntries;
+
alloc = newAlloc;
}
uint idx = hash % alloc;
- while (entries[idx]) {
+ while (entriesByHash[idx]) {
++idx;
idx %= alloc;
}
- entries[idx] = str;
+ entriesByHash[idx] = str;
+
+ idx = str->identifier.id() % alloc;
+ while (entriesById[idx]) {
+ ++idx;
+ idx %= alloc;
+ }
+ entriesById[idx] = str;
+
++size;
}
@@ -120,10 +146,16 @@ Heap::String *IdentifierTable::insertString(const QString &s)
{
uint subtype;
uint hash = String::createHashValue(s.constData(), s.length(), &subtype);
+ if (subtype == Heap::String::StringType_ArrayIndex) {
+ Heap::String *str = engine->newString(s);
+ str->stringHash = hash;
+ str->subtype = subtype;
+ return str;
+ }
uint idx = hash % alloc;
- while (Heap::String *e = entries[idx]) {
+ while (Heap::StringOrSymbol *e = entriesByHash[idx]) {
if (e->stringHash == hash && e->toQString() == s)
- return e;
+ return static_cast<Heap::String *>(e);
++idx;
idx %= alloc;
}
@@ -135,18 +167,42 @@ Heap::String *IdentifierTable::insertString(const QString &s)
return str;
}
+Heap::Symbol *IdentifierTable::insertSymbol(const QString &s)
+{
+ Q_ASSERT(s.at(0) == QLatin1Char('@'));
+
+ uint subtype;
+ uint hash = String::createHashValue(s.constData(), s.length(), &subtype);
+ uint idx = hash % alloc;
+ while (Heap::StringOrSymbol *e = entriesByHash[idx]) {
+ if (e->stringHash == hash && e->toQString() == s)
+ return static_cast<Heap::Symbol *>(e);
+ ++idx;
+ idx %= alloc;
+ }
-Identifier *IdentifierTable::identifierImpl(const Heap::String *str)
+ Heap::Symbol *str = Symbol::create(engine, s);
+ str->stringHash = hash;
+ str->subtype = subtype;
+ addEntry(str);
+ return str;
+
+}
+
+
+PropertyKey IdentifierTable::asPropertyKeyImpl(const Heap::String *str)
{
- if (str->identifier)
+ if (str->identifier.isValid())
return str->identifier;
uint hash = str->hashValue();
- if (str->subtype == Heap::String::StringType_ArrayIndex)
- return nullptr;
+ if (str->subtype == Heap::String::StringType_ArrayIndex) {
+ str->identifier = PropertyKey::fromArrayIndex(hash);
+ return str->identifier;
+ }
uint idx = hash % alloc;
- while (Heap::String *e = entries[idx]) {
- if (e->stringHash == hash && e->isEqualTo(str)) {
+ while (Heap::StringOrSymbol *e = entriesByHash[idx]) {
+ if (e->stringHash == hash && e->toQString() == str->toQString()) {
str->identifier = e->identifier;
return e->identifier;
}
@@ -158,37 +214,96 @@ Identifier *IdentifierTable::identifierImpl(const Heap::String *str)
return str->identifier;
}
-Heap::String *IdentifierTable::stringFromIdentifier(Identifier *i)
+Heap::StringOrSymbol *IdentifierTable::resolveId(PropertyKey i) const
{
- if (!i)
+ uint arrayIdx = i.asArrayIndex();
+ if (arrayIdx < UINT_MAX)
+ return engine->newString(QString::number(arrayIdx));
+ if (!i.isValid())
return nullptr;
- uint idx = i->hashValue % alloc;
+ uint idx = i.id() % alloc;
while (1) {
- Heap::String *e = entries[idx];
- Q_ASSERT(e);
- if (e->identifier == i)
+ Heap::StringOrSymbol *e = entriesById[idx];
+ if (!e || e->identifier == i)
return e;
++idx;
idx %= alloc;
}
}
-Identifier *IdentifierTable::identifier(const QString &s)
+Heap::String *IdentifierTable::stringForId(PropertyKey i) const
+{
+ Heap::StringOrSymbol *s = resolveId(i);
+ Q_ASSERT(s && s->internalClass->vtable->isString);
+ return static_cast<Heap::String *>(s);
+}
+
+Heap::Symbol *IdentifierTable::symbolForId(PropertyKey i) const
+{
+ Heap::StringOrSymbol *s = resolveId(i);
+ Q_ASSERT(!s || !s->internalClass->vtable->isString);
+ return static_cast<Heap::Symbol *>(s);
+}
+
+void IdentifierTable::markObjects(MarkStack *markStack)
+{
+ for (const auto &h : idHashes)
+ h->markObjects(markStack);
+}
+
+void IdentifierTable::sweep()
+{
+ int freed = 0;
+
+ Heap::StringOrSymbol **newTable = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::String *));
+ memset(newTable, 0, alloc*sizeof(Heap::StringOrSymbol *));
+ memset(entriesById, 0, alloc*sizeof(Heap::StringOrSymbol *));
+ for (uint i = 0; i < alloc; ++i) {
+ Heap::StringOrSymbol *e = entriesByHash[i];
+ if (!e)
+ continue;
+ if (!e->isMarked()) {
+ ++freed;
+ continue;
+ }
+ uint idx = e->hashValue() % alloc;
+ while (newTable[idx]) {
+ ++idx;
+ if (idx == alloc)
+ idx = 0;
+ }
+ newTable[idx] = e;
+
+ idx = e->identifier.id() % alloc;
+ while (entriesById[idx]) {
+ ++idx;
+ if (idx == alloc)
+ idx = 0;
+ }
+ entriesById[idx] = e;
+ }
+ free(entriesByHash);
+ entriesByHash = newTable;
+
+ size -= freed;
+}
+
+PropertyKey IdentifierTable::asPropertyKey(const QString &s)
{
return insertString(s)->identifier;
}
-Identifier *IdentifierTable::identifier(const char *s, int len)
+PropertyKey IdentifierTable::asPropertyKey(const char *s, int len)
{
uint subtype;
uint hash = String::createHashValue(s, len, &subtype);
if (hash == UINT_MAX)
- return identifier(QString::fromUtf8(s, len));
+ return asPropertyKey(QString::fromUtf8(s, len));
QLatin1String latin(s, len);
uint idx = hash % alloc;
- while (Heap::String *e = entries[idx]) {
+ while (Heap::StringOrSymbol *e = entriesByHash[idx]) {
if (e->stringHash == hash && e->toQString() == latin)
return e->identifier;
++idx;
diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h
index b0b08f1e54..78e2b6620e 100644
--- a/src/qml/jsruntime/qv4identifiertable_p.h
+++ b/src/qml/jsruntime/qv4identifiertable_p.h
@@ -53,55 +53,61 @@
#include "qv4identifier_p.h"
#include "qv4string_p.h"
#include "qv4engine_p.h"
+#include <qset.h>
#include <limits.h>
QT_BEGIN_NAMESPACE
namespace QV4 {
-struct IdentifierTable
+struct Q_QML_PRIVATE_EXPORT IdentifierTable
{
ExecutionEngine *engine;
- int alloc;
- int size;
+ uint alloc;
+ uint size;
int numBits;
- Heap::String **entries;
+ Heap::StringOrSymbol **entriesByHash;
+ Heap::StringOrSymbol **entriesById;
- void addEntry(Heap::String *str);
+ QSet<IdentifierHashData *> idHashes;
+
+ void addEntry(Heap::StringOrSymbol *str);
public:
- IdentifierTable(ExecutionEngine *engine);
+ IdentifierTable(ExecutionEngine *engine, int numBits = 8);
~IdentifierTable();
Heap::String *insertString(const QString &s);
+ Heap::Symbol *insertSymbol(const QString &s);
- Identifier *identifier(const Heap::String *str) {
- if (str->identifier)
+ PropertyKey asPropertyKey(const Heap::String *str) {
+ if (str->identifier.isValid())
return str->identifier;
- return identifierImpl(str);
+ return asPropertyKeyImpl(str);
}
- Identifier *identifier(const QV4::String *str) {
- return identifier(str->d());
+ PropertyKey asPropertyKey(const QV4::String *str) {
+ return asPropertyKey(str->d());
}
- Identifier *identifier(const QString &s);
- Identifier *identifier(const char *s, int len);
+ PropertyKey asPropertyKey(const QString &s);
+ PropertyKey asPropertyKey(const char *s, int len);
+
+ PropertyKey asPropertyKeyImpl(const Heap::String *str);
- Identifier *identifierImpl(const Heap::String *str);
+ Heap::StringOrSymbol *resolveId(PropertyKey i) const;
+ Heap::String *stringForId(PropertyKey i) const;
+ Heap::Symbol *symbolForId(PropertyKey i) const;
- Heap::String *stringFromIdentifier(Identifier *i);
+ void markObjects(MarkStack *markStack);
+ void sweep();
- void mark(MarkStack *markStack) {
- for (int i = 0; i < alloc; ++i) {
- Heap::String *entry = entries[i];
- if (!entry || entry->isMarked())
- continue;
- entry->setMarkBit();
- Q_ASSERT(entry->vtable()->markObjects);
- entry->vtable()->markObjects(entry, markStack);
- }
+ void addIdentifierHash(IdentifierHashData *h) {
+ idHashes.insert(h);
+ }
+ void removeIdentifierHash(IdentifierHashData *h) {
+ idHashes.remove(h);
}
};
diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp
index d0d66c9b9a..e456879d9c 100644
--- a/src/qml/jsruntime/qv4include.cpp
+++ b/src/qml/jsruntime/qv4include.cpp
@@ -101,11 +101,11 @@ QV4::ReturnedValue QV4Include::resultValue(QV4::ExecutionEngine *v4, Status stat
QV4::ScopedObject o(scope, v4->newObject());
QV4::ScopedString s(scope);
QV4::ScopedValue v(scope);
- o->put((s = v4->newString(QStringLiteral("OK"))), (v = QV4::Primitive::fromInt32(Ok)));
- o->put((s = v4->newString(QStringLiteral("LOADING"))), (v = QV4::Primitive::fromInt32(Loading)));
- o->put((s = v4->newString(QStringLiteral("NETWORK_ERROR"))), (v = QV4::Primitive::fromInt32(NetworkError)));
- o->put((s = v4->newString(QStringLiteral("EXCEPTION"))), (v = QV4::Primitive::fromInt32(Exception)));
- o->put((s = v4->newString(QStringLiteral("status"))), (v = QV4::Primitive::fromInt32(status)));
+ o->put((s = v4->newString(QStringLiteral("OK"))), (v = QV4::Value::fromInt32(Ok)));
+ o->put((s = v4->newString(QStringLiteral("LOADING"))), (v = QV4::Value::fromInt32(Loading)));
+ o->put((s = v4->newString(QStringLiteral("NETWORK_ERROR"))), (v = QV4::Value::fromInt32(NetworkError)));
+ o->put((s = v4->newString(QStringLiteral("EXCEPTION"))), (v = QV4::Value::fromInt32(Exception)));
+ o->put((s = v4->newString(QStringLiteral("status"))), (v = QV4::Value::fromInt32(status)));
if (!statusText.isEmpty())
o->put((s = v4->newString(QStringLiteral("statusText"))), (v = v4->newString(statusText)));
@@ -173,20 +173,20 @@ void QV4Include::finished()
script.run();
if (scope.engine->hasException) {
QV4::ScopedValue ex(scope, scope.engine->catchException());
- resultObj->put(status, QV4::ScopedValue(scope, QV4::Primitive::fromInt32(Exception)));
+ resultObj->put(status, QV4::ScopedValue(scope, QV4::Value::fromInt32(Exception)));
QV4::ScopedString exception(scope, v4->newString(QStringLiteral("exception")));
resultObj->put(exception, ex);
} else {
- resultObj->put(status, QV4::ScopedValue(scope, QV4::Primitive::fromInt32(Ok)));
+ resultObj->put(status, QV4::ScopedValue(scope, QV4::Value::fromInt32(Ok)));
}
} else {
- resultObj->put(status, QV4::ScopedValue(scope, QV4::Primitive::fromInt32(NetworkError)));
+ resultObj->put(status, QV4::ScopedValue(scope, QV4::Value::fromInt32(NetworkError)));
}
#else
QV4::Scope scope(v4);
QV4::ScopedObject resultObj(scope, m_resultObject.value());
QV4::ScopedString status(scope, v4->newString(QStringLiteral("status")));
- resultObj->put(status, QV4::ScopedValue(scope, QV4::Primitive::fromInt32(NetworkError)));
+ resultObj->put(status, QV4::ScopedValue(scope, QV4::Value::fromInt32(NetworkError)));
#endif // qml_network
QV4::ScopedValue cb(scope, m_callbackFunction.value());
@@ -207,10 +207,10 @@ QV4::ReturnedValue QV4Include::method_include(const QV4::FunctionObject *b, cons
QQmlContextData *context = scope.engine->callingQmlContext();
- if (!context || !context->isJSContext)
+ if ((!context || !context->isJSContext) && scope.engine->qmlEngine())
RETURN_RESULT(scope.engine->throwError(QString::fromUtf8("Qt.include(): Can only be called from JavaScript files")));
- QV4::ScopedValue callbackFunction(scope, QV4::Primitive::undefinedValue());
+ QV4::ScopedValue callbackFunction(scope, QV4::Value::undefinedValue());
if (argc >= 2 && argv[1].as<QV4::FunctionObject>())
callbackFunction = argv[1];
diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp
index 3bfcf358bf..ddb8542e07 100644
--- a/src/qml/jsruntime/qv4internalclass.cpp
+++ b/src/qml/jsruntime/qv4internalclass.cpp
@@ -47,7 +47,7 @@
QT_BEGIN_NAMESPACE
-using namespace QV4;
+namespace QV4 {
static const uchar prime_deltas[] = {
0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3,
@@ -74,27 +74,11 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize)
// fill up to max 50%
bool grow = (d->alloc <= d->size*2);
- if (classSize < d->size || grow) {
- PropertyHashData *dd = new PropertyHashData(grow ? d->numBits + 1 : d->numBits);
- for (int i = 0; i < d->alloc; ++i) {
- const Entry &e = d->entries[i];
- if (!e.identifier || e.index >= static_cast<unsigned>(classSize))
- continue;
- uint idx = e.identifier->hashValue % dd->alloc;
- while (dd->entries[idx].identifier) {
- ++idx;
- idx %= dd->alloc;
- }
- dd->entries[idx] = e;
- }
- dd->size = classSize;
- Q_ASSERT(d->refCount > 1);
- --d->refCount;
- d = dd;
- }
+ if (classSize < d->size || grow)
+ detach(grow, classSize);
- uint idx = entry.identifier->hashValue % d->alloc;
- while (d->entries[idx].identifier) {
+ uint idx = entry.identifier.id() % d->alloc;
+ while (d->entries[idx].identifier.isValid()) {
++idx;
idx %= d->alloc;
}
@@ -102,74 +86,205 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize)
++d->size;
}
+int PropertyHash::removeIdentifier(PropertyKey identifier, int classSize)
+{
+ int val = -1;
+ PropertyHashData *dd = new PropertyHashData(d->numBits);
+ for (int i = 0; i < d->alloc; ++i) {
+ const Entry &e = d->entries[i];
+ if (!e.identifier.isValid() || e.index >= static_cast<unsigned>(classSize))
+ continue;
+ if (e.identifier == identifier) {
+ val = e.index;
+ continue;
+ }
+ uint idx = e.identifier.id() % dd->alloc;
+ while (dd->entries[idx].identifier.isValid()) {
+ ++idx;
+ idx %= dd->alloc;
+ }
+ dd->entries[idx] = e;
+ }
+ dd->size = classSize;
+ if (!--d->refCount)
+ delete d;
+ d = dd;
-InternalClass::InternalClass(ExecutionEngine *engine)
- : engine(engine)
- , vtable(nullptr)
- , prototype(nullptr)
- , m_sealed(nullptr)
- , m_frozen(nullptr)
- , size(0)
- , extensible(true)
+ Q_ASSERT(val != -1);
+ return val;
+}
+
+void PropertyHash::detach(bool grow, int classSize)
{
- id = engine->newInternalClassId();
+ if (d->refCount == 1 && !grow)
+ return;
+
+ PropertyHashData *dd = new PropertyHashData(grow ? d->numBits + 1 : d->numBits);
+ for (int i = 0; i < d->alloc; ++i) {
+ const Entry &e = d->entries[i];
+ if (!e.identifier.isValid() || e.index >= static_cast<unsigned>(classSize))
+ continue;
+ uint idx = e.identifier.id() % dd->alloc;
+ while (dd->entries[idx].identifier.isValid()) {
+ ++idx;
+ idx %= dd->alloc;
+ }
+ dd->entries[idx] = e;
+ }
+ dd->size = classSize;
+ if (!--d->refCount)
+ delete d;
+ d = dd;
}
-InternalClass::InternalClass(const QV4::InternalClass &other)
- : QQmlJS::Managed()
- , engine(other.engine)
- , vtable(other.vtable)
- , prototype(other.prototype)
- , propertyTable(other.propertyTable)
- , nameMap(other.nameMap)
- , propertyData(other.propertyData)
- , m_sealed(nullptr)
- , m_frozen(nullptr)
- , size(other.size)
- , extensible(other.extensible)
- , isUsedAsProto(other.isUsedAsProto)
+SharedInternalClassDataPrivate<PropertyKey>::SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate<PropertyKey> &other)
+ : refcount(1),
+ engine(other.engine),
+ data(nullptr)
{
- id = engine->newInternalClassId();
+ if (other.alloc()) {
+ int s = other.size();
+ data = MemberData::allocate(engine, other.alloc(), other.data);
+ setSize(s);
+ }
}
-static void insertHoleIntoPropertyData(Object *object, int idx)
+SharedInternalClassDataPrivate<PropertyKey>::SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate<PropertyKey> &other,
+ uint pos, PropertyKey value)
+ : refcount(1),
+ engine(other.engine)
{
- Heap::Object *o = object->d();
- ExecutionEngine *v4 = o->internalClass->engine;
- int size = o->internalClass->size;
- for (int i = size - 1; i > idx; --i)
- o->setProperty(v4, i, *o->propertyData(i - 1));
+ data = MemberData::allocate(engine, other.alloc(), nullptr);
+ memcpy(data, other.data, sizeof(Heap::MemberData) - sizeof(Value) + pos*sizeof(Value));
+ data->values.size = pos + 1;
+ data->values.set(engine, pos, Value::fromReturnedValue(value.id()));
}
-static void removeFromPropertyData(Object *object, int idx, bool accessor = false)
+void SharedInternalClassDataPrivate<PropertyKey>::grow()
{
- Heap::Object *o = object->d();
- ExecutionEngine *v4 = o->internalClass->engine;
- int size = o->internalClass->size;
- for (int i = idx; i < size; ++i)
- o->setProperty(v4, i, *o->propertyData(i + (accessor ? 2 : 1)));
- o->setProperty(v4, size, Primitive::undefinedValue());
- if (accessor)
- o->setProperty(v4, size + 1, Primitive::undefinedValue());
+ uint a = alloc() * 2;
+ int s = size();
+ data = MemberData::allocate(engine, a, data);
+ setSize(s);
+ Q_ASSERT(alloc() >= a);
}
-void InternalClass::changeMember(Object *object, String *string, PropertyAttributes data, uint *index)
+uint SharedInternalClassDataPrivate<PropertyKey>::alloc() const
{
- uint idx;
- InternalClass *oldClass = object->internalClass();
- InternalClass *newClass = oldClass->changeMember(string->identifier(), data, &idx);
- if (index)
- *index = idx;
+ return data ? data->values.alloc : 0;
+}
- object->setInternalClass(newClass);
- if (newClass->size > oldClass->size) {
- Q_ASSERT(newClass->size == oldClass->size + 1);
- insertHoleIntoPropertyData(object, idx);
- } else if (newClass->size < oldClass->size) {
- Q_ASSERT(newClass->size == oldClass->size - 1);
- removeFromPropertyData(object, idx + 1);
+uint SharedInternalClassDataPrivate<PropertyKey>::size() const
+{
+ return data ? data->values.size : 0;
+}
+
+void SharedInternalClassDataPrivate<PropertyKey>::setSize(uint s)
+{
+ Q_ASSERT(data && s <= alloc());
+ data->values.size = s;
+}
+
+PropertyKey SharedInternalClassDataPrivate<PropertyKey>::at(uint i)
+{
+ Q_ASSERT(data && i < size());
+ return PropertyKey::fromId(data->values.values[i].rawValue());
+}
+
+void SharedInternalClassDataPrivate<PropertyKey>::set(uint i, PropertyKey t)
+{
+ Q_ASSERT(data && i < size());
+ data->values.values[i].rawValueRef() = t.id();
+}
+
+void SharedInternalClassDataPrivate<PropertyKey>::mark(MarkStack *s)
+{
+ if (data)
+ data->mark(s);
+}
+
+
+
+namespace Heap {
+
+void InternalClass::init(ExecutionEngine *engine)
+{
+ Base::init();
+ new (&propertyTable) PropertyHash();
+ new (&nameMap) SharedInternalClassData<PropertyKey>(engine);
+ new (&propertyData) SharedInternalClassData<PropertyAttributes>(engine);
+ new (&transitions) std::vector<Transition>();
+
+ this->engine = engine;
+ vtable = QV4::InternalClass::staticVTable();
+// prototype = nullptr;
+// parent = nullptr;
+// size = 0;
+ extensible = true;
+ isFrozen = false;
+ isSealed = false;
+ isUsedAsProto = false;
+ protoId = engine->newProtoId();
+
+ // Also internal classes need an internal class pointer. Simply make it point to itself
+ internalClass.set(engine, this);
+}
+
+
+void InternalClass::init(Heap::InternalClass *other)
+{
+ Base::init();
+ Q_ASSERT(!other->isFrozen);
+ new (&propertyTable) PropertyHash(other->propertyTable);
+ new (&nameMap) SharedInternalClassData<PropertyKey>(other->nameMap);
+ new (&propertyData) SharedInternalClassData<PropertyAttributes>(other->propertyData);
+ new (&transitions) std::vector<Transition>();
+
+ engine = other->engine;
+ vtable = other->vtable;
+ prototype = other->prototype;
+ parent = other;
+ size = other->size;
+ extensible = other->extensible;
+ isSealed = other->isSealed;
+ isFrozen = other->isFrozen;
+ isUsedAsProto = other->isUsedAsProto;
+ protoId = engine->newProtoId();
+
+ internalClass.set(engine, other->internalClass);
+}
+
+void InternalClass::destroy()
+{
+#ifndef QT_NO_DEBUG
+ for (const auto &t : transitions) {
+ Q_ASSERT(!t.lookup || !t.lookup->isMarked());
}
+#endif
+ if (parent && parent->engine && parent->isMarked())
+ parent->removeChildEntry(this);
+
+ propertyTable.~PropertyHash();
+ nameMap.~SharedInternalClassData<PropertyKey>();
+ propertyData.~SharedInternalClassData<PropertyAttributes>();
+ transitions.~vector<Transition>();
+ engine = nullptr;
+ Base::destroy();
+}
+
+QString InternalClass::keyAt(uint index) const
+{
+ return nameMap.at(index).toQString();
+}
+
+void InternalClass::changeMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, InternalClassEntry *entry)
+{
+ Q_ASSERT(id.isStringOrSymbol());
+
+ Heap::InternalClass *oldClass = object->internalClass();
+ Heap::InternalClass *newClass = oldClass->changeMember(id, data, entry);
+ object->setInternalClass(newClass);
}
InternalClassTransition &InternalClass::lookupOrInsertTransition(const InternalClassTransition &t)
@@ -183,47 +298,66 @@ InternalClassTransition &InternalClass::lookupOrInsertTransition(const InternalC
}
}
-InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttributes data, uint *index)
+static void addDummyEntry(InternalClass *newClass, PropertyHash::Entry e)
+{
+ // add a dummy entry, since we need two entries for accessors
+ newClass->propertyTable.addEntry(e, newClass->size);
+ newClass->nameMap.add(newClass->size, PropertyKey::invalid());
+ newClass->propertyData.add(newClass->size, PropertyAttributes());
+ ++newClass->size;
+}
+
+Heap::InternalClass *InternalClass::changeMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry)
{
- data.resolve();
- uint idx = find(identifier);
+ if (!data.isEmpty())
+ data.resolve();
+ PropertyHash::Entry *e = findEntry(identifier);
+ Q_ASSERT(e && e->index != UINT_MAX);
+ uint idx = e->index;
Q_ASSERT(idx != UINT_MAX);
- if (index)
- *index = idx;
+ if (entry) {
+ entry->index = idx;
+ entry->setterIndex = e->setterIndex;
+ entry->attributes = data;
+ }
if (data == propertyData.at(idx))
- return this;
+ return static_cast<Heap::InternalClass *>(this);
- Transition temp = { { identifier }, nullptr, (int)data.flags() };
+ Transition temp = { { identifier }, nullptr, int(data.all()) };
Transition &t = lookupOrInsertTransition(temp);
if (t.lookup)
return t.lookup;
// create a new class and add it to the tree
- InternalClass *newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable);
- newClass = newClass->changePrototype(prototype);
- for (uint i = 0; i < size; ++i) {
- if (i == idx) {
- newClass = newClass->addMember(nameMap.at(i), data);
- } else if (!propertyData.at(i).isEmpty()) {
- newClass = newClass->addMember(nameMap.at(i), propertyData.at(i));
- }
+ Heap::InternalClass *newClass = engine->newClass(this);
+ if (data.isAccessor() && e->setterIndex == UINT_MAX) {
+ Q_ASSERT(!propertyData.at(idx).isAccessor());
+
+ // add a dummy entry for the accessor
+ entry->setterIndex = newClass->size;
+ e->setterIndex = newClass->size;
+ addDummyEntry(newClass, *e);
}
+ newClass->propertyData.set(idx, data);
+
t.lookup = newClass;
Q_ASSERT(t.lookup);
return newClass;
}
-InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
+Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
{
+ Scope scope(engine);
+ ScopedValue protectThis(scope, this);
if (proto)
proto->setUsedAsProto();
Q_ASSERT(prototype != proto);
Q_ASSERT(!proto || proto->internalClass->isUsedAsProto);
- Transition temp = { { nullptr }, nullptr, Transition::PrototypeChange };
+ Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::PrototypeChange };
temp.prototype = proto;
Transition &t = lookupOrInsertTransition(temp);
@@ -231,29 +365,19 @@ InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
return t.lookup;
// create a new class and add it to the tree
- InternalClass *newClass;
- if (!size && !prototype) {
- newClass = engine->newClass(*this);
- newClass->prototype = proto;
- } else {
- newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable);
- newClass = newClass->changePrototype(proto);
- for (uint i = 0; i < size; ++i) {
- if (!propertyData.at(i).isEmpty())
- newClass = newClass->addMember(nameMap.at(i), propertyData.at(i));
- }
- }
+ Heap::InternalClass *newClass = engine->newClass(this);
+ newClass->prototype = proto;
t.lookup = newClass;
return newClass;
}
-InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
+Heap::InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
{
Q_ASSERT(vtable != vt);
- Transition temp = { { nullptr }, nullptr, Transition::VTableChange };
+ Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::VTableChange };
temp.vtable = vt;
Transition &t = lookupOrInsertTransition(temp);
@@ -261,18 +385,8 @@ InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
return t.lookup;
// create a new class and add it to the tree
- InternalClass *newClass;
- if (this == engine->internalClasses[EngineBase::Class_Empty]) {
- newClass = engine->newClass(*this);
- newClass->vtable = vt;
- } else {
- newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vt);
- newClass = newClass->changePrototype(prototype);
- for (uint i = 0; i < size; ++i) {
- if (!propertyData.at(i).isEmpty())
- newClass = newClass->addMember(nameMap.at(i), propertyData.at(i));
- }
- }
+ Heap::InternalClass *newClass = engine->newClass(this);
+ newClass->vtable = vt;
t.lookup = newClass;
Q_ASSERT(t.lookup);
@@ -280,17 +394,17 @@ InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
return newClass;
}
-InternalClass *InternalClass::nonExtensible()
+Heap::InternalClass *InternalClass::nonExtensible()
{
if (!extensible)
return this;
- Transition temp = { { nullptr }, nullptr, Transition::NotExtensible};
+ Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::NotExtensible};
Transition &t = lookupOrInsertTransition(temp);
if (t.lookup)
return t.lookup;
- InternalClass *newClass = engine->newClass(*this);
+ Heap::InternalClass *newClass = engine->newClass(this);
newClass->extensible = false;
t.lookup = newClass;
@@ -298,177 +412,221 @@ InternalClass *InternalClass::nonExtensible()
return newClass;
}
-void InternalClass::addMember(Object *object, String *string, PropertyAttributes data, uint *index)
+void InternalClass::addMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, InternalClassEntry *entry)
{
- data.resolve();
- object->internalClass()->engine->identifierTable->identifier(string);
- if (object->internalClass()->propertyTable.lookup(string->d()->identifier) < object->internalClass()->size) {
- changeMember(object, string, data, index);
+ Q_ASSERT(id.isStringOrSymbol());
+ if (!data.isEmpty())
+ data.resolve();
+ PropertyHash::Entry *e = object->internalClass()->findEntry(id);
+ if (e) {
+ changeMember(object, id, data, entry);
return;
}
- uint idx;
- InternalClass *newClass = object->internalClass()->addMemberImpl(string->identifier(), data, &idx);
- if (index)
- *index = idx;
-
+ Heap::InternalClass *newClass = object->internalClass()->addMemberImpl(id, data, entry);
object->setInternalClass(newClass);
}
-InternalClass *InternalClass::addMember(String *string, PropertyAttributes data, uint *index)
+Heap::InternalClass *InternalClass::addMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry)
{
- engine->identifierTable->identifier(string);
- return addMember(string->identifier(), data, index);
-}
+ Q_ASSERT(identifier.isStringOrSymbol());
+ if (!data.isEmpty())
+ data.resolve();
-InternalClass *InternalClass::addMember(Identifier *identifier, PropertyAttributes data, uint *index)
-{
- data.resolve();
-
- if (propertyTable.lookup(identifier) < size)
- return changeMember(identifier, data, index);
+ PropertyHash::Entry *e = findEntry(identifier);
+ if (e)
+ return changeMember(identifier, data, entry);
- return addMemberImpl(identifier, data, index);
+ return addMemberImpl(identifier, data, entry);
}
-InternalClass *InternalClass::addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index)
+Heap::InternalClass *InternalClass::addMemberImpl(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry)
{
Transition temp = { { identifier }, nullptr, (int)data.flags() };
Transition &t = lookupOrInsertTransition(temp);
- if (index)
- *index = size;
+ if (entry) {
+ entry->index = size;
+ entry->setterIndex = data.isAccessor() ? size + 1 : UINT_MAX;
+ entry->attributes = data;
+ }
if (t.lookup)
return t.lookup;
// create a new class and add it to the tree
- InternalClass *newClass = engine->newClass(*this);
- PropertyHash::Entry e = { identifier, newClass->size };
+ Scope scope(engine);
+ Scoped<QV4::InternalClass> ic(scope, engine->newClass(this));
+ InternalClass *newClass = ic->d();
+ PropertyHash::Entry e = { identifier, newClass->size, data.isAccessor() ? newClass->size + 1 : UINT_MAX };
newClass->propertyTable.addEntry(e, newClass->size);
newClass->nameMap.add(newClass->size, identifier);
newClass->propertyData.add(newClass->size, data);
++newClass->size;
- if (data.isAccessor()) {
- // add a dummy entry, since we need two entries for accessors
- newClass->propertyTable.addEntry(e, newClass->size);
- newClass->nameMap.add(newClass->size, 0);
- newClass->propertyData.add(newClass->size, PropertyAttributes());
- ++newClass->size;
- }
+ if (data.isAccessor())
+ addDummyEntry(newClass, e);
t.lookup = newClass;
Q_ASSERT(t.lookup);
return newClass;
}
-void InternalClass::removeMember(Object *object, Identifier *id)
+void InternalClass::removeChildEntry(InternalClass *child)
{
- InternalClass *oldClass = object->internalClass();
- uint propIdx = oldClass->propertyTable.lookup(id);
- Q_ASSERT(propIdx < oldClass->size);
-
- Transition temp = { { id }, nullptr, -1 };
- Transition &t = object->internalClass()->lookupOrInsertTransition(temp);
-
- bool accessor = oldClass->propertyData.at(propIdx).isAccessor();
-
- if (t.lookup) {
- object->setInternalClass(t.lookup);
- } else {
- // create a new class and add it to the tree
- InternalClass *newClass = oldClass->engine->internalClasses[EngineBase::Class_Empty]->changeVTable(oldClass->vtable);
- newClass = newClass->changePrototype(oldClass->prototype);
- for (uint i = 0; i < oldClass->size; ++i) {
- if (i == propIdx)
- continue;
- if (!oldClass->propertyData.at(i).isEmpty())
- newClass = newClass->addMember(oldClass->nameMap.at(i), oldClass->propertyData.at(i));
+ Q_ASSERT(engine);
+ for (auto &t : transitions) {
+ if (t.lookup == child) {
+ t.lookup = nullptr;
+ return;
}
- object->setInternalClass(newClass);
}
+ Q_UNREACHABLE();
- Q_ASSERT(object->internalClass()->size == oldClass->size - (accessor ? 2 : 1));
-
- // remove the entry in the property data
- removeFromPropertyData(object, propIdx, accessor);
-
- t.lookup = object->internalClass();
- Q_ASSERT(t.lookup);
}
-uint InternalClass::find(const String *string)
+void InternalClass::removeMember(QV4::Object *object, PropertyKey identifier)
{
- engine->identifierTable->identifier(string);
- const Identifier *id = string->d()->identifier;
+#ifndef QT_NO_DEBUG
+ Heap::InternalClass *oldClass = object->internalClass();
+ Q_ASSERT(oldClass->findEntry(identifier) != nullptr);
+#endif
- uint index = propertyTable.lookup(id);
- if (index < size)
- return index;
+ changeMember(object, identifier, Attr_Invalid);
- return UINT_MAX;
+#ifndef QT_NO_DEBUG
+ // we didn't remove the data slot, just made it inaccessible
+ Q_ASSERT(object->internalClass()->size == oldClass->size);
+#endif
}
-InternalClass *InternalClass::sealed()
+Heap::InternalClass *InternalClass::sealed()
{
- if (m_sealed)
- return m_sealed;
+ if (isSealed)
+ return this;
+
+ bool alreadySealed = !extensible;
+ for (uint i = 0; i < size; ++i) {
+ PropertyAttributes attrs = propertyData.at(i);
+ if (attrs.isEmpty())
+ continue;
+ if (attrs.isConfigurable()) {
+ alreadySealed = false;
+ break;
+ }
+ }
+
+ if (alreadySealed) {
+ isSealed = true;
+ return this;
+ }
+
+ Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Sealed };
+ Transition &t = lookupOrInsertTransition(temp);
+
+ if (t.lookup) {
+ Q_ASSERT(t.lookup && t.lookup->isSealed);
+ return t.lookup;
+ }
+
+ Scope scope(engine);
+ Scoped<QV4::InternalClass> ic(scope, engine->newClass(this));
+ Heap::InternalClass *s = ic->d();
- m_sealed = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable);
- m_sealed = m_sealed->changePrototype(prototype);
for (uint i = 0; i < size; ++i) {
PropertyAttributes attrs = propertyData.at(i);
if (attrs.isEmpty())
continue;
attrs.setConfigurable(false);
- m_sealed = m_sealed->addMember(nameMap.at(i), attrs);
+ s->propertyData.set(i, attrs);
}
- m_sealed = m_sealed->nonExtensible();
+ s->extensible = false;
+ s->isSealed = true;
- m_sealed->m_sealed = m_sealed;
- return m_sealed;
+ t.lookup = s;
+ return s;
}
-InternalClass *InternalClass::frozen()
+Heap::InternalClass *InternalClass::frozen()
{
- if (m_frozen)
- return m_frozen;
+ if (isFrozen)
+ return this;
+
+ bool alreadyFrozen = !extensible;
+ for (uint i = 0; i < size; ++i) {
+ PropertyAttributes attrs = propertyData.at(i);
+ if (attrs.isEmpty())
+ continue;
+ if ((attrs.isData() && attrs.isWritable()) || attrs.isConfigurable()) {
+ alreadyFrozen = false;
+ break;
+ }
+ }
+
+ if (alreadyFrozen) {
+ isSealed = true;
+ isFrozen = true;
+ return this;
+ }
- m_frozen = propertiesFrozen();
- m_frozen = m_frozen->nonExtensible();
+ Transition temp = { { PropertyKey::invalid() }, nullptr, InternalClassTransition::Frozen };
+ Transition &t = lookupOrInsertTransition(temp);
+
+ if (t.lookup) {
+ Q_ASSERT(t.lookup && t.lookup->isSealed && t.lookup->isFrozen);
+ return t.lookup;
+ }
- m_frozen->m_frozen = m_frozen;
- m_frozen->m_sealed = m_frozen;
- return m_frozen;
+ Scope scope(engine);
+ Scoped<QV4::InternalClass> ic(scope, engine->newClass(this));
+ Heap::InternalClass *f = ic->d();
+
+ for (uint i = 0; i < size; ++i) {
+ PropertyAttributes attrs = propertyData.at(i);
+ if (attrs.isEmpty())
+ continue;
+ if (attrs.isData())
+ attrs.setWritable(false);
+ attrs.setConfigurable(false);
+ f->propertyData.set(i, attrs);
+ }
+ f->extensible = false;
+ f->isSealed = true;
+ f->isFrozen = true;
+
+ t.lookup = f;
+ return f;
}
-InternalClass *InternalClass::propertiesFrozen() const
+Heap::InternalClass *InternalClass::propertiesFrozen() const
{
- InternalClass *frozen = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable);
+ Scope scope(engine);
+ Scoped<QV4::InternalClass> frozen(scope, engine->internalClasses(EngineBase::Class_Empty)->changeVTable(vtable));
frozen = frozen->changePrototype(prototype);
for (uint i = 0; i < size; ++i) {
PropertyAttributes attrs = propertyData.at(i);
- if (attrs.isEmpty())
+ if (!nameMap.at(i).isValid())
continue;
- attrs.setWritable(false);
- attrs.setConfigurable(false);
+ if (!attrs.isEmpty()) {
+ attrs.setWritable(false);
+ attrs.setConfigurable(false);
+ }
frozen = frozen->addMember(nameMap.at(i), attrs);
}
- return frozen;
+ return frozen->d();
}
-InternalClass *InternalClass::asProtoClass()
+Heap::InternalClass *InternalClass::asProtoClass()
{
if (isUsedAsProto)
return this;
- Transition temp = { { nullptr }, nullptr, Transition::ProtoClass };
+ Transition temp = { { PropertyKey::invalid() }, nullptr, Transition::ProtoClass };
Transition &t = lookupOrInsertTransition(temp);
if (t.lookup)
return t.lookup;
- InternalClass *newClass = engine->newClass(*this);
+ Heap::InternalClass *newClass = engine->newClass(this);
newClass->isUsedAsProto = true;
t.lookup = newClass;
@@ -476,90 +634,39 @@ InternalClass *InternalClass::asProtoClass()
return newClass;
}
-void InternalClass::destroy()
+static void updateProtoUsage(Heap::Object *o, Heap::InternalClass *ic)
{
- std::vector<InternalClass *> destroyStack;
- destroyStack.reserve(64);
- destroyStack.push_back(this);
-
- while (!destroyStack.empty()) {
- InternalClass *next = destroyStack.back();
- destroyStack.pop_back();
- if (!next->engine)
- continue;
- next->engine = nullptr;
- next->propertyTable.~PropertyHash();
- next->nameMap.~SharedInternalClassData<Identifier *>();
- next->propertyData.~SharedInternalClassData<PropertyAttributes>();
- if (next->m_sealed)
- destroyStack.push_back(next->m_sealed);
- if (next->m_frozen)
- destroyStack.push_back(next->m_frozen);
-
- for (size_t i = 0; i < next->transitions.size(); ++i) {
- Q_ASSERT(next->transitions.at(i).lookup);
- destroyStack.push_back(next->transitions.at(i).lookup);
- }
-
- next->transitions.~vector<Transition>();
+ if (ic->prototype == o)
+ ic->protoId = ic->engine->newProtoId();
+ for (auto &t : ic->transitions) {
+ if (t.lookup)
+ updateProtoUsage(o, t.lookup);
}
}
+
void InternalClass::updateProtoUsage(Heap::Object *o)
{
Q_ASSERT(isUsedAsProto);
- InternalClass *ic = engine->internalClasses[EngineBase::Class_Empty];
+ Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_Empty);
Q_ASSERT(!ic->prototype);
- // only need to go two levels into the IC hierarchy, as prototype changes
- // can only happen there
- for (auto &t : ic->transitions) {
- Q_ASSERT(t.lookup);
- if (t.flags == InternalClassTransition::VTableChange) {
- InternalClass *ic2 = t.lookup;
- for (auto &t2 : ic2->transitions) {
- if (t2.flags == InternalClassTransition::PrototypeChange &&
- t2.lookup->prototype == o)
- ic2->updateInternalClassIdRecursive();
- }
- } else if (t.flags == InternalClassTransition::PrototypeChange && t.lookup->prototype == o) {
- ic->updateInternalClassIdRecursive();
- }
- }
+ Heap::updateProtoUsage(o, ic);
}
-void InternalClass::updateInternalClassIdRecursive()
+void InternalClass::markObjects(Heap::Base *b, MarkStack *stack)
{
- id = engine->newInternalClassId();
- for (auto &t : transitions) {
- Q_ASSERT(t.lookup);
- if (t.flags == InternalClassTransition::VTableChange || t.flags == InternalClassTransition::PrototypeChange)
- continue;
- t.lookup->updateInternalClassIdRecursive();
- }
-}
-
+ 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);
+}
-void InternalClassPool::markObjects(MarkStack *markStack)
-{
- InternalClass *ic = markStack->engine->internalClasses[EngineBase::Class_Empty];
- Q_ASSERT(!ic->prototype);
+}
- // only need to go two levels into the IC hierarchy, as prototype changes
- // can only happen there
- for (auto &t : ic->transitions) {
- Q_ASSERT(t.lookup);
- if (t.flags == InternalClassTransition::VTableChange) {
- InternalClass *ic2 = t.lookup;
- for (auto &t2 : ic2->transitions) {
- if (t2.flags == InternalClassTransition::PrototypeChange)
- t2.lookup->prototype->mark(markStack);
- }
- } else if (t.flags == InternalClassTransition::PrototypeChange) {
- t.lookup->prototype->mark(markStack);
- }
- }
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h
index b689272006..681cbda5f9 100644
--- a/src/qml/jsruntime/qv4internalclass_p.h
+++ b/src/qml/jsruntime/qv4internalclass_p.h
@@ -53,25 +53,30 @@
#include "qv4global_p.h"
#include <QHash>
-#include <private/qqmljsmemorypool_p.h>
-#include <private/qv4identifier_p.h>
+#include <private/qv4propertykey_p.h>
+#include <private/qv4heap_p.h>
QT_BEGIN_NAMESPACE
namespace QV4 {
-struct String;
-struct Object;
-struct Identifier;
struct VTable;
struct MarkStack;
+struct InternalClassEntry {
+ uint index;
+ uint setterIndex;
+ PropertyAttributes attributes;
+ bool isValid() const { return !attributes.isEmpty(); }
+};
+
struct PropertyHashData;
struct PropertyHash
{
struct Entry {
- const Identifier *identifier;
+ PropertyKey identifier;
uint index;
+ uint setterIndex;
};
PropertyHashData *d;
@@ -79,12 +84,12 @@ struct PropertyHash
inline PropertyHash();
inline PropertyHash(const PropertyHash &other);
inline ~PropertyHash();
+ PropertyHash &operator=(const PropertyHash &other);
void addEntry(const Entry &entry, int classSize);
- uint lookup(const Identifier *identifier) const;
-
-private:
- PropertyHash &operator=(const PropertyHash &other);
+ Entry *lookup(PropertyKey identifier) const;
+ int removeIdentifier(PropertyKey identifier, int classSize);
+ void detach(bool grow, int classSize);
};
struct PropertyHashData
@@ -118,40 +123,121 @@ inline PropertyHash::~PropertyHash()
delete d;
}
-inline uint PropertyHash::lookup(const Identifier *identifier) const
+inline PropertyHash &PropertyHash::operator=(const PropertyHash &other)
+{
+ ++other.d->refCount;
+ if (!--d->refCount)
+ delete d;
+ d = other.d;
+ return *this;
+}
+
+
+
+inline PropertyHash::Entry *PropertyHash::lookup(PropertyKey identifier) const
{
Q_ASSERT(d->entries);
- uint idx = identifier->hashValue % d->alloc;
+ uint idx = identifier.id() % d->alloc;
while (1) {
if (d->entries[idx].identifier == identifier)
- return d->entries[idx].index;
- if (!d->entries[idx].identifier)
- return UINT_MAX;
+ return d->entries + idx;
+ if (!d->entries[idx].identifier.isValid())
+ return nullptr;
++idx;
idx %= d->alloc;
}
}
+template<typename T>
+struct SharedInternalClassDataPrivate {
+ SharedInternalClassDataPrivate(ExecutionEngine *)
+ : refcount(1),
+ m_alloc(0),
+ m_size(0),
+ data(nullptr)
+ { }
+ 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; }
+
+
+ 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;
+ }
+
+ 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; }
+
+ void mark(MarkStack *) {}
+
+ int refcount = 1;
+private:
+ uint m_alloc;
+ uint m_size;
+ T *data;
+};
+
+template<>
+struct SharedInternalClassDataPrivate<PropertyKey> {
+ SharedInternalClassDataPrivate(ExecutionEngine *e) : refcount(1), engine(e), data(nullptr) {}
+ SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &other);
+ SharedInternalClassDataPrivate(const SharedInternalClassDataPrivate &other, uint pos, PropertyKey value);
+ ~SharedInternalClassDataPrivate() {}
+
+ void grow();
+ uint alloc() const;
+ uint size() const;
+ void setSize(uint s);
+
+ PropertyKey at(uint i);
+ void set(uint i, PropertyKey t);
+
+ void mark(MarkStack *s);
+
+ int refcount = 1;
+private:
+ ExecutionEngine *engine;
+ Heap::MemberData *data;
+};
+
template <typename T>
struct SharedInternalClassData {
- struct Private {
- Private(int alloc)
- : refcount(1),
- alloc(alloc),
- size(0)
- { data = new T [alloc]; }
- ~Private() { delete [] data; }
-
- int refcount;
- uint alloc;
- uint size;
- T *data;
- };
+ using Private = SharedInternalClassDataPrivate<T>;
Private *d;
- inline SharedInternalClassData() {
- d = new Private(8);
+ inline SharedInternalClassData(ExecutionEngine *e) {
+ d = new Private(e);
}
inline SharedInternalClassData(const SharedInternalClassData &other)
@@ -163,77 +249,72 @@ struct SharedInternalClassData {
if (!--d->refcount)
delete d;
}
+ SharedInternalClassData &operator=(const SharedInternalClassData &other) {
+ ++other.d->refcount;
+ if (!--d->refcount)
+ delete d;
+ d = other.d;
+ return *this;
+ }
void add(uint pos, T value) {
- if (pos < d->size) {
+ if (pos < d->size()) {
Q_ASSERT(d->refcount > 1);
// need to detach
- Private *dd = new Private(pos + 8);
- memcpy(dd->data, d->data, pos*sizeof(T));
- dd->size = pos + 1;
- dd->data[pos] = value;
+ Private *dd = new Private(*d, pos, value);
if (!--d->refcount)
delete d;
d = dd;
return;
}
- Q_ASSERT(pos == d->size);
- if (pos == d->alloc) {
- T *n = new T[d->alloc * 2];
- memcpy(n, d->data, d->alloc*sizeof(T));
- delete [] d->data;
- d->data = n;
- d->alloc *= 2;
- }
- d->data[pos] = value;
- ++d->size;
+ Q_ASSERT(pos == d->size());
+ if (pos == d->alloc())
+ d->grow();
+ d->setSize(d->size() + 1);
+ d->set(pos, value);
}
void set(uint pos, T value) {
- Q_ASSERT(pos < d->size);
+ Q_ASSERT(pos < d->size());
if (d->refcount > 1) {
// need to detach
- Private *dd = new Private(d->alloc);
- memcpy(dd->data, d->data, d->size*sizeof(T));
- dd->size = d->size;
+ Private *dd = new Private(*d);
if (!--d->refcount)
delete d;
d = dd;
}
- d->data[pos] = value;
+ d->set(pos, value);
}
- T *constData() const {
- return d->data;
- }
T at(uint i) const {
- Q_ASSERT(i < d->size);
- return d->data[i];
+ Q_ASSERT(i < d->size());
+ return d->at(i);
}
T operator[] (uint i) {
- Q_ASSERT(i < d->size);
- return d->data[i];
+ Q_ASSERT(i < d->size());
+ return d->at(i);
}
-private:
- SharedInternalClassData &operator=(const SharedInternalClassData &other);
+ void mark(MarkStack *s) { d->mark(s); }
};
struct InternalClassTransition
{
union {
- Identifier *id;
+ PropertyKey id;
const VTable *vtable;
Heap::Object *prototype;
};
- InternalClass *lookup;
+ Heap::InternalClass *lookup;
int flags;
enum {
// range 0-0xff is reserved for attribute changes
NotExtensible = 0x100,
VTableChange = 0x200,
PrototypeChange = 0x201,
- ProtoClass = 0x202
+ ProtoClass = 0x202,
+ Sealed = 0x203,
+ Frozen = 0x204
};
bool operator==(const InternalClassTransition &other) const
@@ -243,79 +324,168 @@ struct InternalClassTransition
{ return id < other.id || (id == other.id && flags < other.flags); }
};
-struct InternalClass : public QQmlJS::Managed {
- int id = 0; // unique across the engine, gets changed also when proto chain changes
+namespace Heap {
+
+struct InternalClass : Base {
ExecutionEngine *engine;
const VTable *vtable;
+ quintptr protoId; // unique across the engine, gets changed whenever the proto chain changes
Heap::Object *prototype;
+ InternalClass *parent;
PropertyHash propertyTable; // id to valueIndex
- SharedInternalClassData<Identifier *> nameMap;
+ SharedInternalClassData<PropertyKey> nameMap;
SharedInternalClassData<PropertyAttributes> propertyData;
typedef InternalClassTransition Transition;
std::vector<Transition> transitions;
InternalClassTransition &lookupOrInsertTransition(const InternalClassTransition &t);
- InternalClass *m_sealed;
- InternalClass *m_frozen;
-
uint size;
bool extensible;
- bool isUsedAsProto = false;
+ bool isSealed;
+ bool isFrozen;
+ bool isUsedAsProto;
+ void init(ExecutionEngine *engine);
+ void init(InternalClass *other);
+ void destroy();
+
+ Q_QML_PRIVATE_EXPORT QString keyAt(uint index) const;
Q_REQUIRED_RESULT InternalClass *nonExtensible();
- Q_REQUIRED_RESULT InternalClass *changeVTable(const VTable *vt) {
- if (vtable == vt)
- return this;
- return changeVTableImpl(vt);
+
+ static void addMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, InternalClassEntry *entry);
+ Q_REQUIRED_RESULT InternalClass *addMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry = nullptr);
+ Q_REQUIRED_RESULT InternalClass *changeMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry = nullptr);
+ static void changeMember(QV4::Object *object, PropertyKey id, PropertyAttributes data, InternalClassEntry *entry = nullptr);
+ static void removeMember(QV4::Object *object, PropertyKey identifier);
+ PropertyHash::Entry *findEntry(const PropertyKey id)
+ {
+ Q_ASSERT(id.isStringOrSymbol());
+
+ PropertyHash::Entry *e = propertyTable.lookup(id);
+ if (e && e->index < size)
+ return e;
+
+ return nullptr;
}
- Q_REQUIRED_RESULT InternalClass *changePrototype(Heap::Object *proto) {
- if (prototype == proto)
- return this;
- return changePrototypeImpl(proto);
+
+ InternalClassEntry find(const PropertyKey id)
+ {
+ Q_ASSERT(id.isStringOrSymbol());
+
+ PropertyHash::Entry *e = propertyTable.lookup(id);
+ if (e && e->index < size) {
+ PropertyAttributes a = propertyData.at(e->index);
+ if (!a.isEmpty())
+ return { e->index, e->setterIndex, a };
+ }
+
+ return { UINT_MAX, UINT_MAX, Attr_Invalid };
+ }
+
+ struct IndexAndAttribute {
+ uint index;
+ PropertyAttributes attrs;
+ bool isValid() const { return index != UINT_MAX; }
+ };
+
+ IndexAndAttribute findValueOrGetter(const PropertyKey id)
+ {
+ Q_ASSERT(id.isStringOrSymbol());
+
+ PropertyHash::Entry *e = propertyTable.lookup(id);
+ if (e && e->index < size) {
+ PropertyAttributes a = propertyData.at(e->index);
+ if (!a.isEmpty())
+ return { e->index, a };
+ }
+
+ return { UINT_MAX, Attr_Invalid };
+ }
+
+ IndexAndAttribute findValueOrSetter(const PropertyKey id)
+ {
+ Q_ASSERT(id.isStringOrSymbol());
+
+ PropertyHash::Entry *e = propertyTable.lookup(id);
+ if (e && e->index < size) {
+ PropertyAttributes a = propertyData.at(e->index);
+ if (!a.isEmpty()) {
+ if (a.isAccessor()) {
+ Q_ASSERT(e->setterIndex != UINT_MAX);
+ return { e->setterIndex, a };
+ }
+ return { e->index, a };
+ }
+ }
+
+ return { UINT_MAX, Attr_Invalid };
}
- static void addMember(Object *object, String *string, PropertyAttributes data, uint *index);
- Q_REQUIRED_RESULT InternalClass *addMember(String *string, PropertyAttributes data, uint *index = nullptr);
- Q_REQUIRED_RESULT InternalClass *addMember(Identifier *identifier, PropertyAttributes data, uint *index = nullptr);
- Q_REQUIRED_RESULT InternalClass *changeMember(Identifier *identifier, PropertyAttributes data, uint *index = nullptr);
- static void changeMember(Object *object, String *string, PropertyAttributes data, uint *index = nullptr);
- static void removeMember(Object *object, Identifier *id);
- uint find(const String *string);
- uint find(const Identifier *id)
+ uint indexOfValueOrGetter(const PropertyKey id)
{
- uint index = propertyTable.lookup(id);
- if (index < size)
- return index;
+ Q_ASSERT(id.isStringOrSymbol());
+
+ PropertyHash::Entry *e = propertyTable.lookup(id);
+ if (e && e->index < size) {
+ Q_ASSERT(!propertyData.at(e->index).isEmpty());
+ return e->index;
+ }
return UINT_MAX;
}
+ bool verifyIndex(const PropertyKey id, uint index)
+ {
+ Q_ASSERT(id.isStringOrSymbol());
+
+ PropertyHash::Entry *e = propertyTable.lookup(id);
+ if (e && e->index < size) {
+ Q_ASSERT(!propertyData.at(e->index).isEmpty());
+ return e->index == index;
+ }
+
+ return false;
+ }
+
Q_REQUIRED_RESULT InternalClass *sealed();
Q_REQUIRED_RESULT InternalClass *frozen();
Q_REQUIRED_RESULT InternalClass *propertiesFrozen() const;
Q_REQUIRED_RESULT InternalClass *asProtoClass();
- void destroy();
+ Q_REQUIRED_RESULT InternalClass *changeVTable(const VTable *vt) {
+ if (vtable == vt)
+ return this;
+ return changeVTableImpl(vt);
+ }
+ Q_REQUIRED_RESULT InternalClass *changePrototype(Heap::Object *proto) {
+ if (prototype == proto)
+ return this;
+ return changePrototypeImpl(proto);
+ }
void updateProtoUsage(Heap::Object *o);
+ static void markObjects(Heap::Base *ic, MarkStack *stack);
+
private:
Q_QML_EXPORT InternalClass *changeVTableImpl(const VTable *vt);
Q_QML_EXPORT InternalClass *changePrototypeImpl(Heap::Object *proto);
- InternalClass *addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index);
- void updateInternalClassIdRecursive();
+ InternalClass *addMemberImpl(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry);
+
+ void removeChildEntry(InternalClass *child);
friend struct ExecutionEngine;
- InternalClass(ExecutionEngine *engine);
- InternalClass(const InternalClass &other);
};
-struct InternalClassPool : public QQmlJS::MemoryPool
+inline
+void Base::markObjects(Base *b, MarkStack *stack)
{
- void markObjects(MarkStack *markStack);
-};
+ b->internalClass->mark(stack);
+}
+
+}
}
diff --git a/src/qml/jsruntime/qv4iterator.cpp b/src/qml/jsruntime/qv4iterator.cpp
new file mode 100644
index 0000000000..a543565b37
--- /dev/null
+++ b/src/qml/jsruntime/qv4iterator.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** 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 <qv4iterator_p.h>
+#include <qv4symbol_p.h>
+#include <qv4engine_p.h>
+
+using namespace QV4;
+
+void IteratorPrototype::init(ExecutionEngine *engine)
+{
+ defineDefaultProperty(engine->symbol_iterator(), method_iterator, 0);
+}
+
+ReturnedValue IteratorPrototype::method_iterator(const FunctionObject *, const Value *thisObject, const Value *, int)
+{
+ return thisObject->asReturnedValue();
+}
+
+
+ReturnedValue IteratorPrototype::createIterResultObject(ExecutionEngine *engine, const Value &value, bool done)
+{
+ Scope scope(engine);
+ ScopedObject obj(scope, engine->newObject());
+ obj->set(ScopedString(scope, engine->newString(QStringLiteral("value"))), value, Object::DoNotThrow);
+ obj->set(ScopedString(scope, engine->newString(QStringLiteral("done"))), Value::fromBoolean(done), Object::DoNotThrow);
+ return obj->asReturnedValue();
+}
+
diff --git a/src/qml/jsruntime/qv4iterator_p.h b/src/qml/jsruntime/qv4iterator_p.h
new file mode 100644
index 0000000000..28e337d21b
--- /dev/null
+++ b/src/qml/jsruntime/qv4iterator_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** 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 QV4ITERATOR_P_H
+#define QV4ITERATOR_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 "qv4object_p.h"
+#include "qv4arraydata_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+namespace QV4 {
+
+enum IteratorKind {
+ KeyIteratorKind,
+ ValueIteratorKind,
+ KeyValueIteratorKind
+};
+
+struct IteratorPrototype : Object
+{
+ void init(ExecutionEngine *engine);
+
+ static ReturnedValue method_iterator(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue createIterResultObject(ExecutionEngine *engine, const Value &value, bool done);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4ARRAYITERATOR_P_H
+
diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h
index c676b57c51..31689b1ba1 100644
--- a/src/qml/jsruntime/qv4jscall_p.h
+++ b/src/qml/jsruntime/qv4jscall_p.h
@@ -55,6 +55,7 @@
#include "qv4functionobject_p.h"
#include "qv4context_p.h"
#include "qv4scopedvalue_p.h"
+#include "qv4stackframe_p.h"
QT_BEGIN_NAMESPACE
@@ -67,7 +68,7 @@ struct JSCallData {
if (thisObject)
this->thisObject = const_cast<Value *>(thisObject);
else
- this->thisObject = scope.alloc(1);
+ this->thisObject = scope.alloc();
if (argv)
this->args = const_cast<Value *>(argv);
else
@@ -80,12 +81,12 @@ struct JSCallData {
CallData *callData(const FunctionObject *f = nullptr) const {
int size = int(offsetof(QV4::CallData, args)/sizeof(QV4::Value)) + argc;
- CallData *ptr = reinterpret_cast<CallData *>(scope.engine->jsStackTop);
- scope.engine->jsStackTop += size;
+ CallData *ptr = reinterpret_cast<CallData *>(scope.alloc<Scope::Uninitialized>(size));
ptr->function = Encode::undefined();
ptr->context = Encode::undefined();
ptr->accumulator = Encode::undefined();
ptr->thisObject = thisObject->asReturnedValue();
+ ptr->newTarget = Encode::undefined();
ptr->setArgc(argc);
if (argc)
memcpy(ptr->args, args, argc*sizeof(Value));
@@ -102,13 +103,13 @@ struct JSCallData {
inline
ReturnedValue FunctionObject::callAsConstructor(const JSCallData &data) const
{
- return d()->jsConstruct(this, data.args, data.argc);
+ return callAsConstructor(data.args, data.argc, this);
}
inline
ReturnedValue FunctionObject::call(const JSCallData &data) const
{
- return d()->jsCall(this, data.thisObject, data.args, data.argc);
+ return call(data.thisObject, data.args, data.argc);
}
diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp
index c3569c29d2..0c5436a0d6 100644
--- a/src/qml/jsruntime/qv4jsonobject.cpp
+++ b/src/qml/jsruntime/qv4jsonobject.cpp
@@ -47,6 +47,7 @@
#include <qv4variantobject_p.h>
#include "qv4string_p.h"
#include "qv4jscall_p.h"
+#include <qv4symbol_p.h>
#include <qstack.h>
#include <qstringlist.h>
@@ -260,11 +261,12 @@ bool JsonParser::parseMember(Object *o)
if (!parseValue(val))
return false;
- ScopedString s(scope, engine->newIdentifier(key));
- uint idx = s->asArrayIndex();
- if (idx < UINT_MAX) {
- o->putIndexed(idx, val);
+ ScopedString s(scope, engine->newString(key));
+ PropertyKey skey = s->toPropertyKey();
+ if (skey.isArrayIndex()) {
+ o->put(skey.asArrayIndex(), val);
} else {
+ // avoid trouble with properties named __proto__
o->insertMember(s, val);
}
@@ -338,7 +340,7 @@ bool JsonParser::parseValue(Value *val)
if (*json++ == 'u' &&
*json++ == 'l' &&
*json++ == 'l') {
- *val = Primitive::nullValue();
+ *val = Value::nullValue();
DEBUG << "value: null";
END;
return true;
@@ -353,7 +355,7 @@ bool JsonParser::parseValue(Value *val)
if (*json++ == 'r' &&
*json++ == 'u' &&
*json++ == 'e') {
- *val = Primitive::fromBoolean(true);
+ *val = Value::fromBoolean(true);
DEBUG << "value: true";
END;
return true;
@@ -369,7 +371,7 @@ bool JsonParser::parseValue(Value *val)
*json++ == 'l' &&
*json++ == 's' &&
*json++ == 'e') {
- *val = Primitive::fromBoolean(false);
+ *val = Value::fromBoolean(false);
DEBUG << "value: false";
END;
return true;
@@ -477,7 +479,7 @@ bool JsonParser::parseNumber(Value *val)
bool ok;
int n = number.toInt(&ok);
if (ok && n < (1<<25) && n > -(1<<25)) {
- *val = Primitive::fromInt32(n);
+ *val = Value::fromInt32(n);
END;
return true;
}
@@ -492,7 +494,7 @@ bool JsonParser::parseNumber(Value *val)
return false;
}
- * val = Primitive::fromDouble(d);
+ * val = Value::fromDouble(d);
END;
return true;
@@ -743,7 +745,7 @@ QString Stringify::Str(const QString &key, const Value &v)
o = value->asReturnedValue();
if (o) {
if (!o->as<FunctionObject>()) {
- if (o->as<ArrayObject>() || o->isListType()) {
+ if (o->isArrayLike()) {
return JA(o.getPointer());
} else {
return JO(o);
@@ -846,7 +848,7 @@ QString Stringify::JA(Object *a)
ScopedValue v(scope);
for (uint i = 0; i < len; ++i) {
bool exists;
- v = a->getIndexed(i, &exists);
+ v = a->get(i, &exists);
if (!exists) {
partial += QStringLiteral("null");
continue;
@@ -881,6 +883,8 @@ void Heap::JsonObject::init()
o->defineDefaultProperty(QStringLiteral("parse"), QV4::JsonObject::method_parse, 2);
o->defineDefaultProperty(QStringLiteral("stringify"), QV4::JsonObject::method_stringify, 3);
+ ScopedString json(scope, scope.engine->newString(QStringLiteral("JSON")));
+ o->defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), json);
}
@@ -908,7 +912,7 @@ ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value
Scope scope(b);
Stringify stringify(scope.engine);
- ScopedObject o(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+ ScopedObject o(scope, argc > 1 ? argv[1] : Value::undefinedValue());
if (o) {
stringify.replacerFunction = o->as<FunctionObject>();
if (o->isArrayObject()) {
@@ -916,7 +920,7 @@ ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value
stringify.propertyList = static_cast<QV4::String *>(scope.alloc(arrayLen));
for (uint i = 0; i < arrayLen; ++i) {
Value *v = stringify.propertyList + i;
- *v = o->getIndexed(i);
+ *v = o->get(i);
if (v->as<NumberObject>() || v->as<StringObject>() || v->isNumber())
*v = v->toString(scope.engine);
if (!v->isString()) {
@@ -933,7 +937,7 @@ ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value
}
}
- ScopedValue s(scope, argc > 2 ? argv[2] : Primitive::undefinedValue());
+ ScopedValue s(scope, argc > 2 ? argv[2] : Value::undefinedValue());
if (NumberObject *n = s->as<NumberObject>())
s = Encode(n->value());
else if (StringObject *so = s->as<StringObject>())
@@ -946,7 +950,7 @@ ReturnedValue JsonObject::method_stringify(const FunctionObject *b, const Value
}
- ScopedValue arg0(scope, argc ? argv[0] : Primitive::undefinedValue());
+ ScopedValue arg0(scope, argc ? argv[0] : Value::undefinedValue());
QString result = stringify.Str(QString(), arg0);
if (result.isEmpty() || scope.engine->hasException)
RETURN_UNDEFINED();
@@ -1078,7 +1082,7 @@ QJsonArray JsonObject::toJsonArray(const ArrayObject *a, V4ObjectSet &visitedObj
ScopedValue v(scope);
quint32 length = a->getLength();
for (quint32 i = 0; i < length; ++i) {
- v = a->getIndexed(i);
+ v = a->get(i);
if (v->as<FunctionObject>())
v = Encode::null();
result.append(toJsonValue(v, visitedObjects));
diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp
index 52ab03cd94..994daa864b 100644
--- a/src/qml/jsruntime/qv4lookup.cpp
+++ b/src/qml/jsruntime/qv4lookup.cpp
@@ -47,13 +47,13 @@ QT_BEGIN_NAMESPACE
using namespace QV4;
-void Lookup::resolveProtoGetter(Identifier *name, const Heap::Object *proto)
+void Lookup::resolveProtoGetter(PropertyKey name, const Heap::Object *proto)
{
while (proto) {
- uint index = proto->internalClass->find(name);
- if (index != UINT_MAX) {
- PropertyAttributes attrs = proto->internalClass->propertyData.at(index);
- protoLookup.data = proto->propertyData(index);
+ auto index = proto->internalClass->findValueOrGetter(name);
+ if (index.isValid()) {
+ PropertyAttributes attrs = index.attrs;
+ protoLookup.data = proto->propertyData(index.index);
if (attrs.isData()) {
getter = getterProto;
} else {
@@ -70,29 +70,34 @@ void Lookup::resolveProtoGetter(Identifier *name, const Heap::Object *proto)
ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *object)
{
Heap::Object *obj = object->d();
- Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ 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);
+ }
- uint index = obj->internalClass->find(name);
- if (index != UINT_MAX) {
- PropertyAttributes attrs = obj->internalClass->propertyData.at(index);
+ auto index = obj->internalClass->findValueOrGetter(name);
+ if (index.isValid()) {
+ PropertyAttributes attrs = index.attrs;
uint nInline = obj->vtable()->nInlineProperties;
if (attrs.isData()) {
- if (index < obj->vtable()->nInlineProperties) {
- index += obj->vtable()->inlinePropertyOffset;
+ if (index.index < obj->vtable()->nInlineProperties) {
+ index.index += obj->vtable()->inlinePropertyOffset;
getter = getter0Inline;
} else {
- index -= nInline;
+ index.index -= nInline;
getter = getter0MemberData;
}
} else {
getter = getterAccessor;
}
objectLookup.ic = obj->internalClass;
- objectLookup.offset = index;
+ objectLookup.offset = index.index;
return getter(this, engine, *object);
}
- protoLookup.icIdentifier = obj->internalClass->id;
+ protoLookup.protoId = obj->internalClass->protoId;
resolveProtoGetter(name, obj->prototype());
return getter(this, engine, *object);
}
@@ -109,11 +114,12 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu
break;
case Value::Managed_Type: {
// ### Should move this over to the Object path, as strings also have an internalClass
- Q_ASSERT(object.isString());
- primitiveLookup.proto = engine->stringPrototype()->d();
+ Q_ASSERT(object.isStringOrSymbol());
+ primitiveLookup.proto = static_cast<const Managed &>(object).internalClass()->prototype;
+ Q_ASSERT(primitiveLookup.proto);
Scope scope(engine);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- if (name->equals(engine->id_length())) {
+ if (object.isString() && name->equals(engine->id_length())) {
// special case, as the property is on the object itself
getter = stringLengthGetter;
return stringLengthGetter(this, engine, object);
@@ -125,8 +131,8 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu
primitiveLookup.proto = engine->numberPrototype()->d();
}
- Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- protoLookup.icIdentifier = primitiveLookup.proto->internalClass->id;
+ PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ protoLookup.protoId = primitiveLookup.proto->internalClass->protoId;
resolveProtoGetter(name, primitiveLookup.proto);
if (getter == getterProto)
@@ -139,8 +145,8 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu
ReturnedValue Lookup::resolveGlobalGetter(ExecutionEngine *engine)
{
Object *o = engine->globalObject;
- Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- protoLookup.icIdentifier = o->internalClass()->id;
+ PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ protoLookup.protoId = o->internalClass()->protoId;
resolveProtoGetter(name, o->d());
if (getter == getterProto)
@@ -188,16 +194,16 @@ ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const
return result;
}
if (first.getter == getterProto && second.getter == getterProto) {
- l->protoLookupTwoClasses.icIdentifier = first.protoLookup.icIdentifier;
- l->protoLookupTwoClasses.icIdentifier2 = second.protoLookup.icIdentifier;
+ l->protoLookupTwoClasses.protoId = first.protoLookup.protoId;
+ l->protoLookupTwoClasses.protoId2 = second.protoLookup.protoId;
l->protoLookupTwoClasses.data = first.protoLookup.data;
l->protoLookupTwoClasses.data2 = second.protoLookup.data;
l->getter = getterProtoTwoClasses;
return result;
}
if (first.getter == getterProtoAccessor && second.getter == getterProtoAccessor) {
- l->protoLookupTwoClasses.icIdentifier = first.protoLookup.icIdentifier;
- l->protoLookupTwoClasses.icIdentifier2 = second.protoLookup.icIdentifier;
+ l->protoLookupTwoClasses.protoId = first.protoLookup.protoId;
+ l->protoLookupTwoClasses.protoId2 = second.protoLookup.protoId;
l->protoLookupTwoClasses.data = first.protoLookup.data;
l->protoLookupTwoClasses.data2 = second.protoLookup.data;
l->getter = getterProtoAccessorTwoClasses;
@@ -250,7 +256,7 @@ ReturnedValue Lookup::getterProto(Lookup *l, ExecutionEngine *engine, const Valu
// the internal class won't match
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
- if (l->protoLookup.icIdentifier == o->internalClass->id)
+ if (l->protoLookup.protoId == o->internalClass->protoId)
return l->protoLookup.data->asReturnedValue();
}
return getterTwoClasses(l, engine, object);
@@ -266,6 +272,9 @@ ReturnedValue Lookup::getter0Inlinegetter0Inline(Lookup *l, ExecutionEngine *eng
return o->inlinePropertyDataWithOffset(l->objectLookupTwoClasses.offset)->asReturnedValue();
if (l->objectLookupTwoClasses.ic2 == o->internalClass)
return o->inlinePropertyDataWithOffset(l->objectLookupTwoClasses.offset2)->asReturnedValue();
+ Value obj = Value::fromHeapObject(o);
+ Value str = Value::fromHeapObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]);
+ return static_cast<Object &>(obj).get(&static_cast<String &>(str));
}
l->getter = getterFallback;
return getterFallback(l, engine, object);
@@ -281,6 +290,9 @@ ReturnedValue Lookup::getter0Inlinegetter0MemberData(Lookup *l, ExecutionEngine
return o->inlinePropertyDataWithOffset(l->objectLookupTwoClasses.offset)->asReturnedValue();
if (l->objectLookupTwoClasses.ic2 == o->internalClass)
return o->memberData->values.data()[l->objectLookupTwoClasses.offset2].asReturnedValue();
+ Value obj = Value::fromHeapObject(o);
+ Value str = Value::fromHeapObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]);
+ return static_cast<Object &>(obj).get(&static_cast<String &>(str));
}
l->getter = getterFallback;
return getterFallback(l, engine, object);
@@ -296,6 +308,9 @@ ReturnedValue Lookup::getter0MemberDatagetter0MemberData(Lookup *l, ExecutionEng
return o->memberData->values.data()[l->objectLookupTwoClasses.offset].asReturnedValue();
if (l->objectLookupTwoClasses.ic2 == o->internalClass)
return o->memberData->values.data()[l->objectLookupTwoClasses.offset2].asReturnedValue();
+ Value obj = Value::fromHeapObject(o);
+ Value str = Value::fromHeapObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]);
+ return static_cast<Object &>(obj).get(&static_cast<String &>(str));
}
l->getter = getterFallback;
return getterFallback(l, engine, object);
@@ -307,11 +322,13 @@ ReturnedValue Lookup::getterProtoTwoClasses(Lookup *l, ExecutionEngine *engine,
// the internal class won't match
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
- if (l->protoLookupTwoClasses.icIdentifier == o->internalClass->id)
+ if (l->protoLookupTwoClasses.protoId == o->internalClass->protoId)
return l->protoLookupTwoClasses.data->asReturnedValue();
- if (l->protoLookupTwoClasses.icIdentifier2 == o->internalClass->id)
+ if (l->protoLookupTwoClasses.protoId2 == o->internalClass->protoId)
return l->protoLookupTwoClasses.data2->asReturnedValue();
- return getterFallback(l, engine, object);
+ Value obj = Value::fromHeapObject(o);
+ Value str = Value::fromHeapObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]);
+ return static_cast<Object &>(obj).get(&static_cast<String &>(str));
}
l->getter = getterFallback;
return getterFallback(l, engine, object);
@@ -340,14 +357,13 @@ ReturnedValue Lookup::getterProtoAccessor(Lookup *l, ExecutionEngine *engine, co
// we can safely cast to a QV4::Object here. If object is actually a string,
// the internal class won't match
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
- if (o && l->protoLookup.icIdentifier == o->internalClass->id) {
+ if (o && l->protoLookup.protoId == o->internalClass->protoId) {
const Value *getter = l->protoLookup.data;
if (!getter->isFunctionObject()) // ### catch at resolve time
return Encode::undefined();
return static_cast<const FunctionObject *>(getter)->call(&object, nullptr, 0);
}
- l->getter = getterTwoClasses;
return getterTwoClasses(l, engine, object);
}
@@ -358,9 +374,9 @@ ReturnedValue Lookup::getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
const Value *getter = nullptr;
- if (l->protoLookupTwoClasses.icIdentifier == o->internalClass->id)
+ if (l->protoLookupTwoClasses.protoId == o->internalClass->protoId)
getter = l->protoLookupTwoClasses.data;
- else if (l->protoLookupTwoClasses.icIdentifier2 == o->internalClass->id)
+ else if (l->protoLookupTwoClasses.protoId2 == o->internalClass->protoId)
getter = l->protoLookupTwoClasses.data2;
if (getter) {
if (!getter->isFunctionObject()) // ### catch at resolve time
@@ -373,11 +389,29 @@ ReturnedValue Lookup::getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *
return getterFallback(l, engine, object);
}
+ReturnedValue Lookup::getterIndexed(Lookup *l, ExecutionEngine *engine, const Value &object)
+{
+ Object *o = object.objectValue();
+ if (o) {
+ Heap::Object *ho = o->d();
+ if (ho->arrayData && ho->arrayData->type == Heap::ArrayData::Simple) {
+ Heap::SimpleArrayData *s = ho->arrayData.cast<Heap::SimpleArrayData>();
+ if (l->indexedLookup.index < s->values.size)
+ if (!s->data(l->indexedLookup.index).isEmpty())
+ return s->data(l->indexedLookup.index).asReturnedValue();
+ }
+ return o->get(l->indexedLookup.index);
+ }
+ l->getter = getterFallback;
+ return getterFallback(l, engine, object);
+
+}
+
ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object)
{
if (object.type() == l->primitiveLookup.type) {
Heap::Object *o = l->primitiveLookup.proto;
- if (l->primitiveLookup.icIdentifier == o->internalClass->id)
+ if (l->primitiveLookup.protoId == o->internalClass->protoId)
return l->primitiveLookup.data->asReturnedValue();
}
l->getter = getterGeneric;
@@ -388,7 +422,7 @@ ReturnedValue Lookup::primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine
{
if (object.type() == l->primitiveLookup.type) {
Heap::Object *o = l->primitiveLookup.proto;
- if (l->primitiveLookup.icIdentifier == o->internalClass->id) {
+ if (l->primitiveLookup.protoId == o->internalClass->protoId) {
const Value *getter = l->primitiveLookup.data;
if (!getter->isFunctionObject()) // ### catch at resolve time
return Encode::undefined();
@@ -417,7 +451,7 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine)
ReturnedValue Lookup::globalGetterProto(Lookup *l, ExecutionEngine *engine)
{
Heap::Object *o = engine->globalObject->d();
- if (l->protoLookup.icIdentifier == o->internalClass->id)
+ if (l->protoLookup.protoId == o->internalClass->protoId)
return l->protoLookup.data->asReturnedValue();
l->globalGetter = globalGetterGeneric;
return globalGetterGeneric(l, engine);
@@ -426,7 +460,7 @@ ReturnedValue Lookup::globalGetterProto(Lookup *l, ExecutionEngine *engine)
ReturnedValue Lookup::globalGetterProtoAccessor(Lookup *l, ExecutionEngine *engine)
{
Heap::Object *o = engine->globalObject->d();
- if (l->protoLookup.icIdentifier == o->internalClass->id) {
+ if (l->protoLookup.protoId == o->internalClass->protoId) {
const Value *getter = l->protoLookup.data;
if (!getter->isFunctionObject()) // ### catch at resolve time
return Encode::undefined();
@@ -442,16 +476,18 @@ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value
Scope scope(engine);
ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- InternalClass *c = object->internalClass();
- uint idx = c->find(name);
- if (idx != UINT_MAX) {
- if (object->isArrayObject() && idx == Heap::ArrayObject::LengthPropertyIndex) {
+ 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 (object->internalClass()->propertyData[idx].isData() && object->internalClass()->propertyData[idx].isWritable()) {
+ } else if (idx.attrs.isData() && idx.attrs.isWritable()) {
objectLookup.ic = object->internalClass();
- objectLookup.offset = idx;
- setter = idx < object->d()->vtable()->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0;
+ objectLookup.offset = idx.index;
+ setter = idx.index < object->d()->vtable()->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0;
return setter(this, engine, *object, value);
} else {
// ### handle setter
@@ -460,8 +496,8 @@ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value
return setter(this, engine, *object, value);
}
- insertionLookup.icIdentifier = c->id;
- if (!object->put(name, value)) {
+ insertionLookup.protoId = c->protoId;
+ if (!object->put(key, value)) {
setter = Lookup::setterFallback;
return false;
}
@@ -471,13 +507,13 @@ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value
setter = setterFallback;
return true;
}
- idx = object->internalClass()->find(name);
- if (idx == UINT_MAX) { // ### can this even happen?
+ 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;
+ insertionLookup.offset = idx.index;
setter = setterInsert;
return true;
}
@@ -487,6 +523,9 @@ bool Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, co
if (object.isObject())
return l->resolveSetter(engine, static_cast<Object *>(&object), value);
+ if (engine->currentStackFrame->v4Function->isStrict())
+ return false;
+
Scope scope(engine);
ScopedObject o(scope, RuntimeHelpers::convertToObject(scope.engine, object));
if (!o) // type error
@@ -574,7 +613,7 @@ bool Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, c
bool Lookup::setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
Object *o = static_cast<Object *>(object.managed());
- if (o && o->internalClass()->id == l->insertionLookup.icIdentifier) {
+ if (o && o->internalClass()->protoId == l->insertionLookup.protoId) {
o->setInternalClass(l->insertionLookup.newClass);
o->d()->setProperty(engine, l->insertionLookup.offset, value);
return true;
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h
index 5f507733fd..83e561863b 100644
--- a/src/qml/jsruntime/qv4lookup_p.h
+++ b/src/qml/jsruntime/qv4lookup_p.h
@@ -65,7 +65,6 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
struct Lookup {
- enum { Size = 4 };
union {
ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object);
ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine);
@@ -73,44 +72,57 @@ struct Lookup {
};
union {
struct {
- InternalClass *ic;
+ Heap::Base *h1;
+ Heap::Base *h2;
+ quintptr unused;
+ quintptr unused2;
+ } markDef;
+ struct {
+ Heap::InternalClass *ic;
+ quintptr _unused;
int offset;
} objectLookup;
struct {
+ quintptr protoId;
+ quintptr _unused;
const Value *data;
- int icIdentifier;
} protoLookup;
struct {
- InternalClass *ic;
- InternalClass *ic2;
+ Heap::InternalClass *ic;
+ Heap::InternalClass *ic2;
int offset;
int offset2;
} objectLookupTwoClasses;
struct {
+ quintptr protoId;
+ quintptr protoId2;
const Value *data;
const Value *data2;
- int icIdentifier;
- int icIdentifier2;
} protoLookupTwoClasses;
struct {
// Make sure the next two values are in sync with protoLookup
- const Value *data;
- int icIdentifier;
- unsigned type;
+ quintptr protoId;
Heap::Object *proto;
+ const Value *data;
+ quintptr type;
} primitiveLookup;
struct {
- InternalClass *newClass;
- int icIdentifier;
+ Heap::InternalClass *newClass;
+ quintptr protoId;
int offset;
} insertionLookup;
+ struct {
+ quintptr _unused;
+ quintptr _unused2;
+ uint index;
+ } indexedLookup;
};
uint nameIndex;
ReturnedValue resolveGetter(ExecutionEngine *engine, const Object *object);
ReturnedValue resolvePrimitiveGetter(ExecutionEngine *engine, const Value &object);
ReturnedValue resolveGlobalGetter(ExecutionEngine *engine);
- void resolveProtoGetter(Identifier *name, const Heap::Object *proto);
+ void resolveProtoGetter(PropertyKey name, const Heap::Object *proto);
static ReturnedValue getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object);
@@ -126,6 +138,7 @@ struct Lookup {
static ReturnedValue getterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterProtoAccessor(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object);
+ static ReturnedValue getterIndexed(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object);
@@ -137,13 +150,24 @@ struct Lookup {
bool resolveSetter(ExecutionEngine *engine, Object *object, const Value &value);
static bool setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
- static bool setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
+ Q_NEVER_INLINE static bool setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool arrayLengthSetter(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
+
+ void markObjects(MarkStack *stack) {
+ if (markDef.h1 && !(reinterpret_cast<quintptr>(markDef.h1) & 1))
+ markDef.h1->mark(stack);
+ if (markDef.h2 && !(reinterpret_cast<quintptr>(markDef.h2) & 1))
+ markDef.h2->mark(stack);
+ }
+
+ void clear() {
+ memset(&markDef, 0, sizeof(markDef));
+ }
};
Q_STATIC_ASSERT(std::is_standard_layout<Lookup>::value);
diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp
index b50e5f0355..d51b03d90b 100644
--- a/src/qml/jsruntime/qv4managed.cpp
+++ b/src/qml/jsruntime/qv4managed.cpp
@@ -43,34 +43,23 @@
using namespace QV4;
+DEFINE_MANAGED_VTABLE(Managed);
-const VTable Managed::static_vtbl =
-{
- nullptr,
- 0,
- 0,
- Managed::IsExecutionContext,
- Managed::IsString,
- Managed::IsObject,
- Managed::IsFunctionObject,
- Managed::IsErrorObject,
- Managed::IsArrayData,
- 0,
- Managed::MyType,
- "Managed",
- nullptr,
- nullptr /*markObjects*/,
- isEqualTo
-};
+DEFINE_MANAGED_VTABLE(InternalClass);
QString Managed::className() const
{
const char *s = nullptr;
- switch (Type(d()->vtable()->type)) {
+ switch (Type(vtable()->type)) {
case Type_Invalid:
- case Type_String:
return QString();
+ case Type_String:
+ s = "String";
+ break;
+ case Type_Symbol:
+ s = "Symbol";
+ break;
case Type_Object:
s = "Object";
break;
@@ -80,6 +69,9 @@ QString Managed::className() const
case Type_FunctionObject:
s = "Function";
break;
+ case Type_GeneratorObject:
+ s = "Generator";
+ break;
case Type_BooleanObject:
s = "Boolean";
break;
@@ -89,6 +81,9 @@ QString Managed::className() const
case Type_StringObject:
s = "String";
break;
+ case Type_SymbolObject:
+ s = "Symbol";
+ break;
case Type_DateObject:
s = "Date";
break;
@@ -96,7 +91,7 @@ QString Managed::className() const
s = "RegExp";
break;
case Type_ErrorObject:
- s = ErrorObject::className(static_cast<Heap::ErrorObject *>(d())->errorType);
+ s = "Error";
break;
case Type_ArgumentsObject:
s = "Arguments";
@@ -104,6 +99,9 @@ QString Managed::className() const
case Type_JsonObject:
s = "JSON";
break;
+ case Type_ProxyObject:
+ s = "ProxyObject";
+ break;
case Type_MathObject:
s = "Math";
break;
@@ -111,8 +109,23 @@ QString Managed::className() const
case Type_ExecutionContext:
s = "__ExecutionContext";
break;
- case Type_ForeachIteratorObject:
- s = "__ForeachIterator";
+ case Type_MapIteratorObject:
+ s = "Map Iterator";
+ break;
+ case Type_SetIteratorObject:
+ s = "Set Iterator";
+ break;
+ case Type_ArrayIteratorObject:
+ s = "Array Iterator";
+ break;
+ case Type_StringIteratorObject:
+ s = "String Iterator";
+ break;
+ case Type_ForInIterator:
+ s = "__ForIn Iterator";
+ break;
+ case Type_InternalClass:
+ s = "__InternalClass";
break;
case Type_RegExp:
s = "__RegExp";
@@ -125,7 +138,12 @@ QString Managed::className() const
return QString::fromLatin1(s);
}
-bool Managed::isEqualTo(Managed *, Managed *)
+bool Managed::virtualIsEqualTo(Managed *, Managed *)
{
return false;
}
+
+
+OwnPropertyKeyIterator::~OwnPropertyKeyIterator()
+{
+}
diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h
index 092c61b81c..d85b30a056 100644
--- a/src/qml/jsruntime/qv4managed_p.h
+++ b/src/qml/jsruntime/qv4managed_p.h
@@ -55,6 +55,7 @@
#include "qv4enginebase_p.h"
#include <private/qv4heap_p.h>
#include <private/qv4writebarrier_p.h>
+#include <private/qv4vtable_p.h>
QT_BEGIN_NAMESPACE
@@ -70,13 +71,9 @@ inline int qYouForgotTheQ_MANAGED_Macro(T, T) { return 0; }
template <typename T1, typename T2>
inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {}
-#ifdef Q_COMPILER_STATIC_ASSERT
-#define V4_MANAGED_SIZE_TEST void __dataTest() { Q_STATIC_ASSERT(sizeof(*this) == sizeof(Managed)); }
-#else
-#define V4_MANAGED_SIZE_TEST
-#endif
+#define V4_MANAGED_SIZE_TEST void __dataTest() { static_assert (sizeof(*this) == sizeof(Managed), "Classes derived from Managed can't have own data members."); }
-#define V4_NEEDS_DESTROY static void destroy(QV4::Heap::Base *b) { static_cast<Data *>(b)->destroy(); }
+#define V4_NEEDS_DESTROY static void virtualDestroy(QV4::Heap::Base *b) { static_cast<Data *>(b)->destroy(); }
#define V4_MANAGED_ITSELF(DataClass, superClass) \
@@ -92,77 +89,30 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {}
QV4::Heap::DataClass *dptr = d_unchecked(); \
dptr->_checkIsInitialized(); \
return dptr; \
- } \
- Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value);
+ }
#define V4_MANAGED(DataClass, superClass) \
private: \
DataClass() Q_DECL_EQ_DELETE; \
Q_DISABLE_COPY(DataClass) \
- V4_MANAGED_ITSELF(DataClass, superClass)
+ V4_MANAGED_ITSELF(DataClass, superClass) \
+ Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value);
#define Q_MANAGED_TYPE(type) \
public: \
enum { MyType = Type_##type };
-#define Q_VTABLE_FUNCTION(classname, func) \
- (classname::func == QV4::Managed::func ? 0 : classname::func)
-
-// Q_VTABLE_FUNCTION triggers a bogus tautological-compare warning in GCC6+
-#if (defined(Q_CC_GNU) && Q_CC_GNU >= 600)
-#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \
- QT_WARNING_PUSH; \
- QT_WARNING_DISABLE_GCC("-Wtautological-compare")
-
-#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF \
- ;QT_WARNING_POP
-#elif defined(Q_CC_CLANG) && Q_CC_CLANG >= 306
-#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \
- QT_WARNING_PUSH; \
- QT_WARNING_DISABLE_CLANG("-Wtautological-compare")
-
-#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF \
- ;QT_WARNING_POP
-#else
-#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON
-#define QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF
-#endif
-
-#define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \
-{ \
- parentVTable, \
- (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \
- (sizeof(classname::Data) + (classname::NInlineProperties*sizeof(QV4::Value)) + QV4::Chunk::SlotSize - 1)/QV4::Chunk::SlotSize*QV4::Chunk::SlotSize/sizeof(QV4::Value) \
- - (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \
- classname::IsExecutionContext, \
- classname::IsString, \
- classname::IsObject, \
- classname::IsFunctionObject, \
- classname::IsErrorObject, \
- classname::IsArrayData, \
- 0, \
- classname::MyType, \
- #classname, \
- Q_VTABLE_FUNCTION(classname, destroy), \
- classname::Data::markObjects, \
- isEqualTo \
-} \
-
-#define DEFINE_MANAGED_VTABLE(classname) \
-QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \
-const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, 0) \
-QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF
-
#define V4_INTERNALCLASS(c) \
- static QV4::InternalClass *defaultInternalClass(QV4::EngineBase *e) \
- { return e->internalClasses[QV4::EngineBase::Class_##c]; }
+ static Heap::InternalClass *defaultInternalClass(QV4::EngineBase *e) \
+ { return e->internalClasses(QV4::EngineBase::Class_##c); }
-struct Q_QML_PRIVATE_EXPORT Managed : Value
+struct Q_QML_PRIVATE_EXPORT Managed : Value, VTableBase
{
V4_MANAGED_ITSELF(Base, Managed)
enum {
IsExecutionContext = false,
IsString = false,
+ IsStringOrSymbol = false,
IsObject = false,
IsFunctionObject = false,
IsErrorObject = false,
@@ -180,47 +130,55 @@ public:
Type_Invalid,
Type_String,
Type_Object,
+ Type_Symbol,
Type_ArrayObject,
Type_FunctionObject,
+ Type_GeneratorObject,
Type_BooleanObject,
Type_NumberObject,
Type_StringObject,
+ Type_SymbolObject,
Type_DateObject,
Type_RegExpObject,
Type_ErrorObject,
Type_ArgumentsObject,
Type_JsonObject,
Type_MathObject,
+ Type_ProxyObject,
Type_ExecutionContext,
- Type_ForeachIteratorObject,
+ Type_InternalClass,
+ Type_SetIteratorObject,
+ Type_MapIteratorObject,
+ Type_ArrayIteratorObject,
+ Type_StringIteratorObject,
+ Type_ForInIterator,
Type_RegExp,
Type_QmlSequence
};
Q_MANAGED_TYPE(Invalid)
- InternalClass *internalClass() const { return d()->internalClass; }
+ Heap::InternalClass *internalClass() const { return d()->internalClass; }
+ const VTable *vtable() const { return d()->internalClass->vtable; }
inline ExecutionEngine *engine() const { return internalClass()->engine; }
- bool isListType() const { return d()->vtable()->type == Type_QmlSequence; }
+ bool isListType() const { return d()->internalClass->vtable->type == Type_QmlSequence; }
+ bool isArrayLike() const { return isArrayObject() || isListType(); }
- bool isArrayObject() const { return d()->vtable()->type == Type_ArrayObject; }
- bool isStringObject() const { return d()->vtable()->type == Type_StringObject; }
+ bool isArrayObject() const { return d()->internalClass->vtable->type == Type_ArrayObject; }
+ bool isStringObject() const { return d()->internalClass->vtable->type == Type_StringObject; }
+ bool isSymbolObject() const { return d()->internalClass->vtable->type == Type_SymbolObject; }
QString className() const;
bool isEqualTo(const Managed *other) const
- { return d()->vtable()->isEqualTo(const_cast<Managed *>(this), const_cast<Managed *>(other)); }
-
- static bool isEqualTo(Managed *m, Managed *other);
+ { return d()->internalClass->vtable->isEqualTo(const_cast<Managed *>(this), const_cast<Managed *>(other)); }
bool inUse() const { return d()->inUse(); }
bool markBit() const { return d()->isMarked(); }
inline void mark(MarkStack *markStack);
- static void destroy(Heap::Base *) {}
-
Q_ALWAYS_INLINE Heap::Base *heapObject() const {
return m();
}
@@ -232,6 +190,9 @@ public:
return static_cast<const T *>(this);
}
+protected:
+ static bool virtualIsEqualTo(Managed *m, Managed *other);
+
private:
friend class MemoryManager;
friend struct Identifiers;
@@ -254,6 +215,33 @@ inline const Object *Value::as() const {
return objectValue();
}
+
+struct InternalClass : Managed
+{
+ V4_MANAGED_ITSELF(InternalClass, Managed)
+ Q_MANAGED_TYPE(InternalClass)
+ V4_INTERNALCLASS(Empty)
+ V4_NEEDS_DESTROY
+
+ Q_REQUIRED_RESULT Heap::InternalClass *changeVTable(const VTable *vt) {
+ return d()->changeVTable(vt);
+ }
+ Q_REQUIRED_RESULT Heap::InternalClass *changePrototype(Heap::Object *proto) {
+ return d()->changePrototype(proto);
+ }
+ Q_REQUIRED_RESULT Heap::InternalClass *addMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry = nullptr) {
+ return d()->addMember(identifier, data, entry);
+ }
+
+ Q_REQUIRED_RESULT Heap::InternalClass *changeMember(PropertyKey identifier, PropertyAttributes data, InternalClassEntry *entry = nullptr) {
+ return d()->changeMember(identifier, data, entry);
+ }
+
+ void operator =(Heap::InternalClass *ic) {
+ Value::operator=(ic);
+ }
+};
+
}
diff --git a/src/qml/jsruntime/qv4mapiterator.cpp b/src/qml/jsruntime/qv4mapiterator.cpp
new file mode 100644
index 0000000000..cd5fbb8e63
--- /dev/null
+++ b/src/qml/jsruntime/qv4mapiterator.cpp
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qv4iterator_p.h>
+#include <private/qv4estable_p.h>
+#include <private/qv4mapiterator_p.h>
+#include <private/qv4mapobject_p.h>
+#include <private/qv4symbol_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(MapIteratorObject);
+
+void MapIteratorPrototype::init(ExecutionEngine *e)
+{
+ defineDefaultProperty(QStringLiteral("next"), method_next, 0);
+
+ Scope scope(e);
+ ScopedString val(scope, e->newString(QLatin1String("Map Iterator")));
+ defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val);
+}
+
+ReturnedValue MapIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int)
+{
+ Scope scope(b);
+ const MapIteratorObject *thisObject = that->as<MapIteratorObject>();
+ if (!thisObject)
+ return scope.engine->throwTypeError(QLatin1String("Not a Map Iterator instance"));
+
+ Scoped<MapObject> s(scope, thisObject->d()->iteratedMap);
+ uint index = thisObject->d()->mapNextIndex;
+ IteratorKind itemKind = thisObject->d()->iterationKind;
+
+ if (!s) {
+ QV4::Value undefined = Value::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+ }
+
+ Value *arguments = scope.alloc(2);
+
+ while (index < s->d()->esTable->size()) {
+ s->d()->esTable->iterate(index, &arguments[0], &arguments[1]);
+ thisObject->d()->mapNextIndex = index + 1;
+
+ ScopedValue result(scope);
+
+ if (itemKind == KeyIteratorKind) {
+ result = arguments[0];
+ } else if (itemKind == ValueIteratorKind) {
+ result = arguments[1];
+ } else {
+ Q_ASSERT(itemKind == KeyValueIteratorKind);
+
+ result = scope.engine->newArrayObject();
+
+ Scoped<ArrayObject> resultArray(scope, result);
+ resultArray->arrayReserve(2);
+ resultArray->arrayPut(0, arguments[0]);
+ resultArray->arrayPut(1, arguments[1]);
+ resultArray->setArrayLengthUnchecked(2);
+ }
+
+ return IteratorPrototype::createIterResultObject(scope.engine, result, false);
+ }
+
+ thisObject->d()->iteratedMap.set(scope.engine, nullptr);
+ QV4::Value undefined = Value::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+}
+
+
diff --git a/src/qml/jsruntime/qv4mapiterator_p.h b/src/qml/jsruntime/qv4mapiterator_p.h
new file mode 100644
index 0000000000..836ba14663
--- /dev/null
+++ b/src/qml/jsruntime/qv4mapiterator_p.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** 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 QV4MAPITERATOR_P_H
+#define QV4MAPITERATOR_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 "qv4object_p.h"
+#include "qv4iterator_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Heap {
+
+#define MapIteratorObjectMembers(class, Member) \
+ Member(class, Pointer, Object *, iteratedMap) \
+ Member(class, NoMark, IteratorKind, iterationKind) \
+ Member(class, NoMark, quint32, mapNextIndex)
+
+DECLARE_HEAP_OBJECT(MapIteratorObject, Object) {
+ DECLARE_MARKOBJECTS(MapIteratorObject);
+ void init(Object *obj, QV4::ExecutionEngine *engine)
+ {
+ Object::init();
+ this->iteratedMap.set(engine, obj);
+ this->mapNextIndex = 0;
+ }
+};
+
+}
+
+struct MapIteratorPrototype : Object
+{
+ V4_PROTOTYPE(iteratorPrototype)
+ void init(ExecutionEngine *engine);
+
+ static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct MapIteratorObject : Object
+{
+ V4_OBJECT2(MapIteratorObject, Object)
+ Q_MANAGED_TYPE(MapIteratorObject)
+ V4_PROTOTYPE(mapIteratorPrototype)
+
+ void init(ExecutionEngine *engine);
+};
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4MAPITERATOR_P_H
+
+
diff --git a/src/qml/jsruntime/qv4mapobject.cpp b/src/qml/jsruntime/qv4mapobject.cpp
new file mode 100644
index 0000000000..7d53b36fcd
--- /dev/null
+++ b/src/qml/jsruntime/qv4mapobject.cpp
@@ -0,0 +1,379 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** 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 "qv4setobject_p.h" // ### temporary
+#include "qv4mapobject_p.h"
+#include "qv4mapiterator_p.h"
+#include "qv4estable_p.h"
+#include "qv4symbol_p.h"
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(WeakMapCtor);
+DEFINE_OBJECT_VTABLE(MapCtor);
+DEFINE_OBJECT_VTABLE(MapObject);
+
+void Heap::WeakMapCtor::init(QV4::ExecutionContext *scope)
+{
+ Heap::FunctionObject::init(scope, QStringLiteral("WeakMap"));
+}
+
+void Heap::MapCtor::init(QV4::ExecutionContext *scope)
+{
+ Heap::FunctionObject::init(scope, QStringLiteral("Map"));
+}
+
+ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv, int argc, const Value *, bool weakMap)
+{
+ Scope scope(f);
+ Scoped<MapObject> a(scope, scope.engine->memoryManager->allocate<MapObject>());
+ if (weakMap) {
+ a->setPrototypeOf(scope.engine->weakMapPrototype());
+ scope.engine->memoryManager->registerWeakMap(a->d());
+ }
+ a->d()->isWeakMap = weakMap;
+
+ if (argc > 0) {
+ ScopedValue iterable(scope, argv[0]);
+
+ if (!iterable->isNullOrUndefined()) {
+ ScopedFunctionObject adder(scope, a->get(ScopedString(scope, scope.engine->newString(QString::fromLatin1("set")))));
+ if (!adder)
+ return scope.engine->throwTypeError();
+
+ ScopedObject iter(scope, Runtime::method_getIterator(scope.engine, iterable, true));
+ if (scope.hasException())
+ return Encode::undefined();
+ Q_ASSERT(iter);
+
+ ScopedValue obj(scope);
+ Value *arguments = scope.alloc(2);
+ ScopedValue done(scope);
+ forever {
+ done = Runtime::method_iteratorNext(scope.engine, iter, obj);
+ if (scope.hasException())
+ break;
+ if (done->toBoolean())
+ return a->asReturnedValue();
+ const Object *o = obj->objectValue();
+ if (!o) {
+ scope.engine->throwTypeError();
+ break;
+ }
+
+ arguments[0] = o->get(PropertyKey::fromArrayIndex(0));
+ if (scope.hasException())
+ break;
+ arguments[1] = o->get(PropertyKey::fromArrayIndex(1));
+ if (scope.hasException())
+ break;
+
+ adder->call(a, arguments, 2);
+ if (scope.hasException())
+ break;
+ }
+ ScopedValue falsey(scope, Encode(false));
+ return Runtime::method_iteratorClose(scope.engine, iter, falsey);
+ }
+ }
+ return a->asReturnedValue();
+
+}
+
+ReturnedValue WeakMapCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
+{
+ return construct(f, argv, argc, newTarget, true);
+}
+
+ReturnedValue WeakMapCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
+{
+ Scope scope(f);
+ return scope.engine->throwTypeError(QString::fromLatin1("(Weak)Map requires new"));
+}
+
+
+ReturnedValue MapCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
+{
+ return construct(f, argv, argc, newTarget, false);
+}
+
+void WeakMapPrototype::init(ExecutionEngine *engine, Object *ctor)
+{
+ Scope scope(engine);
+ ScopedObject o(scope);
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(0));
+ ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
+ defineDefaultProperty(engine->id_constructor(), (o = ctor));
+
+ defineDefaultProperty(QStringLiteral("delete"), method_delete, 1);
+ defineDefaultProperty(QStringLiteral("get"), method_get, 1);
+ defineDefaultProperty(QStringLiteral("has"), method_has, 1);
+ defineDefaultProperty(QStringLiteral("set"), method_set, 2);
+
+ ScopedString val(scope, engine->newString(QLatin1String("WeakMap")));
+ defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), val);
+}
+
+
+void MapPrototype::init(ExecutionEngine *engine, Object *ctor)
+{
+ Scope scope(engine);
+ ScopedObject o(scope);
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(0));
+ ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
+ ctor->addSymbolSpecies();
+ defineDefaultProperty(engine->id_constructor(), (o = ctor));
+
+ defineDefaultProperty(QStringLiteral("clear"), method_clear, 0);
+ defineDefaultProperty(QStringLiteral("delete"), method_delete, 1);
+ defineDefaultProperty(QStringLiteral("forEach"), method_forEach, 1);
+ defineDefaultProperty(QStringLiteral("get"), method_get, 1);
+ defineDefaultProperty(QStringLiteral("has"), method_has, 1);
+ defineDefaultProperty(QStringLiteral("keys"), method_keys, 0);
+ defineDefaultProperty(QStringLiteral("set"), method_set, 2);
+ defineAccessorProperty(QStringLiteral("size"), method_get_size, nullptr);
+ defineDefaultProperty(QStringLiteral("values"), method_values, 0);
+
+ // Per the spec, the value for entries/@@iterator is the same
+ ScopedString valString(scope, scope.engine->newIdentifier(QStringLiteral("entries")));
+ ScopedFunctionObject entriesFn(scope, FunctionObject::createBuiltinFunction(engine, valString, MapPrototype::method_entries, 0));
+ defineDefaultProperty(QStringLiteral("entries"), entriesFn);
+ defineDefaultProperty(engine->symbol_iterator(), entriesFn);
+
+ ScopedString val(scope, engine->newString(QLatin1String("Map")));
+ defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), val);
+}
+
+void Heap::MapObject::init()
+{
+ Object::init();
+ esTable = new ESTable();
+}
+
+void Heap::MapObject::destroy()
+{
+ delete esTable;
+ esTable = nullptr;
+}
+
+void Heap::MapObject::removeUnmarkedKeys()
+{
+ esTable->removeUnmarkedKeys();
+}
+
+void Heap::MapObject::markObjects(Heap::Base *that, MarkStack *markStack)
+{
+ MapObject *m = static_cast<MapObject *>(that);
+ m->esTable->markObjects(markStack, m->isWeakMap);
+ Object::markObjects(that, markStack);
+}
+
+ReturnedValue WeakMapPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that || !that->d()->isWeakMap)
+ return scope.engine->throwTypeError();
+ if (!argc || !argv[0].isObject())
+ return Encode(false);
+
+ return Encode(that->d()->esTable->remove(argv[0]));
+
+}
+
+ReturnedValue WeakMapPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that || !that->d()->isWeakMap)
+ return scope.engine->throwTypeError();
+ if (!argc || !argv[0].isObject())
+ return Encode::undefined();
+
+ return that->d()->esTable->get(argv[0]);
+}
+
+ReturnedValue WeakMapPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that || !that->d()->isWeakMap)
+ return scope.engine->throwTypeError();
+ if (!argc || !argv[0].isObject())
+ return Encode(false);
+
+ return Encode(that->d()->esTable->has(argv[0]));
+}
+
+ReturnedValue WeakMapPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if ((!that || !that->d()->isWeakMap) ||
+ (!argc || !argv[0].isObject()))
+ return scope.engine->throwTypeError();
+
+ that->d()->esTable->set(argv[0], argc > 1 ? argv[1] : Value::undefinedValue());
+ return that.asReturnedValue();
+}
+
+
+ReturnedValue MapPrototype::method_clear(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that || that->d()->isWeakMap)
+ return scope.engine->throwTypeError();
+
+ that->d()->esTable->clear();
+ return Encode::undefined();
+}
+
+ReturnedValue MapPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that || that->d()->isWeakMap)
+ return scope.engine->throwTypeError();
+
+ return Encode(that->d()->esTable->remove(argc ? argv[0] : Value::undefinedValue()));
+}
+
+ReturnedValue MapPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that || that->d()->isWeakMap)
+ return scope.engine->throwTypeError();
+
+ Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(that));
+ ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue MapPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that || that->d()->isWeakMap)
+ return scope.engine->throwTypeError();
+
+ ScopedFunctionObject callbackfn(scope, argv[0]);
+ if (!callbackfn)
+ return scope.engine->throwTypeError();
+
+ ScopedValue thisArg(scope, Value::undefinedValue());
+ if (argc > 1)
+ thisArg = ScopedValue(scope, argv[1]);
+
+ Value *arguments = scope.alloc(3);
+ arguments[2] = that;
+ for (uint i = 0; i < that->d()->esTable->size(); ++i) {
+ that->d()->esTable->iterate(i, &arguments[1], &arguments[0]); // fill in key (0), value (1)
+
+ callbackfn->call(thisArg, arguments, 3);
+ CHECK_EXCEPTION();
+ }
+ return Encode::undefined();
+}
+
+ReturnedValue MapPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that || that->d()->isWeakMap)
+ return scope.engine->throwTypeError();
+
+ return that->d()->esTable->get(argc ? argv[0] : Value::undefinedValue());
+}
+
+ReturnedValue MapPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that || that->d()->isWeakMap)
+ return scope.engine->throwTypeError();
+
+ return Encode(that->d()->esTable->has(argc ? argv[0] : Value::undefinedValue()));
+}
+
+ReturnedValue MapPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that || that->d()->isWeakMap)
+ return scope.engine->throwTypeError();
+
+ Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(that));
+ ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue MapPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that || that->d()->isWeakMap)
+ return scope.engine->throwTypeError();
+
+ that->d()->esTable->set(argc ? argv[0] : Value::undefinedValue(), argc > 1 ? argv[1] : Value::undefinedValue());
+ return that.asReturnedValue();
+}
+
+ReturnedValue MapPrototype::method_get_size(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that || that->d()->isWeakMap)
+ return scope.engine->throwTypeError();
+
+ return Encode(that->d()->esTable->size());
+}
+
+ReturnedValue MapPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that || that->d()->isWeakMap)
+ return scope.engine->throwTypeError();
+
+ Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(that));
+ ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
+ return ao->asReturnedValue();
+}
diff --git a/src/qml/jsruntime/qv4mapobject_p.h b/src/qml/jsruntime/qv4mapobject_p.h
new file mode 100644
index 0000000000..a0fae2a14a
--- /dev/null
+++ b/src/qml/jsruntime/qv4mapobject_p.h
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** 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 QV4MAPOBJECT_P_H
+#define QV4MAPOBJECT_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 "qv4object_p.h"
+#include "qv4objectproto_p.h"
+#include "qv4functionobject_p.h"
+#include "qv4string_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+class ESTable;
+
+namespace Heap {
+
+struct WeakMapCtor : FunctionObject {
+ void init(QV4::ExecutionContext *scope);
+};
+
+struct MapCtor : WeakMapCtor {
+ void init(QV4::ExecutionContext *scope);
+};
+
+struct MapObject : Object {
+ static void markObjects(Heap::Base *that, MarkStack *markStack);
+ void init();
+ void destroy();
+ void removeUnmarkedKeys();
+
+ MapObject *nextWeakMap;
+ ESTable *esTable;
+ bool isWeakMap;
+};
+
+}
+
+struct WeakMapCtor: FunctionObject
+{
+ V4_OBJECT2(WeakMapCtor, FunctionObject)
+
+ static ReturnedValue construct(const FunctionObject *f, const Value *argv, int argc, const Value *, bool weakMap);
+
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct MapCtor : WeakMapCtor
+{
+ V4_OBJECT2(MapCtor, WeakMapCtor)
+
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+};
+
+struct MapObject : Object
+{
+ V4_OBJECT2(MapObject, Object)
+ V4_PROTOTYPE(mapPrototype)
+ V4_NEEDS_DESTROY
+};
+
+struct WeakMapPrototype : Object
+{
+ void init(ExecutionEngine *engine, Object *ctor);
+
+ static ReturnedValue method_delete(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct MapPrototype : WeakMapPrototype
+{
+ void init(ExecutionEngine *engine, Object *ctor);
+
+ static ReturnedValue method_clear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_delete(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_size(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+
+} // namespace QV4
+
+
+QT_END_NAMESPACE
+
+#endif // QV4MAPOBJECT_P_H
+
diff --git a/src/qml/jsruntime/qv4math_p.h b/src/qml/jsruntime/qv4math_p.h
index 67c963622f..90246c4229 100644
--- a/src/qml/jsruntime/qv4math_p.h
+++ b/src/qml/jsruntime/qv4math_p.h
@@ -70,24 +70,24 @@ static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b)
{
int result;
if (Q_UNLIKELY(add_overflow(a, b, &result)))
- return Primitive::fromDouble(static_cast<double>(a) + b).asReturnedValue();
- return Primitive::fromInt32(result).asReturnedValue();
+ return Value::fromDouble(static_cast<double>(a) + b).asReturnedValue();
+ return Value::fromInt32(result).asReturnedValue();
}
static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b)
{
int result;
if (Q_UNLIKELY(sub_overflow(a, b, &result)))
- return Primitive::fromDouble(static_cast<double>(a) - b).asReturnedValue();
- return Primitive::fromInt32(result).asReturnedValue();
+ return Value::fromDouble(static_cast<double>(a) - b).asReturnedValue();
+ return Value::fromInt32(result).asReturnedValue();
}
static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b)
{
int result;
if (Q_UNLIKELY(mul_overflow(a, b, &result)))
- return Primitive::fromDouble(static_cast<double>(a) * b).asReturnedValue();
- return Primitive::fromInt32(result).asReturnedValue();
+ return Value::fromDouble(static_cast<double>(a) * b).asReturnedValue();
+ return Value::fromInt32(result).asReturnedValue();
}
}
diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp
index 0c18d908de..07440047d4 100644
--- a/src/qml/jsruntime/qv4mathobject.cpp
+++ b/src/qml/jsruntime/qv4mathobject.cpp
@@ -39,6 +39,7 @@
#include "qv4mathobject_p.h"
#include "qv4objectproto_p.h"
+#include "qv4symbol_p.h"
#include <QtCore/qdatetime.h>
#include <QtCore/qmath.h>
@@ -59,25 +60,38 @@ void Heap::MathObject::init()
Scope scope(internalClass->engine);
ScopedObject m(scope, this);
- m->defineReadonlyProperty(QStringLiteral("E"), Primitive::fromDouble(M_E));
- m->defineReadonlyProperty(QStringLiteral("LN2"), Primitive::fromDouble(M_LN2));
- m->defineReadonlyProperty(QStringLiteral("LN10"), Primitive::fromDouble(M_LN10));
- m->defineReadonlyProperty(QStringLiteral("LOG2E"), Primitive::fromDouble(M_LOG2E));
- m->defineReadonlyProperty(QStringLiteral("LOG10E"), Primitive::fromDouble(M_LOG10E));
- m->defineReadonlyProperty(QStringLiteral("PI"), Primitive::fromDouble(M_PI));
- m->defineReadonlyProperty(QStringLiteral("SQRT1_2"), Primitive::fromDouble(M_SQRT1_2));
- m->defineReadonlyProperty(QStringLiteral("SQRT2"), Primitive::fromDouble(M_SQRT2));
+ m->defineReadonlyProperty(QStringLiteral("E"), Value::fromDouble(M_E));
+ m->defineReadonlyProperty(QStringLiteral("LN2"), Value::fromDouble(M_LN2));
+ m->defineReadonlyProperty(QStringLiteral("LN10"), Value::fromDouble(M_LN10));
+ m->defineReadonlyProperty(QStringLiteral("LOG2E"), Value::fromDouble(M_LOG2E));
+ m->defineReadonlyProperty(QStringLiteral("LOG10E"), Value::fromDouble(M_LOG10E));
+ m->defineReadonlyProperty(QStringLiteral("PI"), Value::fromDouble(M_PI));
+ m->defineReadonlyProperty(QStringLiteral("SQRT1_2"), Value::fromDouble(M_SQRT1_2));
+ m->defineReadonlyProperty(QStringLiteral("SQRT2"), Value::fromDouble(M_SQRT2));
m->defineDefaultProperty(QStringLiteral("abs"), QV4::MathObject::method_abs, 1);
m->defineDefaultProperty(QStringLiteral("acos"), QV4::MathObject::method_acos, 1);
- m->defineDefaultProperty(QStringLiteral("asin"), QV4::MathObject::method_asin, 0);
+ m->defineDefaultProperty(QStringLiteral("acosh"), QV4::MathObject::method_acosh, 1);
+ m->defineDefaultProperty(QStringLiteral("asin"), QV4::MathObject::method_asin, 1);
+ m->defineDefaultProperty(QStringLiteral("asinh"), QV4::MathObject::method_asinh, 1);
m->defineDefaultProperty(QStringLiteral("atan"), QV4::MathObject::method_atan, 1);
+ m->defineDefaultProperty(QStringLiteral("atanh"), QV4::MathObject::method_atanh, 1);
m->defineDefaultProperty(QStringLiteral("atan2"), QV4::MathObject::method_atan2, 2);
+ m->defineDefaultProperty(QStringLiteral("cbrt"), QV4::MathObject::method_cbrt, 1);
m->defineDefaultProperty(QStringLiteral("ceil"), QV4::MathObject::method_ceil, 1);
+ m->defineDefaultProperty(QStringLiteral("clz32"), QV4::MathObject::method_clz32, 1);
m->defineDefaultProperty(QStringLiteral("cos"), QV4::MathObject::method_cos, 1);
+ m->defineDefaultProperty(QStringLiteral("cosh"), QV4::MathObject::method_cosh, 1);
m->defineDefaultProperty(QStringLiteral("exp"), QV4::MathObject::method_exp, 1);
+ m->defineDefaultProperty(QStringLiteral("expm1"), QV4::MathObject::method_expm1, 1);
m->defineDefaultProperty(QStringLiteral("floor"), QV4::MathObject::method_floor, 1);
+ m->defineDefaultProperty(QStringLiteral("fround"), QV4::MathObject::method_fround, 1);
+ m->defineDefaultProperty(QStringLiteral("hypot"), QV4::MathObject::method_hypot, 2);
+ m->defineDefaultProperty(QStringLiteral("imul"), QV4::MathObject::method_imul, 2);
m->defineDefaultProperty(QStringLiteral("log"), QV4::MathObject::method_log, 1);
+ m->defineDefaultProperty(QStringLiteral("log10"), QV4::MathObject::method_log10, 1);
+ m->defineDefaultProperty(QStringLiteral("log1p"), QV4::MathObject::method_log1p, 1);
+ m->defineDefaultProperty(QStringLiteral("log2"), QV4::MathObject::method_log2, 1);
m->defineDefaultProperty(QStringLiteral("max"), QV4::MathObject::method_max, 2);
m->defineDefaultProperty(QStringLiteral("min"), QV4::MathObject::method_min, 2);
m->defineDefaultProperty(QStringLiteral("pow"), QV4::MathObject::method_pow, 2);
@@ -85,8 +99,14 @@ void Heap::MathObject::init()
m->defineDefaultProperty(QStringLiteral("round"), QV4::MathObject::method_round, 1);
m->defineDefaultProperty(QStringLiteral("sign"), QV4::MathObject::method_sign, 1);
m->defineDefaultProperty(QStringLiteral("sin"), QV4::MathObject::method_sin, 1);
+ m->defineDefaultProperty(QStringLiteral("sinh"), QV4::MathObject::method_sinh, 1);
m->defineDefaultProperty(QStringLiteral("sqrt"), QV4::MathObject::method_sqrt, 1);
m->defineDefaultProperty(QStringLiteral("tan"), QV4::MathObject::method_tan, 1);
+ m->defineDefaultProperty(QStringLiteral("tanh"), QV4::MathObject::method_tanh, 1);
+ m->defineDefaultProperty(QStringLiteral("trunc"), QV4::MathObject::method_trunc, 1);
+
+ ScopedString name(scope, scope.engine->newString(QStringLiteral("Math")));
+ m->defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name);
}
static Q_ALWAYS_INLINE double copySign(double x, double y)
@@ -120,6 +140,19 @@ ReturnedValue MathObject::method_acos(const FunctionObject *, const Value *, con
RETURN_RESULT(Encode(std::acos(v)));
}
+ReturnedValue MathObject::method_acosh(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : 2;
+ if (v < 1)
+ RETURN_RESULT(Encode(qt_qnan()));
+
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ RETURN_RESULT(Encode(std::log(v +std::sqrt(v + 1) * std::sqrt(v - 1))));
+#else
+ RETURN_RESULT(Encode(std::acosh(v)));
+#endif
+}
+
ReturnedValue MathObject::method_asin(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : 2;
@@ -129,6 +162,19 @@ ReturnedValue MathObject::method_asin(const FunctionObject *, const Value *, con
RETURN_RESULT(Encode(std::asin(v)));
}
+ReturnedValue MathObject::method_asinh(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : 2;
+ if (v == 0.0)
+ RETURN_RESULT(Encode(v));
+
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ RETURN_RESULT(Encode(std::log(v +std::sqrt(1 + v * v))));
+#else
+ RETURN_RESULT(Encode(std::asinh(v)));
+#endif
+}
+
ReturnedValue MathObject::method_atan(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
@@ -138,6 +184,25 @@ ReturnedValue MathObject::method_atan(const FunctionObject *, const Value *, con
RETURN_RESULT(Encode(std::atan(v)));
}
+ReturnedValue MathObject::method_atanh(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (v == 0.0)
+ RETURN_RESULT(Encode(v));
+
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ if (-1 < v && v < 1)
+ RETURN_RESULT(Encode(0.5 * (std::log(v + 1) - std::log(v - 1))));
+
+ if (v > 1 || v < -1)
+ RETURN_RESULT(Encode(qt_qnan()));
+
+ RETURN_RESULT(Encode(copySign(qt_inf(), v)));
+#else
+ RETURN_RESULT(Encode(std::atanh(v)));
+#endif
+}
+
ReturnedValue MathObject::method_atan2(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v1 = argc ? argv[0].toNumber() : qt_qnan();
@@ -156,6 +221,16 @@ ReturnedValue MathObject::method_atan2(const FunctionObject *, const Value *, co
RETURN_RESULT(Encode(std::atan2(v1, v2)));
}
+ReturnedValue MathObject::method_cbrt(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ RETURN_RESULT(Encode(copySign(std::exp(std::log(std::abs(v)) / 3), v)));
+#else
+ RETURN_RESULT(Encode(std::cbrt(v))); // cube root
+#endif
+}
+
ReturnedValue MathObject::method_ceil(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
@@ -165,12 +240,24 @@ ReturnedValue MathObject::method_ceil(const FunctionObject *, const Value *, con
RETURN_RESULT(Encode(std::ceil(v)));
}
+ReturnedValue MathObject::method_clz32(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ quint32 v = argc ? argv[0].toUInt32() : 0;
+ RETURN_RESULT(Encode(qint32(qCountLeadingZeroBits(v))));
+}
+
ReturnedValue MathObject::method_cos(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
RETURN_RESULT(Encode(std::cos(v)));
}
+ReturnedValue MathObject::method_cosh(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ RETURN_RESULT(Encode(std::cosh(v)));
+}
+
ReturnedValue MathObject::method_exp(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
@@ -184,14 +271,80 @@ ReturnedValue MathObject::method_exp(const FunctionObject *, const Value *, cons
}
}
+ReturnedValue MathObject::method_expm1(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (std::isnan(v) || qIsNull(v)) {
+ RETURN_RESULT(Encode(v));
+ } else if (qt_is_inf(v)) {
+ if (copySign(1.0, v) == -1.0)
+ RETURN_RESULT(Encode(-1.0));
+ else
+ RETURN_RESULT(Encode(qt_inf()));
+ } else {
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ RETURN_RESULT(Encode(std::exp(v) - 1));
+#else
+ RETURN_RESULT(Encode(std::expm1(v)));
+#endif
+ }
+}
+
ReturnedValue MathObject::method_floor(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
- Value result = Primitive::fromDouble(std::floor(v));
+ Value result = Value::fromDouble(std::floor(v));
result.isInt32();
RETURN_RESULT(result);
}
+ReturnedValue MathObject::method_fround(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (std::isnan(v) || qt_is_inf(v) || qIsNull(v))
+ RETURN_RESULT(Encode(v));
+ else // convert to 32-bit float using roundTiesToEven, then convert back to 64-bit double
+ RETURN_RESULT(Encode(double(float(v))));
+}
+
+ReturnedValue MathObject::method_hypot(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ // ES6 Math.hypot(v1, ..., vn) -> sqrt(sum(vi**2)) but "should take care to
+ // avoid the loss of precision from overflows and underflows" (as std::hypot does).
+ double v = argc ? argv[0].toNumber() : 0;
+ // Spec mandates +0 on no args; and says nothing about what to do if toNumber() signals ...
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ bool big = qt_is_inf(v), bad = std::isnan(v);
+ v *= v;
+ for (int i = 1; !big && i < argc; i++) {
+ double u = argv[i].toNumber();
+ if (qt_is_inf(u))
+ big = true;
+ if (std::isnan(u))
+ bad = true;
+ v += u * u;
+ }
+ if (big)
+ RETURN_RESULT(Encode(qt_inf()));
+ if (bad)
+ RETURN_RESULT(Encode(qt_qnan()));
+ // Should actually check for {und,ov}erflow, but too fiddly !
+ RETURN_RESULT(Value::fromDouble(sqrt(v)));
+#else
+ for (int i = 1; i < argc; i++)
+ v = std::hypot(v, argv[i].toNumber());
+#endif
+ RETURN_RESULT(Value::fromDouble(v));
+}
+
+ReturnedValue MathObject::method_imul(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ quint32 a = argc ? argv[0].toUInt32() : 0;
+ quint32 b = argc > 0 ? argv[1].toUInt32() : 0;
+ qint32 product = a * b;
+ RETURN_RESULT(Encode(product));
+}
+
ReturnedValue MathObject::method_log(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
@@ -201,15 +354,54 @@ ReturnedValue MathObject::method_log(const FunctionObject *, const Value *, cons
RETURN_RESULT(Encode(std::log(v)));
}
+ReturnedValue MathObject::method_log10(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (v < 0)
+ RETURN_RESULT(Encode(qt_qnan()));
+ else
+ RETURN_RESULT(Encode(std::log10(v)));
+}
+
+ReturnedValue MathObject::method_log1p(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+#if !defined(__ANDROID__)
+ using std::log1p;
+#endif
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (v < -1)
+ RETURN_RESULT(Encode(qt_qnan()));
+ else
+ RETURN_RESULT(Encode(log1p(v)));
+}
+
+ReturnedValue MathObject::method_log2(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (v < 0) {
+ RETURN_RESULT(Encode(qt_qnan()));
+ } else {
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ // Android ndk r10e doesn't have std::log2, so fall back.
+ const double ln2 = std::log(2.0);
+ RETURN_RESULT(Encode(std::log(v) / ln2));
+#else
+ RETURN_RESULT(Encode(std::log2(v)));
+#endif
+ }
+}
+
ReturnedValue MathObject::method_max(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double mx = -qt_inf();
for (int i = 0, ei = argc; i < ei; ++i) {
double x = argv[i].toNumber();
- if (x > mx || std::isnan(x))
+ if ((x == 0 && mx == x && copySign(1.0, x) == 1.0)
+ || (x > mx) || std::isnan(x)) {
mx = x;
+ }
}
- RETURN_RESULT(Encode(mx));
+ RETURN_RESULT(Encode::smallestNumber(mx));
}
ReturnedValue MathObject::method_min(const FunctionObject *, const Value *, const Value *argv, int argc)
@@ -222,7 +414,7 @@ ReturnedValue MathObject::method_min(const FunctionObject *, const Value *, cons
mx = x;
}
}
- RETURN_RESULT(Encode(mx));
+ RETURN_RESULT(Encode::smallestNumber(mx));
}
ReturnedValue MathObject::method_pow(const FunctionObject *, const Value *, const Value *argv, int argc)
@@ -283,8 +475,11 @@ ReturnedValue MathObject::method_random(const FunctionObject *, const Value *, c
ReturnedValue MathObject::method_round(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
- v = copySign(std::floor(v + 0.5), v);
- RETURN_RESULT(Encode(v));
+ if (std::isnan(v) || qt_is_inf(v) || qIsNull(v))
+ RETURN_RESULT(Encode(v));
+
+ v = copySign(std::floor(v + 0.5), v);
+ RETURN_RESULT(Encode(v));
}
ReturnedValue MathObject::method_sign(const FunctionObject *, const Value *, const Value *argv, int argc)
@@ -303,7 +498,19 @@ ReturnedValue MathObject::method_sign(const FunctionObject *, const Value *, con
ReturnedValue MathObject::method_sin(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
- RETURN_RESULT(Encode(std::sin(v)));
+ if (v == 0.0)
+ RETURN_RESULT(Encode(v));
+ else
+ RETURN_RESULT(Encode(std::sin(v)));
+}
+
+ReturnedValue MathObject::method_sinh(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (v == 0.0)
+ RETURN_RESULT(Encode(v));
+ else
+ RETURN_RESULT(Encode(std::sinh(v)));
}
ReturnedValue MathObject::method_sqrt(const FunctionObject *, const Value *, const Value *argv, int argc)
@@ -321,3 +528,25 @@ ReturnedValue MathObject::method_tan(const FunctionObject *, const Value *, cons
RETURN_RESULT(Encode(std::tan(v)));
}
+ReturnedValue MathObject::method_tanh(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (v == 0.0)
+ RETURN_RESULT(Encode(v));
+ else
+ RETURN_RESULT(Encode(std::tanh(v)));
+}
+
+ReturnedValue MathObject::method_trunc(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ if (std::isnan(v) || qt_is_inf(v) || qIsNull(v))
+ RETURN_RESULT(Encode(v));
+ // Nearest integer not greater in magnitude:
+ quint64 whole = std::abs(v);
+ RETURN_RESULT(Encode(copySign(whole, v)));
+#else
+ RETURN_RESULT(Encode(std::trunc(v)));
+#endif
+}
diff --git a/src/qml/jsruntime/qv4mathobject_p.h b/src/qml/jsruntime/qv4mathobject_p.h
index 0bf5da9404..2658e25438 100644
--- a/src/qml/jsruntime/qv4mathobject_p.h
+++ b/src/qml/jsruntime/qv4mathobject_p.h
@@ -71,14 +71,27 @@ struct MathObject: Object
static ReturnedValue method_abs(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_acos(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_acosh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_asin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_asinh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_atan(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_atanh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_atan2(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_cbrt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_ceil(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_clz32(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_cos(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_cosh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_exp(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_expm1(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_floor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_fround(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_hypot(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_imul(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_log(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_log10(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_log1p(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_log2(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_max(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_min(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_pow(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
@@ -86,8 +99,11 @@ struct MathObject: Object
static ReturnedValue method_round(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_sign(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_sin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_sinh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_sqrt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_tan(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_tanh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_trunc(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
}
diff --git a/src/qml/jsruntime/qv4memberdata.cpp b/src/qml/jsruntime/qv4memberdata.cpp
index da086bd47a..246f857643 100644
--- a/src/qml/jsruntime/qv4memberdata.cpp
+++ b/src/qml/jsruntime/qv4memberdata.cpp
@@ -62,8 +62,9 @@ static size_t nextPowerOfTwo(size_t s)
Heap::MemberData *MemberData::allocate(ExecutionEngine *e, uint n, Heap::MemberData *old)
{
- Q_ASSERT(!old || old->values.size < n);
- Q_ASSERT(n);
+ Q_ASSERT(!old || old->values.size <= n);
+ if (!n)
+ n = 4;
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
diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h
index ac9671254d..186083b83a 100644
--- a/src/qml/jsruntime/qv4memberdata_p.h
+++ b/src/qml/jsruntime/qv4memberdata_p.h
@@ -74,18 +74,6 @@ struct MemberData : Managed
V4_MANAGED(MemberData, Managed)
V4_INTERNALCLASS(MemberData)
- struct Index {
- Heap::Base *base;
- Value *slot;
-
- void set(EngineBase *e, Value newVal) {
- WriteBarrier::write(e, base, slot->data_ptr(), newVal.asReturnedValue());
- }
- const Value *operator->() const { return slot; }
- const Value &operator*() const { return *slot; }
- bool isNull() const { return !slot; }
- };
-
const Value &operator[] (uint idx) const { return d()->values[idx]; }
const Value *data() const { return d()->values.data(); }
void set(EngineBase *e, uint index, Value v) { d()->values.set(e, index, v); }
diff --git a/src/qml/jsruntime/qv4module.cpp b/src/qml/jsruntime/qv4module.cpp
new file mode 100644
index 0000000000..19a036374f
--- /dev/null
+++ b/src/qml/jsruntime/qv4module.cpp
@@ -0,0 +1,237 @@
+/****************************************************************************
+**
+** 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 "qv4module_p.h"
+
+#include <private/qv4mm_p.h>
+#include <private/qv4vme_moth_p.h>
+#include <private/qv4context_p.h>
+#include <private/qv4symbol_p.h>
+#include <private/qv4identifiertable_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(Module);
+
+void Heap::Module::init(ExecutionEngine *engine, CompiledData::CompilationUnit *moduleUnit)
+{
+ Object::init();
+
+ // This is a back pointer and there is no need to call addref() on the unit, because the unit
+ // owns this object instead.
+ unit = moduleUnit;
+ self.set(engine, this);
+
+ Function *moduleFunction = unit->runtimeFunctions[unit->unitData()->indexOfRootFunction];
+
+ const uint locals = moduleFunction->compiledFunction->nLocals;
+ const size_t requiredMemory = sizeof(QV4::CallContext::Data) - sizeof(Value) + sizeof(Value) * locals;
+ scope.set(engine, engine->memoryManager->allocManaged<QV4::CallContext>(requiredMemory, moduleFunction->internalClass));
+ scope->init();
+ scope->outer.set(engine, engine->rootContext()->d());
+ scope->locals.size = locals;
+ scope->locals.alloc = locals;
+ scope->nArgs = 0;
+
+ // Prepare the temporal dead zone
+ scope->setupLocalTemporalDeadZone(moduleFunction->compiledFunction);
+
+ Scope valueScope(engine);
+
+ // It's possible for example to re-export an import, for example:
+ // import * as foo from "./bar.js"
+ // export { foo }
+ // Since we don't add imports to the locals, it won't be found typically.
+ // Except now we add imports at the end of the internal class in the index
+ // space past the locals, so that resolveExport can find it.
+ {
+ Scoped<QV4::InternalClass> ic(valueScope, scope->internalClass);
+
+ for (uint i = 0; i < unit->data->importEntryTableSize; ++i) {
+ const CompiledData::ImportEntry &import = unit->data->importEntryTable()[i];
+ ic = ic->addMember(engine->identifierTable->asPropertyKey(unit->runtimeStrings[import.localName]), Attr_NotConfigurable);
+ }
+ scope->internalClass.set(engine, ic->d());
+ }
+
+
+ Scoped<QV4::Module> This(valueScope, this);
+ ScopedString name(valueScope, engine->newString(QStringLiteral("Module")));
+ This->insertMember(engine->symbol_toStringTag(), name, Attr_ReadOnly);
+ This->setPrototypeUnchecked(nullptr);
+}
+
+ReturnedValue Module::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
+{
+ if (id.isSymbol())
+ return Object::virtualGet(m, id, receiver, hasProperty);
+
+ const Module *module = static_cast<const Module *>(m);
+ Scope scope(m->engine());
+ ScopedString expectedName(scope, id.toStringOrSymbol(scope.engine));
+ const Value *v = module->d()->unit->resolveExport(expectedName);
+ if (hasProperty)
+ *hasProperty = v != nullptr;
+ if (!v)
+ return Encode::undefined();
+ if (v->isEmpty()) {
+ ScopedValue propName(scope, id.toStringOrSymbol(scope.engine));
+ return scope.engine->throwReferenceError(propName);
+ }
+ return v->asReturnedValue();
+}
+
+PropertyAttributes Module::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
+{
+ if (id.isSymbol())
+ return Object::virtualGetOwnProperty(m, id, p);
+
+ const Module *module = static_cast<const Module *>(m);
+ Scope scope(m->engine());
+ ScopedString expectedName(scope, id.toStringOrSymbol(scope.engine));
+ const Value *v = module->d()->unit->resolveExport(expectedName);
+ if (!v) {
+ if (p)
+ p->value = Encode::undefined();
+ return Attr_Invalid;
+ }
+ if (p)
+ p->value = v->isEmpty() ? Encode::undefined() : v->asReturnedValue();
+ if (v->isEmpty()) {
+ ScopedValue propName(scope, id.toStringOrSymbol(scope.engine));
+ scope.engine->throwReferenceError(propName);
+ }
+ return Attr_Data | Attr_NotConfigurable;
+}
+
+bool Module::virtualHasProperty(const Managed *m, PropertyKey id)
+{
+ if (id.isSymbol())
+ return Object::virtualHasProperty(m, id);
+
+ const Module *module = static_cast<const Module *>(m);
+ Scope scope(m->engine());
+ ScopedString expectedName(scope, id.toStringOrSymbol(scope.engine));
+ const Value *v = module->d()->unit->resolveExport(expectedName);
+ return v != nullptr;
+}
+
+bool Module::virtualPreventExtensions(Managed *)
+{
+ return true;
+}
+
+bool Module::virtualDefineOwnProperty(Managed *, PropertyKey, const Property *, PropertyAttributes)
+{
+ return false;
+}
+
+bool Module::virtualPut(Managed *, PropertyKey, const Value &, Value *)
+{
+ return false;
+}
+
+bool Module::virtualDeleteProperty(Managed *m, PropertyKey id)
+{
+ if (id.isSymbol())
+ return Object::virtualDeleteProperty(m, id);
+ const Module *module = static_cast<const Module *>(m);
+ Scope scope(m->engine());
+ ScopedString expectedName(scope, id.toStringOrSymbol(scope.engine));
+ if (!expectedName)
+ return true;
+ const Value *v = module->d()->unit->resolveExport(expectedName);
+ if (v)
+ return false;
+ return true;
+}
+
+struct ModuleNamespaceIterator : ObjectOwnPropertyKeyIterator
+{
+ QStringList exportedNames;
+ int exportIndex = 0;
+ ModuleNamespaceIterator(const QStringList &names) : exportedNames(names) {}
+ ~ModuleNamespaceIterator() override = default;
+ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
+
+};
+
+PropertyKey ModuleNamespaceIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
+{
+ const Module *module = static_cast<const Module *>(o);
+ if (exportIndex < exportedNames.count()) {
+ if (attrs)
+ *attrs = Attr_Data;
+ Scope scope(module->engine());
+ ScopedString exportName(scope, scope.engine->newString(exportedNames.at(exportIndex)));
+ exportIndex++;
+ const Value *v = module->d()->unit->resolveExport(exportName);
+ if (pd) {
+ if (v->isEmpty())
+ scope.engine->throwReferenceError(exportName);
+ else
+ pd->value = *v;
+ }
+ return exportName->toPropertyKey();
+ }
+ return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
+}
+
+OwnPropertyKeyIterator *Module::virtualOwnPropertyKeys(const Object *o, Value *target)
+{
+ const Module *module = static_cast<const Module *>(o);
+ *target = *o;
+ return new ModuleNamespaceIterator(module->d()->unit->exportedNames());
+}
+
+Heap::Object *Module::virtualGetPrototypeOf(const Managed *)
+{
+ return nullptr;
+}
+
+bool Module::virtualSetPrototypeOf(Managed *, const Object *proto)
+{
+ return proto == nullptr;
+}
+
+bool Module::virtualIsExtensible(const Managed *)
+{
+ return false;
+}
diff --git a/src/qml/jsruntime/qv4module_p.h b/src/qml/jsruntime/qv4module_p.h
new file mode 100644
index 0000000000..0cab161b82
--- /dev/null
+++ b/src/qml/jsruntime/qv4module_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4MODULE
+#define QV4MODULE
+
+//
+// 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 "qv4object_p.h"
+#include "qv4context_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Heap {
+
+#define ModuleMembers(class, Member) \
+ Member(class, NoMark, CompiledData::CompilationUnit *, unit) \
+ Member(class, Pointer, CallContext *, scope) \
+ Member(class, HeapValue, HeapValue, self)
+
+DECLARE_EXPORTED_HEAP_OBJECT(Module, Object) {
+ DECLARE_MARKOBJECTS(Module)
+
+ void init(ExecutionEngine *engine, CompiledData::CompilationUnit *moduleUnit);
+};
+
+}
+
+struct Q_QML_EXPORT Module : public Object {
+ V4_OBJECT2(Module, Object)
+
+ static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
+ static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p);
+ static bool virtualHasProperty(const Managed *m, PropertyKey id);
+ static bool virtualPreventExtensions(Managed *);
+ static bool virtualDefineOwnProperty(Managed *, PropertyKey, const Property *, PropertyAttributes);
+ static bool virtualPut(Managed *, PropertyKey, const Value &, Value *);
+ static bool virtualDeleteProperty(Managed *m, PropertyKey id);
+ static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
+ static Heap::Object *virtualGetPrototypeOf(const Managed *);
+ static bool virtualSetPrototypeOf(Managed *, const Object *proto);
+ static bool virtualIsExtensible(const Managed *);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4MODULE
diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp
index f58ff45801..11ec53ced5 100644
--- a/src/qml/jsruntime/qv4numberobject.cpp
+++ b/src/qml/jsruntime/qv4numberobject.cpp
@@ -78,13 +78,13 @@ void Heap::NumberCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("Number"));
}
-ReturnedValue NumberCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue NumberCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
{
double dbl = argc ? argv[0].toNumber() : 0.;
return Encode(f->engine()->newNumberObject(dbl));
}
-ReturnedValue NumberCtor::call(const FunctionObject *, const Value *, const Value *argv, int argc)
+ReturnedValue NumberCtor::virtualCall(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double dbl = argc ? argv[0].toNumber() : 0.;
return Encode(dbl);
@@ -95,19 +95,19 @@ void NumberPrototype::init(ExecutionEngine *engine, Object *ctor)
Scope scope(engine);
ScopedObject o(scope);
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
- ctor->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(qt_qnan()));
- ctor->defineReadonlyProperty(QStringLiteral("NEGATIVE_INFINITY"), Primitive::fromDouble(-qInf()));
- ctor->defineReadonlyProperty(QStringLiteral("POSITIVE_INFINITY"), Primitive::fromDouble(qInf()));
- ctor->defineReadonlyProperty(QStringLiteral("MAX_VALUE"), Primitive::fromDouble(1.7976931348623158e+308));
- ctor->defineReadonlyProperty(QStringLiteral("EPSILON"), Primitive::fromDouble(std::numeric_limits<double>::epsilon()));
- ctor->defineReadonlyProperty(QStringLiteral("MAX_SAFE_INTEGER"), Primitive::fromDouble(9007199254740991));
- ctor->defineReadonlyProperty(QStringLiteral("MIN_SAFE_INTEGER"), Primitive::fromDouble(-9007199254740991));
+ ctor->defineReadonlyProperty(QStringLiteral("NaN"), Value::fromDouble(qt_qnan()));
+ ctor->defineReadonlyProperty(QStringLiteral("NEGATIVE_INFINITY"), Value::fromDouble(-qInf()));
+ ctor->defineReadonlyProperty(QStringLiteral("POSITIVE_INFINITY"), Value::fromDouble(qInf()));
+ ctor->defineReadonlyProperty(QStringLiteral("MAX_VALUE"), Value::fromDouble(1.7976931348623158e+308));
+ ctor->defineReadonlyProperty(QStringLiteral("EPSILON"), Value::fromDouble(std::numeric_limits<double>::epsilon()));
+ ctor->defineReadonlyProperty(QStringLiteral("MAX_SAFE_INTEGER"), Value::fromDouble(9007199254740991));
+ ctor->defineReadonlyProperty(QStringLiteral("MIN_SAFE_INTEGER"), Value::fromDouble(-9007199254740991));
QT_WARNING_PUSH
QT_WARNING_DISABLE_INTEL(239)
- ctor->defineReadonlyProperty(QStringLiteral("MIN_VALUE"), Primitive::fromDouble(5e-324));
+ ctor->defineReadonlyProperty(QStringLiteral("MIN_VALUE"), Value::fromDouble(5e-324));
QT_WARNING_POP
ctor->defineDefaultProperty(QStringLiteral("isFinite"), method_isFinite, 1);
@@ -117,7 +117,7 @@ QT_WARNING_POP
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(engine->id_toString(), method_toString, 1);
- defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString);
+ defineDefaultProperty(engine->id_toLocaleString(), method_toLocaleString);
defineDefaultProperty(engine->id_valueOf(), method_valueOf);
defineDefaultProperty(QStringLiteral("toFixed"), method_toFixed, 1);
defineDefaultProperty(QStringLiteral("toExponential"), method_toExponential, 1);
@@ -150,7 +150,7 @@ inline double thisNumber(ExecutionEngine *engine, const Value *thisObject)
ReturnedValue NumberPrototype::method_isFinite(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- if (!argc)
+ if (!argc || !argv[0].isNumber())
return Encode(false);
double v = argv[0].toNumber();
@@ -193,7 +193,7 @@ ReturnedValue NumberPrototype::method_isSafeInteger(const FunctionObject *, cons
ReturnedValue NumberPrototype::method_isNaN(const FunctionObject *, const Value *, const Value *argv, int argc)
{
- if (!argc)
+ if (!argc || !argv[0].isNumber())
return Encode(false);
double v = argv[0].toNumber();
@@ -229,7 +229,7 @@ ReturnedValue NumberPrototype::method_toString(const FunctionObject *b, const Va
num = -num;
}
double frac = num - std::floor(num);
- num = Primitive::toInteger(num);
+ num = Value::toInteger(num);
do {
char c = (char)std::fmod(num, radix);
c = (c < 10) ? (c + '0') : (c - 10 + 'a');
@@ -252,7 +252,7 @@ ReturnedValue NumberPrototype::method_toString(const FunctionObject *b, const Va
}
}
- return Encode(Primitive::fromDouble(num).toString(v4));
+ return Encode(Value::fromDouble(num).toString(v4));
}
ReturnedValue NumberPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -282,7 +282,7 @@ ReturnedValue NumberPrototype::method_toFixed(const FunctionObject *b, const Val
if (std::isnan(fdigits))
fdigits = 0;
- if (fdigits < 0 || fdigits > 20)
+ if (fdigits < 0 || fdigits > 100)
return v4->throwRangeError(*thisObject);
QString str;
@@ -305,16 +305,21 @@ ReturnedValue NumberPrototype::method_toExponential(const FunctionObject *b, con
if (v4->hasException)
return QV4::Encode::undefined();
+ bool defaultDigits = !argc || argv[0].isUndefined();
+ int fdigits = !defaultDigits ? argv[0].toInteger() : NumberLocale::instance()->defaultDoublePrecision;
+ if (v4->hasException)
+ return QV4::Encode::undefined();
- int fdigits = NumberLocale::instance()->defaultDoublePrecision;
+ if (std::isnan(d))
+ return Encode(v4->newString(QLatin1String("NaN")));
- if (argc && !argv[0].isUndefined()) {
- fdigits = argv[0].toInt32();
- if (fdigits < 0 || fdigits > 20) {
- Scope scope(v4);
- ScopedString error(scope, v4->newString(QStringLiteral("Number.prototype.toExponential: fractionDigits out of range")));
- return v4->throwRangeError(error);
- }
+ if (qIsInf(d))
+ return Encode(v4->newString(QLatin1String(d < 0 ? "-Infinity" : "Infinity")));
+
+ if (!defaultDigits && (fdigits < 0 || fdigits > 100)) {
+ Scope scope(v4);
+ ScopedString error(scope, v4->newString(QStringLiteral("Number.prototype.toExponential: fractionDigits out of range")));
+ return v4->throwRangeError(error);
}
QString result = NumberLocale::instance()->toString(d, 'e', fdigits);
@@ -327,17 +332,26 @@ ReturnedValue NumberPrototype::method_toPrecision(const FunctionObject *b, const
ScopedValue v(scope, thisNumberValue(scope.engine, thisObject));
if (scope.engine->hasException)
return QV4::Encode::undefined();
-
+ double d = v->asDouble();
if (!argc || argv[0].isUndefined())
return Encode(v->toString(scope.engine));
int precision = argv[0].toInt32();
- if (precision < 1 || precision > 21) {
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
+
+ if (std::isnan(d))
+ return Encode(scope.engine->newString(QLatin1String("NaN")));
+
+ if (qIsInf(d))
+ return Encode(scope.engine->newString(QLatin1String(d < 0 ? "-Infinity" : "Infinity")));
+
+ if (precision < 1 || precision > 100) {
ScopedString error(scope, scope.engine->newString(QStringLiteral("Number.prototype.toPrecision: precision out of range")));
return scope.engine->throwRangeError(error);
}
- QString result = NumberLocale::instance()->toString(v->asDouble(), 'g', precision);
+ QString result = NumberLocale::instance()->toString(d, 'g', precision);
return Encode(scope.engine->newString(result));
}
diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h
index cfdcf9bc1d..576817cf36 100644
--- a/src/qml/jsruntime/qv4numberobject_p.h
+++ b/src/qml/jsruntime/qv4numberobject_p.h
@@ -79,8 +79,8 @@ struct NumberCtor: FunctionObject
{
V4_OBJECT2(NumberCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
struct NumberPrototype: NumberObject
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index 0c6cde84ad..a7ede4627c 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -50,6 +50,8 @@
#include "qv4string_p.h"
#include "qv4identifiertable_p.h"
#include "qv4jscall_p.h"
+#include "qv4symbol_p.h"
+#include "qv4proxy_p.h"
#include <stdint.h>
@@ -57,9 +59,9 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(Object);
-void Object::setInternalClass(InternalClass *ic)
+void Object::setInternalClass(Heap::InternalClass *ic)
{
- d()->internalClass = ic;
+ d()->internalClass.set(engine(), ic);
if (ic->isUsedAsProto)
ic->updateProtoUsage(d());
Q_ASSERT(ic && ic->vtable);
@@ -72,40 +74,26 @@ void Object::setInternalClass(InternalClass *ic)
d()->memberData.set(ic->engine, MemberData::allocate(ic->engine, requiredSize, d()->memberData));
}
-void Object::getProperty(uint index, Property *p, PropertyAttributes *attrs) const
+void Object::getProperty(const InternalClassEntry &entry, Property *p) const
{
- p->value = *propertyData(index);
- *attrs = internalClass()->propertyData.at(index);
- if (attrs->isAccessor())
- p->set = *propertyData(index + SetterOffset);
+ p->value = *propertyData(entry.index);
+ if (entry.attributes.isAccessor())
+ p->set = *propertyData(entry.setterIndex);
}
-void Object::setProperty(uint index, const Property *p)
+void Object::setProperty(const InternalClassEntry &entry, const Property *p)
{
- setProperty(index, p->value);
- if (internalClass()->propertyData.at(index).isAccessor())
- setProperty(index + SetterOffset, p->set);
+ setProperty(entry.index, p->value);
+ if (entry.attributes.isAccessor())
+ setProperty(entry.setterIndex, p->set);
}
void Heap::Object::setUsedAsProto()
{
- internalClass = internalClass->asProtoClass();
+ internalClass.set(internalClass->engine, internalClass->asProtoClass());
}
-bool Object::setPrototype(Object *proto)
-{
- Heap::Object *p = proto ? proto->d() : nullptr;
- Heap::Object *pp = p;
- while (pp) {
- if (pp == d())
- return false;
- pp = pp->prototype();
- }
- setInternalClass(internalClass()->changePrototype(p));
- return true;
-}
-
-ReturnedValue Object::getValue(const Value &thisObject, const Value &v, PropertyAttributes attrs)
+ReturnedValue Object::getValueAccessor(const Value &thisObject, const Value &v, PropertyAttributes attrs)
{
if (!attrs.isAccessor())
return v.asReturnedValue();
@@ -119,16 +107,14 @@ ReturnedValue Object::getValue(const Value &thisObject, const Value &v, Property
return f->call(jsCallData);
}
-bool Object::putValue(uint memberIndex, const Value &value)
+bool Object::putValue(uint memberIndex, PropertyAttributes attrs, const Value &value)
{
- QV4::InternalClass *ic = internalClass();
+ Heap::InternalClass *ic = internalClass();
if (ic->engine->hasException)
return false;
- PropertyAttributes attrs = ic->propertyData[memberIndex];
-
if (attrs.isAccessor()) {
- const FunctionObject *set = propertyData(memberIndex + SetterOffset)->as<FunctionObject>();
+ const FunctionObject *set = propertyData(memberIndex)->as<FunctionObject>();
if (set) {
Scope scope(ic->engine);
ScopedFunctionObject setter(scope, set);
@@ -148,37 +134,34 @@ bool Object::putValue(uint memberIndex, const Value &value)
return true;
}
-void Object::defineDefaultProperty(const QString &name, const Value &value)
+void Object::defineDefaultProperty(const QString &name, const Value &value, PropertyAttributes attributes)
{
ExecutionEngine *e = engine();
Scope scope(e);
ScopedString s(scope, e->newIdentifier(name));
- defineDefaultProperty(s, value);
+ defineDefaultProperty(s, value, attributes);
}
-void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount)
+void Object::defineDefaultProperty(const QString &name, VTable::Call code,
+ int argumentCount, PropertyAttributes attributes)
{
ExecutionEngine *e = engine();
Scope scope(e);
ScopedString s(scope, e->newIdentifier(name));
- ExecutionContext *global = e->rootContext();
- ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(global, s, code));
- function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount));
- defineDefaultProperty(s, function);
+ ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(e, s, code, argumentCount));
+ defineDefaultProperty(s, function, attributes);
}
-void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount)
+void Object::defineDefaultProperty(StringOrSymbol *nameOrSymbol, VTable::Call code,
+ int argumentCount, PropertyAttributes attributes)
{
ExecutionEngine *e = engine();
Scope scope(e);
- ExecutionContext *global = e->rootContext();
- ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(global, name, code));
- function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount));
- defineDefaultProperty(name, function);
+ ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(e, nameOrSymbol, code, argumentCount));
+ defineDefaultProperty(nameOrSymbol, function, attributes);
}
-void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int),
- ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int))
+void Object::defineAccessorProperty(const QString &name, VTable::Call getter, VTable::Call setter)
{
ExecutionEngine *e = engine();
Scope scope(e);
@@ -186,16 +169,27 @@ void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)
defineAccessorProperty(s, getter, setter);
}
-void Object::defineAccessorProperty(String *name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int),
- ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int))
+void Object::defineAccessorProperty(StringOrSymbol *name, VTable::Call getter, VTable::Call setter)
{
ExecutionEngine *v4 = engine();
QV4::Scope scope(v4);
ScopedProperty p(scope);
- ExecutionContext *global = v4->rootContext();
- p->setGetter(ScopedFunctionObject(scope, (getter ? FunctionObject::createBuiltinFunction(global, name, getter) : nullptr)));
- p->setSetter(ScopedFunctionObject(scope, (setter ? FunctionObject::createBuiltinFunction(global, name, setter) : nullptr)));
- insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable);
+ QString n = name->toQString();
+ if (n.at(0) == QLatin1Char('@'))
+ n = QChar::fromLatin1('[') + n.midRef(1) + QChar::fromLatin1(']');
+ if (getter) {
+ ScopedString getName(scope, v4->newString(QString::fromLatin1("get ") + n));
+ p->setGetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(v4, getName, getter, 0)));
+ } else {
+ p->setGetter(nullptr);
+ }
+ if (setter) {
+ ScopedString setName(scope, v4->newString(QString::fromLatin1("set ") + n));
+ p->setSetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(v4, setName, setter, 0)));
+ } else {
+ p->setSetter(nullptr);
+ }
+ insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable);
}
@@ -221,13 +215,23 @@ void Object::defineReadonlyConfigurableProperty(const QString &name, const Value
defineReadonlyConfigurableProperty(s, value);
}
-void Object::defineReadonlyConfigurableProperty(String *name, const Value &value)
+void Object::defineReadonlyConfigurableProperty(StringOrSymbol *name, const Value &value)
{
insertMember(name, value, Attr_ReadOnly_ButConfigurable);
}
+void Object::addSymbolSpecies()
+{
+ Scope scope(engine());
+ ScopedProperty p(scope);
+ p->setGetter(scope.engine->getSymbolSpecies());
+ p->setSetter(nullptr);
+ insertMember(scope.engine->symbol_species(), p, QV4::Attr_Accessor|QV4::Attr_NotWritable|QV4::Attr_NotEnumerable);
+}
+
void Heap::Object::markObjects(Heap::Base *b, MarkStack *stack)
{
+ Base::markObjects(b, stack);
Object *o = static_cast<Object *>(b);
if (o->memberData)
o->memberData->mark(stack);
@@ -242,367 +246,208 @@ void Heap::Object::markObjects(Heap::Base *b, MarkStack *stack)
}
}
-void Object::insertMember(String *s, const Property *p, PropertyAttributes attributes)
+void Object::insertMember(StringOrSymbol *s, const Property *p, PropertyAttributes attributes)
{
- uint idx;
- InternalClass::addMember(this, s, attributes, &idx);
+ InternalClassEntry idx;
+ PropertyKey key = s->toPropertyKey();
+ Heap::InternalClass::addMember(this, key, attributes, &idx);
- if (attributes.isAccessor()) {
- setProperty(idx + GetterOffset, p->value);
- setProperty(idx + SetterOffset, p->set);
- } else {
- setProperty(idx, p->value);
- }
+ setProperty(idx.index, p->value);
+ if (attributes.isAccessor())
+ setProperty(idx.setterIndex, p->set);
}
-// Section 8.12.1
-void Object::getOwnProperty(String *name, PropertyAttributes *attrs, Property *p)
+void Object::setPrototypeUnchecked(const Object *p)
{
- uint idx = name->asArrayIndex();
- if (idx != UINT_MAX)
- return getOwnProperty(idx, attrs, p);
-
- name->makeIdentifier();
- Identifier *id = name->identifier();
-
- uint member = internalClass()->find(id);
- if (member < UINT_MAX) {
- *attrs = internalClass()->propertyData[member];
- if (p) {
- p->value = *propertyData(member);
- if (attrs->isAccessor())
- p->set = *propertyData(member + SetterOffset);
- }
- return;
- }
-
- if (attrs)
- *attrs = Attr_Invalid;
- return;
-}
-
-void Object::getOwnProperty(uint index, PropertyAttributes *attrs, Property *p)
-{
- if (arrayData()) {
- if (arrayData()->getProperty(index, p, attrs))
- return;
- }
- if (isStringObject()) {
- *attrs = Attr_NotConfigurable|Attr_NotWritable;
- if (p)
- p->value = static_cast<StringObject *>(this)->getIndex(index);
- return;
- }
-
- if (attrs)
- *attrs = Attr_Invalid;
- return;
+ setInternalClass(internalClass()->changePrototype(p ? p->d() : nullptr));
}
// Section 8.12.2
-MemberData::Index Object::getValueOrSetter(String *name, PropertyAttributes *attrs)
+PropertyIndex Object::getValueOrSetter(PropertyKey id, PropertyAttributes *attrs)
{
- Q_ASSERT(name->asArrayIndex() == UINT_MAX);
-
- name->makeIdentifier();
- Identifier *id = name->identifier();
-
- Heap::Object *o = d();
- while (o) {
- uint idx = o->internalClass->find(id);
- if (idx < UINT_MAX) {
- *attrs = o->internalClass->propertyData[idx];
- return o->writablePropertyData(attrs->isAccessor() ? idx + SetterOffset : idx );
- }
-
- o = o->prototype();
- }
- *attrs = Attr_Invalid;
- return { nullptr, nullptr };
-}
-
-ArrayData::Index Object::getValueOrSetter(uint index, PropertyAttributes *attrs)
-{
- Heap::Object *o = d();
- while (o) {
- if (o->arrayData) {
- uint idx = o->arrayData->mappedIndex(index);
- if (idx != UINT_MAX) {
- *attrs = o->arrayData->attributes(index);
- return { o->arrayData , attrs->isAccessor() ? idx + SetterOffset : idx };
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+ Heap::Object *o = d();
+ while (o) {
+ if (o->arrayData) {
+ uint idx = o->arrayData->mappedIndex(index);
+ if (idx != UINT_MAX) {
+ *attrs = o->arrayData->attributes(index);
+ return { o->arrayData , o->arrayData->values.values + (attrs->isAccessor() ? idx + SetterOffset : idx) };
+ }
+ }
+ if (o->vtable()->type == Type_StringObject) {
+ if (index < static_cast<const Heap::StringObject *>(o)->length()) {
+ // this is an evil hack, but it works, as the method is only ever called from put,
+ // where we don't use the returned pointer there for non writable attributes
+ *attrs = (Attr_NotWritable|Attr_NotConfigurable);
+ return { reinterpret_cast<Heap::ArrayData *>(0x1), nullptr };
+ }
}
+ o = o->prototype();
}
- if (o->vtable()->type == Type_StringObject) {
- if (index < static_cast<const Heap::StringObject *>(o)->length()) {
- // this is an evil hack, but it works, as the method is only ever called from putIndexed,
- // where we don't use the returned pointer there for non writable attributes
- *attrs = (Attr_NotWritable|Attr_NotConfigurable);
- return { reinterpret_cast<Heap::ArrayData *>(0x1), 0 };
+ } else {
+ Heap::Object *o = d();
+ while (o) {
+ auto idx = o->internalClass->findValueOrSetter(id);
+ if (idx.isValid()) {
+ *attrs = idx.attrs;
+ return o->writablePropertyData(idx.index);
}
+
+ o = o->prototype();
}
- o = o->prototype();
}
*attrs = Attr_Invalid;
- return { nullptr, 0 };
-}
-
-bool Object::hasProperty(String *name) const
-{
- uint idx = name->asArrayIndex();
- if (idx != UINT_MAX)
- return hasProperty(idx);
-
- Scope scope(engine());
- ScopedObject o(scope, d());
- while (o) {
- if (o->hasOwnProperty(name))
- return true;
-
- o = o->prototype();
- }
-
- return false;
-}
-
-bool Object::hasProperty(uint index) const
-{
- Scope scope(engine());
- ScopedObject o(scope, d());
- while (o) {
- if (o->hasOwnProperty(index))
- return true;
-
- o = o->prototype();
- }
-
- return false;
-}
-
-bool Object::hasOwnProperty(String *name) const
-{
- uint idx = name->asArrayIndex();
- if (idx != UINT_MAX)
- return hasOwnProperty(idx);
-
- name->makeIdentifier();
- Identifier *id = name->identifier();
-
- if (internalClass()->find(id) < UINT_MAX)
- return true;
- if (!query(name).isEmpty())
- return true;
- return false;
-}
-
-bool Object::hasOwnProperty(uint index) const
-{
- if (arrayData() && !arrayData()->isEmpty(index))
- return true;
-
- if (isStringObject()) {
- if (index < static_cast<const StringObject *>(this)->length())
- return true;
- }
- if (!queryIndexed(index).isEmpty())
- return true;
- return false;
-}
-
-ReturnedValue Object::callAsConstructor(const FunctionObject *f, const Value *, int)
-{
- return f->engine()->throwTypeError();
-}
-
-ReturnedValue Object::call(const FunctionObject *f, const Value *, const Value *, int)
-{
- return f->engine()->throwTypeError();
-}
-
-ReturnedValue Object::get(const Managed *m, String *name, bool *hasProperty)
-{
- return static_cast<const Object *>(m)->internalGet(name, hasProperty);
-}
-
-ReturnedValue Object::getIndexed(const Managed *m, uint index, bool *hasProperty)
-{
- return static_cast<const Object *>(m)->internalGetIndexed(index, hasProperty);
-}
-
-bool Object::put(Managed *m, String *name, const Value &value)
-{
- return static_cast<Object *>(m)->internalPut(name, value);
-}
-
-bool Object::putIndexed(Managed *m, uint index, const Value &value)
-{
- return static_cast<Object *>(m)->internalPutIndexed(index, value);
-}
-
-PropertyAttributes Object::query(const Managed *m, String *name)
-{
- uint idx = name->asArrayIndex();
- if (idx != UINT_MAX)
- return queryIndexed(m, idx);
-
- name->makeIdentifier();
- Identifier *id = name->identifier();
-
- const Object *o = static_cast<const Object *>(m);
- idx = o->internalClass()->find(id);
- if (idx < UINT_MAX)
- return o->internalClass()->propertyData[idx];
-
- return Attr_Invalid;
+ return { nullptr, nullptr };
}
-PropertyAttributes Object::queryIndexed(const Managed *m, uint index)
+ReturnedValue Object::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
- const Object *o = static_cast<const Object *>(m);
- if (o->arrayData() && !o->arrayData()->isEmpty(index))
- return o->arrayData()->attributes(index);
-
- if (o->isStringObject()) {
- if (index < static_cast<const StringObject *>(o)->length())
- return (Attr_NotWritable|Attr_NotConfigurable);
- }
- return Attr_Invalid;
+ return static_cast<const Object *>(m)->internalGet(id, receiver, hasProperty);
}
-bool Object::deleteProperty(Managed *m, String *name)
+bool Object::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
- return static_cast<Object *>(m)->internalDeleteProperty(name);
+ return static_cast<Object *>(m)->internalPut(id, value, receiver);
}
-bool Object::deleteIndexedProperty(Managed *m, uint index)
+bool Object::virtualDeleteProperty(Managed *m, PropertyKey id)
{
- return static_cast<Object *>(m)->internalDeleteIndexedProperty(index);
+ return static_cast<Object *>(m)->internalDeleteProperty(id);
}
-void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *pd, PropertyAttributes *attrs)
+PropertyKey ObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
{
- Object *o = static_cast<Object *>(m);
- name->setM(nullptr);
- *index = UINT_MAX;
-
- if (o->arrayData()) {
- if (!it->arrayIndex)
- it->arrayNode = o->sparseBegin();
+ if (arrayIndex != UINT_MAX && o->arrayData()) {
+ if (!arrayIndex)
+ arrayNode = o->sparseBegin();
// sparse arrays
- if (it->arrayNode) {
- while (it->arrayNode != o->sparseEnd()) {
- int k = it->arrayNode->key();
- uint pidx = it->arrayNode->value;
+ if (arrayNode) {
+ while (arrayNode != o->sparseEnd()) {
+ uint k = arrayNode->key();
+ uint pidx = arrayNode->value;
Heap::SparseArrayData *sa = o->d()->arrayData.cast<Heap::SparseArrayData>();
const Property *p = reinterpret_cast<const Property *>(sa->values.data() + pidx);
- it->arrayNode = it->arrayNode->nextNode();
+ arrayNode = arrayNode->nextNode();
PropertyAttributes a = sa->attrs ? sa->attrs[pidx] : Attr_Data;
- if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) {
- it->arrayIndex = k + 1;
- *index = k;
- *attrs = a;
+ arrayIndex = k + 1;
+ if (pd)
pd->copy(p, a);
- return;
- }
+ if (attrs)
+ *attrs = a;
+ return PropertyKey::fromArrayIndex(k);
}
- it->arrayNode = nullptr;
- it->arrayIndex = UINT_MAX;
+ arrayNode = nullptr;
+ arrayIndex = UINT_MAX;
}
// dense arrays
- while (it->arrayIndex < o->d()->arrayData->values.size) {
+ while (arrayIndex < o->d()->arrayData->values.size) {
Heap::SimpleArrayData *sa = o->d()->arrayData.cast<Heap::SimpleArrayData>();
- const Value &val = sa->data(it->arrayIndex);
- PropertyAttributes a = o->arrayData()->attributes(it->arrayIndex);
- ++it->arrayIndex;
- if (!val.isEmpty()
- && (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable())) {
- *index = it->arrayIndex - 1;
- *attrs = a;
- pd->value = val;
- return;
+ const Value &val = sa->data(arrayIndex);
+ PropertyAttributes a = o->arrayData()->attributes(arrayIndex);
+ int index = arrayIndex;
+ ++arrayIndex;
+ if (!val.isEmpty()) {
+ if (pd)
+ pd->value = val;
+ if (attrs)
+ *attrs = a;
+ return PropertyKey::fromArrayIndex(index);
}
}
+ arrayIndex = UINT_MAX;
}
- while (it->memberIndex < o->internalClass()->size) {
- Identifier *n = o->internalClass()->nameMap.at(it->memberIndex);
- if (!n) {
- // accessor properties have a dummy entry with n == 0
- ++it->memberIndex;
- continue;
- }
-
- int idx = it->memberIndex;
- PropertyAttributes a = o->internalClass()->propertyData[it->memberIndex];
- ++it->memberIndex;
- if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) {
- name->setM(o->engine()->identifierTable->stringFromIdentifier(n));
- *attrs = a;
- pd->value = *o->propertyData(idx);
- if (a.isAccessor())
- pd->set = *o->propertyData(idx + SetterOffset);
- return;
+ while (true) {
+ while (memberIndex < o->internalClass()->size) {
+ PropertyKey n = o->internalClass()->nameMap.at(memberIndex);
+ ++memberIndex;
+ if (!n.isStringOrSymbol())
+ // accessor properties have a dummy entry with n == 0
+ continue;
+ if (!iterateOverSymbols && n.isSymbol())
+ continue;
+ if (iterateOverSymbols && !n.isSymbol())
+ continue;
+
+ InternalClassEntry e = o->internalClass()->find(n);
+ if (!e.isValid())
+ continue;
+ if (pd) {
+ pd->value = *o->propertyData(e.index);
+ if (e.attributes.isAccessor())
+ pd->set = *o->propertyData(e.setterIndex);
+ }
+ if (attrs)
+ *attrs = e.attributes;
+ return n;
}
+ if (iterateOverSymbols)
+ break;
+ iterateOverSymbols = true;
+ memberIndex = 0;
}
- *attrs = PropertyAttributes();
+ return PropertyKey::invalid();
}
-// Section 8.12.3
-ReturnedValue Object::internalGet(String *name, bool *hasProperty) const
+OwnPropertyKeyIterator *Object::virtualOwnPropertyKeys(const Object *o, Value *target)
{
- uint idx = name->asArrayIndex();
- if (idx != UINT_MAX)
- return getIndexed(idx, hasProperty);
-
- name->makeIdentifier();
- Identifier *id = name->identifier();
-
- Heap::Object *o = d();
- while (o) {
- uint idx = o->internalClass->find(id);
- if (idx < UINT_MAX) {
- if (hasProperty)
- *hasProperty = true;
- return getValue(*o->propertyData(idx), o->internalClass->propertyData.at(idx));
- }
-
- o = o->prototype();
- }
-
- if (hasProperty)
- *hasProperty = false;
- return Encode::undefined();
+ *target = *o;
+ return new ObjectOwnPropertyKeyIterator;
}
-ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const
+// Section 8.12.3
+ReturnedValue Object::internalGet(PropertyKey id, const Value *receiver, bool *hasProperty) const
{
- PropertyAttributes attrs;
- Scope scope(engine());
- ScopedObject o(scope, this);
- ScopedProperty pd(scope);
- bool exists = false;
- while (o) {
- if (o->arrayData() && o->arrayData()->getProperty(index, pd, &attrs)) {
- exists = true;
- break;
+ Heap::Object *o = d();
+
+ uint index = id.asArrayIndex();
+ if (index != UINT_MAX) {
+ Scope scope(this);
+ PropertyAttributes attrs;
+ ScopedProperty pd(scope);
+ while (1) {
+ if (o->arrayData && o->arrayData->getProperty(index, pd, &attrs)) {
+ if (hasProperty)
+ *hasProperty = true;
+ return Object::getValue(*receiver, pd->value, attrs);
+ }
+ if (o->internalClass->vtable->type == Type_StringObject) {
+ ScopedString str(scope, static_cast<Heap::StringObject *>(o)->getIndex(index));
+ if (str) {
+ attrs = (Attr_NotWritable|Attr_NotConfigurable);
+ if (hasProperty)
+ *hasProperty = true;
+ return str.asReturnedValue();
+ }
+ }
+ o = o->prototype();
+ if (!o || o->internalClass->vtable->get != Object::virtualGet)
+ break;
}
- if (o->isStringObject()) {
- ScopedString str(scope, static_cast<StringObject *>(o.getPointer())->getIndex(index));
- if (str) {
- attrs = (Attr_NotWritable|Attr_NotConfigurable);
+ } else {
+ Q_ASSERT(!id.isArrayIndex());
+
+ while (1) {
+ auto idx = o->internalClass->findValueOrGetter(id);
+ if (idx.isValid()) {
if (hasProperty)
*hasProperty = true;
- return str.asReturnedValue();
+ return Object::getValue(*receiver, *o->propertyData(idx.index), idx.attrs);
}
+ o = o->prototype();
+ if (!o || o->internalClass->vtable->get != Object::virtualGet)
+ break;
}
- o = o->prototype();
}
- if (exists) {
- if (hasProperty)
- *hasProperty = true;
- return getValue(pd->value, attrs);
+ if (o) {
+ const Value v = Value::fromHeapObject(o);
+ const Object &obj = static_cast<const Object &>(v);
+ return obj.get(id, receiver, hasProperty);
}
if (hasProperty)
@@ -610,295 +455,141 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const
return Encode::undefined();
}
-
// Section 8.12.5
-bool Object::internalPut(String *name, const Value &value)
+bool Object::internalPut(PropertyKey id, const Value &value, Value *receiver)
{
- ExecutionEngine *engine = this->engine();
- if (engine->hasException)
+ Scope scope(this);
+ if (scope.engine->hasException)
return false;
- uint idx = name->asArrayIndex();
- if (idx != UINT_MAX)
- return putIndexed(idx, value);
-
- name->makeIdentifier();
- Identifier *id = name->identifier();
-
- MemberData::Index memberIndex{nullptr, nullptr};
- uint member = internalClass()->find(id);
- PropertyAttributes attrs;
- if (member < UINT_MAX) {
- attrs = internalClass()->propertyData[member];
- memberIndex = d()->writablePropertyData(attrs.isAccessor() ? member + SetterOffset : member);
- }
-
- // clause 1
- if (!memberIndex.isNull()) {
- if (attrs.isAccessor()) {
- if (memberIndex->as<FunctionObject>())
- goto cont;
- return false;
- } else if (!attrs.isWritable())
- return false;
- else if (isArrayObject() && name->equals(engine->id_length())) {
- bool ok;
- uint l = value.asArrayLength(&ok);
- if (!ok) {
- engine->throwRangeError(value);
- return false;
+ Object *r = receiver->objectValue();
+ if (r && r->d() == d()) {
+ // receiver and this object are the same
+ 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 (arrayData())
+ propertyIndex = arrayData()->getValueOrSetter(index, &attrs);
+ } else {
+ auto member = internalClass()->findValueOrSetter(id);
+ if (member.isValid()) {
+ attrs = member.attrs;
+ propertyIndex = d()->writablePropertyData(member.index);
+ }
}
- ok = setArrayLength(l);
- if (!ok)
- return false;
- } else {
- memberIndex.set(engine, value);
- }
- return true;
- } else if (!prototype()) {
- if (!isExtensible())
- return false;
- } else {
- // clause 4
- Scope scope(engine);
- memberIndex = ScopedObject(scope, prototype())->getValueOrSetter(name, &attrs);
- if (!memberIndex.isNull()) {
- if (attrs.isAccessor()) {
- if (!memberIndex->as<FunctionObject>())
+
+ if (!propertyIndex.isNull() && !attrs.isAccessor()) {
+ if (!attrs.isWritable())
return false;
- } else if (!isExtensible() || !attrs.isWritable()) {
- return false;
+ else if (isArrayObject() && id == scope.engine->id_length()->propertyKey()) {
+ bool ok;
+ uint l = value.asArrayLength(&ok);
+ if (!ok) {
+ scope.engine->throwRangeError(value);
+ return false;
+ }
+ ok = setArrayLength(l);
+ if (!ok)
+ return false;
+ } else {
+ propertyIndex.set(scope.engine, value);
+ }
+ return true;
}
- } else if (!isExtensible()) {
- return false;
}
}
- cont:
-
- // Clause 5
- if (!memberIndex.isNull() && attrs.isAccessor()) {
- Q_ASSERT(memberIndex->as<FunctionObject>());
+ ScopedProperty p(scope);
+ PropertyAttributes attrs;
+ attrs = getOwnProperty(id, p);
+ if (attrs == Attr_Invalid) {
+ ScopedObject p(scope, getPrototypeOf());
+ if (p)
+ return p->put(id, value, receiver);
+ attrs = Attr_Data;
+ }
- Scope scope(engine);
- ScopedFunctionObject setter(scope, *memberIndex);
+ if (attrs.isAccessor()) {
+ ScopedFunctionObject setter(scope, p->setter());
+ if (!setter)
+ return false;
JSCallData jsCallData(scope, 1);
jsCallData->args[0] = value;
- *jsCallData->thisObject = this;
+ *jsCallData->thisObject = *receiver;
setter->call(jsCallData);
- return !engine->hasException;
+ return !scope.engine->hasException;
}
- insertMember(name, value);
- return true;
-}
-
-bool Object::internalPutIndexed(uint index, const Value &value)
-{
- ExecutionEngine *engine = this->engine();
- if (engine->hasException)
+ // Data property
+ if (!attrs.isWritable())
return false;
+ if (!r)
+ return false;
+ attrs = r->getOwnProperty(id, p);
- PropertyAttributes attrs;
-
- ArrayData::Index arrayIndex = arrayData() ? arrayData()->getValueOrSetter(index, &attrs) : ArrayData::Index{ nullptr, 0 };
-
- if (arrayIndex.isNull() && isStringObject()) {
- if (index < static_cast<StringObject *>(this)->length())
- // not writable
- return false;
- }
-
- // clause 1
- if (!arrayIndex.isNull()) {
- if (attrs.isAccessor()) {
- if (arrayIndex->as<FunctionObject>())
- goto cont;
- return false;
- } else if (!attrs.isWritable())
- return false;
-
- arrayIndex.set(engine, value);
- return true;
- } else if (!prototype()) {
- if (!isExtensible())
+ if (attrs != Attr_Invalid) {
+ if (attrs.isAccessor() || !attrs.isWritable())
return false;
} else {
- // clause 4
- Scope scope(engine);
- arrayIndex = ScopedObject(scope, prototype())->getValueOrSetter(index, &attrs);
- if (!arrayIndex.isNull()) {
- if (attrs.isAccessor()) {
- if (!arrayIndex->as<FunctionObject>())
- return false;
- } else if (!isExtensible() || !attrs.isWritable()) {
- return false;
- }
- } else if (!isExtensible()) {
+ if (!r->isExtensible())
return false;
- }
+ attrs = Attr_Data;
}
- cont:
-
- // Clause 5
- if (!arrayIndex.isNull() && attrs.isAccessor()) {
- Q_ASSERT(arrayIndex->as<FunctionObject>());
-
- Scope scope(engine);
- ScopedFunctionObject setter(scope, *arrayIndex);
- JSCallData jsCallData(scope, 1);
- jsCallData->args[0] = value;
- *jsCallData->thisObject = this;
- setter->call(jsCallData);
- return !engine->hasException;
+ if (r->internalClass()->vtable->defineOwnProperty == virtualDefineOwnProperty) {
+ // standard object, we can avoid some more checks
+ uint index = id.asArrayIndex();
+ if (index == UINT_MAX) {
+ ScopedStringOrSymbol s(scope, id.asStringOrSymbol());
+ r->insertMember(s, value);
+ } else {
+ r->arraySet(index, value);
+ }
+ return true;
}
- arraySet(index, value);
- return true;
+ p->value = value;
+ return r->defineOwnProperty(id, p, attrs);
}
// Section 8.12.7
-bool Object::internalDeleteProperty(String *name)
+bool Object::internalDeleteProperty(PropertyKey id)
{
if (internalClass()->engine->hasException)
return false;
- uint idx = name->asArrayIndex();
- if (idx != UINT_MAX)
- return deleteIndexedProperty(idx);
-
- name->makeIdentifier();
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+ Scope scope(engine());
+ if (scope.engine->hasException)
+ return false;
- uint memberIdx = internalClass()->find(name->identifier());
- if (memberIdx != UINT_MAX) {
- if (internalClass()->propertyData[memberIdx].isConfigurable()) {
- InternalClass::removeMember(this, name->identifier());
+ Scoped<ArrayData> ad(scope, arrayData());
+ if (!ad || ad->vtable()->del(this, index))
return true;
- }
- return false;
- }
-
- return true;
-}
-bool Object::internalDeleteIndexedProperty(uint index)
-{
- Scope scope(engine());
- if (scope.engine->hasException)
return false;
+ }
- Scoped<ArrayData> ad(scope, arrayData());
- if (!ad || ad->vtable()->del(this, index))
- return true;
-
- return false;
-}
-
-// Section 8.12.9
-bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const Property *p, PropertyAttributes attrs)
-{
- uint idx = name->asArrayIndex();
- if (idx != UINT_MAX)
- return __defineOwnProperty__(engine, idx, p, attrs);
-
- Scope scope(engine);
- name->makeIdentifier();
-
- uint memberIndex;
-
- if (isArrayObject() && name->equals(engine->id_length())) {
- Q_ASSERT(Heap::ArrayObject::LengthPropertyIndex == internalClass()->find(engine->id_length()));
- ScopedProperty lp(scope);
- PropertyAttributes cattrs;
- getProperty(Heap::ArrayObject::LengthPropertyIndex, lp, &cattrs);
- if (attrs.isEmpty() || p->isSubset(attrs, lp, cattrs))
+ auto memberIdx = internalClass()->findValueOrGetter(id);
+ if (memberIdx.isValid()) {
+ if (memberIdx.attrs.isConfigurable()) {
+ Heap::InternalClass::removeMember(this, id);
return true;
- if (!cattrs.isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable())
- return false;
- bool succeeded = true;
- if (attrs.type() == PropertyAttributes::Data) {
- bool ok;
- uint l = p->value.asArrayLength(&ok);
- if (!ok) {
- ScopedValue v(scope, p->value);
- engine->throwRangeError(v);
- return false;
- }
- succeeded = setArrayLength(l);
- }
- if (attrs.hasWritable() && !attrs.isWritable()) {
- cattrs.setWritable(false);
- InternalClass::changeMember(this, engine->id_length(), cattrs);
}
- if (!succeeded)
- return false;
- return true;
- }
-
- // Clause 1
- memberIndex = internalClass()->find(name->identifier());
-
- if (memberIndex == UINT_MAX) {
- // clause 3
- if (!isExtensible())
- return false;
- // clause 4
- ScopedProperty pd(scope);
- pd->copy(p, attrs);
- pd->fullyPopulated(&attrs);
- insertMember(name, pd, attrs);
- return true;
- }
-
- return __defineOwnProperty__(engine, memberIndex, name, p, attrs);
-}
-
-bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs)
-{
- // 15.4.5.1, 4b
- if (isArrayObject() && index >= getLength() && !internalClass()->propertyData[Heap::ArrayObject::LengthPropertyIndex].isWritable())
return false;
-
- if (ArgumentsObject::isNonStrictArgumentsObject(this))
- return static_cast<ArgumentsObject *>(this)->defineOwnProperty(engine, index, p, attrs);
-
- return defineOwnProperty2(engine, index, p, attrs);
-}
-
-bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs)
-{
- bool hasProperty = 0;
-
- // Clause 1
- if (arrayData()) {
- hasProperty = arrayData()->mappedIndex(index) != UINT_MAX;
- if (!hasProperty && isStringObject())
- hasProperty = (index < static_cast<StringObject *>(this)->length());
}
- if (!hasProperty) {
- // clause 3
- if (!isExtensible())
- return false;
- // clause 4
- Scope scope(engine);
- ScopedProperty pp(scope);
- pp->copy(p, attrs);
- pp->fullyPopulated(&attrs);
- if (attrs == Attr_Data) {
- ScopedValue v(scope, pp->value);
- arraySet(index, v);
- } else {
- arraySet(index, pp, attrs);
- }
- return true;
- }
-
- return __defineOwnProperty__(engine, index, nullptr, p, attrs);
+ return true;
}
-bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *member, const Property *p, PropertyAttributes attrs)
+bool Object::internalDefineOwnProperty(ExecutionEngine *engine, uint index, const InternalClassEntry *memberEntry, const Property *p, PropertyAttributes attrs)
{
// clause 5
if (attrs.isEmpty())
@@ -907,9 +598,9 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *
Scope scope(engine);
ScopedProperty current(scope);
PropertyAttributes cattrs;
- if (member) {
- getProperty(index, current, &cattrs);
- cattrs = internalClass()->propertyData[index];
+ if (memberEntry) {
+ getProperty(*memberEntry, current);
+ cattrs = memberEntry->attributes;
} else if (arrayData()) {
arrayData()->getProperty(index, current, &cattrs);
cattrs = arrayData()->attributes(index);
@@ -940,7 +631,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *
// 9b
cattrs.setType(PropertyAttributes::Accessor);
cattrs.clearWritable();
- if (!member) {
+ if (!memberEntry) {
// need to convert the array and the slot
initSparseArray();
Q_ASSERT(arrayData());
@@ -952,11 +643,11 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *
// 9c
cattrs.setType(PropertyAttributes::Data);
cattrs.setWritable(false);
- if (!member) {
+ if (!memberEntry) {
// need to convert the array and the slot
setArrayAttributes(index, cattrs);
}
- current->value = Primitive::undefinedValue();
+ current->value = Value::undefinedValue();
}
} else if (cattrs.isData() && attrs.isData()) { // clause 10
if (!cattrs.isConfigurable() && !cattrs.isWritable()) {
@@ -976,9 +667,11 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *
accept:
current->merge(cattrs, p, attrs);
- if (member) {
- InternalClass::changeMember(this, member, cattrs);
- setProperty(index, current);
+ if (memberEntry) {
+ PropertyKey key = internalClass()->nameMap.at(memberEntry->index);
+ InternalClassEntry e;
+ Heap::InternalClass::changeMember(this, key, cattrs, &e);
+ setProperty(e, current);
} else {
setArrayAttributes(index, cattrs);
arrayData()->setProperty(scope.engine, index, current);
@@ -986,15 +679,6 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *
return true;
}
-
-bool Object::__defineOwnProperty__(ExecutionEngine *engine, const QString &name, const Property *p, PropertyAttributes attrs)
-{
- Scope scope(engine);
- ScopedString s(scope, engine->newString(name));
- return __defineOwnProperty__(engine, s, p, attrs);
-}
-
-
void Object::copyArrayData(Object *other)
{
Q_ASSERT(isArrayObject());
@@ -1007,7 +691,7 @@ void Object::copyArrayData(Object *other)
ScopedValue v(scope);
for (uint i = 0; i < len; ++i) {
- arraySet(i, (v = other->getIndexed(i)));
+ arraySet(i, (v = other->get(i)));
}
} else if (!other->arrayData()) {
;
@@ -1030,15 +714,15 @@ void Object::copyArrayData(Object *other)
setArrayLengthUnchecked(other->getLength());
}
-uint Object::getLength(const Managed *m)
+qint64 Object::virtualGetLength(const Managed *m)
{
Scope scope(static_cast<const Object *>(m)->engine());
ScopedValue v(scope, static_cast<Object *>(const_cast<Managed *>(m))->get(scope.engine->id_length()));
- return v->toUInt32();
+ return v->toLength();
}
// 'var' is 'V' in 15.3.5.3.
-ReturnedValue Object::instanceOf(const Object *typeObject, const Value &var)
+ReturnedValue Object::virtualInstanceOf(const Object *typeObject, const Value &var)
{
QV4::ExecutionEngine *engine = typeObject->internalClass()->engine;
@@ -1047,9 +731,16 @@ ReturnedValue Object::instanceOf(const Object *typeObject, const Value &var)
if (!function)
return engine->throwTypeError();
- Heap::FunctionObject *f = function->d();
- if (function->isBoundFunction())
- f = function->cast<BoundFunction>()->target();
+ return checkedInstanceOf(engine, function, var);
+}
+
+ReturnedValue Object::checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *f, const Value &var)
+{
+ Scope scope(engine);
+ if (f->isBoundFunction()) {
+ ScopedValue v(scope, static_cast<const BoundFunction *>(f)->target());
+ f = v->as<FunctionObject>();
+ }
// 15.3.5.3, 1: HasInstance can only be used on an object
const Object *lhs = var.as<Object>();
@@ -1057,9 +748,10 @@ ReturnedValue Object::instanceOf(const Object *typeObject, const Value &var)
return Encode(false);
// 15.3.5.3, 2
- const Object *o = f->protoProperty();
+ Value p = Value::fromReturnedValue(f->protoProperty());
+ const Object *o = p.objectValue();
if (!o) // 15.3.5.3, 3
- return engine->throwTypeError();
+ return f->engine()->throwTypeError();
Heap::Object *v = lhs->d();
@@ -1080,6 +772,142 @@ ReturnedValue Object::instanceOf(const Object *typeObject, const Value &var)
return Encode(false);
}
+bool Object::virtualHasProperty(const Managed *m, PropertyKey id)
+{
+ Scope scope(m->engine());
+ ScopedObject o(scope, m);
+ ScopedProperty p(scope);
+
+ if (o->getOwnProperty(id, p) != Attr_Invalid)
+ return true;
+
+ o = o->getPrototypeOf();
+ if (o)
+ return o->hasProperty(id);
+
+ return false;
+}
+
+PropertyAttributes Object::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
+{
+ PropertyAttributes attrs;
+ const Object *o = static_cast<const Object *>(m);
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+ if (o->arrayData()) {
+ if (o->arrayData()->getProperty(index, p, &attrs))
+ return attrs;
+ }
+ } else {
+ Q_ASSERT(id.asStringOrSymbol());
+
+ auto member = o->internalClass()->find(id);
+ if (member.isValid()) {
+ attrs = member.attributes;
+ if (p) {
+ p->value = *o->propertyData(member.index);
+ if (attrs.isAccessor())
+ p->set = *o->propertyData(member.setterIndex);
+ }
+ return attrs;
+ }
+ }
+
+ return Attr_Invalid;
+}
+
+bool Object::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs)
+{
+ Object *o = static_cast<Object *>(m);
+ Scope scope(o);
+
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+
+ bool hasProperty = false;
+
+ if (o->arrayData()) {
+ hasProperty = o->arrayData()->mappedIndex(index) != UINT_MAX;
+ if (!hasProperty && o->isStringObject())
+ hasProperty = (index < static_cast<StringObject *>(o)->length());
+ }
+
+ if (!hasProperty) {
+ if (!o->isExtensible())
+ return false;
+
+ ScopedProperty pp(scope);
+ pp->copy(p, attrs);
+ pp->fullyPopulated(&attrs);
+ if (attrs == Attr_Data) {
+ ScopedValue v(scope, pp->value);
+ o->arraySet(index, v);
+ } else {
+ o->arraySet(index, pp, attrs);
+ }
+ return true;
+ }
+
+ return o->internalDefineOwnProperty(scope.engine, index, nullptr, p, attrs);
+ }
+
+ auto memberIndex = o->internalClass()->find(id);
+
+ if (!memberIndex.isValid()) {
+ if (!o->isExtensible())
+ return false;
+
+ Scoped<StringOrSymbol> name(scope, id.asStringOrSymbol());
+ ScopedProperty pd(scope);
+ pd->copy(p, attrs);
+ pd->fullyPopulated(&attrs);
+ o->insertMember(name, pd, attrs);
+ return true;
+ }
+
+ return o->internalDefineOwnProperty(scope.engine, UINT_MAX, &memberIndex, p, attrs);
+}
+
+bool Object::virtualIsExtensible(const Managed *m)
+{
+ return m->d()->internalClass->extensible;
+}
+
+bool Object::virtualPreventExtensions(Managed *m)
+{
+ Q_ASSERT(m->isObject());
+ Object *o = static_cast<Object *>(m);
+ o->setInternalClass(o->internalClass()->nonExtensible());
+ return true;
+}
+
+Heap::Object *Object::virtualGetPrototypeOf(const Managed *m)
+{
+ return m->internalClass()->prototype;
+}
+
+bool Object::virtualSetPrototypeOf(Managed *m, const Object *proto)
+{
+ Q_ASSERT(m->isObject());
+ Object *o = static_cast<Object *>(m);
+ Heap::Object *current = o->internalClass()->prototype;
+ Heap::Object *protod = proto ? proto->d() : nullptr;
+ if (current == protod)
+ return true;
+ if (!o->internalClass()->extensible)
+ return false;
+ Heap::Object *p = protod;
+ while (p) {
+ if (p == o->d())
+ return false;
+ if (p->vtable()->getPrototypeOf != Object::staticVTable()->getPrototypeOf)
+ break;
+ p = p->prototype();
+ }
+ o->setInternalClass(o->internalClass()->changePrototype(protod));
+ return true;
+}
+
bool Object::setArrayLength(uint newLen)
{
Q_ASSERT(isArrayObject());
@@ -1110,6 +938,54 @@ void Object::initSparseArray()
ArrayData::realloc(this, Heap::ArrayData::Sparse, 0, false);
}
+bool Object::isConcatSpreadable() const
+{
+ Scope scope(this);
+ ScopedValue spreadable(scope, get(scope.engine->symbol_isConcatSpreadable()));
+ if (!spreadable->isUndefined())
+ return spreadable->toBoolean();
+ return isArray();
+}
+
+bool Object::isArray() const
+{
+ if (isArrayObject())
+ return true;
+ if (vtable() == ProxyObject::staticVTable()) {
+ const ProxyObject *p = static_cast<const ProxyObject *>(this);
+ Scope scope(this);
+ if (!p->d()->handler) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+ ScopedObject o(scope, p->d()->target);
+ return o->isArray();
+ }
+ return false;
+}
+
+const FunctionObject *Object::speciesConstructor(Scope &scope, const FunctionObject *defaultConstructor) const
+{
+ ScopedValue C(scope, get(scope.engine->id_constructor()));
+ if (C->isUndefined())
+ return defaultConstructor;
+ const Object *c = C->objectValue();
+ if (!c) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+ ScopedValue S(scope, c->get(scope.engine->symbol_species()));
+ if (S->isNullOrUndefined())
+ return defaultConstructor;
+ const FunctionObject *f = S->as<FunctionObject>();
+ if (!f || !f->isConstructor()) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+ Q_ASSERT(f->isFunctionObject());
+ return static_cast<const FunctionObject *>(f);
+}
+
DEFINE_OBJECT_VTABLE(ArrayObject);
@@ -1132,12 +1008,10 @@ void Heap::ArrayObject::init(const QStringList &list)
a->setArrayLengthUnchecked(len);
}
-uint ArrayObject::getLength(const Managed *m)
+qint64 ArrayObject::virtualGetLength(const Managed *m)
{
const ArrayObject *a = static_cast<const ArrayObject *>(m);
- if (a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->isInteger())
- return a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->integerValue();
- return Primitive::toUInt32(a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->doubleValue());
+ return a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->toLength();
}
QStringList ArrayObject::toQStringList() const
@@ -1150,8 +1024,62 @@ QStringList ArrayObject::toQStringList() const
uint length = getLength();
for (uint i = 0; i < length; ++i) {
- v = const_cast<ArrayObject *>(this)->getIndexed(i);
+ v = const_cast<ArrayObject *>(this)->get(i);
result.append(v->toQStringNoThrow());
}
return result;
}
+
+bool ArrayObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs)
+{
+ Q_ASSERT(m->isArrayObject());
+ ArrayObject *a = static_cast<ArrayObject *>(m);
+
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+ uint len = a->getLength();
+ if (index >= len && !a->internalClass()->propertyData[Heap::ArrayObject::LengthPropertyIndex].isWritable())
+ return false;
+
+ bool succeeded = Object::virtualDefineOwnProperty(m, id, p, attrs);
+ if (!succeeded)
+ return false;
+
+ if (index >= len)
+ a->setArrayLengthUnchecked(index + 1);
+
+ return true;
+ }
+
+ ExecutionEngine *engine = m->engine();
+ if (id == engine->id_length()->propertyKey()) {
+ Scope scope(engine);
+ Q_ASSERT(a->internalClass()->verifyIndex(engine->id_length()->propertyKey(), Heap::ArrayObject::LengthPropertyIndex));
+ ScopedProperty lp(scope);
+ InternalClassEntry e = a->internalClass()->find(scope.engine->id_length()->propertyKey());
+ a->getProperty(e, lp);
+ if (attrs.isEmpty() || p->isSubset(attrs, lp, e.attributes))
+ return true;
+ if (!e.attributes.isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable())
+ return false;
+ bool succeeded = true;
+ if (attrs.type() == PropertyAttributes::Data) {
+ bool ok;
+ uint l = p->value.asArrayLength(&ok);
+ if (!ok) {
+ ScopedValue v(scope, p->value);
+ engine->throwRangeError(v);
+ return false;
+ }
+ succeeded = a->setArrayLength(l);
+ }
+ if (attrs.hasWritable() && !attrs.isWritable()) {
+ e.attributes.setWritable(false);
+ Heap::InternalClass::changeMember(a, engine->id_length()->propertyKey(), e.attributes);
+ }
+ if (!succeeded)
+ return false;
+ return true;
+ }
+ return Object::virtualDefineOwnProperty(m, id, p, attrs);
+}
diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h
index 1731ae3c76..6753ebfcd4 100644
--- a/src/qml/jsruntime/qv4object_p.h
+++ b/src/qml/jsruntime/qv4object_p.h
@@ -74,6 +74,10 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) {
static void markObjects(Heap::Base *base, MarkStack *stack);
void init() { Base::init(); }
+ const VTable *vtable() const {
+ return internalClass->vtable;
+ }
+
const Value *inlinePropertyDataWithOffset(uint indexWithOffset) const {
Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < vtable()->inlinePropertyOffset + vtable()->nInlineProperties);
return reinterpret_cast<const Value *>(this) + indexWithOffset;
@@ -90,15 +94,15 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) {
void setInlineProperty(ExecutionEngine *e, uint index, Heap::Base *b) {
Q_ASSERT(index < vtable()->nInlineProperties);
Value *prop = reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index;
- WriteBarrier::write(e, this, prop->data_ptr(), b->asReturnedValue());
+ WriteBarrier::write(e, this, prop->data_ptr(), Value::fromHeapObject(b).asReturnedValue());
}
- QV4::MemberData::Index writablePropertyData(uint index) {
+ PropertyIndex writablePropertyData(uint index) {
uint nInline = vtable()->nInlineProperties;
if (index < nInline)
- return { this, reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index};
+ return PropertyIndex{ this, reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index};
index -= nInline;
- return { memberData, memberData->values.values + index };
+ return PropertyIndex{ memberData, memberData->values.values + index };
}
const Value *propertyData(uint index) const {
@@ -134,76 +138,6 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) {
}
-#define V4_OBJECT2(DataClass, superClass) \
- private: \
- DataClass() Q_DECL_EQ_DELETE; \
- Q_DISABLE_COPY(DataClass) \
- public: \
- Q_MANAGED_CHECK \
- typedef QV4::Heap::DataClass Data; \
- typedef superClass SuperClass; \
- static const QV4::ObjectVTable static_vtbl; \
- static inline const QV4::VTable *staticVTable() { return &static_vtbl.vTable; } \
- V4_MANAGED_SIZE_TEST \
- QV4::Heap::DataClass *d_unchecked() const { return static_cast<QV4::Heap::DataClass *>(m()); } \
- QV4::Heap::DataClass *d() const { \
- QV4::Heap::DataClass *dptr = d_unchecked(); \
- dptr->_checkIsInitialized(); \
- return dptr; \
- } \
- Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value);
-
-#define V4_PROTOTYPE(p) \
- static QV4::Object *defaultPrototype(QV4::ExecutionEngine *e) \
- { return e->p(); }
-
-struct ObjectVTable
-{
- VTable vTable;
- ReturnedValue (*call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- ReturnedValue (*callAsConstructor)(const FunctionObject *, const Value *argv, int argc);
- ReturnedValue (*get)(const Managed *, String *name, bool *hasProperty);
- ReturnedValue (*getIndexed)(const Managed *, uint index, bool *hasProperty);
- bool (*put)(Managed *, String *name, const Value &value);
- bool (*putIndexed)(Managed *, uint index, const Value &value);
- PropertyAttributes (*query)(const Managed *, String *name);
- PropertyAttributes (*queryIndexed)(const Managed *, uint index);
- bool (*deleteProperty)(Managed *m, String *name);
- bool (*deleteIndexedProperty)(Managed *m, uint index);
- uint (*getLength)(const Managed *m);
- void (*advanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
- ReturnedValue (*instanceOf)(const Object *typeObject, const Value &var);
-};
-
-#define DEFINE_OBJECT_VTABLE_BASE(classname) \
-const QV4::ObjectVTable classname::static_vtbl = \
-{ \
- DEFINE_MANAGED_VTABLE_INT(classname, (std::is_same<classname::SuperClass, Object>::value) ? nullptr : &classname::SuperClass::static_vtbl.vTable), \
- call, \
- callAsConstructor, \
- get, \
- getIndexed, \
- put, \
- putIndexed, \
- query, \
- queryIndexed, \
- deleteProperty, \
- deleteIndexedProperty, \
- getLength, \
- advanceIterator, \
- instanceOf \
-}
-
-#define DEFINE_OBJECT_VTABLE(classname) \
-QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \
-DEFINE_OBJECT_VTABLE_BASE(classname) \
-QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF
-
-#define DEFINE_OBJECT_TEMPLATE_VTABLE(classname) \
-QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \
-template<> DEFINE_OBJECT_VTABLE_BASE(classname) \
-QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF
-
struct Q_QML_EXPORT Object: Managed {
V4_OBJECT2(Object, Object)
Q_MANAGED_TYPE(Object)
@@ -218,82 +152,92 @@ struct Q_QML_EXPORT Object: Managed {
SetterOffset = 1
};
- void setInternalClass(InternalClass *ic);
+ void setInternalClass(Heap::InternalClass *ic);
const Value *propertyData(uint index) const { return d()->propertyData(index); }
Heap::ArrayData *arrayData() const { return d()->arrayData; }
void setArrayData(ArrayData *a) { d()->arrayData.set(engine(), a->d()); }
- void getProperty(uint index, Property *p, PropertyAttributes *attrs) const;
- void setProperty(uint index, const Property *p);
+ void getProperty(const InternalClassEntry &entry, Property *p) const;
+ void setProperty(const InternalClassEntry &entry, const Property *p);
void setProperty(uint index, Value v) const { d()->setProperty(engine(), index, v); }
void setProperty(uint index, Heap::Base *b) const { d()->setProperty(engine(), index, b); }
void setProperty(ExecutionEngine *engine, uint index, Value v) const { d()->setProperty(engine, index, v); }
void setProperty(ExecutionEngine *engine, uint index, Heap::Base *b) const { d()->setProperty(engine, index, b); }
- const ObjectVTable *vtable() const { return reinterpret_cast<const ObjectVTable *>(d()->vtable()); }
- Heap::Object *prototype() const { return d()->prototype(); }
- bool setPrototype(Object *proto);
-
- void getOwnProperty(String *name, PropertyAttributes *attrs, Property *p = nullptr);
- void getOwnProperty(uint index, PropertyAttributes *attrs, Property *p = nullptr);
+ const VTable *vtable() const { return d()->vtable(); }
- MemberData::Index getValueOrSetter(String *name, PropertyAttributes *attrs);
- ArrayData::Index getValueOrSetter(uint index, PropertyAttributes *attrs);
+ PropertyAttributes getOwnProperty(PropertyKey id, Property *p = nullptr) const {
+ return vtable()->getOwnProperty(this, id, p);
+ }
- bool hasProperty(String *name) const;
- bool hasProperty(uint index) const;
+ PropertyIndex getValueOrSetter(PropertyKey id, PropertyAttributes *attrs);
- bool hasOwnProperty(String *name) const;
- bool hasOwnProperty(uint index) const;
+ bool hasProperty(PropertyKey id) const {
+ return vtable()->hasProperty(this, id);
+ }
- bool __defineOwnProperty__(ExecutionEngine *engine, uint index, String *member, const Property *p, PropertyAttributes attrs);
- bool __defineOwnProperty__(ExecutionEngine *engine, String *name, const Property *p, PropertyAttributes attrs);
- bool __defineOwnProperty__(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs);
- bool __defineOwnProperty__(ExecutionEngine *engine, const QString &name, const Property *p, PropertyAttributes attrs);
- bool defineOwnProperty2(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs);
+ bool defineOwnProperty(PropertyKey id, const Property *p, PropertyAttributes attrs) {
+ return vtable()->defineOwnProperty(this, id, p, attrs);
+ }
//
// helpers
//
- static ReturnedValue getValue(const Value &thisObject, const Value &v, PropertyAttributes attrs);
+ static ReturnedValue getValue(const Value &thisObject, const Value &v, PropertyAttributes attrs) {
+ if (attrs.isData())
+ return v.asReturnedValue();
+ return getValueAccessor(thisObject, v, attrs);
+ }
ReturnedValue getValue(const Value &v, PropertyAttributes attrs) const {
- Scope scope(this->engine());
- ScopedValue t(scope, const_cast<Object *>(this));
- return getValue(t, v, attrs);
+ return getValue(*this, v, attrs);
+ }
+ ReturnedValue getValueByIndex(uint propertyIndex) const {
+ PropertyAttributes attrs = internalClass()->propertyData.at(propertyIndex);
+ const Value *v = propertyData(propertyIndex);
+ if (!attrs.isAccessor())
+ return v->asReturnedValue();
+ return getValueAccessor(*this, *v, attrs);
}
+ static ReturnedValue getValueAccessor(const Value &thisObject, const Value &v, PropertyAttributes attrs);
- bool putValue(uint memberIndex, const Value &value);
+ bool putValue(uint memberIndex, PropertyAttributes attrs, const Value &value);
/* The spec default: Writable: true, Enumerable: false, Configurable: true */
- void defineDefaultProperty(String *name, const Value &value) {
- insertMember(name, value, Attr_Data|Attr_NotEnumerable);
- }
- void defineDefaultProperty(const QString &name, const Value &value);
- void defineDefaultProperty(const QString &name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount = 0);
- void defineDefaultProperty(String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount = 0);
- void defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int),
- ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int));
- void defineAccessorProperty(String *name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int),
- ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int));
+ void defineDefaultProperty(StringOrSymbol *name, const Value &value, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable) {
+ insertMember(name, value, attributes);
+ }
+ void defineDefaultProperty(const QString &name, const Value &value, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable);
+ void defineDefaultProperty(const QString &name, VTable::Call code,
+ int argumentCount = 0, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable);
+ void defineDefaultProperty(StringOrSymbol *name, VTable::Call code,
+ int argumentCount = 0, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable);
+ void defineAccessorProperty(const QString &name, VTable::Call getter, VTable::Call setter);
+ void defineAccessorProperty(StringOrSymbol *name, VTable::Call getter, VTable::Call setter);
/* Fixed: Writable: false, Enumerable: false, Configurable: false */
void defineReadonlyProperty(const QString &name, const Value &value);
void defineReadonlyProperty(String *name, const Value &value);
/* Fixed: Writable: false, Enumerable: false, Configurable: true */
void defineReadonlyConfigurableProperty(const QString &name, const Value &value);
- void defineReadonlyConfigurableProperty(String *name, const Value &value);
+ void defineReadonlyConfigurableProperty(StringOrSymbol *name, const Value &value);
+
+ void addSymbolSpecies();
- void insertMember(String *s, const Value &v, PropertyAttributes attributes = Attr_Data) {
+ void insertMember(StringOrSymbol *s, const Value &v, PropertyAttributes attributes = Attr_Data) {
Scope scope(engine());
ScopedProperty p(scope);
p->value = v;
insertMember(s, p, attributes);
}
- void insertMember(String *s, const Property *p, PropertyAttributes attributes);
+ void insertMember(StringOrSymbol *s, const Property *p, PropertyAttributes attributes);
- bool isExtensible() const { return d()->internalClass->extensible; }
+ bool isExtensible() const { return vtable()->isExtensible(this); }
+ bool preventExtensions() { return vtable()->preventExtensions(this); }
+ Heap::Object *getPrototypeOf() const { return vtable()->getPrototypeOf(this); }
+ bool setPrototypeOf(const Object *p) { return vtable()->setPrototypeOf(this, p); }
+ void setPrototypeUnchecked(const Object *p);
// Array handling
@@ -346,40 +290,65 @@ public:
}
void initSparseArray();
- SparseArrayNode *sparseBegin() { return arrayType() == Heap::ArrayData::Sparse ? d()->arrayData->sparse->begin() : nullptr; }
- SparseArrayNode *sparseEnd() { return arrayType() == Heap::ArrayData::Sparse ? d()->arrayData->sparse->end() : nullptr; }
+ SparseArrayNode *sparseBegin() const { return arrayType() == Heap::ArrayData::Sparse ? d()->arrayData->sparse->begin() : nullptr; }
+ SparseArrayNode *sparseEnd() const { return arrayType() == Heap::ArrayData::Sparse ? d()->arrayData->sparse->end() : nullptr; }
inline bool protoHasArray() {
Scope scope(engine());
ScopedObject p(scope, this);
- while ((p = p->prototype()))
+ while ((p = p->getPrototypeOf()))
if (p->arrayData())
return true;
return false;
}
- inline ReturnedValue get(String *name, bool *hasProperty = nullptr) const
- { return vtable()->get(this, name, hasProperty); }
- inline ReturnedValue getIndexed(uint idx, bool *hasProperty = nullptr) const
- { return vtable()->getIndexed(this, idx, hasProperty); }
+ inline ReturnedValue get(StringOrSymbol *name, bool *hasProperty = nullptr, const Value *receiver = nullptr) const
+ { if (!receiver) receiver = this; return vtable()->get(this, name->toPropertyKey(), receiver, hasProperty); }
+ inline ReturnedValue get(uint idx, bool *hasProperty = nullptr, const Value *receiver = nullptr) const
+ { if (!receiver) receiver = this; return vtable()->get(this, PropertyKey::fromArrayIndex(idx), receiver, hasProperty); }
+ QT_DEPRECATED inline ReturnedValue getIndexed(uint idx, bool *hasProperty = nullptr) const
+ { return get(idx, hasProperty); }
+ inline ReturnedValue get(PropertyKey id, const Value *receiver = nullptr, bool *hasProperty = nullptr) const
+ { if (!receiver) receiver = this; return vtable()->get(this, id, receiver, hasProperty); }
// use the set variants instead, to customize throw behavior
- inline bool put(String *name, const Value &v)
- { return vtable()->put(this, name, v); }
- inline bool putIndexed(uint idx, const Value &v)
- { return vtable()->putIndexed(this, idx, v); }
+ inline bool put(StringOrSymbol *name, const Value &v, Value *receiver = nullptr)
+ { if (!receiver) receiver = this; return vtable()->put(this, name->toPropertyKey(), v, receiver); }
+ inline bool put(uint idx, const Value &v, Value *receiver = nullptr)
+ { if (!receiver) receiver = this; return vtable()->put(this, PropertyKey::fromArrayIndex(idx), v, receiver); }
+ QT_DEPRECATED inline bool putIndexed(uint idx, const Value &v)
+ { return put(idx, v); }
+ inline bool put(PropertyKey id, const Value &v, Value *receiver = nullptr)
+ { if (!receiver) receiver = this; return vtable()->put(this, id, v, receiver); }
enum ThrowOnFailure {
DoThrowOnRejection,
DoNotThrow
};
+ // This is the same as set(), but it doesn't require creating a string key,
+ // which is much more efficient for the array case.
+ inline bool setIndexed(uint idx, const Value &v, ThrowOnFailure shouldThrow)
+ {
+ bool ret = vtable()->put(this, PropertyKey::fromArrayIndex(idx), v, this);
+ // ES6: 7.3.3, 6: If success is false and Throw is true, throw a TypeError exception.
+ if (!ret && shouldThrow == ThrowOnFailure::DoThrowOnRejection) {
+ ExecutionEngine *e = engine();
+ if (!e->hasException) { // allow a custom set impl to throw itself
+ QString message = QLatin1String("Cannot assign to read-only property \"") +
+ QString::number(idx) + QLatin1Char('\"');
+ e->throwTypeError(message);
+ }
+ }
+ return ret;
+ }
+
// ES6: 7.3.3 Set (O, P, V, Throw)
- inline bool set(String *name, const Value &v, ThrowOnFailure shouldThrow)
+ inline bool set(StringOrSymbol *name, const Value &v, ThrowOnFailure shouldThrow)
{
- bool ret = vtable()->put(this, name, v);
+ bool ret = vtable()->put(this, name->toPropertyKey(), v, this);
// ES6: 7.3.3, 6: If success is false and Throw is true, throw a TypeError exception.
if (!ret && shouldThrow == ThrowOnFailure::DoThrowOnRejection) {
ExecutionEngine *e = engine();
@@ -392,47 +361,57 @@ public:
return ret;
}
- PropertyAttributes query(String *name) const
- { return vtable()->query(this, name); }
- PropertyAttributes queryIndexed(uint index) const
- { return vtable()->queryIndexed(this, index); }
- bool deleteProperty(String *name)
- { return vtable()->deleteProperty(this, name); }
- bool deleteIndexedProperty(uint index)
- { return vtable()->deleteIndexedProperty(this, index); }
- void advanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
- { vtable()->advanceIterator(this, it, name, index, p, attributes); }
- uint getLength() const { return vtable()->getLength(this); }
+ bool deleteProperty(PropertyKey id)
+ { return vtable()->deleteProperty(this, id); }
+ OwnPropertyKeyIterator *ownPropertyKeys(Value *target) const
+ { return vtable()->ownPropertyKeys(this, target); }
+ qint64 getLength() const { return vtable()->getLength(this); }
ReturnedValue instanceOf(const Value &var) const
{ return vtable()->instanceOf(this, var); }
+ bool isConcatSpreadable() const;
+ bool isArray() const;
+ const FunctionObject *speciesConstructor(Scope &scope, const FunctionObject *defaultConstructor) const;
+
protected:
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty);
- static bool put(Managed *m, String *name, const Value &value);
- static bool putIndexed(Managed *m, uint index, const Value &value);
- static PropertyAttributes query(const Managed *m, String *name);
- static PropertyAttributes queryIndexed(const Managed *m, uint index);
- static bool deleteProperty(Managed *m, String *name);
- static bool deleteIndexedProperty(Managed *m, uint index);
- static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
- static uint getLength(const Managed *m);
- static ReturnedValue instanceOf(const Object *typeObject, const Value &var);
+ 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 bool virtualDeleteProperty(Managed *m, PropertyKey id);
+ static bool virtualHasProperty(const Managed *m, PropertyKey id);
+ static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p);
+ static bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs);
+ static bool virtualIsExtensible(const Managed *m);
+ static bool virtualPreventExtensions(Managed *);
+ static Heap::Object *virtualGetPrototypeOf(const Managed *);
+ static bool virtualSetPrototypeOf(Managed *, const Object *);
+ static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
+ static qint64 virtualGetLength(const Managed *m);
+ static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var);
+public:
+ // qv4runtime uses this directly
+ static ReturnedValue checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *typeObject, const Value &var);
private:
- ReturnedValue internalGet(String *name, bool *hasProperty) const;
- ReturnedValue internalGetIndexed(uint index, bool *hasProperty) const;
- bool internalPut(String *name, const Value &value);
- bool internalPutIndexed(uint index, const Value &value);
- bool internalDeleteProperty(String *name);
- bool internalDeleteIndexedProperty(uint index);
+ bool internalDefineOwnProperty(ExecutionEngine *engine, uint index, const InternalClassEntry *memberEntry, const Property *p, PropertyAttributes attrs);
+ ReturnedValue internalGet(PropertyKey id, const Value *receiver, bool *hasProperty) const;
+ bool internalPut(PropertyKey id, const Value &value, Value *receiver);
+ bool internalDeleteProperty(PropertyKey id);
friend struct ObjectIterator;
friend struct ObjectPrototype;
};
+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;
+
+};
+
namespace Heap {
struct BooleanObject : Object {
@@ -469,7 +448,7 @@ struct ArrayObject : Object {
private:
void commonInit()
- { setProperty(internalClass->engine, LengthPropertyIndex, Primitive::fromInt32(0)); }
+ { setProperty(internalClass->engine, LengthPropertyIndex, Value::fromInt32(0)); }
};
}
@@ -499,16 +478,18 @@ struct ArrayObject: Object {
void init(ExecutionEngine *engine);
- using Object::getLength;
- static uint getLength(const Managed *m);
+ static qint64 virtualGetLength(const Managed *m);
QStringList toQStringList() const;
+protected:
+ static bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs);
+
};
inline void Object::setArrayLengthUnchecked(uint l)
{
if (isArrayObject())
- setProperty(Heap::ArrayObject::LengthPropertyIndex, Primitive::fromUInt32(l));
+ setProperty(Heap::ArrayObject::LengthPropertyIndex, Value::fromUInt32(l));
}
inline void Object::push_back(const Value &v)
@@ -551,7 +532,7 @@ inline void Object::arraySet(uint index, const Value &value)
template<>
inline const ArrayObject *Value::as() const {
- return isManaged() && m()->vtable()->type == Managed::Type_ArrayObject ? static_cast<const ArrayObject *>(this) : nullptr;
+ return isManaged() && m()->internalClass->vtable->type == Managed::Type_ArrayObject ? static_cast<const ArrayObject *>(this) : nullptr;
}
#ifndef V4_BOOTSTRAP
diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp
index 7bf7e1aa04..e529b8e86b 100644
--- a/src/qml/jsruntime/qv4objectiterator.cpp
+++ b/src/qml/jsruntime/qv4objectiterator.cpp
@@ -42,145 +42,170 @@
#include "qv4identifier_p.h"
#include "qv4argumentsobject_p.h"
#include "qv4string_p.h"
+#include "qv4iterator_p.h"
+#include "qv4propertykey_p.h"
using namespace QV4;
-void ObjectIterator::init(const Object *o)
+void ForInIteratorPrototype::init(ExecutionEngine *)
{
- object->setM(o ? o->m() : nullptr);
- current->setM(o ? o->m() : nullptr);
-
- if (object->as<ArgumentsObject>()) {
- Scope scope(engine);
- Scoped<ArgumentsObject> (scope, object->asReturnedValue())->fullyCreate();
- }
+ defineDefaultProperty(QStringLiteral("next"), method_next, 0);
}
-void ObjectIterator::next(Value *name, uint *index, Property *pd, PropertyAttributes *attrs)
+PropertyKey ObjectIterator::next(Property *pd, PropertyAttributes *attrs)
{
- name->setM(nullptr);
- *index = UINT_MAX;
+ if (!object || !iterator)
+ return PropertyKey::invalid();
- if (!object->as<Object>()) {
- *attrs = PropertyAttributes();
- return;
- }
Scope scope(engine);
- ScopedObject o(scope);
- ScopedString n(scope);
+ ScopedPropertyKey key(scope);
while (1) {
- Object *co = current->objectValue();
- if (!co)
- break;
-
- while (1) {
- co->advanceIterator(this, name, index, pd, attrs);
- if (attrs->isEmpty())
- break;
- // check the property is not already defined earlier in the proto chain
- if (co->heapObject() != object->heapObject()) {
- o = object->as<Object>();
- n = *name;
- bool shadowed = false;
- while (o->d() != current->heapObject()) {
- if ((!!n && o->hasOwnProperty(n)) ||
- (*index != UINT_MAX && o->hasOwnProperty(*index))) {
- shadowed = true;
- break;
- }
- o = o->prototype();
- }
- if (shadowed)
- continue;
- }
- return;
+ key = iterator->next(object, pd, attrs);
+ if (!key->isValid()) {
+ object = nullptr;
+ return key;
}
-
- if (flags & WithProtoChain)
- current->setM(co->prototype());
- else
- current->setM(nullptr);
-
- arrayIndex = 0;
- memberIndex = 0;
+ if ((!(flags & WithSymbols) && key->isSymbol()) ||
+ ((flags & EnumerableOnly) && !attrs->isEnumerable()))
+ continue;
+ return key;
}
- *attrs = PropertyAttributes();
}
ReturnedValue ObjectIterator::nextPropertyName(Value *value)
{
- Object *o = object->objectValue();
- if (!o)
+ if (!object)
return Encode::null();
PropertyAttributes attrs;
- uint index;
Scope scope(engine);
ScopedProperty p(scope);
- ScopedString name(scope);
- next(name.getRef(), &index, p, &attrs);
- if (attrs.isEmpty())
+ ScopedPropertyKey key(scope, next(p, &attrs));
+ if (!key->isValid())
return Encode::null();
- *value = o->getValue(p->value, attrs);
-
- if (!!name)
- return name->asReturnedValue();
- Q_ASSERT(index < UINT_MAX);
- return Encode(index);
+ *value = object->getValue(p->value, attrs);
+ if (key->isArrayIndex())
+ return Encode(key->asArrayIndex());
+ Q_ASSERT(key->isStringOrSymbol());
+ return key->asStringOrSymbol()->asReturnedValue();
}
ReturnedValue ObjectIterator::nextPropertyNameAsString(Value *value)
{
- Object *o = object->objectValue();
- if (!o)
+ if (!object)
return Encode::null();
PropertyAttributes attrs;
- uint index;
Scope scope(engine);
ScopedProperty p(scope);
- ScopedString name(scope);
- next(name.getRef(), &index, p, &attrs);
- if (attrs.isEmpty())
+ ScopedPropertyKey key(scope, next(p, &attrs));
+ if (!key->isValid())
return Encode::null();
- *value = o->getValue(p->value, attrs);
+ *value = object->getValue(p->value, attrs);
- if (!!name)
- return name->asReturnedValue();
- Q_ASSERT(index < UINT_MAX);
- return Encode(engine->newString(QString::number(index)));
+ return key->toStringOrSymbol(engine)->asReturnedValue();
}
ReturnedValue ObjectIterator::nextPropertyNameAsString()
{
- if (!object->as<Object>())
+ if (!object)
return Encode::null();
PropertyAttributes attrs;
- uint index;
Scope scope(engine);
- ScopedProperty p(scope);
- ScopedString name(scope);
- next(name.getRef(), &index, p, &attrs);
- if (attrs.isEmpty())
+ ScopedPropertyKey key(scope, next(nullptr, &attrs));
+ if (!key->isValid())
return Encode::null();
- if (!!name)
- return name->asReturnedValue();
- Q_ASSERT(index < UINT_MAX);
- return Encode(engine->newString(QString::number(index)));
+ return key->toStringOrSymbol(engine)->asReturnedValue();
}
-DEFINE_OBJECT_VTABLE(ForEachIteratorObject);
+DEFINE_OBJECT_VTABLE(ForInIteratorObject);
-void Heap::ForEachIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack)
+void Heap::ForInIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack)
{
- ForEachIteratorObject *o = static_cast<ForEachIteratorObject *>(that);
+ ForInIteratorObject *o = static_cast<ForInIteratorObject *>(that);
+ if (o->object)
+ o->object->mark(markStack);
+ if (o->current)
+ o->current->mark(markStack);
o->workArea[0].mark(markStack);
o->workArea[1].mark(markStack);
Object::markObjects(that, markStack);
}
+
+void Heap::ForInIteratorObject::destroy()
+{
+ delete iterator;
+}
+
+ReturnedValue ForInIteratorPrototype::method_next(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ const ForInIteratorObject *forIn = static_cast<const ForInIteratorObject *>(thisObject);
+ Q_ASSERT(forIn);
+ Scope scope(b);
+
+ ScopedPropertyKey key(scope, forIn->nextProperty());
+ bool done = false;
+ if (!key->isValid())
+ done = true;
+ ScopedStringOrSymbol s(scope, key->toStringOrSymbol(scope.engine));
+ return IteratorPrototype::createIterResultObject(scope.engine, s, done);
+}
+
+
+PropertyKey ForInIteratorObject::nextProperty() const
+{
+ if (!d()->current)
+ return PropertyKey::invalid();
+
+ Scope scope(this);
+ ScopedObject c(scope, d()->current);
+ ScopedObject t(scope, d()->target);
+ ScopedObject o(scope);
+ ScopedProperty p(scope);
+ ScopedPropertyKey key(scope);
+ PropertyAttributes attrs;
+
+ while (1) {
+ while (1) {
+ key = d()->iterator->next(t, p, &attrs);
+ if (!key->isValid())
+ break;
+ if (!attrs.isEnumerable() || key->isSymbol())
+ continue;
+ // check the property is not already defined earlier in the proto chain
+ if (d()->current != d()->object) {
+ o = d()->object;
+ bool shadowed = false;
+ while (o->d() != c->heapObject()) {
+ if (o->getOwnProperty(key) != Attr_Invalid) {
+ shadowed = true;
+ break;
+ }
+ o = o->getPrototypeOf();
+ }
+ if (shadowed)
+ continue;
+ }
+ return key;
+ }
+
+ c = c->getPrototypeOf();
+ d()->current.set(scope.engine, c->d());
+ if (!c)
+ break;
+ delete d()->iterator;
+ d()->iterator = c->ownPropertyKeys(t.getRef());
+ d()->target.set(scope.engine, t->d());
+ if (!d()->iterator) {
+ scope.engine->throwTypeError();
+ return PropertyKey::invalid();
+ }
+ }
+ return PropertyKey::invalid();
+}
diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h
index 744d16301a..a20ce9cb88 100644
--- a/src/qml/jsruntime/qv4objectiterator_p.h
+++ b/src/qml/jsruntime/qv4objectiterator_p.h
@@ -57,85 +57,86 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
-struct Q_QML_EXPORT ObjectIteratorData
+struct Q_QML_EXPORT ObjectIterator
{
enum Flags {
NoFlags = 0,
EnumerableOnly = 0x1,
- WithProtoChain = 0x2,
+ WithSymbols = 0x2
};
ExecutionEngine *engine;
- Value *object;
- Value *current;
- SparseArrayNode *arrayNode;
- uint arrayIndex;
- uint memberIndex;
+ Object *object;
+ OwnPropertyKeyIterator *iterator = nullptr;
uint flags;
-};
-Q_STATIC_ASSERT(std::is_trivial< ObjectIteratorData >::value);
-
-struct Q_QML_EXPORT ObjectIterator: ObjectIteratorData
-{
- ObjectIterator(ExecutionEngine *e, Value *scratch1, Value *scratch2, Object *o, uint flags)
- {
- engine = e;
- object = scratch1;
- current = scratch2;
- arrayNode = nullptr;
- arrayIndex = 0;
- memberIndex = 0;
- this->flags = flags;
- init(o);
- }
ObjectIterator(Scope &scope, const Object *o, uint flags)
{
engine = scope.engine;
- object = scope.alloc(1);
- current = scope.alloc(1);
- arrayNode = nullptr;
- arrayIndex = 0;
- memberIndex = 0;
+ object = static_cast<Object *>(scope.alloc());
this->flags = flags;
- init(o);
+ object->setM(o ? o->m() : nullptr);
+ if (o)
+ iterator = object->ownPropertyKeys(object);
+ }
+ ~ObjectIterator()
+ {
+ delete iterator;
}
- void next(Value *name, uint *index, Property *pd, PropertyAttributes *attributes = nullptr);
+ PropertyKey next(Property *pd = nullptr, PropertyAttributes *attributes = nullptr);
ReturnedValue nextPropertyName(Value *value);
ReturnedValue nextPropertyNameAsString(Value *value);
ReturnedValue nextPropertyNameAsString();
-
-private:
- void init(const Object *o);
};
namespace Heap {
-struct ForEachIteratorObject : Object {
+
+#define ForInIteratorObjectMembers(class, Member) \
+ Member(class, Pointer, Object *, object) \
+ Member(class, Pointer, Object *, current) \
+ Member(class, Pointer, Object *, target) \
+ Member(class, NoMark, OwnPropertyKeyIterator *, iterator)
+
+DECLARE_HEAP_OBJECT(ForInIteratorObject, Object) {
void init(QV4::Object *o);
- ObjectIterator &it() { return *reinterpret_cast<ObjectIterator*>(&itData); }
Value workArea[2];
static void markObjects(Heap::Base *that, MarkStack *markStack);
-private:
- ObjectIteratorData itData;
+ void destroy();
};
}
-struct ForEachIteratorObject: Object {
- V4_OBJECT2(ForEachIteratorObject, Object)
- Q_MANAGED_TYPE(ForeachIteratorObject)
+struct ForInIteratorPrototype : Object
+{
+ V4_PROTOTYPE(iteratorPrototype)
+ void init(ExecutionEngine *engine);
+
+ static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct ForInIteratorObject: Object {
+ V4_OBJECT2(ForInIteratorObject, Object)
+ Q_MANAGED_TYPE(ForInIterator)
+ V4_PROTOTYPE(forInIteratorPrototype)
+ V4_NEEDS_DESTROY
- ReturnedValue nextPropertyName() { return d()->it().nextPropertyNameAsString(); }
+ PropertyKey nextProperty() const;
};
inline
-void Heap::ForEachIteratorObject::init(QV4::Object *o)
+void Heap::ForInIteratorObject::init(QV4::Object *o)
{
Object::init();
- it() = ObjectIterator(internalClass->engine, workArea, workArea + 1, o,
- ObjectIterator::EnumerableOnly | ObjectIterator::WithProtoChain);
+ if (!o)
+ return;
+ object.set(o->engine(), o->d());
+ current.set(o->engine(), o->d());
+ Scope scope(o);
+ ScopedObject obj(scope);
+ iterator = o->ownPropertyKeys(obj.getRef());
+ target.set(o->engine(), obj->d());
}
diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp
index b998b78520..6b4c3ba71a 100644
--- a/src/qml/jsruntime/qv4objectproto.cpp
+++ b/src/qml/jsruntime/qv4objectproto.cpp
@@ -47,6 +47,8 @@
#include "qv4objectiterator_p.h"
#include "qv4string_p.h"
#include "qv4jscall_p.h"
+#include "qv4symbol_p.h"
+#include "qv4propertykey_p.h"
#include <QtCore/QDateTime>
#include <QtCore/QStringList>
@@ -61,23 +63,23 @@ void Heap::ObjectCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("Object"));
}
-ReturnedValue ObjectCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue ObjectCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
{
ExecutionEngine *v4 = f->engine();
- const ObjectCtor *ctor = static_cast<const ObjectCtor *>(f);
+ const ObjectCtor *nt = static_cast<const ObjectCtor *>(newTarget);
if (!argc || argv[0].isUndefined() || argv[0].isNull()) {
Scope scope(v4);
ScopedObject obj(scope, scope.engine->newObject());
- ScopedObject proto(scope, ctor->get(scope.engine->id_prototype()));
+ ScopedObject proto(scope, nt->get(scope.engine->id_prototype()));
if (!!proto)
- obj->setPrototype(proto);
+ obj->setPrototypeOf(proto);
return obj.asReturnedValue();
} else {
return argv[0].toObject(v4)->asReturnedValue();
}
}
-ReturnedValue ObjectCtor::call(const FunctionObject *m, const Value *, const Value *argv, int argc)
+ReturnedValue ObjectCtor::virtualCall(const FunctionObject *m, const Value *, const Value *argv, int argc)
{
ExecutionEngine *v4 = m->engine();
if (!argc || argv[0].isUndefined() || argv[0].isNull()) {
@@ -93,25 +95,31 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor)
ScopedObject o(scope, this);
ctor->defineReadonlyProperty(v4->id_prototype(), o);
- ctor->defineReadonlyConfigurableProperty(v4->id_length(), Primitive::fromInt32(1));
+ ctor->defineReadonlyConfigurableProperty(v4->id_length(), Value::fromInt32(1));
ctor->defineDefaultProperty(QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1);
ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2);
+ ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptors"), method_getOwnPropertyDescriptors, 1);
ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1);
+ ctor->defineDefaultProperty(QStringLiteral("getOwnPropertySymbols"), method_getOwnPropertySymbols, 1);
ctor->defineDefaultProperty(QStringLiteral("assign"), method_assign, 2);
ctor->defineDefaultProperty(QStringLiteral("create"), method_create, 2);
ctor->defineDefaultProperty(QStringLiteral("defineProperty"), method_defineProperty, 3);
ctor->defineDefaultProperty(QStringLiteral("defineProperties"), method_defineProperties, 2);
+ ctor->defineDefaultProperty(QStringLiteral("entries"), method_entries, 1);
ctor->defineDefaultProperty(QStringLiteral("seal"), method_seal, 1);
ctor->defineDefaultProperty(QStringLiteral("freeze"), method_freeze, 1);
ctor->defineDefaultProperty(QStringLiteral("preventExtensions"), method_preventExtensions, 1);
+ ctor->defineDefaultProperty(QStringLiteral("is"), method_is, 2);
ctor->defineDefaultProperty(QStringLiteral("isSealed"), method_isSealed, 1);
ctor->defineDefaultProperty(QStringLiteral("isFrozen"), method_isFrozen, 1);
ctor->defineDefaultProperty(QStringLiteral("isExtensible"), method_isExtensible, 1);
ctor->defineDefaultProperty(QStringLiteral("keys"), method_keys, 1);
+ ctor->defineDefaultProperty(QStringLiteral("setPrototypeOf"), method_setPrototypeOf, 2);
+ ctor->defineDefaultProperty(QStringLiteral("values"), method_values, 1);
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(v4->id_toString(), method_toString, 0);
- defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString, 0);
+ defineDefaultProperty(v4->id_toLocaleString(), method_toLocaleString, 0);
defineDefaultProperty(v4->id_valueOf(), method_valueOf, 0);
defineDefaultProperty(QStringLiteral("hasOwnProperty"), method_hasOwnProperty, 1);
defineDefaultProperty(QStringLiteral("isPrototypeOf"), method_isPrototypeOf, 1);
@@ -119,11 +127,7 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor)
defineDefaultProperty(QStringLiteral("__defineGetter__"), method_defineGetter, 2);
defineDefaultProperty(QStringLiteral("__defineSetter__"), method_defineSetter, 2);
- ExecutionContext *global = v4->rootContext();
- ScopedProperty p(scope);
- p->value = FunctionObject::createBuiltinFunction(global, v4->id___proto__(), method_get_proto);
- p->set = FunctionObject::createBuiltinFunction(global, v4->id___proto__(), method_set_proto);
- insertMember(v4->id___proto__(), p, Attr_Accessor|Attr_NotEnumerable);
+ defineAccessorProperty(v4->id___proto__(), method_get_proto, method_set_proto);
}
ReturnedValue ObjectPrototype::method_getPrototypeOf(const FunctionObject *b, const Value *, const Value *argv, int argc)
@@ -136,10 +140,19 @@ ReturnedValue ObjectPrototype::method_getPrototypeOf(const FunctionObject *b, co
if (scope.engine->hasException)
return QV4::Encode::undefined();
- ScopedObject p(scope, o->prototype());
+ ScopedObject p(scope, o->getPrototypeOf());
return (!!p ? p->asReturnedValue() : Encode::null());
}
+ReturnedValue ObjectPrototype::method_is(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ if (!argc)
+ return Encode(true);
+ if (argc == 1)
+ return Encode((argv[0].isUndefined() ? true : false));
+ return Encode(argv[0].sameValue(argv[1]));
+}
+
ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
Scope scope(b);
@@ -153,17 +166,45 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(const FunctionObj
if (ArgumentsObject::isNonStrictArgumentsObject(O))
static_cast<ArgumentsObject *>(O.getPointer())->fullyCreate();
- ScopedValue v(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
- ScopedString name(scope, v->toString(scope.engine));
+ ScopedValue v(scope, argc > 1 ? argv[1] : Value::undefinedValue());
+ ScopedPropertyKey name(scope, v->toPropertyKey(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
- PropertyAttributes attrs;
ScopedProperty desc(scope);
- O->getOwnProperty(name, &attrs, desc);
+ PropertyAttributes attrs = O->getOwnProperty(name, desc);
return fromPropertyDescriptor(scope.engine, desc, attrs);
}
+ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptors(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc)
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, argv[0].toObject(scope.engine));
+ if (scope.engine->hasException)
+ return Encode::undefined();
+
+ ScopedObject descriptors(scope, scope.engine->newObject());
+
+ ObjectIterator it(scope, o, ObjectIterator::WithSymbols);
+ ScopedProperty pd(scope);
+ PropertyAttributes attrs;
+ ScopedPropertyKey key(scope);
+ ScopedObject entry(scope);
+ while (1) {
+ key = it.next(pd, &attrs);
+ if (!key->isValid())
+ break;
+ entry = fromPropertyDescriptor(scope.engine, pd, attrs);
+ descriptors->put(key, entry);
+ }
+
+ return descriptors.asReturnedValue();
+
+}
+
ReturnedValue ObjectPrototype::method_getOwnPropertyNames(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
Scope scope(b);
@@ -177,6 +218,32 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyNames(const FunctionObject *
return Encode(getOwnPropertyNames(scope.engine, argv[0]));
}
+ReturnedValue ObjectPrototype::method_getOwnPropertySymbols(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc)
+ return scope.engine->throwTypeError();
+
+ ScopedObject O(scope, argv[0].toObject(scope.engine));
+ if (!O)
+ return Encode::undefined();
+
+ ScopedArrayObject array(scope, scope.engine->newArrayObject());
+ if (O) {
+ ObjectIterator it(scope, O, ObjectIterator::WithSymbols);
+ ScopedValue name(scope);
+ while (1) {
+ name = it.nextPropertyNameAsString();
+ if (name->isNull())
+ break;
+ if (!name->isSymbol())
+ continue;
+ array->push_back(name);
+ }
+ }
+ return array->asReturnedValue();
+}
+
// 19.1.2.1
ReturnedValue ObjectPrototype::method_assign(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
@@ -204,11 +271,10 @@ ReturnedValue ObjectPrototype::method_assign(const FunctionObject *b, const Valu
ScopedString nextKey(scope);
ScopedValue propValue(scope);
for (quint32 i = 0; i < length; ++i) {
- nextKey = Value::fromReturnedValue(keys->getIndexed(i)).toString(scope.engine);
+ nextKey = Value::fromReturnedValue(keys->get(i)).toString(scope.engine);
- PropertyAttributes attrs;
ScopedProperty prop(scope);
- from->getOwnProperty(nextKey, &attrs, prop);
+ PropertyAttributes attrs = from->getOwnProperty(nextKey->toPropertyKey(), prop);
if (attrs == PropertyFlag::Attr_Invalid)
continue;
@@ -235,7 +301,7 @@ ReturnedValue ObjectPrototype::method_create(const FunctionObject *builtin, cons
ScopedObject O(scope, argv[0]);
ScopedObject newObject(scope, scope.engine->newObject());
- newObject->setPrototype(O);
+ newObject->setPrototypeOf(O);
if (argc > 1 && !argv[1].isUndefined()) {
@@ -255,18 +321,18 @@ ReturnedValue ObjectPrototype::method_defineProperty(const FunctionObject *b, co
return scope.engine->throwTypeError();
ScopedObject O(scope, argv[0]);
- ScopedString name(scope, argc > 1 ? argv[1] : Primitive::undefinedValue(), ScopedString::Convert);
+ ScopedPropertyKey name(scope, (argc > 1 ? argv[1] : Value::undefinedValue()).toPropertyKey(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
- ScopedValue attributes(scope, argc > 2 ? argv[2] : Primitive::undefinedValue());
+ ScopedValue attributes(scope, argc > 2 ? argv[2] : Value::undefinedValue());
ScopedProperty pd(scope);
PropertyAttributes attrs;
toPropertyDescriptor(scope.engine, attributes, pd, &attrs);
if (scope.engine->hasException)
return QV4::Encode::undefined();
- if (!O->__defineOwnProperty__(scope.engine, name, pd, attrs))
+ if (!O->defineOwnProperty(name, pd, attrs))
THROW_TYPE_ERROR();
return O.asReturnedValue();
@@ -287,25 +353,20 @@ ReturnedValue ObjectPrototype::method_defineProperties(const FunctionObject *b,
ScopedValue val(scope);
ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly);
- ScopedString name(scope);
ScopedProperty pd(scope);
ScopedProperty n(scope);
+ ScopedPropertyKey key(scope);
while (1) {
- uint index;
PropertyAttributes attrs;
- it.next(name.getRef(), &index, pd, &attrs);
- if (attrs.isEmpty())
+ key = it.next(pd, &attrs);
+ if (!key->isValid())
break;
PropertyAttributes nattrs;
val = o->getValue(pd->value, attrs);
toPropertyDescriptor(scope.engine, val, n, &nattrs);
if (scope.engine->hasException)
return QV4::Encode::undefined();
- bool ok;
- if (name)
- ok = O->__defineOwnProperty__(scope.engine, name, n, nattrs);
- else
- ok = O->__defineOwnProperty__(scope.engine, index, n, nattrs);
+ bool ok = O->defineOwnProperty(key, n, nattrs);
if (!ok)
THROW_TYPE_ERROR();
}
@@ -313,9 +374,48 @@ ReturnedValue ObjectPrototype::method_defineProperties(const FunctionObject *b,
return O.asReturnedValue();
}
+ReturnedValue ObjectPrototype::method_entries(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc)
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, argv[0].toObject(scope.engine));
+ if (scope.engine->hasException)
+ return Encode::undefined();
+
+ ScopedArrayObject a(scope, scope.engine->newArrayObject());
+
+ ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly);
+ ScopedString name(scope);
+ ScopedArrayObject entry(scope);
+ while (1) {
+ name = it.nextPropertyNameAsString();
+ if (!name)
+ break;
+ entry = scope.engine->newArrayObject();
+ entry->push_back(name);
+ a->push_back(entry);
+ }
+
+ // now add values, do this after the loop above as reading out the values can have side effects
+ uint len = a->getLength();
+ ScopedValue value(scope);
+ for (uint i = 0; i < len; ++i) {
+ entry = a->get(PropertyKey::fromArrayIndex(i));
+ name = entry->get(PropertyKey::fromArrayIndex(0));
+ value = o->get(name->toPropertyKey());
+ if (scope.engine->hasException)
+ return Encode::undefined();
+ entry->push_back(value);
+ }
+
+ return a.asReturnedValue();
+}
+
ReturnedValue ObjectPrototype::method_seal(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- const Value a = argc ? argv[0] : Primitive::undefinedValue();
+ const Value a = argc ? argv[0] : Value::undefinedValue();
if (!a.isObject())
// 19.1.2.17, 1
return a.asReturnedValue();
@@ -337,7 +437,7 @@ ReturnedValue ObjectPrototype::method_seal(const FunctionObject *b, const Value
ReturnedValue ObjectPrototype::method_freeze(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
- const Value a = argc ? argv[0] : Primitive::undefinedValue();
+ const Value a = argc ? argv[0] : Value::undefinedValue();
if (!a.isObject())
// 19.1.2.5, 1
return a.asReturnedValue();
@@ -366,13 +466,13 @@ ReturnedValue ObjectPrototype::method_preventExtensions(const FunctionObject *b,
{
Scope scope(b);
if (!argc)
- return scope.engine->throwTypeError();
+ return Encode::undefined();
- ScopedObject o(scope, argv[0].toObject(scope.engine));
+ ScopedObject o(scope, argv[0]);
if (!o)
return argv[0].asReturnedValue();
- o->setInternalClass(o->internalClass()->nonExtensible());
+ o->preventExtensions();
return o.asReturnedValue();
}
@@ -380,9 +480,9 @@ ReturnedValue ObjectPrototype::method_isSealed(const FunctionObject *b, const Va
{
Scope scope(b);
if (!argc)
- return scope.engine->throwTypeError();
+ return Encode(true);
- ScopedObject o(scope, argv[0].toObject(scope.engine));
+ ScopedObject o(scope, argv[0]);
if (!o)
return Encode(true);
@@ -412,9 +512,9 @@ ReturnedValue ObjectPrototype::method_isFrozen(const FunctionObject *b, const Va
{
Scope scope(b);
if (!argc)
- return scope.engine->throwTypeError();
+ return Encode(true);
- ScopedObject o(scope, argv[0].toObject(scope.engine));
+ ScopedObject o(scope, argv[0]);
if (!o)
return Encode(true);
@@ -444,9 +544,9 @@ ReturnedValue ObjectPrototype::method_isExtensible(const FunctionObject *b, cons
{
Scope scope(b);
if (!argc)
- return scope.engine->throwTypeError();
+ return Encode(false);
- ScopedObject o(scope, argv[0].toObject(scope.engine));
+ ScopedObject o(scope, argv[0]);
if (!o)
return Encode(false);
@@ -467,8 +567,9 @@ ReturnedValue ObjectPrototype::method_keys(const FunctionObject *b, const Value
ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly);
ScopedValue name(scope);
+ ScopedValue value(scope);
while (1) {
- name = it.nextPropertyNameAsString();
+ name = it.nextPropertyNameAsString(value);
if (name->isNull())
break;
a->push_back(name);
@@ -477,19 +578,81 @@ ReturnedValue ObjectPrototype::method_keys(const FunctionObject *b, const Value
return a.asReturnedValue();
}
+ReturnedValue ObjectPrototype::method_setPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f->engine());
+ if (argc < 2 || argv[0].isNullOrUndefined() || !(argv[1].isObject() || argv[1].isNull()))
+ return scope.engine->throwTypeError();
+
+ if (!argv[0].isObject())
+ return argv[0].asReturnedValue();
+
+ ScopedObject o(scope, argv[0]);
+ const Object *p = argv[1].isNull() ? nullptr : static_cast<const Object *>(argv + 1);
+ bool ok = o->setPrototypeOf(p);
+ if (!ok)
+ return scope.engine->throwTypeError(QStringLiteral("Could not change prototype."));
+ return o->asReturnedValue();
+}
+
+ReturnedValue ObjectPrototype::method_values(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc)
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, argv[0].toObject(scope.engine));
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
+
+ ScopedArrayObject a(scope, scope.engine->newArrayObject());
+
+ ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly);
+ ScopedPropertyKey key(scope);
+ ScopedProperty pd(scope);
+ ScopedValue value(scope);
+ PropertyAttributes attrs;
+ while (1) {
+ key = it.next(pd, &attrs);
+ if (!key->isValid())
+ break;
+ value = o->getValue(pd->value, attrs);
+ a->push_back(value);
+ }
+
+ return a.asReturnedValue();
+}
+
ReturnedValue ObjectPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = b->engine();
+ QString string;
if (thisObject->isUndefined()) {
- return Encode(v4->newString(QStringLiteral("[object Undefined]")));
+ string = QStringLiteral("[object Undefined]");
} else if (thisObject->isNull()) {
- return Encode(v4->newString(QStringLiteral("[object Null]")));
+ string = QStringLiteral("[object Null]");
} else {
+ const Object *o = thisObject->as<Object>();
+ if (!o) {
+ // primitive, get the proper prototype
+ if (thisObject->isBoolean())
+ o = v4->booleanPrototype();
+ else if (thisObject->isNumber())
+ o = v4->numberPrototype();
+ else if (thisObject->isString())
+ o = v4->stringPrototype();
+ else if (thisObject->isSymbol())
+ o = v4->symbolPrototype();
+ Q_ASSERT(o);
+ }
+ QString name = o->className();
Scope scope(v4);
- ScopedObject obj(scope, thisObject->toObject(scope.engine));
- QString className = obj->className();
- return Encode(v4->newString(QStringLiteral("[object %1]").arg(className)));
+ ScopedString toStringTag(scope, o->get(v4->symbol_toStringTag()));
+ if (toStringTag)
+ name = toStringTag->toQString();
+ string = QStringLiteral("[object %1]").arg(name);
}
+ return Encode(v4->newString(string));
}
ReturnedValue ObjectPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
@@ -514,15 +677,13 @@ ReturnedValue ObjectPrototype::method_valueOf(const FunctionObject *b, const Val
ReturnedValue ObjectPrototype::method_hasOwnProperty(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
- ScopedString P(scope, argc ? argv[0] : Primitive::undefinedValue(), ScopedString::Convert);
+ ScopedPropertyKey P(scope, (argc ? argv[0] : Value::undefinedValue()).toPropertyKey(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
ScopedObject O(scope, thisObject->toObject(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
- bool r = O->hasOwnProperty(P);
- if (!r)
- r = !O->query(P).isEmpty();
+ bool r = O->getOwnProperty(P) != Attr_Invalid;
return Encode(r);
}
@@ -536,11 +697,11 @@ ReturnedValue ObjectPrototype::method_isPrototypeOf(const FunctionObject *b, con
ScopedObject O(scope, thisObject->toObject(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
- ScopedObject proto(scope, V->prototype());
+ ScopedObject proto(scope, V->getPrototypeOf());
while (proto) {
if (O->d() == proto->d())
return Encode(true);
- proto = proto->prototype();
+ proto = proto->getPrototypeOf();
}
return Encode(false);
}
@@ -548,15 +709,14 @@ ReturnedValue ObjectPrototype::method_isPrototypeOf(const FunctionObject *b, con
ReturnedValue ObjectPrototype::method_propertyIsEnumerable(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
- ScopedString p(scope, argc ? argv[0] : Primitive::undefinedValue(), ScopedString::Convert);
+ ScopedPropertyKey p(scope, (argc ? argv[0] : Value::undefinedValue()).toPropertyKey(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
ScopedObject o(scope, thisObject->toObject(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
- PropertyAttributes attrs;
- o->getOwnProperty(p, &attrs);
+ PropertyAttributes attrs = o->getOwnProperty(p);
return Encode(attrs.isEnumerable());
}
@@ -583,8 +743,8 @@ ReturnedValue ObjectPrototype::method_defineGetter(const FunctionObject *b, cons
ScopedProperty pd(scope);
pd->value = f;
- pd->set = Primitive::emptyValue();
- bool ok = o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor);
+ pd->set = Value::emptyValue();
+ bool ok = o->defineOwnProperty(prop->toPropertyKey(), pd, Attr_Accessor);
if (!ok)
THROW_TYPE_ERROR();
RETURN_UNDEFINED();
@@ -612,9 +772,9 @@ ReturnedValue ObjectPrototype::method_defineSetter(const FunctionObject *b, cons
}
ScopedProperty pd(scope);
- pd->value = Primitive::emptyValue();
+ pd->value = Value::emptyValue();
pd->set = f;
- bool ok = o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor);
+ bool ok = o->defineOwnProperty(prop->toPropertyKey(), pd, Attr_Accessor);
if (!ok)
THROW_TYPE_ERROR();
RETURN_UNDEFINED();
@@ -627,32 +787,21 @@ ReturnedValue ObjectPrototype::method_get_proto(const FunctionObject *b, const V
if (!o)
THROW_TYPE_ERROR();
- return Encode(o->prototype());
+ return Encode(o->getPrototypeOf());
}
ReturnedValue ObjectPrototype::method_set_proto(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
ScopedObject o(scope, thisObject);
- if (!o || !argc)
+ if (!o || !argc || (!argv[0].isObject() && !argv[0].isNull()))
THROW_TYPE_ERROR();
- if (argv[0].isNull()) {
- o->setPrototype(nullptr);
- RETURN_UNDEFINED();
- }
-
- ScopedObject p(scope, argv[0]);
- bool ok = false;
- if (!!p) {
- if (o->prototype() == p->d()) {
- ok = true;
- } else if (o->isExtensible()) {
- ok = o->setPrototype(p);
- }
- }
+ const Object *p = argv[0].isNull() ? nullptr : static_cast<const Object *>(argv);
+ bool ok = o->setPrototypeOf(p);
if (!ok)
- return scope.engine->throwTypeError(QStringLiteral("Cyclic __proto__ value"));
+ return scope.engine->throwTypeError(QStringLiteral("Could not change prototype."));
+ return Encode::undefined();
RETURN_UNDEFINED();
}
@@ -666,17 +815,17 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value
}
attrs->clear();
- desc->value = Primitive::emptyValue();
- desc->set = Primitive::emptyValue();
+ desc->value = Value::emptyValue();
+ desc->set = Value::emptyValue();
ScopedValue tmp(scope);
- if (o->hasProperty(engine->id_enumerable()))
+ if (o->hasProperty(engine->id_enumerable()->toPropertyKey()))
attrs->setEnumerable((tmp = o->get(engine->id_enumerable()))->toBoolean());
- if (o->hasProperty(engine->id_configurable()))
+ if (o->hasProperty(engine->id_configurable()->toPropertyKey()))
attrs->setConfigurable((tmp = o->get(engine->id_configurable()))->toBoolean());
- if (o->hasProperty(engine->id_get())) {
+ if (o->hasProperty(engine->id_get()->toPropertyKey())) {
ScopedValue get(scope, o->get(engine->id_get()));
FunctionObject *f = get->as<FunctionObject>();
if (f || get->isUndefined()) {
@@ -688,7 +837,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value
attrs->setType(PropertyAttributes::Accessor);
}
- if (o->hasProperty(engine->id_set())) {
+ if (o->hasProperty(engine->id_set()->toPropertyKey())) {
ScopedValue set(scope, o->get(engine->id_set()));
FunctionObject *f = set->as<FunctionObject>();
if (f || set->isUndefined()) {
@@ -700,17 +849,15 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value
attrs->setType(PropertyAttributes::Accessor);
}
- if (o->hasProperty(engine->id_writable())) {
+ if (o->hasProperty(engine->id_writable()->toPropertyKey())) {
if (attrs->isAccessor()) {
engine->throwTypeError();
return;
}
attrs->setWritable((tmp = o->get(engine->id_writable()))->toBoolean());
- // writable forces it to be a data descriptor
- desc->value = Primitive::undefinedValue();
}
- if (o->hasProperty(engine->id_value())) {
+ if (o->hasProperty(engine->id_value()->toPropertyKey())) {
if (attrs->isAccessor()) {
engine->throwTypeError();
return;
@@ -720,7 +867,7 @@ void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value
}
if (attrs->isGeneric())
- desc->value = Primitive::emptyValue();
+ desc->value = Value::emptyValue();
}
@@ -734,29 +881,28 @@ ReturnedValue ObjectPrototype::fromPropertyDescriptor(ExecutionEngine *engine, c
// is the standard built-in constructor with that name.
ScopedObject o(scope, engine->newObject());
ScopedString s(scope);
+ ScopedValue v(scope);
- ScopedProperty pd(scope);
if (attrs.isData()) {
- pd->value = desc->value;
s = engine->newString(QStringLiteral("value"));
- o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data);
- pd->value = Primitive::fromBoolean(attrs.isWritable());
+ o->put(s, desc->value);
+ v = Value::fromBoolean(attrs.isWritable());
s = engine->newString(QStringLiteral("writable"));
- o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data);
+ o->put(s, v);
} else {
- pd->value = desc->getter() ? desc->getter()->asReturnedValue() : Encode::undefined();
+ v = desc->getter() ? desc->getter()->asReturnedValue() : Encode::undefined();
s = engine->newString(QStringLiteral("get"));
- o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data);
- pd->value = desc->setter() ? desc->setter()->asReturnedValue() : Encode::undefined();
+ o->put(s, v);
+ v = desc->setter() ? desc->setter()->asReturnedValue() : Encode::undefined();
s = engine->newString(QStringLiteral("set"));
- o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data);
+ o->put(s, v);
}
- pd->value = Primitive::fromBoolean(attrs.isEnumerable());
+ v = Value::fromBoolean(attrs.isEnumerable());
s = engine->newString(QStringLiteral("enumerable"));
- o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data);
- pd->value = Primitive::fromBoolean(attrs.isConfigurable());
+ o->put(s, v);
+ v = Value::fromBoolean(attrs.isConfigurable());
s = engine->newString(QStringLiteral("configurable"));
- o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data);
+ o->put(s, v);
return o.asReturnedValue();
}
@@ -774,6 +920,8 @@ Heap::ArrayObject *ObjectPrototype::getOwnPropertyNames(ExecutionEngine *v4, con
name = it.nextPropertyNameAsString();
if (name->isNull())
break;
+ if (name->isSymbol())
+ continue;
array->push_back(name);
}
}
diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h
index 2b231d46ad..e9515b5b68 100644
--- a/src/qml/jsruntime/qv4objectproto_p.h
+++ b/src/qml/jsruntime/qv4objectproto_p.h
@@ -70,28 +70,34 @@ struct ObjectCtor: FunctionObject
{
V4_OBJECT2(ObjectCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc);
};
struct ObjectPrototype: Object
{
void init(ExecutionEngine *engine, Object *ctor);
- static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_getOwnPropertyNames(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_assign(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_create(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_defineProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_defineProperties(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_seal(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_defineProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_freeze(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_preventExtensions(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_isSealed(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_isFrozen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getOwnPropertyDescriptors(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getOwnPropertyNames(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getOwnPropertySymbols(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_is(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_isExtensible(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isFrozen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isSealed(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_preventExtensions(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_seal(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp
index 7fc74173e3..79c372348f 100644
--- a/src/qml/jsruntime/qv4persistent.cpp
+++ b/src/qml/jsruntime/qv4persistent.cpp
@@ -98,9 +98,9 @@ Page *allocatePage(PersistentValueStorage *storage)
p->header.freeList = 0;
insertInFront(storage, p);
for (int i = 0; i < kEntriesPerPage - 1; ++i) {
- p->values[i].setEmpty(i + 1);
+ p->values[i] = Encode(i + 1);
}
- p->values[kEntriesPerPage - 1].setEmpty(-1);
+ p->values[kEntriesPerPage - 1] = Encode(-1);
return p;
}
@@ -226,7 +226,7 @@ void PersistentValueStorage::free(Value *v)
Page *p = getPage(v);
- v->setEmpty(p->header.freeList);
+ *v = Encode(p->header.freeList);
p->header.freeList = v - p->values;
if (!--p->header.refCount)
freePage(p);
@@ -302,6 +302,10 @@ PersistentValue &PersistentValue::operator=(const PersistentValue &other)
return *this;
val = other.engine()->memoryManager->m_persistentValues->allocate();
}
+ if (!other.val) {
+ *val = Encode::undefined();
+ return *this;
+ }
Q_ASSERT(engine() == other.engine());
@@ -316,6 +320,10 @@ PersistentValue &PersistentValue::operator=(const WeakValue &other)
return *this;
val = other.engine()->memoryManager->m_persistentValues->allocate();
}
+ if (!other.valueRef()) {
+ *val = Encode::undefined();
+ return *this;
+ }
Q_ASSERT(engine() == other.engine());
@@ -379,6 +387,10 @@ WeakValue &WeakValue::operator=(const WeakValue &other)
return *this;
allocVal(other.engine());
}
+ if (!other.val) {
+ *val = Encode::undefined();
+ return *this;
+ }
Q_ASSERT(engine() == other.engine());
diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp
index 5fd200efc1..b337243204 100644
--- a/src/qml/jsruntime/qv4profiling.cpp
+++ b/src/qml/jsruntime/qv4profiling.cpp
@@ -78,7 +78,7 @@ Profiler::Profiler(QV4::ExecutionEngine *engine) : featuresEnabled(0), m_engine(
void Profiler::stopProfiling()
{
featuresEnabled = 0;
- reportData(true);
+ reportData();
m_sentLocations.clear();
}
@@ -89,7 +89,7 @@ bool operator<(const FunctionCall &call1, const FunctionCall &call2)
(call1.m_end == call2.m_end && call1.m_function < call2.m_function)));
}
-void Profiler::reportData(bool trackLocations)
+void Profiler::reportData()
{
std::sort(m_data.begin(), m_data.end());
QVector<FunctionCallProperties> properties;
@@ -100,12 +100,11 @@ void Profiler::reportData(bool trackLocations)
properties.append(call.properties());
Function *function = call.function();
SentMarker &marker = m_sentLocations[reinterpret_cast<quintptr>(function)];
- if (!trackLocations || !marker.isValid()) {
+ if (!marker.isValid()) {
FunctionLocation &location = locations[properties.constLast().id];
if (!location.isValid())
location = call.resolveLocation();
- if (trackLocations)
- marker.setFunction(function);
+ marker.setFunction(function);
}
}
diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h
index e8c154e4e7..8461384e9a 100644
--- a/src/qml/jsruntime/qv4profiling_p.h
+++ b/src/qml/jsruntime/qv4profiling_p.h
@@ -251,7 +251,7 @@ public:
void stopProfiling();
void startProfiling(quint64 features);
- void reportData(bool trackLocations);
+ void reportData();
void setTimer(const QElapsedTimer &timer) { m_timer = timer; }
signals:
diff --git a/src/qml/jsruntime/qv4property_p.h b/src/qml/jsruntime/qv4property_p.h
index 7cb106c424..555f323737 100644
--- a/src/qml/jsruntime/qv4property_p.h
+++ b/src/qml/jsruntime/qv4property_p.h
@@ -66,18 +66,36 @@ struct Property {
// Section 8.10
inline void fullyPopulated(PropertyAttributes *attrs) {
if (!attrs->hasType()) {
- value = Primitive::undefinedValue();
+ value = Value::undefinedValue();
}
if (attrs->type() == PropertyAttributes::Accessor) {
attrs->clearWritable();
if (value.isEmpty())
- value = Primitive::undefinedValue();
+ value = Value::undefinedValue();
if (set.isEmpty())
- set = Primitive::undefinedValue();
+ set = Value::undefinedValue();
}
attrs->resolve();
}
+ // ES8: 6.2.5.6
+ void completed(PropertyAttributes *attrs) {
+ if (value.isEmpty())
+ value = Encode::undefined();
+ if (attrs->isGeneric() || attrs->isData()) {
+ attrs->setType(PropertyAttributes::Data);
+ if (!attrs->hasWritable())
+ attrs->setWritable(false);
+ } else {
+ if (set.isEmpty())
+ set = Encode::undefined();
+ }
+ if (!attrs->hasEnumerable())
+ attrs->setEnumerable(false);
+ if (!attrs->hasConfigurable())
+ attrs->setConfigurable(false);
+ }
+
inline bool isSubset(const PropertyAttributes &attrs, const Property *other, PropertyAttributes otherAttrs) const;
inline void merge(PropertyAttributes &attrs, const Property *other, PropertyAttributes otherAttrs);
@@ -92,6 +110,40 @@ struct Property {
set = other->set;
}
+ // ES8, section 9.1.6.2/9,.1.6.3
+ bool isCompatible(PropertyAttributes &attrs, const Property *other, PropertyAttributes otherAttrs) const {
+ if (otherAttrs.isEmpty())
+ return true;
+ if (!attrs.isConfigurable()) {
+ if (otherAttrs.hasConfigurable() && otherAttrs.isConfigurable())
+ return false;
+ if (otherAttrs.hasEnumerable() && otherAttrs.isEnumerable() != attrs.isEnumerable())
+ return false;
+ }
+ if (otherAttrs.isGeneric())
+ return true;
+ if (attrs.isData() != otherAttrs.isData()) {
+ if (!attrs.isConfigurable())
+ return false;
+ } else if (attrs.isData() && otherAttrs.isData()) {
+ if (!attrs.isConfigurable() && !attrs.isWritable()) {
+ if (otherAttrs.hasWritable() && otherAttrs.isWritable())
+ return false;
+ if (!other->value.isEmpty() && !value.sameValue(other->value))
+ return false;
+ }
+ } else if (attrs.isAccessor() && otherAttrs.isAccessor()) {
+ if (!attrs.isConfigurable()) {
+ if (!other->value.isEmpty() && !value.sameValue(other->value))
+ return false;
+ if (!other->set.isEmpty() && !set.sameValue(other->set))
+ return false;
+ }
+ }
+ return true;
+ }
+
+
explicit Property() { value = Encode::undefined(); set = Value::fromHeapObject(nullptr); }
Property(Heap::FunctionObject *getter, Heap::FunctionObject *setter) {
value.setM(reinterpret_cast<Heap::Base *>(getter));
@@ -142,6 +194,19 @@ inline void Property::merge(PropertyAttributes &attrs, const Property *other, Pr
}
}
+struct PropertyIndex {
+ Heap::Base *base;
+ Value *slot;
+
+ void set(EngineBase *e, Value newVal) {
+ WriteBarrier::write(e, base, slot->data_ptr(), newVal.asReturnedValue());
+ }
+ const Value *operator->() const { return slot; }
+ const Value &operator*() const { return *slot; }
+ bool isNull() const { return !slot; }
+};
+
+
}
Q_DECLARE_TYPEINFO(QV4::Property, Q_MOVABLE_TYPE);
diff --git a/src/qml/jsruntime/qv4propertykey.cpp b/src/qml/jsruntime/qv4propertykey.cpp
new file mode 100644
index 0000000000..064d030b83
--- /dev/null
+++ b/src/qml/jsruntime/qv4propertykey.cpp
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** 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 "qv4propertykey_p.h"
+
+#include <QtCore/qstring.h>
+#include <qv4string_p.h>
+#include <qv4engine_p.h>
+#include <qv4scopedvalue_p.h>
+
+QV4::Heap::StringOrSymbol *QV4::PropertyKey::toStringOrSymbol(QV4::ExecutionEngine *e)
+{
+ if (isArrayIndex())
+ return Value::fromUInt32(asArrayIndex()).toString(e);
+ return static_cast<Heap::StringOrSymbol *>(asStringOrSymbol());
+}
+
+bool QV4::PropertyKey::isString() const {
+ Heap::StringOrSymbol *s = asStringOrSymbol();
+ return s && s->internalClass->vtable->isString;
+}
+
+bool QV4::PropertyKey::isSymbol() const {
+ Heap::Base *s = asStringOrSymbol();
+ return s && !s->internalClass->vtable->isString && s->internalClass->vtable->isStringOrSymbol;
+}
+
+bool QV4::PropertyKey::isCanonicalNumericIndexString() const
+{
+ if (isArrayIndex())
+ return true;
+ if (isSymbol())
+ return false;
+ Heap::String *s = static_cast<Heap::String *>(asStringOrSymbol());
+ Scope scope(s->internalClass->engine);
+ ScopedString str(scope, s);
+ double d = str->toNumber();
+ if (d == 0. && std::signbit(d))
+ return true;
+ ScopedString converted(scope, Value::fromDouble(d).toString(scope.engine));
+ if (converted->equals(str))
+ return true;
+ return false;
+}
+
+QString QV4::PropertyKey::toQString() const
+{
+ if (isArrayIndex())
+ return QString::number(asArrayIndex());
+ Heap::StringOrSymbol *s = asStringOrSymbol();
+ Q_ASSERT(s->internalClass->vtable->isStringOrSymbol);
+ return s->toQString();
+}
+
+QV4::Heap::String *QV4::PropertyKey::asFunctionName(ExecutionEngine *engine, FunctionNamePrefix prefix) const
+{
+ QString n;
+ if (prefix == Getter)
+ n = QStringLiteral("get ");
+ else if (prefix == Setter)
+ n = QStringLiteral("set ");
+ if (isArrayIndex())
+ n += QString::number(asArrayIndex());
+ else {
+ Heap::StringOrSymbol *s = asStringOrSymbol();
+ QString str = s->toQString();
+ if (s->internalClass->vtable->isString)
+ n += s->toQString();
+ else if (str.length() > 1)
+ n += QChar::fromLatin1('[') + str.midRef(1) + QChar::fromLatin1(']');
+ }
+ return engine->newString(n);
+}
diff --git a/src/qml/jsruntime/qv4propertykey_p.h b/src/qml/jsruntime/qv4propertykey_p.h
new file mode 100644
index 0000000000..cb2661f244
--- /dev/null
+++ b/src/qml/jsruntime/qv4propertykey_p.h
@@ -0,0 +1,154 @@
+/****************************************************************************
+**
+** 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 QV4PROPERTYKEY_H
+#define QV4PROPERTYKEY_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>
+
+QT_BEGIN_NAMESPACE
+
+class QString;
+
+namespace QV4 {
+
+struct PropertyKey
+{
+private:
+ // Property keys are Strings, Symbols or unsigned integers.
+ // For convenience we derive them from Values, allowing us to store them
+ // on the JS stack
+ //
+ // They do however behave somewhat different than a Value:
+ // * If the key is a String, the pointer to the string is stored in the identifier
+ // table and thus unique.
+ // * If the key is a Symbol it simply points to the referenced symbol object
+ // * if the key is an array index (a uint < UINT_MAX), it's encoded as an
+ // integer value
+ quint64 val;
+
+ // Important: Always keep this in sync with the definitions for Integers and heap objects in Value
+ static const quint64 ArrayIndexMask = 0x3800000000000ull;
+ enum {
+ IsManagedOrUndefined_Shift = 64-15,
+ };
+ inline bool isManaged() const { return (val >> IsManagedOrUndefined_Shift) == 0; }
+ inline quint32 value() const { return val & quint64(~quint32(0)); }
+
+#if QT_POINTER_SIZE == 8
+ QML_NEARLY_ALWAYS_INLINE Heap::StringOrSymbol *m() const
+ {
+ Heap::StringOrSymbol *b;
+ memcpy(&b, &val, 8);
+ return b;
+ }
+ QML_NEARLY_ALWAYS_INLINE void setM(Heap::StringOrSymbol *b)
+ {
+ memcpy(&val, &b, 8);
+ }
+#elif QT_POINTER_SIZE == 4
+ QML_NEARLY_ALWAYS_INLINE Heap::StringOrSymbol *m() const
+ {
+ Q_STATIC_ASSERT(sizeof(Heap::StringOrSymbol*) == sizeof(quint32));
+ Heap::StringOrSymbol *b;
+ quint32 v = value();
+ memcpy(&b, &v, 4);
+ return b;
+ }
+ QML_NEARLY_ALWAYS_INLINE void setM(Heap::StringOrSymbol *b)
+ {
+ quint32 v;
+ memcpy(&v, &b, 4);
+ val = v;
+ }
+#endif
+
+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; }
+ bool isValid() const { return val != 0; }
+ static PropertyKey fromStringOrSymbol(Heap::StringOrSymbol *b)
+ { PropertyKey key; key.setM(b); return key; }
+ Heap::StringOrSymbol *asStringOrSymbol() const {
+ if (!isManaged())
+ return nullptr;
+ return m();
+ }
+
+ bool isString() const;
+ bool isSymbol() const;
+ bool isCanonicalNumericIndexString() const;
+
+ Q_QML_EXPORT QString toQString() const;
+ Heap::StringOrSymbol *toStringOrSymbol(ExecutionEngine *e);
+ quint64 id() const { return val; }
+ static PropertyKey fromId(quint64 id) {
+ PropertyKey key; key.val = id; return key;
+ }
+
+ enum FunctionNamePrefix {
+ None,
+ Getter,
+ Setter
+ };
+ Heap::String *asFunctionName(ExecutionEngine *e, FunctionNamePrefix prefix) const;
+
+ bool operator ==(const PropertyKey &other) const { return val == other.val; }
+ bool operator !=(const PropertyKey &other) const { return val != other.val; }
+ bool operator <(const PropertyKey &other) const { return val < other.val; }
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/jsruntime/qv4proxy.cpp b/src/qml/jsruntime/qv4proxy.cpp
new file mode 100644
index 0000000000..9325e2e53b
--- /dev/null
+++ b/src/qml/jsruntime/qv4proxy.cpp
@@ -0,0 +1,782 @@
+/****************************************************************************
+**
+** 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 "qv4proxy_p.h"
+#include "qv4symbol_p.h"
+#include "qv4jscall_p.h"
+#include "qv4objectproto_p.h"
+#include "qv4persistent_p.h"
+#include "qv4objectiterator_p.h"
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(ProxyObject);
+DEFINE_OBJECT_VTABLE(ProxyFunctionObject);
+
+void Heap::ProxyObject::init(const QV4::Object *target, const QV4::Object *handler)
+{
+ Object::init();
+ ExecutionEngine *e = internalClass->engine;
+ this->target.set(e, target->d());
+ this->handler.set(e, handler->d());
+}
+
+void Heap::ProxyFunctionObject::init(const QV4::FunctionObject *target, const QV4::Object *handler)
+{
+ ExecutionEngine *e = internalClass->engine;
+ FunctionObject::init(e->rootContext());
+ this->target.set(e, target->d());
+ this->handler.set(e, handler->d());
+
+ if (!target->isConstructor())
+ jsConstruct = nullptr;
+}
+
+
+ReturnedValue ProxyObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler)
+ return scope.engine->throwTypeError();
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedValue trap(scope, handler->get(scope.engine->id_get()));
+ if (scope.hasException())
+ return Encode::undefined();
+ if (trap->isNullOrUndefined())
+ return target->get(id, receiver, hasProperty);
+ if (!trap->isFunctionObject())
+ return scope.engine->throwTypeError();
+ if (hasProperty)
+ *hasProperty = true;
+
+ JSCallData cdata(scope, 3, nullptr, handler);
+ cdata.args[0] = target;
+ cdata.args[1] = id.toStringOrSymbol(scope.engine);
+ cdata.args[2] = *receiver;
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ ScopedProperty targetDesc(scope);
+ PropertyAttributes attributes = target->getOwnProperty(id, targetDesc);
+ if (attributes != Attr_Invalid && !attributes.isConfigurable()) {
+ if (attributes.isData() && !attributes.isWritable()) {
+ if (!trapResult->sameValue(targetDesc->value))
+ return scope.engine->throwTypeError();
+ }
+ if (attributes.isAccessor() && targetDesc->value.isUndefined()) {
+ if (!trapResult->isUndefined())
+ return scope.engine->throwTypeError();
+ }
+ }
+ return trapResult->asReturnedValue();
+}
+
+bool ProxyObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler)
+ return scope.engine->throwTypeError();
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedValue trap(scope, handler->get(scope.engine->id_set()));
+ if (scope.hasException())
+ return Encode::undefined();
+ if (trap->isNullOrUndefined())
+ return target->put(id, value, receiver);
+ if (!trap->isFunctionObject())
+ return scope.engine->throwTypeError();
+
+ JSCallData cdata(scope, 4, nullptr, handler);
+ cdata.args[0] = target;
+ cdata.args[1] = id.toStringOrSymbol(scope.engine);
+ cdata.args[2] = value;
+ cdata.args[3] = *receiver;
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ if (!trapResult->toBoolean())
+ return false;
+ ScopedProperty targetDesc(scope);
+ PropertyAttributes attributes = target->getOwnProperty(id, targetDesc);
+ if (attributes != Attr_Invalid && !attributes.isConfigurable()) {
+ if (attributes.isData() && !attributes.isWritable()) {
+ if (!value.sameValue(targetDesc->value))
+ return scope.engine->throwTypeError();
+ }
+ if (attributes.isAccessor() && targetDesc->set.isUndefined())
+ return scope.engine->throwTypeError();
+ }
+ return true;
+}
+
+bool ProxyObject::virtualDeleteProperty(Managed *m, PropertyKey id)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler)
+ return scope.engine->throwTypeError();
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString deleteProp(scope, scope.engine->newString(QStringLiteral("deleteProperty")));
+ ScopedValue trap(scope, handler->get(deleteProp));
+ if (scope.hasException())
+ return Encode::undefined();
+ if (trap->isNullOrUndefined())
+ return target->deleteProperty(id);
+ if (!trap->isFunctionObject())
+ return scope.engine->throwTypeError();
+
+ JSCallData cdata(scope, 3, nullptr, handler);
+ cdata.args[0] = target;
+ cdata.args[1] = id.toStringOrSymbol(scope.engine);
+ cdata.args[2] = o->d(); // ### fix receiver handling
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ if (!trapResult->toBoolean())
+ return false;
+ ScopedProperty targetDesc(scope);
+ PropertyAttributes attributes = target->getOwnProperty(id, targetDesc);
+ if (attributes == Attr_Invalid)
+ return true;
+ if (!attributes.isConfigurable())
+ return scope.engine->throwTypeError();
+ return true;
+}
+
+bool ProxyObject::virtualHasProperty(const Managed *m, PropertyKey id)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler)
+ return scope.engine->throwTypeError();
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString hasProp(scope, scope.engine->newString(QStringLiteral("has")));
+ ScopedValue trap(scope, handler->get(hasProp));
+ if (scope.hasException())
+ return Encode::undefined();
+ if (trap->isNullOrUndefined())
+ return target->hasProperty(id);
+ if (!trap->isFunctionObject())
+ return scope.engine->throwTypeError();
+
+ JSCallData cdata(scope, 2, nullptr, handler);
+ cdata.args[0] = target;
+ cdata.args[1] = id.isArrayIndex() ? Value::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol();
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ bool result = trapResult->toBoolean();
+ if (!result) {
+ ScopedProperty targetDesc(scope);
+ PropertyAttributes attributes = target->getOwnProperty(id, targetDesc);
+ if (attributes != Attr_Invalid) {
+ if (!attributes.isConfigurable() || !target->isExtensible())
+ return scope.engine->throwTypeError();
+ }
+ }
+ return result;
+}
+
+PropertyAttributes ProxyObject::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler) {
+ scope.engine->throwTypeError();
+ return Attr_Invalid;
+ }
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString deleteProp(scope, scope.engine->newString(QStringLiteral("getOwnPropertyDescriptor")));
+ ScopedValue trap(scope, handler->get(deleteProp));
+ if (scope.hasException())
+ return Attr_Invalid;
+ if (trap->isNullOrUndefined())
+ return target->getOwnProperty(id, p);
+ if (!trap->isFunctionObject()) {
+ scope.engine->throwTypeError();
+ return Attr_Invalid;
+ }
+
+ JSCallData cdata(scope, 2, nullptr, handler);
+ cdata.args[0] = target;
+ cdata.args[1] = id.isArrayIndex() ? Value::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol();
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ if (!trapResult->isObject() && !trapResult->isUndefined()) {
+ scope.engine->throwTypeError();
+ return Attr_Invalid;
+ }
+
+ ScopedProperty targetDesc(scope);
+ PropertyAttributes targetAttributes = target->getOwnProperty(id, targetDesc);
+ if (trapResult->isUndefined()) {
+ p->value = Encode::undefined();
+ if (targetAttributes == Attr_Invalid) {
+ p->value = Encode::undefined();
+ return Attr_Invalid;
+ }
+ if (!targetAttributes.isConfigurable() || !target->isExtensible()) {
+ scope.engine->throwTypeError();
+ return Attr_Invalid;
+ }
+ return Attr_Invalid;
+ }
+
+ //bool extensibleTarget = target->isExtensible();
+ ScopedProperty resultDesc(scope);
+ PropertyAttributes resultAttributes;
+ ObjectPrototype::toPropertyDescriptor(scope.engine, trapResult, resultDesc, &resultAttributes);
+ resultDesc->completed(&resultAttributes);
+
+ if (!targetDesc->isCompatible(targetAttributes, resultDesc, resultAttributes)) {
+ scope.engine->throwTypeError();
+ return Attr_Invalid;
+ }
+
+ if (!resultAttributes.isConfigurable()) {
+ if (targetAttributes == Attr_Invalid || targetAttributes.isConfigurable()) {
+ scope.engine->throwTypeError();
+ return Attr_Invalid;
+ }
+ }
+
+ p->value = resultDesc->value;
+ p->set = resultDesc->set;
+ return resultAttributes;
+}
+
+bool ProxyObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString prop(scope, scope.engine->newString(QStringLiteral("defineProperty")));
+ ScopedValue trap(scope, handler->get(prop));
+ if (scope.hasException())
+ return false;
+ if (trap->isNullOrUndefined())
+ return target->defineOwnProperty(id, p, attrs);
+ if (!trap->isFunctionObject()) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+
+ JSCallData cdata(scope, 3, nullptr, handler);
+ cdata.args[0] = target;
+ cdata.args[1] = id.isArrayIndex() ? Value::fromUInt32(id.asArrayIndex()).toString(scope.engine) : id.asStringOrSymbol();
+ cdata.args[2] = ObjectPrototype::fromPropertyDescriptor(scope.engine, p, attrs);
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ bool result = trapResult->toBoolean();
+ if (!result)
+ return false;
+
+ ScopedProperty targetDesc(scope);
+ PropertyAttributes targetAttributes = target->getOwnProperty(id, targetDesc);
+ bool extensibleTarget = target->isExtensible();
+ bool settingConfigFalse = attrs.hasConfigurable() && !attrs.isConfigurable();
+ if (targetAttributes == Attr_Invalid) {
+ if (!extensibleTarget || settingConfigFalse) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+ } else {
+ if (!targetDesc->isCompatible(targetAttributes, p, attrs)) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+ if (settingConfigFalse && targetAttributes.isConfigurable()) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ProxyObject::virtualIsExtensible(const Managed *m)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler)
+ return scope.engine->throwTypeError();
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString hasProp(scope, scope.engine->newString(QStringLiteral("isExtensible")));
+ ScopedValue trap(scope, handler->get(hasProp));
+ if (scope.hasException())
+ return Encode::undefined();
+ if (trap->isNullOrUndefined())
+ return target->isExtensible();
+ if (!trap->isFunctionObject())
+ return scope.engine->throwTypeError();
+
+ JSCallData cdata(scope, 1, nullptr, handler);
+ cdata.args[0] = target;
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ bool result = trapResult->toBoolean();
+ if (result != target->isExtensible()) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+ return result;
+}
+
+bool ProxyObject::virtualPreventExtensions(Managed *m)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler)
+ return scope.engine->throwTypeError();
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString hasProp(scope, scope.engine->newString(QStringLiteral("preventExtensions")));
+ ScopedValue trap(scope, handler->get(hasProp));
+ if (scope.hasException())
+ return Encode::undefined();
+ if (trap->isNullOrUndefined())
+ return target->preventExtensions();
+ if (!trap->isFunctionObject())
+ return scope.engine->throwTypeError();
+
+ JSCallData cdata(scope, 1, nullptr, handler);
+ cdata.args[0] = target;
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ bool result = trapResult->toBoolean();
+ if (result && target->isExtensible()) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+ return result;
+}
+
+Heap::Object *ProxyObject::virtualGetPrototypeOf(const Managed *m)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString name(scope, scope.engine->newString(QStringLiteral("getPrototypeOf")));
+ ScopedValue trap(scope, handler->get(name));
+ if (scope.hasException())
+ return nullptr;
+ if (trap->isNullOrUndefined())
+ return target->getPrototypeOf();
+ if (!trap->isFunctionObject()) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+
+ JSCallData cdata(scope, 1, nullptr, handler);
+ cdata.args[0] = target;
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ if (!trapResult->isNull() && !trapResult->isObject()) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+ Heap::Object *proto = trapResult->isNull() ? nullptr : static_cast<Heap::Object *>(trapResult->heapObject());
+ if (!target->isExtensible()) {
+ Heap::Object *targetProto = target->getPrototypeOf();
+ if (proto != targetProto) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+ }
+ return proto;
+}
+
+bool ProxyObject::virtualSetPrototypeOf(Managed *m, const Object *p)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString name(scope, scope.engine->newString(QStringLiteral("setPrototypeOf")));
+ ScopedValue trap(scope, handler->get(name));
+ if (scope.hasException())
+ return false;
+ if (trap->isNullOrUndefined())
+ return target->setPrototypeOf(p);
+ if (!trap->isFunctionObject()) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+
+ JSCallData cdata(scope, 2, nullptr, handler);
+ cdata.args[0] = target;
+ cdata.args[1] = p ? p->asReturnedValue() : Encode::null();
+
+ ScopedValue trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ bool result = trapResult->toBoolean();
+ if (!result)
+ return false;
+ if (!target->isExtensible()) {
+ Heap::Object *targetProto = target->getPrototypeOf();
+ if (p->d() != targetProto) {
+ scope.engine->throwTypeError();
+ return false;
+ }
+ }
+ return true;
+}
+
+struct ProxyObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator
+{
+ PersistentValue ownKeys;
+ uint index = 0;
+ uint len = 0;
+
+ ProxyObjectOwnPropertyKeyIterator(ArrayObject *keys);
+ ~ProxyObjectOwnPropertyKeyIterator() override = default;
+ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
+
+};
+
+ProxyObjectOwnPropertyKeyIterator::ProxyObjectOwnPropertyKeyIterator(ArrayObject *keys)
+{
+ ownKeys = keys;
+ len = keys->getLength();
+}
+
+PropertyKey ProxyObjectOwnPropertyKeyIterator::next(const Object *m, Property *pd, PropertyAttributes *attrs)
+{
+ if (index >= len)
+ return PropertyKey::invalid();
+
+ Scope scope(m);
+ ScopedObject keys(scope, ownKeys.asManaged());
+ PropertyKey key = PropertyKey::fromId(keys->get(PropertyKey::fromArrayIndex(index)));
+ ++index;
+
+ if (pd || attrs) {
+ ScopedProperty p(scope);
+ PropertyAttributes a = const_cast<Object *>(m)->getOwnProperty(key, pd ? pd : p);
+ if (attrs)
+ *attrs = a;
+ }
+
+ return key;
+}
+
+static bool removeAllOccurrences(ArrayObject *target, ReturnedValue val) {
+ uint len = target->getLength();
+ bool found = false;
+ for (uint i = 0; i < len; ++i) {
+ ReturnedValue v = target->get(i);
+ if (v == val) {
+ found = true;
+ target->put(i, Value::undefinedValue());
+ }
+ }
+ return found;
+}
+
+OwnPropertyKeyIterator *ProxyObject::virtualOwnPropertyKeys(const Object *m, Value *iteratorTarget)
+{
+ Scope scope(m);
+ const ProxyObject *o = static_cast<const ProxyObject *>(m);
+ if (!o->d()->handler) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+
+ ScopedObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString name(scope, scope.engine->newString(QStringLiteral("ownKeys")));
+ ScopedValue trap(scope, handler->get(name));
+
+ if (scope.hasException())
+ return nullptr;
+ if (trap->isUndefined())
+ return target->ownPropertyKeys(iteratorTarget);
+ if (!trap->isFunctionObject()) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+
+ JSCallData cdata(scope, 1, nullptr, handler);
+ cdata.args[0] = target;
+ ScopedObject trapResult(scope, static_cast<const FunctionObject *>(trap.ptr)->call(cdata));
+ if (!trapResult) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+
+ uint len = trapResult->getLength();
+ ScopedArrayObject trapKeys(scope, scope.engine->newArrayObject());
+ ScopedStringOrSymbol key(scope);
+ for (uint i = 0; i < len; ++i) {
+ key = trapResult->get(i);
+ if (scope.engine->hasException)
+ return nullptr;
+ if (!key) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+ Value keyAsValue = Value::fromReturnedValue(key->toPropertyKey().id());
+ trapKeys->push_back(keyAsValue);
+ }
+
+ ScopedArrayObject targetConfigurableKeys(scope, scope.engine->newArrayObject());
+ ScopedArrayObject targetNonConfigurableKeys(scope, scope.engine->newArrayObject());
+ ObjectIterator it(scope, target, ObjectIterator::EnumerableOnly);
+ ScopedPropertyKey k(scope);
+ while (1) {
+ PropertyAttributes attrs;
+ k = it.next(nullptr, &attrs);
+ if (!k->isValid())
+ break;
+ Value keyAsValue = Value::fromReturnedValue(k->id());
+ if (attrs.isConfigurable())
+ targetConfigurableKeys->push_back(keyAsValue);
+ else
+ targetNonConfigurableKeys->push_back(keyAsValue);
+ }
+ if (target->isExtensible() && targetNonConfigurableKeys->getLength() == 0)
+ return new ProxyObjectOwnPropertyKeyIterator(trapKeys);
+
+ ScopedArrayObject uncheckedResultKeys(scope, scope.engine->newArrayObject());
+ uncheckedResultKeys->copyArrayData(trapKeys);
+
+ len = targetNonConfigurableKeys->getLength();
+ for (uint i = 0; i < len; ++i) {
+ k = PropertyKey::fromId(targetNonConfigurableKeys->get(i));
+ if (!removeAllOccurrences(uncheckedResultKeys, k->id())) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+ }
+
+ if (target->isExtensible())
+ return new ProxyObjectOwnPropertyKeyIterator(trapKeys);
+
+ len = targetConfigurableKeys->getLength();
+ for (uint i = 0; i < len; ++i) {
+ k = PropertyKey::fromId(targetConfigurableKeys->get(i));
+ if (!removeAllOccurrences(uncheckedResultKeys, k->id())) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+ }
+
+ len = uncheckedResultKeys->getLength();
+ for (uint i = 0; i < len; ++i) {
+ if (uncheckedResultKeys->get(i) != Encode::undefined()) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+ }
+
+ *iteratorTarget = *m;
+ return new ProxyObjectOwnPropertyKeyIterator(trapKeys);
+}
+
+
+ReturnedValue ProxyFunctionObject::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
+{
+ Scope scope(f);
+ const ProxyObject *o = static_cast<const ProxyObject *>(f);
+ if (!o->d()->handler)
+ return scope.engine->throwTypeError();
+
+ ScopedFunctionObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString name(scope, scope.engine->newString(QStringLiteral("construct")));
+ ScopedValue trap(scope, handler->get(name));
+
+ if (scope.hasException())
+ return Encode::undefined();
+ if (trap->isNullOrUndefined()) {
+ Q_ASSERT(target->isConstructor());
+ return target->callAsConstructor(argv, argc, newTarget);
+ }
+ if (!trap->isFunctionObject())
+ return scope.engine->throwTypeError();
+
+ ScopedFunctionObject trapFunction(scope, trap);
+ Value *arguments = scope.alloc(3);
+ arguments[0] = target;
+ arguments[1] = scope.engine->newArrayObject(argv, argc);
+ arguments[2] = newTarget ? *newTarget : Value::undefinedValue();
+ ScopedObject result(scope, trapFunction->call(handler, arguments, 3));
+
+ if (!result)
+ return scope.engine->throwTypeError();
+ return result->asReturnedValue();
+}
+
+ReturnedValue ProxyFunctionObject::virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(f);
+
+ const ProxyObject *o = static_cast<const ProxyObject *>(f);
+ if (!o->d()->handler)
+ return scope.engine->throwTypeError();
+
+ ScopedFunctionObject target(scope, o->d()->target);
+ Q_ASSERT(target);
+ ScopedObject handler(scope, o->d()->handler);
+ ScopedString name(scope, scope.engine->newString(QStringLiteral("apply")));
+ ScopedValue trap(scope, handler->get(name));
+
+ if (scope.hasException())
+ return Encode::undefined();
+ if (trap->isNullOrUndefined())
+ return target->call(thisObject, argv, argc);
+ if (!trap->isFunctionObject())
+ return scope.engine->throwTypeError();
+
+ ScopedFunctionObject trapFunction(scope, trap);
+ Value *arguments = scope.alloc(3);
+ arguments[0] = target;
+ arguments[1] = thisObject ? *thisObject : Value::undefinedValue();
+ arguments[2] = scope.engine->newArrayObject(argv, argc);
+ return trapFunction->call(handler, arguments, 3);
+}
+
+DEFINE_OBJECT_VTABLE(Proxy);
+
+void Heap::Proxy::init(QV4::ExecutionContext *ctx)
+{
+ Heap::FunctionObject::init(ctx, QStringLiteral("Proxy"));
+
+ Scope scope(ctx);
+ Scoped<QV4::Proxy> ctor(scope, this);
+ ctor->defineDefaultProperty(QStringLiteral("revocable"), QV4::Proxy::method_revocable, 2);
+ ctor->defineReadonlyConfigurableProperty(scope.engine->id_length(), Value::fromInt32(2));
+}
+
+ReturnedValue Proxy::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
+{
+ Scope scope(f);
+ if (argc < 2 || !argv[0].isObject() || !argv[1].isObject())
+ return scope.engine->throwTypeError();
+
+ const Object *target = static_cast<const Object *>(argv);
+ const Object *handler = static_cast<const Object *>(argv + 1);
+ if (const ProxyObject *ptarget = target->as<ProxyObject>())
+ if (!ptarget->d()->handler)
+ return scope.engine->throwTypeError();
+ if (const ProxyObject *phandler = handler->as<ProxyObject>())
+ if (!phandler->d()->handler)
+ return scope.engine->throwTypeError();
+
+ const FunctionObject *targetFunction = target->as<FunctionObject>();
+ if (targetFunction)
+ return scope.engine->memoryManager->allocate<ProxyFunctionObject>(targetFunction, handler)->asReturnedValue();
+ return scope.engine->memoryManager->allocate<ProxyObject>(target, handler)->asReturnedValue();
+}
+
+ReturnedValue Proxy::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
+{
+ return f->engine()->throwTypeError();
+}
+
+ReturnedValue Proxy::method_revocable(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ ScopedObject proxy(scope, Proxy::virtualCallAsConstructor(f, argv, argc, f));
+ if (scope.hasException())
+ return Encode::undefined();
+ Q_ASSERT(proxy);
+
+ ScopedString revoke(scope, scope.engine->newString(QStringLiteral("revoke")));
+ ScopedFunctionObject revoker(scope, scope.engine->memoryManager->allocate<FunctionObject>(scope.engine->rootContext(), nullptr, method_revoke));
+ revoker->defineReadonlyConfigurableProperty(scope.engine->id_length(), Value::fromInt32(0));
+ revoker->defineDefaultProperty(scope.engine->symbol_revokableProxy(), proxy);
+
+ ScopedObject o(scope, scope.engine->newObject());
+ ScopedString p(scope, scope.engine->newString(QStringLiteral("proxy")));
+ o->defineDefaultProperty(p, proxy);
+ o->defineDefaultProperty(revoke, revoker);
+ return o->asReturnedValue();
+}
+
+ReturnedValue Proxy::method_revoke(const FunctionObject *f, const Value *, const Value *, int)
+{
+ Scope scope(f);
+ ScopedObject o(scope, f->get(scope.engine->symbol_revokableProxy()));
+ Q_ASSERT(o);
+ ProxyObject *proxy = o->cast<ProxyObject>();
+
+ proxy->d()->target.set(scope.engine, nullptr);
+ proxy->d()->handler.set(scope.engine, nullptr);
+ return Encode::undefined();
+}
diff --git a/src/qml/jsruntime/qv4proxy_p.h b/src/qml/jsruntime/qv4proxy_p.h
new file mode 100644
index 0000000000..2ccb50ece6
--- /dev/null
+++ b/src/qml/jsruntime/qv4proxy_p.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** 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 QV4PROXY_P_H
+#define QV4PROXY_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 "qv4object_p.h"
+#include "qv4functionobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Heap {
+
+#define ProxyObjectMembers(class, Member) \
+ Member(class, Pointer, Object *, target) \
+ Member(class, Pointer, Object *, handler)
+
+DECLARE_HEAP_OBJECT(ProxyObject, FunctionObject) {
+ DECLARE_MARKOBJECTS(ProxyObject)
+
+ void init(const QV4::Object *target, const QV4::Object *handler);
+};
+
+struct ProxyFunctionObject : ProxyObject {
+ void init(const QV4::FunctionObject *target, const QV4::Object *handler);
+};
+
+#define ProxyMembers(class, Member) \
+ Member(class, Pointer, Symbol *, revokableProxySymbol) \
+
+DECLARE_HEAP_OBJECT(Proxy, FunctionObject) {
+ DECLARE_MARKOBJECTS(Proxy)
+
+ void init(QV4::ExecutionContext *ctx);
+};
+
+}
+
+/*
+ * The inheritance from FunctionObject is a hack. Regular proxy objects are no function objects.
+ * But this helps implement the proxy for function objects, where we need this and thus gives us
+ * all the virtual methods from ProxyObject without having to duplicate them.
+ *
+ * But it does require a few hacks to make sure we don't recognize regular proxy objects as function
+ * objects in the runtime.
+ */
+struct ProxyObject : FunctionObject {
+ V4_OBJECT2(ProxyObject, Object)
+ Q_MANAGED_TYPE(ProxyObject)
+ V4_INTERNALCLASS(ProxyObject)
+ enum {
+ IsFunctionObject = false
+ };
+
+ 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 bool virtualDeleteProperty(Managed *m, PropertyKey id);
+ static bool virtualHasProperty(const Managed *m, PropertyKey id);
+ static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p);
+ static bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs);
+ static bool virtualIsExtensible(const Managed *m);
+ static bool virtualPreventExtensions(Managed *);
+ static Heap::Object *virtualGetPrototypeOf(const Managed *);
+ static bool virtualSetPrototypeOf(Managed *, const Object *);
+ static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *iteratorTarget);
+};
+
+struct ProxyFunctionObject : ProxyObject {
+ V4_OBJECT2(ProxyFunctionObject, FunctionObject)
+ Q_MANAGED_TYPE(ProxyObject)
+ V4_INTERNALCLASS(ProxyFunctionObject)
+ enum {
+ IsFunctionObject = true
+ };
+
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct Proxy : FunctionObject
+{
+ V4_OBJECT2(Proxy, FunctionObject)
+
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_revocable(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_revoke(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4ECMAOBJECTS_P_H
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp
index 040f060476..cc0b0feeee 100644
--- a/src/qml/jsruntime/qv4qmlcontext.cpp
+++ b/src/qml/jsruntime/qv4qmlcontext.cpp
@@ -65,8 +65,6 @@ DEFINE_MANAGED_VTABLE(QmlContext);
void Heap::QQmlContextWrapper::init(QQmlContextData *context, QObject *scopeObject)
{
Object::init();
- readOnly = true;
- isNullWrapper = false;
this->context = new QQmlContextDataRef(context);
this->scopeObject.init(scopeObject);
}
@@ -78,30 +76,22 @@ void Heap::QQmlContextWrapper::destroy()
Object::destroy();
}
-ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasProperty)
+ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<QQmlContextWrapper>());
+
+ if (!id.isString())
+ return Object::virtualGet(m, id, receiver, hasProperty);
+
const QQmlContextWrapper *resource = static_cast<const QQmlContextWrapper *>(m);
QV4::ExecutionEngine *v4 = resource->engine();
QV4::Scope scope(v4);
- // In V8 the JS global object would come _before_ the QML global object,
- // so simulate that here.
- bool hasProp;
- QV4::ScopedValue result(scope, v4->globalObject->get(name, &hasProp));
- if (hasProp) {
- if (hasProperty)
- *hasProperty = hasProp;
- return result->asReturnedValue();
- }
-
- if (resource->d()->isNullWrapper)
- return Object::get(m, name, hasProperty);
-
if (v4->callingQmlContext() != *resource->d()->context)
- return Object::get(m, name, hasProperty);
+ return Object::virtualGet(m, id, receiver, hasProperty);
- result = Object::get(m, name, &hasProp);
+ bool hasProp = false;
+ ScopedValue result(scope, Object::virtualGet(m, id, receiver, &hasProp));
if (hasProp) {
if (hasProperty)
*hasProperty = hasProp;
@@ -129,6 +119,38 @@ ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasP
QObject *scopeObject = resource->getScopeObject();
+ ScopedString name(scope, id.asStringOrSymbol());
+
+ const auto performGobalLookUp = [&result, v4, &name, hasProperty]() {
+ bool hasProp = false;
+ result = v4->globalObject->get(name, &hasProp);
+ if (hasProp) {
+ if (hasProperty)
+ *hasProperty = hasProp;
+ return true;
+ }
+ return false;
+ };
+
+ // If the scope object is a QAbstractDynamicMetaObject, then QMetaObject::indexOfProperty
+ // will call createProperty() on the QADMO and implicitly create the property. While that
+ // is questionable behavior, there are two use-cases that we support in the light of this:
+ //
+ // (1) The implicit creation of properties is necessary because it will also result in
+ // a recorded capture, which will allow a re-evaluation of bindings when the value
+ // is populated later. See QTBUG-35233 and the test-case in tst_qqmlpropertymap.
+ //
+ // (1) Looking up "console" in order to place a console.log() call for example must
+ // find the console instead of creating a new property. Therefore we prioritize the
+ // lookup in the global object here.
+ //
+ // 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())) {
+ if (performGobalLookUp())
+ return result->asReturnedValue();
+ }
+
if (context->imports && name->startsWithUpper()) {
// Search for attached properties, enums and imported scripts
QQmlTypeNameCache::Result r = context->imports->query(name, QQmlImport::AllowRecursion);
@@ -139,7 +161,7 @@ ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasP
if (r.scriptIndex != -1) {
QV4::ScopedObject scripts(scope, context->importedScripts.valueRef());
if (scripts)
- return scripts->getIndexed(r.scriptIndex);
+ return scripts->get(r.scriptIndex);
return QV4::Encode::null();
} else if (r.type.isValid()) {
return QQmlTypeWrapper::create(v4, scopeObject, r.type);
@@ -219,14 +241,23 @@ ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasP
context = context->parent;
}
+ // 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();
+
expressionContext->unresolvedNames = true;
return Encode::undefined();
}
-bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value)
+bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
Q_ASSERT(m->as<QQmlContextWrapper>());
+
+ if (id.isSymbol() || id.isArrayIndex())
+ return Object::virtualPut(m, id, value, receiver);
+
QQmlContextWrapper *resource = static_cast<QQmlContextWrapper *>(m);
ExecutionEngine *v4 = resource->engine();
QV4::Scope scope(v4);
@@ -234,21 +265,9 @@ bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value)
return false;
QV4::Scoped<QQmlContextWrapper> wrapper(scope, resource);
- uint member = wrapper->internalClass()->find(name);
- if (member < UINT_MAX)
- return wrapper->putValue(member, value);
-
- if (wrapper->d()->isNullWrapper) {
- if (wrapper && wrapper->d()->readOnly) {
- QString error = QLatin1String("Invalid write to global property \"") + name->toQString() +
- QLatin1Char('"');
- ScopedString e(scope, v4->newString(error));
- v4->throwError(e);
- return false;
- }
-
- return Object::put(m, name, value);
- }
+ auto member = wrapper->internalClass()->findValueOrSetter(id);
+ if (member.index < UINT_MAX)
+ return wrapper->putValue(member.index, member.attrs, value);
// It's possible we could delay the calculation of the "actual" context (in the case
// of sub contexts) until it is definitely needed.
@@ -261,6 +280,7 @@ bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value)
// See QV8ContextWrapper::Getter for resolution order
QObject *scopeObject = wrapper->getScopeObject();
+ ScopedString name(scope, id.asStringOrSymbol());
while (context) {
const QV4::IdentifierHash &properties = context->propertyNames();
@@ -284,14 +304,10 @@ bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value)
expressionContext->unresolvedNames = true;
- if (wrapper->d()->readOnly) {
- QString error = QLatin1String("Invalid write to global property \"") + name->toQString() +
- QLatin1Char('"');
- v4->throwError(error);
- return false;
- }
-
- return Object::put(m, name, value);
+ QString error = QLatin1String("Invalid write to global property \"") + name->toQString() +
+ QLatin1Char('"');
+ v4->throwError(error);
+ return false;
}
void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml)
@@ -302,35 +318,11 @@ void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContex
this->activation.set(internalClass->engine, qml->d());
}
-Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, const QUrl &source, Value *sendFunction)
-{
- Scope scope(parent);
-
- QQmlContextData *context = new QQmlContextData;
- context->baseUrl = source;
- context->baseUrlString = source.toString();
- context->isInternal = true;
- context->isJSContext = true;
-
- Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<QQmlContextWrapper>(context, (QObject*)nullptr));
- qml->d()->isNullWrapper = true;
-
- qml->setReadOnly(false);
- QV4::ScopedObject api(scope, scope.engine->newObject());
- api->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("sendMessage"))), *sendFunction);
- qml->QV4::Object::put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("WorkerScript"))), api);
- qml->setReadOnly(true);
-
- Heap::QmlContext *c = scope.engine->memoryManager->alloc<QmlContext>(parent, qml);
- Q_ASSERT(c->vtable() == staticVTable());
- return c;
-}
-
Heap::QmlContext *QmlContext::create(ExecutionContext *parent, QQmlContextData *context, QObject *scopeObject)
{
Scope scope(parent);
- Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<QQmlContextWrapper>(context, scopeObject));
+ Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocate<QQmlContextWrapper>(context, scopeObject));
Heap::QmlContext *c = scope.engine->memoryManager->alloc<QmlContext>(parent, qml);
Q_ASSERT(c->vtable() == staticVTable());
return c;
diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h
index 647bef7fc1..4fe34a0a06 100644
--- a/src/qml/jsruntime/qv4qmlcontext_p.h
+++ b/src/qml/jsruntime/qv4qmlcontext_p.h
@@ -69,8 +69,6 @@ namespace Heap {
struct QQmlContextWrapper : Object {
void init(QQmlContextData *context, QObject *scopeObject);
void destroy();
- bool readOnly;
- bool isNullWrapper;
QQmlContextDataRef *context;
QQmlQPointer<QObject> scopeObject;
@@ -96,10 +94,8 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object
inline QObject *getScopeObject() const { return d()->scopeObject; }
inline QQmlContextData *getContext() const { return *d()->context; }
- void setReadOnly(bool b) { d()->readOnly = b; }
-
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static bool put(Managed *m, String *name, const Value &value);
+ 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);
};
struct Q_QML_EXPORT QmlContext : public ExecutionContext
@@ -107,7 +103,6 @@ struct Q_QML_EXPORT QmlContext : public ExecutionContext
V4_MANAGED(QmlContext, ExecutionContext)
V4_INTERNALCLASS(QmlContext)
- static Heap::QmlContext *createWorkerContext(QV4::ExecutionContext *parent, const QUrl &source, Value *sendFunction);
static Heap::QmlContext *create(QV4::ExecutionContext *parent, QQmlContextData *context, QObject *scopeObject);
QObject *qmlScope() const {
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index d63d42478a..2e4223de7d 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -56,7 +56,11 @@
#include <private/qv4functionobject_p.h>
#include <private/qv4runtime_p.h>
#include <private/qv4variantobject_p.h>
+
+#if QT_CONFIG(qml_sequence_object)
#include <private/qv4sequenceobject_p.h>
+#endif
+
#include <private/qv4objectproto_p.h>
#include <private/qv4jsonobject_p.h>
#include <private/qv4regexpobject_p.h>
@@ -181,11 +185,13 @@ static QV4::ReturnedValue loadProperty(QV4::ExecutionEngine *v4, QObject *object
if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property.propType()))
return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, property.propType());
} else {
+#if QT_CONFIG(qml_sequence_object)
// see if it's a sequence type
bool succeeded = false;
- QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType(), object, property.coreIndex(), &succeeded));
+ QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType(), object, property.coreIndex(), !property.isWritable(), &succeeded));
if (succeeded)
return retn->asReturnedValue();
+#endif
}
if (property.propType() == QMetaType::UnknownType) {
@@ -242,7 +248,7 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
return QV4::QObjectMethod::create(global, object, property->coreIndex());
} else if (property->isSignalHandler()) {
QmlSignalHandler::initProto(engine);
- return engine->memoryManager->allocObject<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue();
+ return engine->memoryManager->allocate<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue();
} else {
ExecutionContext *global = engine->rootContext();
return QV4::QObjectMethod::create(global, object, property->coreIndex());
@@ -308,7 +314,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String
}
}
}
- return QV4::Object::get(this, name, hasProperty);
+ return QV4::Object::virtualGet(this, name->propertyKey(), this, hasProperty);
}
QQmlData *ddata = QQmlData::get(d()->object(), false);
@@ -661,7 +667,7 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, int p
return setProperty(engine, object, property, value);
}
-bool QObjectWrapper::isEqualTo(Managed *a, Managed *b)
+bool QObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
{
Q_ASSERT(a->as<QV4::QObjectWrapper>());
QV4::QObjectWrapper *qobjectWrapper = static_cast<QV4::QObjectWrapper *>(a);
@@ -684,70 +690,92 @@ ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QObject *object)
return result;
}
}
- return (engine->memoryManager->allocObject<QV4::QObjectWrapper>(object))->asReturnedValue();
+ return (engine->memoryManager->allocate<QV4::QObjectWrapper>(object))->asReturnedValue();
}
-QV4::ReturnedValue QObjectWrapper::get(const Managed *m, String *name, bool *hasProperty)
+QV4::ReturnedValue QObjectWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
+ if (!id.isString())
+ return Object::virtualGet(m, id, receiver, hasProperty);
+
const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
+ Scope scope(that);
+ ScopedString n(scope, id.asStringOrSymbol());
QQmlContextData *qmlContext = that->engine()->callingQmlContext();
- return that->getQmlProperty(qmlContext, name, IgnoreRevision, hasProperty, /*includeImports*/ true);
+ return that->getQmlProperty(qmlContext, n, IgnoreRevision, hasProperty, /*includeImports*/ true);
}
-bool QObjectWrapper::put(Managed *m, String *name, const Value &value)
+bool QObjectWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
+ if (!id.isString())
+ return Object::virtualPut(m, id, value, receiver);
+
+ Scope scope(m);
QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
- ExecutionEngine *v4 = that->engine();
+ ScopedString name(scope, id.asStringOrSymbol());
- if (v4->hasException || QQmlData::wasDeleted(that->d()->object()))
+ if (scope.engine->hasException || QQmlData::wasDeleted(that->d()->object()))
return false;
- QQmlContextData *qmlContext = v4->callingQmlContext();
- if (!setQmlProperty(v4, qmlContext, that->d()->object(), name, QV4::QObjectWrapper::IgnoreRevision, value)) {
+ QQmlContextData *qmlContext = scope.engine->callingQmlContext();
+ if (!setQmlProperty(scope.engine, qmlContext, that->d()->object(), name, QV4::QObjectWrapper::IgnoreRevision, value)) {
QQmlData *ddata = QQmlData::get(that->d()->object());
// Types created by QML are not extensible at run-time, but for other QObjects we can store them
// as regular JavaScript properties, like on JavaScript objects.
if (ddata && ddata->context) {
QString error = QLatin1String("Cannot assign to non-existent property \"") +
name->toQString() + QLatin1Char('\"');
- v4->throwError(error);
+ scope.engine->throwError(error);
return false;
} else {
- return QV4::Object::put(m, name, value);
+ return QV4::Object::virtualPut(m, id, value, receiver);
}
}
return true;
}
-PropertyAttributes QObjectWrapper::query(const Managed *m, String *name)
+PropertyAttributes QObjectWrapper::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
{
- const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
- const QObject *thatObject = that->d()->object();
- if (QQmlData::wasDeleted(thatObject))
- return QV4::Object::query(m, name);
+ if (id.isString()) {
+ const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
+ const QObject *thatObject = that->d()->object();
+ if (!QQmlData::wasDeleted(thatObject)) {
+ Scope scope(m);
+ ScopedString n(scope, id.asStringOrSymbol());
+ QQmlContextData *qmlContext = scope.engine->callingQmlContext();
+ QQmlPropertyData local;
+ if (that->findProperty(scope.engine, qmlContext, n, IgnoreRevision, &local)
+ || n->equals(scope.engine->id_destroy()) || n->equals(scope.engine->id_toString())) {
+ if (p) {
+ // ### probably not the fastest implementation
+ bool hasProperty;
+ p->value = that->getQmlProperty(qmlContext, n, IgnoreRevision, &hasProperty, /*includeImports*/ true);
+ }
+ return QV4::Attr_Data;
+ }
+ }
+ }
- ExecutionEngine *engine = that->engine();
- QQmlContextData *qmlContext = engine->callingQmlContext();
- QQmlPropertyData local;
- if (that->findProperty(engine, qmlContext, name, IgnoreRevision, &local)
- || name->equals(engine->id_destroy()) || name->equals(engine->id_toString()))
- return QV4::Attr_Data;
- else
- return QV4::Object::query(m, name);
+ return QV4::Object::virtualGetOwnProperty(m, id, p);
}
-void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
+struct QObjectWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
+{
+ int propertyIndex = 0;
+ ~QObjectWrapperOwnPropertyKeyIterator() override = default;
+ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
+
+};
+
+PropertyKey QObjectWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
{
// Used to block access to QObject::destroyed() and QObject::deleteLater() from QML
static const int destroyedIdx1 = QObject::staticMetaObject.indexOfSignal("destroyed(QObject*)");
static const int destroyedIdx2 = QObject::staticMetaObject.indexOfSignal("destroyed()");
static const int deleteLaterIdx = QObject::staticMetaObject.indexOfSlot("deleteLater()");
- name->setM(nullptr);
- *index = UINT_MAX;
-
- QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
+ const QObjectWrapper *that = static_cast<const QObjectWrapper*>(o);
QObject *thatObject = that->d()->object();
if (thatObject && !QQmlData::wasDeleted(thatObject)) {
@@ -755,40 +783,50 @@ void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name
// These indices don't apply to gadgets, so don't block them.
const bool preventDestruction = mo->superClass() || mo == &QObject::staticMetaObject;
const int propertyCount = mo->propertyCount();
- if (it->arrayIndex < static_cast<uint>(propertyCount)) {
+ if (propertyIndex < propertyCount) {
ExecutionEngine *thatEngine = that->engine();
Scope scope(thatEngine);
- const QMetaProperty property = mo->property(it->arrayIndex);
+ const QMetaProperty property = mo->property(propertyIndex);
ScopedString propName(scope, thatEngine->newString(QString::fromUtf8(property.name())));
- name->setM(propName->d());
- ++it->arrayIndex;
- *attributes = QV4::Attr_Data;
-
- QQmlPropertyData local;
- local.load(property);
- p->value = that->getProperty(thatEngine, thatObject, &local);
- return;
+ ++propertyIndex;
+ if (attrs)
+ *attrs= QV4::Attr_Data;
+ if (pd) {
+ QQmlPropertyData local;
+ local.load(property);
+ pd->value = that->getProperty(thatEngine, thatObject, &local);
+ }
+ return propName->toPropertyKey();
}
const int methodCount = mo->methodCount();
- while (it->arrayIndex < static_cast<uint>(propertyCount + methodCount)) {
- const int index = it->arrayIndex - propertyCount;
+ while (propertyIndex < propertyCount + methodCount) {
+ Q_ASSERT(propertyIndex >= propertyCount);
+ int index = propertyIndex - propertyCount;
const QMetaMethod method = mo->method(index);
- ++it->arrayIndex;
+ ++propertyIndex;
if (method.access() == QMetaMethod::Private || (preventDestruction && (index == deleteLaterIdx || index == destroyedIdx1 || index == destroyedIdx2)))
continue;
ExecutionEngine *thatEngine = that->engine();
Scope scope(thatEngine);
ScopedString methodName(scope, thatEngine->newString(QString::fromUtf8(method.name())));
- name->setM(methodName->d());
- *attributes = QV4::Attr_Data;
-
- QQmlPropertyData local;
- local.load(method);
- p->value = that->getProperty(thatEngine, thatObject, &local);
- return;
+ if (attrs)
+ *attrs = QV4::Attr_Data;
+ if (pd) {
+ QQmlPropertyData local;
+ local.load(method);
+ pd->value = that->getProperty(thatEngine, thatObject, &local);
+ }
+ return methodName->toPropertyKey();
}
}
- QV4::Object::advanceIterator(m, it, name, index, p, attributes);
+
+ return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
+}
+
+OwnPropertyKeyIterator *QObjectWrapper::virtualOwnPropertyKeys(const Object *m, Value *target)
+{
+ *target = *m;
+ return new QObjectWrapperOwnPropertyKeyIterator;
}
namespace QV4 {
@@ -1619,6 +1657,7 @@ void CallArgument::initAsType(int callType)
}
}
+#if QT_CONFIG(qml_sequence_object)
template <class T, class M>
void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M CallArgument::*member, bool &queryEngine)
{
@@ -1631,6 +1670,7 @@ void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M
}
}
}
+#endif
void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const QV4::Value &value)
{
@@ -1685,7 +1725,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q
uint length = array->getLength();
for (uint ii = 0; ii < length; ++ii) {
QObject *o = nullptr;
- qobjectWrapper = array->getIndexed(ii);
+ qobjectWrapper = array->get(ii);
if (!!qobjectWrapper)
o = qobjectWrapper->object();
qlistPtr->append(o);
@@ -1713,6 +1753,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q
type = callType;
} else if (callType == QMetaType::Void) {
*qvariantPtr = QVariant();
+#if QT_CONFIG(qml_sequence_object)
} else if (callType == qMetaTypeId<std::vector<int>>()
|| callType == qMetaTypeId<std::vector<qreal>>()
|| callType == qMetaTypeId<std::vector<bool>>()
@@ -1740,6 +1781,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q
stdVectorQModelIndexPtr = nullptr;
fromContainerValue<std::vector<QModelIndex>>(object, callType, &CallArgument::stdVectorQModelIndexPtr, queryEngine);
}
+#endif
} else {
queryEngine = true;
}
@@ -1833,7 +1875,7 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine)
ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index)
{
Scope valueScope(scope);
- Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocObject<QObjectMethod>(scope));
+ Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
method->d()->setObject(object);
if (QQmlData *ddata = QQmlData::get(object))
@@ -1846,7 +1888,7 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, in
ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index)
{
Scope valueScope(scope);
- Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocObject<QObjectMethod>(scope));
+ Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
method->d()->setPropertyCache(valueType->d()->propertyCache());
method->d()->index = index;
method->d()->valueTypeWrapper.set(valueScope.engine, valueType->d());
@@ -1906,7 +1948,7 @@ QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionEngine *engine, c
return Encode::undefined();
}
-ReturnedValue QObjectMethod::call(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue QObjectMethod::virtualCall(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc)
{
const QObjectMethod *This = static_cast<const QObjectMethod*>(m);
return This->callInternal(thisObject, argv, argc);
@@ -1961,7 +2003,7 @@ ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *
CallData *callData = cData.callData();
if (method.isV4Function()) {
- QV4::ScopedValue rv(scope, QV4::Primitive::undefinedValue());
+ QV4::ScopedValue rv(scope, QV4::Value::undefinedValue());
QQmlV4Function func(callData, rv, v4);
QQmlV4Function *funcptr = &func;
@@ -2019,7 +2061,7 @@ void Heap::QMetaObjectWrapper::ensureConstructorsCache() {
ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) {
QV4::Scope scope(engine);
- Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocObject<QV4::QMetaObjectWrapper>(metaObject)->asReturnedValue());
+ Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocate<QV4::QMetaObjectWrapper>(metaObject)->asReturnedValue());
mo->init(engine);
return mo->asReturnedValue();
}
@@ -2032,12 +2074,12 @@ void QMetaObjectWrapper::init(ExecutionEngine *) {
for (int k = 0; k < Enum.keyCount(); k++) {
const char* key = Enum.key(k);
const int value = Enum.value(k);
- defineReadonlyProperty(QLatin1String(key), Primitive::fromInt32(value));
+ defineReadonlyProperty(QLatin1String(key), Value::fromInt32(value));
}
}
}
-ReturnedValue QMetaObjectWrapper::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue QMetaObjectWrapper::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
{
const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(f);
return This->constructInternal(argv, argc);
@@ -2068,7 +2110,7 @@ ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc)
}
Scoped<QMetaObjectWrapper> metaObject(scope, this);
object->defineDefaultProperty(v4->id_constructor(), metaObject);
- object->setPrototype(const_cast<QMetaObjectWrapper*>(this));
+ object->setPrototypeOf(const_cast<QMetaObjectWrapper*>(this));
return object.asReturnedValue();
}
@@ -2143,7 +2185,7 @@ ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine
}
}
-bool QMetaObjectWrapper::isEqualTo(Managed *a, Managed *b)
+bool QMetaObjectWrapper::virtualIsEqualTo(Managed *a, Managed *b)
{
Q_ASSERT(a->as<QMetaObjectWrapper>());
QMetaObjectWrapper *aMetaObject = a->as<QMetaObjectWrapper>();
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index 1455acc1b3..be46245d5a 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -182,21 +182,20 @@ struct Q_QML_EXPORT QObjectWrapper : public Object
void destroyObject(bool lastCall);
-protected:
- static bool isEqualTo(Managed *that, Managed *o);
-
static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired = true);
+protected:
static void setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value);
+ static bool virtualIsEqualTo(Managed *that, Managed *o);
static ReturnedValue create(ExecutionEngine *engine, QObject *object);
static QQmlPropertyData *findProperty(ExecutionEngine *engine, QObject *o, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local);
QQmlPropertyData *findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const;
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static bool put(Managed *m, String *name, const Value &value);
- static PropertyAttributes query(const Managed *, String *name);
- static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
+ 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 PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p);
+ static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
static ReturnedValue method_connect(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_disconnect(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
@@ -237,7 +236,7 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject
QV4::ReturnedValue method_toString(QV4::ExecutionEngine *engine) const;
QV4::ReturnedValue method_destroy(QV4::ExecutionEngine *ctx, const Value *args, int argc) const;
- static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
ReturnedValue callInternal(const Value *thisObject, const Value *argv, int argc) const;
@@ -251,11 +250,12 @@ struct Q_QML_EXPORT QMetaObjectWrapper : public QV4::FunctionObject
V4_NEEDS_DESTROY
static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject);
- static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc);
- static bool isEqualTo(Managed *a, Managed *b);
-
const QMetaObject *metaObject() const { return d()->metaObject; }
+protected:
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *);
+ static bool virtualIsEqualTo(Managed *a, Managed *b);
+
private:
void init(ExecutionEngine *engine);
ReturnedValue constructInternal(const Value *argv, int argc) const;
diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp
new file mode 100644
index 0000000000..15dcb602eb
--- /dev/null
+++ b/src/qml/jsruntime/qv4reflect.cpp
@@ -0,0 +1,285 @@
+/****************************************************************************
+**
+** 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 "qv4reflect_p.h"
+#include "qv4symbol_p.h"
+#include "qv4runtimeapi_p.h"
+#include "qv4objectproto_p.h"
+#include "qv4propertykey_p.h"
+#include "qv4objectiterator_p.h"
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(Reflect);
+
+void Heap::Reflect::init()
+{
+ Object::init();
+ Scope scope(internalClass->engine);
+ ScopedObject r(scope, this);
+
+ r->defineDefaultProperty(QStringLiteral("apply"), QV4::Reflect::method_apply, 3);
+ r->defineDefaultProperty(QStringLiteral("construct"), QV4::Reflect::method_construct, 2);
+ r->defineDefaultProperty(QStringLiteral("defineProperty"), QV4::Reflect::method_defineProperty, 3);
+ r->defineDefaultProperty(QStringLiteral("deleteProperty"), QV4::Reflect::method_deleteProperty, 2);
+ r->defineDefaultProperty(QStringLiteral("get"), QV4::Reflect::method_get, 2);
+ r->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), QV4::Reflect::method_getOwnPropertyDescriptor, 2);
+ r->defineDefaultProperty(QStringLiteral("getPrototypeOf"), QV4::Reflect::method_getPrototypeOf, 1);
+ r->defineDefaultProperty(QStringLiteral("has"), QV4::Reflect::method_has, 2);
+ r->defineDefaultProperty(QStringLiteral("isExtensible"), QV4::Reflect::method_isExtensible, 1);
+ r->defineDefaultProperty(QStringLiteral("ownKeys"), QV4::Reflect::method_ownKeys, 1);
+ r->defineDefaultProperty(QStringLiteral("preventExtensions"), QV4::Reflect::method_preventExtensions, 1);
+ r->defineDefaultProperty(QStringLiteral("set"), QV4::Reflect::method_set, 3);
+ r->defineDefaultProperty(QStringLiteral("setPrototypeOf"), QV4::Reflect::method_setPrototypeOf, 2);
+}
+
+struct CallArgs {
+ Value *argv;
+ int argc;
+};
+
+static CallArgs createListFromArrayLike(Scope &scope, const Object *o)
+{
+ int len = o->getLength();
+ Value *arguments = scope.alloc(len);
+
+ for (int i = 0; i < len; ++i) {
+ arguments[i] = o->get(i);
+ if (scope.hasException())
+ return { nullptr, 0 };
+ }
+ return { arguments, len };
+}
+
+ReturnedValue Reflect::method_apply(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (argc < 3 || !argv[0].isFunctionObject() || !argv[2].isObject())
+ return scope.engine->throwTypeError();
+
+ const Object *o = static_cast<const Object *>(argv + 2);
+ CallArgs arguments = createListFromArrayLike(scope, o);
+ if (scope.hasException())
+ return Encode::undefined();
+
+ return static_cast<const FunctionObject &>(argv[0]).call(&argv[1], arguments.argv, arguments.argc);
+}
+
+ReturnedValue Reflect::method_construct(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (argc < 2 || !argv[1].isObject())
+ return scope.engine->throwTypeError();
+ const FunctionObject *target = argv[0].as<FunctionObject>();
+ const FunctionObject *newTarget = argc == 3 ? argv[2].as<FunctionObject>() : target;
+ if (!target || !target->isConstructor() || !newTarget || !newTarget->isConstructor())
+ return scope.engine->throwTypeError();
+
+ const Object *o = static_cast<const Object *>(argv + 1);
+ CallArgs arguments = createListFromArrayLike(scope, o);
+ if (scope.hasException())
+ return Encode::undefined();
+
+ return target->callAsConstructor(arguments.argv, arguments.argc, newTarget);
+}
+
+ReturnedValue Reflect::method_defineProperty(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc || !argv[0].isObject())
+ return scope.engine->throwTypeError();
+
+ ScopedObject O(scope, argv[0]);
+ ScopedPropertyKey name(scope, (argc > 1 ? argv[1] : Value::undefinedValue()).toPropertyKey(scope.engine));
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
+
+ ScopedValue attributes(scope, argc > 2 ? argv[2] : Value::undefinedValue());
+ ScopedProperty pd(scope);
+ PropertyAttributes attrs;
+ ObjectPrototype::toPropertyDescriptor(scope.engine, attributes, pd, &attrs);
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
+
+ bool result = O->defineOwnProperty(name, pd, attrs);
+
+ return Encode(result);
+}
+
+ReturnedValue Reflect::method_deleteProperty(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ ExecutionEngine *e = f->engine();
+ if (!argc || !argv[0].isObject())
+ return e->throwTypeError();
+
+ bool result = Runtime::method_deleteProperty(e, argv[0], argc > 1 ? argv[1] : Value::undefinedValue());
+ return Encode(result);
+}
+
+ReturnedValue Reflect::method_get(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc || !argv[0].isObject())
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, static_cast<const Object *>(argv));
+ Value undef = Value::undefinedValue();
+ const Value *index = argc > 1 ? &argv[1] : &undef;
+ ScopedPropertyKey name(scope, index->toPropertyKey(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+ ScopedValue receiver(scope, argc > 2 ? argv[2] : *o);
+
+ return Encode(o->get(name, receiver));
+}
+
+ReturnedValue Reflect::method_getOwnPropertyDescriptor(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ if (!argc || !argv[0].isObject())
+ return f->engine()->throwTypeError();
+
+ return ObjectPrototype::method_getOwnPropertyDescriptor(f, thisObject, argv, argc);
+}
+
+ReturnedValue Reflect::method_getPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ if (!argc || !argv[0].isObject())
+ return f->engine()->throwTypeError();
+
+ const Object *o = static_cast<const Object *>(argv);
+ Heap::Object *p = o->getPrototypeOf();
+ return (p ? p->asReturnedValue() : Encode::null());
+}
+
+ReturnedValue Reflect::method_has(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc || !argv[0].isObject())
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, static_cast<const Object *>(argv));
+ Value undef = Value::undefinedValue();
+ const Value *index = argc > 1 ? &argv[1] : &undef;
+
+ ScopedPropertyKey name(scope, index->toPropertyKey(scope.engine));
+ if (scope.engine->hasException)
+ return false;
+
+ return Encode(o->hasProperty(name));
+}
+
+ReturnedValue Reflect::method_isExtensible(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ if (!argc || !argv[0].isObject())
+ return f->engine()->throwTypeError();
+
+ const Object *o = static_cast<const Object *>(argv);
+ return Encode(o->isExtensible());
+}
+
+
+ReturnedValue Reflect::method_ownKeys(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ if (!argc || !argv[0].isObject())
+ return f->engine()->throwTypeError();
+
+ Scope scope(f);
+ if (!argc)
+ return scope.engine->throwTypeError();
+
+ ScopedObject O(scope, argv[0].toObject(scope.engine));
+ if (!O)
+ return Encode::undefined();
+
+ ScopedArrayObject keys(scope, scope.engine->newArrayObject());
+
+ ObjectIterator it(scope, O, ObjectIterator::WithSymbols);
+ ScopedPropertyKey key(scope);
+ ScopedValue v(scope);
+ while (1) {
+ key = it.next();
+ if (!key->isValid())
+ break;
+ v = key->toStringOrSymbol(scope.engine);
+ keys->push_back(v);
+ }
+
+ return keys->asReturnedValue();
+
+}
+
+ReturnedValue Reflect::method_preventExtensions(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc || !argv[0].isObject())
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, static_cast<const Object *>(argv));
+ return Encode(o->preventExtensions());
+}
+
+ReturnedValue Reflect::method_set(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc || !argv[0].isObject())
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, static_cast<const Object *>(argv));
+ Value undef = Value::undefinedValue();
+ const Value *index = argc > 1 ? &argv[1] : &undef;
+ const Value &val = argc > 2 ? argv[2] : undef;
+ ScopedValue receiver(scope, argc >3 ? argv[3] : argv[0]);
+
+ ScopedPropertyKey propertyKey(scope, index->toPropertyKey(scope.engine));
+ if (scope.engine->hasException)
+ return false;
+ bool result = o->put(propertyKey, val, receiver);
+ return Encode(result);
+}
+
+ReturnedValue Reflect::method_setPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ if (argc < 2 || !argv[0].isObject() || (!argv[1].isNull() && !argv[1].isObject()))
+ return f->engine()->throwTypeError();
+
+ Scope scope(f);
+ ScopedObject o(scope, static_cast<const Object *>(argv));
+ const Object *proto = argv[1].isNull() ? nullptr : static_cast<const Object *>(argv + 1);
+ return Encode(o->setPrototypeOf(proto));
+}
diff --git a/src/qml/jsruntime/qv4reflect_p.h b/src/qml/jsruntime/qv4reflect_p.h
new file mode 100644
index 0000000000..d480e1d914
--- /dev/null
+++ b/src/qml/jsruntime/qv4reflect_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** 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 QV4REFLECT_H
+#define QV4REFLECT_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 "qv4object_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Heap {
+
+struct Reflect : Object {
+ void init();
+};
+
+}
+
+struct Reflect : Object {
+ V4_OBJECT2(Reflect, Object)
+
+ static ReturnedValue method_apply(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_construct(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_defineProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_deleteProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *, const Value *argv, int argc);
+ static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isExtensible(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_ownKeys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_preventExtensions(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp
index e10493b879..05c3b4c4ca 100644
--- a/src/qml/jsruntime/qv4regexp.cpp
+++ b/src/qml/jsruntime/qv4regexp.cpp
@@ -41,6 +41,7 @@
#include "qv4engine_p.h"
#include "qv4scopedvalue_p.h"
#include <private/qv4mm_p.h>
+#include <runtime/VM.h>
using namespace QV4;
@@ -62,16 +63,92 @@ uint RegExp::match(const QString &string, int start, uint *matchOffsets)
WTF::String s(string);
#if ENABLE(YARR_JIT)
- if (d()->hasValidJITCode())
+ if (d()->hasValidJITCode()) {
+#if ENABLE(YARR_JIT_ALL_PARENS_EXPRESSIONS)
+ char buffer[8192];
+ return uint(jitCode()->execute(s.characters16(), start, s.length(), (int*)matchOffsets, buffer, 8192).start);
+#else
return uint(jitCode()->execute(s.characters16(), start, s.length(), (int*)matchOffsets).start);
#endif
+ }
+#endif
return JSC::Yarr::interpret(byteCode(), s.characters16(), string.length(), start, matchOffsets);
}
-Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline, bool global)
+QString RegExp::getSubstitution(const QString &matched, const QString &str, int position, const Value *captures, int nCaptures, const QString &replacement)
+{
+ QString result;
+
+ int matchedLength = matched.length();
+ Q_ASSERT(position >= 0 && position <= str.length());
+ int tailPos = position + matchedLength;
+ int seenDollar = -1;
+ for (int i = 0; i < replacement.length(); ++i) {
+ QChar ch = replacement.at(i);
+ if (seenDollar >= 0) {
+ if (ch.unicode() == '$') {
+ result += QLatin1Char('$');
+ } else if (ch.unicode() == '&') {
+ result += matched;
+ } else if (ch.unicode() == '`') {
+ result += str.left(position);
+ } else if (ch.unicode() == '\'') {
+ result += str.mid(tailPos);
+ } else if (ch.unicode() >= '0' && ch.unicode() <= '9') {
+ int n = ch.unicode() - '0';
+ if (i + 1 < replacement.length()) {
+ ch = replacement.at(i + 1);
+ if (ch.unicode() >= '0' && ch.unicode() <= '9') {
+ n = n*10 + (ch.unicode() - '0');
+ ++i;
+ }
+ }
+ if (n > 0 && n <= nCaptures) {
+ String *s = captures[n].stringValue();
+ if (s)
+ result += s->toQString();
+ } else {
+ for (int j = seenDollar; j <= i; ++j)
+ result += replacement.at(j);
+ }
+ } else {
+ result += QLatin1Char('$');
+ result += ch;
+ }
+ seenDollar = -1;
+ } else {
+ if (ch == QLatin1Char('$')) {
+ seenDollar = i;
+ continue;
+ }
+ result += ch;
+ }
+ }
+ if (seenDollar >= 0)
+ result += QLatin1Char('$');
+ return result;
+}
+
+QString Heap::RegExp::flagsAsString() const
+{
+ QString result;
+ if (flags & CompiledData::RegExp::RegExp_Global)
+ result += QLatin1Char('g');
+ if (flags & CompiledData::RegExp::RegExp_IgnoreCase)
+ result += QLatin1Char('i');
+ if (flags & CompiledData::RegExp::RegExp_Multiline)
+ result += QLatin1Char('m');
+ if (flags & CompiledData::RegExp::RegExp_Unicode)
+ result += QLatin1Char('u');
+ if (flags & CompiledData::RegExp::RegExp_Sticky)
+ result += QLatin1Char('y');
+ return result;
+}
+
+Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, uint flags)
{
- RegExpCacheKey key(pattern, ignoreCase, multiline, global);
+ RegExpCacheKey key(pattern, flags);
RegExpCache *cache = engine->regExpCache;
if (!cache)
@@ -82,7 +159,7 @@ Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bo
return result->d();
Scope scope(engine);
- Scoped<RegExp> result(scope, engine->memoryManager->alloc<RegExp>(engine, pattern, ignoreCase, multiline, global));
+ Scoped<RegExp> result(scope, engine->memoryManager->alloc<RegExp>(engine, pattern, flags));
result->d()->cache = cache;
cachedValue.set(engine, result);
@@ -90,26 +167,36 @@ Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bo
return result->d();
}
-void Heap::RegExp::init(ExecutionEngine *engine, const QString &pattern, bool ignoreCase, bool multiline, bool global)
+void Heap::RegExp::init(ExecutionEngine *engine, const QString &pattern, uint flags)
{
Base::init();
this->pattern = new QString(pattern);
- this->ignoreCase = ignoreCase;
- this->multiLine = multiline;
- this->global = global;
+ this->flags = flags;
valid = false;
- const char* error = nullptr;
- JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), ignoreCase, multiLine, &error);
- if (error)
+ JSC::Yarr::ErrorCode error = JSC::Yarr::ErrorCode::NoError;
+ JSC::RegExpFlags jscFlags = JSC::NoFlags;
+ if (flags & CompiledData::RegExp::RegExp_Global)
+ jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagGlobal);
+ if (flags & CompiledData::RegExp::RegExp_IgnoreCase)
+ jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagIgnoreCase);
+ if (flags & CompiledData::RegExp::RegExp_Multiline)
+ jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagMultiline);
+ if (flags & CompiledData::RegExp::RegExp_Unicode)
+ jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagUnicode);
+ if (flags & CompiledData::RegExp::RegExp_Sticky)
+ jscFlags = static_cast<JSC::RegExpFlags>(flags | JSC::FlagSticky);
+
+ JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), jscFlags, error);
+ if (error != JSC::Yarr::ErrorCode::NoError)
return;
subPatternCount = yarrPattern.m_numSubpatterns;
#if ENABLE(YARR_JIT)
if (!yarrPattern.m_containsBackreferences && engine->canJIT()) {
jitCode = new JSC::Yarr::YarrCodeBlock;
- JSC::JSGlobalData dummy(internalClass->engine->regExpAllocator);
- JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, &dummy, *jitCode);
+ JSC::VM *vm = static_cast<JSC::VM *>(engine);
+ JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, vm, *jitCode);
}
#else
Q_UNUSED(engine)
@@ -118,8 +205,7 @@ void Heap::RegExp::init(ExecutionEngine *engine, const QString &pattern, bool ig
valid = true;
return;
}
- OwnPtr<JSC::Yarr::BytecodePattern> p = JSC::Yarr::byteCompile(yarrPattern, internalClass->engine->bumperPointerAllocator);
- byteCode = p.take();
+ byteCode = JSC::Yarr::byteCompile(yarrPattern, internalClass->engine->bumperPointerAllocator).release();
if (byteCode)
valid = true;
}
diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h
index 56454f73d3..6afb10ea95 100644
--- a/src/qml/jsruntime/qv4regexp_p.h
+++ b/src/qml/jsruntime/qv4regexp_p.h
@@ -76,7 +76,7 @@ struct RegExpCacheKey;
namespace Heap {
struct RegExp : Base {
- void init(ExecutionEngine *engine, const QString& pattern, bool ignoreCase, bool multiline, bool global);
+ void init(ExecutionEngine *engine, const QString& pattern, uint flags);
void destroy();
QString *pattern;
@@ -86,18 +86,24 @@ struct RegExp : Base {
#endif
bool hasValidJITCode() const {
#if ENABLE(YARR_JIT)
- return jitCode && !jitCode->isFallBack() && jitCode->has16BitCode();
+ return jitCode && !jitCode->failureReason().has_value() && jitCode->has16BitCode();
#else
return false;
#endif
}
+
+ bool ignoreCase() const { return flags & CompiledData::RegExp::RegExp_IgnoreCase; }
+ bool multiLine() const { return flags & CompiledData::RegExp::RegExp_Multiline; }
+ bool global() const { return flags & CompiledData::RegExp::RegExp_Global; }
+ bool unicode() const { return flags & CompiledData::RegExp::RegExp_Unicode; }
+ bool sticky() const { return flags & CompiledData::RegExp::RegExp_Sticky; }
+
RegExpCache *cache;
int subPatternCount;
- bool ignoreCase;
- bool multiLine;
- bool global;
+ uint flags;
bool valid;
+ QString flagsAsString() const;
int captureCount() const { return subPatternCount + 1; }
};
Q_STATIC_ASSERT(std::is_trivial< RegExp >::value);
@@ -118,11 +124,13 @@ struct RegExp : public Managed
#endif
RegExpCache *cache() const { return d()->cache; }
int subPatternCount() const { return d()->subPatternCount; }
- bool ignoreCase() const { return d()->ignoreCase; }
- bool multiLine() const { return d()->multiLine; }
- bool global() const { return d()->global; }
+ bool ignoreCase() const { return d()->ignoreCase(); }
+ bool multiLine() const { return d()->multiLine(); }
+ bool global() const { return d()->global(); }
+ bool unicode() const { return d()->unicode(); }
+ bool sticky() const { return d()->sticky(); }
- static Heap::RegExp *create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false, bool global = false);
+ static Heap::RegExp *create(ExecutionEngine* engine, const QString& pattern, uint flags = CompiledData::RegExp::RegExp_NoFlags);
bool isValid() const { return d()->valid; }
@@ -130,35 +138,30 @@ struct RegExp : public Managed
int captureCount() const { return subPatternCount() + 1; }
+ static QString getSubstitution(const QString &matched, const QString &str, int position, const Value *captures, int nCaptures, const QString &replacement);
+
friend class RegExpCache;
};
struct RegExpCacheKey
{
- RegExpCacheKey(const QString &pattern, bool ignoreCase, bool multiLine, bool global)
- : pattern(pattern)
- , ignoreCase(ignoreCase)
- , multiLine(multiLine)
- , global(global)
+ RegExpCacheKey(const QString &pattern, uint flags)
+ : pattern(pattern), flags(flags)
{ }
explicit inline RegExpCacheKey(const RegExp::Data *re);
bool operator==(const RegExpCacheKey &other) const
- { return pattern == other.pattern && ignoreCase == other.ignoreCase && multiLine == other.multiLine && global == other.global; }
+ { return pattern == other.pattern && flags == other.flags;; }
bool operator!=(const RegExpCacheKey &other) const
{ return !operator==(other); }
QString pattern;
- uint ignoreCase : 1;
- uint multiLine : 1;
- uint global : 1;
+ uint flags;
};
inline RegExpCacheKey::RegExpCacheKey(const RegExp::Data *re)
: pattern(*re->pattern)
- , ignoreCase(re->ignoreCase)
- , multiLine(re->multiLine)
- , global(re->global)
+ , flags(re->flags)
{}
inline uint qHash(const RegExpCacheKey& key, uint seed = 0) Q_DECL_NOTHROW
diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp
index 000e2c3a7e..634fbcbd97 100644
--- a/src/qml/jsruntime/qv4regexpobject.cpp
+++ b/src/qml/jsruntime/qv4regexpobject.cpp
@@ -44,11 +44,8 @@
#include <private/qv4mm_p.h>
#include "qv4scopedvalue_p.h"
#include "qv4jscall_p.h"
+#include "qv4symbol_p.h"
-#include <private/qqmljsengine_p.h>
-#include <private/qqmljslexer_p.h>
-#include <private/qqmljsparser_p.h>
-#include <private/qqmljsast_p.h>
#include "private/qlocale_tools_p.h"
#include <QtCore/QDebug>
@@ -71,7 +68,7 @@ void Heap::RegExpObject::init()
Object::init();
Scope scope(internalClass->engine);
Scoped<QV4::RegExpObject> o(scope, this);
- value.set(scope.engine, QV4::RegExp::create(scope.engine, QString(), false, false));
+ value.set(scope.engine, QV4::RegExp::create(scope.engine, QString(), CompiledData::RegExp::RegExp_NoFlags));
o->initProperties();
}
@@ -131,30 +128,17 @@ void Heap::RegExpObject::init(const QRegExp &re)
Scope scope(internalClass->engine);
Scoped<QV4::RegExpObject> o(scope, this);
- o->d()->value.set(scope.engine,
- QV4::RegExp::create(scope.engine, pattern, re.caseSensitivity() == Qt::CaseInsensitive, false));
+ uint flags = (re.caseSensitivity() == Qt::CaseInsensitive ? CompiledData::RegExp::RegExp_IgnoreCase : CompiledData::RegExp::RegExp_NoFlags);
+ o->d()->value.set(scope.engine, QV4::RegExp::create(scope.engine, pattern, flags));
o->initProperties();
}
void RegExpObject::initProperties()
{
- setProperty(Index_LastIndex, Primitive::fromInt32(0));
+ setProperty(Index_LastIndex, Value::fromInt32(0));
Q_ASSERT(value());
-
- QString p = *value()->pattern;
- if (p.isEmpty()) {
- p = QStringLiteral("(?:)");
- } else {
- // escape certain parts, see ch. 15.10.4
- p.replace('/', QLatin1String("\\/"));
- }
-
- setProperty(Index_Source, engine()->newString(p));
- setProperty(Index_Global, Primitive::fromBoolean(global()));
- setProperty(Index_IgnoreCase, Primitive::fromBoolean(value()->ignoreCase));
- setProperty(Index_Multiline, Primitive::fromBoolean(value()->multiLine));
}
// Converts a JS RegExp to a QRegExp.
@@ -162,40 +146,80 @@ void RegExpObject::initProperties()
// have different semantics/flags, but we try to do our best.
QRegExp RegExpObject::toQRegExp() const
{
- Qt::CaseSensitivity caseSensitivity = value()->ignoreCase ? Qt::CaseInsensitive : Qt::CaseSensitive;
+ Qt::CaseSensitivity caseSensitivity = (value()->flags & CompiledData::RegExp::RegExp_IgnoreCase) ? Qt::CaseInsensitive : Qt::CaseSensitive;
return QRegExp(*value()->pattern, caseSensitivity, QRegExp::RegExp2);
}
QString RegExpObject::toString() const
{
- QString result = QLatin1Char('/') + source() + QLatin1Char('/');
- if (global())
- result += QLatin1Char('g');
- if (value()->ignoreCase)
- result += QLatin1Char('i');
- if (value()->multiLine)
- result += QLatin1Char('m');
- return result;
+ QString p = *value()->pattern;
+ if (p.isEmpty()) {
+ p = QStringLiteral("(?:)");
+ } else {
+ // escape certain parts, see ch. 15.10.4
+ p.replace('/', QLatin1String("\\/"));
+ }
+ return p;
}
QString RegExpObject::source() const
{
Scope scope(engine());
- ScopedString source(scope, scope.engine->newIdentifier(QStringLiteral("source")));
- ScopedValue s(scope, const_cast<RegExpObject *>(this)->get(source));
+ ScopedValue s(scope, get(scope.engine->id_source()));
return s->toQString();
}
-uint RegExpObject::flags() const
+ReturnedValue RegExpObject::builtinExec(ExecutionEngine *engine, const String *str)
{
- uint f = 0;
- if (global())
- f |= QV4::RegExpObject::RegExp_Global;
- if (value()->ignoreCase)
- f |= QV4::RegExpObject::RegExp_IgnoreCase;
- if (value()->multiLine)
- f |= QV4::RegExpObject::RegExp_Multiline;
- return f;
+ QString s = str->toQString();
+
+ Scope scope(engine);
+ int offset = (global() || sticky()) ? lastIndex() : 0;
+ if (offset < 0 || offset > s.length()) {
+ setLastIndex(0);
+ RETURN_RESULT(Encode::null());
+ }
+
+ Q_ALLOCA_VAR(uint, matchOffsets, value()->captureCount() * 2 * sizeof(uint));
+ const int result = Scoped<RegExp>(scope, value())->match(s, offset, matchOffsets);
+
+ RegExpCtor *regExpCtor = static_cast<RegExpCtor *>(scope.engine->regExpCtor());
+ regExpCtor->d()->clearLastMatch();
+
+ if (result == -1) {
+ if (global() || sticky())
+ setLastIndex(0);
+ RETURN_RESULT(Encode::null());
+ }
+
+ // fill in result data
+ ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->internalClasses(EngineBase::Class_RegExpExecArray)));
+ int len = value()->captureCount();
+ array->arrayReserve(len);
+ ScopedValue v(scope);
+ int strlen = s.length();
+ for (int i = 0; i < len; ++i) {
+ int start = matchOffsets[i * 2];
+ int end = matchOffsets[i * 2 + 1];
+ if (end > strlen)
+ end = strlen;
+ v = (start != -1) ? scope.engine->memoryManager->alloc<ComplexString>(str->d(), start, end - start)->asReturnedValue() : Encode::undefined();
+ array->arrayPut(i, v);
+ }
+ array->setArrayLengthUnchecked(len);
+ array->setProperty(Index_ArrayIndex, Value::fromInt32(result));
+ array->setProperty(Index_ArrayInput, *str);
+
+ RegExpCtor::Data *dd = regExpCtor->d();
+ dd->lastMatch.set(scope.engine, array);
+ dd->lastInput.set(scope.engine, str->d());
+ dd->lastMatchStart = matchOffsets[0];
+ dd->lastMatchEnd = matchOffsets[1];
+
+ if (global() || sticky())
+ setLastIndex(matchOffsets[1]);
+
+ return array.asReturnedValue();
}
DEFINE_OBJECT_VTABLE(RegExpCtor);
@@ -208,54 +232,102 @@ void Heap::RegExpCtor::init(QV4::ExecutionContext *scope)
void Heap::RegExpCtor::clearLastMatch()
{
- lastMatch.set(internalClass->engine, Primitive::nullValue());
+ lastMatch.set(internalClass->engine, Value::nullValue());
lastInput.set(internalClass->engine, internalClass->engine->id_empty()->d());
lastMatchStart = 0;
lastMatchEnd = 0;
}
-ReturnedValue RegExpCtor::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc)
+static bool isRegExp(ExecutionEngine *e, const Value *arg)
{
- Scope scope(fo->engine());
- ScopedValue r(scope, argc ? argv[0] : Primitive::undefinedValue());
- ScopedValue f(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
- Scoped<RegExpObject> re(scope, r);
- if (re) {
- if (!f->isUndefined())
- return scope.engine->throwTypeError();
-
- Scoped<RegExp> regexp(scope, re->value());
- return Encode(scope.engine->newRegExpObject(regexp));
- }
-
- QString pattern;
- if (!r->isUndefined())
- pattern = r->toQString();
- if (scope.hasException())
- return Encode::undefined();
+ const Object *o = arg->objectValue();
+ if (!o)
+ return false;
+
+ Value isRegExp = Value::fromReturnedValue(o->get(e->symbol_match()));
+ if (!isRegExp.isUndefined())
+ return isRegExp.toBoolean();
+ const RegExpObject *re = o->as<RegExpObject>();
+ return re ? true : false;
+}
- bool global = false;
- bool ignoreCase = false;
- bool multiLine = false;
+uint parseFlags(Scope &scope, const Value *f)
+{
+ uint flags = CompiledData::RegExp::RegExp_NoFlags;
if (!f->isUndefined()) {
ScopedString s(scope, f->toString(scope.engine));
if (scope.hasException())
- return Encode::undefined();
+ return flags;
QString str = s->toQString();
for (int i = 0; i < str.length(); ++i) {
- if (str.at(i) == QLatin1Char('g') && !global) {
- global = true;
- } else if (str.at(i) == QLatin1Char('i') && !ignoreCase) {
- ignoreCase = true;
- } else if (str.at(i) == QLatin1Char('m') && !multiLine) {
- multiLine = true;
+ if (str.at(i) == QLatin1Char('g') && !(flags & CompiledData::RegExp::RegExp_Global)) {
+ flags |= CompiledData::RegExp::RegExp_Global;
+ } else if (str.at(i) == QLatin1Char('i') && !(flags & CompiledData::RegExp::RegExp_IgnoreCase)) {
+ flags |= CompiledData::RegExp::RegExp_IgnoreCase;
+ } else if (str.at(i) == QLatin1Char('m') && !(flags & CompiledData::RegExp::RegExp_Multiline)) {
+ flags |= CompiledData::RegExp::RegExp_Multiline;
+ } else if (str.at(i) == QLatin1Char('u') && !(flags & CompiledData::RegExp::RegExp_Unicode)) {
+ flags |= CompiledData::RegExp::RegExp_Unicode;
+ } else if (str.at(i) == QLatin1Char('y') && !(flags & CompiledData::RegExp::RegExp_Sticky)) {
+ flags |= CompiledData::RegExp::RegExp_Sticky;
} else {
- return scope.engine->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor"));
+ scope.engine->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor"));
+ return flags;
}
}
}
+ return flags;
+}
+
+ReturnedValue RegExpCtor::virtualCallAsConstructor(const FunctionObject *fo, const Value *argv, int argc, const Value *newTarget)
+{
+ Scope scope(fo);
+
+ bool patternIsRegExp = argc ? ::isRegExp(scope.engine, argv) : false;
+
+ if (newTarget == fo) {
+ if (patternIsRegExp && (argc < 2 || argv[1].isUndefined())) {
+ const Object *pattern = static_cast<const Object *>(argv);
+ ScopedValue patternConstructor(scope, pattern->get(scope.engine->id_constructor()));
+ if (patternConstructor->sameValue(*newTarget))
+ return pattern->asReturnedValue();
+ }
+ }
- Scoped<RegExp> regexp(scope, RegExp::create(scope.engine, pattern, ignoreCase, multiLine, global));
+ ScopedValue p(scope, argc ? argv[0] : Value::undefinedValue());
+ ScopedValue f(scope, argc > 1 ? argv[1] : Value::undefinedValue());
+ Scoped<RegExpObject> re(scope, p);
+ QString pattern;
+ uint flags = CompiledData::RegExp::RegExp_NoFlags;
+
+ if (re) {
+ if (f->isUndefined()) {
+ Scoped<RegExp> regexp(scope, re->value());
+ return Encode(scope.engine->newRegExpObject(regexp));
+ }
+ pattern = *re->value()->pattern;
+ flags = parseFlags(scope, f);
+ } else if (patternIsRegExp) {
+ const Object *po = static_cast<const Object *>(argv);
+ p = po->get(scope.engine->id_source());
+ if (!p->isUndefined())
+ pattern = p->toQString();
+ if (scope.hasException())
+ return Encode::undefined();
+ if (f->isUndefined())
+ f = po->get(scope.engine->id_flags());
+ flags = parseFlags(scope, f);
+ } else {
+ if (!p->isUndefined())
+ pattern = p->toQString();
+ if (scope.hasException())
+ return Encode::undefined();
+ flags = parseFlags(scope, f);
+ }
+ if (scope.hasException())
+ return Encode::undefined();
+
+ Scoped<RegExp> regexp(scope, RegExp::create(scope.engine, pattern, flags));
if (!regexp->isValid()) {
return scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression"));
}
@@ -263,14 +335,9 @@ ReturnedValue RegExpCtor::callAsConstructor(const FunctionObject *fo, const Valu
return Encode(scope.engine->newRegExpObject(regexp));
}
-ReturnedValue RegExpCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
+ReturnedValue RegExpCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
- if (argc > 0 && argv[0].as<RegExpObject>()) {
- if (argc == 1 || argv[1].isUndefined())
- return Encode(argv[0]);
- }
-
- return callAsConstructor(f, argv, argc);
+ return virtualCallAsConstructor(f, argv, argc, f);
}
void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor)
@@ -280,7 +347,8 @@ void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor)
ScopedObject ctor(scope, constructor);
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(2));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(2));
+ ctor->addSymbolSpecies();
// Properties deprecated in the spec but required by "the web" :(
ctor->defineAccessorProperty(QStringLiteral("lastMatch"), method_get_lastMatch_n<0>, nullptr);
@@ -304,9 +372,22 @@ void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor)
ctor->defineAccessorProperty(QStringLiteral("$'"), method_get_rightContext, nullptr);
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
+ defineAccessorProperty(scope.engine->id_flags(), method_get_flags, nullptr);
+ defineAccessorProperty(scope.engine->id_global(), method_get_global, nullptr);
+ defineAccessorProperty(scope.engine->id_ignoreCase(), method_get_ignoreCase, nullptr);
defineDefaultProperty(QStringLiteral("exec"), method_exec, 1);
+ defineDefaultProperty(engine->symbol_match(), method_match, 1);
+ defineAccessorProperty(scope.engine->id_multiline(), method_get_multiline, nullptr);
+ defineDefaultProperty(engine->symbol_replace(), method_replace, 2);
+ defineDefaultProperty(engine->symbol_search(), method_search, 1);
+ defineAccessorProperty(scope.engine->id_source(), method_get_source, nullptr);
+ defineDefaultProperty(engine->symbol_split(), method_split, 2);
+ defineAccessorProperty(scope.engine->id_sticky(), method_get_sticky, nullptr);
defineDefaultProperty(QStringLiteral("test"), method_test, 1);
defineDefaultProperty(engine->id_toString(), method_toString, 0);
+ defineAccessorProperty(scope.engine->id_unicode(), method_get_unicode, nullptr);
+
+ // another web extension
defineDefaultProperty(QStringLiteral("compile"), method_compile, 2);
}
@@ -317,7 +398,7 @@ ReturnedValue RegExpPrototype::execFirstMatch(const FunctionObject *b, const Val
Scoped<RegExpObject> r(scope, thisObject->as<RegExpObject>());
Q_ASSERT(r && r->global());
- ScopedString str(scope, argc ? argv[0] : Primitive::undefinedValue());
+ ScopedString str(scope, argc ? argv[0] : Value::undefinedValue());
Q_ASSERT(str);
QString s = str->toQString();
@@ -356,6 +437,23 @@ ReturnedValue RegExpPrototype::execFirstMatch(const FunctionObject *b, const Val
return retVal;
}
+ReturnedValue RegExpPrototype::exec(ExecutionEngine *engine, const Object *o, const String *s)
+{
+ Scope scope(engine);
+ ScopedString key(scope, scope.engine->newString(QStringLiteral("exec")));
+ ScopedFunctionObject exec(scope, o->get(key));
+ if (exec) {
+ ScopedValue result(scope, exec->call(o, s, 1));
+ if (!result->isNull() && !result->isObject())
+ return scope.engine->throwTypeError();
+ return result->asReturnedValue();
+ }
+ Scoped<RegExpObject> re(scope, o);
+ if (!re)
+ return scope.engine->throwTypeError();
+ return re->builtinExec(engine, s);
+}
+
ReturnedValue RegExpPrototype::method_exec(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
@@ -363,54 +461,418 @@ ReturnedValue RegExpPrototype::method_exec(const FunctionObject *b, const Value
if (!r)
return scope.engine->throwTypeError();
- ScopedValue arg(scope, argc ? argv[0]: Primitive::undefinedValue());
+ ScopedValue arg(scope, argc ? argv[0]: Value::undefinedValue());
ScopedString str(scope, arg->toString(scope.engine));
if (scope.hasException())
RETURN_UNDEFINED();
- QString s = str->toQString();
- int offset = r->global() ? r->lastIndex() : 0;
- if (offset < 0 || offset > s.length()) {
- r->setLastIndex(0);
- RETURN_RESULT(Encode::null());
+ return r->builtinExec(scope.engine, str);
+}
+
+ReturnedValue RegExpPrototype::method_get_flags(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ Scope scope(f);
+ ScopedObject o(scope, thisObject);
+ if (!o)
+ return scope.engine->throwTypeError();
+
+ QString result;
+ ScopedValue v(scope);
+ v = o->get(scope.engine->id_global());
+ if (scope.hasException())
+ return Encode::undefined();
+ if (v->toBoolean())
+ result += QLatin1Char('g');
+ v = o->get(scope.engine->id_ignoreCase());
+ if (scope.hasException())
+ return Encode::undefined();
+ if (v->toBoolean())
+ result += QLatin1Char('i');
+ v = o->get(scope.engine->id_multiline());
+ if (scope.hasException())
+ return Encode::undefined();
+ if (v->toBoolean())
+ result += QLatin1Char('m');
+ v = o->get(scope.engine->id_unicode());
+ if (scope.hasException())
+ return Encode::undefined();
+ if (v->toBoolean())
+ result += QLatin1Char('u');
+ v = o->get(scope.engine->id_sticky());
+ if (scope.hasException())
+ return Encode::undefined();
+ if (v->toBoolean())
+ result += QLatin1Char('y');
+ return scope.engine->newString(result)->asReturnedValue();
+}
+
+ReturnedValue RegExpPrototype::method_get_global(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ Scope scope(f);
+ Scoped<RegExpObject> re(scope, thisObject);
+ if (!re) {
+ if (thisObject->sameValue(*scope.engine->regExpPrototype()))
+ return Encode::undefined();
+ return scope.engine->throwTypeError();
}
- Q_ALLOCA_VAR(uint, matchOffsets, r->value()->captureCount() * 2 * sizeof(uint));
- const int result = Scoped<RegExp>(scope, r->value())->match(s, offset, matchOffsets);
+ bool b = re->value()->flags & CompiledData::RegExp::RegExp_Global;
+ return Encode(b);
+}
- RegExpCtor *regExpCtor = static_cast<RegExpCtor *>(scope.engine->regExpCtor());
- regExpCtor->d()->clearLastMatch();
+ReturnedValue RegExpPrototype::method_get_ignoreCase(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ Scope scope(f);
+ Scoped<RegExpObject> re(scope, thisObject);
+ if (!re) {
+ if (thisObject->sameValue(*scope.engine->regExpPrototype()))
+ return Encode::undefined();
+ return scope.engine->throwTypeError();
+ }
- if (result == -1) {
- r->setLastIndex(0);
- RETURN_RESULT(Encode::null());
+ bool b = re->value()->flags & CompiledData::RegExp::RegExp_IgnoreCase;
+ return Encode(b);
+}
+
+static int advanceStringIndex(int index, const QString &str, bool unicode)
+{
+ if (unicode) {
+ if (index < str.length() - 1 &&
+ str.at(index).isHighSurrogate() &&
+ str.at(index + 1).isLowSurrogate())
+ ++index;
}
+ ++index;
+ return index;
+}
- // fill in result data
- ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->internalClasses[EngineBase::Class_RegExpExecArray], scope.engine->arrayPrototype()));
- int len = r->value()->captureCount();
- array->arrayReserve(len);
+static void advanceLastIndexOnEmptyMatch(ExecutionEngine *e, bool unicode, Object *rx, const String *matchString, const QString &str)
+{
+ Scope scope(e);
+ if (matchString->d()->length() == 0) {
+ ScopedValue v(scope, rx->get(scope.engine->id_lastIndex()));
+ int lastIndex = advanceStringIndex(v->toLength(), str, unicode);
+ if (!rx->put(scope.engine->id_lastIndex(), Value::fromInt32(lastIndex)))
+ scope.engine->throwTypeError();
+ }
+}
+
+ReturnedValue RegExpPrototype::method_match(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(f);
+ ScopedObject rx(scope, thisObject);
+ if (!rx)
+ return scope.engine->throwTypeError();
+ ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+ bool global = ScopedValue(scope, rx->get(scope.engine->id_global()))->toBoolean();
+
+ if (!global)
+ return exec(scope.engine, rx, s);
+
+ bool unicode = ScopedValue(scope, rx->get(scope.engine->id_unicode()))->toBoolean();
+
+ rx->put(scope.engine->id_lastIndex(), Value::fromInt32(0));
+ ScopedArrayObject a(scope, scope.engine->newArrayObject());
+ uint n = 0;
+
+ ScopedValue result(scope);
+ ScopedValue match(scope);
+ ScopedString matchString(scope);
ScopedValue v(scope);
- for (int i = 0; i < len; ++i) {
- int start = matchOffsets[i * 2];
- int end = matchOffsets[i * 2 + 1];
- v = (start != -1) ? scope.engine->memoryManager->alloc<ComplexString>(str->d(), start, end - start)->asReturnedValue() : Encode::undefined();
- array->arrayPut(i, v);
+ while (1) {
+ result = exec(scope.engine, rx, s);
+ if (scope.hasException())
+ return Encode::undefined();
+ if (result->isNull()) {
+ if (!n)
+ return Encode::null();
+ return a->asReturnedValue();
+ }
+ Q_ASSERT(result->isObject());
+ match = static_cast<Object &>(*result).get(PropertyKey::fromArrayIndex(0));
+ matchString = match->toString(scope.engine);
+ if (scope.hasException())
+ return Encode::undefined();
+ a->push_back(matchString);
+ advanceLastIndexOnEmptyMatch(scope.engine, unicode, rx, matchString, s->toQString());
+ ++n;
}
- array->setArrayLengthUnchecked(len);
- array->setProperty(Index_ArrayIndex, Primitive::fromInt32(result));
- array->setProperty(Index_ArrayInput, str);
+}
- RegExpCtor::Data *dd = regExpCtor->d();
- dd->lastMatch.set(scope.engine, array);
- dd->lastInput.set(scope.engine, str->d());
- dd->lastMatchStart = matchOffsets[0];
- dd->lastMatchEnd = matchOffsets[1];
+ReturnedValue RegExpPrototype::method_get_multiline(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ Scope scope(f);
+ Scoped<RegExpObject> re(scope, thisObject);
+ if (!re) {
+ if (thisObject->sameValue(*scope.engine->regExpPrototype()))
+ return Encode::undefined();
+ return scope.engine->throwTypeError();
+ }
- if (r->global())
- r->setLastIndex(matchOffsets[1]);
+ bool b = re->value()->flags & CompiledData::RegExp::RegExp_Multiline;
+ return Encode(b);
+}
- return array.asReturnedValue();
+ReturnedValue RegExpPrototype::method_replace(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(f);
+ ScopedObject rx(scope, thisObject);
+ if (!rx)
+ return scope.engine->throwTypeError();
+
+ ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+
+ int lengthS = s->toQString().length();
+
+ ScopedString replaceValue(scope);
+ ScopedFunctionObject replaceFunction(scope, (argc > 1 ? argv[1] : Value::undefinedValue()));
+ bool functionalReplace = !!replaceFunction;
+ if (!functionalReplace)
+ replaceValue = (argc > 1 ? argv[1] : Value::undefinedValue()).toString(scope.engine);
+
+ ScopedValue v(scope);
+ bool global = (v = rx->get(scope.engine->id_global()))->toBoolean();
+ bool unicode = false;
+ if (global) {
+ unicode = (v = rx->get(scope.engine->id_unicode()))->toBoolean();
+ if (!rx->put(scope.engine->id_lastIndex(), Value::fromInt32(0)))
+ return scope.engine->throwTypeError();
+ }
+
+ ScopedArrayObject results(scope, scope.engine->newArrayObject());
+ ScopedValue result(scope);
+ ScopedValue match(scope);
+ ScopedString matchString(scope);
+ while (1) {
+ result = exec(scope.engine, rx, s);
+ if (scope.hasException())
+ return Encode::undefined();
+ if (result->isNull())
+ break;
+ results->push_back(result);
+ if (!global)
+ break;
+ match = static_cast<Object &>(*result).get(PropertyKey::fromArrayIndex(0));
+ matchString = match->toString(scope.engine);
+ if (scope.hasException())
+ return Encode::undefined();
+ advanceLastIndexOnEmptyMatch(scope.engine, unicode, rx, matchString, s->toQString());
+ }
+ QString accumulatedResult;
+ int nextSourcePosition = 0;
+ int resultsLength = results->getLength();
+ ScopedObject resultObject(scope);
+ for (int i = 0; i < resultsLength; ++i) {
+ resultObject = results->get(PropertyKey::fromArrayIndex(i));
+ if (scope.hasException())
+ return Encode::undefined();
+
+ int nCaptures = resultObject->getLength();
+ nCaptures = qMax(nCaptures - 1, 0);
+ match = resultObject->get(PropertyKey::fromArrayIndex(0));
+ matchString = match->toString(scope.engine);
+ if (scope.hasException())
+ return Encode::undefined();
+ QString m = matchString->toQString();
+ int matchLength = m.length();
+ v = resultObject->get(scope.engine->id_index());
+ int position = v->toInt32();
+ position = qMax(qMin(position, lengthS), 0);
+ if (scope.hasException())
+ return Encode::undefined();
+
+ int n = 1;
+ Scope innerScope(scope.engine);
+ JSCallData cData(scope, nCaptures + 3);
+ while (n <= nCaptures) {
+ v = resultObject->get(PropertyKey::fromArrayIndex(n));
+ if (!v->isUndefined())
+ cData->args[n] = v->toString(scope.engine);
+ ++n;
+ }
+ QString replacement;
+ if (functionalReplace) {
+ cData->args[0] = matchString;
+ cData->args[nCaptures + 1] = Encode(position);
+ cData->args[nCaptures + 2] = s;
+ ScopedValue replValue(scope, replaceFunction->call(cData));
+ replacement = replValue->toQString();
+ } else {
+ replacement = RegExp::getSubstitution(matchString->toQString(), s->toQString(), position, cData.args, nCaptures, replaceValue->toQString());
+ }
+ if (scope.hasException())
+ return Encode::undefined();
+ if (position >= nextSourcePosition) {
+ accumulatedResult += s->toQString().midRef(nextSourcePosition, position - nextSourcePosition) + replacement;
+ nextSourcePosition = position + matchLength;
+ }
+ }
+ if (nextSourcePosition < lengthS) {
+ accumulatedResult += s->toQString().midRef(nextSourcePosition);
+ }
+ return scope.engine->newString(accumulatedResult)->asReturnedValue();
+}
+
+ReturnedValue RegExpPrototype::method_search(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(f);
+ ScopedObject rx(scope, thisObject);
+ if (!rx)
+ return scope.engine->throwTypeError();
+
+ ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+
+ ScopedValue previousLastIndex(scope, rx->get(scope.engine->id_lastIndex()));
+ if (previousLastIndex->toNumber() != 0) {
+ if (!rx->put(scope.engine->id_lastIndex(), Value::fromInt32(0)))
+ return scope.engine->throwTypeError();
+ }
+
+ ScopedValue result(scope, exec(scope.engine, rx, s));
+ if (scope.hasException())
+ return Encode::undefined();
+
+ ScopedValue currentLastIndex(scope, rx->get(scope.engine->id_lastIndex()));
+ if (!currentLastIndex->sameValue(previousLastIndex)) {
+ if (!rx->put(scope.engine->id_lastIndex(), previousLastIndex))
+ return scope.engine->throwTypeError();
+ }
+
+ if (result->isNull())
+ return Encode(-1);
+ ScopedObject o(scope, result);
+ Q_ASSERT(o);
+ return o->get(scope.engine->id_index());
+}
+
+
+ReturnedValue RegExpPrototype::method_get_source(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ Scope scope(f);
+ Scoped<RegExpObject> re(scope, thisObject);
+ if (!re) {
+ if (thisObject->sameValue(*scope.engine->regExpPrototype()))
+ return scope.engine->newString(QStringLiteral("(?:)"))->asReturnedValue();
+ return scope.engine->throwTypeError();
+ }
+
+ return scope.engine->newString(re->toString())->asReturnedValue();
+}
+
+ReturnedValue RegExpPrototype::method_split(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(f);
+ ScopedObject rx(scope, thisObject);
+ if (!rx)
+ return scope.engine->throwTypeError();
+
+ ScopedString s(scope, (argc ? argv[0] : Value::undefinedValue()).toString(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+
+ ScopedValue flagsValue(scope, rx->get(scope.engine->id_flags()));
+ ScopedString flags(scope, flagsValue->toString(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+ QString flagsString = flags->toQString();
+ if (!flagsString.contains(QLatin1Char('y')))
+ flags = scope.engine->newString(flagsString + QLatin1Char('y'));
+ bool unicodeMatching = flagsString.contains(QLatin1Char('u'));
+
+ const FunctionObject *C = rx->speciesConstructor(scope, scope.engine->regExpCtor());
+ if (!C)
+ return Encode::undefined();
+
+ Value *args = scope.alloc(2);
+ args[0] = rx;
+ args[1] = flags;
+ ScopedObject splitter(scope, C->callAsConstructor(args, 2, f));
+ if (scope.hasException())
+ return Encode::undefined();
+
+ ScopedArrayObject A(scope, scope.engine->newArrayObject());
+ uint lengthA = 0;
+ uint limit = argc < 2 ? UINT_MAX : argv[1].toUInt32();
+ if (limit == 0)
+ return A->asReturnedValue();
+
+ QString S = s->toQString();
+ int size = S.length();
+ if (size == 0) {
+ ScopedValue z(scope, exec(scope.engine, splitter, s));
+ if (z->isNull())
+ A->push_back(s);
+ return A->asReturnedValue();
+ }
+
+ int p = 0;
+ int q = 0;
+ ScopedValue v(scope);
+ ScopedValue z(scope);
+ ScopedObject zz(scope);
+ ScopedString t(scope);
+ while (q < size) {
+ Value qq = Value::fromInt32(q);
+ if (!splitter->put(scope.engine->id_lastIndex(), qq))
+ return scope.engine->throwTypeError();
+ z = exec(scope.engine, splitter, s);
+ if (scope.hasException())
+ return Encode::undefined();
+
+ if (z->isNull()) {
+ q = advanceStringIndex(q, S, unicodeMatching);
+ continue;
+ }
+
+ v = splitter->get(scope.engine->id_lastIndex());
+ int e = qMin(v->toInt32(), size);
+ if (e == p) {
+ q = advanceStringIndex(q, S, unicodeMatching);
+ continue;
+ }
+ QString T = S.mid(p, q - p);
+ t = scope.engine->newString(T);
+ A->push_back(t);
+ ++lengthA;
+ if (lengthA == limit)
+ return A->asReturnedValue();
+ p = e;
+ zz = *z;
+ uint numberOfCaptures = qMax(zz->getLength() - 1, 0ll);
+ for (uint i = 1; i <= numberOfCaptures; ++i) {
+ v = zz->get(PropertyKey::fromArrayIndex(i));
+ A->push_back(v);
+ ++lengthA;
+ if (lengthA == limit)
+ return A->asReturnedValue();
+ }
+ q = p;
+ }
+
+ QString T = S.mid(p);
+ t = scope.engine->newString(T);
+ A->push_back(t);
+ return A->asReturnedValue();
+}
+
+ReturnedValue RegExpPrototype::method_get_sticky(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ Scope scope(f);
+ Scoped<RegExpObject> re(scope, thisObject);
+ if (!re) {
+ if (thisObject->sameValue(*scope.engine->regExpPrototype()))
+ return Encode::undefined();
+ return scope.engine->throwTypeError();
+ }
+
+ bool b = re->value()->flags & CompiledData::RegExp::RegExp_Sticky;
+ return Encode(b);
}
ReturnedValue RegExpPrototype::method_test(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
@@ -421,12 +883,37 @@ ReturnedValue RegExpPrototype::method_test(const FunctionObject *b, const Value
ReturnedValue RegExpPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
- ExecutionEngine *v4 = b->engine();
- const RegExpObject *r = thisObject->as<RegExpObject>();
+ Scope scope(b);
+ const Object *r = thisObject->as<Object>();
if (!r)
- return v4->throwTypeError();
+ return scope.engine->throwTypeError();
+
+ ScopedValue v(scope);
+ v = r->get(scope.engine->id_source());
+ ScopedString source(scope, v->toString(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+ v = r->get(scope.engine->id_flags());
+ ScopedString flags(scope, v->toString(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+
+ QString result = QLatin1Char('/') + source->toQString() + QLatin1Char('/') + flags->toQString();
+ return Encode(scope.engine->newString(result));
+}
+
+ReturnedValue RegExpPrototype::method_get_unicode(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ Scope scope(f);
+ Scoped<RegExpObject> re(scope, thisObject);
+ if (!re) {
+ if (thisObject->sameValue(*scope.engine->regExpPrototype()))
+ return Encode::undefined();
+ return scope.engine->throwTypeError();
+ }
- return Encode(v4->newString(r->toString()));
+ bool b = re->value()->flags & CompiledData::RegExp::RegExp_Unicode;
+ return Encode(b);
}
ReturnedValue RegExpPrototype::method_compile(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
@@ -442,12 +929,12 @@ ReturnedValue RegExpPrototype::method_compile(const FunctionObject *b, const Val
return Encode::undefined();
}
-template <int index>
+template <uint index>
ReturnedValue RegExpPrototype::method_get_lastMatch_n(const FunctionObject *b, const Value *, const Value *, int)
{
Scope scope(b);
ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastMatch());
- ScopedValue res(scope, lastMatch ? lastMatch->getIndexed(index) : Encode::undefined());
+ ScopedValue res(scope, lastMatch ? lastMatch->get(index) : Encode::undefined());
if (res->isUndefined())
res = scope.engine->newString();
return res->asReturnedValue();
@@ -457,7 +944,7 @@ ReturnedValue RegExpPrototype::method_get_lastParen(const FunctionObject *b, con
{
Scope scope(b);
ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastMatch());
- ScopedValue res(scope, lastMatch ? lastMatch->getIndexed(lastMatch->getLength() - 1) : Encode::undefined());
+ ScopedValue res(scope, lastMatch ? lastMatch->get(lastMatch->getLength() - 1) : Encode::undefined());
if (res->isUndefined())
res = scope.engine->newString();
return res->asReturnedValue();
diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h
index 181628241b..a584404c0b 100644
--- a/src/qml/jsruntime/qv4regexpobject_p.h
+++ b/src/qml/jsruntime/qv4regexpobject_p.h
@@ -76,7 +76,7 @@ namespace Heap {
Member(class, Pointer, RegExp *, value)
DECLARE_HEAP_OBJECT(RegExpObject, Object) {
- DECLARE_MARKOBJECTS(RegExpObject);
+ DECLARE_MARKOBJECTS(RegExpObject)
void init();
void init(QV4::RegExp *value);
@@ -108,39 +108,46 @@ struct RegExpObject: Object {
enum Flags {
RegExp_Global = 0x01,
RegExp_IgnoreCase = 0x02,
- RegExp_Multiline = 0x04
+ RegExp_Multiline = 0x04,
+ RegExp_Unicode = 0x08,
+ RegExp_Sticky = 0x10
};
enum {
Index_LastIndex = 0,
- Index_Source = 1,
- Index_Global = 2,
- Index_IgnoreCase = 3,
- Index_Multiline = 4,
Index_ArrayIndex = Heap::ArrayObject::LengthPropertyIndex + 1,
Index_ArrayInput = Index_ArrayIndex + 1
};
enum { NInlineProperties = 5 };
- Heap::RegExp *value() const { return d()->value; }
- bool global() const { return d()->value->global; }
void initProperties();
int lastIndex() const {
- Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex()));
+ Q_ASSERT(internalClass()->verifyIndex(engine()->id_lastIndex()->propertyKey(), Index_LastIndex));
return propertyData(Index_LastIndex)->toInt32();
}
void setLastIndex(int index) {
- Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex()));
- return setProperty(Index_LastIndex, Primitive::fromInt32(index));
+ Q_ASSERT(internalClass()->verifyIndex(engine()->id_lastIndex()->propertyKey(), Index_LastIndex));
+ if (!internalClass()->propertyData[Index_LastIndex].isWritable()) {
+ engine()->throwTypeError();
+ return;
+ }
+ return setProperty(Index_LastIndex, Value::fromInt32(index));
}
QRegExp toQRegExp() const;
QString toString() const;
QString source() const;
- uint flags() const;
+
+ Heap::RegExp *value() const { return d()->value; }
+ uint flags() const { return d()->value->flags; }
+ bool global() const { return d()->value->global(); }
+ bool sticky() const { return d()->value->sticky(); }
+ bool unicode() const { return d()->value->unicode(); }
+
+ ReturnedValue builtinExec(ExecutionEngine *engine, const String *s);
};
struct RegExpCtor: FunctionObject
@@ -152,20 +159,34 @@ struct RegExpCtor: FunctionObject
int lastMatchStart() { return d()->lastMatchStart; }
int lastMatchEnd() { return d()->lastMatchEnd; }
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
-struct RegExpPrototype: RegExpObject
+struct RegExpPrototype: Object
{
void init(ExecutionEngine *engine, Object *ctor);
static ReturnedValue method_exec(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_flags(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_global(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_ignoreCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_match(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_multiline(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_replace(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_search(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_source(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_split(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_sticky(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_test(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_unicode(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ // Web extension
static ReturnedValue method_compile(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- template <int index>
+ // properties on the constructor, web extensions
+ template <uint index>
static ReturnedValue method_get_lastMatch_n(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_lastParen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_input(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
@@ -173,6 +194,8 @@ struct RegExpPrototype: RegExpObject
static ReturnedValue method_get_rightContext(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue execFirstMatch(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue exec(ExecutionEngine *engine, const Object *o, const String *s);
};
}
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 9729228511..66cd06ee1f 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -61,6 +61,8 @@
#include <private/qqmlengine_p.h>
#include <private/qqmljavascriptexpression_p.h>
#include "qv4qobjectwrapper_p.h"
+#include "qv4symbol_p.h"
+#include "qv4generatorobject_p.h"
#include <private/qv8engine_p.h>
#endif
@@ -285,7 +287,7 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix)
}
double frac = num - ::floor(num);
- num = Primitive::toInteger(num);
+ num = Value::toInteger(num);
do {
char c = (char)::fmod(num, radix);
@@ -314,37 +316,23 @@ ReturnedValue Runtime::method_closure(ExecutionEngine *engine, int functionId)
QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeFunctions[functionId];
Q_ASSERT(clos);
ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context);
+ if (clos->isGenerator())
+ return GeneratorFunction::create(current, clos)->asReturnedValue();
return FunctionObject::createScriptFunction(current, clos)->asReturnedValue();
}
-bool Runtime::method_deleteElement(ExecutionEngine *engine, const Value &base, const Value &index)
+bool Runtime::method_deleteProperty(ExecutionEngine *engine, const Value &base, const Value &index)
{
Scope scope(engine);
- ScopedObject o(scope, base);
- if (o) {
- uint n = index.asArrayIndex();
- if (n < UINT_MAX)
- return o->deleteIndexedProperty(n);
- }
-
- ScopedString name(scope, index.toString(engine));
- return method_deleteMemberString(engine, base, name);
-}
-
-bool Runtime::method_deleteMember(ExecutionEngine *engine, const Value &base, int nameIndex)
-{
- Scope scope(engine);
- ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- return method_deleteMemberString(engine, base, name);
-}
-
-bool Runtime::method_deleteMemberString(ExecutionEngine *engine, const Value &base, String *name)
-{
- Scope scope(engine);
- ScopedObject obj(scope, base.toObject(engine));
+ ScopedObject o(scope, base.toObject(engine));
if (scope.engine->hasException)
return Encode::undefined();
- return obj->deleteProperty(name);
+ Q_ASSERT(o);
+
+ ScopedPropertyKey key(scope, index.toPropertyKey(engine));
+ if (engine->hasException)
+ return false;
+ return o->deleteProperty(key);
}
bool Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex)
@@ -361,8 +349,22 @@ QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Val
if (!rhs)
return engine->throwTypeError();
- // 11.8.6, 7: call "HasInstance", which we term instanceOf, and return the result.
- return rhs->instanceOf(lval);
+ const FunctionObject *f = rhs->as<FunctionObject>();
+ // shortcut hasInstance evaluation. In this case we know that we are calling the regular hasInstance()
+ // method of the FunctionPrototype
+ if (f && f->d()->prototype() == engine->functionPrototype()->d() && !f->hasHasInstanceProperty())
+ return Object::checkedInstanceOf(engine, f, lval);
+
+ Scope scope(engine);
+ ScopedValue hasInstance(scope, rhs->get(engine->symbol_hasInstance()));
+ if (hasInstance->isUndefined())
+ return rhs->instanceOf(lval);
+ FunctionObject *fHasInstance = hasInstance->as<FunctionObject>();
+ if (!fHasInstance)
+ return engine->throwTypeError();
+
+ ScopedValue result(scope, fHasInstance->call(&rval, &lval, 1));
+ return Encode(result->toBoolean());
}
QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left, const Value &right)
@@ -371,7 +373,7 @@ QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left
if (!ro)
return engine->throwTypeError();
Scope scope(engine);
- ScopedString s(scope, left.toString(engine));
+ ScopedPropertyKey s(scope, left.toPropertyKey(engine));
if (scope.hasException())
return Encode::undefined();
bool r = ro->hasProperty(s);
@@ -381,9 +383,24 @@ QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left
double RuntimeHelpers::stringToNumber(const QString &string)
{
const QStringRef s = QStringRef(&string).trimmed();
- if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X")))
- return s.toLong(nullptr, 16);
- bool ok;
+ if (s.startsWith(QLatin1Char('0'))) {
+ int base = -1;
+ if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X")))
+ base = 16;
+ else if (s.startsWith(QLatin1String("0o")) || s.startsWith(QLatin1String("0O")))
+ base = 8;
+ else if (s.startsWith(QLatin1String("0b")) || s.startsWith(QLatin1String("0B")))
+ base = 2;
+ if (base > 0) {
+ bool ok = true;
+ qlonglong num;
+ num = s.mid(2).toLongLong(&ok, base);
+ if (!ok)
+ return qQNaN();
+ return num;
+ }
+ }
+ bool ok = false;
QByteArray ba = s.toLatin1();
const char *begin = ba.constData();
const char *end = nullptr;
@@ -408,27 +425,59 @@ Heap::String *RuntimeHelpers::stringFromNumber(ExecutionEngine *engine, double n
ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeHint)
{
- if (typeHint == PREFERREDTYPE_HINT) {
- if (object->as<DateObject>())
- typeHint = STRING_HINT;
- else
- typeHint = NUMBER_HINT;
+ ExecutionEngine *engine = object->internalClass()->engine;
+ if (engine->hasException)
+ return Encode::undefined();
+
+ String *hint;
+ switch (typeHint) {
+ case STRING_HINT:
+ hint = engine->id_string();
+ break;
+ case NUMBER_HINT:
+ hint = engine->id_number();
+ break;
+ default:
+ hint = engine->id_default();
+ break;
}
- ExecutionEngine *engine = object->internalClass()->engine;
+ Scope scope(engine);
+ ScopedFunctionObject toPrimitive(scope, object->get(engine->symbol_toPrimitive()));
if (engine->hasException)
return Encode::undefined();
+ if (toPrimitive) {
+ ScopedValue result(scope, toPrimitive->call(object, hint, 1));
+ if (engine->hasException)
+ return Encode::undefined();
+ if (!result->isPrimitive())
+ return engine->throwTypeError();
+ return result->asReturnedValue();
+ }
+
+ if (hint == engine->id_default())
+ hint = engine->id_number();
+ return ordinaryToPrimitive(engine, object, hint);
+}
+
+
+ReturnedValue RuntimeHelpers::ordinaryToPrimitive(ExecutionEngine *engine, const Object *object, String *typeHint)
+{
+ Q_ASSERT(!engine->hasException);
String *meth1 = engine->id_toString();
String *meth2 = engine->id_valueOf();
- if (typeHint == NUMBER_HINT)
+ if (typeHint->propertyKey() == engine->id_number()->propertyKey()) {
qSwap(meth1, meth2);
+ } else {
+ Q_ASSERT(typeHint->propertyKey() == engine->id_string()->propertyKey());
+ }
Scope scope(engine);
ScopedValue result(scope);
- ScopedValue conv(scope, object->get(meth1));
+ ScopedValue conv(scope, object->get(meth1));
if (FunctionObject *o = conv->as<FunctionObject>()) {
result = o->call(object, nullptr, 0);
if (result->isPrimitive())
@@ -449,19 +498,22 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeH
}
-
Heap::Object *RuntimeHelpers::convertToObject(ExecutionEngine *engine, const Value &value)
{
Q_ASSERT(!value.isObject());
switch (value.type()) {
case Value::Undefined_Type:
+ engine->throwTypeError(QLatin1String("Value is undefined and could not be converted to an object"));
+ return nullptr;
case Value::Null_Type:
- engine->throwTypeError();
+ engine->throwTypeError(QLatin1String("Value is null and could not be converted to an object"));
return nullptr;
case Value::Boolean_Type:
return engine->newBooleanObject(value.booleanValue());
case Value::Managed_Type:
- Q_ASSERT(value.isString());
+ Q_ASSERT(value.isStringOrSymbol());
+ if (!value.isString())
+ return engine->newSymbolObject(value.symbolValue());
return engine->newStringObject(value.stringValue());
case Value::Integer_Type:
default: // double
@@ -488,7 +540,11 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, Value val
case Value::Managed_Type: {
if (value.isString())
return static_cast<const String &>(value).d();
- value = Primitive::fromReturnedValue(RuntimeHelpers::toPrimitive(value, hint));
+ if (value.isSymbol()) {
+ engine->throwTypeError(QLatin1String("Cannot convert a symbol to a string."));
+ return nullptr;
+ }
+ value = Value::fromReturnedValue(RuntimeHelpers::toPrimitive(value, hint));
Q_ASSERT(value.isPrimitive());
if (value.isString())
return static_cast<const String &>(value).d();
@@ -539,14 +595,21 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu
return Encode(x + y);
}
-bool Runtime::method_storeProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)
+void Runtime::method_storeProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)
{
Scope scope(engine);
- ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- ScopedObject o(scope, object.toObject(engine));
- if (!o)
- return false;
- return o->put(name, value);
+ QV4::Function *v4Function = engine->currentStackFrame->v4Function;
+ ScopedString name(scope, v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ ScopedObject o(scope, object);
+ if (!o) {
+ if (v4Function->isStrict()) {
+ engine->throwTypeError();
+ return;
+ }
+ o = object.toObject(engine);
+ }
+ if ((!o || !o->put(name, value)) && v4Function->isStrict())
+ engine->throwTypeError();
}
static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engine, const Value &object, uint idx)
@@ -579,12 +642,13 @@ static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engin
return v->asReturnedValue();
}
- return o->getIndexed(idx);
+ return o->get(idx);
}
static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine, const Value &object, const Value &index)
{
- Q_ASSERT(index.asArrayIndex() == UINT_MAX);
+ Q_ASSERT(!index.isPositiveInt());
+
Scope scope(engine);
ScopedObject o(scope, object);
@@ -598,26 +662,18 @@ static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine,
Q_ASSERT(!!o); // can't fail as null/undefined is covered above
}
- ScopedString name(scope, index.toString(engine));
+ ScopedPropertyKey name(scope, index.toPropertyKey(engine));
if (scope.hasException())
return Encode::undefined();
return o->get(name);
}
-/* load element:
-
- Managed *m = object.heapObject();
- if (m)
- return m->internalClass->getIndexed(m, index);
- return getIndexedFallback(object, index);
-*/
-
ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value &object, const Value &index)
{
- uint idx = 0;
- if (index.asArrayIndex(idx)) {
+ if (index.isPositiveInt()) {
+ uint idx = static_cast<uint>(index.int_32());
if (Heap::Base *b = object.heapObject()) {
- if (b->vtable()->isObject) {
+ if (b->internalClass->vtable->isObject) {
Heap::Object *o = static_cast<Heap::Object *>(b);
if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>();
@@ -636,12 +692,20 @@ ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value &
static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
{
Scope scope(engine);
- ScopedObject o(scope, object.toObject(engine));
+ ScopedObject o(scope, object);
+ if (!o) {
+ if (engine->currentStackFrame->v4Function->isStrict()) {
+ engine->throwTypeError();
+ return false;
+ }
+
+ o = object.toObject(engine);
+ }
if (engine->hasException)
return false;
- uint idx = 0;
- if (index.asArrayIndex(idx)) {
+ if (index.isPositiveInt()) {
+ uint idx = static_cast<uint>(index.int_32());
if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) {
Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>();
if (idx < s->values.size) {
@@ -649,53 +713,229 @@ static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Val
return true;
}
}
- return o->putIndexed(idx, value);
+ return o->put(idx, value);
}
- ScopedString name(scope, index.toString(engine));
+ ScopedPropertyKey name(scope, index.toPropertyKey(engine));
+ if (engine->hasException)
+ return false;
return o->put(name, value);
}
-bool Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
+void Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
{
- uint idx = 0;
- if (index.asArrayIndex(idx)) {
+ if (index.isPositiveInt()) {
+ uint idx = static_cast<uint>(index.int_32());
if (Heap::Base *b = object.heapObject()) {
- if (b->vtable()->isObject) {
+ if (b->internalClass->vtable->isObject) {
Heap::Object *o = static_cast<Heap::Object *>(b);
if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>();
if (idx < s->values.size) {
s->setData(engine, idx, value);
- return true;
+ return;
}
}
}
}
}
- return setElementFallback(engine, object, index, value);
+ if (!setElementFallback(engine, object, index, value) && engine->currentStackFrame->v4Function->isStrict())
+ engine->throwTypeError();
}
-ReturnedValue Runtime::method_foreachIterator(ExecutionEngine *engine, const Value &in)
+ReturnedValue Runtime::method_getIterator(ExecutionEngine *engine, const Value &in, int iterator)
{
Scope scope(engine);
ScopedObject o(scope, (Object *)nullptr);
if (!in.isNullOrUndefined())
o = in.toObject(engine);
- return engine->newForEachIteratorObject(o)->asReturnedValue();
+ if (engine->hasException)
+ return Encode::undefined();
+ if (iterator == static_cast<int>(QQmlJS::AST::ForEachType::Of)) {
+ if (!o)
+ return engine->throwTypeError();
+ ScopedFunctionObject f(scope, o->get(engine->symbol_iterator()));
+ if (!f)
+ return engine->throwTypeError();
+ JSCallData cData(scope, 0, nullptr, o);
+ ScopedObject it(scope, f->call(cData));
+ if (!it)
+ return engine->throwTypeError();
+ return it->asReturnedValue();
+ }
+ return engine->newForInIteratorObject(o)->asReturnedValue();
}
-ReturnedValue Runtime::method_foreachNextPropertyName(const Value &foreach_iterator)
+ReturnedValue Runtime::method_iteratorNext(ExecutionEngine *engine, const Value &iterator, Value *value)
{
- Q_ASSERT(foreach_iterator.isObject());
+ // if we throw an exception from here, return true, not undefined. This is to ensure iteratorDone is set to true
+ // and the stack unwinding won't close the iterator
+ Q_ASSERT(iterator.isObject());
- ForEachIteratorObject *it = static_cast<ForEachIteratorObject *>(foreach_iterator.objectValue());
- Q_ASSERT(it->as<ForEachIteratorObject>());
+ Scope scope(engine);
+ ScopedFunctionObject f(scope, static_cast<const Object &>(iterator).get(engine->id_next()));
+ if (!f) {
+ engine->throwTypeError();
+ return Encode(true);
+ }
+ JSCallData cData(scope, 0, nullptr, &iterator);
+ ScopedObject o(scope, f->call(cData));
+ if (scope.hasException())
+ return Encode(true);
+ if (!o) {
+ engine->throwTypeError();
+ return Encode(true);
+ }
+
+ ScopedValue d(scope, o->get(engine->id_done()));
+ if (scope.hasException())
+ return Encode(true);
+ bool done = d->toBoolean();
+ if (done) {
+ *value = Encode::undefined();
+ return Encode(true);
+ }
+
+ *value = o->get(engine->id_value());
+ if (scope.hasException())
+ return Encode(true);
+ return Encode(false);
+}
+
+ReturnedValue Runtime::method_iteratorNextForYieldStar(ExecutionEngine *engine, const Value &received, const Value &iterator, Value *object)
+{
+ // the return value encodes how to continue the yield* iteration.
+ // true implies iteration is done, false for iteration to continue
+ // a return value of undefines is a special marker, that the iterator has been invoked with return()
+
+ Scope scope(engine);
+ Q_ASSERT(iterator.isObject());
+
+ const Value *arg = &received;
+ bool returnCalled = false;
+ FunctionObject *f = nullptr;
+ if (engine->hasException) {
+ if (engine->exceptionValue->isEmpty()) {
+ // generator called with return()
+ *engine->exceptionValue = Encode::undefined();
+ engine->hasException = false;
+
+ ScopedValue ret(scope, static_cast<const Object &>(iterator).get(engine->id_return()));
+ if (ret->isUndefined()) {
+ // propagate return()
+ return Encode::undefined();
+ }
+ returnCalled = true;
+ f = ret->as<FunctionObject>();
+ } else {
+ // generator called with throw
+ ScopedValue exceptionValue(scope, *engine->exceptionValue);
+ *engine->exceptionValue = Encode::undefined();
+ engine->hasException = false;
+
+ ScopedValue t(scope, static_cast<const Object &>(iterator).get(engine->id_throw()));
+ if (engine->hasException)
+ return Encode::undefined();
+ if (t->isUndefined()) {
+ // no throw method on the iterator
+ ScopedValue done(scope, Encode(false));
+ method_iteratorClose(engine, iterator, done);
+ if (engine->hasException)
+ return Encode::undefined();
+ return engine->throwTypeError();
+ }
+ f = t->as<FunctionObject>();
+ arg = exceptionValue;
+ }
+ } else {
+ // generator called with next()
+ ScopedFunctionObject next(scope, static_cast<const Object &>(iterator).get(engine->id_next()));
+ f = next->as<FunctionObject>();
+ }
+
+ if (!f)
+ return engine->throwTypeError();
+
+ ScopedObject o(scope, f->call(&iterator, arg, 1));
+ if (scope.hasException())
+ return Encode(true);
+ if (!o)
+ return engine->throwTypeError();
+
+ ScopedValue d(scope, o->get(engine->id_done()));
+ if (scope.hasException())
+ return Encode(true);
+ bool done = d->toBoolean();
+ if (done) {
+ *object = o->get(engine->id_value());
+ return returnCalled ? Encode::undefined() : Encode(true);
+ }
+ *object = o;
+ return Encode(false);
+}
+
+ReturnedValue Runtime::method_iteratorClose(ExecutionEngine *engine, const Value &iterator, const Value &done)
+{
+ Q_ASSERT(iterator.isObject());
+ Q_ASSERT(done.isBoolean());
+ if (done.booleanValue())
+ return Encode::undefined();
- return it->nextPropertyName();
+ Scope scope(engine);
+ ScopedValue e(scope);
+ bool hadException = engine->hasException;
+ if (hadException) {
+ e = *engine->exceptionValue;
+ engine->hasException = false;
+ }
+
+ auto originalCompletion = [=]() {
+ if (hadException) {
+ *engine->exceptionValue = e;
+ engine->hasException = hadException;
+ }
+ return Encode::undefined();
+ };
+
+ ScopedValue ret(scope, static_cast<const Object &>(iterator).get(engine->id_return()));
+ ScopedObject o(scope);
+ if (!ret->isUndefined()) {
+ FunctionObject *f = ret->as<FunctionObject>();
+ o = f->call(&iterator, nullptr, 0);
+ if (engine->hasException && !hadException)
+ return Encode::undefined();
+ }
+ if (hadException || ret->isUndefined())
+ return originalCompletion();
+
+ if (!o)
+ return engine->throwTypeError();
+
+ return originalCompletion();
}
+ReturnedValue Runtime::method_destructureRestElement(ExecutionEngine *engine, const Value &iterator)
+{
+ Q_ASSERT(iterator.isObject());
+
+ Scope scope(engine);
+ ScopedArrayObject array(scope, engine->newArrayObject());
+ array->arrayCreate();
+ uint index = 0;
+ while (1) {
+ ScopedValue n(scope);
+ ScopedValue done(scope, method_iteratorNext(engine, iterator, n));
+ if (engine->hasException)
+ return Encode::undefined();
+ Q_ASSERT(done->isBoolean());
+ if (done->booleanValue())
+ break;
+ array->arraySet(index, n);
+ ++index;
+ }
+ return array->asReturnedValue();
+}
void Runtime::method_storeNameSloppy(ExecutionEngine *engine, int nameIndex, const Value &value)
{
@@ -745,6 +985,82 @@ ReturnedValue Runtime::method_loadName(ExecutionEngine *engine, int nameIndex)
return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name);
}
+static Object *getSuperBase(Scope &scope)
+{
+ if (scope.engine->currentStackFrame->jsFrame->thisObject.isEmpty()) {
+ scope.engine->throwReferenceError(QStringLiteral("Missing call to super()."), QString(), 0, 0);
+ return nullptr;
+ }
+
+ ScopedFunctionObject f(scope, scope.engine->currentStackFrame->jsFrame->function);
+ ScopedObject homeObject(scope, f->getHomeObject());
+ if (!homeObject) {
+ ScopedContext ctx(scope, static_cast<ExecutionContext *>(&scope.engine->currentStackFrame->jsFrame->context));
+ Q_ASSERT(ctx);
+ while (ctx) {
+ if (CallContext *c = ctx->asCallContext()) {
+ f = c->d()->function;
+ QV4::Function *fn = f->function();
+ if (fn && !fn->isArrowFunction() && !fn->isEval)
+ break;
+ }
+ ctx = ctx->d()->outer;
+ }
+ homeObject = f->getHomeObject();
+ }
+ if (!homeObject) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+ Q_ASSERT(homeObject);
+ ScopedObject proto(scope, homeObject->getPrototypeOf());
+ if (!proto) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+ return proto;
+}
+
+ReturnedValue Runtime::method_loadSuperProperty(ExecutionEngine *engine, const Value &property)
+{
+ Scope scope(engine);
+ Object *base = getSuperBase(scope);
+ if (!base)
+ return Encode::undefined();
+ ScopedPropertyKey key(scope, property.toPropertyKey(engine));
+ if (engine->hasException)
+ return Encode::undefined();
+ return base->get(key, &engine->currentStackFrame->jsFrame->thisObject);
+}
+
+void Runtime::method_storeSuperProperty(ExecutionEngine *engine, const Value &property, const Value &value)
+{
+ Scope scope(engine);
+ Object *base = getSuperBase(scope);
+ if (!base)
+ return;
+ ScopedPropertyKey key(scope, property.toPropertyKey(engine));
+ if (engine->hasException)
+ return;
+ bool result = base->put(key, value, &engine->currentStackFrame->jsFrame->thisObject);
+ if (!result && engine->currentStackFrame->v4Function->isStrict())
+ engine->throwTypeError();
+}
+
+ReturnedValue Runtime::method_loadSuperConstructor(ExecutionEngine *engine, const Value &t)
+{
+ if (engine->currentStackFrame->thisObject() != Value::emptyValue().asReturnedValue()) {
+ return engine->throwReferenceError(QStringLiteral("super() already called."), QString(), 0, 0); // ### fix line number
+ }
+ const FunctionObject *f = t.as<FunctionObject>();
+ if (!f)
+ return engine->throwTypeError();
+ Heap::Object *c = static_cast<const Object &>(t).getPrototypeOf();
+ if (!c->vtable()->isFunctionObject || !static_cast<Heap::FunctionObject *>(c)->isConstructor())
+ return engine->throwTypeError();
+ return c->asReturnedValue();
+}
+
#endif // V4_BOOTSTRAP
uint RuntimeHelpers::equalHelper(const Value &x, const Value &y)
@@ -764,9 +1080,9 @@ uint RuntimeHelpers::equalHelper(const Value &x, const Value &y)
double dx = RuntimeHelpers::toNumber(x);
return dx == y.asDouble();
} else if (x.isBoolean()) {
- return Runtime::method_compareEqual(Primitive::fromDouble((double) x.booleanValue()), y);
+ return Runtime::method_compareEqual(Value::fromDouble((double) x.booleanValue()), y);
} else if (y.isBoolean()) {
- return Runtime::method_compareEqual(x, Primitive::fromDouble((double) y.booleanValue()));
+ return Runtime::method_compareEqual(x, Value::fromDouble((double) y.booleanValue()));
} else {
#ifdef V4_BOOTSTRAP
Q_UNIMPLEMENTED();
@@ -817,7 +1133,7 @@ QV4::Bool Runtime::method_compareGreaterThan(const Value &l, const Value &r)
Q_UNIMPLEMENTED();
return false;
#else
- return sr->compare(sl);
+ return sr->lessThan(sl);
#endif
}
@@ -854,7 +1170,7 @@ QV4::Bool Runtime::method_compareLessThan(const Value &l, const Value &r)
Q_UNIMPLEMENTED();
return false;
#else
- return sl->compare(sr);
+ return sl->lessThan(sr);
#endif
}
@@ -891,7 +1207,7 @@ QV4::Bool Runtime::method_compareGreaterEqual(const Value &l, const Value &r)
Q_UNIMPLEMENTED();
return false;
#else
- return !sl->compare(sr);
+ return !sl->lessThan(sr);
#endif
}
@@ -928,7 +1244,7 @@ QV4::Bool Runtime::method_compareLessEqual(const Value &l, const Value &r)
Q_UNIMPLEMENTED();
return false;
#else
- return !sr->compare(sl);
+ return !sr->lessThan(sl);
#endif
}
@@ -978,7 +1294,7 @@ ReturnedValue Runtime::method_callGlobalLookup(ExecutionEngine *engine, uint ind
if (!function.isFunctionObject())
return engine->throwTypeError();
- Value thisObject = Primitive::undefinedValue();
+ Value thisObject = Value::undefinedValue();
return static_cast<FunctionObject &>(function).call(&thisObject, argv, argc);
}
@@ -1033,24 +1349,31 @@ ReturnedValue Runtime::method_callName(ExecutionEngine *engine, int nameIndex, V
ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc)
{
Scope scope(engine);
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ ScopedObject lookupObject(scope, base);
- if (!base->isObject()) {
+ if (!lookupObject) {
Q_ASSERT(!base->isEmpty());
if (base->isNullOrUndefined()) {
QString message = QStringLiteral("Cannot call method '%1' of %2")
- .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(),
- base->toQStringNoThrow());
+ .arg(name->toQString(), base->toQStringNoThrow());
return engine->throwTypeError(message);
}
- ScopedValue thisObject(scope, RuntimeHelpers::convertToObject(engine, *base));
- if (engine->hasException) // type error
- return Encode::undefined();
- base = thisObject;
+ if (base->isManaged()) {
+ Managed *m = static_cast<Managed *>(base);
+ lookupObject = m->internalClass()->prototype;
+ Q_ASSERT(m->internalClass()->prototype);
+ } else {
+ lookupObject = RuntimeHelpers::convertToObject(engine, *base);
+ if (engine->hasException) // type error
+ return Encode::undefined();
+ if (!engine->currentStackFrame->v4Function->isStrict())
+ base = lookupObject;
+ }
}
- ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- ScopedFunctionObject f(scope, static_cast<Object *>(base)->get(name));
+ ScopedFunctionObject f(scope, static_cast<Object *>(lookupObject)->get(name));
if (!f) {
QString error = QStringLiteral("Property '%1' of object %2 is not a function")
@@ -1080,7 +1403,7 @@ ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, Value *base,
ScopedValue thisObject(scope, base->toObject(engine));
base = thisObject;
- ScopedString str(scope, index.toString(engine));
+ ScopedPropertyKey str(scope, index.toPropertyKey(engine));
if (engine->hasException)
return Encode::undefined();
@@ -1095,7 +1418,15 @@ ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &fu
{
if (!func.isFunctionObject())
return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
- return static_cast<const FunctionObject &>(func).call(nullptr, argv, argc);
+ Value undef = Value::undefinedValue();
+ return static_cast<const FunctionObject &>(func).call(&undef, argv, argc);
+}
+
+ReturnedValue Runtime::method_callWithReceiver(ExecutionEngine *engine, const Value &func, const Value *thisObject, Value *argv, int argc)
+{
+ if (!func.isFunctionObject())
+ return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
+ return static_cast<const FunctionObject &>(func).call(thisObject, argv, argc);
}
ReturnedValue Runtime::method_callQmlScopeObjectProperty(ExecutionEngine *engine, Value *base,
@@ -1130,12 +1461,107 @@ ReturnedValue Runtime::method_callQmlContextObjectProperty(ExecutionEngine *engi
return fo->call(qmlContextValue, argv, argc);
}
-ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &function, Value *argv, int argc)
+struct CallArgs {
+ Value *argv;
+ int argc;
+};
+
+static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc)
+{
+ ScopedValue it(scope);
+ ScopedValue done(scope);
+
+ int argCount = 0;
+
+ Value *v = scope.alloc<Scope::Uninitialized>();
+ Value *arguments = v;
+ for (int i = 0; i < argc; ++i) {
+ if (!argv[i].isEmpty()) {
+ *v = argv[i];
+ ++argCount;
+ v = scope.alloc<Scope::Uninitialized>();
+ continue;
+ }
+ // spread element
+ ++i;
+ it = Runtime::method_getIterator(scope.engine, argv[i], /* ForInIterator */ 1);
+ if (scope.engine->hasException)
+ return { nullptr, 0 };
+ while (1) {
+ done = Runtime::method_iteratorNext(scope.engine, it, v);
+ if (scope.engine->hasException)
+ return { nullptr, 0 };
+ Q_ASSERT(done->isBoolean());
+ if (done->booleanValue())
+ break;
+ ++argCount;
+ v = scope.alloc<Scope::Uninitialized>();
+ }
+ }
+ return { arguments, argCount };
+}
+
+ReturnedValue Runtime::method_callWithSpread(ExecutionEngine *engine, const Value &function, const Value &thisObject, Value *argv, int argc)
{
+ Q_ASSERT(argc >= 1);
if (!function.isFunctionObject())
return engine->throwTypeError();
- return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc);
+ Scope scope(engine);
+ CallArgs arguments = createSpreadArguments(scope, argv, argc);
+ if (engine->hasException)
+ return Encode::undefined();
+
+ return static_cast<const FunctionObject &>(function).call(&thisObject, arguments.argv, arguments.argc);
+}
+
+ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc)
+{
+ if (!function.isFunctionObject())
+ return engine->throwTypeError();
+
+ return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc, &newTarget);
+}
+
+ReturnedValue Runtime::method_constructWithSpread(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc)
+{
+ if (!function.isFunctionObject())
+ return engine->throwTypeError();
+
+ Scope scope(engine);
+ CallArgs arguments = createSpreadArguments(scope, argv, argc);
+ if (engine->hasException)
+ return Encode::undefined();
+
+ return static_cast<const FunctionObject &>(function).callAsConstructor(arguments.argv, arguments.argc, &newTarget);
+}
+
+ReturnedValue Runtime::method_tailCall(CppStackFrame *frame, ExecutionEngine *engine)
+{
+ // IMPORTANT! The JIT assumes that this method has the same amount (or less) arguments than
+ // the jitted function, so it can safely do a tail call.
+
+ Value *tos = engine->jsStackTop;
+ const Value &function = tos[StackOffsets::tailCall_function];
+ const Value &thisObject = tos[StackOffsets::tailCall_thisObject];
+ Value *argv = reinterpret_cast<Value *>(frame->jsFrame) + tos[StackOffsets::tailCall_argv].int_32();
+ int argc = tos[StackOffsets::tailCall_argc].int_32();
+
+ if (!function.isFunctionObject())
+ return engine->throwTypeError();
+
+ const FunctionObject &fo = static_cast<const FunctionObject &>(function);
+ if (!frame->callerCanHandleTailCall || !fo.canBeTailCalled() || engine->debugger()) {
+ // Cannot tailcall, do a normal call:
+ return fo.call(&thisObject, argv, argc);
+ }
+
+ memcpy(frame->jsFrame->args, argv, argc * sizeof(Value));
+ frame->init(engine, fo.function(), frame->jsFrame->args, argc, frame->callerCanHandleTailCall);
+ frame->setupJSFrame(frame->savedStackTop, fo, fo.scope(), thisObject, Primitive::undefinedValue());
+ engine->jsStackTop = frame->savedStackTop + frame->requiredJSStackFrameSize();
+ frame->pendingTailCall = true;
+ return Encode::undefined();
}
void Runtime::method_throwException(ExecutionEngine *engine, const Value &value)
@@ -1161,6 +1587,8 @@ ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value &
case Value::Managed_Type:
if (value.isString())
res = engine->id_string();
+ else if (value.isSymbol())
+ res = engine->id_symbol();
else if (value.objectValue()->as<FunctionObject>())
res = engine->id_function();
else
@@ -1183,23 +1611,58 @@ QV4::ReturnedValue Runtime::method_typeofName(ExecutionEngine *engine, int nameI
return method_typeofValue(engine, prop);
}
-/* The next three methods are a bit tricky. They can't open up a Scope, as that
- * would mess up the pushing of the context.
- *
- * Instead the push/pop pair acts as a non local scope.
- */
-ReturnedValue Runtime::method_createWithContext(ExecutionContext *parent, const Value &o)
+ReturnedValue Runtime::method_createWithContext(ExecutionEngine *engine, Value *jsStackFrame)
+{
+ QV4::Value &accumulator = jsStackFrame[CallData::Accumulator];
+ accumulator = accumulator.toObject(engine);
+ if (engine->hasException)
+ return Encode::undefined();
+ Q_ASSERT(accumulator.isObject());
+ const Object &obj = static_cast<const Object &>(accumulator);
+ ExecutionContext *context = static_cast<ExecutionContext *>(jsStackFrame + CallData::Context);
+ return context->newWithContext(obj.d())->asReturnedValue();
+}
+
+ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex)
{
- Q_ASSERT(o.isObject());
- const Object &obj = static_cast<const Object &>(o);
- return parent->newWithContext(obj.d())->asReturnedValue();
+ ExecutionEngine *e = parent->engine();
+ return parent->newCatchContext(e->currentStackFrame, blockIndex,
+ e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex])->asReturnedValue();
}
-ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int exceptionVarNameIndex)
+ReturnedValue Runtime::method_createBlockContext(ExecutionContext *parent, int index)
{
ExecutionEngine *e = parent->engine();
- return parent->newCatchContext(e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex],
- e->catchException(nullptr))->asReturnedValue();
+ return parent->newBlockContext(e->currentStackFrame, index)->asReturnedValue();
+}
+
+ReturnedValue Runtime::method_cloneBlockContext(ExecutionContext *previous)
+{
+ return ExecutionContext::cloneBlockContext(static_cast<Heap::CallContext *>(previous->d()))->asReturnedValue();
+}
+
+
+ReturnedValue Runtime::method_createScriptContext(ExecutionEngine *engine, int index)
+{
+ Q_ASSERT(engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_GlobalContext ||
+ engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_QmlContext);
+ ReturnedValue c = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue();
+ engine->setScriptContext(c);
+ return c;
+}
+
+ReturnedValue Runtime::method_popScriptContext(ExecutionEngine *engine)
+{
+ ReturnedValue root = engine->rootContext()->asReturnedValue();
+ engine->setScriptContext(root);
+ return root;
+}
+
+void Runtime::method_throwReferenceError(ExecutionEngine *engine, int nameIndex)
+{
+ Scope scope(engine);
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ engine->throwReferenceError(name);
}
void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex)
@@ -1214,61 +1677,204 @@ ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *value
return engine->newArrayObject(values, length)->asReturnedValue();
}
-ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4::Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags)
+ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId, int argc, const QV4::Value *args)
{
Scope scope(engine);
- QV4::InternalClass *klass = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeClasses[classId];
- ScopedObject o(scope, engine->newObject(klass, engine->objectPrototype()));
+ Scoped<InternalClass> klass(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeClasses[classId]);
+ ScopedObject o(scope, engine->newObject(klass->d()));
- {
- bool needSparseArray = arrayGetterSetterCountAndFlags >> 30;
- if (needSparseArray)
- o->initSparseArray();
- }
+ Q_ASSERT(uint(argc) >= klass->d()->size);
- for (uint i = 0; i < klass->size; ++i)
+ for (uint i = 0; i < klass->d()->size; ++i)
o->setProperty(i, *args++);
- if (arrayValueCount > 0) {
- ScopedValue entry(scope);
- for (int i = 0; i < arrayValueCount; ++i) {
- uint idx = args->toUInt32();
- ++args;
- entry = *args++;
- o->arraySet(idx, entry);
+ Q_ASSERT((argc - klass->d()->size) % 3 == 0);
+ int additionalArgs = (argc - int(klass->d()->size))/3;
+
+ if (!additionalArgs)
+ return o->asReturnedValue();
+
+ ScopedPropertyKey name(scope);
+ ScopedProperty pd(scope);
+ ScopedFunctionObject fn(scope);
+ ScopedString fnName(scope);
+ ScopedValue value(scope);
+ for (int i = 0; i < additionalArgs; ++i) {
+ Q_ASSERT(args->isInteger());
+ ObjectLiteralArgument arg = ObjectLiteralArgument(args->integerValue());
+ name = args[1].toPropertyKey(engine);
+ value = args[2];
+ if (engine->hasException)
+ return Encode::undefined();
+ if (arg != ObjectLiteralArgument::Value) {
+ Q_ASSERT(args[2].isInteger());
+ int functionId = args[2].integerValue();
+ QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeFunctions[functionId];
+ Q_ASSERT(clos);
+
+ PropertyKey::FunctionNamePrefix prefix = PropertyKey::None;
+ if (arg == ObjectLiteralArgument::Getter)
+ prefix = PropertyKey::Getter;
+ else if (arg == ObjectLiteralArgument::Setter)
+ prefix = PropertyKey::Setter;
+ else
+ arg = ObjectLiteralArgument::Value;
+ fnName = name->asFunctionName(engine, prefix);
+
+ ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context);
+ if (clos->isGenerator())
+ value = MemberGeneratorFunction::create(current, clos, o, fnName)->asReturnedValue();
+ else
+ value = FunctionObject::createMemberFunction(current, clos, o, fnName)->asReturnedValue();
+ } else if (args[2].isFunctionObject()) {
+ fn = static_cast<const FunctionObject &>(args[2]);
+
+ fnName = name->asFunctionName(engine, PropertyKey::None);
+ fn->setName(fnName);
}
+ Q_ASSERT(arg != ObjectLiteralArgument::Method);
+ Q_ASSERT(arg == ObjectLiteralArgument::Value || value->isFunctionObject());
+ if (arg == ObjectLiteralArgument::Value || arg == ObjectLiteralArgument::Getter) {
+ pd->value = value;
+ pd->set = Value::emptyValue();
+ } else {
+ pd->value = Value::emptyValue();
+ pd->set = value;
+ }
+ bool ok = o->defineOwnProperty(name, pd, (arg == ObjectLiteralArgument::Value ? Attr_Data : Attr_Accessor));
+ if (!ok)
+ return engine->throwTypeError();
+
+ args += 3;
}
+ return o.asReturnedValue();
+}
- uint arrayGetterSetterCount = arrayGetterSetterCountAndFlags & ((1 << 30) - 1);
- if (arrayGetterSetterCount > 0) {
- ScopedProperty pd(scope);
- for (uint i = 0; i < arrayGetterSetterCount; ++i) {
- uint idx = args->toUInt32();
- ++args;
- pd->value = *args;
- ++args;
- pd->set = *args;
- ++args;
- o->arraySet(idx, pd, Attr_Accessor);
+ReturnedValue Runtime::method_createClass(ExecutionEngine *engine, int classIndex, const Value &superClass, const Value *computedNames)
+{
+ const CompiledData::CompilationUnit *unit = engine->currentStackFrame->v4Function->compilationUnit;
+ const QV4::CompiledData::Class *cls = unit->unitData()->classAt(classIndex);
+
+ Scope scope(engine);
+ ScopedObject protoParent(scope, engine->objectPrototype());
+ ScopedObject constructorParent(scope, engine->functionPrototype());
+ if (!superClass.isEmpty()) {
+ if (superClass.isNull()) {
+ protoParent = Encode::null();
+ } else {
+ const FunctionObject *superFunction = superClass.as<FunctionObject>();
+ // ### check that the heritage object is a constructor
+ if (!superFunction || !superFunction->isConstructor())
+ return engine->throwTypeError(QStringLiteral("The superclass is not a function object."));
+ const FunctionObject *s = static_cast<const FunctionObject *>(&superClass);
+ ScopedValue result(scope, s->get(scope.engine->id_prototype()));
+ if (!result->isObject() && !result->isNull())
+ return engine->throwTypeError(QStringLiteral("The value of the superclass's prototype property is not an object."));
+ protoParent = *result;
+ constructorParent = superClass;
}
}
- return o.asReturnedValue();
+ ScopedObject proto(scope, engine->newObject());
+ proto->setPrototypeUnchecked(protoParent);
+ ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context);
+
+ ScopedFunctionObject constructor(scope);
+ QV4::Function *f = cls->constructorFunction != UINT_MAX ? unit->runtimeFunctions[cls->constructorFunction] : nullptr;
+ constructor = FunctionObject::createConstructorFunction(current, f, proto, !superClass.isEmpty())->asReturnedValue();
+ constructor->setPrototypeUnchecked(constructorParent);
+ Value argCount = Value::fromInt32(f ? f->nFormals : 0);
+ constructor->defineReadonlyConfigurableProperty(scope.engine->id_length(), argCount);
+ constructor->defineReadonlyConfigurableProperty(engine->id_prototype(), proto);
+ proto->defineDefaultProperty(engine->id_constructor(), constructor);
+
+ ScopedString name(scope);
+ if (cls->nameIndex != UINT_MAX) {
+ name = unit->runtimeStrings[cls->nameIndex];
+ constructor->defineReadonlyConfigurableProperty(engine->id_name(), name);
+ }
+
+ ScopedObject receiver(scope, *constructor);
+ ScopedPropertyKey propertyName(scope);
+ ScopedFunctionObject function(scope);
+ ScopedProperty property(scope);
+ const CompiledData::Method *methods = cls->methodTable();
+ for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) {
+ if (i == cls->nStaticMethods)
+ receiver = proto;
+ if (methods[i].name == UINT_MAX) {
+ propertyName = computedNames->toPropertyKey(engine);
+ if (propertyName == scope.engine->id_prototype()->propertyKey() && receiver->d() == constructor->d())
+ return engine->throwTypeError(QStringLiteral("Cannot declare a static method named 'prototype'."));
+ if (engine->hasException)
+ return Encode::undefined();
+ ++computedNames;
+ } else {
+ name = unit->runtimeStrings[methods[i].name];
+ propertyName = name->toPropertyKey();
+ }
+ QV4::Function *f = unit->runtimeFunctions[methods[i].function];
+ Q_ASSERT(f);
+ PropertyKey::FunctionNamePrefix prefix = PropertyKey::None;
+ if (methods[i].type == CompiledData::Method::Getter)
+ prefix = PropertyKey::Getter;
+ else if (methods[i].type == CompiledData::Method::Setter)
+ prefix = PropertyKey::Setter;
+
+ name = propertyName->asFunctionName(engine, prefix);
+
+ if (f->isGenerator())
+ function = MemberGeneratorFunction::create(current, f, receiver, name);
+ else
+ function = FunctionObject::createMemberFunction(current, f, receiver, name);
+ Q_ASSERT(function);
+ PropertyAttributes attributes;
+ switch (methods[i].type) {
+ case CompiledData::Method::Getter:
+ property->setGetter(function);
+ property->set = Value::emptyValue();
+ attributes = Attr_Accessor|Attr_NotEnumerable;
+ break;
+ case CompiledData::Method::Setter:
+ property->value = Value::emptyValue();
+ property->setSetter(function);
+ attributes = Attr_Accessor|Attr_NotEnumerable;
+ break;
+ default: // Regular
+ property->value = function;
+ property->set = Value::emptyValue();
+ attributes = Attr_Data|Attr_NotEnumerable;
+ break;
+ }
+ receiver->defineOwnProperty(propertyName, property, attributes);
+ }
+
+ return constructor->asReturnedValue();
}
QV4::ReturnedValue Runtime::method_createMappedArgumentsObject(ExecutionEngine *engine)
{
Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_CallContext);
- QV4::InternalClass *ic = engine->internalClasses[EngineBase::Class_ArgumentsObject];
- return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->objectPrototype(), engine->currentStackFrame)->asReturnedValue();
+ Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_ArgumentsObject);
+ return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue();
}
QV4::ReturnedValue Runtime::method_createUnmappedArgumentsObject(ExecutionEngine *engine)
{
- QV4::InternalClass *ic = engine->internalClasses[EngineBase::Class_StrictArgumentsObject];
- return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->objectPrototype(), engine->currentStackFrame)->asReturnedValue();
+ Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_StrictArgumentsObject);
+ return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue();
+}
+
+QV4::ReturnedValue Runtime::method_createRestParameter(ExecutionEngine *engine, int argIndex)
+{
+ const Value *values = engine->currentStackFrame->originalArguments + argIndex;
+ int nValues = engine->currentStackFrame->originalArgumentsCount - argIndex;
+ if (nValues <= 0)
+ return engine->newArrayObject(0)->asReturnedValue();
+ return engine->newArrayObject(values, nValues)->asReturnedValue();
}
+
ReturnedValue Runtime::method_loadQmlContext(NoThrowEngine *engine)
{
Heap::QmlContext *ctx = engine->qmlContext();
@@ -1327,6 +1933,7 @@ ReturnedValue Runtime::method_loadQmlImportedScripts(NoThrowEngine *engine)
return Encode::undefined();
return context->importedScripts.value();
}
+#endif // V4_BOOTSTRAP
ReturnedValue Runtime::method_uMinus(const Value &value)
{
@@ -1352,11 +1959,10 @@ ReturnedValue Runtime::method_add(ExecutionEngine *engine, const Value &left, co
if (Q_LIKELY(left.integerCompatible() && right.integerCompatible()))
return add_int32(left.integerValue(), right.integerValue());
if (left.isNumber() && right.isNumber())
- return Primitive::fromDouble(left.asDouble() + right.asDouble()).asReturnedValue();
+ return Value::fromDouble(left.asDouble() + right.asDouble()).asReturnedValue();
return RuntimeHelpers::addHelper(engine, left, right);
}
-#endif // V4_BOOTSTRAP
ReturnedValue Runtime::method_sub(const Value &left, const Value &right)
{
@@ -1368,7 +1974,7 @@ ReturnedValue Runtime::method_sub(const Value &left, const Value &right)
double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl();
double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl();
- return Primitive::fromDouble(lval - rval).asReturnedValue();
+ return Value::fromDouble(lval - rval).asReturnedValue();
}
ReturnedValue Runtime::method_mul(const Value &left, const Value &right)
@@ -1381,7 +1987,7 @@ ReturnedValue Runtime::method_mul(const Value &left, const Value &right)
double lval = left.isNumber() ? left.asDouble() : left.toNumberImpl();
double rval = right.isNumber() ? right.asDouble() : right.toNumberImpl();
- return Primitive::fromDouble(lval * rval).asReturnedValue();
+ return Value::fromDouble(lval * rval).asReturnedValue();
}
ReturnedValue Runtime::method_div(const Value &left, const Value &right)
@@ -1401,17 +2007,20 @@ ReturnedValue Runtime::method_div(const Value &left, const Value &right)
double lval = left.toNumber();
double rval = right.toNumber();
- return Primitive::fromDouble(lval / rval).asReturnedValue();
+ return Value::fromDouble(lval / rval).asReturnedValue();
}
ReturnedValue Runtime::method_mod(const Value &left, const Value &right)
{
TRACE2(left, right);
- if (Value::integerCompatible(left, right) && left.integerValue() > 0 && right.integerValue() > 0) {
- int intRes = left.integerValue() % right.integerValue();
- if (intRes != 0 || left.integerValue() >= 0)
- return Encode(intRes);
+ if (Value::integerCompatible(left, right) && left.integerValue() >= 0 && right.integerValue() > 0) {
+ // special cases are handled by fmod, among them:
+ // - arithmic execeptions for ints in c++, eg: INT_MIN % -1
+ // - undefined behavior in c++, e.g.: anything % 0
+ // - uncommon cases which would complicate the condition, e.g.: negative integers
+ // (this makes sure that -1 % 1 == -0 by passing it to fmod)
+ return Encode(left.integerValue() % right.integerValue());
}
double lval = RuntimeHelpers::toNumber(left);
@@ -1419,7 +2028,7 @@ ReturnedValue Runtime::method_mod(const Value &left, const Value &right)
#ifdef fmod
# undef fmod
#endif
- return Primitive::fromDouble(std::fmod(lval, rval)).asReturnedValue();
+ return Value::fromDouble(std::fmod(lval, rval)).asReturnedValue();
}
ReturnedValue Runtime::method_shl(const Value &left, const Value &right)
@@ -1485,24 +2094,133 @@ ReturnedValue Runtime::method_lessEqual(const Value &left, const Value &right)
return Encode(r);
}
+struct LazyScope
+{
+ ExecutionEngine *engine = nullptr;
+ Value *stackMark = nullptr;
+ ~LazyScope() {
+ if (engine)
+ engine->jsStackTop = stackMark;
+ }
+ template <typename T>
+ void set(Value **scopedValue, T value, ExecutionEngine *e) {
+ if (!engine) {
+ engine = e;
+ stackMark = engine->jsStackTop;
+ }
+ if (!*scopedValue)
+ *scopedValue = e->jsAlloca(1);
+ **scopedValue = value;
+ }
+};
+
Bool Runtime::method_compareEqual(const Value &left, const Value &right)
{
TRACE2(left, right);
- if (left.rawValue() == right.rawValue())
- // NaN != NaN
- return !left.isNaN();
+ Value lhs = left;
+ Value rhs = right;
- if (left.type() == right.type()) {
- if (left.isDouble() && left.doubleValue() == 0 && right.doubleValue() == 0)
- return true; // this takes care of -0 == +0 (which obviously have different raw values)
- if (!left.isManaged())
- return false;
- if (left.isString() == right.isString())
- return left.cast<Managed>()->isEqualTo(right.cast<Managed>());
+#ifndef V4_BOOTSTRAP
+ LazyScope scope;
+ Value *lhsGuard = nullptr;
+ Value *rhsGuard = nullptr;
+#endif
+
+ redo:
+ if (lhs.asReturnedValue() == rhs.asReturnedValue())
+ return !lhs.isNaN();
+
+ int lt = lhs.quickType();
+ int rt = rhs.quickType();
+ if (rt < lt) {
+ qSwap(lhs, rhs);
+ qSwap(lt, rt);
}
- return RuntimeHelpers::equalHelper(left, right);
+ switch (lt) {
+ case QV4::Value::QT_ManagedOrUndefined:
+ if (lhs.isUndefined())
+ return rhs.isNullOrUndefined();
+ Q_FALLTHROUGH();
+ case QV4::Value::QT_ManagedOrUndefined1:
+ case QV4::Value::QT_ManagedOrUndefined2:
+ case QV4::Value::QT_ManagedOrUndefined3:
+ // LHS: Managed
+ switch (rt) {
+ case QV4::Value::QT_ManagedOrUndefined:
+ if (rhs.isUndefined())
+ return false;
+ Q_FALLTHROUGH();
+ case QV4::Value::QT_ManagedOrUndefined1:
+ case QV4::Value::QT_ManagedOrUndefined2:
+ case QV4::Value::QT_ManagedOrUndefined3: {
+#ifndef V4_BOOTSTRAP
+ // RHS: Managed
+ Heap::Base *l = lhs.m();
+ Heap::Base *r = rhs.m();
+ Q_ASSERT(l);
+ Q_ASSERT(r);
+ if (l->internalClass->vtable->isStringOrSymbol == r->internalClass->vtable->isStringOrSymbol)
+ return static_cast<QV4::Managed &>(lhs).isEqualTo(&static_cast<QV4::Managed &>(rhs));
+ if (l->internalClass->vtable->isStringOrSymbol) {
+ scope.set(&rhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(rhs), PREFERREDTYPE_HINT), r->internalClass->engine);
+ rhs = rhsGuard->asReturnedValue();
+ break;
+ } else {
+ Q_ASSERT(r->internalClass->vtable->isStringOrSymbol);
+ scope.set(&lhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT), l->internalClass->engine);
+ lhs = lhsGuard->asReturnedValue();
+ break;
+ }
+#endif
+ return false;
+ }
+ case QV4::Value::QT_Empty:
+ Q_UNREACHABLE();
+ case QV4::Value::QT_Null:
+ return false;
+ case QV4::Value::QT_Bool:
+ case QV4::Value::QT_Int:
+ rhs = Value::fromDouble(rhs.int_32());
+ // fall through
+ default: // double
+#ifndef V4_BOOTSTRAP
+ if (lhs.m()->internalClass->vtable->isStringOrSymbol) {
+ return lhs.m()->internalClass->vtable->isString ? (RuntimeHelpers::toNumber(lhs) == rhs.doubleValue()) : false;
+ } else {
+ scope.set(&lhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT), lhs.m()->internalClass->engine);
+ lhs = lhsGuard->asReturnedValue();
+ }
+#else
+ Q_UNIMPLEMENTED();
+#endif
+ }
+ goto redo;
+ case QV4::Value::QT_Empty:
+ Q_UNREACHABLE();
+ case QV4::Value::QT_Null:
+ return rhs.isNull();
+ case QV4::Value::QT_Bool:
+ case QV4::Value::QT_Int:
+ switch (rt) {
+ case QV4::Value::QT_ManagedOrUndefined:
+ case QV4::Value::QT_ManagedOrUndefined1:
+ case QV4::Value::QT_ManagedOrUndefined2:
+ case QV4::Value::QT_ManagedOrUndefined3:
+ case QV4::Value::QT_Empty:
+ case QV4::Value::QT_Null:
+ Q_UNREACHABLE();
+ case QV4::Value::QT_Bool:
+ case QV4::Value::QT_Int:
+ return lhs.int_32() == rhs.int_32();
+ default: // double
+ return lhs.int_32() == rhs.doubleValue();
+ }
+ default: // double
+ Q_ASSERT(rhs.isDouble());
+ return lhs.doubleValue() == rhs.doubleValue();
+ }
}
ReturnedValue Runtime::method_equal(const Value &left, const Value &right)
diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h
index 3a26c23990..72af90d1dc 100644
--- a/src/qml/jsruntime/qv4runtime_p.h
+++ b/src/qml/jsruntime/qv4runtime_p.h
@@ -100,6 +100,7 @@ enum TypeHint {
struct Q_QML_PRIVATE_EXPORT RuntimeHelpers {
static ReturnedValue objectDefaultValue(const Object *object, int typeHint);
static ReturnedValue toPrimitive(const Value &value, TypeHint typeHint);
+ static ReturnedValue ordinaryToPrimitive(ExecutionEngine *engine, const Object *object, String *typeHint);
static double stringToNumber(const QString &s);
static Heap::String *stringFromNumber(ExecutionEngine *engine, double number);
diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h
index 91232256a9..d64178a72f 100644
--- a/src/qml/jsruntime/qv4runtimeapi_p.h
+++ b/src/qml/jsruntime/qv4runtimeapi_p.h
@@ -98,34 +98,44 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
F(ReturnedValue, callPropertyLookup, (ExecutionEngine *engine, Value *base, uint index, Value *argv, int argc)) \
F(ReturnedValue, callElement, (ExecutionEngine *engine, Value *base, const Value &index, Value *argv, int argc)) \
F(ReturnedValue, callValue, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \
+ F(ReturnedValue, callWithReceiver, (ExecutionEngine *engine, const Value &func, const Value *thisObject, Value *argv, int argc)) \
F(ReturnedValue, callPossiblyDirectEval, (ExecutionEngine *engine, Value *argv, int argc)) \
+ F(ReturnedValue, callWithSpread, (ExecutionEngine *engine, const Value &func, const Value &thisObject, Value *argv, int argc)) \
+ F(ReturnedValue, tailCall, (CppStackFrame *frame, ExecutionEngine *engine)) \
\
/* construct */ \
- F(ReturnedValue, construct, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \
+ F(ReturnedValue, construct, (ExecutionEngine *engine, const Value &func, const Value &newTarget, Value *argv, int argc)) \
+ F(ReturnedValue, constructWithSpread, (ExecutionEngine *engine, const Value &func, const Value &newTarget, Value *argv, int argc)) \
\
/* load & store */ \
F(void, storeNameStrict, (ExecutionEngine *engine, int nameIndex, const Value &value)) \
F(void, storeNameSloppy, (ExecutionEngine *engine, int nameIndex, const Value &value)) \
- F(bool, storeProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \
- F(bool, storeElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \
+ F(void, storeProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \
+ F(void, storeElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \
F(ReturnedValue, loadProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)) \
F(ReturnedValue, loadName, (ExecutionEngine *engine, int nameIndex)) \
F(ReturnedValue, loadElement, (ExecutionEngine *engine, const Value &object, const Value &index)) \
+ F(ReturnedValue, loadSuperProperty, (ExecutionEngine *engine, const Value &property)) \
+ F(void, storeSuperProperty, (ExecutionEngine *engine, const Value &property, const Value &value)) \
+ F(ReturnedValue, loadSuperConstructor, (ExecutionEngine *engine, const Value &t)) \
\
/* typeof */ \
F(ReturnedValue, typeofValue, (ExecutionEngine *engine, const Value &val)) \
F(ReturnedValue, typeofName, (ExecutionEngine *engine, int nameIndex)) \
\
/* delete */ \
- F(bool, deleteElement, (ExecutionEngine *engine, const Value &base, const Value &index)) \
- F(bool, deleteMember, (ExecutionEngine *engine, const Value &base, int nameIndex)) \
- F(bool, deleteMemberString, (ExecutionEngine *engine, const Value &base, String *name)) \
+ F(bool, deleteProperty, (ExecutionEngine *engine, const Value &base, const Value &index)) \
F(bool, deleteName, (ExecutionEngine *engine, int nameIndex)) \
\
/* exceptions & scopes */ \
F(void, throwException, (ExecutionEngine *engine, const Value &value)) \
- F(ReturnedValue, createWithContext, (ExecutionContext *parent, const Value &o)) \
- F(ReturnedValue, createCatchContext, (ExecutionContext *parent, int exceptionVarNameIndex)) \
+ F(ReturnedValue, createWithContext, (ExecutionEngine *, Value *jsStackFrame)) \
+ F(ReturnedValue, createCatchContext, (ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex)) \
+ F(ReturnedValue, createBlockContext, (ExecutionContext *parent, int index)) \
+ F(ReturnedValue, createScriptContext, (ExecutionEngine *engine, int index)) \
+ F(ReturnedValue, cloneBlockContext, (ExecutionContext *previous)) \
+ F(ReturnedValue, popScriptContext, (ExecutionEngine *engine)) \
+ F(void, throwReferenceError, (ExecutionEngine *engine, int nameIndex)) \
\
/* closures */ \
F(ReturnedValue, closure, (ExecutionEngine *engine, int functionId)) \
@@ -134,14 +144,19 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
F(void, declareVar, (ExecutionEngine *engine, bool deletable, int nameIndex)) \
F(ReturnedValue, createMappedArgumentsObject, (ExecutionEngine *engine)) \
F(ReturnedValue, createUnmappedArgumentsObject, (ExecutionEngine *engine)) \
+ F(ReturnedValue, createRestParameter, (ExecutionEngine *engine, int argIndex)) \
\
/* literals */ \
F(ReturnedValue, arrayLiteral, (ExecutionEngine *engine, Value *values, uint length)) \
- F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, const Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags)) \
+ F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, int classId, int argc, const Value *args)) \
+ F(ReturnedValue, createClass, (ExecutionEngine *engine, int classIndex, const Value &heritage, const Value *computedNames)) \
\
- /* foreach */ \
- F(ReturnedValue, foreachIterator, (ExecutionEngine *engine, const Value &in)) \
- F(ReturnedValue, foreachNextPropertyName, (const Value &foreach_iterator)) \
+ /* for-in, for-of and array destructuring */ \
+ F(ReturnedValue, getIterator, (ExecutionEngine *engine, const Value &in, int iterator)) \
+ F(ReturnedValue, iteratorNext, (ExecutionEngine *engine, const Value &iterator, Value *value)) \
+ F(ReturnedValue, iteratorNextForYieldStar, (ExecutionEngine *engine, const Value &received, const Value &iterator, Value *object)) \
+ F(ReturnedValue, iteratorClose, (ExecutionEngine *engine, const Value &iterator, const Value &done)) \
+ F(ReturnedValue, destructureRestElement, (ExecutionEngine *engine, const Value &iterator)) \
\
/* unary operators */ \
F(ReturnedValue, uMinus, (const Value &value)) \
@@ -219,6 +234,12 @@ struct Q_QML_PRIVATE_EXPORT Runtime {
FOR_EACH_RUNTIME_METHOD(RUNTIME_METHOD)
#undef RUNTIME_METHOD
+ struct StackOffsets {
+ static const int tailCall_function = -1;
+ static const int tailCall_thisObject = -2;
+ static const int tailCall_argv = -3;
+ static const int tailCall_argc = -4;
+ };
};
static_assert(std::is_standard_layout<Runtime>::value, "Runtime needs to be standard layout in order for us to be able to use offsetof");
diff --git a/src/qml/jsruntime/qv4runtimecodegen.cpp b/src/qml/jsruntime/qv4runtimecodegen.cpp
index fe18ddf9ed..9866966936 100644
--- a/src/qml/jsruntime/qv4runtimecodegen.cpp
+++ b/src/qml/jsruntime/qv4runtimecodegen.cpp
@@ -52,13 +52,16 @@ void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName,
_module->finalUrl = fileName;
_context = nullptr;
- Compiler::ScanFunctions scan(this, sourceCode, Compiler::GlobalCode);
+ Compiler::ScanFunctions scan(this, sourceCode, Compiler::ContextType::Global);
// fake a global environment
- scan.enterEnvironment(nullptr, Compiler::FunctionCode);
+ scan.enterEnvironment(nullptr, Compiler::ContextType::Function, QString());
scan(ast);
scan.leaveEnvironment();
- int index = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : nullptr);
+ if (hasError)
+ return;
+
+ int index = defineFunction(ast->name.toString(), ast, ast->formals, ast->body);
_module->rootContext = _module->functions.at(index);
}
diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h
index bb20f384b3..c28a3ffa2d 100644
--- a/src/qml/jsruntime/qv4scopedvalue_p.h
+++ b/src/qml/jsruntime/qv4scopedvalue_p.h
@@ -53,6 +53,7 @@
#include "qv4engine_p.h"
#include "qv4value_p.h"
#include "qv4property_p.h"
+#include "qv4propertykey_p.h"
#ifdef V4_USE_VALGRIND
#include <valgrind/memcheck.h>
@@ -117,8 +118,45 @@ struct Scope {
engine->jsStackTop = mark;
}
- QML_NEARLY_ALWAYS_INLINE Value *alloc(int nValues) const {
- return engine->jsAlloca(nValues);
+ enum AllocMode {
+ Undefined,
+ Empty,
+ /* Be careful when using Uninitialized, the stack has to be fully initialized before calling into the memory manager again */
+ Uninitialized
+ };
+ template <AllocMode mode = Undefined>
+ QML_NEARLY_ALWAYS_INLINE Value *alloc(int nValues) const
+ {
+ Value *ptr = engine->jsAlloca(nValues);
+ switch (mode) {
+ case Undefined:
+ for (int i = 0; i < nValues; ++i)
+ ptr[i] = Value::undefinedValue();
+ break;
+ case Empty:
+ for (int i = 0; i < nValues; ++i)
+ ptr[i] = Value::emptyValue();
+ break;
+ case Uninitialized:
+ break;
+ }
+ return ptr;
+ }
+ template <AllocMode mode = Undefined>
+ QML_NEARLY_ALWAYS_INLINE Value *alloc() const
+ {
+ Value *ptr = engine->jsAlloca(1);
+ switch (mode) {
+ case Undefined:
+ *ptr = Value::undefinedValue();
+ break;
+ case Empty:
+ *ptr = Value::emptyValue();
+ break;
+ case Uninitialized:
+ break;
+ }
+ return ptr;
}
bool hasException() const {
@@ -136,31 +174,31 @@ struct ScopedValue
{
ScopedValue(const Scope &scope)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.alloc<Scope::Uninitialized>();
ptr->setRawValue(0);
}
ScopedValue(const Scope &scope, const Value &v)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.alloc<Scope::Uninitialized>();
*ptr = v;
}
ScopedValue(const Scope &scope, Heap::Base *o)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.alloc<Scope::Uninitialized>();
ptr->setM(o);
}
ScopedValue(const Scope &scope, Managed *m)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.alloc<Scope::Uninitialized>();
ptr->setRawValue(m->asReturnedValue());
}
ScopedValue(const Scope &scope, const ReturnedValue &v)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.alloc<Scope::Uninitialized>();
ptr->setRawValue(v);
}
@@ -203,6 +241,50 @@ struct ScopedValue
Value *ptr;
};
+
+struct ScopedPropertyKey
+{
+ ScopedPropertyKey(const Scope &scope)
+ {
+ ptr = reinterpret_cast<PropertyKey *>(scope.alloc<Scope::Uninitialized>());
+ *ptr = PropertyKey::invalid();
+ }
+
+ ScopedPropertyKey(const Scope &scope, const PropertyKey &v)
+ {
+ ptr = reinterpret_cast<PropertyKey *>(scope.alloc<Scope::Uninitialized>());
+ *ptr = v;
+ }
+
+ ScopedPropertyKey &operator=(const PropertyKey &other) {
+ *ptr = other;
+ return *this;
+ }
+
+ PropertyKey *operator->() {
+ return ptr;
+ }
+ operator PropertyKey() const {
+ return *ptr;
+ }
+
+ bool operator==(const PropertyKey &other) const {
+ return *ptr == other;
+ }
+ bool operator==(const ScopedPropertyKey &other) const {
+ return *ptr == *other.ptr;
+ }
+ bool operator!=(const PropertyKey &other) const {
+ return *ptr != other;
+ }
+ bool operator!=(const ScopedPropertyKey &other) const {
+ return *ptr != *other.ptr;
+ }
+
+ PropertyKey *ptr;
+};
+
+
template<typename T>
struct Scoped
{
@@ -214,66 +296,66 @@ struct Scoped
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Undefined>();
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const Value &v)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(v.as<T>());
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, Heap::Base *o)
{
Value v;
v = o;
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(v.as<T>());
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const ScopedValue &v)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(v.ptr->as<T>());
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const Value &v, ConvertType)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
ptr->setRawValue(value_convert<T>(scope.engine, v));
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const Value *v)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(v ? v->as<T>() : nullptr);
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, T *t)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(t);
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const T *t)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(t);
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, typename T::Data *t)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
*ptr = t;
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const ReturnedValue &v)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(QV4::Value::fromReturnedValue(v).as<T>());
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const ReturnedValue &v, ConvertType)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
ptr->setRawValue(value_convert<T>(scope.engine, QV4::Value::fromReturnedValue(v)));
}
diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp
index 5cd62c90f1..070c048c8f 100644
--- a/src/qml/jsruntime/qv4script.cpp
+++ b/src/qml/jsruntime/qv4script.cpp
@@ -57,10 +57,11 @@
#include <QtCore/QDebug>
#include <QtCore/QString>
+#include <QScopedValueRollback>
using namespace QV4;
-Script::Script(ExecutionEngine *v4, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit)
+Script::Script(ExecutionEngine *v4, QmlContext *qml, const QQmlRefPointer<CompiledData::CompilationUnit> &compilationUnit)
: line(1), column(0), context(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false)
, compilationUnit(compilationUnit), vmFunction(nullptr), parseAsBinding(true)
{
@@ -128,7 +129,7 @@ void Script::parse()
RuntimeCodegen cg(v4, &jsGenerator, strictMode);
if (inheritContext)
cg.setUseFastLookups(false);
- cg.generateFromProgram(sourceFile, sourceFile, sourceCode, program, &module, compilationMode);
+ cg.generateFromProgram(sourceFile, sourceFile, sourceCode, program, &module, contextType);
if (v4->hasException)
return;
@@ -154,7 +155,7 @@ ReturnedValue Script::run(const QV4::Value *thisObject)
QV4::Scope valueScope(engine);
if (qmlContext.isUndefined()) {
- TemporaryAssignment<Function*> savedGlobalCode(engine->globalCode, vmFunction);
+ QScopedValueRollback<Function*> savedGlobalCode(engine->globalCode, vmFunction);
return vmFunction->call(thisObject ? thisObject : engine->globalObject, nullptr, 0,
context);
@@ -171,39 +172,20 @@ Function *Script::function()
return vmFunction;
}
-QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator,
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, Compiler::JSUnitGenerator *unitGenerator,
const QString &fileName, const QString &finalUrl, const QString &source,
- QList<QQmlError> *reportedErrors, Directives *directivesCollector)
+ QList<QQmlError> *reportedErrors)
{
using namespace QV4::Compiler;
using namespace QQmlJS::AST;
- Engine ee;
- if (directivesCollector)
- ee.setDirectives(directivesCollector);
- Lexer lexer(&ee);
+ Lexer lexer(jsEngine);
lexer.setCode(source, /*line*/1, /*qml mode*/false);
- Parser parser(&ee);
+ Parser parser(jsEngine);
parser.parseProgram();
- QList<QQmlError> errors;
-
- const auto diagnosticMessages = parser.diagnosticMessages();
- for (const DiagnosticMessage &m : diagnosticMessages) {
- if (m.isWarning()) {
- qWarning("%s:%d : %s", qPrintable(fileName), m.loc.startLine, qPrintable(m.message));
- continue;
- }
-
- QQmlError error;
- error.setUrl(QUrl(fileName));
- error.setDescription(m.message);
- error.setLine(m.loc.startLine);
- error.setColumn(m.loc.startColumn);
- errors << error;
- }
-
+ QList<QQmlError> errors = QQmlEnginePrivate::qmlErrorFromDiagnostics(fileName, parser.diagnosticMessages());
if (!errors.isEmpty()) {
if (reportedErrors)
*reportedErrors << errors;
@@ -219,7 +201,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compi
Codegen cg(unitGenerator, /*strict mode*/false);
cg.setUseFastLookups(false);
- cg.generateFromProgram(fileName, finalUrl, source, program, module, GlobalCode);
+ cg.generateFromProgram(fileName, finalUrl, source, program, module, ContextType::Global);
errors = cg.qmlErrors();
if (!errors.isEmpty()) {
if (reportedErrors)
diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h
index b4ac150044..e7189664e2 100644
--- a/src/qml/jsruntime/qv4script_p.h
+++ b/src/qml/jsruntime/qv4script_p.h
@@ -62,12 +62,16 @@ QT_BEGIN_NAMESPACE
class QQmlContextData;
+namespace QQmlJS {
+class Engine;
+}
+
namespace QV4 {
struct Q_QML_EXPORT Script {
- Script(ExecutionContext *scope, QV4::Compiler::CompilationMode mode, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0)
+ Script(ExecutionContext *scope, QV4::Compiler::ContextType mode, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0)
: sourceFile(source), line(line), column(column), sourceCode(sourceCode)
- , context(scope), strictMode(false), inheritContext(false), parsed(false), compilationMode(mode)
+ , context(scope), strictMode(false), inheritContext(false), parsed(false), contextType(mode)
, vmFunction(nullptr), parseAsBinding(false) {}
Script(ExecutionEngine *engine, QmlContext *qml, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0)
: sourceFile(source), line(line), column(column), sourceCode(sourceCode)
@@ -76,7 +80,7 @@ struct Q_QML_EXPORT Script {
if (qml)
qmlContext.set(engine, *qml);
}
- Script(ExecutionEngine *engine, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit);
+ Script(ExecutionEngine *engine, QmlContext *qml, const QQmlRefPointer<CompiledData::CompilationUnit> &compilationUnit);
~Script();
QString sourceFile;
int line;
@@ -86,7 +90,7 @@ struct Q_QML_EXPORT Script {
bool strictMode;
bool inheritContext;
bool parsed;
- QV4::Compiler::CompilationMode compilationMode = QV4::Compiler::EvalCode;
+ QV4::Compiler::ContextType contextType = QV4::Compiler::ContextType::Eval;
QV4::PersistentValue qmlContext;
QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit;
Function *vmFunction;
@@ -97,10 +101,9 @@ struct Q_QML_EXPORT Script {
Function *function();
- static QQmlRefPointer<CompiledData::CompilationUnit> precompile(
- QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator,
+ static QQmlRefPointer<CompiledData::CompilationUnit> precompile(QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, Compiler::JSUnitGenerator *unitGenerator,
const QString &fileName, const QString &finalUrl, const QString &source,
- QList<QQmlError> *reportedErrors = nullptr, QQmlJS::Directives *directivesCollector = nullptr);
+ QList<QQmlError> *reportedErrors = nullptr);
static Script *createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlContext, const QString &fileName, const QUrl &originalUrl, QString *error);
static ReturnedValue evaluate(ExecutionEngine *engine, const QString &script, QmlContext *qmlContext);
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index 7d29d0b517..1eef12a491 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -227,7 +227,7 @@ namespace Heap {
template <typename Container>
struct QQmlSequence : Object {
void init(const Container &container);
- void init(QObject *object, int propertyIndex);
+ void init(QObject *object, int propertyIndex, bool readOnly);
void destroy() {
delete container;
object.destroy();
@@ -237,7 +237,8 @@ struct QQmlSequence : Object {
mutable Container *container;
QQmlQPointer<QObject> object;
int propertyIndex;
- bool isReference;
+ bool isReference : 1;
+ bool isReadOnly : 1;
};
}
@@ -294,6 +295,9 @@ public:
return false;
}
+ if (d()->isReadOnly)
+ return false;
+
if (d()->isReference) {
if (!d()->object)
return false;
@@ -338,27 +342,37 @@ public:
return (index < size_t(d()->container->size())) ? QV4::Attr_Data : QV4::Attr_Invalid;
}
- void containerAdvanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs)
+ struct OwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
{
- name->setM(nullptr);
- *index = UINT_MAX;
+ ~OwnPropertyKeyIterator() override = default;
+ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override
+ {
+ const QQmlSequence *s = static_cast<const QQmlSequence *>(o);
- if (d()->isReference) {
- if (!d()->object) {
- QV4::Object::advanceIterator(this, it, name, index, p, attrs);
- return;
+ if (s->d()->isReference) {
+ if (!s->d()->object)
+ return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
+ s->loadReference();
}
- loadReference();
- }
- if (it->arrayIndex < static_cast<uint>(d()->container->size())) {
- *index = it->arrayIndex;
- ++it->arrayIndex;
- *attrs = QV4::Attr_Data;
- p->value = convertElementToValue(engine(), d()->container->at(*index));
- return;
+ if (arrayIndex < static_cast<uint>(s->d()->container->size())) {
+ uint index = arrayIndex;
+ ++arrayIndex;
+ if (attrs)
+ *attrs = QV4::Attr_Data;
+ if (pd)
+ pd->value = convertElementToValue(s->engine(), s->d()->container->at(index));
+ return PropertyKey::fromArrayIndex(index);
+ }
+
+ return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
}
- QV4::Object::advanceIterator(this, it, name, index, p, attrs);
+ };
+
+ static OwnPropertyKeyIterator *containerOwnPropertyKeys(const Object *m, Value *target)
+ {
+ *target = *m;
+ return new OwnPropertyKeyIterator;
}
bool containerDeleteIndexedProperty(uint index)
@@ -366,6 +380,8 @@ public:
/* Qt containers have int (rather than uint) allowable indexes. */
if (index > INT_MAX)
return false;
+ if (d()->isReadOnly)
+ return false;
if (d()->isReference) {
if (!d()->object)
return false;
@@ -432,11 +448,13 @@ public:
const QV4::Value *m_compareFn;
};
- void sort(const FunctionObject *f, const Value *, const Value *argv, int argc)
+ bool sort(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
+ if (d()->isReadOnly)
+ return false;
if (d()->isReference) {
if (!d()->object)
- return;
+ return false;
loadReference();
}
@@ -450,6 +468,8 @@ public:
if (d()->isReference)
storeReference();
+
+ return true;
}
static QV4::ReturnedValue method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -480,6 +500,10 @@ public:
generateWarning(scope.engine, QLatin1String("Index out of range during length set"));
RETURN_UNDEFINED();
}
+
+ if (This->d()->isReadOnly)
+ THROW_TYPE_ERROR();
+
/* Read the sequence from the QObject property if we're a reference */
if (This->d()->isReference) {
if (!This->d()->object)
@@ -524,7 +548,7 @@ public:
quint32 length = array->getLength();
QV4::ScopedValue v(scope);
for (quint32 i = 0; i < length; ++i)
- result.push_back(convertValueToElement<typename Container::value_type>((v = array->getIndexed(i))));
+ result.push_back(convertValueToElement<typename Container::value_type>((v = array->get(i))));
return QVariant::fromValue(result);
}
@@ -549,18 +573,32 @@ public:
QMetaObject::metacall(d()->object, QMetaObject::WriteProperty, d()->propertyIndex, a);
}
- static QV4::ReturnedValue getIndexed(const QV4::Managed *that, uint index, bool *hasProperty)
- { return static_cast<const QQmlSequence<Container> *>(that)->containerGetIndexed(index, hasProperty); }
- static bool putIndexed(Managed *that, uint index, const QV4::Value &value)
- { return static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(index, value); }
+ static QV4::ReturnedValue virtualGet(const QV4::Managed *that, PropertyKey id, const Value *receiver, bool *hasProperty)
+ {
+ if (!id.isArrayIndex())
+ return Object::virtualGet(that, id, receiver, hasProperty);
+ return static_cast<const QQmlSequence<Container> *>(that)->containerGetIndexed(id.asArrayIndex(), hasProperty);
+ }
+ static bool virtualPut(Managed *that, PropertyKey id, const QV4::Value &value, Value *receiver)
+ {
+ if (id.isArrayIndex())
+ return static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(id.asArrayIndex(), value);
+ return Object::virtualPut(that, id, value, receiver);
+ }
static QV4::PropertyAttributes queryIndexed(const QV4::Managed *that, uint index)
{ return static_cast<const QQmlSequence<Container> *>(that)->containerQueryIndexed(index); }
- static bool deleteIndexedProperty(QV4::Managed *that, uint index)
- { return static_cast<QQmlSequence<Container> *>(that)->containerDeleteIndexedProperty(index); }
- static bool isEqualTo(Managed *that, Managed *other)
+ static bool virtualDeleteProperty(QV4::Managed *that, PropertyKey id)
+ {
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+ return static_cast<QQmlSequence<Container> *>(that)->containerDeleteIndexedProperty(index);
+ }
+ return Object::virtualDeleteProperty(that, id);
+ }
+ static bool virtualIsEqualTo(Managed *that, Managed *other)
{ return static_cast<QQmlSequence<Container> *>(that)->containerIsEqualTo(other); }
- static void advanceIterator(Managed *that, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs)
- { return static_cast<QQmlSequence<Container> *>(that)->containerAdvanceIterator(it, name, index, p, attrs); }
+ static QV4::OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target)
+ { return static_cast<const QQmlSequence<Container> *>(m)->containerOwnPropertyKeys(m, target);}
};
@@ -572,6 +610,7 @@ void Heap::QQmlSequence<Container>::init(const Container &container)
this->container = new Container(container);
propertyIndex = -1;
isReference = false;
+ isReadOnly = false;
object.init();
QV4::Scope scope(internalClass->engine);
@@ -581,12 +620,13 @@ void Heap::QQmlSequence<Container>::init(const Container &container)
}
template <typename Container>
-void Heap::QQmlSequence<Container>::init(QObject *object, int propertyIndex)
+void Heap::QQmlSequence<Container>::init(QObject *object, int propertyIndex, bool readOnly)
{
Object::init();
this->container = new Container;
this->propertyIndex = propertyIndex;
isReference = true;
+ this->isReadOnly = readOnly;
this->object.init(object);
QV4::Scope scope(internalClass->engine);
QV4::Scoped<QV4::QQmlSequence<Container> > o(scope, this);
@@ -668,7 +708,8 @@ ReturnedValue SequencePrototype::method_sort(const FunctionObject *b, const Valu
#define CALL_SORT(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \
if (QQml##SequenceElementTypeName##List *s = o->as<QQml##SequenceElementTypeName##List>()) { \
- s->sort(b, thisObject, argv, argc); \
+ if (!s->sort(b, thisObject, argv, argc)) \
+ THROW_TYPE_ERROR(); \
} else
FOREACH_QML_SEQUENCE_TYPE(CALL_SORT)
@@ -691,11 +732,11 @@ bool SequencePrototype::isSequenceType(int sequenceTypeId)
#define NEW_REFERENCE_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \
if (sequenceType == qMetaTypeId<SequenceType>()) { \
- QV4::ScopedObject obj(scope, engine->memoryManager->allocObject<QQml##ElementTypeName##List>(object, propertyIndex)); \
+ QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QQml##ElementTypeName##List>(object, propertyIndex, readOnly)); \
return obj.asReturnedValue(); \
} else
-ReturnedValue SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int sequenceType, QObject *object, int propertyIndex, bool *succeeded)
+ReturnedValue SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int sequenceType, QObject *object, int propertyIndex, bool readOnly, bool *succeeded)
{
QV4::Scope scope(engine);
// This function is called when the property is a QObject Q_PROPERTY of
@@ -709,7 +750,7 @@ ReturnedValue SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int s
#define NEW_COPY_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \
if (sequenceType == qMetaTypeId<SequenceType>()) { \
- QV4::ScopedObject obj(scope, engine->memoryManager->allocObject<QQml##ElementTypeName##List>(v.value<SequenceType >())); \
+ QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QQml##ElementTypeName##List>(v.value<SequenceType >())); \
return obj.asReturnedValue(); \
} else
diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h
index e9bef2f604..da71215bed 100644
--- a/src/qml/jsruntime/qv4sequenceobject_p.h
+++ b/src/qml/jsruntime/qv4sequenceobject_p.h
@@ -59,6 +59,8 @@
#include "qv4context_p.h"
#include "qv4string_p.h"
+QT_REQUIRE_CONFIG(qml_sequence_object);
+
QT_BEGIN_NAMESPACE
namespace QV4 {
@@ -72,7 +74,7 @@ struct SequencePrototype : public QV4::Object
static ReturnedValue method_sort(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static bool isSequenceType(int sequenceTypeId);
- static ReturnedValue newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool *succeeded);
+ static ReturnedValue newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool readOnly, bool *succeeded);
static ReturnedValue fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded);
static int metaTypeForSequence(const Object *object);
static QVariant toVariant(Object *object);
diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp
index 14def49d0a..50871a4d87 100644
--- a/src/qml/jsruntime/qv4serialize.cpp
+++ b/src/qml/jsruntime/qv4serialize.cpp
@@ -40,13 +40,17 @@
#include "qv4serialize_p.h"
#include <private/qv8engine_p.h>
+#if QT_CONFIG(qml_list_model)
#include <private/qqmllistmodel_p.h>
#include <private/qqmllistmodelworkeragent_p.h>
+#endif
#include <private/qv4value_p.h>
#include <private/qv4dateobject_p.h>
#include <private/qv4regexpobject_p.h>
+#if QT_CONFIG(qml_sequence_object)
#include <private/qv4sequenceobject_p.h>
+#endif
#include <private/qv4objectproto_p.h>
#include <private/qv4qobjectwrapper_p.h>
@@ -82,8 +86,12 @@ enum Type {
WorkerNumber,
WorkerDate,
WorkerRegexp,
+#if QT_CONFIG(qml_list_model)
WorkerListModel,
+#endif
+#if QT_CONFIG(qml_sequence_object)
WorkerSequence
+#endif
};
static inline quint32 valueheader(Type type, quint32 size = 0)
@@ -189,7 +197,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine
push(data, valueheader(WorkerArray, length));
ScopedValue val(scope);
for (uint ii = 0; ii < length; ++ii)
- serialize(data, (val = array->getIndexed(ii)), engine);
+ serialize(data, (val = array->get(ii)), engine);
} else if (v.isInteger()) {
reserve(data, 2 * sizeof(quint32));
push(data, valueheader(WorkerInt32));
@@ -228,6 +236,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine
} else if (const QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) {
// XXX TODO: Generalize passing objects between the main thread and worker scripts so
// that others can trivially plug in their elements.
+#if QT_CONFIG(qml_list_model)
QQmlListModel *lm = qobject_cast<QQmlListModel *>(qobjectWrapper->object());
if (lm && lm->agent()) {
QQmlListModelWorkerAgent *agent = lm->agent();
@@ -236,9 +245,13 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine
push(data, (void *)agent);
return;
}
+#else
+ Q_UNUSED(qobjectWrapper);
+#endif
// No other QObject's are allowed to be sent
push(data, valueheader(WorkerUndefined));
} else if (const Object *o = v.as<Object>()) {
+#if QT_CONFIG(qml_sequence_object)
if (o->isListType()) {
// valid sequence. we generate a length (sequence length + 1 for the sequence type)
uint seqLength = ScopedValue(scope, o->get(engine->id_length()))->toUInt32();
@@ -249,13 +262,14 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine
}
reserve(data, sizeof(quint32) + length * sizeof(quint32));
push(data, valueheader(WorkerSequence, length));
- serialize(data, QV4::Primitive::fromInt32(QV4::SequencePrototype::metaTypeForSequence(o)), engine); // sequence type
+ serialize(data, QV4::Value::fromInt32(QV4::SequencePrototype::metaTypeForSequence(o)), engine); // sequence type
ScopedValue val(scope);
for (uint ii = 0; ii < seqLength; ++ii)
- serialize(data, (val = o->getIndexed(ii)), engine); // sequence elements
+ serialize(data, (val = o->get(ii)), engine); // sequence elements
return;
}
+#endif
// regular object
QV4::ScopedValue val(scope, v);
@@ -269,7 +283,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine
QV4::ScopedValue s(scope);
for (quint32 ii = 0; ii < length; ++ii) {
- s = properties->getIndexed(ii);
+ s = properties->get(ii);
serialize(data, s, engine);
QV4::String *str = s->as<String>();
@@ -318,7 +332,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
ScopedValue v(scope);
for (quint32 ii = 0; ii < size; ++ii) {
v = deserialize(data, engine);
- a->putIndexed(ii, v);
+ a->put(ii, v);
}
return a.asReturnedValue();
}
@@ -344,7 +358,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
case WorkerNumber:
return QV4::Encode(popDouble(data));
case WorkerDate:
- return QV4::Encode(engine->newDateObject(QV4::Primitive::fromDouble(popDouble(data))));
+ return QV4::Encode(engine->newDateObject(QV4::Value::fromDouble(popDouble(data))));
case WorkerRegexp:
{
quint32 flags = headersize(header);
@@ -353,6 +367,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
data += ALIGN(length * sizeof(quint16));
return Encode(engine->newRegExpObject(pattern, flags));
}
+#if QT_CONFIG(qml_list_model)
case WorkerListModel:
{
void *ptr = popPtr(data);
@@ -369,6 +384,8 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
agent->setEngine(engine);
return rv->asReturnedValue();
}
+#endif
+#if QT_CONFIG(qml_sequence_object)
case WorkerSequence:
{
ScopedValue value(scope);
@@ -387,6 +404,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
QVariant seqVariant = QV4::SequencePrototype::toVariant(array, sequenceType, &succeeded);
return QV4::SequencePrototype::fromVariant(engine, seqVariant, &succeeded);
}
+#endif
}
Q_ASSERT(!"Unreachable");
return QV4::Encode::undefined();
diff --git a/src/qml/jsruntime/qv4setiterator.cpp b/src/qml/jsruntime/qv4setiterator.cpp
new file mode 100644
index 0000000000..d32e2079a0
--- /dev/null
+++ b/src/qml/jsruntime/qv4setiterator.cpp
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qv4iterator_p.h>
+#include <private/qv4estable_p.h>
+#include <private/qv4setiterator_p.h>
+#include <private/qv4setobject_p.h>
+#include <private/qv4symbol_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(SetIteratorObject);
+
+void SetIteratorPrototype::init(ExecutionEngine *e)
+{
+ defineDefaultProperty(QStringLiteral("next"), method_next, 0);
+
+ Scope scope(e);
+ ScopedString val(scope, e->newString(QLatin1String("Set Iterator")));
+ defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val);
+}
+
+ReturnedValue SetIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int)
+{
+ Scope scope(b);
+ const SetIteratorObject *thisObject = that->as<SetIteratorObject>();
+ if (!thisObject)
+ return scope.engine->throwTypeError(QLatin1String("Not a Set Iterator instance"));
+
+ Scoped<SetObject> s(scope, thisObject->d()->iteratedSet);
+ uint index = thisObject->d()->setNextIndex;
+ IteratorKind itemKind = thisObject->d()->iterationKind;
+
+ if (!s) {
+ QV4::Value undefined = Value::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+ }
+
+ Value *arguments = scope.alloc(2);
+
+ while (index < s->d()->esTable->size()) {
+ s->d()->esTable->iterate(index, &arguments[0], &arguments[1]);
+ thisObject->d()->setNextIndex = index + 1;
+
+ if (itemKind == KeyValueIteratorKind) {
+ ScopedArrayObject resultArray(scope, scope.engine->newArrayObject());
+ resultArray->arrayReserve(2);
+ resultArray->arrayPut(0, arguments[0]);
+ resultArray->arrayPut(1, arguments[0]); // yes, the key is repeated.
+ resultArray->setArrayLengthUnchecked(2);
+
+ return IteratorPrototype::createIterResultObject(scope.engine, resultArray, false);
+ }
+
+ return IteratorPrototype::createIterResultObject(scope.engine, arguments[0], false);
+ }
+
+ thisObject->d()->iteratedSet.set(scope.engine, nullptr);
+ QV4::Value undefined = Value::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+}
+
diff --git a/src/qml/jsruntime/qv4setiterator_p.h b/src/qml/jsruntime/qv4setiterator_p.h
new file mode 100644
index 0000000000..78eda6d57b
--- /dev/null
+++ b/src/qml/jsruntime/qv4setiterator_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** 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 QV4SETITERATOR_P_H
+#define QV4SETITERATOR_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 "qv4object_p.h"
+#include "qv4iterator_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Heap {
+
+#define SetIteratorObjectMembers(class, Member) \
+ Member(class, Pointer, Object *, iteratedSet) \
+ Member(class, NoMark, IteratorKind, iterationKind) \
+ Member(class, NoMark, quint32, setNextIndex)
+
+DECLARE_HEAP_OBJECT(SetIteratorObject, Object) {
+ DECLARE_MARKOBJECTS(SetIteratorObject);
+ void init(Object *obj, QV4::ExecutionEngine *engine)
+ {
+ Object::init();
+ this->iteratedSet.set(engine, obj);
+ this->setNextIndex = 0;
+ }
+};
+
+}
+
+struct SetIteratorPrototype : Object
+{
+ V4_PROTOTYPE(iteratorPrototype)
+ void init(ExecutionEngine *engine);
+
+ static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct SetIteratorObject : Object
+{
+ V4_OBJECT2(SetIteratorObject, Object)
+ Q_MANAGED_TYPE(SetIteratorObject)
+ V4_PROTOTYPE(setIteratorPrototype)
+
+ void init(ExecutionEngine *engine);
+};
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4SETITERATOR_P_H
+
diff --git a/src/qml/jsruntime/qv4setobject.cpp b/src/qml/jsruntime/qv4setobject.cpp
new file mode 100644
index 0000000000..3c9b5031d1
--- /dev/null
+++ b/src/qml/jsruntime/qv4setobject.cpp
@@ -0,0 +1,323 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** 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 "qv4setobject_p.h"
+#include "qv4setiterator_p.h"
+#include "qv4estable_p.h"
+#include "qv4symbol_p.h"
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(SetCtor);
+DEFINE_OBJECT_VTABLE(WeakSetCtor);
+DEFINE_OBJECT_VTABLE(SetObject);
+
+void Heap::WeakSetCtor::init(QV4::ExecutionContext *scope)
+{
+ Heap::FunctionObject::init(scope, QStringLiteral("WeakSet"));
+}
+
+void Heap::SetCtor::init(QV4::ExecutionContext *scope)
+{
+ Heap::FunctionObject::init(scope, QStringLiteral("Set"));
+}
+
+ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv, int argc, const Value *, bool isWeak)
+{
+ Scope scope(f);
+ Scoped<SetObject> a(scope, scope.engine->memoryManager->allocate<SetObject>());
+ if (isWeak)
+ a->setPrototypeOf(scope.engine->weakSetPrototype());
+ a->d()->isWeakSet = isWeak;
+
+ if (argc > 0) {
+ ScopedValue iterable(scope, argv[0]);
+ if (!iterable->isUndefined() && !iterable->isNull()) {
+ ScopedFunctionObject adder(scope, a->get(ScopedString(scope, scope.engine->newString(QString::fromLatin1("add")))));
+ if (!adder)
+ return scope.engine->throwTypeError();
+ ScopedObject iter(scope, Runtime::method_getIterator(scope.engine, iterable, true));
+ CHECK_EXCEPTION();
+ if (!iter)
+ return a.asReturnedValue();
+
+ Value *nextValue = scope.alloc(1);
+ ScopedValue done(scope);
+ forever {
+ done = Runtime::method_iteratorNext(scope.engine, iter, nextValue);
+ CHECK_EXCEPTION();
+ if (done->toBoolean())
+ return a.asReturnedValue();
+
+ adder->call(a, nextValue, 1);
+ if (scope.engine->hasException) {
+ ScopedValue falsey(scope, Encode(false));
+ return Runtime::method_iteratorClose(scope.engine, iter, falsey);
+ }
+ }
+ }
+ }
+
+ return a.asReturnedValue();
+}
+
+ReturnedValue WeakSetCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
+{
+ return construct(f, argv, argc, newTarget, true);
+}
+
+ReturnedValue WeakSetCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
+{
+ Scope scope(f);
+ return scope.engine->throwTypeError(QString::fromLatin1("Set requires new"));
+}
+
+ReturnedValue SetCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
+{
+ return construct(f, argv, argc, newTarget, false);
+}
+
+void WeakSetPrototype::init(ExecutionEngine *engine, Object *ctor)
+{
+ Scope scope(engine);
+ ScopedObject o(scope);
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(0));
+ ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
+ defineDefaultProperty(engine->id_constructor(), (o = ctor));
+
+ defineDefaultProperty(QStringLiteral("add"), method_add, 1);
+ defineDefaultProperty(QStringLiteral("delete"), method_delete, 1);
+ defineDefaultProperty(QStringLiteral("has"), method_has, 1);
+
+ ScopedString val(scope, engine->newString(QLatin1String("WeakSet")));
+ defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), val);
+}
+
+ReturnedValue WeakSetPrototype::method_add(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if ((!that || !that->d()->isWeakSet) ||
+ (!argc || !argv[0].isObject()))
+ return scope.engine->throwTypeError();
+
+ that->d()->esTable->set(argv[0], Value::undefinedValue());
+ return that.asReturnedValue();
+}
+
+ReturnedValue WeakSetPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that || !that->d()->isWeakSet)
+ return scope.engine->throwTypeError();
+ if (!argc || !argv[0].isObject())
+ return Encode(false);
+
+ return Encode(that->d()->esTable->remove(argv[0]));
+}
+
+ReturnedValue WeakSetPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that || !that->d()->isWeakSet)
+ return scope.engine->throwTypeError();
+ if (!argc || !argv[0].isObject())
+ return Encode(false);
+
+ return Encode(that->d()->esTable->has(argv[0]));
+}
+
+void SetPrototype::init(ExecutionEngine *engine, Object *ctor)
+{
+ Scope scope(engine);
+ ScopedObject o(scope);
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(0));
+ ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
+ ctor->addSymbolSpecies();
+ defineDefaultProperty(engine->id_constructor(), (o = ctor));
+
+ defineDefaultProperty(QStringLiteral("add"), method_add, 1);
+ defineDefaultProperty(QStringLiteral("clear"), method_clear, 0);
+ defineDefaultProperty(QStringLiteral("delete"), method_delete, 1);
+ defineDefaultProperty(QStringLiteral("entries"), method_entries, 0);
+ defineDefaultProperty(QStringLiteral("forEach"), method_forEach, 1);
+ defineDefaultProperty(QStringLiteral("has"), method_has, 1);
+ defineAccessorProperty(QStringLiteral("size"), method_get_size, nullptr);
+
+ // Per the spec, the value for 'keys' is the same as 'values'.
+ ScopedString valString(scope, scope.engine->newIdentifier(QStringLiteral("values")));
+ ScopedFunctionObject valuesFn(scope, FunctionObject::createBuiltinFunction(engine, valString, SetPrototype::method_values, 0));
+ defineDefaultProperty(QStringLiteral("keys"), valuesFn);
+ defineDefaultProperty(QStringLiteral("values"), valuesFn);
+
+ defineDefaultProperty(engine->symbol_iterator(), valuesFn);
+
+ ScopedString val(scope, engine->newString(QLatin1String("Set")));
+ defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), val);
+}
+
+void Heap::SetObject::init()
+{
+ Object::init();
+ esTable = new ESTable();
+}
+
+void Heap::SetObject::destroy()
+{
+ delete esTable;
+ esTable = 0;
+}
+
+void Heap::SetObject::removeUnmarkedKeys()
+{
+ esTable->removeUnmarkedKeys();
+}
+
+void Heap::SetObject::markObjects(Heap::Base *that, MarkStack *markStack)
+{
+ SetObject *s = static_cast<SetObject *>(that);
+ s->esTable->markObjects(markStack, s->isWeakSet);
+ Object::markObjects(that, markStack);
+}
+
+ReturnedValue SetPrototype::method_add(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that || that->d()->isWeakSet)
+ return scope.engine->throwTypeError();
+
+ that->d()->esTable->set(argv[0], Value::undefinedValue());
+ return that.asReturnedValue();
+}
+
+ReturnedValue SetPrototype::method_clear(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that || that->d()->isWeakSet)
+ return scope.engine->throwTypeError();
+
+ that->d()->esTable->clear();
+ return Encode::undefined();
+}
+
+ReturnedValue SetPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that || that->d()->isWeakSet)
+ return scope.engine->throwTypeError();
+
+ return Encode(that->d()->esTable->remove(argv[0]));
+}
+
+ReturnedValue SetPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that || that->d()->isWeakSet)
+ return scope.engine->throwTypeError();
+
+ Scoped<SetIteratorObject> ao(scope, scope.engine->newSetIteratorObject(that));
+ ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue SetPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that || that->d()->isWeakSet)
+ return scope.engine->throwTypeError();
+
+ ScopedFunctionObject callbackfn(scope, argv[0]);
+ if (!callbackfn)
+ return scope.engine->throwTypeError();
+
+ ScopedValue thisArg(scope, Value::undefinedValue());
+ if (argc > 1)
+ thisArg = ScopedValue(scope, argv[1]);
+
+ Value *arguments = scope.alloc(3);
+ for (uint i = 0; i < that->d()->esTable->size(); ++i) {
+ that->d()->esTable->iterate(i, &arguments[0], &arguments[1]); // fill in key (0), value (1)
+ arguments[1] = arguments[0]; // but for set, we want to return the key twice; value is always undefined.
+
+ arguments[2] = that;
+ callbackfn->call(thisArg, arguments, 3);
+ CHECK_EXCEPTION();
+ }
+ return Encode::undefined();
+}
+
+ReturnedValue SetPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that || that->d()->isWeakSet)
+ return scope.engine->throwTypeError();
+
+ return Encode(that->d()->esTable->has(argv[0]));
+}
+
+ReturnedValue SetPrototype::method_get_size(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that || that->d()->isWeakSet)
+ return scope.engine->throwTypeError();
+
+ return Encode(that->d()->esTable->size());
+}
+
+ReturnedValue SetPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that || that->d()->isWeakSet)
+ return scope.engine->throwTypeError();
+
+ Scoped<SetIteratorObject> ao(scope, scope.engine->newSetIteratorObject(that));
+ ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
+ return ao->asReturnedValue();
+}
diff --git a/src/qml/jsruntime/qv4setobject_p.h b/src/qml/jsruntime/qv4setobject_p.h
new file mode 100644
index 0000000000..21584e2132
--- /dev/null
+++ b/src/qml/jsruntime/qv4setobject_p.h
@@ -0,0 +1,144 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** 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 QV4SETOBJECT_P_H
+#define QV4SETOBJECT_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 "qv4object_p.h"
+#include "qv4objectproto_p.h"
+#include "qv4functionobject_p.h"
+#include "qv4string_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+class ESTable;
+
+namespace Heap {
+
+struct WeakSetCtor : FunctionObject {
+ void init(QV4::ExecutionContext *scope);
+};
+
+
+struct SetCtor : WeakSetCtor {
+ void init(QV4::ExecutionContext *scope);
+};
+
+struct SetObject : Object {
+ static void markObjects(Heap::Base *that, MarkStack *markStack);
+ void init();
+ void destroy();
+ void removeUnmarkedKeys();
+
+ ESTable *esTable;
+ SetObject *nextWeakSet;
+ bool isWeakSet;
+};
+
+}
+
+
+struct WeakSetCtor: FunctionObject
+{
+ V4_OBJECT2(WeakSetCtor, FunctionObject)
+
+ static ReturnedValue construct(const FunctionObject *f, const Value *argv, int argc, const Value *, bool weakSet);
+
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct SetCtor : WeakSetCtor
+{
+ V4_OBJECT2(SetCtor, WeakSetCtor)
+
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+};
+
+struct SetObject : Object
+{
+ V4_OBJECT2(SetObject, Object)
+ V4_PROTOTYPE(setPrototype)
+ V4_NEEDS_DESTROY
+};
+
+struct WeakSetPrototype : Object
+{
+ void init(ExecutionEngine *engine, Object *ctor);
+
+ static ReturnedValue method_add(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_delete(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+
+struct SetPrototype : WeakSetPrototype
+{
+ void init(ExecutionEngine *engine, Object *ctor);
+
+ static ReturnedValue method_add(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_clear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_delete(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_size(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+
+} // namespace QV4
+
+
+QT_END_NAMESPACE
+
+#endif // QV4SETOBJECT_P_H
diff --git a/src/qml/jsruntime/qv4sparsearray.cpp b/src/qml/jsruntime/qv4sparsearray.cpp
index 2a3e28bf63..8930c9a94d 100644
--- a/src/qml/jsruntime/qv4sparsearray.cpp
+++ b/src/qml/jsruntime/qv4sparsearray.cpp
@@ -395,7 +395,7 @@ void SparseArray::freeTree(SparseArrayNode *root, int alignment)
SparseArray::SparseArray()
: numEntries(0)
{
- freeList = Primitive::emptyValue(UINT_MAX).asReturnedValue();
+ freeList = Encode(-1);
header.p = 0;
header.left = nullptr;
header.right = nullptr;
diff --git a/src/qml/jsruntime/qv4sparsearray_p.h b/src/qml/jsruntime/qv4sparsearray_p.h
index 51869b259f..c1e50c8dcf 100644
--- a/src/qml/jsruntime/qv4sparsearray_p.h
+++ b/src/qml/jsruntime/qv4sparsearray_p.h
@@ -52,6 +52,7 @@
//
#include "qv4global_p.h"
+#include "qv4value_p.h"
#include <QtCore/qlist.h>
//#define Q_MAP_DEBUG
@@ -151,7 +152,7 @@ struct Q_QML_EXPORT SparseArray
SparseArray(const SparseArray &other);
- ReturnedValue freeList;
+ Value freeList;
private:
SparseArray &operator=(const SparseArray &other);
diff --git a/src/qml/jsruntime/qv4stackframe.cpp b/src/qml/jsruntime/qv4stackframe.cpp
new file mode 100644
index 0000000000..a716c53aea
--- /dev/null
+++ b/src/qml/jsruntime/qv4stackframe.cpp
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** 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 "qv4stackframe_p.h"
+#include <QtCore/qstring.h>
+
+using namespace QV4;
+
+QString CppStackFrame::source() const
+{
+ return v4Function ? v4Function->sourceFile() : QString();
+}
+
+QString CppStackFrame::function() const
+{
+ return v4Function ? v4Function->name()->toQString() : QString();
+}
+
+int CppStackFrame::lineNumber() const
+{
+ if (!v4Function)
+ return -1;
+
+ auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) {
+ return entry.codeOffset < offset;
+ };
+
+ const QV4::CompiledData::Function *cf = v4Function->compiledFunction;
+ uint offset = instructionPointer;
+ const CompiledData::CodeOffsetToLine *lineNumbers = cf->lineNumberTable();
+ uint nLineNumbers = cf->nLineNumbers;
+ const CompiledData::CodeOffsetToLine *line = std::lower_bound(lineNumbers, lineNumbers + nLineNumbers, offset, findLine) - 1;
+ return line->line;
+}
+
+ReturnedValue CppStackFrame::thisObject() const {
+ return jsFrame->thisObject.asReturnedValue();
+}
+
diff --git a/src/qml/jsruntime/qv4stackframe_p.h b/src/qml/jsruntime/qv4stackframe_p.h
new file mode 100644
index 0000000000..a97ae0e7c9
--- /dev/null
+++ b/src/qml/jsruntime/qv4stackframe_p.h
@@ -0,0 +1,221 @@
+/****************************************************************************
+**
+** 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 QV4STACKFRAME_H
+#define QV4STACKFRAME_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/qv4context_p.h>
+#include <private/qv4enginebase_p.h>
+#ifndef V4_BOOTSTRAP
+#include <private/qv4function_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct CallData
+{
+ enum Offsets {
+ Function = 0,
+ Context = 1,
+ Accumulator = 2,
+ This = 3,
+ NewTarget = 4,
+ Argc = 5,
+
+ LastOffset = Argc,
+ OffsetCount = LastOffset + 1
+ };
+
+ Value function;
+ Value context;
+ Value accumulator;
+ Value thisObject;
+ Value newTarget;
+ Value _argc;
+
+ int argc() const {
+ Q_ASSERT(_argc.isInteger());
+ return _argc.int_32();
+ }
+
+ void setArgc(int argc) {
+ Q_ASSERT(argc >= 0);
+ _argc.setInt_32(argc);
+ }
+
+ inline ReturnedValue argument(int i) const {
+ return i < argc() ? args[i].asReturnedValue() : Value::undefinedValue().asReturnedValue();
+ }
+
+ Value args[1];
+
+ static Q_DECL_CONSTEXPR int HeaderSize() { return offsetof(CallData, args) / sizeof(QV4::Value); }
+};
+
+Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value);
+Q_STATIC_ASSERT(offsetof(CallData, function ) == CallData::Function * sizeof(Value));
+Q_STATIC_ASSERT(offsetof(CallData, context ) == CallData::Context * sizeof(Value));
+Q_STATIC_ASSERT(offsetof(CallData, accumulator) == CallData::Accumulator * sizeof(Value));
+Q_STATIC_ASSERT(offsetof(CallData, thisObject ) == CallData::This * sizeof(Value));
+Q_STATIC_ASSERT(offsetof(CallData, newTarget ) == CallData::NewTarget * sizeof(Value));
+Q_STATIC_ASSERT(offsetof(CallData, _argc ) == CallData::Argc * sizeof(Value));
+Q_STATIC_ASSERT(offsetof(CallData, args ) == 6 * sizeof(Value));
+
+struct Q_QML_EXPORT CppStackFrame {
+ EngineBase *engine;
+ Value *savedStackTop;
+ CppStackFrame *parent;
+ Function *v4Function;
+ CallData *jsFrame;
+ const Value *originalArguments;
+ int originalArgumentsCount;
+ int instructionPointer;
+ const char *yield;
+ const char *unwindHandler;
+ const char *unwindLabel;
+ int unwindLevel;
+ bool yieldIsIterator;
+ bool callerCanHandleTailCall;
+ bool pendingTailCall;
+
+ void init(EngineBase *engine, Function *v4Function, const Value *argv, int argc, bool callerCanHandleTailCall = false) {
+ this->engine = engine;
+
+ this->v4Function = v4Function;
+ originalArguments = argv;
+ originalArgumentsCount = argc;
+ instructionPointer = 0;
+ yield = nullptr;
+ unwindHandler = nullptr;
+ unwindLabel = nullptr;
+ unwindLevel = 0;
+ yieldIsIterator = false;
+ this->callerCanHandleTailCall = callerCanHandleTailCall;
+ pendingTailCall = false;
+ }
+
+ void push() {
+ parent = engine->currentStackFrame;
+ engine->currentStackFrame = this;
+ savedStackTop = engine->jsStackTop;
+ }
+
+ void pop() {
+ engine->currentStackFrame = parent;
+ engine->jsStackTop = savedStackTop;
+ }
+
+#ifndef V4_BOOTSTRAP
+ static uint requiredJSStackFrameSize(uint nRegisters) {
+ return CallData::HeaderSize() + nRegisters;
+ }
+ static uint requiredJSStackFrameSize(Function *v4Function) {
+ return CallData::HeaderSize() + v4Function->compiledFunction->nRegisters;
+ }
+ uint requiredJSStackFrameSize() const {
+ return requiredJSStackFrameSize(v4Function);
+ }
+ void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope,
+ const Value &thisObject, const Value &newTarget = Value::undefinedValue()) {
+ setupJSFrame(stackSpace, function, scope, thisObject, newTarget,
+ v4Function->nFormals, v4Function->compiledFunction->nRegisters);
+ }
+ void setupJSFrame(Value *stackSpace, const Value &function, const Heap::ExecutionContext *scope,
+ const Value &thisObject, const Value &newTarget, uint nFormals, uint nRegisters)
+ {
+ jsFrame = reinterpret_cast<CallData *>(stackSpace);
+ jsFrame->function = function;
+ jsFrame->context = scope->asReturnedValue();
+ jsFrame->accumulator = Encode::undefined();
+ jsFrame->thisObject = thisObject;
+ jsFrame->newTarget = newTarget;
+
+ uint argc = uint(originalArgumentsCount);
+ if (argc > nFormals)
+ argc = nFormals;
+ jsFrame->setArgc(argc);
+
+ memcpy(jsFrame->args, originalArguments, argc*sizeof(Value));
+ Q_STATIC_ASSERT(Encode::undefined() == 0);
+ memset(jsFrame->args + argc, 0, (nRegisters - argc)*sizeof(Value));
+
+ if (v4Function && v4Function->compiledFunction) {
+ const int firstDeadZoneRegister = v4Function->compiledFunction->firstTemporalDeadZoneRegister;
+ const int registerDeadZoneSize = v4Function->compiledFunction->sizeOfRegisterTemporalDeadZone;
+
+ const Value * tdzEnd = stackSpace + firstDeadZoneRegister + registerDeadZoneSize;
+ for (Value *v = stackSpace + firstDeadZoneRegister; v < tdzEnd; ++v)
+ *v = Value::emptyValue().asReturnedValue();
+ }
+ }
+#endif
+
+ QString source() const;
+ QString function() const;
+ inline QV4::ExecutionContext *context() const {
+ return static_cast<ExecutionContext *>(&jsFrame->context);
+ }
+ int lineNumber() const;
+
+ inline QV4::Heap::CallContext *callContext() const {
+ Heap::ExecutionContext *ctx = static_cast<ExecutionContext &>(jsFrame->context).d();\
+ while (ctx->type != Heap::ExecutionContext::Type_CallContext)
+ ctx = ctx->outer;
+ return static_cast<Heap::CallContext *>(ctx);
+ }
+ ReturnedValue thisObject() const;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp
index 447992ebec..68d65f2e24 100644
--- a/src/qml/jsruntime/qv4string.cpp
+++ b/src/qml/jsruntime/qv4string.cpp
@@ -52,8 +52,17 @@ using namespace QV4;
#ifndef V4_BOOTSTRAP
+void Heap::StringOrSymbol::markObjects(Heap::Base *that, MarkStack *markStack)
+{
+ StringOrSymbol *s = static_cast<StringOrSymbol *>(that);
+ Heap::StringOrSymbol *id = s->identifier.asStringOrSymbol();
+ if (id)
+ id->mark(markStack);
+}
+
void Heap::String::markObjects(Heap::Base *that, MarkStack *markStack)
{
+ StringOrSymbol::markObjects(that, markStack);
String *s = static_cast<String *>(that);
if (s->subtype < StringType_Complex)
return;
@@ -68,15 +77,16 @@ void Heap::String::markObjects(Heap::Base *that, MarkStack *markStack)
}
}
+DEFINE_MANAGED_VTABLE(StringOrSymbol);
DEFINE_MANAGED_VTABLE(String);
-bool String::isEqualTo(Managed *t, Managed *o)
+bool String::virtualIsEqualTo(Managed *t, Managed *o)
{
if (t == o)
return true;
- if (!o->d()->vtable()->isString)
+ if (!o->vtable()->isString)
return false;
return static_cast<String *>(t)->isEqualTo(static_cast<String *>(o));
@@ -128,7 +138,8 @@ void Heap::ComplexString::init(Heap::String *ref, int from, int len)
this->len = len;
}
-void Heap::String::destroy() {
+void Heap::StringOrSymbol::destroy()
+{
if (text) {
internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(-text->size) * (int)sizeof(QChar));
if (!text->ref.deref())
@@ -155,12 +166,12 @@ uint String::toUInt(bool *ok) const
return UINT_MAX;
}
-void String::makeIdentifierImpl() const
+void String::createPropertyKeyImpl() const
{
if (!d()->text)
d()->simplifyString();
Q_ASSERT(d()->text);
- engine()->identifierTable->identifier(this);
+ engine()->identifierTable->asPropertyKey(this);
}
void Heap::String::simplifyString() const
@@ -174,7 +185,7 @@ void Heap::String::simplifyString() const
text = result.data_ptr();
text->ref.ref();
const ComplexString *cs = static_cast<const ComplexString *>(this);
- identifier = nullptr;
+ identifier = PropertyKey::invalid();
cs->left = cs->right = nullptr;
internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(text->size) * (qptrdiff)sizeof(QChar));
@@ -227,17 +238,19 @@ void Heap::String::append(const String *data, QChar *ch)
}
}
-void Heap::String::createHashValue() const
+void Heap::StringOrSymbol::createHashValue() const
{
- if (!text)
- simplifyString();
+ if (!text) {
+ Q_ASSERT(internalClass->vtable->isString);
+ static_cast<const Heap::String *>(this)->simplifyString();
+ }
Q_ASSERT(text);
const QChar *ch = reinterpret_cast<const QChar *>(text->data());
const QChar *end = ch + text->size;
stringHash = QV4::String::calculateHashValue(ch, end, &subtype);
}
-uint String::getLength(const Managed *m)
+qint64 String::virtualGetLength(const Managed *m)
{
return static_cast<const String *>(m)->d()->length();
}
@@ -248,4 +261,3 @@ uint String::toArrayIndex(const QString &str)
{
return QV4::String::toArrayIndex(str.constData(), str.constData() + str.length());
}
-
diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h
index 5466cc274d..fbd4f5f550 100644
--- a/src/qml/jsruntime/qv4string_p.h
+++ b/src/qml/jsruntime/qv4string_p.h
@@ -60,13 +60,14 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
struct ExecutionEngine;
-struct Identifier;
+struct PropertyKey;
namespace Heap {
-struct Q_QML_PRIVATE_EXPORT String : Base {
- static void markObjects(Heap::Base *that, MarkStack *markStack);
+struct Q_QML_PRIVATE_EXPORT StringOrSymbol : Base
+{
enum StringType {
+ StringType_Symbol,
StringType_Regular,
StringType_ArrayIndex,
StringType_Unknown,
@@ -75,13 +76,20 @@ struct Q_QML_PRIVATE_EXPORT String : Base {
StringType_Complex = StringType_AddedString
};
-#ifndef V4_BOOTSTRAP
- void init(const QString &text);
+ mutable QStringData *text;
+ mutable PropertyKey identifier;
+ mutable uint subtype;
+ mutable uint stringHash;
+
+ static void markObjects(Heap::Base *that, MarkStack *markStack);
void destroy();
- void simplifyString() const;
- int length() const;
- std::size_t retainedTextSize() const {
- return subtype >= StringType_Complex ? 0 : (std::size_t(text->size) * sizeof(QChar));
+
+ inline QString toQString() const {
+ if (!text)
+ return QString();
+ QStringDataPtr ptr = { text };
+ text->ref.ref();
+ return QString(ptr);
}
void createHashValue() const;
inline unsigned hashValue() const {
@@ -91,6 +99,22 @@ struct Q_QML_PRIVATE_EXPORT String : Base {
return stringHash;
}
+};
+
+struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol {
+ static void markObjects(Heap::Base *that, MarkStack *markStack);
+
+#ifndef V4_BOOTSTRAP
+ const VTable *vtable() const {
+ return internalClass->vtable;
+ }
+
+ void init(const QString &text);
+ void simplifyString() const;
+ int length() const;
+ std::size_t retainedTextSize() const {
+ return subtype >= StringType_Complex ? 0 : (std::size_t(text->size) * sizeof(QChar));
+ }
inline QString toQString() const {
if (subtype >= StringType_Complex)
simplifyString();
@@ -104,7 +128,7 @@ struct Q_QML_PRIVATE_EXPORT String : Base {
if (hashValue() != other->hashValue())
return false;
Q_ASSERT(subtype < StringType_Complex);
- if (identifier && identifier == other->identifier)
+ if (identifier.isValid() && identifier == other->identifier)
return true;
if (subtype == Heap::String::StringType_ArrayIndex && other->subtype == Heap::String::StringType_ArrayIndex)
return true;
@@ -114,10 +138,6 @@ struct Q_QML_PRIVATE_EXPORT String : Base {
bool startsWithUpper() const;
- mutable QStringData *text;
- mutable Identifier *identifier;
- mutable uint subtype;
- mutable uint stringHash;
private:
static void append(const String *data, QChar *ch);
#endif
@@ -146,12 +166,32 @@ int String::length() const {
}
-struct Q_QML_PRIVATE_EXPORT String : public Managed {
+struct Q_QML_PRIVATE_EXPORT StringOrSymbol : public Managed {
#ifndef V4_BOOTSTRAP
- V4_MANAGED(String, Managed)
+ V4_MANAGED(StringOrSymbol, Managed)
+ V4_NEEDS_DESTROY
+ enum {
+ IsStringOrSymbol = true
+ };
+
+private:
+ inline void createPropertyKey() const;
+public:
+ PropertyKey propertyKey() const { Q_ASSERT(d()->identifier.isValid()); return d()->identifier; }
+ PropertyKey toPropertyKey() const;
+
+
+ inline QString toQString() const {
+ return d()->toQString();
+ }
+#endif
+};
+
+struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol {
+#ifndef V4_BOOTSTRAP
+ V4_MANAGED(String, StringOrSymbol)
Q_MANAGED_TYPE(String)
V4_INTERNALCLASS(String)
- V4_NEEDS_DESTROY
enum {
IsString = true
};
@@ -166,7 +206,7 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed {
return d()->isEqualTo(other->d());
}
- inline bool compare(const String *other) {
+ inline bool lessThan(const String *other) {
return toQString() < other->toQString();
}
@@ -177,24 +217,10 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed {
inline unsigned hashValue() const {
return d()->hashValue();
}
- uint asArrayIndex() const {
- if (subtype() >= Heap::String::StringType_Unknown)
- d()->createHashValue();
- Q_ASSERT(d()->subtype < Heap::String::StringType_Complex);
- if (subtype() == Heap::String::StringType_ArrayIndex)
- return d()->stringHash;
- return UINT_MAX;
- }
uint toUInt(bool *ok) const;
- void makeIdentifier() const {
- if (d()->identifier)
- return;
- makeIdentifierImpl();
- }
-
// slow path
- Q_NEVER_INLINE void makeIdentifierImpl() const;
+ Q_NEVER_INLINE void createPropertyKeyImpl() const;
static uint createHashValue(const QChar *ch, int length, uint *subtype)
{
@@ -210,11 +236,9 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed {
bool startsWithUpper() const { return d()->startsWithUpper(); }
- Identifier *identifier() const { return d()->identifier; }
-
protected:
- static bool isEqualTo(Managed *that, Managed *o);
- static uint getLength(const Managed *m);
+ static bool virtualIsEqualTo(Managed *that, Managed *o);
+ static qint64 virtualGetLength(const Managed *m);
#endif
public:
@@ -254,7 +278,7 @@ public:
uint h = toArrayIndex(ch, end);
if (h != UINT_MAX) {
if (subtype)
- *subtype = Heap::String::StringType_ArrayIndex;
+ *subtype = Heap::StringOrSymbol::StringType_ArrayIndex;
return h;
}
@@ -264,7 +288,7 @@ public:
}
if (subtype)
- *subtype = Heap::String::StringType_Regular;
+ *subtype = (toUInt(ch) == '@') ? Heap::StringOrSymbol::StringType_Symbol : Heap::StringOrSymbol::StringType_Regular;
return h;
}
};
@@ -280,9 +304,27 @@ struct ComplexString : String {
}
};
+inline
+void StringOrSymbol::createPropertyKey() const {
+ Q_ASSERT(!d()->identifier.isValid());
+ Q_ASSERT(isString());
+ static_cast<const String *>(this)->createPropertyKeyImpl();
+}
+
+inline PropertyKey StringOrSymbol::toPropertyKey() const {
+ if (!d()->identifier.isValid())
+ createPropertyKey();
+ return d()->identifier;
+}
+
+template<>
+inline const StringOrSymbol *Value::as() const {
+ return isManaged() && m()->internalClass->vtable->isStringOrSymbol ? static_cast<const String *>(this) : nullptr;
+}
+
template<>
inline const String *Value::as() const {
- return isManaged() && m()->vtable()->isString ? static_cast<const String *>(this) : nullptr;
+ return isManaged() && m()->internalClass->vtable->isString ? static_cast<const String *>(this) : nullptr;
}
template<>
diff --git a/src/qml/jsruntime/qv4stringiterator.cpp b/src/qml/jsruntime/qv4stringiterator.cpp
new file mode 100644
index 0000000000..62db83ff26
--- /dev/null
+++ b/src/qml/jsruntime/qv4stringiterator.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Crimson AS <info@crimson.no>
+** 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 <private/qv4iterator_p.h>
+#include <private/qv4stringiterator_p.h>
+#include <private/qv4symbol_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(StringIteratorObject);
+
+void StringIteratorPrototype::init(ExecutionEngine *e)
+{
+ defineDefaultProperty(QStringLiteral("next"), method_next, 0);
+
+ Scope scope(e);
+ ScopedString val(scope, e->newString(QLatin1String("String Iterator")));
+ defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val);
+}
+
+ReturnedValue StringIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int)
+{
+ Scope scope(b);
+ const StringIteratorObject *thisObject = that->as<StringIteratorObject>();
+ if (!thisObject)
+ return scope.engine->throwTypeError(QLatin1String("Not an String Iterator instance"));
+
+ ScopedString s(scope, thisObject->d()->iteratedString);
+ if (!s) {
+ QV4::Value undefined = Value::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+ }
+
+ quint32 index = thisObject->d()->nextIndex;
+
+ QString str = s->toQString();
+ quint32 len = str.length();
+
+ if (index >= len) {
+ thisObject->d()->iteratedString.set(scope.engine, nullptr);
+ QV4::Value undefined = Value::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+ }
+
+ QChar ch = str.at(index);
+ int num = 1;
+ if (ch.unicode() >= 0xd800 && ch.unicode() <= 0xdbff && index + 1 != len) {
+ ch = str.at(index + 1);
+ if (ch.unicode() >= 0xdc00 && ch.unicode() <= 0xdfff)
+ num = 2;
+ }
+
+ thisObject->d()->nextIndex += num;
+
+ ScopedString resultString(scope, scope.engine->newString(s->toQString().mid(index, num)));
+ return IteratorPrototype::createIterResultObject(scope.engine, resultString, false);
+}
+
diff --git a/src/qml/jsruntime/qv4stringiterator_p.h b/src/qml/jsruntime/qv4stringiterator_p.h
new file mode 100644
index 0000000000..672ccc9963
--- /dev/null
+++ b/src/qml/jsruntime/qv4stringiterator_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** 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 QV4STRINGITERATOR_P_H
+#define QV4STRINGITERATOR_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 "qv4object_p.h"
+#include "qv4string_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+namespace QV4 {
+
+namespace Heap {
+
+#define StringIteratorObjectMembers(class, Member) \
+ Member(class, Pointer, String *, iteratedString) \
+ Member(class, NoMark, quint32, nextIndex)
+
+DECLARE_HEAP_OBJECT(StringIteratorObject, Object) {
+ DECLARE_MARKOBJECTS(StringIteratorObject);
+ void init(String *str, QV4::ExecutionEngine *engine)
+ {
+ Object::init();
+ this->iteratedString.set(engine, str);
+ this->nextIndex = 0;
+ }
+};
+
+}
+
+struct StringIteratorPrototype : Object
+{
+ V4_PROTOTYPE(iteratorPrototype)
+ void init(ExecutionEngine *engine);
+
+ static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct StringIteratorObject : Object
+{
+ V4_OBJECT2(StringIteratorObject, Object)
+ Q_MANAGED_TYPE(StringIteratorObject)
+ V4_PROTOTYPE(stringIteratorPrototype)
+
+ void init(ExecutionEngine *engine);
+};
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4ARRAYITERATOR_P_H
+
diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp
index 61176b3706..f6b9c5ba94 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -44,8 +44,10 @@
#include "qv4objectproto_p.h"
#include <private/qv4mm_p.h>
#include "qv4scopedvalue_p.h"
+#include "qv4symbol_p.h"
#include "qv4alloca_p.h"
#include "qv4jscall_p.h"
+#include "qv4stringiterator_p.h"
#include <QtCore/QDateTime>
#include <QtCore/QDebug>
#include <QtCore/QStringList>
@@ -72,14 +74,14 @@ void Heap::StringObject::init()
Object::init();
Q_ASSERT(vtable() == QV4::StringObject::staticVTable());
string.set(internalClass->engine, internalClass->engine->id_empty()->d());
- setProperty(internalClass->engine, LengthPropertyIndex, Primitive::fromInt32(0));
+ setProperty(internalClass->engine, LengthPropertyIndex, Value::fromInt32(0));
}
void Heap::StringObject::init(const QV4::String *str)
{
Object::init();
string.set(internalClass->engine, str->d());
- setProperty(internalClass->engine, LengthPropertyIndex, Primitive::fromInt32(length()));
+ setProperty(internalClass->engine, LengthPropertyIndex, Value::fromInt32(length()));
}
Heap::String *Heap::StringObject::getIndex(uint index) const
@@ -95,45 +97,70 @@ uint Heap::StringObject::length() const
return string->length();
}
-bool StringObject::deleteIndexedProperty(Managed *m, uint index)
+bool StringObject::virtualDeleteProperty(Managed *m, PropertyKey id)
{
- ExecutionEngine *v4 = static_cast<StringObject *>(m)->engine();
- Scope scope(v4);
- Scoped<StringObject> o(scope, m->as<StringObject>());
- Q_ASSERT(!!o);
-
- if (index < static_cast<uint>(o->d()->string->toQString().length()))
- return false;
- return true;
+ Q_ASSERT(m->as<StringObject>());
+ if (id.isArrayIndex()) {
+ StringObject *o = static_cast<StringObject *>(m);
+ uint index = id.asArrayIndex();
+ if (index < static_cast<uint>(o->d()->string->toQString().length()))
+ return false;
+ }
+ return Object::virtualDeleteProperty(m, id);
}
-void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs)
+struct StringObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
+{
+ ~StringObjectOwnPropertyKeyIterator() override = default;
+ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
+
+};
+
+PropertyKey StringObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
{
- name->setM(nullptr);
- StringObject *s = static_cast<StringObject *>(m);
+ const StringObject *s = static_cast<const StringObject *>(o);
uint slen = s->d()->string->toQString().length();
- if (it->arrayIndex <= slen) {
- while (it->arrayIndex < slen) {
- *index = it->arrayIndex;
- ++it->arrayIndex;
- PropertyAttributes a;
- Property pd;
- s->getOwnProperty(*index, &a, &pd);
- if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) {
- *attrs = a;
- p->copy(&pd, a);
- return;
- }
- }
+ if (arrayIndex < slen) {
+ uint index = arrayIndex;
+ ++arrayIndex;
+ if (attrs)
+ *attrs = Attr_NotConfigurable|Attr_NotWritable;
+ if (pd)
+ pd->value = s->getIndex(index);
+ return PropertyKey::fromArrayIndex(index);
+ } else if (arrayIndex == slen) {
if (s->arrayData()) {
- it->arrayNode = s->sparseBegin();
+ arrayNode = s->sparseBegin();
// iterate until we're past the end of the string
- while (it->arrayNode && it->arrayNode->key() < slen)
- it->arrayNode = it->arrayNode->nextNode();
+ while (arrayNode && arrayNode->key() < slen)
+ arrayNode = arrayNode->nextNode();
}
}
- return Object::advanceIterator(m, it, name, index, p, attrs);
+ return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
+}
+
+OwnPropertyKeyIterator *StringObject::virtualOwnPropertyKeys(const Object *m, Value *target)
+{
+ *target = *m;
+ return new StringObjectOwnPropertyKeyIterator;
+}
+
+PropertyAttributes StringObject::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
+{
+ PropertyAttributes attributes = Object::virtualGetOwnProperty(m, id, p);
+ 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;
+ }
+ return Object::virtualGetOwnProperty(m, id, p);
}
DEFINE_OBJECT_VTABLE(StringCtor);
@@ -143,7 +170,7 @@ void Heap::StringCtor::init(QV4::ExecutionContext *scope)
Heap::FunctionObject::init(scope, QStringLiteral("String"));
}
-ReturnedValue StringCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue StringCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *)
{
ExecutionEngine *v4 = static_cast<const Object *>(f)->engine();
Scope scope(v4);
@@ -152,16 +179,99 @@ ReturnedValue StringCtor::callAsConstructor(const FunctionObject *f, const Value
value = argv[0].toString(v4);
else
value = v4->newString();
+ CHECK_EXCEPTION();
return Encode(v4->newStringObject(value));
}
-ReturnedValue StringCtor::call(const FunctionObject *m, const Value *, const Value *argv, int argc)
+ReturnedValue StringCtor::virtualCall(const FunctionObject *m, const Value *, const Value *argv, int argc)
{
ExecutionEngine *v4 = m->engine();
- if (argc)
- return argv[0].toString(v4)->asReturnedValue();
- else
+ if (!argc)
return v4->newString()->asReturnedValue();
+ if (argv[0].isSymbol())
+ return v4->newString(argv[0].symbolValue()->descriptiveString())->asReturnedValue();
+ return argv[0].toString(v4)->asReturnedValue();
+}
+
+ReturnedValue StringCtor::method_fromCharCode(const FunctionObject *b, const Value *, const Value *argv, int argc)
+{
+ QString str(argc, Qt::Uninitialized);
+ QChar *ch = str.data();
+ for (int i = 0, ei = argc; i < ei; ++i) {
+ *ch = QChar(argv[i].toUInt16());
+ ++ch;
+ }
+ *ch = 0;
+ return Encode(b->engine()->newString(str));
+}
+
+
+
+ReturnedValue StringCtor::method_fromCodePoint(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ ExecutionEngine *e = f->engine();
+ QString result(argc*2, Qt::Uninitialized); // assume worst case
+ QChar *ch = result.data();
+ for (int i = 0; i < argc; ++i) {
+ double num = argv[i].toNumber();
+ if (e->hasException)
+ return Encode::undefined();
+ int cp = static_cast<int>(num);
+ if (cp != num || cp < 0 || cp > 0x10ffff)
+ return e->throwRangeError(QStringLiteral("String.fromCodePoint: argument out of range."));
+ if (cp > 0xffff) {
+ *ch = QChar::highSurrogate(cp);
+ ++ch;
+ *ch = QChar::lowSurrogate(cp);
+ } else {
+ *ch = cp;
+ }
+ ++ch;
+ }
+ *ch = 0;
+ result.truncate(ch - result.constData());
+ return e->newString(result)->asReturnedValue();
+}
+
+ReturnedValue StringCtor::method_raw(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc)
+ return scope.engine->throwTypeError();
+
+ ScopedObject cooked(scope, argv[0].toObject(scope.engine));
+ if (!cooked)
+ return scope.engine->throwTypeError();
+ ScopedString rawString(scope, scope.engine->newIdentifier(QStringLiteral("raw")));
+ ScopedValue rawValue(scope, cooked->get(rawString));
+ ScopedObject raw(scope, rawValue->toObject(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+
+ ++argv;
+ --argc;
+
+ QString result;
+ uint literalSegments = raw->getLength();
+ if (!literalSegments)
+ return scope.engine->id_empty()->asReturnedValue();
+
+ uint nextIndex = 0;
+ ScopedValue val(scope);
+ while (1) {
+ val = raw->get(nextIndex);
+ result += val->toQString();
+ if (scope.engine->hasException)
+ return Encode::undefined();
+ if (nextIndex + 1 == literalSegments)
+ return scope.engine->newString(result)->asReturnedValue();
+
+ if (nextIndex < static_cast<uint>(argc))
+ result += argv[nextIndex].toQString();
+ if (scope.engine->hasException)
+ return Encode::undefined();
+ ++nextIndex;
+ }
}
void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
@@ -169,15 +279,24 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
Scope scope(engine);
ScopedObject o(scope);
+ // need to set this once again, as these were not fully defined when creating the string proto
+ Heap::InternalClass *ic = scope.engine->classes[ExecutionEngine::Class_StringObject]->changePrototype(scope.engine->objectPrototype()->d());
+ d()->internalClass.set(scope.engine, ic);
+ d()->string.set(scope.engine, scope.engine->id_empty()->d());
+ setProperty(scope.engine, Heap::StringObject::LengthPropertyIndex, Value::fromInt32(0));
+
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1));
- ctor->defineDefaultProperty(QStringLiteral("fromCharCode"), method_fromCharCode, 1);
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(1));
+ ctor->defineDefaultProperty(QStringLiteral("fromCharCode"), StringCtor::method_fromCharCode, 1);
+ ctor->defineDefaultProperty(QStringLiteral("fromCodePoint"), StringCtor::method_fromCodePoint, 1);
+ ctor->defineDefaultProperty(QStringLiteral("raw"), StringCtor::method_raw, 1);
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(engine->id_toString(), method_toString);
defineDefaultProperty(engine->id_valueOf(), method_toString); // valueOf and toString are identical
defineDefaultProperty(QStringLiteral("charAt"), method_charAt, 1);
defineDefaultProperty(QStringLiteral("charCodeAt"), method_charCodeAt, 1);
+ defineDefaultProperty(QStringLiteral("codePointAt"), method_codePointAt, 1);
defineDefaultProperty(QStringLiteral("concat"), method_concat, 1);
defineDefaultProperty(QStringLiteral("endsWith"), method_endsWith, 1);
defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1);
@@ -185,6 +304,9 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare, 1);
defineDefaultProperty(QStringLiteral("match"), method_match, 1);
+ defineDefaultProperty(QStringLiteral("normalize"), method_normalize, 0);
+ defineDefaultProperty(QStringLiteral("padEnd"), method_padEnd, 1);
+ defineDefaultProperty(QStringLiteral("padStart"), method_padStart, 1);
defineDefaultProperty(QStringLiteral("repeat"), method_repeat, 1);
defineDefaultProperty(QStringLiteral("replace"), method_replace, 2);
defineDefaultProperty(QStringLiteral("search"), method_search, 1);
@@ -198,6 +320,7 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(QStringLiteral("toUpperCase"), method_toUpperCase);
defineDefaultProperty(QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase);
defineDefaultProperty(QStringLiteral("trim"), method_trim);
+ defineDefaultProperty(engine->symbol_iterator(), method_iterator);
}
static Heap::String *thisAsString(ExecutionEngine *v4, const QV4::Value *thisObject)
@@ -270,6 +393,29 @@ ReturnedValue StringPrototype::method_charCodeAt(const FunctionObject *b, const
return Encode(qt_qnan());
}
+ReturnedValue StringPrototype::method_codePointAt(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *v4 = f->engine();
+ QString value = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+
+ int index = argc ? argv[0].toInteger() : 0;
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+
+ if (index < 0 || index >= value.size())
+ return Encode::undefined();
+
+ uint first = value.at(index).unicode();
+ if (QChar::isHighSurrogate(first) && index + 1 < value.size()) {
+ uint second = value.at(index + 1).unicode();
+ if (QChar::isLowSurrogate(second))
+ return Encode(QChar::surrogateToUcs4(first, second));
+ }
+ return Encode(first);
+}
+
ReturnedValue StringPrototype::method_concat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
ExecutionEngine *v4 = b->engine();
@@ -298,12 +444,11 @@ ReturnedValue StringPrototype::method_endsWith(const FunctionObject *b, const Va
if (v4->hasException)
return QV4::Encode::undefined();
- QString searchString;
- if (argc) {
- if (argv[0].as<RegExpObject>())
- return v4->throwTypeError();
- searchString = argv[0].toQString();
- }
+ if (argc && argv[0].as<RegExpObject>())
+ return v4->throwTypeError();
+ QString searchString = (argc ? argv[0] : Value::undefinedValue()).toQString();
+ if (v4->hasException)
+ return Encode::undefined();
int pos = value.length();
if (argc > 1)
@@ -323,9 +468,9 @@ ReturnedValue StringPrototype::method_indexOf(const FunctionObject *b, const Val
if (v4->hasException)
return QV4::Encode::undefined();
- QString searchString;
- if (argc)
- searchString = argv[0].toQString();
+ QString searchString = (argc ? argv[0] : Value::undefinedValue()).toQString();
+ if (v4->hasException)
+ return Encode::undefined();
int pos = 0;
if (argc > 1)
@@ -345,12 +490,11 @@ ReturnedValue StringPrototype::method_includes(const FunctionObject *b, const Va
if (v4->hasException)
return QV4::Encode::undefined();
- QString searchString;
- if (argc) {
- if (argv[0].as<RegExpObject>())
- return v4->throwTypeError();
- searchString = argv[0].toQString();
- }
+ if (argc && argv[0].as<RegExpObject>())
+ return v4->throwTypeError();
+ QString searchString = (argc ? argv[0] : Value::undefinedValue()).toQString();
+ if (v4->hasException)
+ return Encode::undefined();
int pos = 0;
if (argc > 1) {
@@ -374,9 +518,9 @@ ReturnedValue StringPrototype::method_lastIndexOf(const FunctionObject *b, const
if (v4->hasException)
return QV4::Encode::undefined();
- QString searchString;
- if (argc)
- searchString = argv[0].toQString();
+ QString searchString = (argc ? argv[0] : Value::undefinedValue()).toQString();
+ if (v4->hasException)
+ return Encode::undefined();
double position = argc > 1 ? RuntimeHelpers::toNumber(argv[1]) : +qInf();
if (std::isnan(position))
@@ -400,7 +544,7 @@ ReturnedValue StringPrototype::method_localeCompare(const FunctionObject *b, con
if (v4->hasException)
return QV4::Encode::undefined();
- const QString that = (argc ? argv[0] : Primitive::undefinedValue()).toQString();
+ const QString that = (argc ? argv[0] : Value::undefinedValue()).toQString();
return Encode(QString::localeAwareCompare(value, that));
}
@@ -411,50 +555,147 @@ ReturnedValue StringPrototype::method_match(const FunctionObject *b, const Value
return v4->throwTypeError();
Scope scope(v4);
+ if (argc && !argv[0].isNullOrUndefined()) {
+ ScopedObject r(scope, argv[0].toObject(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+ ScopedValue f(scope, r->get(scope.engine->symbol_match()));
+ if (!f->isNullOrUndefined()) {
+ ScopedFunctionObject fo(scope, f);
+ if (!fo)
+ return scope.engine->throwTypeError();
+ return fo->call(r, thisObject, 1);
+ }
+ }
+
ScopedString s(scope, thisObject->toString(v4));
if (v4->hasException)
return Encode::undefined();
- Scoped<RegExpObject> that(scope, argc ? argv[0] : Primitive::undefinedValue());
+ Scoped<RegExpObject> that(scope, argc ? argv[0] : Value::undefinedValue());
if (!that) {
// convert args[0] to a regexp
- that = RegExpCtor::callAsConstructor(b, argv, argc);
+ that = RegExpCtor::virtualCallAsConstructor(b, argv, argc, b);
if (v4->hasException)
return Encode::undefined();
}
Q_ASSERT(!!that);
- bool global = that->global();
+ ScopedFunctionObject match(scope, that->get(scope.engine->symbol_match()));
+ if (!match)
+ return scope.engine->throwTypeError();
+ return match->call(that, s, 1);
+}
+
+ReturnedValue StringPrototype::method_normalize(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *v4 = f->engine();
+ const QString value = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return Encode::undefined();
+
+ QString::NormalizationForm form = QString::NormalizationForm_C;
+ if (argc >= 1 && !argv[0].isUndefined()) {
+ QString f = argv[0].toQString();
+ if (v4->hasException)
+ return Encode::undefined();
+ if (f == QLatin1String("NFC"))
+ form = QString::NormalizationForm_C;
+ else if (f == QLatin1String("NFD"))
+ form = QString::NormalizationForm_D;
+ else if (f == QLatin1String("NFKC"))
+ form = QString::NormalizationForm_KC;
+ else if (f == QLatin1String("NFKD"))
+ form = QString::NormalizationForm_KD;
+ else
+ return v4->throwRangeError(QLatin1String("String.prototype.normalize: Invalid normalization form."));
+ }
+ QString normalized = value.normalized(form);
+ return v4->newString(normalized)->asReturnedValue();
+}
+
+ReturnedValue StringPrototype::method_padEnd(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *v4 = f->engine();
+ if (thisObject->isNullOrUndefined())
+ return v4->throwTypeError();
- if (!global)
- return RegExpPrototype::method_exec(b, that, s, 1);
+ Scope scope(v4);
+ ScopedString s(scope, thisAsString(v4, thisObject));
+ if (v4->hasException)
+ return Encode::undefined();
+ if (!argc)
+ return s->asReturnedValue();
- // rx is now in thisObject
- that->setLastIndex(0);
- ScopedArrayObject a(scope, scope.engine->newArrayObject());
+ int maxLen = argv[0].toInteger();
+ if (maxLen <= s->d()->length())
+ return s->asReturnedValue();
+ QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : QString::fromLatin1(" ");
+ if (v4->hasException)
+ return Encode::undefined();
- int previousLastIndex = 0;
- uint n = 0;
- while (1) {
- Value result = Primitive::fromReturnedValue(RegExpPrototype::execFirstMatch(b, that, s, 1));
- if (result.isNull())
- break;
- int index = that->lastIndex();
- if (previousLastIndex == index) {
- previousLastIndex = index + 1;
- that->setLastIndex(previousLastIndex);
- } else {
- previousLastIndex = index;
- }
- a->arraySet(n, result);
- ++n;
+ if (fillString.isEmpty())
+ return s->asReturnedValue();
+
+ QString padded = s->toQString();
+ int oldLength = padded.length();
+ int toFill = maxLen - oldLength;
+ padded.resize(maxLen);
+ QChar *ch = padded.data() + oldLength;
+ while (toFill) {
+ int copy = qMin(fillString.length(), toFill);
+ memcpy(ch, fillString.constData(), copy*sizeof(QChar));
+ toFill -= copy;
+ ch += copy;
}
- if (!n)
- return Encode::null();
- else
- return a.asReturnedValue();
+ *ch = 0;
+
+ return v4->newString(padded)->asReturnedValue();
+}
+
+ReturnedValue StringPrototype::method_padStart(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *v4 = f->engine();
+ if (thisObject->isNullOrUndefined())
+ return v4->throwTypeError();
+
+ Scope scope(v4);
+ ScopedString s(scope, thisAsString(v4, thisObject));
+ if (v4->hasException)
+ return Encode::undefined();
+ if (!argc)
+ return s->asReturnedValue();
+
+ int maxLen = argv[0].toInteger();
+ if (maxLen <= s->d()->length())
+ return s->asReturnedValue();
+ QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : QString::fromLatin1(" ");
+ if (v4->hasException)
+ return Encode::undefined();
+
+ if (fillString.isEmpty())
+ return s->asReturnedValue();
+
+ QString original = s->toQString();
+ int oldLength = original.length();
+ int toFill = maxLen - oldLength;
+ QString padded;
+ padded.resize(maxLen);
+ QChar *ch = padded.data();
+ while (toFill) {
+ int copy = qMin(fillString.length(), toFill);
+ memcpy(ch, fillString.constData(), copy*sizeof(QChar));
+ toFill -= copy;
+ ch += copy;
+ }
+ memcpy(ch, original.constData(), oldLength*sizeof(QChar));
+ ch += oldLength;
+ *ch = 0;
+
+ return v4->newString(padded)->asReturnedValue();
}
+
ReturnedValue StringPrototype::method_repeat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
ExecutionEngine *v4 = b->engine();
@@ -462,7 +703,7 @@ ReturnedValue StringPrototype::method_repeat(const FunctionObject *b, const Valu
if (v4->hasException)
return QV4::Encode::undefined();
- double repeats = (argc ? argv[0] : Primitive::undefinedValue()).toInteger();
+ double repeats = (argc ? argv[0] : Value::undefinedValue()).toInteger();
if (repeats < 0 || qIsInf(repeats))
return v4->throwRangeError(QLatin1String("Invalid count value"));
@@ -532,7 +773,7 @@ ReturnedValue StringPrototype::method_replace(const FunctionObject *b, const Val
uint *matchOffsets = _matchOffsets;
Scope scope(b);
- ScopedValue searchValue(scope, argc ? argv[0] : Primitive::undefinedValue());
+ ScopedValue searchValue(scope, argc ? argv[0] : Value::undefinedValue());
Scoped<RegExpObject> regExp(scope, searchValue);
if (regExp) {
uint offset = 0;
@@ -559,8 +800,11 @@ ReturnedValue StringPrototype::method_replace(const FunctionObject *b, const Val
break;
offset = qMax(offset + 1, matchOffsets[oldSize + 1]);
}
- if (regExp->global())
+ if (regExp->global()) {
regExp->setLastIndex(0);
+ if (scope.hasException())
+ return Encode::undefined();
+ }
numStringMatches = nMatchOffsets / (regExp->value()->captureCount() * 2);
numCaptures = regExp->value()->captureCount();
} else {
@@ -576,7 +820,7 @@ ReturnedValue StringPrototype::method_replace(const FunctionObject *b, const Val
QString result;
ScopedValue replacement(scope);
- ScopedValue replaceValue(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+ ScopedValue replaceValue(scope, argc > 1 ? argv[1] : Value::undefinedValue());
ScopedFunctionObject searchCallback(scope, replaceValue);
if (!!searchCallback) {
result.reserve(string.length() + 10*numStringMatches);
@@ -588,7 +832,7 @@ ReturnedValue StringPrototype::method_replace(const FunctionObject *b, const Val
int idx = (i * numCaptures + k) * 2;
uint start = matchOffsets[idx];
uint end = matchOffsets[idx + 1];
- entry = Primitive::undefinedValue();
+ entry = Value::undefinedValue();
if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch)
entry = scope.engine->newString(string.mid(start, end - start));
arguments[k] = entry;
@@ -596,10 +840,10 @@ ReturnedValue StringPrototype::method_replace(const FunctionObject *b, const Val
uint matchStart = matchOffsets[i * numCaptures * 2];
Q_ASSERT(matchStart >= static_cast<uint>(lastEnd));
uint matchEnd = matchOffsets[i * numCaptures * 2 + 1];
- arguments[numCaptures] = Primitive::fromUInt32(matchStart);
+ arguments[numCaptures] = Value::fromUInt32(matchStart);
arguments[numCaptures + 1] = scope.engine->newString(string);
- Value that = Primitive::undefinedValue();
+ Value that = Value::undefinedValue();
replacement = searchCallback->call(&that, arguments, numCaptures + 2);
result += string.midRef(lastEnd, matchStart - lastEnd);
result += replacement->toQString();
@@ -638,7 +882,7 @@ ReturnedValue StringPrototype::method_search(const FunctionObject *b, const Valu
if (scope.engine->hasException)
return QV4::Encode::undefined();
- Scoped<RegExpObject> regExp(scope, argc ? argv[0] : Primitive::undefinedValue());
+ Scoped<RegExpObject> regExp(scope, argc ? argv[0] : Value::undefinedValue());
if (!regExp) {
regExp = scope.engine->regExpCtor()->callAsConstructor(argv, 1);
if (scope.engine->hasException)
@@ -695,8 +939,8 @@ ReturnedValue StringPrototype::method_split(const FunctionObject *b, const Value
return QV4::Encode::undefined();
Scope scope(v4);
- ScopedValue separatorValue(scope, argc ? argv[0] : Primitive::undefinedValue());
- ScopedValue limitValue(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
+ ScopedValue separatorValue(scope, argc ? argv[0] : Value::undefinedValue());
+ ScopedValue limitValue(scope, argc > 1 ? argv[1] : Value::undefinedValue());
ScopedArrayObject array(scope, scope.engine->newArrayObject());
@@ -777,12 +1021,11 @@ ReturnedValue StringPrototype::method_startsWith(const FunctionObject *b, const
if (v4->hasException)
return QV4::Encode::undefined();
- QString searchString;
- if (argc) {
- if (argv[0].as<RegExpObject>())
- return v4->throwTypeError();
- searchString = argv[0].toQString();
- }
+ if (argc && argv[0].as<RegExpObject>())
+ return v4->throwTypeError();
+ QString searchString = (argc ? argv[0] : Value::undefinedValue()).toQString();
+ if (v4->hasException)
+ return Encode::undefined();
int pos = 0;
if (argc > 1)
@@ -816,8 +1059,8 @@ ReturnedValue StringPrototype::method_substr(const FunctionObject *b, const Valu
length = qMin(qMax(length, 0.0), count - start);
- qint32 x = Primitive::toInt32(start);
- qint32 y = Primitive::toInt32(length);
+ qint32 x = Value::toInt32(start);
+ qint32 y = Value::toInt32(length);
return Encode(v4->newString(value.mid(x, y)));
}
@@ -892,17 +1135,6 @@ ReturnedValue StringPrototype::method_toLocaleUpperCase(const FunctionObject *b,
return method_toUpperCase(b, thisObject, argv, argc);
}
-ReturnedValue StringPrototype::method_fromCharCode(const FunctionObject *b, const Value *, const Value *argv, int argc)
-{
- QString str(argc, Qt::Uninitialized);
- QChar *ch = str.data();
- for (int i = 0, ei = argc; i < ei; ++i) {
- *ch = QChar(argv[i].toUInt16());
- ++ch;
- }
- return Encode(b->engine()->newString(str));
-}
-
ReturnedValue StringPrototype::method_trim(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = b->engine();
@@ -923,3 +1155,16 @@ ReturnedValue StringPrototype::method_trim(const FunctionObject *b, const Value
return Encode(v4->newString(QString(chars + start, end - start + 1)));
}
+
+
+
+ReturnedValue StringPrototype::method_iterator(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ ScopedString s(scope, thisObject->toString(scope.engine));
+ if (!s || thisObject->isNullOrUndefined())
+ return scope.engine->throwTypeError();
+
+ Scoped<StringIteratorObject> si(scope, scope.engine->memoryManager->allocate<StringIteratorObject>(s->d(), scope.engine));
+ return si->asReturnedValue();
+}
diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h
index 7d25678b61..794ee91575 100644
--- a/src/qml/jsruntime/qv4stringobject_p.h
+++ b/src/qml/jsruntime/qv4stringobject_p.h
@@ -70,6 +70,8 @@ DECLARE_HEAP_OBJECT(StringObject, Object) {
LengthPropertyIndex = 0
};
+ void init(bool /*don't init*/)
+ { Object::init(); }
void init();
void init(const QV4::String *string);
@@ -96,18 +98,23 @@ struct StringObject: Object {
return d()->length();
}
- static bool deleteIndexedProperty(Managed *m, uint index);
-
+ using Object::getOwnProperty;
protected:
- static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs);
+ static bool virtualDeleteProperty(Managed *m, PropertyKey id);
+ static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
+ static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p);
};
struct StringCtor: FunctionObject
{
V4_OBJECT2(StringCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_fromCharCode(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_fromCodePoint(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_raw(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
struct StringPrototype: StringObject
@@ -118,6 +125,7 @@ struct StringPrototype: StringObject
static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_charAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_charCodeAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_codePointAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_concat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_endsWith(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_indexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
@@ -125,6 +133,9 @@ struct StringPrototype: StringObject
static ReturnedValue method_lastIndexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_localeCompare(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_match(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_normalize(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_padEnd(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_padStart(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_repeat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_replace(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_search(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
@@ -137,8 +148,8 @@ struct StringPrototype: StringObject
static ReturnedValue method_toLocaleLowerCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toUpperCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toLocaleUpperCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_fromCharCode(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_trim(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_iterator(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
}
diff --git a/src/qml/jsruntime/qv4symbol.cpp b/src/qml/jsruntime/qv4symbol.cpp
new file mode 100644
index 0000000000..004a9938e2
--- /dev/null
+++ b/src/qml/jsruntime/qv4symbol.cpp
@@ -0,0 +1,187 @@
+/****************************************************************************
+**
+** 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 <qv4symbol_p.h>
+#include <qv4functionobject_p.h>
+#include <qv4identifiertable_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(SymbolCtor);
+DEFINE_MANAGED_VTABLE(Symbol);
+DEFINE_OBJECT_VTABLE(SymbolObject);
+
+void Heap::Symbol::init(const QString &s)
+{
+ Q_ASSERT(s.at(0) == QLatin1Char('@'));
+ identifier = PropertyKey::fromStringOrSymbol(this);
+ QString desc(s);
+ text = desc.data_ptr();
+ text->ref.ref();
+}
+
+void Heap::SymbolCtor::init(QV4::ExecutionContext *scope)
+{
+ Heap::FunctionObject::init(scope, QStringLiteral("Symbol"));
+}
+
+void Heap::SymbolObject::init(const QV4::Symbol *s)
+{
+ Object::init();
+ symbol.set(internalClass->engine, s->d());
+}
+
+ReturnedValue QV4::SymbolCtor::virtualCall(const QV4::FunctionObject *f, const QV4::Value *, const QV4::Value *argv, int argc)
+{
+ Scope scope(f);
+ QString desc = QChar::fromLatin1('@');
+ if (argc && !argv[0].isUndefined()) {
+ ScopedString s(scope, argv[0].toString(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+ desc += s->toQString();
+ }
+ return Symbol::create(scope.engine, desc)->asReturnedValue();
+}
+
+ReturnedValue SymbolCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *)
+{
+ return f->engine()->throwTypeError(QStringLiteral("Symbol can't be used together with |new|."));
+}
+
+ReturnedValue SymbolCtor::method_for(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ ScopedValue k(scope, argc ? argv[0]: Value::undefinedValue());
+ ScopedString key(scope, k->toString(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+ QString desc = QLatin1Char('@') + key->toQString();
+ return scope.engine->identifierTable->insertSymbol(desc)->asReturnedValue();
+}
+
+ReturnedValue SymbolCtor::method_keyFor(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ ExecutionEngine *e = f->engine();
+ if (!argc || !argv[0].isSymbol())
+ return e->throwTypeError(QLatin1String("Symbol.keyFor: Argument is not a symbol."));
+ const Symbol &arg = static_cast<const Symbol &>(argv[0]);
+ Heap::Symbol *s = e->identifierTable->symbolForId(arg.propertyKey());
+ Q_ASSERT(!s || s == arg.d());
+ if (s)
+ return e->newString(arg.toQString().mid((1)))->asReturnedValue();
+ return Encode::undefined();
+}
+
+void SymbolPrototype::init(ExecutionEngine *engine, Object *ctor)
+{
+ Scope scope(engine);
+ ScopedValue v(scope);
+ ctor->defineReadonlyProperty(engine->id_prototype(), (v = this));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(0));
+
+ ctor->defineDefaultProperty(QStringLiteral("for"), SymbolCtor::method_for, 1);
+ ctor->defineDefaultProperty(QStringLiteral("keyFor"), SymbolCtor::method_keyFor, 1);
+ ctor->defineReadonlyProperty(QStringLiteral("hasInstance"), *engine->symbol_hasInstance());
+ ctor->defineReadonlyProperty(QStringLiteral("isConcatSpreadable"), *engine->symbol_isConcatSpreadable());
+ ctor->defineReadonlyProperty(QStringLiteral("iterator"), *engine->symbol_iterator());
+ ctor->defineReadonlyProperty(QStringLiteral("match"), *engine->symbol_match());
+ ctor->defineReadonlyProperty(QStringLiteral("replace"), *engine->symbol_replace());
+ ctor->defineReadonlyProperty(QStringLiteral("search"), *engine->symbol_search());
+ ctor->defineReadonlyProperty(QStringLiteral("species"), *engine->symbol_species());
+ ctor->defineReadonlyProperty(QStringLiteral("split"), *engine->symbol_split());
+ ctor->defineReadonlyProperty(QStringLiteral("toPrimitive"), *engine->symbol_toPrimitive());
+ ctor->defineReadonlyProperty(QStringLiteral("toStringTag"), *engine->symbol_toStringTag());
+ ctor->defineReadonlyProperty(QStringLiteral("unscopables"), *engine->symbol_unscopables());
+
+ defineDefaultProperty(QStringLiteral("constructor"), (v = ctor));
+ defineDefaultProperty(QStringLiteral("toString"), method_toString);
+ defineDefaultProperty(QStringLiteral("valueOf"), method_valueOf);
+ defineDefaultProperty(engine->symbol_toPrimitive(), method_symbolToPrimitive, 1, Attr_ReadOnly_ButConfigurable);
+
+ v = engine->newString(QStringLiteral("Symbol"));
+ defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), v);
+
+}
+
+ReturnedValue SymbolPrototype::method_toString(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ Scope scope(f);
+ Scoped<Symbol> s(scope, thisObject->as<Symbol>());
+ if (!s) {
+ if (const SymbolObject *o = thisObject->as<SymbolObject>())
+ s = o->d()->symbol;
+ else
+ return scope.engine->throwTypeError();
+ }
+ return scope.engine->newString(s->descriptiveString())->asReturnedValue();
+}
+
+ReturnedValue SymbolPrototype::method_valueOf(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ Scope scope(f);
+ Scoped<Symbol> s(scope, thisObject->as<Symbol>());
+ if (!s) {
+ if (const SymbolObject *o = thisObject->as<SymbolObject>())
+ s = o->d()->symbol;
+ else
+ return scope.engine->throwTypeError();
+ }
+ return s->asReturnedValue();
+}
+
+ReturnedValue SymbolPrototype::method_symbolToPrimitive(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ if (thisObject->isSymbol())
+ return thisObject->asReturnedValue();
+ if (const SymbolObject *o = thisObject->as<SymbolObject>())
+ return o->d()->symbol->asReturnedValue();
+ return f->engine()->throwTypeError();
+}
+
+Heap::Symbol *Symbol::create(ExecutionEngine *e, const QString &s)
+{
+ Q_ASSERT(s.at(0) == QLatin1Char('@'));
+ return e->memoryManager->alloc<Symbol>(s);
+}
+
+QString Symbol::descriptiveString() const
+{
+ return QLatin1String("Symbol(") + toQString().midRef(1) + QLatin1String(")");
+}
diff --git a/src/qml/jsruntime/qv4symbol_p.h b/src/qml/jsruntime/qv4symbol_p.h
new file mode 100644
index 0000000000..c7e12b512b
--- /dev/null
+++ b/src/qml/jsruntime/qv4symbol_p.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** 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 QV4_SYMBOL_H
+#define QV4_SYMBOL_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 "qv4string_p.h"
+#include "qv4functionobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+namespace QV4 {
+
+namespace Heap {
+
+struct SymbolCtor : FunctionObject {
+ void init(QV4::ExecutionContext *scope);
+};
+
+struct Symbol : StringOrSymbol {
+ void init(const QString &s);
+};
+
+#define SymbolObjectMembers(class, Member) \
+ Member(class, Pointer, Symbol *, symbol)
+
+DECLARE_HEAP_OBJECT(SymbolObject, Object) {
+ DECLARE_MARKOBJECTS(SymbolObject);
+ void init(const QV4::Symbol *s);
+};
+
+}
+
+struct SymbolCtor : FunctionObject
+{
+ V4_OBJECT2(SymbolCtor, FunctionObject)
+
+ static ReturnedValue virtualCall(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *, const Value *argv, int argc, const Value *newTarget);
+ static ReturnedValue method_for(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_keyFor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct SymbolPrototype : Object
+{
+ V4_PROTOTYPE(objectPrototype)
+ void init(ExecutionEngine *engine, Object *ctor);
+
+ static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_valueOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_symbolToPrimitive(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct Symbol : StringOrSymbol
+{
+ V4_MANAGED(Symbol, StringOrSymbol)
+ Q_MANAGED_TYPE(Symbol)
+ V4_INTERNALCLASS(Symbol)
+ V4_NEEDS_DESTROY
+
+ static Heap::Symbol *create(ExecutionEngine *e, const QString &s);
+
+ QString descriptiveString() const;
+};
+
+struct SymbolObject : Object
+{
+ V4_OBJECT2(SymbolObject, Object)
+ Q_MANAGED_TYPE(SymbolObject)
+ V4_INTERNALCLASS(SymbolObject)
+ V4_PROTOTYPE(symbolPrototype)
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp
index ea1532b8ce..faf7934c06 100644
--- a/src/qml/jsruntime/qv4typedarray.cpp
+++ b/src/qml/jsruntime/qv4typedarray.cpp
@@ -36,170 +36,241 @@
** $QT_END_LICENSE$
**
****************************************************************************/
+
#include "qv4typedarray_p.h"
+#include "qv4arrayiterator_p.h"
#include "qv4arraybuffer_p.h"
#include "qv4string_p.h"
#include "qv4jscall_p.h"
+#include "qv4symbol_p.h"
+#include "qv4runtime_p.h"
+#include <QtCore/qatomic.h>
#include <cmath>
using namespace QV4;
+DEFINE_OBJECT_VTABLE(IntrinsicTypedArrayCtor);
+DEFINE_OBJECT_VTABLE(IntrinsicTypedArrayPrototype);
DEFINE_OBJECT_VTABLE(TypedArrayCtor);
DEFINE_OBJECT_VTABLE(TypedArrayPrototype);
DEFINE_OBJECT_VTABLE(TypedArray);
-Q_STATIC_ASSERT((int)ExecutionEngine::NTypedArrayTypes == (int)Heap::TypedArray::NTypes);
+Q_STATIC_ASSERT((int)ExecutionEngine::NTypedArrayTypes == (int)NTypedArrayTypes);
-ReturnedValue Int8ArrayRead(const char *data, int index)
+static inline int toInt32(Value v)
{
- return Encode((int)(signed char)data[index]);
+ Q_ASSERT(v.isNumber());
+ if (v.isInteger())
+ return v.integerValue();
+ return Double::toInt32(v.doubleValue());
}
-void Int8ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value)
+static inline double toDouble(Value v)
{
- signed char v = (signed char)value.toUInt32();
- if (e->hasException)
- return;
- data[index] = v;
+ Q_ASSERT(v.isNumber());
+ if (v.isInteger())
+ return v.integerValue();
+ return v.doubleValue();
}
-ReturnedValue UInt8ArrayRead(const char *data, int index)
-{
- return Encode((int)(unsigned char)data[index]);
+struct ClampedUInt8 {
+ quint8 c;
+};
+
+template <typename T>
+ReturnedValue typeToValue(T t) {
+ return Encode(t);
}
-void UInt8ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value)
+template <>
+ReturnedValue typeToValue(ClampedUInt8 t) {
+ return Encode(t.c);
+}
+
+template <typename T>
+T valueToType(Value value)
{
- unsigned char v = (unsigned char)value.toUInt32();
- if (e->hasException)
- return;
- data[index] = v;
+ Q_ASSERT(value.isNumber());
+ int n = toInt32(value);
+ return static_cast<T>(n);
}
-void UInt8ClampedArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value)
+template <>
+ClampedUInt8 valueToType(Value value)
{
- if (value.isInteger()) {
- data[index] = (char)(unsigned char)qBound(0, value.integerValue(), 255);
- return;
- }
- double d = value.toNumber();
- if (e->hasException)
- return;
+ Q_ASSERT(value.isNumber());
+ if (value.isInteger())
+ return { static_cast<quint8>(qBound(0, value.integerValue(), 255)) };
+ Q_ASSERT(value.isDouble());
+ double d = value.doubleValue();
// ### is there a way to optimise this?
- if (d <= 0 || std::isnan(d)) {
- data[index] = 0;
- return;
- }
- if (d >= 255) {
- data[index] = (char)(255);
- return;
- }
+ if (d <= 0 || std::isnan(d))
+ return { 0 };
+ if (d >= 255)
+ return { 255 };
double f = std::floor(d);
- if (f + 0.5 < d) {
- data[index] = (unsigned char)(f + 1);
- return;
- }
- if (d < f + 0.5) {
- data[index] = (unsigned char)(f);
- return;
- }
- if (int(f) % 2) {
+ if (f + 0.5 < d)
+ return { (quint8)(f + 1) };
+ if (d < f + 0.5)
+ return { (quint8)(f) };
+ if (int(f) % 2)
// odd number
- data[index] = (unsigned char)(f + 1);
- return;
- }
- data[index] = (unsigned char)(f);
+ return { (quint8)(f + 1) };
+ return { (quint8)(f) };
}
-ReturnedValue Int16ArrayRead(const char *data, int index)
+template <>
+float valueToType(Value value)
{
- return Encode((int)*(const short *)(data + index));
+ Q_ASSERT(value.isNumber());
+ double d = toDouble(value);
+ return static_cast<float>(d);
}
-void Int16ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value)
+template <>
+double valueToType(Value value)
{
- short v = (short)value.toInt32();
- if (e->hasException)
- return;
- *(short *)(data + index) = v;
+ Q_ASSERT(value.isNumber());
+ return toDouble(value);
}
-ReturnedValue UInt16ArrayRead(const char *data, int index)
+template <typename T>
+ReturnedValue read(const char *data) {
+ return typeToValue(*reinterpret_cast<const T *>(data));
+}
+template <typename T>
+void write(char *data, Value value)
{
- return Encode((int)*(const unsigned short *)(data + index));
+ *reinterpret_cast<T *>(data) = valueToType<T>(value);
}
-void UInt16ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value)
+template <typename T>
+ReturnedValue atomicAdd(char *data, Value v)
{
- unsigned short v = (unsigned short)value.toInt32();
- if (e->hasException)
- return;
- *(unsigned short *)(data + index) = v;
+ T value = valueToType<T>(v);
+ typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
+ value = QAtomicOps<T>::fetchAndAddOrdered(*mem, value);
+ return typeToValue(value);
}
-ReturnedValue Int32ArrayRead(const char *data, int index)
+template <typename T>
+ReturnedValue atomicAnd(char *data, Value v)
{
- return Encode(*(const int *)(data + index));
+ T value = valueToType<T>(v);
+ typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
+ value = QAtomicOps<T>::fetchAndAndOrdered(*mem, value);
+ return typeToValue(value);
}
-void Int32ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value)
+template <typename T>
+ReturnedValue atomicExchange(char *data, Value v)
{
- int v = (int)value.toInt32();
- if (e->hasException)
- return;
- *(int *)(data + index) = v;
+ T value = valueToType<T>(v);
+ typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
+ value = QAtomicOps<T>::fetchAndStoreOrdered(*mem, value);
+ return typeToValue(value);
}
-ReturnedValue UInt32ArrayRead(const char *data, int index)
+template <typename T>
+ReturnedValue atomicOr(char *data, Value v)
{
- return Encode(*(const unsigned int *)(data + index));
+ T value = valueToType<T>(v);
+ typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
+ value = QAtomicOps<T>::fetchAndOrOrdered(*mem, value);
+ return typeToValue(value);
}
-void UInt32ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value)
+template <typename T>
+ReturnedValue atomicSub(char *data, Value v)
{
- unsigned int v = (unsigned int)value.toUInt32();
- if (e->hasException)
- return;
- *(unsigned int *)(data + index) = v;
+ T value = valueToType<T>(v);
+ typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
+ value = QAtomicOps<T>::fetchAndSubOrdered(*mem, value);
+ return typeToValue(value);
}
-ReturnedValue Float32ArrayRead(const char *data, int index)
+template <typename T>
+ReturnedValue atomicXor(char *data, Value v)
{
- return Encode(*(const float *)(data + index));
+ T value = valueToType<T>(v);
+ typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
+ value = QAtomicOps<T>::fetchAndXorOrdered(*mem, value);
+ return typeToValue(value);
}
-void Float32ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value)
+template <typename T>
+ReturnedValue atomicCompareExchange(char *data, Value expected, Value v)
{
- float v = value.toNumber();
- if (e->hasException)
- return;
- *(float *)(data + index) = v;
+ T value = valueToType<T>(v);
+ T exp = valueToType<T>(expected);
+ typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
+ T old;
+ QAtomicOps<T>::testAndSetOrdered(*mem, exp, value, &old);
+ return typeToValue(old);
}
-ReturnedValue Float64ArrayRead(const char *data, int index)
+template <typename T>
+ReturnedValue atomicLoad(char *data)
{
- return Encode(*(const double *)(data + index));
+ typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
+ T val = QAtomicOps<T>::load(*mem);
+ return typeToValue(val);
}
-void Float64ArrayWrite(ExecutionEngine *e, char *data, int index, const Value &value)
+template <typename T>
+ReturnedValue atomicStore(char *data, Value v)
{
- double v = value.toNumber();
- if (e->hasException)
- return;
- *(double *)(data + index) = v;
+ T value = valueToType<T>(v);
+ typename QAtomicOps<T>::Type *mem = reinterpret_cast<typename QAtomicOps<T>::Type *>(data);
+ QAtomicOps<T>::store(*mem, value);
+ return typeToValue(value);
}
-const TypedArrayOperations operations[Heap::TypedArray::NTypes] = {
- { 1, "Int8Array", Int8ArrayRead, Int8ArrayWrite },
- { 1, "Uint8Array", UInt8ArrayRead, UInt8ArrayWrite },
- { 1, "Uint8ClampedArray", UInt8ArrayRead, UInt8ClampedArrayWrite },
- { 2, "Int16Array", Int16ArrayRead, Int16ArrayWrite },
- { 2, "Uint16Array", UInt16ArrayRead, UInt16ArrayWrite },
- { 4, "Int32Array", Int32ArrayRead, Int32ArrayWrite },
- { 4, "Uint32Array", UInt32ArrayRead, UInt32ArrayWrite },
- { 4, "Float32Array", Float32ArrayRead, Float32ArrayWrite },
- { 8, "Float64Array", Float64ArrayRead, Float64ArrayWrite },
+
+template<typename T>
+constexpr TypedArrayOperations TypedArrayOperations::create(const char *name)
+{
+ return { sizeof(T),
+ name,
+ ::read<T>,
+ ::write<T>,
+ { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr },
+ nullptr,
+ nullptr,
+ nullptr
+ };
+}
+
+template<typename T>
+constexpr TypedArrayOperations TypedArrayOperations::createWithAtomics(const char *name)
+{
+ return { sizeof(T),
+ name,
+ ::read<T>,
+ ::write<T>,
+ { ::atomicAdd<T>, ::atomicAnd<T>, ::atomicExchange<T>, ::atomicOr<T>, ::atomicSub<T>, ::atomicXor<T> },
+ ::atomicCompareExchange<T>,
+ ::atomicLoad<T>,
+ ::atomicStore<T>
+ };
+}
+
+const TypedArrayOperations operations[NTypedArrayTypes] = {
+#ifdef Q_ATOMIC_INT8_IS_SUPPORTED
+ TypedArrayOperations::createWithAtomics<qint8>("Int8Array"),
+ TypedArrayOperations::createWithAtomics<quint8>("Uint8Array"),
+#else
+ TypedArrayOperations::create<qint8>("Int8Array"),
+ TypedArrayOperations::create<quint8>("Uint8Array"),
+#endif
+ TypedArrayOperations::createWithAtomics<qint16>("Int16Array"),
+ TypedArrayOperations::createWithAtomics<quint16>("Uint16Array"),
+ TypedArrayOperations::createWithAtomics<qint32>("Int32Array"),
+ TypedArrayOperations::createWithAtomics<quint32>("Uint32Array"),
+ TypedArrayOperations::create<ClampedUInt8>("Uint8ClampedArray"),
+ TypedArrayOperations::create<float>("Float32Array"),
+ TypedArrayOperations::create<double>("Float64Array")
};
@@ -209,16 +280,28 @@ void Heap::TypedArrayCtor::init(QV4::ExecutionContext *scope, TypedArray::Type t
type = t;
}
-ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+ReturnedValue TypedArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
{
Scope scope(f->engine());
const TypedArrayCtor *that = static_cast<const TypedArrayCtor *>(f);
+ auto updateProto = [=](Scope &scope, Scoped<TypedArray> &a) {
+ if (newTarget->heapObject() != f->heapObject() && newTarget->isFunctionObject()) {
+ const FunctionObject *nt = static_cast<const FunctionObject *>(newTarget);
+ ScopedObject o(scope, nt->protoProperty());
+ if (o)
+ a->setPrototypeOf(o);
+ }
+ };
+
if (!argc || !argv[0].isObject()) {
// ECMA 6 22.2.1.1
- double l = argc ? argv[0].toNumber() : 0;
+ qint64 l = argc ? argv[0].toIndex() : 0;
if (scope.engine->hasException)
return Encode::undefined();
+ // ### lift UINT_MAX restriction
+ if (l < 0 || l > UINT_MAX)
+ return scope.engine->throwRangeError(QLatin1String("Index out of range."));
uint len = (uint)l;
if (l != len)
scope.engine->throwRangeError(QStringLiteral("Non integer length for typed array."));
@@ -232,12 +315,15 @@ ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const V
array->d()->byteLength = byteLength;
array->d()->byteOffset = 0;
+ updateProto(scope, array);
return array.asReturnedValue();
}
- Scoped<TypedArray> typedArray(scope, argc ? argv[0] : Primitive::undefinedValue());
+ Scoped<TypedArray> typedArray(scope, argc ? argv[0] : Value::undefinedValue());
if (!!typedArray) {
// ECMA 6 22.2.1.2
Scoped<ArrayBuffer> buffer(scope, typedArray->d()->buffer);
+ if (!buffer || buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
uint srcElementSize = typedArray->d()->type->bytesPerElement;
uint destElementSize = operations[that->d()->type].bytesPerElement;
uint byteLength = typedArray->d()->byteLength;
@@ -261,22 +347,27 @@ ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const V
} else {
// not same size, we need to loop
uint l = typedArray->length();
- TypedArrayRead read = typedArray->d()->type->read;
- TypedArrayWrite write =array->d()->type->write;
+ TypedArrayOperations::Read read = typedArray->d()->type->read;
+ TypedArrayOperations::Write write =array->d()->type->write;
for (uint i = 0; i < l; ++i) {
- Primitive val;
- val.setRawValue(read(src, i*srcElementSize));
- write(scope.engine, dest, i*destElementSize, val);
+ Value val;
+ val.setRawValue(read(src + i*srcElementSize));
+ write(dest + i*destElementSize, val);
}
}
+ updateProto(scope, array);
return array.asReturnedValue();
}
- Scoped<ArrayBuffer> buffer(scope, argc ? argv[0] : Primitive::undefinedValue());
+ Scoped<ArrayBuffer> buffer(scope, argc ? argv[0] : Value::undefinedValue());
if (!!buffer) {
// ECMA 6 22.2.1.4
double dbyteOffset = argc > 1 ? argv[1].toInteger() : 0;
+
+ if (buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
uint byteOffset = (uint)dbyteOffset;
uint elementSize = operations[that->d()->type].bytesPerElement;
if (dbyteOffset < 0 || (byteOffset % elementSize) || dbyteOffset > buffer->byteLength())
@@ -291,6 +382,8 @@ ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const V
double l = qBound(0., argv[2].toInteger(), (double)UINT_MAX);
if (scope.engine->hasException)
return Encode::undefined();
+ if (buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
l *= elementSize;
if (buffer->byteLength() - byteOffset < l)
return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length"));
@@ -301,18 +394,23 @@ ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const V
array->d()->buffer.set(scope.engine, buffer->d());
array->d()->byteLength = byteLength;
array->d()->byteOffset = byteOffset;
+
+ updateProto(scope, array);
return array.asReturnedValue();
}
// ECMA 6 22.2.1.3
- ScopedObject o(scope, argc ? argv[0] : Primitive::undefinedValue());
+ ScopedObject o(scope, argc ? argv[0] : Value::undefinedValue());
uint l = (uint) qBound(0., ScopedValue(scope, o->get(scope.engine->id_length()))->toInteger(), (double)UINT_MAX);
if (scope.engine->hasException)
return scope.engine->throwTypeError();
uint elementSize = operations[that->d()->type].bytesPerElement;
- Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(l * elementSize));
+ size_t bufferSize;
+ if (mul_overflow(size_t(l), size_t(elementSize), &bufferSize))
+ return scope.engine->throwRangeError(QLatin1String("new TypedArray: invalid length"));
+ Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(bufferSize));
if (scope.engine->hasException)
return Encode::undefined();
@@ -325,91 +423,207 @@ ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const V
char *b = newBuffer->d()->data->data();
ScopedValue val(scope);
while (idx < l) {
- val = o->getIndexed(idx);
- array->d()->type->write(scope.engine, b, 0, val);
+ val = o->get(idx);
+ val = val->convertedToNumber();
+ if (scope.engine->hasException)
+ return Encode::undefined();
+ array->d()->type->write(b, val);
if (scope.engine->hasException)
return Encode::undefined();
++idx;
b += elementSize;
}
-
+ updateProto(scope, array);
return array.asReturnedValue();
}
-ReturnedValue TypedArrayCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
+ReturnedValue TypedArrayCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
{
- return callAsConstructor(f, argv, argc);
+ return f->engine()->throwTypeError(QStringLiteral("calling a TypedArray constructor without new is invalid"));
}
void Heap::TypedArray::init(Type t)
{
Object::init();
- type = operations + t;
- arrayType = t;
+ type = operations + static_cast<int>(t);
+ arrayType = static_cast<int>(t);
}
Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type t)
{
- QV4::InternalClass *ic = e->internalClasses[EngineBase::Class_Empty]->changeVTable(staticVTable());
- ic = ic->changePrototype(e->typedArrayPrototype[t].d());
- return e->memoryManager->allocObject<TypedArray>(ic, e->typedArrayPrototype + t, t);
+ Scope scope(e);
+ Scoped<InternalClass> ic(scope, e->newInternalClass(staticVTable(), e->typedArrayPrototype + static_cast<int>(t)));
+ return e->memoryManager->allocObject<TypedArray>(ic->d(), t);
}
-ReturnedValue TypedArray::getIndexed(const Managed *m, uint index, bool *hasProperty)
+ReturnedValue TypedArray::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
+ uint index = id.asArrayIndex();
+ if (index == UINT_MAX && !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();
- uint bytesPerElement = a->d()->type->bytesPerElement;
- uint byteOffset = a->d()->byteOffset + index * bytesPerElement;
- if (byteOffset + bytesPerElement > (uint)a->d()->buffer->byteLength()) {
+ if (index >= a->length()) {
if (hasProperty)
*hasProperty = false;
return Encode::undefined();
}
+
+ uint bytesPerElement = a->d()->type->bytesPerElement;
+ uint byteOffset = a->d()->byteOffset + index * bytesPerElement;
+ Q_ASSERT(byteOffset + bytesPerElement <= (uint)a->d()->buffer->byteLength());
+
if (hasProperty)
*hasProperty = true;
- return a->d()->type->read(a->d()->buffer->data->data(), byteOffset);
+ return a->d()->type->read(a->d()->buffer->data->data() + byteOffset);
+}
+
+bool TypedArray::virtualHasProperty(const Managed *m, PropertyKey id)
+{
+ uint index = id.asArrayIndex();
+ if (index == UINT_MAX && !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;
}
-bool TypedArray::putIndexed(Managed *m, uint index, const Value &value)
+PropertyAttributes TypedArray::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
{
+ uint index = id.asArrayIndex();
+ if (index == UINT_MAX && !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);
+ if (p)
+ p->value = v;
+ return hasProperty ? Attr_NotConfigurable : PropertyAttributes();
+}
+
+bool TypedArray::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
+{
+ uint index = id.asArrayIndex();
+ if (index == UINT_MAX && !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)
return false;
Scope scope(v4);
Scoped<TypedArray> a(scope, static_cast<TypedArray *>(m));
+ if (a->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ if (index >= a->length())
+ return false;
uint bytesPerElement = a->d()->type->bytesPerElement;
uint byteOffset = a->d()->byteOffset + index * bytesPerElement;
- if (byteOffset + bytesPerElement > (uint)a->d()->buffer->byteLength())
+ Q_ASSERT(byteOffset + bytesPerElement <= (uint)a->d()->buffer->byteLength());
+
+ Value v = Value::fromReturnedValue(value.convertedToNumber());
+ if (scope.hasException() || a->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ a->d()->type->write(a->d()->buffer->data->data() + byteOffset, v);
+ return true;
+}
+
+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.
+
+ TypedArray *a = static_cast<TypedArray *>(m);
+ if (index >= a->length() || attrs.isAccessor())
return false;
- a->d()->type->write(scope.engine, a->d()->buffer->data->data(), byteOffset, value);
+ if (attrs.hasConfigurable() && attrs.isConfigurable())
+ return false;
+ if (attrs.hasEnumerable() && !attrs.isEnumerable())
+ return false;
+ if (attrs.hasWritable() && !attrs.isWritable())
+ return false;
+ if (!p->value.isEmpty()) {
+ ExecutionEngine *engine = a->engine();
+
+ Value v = Value::fromReturnedValue(p->value.convertedToNumber());
+ if (engine->hasException || a->d()->buffer->isDetachedBuffer())
+ return engine->throwTypeError();
+ uint bytesPerElement = a->d()->type->bytesPerElement;
+ uint byteOffset = a->d()->byteOffset + index * bytesPerElement;
+ Q_ASSERT(byteOffset + bytesPerElement <= (uint)a->d()->buffer->byteLength());
+ a->d()->type->write(a->d()->buffer->data->data() + byteOffset, v);
+ }
return true;
}
+struct TypedArrayOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
+{
+ ~TypedArrayOwnPropertyKeyIterator() override = default;
+ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
+
+};
+
+PropertyKey TypedArrayOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
+{
+ const TypedArray *a = static_cast<const TypedArray *>(o);
+ if (arrayIndex < a->length()) {
+ if (attrs)
+ *attrs = Attr_NotConfigurable;
+ PropertyKey id = PropertyKey::fromArrayIndex(arrayIndex);
+ if (pd) {
+ bool hasProperty = false;
+ pd->value = TypedArray::virtualGet(a, id, a, &hasProperty);
+ }
+ ++arrayIndex;
+ return id;
+ }
+
+ arrayIndex = UINT_MAX;
+ return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
+}
+
+OwnPropertyKeyIterator *TypedArray::virtualOwnPropertyKeys(const Object *m, Value *target)
+{
+ *target = *m;
+ return new TypedArrayOwnPropertyKeyIterator();
+}
+
void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor)
{
Scope scope(engine);
ScopedObject o(scope);
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(3));
- ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
- ctor->defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Primitive::fromInt32(operations[ctor->d()->type].bytesPerElement));
- defineDefaultProperty(engine->id_constructor(), (o = ctor));
- defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr);
- defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
- defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr);
- defineAccessorProperty(QStringLiteral("length"), method_get_length, nullptr);
- defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Primitive::fromInt32(operations[ctor->d()->type].bytesPerElement));
- defineDefaultProperty(QStringLiteral("set"), method_set, 1);
- defineDefaultProperty(QStringLiteral("subarray"), method_subarray, 0);
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(3));
+ ctor->defineReadonlyProperty(engine->id_prototype(), *this);
+ ctor->defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Value::fromInt32(operations[static_cast<int>(ctor->d()->type)].bytesPerElement));
+ ctor->setPrototypeOf(engine->intrinsicTypedArrayCtor());
+
+ setPrototypeOf(engine->intrinsicTypedArrayPrototype());
+ defineDefaultProperty(engine->id_constructor(), (o = ctor));
+ defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Value::fromInt32(operations[static_cast<int>(ctor->d()->type)].bytesPerElement));
}
-ReturnedValue TypedArrayPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int)
+ReturnedValue IntrinsicTypedArrayPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = b->engine();
const TypedArray *v = thisObject->as<TypedArray>();
@@ -419,49 +633,773 @@ ReturnedValue TypedArrayPrototype::method_get_buffer(const FunctionObject *b, co
return v->d()->buffer->asReturnedValue();
}
-ReturnedValue TypedArrayPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
+ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = b->engine();
const TypedArray *v = thisObject->as<TypedArray>();
if (!v)
return v4->throwTypeError();
+ if (v->d()->buffer->isDetachedBuffer())
+ return Encode(0);
+
return Encode(v->d()->byteLength);
}
-ReturnedValue TypedArrayPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int)
+ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = b->engine();
const TypedArray *v = thisObject->as<TypedArray>();
if (!v)
return v4->throwTypeError();
+ if (v->d()->buffer->isDetachedBuffer())
+ return Encode(0);
+
return Encode(v->d()->byteOffset);
}
-ReturnedValue TypedArrayPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int)
+ReturnedValue IntrinsicTypedArrayPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = b->engine();
const TypedArray *v = thisObject->as<TypedArray>();
if (!v)
return v4->throwTypeError();
+ if (v->d()->buffer->isDetachedBuffer())
+ return Encode(0);
+
return Encode(v->d()->byteLength/v->d()->type->bytesPerElement);
}
-ReturnedValue TypedArrayPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue IntrinsicTypedArrayPrototype::method_copyWithin(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(f);
+ Scoped<TypedArray> O(scope, thisObject);
+ if (!O || O->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ if (!argc)
+ return O->asReturnedValue();
+
+ qint64 len = static_cast<uint>(O->length());
+
+ qint64 to = static_cast<qint64>(argv[0].toInteger());
+ if (to < 0)
+ to = qMax(len + to, 0ll);
+ else
+ to = qMin(to, len);
+
+ qint64 from = (argc > 1) ? static_cast<qint64>(argv[1].toInteger()) : 0ll;
+ if (from < 0)
+ from = qMax(len + from, 0ll);
+ else
+ from = qMin(from, len);
+
+ double fend = argv[2].toInteger();
+ if (fend > len)
+ fend = len;
+ qint64 end = (argc > 2 && !argv[2].isUndefined()) ? static_cast<qint64>(fend) : len;
+ if (end < 0)
+ end = qMax(len + end, 0ll);
+ else
+ end = qMin(end, len);
+
+ qint64 count = qMin(end - from, len - to);
+
+ if (count <= 0)
+ return O->asReturnedValue();
+
+ if (O->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ if (from != to) {
+ int elementSize = O->d()->type->bytesPerElement;
+ char *data = O->d()->buffer->data->data() + O->d()->byteOffset;
+ memmove(data + to*elementSize, data + from*elementSize, count*elementSize);
+ }
+
+ return O->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<TypedArray> v(scope, thisObject);
+ if (!v || v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v));
+ ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_every(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<TypedArray> v(scope, thisObject);
+ if (!v || v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = v->length();
+
+ if (!argc || !argv->isFunctionObject())
+ THROW_TYPE_ERROR();
+ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
+
+ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
+ ScopedValue r(scope);
+ Value *arguments = scope.alloc(3);
+
+ const char *data = v->d()->buffer->data->data();
+ uint bytesPerElement = v->d()->type->bytesPerElement;
+ uint byteOffset = v->d()->byteOffset;
+
+ bool ok = true;
+ for (uint k = 0; ok && k < len; ++k) {
+ if (v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ arguments[0] = v->d()->type->read(data + byteOffset + k * bytesPerElement);
+
+ arguments[1] = Value::fromDouble(k);
+ arguments[2] = v;
+ r = callback->call(that, arguments, 3);
+ ok = r->toBoolean();
+ }
+ return Encode(ok);
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_fill(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<TypedArray> v(scope, thisObject);
+ if (!v || v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = v->length();
+ double dlen = len;
+ double relativeStart = argc > 1 ? argv[1].toInteger() : 0.;
+ double relativeEnd = len;
+ if (argc > 2 && !argv[2].isUndefined())
+ relativeEnd = argv[2].toInteger();
+
+ uint k = 0;
+ uint fin = 0;
+
+ if (relativeStart < 0) {
+ k = static_cast<uint>(std::max(len+relativeStart, 0.));
+ } else {
+ k = static_cast<uint>(std::min(relativeStart, dlen));
+ }
+
+ if (relativeEnd < 0) {
+ fin = static_cast<uint>(std::max(len + relativeEnd, 0.));
+ } else {
+ fin = static_cast<uint>(std::min(relativeEnd, dlen));
+ }
+
+ double val = argc ? argv[0].toNumber() : std::numeric_limits<double>::quiet_NaN();
+ Value value = Value::fromDouble(val);
+ if (scope.hasException() || v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ char *data = v->d()->buffer->data->data();
+ uint bytesPerElement = v->d()->type->bytesPerElement;
+ uint byteOffset = v->d()->byteOffset;
+
+ while (k < fin) {
+ v->d()->type->write(data + byteOffset + k * bytesPerElement, value);
+ k++;
+ }
+
+ return v.asReturnedValue();
+}
+
+static TypedArray *typedArraySpeciesCreate(Scope &scope, const TypedArray *instance, uint len)
+{
+ const FunctionObject *constructor = instance->speciesConstructor(scope, scope.engine->typedArrayCtors + instance->d()->arrayType);
+ if (!constructor) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+
+ Value *arguments = scope.alloc(1);
+ arguments[0] = Encode(len);
+ Scoped<TypedArray> a(scope, constructor->callAsConstructor(arguments, 1));
+ if (!a || a->d()->buffer->isDetachedBuffer() || a->length() < len) {
+ scope.engine->throwTypeError();
+ return nullptr;
+ }
+ return a;
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_filter(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<TypedArray> instance(scope, thisObject);
+ if (!instance || instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = instance->length();
+
+ if (!argc || !argv->isFunctionObject())
+ THROW_TYPE_ERROR();
+ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
+
+ ScopedValue selected(scope);
+ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
+ Value *arguments = scope.alloc(3);
+ Value *list = arguments;
+
+ uint to = 0;
+ for (uint k = 0; k < len; ++k) {
+ if (instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ bool exists;
+ arguments[0] = instance->get(k, &exists);
+ if (!exists)
+ continue;
+
+ arguments[1] = Value::fromDouble(k);
+ arguments[2] = instance;
+ selected = callback->call(that, arguments, 3);
+ if (selected->toBoolean()) {
+ ++arguments;
+ scope.alloc(1);
+ ++to;
+ }
+ }
+
+ TypedArray *a = typedArraySpeciesCreate(scope, instance, to);
+ if (!a)
+ return Encode::undefined();
+
+ for (uint i = 0; i < to; ++i)
+ a->put(i, list[i]);
+
+ return a->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_find(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<TypedArray> v(scope, thisObject);
+ if (!v || v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = v->length();
+
+ if (!argc || !argv[0].isFunctionObject())
+ THROW_TYPE_ERROR();
+ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
+
+ ScopedValue result(scope);
+ Value *arguments = scope.alloc(3);
+
+ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
+
+ for (uint k = 0; k < len; ++k) {
+ if (v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ arguments[0] = v->get(k);
+ CHECK_EXCEPTION();
+
+ arguments[1] = Value::fromDouble(k);
+ arguments[2] = v;
+ result = callback->call(that, arguments, 3);
+
+ CHECK_EXCEPTION();
+ if (result->toBoolean())
+ return arguments[0].asReturnedValue();
+ }
+
+ RETURN_UNDEFINED();
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_findIndex(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<TypedArray> v(scope, thisObject);
+ if (!v || v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = v->length();
+
+ if (!argc || !argv[0].isFunctionObject())
+ THROW_TYPE_ERROR();
+ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
+
+ ScopedValue result(scope);
+ Value *arguments = scope.alloc(3);
+
+ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
+
+ for (uint k = 0; k < len; ++k) {
+ if (v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ arguments[0] = v->get(k);
+ CHECK_EXCEPTION();
+
+ arguments[1] = Value::fromDouble(k);
+ arguments[2] = v;
+ result = callback->call(that, arguments, 3);
+
+ CHECK_EXCEPTION();
+ if (result->toBoolean())
+ return Encode(k);
+ }
+
+ return Encode(-1);
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<TypedArray> v(scope, thisObject);
+ if (!v || v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = v->length();
+
+ if (!argc || !argv->isFunctionObject())
+ THROW_TYPE_ERROR();
+ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
+
+ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
+ Value *arguments = scope.alloc(3);
+
+ for (uint k = 0; k < len; ++k) {
+ if (v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ bool exists;
+ arguments[0] = v->get(k, &exists);
+ if (!exists)
+ continue;
+
+ arguments[1] = Value::fromDouble(k);
+ arguments[2] = v;
+ callback->call(that, arguments, 3);
+ }
+ RETURN_UNDEFINED();
+}
+
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_includes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<TypedArray> v(scope, thisObject);
+ if (!v || v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = v->length();
+ if (len == 0) {
+ return Encode(false);
+ }
+
+ double n = 0;
+ if (argc > 1 && !argv[1].isUndefined()) {
+ n = argv[1].toInteger();
+ }
+
+ double k = 0;
+ if (n >= 0) {
+ k = n;
+ } else {
+ k = len + n;
+ if (k < 0) {
+ k = 0;
+ }
+ }
+
+ while (k < len) {
+ ScopedValue val(scope, v->get(k));
+ if (val->sameValueZero(argv[0])) {
+ return Encode(true);
+ }
+ k++;
+ }
+
+ return Encode(false);
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<TypedArray> v(scope, thisObject);
+ if (!v || v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = v->length();
+ if (!len)
+ return Encode(-1);
+
+ ScopedValue searchValue(scope, argc ? argv[0] : Value::undefinedValue());
+ uint fromIndex = 0;
+
+ if (argc >= 2) {
+ double f = argv[1].toInteger();
+ CHECK_EXCEPTION();
+ if (f >= len)
+ return Encode(-1);
+ if (f < 0)
+ f = qMax(len + f, 0.);
+ fromIndex = (uint) f;
+ }
+
+ if (v->isStringObject()) {
+ ScopedValue value(scope);
+ for (uint k = fromIndex; k < len; ++k) {
+ bool exists;
+ value = v->get(k, &exists);
+ if (exists && RuntimeHelpers::strictEqual(value, searchValue))
+ return Encode(k);
+ }
+ return Encode(-1);
+ }
+
+ ScopedValue value(scope);
+
+ for (uint i = fromIndex; i < len; ++i) {
+ bool exists;
+ value = v->get(i, &exists);
+ CHECK_EXCEPTION();
+ if (exists && RuntimeHelpers::strictEqual(value, searchValue))
+ return Encode(i);
+ }
+ return Encode(-1);
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_join(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<TypedArray> v(scope, thisObject);
+ if (!v || v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = v->length();
+
+ ScopedValue arg(scope, argc ? argv[0] : Value::undefinedValue());
+
+ QString r4;
+ if (arg->isUndefined())
+ r4 = QStringLiteral(",");
+ else
+ r4 = arg->toQString();
+
+ const quint32 r2 = len;
+
+ if (!r2)
+ return Encode(scope.engine->newString());
+
+ QString R;
+
+ //
+ // crazy!
+ //
+ ScopedString name(scope, scope.engine->newString(QStringLiteral("0")));
+ ScopedValue r6(scope, v->get(name));
+ if (!r6->isNullOrUndefined())
+ R = r6->toQString();
+
+ ScopedValue r12(scope);
+ for (quint32 k = 1; k < r2; ++k) {
+ R += r4;
+
+ name = Value::fromDouble(k).toString(scope.engine);
+ r12 = v->get(name);
+ CHECK_EXCEPTION();
+
+ if (!r12->isNullOrUndefined())
+ R += r12->toQString();
+ }
+
+ return Encode(scope.engine->newString(R));
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<TypedArray> v(scope, thisObject);
+ if (!v || v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v));
+ ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
+ return ao->asReturnedValue();
+}
+
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<TypedArray> instance(scope, thisObject);
+ if (!instance || instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = instance->length();
+ if (!len)
+ return Encode(-1);
+
+ ScopedValue searchValue(scope);
+ uint fromIndex = len;
+
+ if (argc >= 1)
+ searchValue = argv[0];
+ else
+ searchValue = Value::undefinedValue();
+
+ if (argc >= 2) {
+ double f = argv[1].toInteger();
+ CHECK_EXCEPTION();
+ if (f > 0)
+ f = qMin(f, (double)(len - 1));
+ else if (f < 0) {
+ f = len + f;
+ if (f < 0)
+ return Encode(-1);
+ }
+ fromIndex = (uint) f + 1;
+ }
+
+ ScopedValue value(scope);
+ for (uint k = fromIndex; k > 0;) {
+ --k;
+ bool exists;
+ value = instance->get(k, &exists);
+ if (exists && RuntimeHelpers::strictEqual(value, searchValue))
+ return Encode(k);
+ }
+ return Encode(-1);
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_map(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<TypedArray> instance(scope, thisObject);
+ if (!instance || instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = instance->length();
+
+ if (!argc || !argv->isFunctionObject())
+ THROW_TYPE_ERROR();
+ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
+
+ TypedArray *a = typedArraySpeciesCreate(scope, instance, len);
+ if (!a)
+ return Encode::undefined();
+
+ ScopedValue v(scope);
+ ScopedValue mapped(scope);
+ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
+ Value *arguments = scope.alloc(3);
+
+ for (uint k = 0; k < len; ++k) {
+ if (instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ arguments[0] = instance->get(k);
+
+ arguments[1] = Value::fromDouble(k);
+ arguments[2] = instance;
+ mapped = callback->call(that, arguments, 3);
+ a->put(k, mapped);
+ }
+ return a->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_reduce(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<TypedArray> instance(scope, thisObject);
+ if (!instance || instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = instance->length();
+
+ if (!argc || !argv->isFunctionObject())
+ THROW_TYPE_ERROR();
+ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
+
+ uint k = 0;
+ ScopedValue acc(scope);
+ ScopedValue v(scope);
+
+ if (argc > 1) {
+ acc = argv[1];
+ } else {
+ bool kPresent = false;
+ while (k < len && !kPresent) {
+ v = instance->get(k, &kPresent);
+ if (kPresent)
+ acc = v;
+ ++k;
+ }
+ if (!kPresent)
+ THROW_TYPE_ERROR();
+ }
+
+ Value *arguments = scope.alloc(4);
+
+ while (k < len) {
+ if (instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ bool kPresent;
+ v = instance->get(k, &kPresent);
+ if (kPresent) {
+ arguments[0] = acc;
+ arguments[1] = v;
+ arguments[2] = Value::fromDouble(k);
+ arguments[3] = instance;
+ acc = callback->call(nullptr, arguments, 4);
+ }
+ ++k;
+ }
+ return acc->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_reduceRight(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<TypedArray> instance(scope, thisObject);
+ if (!instance || instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = instance->length();
+
+ if (!argc || !argv->isFunctionObject())
+ THROW_TYPE_ERROR();
+ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
+
+ if (len == 0) {
+ if (argc == 1)
+ THROW_TYPE_ERROR();
+ return argv[1].asReturnedValue();
+ }
+
+ uint k = len;
+ ScopedValue acc(scope);
+ ScopedValue v(scope);
+ if (argc > 1) {
+ acc = argv[1];
+ } else {
+ bool kPresent = false;
+ while (k > 0 && !kPresent) {
+ v = instance->get(k - 1, &kPresent);
+ if (kPresent)
+ acc = v;
+ --k;
+ }
+ if (!kPresent)
+ THROW_TYPE_ERROR();
+ }
+
+ Value *arguments = scope.alloc(4);
+
+ while (k > 0) {
+ if (instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ bool kPresent;
+ v = instance->get(k - 1, &kPresent);
+ if (kPresent) {
+ arguments[0] = acc;
+ arguments[1] = v;
+ arguments[2] = Value::fromDouble(k - 1);
+ arguments[3] = instance;
+ acc = callback->call(nullptr, arguments, 4);
+ }
+ --k;
+ }
+ return acc->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_reverse(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<TypedArray> instance(scope, thisObject);
+ if (!instance || instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint length = instance->length();
+
+ int lo = 0, hi = length - 1;
+
+ ScopedValue lval(scope);
+ ScopedValue hval(scope);
+ for (; lo < hi; ++lo, --hi) {
+ bool loExists, hiExists;
+ lval = instance->get(lo, &loExists);
+ hval = instance->get(hi, &hiExists);
+ Q_ASSERT(hiExists && loExists);
+ bool ok;
+ ok = instance->put(lo, hval);
+ Q_ASSERT(ok);
+ ok = instance->put(hi, lval);
+ Q_ASSERT(ok);
+ }
+ return instance->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_some(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<TypedArray> instance(scope, thisObject);
+ if (!instance || instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = instance->length();
+
+ if (!argc || !argv->isFunctionObject())
+ THROW_TYPE_ERROR();
+ const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
+
+ ScopedValue that(scope, argc > 1 ? argv[1] : Value::undefinedValue());
+ ScopedValue result(scope);
+ Value *arguments = scope.alloc(3);
+
+ for (uint k = 0; k < len; ++k) {
+ if (instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ bool exists;
+ arguments[0] = instance->get(k, &exists);
+ if (!exists)
+ continue;
+
+ arguments[1] = Value::fromDouble(k);
+ arguments[2] = instance;
+ result = callback->call(that, arguments, 3);
+ if (result->toBoolean())
+ return Encode(true);
+ }
+ return Encode(false);
+}
+
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<TypedArray> v(scope, thisObject);
+ if (!v || v->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(v));
+ ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
Scoped<TypedArray> a(scope, *thisObject);
if (!a)
return scope.engine->throwTypeError();
Scoped<ArrayBuffer> buffer(scope, a->d()->buffer);
- if (!buffer)
- scope.engine->throwTypeError();
double doffset = argc >= 2 ? argv[1].toInteger() : 0;
if (scope.engine->hasException)
RETURN_UNDEFINED();
+ if (!buffer || buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
if (doffset < 0 || doffset >= UINT_MAX)
RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range")));
@@ -484,11 +1422,18 @@ ReturnedValue TypedArrayPrototype::method_set(const FunctionObject *b, const Val
RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range")));
uint idx = 0;
+ if (buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
char *b = buffer->d()->data->data() + a->d()->byteOffset + offset*elementSize;
ScopedValue val(scope);
while (idx < l) {
- val = o->getIndexed(idx);
- a->d()->type->write(scope.engine, b, 0, val);
+ val = o->get(idx);
+ if (scope.hasException())
+ return Encode::undefined();
+ val = val->convertedToNumber();
+ if (scope.hasException() || buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ a->d()->type->write(b, val);
if (scope.engine->hasException)
RETURN_UNDEFINED();
++idx;
@@ -499,7 +1444,7 @@ ReturnedValue TypedArrayPrototype::method_set(const FunctionObject *b, const Val
// src is a typed array
Scoped<ArrayBuffer> srcBuffer(scope, srcTypedArray->d()->buffer);
- if (!srcBuffer)
+ if (!srcBuffer || srcBuffer->isDetachedBuffer())
return scope.engine->throwTypeError();
uint l = srcTypedArray->length();
@@ -524,12 +1469,12 @@ ReturnedValue TypedArrayPrototype::method_set(const FunctionObject *b, const Val
// typed arrays of different kind, need to manually loop
uint srcElementSize = srcTypedArray->d()->type->bytesPerElement;
- TypedArrayRead read = srcTypedArray->d()->type->read;
- TypedArrayWrite write = a->d()->type->write;
+ TypedArrayOperations::Read read = srcTypedArray->d()->type->read;
+ TypedArrayOperations::Write write = a->d()->type->write;
for (uint i = 0; i < l; ++i) {
- Primitive val;
- val.setRawValue(read(src, i*srcElementSize));
- write(scope.engine, dest, i*elementSize, val);
+ Value val;
+ val.setRawValue(read(src + i*srcElementSize));
+ write(dest + i*elementSize, val);
}
if (srcCopy)
@@ -538,7 +1483,54 @@ ReturnedValue TypedArrayPrototype::method_set(const FunctionObject *b, const Val
RETURN_UNDEFINED();
}
-ReturnedValue TypedArrayPrototype::method_subarray(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue IntrinsicTypedArrayPrototype::method_slice(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<TypedArray> instance(scope, thisObject);
+ if (!instance || instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = instance->length();
+
+ double s = (argc ? argv[0] : Value::undefinedValue()).toInteger();
+ uint start;
+ if (s < 0)
+ start = (uint)qMax(len + s, 0.);
+ else if (s > len)
+ start = len;
+ else
+ start = (uint) s;
+ uint end = len;
+ if (argc > 1 && !argv[1].isUndefined()) {
+ double e = argv[1].toInteger();
+ if (e < 0)
+ end = (uint)qMax(len + e, 0.);
+ else if (e > len)
+ end = len;
+ else
+ end = (uint) e;
+ }
+ uint count = start > end ? 0 : end - start;
+
+ TypedArray *a = typedArraySpeciesCreate(scope, instance, count);
+ if (!a)
+ return Encode::undefined();
+
+ ScopedValue v(scope);
+ uint n = 0;
+ for (uint i = start; i < end; ++i) {
+ if (instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ v = instance->get(i);
+ if (a->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ a->put(n, v);
+ ++n;
+ }
+ return a->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(builtin);
Scoped<TypedArray> a(scope, *thisObject);
@@ -547,8 +1539,7 @@ ReturnedValue TypedArrayPrototype::method_subarray(const FunctionObject *builtin
return scope.engine->throwTypeError();
Scoped<ArrayBuffer> buffer(scope, a->d()->buffer);
- if (!buffer)
- return scope.engine->throwTypeError();
+ Q_ASSERT(buffer);
int len = a->length();
double b = argc > 0 ? argv[0].toInteger() : 0;
@@ -568,7 +1559,7 @@ ReturnedValue TypedArrayPrototype::method_subarray(const FunctionObject *builtin
int newLen = end - begin;
- ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor()));
+ ScopedFunctionObject constructor(scope, a->speciesConstructor(scope, scope.engine->typedArrayCtors + a->d()->arrayType));
if (!constructor)
return scope.engine->throwTypeError();
@@ -576,5 +1567,134 @@ ReturnedValue TypedArrayPrototype::method_subarray(const FunctionObject *builtin
arguments[0] = buffer;
arguments[1] = Encode(a->d()->byteOffset + begin*a->d()->type->bytesPerElement);
arguments[2] = Encode(newLen);
- return constructor->callAsConstructor(arguments, 3);
+ a = constructor->callAsConstructor(arguments, 3);
+ if (!a || a->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ return a->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<TypedArray> instance(scope, thisObject);
+ if (!instance || instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+
+ uint len = instance->length();
+ const QString separator = QStringLiteral(",");
+
+ QString R;
+
+ ScopedValue v(scope);
+ ScopedString s(scope);
+
+ for (uint k = 0; k < len; ++k) {
+ if (instance->d()->buffer->isDetachedBuffer())
+ return scope.engine->throwTypeError();
+ if (k)
+ R += separator;
+
+ v = instance->get(k);
+ v = Runtime::method_callElement(scope.engine, v, *scope.engine->id_toLocaleString(), nullptr, 0);
+ s = v->toString(scope.engine);
+ if (scope.hasException())
+ return Encode::undefined();
+
+ R += s->toQString();
+ }
+ return scope.engine->newString(R)->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_get_toStringTag(const FunctionObject *, const Value *thisObject, const Value *, int)
+{
+ const TypedArray *a = thisObject->as<TypedArray>();
+ if (!a)
+ return Encode::undefined();
+
+ return a->engine()->newString(QString::fromLatin1(a->d()->type->name))->asReturnedValue();
+}
+
+static bool validateTypedArray(const Object *o)
+{
+ const TypedArray *a = o->as<TypedArray>();
+ if (!a)
+ return false;
+ if (a->d()->buffer->isDetachedBuffer())
+ return false;
+ return true;
+}
+
+ReturnedValue IntrinsicTypedArrayCtor::method_of(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(f);
+ int len = argc;
+ const Value *items = argv;
+ const FunctionObject *C = thisObject->as<FunctionObject>();
+ if (!C || !C->isConstructor())
+ return scope.engine->throwTypeError();
+
+ Value lenValue = Value::fromInt32(len);
+ ScopedObject newObj(scope, C->callAsConstructor(&lenValue, 1));
+ if (scope.hasException())
+ return Encode::undefined();
+ if (!::validateTypedArray(newObj))
+ return scope.engine->throwTypeError();
+ TypedArray *a = newObj->as<TypedArray>();
+ Q_ASSERT(a);
+ if (a->length() < static_cast<uint>(len))
+ return scope.engine->throwTypeError();
+
+ for (int k = 0; k < len; ++k) {
+ newObj->put(PropertyKey::fromArrayIndex(k), items[k]);
+ }
+ return newObj->asReturnedValue();
+}
+
+void IntrinsicTypedArrayPrototype::init(ExecutionEngine *engine, IntrinsicTypedArrayCtor *ctor)
+{
+ Scope scope(engine);
+ ctor->defineReadonlyProperty(engine->id_prototype(), *this);
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Value::fromInt32(0));
+ ScopedString s(scope, engine->newString(QStringLiteral("TypedArray")));
+ ctor->defineReadonlyConfigurableProperty(engine->id_name(), s);
+ s = scope.engine->newString(QStringLiteral("of"));
+ ctor->defineDefaultProperty(s, IntrinsicTypedArrayCtor::method_of);
+ ctor->addSymbolSpecies();
+
+ defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr);
+ defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
+ defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr);
+ defineAccessorProperty(QStringLiteral("length"), method_get_length, nullptr);
+
+ defineDefaultProperty(QStringLiteral("copyWithin"), method_copyWithin, 2);
+ defineDefaultProperty(QStringLiteral("entries"), method_entries, 0);
+ defineDefaultProperty(QStringLiteral("every"), method_every, 1);
+ defineDefaultProperty(QStringLiteral("fill"), method_fill, 1);
+ defineDefaultProperty(QStringLiteral("filter"), method_filter, 1);
+ defineDefaultProperty(QStringLiteral("find"), method_find, 1);
+ defineDefaultProperty(QStringLiteral("findIndex"), method_findIndex, 1);
+ defineDefaultProperty(QStringLiteral("forEach"), method_forEach, 1);
+ defineDefaultProperty(QStringLiteral("includes"), method_includes, 1);
+ defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1);
+ defineDefaultProperty(QStringLiteral("join"), method_join, 1);
+ defineDefaultProperty(QStringLiteral("keys"), method_keys, 0);
+ defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
+ defineDefaultProperty(QStringLiteral("map"), method_map, 1);
+ defineDefaultProperty(QStringLiteral("reduce"), method_reduce, 1);
+ defineDefaultProperty(QStringLiteral("reduceRight"), method_reduceRight, 1);
+ defineDefaultProperty(QStringLiteral("reverse"), method_reverse, 0);
+ defineDefaultProperty(QStringLiteral("some"), method_some, 1);
+ defineDefaultProperty(QStringLiteral("set"), method_set, 1);
+ defineDefaultProperty(QStringLiteral("slice"), method_slice, 2);
+ defineDefaultProperty(QStringLiteral("subarray"), method_subarray, 2);
+ defineDefaultProperty(engine->id_toLocaleString(), method_toLocaleString, 0);
+ ScopedObject f(scope, engine->arrayPrototype()->get(engine->id_toString()));
+ defineDefaultProperty(engine->id_toString(), f);
+
+ ScopedString valuesString(scope, engine->newIdentifier(QStringLiteral("values")));
+ ScopedObject values(scope, FunctionObject::createBuiltinFunction(engine, valuesString, method_values, 0));
+ defineDefaultProperty(QStringLiteral("values"), values);
+ defineDefaultProperty(engine->symbol_iterator(), values);
+
+ defineAccessorProperty(engine->symbol_toStringTag(), method_get_toStringTag, nullptr);
}
diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h
index 129c662c97..64792f23a2 100644
--- a/src/qml/jsruntime/qv4typedarray_p.h
+++ b/src/qml/jsruntime/qv4typedarray_p.h
@@ -60,14 +60,50 @@ namespace QV4 {
struct ArrayBuffer;
-typedef ReturnedValue (*TypedArrayRead)(const char *data, int index);
-typedef void (*TypedArrayWrite)(ExecutionEngine *engine, char *data, int index, const Value &value);
+enum TypedArrayType {
+ Int8Array,
+ UInt8Array,
+ Int16Array,
+ UInt16Array,
+ Int32Array,
+ UInt32Array,
+ UInt8ClampedArray,
+ Float32Array,
+ Float64Array,
+ NTypedArrayTypes
+};
+
+enum AtomicModifyOps {
+ AtomicAdd,
+ AtomicAnd,
+ AtomicExchange,
+ AtomicOr,
+ AtomicSub,
+ AtomicXor,
+ NAtomicModifyOps
+};
struct TypedArrayOperations {
+ typedef ReturnedValue (*Read)(const char *data);
+ typedef void (*Write)(char *data, Value value);
+ typedef ReturnedValue (*AtomicModify)(char *data, Value value);
+ typedef ReturnedValue (*AtomicCompareExchange)(char *data, Value expected, Value v);
+ typedef ReturnedValue (*AtomicLoad)(char *data);
+ typedef ReturnedValue (*AtomicStore)(char *data, Value value);
+
+ template<typename T>
+ static constexpr TypedArrayOperations create(const char *name);
+ template<typename T>
+ static constexpr TypedArrayOperations createWithAtomics(const char *name);
+
int bytesPerElement;
const char *name;
- TypedArrayRead read;
- TypedArrayWrite write;
+ Read read;
+ Write write;
+ AtomicModify atomicModifyOps[AtomicModifyOps::NAtomicModifyOps];
+ AtomicCompareExchange atomicCompareExchange;
+ AtomicLoad atomicLoad;
+ AtomicStore atomicStore;
};
namespace Heap {
@@ -81,28 +117,23 @@ namespace Heap {
DECLARE_HEAP_OBJECT(TypedArray, Object) {
DECLARE_MARKOBJECTS(TypedArray);
- enum Type {
- Int8Array,
- UInt8Array,
- UInt8ClampedArray,
- Int16Array,
- UInt16Array,
- Int32Array,
- UInt32Array,
- Float32Array,
- Float64Array,
- NTypes
- };
+ using Type = TypedArrayType;
void init(Type t);
};
+struct IntrinsicTypedArrayCtor : FunctionObject {
+};
+
struct TypedArrayCtor : FunctionObject {
void init(QV4::ExecutionContext *scope, TypedArray::Type t);
TypedArray::Type type;
};
+struct IntrinsicTypedArrayPrototype : Object {
+};
+
struct TypedArrayPrototype : Object {
inline void init(TypedArray::Type t);
TypedArray::Type type;
@@ -132,34 +163,80 @@ struct Q_QML_PRIVATE_EXPORT TypedArray : Object
Heap::TypedArray::Type arrayType() const {
return static_cast<Heap::TypedArray::Type>(d()->arrayType);
}
+ using Object::get;
+
+ static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
+ static bool virtualHasProperty(const Managed *m, PropertyKey id);
+ static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p);
+ static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
+ static bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs);
+ static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
+
+};
+
+struct IntrinsicTypedArrayCtor: FunctionObject
+{
+ V4_OBJECT2(IntrinsicTypedArrayCtor, FunctionObject)
+
+ static constexpr VTable::Call virtualCall = nullptr;
- static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty);
- static bool putIndexed(Managed *m, uint index, const Value &value);
+ static ReturnedValue method_of(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
struct TypedArrayCtor: FunctionObject
{
V4_OBJECT2(TypedArrayCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
- static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *);
+ static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
-
-struct TypedArrayPrototype : Object
+struct IntrinsicTypedArrayPrototype : Object
{
- V4_OBJECT2(TypedArrayPrototype, Object)
+ V4_OBJECT2(IntrinsicTypedArrayPrototype, Object)
V4_PROTOTYPE(objectPrototype)
- void init(ExecutionEngine *engine, TypedArrayCtor *ctor);
+ void init(ExecutionEngine *engine, IntrinsicTypedArrayCtor *ctor);
static ReturnedValue method_get_buffer(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_byteLength(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_byteOffset(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_length(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_copyWithin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_every(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_fill(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_filter(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_find(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_findIndex(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_includes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_indexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_join(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_lastIndexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_map(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_reduce(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_reduceRight(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_reverse(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_some(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_slice(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_subarray(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_get_toStringTag(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+};
+
+struct TypedArrayPrototype : Object
+{
+ V4_OBJECT2(TypedArrayPrototype, Object)
+ V4_PROTOTYPE(objectPrototype)
+
+ void init(ExecutionEngine *engine, TypedArrayCtor *ctor);
};
inline void
diff --git a/src/qml/jsruntime/qv4util_p.h b/src/qml/jsruntime/qv4util_p.h
index 2669a3e4bf..073832937d 100644
--- a/src/qml/jsruntime/qv4util_p.h
+++ b/src/qml/jsruntime/qv4util_p.h
@@ -59,26 +59,6 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
-template <typename T>
-struct TemporaryAssignment
-{
- TemporaryAssignment(T &var, const T& temporaryValue)
- : variable(var)
- , savedValue(var)
- {
- variable = temporaryValue;
- }
- ~TemporaryAssignment()
- {
- variable = savedValue;
- }
- T &variable;
- T savedValue;
-private:
- TemporaryAssignment(const TemporaryAssignment<T>&);
- TemporaryAssignment operator=(const TemporaryAssignment<T>&);
-};
-
#if !defined(BROKEN_STD_VECTOR_BOOL_OR_BROKEN_STD_FIND)
// Sanity:
class BitVector
diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp
index 0d4711df3c..cbc153bb86 100644
--- a/src/qml/jsruntime/qv4value.cpp
+++ b/src/qml/jsruntime/qv4value.cpp
@@ -39,7 +39,9 @@
#include <qv4engine_p.h>
#include <qv4runtime_p.h>
#include <qv4string_p.h>
+#include <qv4propertykey_p.h>
#ifndef V4_BOOTSTRAP
+#include <qv4symbol_p.h>
#include <qv4object_p.h>
#include <qv4objectproto_p.h>
#include <private/qv4mm_p.h>
@@ -84,7 +86,7 @@ bool Value::toBooleanImpl(Value val)
#ifdef V4_BOOTSTRAP
Q_UNIMPLEMENTED();
#else
- if (b->vtable()->isString)
+ if (b->internalClass->vtable->isString)
return static_cast<Heap::String *>(b)->length() > 0;
#endif
return true;
@@ -107,6 +109,11 @@ double Value::toNumberImpl(Value val)
#else
if (String *s = val.stringValue())
return RuntimeHelpers::stringToNumber(s->toQString());
+ if (val.isSymbol()) {
+ Managed &m = static_cast<Managed &>(val);
+ m.engine()->throwTypeError();
+ return 0;
+ }
{
Q_ASSERT(val.isObject());
Scope scope(val.objectValue()->engine());
@@ -145,6 +152,8 @@ QString Value::toQStringNoThrow() const
case Value::Managed_Type:
if (String *s = stringValue())
return s->toQString();
+ if (Symbol *s = symbolValue())
+ return s->descriptiveString();
{
Q_ASSERT(isObject());
Scope scope(objectValue()->engine());
@@ -197,9 +206,12 @@ QString Value::toQString() const
else
return QStringLiteral("false");
case Value::Managed_Type:
- if (String *s = stringValue())
+ if (String *s = stringValue()) {
return s->toQString();
- {
+ } else if (isSymbol()) {
+ static_cast<const Managed *>(this)->engine()->throwTypeError();
+ return QString();
+ } else {
Q_ASSERT(isObject());
Scope scope(objectValue()->engine());
ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT));
@@ -217,6 +229,25 @@ QString Value::toQString() const
}
} // switch
}
+
+QV4::PropertyKey Value::toPropertyKey(ExecutionEngine *e) const
+{
+ if (isInteger() && int_32() >= 0)
+ return PropertyKey::fromArrayIndex(static_cast<uint>(int_32()));
+ if (isStringOrSymbol()) {
+ Scope scope(e);
+ ScopedStringOrSymbol s(scope, this);
+ return s->toPropertyKey();
+ }
+ Scope scope(e);
+ ScopedValue v(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT));
+ if (!v->isStringOrSymbol())
+ v = v->toString(e);
+ if (e->hasException)
+ return PropertyKey::invalid();
+ ScopedStringOrSymbol s(scope, v);
+ return s->toPropertyKey();
+}
#endif // V4_BOOTSTRAP
bool Value::sameValue(Value other) const {
@@ -235,6 +266,25 @@ bool Value::sameValue(Value other) const {
return false;
}
+bool Value::sameValueZero(Value other) const {
+ if (_val == other._val)
+ return true;
+ String *s = stringValue();
+ String *os = other.stringValue();
+ if (s && os)
+ return s->isEqualTo(os);
+ if (isInteger() && other.isDouble())
+ return double(int_32()) == other.doubleValue();
+ if (isDouble() && other.isInteger())
+ return other.int_32() == doubleValue();
+ if (isDouble() && other.isDouble()) {
+ if (doubleValue() == 0 && other.doubleValue() == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
#ifndef V4_BOOTSTRAP
Heap::String *Value::toString(ExecutionEngine *e, Value val)
{
diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h
index a5ee6b5373..7c895e3637 100644
--- a/src/qml/jsruntime/qv4value_p.h
+++ b/src/qml/jsruntime/qv4value_p.h
@@ -56,6 +56,7 @@
#include <QtCore/QString>
#include "qv4global_p.h"
#include <private/qv4heap_p.h>
+#include <private/qv4internalclass_p.h>
#include <private/qnumeric_p.h>
@@ -69,7 +70,6 @@ namespace Heap {
struct Q_QML_PRIVATE_EXPORT Value
{
-private:
/*
We use 8 bytes for a value and a different variant of NaN boxing. A Double
NaN (actually -qNaN) is indicated by a number that has the top 13 bits set, and for a
@@ -125,10 +125,9 @@ private:
quint64 _val;
-public:
- QML_NEARLY_ALWAYS_INLINE quint64 &rawValueRef() { return _val; }
- QML_NEARLY_ALWAYS_INLINE quint64 rawValue() const { return _val; }
- QML_NEARLY_ALWAYS_INLINE void setRawValue(quint64 raw) { _val = raw; }
+ QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 &rawValueRef() { return _val; }
+ QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR quint64 rawValue() const { return _val; }
+ QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setRawValue(quint64 raw) { _val = raw; }
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
static inline int valueOffset() { return 0; }
@@ -137,10 +136,11 @@ public:
static inline int valueOffset() { return 4; }
static inline int tagOffset() { return 0; }
#endif
- QML_NEARLY_ALWAYS_INLINE void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; }
- QML_NEARLY_ALWAYS_INLINE quint32 value() const { return _val & quint64(~quint32(0)); }
- QML_NEARLY_ALWAYS_INLINE quint32 tag() const { return _val >> 32; }
- QML_NEARLY_ALWAYS_INLINE void setTag(quint32 tag) { setTagValue(tag, value()); }
+ static inline constexpr quint64 tagValue(quint32 tag, quint32 value) { return quint64(tag) << 32 | value; }
+ QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; }
+ QML_NEARLY_ALWAYS_INLINE constexpr quint32 value() const { return _val & quint64(~quint32(0)); }
+ QML_NEARLY_ALWAYS_INLINE constexpr quint32 tag() const { return _val >> 32; }
+ QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setTag(quint32 tag) { setTagValue(tag, value()); }
#if QT_POINTER_SIZE == 8
QML_NEARLY_ALWAYS_INLINE Heap::Base *m() const
@@ -172,35 +172,19 @@ public:
# error "unsupported pointer size"
#endif
- QML_NEARLY_ALWAYS_INLINE int int_32() const
+ QML_NEARLY_ALWAYS_INLINE constexpr int int_32() const
{
return int(value());
}
- QML_NEARLY_ALWAYS_INLINE void setInt_32(int i)
+ QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setInt_32(int i)
{
setTagValue(quint32(ValueTypeInternal::Integer), quint32(i));
}
QML_NEARLY_ALWAYS_INLINE uint uint_32() const { return value(); }
- QML_NEARLY_ALWAYS_INLINE void setEmpty()
- {
- setTagValue(quint32(ValueTypeInternal::Empty), value());
- }
-
- QML_NEARLY_ALWAYS_INLINE void setEmpty(int i)
- {
- setTagValue(quint32(ValueTypeInternal::Empty), quint32(i));
- }
-
- QML_NEARLY_ALWAYS_INLINE void setEmpty(quint32 i)
+ QML_NEARLY_ALWAYS_INLINE Q_DECL_RELAXED_CONSTEXPR void setEmpty()
{
- setTagValue(quint32(ValueTypeInternal::Empty), i);
- }
-
- QML_NEARLY_ALWAYS_INLINE quint32 emptyValue()
- {
- Q_ASSERT(isEmpty());
- return quint32(value());
+ setTagValue(quint32(ValueTypeInternal::Empty), 0);
}
// ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible
@@ -248,7 +232,8 @@ public:
IsManagedOrUndefined_Shift = 64-15,
IsIntegerConvertible_Shift = 64-15,
IsIntegerOrBool_Shift = 64-16,
- QuickType_Shift = 64 - 17
+ QuickType_Shift = 64 - 17,
+ IsPositiveIntShift = 31
};
static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49
@@ -315,6 +300,14 @@ public:
}
inline bool isNaN() const { return (tag() & 0x7ffc0000 ) == 0x00040000; }
+ inline bool isPositiveInt() const {
+#if QT_POINTER_SIZE == 4
+ return isInteger() && int_32() >= 0;
+#else
+ return (_val >> IsPositiveIntShift) == (quint64(ValueTypeInternal::Integer) << 1);
+#endif
+ }
+
QML_NEARLY_ALWAYS_INLINE double doubleValue() const {
Q_ASSERT(isDouble());
double d;
@@ -331,6 +324,8 @@ public:
Q_ASSERT(isDouble());
}
inline bool isString() const;
+ inline bool isStringOrSymbol() const;
+ inline bool isSymbol() const;
inline bool isObject() const;
inline bool isFunctionObject() const;
inline bool isInt32() {
@@ -338,14 +333,17 @@ public:
return true;
if (isDouble()) {
double d = doubleValue();
- int i = (int)d;
- if (i == d && !(d == 0 && std::signbit(d))) {
- setInt_32(i);
+ if (isInt32(d)) {
+ setInt_32(int(d));
return true;
}
}
return false;
}
+ QML_NEARLY_ALWAYS_INLINE static bool isInt32(double d) {
+ int i = int(d);
+ return (i == d && !(d == 0 && std::signbit(d)));
+ }
double asDouble() const {
if (tag() == quint32(ValueTypeInternal::Integer))
return int_32();
@@ -362,7 +360,17 @@ public:
QML_NEARLY_ALWAYS_INLINE String *stringValue() const {
if (!isString())
return nullptr;
- return reinterpret_cast<String*>(const_cast<Value *>(this));
+ return reinterpret_cast<String *>(const_cast<Value *>(this));
+ }
+ QML_NEARLY_ALWAYS_INLINE StringOrSymbol *stringOrSymbolValue() const {
+ if (!isStringOrSymbol())
+ return nullptr;
+ return reinterpret_cast<StringOrSymbol *>(const_cast<Value *>(this));
+ }
+ QML_NEARLY_ALWAYS_INLINE Symbol *symbolValue() const {
+ if (!isSymbol())
+ return nullptr;
+ return reinterpret_cast<Symbol *>(const_cast<Value *>(this));
}
QML_NEARLY_ALWAYS_INLINE Object *objectValue() const {
if (!isObject())
@@ -388,6 +396,8 @@ public:
int toUInt16() const;
inline int toInt32() const;
inline unsigned int toUInt32() const;
+ qint64 toLength() const;
+ inline qint64 toIndex() const;
bool toBoolean() const {
if (integerCompatible())
@@ -397,6 +407,7 @@ public:
}
static bool toBooleanImpl(Value val);
double toInteger() const;
+ inline ReturnedValue convertedToNumber() const;
inline double toNumber() const;
static double toNumberImpl(Value v);
double toNumberImpl() const { return toNumberImpl(*this); }
@@ -407,6 +418,8 @@ public:
return reinterpret_cast<Heap::String *>(m());
return toString(e, *this);
}
+ QV4::PropertyKey toPropertyKey(ExecutionEngine *e) const;
+
static Heap::String *toString(ExecutionEngine *e, Value val);
Heap::Object *toObject(ExecutionEngine *e) const {
if (isObject())
@@ -428,11 +441,11 @@ public:
if (!isManaged())
return nullptr;
- Q_ASSERT(m()->vtable());
+ Q_ASSERT(m()->internalClass->vtable);
#if !defined(QT_NO_QOBJECT_CHECK)
static_cast<const T *>(this)->qt_check_for_QMANAGED_macro(static_cast<const T *>(this));
#endif
- const VTable *vt = m()->vtable();
+ const VTable *vt = m()->internalClass->vtable;
while (vt) {
if (vt == T::staticVTable())
return static_cast<const T *>(this);
@@ -455,21 +468,32 @@ public:
return static_cast<const T *>(managed());
}
- inline uint asArrayIndex() const;
- inline bool asArrayIndex(uint &idx) const;
#ifndef V4_BOOTSTRAP
uint asArrayLength(bool *ok) const;
#endif
ReturnedValue *data_ptr() { return &_val; }
- ReturnedValue asReturnedValue() const { return _val; }
+ constexpr ReturnedValue asReturnedValue() const { return _val; }
static Value fromReturnedValue(ReturnedValue val) { Value v; v._val = val; return v; }
- // Section 9.12
+ // As per ES specs
bool sameValue(Value other) const;
+ bool sameValueZero(Value other) const;
inline void mark(MarkStack *markStack);
+ inline static constexpr Value emptyValue() { return { tagValue(quint32(ValueTypeInternal::Empty), 0) }; }
+ static inline constexpr Value fromBoolean(bool b) { return { tagValue(quint32(ValueTypeInternal::Boolean), b) }; }
+ static inline constexpr Value fromInt32(int i) { return { tagValue(quint32(ValueTypeInternal::Integer), quint32(i)) }; }
+ inline static constexpr Value undefinedValue() { return { 0 }; }
+ static inline constexpr Value nullValue() { return { tagValue(quint32(ValueTypeInternal::Null), 0) }; }
+ static inline Value fromDouble(double d);
+ static inline Value fromUInt32(uint i);
+
+ static double toInteger(double d);
+ static int toInt32(double d);
+ static unsigned int toUInt32(double d);
+
Value &operator =(const ScopedValue &v);
Value &operator=(ReturnedValue v) { _val = v; return *this; }
Value &operator=(Managed *m) {
@@ -500,18 +524,32 @@ inline void Value::mark(MarkStack *markStack)
inline bool Value::isString() const
{
Heap::Base *b = heapObject();
- return b && b->vtable()->isString;
+ return b && b->internalClass->vtable->isString;
}
+
+bool Value::isStringOrSymbol() const
+{
+ Heap::Base *b = heapObject();
+ return b && b->internalClass->vtable->isStringOrSymbol;
+}
+
+bool Value::isSymbol() const
+{
+ Heap::Base *b = heapObject();
+ return b && b->internalClass->vtable->isStringOrSymbol && !b->internalClass->vtable->isString;
+}
+
inline bool Value::isObject() const
+
{
Heap::Base *b = heapObject();
- return b && b->vtable()->isObject;
+ return b && b->internalClass->vtable->isObject;
}
inline bool Value::isFunctionObject() const
{
Heap::Base *b = heapObject();
- return b && b->vtable()->isFunctionObject;
+ return b && b->internalClass->vtable->isFunctionObject;
}
inline bool Value::isPrimitive() const
@@ -528,42 +566,14 @@ inline double Value::toNumber() const
return toNumberImpl();
}
-
-#ifndef V4_BOOTSTRAP
-inline uint Value::asArrayIndex() const
+inline ReturnedValue Value::convertedToNumber() const
{
-#if QT_POINTER_SIZE == 8
- if (!isNumber())
- return UINT_MAX;
- if (isInteger())
- return int_32() >= 0 ? (uint)int_32() : UINT_MAX;
-#else
- if (isInteger() && int_32() >= 0)
- return (uint)int_32();
- if (!isDouble())
- return UINT_MAX;
-#endif
- double d = doubleValue();
- uint idx = (uint)d;
- if (idx != d)
- return UINT_MAX;
- return idx;
-}
-
-inline bool Value::asArrayIndex(uint &idx) const
-{
- if (Q_LIKELY(!isDouble())) {
- if (Q_LIKELY(isInteger() && int_32() >= 0)) {
- idx = (uint)int_32();
- return true;
- }
- return false;
- }
- double d = doubleValue();
- idx = (uint)d;
- return (idx == d && idx != UINT_MAX);
+ if (isInteger() || isDouble())
+ return asReturnedValue();
+ Value v;
+ v.setDouble(toNumberImpl());
+ return v.asReturnedValue();
}
-#endif
inline
ReturnedValue Heap::Base::asReturnedValue() const
@@ -571,79 +581,16 @@ ReturnedValue Heap::Base::asReturnedValue() const
return Value::fromHeapObject(const_cast<Heap::Base *>(this)).asReturnedValue();
}
-
-
-struct Q_QML_PRIVATE_EXPORT Primitive : public Value
-{
- inline static Primitive emptyValue();
- inline static Primitive emptyValue(uint v);
- static inline Primitive fromBoolean(bool b);
- static inline Primitive fromInt32(int i);
- inline static Primitive undefinedValue();
- static inline Primitive nullValue();
- static inline Primitive fromDouble(double d);
- static inline Primitive fromUInt32(uint i);
-
- using Value::toInt32;
- using Value::toUInt32;
-
- static double toInteger(double d);
- static int toInt32(double d);
- static unsigned int toUInt32(double d);
-};
-
-inline Primitive Primitive::undefinedValue()
-{
- Primitive v;
- v.setM(nullptr);
- return v;
-}
-
-inline Primitive Primitive::emptyValue()
-{
- Primitive v;
- v.setEmpty(0);
- return v;
-}
-
-inline Primitive Primitive::emptyValue(uint e)
-{
- Primitive v;
- v.setEmpty(e);
- return v;
-}
-
-inline Primitive Primitive::nullValue()
-{
- Primitive v;
- v.setTagValue(quint32(ValueTypeInternal::Null), 0);
- return v;
-}
-
-inline Primitive Primitive::fromBoolean(bool b)
+inline Value Value::fromDouble(double d)
{
- Primitive v;
- v.setTagValue(quint32(ValueTypeInternal::Boolean), b);
- return v;
-}
-
-inline Primitive Primitive::fromDouble(double d)
-{
- Primitive v;
+ Value v;
v.setDouble(d);
return v;
}
-inline Primitive Primitive::fromInt32(int i)
-{
- Primitive v;
- v.setInt_32(i);
- return v;
-}
-
-inline Primitive Primitive::fromUInt32(uint i)
+inline Value Value::fromUInt32(uint i)
{
- Primitive v;
+ Value v;
if (i < INT_MAX) {
v.setTagValue(quint32(ValueTypeInternal::Integer), i);
} else {
@@ -699,7 +646,7 @@ struct Double {
}
};
-inline double Primitive::toInteger(double d)
+inline double Value::toInteger(double d)
{
if (std::isnan(d))
return +0;
@@ -708,41 +655,48 @@ inline double Primitive::toInteger(double d)
return d >= 0 ? std::floor(d) : std::ceil(d);
}
-inline int Primitive::toInt32(double value)
+inline int Value::toInt32(double value)
{
return Double::toInt32(value);
}
-inline unsigned int Primitive::toUInt32(double d)
+inline unsigned int Value::toUInt32(double d)
{
return static_cast<uint>(toInt32(d));
}
+// For source compat with older code in other modules
+using Primitive = Value;
+
struct Encode {
- static ReturnedValue undefined() {
- return Primitive::undefinedValue().rawValue();
+ static constexpr ReturnedValue undefined() {
+ return Value::undefinedValue().asReturnedValue();
}
- static ReturnedValue null() {
- return Primitive::nullValue().rawValue();
+ static constexpr ReturnedValue null() {
+ return Value::nullValue().asReturnedValue();
}
- explicit Encode(bool b) {
- val = Primitive::fromBoolean(b).rawValue();
+ explicit constexpr Encode(bool b)
+ : val(Value::fromBoolean(b).asReturnedValue())
+ {
}
explicit Encode(double d) {
- val = Primitive::fromDouble(d).rawValue();
+ val = Value::fromDouble(d).asReturnedValue();
}
- explicit Encode(int i) {
- val = Primitive::fromInt32(i).rawValue();
+ explicit constexpr Encode(int i)
+ : val(Value::fromInt32(i).asReturnedValue())
+ {
}
explicit Encode(uint i) {
- val = Primitive::fromUInt32(i).rawValue();
+ val = Value::fromUInt32(i).asReturnedValue();
}
- explicit Encode(ReturnedValue v) {
- val = v;
+ explicit constexpr Encode(ReturnedValue v)
+ : val(v)
+ {
}
- Encode(Value v) {
- val = v.rawValue();
+ constexpr Encode(Value v)
+ : val(v.asReturnedValue())
+ {
}
explicit Encode(Heap::Base *o) {
@@ -755,13 +709,13 @@ struct Encode {
}
static ReturnedValue smallestNumber(double d) {
- if (static_cast<int>(d) == d && !(d == 0. && std::signbit(d)))
+ if (Value::isInt32(d))
return Encode(static_cast<int>(d));
else
return Encode(d);
}
- operator ReturnedValue() const {
+ constexpr operator ReturnedValue() const {
return val;
}
quint64 val;
@@ -788,12 +742,37 @@ inline unsigned int Value::toUInt32() const
return static_cast<unsigned int>(toInt32());
}
+inline qint64 Value::toLength() const
+{
+ if (Q_LIKELY(integerCompatible()))
+ return int_32() < 0 ? 0 : int_32();
+ double i = Value::toInteger(isDouble() ? doubleValue() : toNumberImpl());
+ if (i <= 0)
+ return 0;
+ if (i > (static_cast<qint64>(1) << 53) - 1)
+ return (static_cast<qint64>(1) << 53) - 1;
+ return static_cast<qint64>(i);
+}
+
+inline qint64 Value::toIndex() const
+{
+ qint64 idx;
+ if (Q_LIKELY(integerCompatible())) {
+ idx = int_32();
+ } else {
+ idx = static_cast<qint64>(Value::toInteger(isDouble() ? doubleValue() : toNumberImpl()));
+ }
+ if (idx > (static_cast<qint64>(1) << 53) - 1)
+ idx = -1;
+ return idx;
+}
+
inline double Value::toInteger() const
{
if (integerCompatible())
return int_32();
- return Primitive::toInteger(isDouble() ? doubleValue() : toNumberImpl());
+ return Value::toInteger(isDouble() ? doubleValue() : toNumberImpl());
}
@@ -831,7 +810,7 @@ struct ValueArray {
WriteBarrier::write(e, base(), values[index].data_ptr(), v.asReturnedValue());
}
void set(EngineBase *e, uint index, Heap::Base *b) {
- WriteBarrier::write(e, base(), values[index].data_ptr(), b->asReturnedValue());
+ WriteBarrier::write(e, base(), values[index].data_ptr(), Value::fromHeapObject(b).asReturnedValue());
}
inline const Value &operator[] (uint index) const {
Q_ASSERT(index < alloc);
@@ -884,7 +863,6 @@ struct ValueArray {
// have wrong offsets between host and target.
Q_STATIC_ASSERT(offsetof(ValueArray<0>, values) == 8);
-
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp
index bee17e0390..ef0877dbd0 100644
--- a/src/qml/jsruntime/qv4variantobject.cpp
+++ b/src/qml/jsruntime/qv4variantobject.cpp
@@ -70,7 +70,7 @@ bool VariantObject::Data::isScarce() const
return t == QVariant::Pixmap || t == QVariant::Image;
}
-bool VariantObject::isEqualTo(Managed *m, Managed *other)
+bool VariantObject::virtualIsEqualTo(Managed *m, Managed *other)
{
Q_ASSERT(m->as<QV4::VariantObject>());
QV4::VariantObject *lv = static_cast<QV4::VariantObject *>(m);
diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h
index 62fa7ff9a8..78e0a5373a 100644
--- a/src/qml/jsruntime/qv4variantobject_p.h
+++ b/src/qml/jsruntime/qv4variantobject_p.h
@@ -99,7 +99,8 @@ struct VariantObject : Object
void addVmePropertyReference() const;
void removeVmePropertyReference() const;
- static bool isEqualTo(Managed *m, Managed *other);
+protected:
+ static bool virtualIsEqualTo(Managed *m, Managed *other);
};
struct VariantPrototype : VariantObject
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index a1f5b01fa9..5d95c8c2ab 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -55,12 +55,13 @@
#include <private/qv4string_p.h>
#include <private/qv4profiling_p.h>
#include <private/qv4jscall_p.h>
+#include <private/qv4generatorobject_p.h>
#include <private/qqmljavascriptexpression_p.h>
#include <iostream>
#include "qv4alloca_p.h"
-#include <private/qv4jit_p.h>
+#include <private/qv4baselinejit_p.h>
#undef COUNT_INSTRUCTIONS
@@ -327,7 +328,7 @@ static struct InstrCount {
#ifdef MOTH_COMPUTED_GOTO
#define MOTH_END_INSTR(instr) \
- MOTH_DISPATCH() \
+ MOTH_DISPATCH_SINGLE() \
}
#else // !MOTH_COMPUTED_GOTO
#define MOTH_END_INSTR(instr) \
@@ -343,7 +344,7 @@ static struct InstrCount {
#endif
#define CHECK_EXCEPTION \
if (engine->hasException) \
- goto catchException
+ goto handleUnwind
static inline Heap::CallContext *getScope(QV4::Value *stack, int level)
{
@@ -361,95 +362,6 @@ static inline const QV4::Value &constant(Function *function, int index)
return function->compilationUnit->constants[index];
}
-
-static bool compareEqual(QV4::Value lhs, QV4::Value rhs)
-{
- redo:
- if (lhs.asReturnedValue() == rhs.asReturnedValue())
- return !lhs.isNaN();
-
- int lt = lhs.quickType();
- int rt = rhs.quickType();
- if (rt < lt) {
- qSwap(lhs, rhs);
- qSwap(lt, rt);
- }
-
- switch (lt) {
- case QV4::Value::QT_ManagedOrUndefined:
- if (lhs.isUndefined())
- return rhs.isNullOrUndefined();
- Q_FALLTHROUGH();
- case QV4::Value::QT_ManagedOrUndefined1:
- case QV4::Value::QT_ManagedOrUndefined2:
- case QV4::Value::QT_ManagedOrUndefined3:
- // LHS: Managed
- switch (rt) {
- case QV4::Value::QT_ManagedOrUndefined:
- if (rhs.isUndefined())
- return false;
- Q_FALLTHROUGH();
- case QV4::Value::QT_ManagedOrUndefined1:
- case QV4::Value::QT_ManagedOrUndefined2:
- case QV4::Value::QT_ManagedOrUndefined3: {
- // RHS: Managed
- Heap::Base *l = lhs.m();
- Heap::Base *r = rhs.m();
- Q_ASSERT(l);
- Q_ASSERT(r);
- if (l->vtable()->isString == r->vtable()->isString)
- return static_cast<QV4::Managed &>(lhs).isEqualTo(&static_cast<QV4::Managed &>(rhs));
- if (l->vtable()->isString) {
- rhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(rhs), PREFERREDTYPE_HINT));
- break;
- } else {
- Q_ASSERT(r->vtable()->isString);
- lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT));
- break;
- }
- return false;
- }
- case QV4::Value::QT_Empty:
- Q_UNREACHABLE();
- case QV4::Value::QT_Null:
- return false;
- case QV4::Value::QT_Bool:
- case QV4::Value::QT_Int:
- rhs = Primitive::fromDouble(rhs.int_32());
- // fall through
- default: // double
- if (lhs.m()->vtable()->isString)
- return RuntimeHelpers::toNumber(lhs) == rhs.doubleValue();
- else
- lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT));
- }
- goto redo;
- case QV4::Value::QT_Empty:
- Q_UNREACHABLE();
- case QV4::Value::QT_Null:
- return rhs.isNull();
- case QV4::Value::QT_Bool:
- case QV4::Value::QT_Int:
- switch (rt) {
- case QV4::Value::QT_ManagedOrUndefined:
- case QV4::Value::QT_ManagedOrUndefined1:
- case QV4::Value::QT_ManagedOrUndefined2:
- case QV4::Value::QT_ManagedOrUndefined3:
- case QV4::Value::QT_Empty:
- case QV4::Value::QT_Null:
- Q_UNREACHABLE();
- case QV4::Value::QT_Bool:
- case QV4::Value::QT_Int:
- return lhs.int_32() == rhs.int_32();
- default: // double
- return lhs.int_32() == rhs.doubleValue();
- }
- default: // double
- Q_ASSERT(rhs.isDouble());
- return lhs.doubleValue() == rhs.doubleValue();
- }
-}
-
static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs)
{
redo:
@@ -462,10 +374,10 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs)
case QV4::Value::QT_ManagedOrUndefined2:
case QV4::Value::QT_ManagedOrUndefined3:
// LHS: Managed
- if (lhs.m()->vtable()->isString)
+ if (lhs.m()->internalClass->vtable->isString)
return RuntimeHelpers::stringToNumber(static_cast<String &>(lhs).toQString()) == rhs;
accumulator = lhs;
- lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(accumulator), PREFERREDTYPE_HINT));
+ lhs = Value::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(accumulator), PREFERREDTYPE_HINT));
goto redo;
case QV4::Value::QT_Empty:
Q_UNREACHABLE();
@@ -479,9 +391,9 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs)
}
}
-#define STORE_IP() frame.instructionPointer = int(code - codeStart);
+#define STORE_IP() frame->instructionPointer = int(code - function->codeData);
#define STORE_ACC() accumulator = acc;
-#define ACC Primitive::fromReturnedValue(acc)
+#define ACC Value::fromReturnedValue(acc)
#define VALUE_TO_INT(i, val) \
int i; \
do { \
@@ -500,82 +412,46 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs)
} \
} while (false)
-QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
+ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine)
{
qt_v4ResolvePendingBreakpointsHook();
- ExecutionEngine *engine;
- QV4::Value *stack;
- CppStackFrame frame;
- frame.originalArguments = argv;
- frame.originalArgumentsCount = argc;
- Function *function;
-
- {
- Heap::ExecutionContext *scope;
-
- quintptr d = reinterpret_cast<quintptr>(fo);
- if (d & 0x1) {
- // we don't have a FunctionObject, but a ExecData
- ExecData *data = reinterpret_cast<ExecData *>(d - 1);
- function = data->function;
- scope = data->scope->d();
- fo = nullptr;
- } else {
- function = fo->function();
- scope = fo->scope();
- }
-
- engine = function->internalClass->engine;
-
- stack = engine->jsStackTop;
- CallData *callData = reinterpret_cast<CallData *>(stack);
- callData->function = fo ? fo->asReturnedValue() : Encode::undefined();
- callData->context = scope;
- callData->accumulator = Encode::undefined();
- callData->thisObject = thisObject ? *thisObject : Primitive::undefinedValue();
- if (argc > int(function->nFormals))
- argc = int(function->nFormals);
- callData->setArgc(argc);
-
- int jsStackFrameSize = offsetof(CallData, args)/sizeof(Value) + function->compiledFunction->nRegisters;
- engine->jsStackTop += jsStackFrameSize;
- memcpy(callData->args, argv, argc*sizeof(Value));
- for (Value *v = callData->args + argc; v < engine->jsStackTop; ++v)
- *v = Encode::undefined();
-
- frame.parent = engine->currentStackFrame;
- frame.v4Function = function;
- frame.instructionPointer = 0;
- frame.jsFrame = callData;
- engine->currentStackFrame = &frame;
- }
CHECK_STACK_LIMITS(engine);
+ Function *function = frame->v4Function;
Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling
QV4::Debugging::Debugger *debugger = engine->debugger();
- const uchar *exceptionHandler = nullptr;
-
- QV4::Value &accumulator = frame.jsFrame->accumulator;
- QV4::ReturnedValue acc = Encode::undefined();
#ifdef V4_ENABLE_JIT
- if (function->jittedCode == nullptr && debugger == nullptr) {
- if (engine->canJIT(function))
- QV4::JIT::BaselineJIT(function).generate();
- else
- ++function->interpreterCallCount;
+ if (debugger == nullptr) {
+ if (function->jittedCode == nullptr) {
+ if (engine->canJIT(function))
+ QV4::JIT::BaselineJIT(function).generate();
+ else
+ ++function->interpreterCallCount;
+ }
+ if (function->jittedCode != nullptr)
+ return function->jittedCode(frame, engine);
}
#endif // V4_ENABLE_JIT
+ // interpreter
if (debugger)
debugger->enteringFunction();
- if (function->jittedCode != nullptr && debugger == nullptr) {
- acc = function->jittedCode(&frame, engine);
- } else {
- // interpreter
- const uchar *code = function->codeData;
- const uchar *codeStart = code;
+ ReturnedValue result = interpret(frame, engine, function->codeData);
+
+ if (debugger)
+ debugger->leavingFunction(result);
+
+ return result;
+}
+
+QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, const char *code)
+{
+ QV4::Function *function = frame->v4Function;
+ QV4::Value &accumulator = frame->jsFrame->accumulator;
+ QV4::ReturnedValue acc = accumulator.asReturnedValue();
+ Value *stack = reinterpret_cast<Value *>(frame->jsFrame);
MOTH_JUMP_TABLE;
@@ -627,14 +503,20 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
STACK_VALUE(destReg) = STACK_VALUE(srcReg);
MOTH_END_INSTR(MoveReg)
+ MOTH_BEGIN_INSTR(LoadImport)
+ acc = function->compilationUnit->imports[index]->asReturnedValue();
+ MOTH_END_INSTR(LoadImport)
+
MOTH_BEGIN_INSTR(LoadLocal)
auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m());
+ Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext);
acc = cc->locals[index].asReturnedValue();
MOTH_END_INSTR(LoadLocal)
MOTH_BEGIN_INSTR(StoreLocal)
CHECK_EXCEPTION;
auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m());
+ Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext);
QV4::WriteBarrier::write(engine, cc, cc->locals.values[index].data_ptr(), acc);
MOTH_END_INSTR(StoreLocal)
@@ -689,58 +571,37 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
MOTH_BEGIN_INSTR(LoadElement)
STORE_IP();
- acc = Runtime::method_loadElement(engine, STACK_VALUE(base), STACK_VALUE(index));
- CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadElement)
-
- MOTH_BEGIN_INSTR(LoadElementA)
- STORE_IP();
STORE_ACC();
acc = Runtime::method_loadElement(engine, STACK_VALUE(base), accumulator);
CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadElementA)
+ MOTH_END_INSTR(LoadElement)
MOTH_BEGIN_INSTR(StoreElement)
STORE_IP();
STORE_ACC();
- if (!Runtime::method_storeElement(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator) && function->isStrict())
- engine->throwTypeError();
+ Runtime::method_storeElement(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator);
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreElement)
MOTH_BEGIN_INSTR(LoadProperty)
STORE_IP();
- acc = Runtime::method_loadProperty(engine, STACK_VALUE(base), name);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadProperty)
-
- MOTH_BEGIN_INSTR(LoadPropertyA)
- STORE_IP();
STORE_ACC();
acc = Runtime::method_loadProperty(engine, accumulator, name);
CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadPropertyA)
+ MOTH_END_INSTR(LoadProperty)
MOTH_BEGIN_INSTR(GetLookup)
STORE_IP();
- QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
- acc = l->getter(l, engine, STACK_VALUE(base));
- CHECK_EXCEPTION;
- MOTH_END_INSTR(GetLookup)
-
- MOTH_BEGIN_INSTR(GetLookupA)
- STORE_IP();
STORE_ACC();
QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
acc = l->getter(l, engine, accumulator);
CHECK_EXCEPTION;
- MOTH_END_INSTR(GetLookupA)
+ MOTH_END_INSTR(GetLookup)
MOTH_BEGIN_INSTR(StoreProperty)
STORE_IP();
STORE_ACC();
- if (!Runtime::method_storeProperty(engine, STACK_VALUE(base), name, accumulator) && function->isStrict())
- engine->throwTypeError();
+ Runtime::method_storeProperty(engine, STACK_VALUE(base), name, accumulator);
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreProperty)
@@ -753,6 +614,20 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
CHECK_EXCEPTION;
MOTH_END_INSTR(SetLookup)
+ MOTH_BEGIN_INSTR(LoadSuperProperty)
+ STORE_IP();
+ STORE_ACC();
+ acc = Runtime::method_loadSuperProperty(engine, STACK_VALUE(property));
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(LoadSuperProperty)
+
+ MOTH_BEGIN_INSTR(StoreSuperProperty)
+ STORE_IP();
+ STORE_ACC();
+ Runtime::method_storeSuperProperty(engine, STACK_VALUE(property), accumulator);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(StoreSuperProperty)
+
MOTH_BEGIN_INSTR(StoreScopeObjectProperty)
STORE_ACC();
Runtime::method_storeQmlScopeObjectProperty(engine, STACK_VALUE(base), propertyIndex, accumulator);
@@ -784,17 +659,60 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
CHECK_EXCEPTION;
MOTH_END_INSTR(LoadIdObject)
+ MOTH_BEGIN_INSTR(Yield)
+ frame->yield = code;
+ frame->yieldIsIterator = false;
+ return acc;
+ MOTH_END_INSTR(Yield)
+
+ MOTH_BEGIN_INSTR(YieldStar)
+ frame->yield = code;
+ frame->yieldIsIterator = true;
+ return acc;
+ MOTH_END_INSTR(YieldStar)
+
+ MOTH_BEGIN_INSTR(Resume)
+ // check exception, in case the generator was called with throw() or return()
+ if (engine->hasException) {
+ // an empty value indicates that the generator was called with return()
+ if (engine->exceptionValue->asReturnedValue() != Value::emptyValue().asReturnedValue())
+ goto handleUnwind;
+ engine->hasException = false;
+ *engine->exceptionValue = Value::undefinedValue();
+ } else {
+ code += offset;
+ }
+ MOTH_END_INSTR(Resume)
+
+ MOTH_BEGIN_INSTR(IteratorNextForYieldStar)
+ STORE_ACC();
+ acc = Runtime::method_iteratorNextForYieldStar(engine, accumulator, STACK_VALUE(iterator), &STACK_VALUE(object));
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(IteratorNextForYieldStar)
+
MOTH_BEGIN_INSTR(CallValue)
STORE_IP();
Value func = STACK_VALUE(name);
if (Q_UNLIKELY(!func.isFunctionObject())) {
acc = engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
- goto catchException;
+ goto handleUnwind;
}
- acc = static_cast<const FunctionObject &>(func).call(nullptr, stack + argv, argc);
+ Value undef = Value::undefinedValue();
+ acc = static_cast<const FunctionObject &>(func).call(&undef, stack + argv, argc);
CHECK_EXCEPTION;
MOTH_END_INSTR(CallValue)
+ MOTH_BEGIN_INSTR(CallWithReceiver)
+ STORE_IP();
+ Value func = STACK_VALUE(name);
+ if (Q_UNLIKELY(!func.isFunctionObject())) {
+ acc = engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
+ goto handleUnwind;
+ }
+ acc = static_cast<const FunctionObject &>(func).call(stack + thisObject, stack + argv, argc);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(CallWithReceiver)
+
MOTH_BEGIN_INSTR(CallProperty)
STORE_IP();
acc = Runtime::method_callProperty(engine, stack + base, name, stack + argv, argc);
@@ -809,7 +727,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
if (Q_UNLIKELY(!f.isFunctionObject())) {
acc = engine->throwTypeError();
- goto catchException;
+ goto handleUnwind;
}
acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc);
@@ -852,89 +770,162 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
CHECK_EXCEPTION;
MOTH_END_INSTR(CallContextObjectProperty)
- MOTH_BEGIN_INSTR(SetExceptionHandler)
- exceptionHandler = offset ? code + offset : nullptr;
- MOTH_END_INSTR(SetExceptionHandler)
+ MOTH_BEGIN_INSTR(CallWithSpread)
+ STORE_IP();
+ acc = Runtime::method_callWithSpread(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(CallWithSpread)
+
+ MOTH_BEGIN_INSTR(TailCall)
+ STORE_IP();
+ *engine->jsAlloca(1) = Primitive::fromInt32(argc);
+ *engine->jsAlloca(1) = Primitive::fromInt32(argv);
+ *engine->jsAlloca(1) = STACK_VALUE(thisObject);
+ *engine->jsAlloca(1) = STACK_VALUE(func);
+ return Runtime::method_tailCall(frame, engine);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(TailCall)
+
+ MOTH_BEGIN_INSTR(Construct)
+ STORE_IP();
+ acc = Runtime::method_construct(engine, STACK_VALUE(func), ACC, stack + argv, argc);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(Construct)
+
+ MOTH_BEGIN_INSTR(ConstructWithSpread)
+ STORE_IP();
+ acc = Runtime::method_constructWithSpread(engine, STACK_VALUE(func), ACC, stack + argv, argc);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(ConstructWithSpread)
+
+ MOTH_BEGIN_INSTR(SetUnwindHandler)
+ frame->unwindHandler = offset ? code + offset : nullptr;
+ MOTH_END_INSTR(SetUnwindHandler)
+
+ MOTH_BEGIN_INSTR(UnwindDispatch)
+ CHECK_EXCEPTION;
+ if (frame->unwindLevel) {
+ --frame->unwindLevel;
+ if (frame->unwindLevel)
+ goto handleUnwind;
+ code = frame->unwindLabel;
+ }
+ MOTH_END_INSTR(UnwindDispatch)
+
+ MOTH_BEGIN_INSTR(UnwindToLabel)
+ frame->unwindLevel = level;
+ frame->unwindLabel = code + offset;
+ goto handleUnwind;
+ MOTH_END_INSTR(UnwindToLabel)
+
+ MOTH_BEGIN_INSTR(DeadTemporalZoneCheck)
+ if (ACC.isEmpty()) {
+ STORE_IP();
+ STORE_ACC();
+ Runtime::method_throwReferenceError(engine, name);
+ goto handleUnwind;
+ }
+ MOTH_END_INSTR(DeadTemporalZoneCheck)
MOTH_BEGIN_INSTR(ThrowException)
STORE_IP();
STORE_ACC();
Runtime::method_throwException(engine, accumulator);
- goto catchException;
+ goto handleUnwind;
MOTH_END_INSTR(ThrowException)
MOTH_BEGIN_INSTR(GetException)
acc = engine->hasException ? engine->exceptionValue->asReturnedValue()
- : Primitive::emptyValue().asReturnedValue();
+ : Value::emptyValue().asReturnedValue();
engine->hasException = false;
MOTH_END_INSTR(HasException)
MOTH_BEGIN_INSTR(SetException)
- *engine->exceptionValue = acc;
- engine->hasException = true;
+ if (acc != Value::emptyValue().asReturnedValue()) {
+ *engine->exceptionValue = acc;
+ engine->hasException = true;
+ }
MOTH_END_INSTR(SetException)
MOTH_BEGIN_INSTR(PushCatchContext)
- STACK_VALUE(reg) = STACK_VALUE(CallData::Context);
ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
- STACK_VALUE(CallData::Context) = Runtime::method_createCatchContext(c, name);
+ STACK_VALUE(CallData::Context) = Runtime::method_createCatchContext(c, index, name);
MOTH_END_INSTR(PushCatchContext)
MOTH_BEGIN_INSTR(CreateCallContext)
- stack[CallData::Context] = ExecutionContext::newCallContext(&frame);
+ stack[CallData::Context] = ExecutionContext::newCallContext(frame);
MOTH_END_INSTR(CreateCallContext)
MOTH_BEGIN_INSTR(PushWithContext)
STORE_IP();
STORE_ACC();
- accumulator = accumulator.toObject(engine);
+ auto ctx = Runtime::method_createWithContext(engine, stack);
CHECK_EXCEPTION;
- STACK_VALUE(reg) = STACK_VALUE(CallData::Context);
- ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
- STACK_VALUE(CallData::Context) = Runtime::method_createWithContext(c, accumulator);
+ STACK_VALUE(CallData::Context) = ctx;
MOTH_END_INSTR(PushWithContext)
+ MOTH_BEGIN_INSTR(PushBlockContext)
+ STORE_ACC();
+ ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
+ STACK_VALUE(CallData::Context) = Runtime::method_createBlockContext(c, index);
+ MOTH_END_INSTR(PushBlockContext)
+
+ MOTH_BEGIN_INSTR(CloneBlockContext)
+ STORE_ACC();
+ ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
+ STACK_VALUE(CallData::Context) = Runtime::method_cloneBlockContext(c);
+ MOTH_END_INSTR(CloneBlockContext)
+
+ MOTH_BEGIN_INSTR(PushScriptContext)
+ STACK_VALUE(CallData::Context) = Runtime::method_createScriptContext(engine, index);
+ MOTH_END_INSTR(PushScriptContext)
+
+ MOTH_BEGIN_INSTR(PopScriptContext)
+ STACK_VALUE(CallData::Context) = Runtime::method_popScriptContext(engine);
+ MOTH_END_INSTR(PopScriptContext)
+
MOTH_BEGIN_INSTR(PopContext)
- STACK_VALUE(CallData::Context) = STACK_VALUE(reg);
+ ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
+ STACK_VALUE(CallData::Context) = c->d()->outer;
MOTH_END_INSTR(PopContext)
- MOTH_BEGIN_INSTR(ForeachIteratorObject)
+ MOTH_BEGIN_INSTR(GetIterator)
STORE_ACC();
- acc = Runtime::method_foreachIterator(engine, accumulator);
+ acc = Runtime::method_getIterator(engine, accumulator, iterator);
CHECK_EXCEPTION;
- MOTH_END_INSTR(ForeachIteratorObject)
+ MOTH_END_INSTR(GetIterator)
- MOTH_BEGIN_INSTR(ForeachNextPropertyName)
+ MOTH_BEGIN_INSTR(IteratorNext)
STORE_ACC();
- acc = Runtime::method_foreachNextPropertyName(accumulator);
+ acc = Runtime::method_iteratorNext(engine, accumulator, &STACK_VALUE(value));
+ STACK_VALUE(done) = acc;
CHECK_EXCEPTION;
- MOTH_END_INSTR(ForeachNextPropertyName)
+ MOTH_END_INSTR(IteratorNext)
- MOTH_BEGIN_INSTR(DeleteMember)
- if (!Runtime::method_deleteMember(engine, STACK_VALUE(base), member)) {
- if (function->isStrict()) {
- STORE_IP();
- engine->throwTypeError();
- goto catchException;
- }
- acc = Encode(false);
- } else {
- acc = Encode(true);
- }
- MOTH_END_INSTR(DeleteMember)
+ MOTH_BEGIN_INSTR(IteratorClose)
+ STORE_ACC();
+ acc = Runtime::method_iteratorClose(engine, accumulator, STACK_VALUE(done));
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(IteratorClose)
+
+ MOTH_BEGIN_INSTR(DestructureRestElement)
+ STORE_ACC();
+ acc = Runtime::method_destructureRestElement(engine, ACC);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(DestructureRestElement)
- MOTH_BEGIN_INSTR(DeleteSubscript)
- if (!Runtime::method_deleteElement(engine, STACK_VALUE(base), STACK_VALUE(index))) {
+ MOTH_BEGIN_INSTR(DeleteProperty)
+ if (!Runtime::method_deleteProperty(engine, STACK_VALUE(base), STACK_VALUE(index))) {
if (function->isStrict()) {
STORE_IP();
engine->throwTypeError();
- goto catchException;
+ goto handleUnwind;
}
acc = Encode(false);
} else {
acc = Encode(true);
}
- MOTH_END_INSTR(DeleteSubscript)
+ MOTH_END_INSTR(DeleteProperty)
MOTH_BEGIN_INSTR(DeleteName)
if (!Runtime::method_deleteName(engine, name)) {
@@ -942,7 +933,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
STORE_IP();
QString n = function->compilationUnit->runtimeStrings[name]->toQString();
engine->throwSyntaxError(QStringLiteral("Can't delete property %1").arg(n));
- goto catchException;
+ goto handleUnwind;
}
acc = Encode(false);
} else {
@@ -970,9 +961,13 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
MOTH_BEGIN_INSTR(DefineObjectLiteral)
QV4::Value *arguments = stack + args;
- acc = Runtime::method_objectLiteral(engine, arguments, internalClassId, arrayValueCount, arrayGetterSetterCountAndFlags);
+ acc = Runtime::method_objectLiteral(engine, internalClassId, argc, arguments);
MOTH_END_INSTR(DefineObjectLiteral)
+ MOTH_BEGIN_INSTR(CreateClass)
+ acc = Runtime::method_createClass(engine, classIndex, STACK_VALUE(heritage), stack + computedNames);
+ MOTH_END_INSTR(CreateClass)
+
MOTH_BEGIN_INSTR(CreateMappedArgumentsObject)
acc = Runtime::method_createMappedArgumentsObject(engine);
MOTH_END_INSTR(CreateMappedArgumentsObject)
@@ -981,6 +976,10 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
acc = Runtime::method_createUnmappedArgumentsObject(engine);
MOTH_END_INSTR(CreateUnmappedArgumentsObject)
+ MOTH_BEGIN_INSTR(CreateRestParameter)
+ acc = Runtime::method_createRestParameter(engine, argIndex);
+ MOTH_END_INSTR(CreateRestParameter)
+
MOTH_BEGIN_INSTR(ConvertThisToObject)
Value *t = &stack[CallData::This];
if (!t->isObject()) {
@@ -993,11 +992,15 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
}
MOTH_END_INSTR(ConvertThisToObject)
- MOTH_BEGIN_INSTR(Construct)
- STORE_IP();
- acc = Runtime::method_construct(engine, STACK_VALUE(func), stack + argv, argc);
+ MOTH_BEGIN_INSTR(LoadSuperConstructor)
+ acc = Runtime::method_loadSuperConstructor(engine, stack[CallData::Function]);
CHECK_EXCEPTION;
- MOTH_END_INSTR(Construct)
+ MOTH_END_INSTR(LoadSuperConstructor)
+
+ MOTH_BEGIN_INSTR(ToObject)
+ acc = ACC.toObject(engine)->asReturnedValue();
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(ToObject)
MOTH_BEGIN_INSTR(Jump)
code += offset;
@@ -1023,6 +1026,16 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
}
MOTH_END_INSTR(JumpFalse)
+ MOTH_BEGIN_INSTR(JumpNoException)
+ if (!engine->hasException)
+ code += offset;
+ MOTH_END_INSTR(JumpNoException)
+
+ MOTH_BEGIN_INSTR(JumpNotUndefined)
+ if (Q_LIKELY(acc != QV4::Encode::undefined()))
+ code += offset;
+ MOTH_END_INSTR(JumpNotUndefined)
+
MOTH_BEGIN_INSTR(CmpEqNull)
acc = Encode(ACC.isNullOrUndefined());
MOTH_END_INSTR(CmpEqNull)
@@ -1059,7 +1072,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
acc = Encode(left.int_32() == ACC.int_32());
} else {
STORE_ACC();
- acc = Encode(compareEqual(left, accumulator));
+ acc = Encode(bool(Runtime::method_compareEqual(left, accumulator)));
CHECK_EXCEPTION;
}
MOTH_END_INSTR(CmpEq)
@@ -1070,7 +1083,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
acc = Encode(bool(left.int_32() != ACC.int_32()));
} else {
STORE_ACC();
- acc = Encode(!compareEqual(left, accumulator));
+ acc = Encode(bool(!Runtime::method_compareEqual(left, accumulator)));
CHECK_EXCEPTION;
}
MOTH_END_INSTR(CmpNe)
@@ -1154,27 +1167,11 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
MOTH_END_INSTR(CmpIn)
MOTH_BEGIN_INSTR(CmpInstanceOf)
- // 11.8.6, 5: rval must be an Object
- if (Q_UNLIKELY(!Primitive::fromReturnedValue(acc).isObject())) {
- acc = engine->throwTypeError();
- goto catchException;
- }
-
- // 11.8.6, 7: call "HasInstance", which we term instanceOf, and return the result.
- acc = Primitive::fromReturnedValue(acc).objectValue()->instanceOf(STACK_VALUE(lhs));
+ STORE_ACC();
+ acc = Runtime::method_instanceof(engine, STACK_VALUE(lhs), ACC);
CHECK_EXCEPTION;
MOTH_END_INSTR(CmpInstanceOf)
- MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt)
- if (STACK_VALUE(lhs).int_32() != rhs || STACK_VALUE(lhs).isUndefined())
- code += offset;
- MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
- MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt)
- if (STACK_VALUE(lhs).int_32() == rhs && !STACK_VALUE(lhs).isUndefined())
- code += offset;
- MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
MOTH_BEGIN_INSTR(UNot)
if (ACC.integerCompatible()) {
acc = Encode(!static_cast<bool>(ACC.int_32()));
@@ -1259,6 +1256,16 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
}
MOTH_END_INSTR(Sub)
+ MOTH_BEGIN_INSTR(Exp)
+ const Value left = STACK_VALUE(lhs);
+ double base = left.toNumber();
+ double exp = ACC.toNumber();
+ if (qIsInf(exp) && (base == 1 || base == -1))
+ acc = Encode(qSNaN());
+ else
+ acc = Encode(pow(base,exp));
+ MOTH_END_INSTR(Exp)
+
MOTH_BEGIN_INSTR(Mul)
const Value left = STACK_VALUE(lhs);
if (Q_LIKELY(Value::integerCompatible(left, ACC))) {
@@ -1351,9 +1358,22 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
MOTH_END_INSTR(ShlConst)
MOTH_BEGIN_INSTR(Ret)
- goto functionExit;
+ return acc;
MOTH_END_INSTR(Ret)
+ MOTH_BEGIN_INSTR(InitializeBlockDeadTemporalZone)
+ acc = Encode(Value::emptyValue());
+ for (int i = firstReg, end = firstReg + count; i < end; ++i)
+ STACK_VALUE(i) = acc;
+ MOTH_END_INSTR(InitializeBlockDeadTemporalZone)
+
+ MOTH_BEGIN_INSTR(ThrowOnNullOrUndefined)
+ if (Value::fromReturnedValue(acc).isNullOrUndefined()) {
+ engine->throwTypeError();
+ goto handleUnwind;
+ }
+ MOTH_END_INSTR(ThrowOnNullOrUndefined)
+
MOTH_BEGIN_INSTR(Debug)
#if QT_CONFIG(qml_debug)
STORE_IP();
@@ -1369,21 +1389,12 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
STACK_VALUE(result) = Runtime::method_loadQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine));
MOTH_END_INSTR(LoadQmlImportedScripts)
- catchException:
- Q_ASSERT(engine->hasException);
- if (!exceptionHandler) {
+ handleUnwind:
+ Q_ASSERT(engine->hasException || frame->unwindLevel);
+ if (!frame->unwindHandler) {
acc = Encode::undefined();
- goto functionExit;
+ return acc;
}
- code = exceptionHandler;
- }
+ code = frame->unwindHandler;
}
-
-functionExit:
- if (QV4::Debugging::Debugger *debugger = engine->debugger())
- debugger->leavingFunction(ACC.asReturnedValue());
- engine->currentStackFrame = frame.parent;
- engine->jsStackTop = stack;
-
- return acc;
}
diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h
index 3b7723ca7e..8a76e60f20 100644
--- a/src/qml/jsruntime/qv4vme_moth_p.h
+++ b/src/qml/jsruntime/qv4vme_moth_p.h
@@ -65,13 +65,8 @@ public:
QV4::Function *function;
const QV4::ExecutionContext *scope;
};
- static inline
- QV4::ReturnedValue exec(Function *v4Function, const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context) {
- ExecData data{v4Function, context};
- quintptr d = reinterpret_cast<quintptr>(&data) | 0x1;
- return exec(reinterpret_cast<const FunctionObject *>(d), thisObject, argv, argc);
- }
- static QV4::ReturnedValue exec(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc);
+ static QV4::ReturnedValue exec(CppStackFrame *frame, ExecutionEngine *engine);
+ static QV4::ReturnedValue interpret(CppStackFrame *frame, ExecutionEngine *engine, const char *codeEntry);
};
} // namespace Moth
diff --git a/src/qml/jsruntime/qv4vtable_p.h b/src/qml/jsruntime/qv4vtable_p.h
new file mode 100644
index 0000000000..4acefecdf5
--- /dev/null
+++ b/src/qml/jsruntime/qv4vtable_p.h
@@ -0,0 +1,226 @@
+/****************************************************************************
+**
+** 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 QV4VTABLE_P_H
+#define QV4VTABLE_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"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct OwnPropertyKeyIterator {
+ virtual ~OwnPropertyKeyIterator() = 0;
+ virtual PropertyKey next(const Object *o, Property *p = nullptr, PropertyAttributes *attrs = nullptr) = 0;
+};
+
+struct VTable
+{
+ typedef void (*Destroy)(Heap::Base *);
+ typedef void (*MarkObjects)(Heap::Base *, MarkStack *markStack);
+ typedef bool (*IsEqualTo)(Managed *m, Managed *other);
+
+ typedef ReturnedValue (*Get)(const Managed *, PropertyKey id, const Value *receiver, bool *hasProperty);
+ typedef bool (*Put)(Managed *, PropertyKey id, const Value &value, Value *receiver);
+ typedef bool (*DeleteProperty)(Managed *m, PropertyKey id);
+ typedef bool (*HasProperty)(const Managed *m, PropertyKey id);
+ typedef PropertyAttributes (*GetOwnProperty)(const Managed *m, PropertyKey id, Property *p);
+ typedef bool (*DefineOwnProperty)(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs);
+ typedef bool (*IsExtensible)(const Managed *);
+ typedef bool (*PreventExtensions)(Managed *);
+ typedef Heap::Object *(*GetPrototypeOf)(const Managed *);
+ typedef bool (*SetPrototypeOf)(Managed *, const Object *);
+ typedef qint64 (*GetLength)(const Managed *m);
+ typedef OwnPropertyKeyIterator *(*OwnPropertyKeys)(const Object *m, Value *target);
+ typedef ReturnedValue (*InstanceOf)(const Object *typeObject, const Value &var);
+
+ 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);
+
+ const VTable * const parent;
+ quint32 inlinePropertyOffset : 16;
+ quint32 nInlineProperties : 16;
+ quint8 isExecutionContext;
+ quint8 isString;
+ quint8 isObject;
+ quint8 isFunctionObject;
+ quint8 isErrorObject;
+ quint8 isArrayData;
+ quint8 isStringOrSymbol;
+ quint8 type;
+ quint8 unused[4];
+ const char *className;
+
+ Destroy destroy;
+ MarkObjects markObjects;
+ IsEqualTo isEqualTo;
+
+ Get get;
+ Put put;
+ DeleteProperty deleteProperty;
+ HasProperty hasProperty;
+ GetOwnProperty getOwnProperty;
+ DefineOwnProperty defineOwnProperty;
+ IsExtensible isExtensible;
+ PreventExtensions preventExtensions;
+ GetPrototypeOf getPrototypeOf;
+ SetPrototypeOf setPrototypeOf;
+ GetLength getLength;
+ OwnPropertyKeys ownPropertyKeys;
+ InstanceOf instanceOf;
+
+ Call call;
+ CallAsConstructor callAsConstructor;
+};
+
+
+struct VTableBase {
+protected:
+ static constexpr VTable::Destroy virtualDestroy = nullptr;
+ static constexpr VTable::IsEqualTo virtualIsEqualTo = nullptr;
+
+ static constexpr VTable::Get virtualGet = nullptr;
+ static constexpr VTable::Put virtualPut = nullptr;
+ static constexpr VTable::DeleteProperty virtualDeleteProperty = nullptr;
+ static constexpr VTable::HasProperty virtualHasProperty = nullptr;
+ static constexpr VTable::GetOwnProperty virtualGetOwnProperty = nullptr;
+ static constexpr VTable::DefineOwnProperty virtualDefineOwnProperty = nullptr;
+ static constexpr VTable::IsExtensible virtualIsExtensible = nullptr;
+ static constexpr VTable::PreventExtensions virtualPreventExtensions = nullptr;
+ static constexpr VTable::GetPrototypeOf virtualGetPrototypeOf = nullptr;
+ static constexpr VTable::SetPrototypeOf virtualSetPrototypeOf = nullptr;
+ static constexpr VTable::GetLength virtualGetLength = nullptr;
+ static constexpr VTable::OwnPropertyKeys virtualOwnPropertyKeys = nullptr;
+ static constexpr VTable::InstanceOf virtualInstanceOf = nullptr;
+
+ static constexpr VTable::Call virtualCall = nullptr;
+ static constexpr VTable::CallAsConstructor virtualCallAsConstructor = nullptr;
+};
+
+#define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \
+{ \
+ parentVTable, \
+ (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \
+ (sizeof(classname::Data) + (classname::NInlineProperties*sizeof(QV4::Value)) + QV4::Chunk::SlotSize - 1)/QV4::Chunk::SlotSize*QV4::Chunk::SlotSize/sizeof(QV4::Value) \
+ - (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \
+ classname::IsExecutionContext, \
+ classname::IsString, \
+ classname::IsObject, \
+ classname::IsFunctionObject, \
+ classname::IsErrorObject, \
+ classname::IsArrayData, \
+ classname::IsStringOrSymbol, \
+ classname::MyType, \
+ { 0, 0, 0, 0 }, \
+ #classname, \
+ \
+ classname::virtualDestroy, \
+ classname::Data::markObjects, \
+ classname::virtualIsEqualTo, \
+ \
+ classname::virtualGet, \
+ classname::virtualPut, \
+ classname::virtualDeleteProperty, \
+ classname::virtualHasProperty, \
+ classname::virtualGetOwnProperty, \
+ classname::virtualDefineOwnProperty, \
+ classname::virtualIsExtensible, \
+ classname::virtualPreventExtensions, \
+ classname::virtualGetPrototypeOf, \
+ classname::virtualSetPrototypeOf, \
+ classname::virtualGetLength, \
+ classname::virtualOwnPropertyKeys, \
+ classname::virtualInstanceOf, \
+ \
+ classname::virtualCall, \
+ classname::virtualCallAsConstructor, \
+}
+
+#define DEFINE_MANAGED_VTABLE(classname) \
+const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, 0)
+
+#define V4_OBJECT2(DataClass, superClass) \
+ private: \
+ DataClass() Q_DECL_EQ_DELETE; \
+ Q_DISABLE_COPY(DataClass) \
+ public: \
+ Q_MANAGED_CHECK \
+ typedef QV4::Heap::DataClass Data; \
+ typedef superClass SuperClass; \
+ static const QV4::VTable static_vtbl; \
+ static inline const QV4::VTable *staticVTable() { return &static_vtbl; } \
+ V4_MANAGED_SIZE_TEST \
+ QV4::Heap::DataClass *d_unchecked() const { return static_cast<QV4::Heap::DataClass *>(m()); } \
+ QV4::Heap::DataClass *d() const { \
+ QV4::Heap::DataClass *dptr = d_unchecked(); \
+ dptr->_checkIsInitialized(); \
+ return dptr; \
+ } \
+ Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value);
+
+#define V4_PROTOTYPE(p) \
+ static QV4::Object *defaultPrototype(QV4::ExecutionEngine *e) \
+ { return e->p(); }
+
+
+#define DEFINE_OBJECT_VTABLE_BASE(classname) \
+ const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, (std::is_same<classname::SuperClass, Object>::value) ? nullptr : &classname::SuperClass::static_vtbl)
+
+#define DEFINE_OBJECT_VTABLE(classname) \
+DEFINE_OBJECT_VTABLE_BASE(classname)
+
+#define DEFINE_OBJECT_TEMPLATE_VTABLE(classname) \
+template<> DEFINE_OBJECT_VTABLE_BASE(classname)
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h
index 53933cd090..07af5a6f7a 100644
--- a/src/qml/memory/qv4heap_p.h
+++ b/src/qml/memory/qv4heap_p.h
@@ -54,7 +54,7 @@
#include <private/qv4global_p.h>
#include <private/qv4mmdefs_p.h>
#include <private/qv4writebarrier_p.h>
-#include <private/qv4internalclass_p.h>
+#include <private/qv4vtable_p.h>
#include <QSharedPointer>
// To check if Heap::Base::init is called (meaning, all subclasses did their init and called their
@@ -65,40 +65,43 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
-struct InternalClass;
+namespace Heap {
-struct VTable
-{
- const VTable * const parent;
- uint inlinePropertyOffset : 16;
- uint nInlineProperties : 16;
- uint isExecutionContext : 1;
- uint isString : 1;
- uint isObject : 1;
- uint isFunctionObject : 1;
- uint isErrorObject : 1;
- uint isArrayData : 1;
- uint unused : 18;
- uint type : 8;
- const char *className;
- void (*destroy)(Heap::Base *);
- void (*markObjects)(Heap::Base *, MarkStack *markStack);
- bool (*isEqualTo)(Managed *m, Managed *other);
-};
+template <typename T, size_t o>
+struct Pointer {
+ static Q_CONSTEXPR size_t offset = o;
+ T operator->() const { return get(); }
+ operator T () const { return get(); }
-namespace Heap {
+ Base *base();
+
+ void set(EngineBase *e, T newVal) {
+ WriteBarrier::write(e, base(), &ptr, reinterpret_cast<Base *>(newVal));
+ }
+
+ T get() const { return reinterpret_cast<T>(ptr); }
+
+ template <typename Type>
+ Type *cast() { return static_cast<Type *>(ptr); }
+
+ Base *heapObject() const { return ptr; }
+
+private:
+ Base *ptr;
+};
+typedef Pointer<char *, 0> V4PointerCheck;
+Q_STATIC_ASSERT(std::is_trivial< V4PointerCheck >::value);
struct Q_QML_EXPORT Base {
void *operator new(size_t) = delete;
- static void markObjects(Heap::Base *, MarkStack *) {}
+ static void markObjects(Base *, MarkStack *);
- InternalClass *internalClass;
+ Pointer<InternalClass *, 0> internalClass;
inline ReturnedValue asReturnedValue() const;
inline void mark(QV4::MarkStack *markStack);
- const VTable *vtable() const { return internalClass->vtable; }
inline bool isMarked() const {
const HeapItem *h = reinterpret_cast<const HeapItem *>(this);
Chunk *c = h->chunk();
@@ -125,13 +128,9 @@ struct Q_QML_EXPORT Base {
return Chunk::testBit(c->objectBitmap, h - c->realBase());
}
- inline void markChildren(MarkStack *markStack) {
- vtable()->markObjects(this, markStack);
- }
-
void *operator new(size_t, Managed *m) { return m; }
- void *operator new(size_t, Heap::Base *m) { return m; }
- void operator delete(void *, Heap::Base *) {}
+ void *operator new(size_t, Base *m) { return m; }
+ void operator delete(void *, Base *) {}
void init() { _setInitialized(); }
void destroy() { _setDestroyed(); }
@@ -178,10 +177,8 @@ Q_STATIC_ASSERT(std::is_standard_layout<Base>::value);
Q_STATIC_ASSERT(offsetof(Base, internalClass) == 0);
Q_STATIC_ASSERT(sizeof(Base) == QT_POINTER_SIZE);
-}
-
inline
-void Heap::Base::mark(QV4::MarkStack *markStack)
+void Base::mark(QV4::MarkStack *markStack)
{
Q_ASSERT(inUse());
const HeapItem *h = reinterpret_cast<const HeapItem *>(this);
@@ -196,36 +193,12 @@ void Heap::Base::mark(QV4::MarkStack *markStack)
}
}
-namespace Heap {
-
-template <typename T, size_t o>
-struct Pointer {
- static Q_CONSTEXPR size_t offset = o;
- T operator->() const { return get(); }
- operator T () const { return get(); }
-
- Heap::Base *base() {
- Heap::Base *base = reinterpret_cast<Heap::Base *>(this) - (offset/sizeof(Heap::Base));
- Q_ASSERT(base->inUse());
- return base;
- }
-
- void set(EngineBase *e, T newVal) {
- WriteBarrier::write(e, base(), &ptr, reinterpret_cast<Heap::Base *>(newVal));
- }
-
- T get() const { return reinterpret_cast<T>(ptr); }
-
- template <typename Type>
- Type *cast() { return static_cast<Type *>(ptr); }
-
- Heap::Base *heapObject() const { return ptr; }
-
-private:
- Heap::Base *ptr;
-};
-typedef Pointer<char *, 0> V4PointerCheck;
-Q_STATIC_ASSERT(std::is_trivial< V4PointerCheck >::value);
+template<typename T, size_t o>
+Base *Pointer<T, o>::base() {
+ Base *base = reinterpret_cast<Base *>(this) - (offset/sizeof(Base *));
+ Q_ASSERT(base->inUse());
+ return base;
+}
}
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp
index 4e83abebdf..fb6d9478db 100644
--- a/src/qml/memory/qv4mm.cpp
+++ b/src/qml/memory/qv4mm.cpp
@@ -42,6 +42,7 @@
#include "qv4objectproto_p.h"
#include "qv4mm_p.h"
#include "qv4qobjectwrapper_p.h"
+#include "qv4identifiertable_p.h"
#include <QtCore/qalgorithms.h>
#include <QtCore/private/qnumeric_p.h>
#include <QtCore/qloggingcategory.h>
@@ -60,6 +61,8 @@
#include <algorithm>
#include "qv4alloca_p.h"
#include "qv4profiling_p.h"
+#include "qv4mapobject_p.h"
+#include "qv4setobject_p.h"
//#define MM_STATS
@@ -335,7 +338,7 @@ bool Chunk::sweep(ExecutionEngine *engine)
HeapItem *itemToFree = o + index;
Heap::Base *b = *itemToFree;
- const VTable *v = b->vtable();
+ const VTable *v = b->internalClass->vtable;
// if (Q_UNLIKELY(classCountPtr))
// classCountPtr(v->className);
if (v->destroy) {
@@ -389,8 +392,8 @@ void Chunk::freeAll(ExecutionEngine *engine)
HeapItem *itemToFree = o + index;
Heap::Base *b = *itemToFree;
- if (b->vtable()->destroy) {
- b->vtable()->destroy(b);
+ if (b->internalClass->vtable->destroy) {
+ b->internalClass->vtable->destroy(b);
b->_checkIsDestroyed();
}
#ifdef V4_USE_HEAPTRACK
@@ -412,10 +415,6 @@ void Chunk::resetBlackBits()
memset(blackBitmap, 0, sizeof(blackBitmap));
}
-#ifdef MM_STATS
-static uint nGrayItems = 0;
-#endif
-
void Chunk::collectGrayItems(MarkStack *markStack)
{
// DEBUG << "sweeping chunk" << this << (*freeList);
@@ -438,10 +437,6 @@ void Chunk::collectGrayItems(MarkStack *markStack)
Heap::Base *b = *itemToFree;
Q_ASSERT(b->inUse());
markStack->push(b);
-#ifdef MM_STATS
- ++nGrayItems;
-// qDebug() << "adding gray item" << b << "to mark stack";
-#endif
}
grayBitmap[i] = 0;
o += Chunk::Bits;
@@ -459,7 +454,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins)
#else
const int start = 1;
#endif
-#ifdef MM_STATS
+#ifndef QT_NO_DEBUG
uint freeSlots = 0;
uint allocatedSlots = 0;
#endif
@@ -469,7 +464,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins)
if (!i)
usedSlots |= (static_cast<quintptr>(1) << (HeaderSize/SlotSize)) - 1;
#endif
-#ifdef MM_STATS
+#ifndef QT_NO_DEBUG
allocatedSlots += qPopulationCount(usedSlots);
// qDebug() << hex << " i=" << i << "used=" << usedSlots;
#endif
@@ -486,7 +481,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins)
break;
}
usedSlots = (objectBitmap[i]|extendsBitmap[i]);
-#ifdef MM_STATS
+#ifndef QT_NO_DEBUG
allocatedSlots += qPopulationCount(usedSlots);
// qDebug() << hex << " i=" << i << "used=" << usedSlots;
#endif
@@ -497,7 +492,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins)
usedSlots |= (quintptr(1) << index) - 1;
uint freeEnd = i*Bits + index;
uint nSlots = freeEnd - freeStart;
-#ifdef MM_STATS
+#ifndef QT_NO_DEBUG
// qDebug() << hex << " got free slots from" << freeStart << "to" << freeEnd << "n=" << nSlots << "usedSlots=" << usedSlots;
freeSlots += nSlots;
#endif
@@ -508,7 +503,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins)
bins[bin] = freeItem;
}
}
-#ifdef MM_STATS
+#ifndef QT_NO_DEBUG
Q_ASSERT(freeSlots + allocatedSlots == (EntriesInBitmap - start) * 8 * sizeof(quintptr));
#endif
}
@@ -624,27 +619,30 @@ void BlockAllocator::sweep()
// qDebug() << "BlockAlloc: sweep";
usedSlotsAfterLastSweep = 0;
- auto isFree = [this] (Chunk *c) {
- bool isUsed = c->sweep(engine);
+ auto firstEmptyChunk = std::partition(chunks.begin(), chunks.end(), [this](Chunk *c) {
+ return c->sweep(engine);
+ });
- if (isUsed) {
- c->sortIntoBins(freeBins, NumBins);
- usedSlotsAfterLastSweep += c->nUsedSlots();
- } else {
- Q_V4_PROFILE_DEALLOC(engine, Chunk::DataSize, Profiling::HeapPage);
- chunkAllocator->free(c);
- }
- return !isUsed;
- };
+ std::for_each(chunks.begin(), firstEmptyChunk, [this](Chunk *c) {
+ c->sortIntoBins(freeBins, NumBins);
+ usedSlotsAfterLastSweep += c->nUsedSlots();
+ });
- auto newEnd = std::remove_if(chunks.begin(), chunks.end(), isFree);
- chunks.erase(newEnd, chunks.end());
+ // only free the chunks at the end to avoid that the sweep() calls indirectly
+ // access freed memory
+ std::for_each(firstEmptyChunk, chunks.end(), [this](Chunk *c) {
+ Q_V4_PROFILE_DEALLOC(engine, Chunk::DataSize, Profiling::HeapPage);
+ chunkAllocator->free(c);
+ });
+
+ chunks.erase(firstEmptyChunk, chunks.end());
}
void BlockAllocator::freeAll()
{
- for (auto c : chunks) {
+ for (auto c : chunks)
c->freeAll(engine);
+ for (auto c : chunks) {
Q_V4_PROFILE_DEALLOC(engine, Chunk::DataSize, Profiling::HeapPage);
chunkAllocator->free(c);
}
@@ -691,7 +689,7 @@ static void freeHugeChunk(ChunkAllocator *chunkAllocator, const HugeItemAllocato
{
HeapItem *itemToFree = c.chunk->first();
Heap::Base *b = *itemToFree;
- const VTable *v = b->vtable();
+ const VTable *v = b->internalClass->vtable;
if (Q_UNLIKELY(classCountPtr))
classCountPtr(v->className);
@@ -758,6 +756,7 @@ MemoryManager::MemoryManager(ExecutionEngine *engine)
: engine(engine)
, chunkAllocator(new ChunkAllocator)
, blockAllocator(chunkAllocator, engine)
+ , icAllocator(chunkAllocator, engine)
, hugeItemAllocator(chunkAllocator, engine)
, m_persistentValues(new PersistentValueStorage(engine))
, m_weakValues(new PersistentValueStorage(engine))
@@ -774,11 +773,6 @@ MemoryManager::MemoryManager(ExecutionEngine *engine)
blockAllocator.allocationStats = statistics.allocations;
}
-#ifdef MM_STATS
-static int allocationCount = 0;
-static size_t lastAllocRequestedSlots = 0;
-#endif
-
Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize)
{
const size_t stringSize = align(sizeof(Heap::String));
@@ -884,7 +878,7 @@ Heap::Object *MemoryManager::allocObjectWithMemberData(const QV4::VTable *vtable
Chunk::clearBit(c->extendsBitmap, index);
}
o->memberData.set(engine, m);
- m->internalClass = engine->internalClasses[EngineBase::Class_MemberData];
+ m->internalClass.set(engine, engine->internalClasses(EngineBase::Class_MemberData));
Q_ASSERT(o->memberData->internalClass);
m->values.alloc = static_cast<uint>((memberSize - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value));
m->values.size = o->memberData->values.alloc;
@@ -911,7 +905,7 @@ void MarkStack::drain()
Heap::Base *h = pop();
++markStackSize;
Q_ASSERT(h); // at this point we should only have Heap::Base objects in this area on the stack. If not, weird things might happen.
- h->markChildren(this);
+ h->internalClass->vtable->markObjects(h, this);
}
}
@@ -981,7 +975,30 @@ void MemoryManager::sweep(bool lastSweep, ClassDestroyStatsCallback classCountPt
if (QObjectWrapper *qobjectWrapper = (*it).as<QObjectWrapper>())
qobjectWrapper->destroyObject(lastSweep);
- (*it) = Primitive::undefinedValue();
+ (*it) = Value::undefinedValue();
+ }
+
+ // remove objects from weak maps and sets
+ Heap::MapObject *map = weakMaps;
+ Heap::MapObject **lastMap = &weakMaps;
+ while (map) {
+ if (map->isMarked()) {
+ map->removeUnmarkedKeys();
+ *lastMap = map;
+ lastMap = &map->nextWeakMap;
+ }
+ map = map->nextWeakMap;
+ }
+
+ Heap::SetObject *set = weakSets;
+ Heap::SetObject **lastSet = &weakSets;
+ while (set) {
+ if (set->isMarked()) {
+ set->removeUnmarkedKeys();
+ *lastSet = set;
+ lastSet = &set->nextWeakSet;
+ }
+ set = set->nextWeakSet;
}
// onDestruction handlers may have accessed other QObject wrappers and reset their value, so ensure
@@ -990,7 +1007,7 @@ void MemoryManager::sweep(bool lastSweep, ClassDestroyStatsCallback classCountPt
Managed *m = (*it).managed();
if (!m || m->markBit())
continue;
- (*it) = Primitive::undefinedValue();
+ (*it) = Value::undefinedValue();
}
// Now it is time to free QV4::QObjectWrapper Value, we must check the Value's tag to make sure its object has been destroyed
@@ -1017,13 +1034,18 @@ void MemoryManager::sweep(bool lastSweep, ClassDestroyStatsCallback classCountPt
}
}
- blockAllocator.sweep();
- hugeItemAllocator.sweep(classCountPtr);
+
+ if (!lastSweep) {
+ engine->identifierTable->sweep();
+ blockAllocator.sweep(/*classCountPtr*/);
+ hugeItemAllocator.sweep(classCountPtr);
+ icAllocator.sweep(/*classCountPtr*/);
+ }
}
bool MemoryManager::shouldRunGC() const
{
- size_t total = blockAllocator.totalSlots();
+ size_t total = blockAllocator.totalSlots() + icAllocator.totalSlots();
if (total > MinSlotsGCLimit && usedSlotsAfterLastFullSweep * GCOverallocation < total * 100)
return true;
return false;
@@ -1096,10 +1118,6 @@ void MemoryManager::runGC()
qDebug(stats) << "Fragmented memory before GC" << (totalMem - usedBefore);
dumpBins(&blockAllocator);
-#ifdef MM_STATS
- nGrayItems = 0;
-#endif
-
QElapsedTimer t;
t.start();
mark();
@@ -1159,24 +1177,26 @@ void MemoryManager::runGC()
if (aggressiveGC) {
// ensure we don't 'loose' any memory
- Q_ASSERT(blockAllocator.allocatedMem() == getUsedMem() + dumpBins(&blockAllocator, false));
+ Q_ASSERT(blockAllocator.allocatedMem()
+ == blockAllocator.usedMem() + dumpBins(&blockAllocator, false));
}
- usedSlotsAfterLastFullSweep = blockAllocator.usedSlotsAfterLastSweep;
+ usedSlotsAfterLastFullSweep = blockAllocator.usedSlotsAfterLastSweep + icAllocator.usedSlotsAfterLastSweep;
// reset all black bits
blockAllocator.resetBlackBits();
hugeItemAllocator.resetBlackBits();
+ icAllocator.resetBlackBits();
}
size_t MemoryManager::getUsedMem() const
{
- return blockAllocator.usedMem();
+ return blockAllocator.usedMem() + icAllocator.usedMem();
}
size_t MemoryManager::getAllocatedMem() const
{
- return blockAllocator.allocatedMem() + hugeItemAllocator.usedMem();
+ return blockAllocator.allocatedMem() + icAllocator.allocatedMem() + hugeItemAllocator.usedMem();
}
size_t MemoryManager::getLargeItemsMem() const
@@ -1184,6 +1204,18 @@ size_t MemoryManager::getLargeItemsMem() const
return hugeItemAllocator.usedMem();
}
+void MemoryManager::registerWeakMap(Heap::MapObject *map)
+{
+ map->nextWeakMap = weakMaps;
+ weakMaps = map;
+}
+
+void MemoryManager::registerWeakSet(Heap::SetObject *set)
+{
+ set->nextWeakSet = weakSets;
+ weakSets = set;
+}
+
MemoryManager::~MemoryManager()
{
delete m_persistentValues;
@@ -1193,6 +1225,7 @@ MemoryManager::~MemoryManager()
sweep(/*lastSweep*/true);
blockAllocator.freeAll();
hugeItemAllocator.freeAll();
+ icAllocator.freeAll();
delete m_weakValues;
#ifdef V4_USE_VALGRIND
diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h
index 40670bcdc7..bbbbb1aef6 100644
--- a/src/qml/memory/qv4mm_p.h
+++ b/src/qml/memory/qv4mm_p.h
@@ -160,224 +160,98 @@ public:
{ return (size + Chunk::SlotSize - 1) & ~(Chunk::SlotSize - 1); }
template<typename ManagedType>
- inline typename ManagedType::Data *allocManaged(std::size_t size)
+ inline typename ManagedType::Data *allocManaged(std::size_t size, Heap::InternalClass *ic)
{
Q_STATIC_ASSERT(std::is_trivial< typename ManagedType::Data >::value);
size = align(size);
- Heap::Base *o = allocData(size);
- InternalClass *ic = ManagedType::defaultInternalClass(engine);
- ic = ic->changeVTable(ManagedType::staticVTable());
- o->internalClass = ic;
- Q_ASSERT(o->internalClass && o->internalClass->vtable);
- return static_cast<typename ManagedType::Data *>(o);
+ typename ManagedType::Data *d = static_cast<typename ManagedType::Data *>(allocData(size));
+ d->internalClass.set(engine, ic);
+ Q_ASSERT(d->internalClass && d->internalClass->vtable);
+ Q_ASSERT(ic->vtable == ManagedType::staticVTable());
+ return d;
}
template<typename ManagedType>
inline typename ManagedType::Data *allocManaged(std::size_t size, InternalClass *ic)
{
- Q_STATIC_ASSERT(std::is_trivial< typename ManagedType::Data >::value);
- size = align(size);
- Heap::Base *o = allocData(size);
- o->internalClass = ic;
- Q_ASSERT(o->internalClass && o->internalClass->vtable);
- Q_ASSERT(ic->vtable == ManagedType::staticVTable());
- return static_cast<typename ManagedType::Data *>(o);
+ return allocManaged<ManagedType>(size, ic->d());
+ }
+
+ template<typename ManagedType>
+ inline typename ManagedType::Data *allocManaged(std::size_t size)
+ {
+ Scope scope(engine);
+ Scoped<InternalClass> ic(scope, ManagedType::defaultInternalClass(engine));
+ return allocManaged<ManagedType>(size, ic);
}
template <typename ObjectType>
- typename ObjectType::Data *allocateObject(InternalClass *ic)
+ typename ObjectType::Data *allocateObject(Heap::InternalClass *ic)
{
Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size);
- o->internalClass = ic;
- Q_ASSERT(o->internalClass && o->internalClass->vtable);
- Q_ASSERT(ic->vtable == ObjectType::staticVTable());
+ o->internalClass.set(engine, ic);
+ Q_ASSERT(o->internalClass.get() && o->vtable());
+ Q_ASSERT(o->vtable() == ObjectType::staticVTable());
return static_cast<typename ObjectType::Data *>(o);
}
template <typename ObjectType>
+ typename ObjectType::Data *allocateObject(InternalClass *ic)
+ {
+ return allocateObject<ObjectType>(ic->d());
+ }
+
+ template <typename ObjectType>
typename ObjectType::Data *allocateObject()
{
- InternalClass *ic = ObjectType::defaultInternalClass(engine);
+ Scope scope(engine);
+ Scoped<InternalClass> ic(scope, ObjectType::defaultInternalClass(engine));
ic = ic->changeVTable(ObjectType::staticVTable());
ic = ic->changePrototype(ObjectType::defaultPrototype(engine)->d());
- Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size);
- o->internalClass = ic;
- Q_ASSERT(o->internalClass && o->internalClass->vtable);
- Q_ASSERT(o->internalClass->prototype == ObjectType::defaultPrototype(engine)->d());
- return static_cast<typename ObjectType::Data *>(o);
+ return allocateObject<ObjectType>(ic);
}
template <typename ManagedType, typename Arg1>
typename ManagedType::Data *allocWithStringData(std::size_t unmanagedSize, Arg1 arg1)
{
typename ManagedType::Data *o = reinterpret_cast<typename ManagedType::Data *>(allocString(unmanagedSize));
- o->internalClass = ManagedType::defaultInternalClass(engine);
+ o->internalClass.set(engine, ManagedType::defaultInternalClass(engine));
Q_ASSERT(o->internalClass && o->internalClass->vtable);
o->init(arg1);
return o;
}
- template <typename ObjectType>
- typename ObjectType::Data *allocObject(InternalClass *ic)
+ template <typename ObjectType, typename... Args>
+ typename ObjectType::Data *allocObject(Heap::InternalClass *ic, Args... args)
{
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- t->d_unchecked()->init();
- return t->d();
+ typename ObjectType::Data *d = allocateObject<ObjectType>(ic);
+ d->init(args...);
+ return d;
}
- template <typename ObjectType>
- typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype)
+ template <typename ObjectType, typename... Args>
+ typename ObjectType::Data *allocObject(InternalClass *ic, Args... args)
{
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : nullptr));
- Q_UNUSED(prototype);
- t->d_unchecked()->init();
- return t->d();
- }
-
- template <typename ObjectType, typename Arg1>
- typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype, Arg1 arg1)
- {
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : nullptr));
- Q_UNUSED(prototype);
- t->d_unchecked()->init(arg1);
- return t->d();
- }
-
- template <typename ObjectType, typename Arg1, typename Arg2>
- typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype, Arg1 arg1, Arg2 arg2)
- {
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : nullptr));
- Q_UNUSED(prototype);
- t->d_unchecked()->init(arg1, arg2);
- return t->d();
- }
-
- template <typename ObjectType, typename Arg1, typename Arg2, typename Arg3>
- typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype, Arg1 arg1, Arg2 arg2, Arg3 arg3)
- {
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : nullptr));
- Q_UNUSED(prototype);
- t->d_unchecked()->init(arg1, arg2, arg3);
- return t->d();
- }
-
- template <typename ObjectType, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
- typename ObjectType::Data *allocObject(InternalClass *ic, Object *prototype, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)
- {
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : nullptr));
- Q_UNUSED(prototype);
- t->d_unchecked()->init(arg1, arg2, arg3, arg4);
- return t->d();
- }
-
- template <typename ObjectType>
- typename ObjectType::Data *allocObject()
- {
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>());
- t->d_unchecked()->init();
- return t->d();
+ typename ObjectType::Data *d = allocateObject<ObjectType>(ic);
+ d->init(args...);
+ return d;
}
- template <typename ObjectType, typename Arg1>
- typename ObjectType::Data *allocObject(Arg1 arg1)
+ template <typename ObjectType, typename... Args>
+ typename ObjectType::Data *allocate(Args... args)
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>());
- t->d_unchecked()->init(arg1);
+ t->d_unchecked()->init(args...);
return t->d();
}
- template <typename ObjectType, typename Arg1, typename Arg2>
- typename ObjectType::Data *allocObject(Arg1 arg1, Arg2 arg2)
- {
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>());
- t->d_unchecked()->init(arg1, arg2);
- return t->d();
- }
-
- template <typename ObjectType, typename Arg1, typename Arg2, typename Arg3>
- typename ObjectType::Data *allocObject(Arg1 arg1, Arg2 arg2, Arg3 arg3)
- {
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>());
- t->d_unchecked()->init(arg1, arg2, arg3);
- return t->d();
- }
-
- template <typename ObjectType, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
- typename ObjectType::Data *allocObject(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)
- {
- Scope scope(engine);
- Scoped<ObjectType> t(scope, allocateObject<ObjectType>());
- t->d_unchecked()->init(arg1, arg2, arg3, arg4);
- return t->d();
- }
-
-
- template <typename ManagedType>
- typename ManagedType::Data *alloc()
- {
- Scope scope(engine);
- Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
- t->d_unchecked()->init();
- return t->d();
- }
-
- template <typename ManagedType, typename Arg1>
- typename ManagedType::Data *alloc(Arg1 arg1)
- {
- Scope scope(engine);
- Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
- t->d_unchecked()->init(arg1);
- return t->d();
- }
-
- template <typename ManagedType, typename Arg1, typename Arg2>
- typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2)
- {
- Scope scope(engine);
- Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
- t->d_unchecked()->init(arg1, arg2);
- return t->d();
- }
-
- template <typename ManagedType, typename Arg1, typename Arg2, typename Arg3>
- typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2, Arg3 arg3)
- {
- Scope scope(engine);
- Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
- t->d_unchecked()->init(arg1, arg2, arg3);
- return t->d();
- }
-
- template <typename ManagedType, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
- typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4)
- {
- Scope scope(engine);
- Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
- t->d_unchecked()->init(arg1, arg2, arg3, arg4);
- return t->d();
- }
-
- template <typename ManagedType, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
- typename ManagedType::Data *alloc(Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5)
+ template <typename ManagedType, typename... Args>
+ typename ManagedType::Data *alloc(Args... args)
{
Scope scope(engine);
Scoped<ManagedType> t(scope, allocManaged<ManagedType>(sizeof(typename ManagedType::Data)));
- t->d_unchecked()->init(arg1, arg2, arg3, arg4, arg5);
+ t->d_unchecked()->init(args...);
return t->d();
}
@@ -392,6 +266,17 @@ public:
// called when a JS object grows itself. Specifically: Heap::String::append
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);
+ return static_cast<typename ManagedType::Data *>(b);
+ }
+
+ void registerWeakMap(Heap::MapObject *map);
+ void registerWeakSet(Heap::SetObject *set);
+
protected:
/// expects size to be aligned
Heap::Base *allocString(std::size_t unmanagedSize);
@@ -409,10 +294,13 @@ public:
QV4::ExecutionEngine *engine;
ChunkAllocator *chunkAllocator;
BlockAllocator blockAllocator;
+ BlockAllocator icAllocator;
HugeItemAllocator hugeItemAllocator;
PersistentValueStorage *m_persistentValues;
PersistentValueStorage *m_weakValues;
QVector<Value *> m_pendingFreedObjectWrapperValue;
+ Heap::MapObject *weakMaps = nullptr;
+ Heap::SetObject *weakSets = nullptr;
std::size_t unmanagedHeapSize = 0; // the amount of bytes of heap that is not managed by the memory manager, but which is held onto by managed items.
std::size_t unmanagedHeapSizeGCLimit;
@@ -423,6 +311,9 @@ public:
bool gcStats = false;
bool gcCollectorStats = false;
+ int allocationCount = 0;
+ size_t lastAllocRequestedSlots = 0;
+
struct {
size_t maxReservedMem = 0;
size_t maxAllocatedMem = 0;
diff --git a/src/qml/parser/parser.pri b/src/qml/parser/parser.pri
index e5b8ae2749..adab4ef9a2 100644
--- a/src/qml/parser/parser.pri
+++ b/src/qml/parser/parser.pri
@@ -3,20 +3,24 @@ HEADERS += \
$$PWD/qqmljsastfwd_p.h \
$$PWD/qqmljsastvisitor_p.h \
$$PWD/qqmljsengine_p.h \
- $$PWD/qqmljsgrammar_p.h \
$$PWD/qqmljslexer_p.h \
$$PWD/qqmljsmemorypool_p.h \
- $$PWD/qqmljsparser_p.h \
$$PWD/qqmljsglobal_p.h \
$$PWD/qqmljskeywords_p.h \
+ $$PWD/qqmljsengine_p.h \
+ $$PWD/qqmljsglobal_p.h
SOURCES += \
$$PWD/qqmljsast.cpp \
$$PWD/qqmljsastvisitor.cpp \
$$PWD/qqmljsengine_p.cpp \
- $$PWD/qqmljsgrammar.cpp \
$$PWD/qqmljslexer.cpp \
- $$PWD/qqmljsparser.cpp \
-OTHER_FILES += \
- $$PWD/qqmljs.g
+CONFIG += qlalr
+QLALRSOURCES = $$PWD/qqmljs.g
+QMAKE_QLALRFLAGS = --no-debug --qt
+
+OTHER_FILES += $$QLALRSOURCES
+
+# make sure we install the headers generated by qlalr
+private_headers.CONFIG += no_check_exist
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g
index d2d947e55c..a844e3cb3e 100644
--- a/src/qml/parser/qqmljs.g
+++ b/src/qml/parser/qqmljs.g
@@ -40,8 +40,7 @@
%parser QQmlJSGrammar
%decl qqmljsparser_p.h
%impl qqmljsparser.cpp
-%expect 5
-%expect-rr 2
+%expect 1
%token T_AND "&" T_AND_AND "&&" T_AND_EQ "&="
%token T_BREAK "break" T_CASE "case" T_CATCH "catch"
@@ -64,7 +63,8 @@
%token T_RBRACE "}" T_RBRACKET "]" T_REMAINDER "%"
%token T_REMAINDER_EQ "%=" T_RETURN "return" T_RPAREN ")"
%token T_SEMICOLON ";" T_AUTOMATIC_SEMICOLON T_STAR "*"
-%token T_STAR_EQ "*=" T_STRING_LITERAL "string literal"
+%token T_STAR_STAR "**" T_STAR_STAR_EQ "**=" T_STAR_EQ "*="
+%token T_STRING_LITERAL "string literal"
%token T_PROPERTY "property" T_SIGNAL "signal" T_READONLY "readonly"
%token T_SWITCH "switch" T_THIS "this" T_THROW "throw"
%token T_TILDE "~" T_TRY "try" T_TYPEOF "typeof"
@@ -77,14 +77,29 @@
%token T_MULTILINE_STRING_LITERAL "multiline string literal"
%token T_COMMENT "comment"
%token T_COMPATIBILITY_SEMICOLON
+%token T_ARROW "=>"
%token T_ENUM "enum"
+%token T_ELLIPSIS "..."
+%token T_YIELD "yield"
+%token T_SUPER "super"
+%token T_CLASS "class"
+%token T_EXTENDS "extends"
+%token T_STATIC "static"
+%token T_EXPORT "export"
+%token T_FROM "from"
+
+--- template strings
+%token T_NO_SUBSTITUTION_TEMPLATE"(no subst template)"
+%token T_TEMPLATE_HEAD "(template head)"
+%token T_TEMPLATE_MIDDLE "(template middle)"
+%token T_TEMPLATE_TAIL "(template tail)"
--- context keywords.
%token T_PUBLIC "public"
%token T_IMPORT "import"
%token T_PRAGMA "pragma"
%token T_AS "as"
-%token T_ON "on"
+%token T_OF "of"
%token T_GET "get"
%token T_SET "set"
@@ -95,11 +110,16 @@
%token T_FEED_UI_OBJECT_MEMBER
%token T_FEED_JS_STATEMENT
%token T_FEED_JS_EXPRESSION
-%token T_FEED_JS_SOURCE_ELEMENT
-%token T_FEED_JS_PROGRAM
+%token T_FEED_JS_SCRIPT
+%token T_FEED_JS_MODULE
-%nonassoc SHIFT_THERE
-%nonassoc T_IDENTIFIER T_COLON T_SIGNAL T_PROPERTY T_READONLY T_ON T_SET T_GET
+--- Lookahead handling
+%token T_FORCE_DECLARATION "(force decl)"
+%token T_FORCE_BLOCK "(force block)"
+%token T_FOR_LOOKAHEAD_OK "(for lookahead ok)"
+
+--%left T_PLUS T_MINUS
+%nonassoc T_IDENTIFIER T_COLON T_SIGNAL T_PROPERTY T_READONLY T_ON T_SET T_GET T_OF T_STATIC T_FROM T_AS
%nonassoc REDUCE_HERE
%start TopLevel
@@ -143,10 +163,10 @@
**
****************************************************************************/
-#include "qqmljsengine_p.h"
-#include "qqmljslexer_p.h"
-#include "qqmljsast_p.h"
-#include "qqmljsmemorypool_p.h"
+#include <private/qqmljsengine_p.h>
+#include <private/qqmljslexer_p.h>
+#include <private/qqmljsast_p.h>
+#include <private/qqmljsmemorypool_p.h>
#include <QtCore/qdebug.h>
#include <QtCore/qcoreapplication.h>
@@ -221,10 +241,10 @@
#ifndef QQMLJSPARSER_P_H
#define QQMLJSPARSER_P_H
-#include "qqmljsglobal_p.h"
-#include "qqmljsgrammar_p.h"
-#include "qqmljsast_p.h"
-#include "qqmljsengine_p.h"
+#include <private/qqmljsglobal_p.h>
+#include <private/qqmljsgrammar_p.h>
+#include <private/qqmljsast_p.h>
+#include <private/qqmljsengine_p.h>
#include <QtCore/qlist.h>
#include <QtCore/qstring.h>
@@ -241,30 +261,42 @@ public:
union Value {
int ival;
double dval;
+ AST::VariableScope scope;
+ AST::ForEachType forEachType;
AST::ArgumentList *ArgumentList;
AST::CaseBlock *CaseBlock;
AST::CaseClause *CaseClause;
AST::CaseClauses *CaseClauses;
AST::Catch *Catch;
AST::DefaultClause *DefaultClause;
- AST::ElementList *ElementList;
AST::Elision *Elision;
AST::ExpressionNode *Expression;
+ AST::TemplateLiteral *Template;
AST::Finally *Finally;
AST::FormalParameterList *FormalParameterList;
- AST::FunctionBody *FunctionBody;
AST::FunctionDeclaration *FunctionDeclaration;
AST::Node *Node;
AST::PropertyName *PropertyName;
- AST::PropertyAssignment *PropertyAssignment;
- AST::PropertyAssignmentList *PropertyAssignmentList;
- AST::SourceElement *SourceElement;
- AST::SourceElements *SourceElements;
AST::Statement *Statement;
AST::StatementList *StatementList;
AST::Block *Block;
- AST::VariableDeclaration *VariableDeclaration;
AST::VariableDeclarationList *VariableDeclarationList;
+ AST::Pattern *Pattern;
+ AST::PatternElement *PatternElement;
+ AST::PatternElementList *PatternElementList;
+ AST::PatternProperty *PatternProperty;
+ AST::PatternPropertyList *PatternPropertyList;
+ AST::ClassElementList *ClassElementList;
+ AST::ImportClause *ImportClause;
+ AST::FromClause *FromClause;
+ AST::NameSpaceImport *NameSpaceImport;
+ AST::ImportsList *ImportsList;
+ AST::NamedImports *NamedImports;
+ AST::ImportSpecifier *ImportSpecifier;
+ AST::ExportSpecifier *ExportSpecifier;
+ AST::ExportsList *ExportsList;
+ AST::ExportClause *ExportClause;
+ AST::ExportDeclaration *ExportDeclaration;
AST::UiProgram *UiProgram;
AST::UiHeaderItemList *UiHeaderItemList;
@@ -281,7 +313,6 @@ public:
AST::UiObjectMemberList *UiObjectMemberList;
AST::UiArrayMemberList *UiArrayMemberList;
AST::UiQualifiedId *UiQualifiedId;
- AST::UiQualifiedPragmaId *UiQualifiedPragmaId;
AST::UiEnumMemberList *UiEnumMemberList;
};
@@ -290,12 +321,13 @@ public:
~Parser();
// parse a UI program
- bool parse() { return parse(T_FEED_UI_PROGRAM); }
+ bool parse() { ++functionNestingLevel; bool r = parse(T_FEED_UI_PROGRAM); --functionNestingLevel; return r; }
bool parseStatement() { return parse(T_FEED_JS_STATEMENT); }
bool parseExpression() { return parse(T_FEED_JS_EXPRESSION); }
- bool parseSourceElement() { return parse(T_FEED_JS_SOURCE_ELEMENT); }
- bool parseUiObjectMember() { return parse(T_FEED_UI_OBJECT_MEMBER); }
- bool parseProgram() { return parse(T_FEED_JS_PROGRAM); }
+ bool parseUiObjectMember() { ++functionNestingLevel; bool r = parse(T_FEED_UI_OBJECT_MEMBER); --functionNestingLevel; return r; }
+ bool parseProgram() { return parse(T_FEED_JS_SCRIPT); }
+ bool parseScript() { return parse(T_FEED_JS_SCRIPT); }
+ bool parseModule() { return parse(T_FEED_JS_MODULE); }
AST::UiProgram *ast() const
{ return AST::cast<AST::UiProgram *>(program); }
@@ -364,22 +396,31 @@ protected:
{ return location_stack [tos + index - 1]; }
AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr);
- AST::UiQualifiedPragmaId *reparseAsQualifiedPragmaId(AST::ExpressionNode *expr);
+
+ void pushToken(int token);
+ int lookaheadToken(Lexer *lexer);
+
+ void syntaxError(const AST::SourceLocation &location, const char *message) {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location, QLatin1String(message)));
+ }
+ void syntaxError(const AST::SourceLocation &location, const QString &message) {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location, message));
+ }
protected:
Engine *driver;
MemoryPool *pool;
- int tos;
- int stack_size;
- Value *sym_stack;
- int *state_stack;
- AST::SourceLocation *location_stack;
- QStringRef *string_stack;
+ int tos = 0;
+ int stack_size = 0;
+ Value *sym_stack = nullptr;
+ int *state_stack = nullptr;
+ AST::SourceLocation *location_stack = nullptr;
+ QVector<QStringRef> string_stack;
- AST::Node *program;
+ AST::Node *program = nullptr;
- // error recovery
- enum { TOKEN_BUFFER_SIZE = 3 };
+ // error recovery and lookahead handling
+ enum { TOKEN_BUFFER_SIZE = 5 };
struct SavedToken {
int token;
@@ -388,14 +429,25 @@ protected:
QStringRef spell;
};
- double yylval;
+ int yytoken = -1;
+ double yylval = 0.;
QStringRef yytokenspell;
AST::SourceLocation yylloc;
AST::SourceLocation yyprevlloc;
SavedToken token_buffer[TOKEN_BUFFER_SIZE];
- SavedToken *first_token;
- SavedToken *last_token;
+ SavedToken *first_token = nullptr;
+ SavedToken *last_token = nullptr;
+
+ int functionNestingLevel = 0;
+
+ enum CoverExpressionType {
+ CE_Invalid,
+ CE_ParenthesizedExpression,
+ CE_FormalParameterList
+ };
+ AST::SourceLocation coverExpressionErrorLocation;
+ CoverExpressionType coverExpressionType = CE_Invalid;
QList<DiagnosticMessage> diagnostic_messages;
};
@@ -424,6 +476,8 @@ protected:
// qlalr --no-debug --no-lines --qt qqmljs.g
//
+#define UNIMPLEMENTED syntaxError(loc(1), "Unimplemented"); return false
+
using namespace QQmlJS;
QT_QML_BEGIN_NAMESPACE
@@ -438,22 +492,12 @@ void Parser::reallocateStack()
sym_stack = reinterpret_cast<Value*> (realloc(sym_stack, stack_size * sizeof(Value)));
state_stack = reinterpret_cast<int*> (realloc(state_stack, stack_size * sizeof(int)));
location_stack = reinterpret_cast<AST::SourceLocation*> (realloc(location_stack, stack_size * sizeof(AST::SourceLocation)));
- string_stack = reinterpret_cast<QStringRef*> (realloc(string_stack, stack_size * sizeof(QStringRef)));
+ string_stack.resize(stack_size);
}
Parser::Parser(Engine *engine):
driver(engine),
- pool(engine->pool()),
- tos(0),
- stack_size(0),
- sym_stack(0),
- state_stack(0),
- location_stack(0),
- string_stack(0),
- program(0),
- yylval(0),
- first_token(0),
- last_token(0)
+ pool(engine->pool())
{
}
@@ -463,7 +507,6 @@ Parser::~Parser()
free(sym_stack);
free(state_stack);
free(location_stack);
- free(string_stack);
}
}
@@ -505,29 +548,39 @@ AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr)
return 0;
}
-AST::UiQualifiedPragmaId *Parser::reparseAsQualifiedPragmaId(AST::ExpressionNode *expr)
+void Parser::pushToken(int token)
{
- if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(expr)) {
- AST::UiQualifiedPragmaId *q = new (pool) AST::UiQualifiedPragmaId(idExpr->name);
- q->identifierToken = idExpr->identifierToken;
+ last_token->token = yytoken;
+ last_token->dval = yylval;
+ last_token->spell = yytokenspell;
+ last_token->loc = yylloc;
+ ++last_token;
+ yytoken = token;
+}
- return q->finish();
+int Parser::lookaheadToken(Lexer *lexer)
+{
+ if (yytoken < 0) {
+ yytoken = lexer->lex();
+ yylval = lexer->tokenValue();
+ yytokenspell = lexer->tokenSpell();
+ yylloc = location(lexer);
}
-
- return 0;
+ return yytoken;
}
+//#define PARSER_DEBUG
bool Parser::parse(int startToken)
{
Lexer *lexer = driver->lexer();
bool hadErrors = false;
- int yytoken = -1;
+ yytoken = -1;
int action = 0;
token_buffer[0].token = startToken;
first_token = &token_buffer[0];
- if (startToken == T_FEED_JS_PROGRAM && !lexer->qmlMode()) {
+ if (startToken == T_FEED_JS_SCRIPT && !lexer->qmlMode()) {
Directives ignoreDirectives;
Directives *directives = driver->directives();
if (!directives)
@@ -570,10 +623,19 @@ bool Parser::parse(int startToken)
yytokenspell = first_token->spell;
yylloc = first_token->loc;
++first_token;
+ if (first_token == last_token)
+ first_token = last_token = &token_buffer[0];
}
}
+#ifdef PARSER_DEBUG
+ qDebug() << " in state" << action;
+#endif
+
action = t_action(action, yytoken);
+#ifdef PARSER_DEBUG
+ qDebug() << " current token" << yytoken << (yytoken >= 0 ? spell[yytoken] : "(null)") << "new state" << action;
+#endif
if (action > 0) {
if (action != ACCEPT_STATE) {
yytoken = -1;
@@ -588,6 +650,10 @@ bool Parser::parse(int startToken)
const int r = -action - 1;
tos -= rhs[r];
+#ifdef PARSER_DEBUG
+ qDebug() << " reducing through rule " << -action;
+#endif
+
switch (r) {
./
@@ -595,2582 +661,3693 @@ bool Parser::parse(int startToken)
-- Declarative UI
--------------------------------------------------------------------------------------------------------
-TopLevel: T_FEED_UI_PROGRAM UiProgram ;
+TopLevel: T_FEED_UI_PROGRAM UiProgram;
/.
-case $rule_number: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+ } break;
./
-TopLevel: T_FEED_JS_STATEMENT Statement ;
+TopLevel: T_FEED_JS_STATEMENT Statement;
/.
-case $rule_number: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+ } break;
./
-TopLevel: T_FEED_JS_EXPRESSION Expression ;
+TopLevel: T_FEED_JS_EXPRESSION Expression;
/.
-case $rule_number: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+ } break;
./
-TopLevel: T_FEED_JS_SOURCE_ELEMENT SourceElement ;
+TopLevel: T_FEED_UI_OBJECT_MEMBER UiObjectMember;
/.
-case $rule_number: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+ } break;
./
-TopLevel: T_FEED_UI_OBJECT_MEMBER UiObjectMember ;
+TopLevel: T_FEED_JS_SCRIPT Script;
/.
-case $rule_number: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+ } break;
./
-TopLevel: T_FEED_JS_PROGRAM Program ;
+TopLevel: T_FEED_JS_MODULE Module;
/.
-case $rule_number: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ program = sym(1).Node;
+ } break;
./
+
UiProgram: UiHeaderItemListOpt UiRootMember;
/.
-case $rule_number: {
- sym(1).UiProgram = new (pool) AST::UiProgram(sym(1).UiHeaderItemList,
- sym(2).UiObjectMemberList->finish());
-} break;
+ case $rule_number: {
+ sym(1).UiProgram = new (pool) AST::UiProgram(sym(1).UiHeaderItemList, sym(2).UiObjectMemberList->finish());
+ } break;
./
-UiHeaderItemListOpt: Empty ;
-UiHeaderItemListOpt: UiHeaderItemList ;
+UiHeaderItemListOpt: Empty;
+UiHeaderItemListOpt: UiHeaderItemList;
/.
-case $rule_number: {
- sym(1).Node = sym(1).UiHeaderItemList->finish();
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(1).UiHeaderItemList->finish();
+ } break;
./
-UiHeaderItemList: UiPragma ;
+UiHeaderItemList: UiPragma;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiPragma);
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiPragma);
+ } break;
./
-UiHeaderItemList: UiImport ;
+UiHeaderItemList: UiImport;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiImport);
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiImport);
+ } break;
./
-UiHeaderItemList: UiHeaderItemList UiPragma ;
+UiHeaderItemList: UiHeaderItemList UiPragma;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiPragma);
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiPragma);
+ } break;
./
-UiHeaderItemList: UiHeaderItemList UiImport ;
+UiHeaderItemList: UiHeaderItemList UiImport;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiImport);
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiImport);
+ } break;
./
-PragmaId: MemberExpression ;
-
-ImportId: MemberExpression ;
+PragmaId: JsIdentifier;
-UiPragma: UiPragmaHead T_AUTOMATIC_SEMICOLON ;
-UiPragma: UiPragmaHead T_SEMICOLON ;
+UiPragma: T_PRAGMA PragmaId T_AUTOMATIC_SEMICOLON;
+UiPragma: T_PRAGMA PragmaId T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).UiPragma->semicolonToken = loc(2);
-} break;
+ case $rule_number: {
+ AST::UiPragma *pragma = new (pool) AST::UiPragma(stringRef(2));
+ pragma->pragmaToken = loc(1);
+ pragma->semicolonToken = loc(3);
+ sym(1).Node = pragma;
+ } break;
./
-UiImport: UiImportHead T_AUTOMATIC_SEMICOLON ;
-UiImport: UiImportHead T_SEMICOLON ;
+ImportId: MemberExpression;
+
+UiImport: UiImportHead T_AUTOMATIC_SEMICOLON;
+UiImport: UiImportHead T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).UiImport->semicolonToken = loc(2);
-} break;
+ case $rule_number: {
+ sym(1).UiImport->semicolonToken = loc(2);
+ } break;
./
-UiImport: UiImportHead T_NUMERIC_LITERAL T_AUTOMATIC_SEMICOLON ;
-UiImport: UiImportHead T_NUMERIC_LITERAL T_SEMICOLON ;
+UiImport: UiImportHead T_NUMERIC_LITERAL T_AUTOMATIC_SEMICOLON;
+UiImport: UiImportHead T_NUMERIC_LITERAL T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).UiImport->versionToken = loc(2);
- sym(1).UiImport->semicolonToken = loc(3);
-} break;
+ case $rule_number: {
+ sym(1).UiImport->versionToken = loc(2);
+ sym(1).UiImport->semicolonToken = loc(3);
+ } break;
./
-UiImport: UiImportHead T_NUMERIC_LITERAL T_AS JsIdentifier T_AUTOMATIC_SEMICOLON ;
-UiImport: UiImportHead T_NUMERIC_LITERAL T_AS JsIdentifier T_SEMICOLON ;
+UiImport: UiImportHead T_NUMERIC_LITERAL T_AS QmlIdentifier T_AUTOMATIC_SEMICOLON;
+UiImport: UiImportHead T_NUMERIC_LITERAL T_AS QmlIdentifier T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).UiImport->versionToken = loc(2);
- sym(1).UiImport->asToken = loc(3);
- sym(1).UiImport->importIdToken = loc(4);
- sym(1).UiImport->importId = stringRef(4);
- sym(1).UiImport->semicolonToken = loc(5);
-} break;
+ case $rule_number: {
+ sym(1).UiImport->versionToken = loc(2);
+ sym(1).UiImport->asToken = loc(3);
+ sym(1).UiImport->importIdToken = loc(4);
+ sym(1).UiImport->importId = stringRef(4);
+ sym(1).UiImport->semicolonToken = loc(5);
+ } break;
./
-UiImport: UiImportHead T_AS JsIdentifier T_AUTOMATIC_SEMICOLON ;
-UiImport: UiImportHead T_AS JsIdentifier T_SEMICOLON ;
+UiImport: UiImportHead T_AS QmlIdentifier T_AUTOMATIC_SEMICOLON;
+UiImport: UiImportHead T_AS QmlIdentifier T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).UiImport->asToken = loc(2);
- sym(1).UiImport->importIdToken = loc(3);
- sym(1).UiImport->importId = stringRef(3);
- sym(1).UiImport->semicolonToken = loc(4);
-} break;
+ case $rule_number: {
+ sym(1).UiImport->asToken = loc(2);
+ sym(1).UiImport->importIdToken = loc(3);
+ sym(1).UiImport->importId = stringRef(3);
+ sym(1).UiImport->semicolonToken = loc(4);
+ } break;
./
-UiPragmaHead: T_PRAGMA PragmaId ;
+UiImportHead: T_IMPORT ImportId;
/.
-case $rule_number: {
- AST::UiPragma *node = 0;
+ case $rule_number: {
+ AST::UiImport *node = 0;
- if (AST::UiQualifiedPragmaId *qualifiedId = reparseAsQualifiedPragmaId(sym(2).Expression)) {
- node = new (pool) AST::UiPragma(qualifiedId);
- }
+ if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) {
+ node = new (pool) AST::UiImport(importIdLiteral->value);
+ node->fileNameToken = loc(2);
+ } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) {
+ node = new (pool) AST::UiImport(qualifiedId);
+ node->fileNameToken = loc(2);
+ }
- sym(1).Node = node;
+ sym(1).Node = node;
- if (node) {
- node->pragmaToken = loc(1);
- } else {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
- QLatin1String("Expected a qualified name id")));
+ if (node) {
+ node->importToken = loc(1);
+ } else {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
+ QLatin1String("Expected a qualified name id or a string literal")));
- return false; // ### remove me
- }
-} break;
+ return false; // ### remove me
+ }
+ } break;
./
+Empty: ;
+/.
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
+./
-UiImportHead: T_IMPORT ImportId ;
+UiRootMember: UiObjectDefinition;
/.
-case $rule_number: {
- AST::UiImport *node = 0;
-
- if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) {
- node = new (pool) AST::UiImport(importIdLiteral->value);
- node->fileNameToken = loc(2);
- } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) {
- node = new (pool) AST::UiImport(qualifiedId);
- node->fileNameToken = loc(2);
- }
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember);
+ } break;
+./
- sym(1).Node = node;
+UiObjectMemberList: UiObjectMember;
+/.
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember);
+ } break;
+./
- if (node) {
- node->importToken = loc(1);
- } else {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
- QLatin1String("Expected a qualified name id or a string literal")));
+UiObjectMemberList: UiObjectMemberList UiObjectMember;
+/.
+ case $rule_number: {
+ AST::UiObjectMemberList *node = new (pool) AST:: UiObjectMemberList(sym(1).UiObjectMemberList, sym(2).UiObjectMember);
+ sym(1).Node = node;
+ } break;
+./
- return false; // ### remove me
- }
-} break;
+UiArrayMemberList: UiObjectDefinition;
+/.
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::UiArrayMemberList(sym(1).UiObjectMember);
+ } break;
./
-Empty: ;
+UiArrayMemberList: UiArrayMemberList T_COMMA UiObjectDefinition;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ AST::UiArrayMemberList *node = new (pool) AST::UiArrayMemberList(sym(1).UiArrayMemberList, sym(3).UiObjectMember);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-UiRootMember: UiObjectDefinition ;
+UiObjectInitializer: T_LBRACE T_RBRACE;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember);
-} break;
+ case $rule_number: {
+ AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer((AST::UiObjectMemberList*)0);
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-UiObjectMemberList: UiObjectMember ;
+UiObjectInitializer: T_LBRACE UiObjectMemberList T_RBRACE;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember);
-} break;
+ case $rule_number: {
+ AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer(sym(2).UiObjectMemberList->finish());
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-UiObjectMemberList: UiObjectMemberList UiObjectMember ;
+UiObjectDefinition: UiQualifiedId UiObjectInitializer;
/.
-case $rule_number: {
- AST::UiObjectMemberList *node = new (pool) AST:: UiObjectMemberList(
- sym(1).UiObjectMemberList, sym(2).UiObjectMember);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiObjectDefinition *node = new (pool) AST::UiObjectDefinition(sym(1).UiQualifiedId, sym(2).UiObjectInitializer);
+ sym(1).Node = node;
+ } break;
./
-UiArrayMemberList: UiObjectDefinition ;
+UiObjectMember: UiObjectDefinition;
+
+UiObjectMember: UiQualifiedId T_COLON ExpressionStatementLookahead T_LBRACKET UiArrayMemberList T_RBRACKET;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::UiArrayMemberList(sym(1).UiObjectMember);
-} break;
+ case $rule_number: {
+ AST::UiArrayBinding *node = new (pool) AST::UiArrayBinding(sym(1).UiQualifiedId, sym(5).UiArrayMemberList->finish());
+ node->colonToken = loc(2);
+ node->lbracketToken = loc(4);
+ node->rbracketToken = loc(6);
+ sym(1).Node = node;
+ } break;
./
-UiArrayMemberList: UiArrayMemberList T_COMMA UiObjectDefinition ;
+UiObjectMember: UiQualifiedId T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer;
/.
-case $rule_number: {
- AST::UiArrayMemberList *node = new (pool) AST::UiArrayMemberList(
- sym(1).UiArrayMemberList, sym(3).UiObjectMember);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding(
+ sym(1).UiQualifiedId, sym(4).UiQualifiedId, sym(5).UiObjectInitializer);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-UiObjectInitializer: T_LBRACE T_RBRACE ;
+UiObjectMember: UiQualifiedId T_ON UiQualifiedId UiObjectInitializer;
/.
-case $rule_number: {
- AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer((AST::UiObjectMemberList*)0);
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding(
+ sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer);
+ node->colonToken = loc(2);
+ node->hasOnToken = true;
+ sym(1).Node = node;
+ } break;
./
-UiObjectInitializer: T_LBRACE UiObjectMemberList T_RBRACE ;
+
+UiObjectLiteral: T_LBRACE ExpressionStatementLookahead UiPropertyDefinitionList T_RBRACE;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+UiObjectLiteral: T_LBRACE ExpressionStatementLookahead UiPropertyDefinitionList T_COMMA T_RBRACE;
/.
-case $rule_number: {
- AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer(sym(2).UiObjectMemberList->finish());
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ObjectPattern *l = new (pool) AST::ObjectPattern(sym(3).PatternPropertyList->finish());
+ l->lbraceToken = loc(1);
+ l->rbraceToken = loc(4);
+ AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(l);
+ sym(1).Node = node;
+ } break;
./
-UiObjectDefinition: UiQualifiedId UiObjectInitializer ;
+
+UiScriptStatement: ExpressionStatementLookahead T_FORCE_DECLARATION ExpressionStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+UiScriptStatement: ExpressionStatementLookahead T_FORCE_BLOCK Block;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+UiScriptStatement: ExpressionStatementLookahead T_FORCE_BLOCK UiObjectLiteral;
/.
-case $rule_number: {
- AST::UiObjectDefinition *node = new (pool) AST::UiObjectDefinition(sym(1).UiQualifiedId,
- sym(2).UiObjectInitializer);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(3).Node;
+ } break;
./
-UiObjectMember: UiObjectDefinition ;
-UiObjectMember: UiQualifiedId T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ;
+UiScriptStatement: ExpressionStatementLookahead EmptyStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+UiScriptStatement: ExpressionStatementLookahead ExpressionStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+UiScriptStatement: ExpressionStatementLookahead IfStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+UiScriptStatement: ExpressionStatementLookahead WithStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+UiScriptStatement: ExpressionStatementLookahead SwitchStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+UiScriptStatement: ExpressionStatementLookahead TryStatement;
/.
-case $rule_number: {
- AST::UiArrayBinding *node = new (pool) AST::UiArrayBinding(
- sym(1).UiQualifiedId, sym(4).UiArrayMemberList->finish());
- node->colonToken = loc(2);
- node->lbracketToken = loc(3);
- node->rbracketToken = loc(5);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ } break;
./
-UiObjectMember: UiQualifiedId T_COLON UiQualifiedId UiObjectInitializer ;
+UiObjectMember: UiQualifiedId T_COLON UiScriptStatement;
/.
-case $rule_number: {
- AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding(
- sym(1).UiQualifiedId, sym(3).UiQualifiedId, sym(4).UiObjectInitializer);
+case $rule_number:
+{
+ AST::UiScriptBinding *node = new (pool) AST::UiScriptBinding(sym(1).UiQualifiedId, sym(3).Statement);
node->colonToken = loc(2);
sym(1).Node = node;
-} break;
+ } break;
./
-UiObjectMember: UiQualifiedId T_ON UiQualifiedId UiObjectInitializer ;
+UiPropertyType: T_VAR;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+UiPropertyType: T_RESERVED_WORD;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+UiPropertyType: T_IDENTIFIER;
/.
-case $rule_number: {
- AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding(
- sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer);
- node->colonToken = loc(2);
- node->hasOnToken = true;
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1));
+ node->identifierToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-UiScriptStatement: Block ;
-UiScriptStatement: EmptyStatement ;
-UiScriptStatement: ExpressionStatement ;
-UiScriptStatement: IfStatement ;
-UiScriptStatement: WithStatement ;
-UiScriptStatement: SwitchStatement ;
-UiScriptStatement: TryStatement ;
+UiPropertyType: UiPropertyType T_DOT T_IDENTIFIER;
+/.
+ case $rule_number: {
+ AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(sym(1).UiQualifiedId, stringRef(3));
+ node->identifierToken = loc(3);
+ sym(1).Node = node;
+ } break;
+./
-UiObjectMember: UiQualifiedId T_COLON UiScriptStatement ;
+UiParameterListOpt: ;
/.
-case $rule_number:
-{
- AST::UiScriptBinding *node = new (pool) AST::UiScriptBinding(
- sym(1).UiQualifiedId, sym(3).Statement);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-UiPropertyType: T_VAR ;
+UiParameterListOpt: UiParameterList;
/.
-case $rule_number: {
- AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(1).UiParameterList->finish();
+ } break;
./
-UiPropertyType: T_RESERVED_WORD ;
+UiParameterList: UiPropertyType QmlIdentifier;
/.
-case $rule_number: {
- AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiQualifiedId->finish(), stringRef(2));
+ node->propertyTypeToken = loc(1);
+ node->identifierToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
+
+UiParameterList: UiParameterList T_COMMA UiPropertyType QmlIdentifier;
+/.
+ case $rule_number: {
+ AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, sym(3).UiQualifiedId->finish(), stringRef(4));
+ node->propertyTypeToken = loc(3);
+ node->commaToken = loc(2);
+ node->identifierToken = loc(4);
+ sym(1).Node = node;
+ } break;
+./
+
+UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_AUTOMATIC_SEMICOLON;
+UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_SEMICOLON;
+/.
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2));
+ node->type = AST::UiPublicMember::Signal;
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(2);
+ node->parameters = sym(4).UiParameterList;
+ node->semicolonToken = loc(6);
+ sym(1).Node = node;
+ } break;
+./
+
+UiObjectMember: T_SIGNAL T_IDENTIFIER T_AUTOMATIC_SEMICOLON;
+UiObjectMember: T_SIGNAL T_IDENTIFIER T_SEMICOLON;
+/.
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2));
+ node->type = AST::UiPublicMember::Signal;
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(2);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+ } break;
+./
+
+UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_AUTOMATIC_SEMICOLON;
+UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_SEMICOLON;
+/.
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6));
+ node->typeModifier = stringRef(2);
+ node->propertyToken = loc(1);
+ node->typeModifierToken = loc(2);
+ node->typeToken = loc(4);
+ node->identifierToken = loc(6);
+ node->semicolonToken = loc(7);
+ sym(1).Node = node;
+ } break;
+./
+
+UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_AUTOMATIC_SEMICOLON;
+UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_SEMICOLON;
+/.
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3));
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(3);
+ node->semicolonToken = loc(4);
+ sym(1).Node = node;
+ } break;
+./
+
+UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_AUTOMATIC_SEMICOLON;
+UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_SEMICOLON;
+/.
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4));
+ node->isDefaultMember = true;
+ node->defaultToken = loc(1);
+ node->propertyToken = loc(2);
+ node->typeToken = loc(3);
+ node->identifierToken = loc(4);
+ node->semicolonToken = loc(5);
+ sym(1).Node = node;
+ } break;
+./
+
+UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_AUTOMATIC_SEMICOLON;
+UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_SEMICOLON;
+/.
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(5).UiQualifiedId->finish(), stringRef(7));
+ node->isDefaultMember = true;
+ node->defaultToken = loc(1);
+ node->typeModifier = stringRef(3);
+ node->propertyToken = loc(2);
+ node->typeModifierToken = loc(2);
+ node->typeToken = loc(4);
+ node->identifierToken = loc(7);
+ node->semicolonToken = loc(8);
+ sym(1).Node = node;
+ } break;
+./
+
+UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement;
+/.
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3), sym(5).Statement);
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(3);
+ node->colonToken = loc(4);
+ sym(1).Node = node;
+ } break;
+./
+
+UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement;
+/.
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4), sym(6).Statement);
+ node->isReadonlyMember = true;
+ node->readonlyToken = loc(1);
+ node->propertyToken = loc(2);
+ node->typeToken = loc(3);
+ node->identifierToken = loc(4);
+ node->colonToken = loc(5);
+ sym(1).Node = node;
+ } break;
./
-UiPropertyType: T_IDENTIFIER ;
+UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType QmlIdentifier T_COLON UiScriptStatement;
/.
-case $rule_number: {
- AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4), sym(6).Statement);
+ node->isDefaultMember = true;
+ node->defaultToken = loc(1);
+ node->propertyToken = loc(2);
+ node->typeToken = loc(3);
+ node->identifierToken = loc(4);
+ node->colonToken = loc(5);
+ sym(1).Node = node;
+ } break;
./
-UiPropertyType: UiPropertyType T_DOT T_IDENTIFIER ;
+UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT QmlIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET;
/.
-case $rule_number: {
- AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(sym(1).UiQualifiedId, stringRef(3));
- node->identifierToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6));
+ node->typeModifier = stringRef(2);
+ node->propertyToken = loc(1);
+ node->typeModifierToken = loc(2);
+ node->typeToken = loc(4);
+ node->identifierToken = loc(6);
+ node->semicolonToken = loc(7); // insert a fake ';' before ':'
+
+ AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(6));
+ propertyName->identifierToken = loc(6);
+ propertyName->next = 0;
+
+ AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding(propertyName, sym(9).UiArrayMemberList->finish());
+ binding->colonToken = loc(7);
+ binding->lbracketToken = loc(8);
+ binding->rbracketToken = loc(10);
+
+ node->binding = binding;
+
+ sym(1).Node = node;
+ } break;
./
-UiParameterListOpt: ;
+UiObjectMember: T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3));
+ node->propertyToken = loc(1);
+ node->typeToken = loc(2);
+ node->identifierToken = loc(3);
+ node->semicolonToken = loc(4); // insert a fake ';' before ':'
+
+ AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(3));
+ propertyName->identifierToken = loc(3);
+ propertyName->next = 0;
+
+ AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding(
+ propertyName, sym(6).UiQualifiedId, sym(7).UiObjectInitializer);
+ binding->colonToken = loc(4);
+
+ node->binding = binding;
+
+ sym(1).Node = node;
+ } break;
./
-UiParameterListOpt: UiParameterList ;
+UiObjectMember: T_READONLY T_PROPERTY UiPropertyType QmlIdentifier T_COLON ExpressionStatementLookahead UiQualifiedId UiObjectInitializer;
/.
-case $rule_number: {
- sym(1).Node = sym(1).UiParameterList->finish ();
-} break;
+ case $rule_number: {
+ AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4));
+ node->isReadonlyMember = true;
+ node->readonlyToken = loc(1);
+ node->propertyToken = loc(2);
+ node->typeToken = loc(3);
+ node->identifierToken = loc(4);
+ node->semicolonToken = loc(5); // insert a fake ';' before ':'
+
+ AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(4));
+ propertyName->identifierToken = loc(4);
+ propertyName->next = 0;
+
+ AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding(
+ propertyName, sym(7).UiQualifiedId, sym(8).UiObjectInitializer);
+ binding->colonToken = loc(5);
+
+ node->binding = binding;
+
+ sym(1).Node = node;
+ } break;
./
-UiParameterList: UiPropertyType JsIdentifier ;
+UiObjectMember: FunctionDeclaration;
/.
-case $rule_number: {
- AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiQualifiedId->finish(), stringRef(2));
- node->propertyTypeToken = loc(1);
- node->identifierToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node);
+ } break;
./
-UiParameterList: UiParameterList T_COMMA UiPropertyType JsIdentifier ;
+UiObjectMember: VariableStatement;
/.
-case $rule_number: {
- AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, sym(3).UiQualifiedId->finish(), stringRef(4));
- node->propertyTypeToken = loc(3);
- node->commaToken = loc(2);
- node->identifierToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node);
+ } break;
./
-UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_AUTOMATIC_SEMICOLON ;
-UiObjectMember: T_SIGNAL T_IDENTIFIER T_LPAREN UiParameterListOpt T_RPAREN T_SEMICOLON ;
+UiQualifiedId: MemberExpression;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2));
- node->type = AST::UiPublicMember::Signal;
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(2);
- node->parameters = sym(4).UiParameterList;
- node->semicolonToken = loc(6);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken,
+ QLatin1String("Ignored annotation")));
+
+ sym(1).Expression = mem->base;
+ }
+
+ if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) {
+ sym(1).UiQualifiedId = qualifiedId;
+ } else {
+ sym(1).UiQualifiedId = 0;
+
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
+ QLatin1String("Expected a qualified name id")));
+
+ return false; // ### recover
+ }
+ } break;
./
-UiObjectMember: T_SIGNAL T_IDENTIFIER T_AUTOMATIC_SEMICOLON ;
-UiObjectMember: T_SIGNAL T_IDENTIFIER T_SEMICOLON ;
+UiObjectMember: T_ENUM T_IDENTIFIER T_LBRACE EnumMemberList T_RBRACE;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2));
- node->type = AST::UiPublicMember::Signal;
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(2);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiEnumDeclaration *enumDeclaration = new (pool) AST::UiEnumDeclaration(stringRef(2), sym(4).UiEnumMemberList->finish());
+ enumDeclaration->enumToken = loc(1);
+ enumDeclaration->rbraceToken = loc(5);
+ sym(1).Node = enumDeclaration;
+ break;
+ }
./
-UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_AUTOMATIC_SEMICOLON ;
-UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_SEMICOLON ;
+EnumMemberList: T_IDENTIFIER;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6));
- node->typeModifier = stringRef(2);
- node->propertyToken = loc(1);
- node->typeModifierToken = loc(2);
- node->typeToken = loc(4);
- node->identifierToken = loc(6);
- node->semicolonToken = loc(7);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1));
+ node->memberToken = loc(1);
+ sym(1).Node = node;
+ break;
+ }
./
-UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_AUTOMATIC_SEMICOLON ;
-UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_SEMICOLON ;
+EnumMemberList: T_IDENTIFIER T_EQ T_NUMERIC_LITERAL;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3));
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(3);
- node->semicolonToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1), sym(3).dval);
+ node->memberToken = loc(1);
+ node->valueToken = loc(3);
+ sym(1).Node = node;
+ break;
+ }
./
-UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_AUTOMATIC_SEMICOLON ;
-UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_SEMICOLON ;
+EnumMemberList: EnumMemberList T_COMMA T_IDENTIFIER;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4));
- node->isDefaultMember = true;
- node->defaultToken = loc(1);
- node->propertyToken = loc(2);
- node->typeToken = loc(3);
- node->identifierToken = loc(4);
- node->semicolonToken = loc(5);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3));
+ node->memberToken = loc(3);
+ sym(1).Node = node;
+ break;
+ }
./
-UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_AUTOMATIC_SEMICOLON ;
-UiObjectMember: T_DEFAULT T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_SEMICOLON ;
+EnumMemberList: EnumMemberList T_COMMA T_IDENTIFIER T_EQ T_NUMERIC_LITERAL;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(5).UiQualifiedId->finish(), stringRef(7));
- node->isDefaultMember = true;
- node->defaultToken = loc(1);
- node->typeModifier = stringRef(3);
- node->propertyToken = loc(2);
- node->typeModifierToken = loc(2);
- node->typeToken = loc(4);
- node->identifierToken = loc(7);
- node->semicolonToken = loc(8);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3), sym(5).dval);
+ node->memberToken = loc(3);
+ node->valueToken = loc(5);
+ sym(1).Node = node;
+ break;
+ }
./
-UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ;
+QmlIdentifier: T_IDENTIFIER;
+QmlIdentifier: T_PROPERTY;
+QmlIdentifier: T_SIGNAL;
+QmlIdentifier: T_READONLY;
+QmlIdentifier: T_ON;
+QmlIdentifier: T_GET;
+QmlIdentifier: T_SET;
+QmlIdentifier: T_FROM;
+QmlIdentifier: T_OF;
+
+JsIdentifier: T_IDENTIFIER;
+JsIdentifier: T_PROPERTY;
+JsIdentifier: T_SIGNAL;
+JsIdentifier: T_READONLY;
+JsIdentifier: T_ON;
+JsIdentifier: T_GET;
+JsIdentifier: T_SET;
+JsIdentifier: T_FROM;
+JsIdentifier: T_STATIC;
+JsIdentifier: T_OF;
+JsIdentifier: T_AS;
+
+IdentifierReference: JsIdentifier;
+BindingIdentifier: IdentifierReference;
+
+--------------------------------------------------------------------------------------------------------
+-- Expressions
+--------------------------------------------------------------------------------------------------------
+
+PrimaryExpression: T_THIS;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3),
- sym(5).Statement);
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(3);
- node->colonToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ThisExpression *node = new (pool) AST::ThisExpression();
+ node->thisToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-UiObjectMember: T_READONLY T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ;
+PrimaryExpression: IdentifierReference;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4),
- sym(6).Statement);
- node->isReadonlyMember = true;
- node->readonlyToken = loc(1);
- node->propertyToken = loc(2);
- node->typeToken = loc(3);
- node->identifierToken = loc(4);
- node->colonToken = loc(5);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1));
+ node->identifierToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-UiObjectMember: T_DEFAULT T_PROPERTY UiPropertyType JsIdentifier T_COLON UiScriptStatement ;
+PrimaryExpression: Literal;
+PrimaryExpression: ArrayLiteral;
+PrimaryExpression: ObjectLiteral;
+PrimaryExpression: FunctionExpression;
+PrimaryExpression: ClassExpression;
+PrimaryExpression: GeneratorExpression;
+PrimaryExpression: RegularExpressionLiteral;
+PrimaryExpression: TemplateLiteral;
+
+PrimaryExpression: CoverParenthesizedExpressionAndArrowParameterList;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4),
- sym(6).Statement);
- node->isDefaultMember = true;
- node->defaultToken = loc(1);
- node->propertyToken = loc(2);
- node->typeToken = loc(3);
- node->identifierToken = loc(4);
- node->colonToken = loc(5);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ if (coverExpressionType != CE_ParenthesizedExpression) {
+ syntaxError(coverExpressionErrorLocation, "Expected token ')'.");
+ return false;
+ }
+ } break;
./
-UiObjectMember: T_PROPERTY T_IDENTIFIER T_LT UiPropertyType T_GT JsIdentifier T_COLON T_LBRACKET UiArrayMemberList T_RBRACKET ;
+-- Parsing of the CoverParenthesizedExpressionAndArrowParameterList is restricted to the one rule below when this is parsed as a primary expression
+CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN Expression_In T_RPAREN;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6));
- node->typeModifier = stringRef(2);
- node->propertyToken = loc(1);
- node->typeModifierToken = loc(2);
- node->typeToken = loc(4);
- node->identifierToken = loc(6);
- node->semicolonToken = loc(7); // insert a fake ';' before ':'
-
- AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(6));
- propertyName->identifierToken = loc(6);
- propertyName->next = 0;
-
- AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding(
- propertyName, sym(9).UiArrayMemberList->finish());
- binding->colonToken = loc(7);
- binding->lbracketToken = loc(8);
- binding->rbracketToken = loc(10);
-
- node->binding = binding;
+ case $rule_number: {
+ AST::NestedExpression *node = new (pool) AST::NestedExpression(sym(2).Expression);
+ node->lparenToken = loc(1);
+ node->rparenToken = loc(3);
+ sym(1).Node = node;
+ coverExpressionType = CE_ParenthesizedExpression;
+ } break;
+./
- sym(1).Node = node;
-} break;
+CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN T_RPAREN;
+/.
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ coverExpressionErrorLocation = loc(2);
+ coverExpressionType = CE_FormalParameterList;
+ } break;
./
-UiObjectMember: T_PROPERTY UiPropertyType JsIdentifier T_COLON UiQualifiedId UiObjectInitializer ;
+CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN BindingRestElement T_RPAREN;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3));
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(3);
- node->semicolonToken = loc(4); // insert a fake ';' before ':'
+ case $rule_number: {
+ AST::FormalParameterList *node = (new (pool) AST::FormalParameterList(nullptr, sym(2).PatternElement))->finish(pool);
+ sym(1).Node = node;
+ coverExpressionErrorLocation = loc(2);
+ coverExpressionType = CE_FormalParameterList;
+ } break;
+./
- AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(3));
- propertyName->identifierToken = loc(3);
- propertyName->next = 0;
+CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN Expression_In T_COMMA BindingRestElementOpt T_RPAREN;
+/.
+ case $rule_number: {
+ AST::FormalParameterList *list = sym(2).Expression->reparseAsFormalParameterList(pool);
+ if (!list) {
+ syntaxError(loc(1), "Invalid Arrow parameter list.");
+ return false;
+ }
+ if (sym(4).Node) {
+ list = new (pool) AST::FormalParameterList(list, sym(4).PatternElement);
+ }
+ coverExpressionErrorLocation = loc(4);
+ coverExpressionType = CE_FormalParameterList;
+ sym(1).Node = list->finish(pool);
+ } break;
+./
- AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding(
- propertyName, sym(5).UiQualifiedId, sym(6).UiObjectInitializer);
- binding->colonToken = loc(4);
+Literal: T_NULL;
+/.
+ case $rule_number: {
+ AST::NullExpression *node = new (pool) AST::NullExpression();
+ node->nullToken = loc(1);
+ sym(1).Node = node;
+ } break;
+./
- node->binding = binding;
+Literal: T_TRUE;
+/.
+ case $rule_number: {
+ AST::TrueLiteral *node = new (pool) AST::TrueLiteral();
+ node->trueToken = loc(1);
+ sym(1).Node = node;
+ } break;
+./
- sym(1).Node = node;
-} break;
+Literal: T_FALSE;
+/.
+ case $rule_number: {
+ AST::FalseLiteral *node = new (pool) AST::FalseLiteral();
+ node->falseToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-UiObjectMember: T_READONLY T_PROPERTY UiPropertyType JsIdentifier T_COLON UiQualifiedId UiObjectInitializer ;
+Literal: T_NUMERIC_LITERAL;
/.
-case $rule_number: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4));
- node->isReadonlyMember = true;
- node->readonlyToken = loc(1);
- node->propertyToken = loc(2);
- node->typeToken = loc(3);
- node->identifierToken = loc(4);
- node->semicolonToken = loc(5); // insert a fake ';' before ':'
+ case $rule_number: {
+ AST::NumericLiteral *node = new (pool) AST::NumericLiteral(sym(1).dval);
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+ } break;
+./
- AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(4));
- propertyName->identifierToken = loc(4);
- propertyName->next = 0;
+Literal: T_MULTILINE_STRING_LITERAL;
+/. case $rule_number: Q_FALLTHROUGH(); ./
- AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding(
- propertyName, sym(6).UiQualifiedId, sym(7).UiObjectInitializer);
- binding->colonToken = loc(5);
+Literal: T_STRING_LITERAL;
+/.
+ case $rule_number: {
+ AST::StringLiteral *node = new (pool) AST::StringLiteral(stringRef(1));
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+ } break;
+./
+
+RegularExpressionLiteral: T_DIVIDE_;
+/:
+#define J_SCRIPT_REGEXPLITERAL_RULE1 $rule_number
+:/
+/.
+{
+ Lexer::RegExpBodyPrefix prefix;
+ case $rule_number:
+ prefix = Lexer::NoPrefix;
+ goto scan_regexp;
+./
- node->binding = binding;
+RegularExpressionLiteral: T_DIVIDE_EQ;
+/:
+#define J_SCRIPT_REGEXPLITERAL_RULE2 $rule_number
+:/
+/.
+ case $rule_number:
+ prefix = Lexer::EqualPrefix;
+ goto scan_regexp;
- sym(1).Node = node;
-} break;
+ scan_regexp: {
+ bool rx = lexer->scanRegExp(prefix);
+ if (!rx) {
+ diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage()));
+ return false;
+ }
+
+ loc(1).length = lexer->tokenLength();
+ yylloc = loc(1); // adjust the location of the current token
+
+ AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral(driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags());
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+ } break;
+}
./
-UiObjectMember: FunctionDeclaration ;
+
+ArrayLiteral: T_LBRACKET ElisionOpt T_RBRACKET;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node);
-} break;
+ case $rule_number: {
+ AST::PatternElementList *list = nullptr;
+ if (sym(2).Elision)
+ list = (new (pool) AST::PatternElementList(sym(2).Elision, nullptr))->finish();
+ AST::ArrayPattern *node = new (pool) AST::ArrayPattern(list);
+ node->lbracketToken = loc(1);
+ node->rbracketToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-UiObjectMember: VariableStatement ;
+ArrayLiteral: T_LBRACKET ElementList T_RBRACKET;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node);
-} break;
+ case $rule_number: {
+ AST::ArrayPattern *node = new (pool) AST::ArrayPattern(sym(2).PatternElementList->finish());
+ node->lbracketToken = loc(1);
+ node->rbracketToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-UiObjectMember: T_ENUM T_IDENTIFIER T_LBRACE EnumMemberList T_RBRACE;
+ArrayLiteral: T_LBRACKET ElementList T_COMMA ElisionOpt T_RBRACKET;
/.
-case $rule_number: {
- AST::UiEnumDeclaration *enumDeclaration = new (pool) AST::UiEnumDeclaration(stringRef(2), sym(4).UiEnumMemberList->finish());
- enumDeclaration->enumToken = loc(1);
- enumDeclaration->rbraceToken = loc(5);
- sym(1).Node = enumDeclaration;
- break;
-}
+ case $rule_number: {
+ auto *list = sym(2).PatternElementList;
+ if (sym(4).Elision) {
+ AST::PatternElementList *l = new (pool) AST::PatternElementList(sym(4).Elision, nullptr);
+ list = list->append(l);
+ }
+ AST::ArrayPattern *node = new (pool) AST::ArrayPattern(list->finish());
+ node->lbracketToken = loc(1);
+ node->commaToken = loc(3);
+ node->rbracketToken = loc(5);
+ sym(1).Node = node;
+ Q_ASSERT(node->isValidArrayLiteral());
+ } break;
./
-EnumMemberList: T_IDENTIFIER;
+ElementList: AssignmentExpression_In;
/.
-case $rule_number: {
- AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1));
- node->memberToken = loc(1);
- sym(1).Node = node;
- break;
-}
+ case $rule_number: {
+ AST::PatternElement *e = new (pool) AST::PatternElement(sym(1).Expression);
+ sym(1).Node = new (pool) AST::PatternElementList(nullptr, e);
+ } break;
./
-EnumMemberList: T_IDENTIFIER T_EQ T_NUMERIC_LITERAL;
+ElementList: Elision AssignmentExpression_In;
/.
-case $rule_number: {
- AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1), sym(3).dval);
- node->memberToken = loc(1);
- node->valueToken = loc(3);
- sym(1).Node = node;
- break;
-}
+ case $rule_number: {
+ AST::PatternElement *e = new (pool) AST::PatternElement(sym(2).Expression);
+ sym(1).Node = new (pool) AST::PatternElementList(sym(1).Elision->finish(), e);
+ } break;
./
-EnumMemberList: EnumMemberList T_COMMA T_IDENTIFIER;
+ElementList: ElisionOpt SpreadElement;
/.
-case $rule_number: {
- AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3));
- node->memberToken = loc(3);
- sym(1).Node = node;
- break;
-}
+ case $rule_number: {
+ AST::PatternElementList *node = new (pool) AST::PatternElementList(sym(1).Elision, sym(2).PatternElement);
+ sym(1).Node = node;
+ } break;
./
-EnumMemberList: EnumMemberList T_COMMA T_IDENTIFIER T_EQ T_NUMERIC_LITERAL;
+ElementList: ElementList T_COMMA ElisionOpt AssignmentExpression_In;
/.
-case $rule_number: {
- AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3), sym(5).dval);
- node->memberToken = loc(3);
- node->valueToken = loc(5);
- sym(1).Node = node;
- break;
-}
+ case $rule_number: {
+ AST::PatternElement *e = new (pool) AST::PatternElement(sym(4).Expression);
+ AST::PatternElementList *node = new (pool) AST::PatternElementList(sym(3).Elision, e);
+ sym(1).Node = sym(1).PatternElementList->append(node);
+ } break;
./
-JsIdentifier: T_IDENTIFIER;
+ElementList: ElementList T_COMMA ElisionOpt SpreadElement;
+/.
+ case $rule_number: {
+ AST::PatternElementList *node = new (pool) AST::PatternElementList(sym(3).Elision, sym(4).PatternElement);
+ sym(1).Node = sym(1).PatternElementList->append(node);
+ } break;
+./
-JsIdentifier: T_PROPERTY ;
-JsIdentifier: T_SIGNAL ;
-JsIdentifier: T_READONLY ;
-JsIdentifier: T_ON ;
-JsIdentifier: T_GET ;
-JsIdentifier: T_SET ;
+Elision: T_COMMA;
+/.
+ case $rule_number: {
+ AST::Elision *node = new (pool) AST::Elision();
+ node->commaToken = loc(1);
+ sym(1).Node = node;
+ } break;
+./
---------------------------------------------------------------------------------------------------------
--- Expressions
---------------------------------------------------------------------------------------------------------
+Elision: Elision T_COMMA;
+/.
+ case $rule_number: {
+ AST::Elision *node = new (pool) AST::Elision(sym(1).Elision);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
-PrimaryExpression: T_THIS ;
+ElisionOpt: ;
/.
-case $rule_number: {
- AST::ThisExpression *node = new (pool) AST::ThisExpression();
- node->thisToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-PrimaryExpression: JsIdentifier ;
+ElisionOpt: Elision;
/.
-case $rule_number: {
- AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(1).Elision->finish();
+ } break;
./
-PrimaryExpression: T_NULL ;
+SpreadElement: T_ELLIPSIS AssignmentExpression;
/.
-case $rule_number: {
- AST::NullExpression *node = new (pool) AST::NullExpression();
- node->nullToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PatternElement *node = new (pool) AST::PatternElement(sym(2).Expression, AST::PatternElement::SpreadElement);
+ sym(1).Node = node;
+ } break;
./
-PrimaryExpression: T_TRUE ;
+ObjectLiteral: T_LBRACE T_RBRACE;
/.
-case $rule_number: {
- AST::TrueLiteral *node = new (pool) AST::TrueLiteral();
- node->trueToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ObjectPattern *node = new (pool) AST::ObjectPattern();
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-PrimaryExpression: T_FALSE ;
+ObjectLiteral: T_LBRACE PropertyDefinitionList T_RBRACE;
/.
-case $rule_number: {
- AST::FalseLiteral *node = new (pool) AST::FalseLiteral();
- node->falseToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ObjectPattern *node = new (pool) AST::ObjectPattern(sym(2).PatternPropertyList->finish());
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-PrimaryExpression: T_NUMERIC_LITERAL ;
+ObjectLiteral: T_LBRACE PropertyDefinitionList T_COMMA T_RBRACE;
/.
-case $rule_number: {
- AST::NumericLiteral *node = new (pool) AST::NumericLiteral(sym(1).dval);
- node->literalToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ObjectPattern *node = new (pool) AST::ObjectPattern(sym(2).PatternPropertyList->finish());
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-PrimaryExpression: T_MULTILINE_STRING_LITERAL ;
-/.case $rule_number:./
-PrimaryExpression: T_STRING_LITERAL ;
+UiPropertyDefinitionList: UiPropertyDefinition;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+PropertyDefinitionList: PropertyDefinition;
/.
-case $rule_number: {
- AST::StringLiteral *node = new (pool) AST::StringLiteral(stringRef(1));
- node->literalToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::PatternPropertyList(sym(1).PatternProperty);
+ } break;
./
-PrimaryExpression: T_DIVIDE_ ;
-/:
-#define J_SCRIPT_REGEXPLITERAL_RULE1 $rule_number
-:/
+UiPropertyDefinitionList: UiPropertyDefinitionList T_COMMA UiPropertyDefinition;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+PropertyDefinitionList: PropertyDefinitionList T_COMMA PropertyDefinition;
/.
-case $rule_number: {
- bool rx = lexer->scanRegExp(Lexer::NoPrefix);
- if (!rx) {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage()));
- return false; // ### remove me
- }
-
- loc(1).length = lexer->tokenLength();
- yylloc = loc(1); // adjust the location of the current token
-
- AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral(
- driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags());
- node->literalToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PatternPropertyList *node = new (pool) AST::PatternPropertyList(sym(1).PatternPropertyList, sym(3).PatternProperty);
+ sym(1).Node = node;
+ } break;
./
-PrimaryExpression: T_DIVIDE_EQ ;
-/:
-#define J_SCRIPT_REGEXPLITERAL_RULE2 $rule_number
-:/
+PropertyDefinition: IdentifierReference;
/.
-case $rule_number: {
- bool rx = lexer->scanRegExp(Lexer::EqualPrefix);
- if (!rx) {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage()));
- return false;
- }
+ case $rule_number: {
+ AST::IdentifierPropertyName *name = new (pool) AST::IdentifierPropertyName(stringRef(1));
+ name->propertyNameToken = loc(1);
+ AST::IdentifierExpression *expr = new (pool) AST::IdentifierExpression(stringRef(1));
+ expr->identifierToken = loc(1);
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(name, expr);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
- loc(1).length = lexer->tokenLength();
- yylloc = loc(1); // adjust the location of the current token
+-- Using this production should result in a syntax error when used in an ObjectLiteral
+PropertyDefinition: CoverInitializedName;
- AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral(
- driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags());
- node->literalToken = loc(1);
- sym(1).Node = node;
-} break;
+CoverInitializedName: IdentifierReference Initializer_In;
+/.
+ case $rule_number: {
+ AST::IdentifierPropertyName *name = new (pool) AST::IdentifierPropertyName(stringRef(1));
+ name->propertyNameToken = loc(1);
+ AST::IdentifierExpression *left = new (pool) AST::IdentifierExpression(stringRef(1));
+ left->identifierToken = loc(1);
+ // if initializer is an anonymous function expression, we need to assign identifierref as it's name
+ if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression))
+ f->name = stringRef(1);
+ if (auto *c = asAnonymousClassDefinition(sym(2).Expression))
+ c->name = stringRef(1);
+ AST::BinaryExpression *assignment = new (pool) AST::BinaryExpression(left, QSOperator::Assign, sym(2).Expression);
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(name, assignment);
+ node->colonToken = loc(1);
+ sym(1).Node = node;
+
+ } break;
./
-PrimaryExpression: T_LBRACKET T_RBRACKET ;
+UiPropertyDefinition: UiPropertyName T_COLON AssignmentExpression_In;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+PropertyDefinition: PropertyName T_COLON AssignmentExpression_In;
/.
-case $rule_number: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral((AST::Elision *) 0);
- node->lbracketToken = loc(1);
- node->rbracketToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, sym(3).Expression);
+ if (auto *f = asAnonymousFunctionDefinition(sym(3).Expression)) {
+ if (!AST::cast<AST::ComputedPropertyName *>(sym(1).PropertyName))
+ f->name = driver->newStringRef(sym(1).PropertyName->asString());
+ }
+ if (auto *c = asAnonymousClassDefinition(sym(3).Expression)) {
+ if (!AST::cast<AST::ComputedPropertyName *>(sym(1).PropertyName))
+ c->name = driver->newStringRef(sym(1).PropertyName->asString());
+ }
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
+
+PropertyDefinition: MethodDefinition;
+
+PropertyName: LiteralPropertyName;
+PropertyName: ComputedPropertyName;
+
+LiteralPropertyName: IdentifierName;
+/.
+ case $rule_number: {
+ AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1));
+ node->propertyNameToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-PrimaryExpression: T_LBRACKET Elision T_RBRACKET ;
+UiPropertyName: T_STRING_LITERAL;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+LiteralPropertyName: T_STRING_LITERAL;
/.
-case $rule_number: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).Elision->finish());
- node->lbracketToken = loc(1);
- node->rbracketToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::StringLiteralPropertyName *node = new (pool) AST::StringLiteralPropertyName(stringRef(1));
+ node->propertyNameToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-PrimaryExpression: T_LBRACKET ElementList T_RBRACKET ;
+UiPropertyName: T_NUMERIC_LITERAL;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+LiteralPropertyName: T_NUMERIC_LITERAL;
/.
-case $rule_number: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish ());
- node->lbracketToken = loc(1);
- node->rbracketToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::NumericLiteralPropertyName *node = new (pool) AST::NumericLiteralPropertyName(sym(1).dval);
+ node->propertyNameToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-PrimaryExpression: T_LBRACKET ElementList T_COMMA T_RBRACKET ;
+IdentifierName: IdentifierReference;
+IdentifierName: ReservedIdentifier;
+
+ReservedIdentifier: T_BREAK;
+ReservedIdentifier: T_CASE;
+ReservedIdentifier: T_CATCH;
+ReservedIdentifier: T_CONTINUE;
+ReservedIdentifier: T_DEFAULT;
+ReservedIdentifier: T_DELETE;
+ReservedIdentifier: T_DO;
+ReservedIdentifier: T_ELSE;
+ReservedIdentifier: T_ENUM;
+ReservedIdentifier: T_FALSE;
+ReservedIdentifier: T_FINALLY;
+ReservedIdentifier: T_FOR;
+ReservedIdentifier: T_FUNCTION;
+ReservedIdentifier: T_IF;
+ReservedIdentifier: T_IN;
+ReservedIdentifier: T_INSTANCEOF;
+ReservedIdentifier: T_NEW;
+ReservedIdentifier: T_NULL;
+ReservedIdentifier: T_RETURN;
+ReservedIdentifier: T_SWITCH;
+ReservedIdentifier: T_THIS;
+ReservedIdentifier: T_THROW;
+ReservedIdentifier: T_TRUE;
+ReservedIdentifier: T_TRY;
+ReservedIdentifier: T_TYPEOF;
+ReservedIdentifier: T_VAR;
+ReservedIdentifier: T_VOID;
+ReservedIdentifier: T_WHILE;
+ReservedIdentifier: T_CONST;
+ReservedIdentifier: T_LET;
+ReservedIdentifier: T_DEBUGGER;
+ReservedIdentifier: T_RESERVED_WORD;
+ReservedIdentifier: T_SUPER;
+ReservedIdentifier: T_WITH;
+ReservedIdentifier: T_CLASS;
+ReservedIdentifier: T_EXTENDS;
+ReservedIdentifier: T_EXPORT;
+ReservedIdentifier: T_IMPORT;
+
+ComputedPropertyName: T_LBRACKET AssignmentExpression_In T_RBRACKET;
/.
-case $rule_number: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (),
- (AST::Elision *) 0);
- node->lbracketToken = loc(1);
- node->commaToken = loc(3);
- node->rbracketToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ComputedPropertyName *node = new (pool) AST::ComputedPropertyName(sym(2).Expression);
+ node->propertyNameToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-PrimaryExpression: T_LBRACKET ElementList T_COMMA Elision T_RBRACKET ;
+Initializer: T_EQ AssignmentExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Initializer_In: T_EQ AssignmentExpression_In;
/.
case $rule_number: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (),
- sym(4).Elision->finish());
- node->lbracketToken = loc(1);
- node->commaToken = loc(3);
- node->rbracketToken = loc(5);
- sym(1).Node = node;
+ sym(1) = sym(2);
} break;
./
--- PrimaryExpression: T_LBRACE T_RBRACE ;
--- /.
--- case $rule_number: {
--- sym(1).Node = new (pool) AST::ObjectLiteral();
--- } break;
--- ./
-PrimaryExpression: T_LBRACE PropertyAssignmentListOpt T_RBRACE ;
+InitializerOpt: ;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+InitializerOpt_In: ;
/.
-case $rule_number: {
- AST::ObjectLiteral *node = 0;
- if (sym(2).Node)
- node = new (pool) AST::ObjectLiteral(
- sym(2).PropertyAssignmentList->finish ());
- else
- node = new (pool) AST::ObjectLiteral();
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-PrimaryExpression: T_LBRACE PropertyAssignmentList T_COMMA T_RBRACE ;
+InitializerOpt: Initializer;
+InitializerOpt_In: Initializer_In;
+
+TemplateLiteral: T_NO_SUBSTITUTION_TEMPLATE;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+
+TemplateSpans: T_TEMPLATE_TAIL;
/.
-case $rule_number: {
- AST::ObjectLiteral *node = new (pool) AST::ObjectLiteral(
- sym(2).PropertyAssignmentList->finish ());
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::TemplateLiteral *node = new (pool) AST::TemplateLiteral(stringRef(1), nullptr);
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-PrimaryExpression: T_LPAREN Expression T_RPAREN ;
+TemplateSpans: T_TEMPLATE_MIDDLE Expression TemplateSpans;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+
+TemplateLiteral: T_TEMPLATE_HEAD Expression TemplateSpans;
/.
-case $rule_number: {
- AST::NestedExpression *node = new (pool) AST::NestedExpression(sym(2).Expression);
- node->lparenToken = loc(1);
- node->rparenToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::TemplateLiteral *node = new (pool) AST::TemplateLiteral(stringRef(1), sym(2).Expression);
+ node->next = sym(3).Template;
+ node->literalToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-UiQualifiedId: MemberExpression ;
+MemberExpression: PrimaryExpression;
+
+Super: T_SUPER;
/.
-case $rule_number: {
- if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken,
- QLatin1String("Ignored annotation")));
+ case $rule_number: {
+ AST::SuperLiteral *node = new (pool) AST::SuperLiteral();
+ node->superToken = loc(1);
+ sym(1).Node = node;
+ } break;
+./
- sym(1).Expression = mem->base;
- }
- if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) {
- sym(1).UiQualifiedId = qualifiedId;
- } else {
- sym(1).UiQualifiedId = 0;
+MemberExpression: Super T_LBRACKET Expression_In T_RBRACKET;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+MemberExpression: MemberExpression T_LBRACKET Expression_In T_RBRACKET;
+/.
+ case $rule_number: {
+ AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression);
+ node->lbracketToken = loc(2);
+ node->rbracketToken = loc(4);
+ sym(1).Node = node;
+ } break;
+./
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
- QLatin1String("Expected a qualified name id")));
- return false; // ### recover
- }
-} break;
+-- the identifier has to be "target", catched at codegen time
+NewTarget: T_NEW T_DOT T_IDENTIFIER;
+/. case $rule_number:
+ {
+ AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1));
+ node->identifierToken= loc(1);
+ sym(1).Node = node;
+ } Q_FALLTHROUGH();
+./
+MemberExpression: Super T_DOT IdentifierName;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+MemberExpression: MemberExpression T_DOT IdentifierName;
+/.
+ case $rule_number: {
+ AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3));
+ node->dotToken = loc(2);
+ node->identifierToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-ElementList: AssignmentExpression ;
+MemberExpression: MetaProperty;
+
+MemberExpression: T_NEW MemberExpression T_LPAREN Arguments T_RPAREN;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::ElementList((AST::Elision *) 0, sym(1).Expression);
-} break;
+ case $rule_number: {
+ AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList);
+ node->newToken = loc(1);
+ node->lparenToken = loc(3);
+ node->rparenToken = loc(5);
+ sym(1).Node = node;
+ } break;
./
-ElementList: Elision AssignmentExpression ;
+MetaProperty: NewTarget;
+
+
+NewExpression: MemberExpression;
+
+NewExpression: T_NEW NewExpression;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::ElementList(sym(1).Elision->finish(), sym(2).Expression);
-} break;
+ case $rule_number: {
+ AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression);
+ node->newToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-ElementList: ElementList T_COMMA AssignmentExpression ;
+
+CallExpression: CallExpression TemplateLiteral;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+MemberExpression: MemberExpression TemplateLiteral;
/.
-case $rule_number: {
- AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList,
- (AST::Elision *) 0, sym(3).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::TaggedTemplate *node = new (pool) AST::TaggedTemplate(sym(1).Expression, sym(2).Template);
+ sym(1).Node = node;
+ } break;
./
-ElementList: ElementList T_COMMA Elision AssignmentExpression ;
+CallExpression: MemberExpression T_LPAREN Arguments T_RPAREN;
/.
-case $rule_number: {
- AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, sym(3).Elision->finish(),
- sym(4).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-Elision: T_COMMA ;
+CallExpression: Super T_LPAREN Arguments T_RPAREN;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+CallExpression: CallExpression T_LPAREN Arguments T_RPAREN;
/.
-case $rule_number: {
- AST::Elision *node = new (pool) AST::Elision();
- node->commaToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-Elision: Elision T_COMMA ;
+CallExpression: CallExpression T_LBRACKET Expression_In T_RBRACKET;
/.
-case $rule_number: {
- AST::Elision *node = new (pool) AST::Elision(sym(1).Elision);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression);
+ node->lbracketToken = loc(2);
+ node->rbracketToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-PropertyAssignment: PropertyName T_COLON AssignmentExpression ;
+CallExpression: CallExpression T_DOT IdentifierName;
/.
-case $rule_number: {
- AST::PropertyNameAndValue *node = new (pool) AST::PropertyNameAndValue(
- sym(1).PropertyName, sym(3).Expression);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3));
+ node->dotToken = loc(2);
+ node->identifierToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-PropertyAssignment: T_GET PropertyName T_LPAREN T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ;
+Arguments: ;
/.
-case $rule_number: {
- AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter(
- sym(2).PropertyName, sym(6).FunctionBody);
- node->getSetToken = loc(1);
- node->lparenToken = loc(3);
- node->rparenToken = loc(4);
- node->lbraceToken = loc(5);
- node->rbraceToken = loc(7);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-PropertyAssignment: T_SET PropertyName T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ;
+Arguments: ArgumentList;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Arguments: ArgumentList T_COMMA;
/.
-case $rule_number: {
- AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter(
- sym(2).PropertyName, sym(4).FormalParameterList, sym(7).FunctionBody);
- node->getSetToken = loc(1);
- node->lparenToken = loc(3);
- node->rparenToken = loc(5);
- node->lbraceToken = loc(6);
- node->rbraceToken = loc(8);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(1).ArgumentList->finish();
+ } break;
./
-PropertyAssignmentList: PropertyAssignment ;
+ArgumentList: AssignmentExpression_In;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::PropertyAssignmentList(sym(1).PropertyAssignment);
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression);
+ } break;
./
-PropertyAssignmentList: PropertyAssignmentList T_COMMA PropertyAssignment ;
+ArgumentList: T_ELLIPSIS AssignmentExpression_In;
/.
-case $rule_number: {
- AST::PropertyAssignmentList *node = new (pool) AST::PropertyAssignmentList(
- sym(1).PropertyAssignmentList, sym(3).PropertyAssignment);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(2).Expression);
+ node->isSpreadElement = true;
+ sym(1).Node = node;
+ } break;
./
-PropertyName: JsIdentifier %prec SHIFT_THERE ;
+ArgumentList: ArgumentList T_COMMA AssignmentExpression_In;
/.
-case $rule_number: {
- AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1));
- node->propertyNameToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-PropertyName: T_STRING_LITERAL ;
+ArgumentList: ArgumentList T_COMMA T_ELLIPSIS AssignmentExpression_In;
/.
-case $rule_number: {
- AST::StringLiteralPropertyName *node = new (pool) AST::StringLiteralPropertyName(stringRef(1));
- node->propertyNameToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(4).Expression);
+ node->commaToken = loc(2);
+ node->isSpreadElement = true;
+ sym(1).Node = node;
+ } break;
./
-PropertyName: T_NUMERIC_LITERAL ;
+LeftHandSideExpression: NewExpression;
+LeftHandSideExpression: CallExpression;
+
+UpdateExpression: LeftHandSideExpression;
+
+UpdateExpression: LeftHandSideExpression T_PLUS_PLUS;
/.
-case $rule_number: {
- AST::NumericLiteralPropertyName *node = new (pool) AST::NumericLiteralPropertyName(sym(1).dval);
- node->propertyNameToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression);
+ node->incrementToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-PropertyName: ReservedIdentifier ;
+UpdateExpression: LeftHandSideExpression T_MINUS_MINUS;
/.
-case $rule_number: {
- AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1));
- node->propertyNameToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression);
+ node->decrementToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-ReservedIdentifier: T_BREAK ;
-ReservedIdentifier: T_CASE ;
-ReservedIdentifier: T_CATCH ;
-ReservedIdentifier: T_CONTINUE ;
-ReservedIdentifier: T_DEFAULT ;
-ReservedIdentifier: T_DELETE ;
-ReservedIdentifier: T_DO ;
-ReservedIdentifier: T_ELSE ;
-ReservedIdentifier: T_ENUM ;
-ReservedIdentifier: T_FALSE ;
-ReservedIdentifier: T_FINALLY ;
-ReservedIdentifier: T_FOR ;
-ReservedIdentifier: T_FUNCTION ;
-ReservedIdentifier: T_IF ;
-ReservedIdentifier: T_IN ;
-ReservedIdentifier: T_INSTANCEOF ;
-ReservedIdentifier: T_NEW ;
-ReservedIdentifier: T_NULL ;
-ReservedIdentifier: T_RETURN ;
-ReservedIdentifier: T_SWITCH ;
-ReservedIdentifier: T_THIS ;
-ReservedIdentifier: T_THROW ;
-ReservedIdentifier: T_TRUE ;
-ReservedIdentifier: T_TRY ;
-ReservedIdentifier: T_TYPEOF ;
-ReservedIdentifier: T_VAR ;
-ReservedIdentifier: T_VOID ;
-ReservedIdentifier: T_WHILE ;
-ReservedIdentifier: T_CONST ;
-ReservedIdentifier: T_LET ;
-ReservedIdentifier: T_DEBUGGER ;
-ReservedIdentifier: T_RESERVED_WORD ;
-ReservedIdentifier: T_WITH ;
-
-PropertyIdentifier: JsIdentifier ;
-PropertyIdentifier: ReservedIdentifier ;
-
-MemberExpression: PrimaryExpression ;
-MemberExpression: FunctionExpression ;
-
-MemberExpression: MemberExpression T_LBRACKET Expression T_RBRACKET ;
+UpdateExpression: T_PLUS_PLUS UnaryExpression;
/.
-case $rule_number: {
- AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression);
- node->lbracketToken = loc(2);
- node->rbracketToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression);
+ node->incrementToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-MemberExpression: MemberExpression T_DOT PropertyIdentifier ;
+UpdateExpression: T_MINUS_MINUS UnaryExpression;
/.
-case $rule_number: {
- AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3));
- node->dotToken = loc(2);
- node->identifierToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression);
+ node->decrementToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-MemberExpression: T_NEW MemberExpression T_LPAREN ArgumentListOpt T_RPAREN ;
+UnaryExpression: UpdateExpression;
+
+UnaryExpression: T_DELETE UnaryExpression;
/.
-case $rule_number: {
- AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList);
- node->newToken = loc(1);
- node->lparenToken = loc(3);
- node->rparenToken = loc(5);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression);
+ node->deleteToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-NewExpression: MemberExpression ;
+UnaryExpression: T_VOID UnaryExpression;
+/.
+ case $rule_number: {
+ AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression);
+ node->voidToken = loc(1);
+ sym(1).Node = node;
+ } break;
+./
-NewExpression: T_NEW NewExpression ;
+UnaryExpression: T_TYPEOF UnaryExpression;
/.
-case $rule_number: {
- AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression);
- node->newToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression);
+ node->typeofToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-CallExpression: MemberExpression T_LPAREN ArgumentListOpt T_RPAREN ;
+UnaryExpression: T_PLUS UnaryExpression;
/.
-case $rule_number: {
- AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression);
+ node->plusToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-CallExpression: CallExpression T_LPAREN ArgumentListOpt T_RPAREN ;
+UnaryExpression: T_MINUS UnaryExpression;
/.
-case $rule_number: {
- AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression);
+ node->minusToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-CallExpression: CallExpression T_LBRACKET Expression T_RBRACKET ;
+UnaryExpression: T_TILDE UnaryExpression;
/.
-case $rule_number: {
- AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression);
- node->lbracketToken = loc(2);
- node->rbracketToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression);
+ node->tildeToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-CallExpression: CallExpression T_DOT PropertyIdentifier ;
+UnaryExpression: T_NOT UnaryExpression;
/.
-case $rule_number: {
- AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3));
- node->dotToken = loc(2);
- node->identifierToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression);
+ node->notToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-ArgumentListOpt: ;
+ExponentiationExpression: UnaryExpression;
+
+ExponentiationExpression: UpdateExpression T_STAR_STAR ExponentiationExpression;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Exp, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-ArgumentListOpt: ArgumentList ;
+MultiplicativeExpression: ExponentiationExpression;
+
+MultiplicativeExpression: MultiplicativeExpression MultiplicativeOperator ExponentiationExpression;
/.
-case $rule_number: {
- sym(1).Node = sym(1).ArgumentList->finish();
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-ArgumentList: AssignmentExpression ;
+MultiplicativeOperator: T_STAR;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression);
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::Mul;
+ } break;
./
-ArgumentList: ArgumentList T_COMMA AssignmentExpression ;
+MultiplicativeOperator: T_DIVIDE_;
/.
-case $rule_number: {
- AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::Div;
+ } break;
+./
+
+MultiplicativeOperator: T_REMAINDER;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::Mod;
+ } break;
./
-LeftHandSideExpression: NewExpression ;
-LeftHandSideExpression: CallExpression ;
-PostfixExpression: LeftHandSideExpression ;
+AdditiveExpression: MultiplicativeExpression;
-PostfixExpression: LeftHandSideExpression T_PLUS_PLUS ;
+AdditiveExpression: AdditiveExpression T_PLUS MultiplicativeExpression;
/.
-case $rule_number: {
- AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression);
- node->incrementToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Add, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-PostfixExpression: LeftHandSideExpression T_MINUS_MINUS ;
+AdditiveExpression: AdditiveExpression T_MINUS MultiplicativeExpression;
/.
-case $rule_number: {
- AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression);
- node->decrementToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Sub, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-UnaryExpression: PostfixExpression ;
+ShiftExpression: AdditiveExpression;
-UnaryExpression: T_DELETE UnaryExpression ;
+ShiftExpression: ShiftExpression T_LT_LT AdditiveExpression;
/.
-case $rule_number: {
- AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression);
- node->deleteToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::LShift, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-UnaryExpression: T_VOID UnaryExpression ;
+ShiftExpression: ShiftExpression T_GT_GT AdditiveExpression;
/.
-case $rule_number: {
- AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression);
- node->voidToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::RShift, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-UnaryExpression: T_TYPEOF UnaryExpression ;
+ShiftExpression: ShiftExpression T_GT_GT_GT AdditiveExpression;
/.
-case $rule_number: {
- AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression);
- node->typeofToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::URShift, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-UnaryExpression: T_PLUS_PLUS UnaryExpression ;
+RelationalExpression_In: ShiftExpression;
+RelationalExpression: ShiftExpression;
+
+RelationalExpression_In: RelationalExpression_In RelationalOperator ShiftExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+RelationalExpression: RelationalExpression RelationalOperator ShiftExpression;
/.
-case $rule_number: {
- AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression);
- node->incrementToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-UnaryExpression: T_MINUS_MINUS UnaryExpression ;
+RelationalOperator: T_LT;
/.
-case $rule_number: {
- AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression);
- node->decrementToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::Lt;
+ } break;
+./
+RelationalOperator: T_GT;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::Gt;
+ } break;
+./
+RelationalOperator: T_LE;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::Le;
+ } break;
+./
+RelationalOperator: T_GE;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::Ge;
+ } break;
+./
+RelationalOperator: T_INSTANCEOF;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::InstanceOf;
+ } break;
./
-UnaryExpression: T_PLUS UnaryExpression ;
+RelationalExpression_In: RelationalExpression_In T_IN ShiftExpression;
/.
-case $rule_number: {
- AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression);
- node->plusToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::In, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-UnaryExpression: T_MINUS UnaryExpression ;
+EqualityExpression_In: RelationalExpression_In;
+EqualityExpression: RelationalExpression;
+
+EqualityExpression_In: EqualityExpression_In EqualityOperator RelationalExpression_In;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+EqualityExpression: EqualityExpression EqualityOperator RelationalExpression;
/.
-case $rule_number: {
- AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression);
- node->minusToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-UnaryExpression: T_TILDE UnaryExpression ;
+EqualityOperator: T_EQ_EQ;
/.
-case $rule_number: {
- AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression);
- node->tildeToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::Equal;
+ } break;
+./
+EqualityOperator: T_NOT_EQ;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::NotEqual;
+ } break;
+./
+EqualityOperator: T_EQ_EQ_EQ;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::StrictEqual;
+ } break;
+./
+EqualityOperator: T_NOT_EQ_EQ;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::StrictNotEqual;
+ } break;
./
-UnaryExpression: T_NOT UnaryExpression ;
+
+BitwiseANDExpression: EqualityExpression;
+BitwiseANDExpression_In: EqualityExpression_In;
+
+BitwiseANDExpression: BitwiseANDExpression T_AND EqualityExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+BitwiseANDExpression_In: BitwiseANDExpression_In T_AND EqualityExpression_In;
/.
-case $rule_number: {
- AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression);
- node->notToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitAnd, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-MultiplicativeExpression: UnaryExpression ;
-MultiplicativeExpression: MultiplicativeExpression T_STAR UnaryExpression ;
+BitwiseXORExpression: BitwiseANDExpression;
+BitwiseXORExpression_In: BitwiseANDExpression_In;
+
+BitwiseXORExpression: BitwiseXORExpression T_XOR BitwiseANDExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+BitwiseXORExpression_In: BitwiseXORExpression_In T_XOR BitwiseANDExpression_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Mul, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitXor, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-MultiplicativeExpression: MultiplicativeExpression T_DIVIDE_ UnaryExpression ;
+BitwiseORExpression: BitwiseXORExpression;
+BitwiseORExpression_In: BitwiseXORExpression_In;
+
+BitwiseORExpression: BitwiseORExpression T_OR BitwiseXORExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+BitwiseORExpression_In: BitwiseORExpression_In T_OR BitwiseXORExpression_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Div, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitOr, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-MultiplicativeExpression: MultiplicativeExpression T_REMAINDER UnaryExpression ;
+LogicalANDExpression: BitwiseORExpression;
+LogicalANDExpression_In: BitwiseORExpression_In;
+
+LogicalANDExpression: LogicalANDExpression T_AND_AND BitwiseORExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+LogicalANDExpression_In: LogicalANDExpression_In T_AND_AND BitwiseORExpression_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Mod, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::And, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-AdditiveExpression: MultiplicativeExpression ;
+LogicalORExpression: LogicalANDExpression;
+LogicalORExpression_In: LogicalANDExpression_In;
-AdditiveExpression: AdditiveExpression T_PLUS MultiplicativeExpression ;
+LogicalORExpression: LogicalORExpression T_OR_OR LogicalANDExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+LogicalORExpression_In: LogicalORExpression_In T_OR_OR LogicalANDExpression_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Add, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Or, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-AdditiveExpression: AdditiveExpression T_MINUS MultiplicativeExpression ;
+
+ConditionalExpression: LogicalORExpression;
+ConditionalExpression_In: LogicalORExpression_In;
+
+ConditionalExpression: LogicalORExpression T_QUESTION AssignmentExpression_In T_COLON AssignmentExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+ConditionalExpression_In: LogicalORExpression_In T_QUESTION AssignmentExpression_In T_COLON AssignmentExpression_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Sub, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, sym(3).Expression, sym(5).Expression);
+ node->questionToken = loc(2);
+ node->colonToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-ShiftExpression: AdditiveExpression ;
+AssignmentExpression: ConditionalExpression;
+AssignmentExpression_In: ConditionalExpression_In;
+
+AssignmentExpression: YieldExpression;
+AssignmentExpression_In: YieldExpression_In;
-ShiftExpression: ShiftExpression T_LT_LT AdditiveExpression ;
+AssignmentExpression: ArrowFunction;
+AssignmentExpression_In: ArrowFunction_In;
+
+AssignmentExpression: LeftHandSideExpression T_EQ AssignmentExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+AssignmentExpression_In: LeftHandSideExpression T_EQ AssignmentExpression_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::LShift, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ // need to convert the LHS to an AssignmentPattern if it was an Array/ObjectLiteral
+ if (AST::Pattern *p = sym(1).Expression->patternCast()) {
+ AST::SourceLocation errorLoc;
+ QString errorMsg;
+ if (!p->convertLiteralToAssignmentPattern(pool, &errorLoc, &errorMsg)) {
+ syntaxError(errorLoc, errorMsg);
+ return false;
+ }
+ }
+ // if lhs is an identifier expression and rhs is an anonymous function expression, we need to assign the name of lhs to the function
+ if (auto *f = asAnonymousFunctionDefinition(sym(3).Expression)) {
+ if (auto *id = AST::cast<AST::IdentifierExpression *>(sym(1).Expression))
+ f->name = id->name;
+ }
+ if (auto *c = asAnonymousClassDefinition(sym(3).Expression)) {
+ if (auto *id = AST::cast<AST::IdentifierExpression *>(sym(1).Expression))
+ c->name = id->name;
+ }
+
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Assign, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-ShiftExpression: ShiftExpression T_GT_GT AdditiveExpression ;
+AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+AssignmentExpression_In: LeftHandSideExpression AssignmentOperator AssignmentExpression_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::RShift, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression);
+ node->operatorToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-ShiftExpression: ShiftExpression T_GT_GT_GT AdditiveExpression ;
+AssignmentOperator: T_STAR_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::URShift, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceMul;
+ } break;
./
-RelationalExpression: ShiftExpression ;
+AssignmentOperator: T_STAR_STAR_EQ;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceExp;
+ } break;
+./
-RelationalExpression: RelationalExpression T_LT ShiftExpression ;
+AssignmentOperator: T_DIVIDE_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Lt, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceDiv;
+ } break;
./
-RelationalExpression: RelationalExpression T_GT ShiftExpression ;
+AssignmentOperator: T_REMAINDER_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Gt, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceMod;
+ } break;
./
-RelationalExpression: RelationalExpression T_LE ShiftExpression ;
+AssignmentOperator: T_PLUS_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Le, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceAdd;
+ } break;
./
-RelationalExpression: RelationalExpression T_GE ShiftExpression ;
+AssignmentOperator: T_MINUS_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Ge, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceSub;
+ } break;
./
-RelationalExpression: RelationalExpression T_INSTANCEOF ShiftExpression ;
+AssignmentOperator: T_LT_LT_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::InstanceOf, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceLeftShift;
+ } break;
./
-RelationalExpression: RelationalExpression T_IN ShiftExpression ;
+AssignmentOperator: T_GT_GT_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::In, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceRightShift;
+ } break;
./
-RelationalExpressionNotIn: ShiftExpression ;
+AssignmentOperator: T_GT_GT_GT_EQ;
+/.
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceURightShift;
+ } break;
+./
-RelationalExpressionNotIn: RelationalExpressionNotIn T_LT ShiftExpression ;
+AssignmentOperator: T_AND_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Lt, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceAnd;
+ } break;
./
-RelationalExpressionNotIn: RelationalExpressionNotIn T_GT ShiftExpression ;
+AssignmentOperator: T_XOR_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Gt, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceXor;
+ } break;
./
-RelationalExpressionNotIn: RelationalExpressionNotIn T_LE ShiftExpression ;
+AssignmentOperator: T_OR_EQ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Le, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).ival = QSOperator::InplaceOr;
+ } break;
./
-RelationalExpressionNotIn: RelationalExpressionNotIn T_GE ShiftExpression ;
+Expression: AssignmentExpression;
+Expression_In: AssignmentExpression_In;
+
+Expression: Expression T_COMMA AssignmentExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Expression_In: Expression_In T_COMMA AssignmentExpression_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Ge, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-RelationalExpressionNotIn: RelationalExpressionNotIn T_INSTANCEOF ShiftExpression ;
+ExpressionOpt: ;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+ExpressionOpt_In: ;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::InstanceOf, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-EqualityExpression: RelationalExpression ;
+ExpressionOpt: Expression;
+ExpressionOpt_In: Expression_In;
-EqualityExpression: EqualityExpression T_EQ_EQ RelationalExpression ;
+-- Statements
+
+Statement: ExpressionStatementLookahead T_FORCE_BLOCK BlockStatement;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Equal, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(3).Node;
+ } break;
./
-EqualityExpression: EqualityExpression T_NOT_EQ RelationalExpression ;
+Statement: ExpressionStatementLookahead VariableStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead EmptyStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead ExpressionStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead IfStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead BreakableStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead ContinueStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead BreakStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead ReturnStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead WithStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead LabelledStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead ThrowStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead TryStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+Statement: ExpressionStatementLookahead DebuggerStatement;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::NotEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ } break;
./
-EqualityExpression: EqualityExpression T_EQ_EQ_EQ RelationalExpression ;
+Declaration: HoistableDeclaration;
+Declaration: ClassDeclaration;
+Declaration: LexicalDeclaration_In;
+
+HoistableDeclaration: FunctionDeclaration;
+HoistableDeclaration: GeneratorDeclaration;
+
+HoistableDeclaration_Default: FunctionDeclaration_Default;
+HoistableDeclaration_Default: GeneratorDeclaration_Default;
+
+BreakableStatement: IterationStatement;
+BreakableStatement: SwitchStatement;
+
+BlockStatement: Block;
+
+Block: T_LBRACE StatementListOpt T_RBRACE;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::StrictEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::Block *node = new (pool) AST::Block(sym(2).StatementList);
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-EqualityExpression: EqualityExpression T_NOT_EQ_EQ RelationalExpression ;
+StatementList: StatementListItem;
+
+StatementList: StatementList StatementListItem;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::StrictNotEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).StatementList = sym(1).StatementList->append(sym(2).StatementList);
+ } break;
./
-EqualityExpressionNotIn: RelationalExpressionNotIn ;
+StatementListItem: Statement;
+/.
+ case $rule_number: {
+ sym(1).StatementList = new (pool) AST::StatementList(sym(1).Statement);
+ } break;
+./
-EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ RelationalExpressionNotIn ;
+StatementListItem: ExpressionStatementLookahead T_FORCE_DECLARATION Declaration T_AUTOMATIC_SEMICOLON;
+StatementListItem: ExpressionStatementLookahead T_FORCE_DECLARATION Declaration T_SEMICOLON;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Equal, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::StatementList(sym(3).FunctionDeclaration);
+ } break;
./
-EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ RelationalExpressionNotIn;
+StatementListOpt: ExpressionStatementLookahead;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::NotEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-EqualityExpressionNotIn: EqualityExpressionNotIn T_EQ_EQ_EQ RelationalExpressionNotIn ;
+StatementListOpt: StatementList;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::StrictEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(1).StatementList->finish();
+ } break;
./
-EqualityExpressionNotIn: EqualityExpressionNotIn T_NOT_EQ_EQ RelationalExpressionNotIn ;
+LetOrConst: T_LET;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::StrictNotEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).scope = AST::VariableScope::Let;
+ } break;
+./
+LetOrConst: T_CONST;
+/.
+ case $rule_number: {
+ sym(1).scope = AST::VariableScope::Const;
+ } break;
./
-BitwiseANDExpression: EqualityExpression ;
+Var: T_VAR;
+/.
+ case $rule_number: {
+ sym(1).scope = AST::VariableScope::Var;
+ } break;
+./
-BitwiseANDExpression: BitwiseANDExpression T_AND EqualityExpression ;
+LexicalDeclaration: LetOrConst BindingList;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+LexicalDeclaration_In: LetOrConst BindingList_In;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VarDeclaration: Var VariableDeclarationList;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VarDeclaration_In: Var VariableDeclarationList_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitAnd, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::VariableStatement *node = new (pool) AST::VariableStatement(sym(2).VariableDeclarationList->finish(sym(1).scope));
+ node->declarationKindToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-BitwiseANDExpressionNotIn: EqualityExpressionNotIn ;
+VariableStatement: VarDeclaration_In T_AUTOMATIC_SEMICOLON;
+VariableStatement: VarDeclaration_In T_SEMICOLON;
-BitwiseANDExpressionNotIn: BitwiseANDExpressionNotIn T_AND EqualityExpressionNotIn ;
+BindingList: LexicalBinding_In;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+BindingList_In: LexicalBinding_In;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VariableDeclarationList: VariableDeclaration;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VariableDeclarationList_In: VariableDeclaration_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitAnd, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).PatternElement);
+ } break;
./
-BitwiseXORExpression: BitwiseANDExpression ;
-
-BitwiseXORExpression: BitwiseXORExpression T_XOR BitwiseANDExpression ;
+BindingList: BindingList T_COMMA LexicalBinding;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+BindingList_In: BindingList_In T_COMMA LexicalBinding_In;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VariableDeclarationList: VariableDeclarationList T_COMMA VariableDeclaration;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VariableDeclarationList_In: VariableDeclarationList_In T_COMMA VariableDeclaration_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitXor, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).PatternElement);
+ node->commaToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-BitwiseXORExpressionNotIn: BitwiseANDExpressionNotIn ;
+LexicalBinding: BindingIdentifier InitializerOpt;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+LexicalBinding_In: BindingIdentifier InitializerOpt_In;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VariableDeclaration: BindingIdentifier InitializerOpt;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VariableDeclaration_In: BindingIdentifier InitializerOpt_In;
+/.
+ case $rule_number: {
+ auto *node = new (pool) AST::PatternElement(stringRef(1), sym(2).Expression);
+ node->identifierToken = loc(1);
+ sym(1).Node = node;
+ // if initializer is an anonymous function expression, we need to assign identifierref as it's name
+ if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression))
+ f->name = stringRef(1);
+ if (auto *c = asAnonymousClassDefinition(sym(2).Expression))
+ c->name = stringRef(1);
+ } break;
+./
-BitwiseXORExpressionNotIn: BitwiseXORExpressionNotIn T_XOR BitwiseANDExpressionNotIn ;
+LexicalBinding: BindingPattern Initializer;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+LexicalBinding_In: BindingPattern Initializer_In;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VariableDeclaration: BindingPattern Initializer;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+VariableDeclaration_In: BindingPattern Initializer_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitXor, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ auto *node = new (pool) AST::PatternElement(sym(1).Pattern, sym(2).Expression);
+ node->identifierToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-BitwiseORExpression: BitwiseXORExpression ;
+BindingPattern: T_LBRACE ObjectBindingPattern T_RBRACE;
+/.
+ case $rule_number: {
+ auto *node = new (pool) AST::ObjectPattern(sym(2).PatternPropertyList);
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(3);
+ node->parseMode = AST::Pattern::Binding;
+ sym(1).Node = node;
+ } break;
+./
-BitwiseORExpression: BitwiseORExpression T_OR BitwiseXORExpression ;
+BindingPattern: T_LBRACKET ArrayBindingPattern T_RBRACKET;
+/.
+ case $rule_number: {
+ auto *node = new (pool) AST::ArrayPattern(sym(2).PatternElementList);
+ node->lbracketToken = loc(1);
+ node->rbracketToken = loc(3);
+ node->parseMode = AST::Pattern::Binding;
+ sym(1).Node = node;
+ } break;
+./
+
+ObjectBindingPattern: ;
+/.
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
+./
+
+ObjectBindingPattern: BindingPropertyList;
+/. case $rule_number: ./
+ObjectBindingPattern: BindingPropertyList T_COMMA;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitOr, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(1).PatternPropertyList->finish();
+ } break;
+./
+
+ArrayBindingPattern: ElisionOpt BindingRestElementOpt;
+/.
+ case $rule_number: {
+ if (sym(1).Elision || sym(2).Node) {
+ auto *l = new (pool) AST::PatternElementList(sym(1).Elision, sym(2).PatternElement);
+ sym(1).Node = l->finish();
+ } else {
+ sym(1).Node = nullptr;
+ }
+ } break;
./
-BitwiseORExpressionNotIn: BitwiseXORExpressionNotIn ;
+ArrayBindingPattern: BindingElementList;
+/.
+ case $rule_number: {
+ sym(1).Node = sym(1).PatternElementList->finish();
+ } break;
+./
-BitwiseORExpressionNotIn: BitwiseORExpressionNotIn T_OR BitwiseXORExpressionNotIn ;
+ArrayBindingPattern: BindingElementList T_COMMA ElisionOpt BindingRestElementOpt;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitOr, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ if (sym(3).Elision || sym(4).Node) {
+ auto *l = new (pool) AST::PatternElementList(sym(3).Elision, sym(4).PatternElement);
+ l = sym(1).PatternElementList->append(l);
+ sym(1).Node = l;
+ }
+ sym(1).Node = sym(1).PatternElementList->finish();
+ } break;
./
-LogicalANDExpression: BitwiseORExpression ;
+BindingPropertyList: BindingProperty;
+/.
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::PatternPropertyList(sym(1).PatternProperty);
+ } break;
+./
-LogicalANDExpression: LogicalANDExpression T_AND_AND BitwiseORExpression ;
+BindingPropertyList: BindingPropertyList T_COMMA BindingProperty;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::And, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::PatternPropertyList(sym(1).PatternPropertyList, sym(3).PatternProperty);
+ } break;
./
-LogicalANDExpressionNotIn: BitwiseORExpressionNotIn ;
+BindingElementList: BindingElisionElement;
-LogicalANDExpressionNotIn: LogicalANDExpressionNotIn T_AND_AND BitwiseORExpressionNotIn ;
+BindingElementList: BindingElementList T_COMMA BindingElisionElement;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::And, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).PatternElementList = sym(1).PatternElementList->append(sym(3).PatternElementList);
+ } break;
./
-LogicalORExpression: LogicalANDExpression ;
-
-LogicalORExpression: LogicalORExpression T_OR_OR LogicalANDExpression ;
+BindingElisionElement: ElisionOpt BindingElement;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Or, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::PatternElementList(sym(1).Elision, sym(2).PatternElement);
+ } break;
./
-LogicalORExpressionNotIn: LogicalANDExpressionNotIn ;
-LogicalORExpressionNotIn: LogicalORExpressionNotIn T_OR_OR LogicalANDExpressionNotIn ;
+BindingProperty: BindingIdentifier InitializerOpt_In;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Or, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::StringLiteralPropertyName *name = new (pool) AST::StringLiteralPropertyName(stringRef(1));
+ name->propertyNameToken = loc(1);
+ // if initializer is an anonymous function expression, we need to assign identifierref as it's name
+ if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression))
+ f->name = stringRef(1);
+ if (auto *c = asAnonymousClassDefinition(sym(2).Expression))
+ c->name = stringRef(1);
+ sym(1).Node = new (pool) AST::PatternProperty(name, stringRef(1), sym(2).Expression);
+ } break;
./
-ConditionalExpression: LogicalORExpression ;
+BindingProperty: PropertyName T_COLON BindingIdentifier InitializerOpt_In;
+/.
+ case $rule_number: {
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, stringRef(3), sym(4).Expression);
+ sym(1).Node = node;
+ } break;
+./
-ConditionalExpression: LogicalORExpression T_QUESTION AssignmentExpression T_COLON AssignmentExpression ;
+BindingProperty: PropertyName T_COLON BindingPattern InitializerOpt_In;
/.
-case $rule_number: {
- AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression,
- sym(3).Expression, sym(5).Expression);
- node->questionToken = loc(2);
- node->colonToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, sym(3).Pattern, sym(4).Expression);
+ sym(1).Node = node;
+ } break;
./
-ConditionalExpressionNotIn: LogicalORExpressionNotIn ;
+BindingElement: BindingIdentifier InitializerOpt_In;
+/.
+ case $rule_number: {
+ AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(1), sym(2).Expression);
+ node->identifierToken = loc(1);
+ // if initializer is an anonymous function expression, we need to assign identifierref as it's name
+ if (auto *f = asAnonymousFunctionDefinition(sym(2).Expression))
+ f->name = stringRef(1);
+ if (auto *c = asAnonymousClassDefinition(sym(2).Expression))
+ c->name = stringRef(1);
+ sym(1).Node = node;
+ } break;
+./
-ConditionalExpressionNotIn: LogicalORExpressionNotIn T_QUESTION AssignmentExpressionNotIn T_COLON AssignmentExpressionNotIn ;
+BindingElement: BindingPattern InitializerOpt_In;
/.
-case $rule_number: {
- AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression,
- sym(3).Expression, sym(5).Expression);
- node->questionToken = loc(2);
- node->colonToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PatternElement *node = new (pool) AST::PatternElement(sym(1).Pattern, sym(2).Expression);
+ sym(1).Node = node;
+ } break;
./
-AssignmentExpression: ConditionalExpression ;
+BindingRestElement: T_ELLIPSIS BindingIdentifier;
+/.
+ case $rule_number: {
+ AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(2), nullptr, AST::PatternElement::RestElement);
+ node->identifierToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
-AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression ;
+BindingRestElement: T_ELLIPSIS BindingPattern;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- sym(2).ival, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::PatternElement *node = new (pool) AST::PatternElement(sym(2).Pattern, nullptr, AST::PatternElement::RestElement);
+ sym(1).Node = node;
+ } break;
+./
+
+BindingRestElementOpt: ;
+/.
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-AssignmentExpressionNotIn: ConditionalExpressionNotIn ;
+BindingRestElementOpt: BindingRestElement;
-AssignmentExpressionNotIn: LeftHandSideExpression AssignmentOperator AssignmentExpressionNotIn ;
+
+EmptyStatement: T_SEMICOLON;
/.
-case $rule_number: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- sym(2).ival, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::EmptyStatement *node = new (pool) AST::EmptyStatement();
+ node->semicolonToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-AssignmentOperator: T_EQ ;
+-- Spec says it should have a "[lookahead ∉ { {, function, class, let [ }]" before the Expression statement.
+-- This is implemented with the rule below that is run before any statement and inserts a T_EXPRESSION_STATEMENT_OK
+-- token if it's ok to parse as an expression statement.
+ExpressionStatementLookahead: ;
+/:
+#define J_SCRIPT_EXPRESSIONSTATEMENTLOOKAHEAD_RULE $rule_number
+:/
/.
-case $rule_number: {
- sym(1).ival = QSOperator::Assign;
-} break;
+ case $rule_number: {
+ int token = lookaheadToken(lexer);
+ if (token == T_LBRACE)
+ pushToken(T_FORCE_BLOCK);
+ else if (token == T_FUNCTION || token == T_CLASS || token == T_LET || token == T_CONST)
+ pushToken(T_FORCE_DECLARATION);
+ } break;
./
-AssignmentOperator: T_STAR_EQ ;
+ExpressionStatement: Expression_In T_AUTOMATIC_SEMICOLON;
+ExpressionStatement: Expression_In T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceMul;
-} break;
+ case $rule_number: {
+ AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression);
+ node->semicolonToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-AssignmentOperator: T_DIVIDE_EQ ;
+IfStatement: T_IF T_LPAREN Expression_In T_RPAREN Statement T_ELSE Statement;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceDiv;
-} break;
+ case $rule_number: {
+ AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement);
+ node->ifToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ node->elseToken = loc(6);
+ sym(1).Node = node;
+ } break;
./
-AssignmentOperator: T_REMAINDER_EQ ;
+IfStatement: T_IF T_LPAREN Expression_In T_RPAREN Statement;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceMod;
-} break;
+ case $rule_number: {
+ AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement);
+ node->ifToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-AssignmentOperator: T_PLUS_EQ ;
+
+IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression_In T_RPAREN T_AUTOMATIC_SEMICOLON;
+IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression_In T_RPAREN T_COMPATIBILITY_SEMICOLON; -- for JSC/V8 compatibility
+IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression_In T_RPAREN T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceAdd;
-} break;
+ case $rule_number: {
+ AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression);
+ node->doToken = loc(1);
+ node->whileToken = loc(3);
+ node->lparenToken = loc(4);
+ node->rparenToken = loc(6);
+ node->semicolonToken = loc(7);
+ sym(1).Node = node;
+ } break;
./
-AssignmentOperator: T_MINUS_EQ ;
+IterationStatement: T_WHILE T_LPAREN Expression_In T_RPAREN Statement;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceSub;
-} break;
+ case $rule_number: {
+ AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement);
+ node->whileToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-AssignmentOperator: T_LT_LT_EQ ;
+IterationStatement: T_FOR T_LPAREN ExpressionOpt T_SEMICOLON ExpressionOpt_In T_SEMICOLON ExpressionOpt_In T_RPAREN Statement; -- [lookahead != { let [ }]
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceLeftShift;
-} break;
+ case $rule_number: {
+ AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression, sym(5).Expression, sym(7).Expression, sym(9).Statement);
+ node->forToken = loc(1);
+ node->lparenToken = loc(2);
+ node->firstSemicolonToken = loc(4);
+ node->secondSemicolonToken = loc(6);
+ node->rparenToken = loc(8);
+ sym(1).Node = node;
+ } break;
./
-AssignmentOperator: T_GT_GT_EQ ;
+IterationStatement: T_FOR T_LPAREN VarDeclaration T_SEMICOLON ExpressionOpt_In T_SEMICOLON ExpressionOpt_In T_RPAREN Statement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+IterationStatement: T_FOR T_LPAREN LexicalDeclaration T_SEMICOLON ExpressionOpt_In T_SEMICOLON ExpressionOpt_In T_RPAREN Statement;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceRightShift;
-} break;
+ case $rule_number: {
+ // ### get rid of the static_cast!
+ AST::ForStatement *node = new (pool) AST::ForStatement(
+ static_cast<AST::VariableStatement *>(sym(3).Node)->declarations, sym(5).Expression,
+ sym(7).Expression, sym(9).Statement);
+ node->forToken = loc(1);
+ node->lparenToken = loc(2);
+ node->firstSemicolonToken = loc(4);
+ node->secondSemicolonToken = loc(6);
+ node->rparenToken = loc(8);
+ sym(1).Node = node;
+ } break;
./
-AssignmentOperator: T_GT_GT_GT_EQ ;
+InOrOf: T_IN;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceURightShift;
-} break;
+ case $rule_number: {
+ sym(1).forEachType = AST::ForEachType::In;
+ } break;
./
-AssignmentOperator: T_AND_EQ ;
+InOrOf: T_OF;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceAnd;
-} break;
+ case $rule_number: {
+ sym(1).forEachType = AST::ForEachType::Of;
+ } break;
./
-AssignmentOperator: T_XOR_EQ ;
+IterationStatement: T_FOR T_LPAREN LeftHandSideExpression InOrOf Expression_In T_RPAREN Statement;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceXor;
-} break;
+ case $rule_number: {
+ // need to convert the LHS to an AssignmentPattern if it was an Array/ObjectLiteral
+ if (AST::Pattern *p = sym(3).Expression->patternCast()) {
+ AST::SourceLocation errorLoc;
+ QString errorMsg;
+ if (!p->convertLiteralToAssignmentPattern(pool, &errorLoc, &errorMsg)) {
+ syntaxError(errorLoc, errorMsg);
+ return false;
+ }
+ }
+ AST::ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression, sym(5).Expression, sym(7).Statement);
+ node->forToken = loc(1);
+ node->lparenToken = loc(2);
+ node->inOfToken = loc(4);
+ node->rparenToken = loc(6);
+ node->type = sym(4).forEachType;
+ sym(1).Node = node;
+ } break;
+./
+
+IterationStatement: T_FOR T_LPAREN ForDeclaration InOrOf Expression_In T_RPAREN Statement;
+/.
+ case $rule_number: {
+ AST::ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).PatternElement, sym(5).Expression, sym(7).Statement);
+ node->forToken = loc(1);
+ node->lparenToken = loc(2);
+ node->inOfToken = loc(4);
+ node->rparenToken = loc(6);
+ node->type = sym(4).forEachType;
+ sym(1).Node = node;
+ } break;
+./
+
+ForDeclaration: LetOrConst BindingIdentifier;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+ForDeclaration: Var BindingIdentifier;
+/.
+ case $rule_number: {
+ auto *node = new (pool) AST::PatternElement(stringRef(2), nullptr);
+ node->identifierToken = loc(2);
+ node->scope = sym(1).scope;
+ node->isForDeclaration = true;
+ sym(1).Node = node;
+ } break;
+./
+
+ForDeclaration: LetOrConst BindingPattern;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+ForDeclaration: Var BindingPattern;
+/.
+ case $rule_number: {
+ auto *node = new (pool) AST::PatternElement(sym(2).Pattern, nullptr);
+ node->scope = sym(1).scope;
+ node->isForDeclaration = true;
+ sym(1).Node = node;
+ } break;
+./
+
+ContinueStatement: T_CONTINUE T_AUTOMATIC_SEMICOLON;
+ContinueStatement: T_CONTINUE T_SEMICOLON;
+/.
+ case $rule_number: {
+ AST::ContinueStatement *node = new (pool) AST::ContinueStatement();
+ node->continueToken = loc(1);
+ node->semicolonToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
+
+ContinueStatement: T_CONTINUE IdentifierReference T_AUTOMATIC_SEMICOLON;
+ContinueStatement: T_CONTINUE IdentifierReference T_SEMICOLON;
+/.
+ case $rule_number: {
+ AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2));
+ node->continueToken = loc(1);
+ node->identifierToken = loc(2);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+ } break;
+./
+
+BreakStatement: T_BREAK T_AUTOMATIC_SEMICOLON;
+BreakStatement: T_BREAK T_SEMICOLON;
+/.
+ case $rule_number: {
+ AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef());
+ node->breakToken = loc(1);
+ node->semicolonToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
+
+BreakStatement: T_BREAK IdentifierReference T_AUTOMATIC_SEMICOLON;
+BreakStatement: T_BREAK IdentifierReference T_SEMICOLON;
+/.
+ case $rule_number: {
+ AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2));
+ node->breakToken = loc(1);
+ node->identifierToken = loc(2);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+ } break;
+./
+
+ReturnStatement: T_RETURN ExpressionOpt_In T_AUTOMATIC_SEMICOLON;
+ReturnStatement: T_RETURN ExpressionOpt_In T_SEMICOLON;
+/.
+ case $rule_number: {
+ if (!functionNestingLevel) {
+ syntaxError(loc(1), "Return statement not allowed outside of Function declaration.");
+ return false;
+ }
+ AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression);
+ node->returnToken = loc(1);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-AssignmentOperator: T_OR_EQ ;
+WithStatement: T_WITH T_LPAREN Expression_In T_RPAREN Statement;
/.
-case $rule_number: {
- sym(1).ival = QSOperator::InplaceOr;
-} break;
+ case $rule_number: {
+ AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement);
+ node->withToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-Expression: AssignmentExpression ;
+SwitchStatement: T_SWITCH T_LPAREN Expression_In T_RPAREN CaseBlock;
+/.
+ case $rule_number: {
+ AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock);
+ node->switchToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+ } break;
+./
-Expression: Expression T_COMMA AssignmentExpression ;
+CaseBlock: T_LBRACE CaseClausesOpt T_RBRACE;
/.
-case $rule_number: {
- AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses);
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-ExpressionOpt: ;
+CaseBlock: T_LBRACE CaseClausesOpt DefaultClause CaseClausesOpt T_RBRACE;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses);
+ node->lbraceToken = loc(1);
+ node->rbraceToken = loc(5);
+ sym(1).Node = node;
+ } break;
./
-ExpressionOpt: Expression ;
+CaseClauses: CaseClause;
+/.
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause);
+ } break;
+./
-ExpressionNotIn: AssignmentExpressionNotIn ;
+CaseClauses: CaseClauses CaseClause;
+/.
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause);
+ } break;
+./
-ExpressionNotIn: ExpressionNotIn T_COMMA AssignmentExpressionNotIn ;
+CaseClausesOpt: ;
/.
-case $rule_number: {
- AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-ExpressionNotInOpt: ;
+CaseClausesOpt: CaseClauses;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(1).CaseClauses->finish();
+ } break;
./
-ExpressionNotInOpt: ExpressionNotIn ;
+CaseClause: T_CASE Expression_In T_COLON StatementListOpt;
+/.
+ case $rule_number: {
+ AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList);
+ node->caseToken = loc(1);
+ node->colonToken = loc(3);
+ sym(1).Node = node;
+ } break;
+./
-Statement: Block ;
-Statement: VariableStatement ;
-Statement: EmptyStatement ;
-Statement: ExpressionStatement ;
-Statement: IfStatement ;
-Statement: IterationStatement ;
-Statement: ContinueStatement ;
-Statement: BreakStatement ;
-Statement: ReturnStatement ;
-Statement: WithStatement ;
-Statement: LabelledStatement ;
-Statement: SwitchStatement ;
-Statement: ThrowStatement ;
-Statement: TryStatement ;
-Statement: DebuggerStatement ;
+DefaultClause: T_DEFAULT T_COLON StatementListOpt;
+/.
+ case $rule_number: {
+ AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList);
+ node->defaultToken = loc(1);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
+
+LabelledStatement: IdentifierReference T_COLON LabelledItem;
+/.
+ case $rule_number: {
+ AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement);
+ node->identifierToken = loc(1);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
+LabelledItem: Statement;
-Block: T_LBRACE StatementListOpt T_RBRACE ;
+LabelledItem: ExpressionStatementLookahead T_FORCE_DECLARATION FunctionDeclaration;
/.
-case $rule_number: {
- AST::Block *node = new (pool) AST::Block(sym(2).StatementList);
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ syntaxError(loc(3), "FunctionDeclarations are not allowed after a label.");
+ return false;
+ } break;
./
-StatementList: Statement ;
+ThrowStatement: T_THROW Expression_In T_AUTOMATIC_SEMICOLON;
+ThrowStatement: T_THROW Expression_In T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::StatementList(sym(1).Statement);
-} break;
+ case $rule_number: {
+ AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression);
+ node->throwToken = loc(1);
+ node->semicolonToken = loc(3);
+ sym(1).Node = node;
+ } break;
./
-StatementList: StatementList Statement ;
+TryStatement: T_TRY Block Catch;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::StatementList(sym(1).StatementList, sym(2).Statement);
-} break;
+ case $rule_number: {
+ AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch);
+ node->tryToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-StatementListOpt: ;
+TryStatement: T_TRY Block Finally;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally);
+ node->tryToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-StatementListOpt: StatementList ;
+TryStatement: T_TRY Block Catch Finally;
/.
-case $rule_number: {
- sym(1).Node = sym(1).StatementList->finish ();
-} break;
+ case $rule_number: {
+ AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally);
+ node->tryToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-VariableStatement: VariableDeclarationKind VariableDeclarationList T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-VariableStatement: VariableDeclarationKind VariableDeclarationList T_SEMICOLON ;
+Catch: T_CATCH T_LPAREN CatchParameter T_RPAREN Block;
/.
-case $rule_number: {
- AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope;
- if (sym(1).ival == T_LET)
- s = AST::VariableDeclaration::BlockScope;
- else if (sym(1).ival == T_CONST)
- s = AST::VariableDeclaration::ReadOnlyBlockScope;
-
- AST::VariableStatement *node = new (pool) AST::VariableStatement(sym(2).VariableDeclarationList->finish(s));
- node->declarationKindToken = loc(1);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::Catch *node = new (pool) AST::Catch(sym(3).PatternElement, sym(5).Block);
+ node->catchToken = loc(1);
+ node->lparenToken = loc(2);
+ node->identifierToken = loc(3);
+ node->rparenToken = loc(4);
+ sym(1).Node = node;
+ } break;
./
-VariableDeclarationKind: T_LET ;
+Finally: T_FINALLY Block;
/.
-case $rule_number: {
- sym(1).ival = T_LET;
-} break;
+ case $rule_number: {
+ AST::Finally *node = new (pool) AST::Finally(sym(2).Block);
+ node->finallyToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-VariableDeclarationKind: T_CONST ;
+CatchParameter: BindingIdentifier;
/.
-case $rule_number: {
- sym(1).ival = T_CONST;
-} break;
+ case $rule_number: {
+ AST::PatternElement *node = new (pool) AST::PatternElement(stringRef(1));
+ node->identifierToken = loc(1);
+ node->scope = AST::VariableScope::Let;
+ sym(1).Node = node;
+ } break;
./
-VariableDeclarationKind: T_VAR ;
+CatchParameter: BindingPattern;
/.
-case $rule_number: {
- sym(1).ival = T_VAR;
-} break;
+ case $rule_number: {
+ AST::PatternElement *node = new (pool) AST::PatternElement(sym(1).Pattern);
+ node->scope = AST::VariableScope::Let;
+ sym(1).Node = node;
+ } break;
./
-VariableDeclarationList: VariableDeclaration ;
+DebuggerStatement: T_DEBUGGER T_AUTOMATIC_SEMICOLON; -- automatic semicolon
+DebuggerStatement: T_DEBUGGER T_SEMICOLON;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration);
-} break;
+ case $rule_number: {
+ AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement();
+ node->debuggerToken = loc(1);
+ node->semicolonToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-VariableDeclarationList: VariableDeclarationList T_COMMA VariableDeclaration ;
+-- tell the parser to prefer function declarations to function expressions.
+-- That is, the `Function' symbol is used to mark the start of a function
+-- declaration.
+-- This is still required for parsing QML, where MemberExpression and FunctionDeclaration would
+-- otherwise conflict.
+Function: T_FUNCTION %prec REDUCE_HERE;
+
+FunctionDeclaration: Function BindingIdentifier T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
/.
-case $rule_number: {
- AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList(
- sym(1).VariableDeclarationList, sym(3).VariableDeclaration);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList);
+ node->functionToken = loc(1);
+ node->identifierToken = loc(2);
+ node->lparenToken = loc(3);
+ node->rparenToken = loc(5);
+ node->lbraceToken = loc(6);
+ node->rbraceToken = loc(8);
+ sym(1).Node = node;
+ } break;
./
-VariableDeclarationListNotIn: VariableDeclarationNotIn ;
+
+FunctionDeclaration_Default: FunctionDeclaration;
+FunctionDeclaration_Default: Function T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration);
-} break;
+ case $rule_number: {
+ AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(QStringRef(), sym(3).FormalParameterList, sym(6).StatementList);
+ node->functionToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ node->lbraceToken = loc(5);
+ node->rbraceToken = loc(7);
+ sym(1).Node = node;
+ } break;
./
-VariableDeclarationListNotIn: VariableDeclarationListNotIn T_COMMA VariableDeclarationNotIn ;
+FunctionExpression: T_FUNCTION BindingIdentifier T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).VariableDeclaration);
-} break;
+ case $rule_number: {
+ AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList);
+ node->functionToken = loc(1);
+ if (! stringRef(2).isNull())
+ node->identifierToken = loc(2);
+ node->lparenToken = loc(3);
+ node->rparenToken = loc(5);
+ node->lbraceToken = loc(6);
+ node->rbraceToken = loc(8);
+ sym(1).Node = node;
+ } break;
./
-VariableDeclaration: JsIdentifier InitialiserOpt ;
+FunctionExpression: T_FUNCTION T_LPAREN FormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
/.
-case $rule_number: {
- AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope;
- AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s);
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(6).StatementList);
+ node->functionToken = loc(1);
+ node->lparenToken = loc(2);
+ node->rparenToken = loc(4);
+ node->lbraceToken = loc(5);
+ node->rbraceToken = loc(7);
+ sym(1).Node = node;
+ } break;
./
-VariableDeclarationNotIn: JsIdentifier InitialiserNotInOpt ;
+StrictFormalParameters: FormalParameters;
+
+FormalParameters: ;
/.
-case $rule_number: {
- AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope;
- AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s);
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-Initialiser: T_EQ AssignmentExpression ;
+FormalParameters: BindingRestElement;
/.
-case $rule_number: {
- // ### TODO: AST for initializer
- sym(1) = sym(2);
-} break;
+ case $rule_number: {
+ AST::FormalParameterList *node = (new (pool) AST::FormalParameterList(nullptr, sym(1).PatternElement))->finish(pool);
+ sym(1).Node = node;
+ } break;
./
-InitialiserOpt: ;
+FormalParameters: FormalParameterList;
+/. case $rule_number: ./
+FormalParameters: FormalParameterList T_COMMA;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(1).FormalParameterList->finish(pool);
+ } break;
./
-InitialiserOpt: Initialiser ;
+FormalParameters: FormalParameterList T_COMMA BindingRestElement;
+/.
+ case $rule_number: {
+ AST::FormalParameterList *node = (new (pool) AST::FormalParameterList(sym(1).FormalParameterList, sym(3).PatternElement))->finish(pool);
+ sym(1).Node = node;
+ } break;
+./
-InitialiserNotIn: T_EQ AssignmentExpressionNotIn ;
+FormalParameterList: BindingElement;
/.
-case $rule_number: {
- // ### TODO: AST for initializer
- sym(1) = sym(2);
-} break;
+ case $rule_number: {
+ AST::FormalParameterList *node = new (pool) AST::FormalParameterList(nullptr, sym(1).PatternElement);
+ sym(1).Node = node;
+ } break;
./
-InitialiserNotInOpt: ;
+
+FormalParameterList: FormalParameterList T_COMMA BindingElement;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, sym(3).PatternElement);
+ sym(1).Node = node;
+ } break;
./
-InitialiserNotInOpt: InitialiserNotIn ;
+FormalParameter: BindingElement;
-EmptyStatement: T_SEMICOLON ;
+FunctionLBrace: T_LBRACE;
/.
-case $rule_number: {
- AST::EmptyStatement *node = new (pool) AST::EmptyStatement();
- node->semicolonToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ ++functionNestingLevel;
+ } break;
./
-ExpressionStatement: Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-ExpressionStatement: Expression T_SEMICOLON ;
+FunctionRBrace: T_RBRACE;
/.
-case $rule_number: {
- AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression);
- node->semicolonToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ --functionNestingLevel;
+ } break;
./
-IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement T_ELSE Statement ;
+
+FunctionBody: StatementListOpt;
+
+ArrowFunction: ArrowParameters T_ARROW ConciseBodyLookahead AssignmentExpression; -- [lookahead ≠ {]
+/. case $rule_number: Q_FALLTHROUGH(); ./
+ArrowFunction_In: ArrowParameters T_ARROW ConciseBodyLookahead AssignmentExpression_In; -- [lookahead ≠ {]
/.
-case $rule_number: {
- AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement);
- node->ifToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- node->elseToken = loc(6);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ReturnStatement *ret = new (pool) AST::ReturnStatement(sym(4).Expression);
+ ret->returnToken = sym(4).Node->firstSourceLocation();
+ ret->semicolonToken = sym(4).Node->lastSourceLocation();
+ AST::StatementList *statements = (new (pool) AST::StatementList(ret))->finish();
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(QStringRef(), sym(1).FormalParameterList, statements);
+ f->isArrowFunction = true;
+ f->functionToken = sym(1).Node ? sym(1).Node->firstSourceLocation() : loc(1);
+ f->lbraceToken = sym(4).Node->firstSourceLocation();
+ f->rbraceToken = sym(4).Node->lastSourceLocation();
+ sym(1).Node = f;
+ } break;
./
-IfStatement: T_IF T_LPAREN Expression T_RPAREN Statement ;
+ArrowFunction: ArrowParameters T_ARROW ConciseBodyLookahead T_FORCE_BLOCK FunctionLBrace FunctionBody FunctionRBrace;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+ArrowFunction_In: ArrowParameters T_ARROW ConciseBodyLookahead T_FORCE_BLOCK FunctionLBrace FunctionBody FunctionRBrace;
/.
-case $rule_number: {
- AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement);
- node->ifToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(QStringRef(), sym(1).FormalParameterList, sym(6).StatementList);
+ f->isArrowFunction = true;
+ f->functionToken = sym(1).Node ? sym(1).Node->firstSourceLocation() : loc(1);
+ f->lbraceToken = loc(6);
+ f->rbraceToken = loc(7);
+ sym(1).Node = f;
+ } break;
./
+ArrowParameters: BindingIdentifier;
+/.
+ case $rule_number: {
+ AST::PatternElement *e = new (pool) AST::PatternElement(stringRef(1), nullptr, AST::PatternElement::Binding);
+ e->identifierToken = loc(1);
+ sym(1).FormalParameterList = (new (pool) AST::FormalParameterList(nullptr, e))->finish(pool);
+ } break;
+./
-IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_COMPATIBILITY_SEMICOLON ; -- for JSC/V8 compatibility
-IterationStatement: T_DO Statement T_WHILE T_LPAREN Expression T_RPAREN T_SEMICOLON ;
+-- CoverParenthesizedExpressionAndArrowParameterList for ArrowParameters i being refined to:
+-- ArrowFormalParameters: T_LPAREN StrictFormalParameters T_RPAREN
+ArrowParameters: CoverParenthesizedExpressionAndArrowParameterList;
/.
-case $rule_number: {
- AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression);
- node->doToken = loc(1);
- node->whileToken = loc(3);
- node->lparenToken = loc(4);
- node->rparenToken = loc(6);
- node->semicolonToken = loc(7);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ if (coverExpressionType != CE_FormalParameterList) {
+ AST::NestedExpression *ne = static_cast<AST::NestedExpression *>(sym(1).Node);
+ AST::FormalParameterList *list = ne->expression->reparseAsFormalParameterList(pool);
+ if (!list) {
+ syntaxError(loc(1), "Invalid Arrow parameter list.");
+ return false;
+ }
+ sym(1).Node = list->finish(pool);
+ }
+ } break;
./
-IterationStatement: T_WHILE T_LPAREN Expression T_RPAREN Statement ;
+ConciseBodyLookahead: ;
+/:
+#define J_SCRIPT_CONCISEBODYLOOKAHEAD_RULE $rule_number
+:/
/.
-case $rule_number: {
- AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement);
- node->whileToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ if (lookaheadToken(lexer) == T_LBRACE)
+ pushToken(T_FORCE_BLOCK);
+ } break;
./
-IterationStatement: T_FOR T_LPAREN ExpressionNotInOpt T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ;
+MethodDefinition: PropertyName T_LPAREN StrictFormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
/.
-case $rule_number: {
- AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression,
- sym(5).Expression, sym(7).Expression, sym(9).Statement);
- node->forToken = loc(1);
- node->lparenToken = loc(2);
- node->firstSemicolonToken = loc(4);
- node->secondSemicolonToken = loc(6);
- node->rparenToken = loc(8);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(1), sym(3).FormalParameterList, sym(6).StatementList);
+ f->functionToken = sym(1).PropertyName->firstSourceLocation();
+ f->lparenToken = loc(2);
+ f->rparenToken = loc(4);
+ f->lbraceToken = loc(5);
+ f->rbraceToken = loc(7);
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(1).PropertyName, f, AST::PatternProperty::Method);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationListNotIn T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ;
+MethodDefinition: T_STAR PropertyName GeneratorLParen StrictFormalParameters T_RPAREN FunctionLBrace GeneratorBody GeneratorRBrace;
/.
-case $rule_number: {
- AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope;
- AST::LocalForStatement *node = new (pool) AST::LocalForStatement(
- sym(4).VariableDeclarationList->finish(s), sym(6).Expression,
- sym(8).Expression, sym(10).Statement);
- node->forToken = loc(1);
- node->lparenToken = loc(2);
- node->varToken = loc(3);
- node->firstSemicolonToken = loc(5);
- node->secondSemicolonToken = loc(7);
- node->rparenToken = loc(9);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList);
+ f->functionToken = sym(2).PropertyName->firstSourceLocation();
+ f->lparenToken = loc(3);
+ f->rparenToken = loc(5);
+ f->lbraceToken = loc(6);
+ f->rbraceToken = loc(8);
+ f->isGenerator = true;
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Method);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
+./
+
+
+MethodDefinition: T_GET PropertyName T_LPAREN T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
+/.
+ case $rule_number: {
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), nullptr, sym(6).StatementList);
+ f->functionToken = sym(2).PropertyName->firstSourceLocation();
+ f->lparenToken = loc(3);
+ f->rparenToken = loc(4);
+ f->lbraceToken = loc(5);
+ f->rbraceToken = loc(7);
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Getter);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-IterationStatement: T_FOR T_LPAREN LeftHandSideExpression T_IN Expression T_RPAREN Statement ;
+MethodDefinition: T_SET PropertyName T_LPAREN PropertySetParameterList T_RPAREN FunctionLBrace FunctionBody FunctionRBrace;
/.
-case $rule_number: {
- AST:: ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression,
- sym(5).Expression, sym(7).Statement);
- node->forToken = loc(1);
- node->lparenToken = loc(2);
- node->inToken = loc(4);
- node->rparenToken = loc(6);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FunctionExpression *f = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).StatementList);
+ f->functionToken = sym(2).PropertyName->firstSourceLocation();
+ f->lparenToken = loc(3);
+ f->rparenToken = loc(5);
+ f->lbraceToken = loc(6);
+ f->rbraceToken = loc(8);
+ AST::PatternProperty *node = new (pool) AST::PatternProperty(sym(2).PropertyName, f, AST::PatternProperty::Setter);
+ node->colonToken = loc(2);
+ sym(1).Node = node;
+ } break;
./
-IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationNotIn T_IN Expression T_RPAREN Statement ;
+
+PropertySetParameterList: FormalParameter;
/.
-case $rule_number: {
- AST::LocalForEachStatement *node = new (pool) AST::LocalForEachStatement(
- sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement);
- node->forToken = loc(1);
- node->lparenToken = loc(2);
- node->varToken = loc(3);
- node->inToken = loc(5);
- node->rparenToken = loc(7);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FormalParameterList *node = (new (pool) AST::FormalParameterList(nullptr, sym(1).PatternElement))->finish(pool);
+ sym(1).Node = node;
+ } break;
./
-ContinueStatement: T_CONTINUE T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-ContinueStatement: T_CONTINUE T_SEMICOLON ;
+GeneratorLParen: T_LPAREN;
/.
-case $rule_number: {
- AST::ContinueStatement *node = new (pool) AST::ContinueStatement();
- node->continueToken = loc(1);
- node->semicolonToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ lexer->enterGeneratorBody();
+ } break;
./
-ContinueStatement: T_CONTINUE JsIdentifier T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-ContinueStatement: T_CONTINUE JsIdentifier T_SEMICOLON ;
+GeneratorRBrace: T_RBRACE;
/.
-case $rule_number: {
- AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2));
- node->continueToken = loc(1);
- node->identifierToken = loc(2);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ --functionNestingLevel;
+ lexer->leaveGeneratorBody();
+ } break;
./
-BreakStatement: T_BREAK T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-BreakStatement: T_BREAK T_SEMICOLON ;
+GeneratorDeclaration: Function T_STAR BindingIdentifier GeneratorLParen FormalParameters T_RPAREN FunctionLBrace GeneratorBody GeneratorRBrace;
/.
-case $rule_number: {
- AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef());
- node->breakToken = loc(1);
- node->semicolonToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(3), sym(5).FormalParameterList, sym(8).StatementList);
+ node->functionToken = loc(1);
+ node->identifierToken = loc(3);
+ node->lparenToken = loc(4);
+ node->rparenToken = loc(6);
+ node->lbraceToken = loc(7);
+ node->rbraceToken = loc(9);
+ node->isGenerator = true;
+ sym(1).Node = node;
+ } break;
./
-BreakStatement: T_BREAK JsIdentifier T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-BreakStatement: T_BREAK JsIdentifier T_SEMICOLON ;
+GeneratorDeclaration_Default: GeneratorDeclaration;
+GeneratorDeclaration_Default: Function T_STAR GeneratorLParen FormalParameters T_RPAREN FunctionLBrace GeneratorBody GeneratorRBrace;
+/.
+ case $rule_number: {
+ AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(QStringRef(), sym(4).FormalParameterList, sym(7).StatementList);
+ node->functionToken = loc(1);
+ node->lparenToken = loc(3);
+ node->rparenToken = loc(5);
+ node->lbraceToken = loc(6);
+ node->rbraceToken = loc(8);
+ node->isGenerator = true;
+ sym(1).Node = node;
+ } break;
+./
+
+GeneratorExpression: T_FUNCTION T_STAR BindingIdentifier GeneratorLParen FormalParameters T_RPAREN FunctionLBrace GeneratorBody GeneratorRBrace;
/.
-case $rule_number: {
- AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2));
- node->breakToken = loc(1);
- node->identifierToken = loc(2);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(3), sym(5).FormalParameterList, sym(8).StatementList);
+ node->functionToken = loc(1);
+ if (!stringRef(3).isNull())
+ node->identifierToken = loc(3);
+ node->lparenToken = loc(4);
+ node->rparenToken = loc(6);
+ node->lbraceToken = loc(7);
+ node->rbraceToken = loc(9);
+ node->isGenerator = true;
+ sym(1).Node = node;
+ } break;
./
-ReturnStatement: T_RETURN ExpressionOpt T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-ReturnStatement: T_RETURN ExpressionOpt T_SEMICOLON ;
+GeneratorExpression: T_FUNCTION T_STAR GeneratorLParen FormalParameters T_RPAREN FunctionLBrace GeneratorBody GeneratorRBrace;
/.
-case $rule_number: {
- AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression);
- node->returnToken = loc(1);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(4).FormalParameterList, sym(7).StatementList);
+ node->functionToken = loc(1);
+ node->lparenToken = loc(3);
+ node->rparenToken = loc(5);
+ node->lbraceToken = loc(6);
+ node->rbraceToken = loc(8);
+ node->isGenerator = true;
+ sym(1).Node = node;
+ } break;
./
-WithStatement: T_WITH T_LPAREN Expression T_RPAREN Statement ;
+GeneratorBody: FunctionBody;
+
+YieldExpression: T_YIELD;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+YieldExpression_In: T_YIELD;
/.
-case $rule_number: {
- AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement);
- node->withToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::YieldExpression *node = new (pool) AST::YieldExpression();
+ node->yieldToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-SwitchStatement: T_SWITCH T_LPAREN Expression T_RPAREN CaseBlock ;
+YieldExpression: T_YIELD T_STAR AssignmentExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+YieldExpression_In: T_YIELD T_STAR AssignmentExpression_In;
/.
-case $rule_number: {
- AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock);
- node->switchToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::YieldExpression *node = new (pool) AST::YieldExpression(sym(3).Expression);
+ node->yieldToken = loc(1);
+ node->isYieldStar = true;
+ sym(1).Node = node;
+ } break;
./
-CaseBlock: T_LBRACE CaseClausesOpt T_RBRACE ;
+YieldExpression: T_YIELD AssignmentExpression;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+YieldExpression_In: T_YIELD AssignmentExpression_In;
/.
-case $rule_number: {
- AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses);
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::YieldExpression *node = new (pool) AST::YieldExpression(sym(2).Expression);
+ node->yieldToken = loc(1);
+ sym(1).Node = node;
+ } break;
./
-CaseBlock: T_LBRACE CaseClausesOpt DefaultClause CaseClausesOpt T_RBRACE ;
+
+ClassDeclaration: T_CLASS BindingIdentifier ClassHeritageOpt ClassLBrace ClassBodyOpt ClassRBrace;
/.
-case $rule_number: {
- AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses);
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(5);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ClassDeclaration *node = new (pool) AST::ClassDeclaration(stringRef(2), sym(3).Expression, sym(5).ClassElementList);
+ node->classToken = loc(1);
+ node->identifierToken = loc(2);
+ node->lbraceToken = loc(4);
+ node->rbraceToken = loc(6);
+ sym(1).Node = node;
+ } break;
./
-CaseClauses: CaseClause ;
+ClassExpression: T_CLASS BindingIdentifier ClassHeritageOpt ClassLBrace ClassBodyOpt ClassRBrace;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause);
-} break;
+ case $rule_number: {
+ AST::ClassExpression *node = new (pool) AST::ClassExpression(stringRef(2), sym(3).Expression, sym(5).ClassElementList);
+ node->classToken = loc(1);
+ node->identifierToken = loc(2);
+ node->lbraceToken = loc(4);
+ node->rbraceToken = loc(6);
+ sym(1).Node = node;
+ } break;
./
-CaseClauses: CaseClauses CaseClause ;
+ClassDeclaration_Default: T_CLASS ClassHeritageOpt ClassLBrace ClassBodyOpt ClassRBrace;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause);
-} break;
+ case $rule_number: {
+ AST::ClassDeclaration *node = new (pool) AST::ClassDeclaration(QStringRef(), sym(2).Expression, sym(4).ClassElementList);
+ node->classToken = loc(1);
+ node->lbraceToken = loc(3);
+ node->rbraceToken = loc(5);
+ sym(1).Node = node;
+ } break;
./
-CaseClausesOpt: ;
+ClassExpression: T_CLASS ClassHeritageOpt ClassLBrace ClassBodyOpt ClassRBrace;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ AST::ClassExpression *node = new (pool) AST::ClassExpression(QStringRef(), sym(2).Expression, sym(4).ClassElementList);
+ node->classToken = loc(1);
+ node->lbraceToken = loc(3);
+ node->rbraceToken = loc(5);
+ sym(1).Node = node;
+ } break;
./
-CaseClausesOpt: CaseClauses ;
+ClassDeclaration_Default: ClassDeclaration;
+
+ClassLBrace: T_LBRACE;
/.
-case $rule_number: {
- sym(1).Node = sym(1).CaseClauses->finish ();
-} break;
+ case $rule_number: {
+ lexer->setStaticIsKeyword(true);
+ } break;
./
-CaseClause: T_CASE Expression T_COLON StatementListOpt ;
+ClassRBrace: T_RBRACE;
+/. case $rule_number: ./
+ClassStaticQualifier: T_STATIC;
/.
-case $rule_number: {
- AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList);
- node->caseToken = loc(1);
- node->colonToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ lexer->setStaticIsKeyword(false);
+ } break;
./
-DefaultClause: T_DEFAULT T_COLON StatementListOpt ;
+ClassHeritageOpt: ;
/.
-case $rule_number: {
- AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList);
- node->defaultToken = loc(1);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-LabelledStatement: JsIdentifier T_COLON Statement ;
+ClassHeritageOpt: T_EXTENDS LeftHandSideExpression;
/.
-case $rule_number: {
- AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement);
- node->identifierToken = loc(1);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = sym(2).Node;
+ } break;
./
-ThrowStatement: T_THROW Expression T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-ThrowStatement: T_THROW Expression T_SEMICOLON ;
+ClassBodyOpt: ;
/.
-case $rule_number: {
- AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression);
- node->throwToken = loc(1);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-TryStatement: T_TRY Block Catch ;
+ClassBodyOpt: ClassElementList;
/.
-case $rule_number: {
- AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch);
- node->tryToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ if (sym(1).Node)
+ sym(1).Node = sym(1).ClassElementList->finish();
+ } break;
./
-TryStatement: T_TRY Block Finally ;
+ClassElementList: ClassElement;
+
+ClassElementList: ClassElementList ClassElement;
/.
-case $rule_number: {
- AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally);
- node->tryToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ if (sym(2).Node)
+ sym(1).ClassElementList = sym(1).ClassElementList->append(sym(2).ClassElementList);
+ } break;
./
-TryStatement: T_TRY Block Catch Finally ;
+ClassElement: MethodDefinition;
/.
-case $rule_number: {
- AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally);
- node->tryToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ AST::ClassElementList *node = new (pool) AST::ClassElementList(sym(1).PatternProperty, false);
+ sym(1).Node = node;
+ } break;
./
-Catch: T_CATCH T_LPAREN JsIdentifier T_RPAREN Block ;
+ClassElement: ClassStaticQualifier MethodDefinition;
/.
-case $rule_number: {
- AST::Catch *node = new (pool) AST::Catch(stringRef(3), sym(5).Block);
- node->catchToken = loc(1);
- node->lparenToken = loc(2);
- node->identifierToken = loc(3);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ lexer->setStaticIsKeyword(true);
+ AST::ClassElementList *node = new (pool) AST::ClassElementList(sym(2).PatternProperty, true);
+ sym(1).Node = node;
+ } break;
./
-Finally: T_FINALLY Block ;
+ClassElement: T_SEMICOLON;
/.
-case $rule_number: {
- AST::Finally *node = new (pool) AST::Finally(sym(2).Block);
- node->finallyToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
-DebuggerStatement: T_DEBUGGER T_AUTOMATIC_SEMICOLON ; -- automatic semicolon
-DebuggerStatement: T_DEBUGGER T_SEMICOLON ;
+-- Scripts and Modules
+
+Script: ;
/.
-case $rule_number: {
- AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement();
- node->debuggerToken = loc(1);
- node->semicolonToken = loc(2);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = nullptr;
+ } break;
./
--- tell the parser to prefer function declarations to function expressions.
--- That is, the `Function' symbol is used to mark the start of a function
--- declaration.
-Function: T_FUNCTION %prec REDUCE_HERE ;
+Script: ScriptBody;
-FunctionDeclaration: Function JsIdentifier T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ;
+ScriptBody: StatementList;
/.
-case $rule_number: {
- AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody);
- node->functionToken = loc(1);
- node->identifierToken = loc(2);
- node->lparenToken = loc(3);
- node->rparenToken = loc(5);
- node->lbraceToken = loc(6);
- node->rbraceToken = loc(8);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).Node = new (pool) AST::Program(sym(1).StatementList->finish());
+ } break;
./
-FunctionExpression: T_FUNCTION JsIdentifier T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ;
+Module: ModuleBodyOpt;
+/. case $rule_number: {
+ sym(1).Node = new (pool) AST::ESModule(sym(1).StatementList);
+ } break;
+./
+
+ModuleBody: ModuleItemList;
/.
-case $rule_number: {
- AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody);
- node->functionToken = loc(1);
- if (! stringRef(2).isNull())
- node->identifierToken = loc(2);
- node->lparenToken = loc(3);
- node->rparenToken = loc(5);
- node->lbraceToken = loc(6);
- node->rbraceToken = loc(8);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).StatementList = sym(1).StatementList->finish();
+ } break;
./
-FunctionExpression: T_FUNCTION T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ;
+ModuleBodyOpt: ;
/.
-case $rule_number: {
- AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(6).FunctionBody);
- node->functionToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- node->lbraceToken = loc(5);
- node->rbraceToken = loc(7);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).StatementList = nullptr;
+ } break;
./
+ModuleBodyOpt: ModuleBody;
+
+ModuleItemList: ModuleItem;
-FormalParameterList: JsIdentifier ;
+ModuleItemList: ModuleItemList ModuleItem;
/.
-case $rule_number: {
- AST::FormalParameterList *node = new (pool) AST::FormalParameterList(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).StatementList = sym(1).StatementList->append(sym(2).StatementList);
+ } break;
./
-FormalParameterList: FormalParameterList T_COMMA JsIdentifier ;
+ModuleItem: ImportDeclaration T_AUTOMATIC_SEMICOLON;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+ModuleItem: ImportDeclaration T_SEMICOLON;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+ModuleItem: ExportDeclaration T_AUTOMATIC_SEMICOLON;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+ModuleItem: ExportDeclaration T_SEMICOLON;
/.
-case $rule_number: {
- AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, stringRef(3));
- node->commaToken = loc(2);
- node->identifierToken = loc(3);
- sym(1).Node = node;
-} break;
+ case $rule_number: {
+ sym(1).StatementList = new (pool) AST::StatementList(sym(1).Node);
+ } break;
./
-FormalParameterListOpt: ;
+ModuleItem: StatementListItem;
+
+ImportDeclaration: T_IMPORT ImportClause FromClause;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ auto decl = new (pool) AST::ImportDeclaration(sym(2).ImportClause, sym(3).FromClause);
+ decl->importToken = loc(1);
+ sym(1).Node = decl;
+ } break;
+./
+ImportDeclaration: T_IMPORT ModuleSpecifier;
+/.
+ case $rule_number: {
+ auto decl = new (pool) AST::ImportDeclaration(stringRef(2));
+ decl->importToken = loc(1);
+ decl->moduleSpecifierToken = loc(2);
+ sym(1).Node = decl;
+ } break;
./
-FormalParameterListOpt: FormalParameterList ;
+ImportClause: ImportedDefaultBinding;
/.
-case $rule_number: {
- sym(1).Node = sym(1).FormalParameterList->finish ();
-} break;
+ case $rule_number: {
+ auto clause = new (pool) AST::ImportClause(stringRef(1));
+ clause->importedDefaultBindingToken = loc(1);
+ sym(1).ImportClause = clause;
+ } break;
+./
+ImportClause: NameSpaceImport;
+/.
+ case $rule_number: {
+ sym(1).ImportClause = new (pool) AST::ImportClause(sym(1).NameSpaceImport);
+ } break;
+./
+ImportClause: NamedImports;
+/.
+ case $rule_number: {
+ sym(1).ImportClause = new (pool) AST::ImportClause(sym(1).NamedImports);
+ } break;
+./
+ImportClause: ImportedDefaultBinding T_COMMA NameSpaceImport;
+/.
+ case $rule_number: {
+ auto importClause = new (pool) AST::ImportClause(stringRef(1), sym(3).NameSpaceImport);
+ importClause->importedDefaultBindingToken = loc(1);
+ sym(1).ImportClause = importClause;
+ } break;
+./
+ImportClause: ImportedDefaultBinding T_COMMA NamedImports;
+/.
+ case $rule_number: {
+ auto importClause = new (pool) AST::ImportClause(stringRef(1), sym(3).NamedImports);
+ importClause->importedDefaultBindingToken = loc(1);
+ sym(1).ImportClause = importClause;
+ } break;
./
-FunctionBodyOpt: ;
+ImportedDefaultBinding: ImportedBinding;
+
+NameSpaceImport: T_STAR T_AS ImportedBinding;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ auto import = new (pool) AST::NameSpaceImport(stringRef(3));
+ import->starToken = loc(1);
+ import->importedBindingToken = loc(3);
+ sym(1).NameSpaceImport = import;
+ } break;
./
-FunctionBodyOpt: FunctionBody ;
+NamedImports: T_LBRACE T_RBRACE;
+/.
+ case $rule_number: {
+ auto namedImports = new (pool) AST::NamedImports();
+ namedImports->leftBraceToken = loc(1);
+ namedImports->rightBraceToken = loc(2);
+ sym(1).NamedImports = namedImports;
+ } break;
+./
+NamedImports: T_LBRACE ImportsList T_RBRACE;
+/.
+ case $rule_number: {
+ auto namedImports = new (pool) AST::NamedImports(sym(2).ImportsList->finish());
+ namedImports->leftBraceToken = loc(1);
+ namedImports->rightBraceToken = loc(3);
+ sym(1).NamedImports = namedImports;
+ } break;
+./
+NamedImports: T_LBRACE ImportsList T_COMMA T_RBRACE;
+/.
+ case $rule_number: {
+ auto namedImports = new (pool) AST::NamedImports(sym(2).ImportsList->finish());
+ namedImports->leftBraceToken = loc(1);
+ namedImports->rightBraceToken = loc(4);
+ sym(1).NamedImports = namedImports;
+ } break;
+./
-FunctionBody: SourceElements ;
+FromClause: T_FROM ModuleSpecifier;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::FunctionBody(sym(1).SourceElements->finish ());
-} break;
+ case $rule_number: {
+ auto clause = new (pool) AST::FromClause(stringRef(2));
+ clause->fromToken = loc(1);
+ clause->moduleSpecifierToken = loc(2);
+ sym(1).FromClause = clause;
+ } break;
./
-Program: Empty ;
+ImportsList: ImportSpecifier;
+/.
+ case $rule_number: {
+ auto importsList = new (pool) AST::ImportsList(sym(1).ImportSpecifier);
+ importsList->importSpecifierToken = loc(1);
+ sym(1).ImportsList = importsList;
+ } break;
+./
+ImportsList: ImportsList T_COMMA ImportSpecifier;
+/.
+ case $rule_number: {
+ auto importsList = new (pool) AST::ImportsList(sym(1).ImportsList, sym(3).ImportSpecifier);
+ importsList->importSpecifierToken = loc(3);
+ sym(1).ImportsList = importsList;
+ } break;
+./
-Program: SourceElements ;
+ImportSpecifier: ImportedBinding;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::Program(sym(1).SourceElements->finish ());
-} break;
+ case $rule_number: {
+ auto importSpecifier = new (pool) AST::ImportSpecifier(stringRef(1));
+ importSpecifier->importedBindingToken = loc(1);
+ sym(1).ImportSpecifier = importSpecifier;
+ } break;
./
+ImportSpecifier: IdentifierName T_AS ImportedBinding;
+/.
+ case $rule_number: {
+ auto importSpecifier = new (pool) AST::ImportSpecifier(stringRef(1), stringRef(3));
+ importSpecifier->identifierToken = loc(1);
+ importSpecifier->importedBindingToken = loc(3);
+ sym(1).ImportSpecifier = importSpecifier;
+ } break;
+./
+
+ModuleSpecifier: T_STRING_LITERAL;
-SourceElements: SourceElement ;
+ImportedBinding: BindingIdentifier;
+
+ExportDeclarationLookahead: ;
+/:
+#define J_SCRIPT_EXPORTDECLARATIONLOOKAHEAD_RULE $rule_number
+:/
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElement);
-} break;
+ case $rule_number: {
+ int token = lookaheadToken(lexer);
+ if (token == T_FUNCTION || token == T_CLASS)
+ pushToken(T_FORCE_DECLARATION);
+ } break;
./
-SourceElements: SourceElements SourceElement ;
+ExportDeclaration: T_EXPORT T_STAR FromClause;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElements, sym(2).SourceElement);
-} break;
+ case $rule_number: {
+ auto exportDeclaration = new (pool) AST::ExportDeclaration(sym(3).FromClause);
+ exportDeclaration->exportToken = loc(1);
+ sym(1).ExportDeclaration = exportDeclaration;
+ } break;
+./
+ExportDeclaration: T_EXPORT ExportClause FromClause;
+/.
+ case $rule_number: {
+ auto exportDeclaration = new (pool) AST::ExportDeclaration(sym(2).ExportClause, sym(3).FromClause);
+ exportDeclaration->exportToken = loc(1);
+ sym(1).ExportDeclaration = exportDeclaration;
+ } break;
+./
+ExportDeclaration: T_EXPORT ExportClause;
+/.
+ case $rule_number: {
+ auto exportDeclaration = new (pool) AST::ExportDeclaration(sym(2).ExportClause);
+ exportDeclaration->exportToken = loc(1);
+ sym(1).ExportDeclaration = exportDeclaration;
+ } break;
+./
+ExportDeclaration: T_EXPORT VariableStatement;
+/. case $rule_number: Q_FALLTHROUGH(); ./
+ExportDeclaration: T_EXPORT Declaration;
+/.
+ case $rule_number: {
+ auto exportDeclaration = new (pool) AST::ExportDeclaration(/*exportDefault=*/false, sym(2).Node);
+ exportDeclaration->exportToken = loc(1);
+ sym(1).ExportDeclaration = exportDeclaration;
+ } break;
+./
+ExportDeclaration: T_EXPORT T_DEFAULT ExportDeclarationLookahead T_FORCE_DECLARATION HoistableDeclaration_Default;
+/.
+ case $rule_number: {
+ if (auto *f = AST::cast<AST::FunctionDeclaration*>(sym(5).Node)) {
+ if (f->name.isEmpty()) {
+ f->name = stringRef(2);
+ f->identifierToken = loc(2);
+ }
+ }
+ } Q_FALLTHROUGH();
./
+ExportDeclaration: T_EXPORT T_DEFAULT ExportDeclarationLookahead T_FORCE_DECLARATION ClassDeclaration_Default;
+/.
+ case $rule_number: {
+ // Emulate 15.2.3.11
+ if (auto *cls = AST::cast<AST::ClassDeclaration*>(sym(5).Node)) {
+ if (cls->name.isEmpty()) {
+ cls->name = stringRef(2);
+ cls->identifierToken = loc(2);
+ }
+ }
-SourceElement: Statement ;
+ auto exportDeclaration = new (pool) AST::ExportDeclaration(/*exportDefault=*/true, sym(5).Node);
+ exportDeclaration->exportToken = loc(1);
+ sym(1).ExportDeclaration = exportDeclaration;
+ } break;
+./
+ExportDeclaration: T_EXPORT T_DEFAULT ExportDeclarationLookahead AssignmentExpression_In; -- [lookahead ∉ { function, class }]
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::StatementSourceElement(sym(1).Statement);
-} break;
+ case $rule_number: {
+ // if lhs is an identifier expression and rhs is an anonymous function expression, we need to assign the name of lhs to the function
+ if (auto *f = asAnonymousFunctionDefinition(sym(4).Node)) {
+ f->name = stringRef(2);
+ }
+ if (auto *c = asAnonymousClassDefinition(sym(4).Expression)) {
+ c->name = stringRef(2);
+ }
+
+ auto exportDeclaration = new (pool) AST::ExportDeclaration(/*exportDefault=*/true, sym(4).Node);
+ exportDeclaration->exportToken = loc(1);
+ sym(1).ExportDeclaration = exportDeclaration;
+ } break;
./
-SourceElement: FunctionDeclaration ;
+ExportClause: T_LBRACE T_RBRACE;
/.
-case $rule_number: {
- sym(1).Node = new (pool) AST::FunctionSourceElement(sym(1).FunctionDeclaration);
-} break;
+ case $rule_number: {
+ auto exportClause = new (pool) AST::ExportClause();
+ exportClause->leftBraceToken = loc(1);
+ exportClause->rightBraceToken = loc(2);
+ sym(1).ExportClause = exportClause;
+ } break;
+./
+ExportClause: T_LBRACE ExportsList T_RBRACE;
+/.
+ case $rule_number: {
+ auto exportClause = new (pool) AST::ExportClause(sym(2).ExportsList->finish());
+ exportClause->leftBraceToken = loc(1);
+ exportClause->rightBraceToken = loc(3);
+ sym(1).ExportClause = exportClause;
+ } break;
+./
+ExportClause: T_LBRACE ExportsList T_COMMA T_RBRACE;
+/.
+ case $rule_number: {
+ auto exportClause = new (pool) AST::ExportClause(sym(2).ExportsList->finish());
+ exportClause->leftBraceToken = loc(1);
+ exportClause->rightBraceToken = loc(4);
+ sym(1).ExportClause = exportClause;
+ } break;
./
-PropertyAssignmentListOpt: ;
+ExportsList: ExportSpecifier;
/.
-case $rule_number: {
- sym(1).Node = 0;
-} break;
+ case $rule_number: {
+ sym(1).ExportsList = new (pool) AST::ExportsList(sym(1).ExportSpecifier);
+ } break;
+./
+ExportsList: ExportsList T_COMMA ExportSpecifier;
+/.
+ case $rule_number: {
+ sym(1).ExportsList = new (pool) AST::ExportsList(sym(1).ExportsList, sym(3).ExportSpecifier);
+ } break;
./
-PropertyAssignmentListOpt: PropertyAssignmentList ;
+ExportSpecifier: IdentifierName;
+/.
+ case $rule_number: {
+ auto exportSpecifier = new (pool) AST::ExportSpecifier(stringRef(1));
+ exportSpecifier->identifierToken = loc(1);
+ sym(1).ExportSpecifier = exportSpecifier;
+ } break;
+./
+ExportSpecifier: IdentifierName T_AS IdentifierName;
+/.
+ case $rule_number: {
+ auto exportSpecifier = new (pool) AST::ExportSpecifier(stringRef(1), stringRef(3));
+ exportSpecifier->identifierToken = loc(1);
+ exportSpecifier->exportedIdentifierToken = loc(3);
+ sym(1).ExportSpecifier = exportSpecifier;
+ } break;
+./
+
+-- Old top level code
/.
+ // ------------ end of switch statement
} // switch
action = nt_action(state_stack[tos], lhs[r] - TERMINAL_COUNT);
} // if
} while (action != 0);
+#ifdef PARSER_DEBUG
+ qDebug() << "Done or error.";
+#endif
+
if (first_token == last_token) {
const int errorState = state_stack[tos];
// automatic insertion of `;'
if (yytoken != -1 && ((t_action(errorState, T_AUTOMATIC_SEMICOLON) && lexer->canInsertAutomaticSemicolon(yytoken))
|| t_action(errorState, T_COMPATIBILITY_SEMICOLON))) {
+#ifdef PARSER_DEBUG
+ qDebug() << "Inserting automatic semicolon.";
+#endif
SavedToken &tk = token_buffer[0];
tk.token = yytoken;
tk.dval = yylval;
@@ -3260,8 +4437,7 @@ PropertyAssignmentListOpt: PropertyAssignmentList ;
for (int tk = 1; tk < TERMINAL_COUNT; ++tk) {
if (tk == T_AUTOMATIC_SEMICOLON || tk == T_FEED_UI_PROGRAM ||
- tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION ||
- tk == T_FEED_JS_SOURCE_ELEMENT)
+ tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION)
continue;
int a = t_action(errorState, tk);
diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp
index 0d1b512c92..4ebb2d3b5c 100644
--- a/src/qml/parser/qqmljsast.cpp
+++ b/src/qml/parser/qqmljsast.cpp
@@ -45,6 +45,27 @@ QT_QML_BEGIN_NAMESPACE
namespace QQmlJS { namespace AST {
+FunctionExpression *asAnonymousFunctionDefinition(Node *n)
+{
+ if (!n)
+ return nullptr;
+ FunctionExpression *f = n->asFunctionDefinition();
+ if (!f || !f->name.isNull())
+ return nullptr;
+ return f;
+}
+
+ClassExpression *asAnonymousClassDefinition(Node *n)
+{
+ if (!n)
+ return nullptr;
+ ClassExpression *c = n->asClassDefinition();
+ if (!c || !c->name.isNull())
+ return nullptr;
+ return c;
+}
+
+
void Node::accept(Visitor *visitor)
{
if (visitor->preVisit(this)) {
@@ -79,11 +100,67 @@ UiObjectMember *Node::uiObjectMemberCast()
return nullptr;
}
+LeftHandSideExpression *Node::leftHandSideExpressionCast()
+{
+ return nullptr;
+}
+
+Pattern *Node::patternCast()
+{
+ return nullptr;
+}
+
+FunctionExpression *Node::asFunctionDefinition()
+{
+ return nullptr;
+}
+
+ClassExpression *Node::asClassDefinition()
+{
+ return nullptr;
+}
+
ExpressionNode *ExpressionNode::expressionCast()
{
return this;
}
+FormalParameterList *ExpressionNode::reparseAsFormalParameterList(MemoryPool *pool)
+{
+ AST::ExpressionNode *expr = this;
+ AST::FormalParameterList *f = nullptr;
+ if (AST::Expression *commaExpr = AST::cast<AST::Expression *>(expr)) {
+ f = commaExpr->left->reparseAsFormalParameterList(pool);
+ if (!f)
+ return nullptr;
+
+ expr = commaExpr->right;
+ }
+
+ AST::ExpressionNode *rhs = nullptr;
+ if (AST::BinaryExpression *assign = AST::cast<AST::BinaryExpression *>(expr)) {
+ if (assign->op != QSOperator::Assign)
+ return nullptr;
+ expr = assign->left;
+ rhs = assign->right;
+ }
+ AST::PatternElement *binding = nullptr;
+ if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(expr)) {
+ binding = new (pool) AST::PatternElement(idExpr->name, rhs);
+ binding->identifierToken = idExpr->identifierToken;
+ } else if (AST::Pattern *p = expr->patternCast()) {
+ SourceLocation loc;
+ QString s;
+ if (!p->convertLiteralToAssignmentPattern(pool, &loc, &s))
+ return nullptr;
+ binding = new (pool) AST::PatternElement(p, rhs);
+ binding->identifierToken = p->firstSourceLocation();
+ }
+ if (!binding)
+ return nullptr;
+ return new (pool) AST::FormalParameterList(f, binding);
+}
+
BinaryExpression *BinaryExpression::binaryExpressionCast()
{
return this;
@@ -107,6 +184,16 @@ void NestedExpression::accept0(Visitor *visitor)
visitor->endVisit(this);
}
+FunctionExpression *NestedExpression::asFunctionDefinition()
+{
+ return expression->asFunctionDefinition();
+}
+
+ClassExpression *NestedExpression::asClassDefinition()
+{
+ return expression->asClassDefinition();
+}
+
void ThisExpression::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
@@ -147,7 +234,7 @@ void FalseLiteral::accept0(Visitor *visitor)
visitor->endVisit(this);
}
-void StringLiteral::accept0(Visitor *visitor)
+void SuperLiteral::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
}
@@ -155,7 +242,8 @@ void StringLiteral::accept0(Visitor *visitor)
visitor->endVisit(this);
}
-void NumericLiteral::accept0(Visitor *visitor)
+
+void StringLiteral::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
}
@@ -163,81 +251,231 @@ void NumericLiteral::accept0(Visitor *visitor)
visitor->endVisit(this);
}
-void RegExpLiteral::accept0(Visitor *visitor)
+void TemplateLiteral::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
+ if (next)
+ accept(next, visitor);
}
visitor->endVisit(this);
}
-void ArrayLiteral::accept0(Visitor *visitor)
+void NumericLiteral::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- accept(elements, visitor);
- accept(elision, visitor);
}
visitor->endVisit(this);
}
-void ObjectLiteral::accept0(Visitor *visitor)
+void RegExpLiteral::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- accept(properties, visitor);
}
visitor->endVisit(this);
}
-void ElementList::accept0(Visitor *visitor)
+void ArrayPattern::accept0(Visitor *visitor)
{
- if (visitor->visit(this)) {
- for (ElementList *it = this; it; it = it->next) {
- accept(it->elision, visitor);
- accept(it->expression, visitor);
- }
- }
+ if (visitor->visit(this))
+ accept(elements, visitor);
visitor->endVisit(this);
}
-void Elision::accept0(Visitor *visitor)
+bool ArrayPattern::isValidArrayLiteral(SourceLocation *errorLocation) const {
+ for (PatternElementList *it = elements; it != nullptr; it = it->next) {
+ PatternElement *e = it->element;
+ if (e && e->bindingTarget != nullptr) {
+ if (errorLocation)
+ *errorLocation = e->firstSourceLocation();
+ return false;
+ }
+ }
+ return true;
+}
+
+void ObjectPattern::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- // ###
+ accept(properties, visitor);
}
visitor->endVisit(this);
}
-void PropertyNameAndValue::accept0(Visitor *visitor)
-{
- if (visitor->visit(this)) {
- accept(name, visitor);
- accept(value, visitor);
+/*
+ This is the grammar for AssignmentPattern that we need to convert the literal to:
+
+ AssignmentPattern:
+ ObjectAssignmentPattern
+ ArrayAssignmentPattern
+ ArrayAssignmentPattern:
+ [ ElisionOpt AssignmentRestElementOpt ]
+ [ AssignmentElementList ]
+ [ AssignmentElementList , ElisionOpt AssignmentRestElementOpt ]
+ AssignmentElementList:
+ AssignmentElisionElement
+ AssignmentElementList , AssignmentElisionElement
+ AssignmentElisionElement:
+ ElisionOpt AssignmentElement
+ AssignmentRestElement:
+ ... DestructuringAssignmentTarget
+
+ ObjectAssignmentPattern:
+ {}
+ { AssignmentPropertyList }
+ { AssignmentPropertyList, }
+ AssignmentPropertyList:
+ AssignmentProperty
+ AssignmentPropertyList , AssignmentProperty
+ AssignmentProperty:
+ IdentifierReference InitializerOpt_In
+ PropertyName:
+ AssignmentElement
+
+ AssignmentElement:
+ DestructuringAssignmentTarget InitializerOpt_In
+ DestructuringAssignmentTarget:
+ LeftHandSideExpression
+
+ It was originally parsed with the following grammar:
+
+ArrayLiteral:
+ [ ElisionOpt ]
+ [ ElementList ]
+ [ ElementList , ElisionOpt ]
+ElementList:
+ ElisionOpt AssignmentExpression_In
+ ElisionOpt SpreadElement
+ ElementList , ElisionOpt AssignmentExpression_In
+ ElementList , Elisionopt SpreadElement
+SpreadElement:
+ ... AssignmentExpression_In
+ObjectLiteral:
+ {}
+ { PropertyDefinitionList }
+ { PropertyDefinitionList , }
+PropertyDefinitionList:
+ PropertyDefinition
+ PropertyDefinitionList , PropertyDefinition
+PropertyDefinition:
+ IdentifierReference
+ CoverInitializedName
+ PropertyName : AssignmentExpression_In
+ MethodDefinition
+PropertyName:
+ LiteralPropertyName
+ ComputedPropertyName
+
+*/
+bool ArrayPattern::convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage)
+{
+ if (parseMode == Binding)
+ return true;
+ for (auto *it = elements; it; it = it->next) {
+ if (!it->element)
+ continue;
+ if (it->element->type == PatternElement::SpreadElement && it->next) {
+ *errorLocation = it->element->firstSourceLocation();
+ *errorMessage = QString::fromLatin1("'...' can only appear as last element in a destructuring list.");
+ return false;
+ }
+ if (!it->element->convertLiteralToAssignmentPattern(pool, errorLocation, errorMessage))
+ return false;
}
+ parseMode = Binding;
+ return true;
+}
- visitor->endVisit(this);
+bool ObjectPattern::convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage)
+{
+ if (parseMode == Binding)
+ return true;
+ for (auto *it = properties; it; it = it->next) {
+ if (!it->property->convertLiteralToAssignmentPattern(pool, errorLocation, errorMessage))
+ return false;
+ }
+ parseMode = Binding;
+ return true;
}
-void PropertyGetterSetter::accept0(Visitor *visitor)
+bool PatternElement::convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage)
{
- if (visitor->visit(this)) {
- accept(name, visitor);
- accept(formals, visitor);
- accept(functionBody, visitor);
+ Q_ASSERT(type == Literal || type == SpreadElement);
+ Q_ASSERT(bindingIdentifier.isNull());
+ Q_ASSERT(bindingTarget == nullptr);
+ Q_ASSERT(bindingTarget == nullptr);
+ Q_ASSERT(initializer);
+ ExpressionNode *init = initializer;
+
+ initializer = nullptr;
+ LeftHandSideExpression *lhs = init->leftHandSideExpressionCast();
+ if (type == SpreadElement) {
+ if (!lhs) {
+ *errorLocation = init->firstSourceLocation();
+ *errorMessage = QString::fromLatin1("Invalid lhs expression after '...' in destructuring expression.");
+ return false;
+ }
+ } else {
+ type = PatternElement::Binding;
+
+ if (BinaryExpression *b = init->binaryExpressionCast()) {
+ if (b->op != QSOperator::Assign) {
+ *errorLocation = b->operatorToken;
+ *errorMessage = QString::fromLatin1("Invalid assignment operation in destructuring expression");
+ return false;
+ }
+ lhs = b->left->leftHandSideExpressionCast();
+ initializer = b->right;
+ Q_ASSERT(lhs);
+ } else {
+ lhs = init->leftHandSideExpressionCast();
+ }
+ if (!lhs) {
+ *errorLocation = init->firstSourceLocation();
+ *errorMessage = QString::fromLatin1("Destructuring target is not a left hand side expression.");
+ return false;
+ }
}
- visitor->endVisit(this);
+ if (auto *i = cast<IdentifierExpression *>(lhs)) {
+ bindingIdentifier = i->name;
+ identifierToken = i->identifierToken;
+ return true;
+ }
+
+ bindingTarget = lhs;
+ if (auto *p = lhs->patternCast()) {
+ if (!p->convertLiteralToAssignmentPattern(pool, errorLocation, errorMessage))
+ return false;
+ }
+ return true;
+}
+
+bool PatternProperty::convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage)
+{
+ Q_ASSERT(type != SpreadElement);
+ if (type == Binding)
+ return true;
+ if (type == Getter || type == Setter) {
+ *errorLocation = firstSourceLocation();
+ *errorMessage = QString::fromLatin1("Invalid getter/setter in destructuring expression.");
+ return false;
+ }
+ if (type == Method)
+ type = Literal;
+ Q_ASSERT(type == Literal);
+ return PatternElement::convertLiteralToAssignmentPattern(pool, errorLocation, errorMessage);
}
-void PropertyAssignmentList::accept0(Visitor *visitor)
+
+void Elision::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- for (PropertyAssignmentList *it = this; it; it = it->next) {
- accept(it->assignment, visitor);
- }
+ // ###
}
visitor->endVisit(this);
@@ -518,15 +756,6 @@ void VariableDeclarationList::accept0(Visitor *visitor)
visitor->endVisit(this);
}
-void VariableDeclaration::accept0(Visitor *visitor)
-{
- if (visitor->visit(this)) {
- accept(expression, visitor);
- }
-
- visitor->endVisit(this);
-}
-
void EmptyStatement::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
@@ -579,17 +808,6 @@ void ForStatement::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
accept(initialiser, visitor);
- accept(condition, visitor);
- accept(expression, visitor);
- accept(statement, visitor);
- }
-
- visitor->endVisit(this);
-}
-
-void LocalForStatement::accept0(Visitor *visitor)
-{
- if (visitor->visit(this)) {
accept(declarations, visitor);
accept(condition, visitor);
accept(expression, visitor);
@@ -602,7 +820,7 @@ void LocalForStatement::accept0(Visitor *visitor)
void ForEachStatement::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- accept(initialiser, visitor);
+ accept(lhs, visitor);
accept(expression, visitor);
accept(statement, visitor);
}
@@ -610,18 +828,15 @@ void ForEachStatement::accept0(Visitor *visitor)
visitor->endVisit(this);
}
-void LocalForEachStatement::accept0(Visitor *visitor)
+void ContinueStatement::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- accept(declaration, visitor);
- accept(expression, visitor);
- accept(statement, visitor);
}
visitor->endVisit(this);
}
-void ContinueStatement::accept0(Visitor *visitor)
+void BreakStatement::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
}
@@ -629,15 +844,16 @@ void ContinueStatement::accept0(Visitor *visitor)
visitor->endVisit(this);
}
-void BreakStatement::accept0(Visitor *visitor)
+void ReturnStatement::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
+ accept(expression, visitor);
}
visitor->endVisit(this);
}
-void ReturnStatement::accept0(Visitor *visitor)
+void YieldExpression::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
accept(expression, visitor);
@@ -646,6 +862,7 @@ void ReturnStatement::accept0(Visitor *visitor)
visitor->endVisit(this);
}
+
void WithStatement::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
@@ -739,6 +956,7 @@ void TryStatement::accept0(Visitor *visitor)
void Catch::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
+ accept(patternElement, visitor);
accept(statement, visitor);
}
@@ -774,57 +992,182 @@ void FunctionExpression::accept0(Visitor *visitor)
visitor->endVisit(this);
}
+FunctionExpression *FunctionExpression::asFunctionDefinition()
+{
+ return this;
+}
+
+QStringList FormalParameterList::formals() const
+{
+ QStringList formals;
+ int i = 0;
+ for (const FormalParameterList *it = this; it; it = it->next) {
+ if (it->element) {
+ QString name = it->element->bindingIdentifier.toString();
+ int duplicateIndex = formals.indexOf(name);
+ if (duplicateIndex >= 0) {
+ // change the name of the earlier argument to enforce the lookup semantics from the spec
+ formals[duplicateIndex] += QLatin1String("#") + QString::number(i);
+ }
+ formals += name;
+ }
+ ++i;
+ }
+ return formals;
+}
+
+QStringList FormalParameterList::boundNames() const
+{
+ QStringList names;
+ for (const FormalParameterList *it = this; it; it = it->next) {
+ if (it->element)
+ it->element->boundNames(&names);
+ }
+ return names;
+}
+
void FormalParameterList::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- // ###
+ accept(element, visitor);
+ if (next)
+ accept(next, visitor);
}
visitor->endVisit(this);
}
-void FunctionBody::accept0(Visitor *visitor)
+FormalParameterList *FormalParameterList::finish(QQmlJS::MemoryPool *pool)
+{
+ FormalParameterList *front = next;
+ next = nullptr;
+
+ int i = 0;
+ for (const FormalParameterList *it = this; it; it = it->next) {
+ if (it->element && it->element->bindingIdentifier.isEmpty())
+ it->element->bindingIdentifier = pool->newString(QLatin1String("arg#") + QString::number(i));
+ ++i;
+ }
+ return front;
+}
+
+void Program::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- accept(elements, visitor);
+ accept(statements, visitor);
}
visitor->endVisit(this);
}
-void Program::accept0(Visitor *visitor)
+void ImportSpecifier::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+
+ }
+ visitor->endVisit(this);
+}
+
+void ImportsList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ for (ImportsList *it = this; it; it = it->next) {
+ accept(it->importSpecifier, visitor);
+ }
+ }
+
+ visitor->endVisit(this);
+}
+
+void NamedImports::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(importsList, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void FromClause::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ }
+
+ visitor->endVisit(this);
+}
+
+void NameSpaceImport::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- accept(elements, visitor);
}
visitor->endVisit(this);
}
-void SourceElements::accept0(Visitor *visitor)
+void ImportClause::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- for (SourceElements *it = this; it; it = it->next) {
- accept(it->element, visitor);
+ accept(nameSpaceImport, visitor);
+ accept(namedImports, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void ImportDeclaration::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(importClause, visitor);
+ accept(fromClause, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void ExportSpecifier::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+
+ }
+
+ visitor->endVisit(this);
+}
+
+void ExportsList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ for (ExportsList *it = this; it; it = it->next) {
+ accept(it->exportSpecifier, visitor);
}
}
visitor->endVisit(this);
}
-void FunctionSourceElement::accept0(Visitor *visitor)
+void ExportClause::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- accept(declaration, visitor);
+ accept(exportsList, visitor);
}
visitor->endVisit(this);
}
-void StatementSourceElement::accept0(Visitor *visitor)
+void ExportDeclaration::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- accept(statement, visitor);
+ accept(fromClause, visitor);
+ accept(exportClause, visitor);
+ accept(variableStatementOrDeclaration, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void ESModule::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(body, visitor);
}
visitor->endVisit(this);
@@ -952,18 +1295,9 @@ void UiImport::accept0(Visitor *visitor)
visitor->endVisit(this);
}
-void UiQualifiedPragmaId::accept0(Visitor *visitor)
-{
- if (visitor->visit(this)) {
- }
-
- visitor->endVisit(this);
-}
-
void UiPragma::accept0(Visitor *visitor)
{
if (visitor->visit(this)) {
- accept(pragmaType, visitor);
}
visitor->endVisit(this);
@@ -1006,6 +1340,153 @@ void UiEnumMemberList::accept0(Visitor *visitor)
visitor->endVisit(this);
}
+void TaggedTemplate::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(base, visitor);
+ accept(templateLiteral, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void PatternElement::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(bindingTarget, visitor);
+ accept(initializer, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void PatternElement::boundNames(QStringList *names)
+{
+ if (bindingTarget) {
+ if (PatternElementList *e = elementList())
+ e->boundNames(names);
+ else if (PatternPropertyList *p = propertyList())
+ p->boundNames(names);
+ } else {
+ names->append(bindingIdentifier.toString());
+ }
+}
+
+void PatternElementList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(elision, visitor);
+ accept(element, visitor);
+ if (next)
+ accept(next, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void PatternElementList::boundNames(QStringList *names)
+{
+ for (PatternElementList *it = this; it; it = it->next) {
+ if (it->element)
+ it->element->boundNames(names);
+ }
+}
+
+void PatternProperty::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(name, visitor);
+ accept(bindingTarget, visitor);
+ accept(initializer, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void PatternProperty::boundNames(QStringList *names)
+{
+ PatternElement::boundNames(names);
+}
+
+void PatternPropertyList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(property, visitor);
+ if (next)
+ accept(next, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void PatternPropertyList::boundNames(QStringList *names)
+{
+ for (PatternPropertyList *it = this; it; it = it->next)
+ it->property->boundNames(names);
+}
+
+void ComputedPropertyName::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(expression, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void ClassExpression::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(heritage, visitor);
+ accept(elements, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+ClassExpression *ClassExpression::asClassDefinition()
+{
+ return this;
+}
+
+void ClassDeclaration::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(heritage, visitor);
+ accept(elements, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+void ClassElementList::accept0(Visitor *visitor)
+{
+ if (visitor->visit(this)) {
+ accept(property, visitor);
+ if (next)
+ accept(next, visitor);
+ }
+
+ visitor->endVisit(this);
+}
+
+ClassElementList *ClassElementList::finish()
+{
+ ClassElementList *front = next;
+ next = nullptr;
+ return front;
+}
+
+Pattern *Pattern::patternCast()
+{
+ return this;
+}
+
+LeftHandSideExpression *LeftHandSideExpression::leftHandSideExpressionCast()
+{
+ return this;
+}
+
} } // namespace QQmlJS::AST
QT_QML_END_NAMESPACE
diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h
index 9c5fd5adf6..72c47cbe32 100644
--- a/src/qml/parser/qqmljsast_p.h
+++ b/src/qml/parser/qqmljsast_p.h
@@ -77,6 +77,8 @@ enum Op {
Div,
InplaceDiv,
Equal,
+ Exp,
+ InplaceExp,
Ge,
Gt,
In,
@@ -110,6 +112,13 @@ namespace QQmlJS {
namespace AST {
+enum class VariableScope {
+ NoScope,
+ Var,
+ Let,
+ Const
+};
+
template <typename T1, typename T2>
T1 cast(T2 *ast)
{
@@ -119,6 +128,9 @@ T1 cast(T2 *ast)
return 0;
}
+FunctionExpression *asAnonymousFunctionDefinition(AST::Node *n);
+ClassExpression *asAnonymousClassDefinition(AST::Node *n);
+
class QML_PARSER_EXPORT Node: public Managed
{
public:
@@ -126,7 +138,7 @@ public:
Kind_Undefined,
Kind_ArgumentList,
- Kind_ArrayLiteral,
+ Kind_ArrayPattern,
Kind_ArrayMemberExpression,
Kind_BinaryExpression,
Kind_Block,
@@ -148,6 +160,7 @@ public:
Kind_Expression,
Kind_ExpressionStatement,
Kind_FalseLiteral,
+ Kind_SuperLiteral,
Kind_FieldMemberExpression,
Kind_Finally,
Kind_ForEachStatement,
@@ -156,38 +169,50 @@ public:
Kind_FunctionBody,
Kind_FunctionDeclaration,
Kind_FunctionExpression,
- Kind_FunctionSourceElement,
+ Kind_ClassExpression,
+ Kind_ClassDeclaration,
Kind_IdentifierExpression,
Kind_IdentifierPropertyName,
+ Kind_ComputedPropertyName,
Kind_IfStatement,
Kind_LabelledStatement,
- Kind_LocalForEachStatement,
- Kind_LocalForStatement,
+ Kind_NameSpaceImport,
+ Kind_ImportSpecifier,
+ Kind_ImportsList,
+ Kind_NamedImports,
+ Kind_ImportClause,
+ Kind_FromClause,
+ Kind_ImportDeclaration,
+ Kind_Module,
+ Kind_ExportSpecifier,
+ Kind_ExportsList,
+ Kind_ExportClause,
+ Kind_ExportDeclaration,
Kind_NewExpression,
Kind_NewMemberExpression,
Kind_NotExpression,
Kind_NullExpression,
+ Kind_YieldExpression,
Kind_NumericLiteral,
Kind_NumericLiteralPropertyName,
- Kind_ObjectLiteral,
+ Kind_ObjectPattern,
Kind_PostDecrementExpression,
Kind_PostIncrementExpression,
Kind_PreDecrementExpression,
Kind_PreIncrementExpression,
Kind_Program,
- Kind_PropertyAssignmentList,
+ Kind_PropertyDefinitionList,
Kind_PropertyGetterSetter,
Kind_PropertyName,
Kind_PropertyNameAndValue,
Kind_RegExpLiteral,
Kind_ReturnStatement,
- Kind_SourceElement,
- Kind_SourceElements,
Kind_StatementList,
- Kind_StatementSourceElement,
Kind_StringLiteral,
Kind_StringLiteralPropertyName,
Kind_SwitchStatement,
+ Kind_TemplateLiteral,
+ Kind_TaggedTemplate,
Kind_ThisExpression,
Kind_ThrowStatement,
Kind_TildeExpression,
@@ -203,6 +228,12 @@ public:
Kind_WhileStatement,
Kind_WithStatement,
Kind_NestedExpression,
+ Kind_ClassElementList,
+ Kind_PatternElement,
+ Kind_PatternElementList,
+ Kind_PatternProperty,
+ Kind_PatternPropertyList,
+
Kind_UiArrayBinding,
Kind_UiImport,
@@ -216,7 +247,6 @@ public:
Kind_UiParameterList,
Kind_UiPublicMember,
Kind_UiQualifiedId,
- Kind_UiQualifiedPragmaId,
Kind_UiScriptBinding,
Kind_UiSourceElement,
Kind_UiHeaderItemList,
@@ -235,6 +265,11 @@ public:
virtual BinaryExpression *binaryExpressionCast();
virtual Statement *statementCast();
virtual UiObjectMember *uiObjectMemberCast();
+ virtual LeftHandSideExpression *leftHandSideExpressionCast();
+ virtual Pattern *patternCast();
+ // implements the IsFunctionDefinition rules in the spec
+ virtual FunctionExpression *asFunctionDefinition();
+ virtual ClassExpression *asClassDefinition();
void accept(Visitor *visitor);
static void accept(Node *node, Visitor *visitor);
@@ -256,6 +291,14 @@ public:
ExpressionNode() {}
ExpressionNode *expressionCast() override;
+
+ AST::FormalParameterList *reparseAsFormalParameterList(MemoryPool *pool);
+
+};
+
+class QML_PARSER_EXPORT LeftHandSideExpression : public ExpressionNode
+{
+ LeftHandSideExpression *leftHandSideExpressionCast() override;
};
class QML_PARSER_EXPORT Statement: public Node
@@ -266,7 +309,7 @@ public:
Statement *statementCast() override;
};
-class QML_PARSER_EXPORT NestedExpression: public ExpressionNode
+class QML_PARSER_EXPORT NestedExpression: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(NestedExpression)
@@ -283,13 +326,17 @@ public:
SourceLocation lastSourceLocation() const override
{ return rparenToken; }
+ FunctionExpression *asFunctionDefinition() override;
+ ClassExpression *asClassDefinition() override;
+
+
// attributes
ExpressionNode *expression;
SourceLocation lparenToken;
SourceLocation rparenToken;
};
-class QML_PARSER_EXPORT ThisExpression: public ExpressionNode
+class QML_PARSER_EXPORT ThisExpression: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(ThisExpression)
@@ -308,7 +355,7 @@ public:
SourceLocation thisToken;
};
-class QML_PARSER_EXPORT IdentifierExpression: public ExpressionNode
+class QML_PARSER_EXPORT IdentifierExpression: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(IdentifierExpression)
@@ -329,7 +376,7 @@ public:
SourceLocation identifierToken;
};
-class QML_PARSER_EXPORT NullExpression: public ExpressionNode
+class QML_PARSER_EXPORT NullExpression: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(NullExpression)
@@ -348,7 +395,7 @@ public:
SourceLocation nullToken;
};
-class QML_PARSER_EXPORT TrueLiteral: public ExpressionNode
+class QML_PARSER_EXPORT TrueLiteral: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(TrueLiteral)
@@ -367,7 +414,7 @@ public:
SourceLocation trueToken;
};
-class QML_PARSER_EXPORT FalseLiteral: public ExpressionNode
+class QML_PARSER_EXPORT FalseLiteral: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(FalseLiteral)
@@ -386,7 +433,27 @@ public:
SourceLocation falseToken;
};
-class QML_PARSER_EXPORT NumericLiteral: public ExpressionNode
+class QML_PARSER_EXPORT SuperLiteral : public LeftHandSideExpression
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(SuperLiteral)
+
+ SuperLiteral() { kind = K; }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return superToken; }
+
+ SourceLocation lastSourceLocation() const override
+ { return superToken; }
+
+// attributes
+ SourceLocation superToken;
+};
+
+
+class QML_PARSER_EXPORT NumericLiteral: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(NumericLiteral)
@@ -407,7 +474,7 @@ public:
SourceLocation literalToken;
};
-class QML_PARSER_EXPORT StringLiteral: public ExpressionNode
+class QML_PARSER_EXPORT StringLiteral: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(StringLiteral)
@@ -428,7 +495,30 @@ public:
SourceLocation literalToken;
};
-class QML_PARSER_EXPORT RegExpLiteral: public ExpressionNode
+class QML_PARSER_EXPORT TemplateLiteral : public LeftHandSideExpression
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(TemplateLiteral)
+
+ TemplateLiteral(const QStringRef &str, ExpressionNode *e)
+ : value(str), expression(e), next(nullptr)
+ { kind = K; }
+
+ SourceLocation firstSourceLocation() const override
+ { return literalToken; }
+
+ SourceLocation lastSourceLocation() const override
+ { return next ? next->lastSourceLocation() : (expression ? expression->lastSourceLocation() : literalToken); }
+
+ void accept0(Visitor *visitor) override;
+
+ QStringRef value;
+ ExpressionNode *expression;
+ TemplateLiteral *next;
+ SourceLocation literalToken;
+};
+
+class QML_PARSER_EXPORT RegExpLiteral: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(RegExpLiteral)
@@ -450,22 +540,26 @@ public:
SourceLocation literalToken;
};
-class QML_PARSER_EXPORT ArrayLiteral: public ExpressionNode
+class QML_PARSER_EXPORT Pattern : public LeftHandSideExpression
{
public:
- QQMLJS_DECLARE_AST_NODE(ArrayLiteral)
-
- ArrayLiteral(Elision *e):
- elements (nullptr), elision (e)
- { kind = K; }
+ enum ParseMode {
+ Literal,
+ Binding
+ };
+ Pattern *patternCast() override;
+ virtual bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) = 0;
+ ParseMode parseMode = Literal;
+};
- ArrayLiteral(ElementList *elts):
- elements (elts), elision (nullptr)
- { kind = K; }
+class QML_PARSER_EXPORT ArrayPattern : public Pattern
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(ArrayPattern)
- ArrayLiteral(ElementList *elts, Elision *e):
- elements (elts), elision (e)
- { kind = K; }
+ ArrayPattern(PatternElementList *elts)
+ : elements(elts)
+ { kind = K; }
void accept0(Visitor *visitor) override;
@@ -475,24 +569,28 @@ public:
SourceLocation lastSourceLocation() const override
{ return rbracketToken; }
+ bool isValidArrayLiteral(SourceLocation *errorLocation = nullptr) const;
+
+ bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) override;
+
// attributes
- ElementList *elements;
- Elision *elision;
+ PatternElementList *elements = nullptr;
SourceLocation lbracketToken;
SourceLocation commaToken;
SourceLocation rbracketToken;
};
-class QML_PARSER_EXPORT ObjectLiteral: public ExpressionNode
+class QML_PARSER_EXPORT ObjectPattern : public Pattern
{
public:
- QQMLJS_DECLARE_AST_NODE(ObjectLiteral)
+ QQMLJS_DECLARE_AST_NODE(ObjectPattern)
- ObjectLiteral()
+ ObjectPattern()
{ kind = K; }
- ObjectLiteral(PropertyAssignmentList *plist):
- properties (plist) { kind = K; }
+ ObjectPattern(PatternPropertyList *plist)
+ : properties(plist)
+ { kind = K; }
void accept0(Visitor *visitor) override;
@@ -502,8 +600,10 @@ public:
SourceLocation lastSourceLocation() const override
{ return rbraceToken; }
+ bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) override;
+
// attributes
- PropertyAssignmentList *properties = nullptr;
+ PatternPropertyList *properties = nullptr;
SourceLocation lbraceToken;
SourceLocation rbraceToken;
};
@@ -543,53 +643,6 @@ public:
SourceLocation commaToken;
};
-class QML_PARSER_EXPORT ElementList: public Node
-{
-public:
- QQMLJS_DECLARE_AST_NODE(ElementList)
-
- ElementList(Elision *e, ExpressionNode *expr):
- elision (e), expression (expr), next (this)
- { kind = K; }
-
- ElementList(ElementList *previous, Elision *e, ExpressionNode *expr):
- elision (e), expression (expr)
- {
- kind = K;
- next = previous->next;
- previous->next = this;
- }
-
- inline ElementList *finish ()
- {
- ElementList *front = next;
- next = nullptr;
- return front;
- }
-
- void accept0(Visitor *visitor) override;
-
- SourceLocation firstSourceLocation() const override
- {
- if (elision)
- return elision->firstSourceLocation();
- return expression->firstSourceLocation();
- }
-
- SourceLocation lastSourceLocation() const override
- {
- if (next)
- return next->lastSourceLocation();
- return expression->lastSourceLocation();
- }
-
-// attributes
- Elision *elision;
- ExpressionNode *expression;
- ElementList *next;
- SourceLocation commaToken;
-};
-
class QML_PARSER_EXPORT PropertyName: public Node
{
public:
@@ -609,116 +662,186 @@ public:
SourceLocation propertyNameToken;
};
-class QML_PARSER_EXPORT PropertyAssignment: public Node
+class QML_PARSER_EXPORT PatternElement : public Node
{
public:
- PropertyAssignment(PropertyName *n)
- : name(n)
- {}
+ QQMLJS_DECLARE_AST_NODE(PatternElement)
+
+ enum Type {
+ // object literal types
+ Literal,
+ Method,
+ Getter,
+ Setter,
+
+ // used by both bindings and literals
+ SpreadElement,
+ RestElement = SpreadElement,
+
+ // binding types
+ Binding,
+ };
+
+ PatternElement(ExpressionNode *i = nullptr, Type t = Literal)
+ : initializer(i), type(t)
+ { kind = K; }
+
+ PatternElement(const QStringRef &n, ExpressionNode *i = nullptr, Type t = Binding)
+ : bindingIdentifier(n), initializer(i), type(t)
+ {
+ Q_ASSERT(t >= RestElement);
+ kind = K;
+ }
+
+ PatternElement(Pattern *pattern, ExpressionNode *i = nullptr, Type t = Binding)
+ : bindingTarget(pattern), initializer(i), type(t)
+ {
+ Q_ASSERT(t >= RestElement);
+ kind = K;
+ }
+
+ void accept0(Visitor *visitor) override;
+ virtual bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage);
+
+ SourceLocation firstSourceLocation() const override
+ { return identifierToken.isValid() ? identifierToken : (bindingTarget ? bindingTarget->firstSourceLocation() : initializer->firstSourceLocation()); }
+
+ SourceLocation lastSourceLocation() const override
+ { return initializer ? initializer->lastSourceLocation() : (bindingTarget ? bindingTarget->lastSourceLocation() : identifierToken); }
+
+ ExpressionNode *destructuringTarget() const { return bindingTarget; }
+ Pattern *destructuringPattern() const { return bindingTarget ? bindingTarget->patternCast() : nullptr; }
+ PatternElementList *elementList() const { ArrayPattern *a = cast<ArrayPattern *>(bindingTarget); return a ? a->elements : nullptr; }
+ PatternPropertyList *propertyList() const { ObjectPattern *o = cast<ObjectPattern *>(bindingTarget); return o ? o->properties : nullptr; }
+
+ bool isVariableDeclaration() const { return scope != VariableScope::NoScope; }
+ bool isLexicallyScoped() const { return scope == VariableScope::Let || scope == VariableScope::Const; }
+
+ virtual void boundNames(QStringList *names);
+
// attributes
- PropertyName *name;
+ SourceLocation identifierToken;
+ QStringRef bindingIdentifier;
+ ExpressionNode *bindingTarget = nullptr;
+ ExpressionNode *initializer = nullptr;
+ Type type = Literal;
+ // when used in a VariableDeclarationList
+ VariableScope scope = VariableScope::NoScope;
+ bool isForDeclaration = false;
};
-class QML_PARSER_EXPORT PropertyAssignmentList: public Node
+class QML_PARSER_EXPORT PatternElementList : public Node
{
public:
- QQMLJS_DECLARE_AST_NODE(PropertyAssignmentList)
+ QQMLJS_DECLARE_AST_NODE(PatternElementList)
- PropertyAssignmentList(PropertyAssignment *assignment)
- : assignment(assignment)
- , next(this)
+ PatternElementList(Elision *elision, PatternElement *element)
+ : elision(elision), element(element), next(this)
{ kind = K; }
- PropertyAssignmentList(PropertyAssignmentList *previous, PropertyAssignment *assignment)
- : assignment(assignment)
- {
- kind = K;
- next = previous->next;
- previous->next = this;
+ PatternElementList *append(PatternElementList *n) {
+ n->next = next;
+ next = n;
+ return n;
}
- inline PropertyAssignmentList *finish ()
+ inline PatternElementList *finish ()
{
- PropertyAssignmentList *front = next;
- next = nullptr;
+ PatternElementList *front = next;
+ next = 0;
return front;
}
void accept0(Visitor *visitor) override;
+ void boundNames(QStringList *names);
+
SourceLocation firstSourceLocation() const override
- { return assignment->firstSourceLocation(); }
+ { return elision ? elision->firstSourceLocation() : element->firstSourceLocation(); }
SourceLocation lastSourceLocation() const override
- { return next ? next->lastSourceLocation() : assignment->lastSourceLocation(); }
+ { return next ? next->lastSourceLocation() : (element ? element->lastSourceLocation() : elision->lastSourceLocation()); }
-// attributes
- PropertyAssignment *assignment;
- PropertyAssignmentList *next;
- SourceLocation commaToken;
+ Elision *elision = nullptr;
+ PatternElement *element = nullptr;
+ PatternElementList *next;
};
-class QML_PARSER_EXPORT PropertyNameAndValue: public PropertyAssignment
+class QML_PARSER_EXPORT PatternProperty : public PatternElement
{
public:
- QQMLJS_DECLARE_AST_NODE(PropertyNameAndValue)
+ QQMLJS_DECLARE_AST_NODE(PatternProperty)
- PropertyNameAndValue(PropertyName *n, ExpressionNode *v)
- : PropertyAssignment(n), value(v)
+ PatternProperty(PropertyName *name, ExpressionNode *i = nullptr, Type t = Literal)
+ : PatternElement(i, t), name(name)
+ { kind = K; }
+
+ PatternProperty(PropertyName *name, const QStringRef &n, ExpressionNode *i = nullptr)
+ : PatternElement(n, i), name(name)
+ { kind = K; }
+
+ PatternProperty(PropertyName *name, Pattern *pattern, ExpressionNode *i = nullptr)
+ : PatternElement(pattern, i), name(name)
{ kind = K; }
void accept0(Visitor *visitor) override;
SourceLocation firstSourceLocation() const override
{ return name->firstSourceLocation(); }
-
SourceLocation lastSourceLocation() const override
- { return value->lastSourceLocation(); }
+ {
+ SourceLocation loc = PatternElement::lastSourceLocation();
+ return loc.isValid() ? loc : name->lastSourceLocation();
+ }
+
+ void boundNames(QStringList *names) override;
+ bool convertLiteralToAssignmentPattern(MemoryPool *pool, SourceLocation *errorLocation, QString *errorMessage) override;
// attributes
+ PropertyName *name;
SourceLocation colonToken;
- ExpressionNode *value;
- SourceLocation commaToken;
};
-class QML_PARSER_EXPORT PropertyGetterSetter: public PropertyAssignment
+
+class QML_PARSER_EXPORT PatternPropertyList : public Node
{
public:
- QQMLJS_DECLARE_AST_NODE(PropertyGetterSetter)
+ QQMLJS_DECLARE_AST_NODE(PatternPropertyList)
- enum Type {
- Getter,
- Setter
- };
-
- PropertyGetterSetter(PropertyName *n, FunctionBody *b)
- : PropertyAssignment(n), type(Getter), formals(nullptr), functionBody (b)
+ PatternPropertyList(PatternProperty *property)
+ : property(property), next(this)
{ kind = K; }
- PropertyGetterSetter(PropertyName *n, FormalParameterList *f, FunctionBody *b)
- : PropertyAssignment(n), type(Setter), formals(f), functionBody (b)
- { kind = K; }
+ PatternPropertyList(PatternPropertyList *previous, PatternProperty *property)
+ : property(property), next(this)
+ {
+ kind = K;
+ next = previous->next;
+ previous->next = this;
+ }
void accept0(Visitor *visitor) override;
+ void boundNames(QStringList *names);
+
+ inline PatternPropertyList *finish ()
+ {
+ PatternPropertyList *front = next;
+ next = 0;
+ return front;
+ }
+
SourceLocation firstSourceLocation() const override
- { return getSetToken; }
+ { return property->firstSourceLocation(); }
SourceLocation lastSourceLocation() const override
- { return rbraceToken; }
+ { return next ? next->lastSourceLocation() : property->lastSourceLocation(); }
-// attributes
- Type type;
- SourceLocation getSetToken;
- SourceLocation lparenToken;
- FormalParameterList *formals;
- SourceLocation rparenToken;
- SourceLocation lbraceToken;
- FunctionBody *functionBody;
- SourceLocation rbraceToken;
+ PatternProperty *property;
+ PatternPropertyList *next;
};
-class QML_PARSER_EXPORT IdentifierPropertyName: public PropertyName
+class QML_PARSER_EXPORT IdentifierPropertyName : public PropertyName
{
public:
QQMLJS_DECLARE_AST_NODE(IdentifierPropertyName)
@@ -766,7 +889,31 @@ public:
double id;
};
-class QML_PARSER_EXPORT ArrayMemberExpression: public ExpressionNode
+class QML_PARSER_EXPORT ComputedPropertyName : public PropertyName
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(ComputedPropertyName)
+
+ ComputedPropertyName(ExpressionNode *expression)
+ : expression(expression)
+ { kind = K; }
+
+ void accept0(Visitor *visitor) override;
+
+ QString asString() const override { return QString(); }
+
+ SourceLocation firstSourceLocation() const override
+ { return expression->firstSourceLocation(); }
+
+ SourceLocation lastSourceLocation() const override
+ { return expression->lastSourceLocation(); }
+
+// attributes
+ ExpressionNode *expression;
+};
+
+
+class QML_PARSER_EXPORT ArrayMemberExpression: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(ArrayMemberExpression)
@@ -790,7 +937,7 @@ public:
SourceLocation rbracketToken;
};
-class QML_PARSER_EXPORT FieldMemberExpression: public ExpressionNode
+class QML_PARSER_EXPORT FieldMemberExpression: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(FieldMemberExpression)
@@ -814,7 +961,29 @@ public:
SourceLocation identifierToken;
};
-class QML_PARSER_EXPORT NewMemberExpression: public ExpressionNode
+class QML_PARSER_EXPORT TaggedTemplate : public LeftHandSideExpression
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(TaggedTemplate)
+
+ TaggedTemplate(ExpressionNode *b, TemplateLiteral *t)
+ : base (b), templateLiteral(t)
+ { kind = K; }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return base->firstSourceLocation(); }
+
+ SourceLocation lastSourceLocation() const override
+ { return templateLiteral->lastSourceLocation(); }
+
+ // attributes
+ ExpressionNode *base;
+ TemplateLiteral *templateLiteral;
+};
+
+class QML_PARSER_EXPORT NewMemberExpression: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(NewMemberExpression)
@@ -839,7 +1008,7 @@ public:
SourceLocation rparenToken;
};
-class QML_PARSER_EXPORT NewExpression: public ExpressionNode
+class QML_PARSER_EXPORT NewExpression: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(NewExpression)
@@ -860,7 +1029,7 @@ public:
SourceLocation newToken;
};
-class QML_PARSER_EXPORT CallExpression: public ExpressionNode
+class QML_PARSER_EXPORT CallExpression: public LeftHandSideExpression
{
public:
QQMLJS_DECLARE_AST_NODE(CallExpression)
@@ -924,6 +1093,7 @@ public:
ExpressionNode *expression;
ArgumentList *next;
SourceLocation commaToken;
+ bool isSpreadElement = false;
};
class QML_PARSER_EXPORT PostIncrementExpression: public ExpressionNode
@@ -1257,16 +1427,15 @@ class QML_PARSER_EXPORT StatementList: public Node
public:
QQMLJS_DECLARE_AST_NODE(StatementList)
- StatementList(Statement *stmt):
- statement (stmt), next (this)
- { kind = K; }
+ // ### This should be a Statement, but FunctionDeclaration currently doesn't inherit it.
+ StatementList(Node *stmt)
+ : statement(stmt), next (this)
+ { kind = K; }
- StatementList(StatementList *previous, Statement *stmt):
- statement (stmt)
- {
- kind = K;
- next = previous->next;
- previous->next = this;
+ StatementList *append(StatementList *n) {
+ n->next = next;
+ next = n;
+ return n;
}
void accept0(Visitor *visitor) override;
@@ -1285,76 +1454,21 @@ public:
}
// attributes
- Statement *statement;
+ Node *statement = nullptr;
StatementList *next;
};
-class QML_PARSER_EXPORT VariableStatement: public Statement
-{
-public:
- QQMLJS_DECLARE_AST_NODE(VariableStatement)
-
- VariableStatement(VariableDeclarationList *vlist):
- declarations (vlist)
- { kind = K; }
-
- void accept0(Visitor *visitor) override;
-
- SourceLocation firstSourceLocation() const override
- { return declarationKindToken; }
-
- SourceLocation lastSourceLocation() const override
- { return semicolonToken; }
-
-// attributes
- VariableDeclarationList *declarations;
- SourceLocation declarationKindToken;
- SourceLocation semicolonToken;
-};
-
-class QML_PARSER_EXPORT VariableDeclaration: public Node
-{
-public:
- QQMLJS_DECLARE_AST_NODE(VariableDeclaration)
-
- enum VariableScope {
- FunctionScope,
- BlockScope, // let
- ReadOnlyBlockScope // const
- };
-
- VariableDeclaration(const QStringRef &n, ExpressionNode *e, VariableScope s):
- name (n), expression (e), scope(s)
- { kind = K; }
-
- bool isLexicallyScoped() const { return scope != FunctionScope; }
-
- void accept0(Visitor *visitor) override;
-
- SourceLocation firstSourceLocation() const override
- { return identifierToken; }
-
- SourceLocation lastSourceLocation() const override
- { return expression ? expression->lastSourceLocation() : identifierToken; }
-
-// attributes
- QStringRef name;
- ExpressionNode *expression;
- SourceLocation identifierToken;
- VariableScope scope;
-};
-
class QML_PARSER_EXPORT VariableDeclarationList: public Node
{
public:
QQMLJS_DECLARE_AST_NODE(VariableDeclarationList)
- VariableDeclarationList(VariableDeclaration *decl):
- declaration (decl), next (this)
- { kind = K; }
+ VariableDeclarationList(PatternElement *decl)
+ : declaration(decl), next(this)
+ { kind = K; }
- VariableDeclarationList(VariableDeclarationList *previous, VariableDeclaration *decl):
- declaration (decl)
+ VariableDeclarationList(VariableDeclarationList *previous, PatternElement *decl)
+ : declaration(decl)
{
kind = K;
next = previous->next;
@@ -1373,7 +1487,7 @@ public:
return declaration->lastSourceLocation();
}
- inline VariableDeclarationList *finish(VariableDeclaration::VariableScope s)
+ inline VariableDeclarationList *finish(VariableScope s)
{
VariableDeclarationList *front = next;
next = nullptr;
@@ -1385,11 +1499,33 @@ public:
}
// attributes
- VariableDeclaration *declaration;
+ PatternElement *declaration;
VariableDeclarationList *next;
SourceLocation commaToken;
};
+class QML_PARSER_EXPORT VariableStatement: public Statement
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(VariableStatement)
+
+ VariableStatement(VariableDeclarationList *vlist):
+ declarations (vlist)
+ { kind = K; }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return declarationKindToken; }
+
+ SourceLocation lastSourceLocation() const override
+ { return declarations->lastSourceLocation(); }
+
+// attributes
+ VariableDeclarationList *declarations;
+ SourceLocation declarationKindToken;
+};
+
class QML_PARSER_EXPORT EmptyStatement: public Statement
{
public:
@@ -1423,7 +1559,7 @@ public:
{ return expression->firstSourceLocation(); }
SourceLocation lastSourceLocation() const override
- { return semicolonToken; }
+ { return expression->lastSourceLocation(); }
// attributes
ExpressionNode *expression;
@@ -1523,35 +1659,11 @@ public:
initialiser (i), condition (c), expression (e), statement (stmt)
{ kind = K; }
- void accept0(Visitor *visitor) override;
-
- SourceLocation firstSourceLocation() const override
- { return forToken; }
-
- SourceLocation lastSourceLocation() const override
- { return statement->lastSourceLocation(); }
-
-// attributes
- ExpressionNode *initialiser;
- ExpressionNode *condition;
- ExpressionNode *expression;
- Statement *statement;
- SourceLocation forToken;
- SourceLocation lparenToken;
- SourceLocation firstSemicolonToken;
- SourceLocation secondSemicolonToken;
- SourceLocation rparenToken;
-};
-
-class QML_PARSER_EXPORT LocalForStatement: public Statement
-{
-public:
- QQMLJS_DECLARE_AST_NODE(LocalForStatement)
-
- LocalForStatement(VariableDeclarationList *vlist, ExpressionNode *c, ExpressionNode *e, Statement *stmt):
+ ForStatement(VariableDeclarationList *vlist, ExpressionNode *c, ExpressionNode *e, Statement *stmt):
declarations (vlist), condition (c), expression (e), statement (stmt)
{ kind = K; }
+
void accept0(Visitor *visitor) override;
SourceLocation firstSourceLocation() const override
@@ -1561,26 +1673,34 @@ public:
{ return statement->lastSourceLocation(); }
// attributes
- VariableDeclarationList *declarations;
+ ExpressionNode *initialiser = nullptr;
+ VariableDeclarationList *declarations = nullptr;
ExpressionNode *condition;
ExpressionNode *expression;
Statement *statement;
SourceLocation forToken;
SourceLocation lparenToken;
- SourceLocation varToken;
SourceLocation firstSemicolonToken;
SourceLocation secondSemicolonToken;
SourceLocation rparenToken;
};
+enum class ForEachType {
+ In,
+ Of
+};
+
class QML_PARSER_EXPORT ForEachStatement: public Statement
{
public:
QQMLJS_DECLARE_AST_NODE(ForEachStatement)
- ForEachStatement(ExpressionNode *i, ExpressionNode *e, Statement *stmt):
- initialiser (i), expression (e), statement (stmt)
- { kind = K; }
+ ForEachStatement(ExpressionNode *i, ExpressionNode *e, Statement *stmt)
+ : lhs(i), expression(e), statement(stmt)
+ { kind = K; }
+ ForEachStatement(PatternElement *v, ExpressionNode *e, Statement *stmt)
+ : lhs(v), expression(e), statement(stmt)
+ { kind = K; }
void accept0(Visitor *visitor) override;
@@ -1590,42 +1710,19 @@ public:
SourceLocation lastSourceLocation() const override
{ return statement->lastSourceLocation(); }
-// attributes
- ExpressionNode *initialiser;
- ExpressionNode *expression;
- Statement *statement;
- SourceLocation forToken;
- SourceLocation lparenToken;
- SourceLocation inToken;
- SourceLocation rparenToken;
-};
-
-class QML_PARSER_EXPORT LocalForEachStatement: public Statement
-{
-public:
- QQMLJS_DECLARE_AST_NODE(LocalForEachStatement)
-
- LocalForEachStatement(VariableDeclaration *v, ExpressionNode *e, Statement *stmt):
- declaration (v), expression (e), statement (stmt)
- { kind = K; }
-
- void accept0(Visitor *visitor) override;
-
- SourceLocation firstSourceLocation() const override
- { return forToken; }
-
- SourceLocation lastSourceLocation() const override
- { return statement->lastSourceLocation(); }
+ PatternElement *declaration() const {
+ return AST::cast<PatternElement *>(lhs);
+ }
// attributes
- VariableDeclaration *declaration;
+ Node *lhs;
ExpressionNode *expression;
Statement *statement;
SourceLocation forToken;
SourceLocation lparenToken;
- SourceLocation varToken;
- SourceLocation inToken;
+ SourceLocation inOfToken;
SourceLocation rparenToken;
+ ForEachType type;
};
class QML_PARSER_EXPORT ContinueStatement: public Statement
@@ -1696,6 +1793,28 @@ public:
SourceLocation semicolonToken;
};
+class QML_PARSER_EXPORT YieldExpression: public ExpressionNode
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(YieldExpression)
+
+ YieldExpression(ExpressionNode *e = nullptr):
+ expression (e) { kind = K; }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return yieldToken; }
+
+ SourceLocation lastSourceLocation() const override
+ { return expression ? expression->lastSourceLocation() : yieldToken; }
+
+// attributes
+ ExpressionNode *expression;
+ bool isYieldStar = false;
+ SourceLocation yieldToken;
+};
+
class QML_PARSER_EXPORT WithStatement: public Statement
{
public:
@@ -1906,9 +2025,9 @@ class QML_PARSER_EXPORT Catch: public Node
public:
QQMLJS_DECLARE_AST_NODE(Catch)
- Catch(const QStringRef &n, Block *stmt):
- name (n), statement (stmt)
- { kind = K; }
+ Catch(PatternElement *p, Block *stmt)
+ : patternElement(p), statement(stmt)
+ { kind = K; }
void accept0(Visitor *visitor) override;
@@ -1919,7 +2038,7 @@ public:
{ return statement->lastSourceLocation(); }
// attributes
- QStringRef name;
+ PatternElement *patternElement;
Block *statement;
SourceLocation catchToken;
SourceLocation lparenToken;
@@ -1993,7 +2112,7 @@ class QML_PARSER_EXPORT FunctionExpression: public ExpressionNode
public:
QQMLJS_DECLARE_AST_NODE(FunctionExpression)
- FunctionExpression(const QStringRef &n, FormalParameterList *f, FunctionBody *b):
+ FunctionExpression(const QStringRef &n, FormalParameterList *f, StatementList *b):
name (n), formals (f), body (b)
{ kind = K; }
@@ -2005,10 +2124,14 @@ public:
SourceLocation lastSourceLocation() const override
{ return rbraceToken; }
+ FunctionExpression *asFunctionDefinition() override;
+
// attributes
QStringRef name;
+ bool isArrowFunction = false;
+ bool isGenerator = false;
FormalParameterList *formals;
- FunctionBody *body;
+ StatementList *body;
SourceLocation functionToken;
SourceLocation identifierToken;
SourceLocation lparenToken;
@@ -2022,7 +2145,7 @@ class QML_PARSER_EXPORT FunctionDeclaration: public FunctionExpression
public:
QQMLJS_DECLARE_AST_NODE(FunctionDeclaration)
- FunctionDeclaration(const QStringRef &n, FormalParameterList *f, FunctionBody *b):
+ FunctionDeclaration(const QStringRef &n, FormalParameterList *f, StatementList *b):
FunctionExpression(n, f, b)
{ kind = K; }
@@ -2034,168 +2157,593 @@ class QML_PARSER_EXPORT FormalParameterList: public Node
public:
QQMLJS_DECLARE_AST_NODE(FormalParameterList)
- FormalParameterList(const QStringRef &n):
- name (n), next (this)
- { kind = K; }
-
- FormalParameterList(FormalParameterList *previous, const QStringRef &n):
- name (n)
+ FormalParameterList(FormalParameterList *previous, PatternElement *e)
+ : element(e)
{
kind = K;
- next = previous->next;
- previous->next = this;
+ if (previous) {
+ next = previous->next;
+ previous->next = this;
+ } else {
+ next = this;
+ }
+ }
+
+ FormalParameterList *append(FormalParameterList *n) {
+ n->next = next;
+ next = n;
+ return n;
+ }
+
+ bool isSimpleParameterList()
+ {
+ AST::FormalParameterList *formals = this;
+ while (formals) {
+ PatternElement *e = formals->element;
+ if (e && e->type == PatternElement::RestElement)
+ return false;
+ if (e && (e->initializer || e->bindingTarget))
+ return false;
+ formals = formals->next;
+ }
+ return true;
}
+ int length()
+ {
+ // the length property of Function objects
+ int l = 0;
+ AST::FormalParameterList *formals = this;
+ while (formals) {
+ PatternElement *e = formals->element;
+ if (!e || e->initializer)
+ break;
+ if (e->type == PatternElement::RestElement)
+ break;
+ ++l;
+ formals = formals->next;
+ }
+ return l;
+ }
+
+ bool containsName(const QString &name) const {
+ for (const FormalParameterList *it = this; it; it = it->next) {
+ PatternElement *b = it->element;
+ // ### handle binding patterns
+ if (b && b->bindingIdentifier == name)
+ return true;
+ }
+ return false;
+ }
+
+ QStringList formals() const;
+
+ QStringList boundNames() const;
+
void accept0(Visitor *visitor) override;
SourceLocation firstSourceLocation() const override
- { return identifierToken; }
+ { return element->firstSourceLocation(); }
SourceLocation lastSourceLocation() const override
- { return next ? next->lastSourceLocation() : identifierToken; }
+ { return next ? next->lastSourceLocation() : element->lastSourceLocation(); }
- inline FormalParameterList *finish ()
- {
- FormalParameterList *front = next;
- next = nullptr;
- return front;
- }
+ FormalParameterList *finish(MemoryPool *pool);
// attributes
- QStringRef name;
+ PatternElement *element = nullptr;
FormalParameterList *next;
- SourceLocation commaToken;
- SourceLocation identifierToken;
};
-class QML_PARSER_EXPORT SourceElement: public Node
+class QML_PARSER_EXPORT ClassExpression : public ExpressionNode
{
public:
- QQMLJS_DECLARE_AST_NODE(SourceElement)
+ QQMLJS_DECLARE_AST_NODE(ClassExpression)
- inline SourceElement()
+ ClassExpression(const QStringRef &n, ExpressionNode *heritage, ClassElementList *elements)
+ : name(n), heritage(heritage), elements(elements)
{ kind = K; }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return classToken; }
+
+ SourceLocation lastSourceLocation() const override
+ { return rbraceToken; }
+
+ ClassExpression *asClassDefinition() override;
+
+// attributes
+ QStringRef name;
+ ExpressionNode *heritage;
+ ClassElementList *elements;
+ SourceLocation classToken;
+ SourceLocation identifierToken;
+ SourceLocation lbraceToken;
+ SourceLocation rbraceToken;
};
-class QML_PARSER_EXPORT SourceElements: public Node
+class QML_PARSER_EXPORT ClassDeclaration: public ClassExpression
{
public:
- QQMLJS_DECLARE_AST_NODE(SourceElements)
+ QQMLJS_DECLARE_AST_NODE(ClassDeclaration)
- SourceElements(SourceElement *elt):
- element (elt), next (this)
+ ClassDeclaration(const QStringRef &n, ExpressionNode *heritage, ClassElementList *elements)
+ : ClassExpression(n, heritage, elements)
{ kind = K; }
- SourceElements(SourceElements *previous, SourceElement *elt):
- element (elt)
+ void accept0(Visitor *visitor) override;
+};
+
+
+class QML_PARSER_EXPORT ClassElementList : public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(ClassElementList)
+
+ ClassElementList(PatternProperty *property, bool isStatic)
+ : isStatic(isStatic), property(property)
{
kind = K;
- next = previous->next;
- previous->next = this;
+ next = this;
+ }
+
+ ClassElementList *append(ClassElementList *n) {
+ n->next = next;
+ next = n;
+ return n;
}
void accept0(Visitor *visitor) override;
SourceLocation firstSourceLocation() const override
- { return element->firstSourceLocation(); }
+ { return property->firstSourceLocation(); }
SourceLocation lastSourceLocation() const override
- { return next ? next->lastSourceLocation() : element->lastSourceLocation(); }
+ {
+ if (next)
+ return next->lastSourceLocation();
+ return property->lastSourceLocation();
+ }
+
+ ClassElementList *finish();
+
+ bool isStatic;
+ ClassElementList *next;
+ PatternProperty *property;
+};
+
+class QML_PARSER_EXPORT Program: public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(Program)
+
+ Program(StatementList *statements)
+ : statements(statements)
+ { kind = K; }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return statements ? statements->firstSourceLocation() : SourceLocation(); }
+
+ SourceLocation lastSourceLocation() const override
+ { return statements ? statements->lastSourceLocation() : SourceLocation(); }
+
+// attributes
+ StatementList *statements;
+};
+
+class QML_PARSER_EXPORT ImportSpecifier: public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(ImportSpecifier)
+
+ ImportSpecifier(const QStringRef &importedBinding)
+ : importedBinding(importedBinding)
+ {
+ kind = K;
+ }
+
+ ImportSpecifier(const QStringRef &identifier, const QStringRef &importedBinding)
+ : identifier(identifier), importedBinding(importedBinding)
+ {
+ kind = K;
+ }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return identifier.isNull() ? importedBindingToken : identifierToken; }
+ SourceLocation lastSourceLocation() const override
+ { return importedBindingToken; }
+
+// attributes
+ SourceLocation identifierToken;
+ SourceLocation importedBindingToken;
+ QStringRef identifier;
+ QStringRef importedBinding;
+};
+
+class QML_PARSER_EXPORT ImportsList: public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(ImportsList)
- inline SourceElements *finish ()
+ ImportsList(ImportSpecifier *importSpecifier)
+ : importSpecifier(importSpecifier)
{
- SourceElements *front = next;
+ kind = K;
+ next = this;
+ }
+
+ ImportsList(ImportsList *previous, ImportSpecifier *importSpecifier)
+ : importSpecifier(importSpecifier)
+ {
+ kind = K;
+ if (previous) {
+ next = previous->next;
+ previous->next = this;
+ } else {
+ next = this;
+ }
+ }
+
+ ImportsList *finish()
+ {
+ ImportsList *head = next;
next = nullptr;
- return front;
+ return head;
}
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return importSpecifierToken; }
+
+ SourceLocation lastSourceLocation() const override
+ { return next ? next->lastSourceLocation() : importSpecifierToken; }
+
// attributes
- SourceElement *element;
- SourceElements *next;
+ SourceLocation importSpecifierToken;
+ ImportSpecifier *importSpecifier;
+ ImportsList *next = this;
};
-class QML_PARSER_EXPORT FunctionBody: public Node
+class QML_PARSER_EXPORT NamedImports: public Node
{
public:
- QQMLJS_DECLARE_AST_NODE(FunctionBody)
+ QQMLJS_DECLARE_AST_NODE(NamedImports)
- FunctionBody(SourceElements *elts):
- elements (elts)
- { kind = K; }
+ NamedImports()
+ {
+ kind = K;
+ }
+
+ NamedImports(ImportsList *importsList)
+ : importsList(importsList)
+ {
+ kind = K;
+ }
void accept0(Visitor *visitor) override;
SourceLocation firstSourceLocation() const override
- { return elements ? elements->firstSourceLocation() : SourceLocation(); }
+ { return leftBraceToken; }
+ SourceLocation lastSourceLocation() const override
+ { return rightBraceToken; }
+
+// attributes
+ SourceLocation leftBraceToken;
+ SourceLocation rightBraceToken;
+ ImportsList *importsList = nullptr;
+};
+
+class QML_PARSER_EXPORT NameSpaceImport: public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(NameSpaceImport)
+
+ NameSpaceImport(const QStringRef &importedBinding)
+ : importedBinding(importedBinding)
+ {
+ kind = K;
+ }
+
+ void accept0(Visitor *visitor) override;
+
+ virtual SourceLocation firstSourceLocation() const override
+ { return starToken; }
+ virtual SourceLocation lastSourceLocation() const override
+ { return importedBindingToken; }
+
+// attributes
+ SourceLocation starToken;
+ SourceLocation importedBindingToken;
+ QStringRef importedBinding;
+};
+
+class QML_PARSER_EXPORT ImportClause: public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(ImportClause)
+
+ ImportClause(const QStringRef &importedDefaultBinding)
+ : importedDefaultBinding(importedDefaultBinding)
+ {
+ kind = K;
+ }
+
+ ImportClause(NameSpaceImport *nameSpaceImport)
+ : nameSpaceImport(nameSpaceImport)
+ {
+ kind = K;
+ }
+
+ ImportClause(NamedImports *namedImports)
+ : namedImports(namedImports)
+ {
+ kind = K;
+ }
+
+ ImportClause(const QStringRef &importedDefaultBinding, NameSpaceImport *nameSpaceImport)
+ : importedDefaultBinding(importedDefaultBinding)
+ , nameSpaceImport(nameSpaceImport)
+ {
+ kind = K;
+ }
+
+ ImportClause(const QStringRef &importedDefaultBinding, NamedImports *namedImports)
+ : importedDefaultBinding(importedDefaultBinding)
+ , namedImports(namedImports)
+ {
+ kind = K;
+ }
+
+ void accept0(Visitor *visitor) override;
+
+ virtual SourceLocation firstSourceLocation() const override
+ { return importedDefaultBinding.isNull() ? (nameSpaceImport ? nameSpaceImport->firstSourceLocation() : namedImports->firstSourceLocation()) : importedDefaultBindingToken; }
+ virtual SourceLocation lastSourceLocation() const override
+ { return importedDefaultBinding.isNull() ? (nameSpaceImport ? nameSpaceImport->lastSourceLocation() : namedImports->lastSourceLocation()) : importedDefaultBindingToken; }
+
+// attributes
+ SourceLocation importedDefaultBindingToken;
+ QStringRef importedDefaultBinding;
+ NameSpaceImport *nameSpaceImport = nullptr;
+ NamedImports *namedImports = nullptr;
+};
+
+class QML_PARSER_EXPORT FromClause: public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(FromClause)
+
+ FromClause(const QStringRef &moduleSpecifier)
+ : moduleSpecifier(moduleSpecifier)
+ {
+ kind = K;
+ }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return fromToken; }
SourceLocation lastSourceLocation() const override
- { return elements ? elements->lastSourceLocation() : SourceLocation(); }
+ { return moduleSpecifierToken; }
// attributes
- SourceElements *elements;
+ SourceLocation fromToken;
+ SourceLocation moduleSpecifierToken;
+ QStringRef moduleSpecifier;
};
-class QML_PARSER_EXPORT Program: public Node
+class QML_PARSER_EXPORT ImportDeclaration: public Statement
{
public:
- QQMLJS_DECLARE_AST_NODE(Program)
+ QQMLJS_DECLARE_AST_NODE(ImportDeclaration)
- Program(SourceElements *elts):
- elements (elts)
- { kind = K; }
+ ImportDeclaration(ImportClause *importClause, FromClause *fromClause)
+ : importClause(importClause), fromClause(fromClause)
+ {
+ kind = K;
+ }
+
+ ImportDeclaration(const QStringRef &moduleSpecifier)
+ : moduleSpecifier(moduleSpecifier)
+ {
+ kind = K;
+ }
void accept0(Visitor *visitor) override;
SourceLocation firstSourceLocation() const override
- { return elements ? elements->firstSourceLocation() : SourceLocation(); }
+ { return importToken; }
SourceLocation lastSourceLocation() const override
- { return elements ? elements->lastSourceLocation() : SourceLocation(); }
+ { return moduleSpecifier.isNull() ? fromClause->lastSourceLocation() : moduleSpecifierToken; }
// attributes
- SourceElements *elements;
+ SourceLocation importToken;
+ SourceLocation moduleSpecifierToken;
+ QStringRef moduleSpecifier;
+ ImportClause *importClause = nullptr;
+ FromClause *fromClause = nullptr;
};
-class QML_PARSER_EXPORT FunctionSourceElement: public SourceElement
+class QML_PARSER_EXPORT ExportSpecifier: public Node
{
public:
- QQMLJS_DECLARE_AST_NODE(FunctionSourceElement)
+ QQMLJS_DECLARE_AST_NODE(ExportSpecifier)
- FunctionSourceElement(FunctionDeclaration *f):
- declaration (f)
- { kind = K; }
+ ExportSpecifier(const QStringRef &identifier)
+ : identifier(identifier), exportedIdentifier(identifier)
+ {
+ kind = K;
+ }
+
+ ExportSpecifier(const QStringRef &identifier, const QStringRef &exportedIdentifier)
+ : identifier(identifier), exportedIdentifier(exportedIdentifier)
+ {
+ kind = K;
+ }
void accept0(Visitor *visitor) override;
SourceLocation firstSourceLocation() const override
- { return declaration->firstSourceLocation(); }
+ { return identifierToken; }
+ SourceLocation lastSourceLocation() const override
+ { return exportedIdentifierToken.isValid() ? exportedIdentifierToken : identifierToken; }
+
+// attributes
+ SourceLocation identifierToken;
+ SourceLocation exportedIdentifierToken;
+ QStringRef identifier;
+ QStringRef exportedIdentifier;
+};
+class QML_PARSER_EXPORT ExportsList: public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(ExportsList)
+
+ ExportsList(ExportSpecifier *exportSpecifier)
+ : exportSpecifier(exportSpecifier)
+ {
+ kind = K;
+ next = this;
+ }
+
+ ExportsList(ExportsList *previous, ExportSpecifier *exportSpecifier)
+ : exportSpecifier(exportSpecifier)
+ {
+ kind = K;
+ if (previous) {
+ next = previous->next;
+ previous->next = this;
+ } else {
+ next = this;
+ }
+ }
+
+ ExportsList *finish()
+ {
+ ExportsList *head = next;
+ next = nullptr;
+ return head;
+ }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return exportSpecifier->firstSourceLocation(); }
SourceLocation lastSourceLocation() const override
- { return declaration->lastSourceLocation(); }
+ { return next ? next->lastSourceLocation() : exportSpecifier->lastSourceLocation(); }
// attributes
- FunctionDeclaration *declaration;
+ ExportSpecifier *exportSpecifier;
+ ExportsList *next;
};
-class QML_PARSER_EXPORT StatementSourceElement: public SourceElement
+class QML_PARSER_EXPORT ExportClause: public Node
{
public:
- QQMLJS_DECLARE_AST_NODE(StatementSourceElement)
+ QQMLJS_DECLARE_AST_NODE(ExportClause)
- StatementSourceElement(Statement *stmt):
- statement (stmt)
- { kind = K; }
+ ExportClause()
+ {
+ kind = K;
+ }
+
+ ExportClause(ExportsList *exportsList)
+ : exportsList(exportsList)
+ {
+ kind = K;
+ }
void accept0(Visitor *visitor) override;
SourceLocation firstSourceLocation() const override
- { return statement->firstSourceLocation(); }
+ { return leftBraceToken; }
+ SourceLocation lastSourceLocation() const override
+ { return rightBraceToken; }
+// attributes
+ SourceLocation leftBraceToken;
+ SourceLocation rightBraceToken;
+ ExportsList *exportsList = nullptr;
+};
+
+class QML_PARSER_EXPORT ExportDeclaration: public Statement
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(ExportDeclaration)
+
+ ExportDeclaration(FromClause *fromClause)
+ : fromClause(fromClause)
+ {
+ exportAll = true;
+ kind = K;
+ }
+
+ ExportDeclaration(ExportClause *exportClause, FromClause *fromClause)
+ : exportClause(exportClause), fromClause(fromClause)
+ {
+ kind = K;
+ }
+
+ ExportDeclaration(ExportClause *exportClause)
+ : exportClause(exportClause)
+ {
+ kind = K;
+ }
+
+ ExportDeclaration(bool exportDefault, Node *variableStatementOrDeclaration)
+ : variableStatementOrDeclaration(variableStatementOrDeclaration)
+ , exportDefault(exportDefault)
+ {
+ kind = K;
+ }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return exportToken; }
SourceLocation lastSourceLocation() const override
- { return statement->lastSourceLocation(); }
+ { return fromClause ? fromClause->lastSourceLocation() : (exportClause ? exportClause->lastSourceLocation() : variableStatementOrDeclaration->lastSourceLocation()); }
// attributes
- Statement *statement;
+ SourceLocation exportToken;
+ bool exportAll = false;
+ ExportClause *exportClause = nullptr;
+ FromClause *fromClause = nullptr;
+ Node *variableStatementOrDeclaration = nullptr;
+ bool exportDefault = false;
+};
+
+class QML_PARSER_EXPORT ESModule: public Node
+{
+public:
+ QQMLJS_DECLARE_AST_NODE(Module)
+
+ ESModule(StatementList *body)
+ : body(body)
+ {
+ kind = K;
+ }
+
+ void accept0(Visitor *visitor) override;
+
+ SourceLocation firstSourceLocation() const override
+ { return body ? body->firstSourceLocation() : SourceLocation(); }
+
+ SourceLocation lastSourceLocation() const override
+ { return body ? body->lastSourceLocation() : SourceLocation(); }
+
+// attributes
+ StatementList *body;
};
class QML_PARSER_EXPORT DebuggerStatement: public Statement
@@ -2336,51 +2884,13 @@ public:
UiObjectMember *member;
};
-class QML_PARSER_EXPORT UiQualifiedPragmaId: public Node
-{
-public:
- QQMLJS_DECLARE_AST_NODE(UiQualifiedPragmaId)
-
- UiQualifiedPragmaId(const QStringRef &name)
- : next(this), name(name)
- { kind = K; }
-
- UiQualifiedPragmaId(UiQualifiedPragmaId *previous, const QStringRef &name)
- : name(name)
- {
- kind = K;
- next = previous->next;
- previous->next = this;
- }
-
- UiQualifiedPragmaId *finish()
- {
- UiQualifiedPragmaId *head = next;
- next = nullptr;
- return head;
- }
-
- void accept0(Visitor *visitor) override;
-
- SourceLocation firstSourceLocation() const override
- { return identifierToken; }
-
- SourceLocation lastSourceLocation() const override
- { return next ? next->lastSourceLocation() : identifierToken; }
-
-// attributes
- UiQualifiedPragmaId *next;
- QStringRef name;
- SourceLocation identifierToken;
-};
-
class QML_PARSER_EXPORT UiPragma: public Node
{
public:
QQMLJS_DECLARE_AST_NODE(UiPragma)
- UiPragma(UiQualifiedPragmaId *type)
- : pragmaType(type)
+ UiPragma(QStringRef name)
+ : name(name)
{ kind = K; }
void accept0(Visitor *visitor) override;
@@ -2392,7 +2902,7 @@ public:
{ return semicolonToken; }
// attributes
- UiQualifiedPragmaId *pragmaType;
+ QStringRef name;
SourceLocation pragmaToken;
SourceLocation semicolonToken;
};
diff --git a/src/qml/parser/qqmljsastfwd_p.h b/src/qml/parser/qqmljsastfwd_p.h
index 140a757e51..996264db59 100644
--- a/src/qml/parser/qqmljsastfwd_p.h
+++ b/src/qml/parser/qqmljsastfwd_p.h
@@ -89,22 +89,27 @@ class IdentifierExpression;
class NullExpression;
class TrueLiteral;
class FalseLiteral;
+class SuperLiteral;
class NumericLiteral;
class StringLiteral;
+class TemplateLiteral;
class RegExpLiteral;
-class ArrayLiteral;
-class ObjectLiteral;
-class ElementList;
+class Pattern;
+class ArrayPattern;
+class ObjectPattern;
+class PatternElement;
+class PatternElementList;
+class PatternProperty;
+class PatternPropertyList;
class Elision;
-class PropertyAssignmentList;
-class PropertyGetterSetter;
-class PropertyNameAndValue;
class PropertyName;
class IdentifierPropertyName;
class StringLiteralPropertyName;
class NumericLiteralPropertyName;
+class ComputedPropertyName;
class ArrayMemberExpression;
class FieldMemberExpression;
+class TaggedTemplate;
class NewMemberExpression;
class NewExpression;
class CallExpression;
@@ -123,20 +128,19 @@ class NotExpression;
class BinaryExpression;
class ConditionalExpression;
class Expression; // ### rename
+class YieldExpression;
class Block;
+class LeftHandSideExpression;
class StatementList;
class VariableStatement;
class VariableDeclarationList;
-class VariableDeclaration;
class EmptyStatement;
class ExpressionStatement;
class IfStatement;
class DoWhileStatement;
class WhileStatement;
class ForStatement;
-class LocalForStatement;
class ForEachStatement;
-class LocalForEachStatement;
class ContinueStatement;
class BreakStatement;
class ReturnStatement;
@@ -154,14 +158,26 @@ class Finally;
class FunctionDeclaration;
class FunctionExpression;
class FormalParameterList;
-class FunctionBody;
+class ExportSpecifier;
+class ExportsList;
+class ExportClause;
+class ExportDeclaration;
class Program;
-class SourceElements;
-class SourceElement;
-class FunctionSourceElement;
-class StatementSourceElement;
+class ImportSpecifier;
+class ImportsList;
+class NamedImports;
+class NameSpaceImport;
+class NamedImport;
+class ImportClause;
+class FromClause;
+class ImportDeclaration;
+class ModuleItem;
+class ESModule;
class DebuggerStatement;
class NestedExpression;
+class ClassExpression;
+class ClassDeclaration;
+class ClassElementList;
// ui elements
class UiProgram;
@@ -179,7 +195,6 @@ class UiObjectMember;
class UiObjectMemberList;
class UiArrayMemberList;
class UiQualifiedId;
-class UiQualifiedPragmaId;
class UiHeaderItemList;
class UiEnumDeclaration;
class UiEnumMemberList;
diff --git a/src/qml/parser/qqmljsastvisitor_p.h b/src/qml/parser/qqmljsastvisitor_p.h
index 13218f0e98..c925096de6 100644
--- a/src/qml/parser/qqmljsastvisitor_p.h
+++ b/src/qml/parser/qqmljsastvisitor_p.h
@@ -83,7 +83,6 @@ public:
virtual bool visit(UiObjectMemberList *) { return true; }
virtual bool visit(UiArrayMemberList *) { return true; }
virtual bool visit(UiQualifiedId *) { return true; }
- virtual bool visit(UiQualifiedPragmaId *) { return true; }
virtual bool visit(UiEnumDeclaration *) { return true; }
virtual bool visit(UiEnumMemberList *) { return true; }
@@ -102,7 +101,6 @@ public:
virtual void endVisit(UiObjectMemberList *) {}
virtual void endVisit(UiArrayMemberList *) {}
virtual void endVisit(UiQualifiedId *) {}
- virtual void endVisit(UiQualifiedPragmaId *) {}
virtual void endVisit(UiEnumDeclaration *) {}
virtual void endVisit(UiEnumMemberList *) { }
@@ -122,35 +120,41 @@ public:
virtual bool visit(FalseLiteral *) { return true; }
virtual void endVisit(FalseLiteral *) {}
+ virtual bool visit(SuperLiteral *) { return true; }
+ virtual void endVisit(SuperLiteral *) {}
+
virtual bool visit(StringLiteral *) { return true; }
virtual void endVisit(StringLiteral *) {}
+ virtual bool visit(TemplateLiteral *) { return true; }
+ virtual void endVisit(TemplateLiteral *) {}
+
virtual bool visit(NumericLiteral *) { return true; }
virtual void endVisit(NumericLiteral *) {}
virtual bool visit(RegExpLiteral *) { return true; }
virtual void endVisit(RegExpLiteral *) {}
- virtual bool visit(ArrayLiteral *) { return true; }
- virtual void endVisit(ArrayLiteral *) {}
+ virtual bool visit(ArrayPattern *) { return true; }
+ virtual void endVisit(ArrayPattern *) {}
- virtual bool visit(ObjectLiteral *) { return true; }
- virtual void endVisit(ObjectLiteral *) {}
+ virtual bool visit(ObjectPattern *) { return true; }
+ virtual void endVisit(ObjectPattern *) {}
- virtual bool visit(ElementList *) { return true; }
- virtual void endVisit(ElementList *) {}
+ virtual bool visit(PatternElementList *) { return true; }
+ virtual void endVisit(PatternElementList *) {}
- virtual bool visit(Elision *) { return true; }
- virtual void endVisit(Elision *) {}
+ virtual bool visit(PatternPropertyList *) { return true; }
+ virtual void endVisit(PatternPropertyList *) {}
- virtual bool visit(PropertyAssignmentList *) { return true; }
- virtual void endVisit(PropertyAssignmentList *) {}
+ virtual bool visit(PatternElement *) { return true; }
+ virtual void endVisit(PatternElement *) {}
- virtual bool visit(PropertyNameAndValue *) { return true; }
- virtual void endVisit(PropertyNameAndValue *) {}
+ virtual bool visit(PatternProperty *) { return true; }
+ virtual void endVisit(PatternProperty *) {}
- virtual bool visit(PropertyGetterSetter *) { return true; }
- virtual void endVisit(PropertyGetterSetter *) {}
+ virtual bool visit(Elision *) { return true; }
+ virtual void endVisit(Elision *) {}
virtual bool visit(NestedExpression *) { return true; }
virtual void endVisit(NestedExpression *) {}
@@ -164,12 +168,18 @@ public:
virtual bool visit(NumericLiteralPropertyName *) { return true; }
virtual void endVisit(NumericLiteralPropertyName *) {}
+ virtual bool visit(ComputedPropertyName *) { return true; }
+ virtual void endVisit(ComputedPropertyName *) {}
+
virtual bool visit(ArrayMemberExpression *) { return true; }
virtual void endVisit(ArrayMemberExpression *) {}
virtual bool visit(FieldMemberExpression *) { return true; }
virtual void endVisit(FieldMemberExpression *) {}
+ virtual bool visit(TaggedTemplate *) { return true; }
+ virtual void endVisit(TaggedTemplate *) {}
+
virtual bool visit(NewMemberExpression *) { return true; }
virtual void endVisit(NewMemberExpression *) {}
@@ -236,9 +246,6 @@ public:
virtual bool visit(VariableDeclarationList *) { return true; }
virtual void endVisit(VariableDeclarationList *) {}
- virtual bool visit(VariableDeclaration *) { return true; }
- virtual void endVisit(VariableDeclaration *) {}
-
virtual bool visit(EmptyStatement *) { return true; }
virtual void endVisit(EmptyStatement *) {}
@@ -257,15 +264,9 @@ public:
virtual bool visit(ForStatement *) { return true; }
virtual void endVisit(ForStatement *) {}
- virtual bool visit(LocalForStatement *) { return true; }
- virtual void endVisit(LocalForStatement *) {}
-
virtual bool visit(ForEachStatement *) { return true; }
virtual void endVisit(ForEachStatement *) {}
- virtual bool visit(LocalForEachStatement *) { return true; }
- virtual void endVisit(LocalForEachStatement *) {}
-
virtual bool visit(ContinueStatement *) { return true; }
virtual void endVisit(ContinueStatement *) {}
@@ -275,6 +276,9 @@ public:
virtual bool visit(ReturnStatement *) { return true; }
virtual void endVisit(ReturnStatement *) {}
+ virtual bool visit(YieldExpression *) { return true; }
+ virtual void endVisit(YieldExpression *) {}
+
virtual bool visit(WithStatement *) { return true; }
virtual void endVisit(WithStatement *) {}
@@ -317,20 +321,56 @@ public:
virtual bool visit(FormalParameterList *) { return true; }
virtual void endVisit(FormalParameterList *) {}
- virtual bool visit(FunctionBody *) { return true; }
- virtual void endVisit(FunctionBody *) {}
+ virtual bool visit(ClassExpression *) { return true; }
+ virtual void endVisit(ClassExpression *) {}
+
+ virtual bool visit(ClassDeclaration *) { return true; }
+ virtual void endVisit(ClassDeclaration *) {}
+
+ virtual bool visit(ClassElementList *) { return true; }
+ virtual void endVisit(ClassElementList *) {}
virtual bool visit(Program *) { return true; }
virtual void endVisit(Program *) {}
- virtual bool visit(SourceElements *) { return true; }
- virtual void endVisit(SourceElements *) {}
+ virtual bool visit(NameSpaceImport *) { return true; }
+ virtual void endVisit(NameSpaceImport *) {}
+
+ virtual bool visit(ImportSpecifier *) { return true; }
+ virtual void endVisit(ImportSpecifier *) {}
+
+ virtual bool visit(ImportsList *) { return true; }
+ virtual void endVisit(ImportsList *) {}
+
+ virtual bool visit(NamedImports *) { return true; }
+ virtual void endVisit(NamedImports *) {}
+
+ virtual bool visit(FromClause *) { return true; }
+ virtual void endVisit(FromClause *) {}
+
+ virtual bool visit(ImportClause *) { return true; }
+ virtual void endVisit(ImportClause *) {}
+
+ virtual bool visit(ImportDeclaration *) { return true; }
+ virtual void endVisit(ImportDeclaration *) {}
+
+ virtual bool visit(ExportSpecifier *) { return true; }
+ virtual void endVisit(ExportSpecifier *) {}
+
+ virtual bool visit(ExportsList *) { return true; }
+ virtual void endVisit(ExportsList *) {}
+
+ virtual bool visit(ExportClause *) { return true; }
+ virtual void endVisit(ExportClause *) {}
+
+ virtual bool visit(ExportDeclaration *) { return true; }
+ virtual void endVisit(ExportDeclaration *) {}
- virtual bool visit(FunctionSourceElement *) { return true; }
- virtual void endVisit(FunctionSourceElement *) {}
+ virtual bool visit(ModuleItem *) { return true; }
+ virtual void endVisit(ModuleItem *) {}
- virtual bool visit(StatementSourceElement *) { return true; }
- virtual void endVisit(StatementSourceElement *) {}
+ virtual bool visit(ESModule *) { return true; }
+ virtual void endVisit(ESModule *) {}
virtual bool visit(DebuggerStatement *) { return true; }
virtual void endVisit(DebuggerStatement *) {}
diff --git a/src/qml/parser/qqmljsengine_p.cpp b/src/qml/parser/qqmljsengine_p.cpp
index b4f0debf85..97ce6ebea3 100644
--- a/src/qml/parser/qqmljsengine_p.cpp
+++ b/src/qml/parser/qqmljsengine_p.cpp
@@ -112,13 +112,6 @@ double integerFromString(const char *buf, int size, int radix)
return result;
}
-double integerFromString(const QString &str, int radix)
-{
- QByteArray ba = QStringRef(&str).trimmed().toLatin1();
- return integerFromString(ba.constData(), ba.size(), radix);
-}
-
-
Engine::Engine()
: _lexer(nullptr), _directives(nullptr)
{ }
diff --git a/src/qml/parser/qqmljsengine_p.h b/src/qml/parser/qqmljsengine_p.h
index af26bac0ff..1de907d296 100644
--- a/src/qml/parser/qqmljsengine_p.h
+++ b/src/qml/parser/qqmljsengine_p.h
@@ -63,9 +63,35 @@ QT_QML_BEGIN_NAMESPACE
namespace QQmlJS {
class Lexer;
-class Directives;
class MemoryPool;
+class QML_PARSER_EXPORT Directives {
+public:
+ virtual ~Directives() {}
+
+ virtual void pragmaLibrary()
+ {
+ }
+
+ virtual void importFile(const QString &jsfile, const QString &module, int line, int column)
+ {
+ Q_UNUSED(jsfile);
+ Q_UNUSED(module);
+ Q_UNUSED(line);
+ Q_UNUSED(column);
+ }
+
+ virtual void importModule(const QString &uri, const QString &version, const QString &module, int line, int column)
+ {
+ Q_UNUSED(uri);
+ Q_UNUSED(version);
+ Q_UNUSED(module);
+ Q_UNUSED(line);
+ Q_UNUSED(column);
+ }
+};
+
+
class QML_PARSER_EXPORT DiagnosticMessage
{
public:
diff --git a/src/qml/parser/qqmljsgrammar.cpp b/src/qml/parser/qqmljsgrammar.cpp
deleted file mode 100644
index 2aaeb385e3..0000000000
--- a/src/qml/parser/qqmljsgrammar.cpp
+++ /dev/null
@@ -1,1167 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part 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$
-**
-****************************************************************************/
-
-// This file was generated by qlalr - DO NOT EDIT!
-#include "qqmljsgrammar_p.h"
-
-QT_BEGIN_NAMESPACE
-
-const char *const QQmlJSGrammar::spell [] = {
- "end of file", "&", "&&", "&=", "break", "case", "catch", ":", ",", "continue",
- "default", "delete", "/", "/=", "do", ".", "else", "=", "==", "===",
- "finally", "for", "function", ">=", ">", ">>", ">>=", ">>>", ">>>=", "identifier",
- "if", "in", "instanceof", "{", "[", "<=", "(", "<", "<<", "<<=",
- "-", "-=", "--", "new", "!", "!=", "!==", "numeric literal", "|", "|=",
- "||", "+", "+=", "++", "?", "}", "]", "%", "%=", "return",
- ")", ";", nullptr, "*", "*=", "string literal", "property", "signal", "readonly", "switch",
- "this", "throw", "~", "try", "typeof", "var", "void", "while", "with", "^",
- "^=", "null", "true", "false", "const", "let", "debugger", "reserved word", "multiline string literal", "comment",
- nullptr, "enum", "public", "import", "pragma", "as", "on", "get", "set", nullptr,
- nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
-};
-
-const short QQmlJSGrammar::lhs [] = {
- 108, 108, 108, 108, 108, 108, 109, 115, 115, 118,
- 118, 118, 118, 121, 123, 119, 119, 120, 120, 120,
- 120, 120, 120, 120, 120, 124, 125, 117, 116, 128,
- 128, 129, 129, 130, 130, 127, 113, 113, 113, 113,
- 132, 132, 132, 132, 132, 132, 132, 113, 140, 140,
- 140, 140, 141, 141, 142, 142, 113, 113, 113, 113,
- 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
- 113, 113, 113, 113, 113, 113, 113, 145, 145, 145,
- 145, 126, 126, 126, 126, 126, 126, 126, 146, 146,
- 146, 146, 146, 146, 146, 146, 146, 146, 146, 146,
- 146, 146, 146, 146, 146, 146, 131, 148, 148, 148,
- 148, 147, 147, 152, 152, 152, 150, 150, 153, 153,
- 153, 153, 156, 156, 156, 156, 156, 156, 156, 156,
- 156, 156, 156, 156, 156, 156, 156, 156, 156, 156,
- 156, 156, 156, 156, 156, 156, 156, 156, 156, 156,
- 156, 156, 156, 156, 156, 157, 157, 122, 122, 122,
- 122, 122, 160, 160, 161, 161, 161, 161, 159, 159,
- 162, 162, 163, 163, 164, 164, 164, 165, 165, 165,
- 165, 165, 165, 165, 165, 165, 165, 166, 166, 166,
- 166, 167, 167, 167, 168, 168, 168, 168, 169, 169,
- 169, 169, 169, 169, 169, 170, 170, 170, 170, 170,
- 170, 171, 171, 171, 171, 171, 172, 172, 172, 172,
- 172, 173, 173, 174, 174, 175, 175, 176, 176, 177,
- 177, 178, 178, 179, 179, 180, 180, 181, 181, 182,
- 182, 183, 183, 184, 184, 151, 151, 185, 185, 186,
- 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
- 186, 111, 111, 187, 187, 188, 188, 189, 189, 110,
- 110, 110, 110, 110, 110, 110, 110, 110, 110, 110,
- 110, 110, 110, 110, 133, 198, 198, 197, 197, 144,
- 144, 199, 199, 199, 200, 200, 202, 202, 201, 203,
- 206, 204, 204, 207, 205, 205, 134, 135, 135, 136,
- 136, 190, 190, 190, 190, 190, 190, 190, 190, 191,
- 191, 191, 191, 192, 192, 192, 192, 193, 193, 137,
- 138, 208, 208, 211, 211, 209, 209, 212, 210, 194,
- 195, 195, 139, 139, 139, 213, 214, 196, 196, 215,
- 143, 158, 158, 216, 216, 155, 155, 154, 154, 217,
- 114, 114, 218, 218, 112, 112, 149, 149, 219
-};
-
-const short QQmlJSGrammar::rhs [] = {
- 2, 2, 2, 2, 2, 2, 2, 1, 1, 1,
- 1, 2, 2, 1, 1, 2, 2, 2, 2, 3,
- 3, 5, 5, 4, 4, 2, 2, 0, 1, 1,
- 2, 1, 3, 2, 3, 2, 1, 5, 4, 4,
- 1, 1, 1, 1, 1, 1, 1, 3, 1, 1,
- 1, 3, 0, 1, 2, 4, 6, 6, 3, 3,
- 7, 7, 4, 4, 5, 5, 8, 8, 5, 6,
- 6, 10, 6, 7, 1, 1, 5, 1, 3, 3,
- 5, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
- 3, 4, 5, 3, 4, 3, 1, 1, 2, 3,
- 4, 1, 2, 3, 7, 8, 1, 3, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 4,
- 3, 5, 1, 2, 4, 4, 4, 3, 0, 1,
- 1, 3, 1, 1, 1, 2, 2, 1, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 1, 3, 3,
- 3, 1, 3, 3, 1, 3, 3, 3, 1, 3,
- 3, 3, 3, 3, 3, 1, 3, 3, 3, 3,
- 3, 1, 3, 3, 3, 3, 1, 3, 3, 3,
- 3, 1, 3, 1, 3, 1, 3, 1, 3, 1,
- 3, 1, 3, 1, 3, 1, 3, 1, 3, 1,
- 3, 1, 5, 1, 5, 1, 3, 1, 3, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 3, 0, 1, 1, 3, 0, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 3, 1, 2, 0, 1, 3,
- 3, 1, 1, 1, 1, 3, 1, 3, 2, 2,
- 2, 0, 1, 2, 0, 1, 1, 2, 2, 7,
- 5, 7, 7, 7, 5, 9, 10, 7, 8, 2,
- 2, 3, 3, 2, 2, 3, 3, 3, 3, 5,
- 5, 3, 5, 1, 2, 0, 1, 4, 3, 3,
- 3, 3, 3, 3, 4, 5, 2, 2, 2, 1,
- 8, 8, 7, 1, 3, 0, 1, 0, 1, 1,
- 1, 1, 1, 2, 1, 1, 0, 1, 2
-};
-
-const short QQmlJSGrammar::action_default [] = {
- 0, 0, 28, 0, 0, 0, 28, 0, 195, 262,
- 226, 234, 230, 174, 246, 222, 3, 159, 90, 175,
- 238, 242, 163, 192, 173, 178, 158, 212, 199, 0,
- 97, 98, 93, 0, 87, 82, 367, 0, 0, 0,
- 0, 95, 0, 0, 91, 94, 86, 0, 0, 83,
- 85, 88, 84, 96, 89, 0, 92, 0, 0, 188,
- 0, 0, 175, 194, 177, 176, 0, 0, 0, 190,
- 191, 189, 193, 0, 223, 0, 0, 0, 0, 213,
- 0, 0, 0, 0, 0, 0, 203, 0, 0, 0,
- 197, 198, 196, 201, 205, 204, 202, 200, 215, 214,
- 216, 0, 231, 0, 227, 0, 0, 169, 156, 168,
- 157, 123, 124, 125, 151, 126, 153, 127, 128, 129,
- 130, 131, 132, 133, 134, 135, 136, 137, 138, 152,
- 139, 140, 154, 141, 142, 143, 144, 145, 146, 147,
- 148, 149, 150, 155, 0, 0, 167, 263, 170, 0,
- 171, 0, 172, 166, 0, 259, 252, 250, 257, 258,
- 256, 255, 261, 254, 253, 251, 260, 247, 0, 235,
- 0, 0, 239, 0, 0, 243, 0, 0, 169, 161,
- 0, 160, 0, 165, 179, 0, 356, 356, 357, 0,
- 354, 0, 355, 0, 358, 270, 277, 276, 284, 272,
- 0, 273, 0, 359, 0, 366, 274, 275, 90, 280,
- 278, 363, 360, 365, 281, 0, 293, 0, 0, 0,
- 0, 350, 0, 367, 292, 264, 307, 0, 0, 0,
- 294, 0, 0, 282, 283, 0, 271, 279, 308, 309,
- 0, 356, 0, 0, 358, 0, 351, 352, 0, 340,
- 364, 0, 324, 325, 326, 327, 0, 320, 321, 322,
- 323, 348, 349, 0, 0, 0, 0, 0, 312, 313,
- 314, 268, 266, 228, 236, 232, 248, 224, 269, 0,
- 175, 240, 244, 217, 206, 0, 0, 225, 0, 0,
- 0, 0, 218, 0, 0, 0, 0, 0, 210, 208,
- 211, 209, 207, 220, 219, 221, 0, 233, 0, 229,
- 0, 267, 175, 0, 249, 264, 265, 0, 264, 0,
- 0, 316, 0, 0, 0, 318, 0, 237, 0, 0,
- 241, 0, 0, 245, 305, 0, 297, 306, 300, 0,
- 304, 0, 264, 298, 0, 264, 0, 0, 317, 0,
- 0, 0, 319, 0, 0, 0, 311, 0, 310, 90,
- 117, 368, 0, 0, 122, 286, 289, 0, 123, 293,
- 126, 153, 128, 129, 93, 134, 135, 87, 136, 292,
- 139, 91, 94, 264, 88, 96, 142, 89, 144, 92,
- 146, 147, 294, 149, 150, 155, 0, 119, 118, 121,
- 105, 120, 104, 0, 114, 287, 285, 0, 0, 0,
- 358, 0, 115, 163, 164, 169, 0, 162, 0, 328,
- 329, 0, 356, 0, 0, 358, 0, 116, 0, 0,
- 0, 331, 336, 334, 337, 0, 0, 335, 336, 0,
- 332, 0, 333, 288, 339, 0, 288, 338, 0, 341,
- 342, 0, 288, 343, 344, 0, 0, 345, 0, 0,
- 0, 346, 347, 181, 180, 0, 0, 0, 315, 0,
- 0, 0, 330, 302, 295, 0, 303, 299, 0, 301,
- 290, 0, 291, 296, 0, 0, 358, 0, 353, 108,
- 0, 0, 112, 99, 0, 101, 110, 0, 102, 111,
- 113, 103, 109, 100, 0, 106, 185, 183, 187, 184,
- 182, 186, 361, 6, 362, 4, 2, 75, 107, 0,
- 0, 0, 83, 85, 84, 37, 5, 0, 76, 0,
- 51, 50, 49, 0, 0, 51, 0, 0, 0, 52,
- 0, 67, 68, 0, 65, 0, 66, 41, 42, 43,
- 44, 46, 47, 71, 45, 0, 0, 0, 78, 0,
- 77, 80, 0, 81, 0, 79, 0, 51, 0, 0,
- 0, 0, 0, 61, 0, 62, 0, 0, 32, 0,
- 0, 72, 33, 0, 36, 34, 30, 0, 35, 31,
- 0, 63, 0, 64, 163, 0, 69, 73, 0, 0,
- 0, 0, 163, 288, 0, 70, 90, 123, 293, 126,
- 153, 128, 129, 93, 134, 135, 136, 292, 139, 91,
- 94, 264, 96, 142, 89, 144, 92, 146, 147, 294,
- 149, 150, 155, 74, 0, 59, 53, 60, 54, 0,
- 0, 0, 0, 56, 0, 57, 58, 55, 0, 0,
- 0, 0, 48, 0, 38, 39, 0, 40, 8, 0,
- 0, 9, 0, 11, 0, 10, 0, 1, 27, 15,
- 14, 26, 13, 12, 29, 7, 0, 18, 0, 19,
- 0, 24, 25, 0, 20, 21, 0, 22, 23, 16,
- 17, 369
-};
-
-const short QQmlJSGrammar::goto_default [] = {
- 7, 667, 213, 200, 211, 526, 513, 662, 675, 512,
- 661, 665, 663, 671, 22, 668, 666, 664, 18, 525,
- 587, 577, 584, 579, 553, 195, 199, 201, 206, 237,
- 214, 234, 568, 639, 638, 205, 236, 557, 26, 491,
- 490, 362, 361, 9, 360, 363, 204, 484, 364, 109,
- 17, 149, 24, 13, 148, 19, 25, 59, 23, 8,
- 28, 27, 283, 15, 277, 10, 273, 12, 275, 11,
- 274, 20, 281, 21, 282, 14, 276, 272, 313, 418,
- 278, 279, 207, 197, 196, 210, 209, 233, 198, 367,
- 366, 235, 475, 474, 335, 336, 477, 338, 476, 337,
- 431, 435, 438, 434, 433, 453, 454, 202, 188, 203,
- 212, 0
-};
-
-const short QQmlJSGrammar::action_index [] = {
- 350, 1528, 3041, 3041, 2937, 1235, 115, 105, 239, -108,
- 102, 93, 95, 205, -108, 422, 110, -108, -108, 727,
- 92, 118, 265, 256, -108, -108, -108, 507, 247, 1528,
- -108, -108, -108, 665, -108, -108, 2729, 1826, 1528, 1528,
- 1528, -108, 1041, 1528, -108, -108, -108, 1528, 1528, -108,
- -108, -108, -108, -108, -108, 1528, -108, 1528, 1528, -108,
- 1528, 1528, 174, 221, -108, -108, 1528, 1528, 1528, -108,
- -108, -108, 177, 1528, 422, 1528, 1528, 1528, 1528, 411,
- 1528, 1528, 1528, 1528, 1528, 1528, 211, 1528, 1528, 1528,
- 142, 148, 154, 217, 223, 226, 227, 231, 507, 385,
- 395, 1528, 57, 1528, 83, 2521, 1528, 1528, -108, -108,
- -108, -108, -108, -108, -108, -108, -108, -108, -108, -108,
- -108, -108, -108, -108, -108, -108, -108, -108, -108, -108,
- -108, -108, -108, -108, -108, -108, -108, -108, -108, -108,
- -108, -108, -108, -108, 179, 1528, -108, -108, 77, 36,
- -108, 1528, -108, -108, 1528, -108, -108, -108, -108, -108,
- -108, -108, -108, -108, -108, -108, -108, -108, 1528, 56,
- 1528, 1528, 80, 74, 1528, -108, 2521, 1528, 1528, -108,
- 125, -108, 55, -108, -108, 53, 410, 418, 72, 52,
- -108, 392, -108, 46, 3041, -108, -108, -108, -108, -108,
- 273, -108, 396, -108, 44, -108, -108, -108, 76, -108,
- -108, -108, 3041, -108, -108, 744, -108, 589, 98, 2937,
- 91, 90, 88, 3249, -108, 1528, -108, 86, 1528, 81,
- -108, 75, 73, -108, -108, 586, -108, -108, -108, -108,
- 71, 491, 69, 65, 3041, 64, -108, -108, 2937, -108,
- -108, 139, -108, -108, -108, -108, 134, -108, -108, -108,
- -108, -108, -108, 63, 66, 1528, 147, 264, -108, -108,
- -108, 1726, -108, 87, 68, 70, -108, 334, 82, 78,
- 796, 89, 121, 349, 318, 469, 1528, 330, 1528, 1528,
- 1528, 1528, 359, 1528, 1528, 1528, 1528, 1528, 284, 289,
- 303, 306, 313, 365, 369, 375, 1528, 58, 1528, 85,
- 1528, -108, 849, 1528, -108, 1528, 79, 59, 1528, 61,
- 2937, -108, 1528, 146, 2937, -108, 1528, 62, 1528, 1528,
- 106, 99, 1528, -108, 96, 176, 171, -108, -108, 1528,
- -108, 407, 1528, -108, 97, 1528, -52, 2937, -108, 1528,
- 120, 2937, -108, 1528, 123, 2937, 101, 2937, -108, 116,
- -108, 84, 45, 0, -108, -108, 2937, -39, 641, -3,
- 652, 156, 1528, 2937, -7, -11, 567, 2625, -21, 5,
- 945, 2, 94, 1629, 2625, -1, -26, 6, 1528, 10,
- -15, 1528, 14, 1528, -25, -12, 2833, -108, -108, -108,
- -108, -108, -108, 1528, -108, -108, -108, -14, -58, -13,
- 3041, -36, -108, 287, -108, 1528, -46, -108, 153, -108,
- -108, -31, 586, -57, -32, 3041, 11, -108, 1528, 168,
- 29, -108, 47, -108, 48, 169, 1528, -108, 49, 50,
- -108, 9, -108, 2937, -108, 136, 2937, -108, 275, -108,
- -108, 126, 2937, 35, -108, 33, 37, -108, 466, -4,
- 38, -108, -108, -108, -108, 1528, 130, 2937, -108, 1528,
- 117, 2937, -108, 34, -108, 296, -108, -108, 1528, -108,
- -108, 404, -108, -108, 12, 40, 3041, 13, -108, -108,
- 155, 1926, -108, -108, 2026, -108, -108, 2126, -108, -108,
- -108, -108, -108, -108, 144, -108, -108, -108, -108, -108,
- -108, -108, -108, -108, 3041, -108, -108, -108, 132, -27,
- 15, 1137, 218, -23, 17, -108, -108, 196, -108, 242,
- 8, -108, -108, 579, 237, -108, 127, 18, 415, -108,
- 103, -108, -108, 236, -108, 2223, -108, -108, -108, -108,
- -108, -108, -108, -108, -108, 27, 21, 137, 31, 20,
- -108, 23, -5, -108, -9, -108, 332, -10, 571, 201,
- 195, 586, 225, -108, 7, -108, 1137, 165, -108, 4,
- 1137, -108, -108, 1333, -108, -108, -108, 1431, -108, -108,
- 184, -108, 2223, -108, 331, 3, -108, -108, 202, 531,
- 26, 2417, 327, 3145, 1, -108, 25, 629, 24, 649,
- 124, 1528, 2937, 22, -6, 514, -8, 19, 1041, 16,
- 94, 1629, 28, 42, 67, 1528, 60, 43, 1528, 54,
- 1528, 41, 39, -108, 234, -108, 228, -108, 51, -2,
- 564, 233, 575, -108, 100, -108, -108, -108, 2320, 1137,
- 1826, 32, -108, 122, -108, -108, 30, -108, -108, 1137,
- 1137, 104, 903, -108, 308, -108, 108, -108, -108, 133,
- 119, -108, -108, -108, -108, -108, 451, -108, 164, -108,
- 161, -108, -108, 458, -108, -108, 151, -108, -108, -108,
- -108, -108,
-
- -112, 18, 86, 97, 69, 316, 7, -112, -112, -112,
- -112, -112, -112, -112, -112, -112, -112, -112, -112, -64,
- -112, -112, -112, -112, -112, -112, -112, -112, -112, 66,
- -112, -112, -112, -17, -112, -112, -10, -36, 3, 90,
- 95, -112, 149, 74, -112, -112, -112, 67, 13, -112,
- -112, -112, -112, -112, -112, 178, -112, 181, 185, -112,
- 189, 190, -112, -112, -112, -112, 198, 208, 212, -112,
- -112, -112, -112, 209, -112, 201, 164, 111, 113, -112,
- 116, 130, 131, 132, 134, 142, -112, 118, 124, 144,
- -112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
- -112, 154, -112, 155, -112, 268, 28, -8, -112, -112,
- -112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
- -112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
- -112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
- -112, -112, -112, -112, -112, 42, -112, -112, -112, -112,
- -112, 47, -112, -112, 50, -112, -112, -112, -112, -112,
- -112, -112, -112, -112, -112, -112, -112, -112, 159, -112,
- 158, 56, -112, -112, 57, -112, 362, 60, 157, -112,
- -112, -112, -112, -112, -112, -112, 20, 151, -112, -112,
- -112, 25, -112, -112, 30, -112, -112, -112, -112, -112,
- -112, -112, 31, -112, -112, -112, -112, -112, -112, -112,
- -112, -112, 233, -112, -112, 34, -112, 35, -112, 225,
- -112, 36, -112, 216, -112, 55, -112, -112, 53, 39,
- -112, -112, -112, -112, -112, 19, -112, -112, -112, -112,
- -112, 94, -112, -112, 92, -112, -112, -112, 117, -112,
- -112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
- -112, -112, -112, -112, -112, 33, -112, -112, -112, -112,
- -112, 88, -112, -112, -112, -112, -112, -112, -112, -112,
- -112, -112, -112, -112, -112, 51, 220, -112, 227, 235,
- 236, 244, -112, 21, 17, 102, 91, 89, -112, -112,
- -112, -112, -112, -112, -112, -112, 211, -112, 247, -112,
- 257, -112, -112, 264, -112, 75, -112, -112, 103, -112,
- 112, -112, 29, -112, 73, -112, 263, -112, 255, 254,
- -112, -112, 245, -112, -112, -112, -112, -112, -112, 248,
- -112, 65, 105, -112, -112, 99, -112, 162, -112, 37,
- -112, 115, -112, 44, -112, 135, -112, 137, -112, -112,
- -112, -112, -112, -112, -112, -112, 138, -112, 24, -112,
- 26, -112, 104, 140, -112, -112, 32, 64, -112, -112,
- 174, -112, -112, 48, 87, -112, -112, -112, 54, -112,
- 40, 71, -112, 150, -112, -112, 197, -112, -112, -112,
- -112, -112, -112, 12, -112, -112, -112, -112, -112, -112,
- 206, -112, -112, -112, -112, 207, -112, -112, -112, -112,
- -112, -112, 231, -112, -112, 239, -112, -112, 43, -112,
- -112, -112, -112, -112, -59, -112, 38, -112, -62, -112,
- -112, -112, -112, 258, -112, -112, 259, -112, -112, -112,
- -112, -112, 163, -72, -112, -112, 41, -112, 62, -112,
- 61, -112, -112, -112, -112, 59, -112, 193, -112, 58,
- -112, 204, -112, -112, -112, -112, -112, -112, 52, -112,
- -112, 175, -112, -112, -112, -112, 186, -112, -112, -112,
- -112, 49, -112, -112, 173, -112, -112, 45, -112, -112,
- -112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
- -112, -112, -112, -112, 213, -112, -112, -112, -112, -112,
- -112, 46, -112, -112, -112, -112, -112, -112, -112, 27,
- -112, -112, -112, -18, -9, -112, -112, -112, 15, -112,
- -112, -112, -112, -112, -112, 331, -112, -112, -112, -112,
- -112, -112, -112, -112, -112, -112, -112, -112, -112, -112,
- -112, -112, -112, -112, -112, -112, -112, -112, 11, -6,
- -112, 10, -112, -112, -112, -112, 156, -112, -112, -112,
- 240, -112, -112, 330, -112, -112, -112, 332, -112, -112,
- -112, -112, 376, -112, -112, 8, -112, -112, -7, 76,
- -112, 358, -112, 228, 5, -112, -112, 6, -112, 4,
- -112, 79, 221, -112, -112, 2, -112, -112, 174, -112,
- -112, 16, -112, -112, -112, 14, -112, -16, 70, -112,
- 63, -112, -112, -112, -112, -112, -30, -112, -112, -112,
- -15, -28, -13, -112, -112, -112, -112, -112, 460, 93,
- 307, -12, -112, -112, -112, -112, -11, -112, -112, -2,
- -1, 85, 84, -112, -112, -112, -112, -112, -112, -112,
- -112, -112, -112, -112, -112, -112, -3, -112, -112, -112,
- -112, -112, -112, 0, -112, -112, -112, -112, -112, -112,
- -112, -112
-};
-
-const short QQmlJSGrammar::action_info [] = {
- -132, 425, 409, 424, -151, 422, -120, 403, 347, -140,
- 428, 465, -152, -143, 417, 353, 406, -145, 452, 412,
- 410, -148, 408, -140, 469, 271, -152, 569, 353, -132,
- 271, -151, 248, 601, 583, -120, 583, 583, 565, 529,
- 562, 576, 563, 598, 555, 534, 634, 539, 564, 561,
- 558, 478, 436, 436, 436, 456, 460, 443, 644, 641,
- 556, -148, 432, 583, 442, 583, 427, -145, 488, 458,
- 452, 452, 485, 486, -143, 469, 452, 465, 428, 194,
- 191, 174, 168, 248, 73, 151, 286, 145, 286, 187,
- 310, 326, 396, 0, 168, 0, 153, 0, 244, 247,
- 402, -121, 265, 73, 101, 691, 332, 241, 326, 469,
- 306, 465, 193, 339, 452, 183, 306, 357, 145, 246,
- 318, 320, 428, 248, 353, 145, 186, 271, 145, 243,
- 580, 145, 455, 145, 176, 0, 103, 308, 145, 315,
- 264, 101, 537, 446, 145, 559, 456, 176, 176, 308,
- 0, 538, 145, 177, 145, 145, 0, 0, 345, 262,
- 261, 646, 645, 494, 542, 541, 177, 177, 170, 690,
- 689, 328, 171, 580, 103, 329, 145, 471, 654, 439,
- 351, 181, 60, 355, 341, 262, 261, 145, 60, 66,
- 467, 592, 560, 61, 60, 260, 259, 659, 660, 61,
- 255, 254, 349, 648, 505, 61, 324, 267, 659, 660,
- 537, 495, 688, 687, 420, 419, 64, 262, 261, 571,
- 105, 581, 682, 681, 440, 685, 684, 65, 430, 583,
- 535, 535, 574, 66, 67, 146, 87, 342, 88, 106,
- 68, 107, 87, 545, 88, 593, 591, 567, 87, 89,
- 88, 87, 87, 88, 88, 89, 87, 535, 88, 683,
- 0, 89, 535, 0, 89, 89, 535, 0, 66, 89,
- 636, 530, 87, 0, 88, 0, 532, 532, 67, 60,
- 176, 145, 0, 145, 68, 89, 575, 573, 531, 531,
- 61, 0, 649, 532, 0, 637, 635, 546, 544, 177,
- 0, 178, 176, 532, 481, 531, 0, 0, 532, 87,
- 0, 88, 532, 67, 87, 531, 88, 532, 0, 68,
- 531, 177, 89, 415, 531, 270, 268, 89, 87, 531,
- 88, 87, 0, 88, 239, 238, 450, 449, 87, 0,
- 88, 89, 176, 87, 89, 88, 176, 176, 288, 289,
- 0, 89, 288, 289, 269, 678, 89, 482, 480, 0,
- -107, 177, 0, 178, -107, 177, 177, 178, 415, 679,
- 677, 0, 293, 294, 0, 290, 291, 0, 0, 290,
- 291, 295, 293, 294, 296, 0, 297, 0, 293, 294,
- 0, 295, 293, 294, 296, 0, 297, 295, 293, 294,
- 296, 295, 297, 676, 296, 0, 297, 295, 80, 81,
- 296, 0, 297, 0, 0, 0, 82, 83, 80, 81,
- 84, 35, 85, 0, 0, 35, 82, 83, 0, 0,
- 84, 0, 85, 35, 80, 81, 35, 0, 0, 35,
- 75, 76, 82, 83, 35, 0, 84, 35, 85, 0,
- 6, 5, 4, 1, 3, 2, 0, 0, 49, 52,
- 50, 0, 49, 52, 50, 0, 0, 77, 78, 0,
- 49, 52, 50, 49, 52, 50, 49, 52, 50, 0,
- 35, 49, 52, 50, 49, 52, 50, 35, 46, 34,
- 51, 0, 46, 34, 51, 35, 0, 0, 35, 0,
- 46, 34, 51, 46, 34, 51, 46, 34, 51, 0,
- 0, 46, 34, 51, 46, 34, 51, 49, 52, 50,
- 35, 0, 0, 0, 49, 52, 50, 0, 0, 0,
- 80, 81, 49, 52, 50, 49, 52, 50, 82, 83,
- 0, 0, 84, 35, 85, 0, 537, 46, 34, 51,
- 186, 0, 0, 0, 46, 34, 51, 49, 52, 50,
- 35, 0, 46, 34, 51, 46, 34, 51, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 537,
- 49, 52, 50, 0, 0, 0, 537, 46, 34, 51,
- 537, 0, 0, 35, 537, 0, 35, 49, 52, 50,
- 35, 0, 0, 186, 35, 0, 0, 0, 35, 0,
- 46, 34, 51, 0, 0, 35, 0, 0, 35, 0,
- 0, 0, 0, 0, 0, 0, 0, 46, 34, 51,
- 49, 52, 50, 49, 52, 50, 0, 49, 52, 50,
- 0, 49, 52, 50, 0, 49, 52, 50, 0, 0,
- 258, 257, 49, 52, 50, 49, 52, 50, 35, 0,
- 46, 34, 51, 46, 34, 51, 0, 46, 34, 51,
- 35, 46, 34, 51, 0, 46, 34, 51, 35, 0,
- 0, 35, 46, 34, 51, 46, 34, 51, 0, 0,
- 253, 252, 0, 0, 35, 49, 52, 50, 0, 0,
- 0, 186, 253, 252, 0, 0, 0, 49, 52, 50,
- 258, 257, 0, 258, 257, 49, 52, 50, 49, 52,
- 50, 0, 0, 0, 0, 46, 34, 51, 0, 0,
- 155, 49, 52, 50, 0, 0, 0, 46, 34, 51,
- 156, 0, 0, 0, 157, 46, 34, 51, 46, 34,
- 51, 0, 0, 158, 0, 159, 0, 0, 0, 0,
- 0, 46, 34, 51, 0, 0, 160, 0, 161, 64,
- 0, 0, 0, 35, 0, 0, 162, 0, 0, 163,
- 65, 0, 0, 0, 0, 164, 0, 0, 0, 0,
- 0, 165, 0, 0, 0, 0, 0, 0, 0, 155,
- 0, 0, 0, 0, 0, 253, 252, 166, 0, 156,
- 49, 52, 50, 157, 0, 0, 0, 0, 0, 0,
- 0, 0, 158, 0, 159, 0, 0, 322, 0, 0,
- 0, 0, 0, 0, 0, 160, 0, 161, 64, 0,
- 46, 34, 51, 0, 0, 162, 0, 0, 163, 65,
- 0, 0, 155, 0, 164, 0, 0, 0, 0, 0,
- 165, 0, 156, 0, 0, 0, 157, 0, 0, 0,
- 0, 0, 0, 0, 0, 158, 166, 159, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 160, 0,
- 161, 64, 0, 0, 0, 0, 0, 0, 162, 0,
- 0, 163, 65, 0, 0, 0, 0, 164, 0, 0,
- 0, 0, 0, 165, 0, 30, 31, 0, 0, 0,
- 0, 0, 0, 0, 0, 33, 0, 0, 0, 166,
- 0, 0, 35, 0, 0, 0, 36, 37, 0, 38,
- 0, 0, 0, 0, 0, 0, 521, 0, 0, 0,
- 45, 0, 0, 0, 0, 0, 0, 30, 31, 0,
- 0, 0, 0, 0, 0, 0, 0, 33, 53, 49,
- 52, 50, 0, 54, 35, 0, 0, 0, 36, 37,
- 0, 38, 0, 0, 44, 56, 32, 0, 42, 0,
- 0, 41, 45, 0, 0, 0, 0, 0, 0, 46,
- 34, 51, 0, 0, 0, 0, 0, 0, 0, 0,
- 53, 49, 52, 50, 0, 54, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 44, 56, 32, 0,
- 0, 0, 0, 41, 0, 0, 0, 0, 0, 0,
- 0, 46, 34, 51, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 30, 31, 0, 0, 0, 0, 0,
- 0, 0, 0, 33, 0, 0, 0, 0, 0, 0,
- 35, 0, 0, 0, 36, 37, 0, 38, 0, 0,
- 0, 0, 0, 0, 42, 0, 0, 0, 45, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 53, 49, 52, 50,
- 0, 54, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 44, 56, 32, 0, 0, 0, 0, 41,
- 0, 0, 0, 0, 0, 0, 0, 46, 34, 51,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 30,
- 31, 0, 0, 0, 0, 0, 0, 0, 0, 33,
- 0, 0, 0, 0, 0, 0, 35, 0, 0, 0,
- 36, 37, 0, 38, 0, 0, 0, 0, 0, 0,
- 521, 0, 0, 0, 45, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 53, 49, 52, 50, 0, 54, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 44, 56,
- 32, 0, 0, 0, 0, 41, 0, 0, 0, 0,
- 0, 0, 0, 46, 34, 51, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 519, 0, 30, 31, 0,
- 0, 0, 0, 0, 0, 0, 0, 221, 0, 0,
- 0, 0, 0, 0, 35, 0, 0, 0, 36, 37,
- 0, 38, 0, 0, 0, 0, 0, 0, 521, 0,
- 0, 0, 45, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 53, 522, 524, 523, 0, 54, 0, 0, 0, 0,
- 230, 0, 0, 0, 0, 0, 44, 56, 32, 216,
- 224, 0, 0, 41, 0, 0, 520, 0, 0, 0,
- 0, 46, 34, 51, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 519, 0, 30, 31, 0, 0, 0,
- 0, 0, 0, 0, 0, 221, 0, 0, 0, 0,
- 0, 0, 35, 0, 0, 0, 36, 37, 0, 38,
- 0, 0, 0, 0, 0, 0, 521, 0, 0, 0,
- 45, 0, 0, 0, 0, 0, 0, 0, 585, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 53, 522,
- 524, 523, 0, 54, 0, 0, 0, 0, 230, 0,
- 0, 0, 0, 0, 44, 56, 32, 216, 224, 0,
- 0, 41, 0, 0, 520, 0, 0, 0, 0, 46,
- 34, 51, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 519, 0, 30, 31, 0, 0, 0, 0, 0,
- 0, 0, 0, 221, 0, 0, 0, 0, 0, 0,
- 35, 0, 0, 0, 36, 37, 0, 38, 0, 0,
- 0, 0, 0, 0, 521, 0, 0, 0, 45, 0,
- 0, 0, 0, 0, 0, 0, 588, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 53, 522, 524, 523,
- 0, 54, 0, 0, 0, 0, 230, 0, 0, 0,
- 0, 0, 44, 56, 32, 216, 224, 0, 0, 41,
- 0, 0, 520, 0, 0, 0, 0, 46, 34, 51,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 29,
- 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
- 33, 0, 0, 0, 0, 0, 0, 35, 0, 0,
- 0, 36, 37, 0, 38, 0, 0, 0, 39, 0,
- 40, 42, 43, 0, 0, 45, 0, 0, 0, 47,
- 0, 48, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 53, 49, 52, 50, 0, 54, 0,
- 55, 0, 57, 0, 58, 0, 0, 0, 0, 44,
- 56, 32, 0, 0, 0, 0, 41, 0, 0, 0,
- 0, 0, 0, 0, 46, 34, 51, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, -141, 0, 0, 0,
- 29, 30, 31, 0, 0, 0, 0, 0, 0, 0,
- 0, 33, 0, 0, 0, 0, 0, 0, 35, 0,
- 0, 0, 36, 37, 0, 38, 0, 0, 0, 39,
- 0, 40, 42, 43, 0, 0, 45, 0, 0, 0,
- 47, 0, 48, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 53, 49, 52, 50, 0, 54,
- 0, 55, 0, 57, 0, 58, 0, 0, 0, 0,
- 44, 56, 32, 0, 0, 0, 0, 41, 0, 0,
- 0, 0, 0, 0, 0, 46, 34, 51, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 29, 30, 31,
- 0, 0, 0, 0, 0, 0, 0, 0, 33, 0,
- 0, 0, 0, 0, 0, 35, 0, 0, 0, 36,
- 37, 0, 38, 0, 0, 0, 39, 0, 40, 42,
- 43, 0, 0, 45, 0, 0, 0, 47, 0, 48,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 53, 49, 52, 50, 0, 54, 0, 55, 0,
- 57, 285, 58, 0, 0, 0, 0, 44, 56, 32,
- 0, 0, 0, 0, 41, 0, 0, 0, 0, 0,
- 0, 0, 46, 34, 51, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 492, 0, 0, 29, 30, 31,
- 0, 0, 0, 0, 0, 0, 0, 0, 33, 0,
- 0, 0, 0, 0, 0, 35, 0, 0, 0, 36,
- 37, 0, 38, 0, 0, 0, 39, 0, 40, 42,
- 43, 0, 0, 45, 0, 0, 0, 47, 0, 48,
- 0, 0, 493, 0, 0, 0, 0, 0, 0, 0,
- 0, 53, 49, 52, 50, 0, 54, 0, 55, 0,
- 57, 0, 58, 0, 0, 0, 0, 44, 56, 32,
- 0, 0, 0, 0, 41, 0, 0, 0, 0, 0,
- 0, 0, 46, 34, 51, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 500, 0, 0, 29, 30, 31,
- 0, 0, 0, 0, 0, 0, 0, 0, 33, 0,
- 0, 0, 0, 0, 0, 35, 0, 0, 0, 36,
- 37, 0, 38, 0, 0, 0, 39, 0, 40, 42,
- 43, 0, 0, 45, 0, 0, 0, 47, 0, 48,
- 0, 0, 503, 0, 0, 0, 0, 0, 0, 0,
- 0, 53, 49, 52, 50, 0, 54, 0, 55, 0,
- 57, 0, 58, 0, 0, 0, 0, 44, 56, 32,
- 0, 0, 0, 0, 41, 0, 0, 0, 0, 0,
- 0, 0, 46, 34, 51, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 492, 0, 0, 29, 30, 31,
- 0, 0, 0, 0, 0, 0, 0, 0, 33, 0,
- 0, 0, 0, 0, 0, 35, 0, 0, 0, 36,
- 37, 0, 38, 0, 0, 0, 39, 0, 40, 42,
- 43, 0, 0, 45, 0, 0, 0, 47, 0, 48,
- 0, 0, 498, 0, 0, 0, 0, 0, 0, 0,
- 0, 53, 49, 52, 50, 0, 54, 0, 55, 0,
- 57, 0, 58, 0, 0, 0, 0, 44, 56, 32,
- 0, 0, 0, 0, 41, 0, 0, 0, 0, 0,
- 0, 0, 46, 34, 51, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 500, 0, 0, 29, 30, 31,
- 0, 0, 0, 0, 0, 0, 0, 0, 33, 0,
- 0, 0, 0, 0, 0, 35, 0, 0, 0, 36,
- 37, 0, 38, 0, 0, 0, 39, 0, 40, 42,
- 43, 0, 0, 45, 0, 0, 0, 47, 0, 48,
- 0, 0, 501, 0, 0, 0, 0, 0, 0, 0,
- 0, 53, 49, 52, 50, 0, 54, 0, 55, 0,
- 57, 0, 58, 0, 0, 0, 0, 44, 56, 32,
- 0, 0, 0, 0, 41, 0, 0, 0, 0, 0,
- 0, 0, 46, 34, 51, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 29, 30, 31, 0, 0, 0,
- 0, 0, 0, 0, 0, 33, 0, 0, 0, 0,
- 0, 0, 35, 222, 0, 0, 223, 37, 0, 38,
- 0, 0, 0, 39, 0, 40, 42, 43, 0, 0,
- 45, 0, 0, 0, 47, 0, 48, 0, 0, 0,
- 0, 0, 0, 0, 226, 0, 0, 0, 53, 49,
- 52, 50, 227, 54, 0, 55, 229, 57, 0, 58,
- 0, 232, 0, 0, 44, 56, 32, 0, 0, 0,
- 0, 41, 0, 0, 0, 0, 0, 0, 0, 46,
- 34, 51, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 29, 30, 31, 0, 0, 0, 0, 0, 0,
- 0, 0, 33, 0, 0, 0, 0, 0, 0, 35,
- 222, 0, 0, 603, 650, 0, 38, 0, 0, 0,
- 39, 0, 40, 42, 43, 0, 0, 45, 0, 0,
- 0, 47, 0, 48, 0, 0, 0, 0, 0, 0,
- 0, 226, 0, 0, 0, 53, 49, 52, 50, 227,
- 54, 0, 55, 229, 57, 0, 58, 0, 232, 0,
- 0, 44, 56, 32, 0, 0, 0, 0, 41, 0,
- 0, 0, 0, 0, 0, 0, 46, 34, 51, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 29, 30,
- 31, 0, 0, 0, 0, 0, 0, 0, 0, 33,
- 0, 0, 0, 0, 0, 0, 35, 222, 0, 0,
- 603, 37, 0, 38, 0, 0, 0, 39, 0, 40,
- 42, 43, 0, 0, 45, 0, 0, 0, 47, 0,
- 48, 0, 0, 0, 0, 0, 0, 0, 226, 0,
- 0, 0, 53, 49, 52, 50, 227, 54, 0, 55,
- 229, 57, 0, 58, 0, 232, 0, 0, 44, 56,
- 32, 0, 0, 0, 0, 41, 0, 0, 0, 0,
- 0, 0, 0, 46, 34, 51, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 111, 112, 113, 0, 0,
- 115, 117, 118, 0, 0, 119, 0, 120, 0, 0,
- 0, 123, 124, 125, 0, 0, 0, 0, 0, 0,
- 35, 126, 127, 128, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 130, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 133, 0, 0, 0, 0, 0, 0, 49, 52, 50,
- 134, 135, 136, 0, 138, 139, 140, 141, 142, 143,
- 0, 0, 131, 137, 122, 114, 129, 116, 132, 0,
- 0, 0, 121, 0, 0, 0, 0, 46, 34, 51,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 111,
- 112, 113, 0, 0, 115, 117, 118, 0, 0, 119,
- 0, 120, 0, 0, 0, 123, 124, 125, 0, 0,
- 0, 0, 0, 0, 35, 126, 127, 128, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 130, 0,
- 0, 0, 399, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 133, 0, 0, 0, 0, 0,
- 401, 49, 52, 50, 134, 135, 136, 0, 138, 139,
- 140, 141, 142, 143, 0, 0, 131, 137, 122, 114,
- 129, 116, 132, 0, 0, 0, 121, 0, 0, 0,
- 0, 46, 34, 51, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 111, 112, 113, 0, 0, 115, 117,
- 118, 0, 0, 119, 0, 120, 0, 0, 0, 123,
- 124, 125, 0, 0, 0, 0, 0, 0, 35, 126,
- 127, 128, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 130, 0, 0, 0, 399, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 133, 0,
- 0, 0, 0, 0, 401, 49, 52, 50, 134, 135,
- 136, 0, 138, 139, 140, 141, 142, 143, 0, 0,
- 131, 137, 122, 114, 129, 116, 132, 0, 0, 0,
- 121, 0, 0, 0, 0, 46, 377, 384, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 111, 112, 113,
- 0, 0, 115, 117, 118, 0, 0, 119, 0, 120,
- 0, 0, 0, 123, 124, 125, 0, 0, 0, 0,
- 0, 0, 35, 126, 127, 128, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 130, 0, 0, 0,
- 399, 0, 0, 0, 0, 0, 0, 0, 400, 0,
- 0, 0, 133, 0, 0, 0, 0, 0, 401, 49,
- 52, 50, 134, 135, 136, 0, 138, 139, 140, 141,
- 142, 143, 0, 0, 131, 137, 122, 114, 129, 116,
- 132, 0, 0, 0, 121, 0, 0, 0, 0, 46,
- 377, 384, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 215, 0, 0, 0, 0, 217, 0, 29, 30,
- 31, 219, 0, 0, 0, 0, 0, 0, 220, 33,
- 0, 0, 0, 0, 0, 0, 35, 222, 0, 0,
- 223, 37, 0, 38, 0, 0, 0, 39, 0, 40,
- 42, 43, 0, 0, 45, 0, 0, 0, 47, 0,
- 48, 0, 0, 0, 0, 0, 225, 0, 226, 0,
- 0, 0, 53, 49, 52, 50, 227, 54, 228, 55,
- 229, 57, 230, 58, 231, 232, 0, 0, 44, 56,
- 32, 216, 224, 218, 0, 41, 0, 0, 0, 0,
- 0, 0, 0, 46, 34, 51, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 215, 0, 0, 0, 0,
- 217, 0, 29, 30, 31, 219, 0, 0, 0, 0,
- 0, 0, 220, 221, 0, 0, 0, 0, 0, 0,
- 35, 222, 0, 0, 223, 37, 0, 38, 0, 0,
- 0, 39, 0, 40, 42, 43, 0, 0, 45, 0,
- 0, 0, 47, 0, 48, 0, 0, 0, 0, 0,
- 225, 0, 226, 0, 0, 0, 53, 49, 52, 50,
- 227, 54, 228, 55, 229, 57, 230, 58, 231, 232,
- 0, 0, 44, 56, 32, 216, 224, 218, 0, 41,
- 0, 0, 0, 0, 0, 0, 0, 46, 34, 51,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 607,
- 112, 113, 0, 0, 609, 117, 611, 30, 31, 612,
- 0, 120, 0, 0, 0, 123, 614, 615, 0, 0,
- 0, 0, 0, 0, 35, 616, 127, 128, 223, 37,
- 0, 38, 0, 0, 0, 39, 0, 40, 618, 43,
- 0, 0, 620, 0, 0, 0, 47, 0, 48, 0,
- 0, 0, 0, 0, 621, 0, 226, 0, 0, 0,
- 622, 49, 52, 50, 623, 624, 625, 55, 627, 628,
- 629, 630, 631, 632, 0, 0, 619, 626, 613, 608,
- 617, 610, 132, 41, 0, 0, 121, 0, 0, 0,
- 0, 46, 377, 384, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 368, 112, 113, 0, 0, 370, 117,
- 372, 30, 31, 373, 0, 120, 0, 0, 0, 123,
- 375, 376, 0, 0, 0, 0, 0, 0, 35, 378,
- 127, 128, 223, 37, 0, 38, 0, 0, 0, 39,
- 0, 40, 380, 43, 0, 0, 382, 0, 0, 0,
- 47, 0, 48, 0, -288, 0, 0, 0, 383, 0,
- 226, 0, 0, 0, 385, 49, 52, 50, 386, 387,
- 388, 55, 390, 391, 392, 393, 394, 395, 0, 0,
- 381, 389, 374, 369, 379, 371, 132, 41, 0, 0,
- 121, 0, 0, 0, 0, 46, 377, 384, 0, 0,
- 0, 0, 0, 0, 0, 0, 0,
-
- 543, 185, 640, 647, 642, 643, 504, 489, 397, 451,
- 655, 657, 669, 670, 154, 680, 658, 448, 686, 316,
- 185, 16, 256, 536, 251, 599, 570, 633, 572, 590,
- 597, 144, 323, 540, 457, 150, 266, 473, 190, 441,
- 350, 445, 251, 192, 256, 437, 429, 354, 208, 240,
- 185, 316, 251, 256, 185, 404, 448, 448, 316, 533,
- 566, 470, 466, 180, 451, 451, 462, 0, 62, 334,
- 510, 516, 62, 0, 0, 325, 62, 299, 316, 0,
- 459, 298, 397, 334, 0, 147, 461, 208, 499, 0,
- 152, 208, 502, 167, 600, 479, 673, 672, 518, 173,
- 175, 515, 316, 674, 208, 397, 316, 518, 316, 407,
- 208, 0, 190, 0, 321, 208, 656, 352, 62, 249,
- 464, 62, 62, 184, 509, 62, 62, 463, 463, 62,
- 208, 508, 421, 208, 62, 208, 184, 356, 245, 358,
- 405, 242, 263, 280, 62, 62, 62, 506, 284, 302,
- 62, 301, 507, 208, 317, 208, 208, 62, 208, 62,
- 343, 184, 300, 413, 348, 365, 62, 0, 62, 190,
- 518, 62, 99, 62, 100, 578, 86, 90, 346, 62,
- 208, 208, 319, 91, 344, 62, 62, 62, 413, 62,
- 93, 94, 95, 473, 96, 468, 514, 62, 189, 62,
- 150, 414, 97, 92, 208, 62, 472, 464, 182, 62,
- 62, 208, 497, 62, 62, 397, 496, 250, 365, 62,
- 104, 102, 208, 263, 208, 98, 414, 263, 169, 172,
- 365, 208, 487, 62, 359, 511, 62, 250, 463, 208,
- 62, 398, 464, 208, 62, 62, 606, 63, 72, 190,
- 150, 208, 411, 62, 518, 69, 62, 208, 416, 582,
- 365, 365, 79, 62, 62, 70, 62, 62, 483, 71,
- 0, 284, 74, 0, 0, 62, 208, 208, 423, 307,
- 284, 0, 62, 0, 287, 426, 108, 284, 0, 292,
- 62, 62, 0, 0, 0, 284, 284, 303, 304, 62,
- 312, 0, 62, 312, 284, 284, 305, 284, 284, 312,
- 62, 0, 312, 309, 284, 284, 110, 284, 62, 312,
- 0, 594, 333, 284, 284, 340, 578, 330, 653, 0,
- 518, 331, 0, 327, 311, 586, 0, 589, 0, 527,
- 0, 314, 0, 0, 518, 0, 518, 444, 447, 0,
- 489, 517, 528, 527, 0, 527, 547, 548, 549, 550,
- 554, 551, 552, 0, 0, 517, 528, 517, 528, 0,
- 0, 0, 602, 0, 0, 0, 0, 0, 0, 0,
- 108, 604, 605, 547, 548, 549, 550, 554, 551, 552,
- 594, 0, 0, 0, 0, 0, 0, 0, 0, 595,
- 596, 547, 548, 549, 550, 554, 551, 552, 0, 0,
- 110, 179, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 602, 0, 0, 0, 0, 0,
- 0, 0, 0, 651, 652, 547, 548, 549, 550, 554,
- 551, 552, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0
-};
-
-const short QQmlJSGrammar::action_check [] = {
- 7, 33, 60, 60, 7, 36, 7, 7, 60, 7,
- 36, 36, 7, 7, 60, 36, 55, 7, 33, 55,
- 33, 7, 36, 7, 36, 36, 7, 37, 36, 7,
- 36, 7, 7, 7, 33, 7, 33, 33, 47, 66,
- 17, 34, 47, 66, 29, 37, 29, 29, 17, 29,
- 29, 17, 5, 5, 5, 20, 60, 7, 60, 8,
- 33, 7, 33, 33, 55, 33, 55, 7, 55, 36,
- 33, 33, 60, 33, 7, 36, 33, 36, 36, 33,
- 8, 7, 2, 7, 1, 8, 1, 8, 1, 36,
- 8, 2, 8, -1, 2, -1, 60, -1, 33, 55,
- 55, 7, 36, 1, 48, 0, 7, 36, 2, 36,
- 48, 36, 60, 17, 33, 60, 48, 16, 8, 55,
- 61, 60, 36, 7, 36, 8, 36, 36, 8, 60,
- 8, 8, 6, 8, 15, -1, 79, 79, 8, 61,
- 77, 48, 15, 7, 8, 8, 20, 15, 15, 79,
- -1, 24, 8, 34, 8, 8, -1, -1, 61, 61,
- 62, 61, 62, 8, 61, 62, 34, 34, 50, 61,
- 62, 50, 54, 8, 79, 54, 8, 60, 56, 10,
- 60, 56, 40, 60, 8, 61, 62, 8, 40, 12,
- 60, 7, 55, 51, 40, 61, 62, 93, 94, 51,
- 61, 62, 31, 7, 60, 51, 60, 60, 93, 94,
- 15, 56, 61, 62, 61, 62, 42, 61, 62, 24,
- 15, 56, 61, 62, 55, 61, 62, 53, 60, 33,
- 29, 29, 7, 12, 57, 56, 25, 61, 27, 34,
- 63, 36, 25, 7, 27, 61, 62, 29, 25, 38,
- 27, 25, 25, 27, 27, 38, 25, 29, 27, 95,
- -1, 38, 29, -1, 38, 38, 29, -1, 12, 38,
- 36, 29, 25, -1, 27, -1, 75, 75, 57, 40,
- 15, 8, -1, 8, 63, 38, 61, 62, 87, 87,
- 51, -1, 96, 75, -1, 61, 62, 61, 62, 34,
- -1, 36, 15, 75, 8, 87, -1, -1, 75, 25,
- -1, 27, 75, 57, 25, 87, 27, 75, -1, 63,
- 87, 34, 38, 36, 87, 61, 62, 38, 25, 87,
- 27, 25, -1, 27, 61, 62, 61, 62, 25, -1,
- 27, 38, 15, 25, 38, 27, 15, 15, 18, 19,
- -1, 38, 18, 19, 90, 47, 38, 61, 62, -1,
- 33, 34, -1, 36, 33, 34, 34, 36, 36, 61,
- 62, -1, 23, 24, -1, 45, 46, -1, -1, 45,
- 46, 32, 23, 24, 35, -1, 37, -1, 23, 24,
- -1, 32, 23, 24, 35, -1, 37, 32, 23, 24,
- 35, 32, 37, 95, 35, -1, 37, 32, 23, 24,
- 35, -1, 37, -1, -1, -1, 31, 32, 23, 24,
- 35, 29, 37, -1, -1, 29, 31, 32, -1, -1,
- 35, -1, 37, 29, 23, 24, 29, -1, -1, 29,
- 18, 19, 31, 32, 29, -1, 35, 29, 37, -1,
- 100, 101, 102, 103, 104, 105, -1, -1, 66, 67,
- 68, -1, 66, 67, 68, -1, -1, 45, 46, -1,
- 66, 67, 68, 66, 67, 68, 66, 67, 68, -1,
- 29, 66, 67, 68, 66, 67, 68, 29, 96, 97,
- 98, -1, 96, 97, 98, 29, -1, -1, 29, -1,
- 96, 97, 98, 96, 97, 98, 96, 97, 98, -1,
- -1, 96, 97, 98, 96, 97, 98, 66, 67, 68,
- 29, -1, -1, -1, 66, 67, 68, -1, -1, -1,
- 23, 24, 66, 67, 68, 66, 67, 68, 31, 32,
- -1, -1, 35, 29, 37, -1, 15, 96, 97, 98,
- 36, -1, -1, -1, 96, 97, 98, 66, 67, 68,
- 29, -1, 96, 97, 98, 96, 97, 98, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, 15,
- 66, 67, 68, -1, -1, -1, 15, 96, 97, 98,
- 15, -1, -1, 29, 15, -1, 29, 66, 67, 68,
- 29, -1, -1, 36, 29, -1, -1, -1, 29, -1,
- 96, 97, 98, -1, -1, 29, -1, -1, 29, -1,
- -1, -1, -1, -1, -1, -1, -1, 96, 97, 98,
- 66, 67, 68, 66, 67, 68, -1, 66, 67, 68,
- -1, 66, 67, 68, -1, 66, 67, 68, -1, -1,
- 61, 62, 66, 67, 68, 66, 67, 68, 29, -1,
- 96, 97, 98, 96, 97, 98, -1, 96, 97, 98,
- 29, 96, 97, 98, -1, 96, 97, 98, 29, -1,
- -1, 29, 96, 97, 98, 96, 97, 98, -1, -1,
- 61, 62, -1, -1, 29, 66, 67, 68, -1, -1,
- -1, 36, 61, 62, -1, -1, -1, 66, 67, 68,
- 61, 62, -1, 61, 62, 66, 67, 68, 66, 67,
- 68, -1, -1, -1, -1, 96, 97, 98, -1, -1,
- 3, 66, 67, 68, -1, -1, -1, 96, 97, 98,
- 13, -1, -1, -1, 17, 96, 97, 98, 96, 97,
- 98, -1, -1, 26, -1, 28, -1, -1, -1, -1,
- -1, 96, 97, 98, -1, -1, 39, -1, 41, 42,
- -1, -1, -1, 29, -1, -1, 49, -1, -1, 52,
- 53, -1, -1, -1, -1, 58, -1, -1, -1, -1,
- -1, 64, -1, -1, -1, -1, -1, -1, -1, 3,
- -1, -1, -1, -1, -1, 61, 62, 80, -1, 13,
- 66, 67, 68, 17, -1, -1, -1, -1, -1, -1,
- -1, -1, 26, -1, 28, -1, -1, 31, -1, -1,
- -1, -1, -1, -1, -1, 39, -1, 41, 42, -1,
- 96, 97, 98, -1, -1, 49, -1, -1, 52, 53,
- -1, -1, 3, -1, 58, -1, -1, -1, -1, -1,
- 64, -1, 13, -1, -1, -1, 17, -1, -1, -1,
- -1, -1, -1, -1, -1, 26, 80, 28, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, 39, -1,
- 41, 42, -1, -1, -1, -1, -1, -1, 49, -1,
- -1, 52, 53, -1, -1, -1, -1, 58, -1, -1,
- -1, -1, -1, 64, -1, 12, 13, -1, -1, -1,
- -1, -1, -1, -1, -1, 22, -1, -1, -1, 80,
- -1, -1, 29, -1, -1, -1, 33, 34, -1, 36,
- -1, -1, -1, -1, -1, -1, 43, -1, -1, -1,
- 47, -1, -1, -1, -1, -1, -1, 12, 13, -1,
- -1, -1, -1, -1, -1, -1, -1, 22, 65, 66,
- 67, 68, -1, 70, 29, -1, -1, -1, 33, 34,
- -1, 36, -1, -1, 81, 82, 83, -1, 43, -1,
- -1, 88, 47, -1, -1, -1, -1, -1, -1, 96,
- 97, 98, -1, -1, -1, -1, -1, -1, -1, -1,
- 65, 66, 67, 68, -1, 70, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 81, 82, 83, -1,
- -1, -1, -1, 88, -1, -1, -1, -1, -1, -1,
- -1, 96, 97, 98, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 12, 13, -1, -1, -1, -1, -1,
- -1, -1, -1, 22, -1, -1, -1, -1, -1, -1,
- 29, -1, -1, -1, 33, 34, -1, 36, -1, -1,
- -1, -1, -1, -1, 43, -1, -1, -1, 47, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 65, 66, 67, 68,
- -1, 70, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 81, 82, 83, -1, -1, -1, -1, 88,
- -1, -1, -1, -1, -1, -1, -1, 96, 97, 98,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, 12,
- 13, -1, -1, -1, -1, -1, -1, -1, -1, 22,
- -1, -1, -1, -1, -1, -1, 29, -1, -1, -1,
- 33, 34, -1, 36, -1, -1, -1, -1, -1, -1,
- 43, -1, -1, -1, 47, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 65, 66, 67, 68, -1, 70, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, 81, 82,
- 83, -1, -1, -1, -1, 88, -1, -1, -1, -1,
- -1, -1, -1, 96, 97, 98, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 10, -1, 12, 13, -1,
- -1, -1, -1, -1, -1, -1, -1, 22, -1, -1,
- -1, -1, -1, -1, 29, -1, -1, -1, 33, 34,
- -1, 36, -1, -1, -1, -1, -1, -1, 43, -1,
- -1, -1, 47, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- 65, 66, 67, 68, -1, 70, -1, -1, -1, -1,
- 75, -1, -1, -1, -1, -1, 81, 82, 83, 84,
- 85, -1, -1, 88, -1, -1, 91, -1, -1, -1,
- -1, 96, 97, 98, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 10, -1, 12, 13, -1, -1, -1,
- -1, -1, -1, -1, -1, 22, -1, -1, -1, -1,
- -1, -1, 29, -1, -1, -1, 33, 34, -1, 36,
- -1, -1, -1, -1, -1, -1, 43, -1, -1, -1,
- 47, -1, -1, -1, -1, -1, -1, -1, 55, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, 65, 66,
- 67, 68, -1, 70, -1, -1, -1, -1, 75, -1,
- -1, -1, -1, -1, 81, 82, 83, 84, 85, -1,
- -1, 88, -1, -1, 91, -1, -1, -1, -1, 96,
- 97, 98, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 10, -1, 12, 13, -1, -1, -1, -1, -1,
- -1, -1, -1, 22, -1, -1, -1, -1, -1, -1,
- 29, -1, -1, -1, 33, 34, -1, 36, -1, -1,
- -1, -1, -1, -1, 43, -1, -1, -1, 47, -1,
- -1, -1, -1, -1, -1, -1, 55, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 65, 66, 67, 68,
- -1, 70, -1, -1, -1, -1, 75, -1, -1, -1,
- -1, -1, 81, 82, 83, 84, 85, -1, -1, 88,
- -1, -1, 91, -1, -1, -1, -1, 96, 97, 98,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, 11,
- 12, 13, -1, -1, -1, -1, -1, -1, -1, -1,
- 22, -1, -1, -1, -1, -1, -1, 29, -1, -1,
- -1, 33, 34, -1, 36, -1, -1, -1, 40, -1,
- 42, 43, 44, -1, -1, 47, -1, -1, -1, 51,
- -1, 53, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 65, 66, 67, 68, -1, 70, -1,
- 72, -1, 74, -1, 76, -1, -1, -1, -1, 81,
- 82, 83, -1, -1, -1, -1, 88, -1, -1, -1,
- -1, -1, -1, -1, 96, 97, 98, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 7, -1, -1, -1,
- 11, 12, 13, -1, -1, -1, -1, -1, -1, -1,
- -1, 22, -1, -1, -1, -1, -1, -1, 29, -1,
- -1, -1, 33, 34, -1, 36, -1, -1, -1, 40,
- -1, 42, 43, 44, -1, -1, 47, -1, -1, -1,
- 51, -1, 53, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 65, 66, 67, 68, -1, 70,
- -1, 72, -1, 74, -1, 76, -1, -1, -1, -1,
- 81, 82, 83, -1, -1, -1, -1, 88, -1, -1,
- -1, -1, -1, -1, -1, 96, 97, 98, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 11, 12, 13,
- -1, -1, -1, -1, -1, -1, -1, -1, 22, -1,
- -1, -1, -1, -1, -1, 29, -1, -1, -1, 33,
- 34, -1, 36, -1, -1, -1, 40, -1, 42, 43,
- 44, -1, -1, 47, -1, -1, -1, 51, -1, 53,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 65, 66, 67, 68, -1, 70, -1, 72, -1,
- 74, 75, 76, -1, -1, -1, -1, 81, 82, 83,
- -1, -1, -1, -1, 88, -1, -1, -1, -1, -1,
- -1, -1, 96, 97, 98, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 8, -1, -1, 11, 12, 13,
- -1, -1, -1, -1, -1, -1, -1, -1, 22, -1,
- -1, -1, -1, -1, -1, 29, -1, -1, -1, 33,
- 34, -1, 36, -1, -1, -1, 40, -1, 42, 43,
- 44, -1, -1, 47, -1, -1, -1, 51, -1, 53,
- -1, -1, 56, -1, -1, -1, -1, -1, -1, -1,
- -1, 65, 66, 67, 68, -1, 70, -1, 72, -1,
- 74, -1, 76, -1, -1, -1, -1, 81, 82, 83,
- -1, -1, -1, -1, 88, -1, -1, -1, -1, -1,
- -1, -1, 96, 97, 98, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 8, -1, -1, 11, 12, 13,
- -1, -1, -1, -1, -1, -1, -1, -1, 22, -1,
- -1, -1, -1, -1, -1, 29, -1, -1, -1, 33,
- 34, -1, 36, -1, -1, -1, 40, -1, 42, 43,
- 44, -1, -1, 47, -1, -1, -1, 51, -1, 53,
- -1, -1, 56, -1, -1, -1, -1, -1, -1, -1,
- -1, 65, 66, 67, 68, -1, 70, -1, 72, -1,
- 74, -1, 76, -1, -1, -1, -1, 81, 82, 83,
- -1, -1, -1, -1, 88, -1, -1, -1, -1, -1,
- -1, -1, 96, 97, 98, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 8, -1, -1, 11, 12, 13,
- -1, -1, -1, -1, -1, -1, -1, -1, 22, -1,
- -1, -1, -1, -1, -1, 29, -1, -1, -1, 33,
- 34, -1, 36, -1, -1, -1, 40, -1, 42, 43,
- 44, -1, -1, 47, -1, -1, -1, 51, -1, 53,
- -1, -1, 56, -1, -1, -1, -1, -1, -1, -1,
- -1, 65, 66, 67, 68, -1, 70, -1, 72, -1,
- 74, -1, 76, -1, -1, -1, -1, 81, 82, 83,
- -1, -1, -1, -1, 88, -1, -1, -1, -1, -1,
- -1, -1, 96, 97, 98, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 8, -1, -1, 11, 12, 13,
- -1, -1, -1, -1, -1, -1, -1, -1, 22, -1,
- -1, -1, -1, -1, -1, 29, -1, -1, -1, 33,
- 34, -1, 36, -1, -1, -1, 40, -1, 42, 43,
- 44, -1, -1, 47, -1, -1, -1, 51, -1, 53,
- -1, -1, 56, -1, -1, -1, -1, -1, -1, -1,
- -1, 65, 66, 67, 68, -1, 70, -1, 72, -1,
- 74, -1, 76, -1, -1, -1, -1, 81, 82, 83,
- -1, -1, -1, -1, 88, -1, -1, -1, -1, -1,
- -1, -1, 96, 97, 98, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 11, 12, 13, -1, -1, -1,
- -1, -1, -1, -1, -1, 22, -1, -1, -1, -1,
- -1, -1, 29, 30, -1, -1, 33, 34, -1, 36,
- -1, -1, -1, 40, -1, 42, 43, 44, -1, -1,
- 47, -1, -1, -1, 51, -1, 53, -1, -1, -1,
- -1, -1, -1, -1, 61, -1, -1, -1, 65, 66,
- 67, 68, 69, 70, -1, 72, 73, 74, -1, 76,
- -1, 78, -1, -1, 81, 82, 83, -1, -1, -1,
- -1, 88, -1, -1, -1, -1, -1, -1, -1, 96,
- 97, 98, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 11, 12, 13, -1, -1, -1, -1, -1, -1,
- -1, -1, 22, -1, -1, -1, -1, -1, -1, 29,
- 30, -1, -1, 33, 34, -1, 36, -1, -1, -1,
- 40, -1, 42, 43, 44, -1, -1, 47, -1, -1,
- -1, 51, -1, 53, -1, -1, -1, -1, -1, -1,
- -1, 61, -1, -1, -1, 65, 66, 67, 68, 69,
- 70, -1, 72, 73, 74, -1, 76, -1, 78, -1,
- -1, 81, 82, 83, -1, -1, -1, -1, 88, -1,
- -1, -1, -1, -1, -1, -1, 96, 97, 98, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, 11, 12,
- 13, -1, -1, -1, -1, -1, -1, -1, -1, 22,
- -1, -1, -1, -1, -1, -1, 29, 30, -1, -1,
- 33, 34, -1, 36, -1, -1, -1, 40, -1, 42,
- 43, 44, -1, -1, 47, -1, -1, -1, 51, -1,
- 53, -1, -1, -1, -1, -1, -1, -1, 61, -1,
- -1, -1, 65, 66, 67, 68, 69, 70, -1, 72,
- 73, 74, -1, 76, -1, 78, -1, -1, 81, 82,
- 83, -1, -1, -1, -1, 88, -1, -1, -1, -1,
- -1, -1, -1, 96, 97, 98, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4, 5, 6, -1, -1,
- 9, 10, 11, -1, -1, 14, -1, 16, -1, -1,
- -1, 20, 21, 22, -1, -1, -1, -1, -1, -1,
- 29, 30, 31, 32, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 43, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- 59, -1, -1, -1, -1, -1, -1, 66, 67, 68,
- 69, 70, 71, -1, 73, 74, 75, 76, 77, 78,
- -1, -1, 81, 82, 83, 84, 85, 86, 87, -1,
- -1, -1, 91, -1, -1, -1, -1, 96, 97, 98,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, 4,
- 5, 6, -1, -1, 9, 10, 11, -1, -1, 14,
- -1, 16, -1, -1, -1, 20, 21, 22, -1, -1,
- -1, -1, -1, -1, 29, 30, 31, 32, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, 43, -1,
- -1, -1, 47, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 59, -1, -1, -1, -1, -1,
- 65, 66, 67, 68, 69, 70, 71, -1, 73, 74,
- 75, 76, 77, 78, -1, -1, 81, 82, 83, 84,
- 85, 86, 87, -1, -1, -1, 91, -1, -1, -1,
- -1, 96, 97, 98, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4, 5, 6, -1, -1, 9, 10,
- 11, -1, -1, 14, -1, 16, -1, -1, -1, 20,
- 21, 22, -1, -1, -1, -1, -1, -1, 29, 30,
- 31, 32, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, 43, -1, -1, -1, 47, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, 59, -1,
- -1, -1, -1, -1, 65, 66, 67, 68, 69, 70,
- 71, -1, 73, 74, 75, 76, 77, 78, -1, -1,
- 81, 82, 83, 84, 85, 86, 87, -1, -1, -1,
- 91, -1, -1, -1, -1, 96, 97, 98, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 4, 5, 6,
- -1, -1, 9, 10, 11, -1, -1, 14, -1, 16,
- -1, -1, -1, 20, 21, 22, -1, -1, -1, -1,
- -1, -1, 29, 30, 31, 32, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, 43, -1, -1, -1,
- 47, -1, -1, -1, -1, -1, -1, -1, 55, -1,
- -1, -1, 59, -1, -1, -1, -1, -1, 65, 66,
- 67, 68, 69, 70, 71, -1, 73, 74, 75, 76,
- 77, 78, -1, -1, 81, 82, 83, 84, 85, 86,
- 87, -1, -1, -1, 91, -1, -1, -1, -1, 96,
- 97, 98, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 4, -1, -1, -1, -1, 9, -1, 11, 12,
- 13, 14, -1, -1, -1, -1, -1, -1, 21, 22,
- -1, -1, -1, -1, -1, -1, 29, 30, -1, -1,
- 33, 34, -1, 36, -1, -1, -1, 40, -1, 42,
- 43, 44, -1, -1, 47, -1, -1, -1, 51, -1,
- 53, -1, -1, -1, -1, -1, 59, -1, 61, -1,
- -1, -1, 65, 66, 67, 68, 69, 70, 71, 72,
- 73, 74, 75, 76, 77, 78, -1, -1, 81, 82,
- 83, 84, 85, 86, -1, 88, -1, -1, -1, -1,
- -1, -1, -1, 96, 97, 98, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, 4, -1, -1, -1, -1,
- 9, -1, 11, 12, 13, 14, -1, -1, -1, -1,
- -1, -1, 21, 22, -1, -1, -1, -1, -1, -1,
- 29, 30, -1, -1, 33, 34, -1, 36, -1, -1,
- -1, 40, -1, 42, 43, 44, -1, -1, 47, -1,
- -1, -1, 51, -1, 53, -1, -1, -1, -1, -1,
- 59, -1, 61, -1, -1, -1, 65, 66, 67, 68,
- 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
- -1, -1, 81, 82, 83, 84, 85, 86, -1, 88,
- -1, -1, -1, -1, -1, -1, -1, 96, 97, 98,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, 4,
- 5, 6, -1, -1, 9, 10, 11, 12, 13, 14,
- -1, 16, -1, -1, -1, 20, 21, 22, -1, -1,
- -1, -1, -1, -1, 29, 30, 31, 32, 33, 34,
- -1, 36, -1, -1, -1, 40, -1, 42, 43, 44,
- -1, -1, 47, -1, -1, -1, 51, -1, 53, -1,
- -1, -1, -1, -1, 59, -1, 61, -1, -1, -1,
- 65, 66, 67, 68, 69, 70, 71, 72, 73, 74,
- 75, 76, 77, 78, -1, -1, 81, 82, 83, 84,
- 85, 86, 87, 88, -1, -1, 91, -1, -1, -1,
- -1, 96, 97, 98, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, 4, 5, 6, -1, -1, 9, 10,
- 11, 12, 13, 14, -1, 16, -1, -1, -1, 20,
- 21, 22, -1, -1, -1, -1, -1, -1, 29, 30,
- 31, 32, 33, 34, -1, 36, -1, -1, -1, 40,
- -1, 42, 43, 44, -1, -1, 47, -1, -1, -1,
- 51, -1, 53, -1, 55, -1, -1, -1, 59, -1,
- 61, -1, -1, -1, 65, 66, 67, 68, 69, 70,
- 71, 72, 73, 74, 75, 76, 77, 78, -1, -1,
- 81, 82, 83, 84, 85, 86, 87, 88, -1, -1,
- 91, -1, -1, -1, -1, 96, 97, 98, -1, -1,
- -1, -1, -1, -1, -1, -1, -1,
-
- 18, 18, 32, 18, 32, 18, 3, 43, 18, 25,
- 22, 22, 14, 14, 78, 18, 9, 3, 18, 3,
- 18, 3, 18, 32, 18, 32, 32, 22, 18, 18,
- 22, 3, 3, 18, 106, 43, 3, 18, 18, 101,
- 3, 3, 18, 18, 18, 104, 3, 3, 18, 18,
- 18, 3, 18, 18, 18, 43, 3, 3, 3, 32,
- 14, 3, 3, 3, 25, 25, 25, -1, 55, 18,
- 57, 2, 55, -1, -1, 2, 55, 60, 3, -1,
- 18, 60, 18, 18, -1, 43, 25, 18, 43, -1,
- 43, 18, 43, 43, 18, 43, 11, 12, 14, 43,
- 43, 4, 3, 19, 18, 18, 3, 14, 3, 45,
- 18, -1, 18, -1, 2, 18, 23, 2, 55, 2,
- 57, 55, 55, 57, 57, 55, 55, 57, 57, 55,
- 18, 57, 45, 18, 55, 18, 57, 2, 46, 2,
- 2, 47, 2, 55, 55, 55, 55, 57, 60, 60,
- 55, 60, 57, 18, 79, 18, 18, 55, 18, 55,
- 95, 57, 60, 14, 2, 2, 55, -1, 55, 18,
- 14, 55, 61, 55, 61, 19, 60, 59, 79, 55,
- 18, 18, 79, 59, 79, 55, 55, 55, 14, 55,
- 60, 60, 60, 18, 60, 2, 110, 55, 47, 55,
- 43, 52, 60, 59, 18, 55, 2, 57, 51, 55,
- 55, 18, 39, 55, 55, 18, 43, 4, 2, 55,
- 65, 67, 18, 2, 18, 61, 52, 2, 69, 71,
- 2, 18, 46, 55, 18, 57, 55, 4, 57, 18,
- 55, 44, 57, 18, 55, 55, 18, 58, 58, 18,
- 43, 18, 46, 55, 14, 57, 55, 18, 51, 19,
- 2, 2, 61, 55, 55, 57, 55, 55, 93, 57,
- -1, 60, 63, -1, -1, 55, 18, 18, 47, 68,
- 60, -1, 55, -1, 64, 46, 18, 60, -1, 62,
- 55, 55, -1, -1, -1, 60, 60, 62, 62, 55,
- 55, -1, 55, 55, 60, 60, 62, 60, 60, 55,
- 55, -1, 55, 66, 60, 60, 48, 60, 55, 55,
- -1, 14, 77, 60, 60, 77, 19, 72, 21, -1,
- 14, 77, -1, 70, 77, 5, -1, 5, -1, 23,
- -1, 77, -1, -1, 14, -1, 14, 89, 89, -1,
- 43, 35, 36, 23, -1, 23, 25, 26, 27, 28,
- 29, 30, 31, -1, -1, 35, 36, 35, 36, -1,
- -1, -1, 14, -1, -1, -1, -1, -1, -1, -1,
- 18, 23, 24, 25, 26, 27, 28, 29, 30, 31,
- 14, -1, -1, -1, -1, -1, -1, -1, -1, 23,
- 24, 25, 26, 27, 28, 29, 30, 31, -1, -1,
- 48, 49, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, 14, -1, -1, -1, -1, -1,
- -1, -1, -1, 23, 24, 25, 26, 27, 28, 29,
- 30, 31, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1
-};
-
-QT_END_NAMESPACE
diff --git a/src/qml/parser/qqmljsgrammar_p.h b/src/qml/parser/qqmljsgrammar_p.h
deleted file mode 100644
index 9d028f2191..0000000000
--- a/src/qml/parser/qqmljsgrammar_p.h
+++ /dev/null
@@ -1,215 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part 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$
-**
-****************************************************************************/
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists for the convenience
-// of other Qt classes. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-// This file was generated by qlalr - DO NOT EDIT!
-#ifndef QQMLJSGRAMMAR_P_H
-#define QQMLJSGRAMMAR_P_H
-
-#include <QtCore/qglobal.h>
-
-QT_BEGIN_NAMESPACE
-
-class QQmlJSGrammar
-{
-public:
- enum VariousConstants {
- EOF_SYMBOL = 0,
- REDUCE_HERE = 107,
- SHIFT_THERE = 106,
- T_AND = 1,
- T_AND_AND = 2,
- T_AND_EQ = 3,
- T_AS = 95,
- T_AUTOMATIC_SEMICOLON = 62,
- T_BREAK = 4,
- T_CASE = 5,
- T_CATCH = 6,
- T_COLON = 7,
- T_COMMA = 8,
- T_COMMENT = 89,
- T_COMPATIBILITY_SEMICOLON = 90,
- T_CONST = 84,
- T_CONTINUE = 9,
- T_DEBUGGER = 86,
- T_DEFAULT = 10,
- T_DELETE = 11,
- T_DIVIDE_ = 12,
- T_DIVIDE_EQ = 13,
- T_DO = 14,
- T_DOT = 15,
- T_ELSE = 16,
- T_ENUM = 91,
- T_EQ = 17,
- T_EQ_EQ = 18,
- T_EQ_EQ_EQ = 19,
- T_ERROR = 99,
- T_FALSE = 83,
- T_FEED_JS_EXPRESSION = 103,
- T_FEED_JS_PROGRAM = 105,
- T_FEED_JS_SOURCE_ELEMENT = 104,
- T_FEED_JS_STATEMENT = 102,
- T_FEED_UI_OBJECT_MEMBER = 101,
- T_FEED_UI_PROGRAM = 100,
- T_FINALLY = 20,
- T_FOR = 21,
- T_FUNCTION = 22,
- T_GE = 23,
- T_GET = 97,
- T_GT = 24,
- T_GT_GT = 25,
- T_GT_GT_EQ = 26,
- T_GT_GT_GT = 27,
- T_GT_GT_GT_EQ = 28,
- T_IDENTIFIER = 29,
- T_IF = 30,
- T_IMPORT = 93,
- T_IN = 31,
- T_INSTANCEOF = 32,
- T_LBRACE = 33,
- T_LBRACKET = 34,
- T_LE = 35,
- T_LET = 85,
- T_LPAREN = 36,
- T_LT = 37,
- T_LT_LT = 38,
- T_LT_LT_EQ = 39,
- T_MINUS = 40,
- T_MINUS_EQ = 41,
- T_MINUS_MINUS = 42,
- T_MULTILINE_STRING_LITERAL = 88,
- T_NEW = 43,
- T_NOT = 44,
- T_NOT_EQ = 45,
- T_NOT_EQ_EQ = 46,
- T_NULL = 81,
- T_NUMERIC_LITERAL = 47,
- T_ON = 96,
- T_OR = 48,
- T_OR_EQ = 49,
- T_OR_OR = 50,
- T_PLUS = 51,
- T_PLUS_EQ = 52,
- T_PLUS_PLUS = 53,
- T_PRAGMA = 94,
- T_PROPERTY = 66,
- T_PUBLIC = 92,
- T_QUESTION = 54,
- T_RBRACE = 55,
- T_RBRACKET = 56,
- T_READONLY = 68,
- T_REMAINDER = 57,
- T_REMAINDER_EQ = 58,
- T_RESERVED_WORD = 87,
- T_RETURN = 59,
- T_RPAREN = 60,
- T_SEMICOLON = 61,
- T_SET = 98,
- T_SIGNAL = 67,
- T_STAR = 63,
- T_STAR_EQ = 64,
- T_STRING_LITERAL = 65,
- T_SWITCH = 69,
- T_THIS = 70,
- T_THROW = 71,
- T_TILDE = 72,
- T_TRUE = 82,
- T_TRY = 73,
- T_TYPEOF = 74,
- T_VAR = 75,
- T_VOID = 76,
- T_WHILE = 77,
- T_WITH = 78,
- T_XOR = 79,
- T_XOR_EQ = 80,
-
- ACCEPT_STATE = 691,
- RULE_COUNT = 369,
- STATE_COUNT = 692,
- TERMINAL_COUNT = 108,
- NON_TERMINAL_COUNT = 112,
-
- GOTO_INDEX_OFFSET = 692,
- GOTO_INFO_OFFSET = 3357,
- GOTO_CHECK_OFFSET = 3357
- };
-
- static const char *const spell[];
- static const short lhs[];
- static const short rhs[];
- static const short goto_default[];
- static const short action_default[];
- static const short action_index[];
- static const short action_info[];
- static const short action_check[];
-
- static inline int nt_action (int state, int nt)
- {
- const int yyn = action_index [GOTO_INDEX_OFFSET + state] + nt;
- if (yyn < 0 || action_check [GOTO_CHECK_OFFSET + yyn] != nt)
- return goto_default [nt];
-
- return action_info [GOTO_INFO_OFFSET + yyn];
- }
-
- static inline int t_action (int state, int token)
- {
- const int yyn = action_index [state] + token;
-
- if (yyn < 0 || action_check [yyn] != token)
- return - action_default [state];
-
- return action_info [yyn];
- }
-};
-
-
-QT_END_NAMESPACE
-#endif // QQMLJSGRAMMAR_P_H
-
diff --git a/src/qml/parser/qqmljskeywords_p.h b/src/qml/parser/qqmljskeywords_p.h
index 20daf545a9..b0a4951ece 100644
--- a/src/qml/parser/qqmljskeywords_p.h
+++ b/src/qml/parser/qqmljskeywords_p.h
@@ -57,10 +57,10 @@ QT_QML_BEGIN_NAMESPACE
namespace QQmlJS {
-static inline int classify2(const QChar *s, bool qmlMode) {
+static inline int classify2(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 'a') {
if (s[1].unicode() == 's') {
- return qmlMode ? Lexer::T_AS : Lexer::T_IDENTIFIER;
+ return Lexer::T_AS;
}
}
else if (s[0].unicode() == 'd') {
@@ -76,15 +76,18 @@ static inline int classify2(const QChar *s, bool qmlMode) {
return Lexer::T_IN;
}
}
- else if (qmlMode && s[0].unicode() == 'o') {
+ else if (s[0].unicode() == 'o') {
if (s[1].unicode() == 'n') {
- return qmlMode ? Lexer::T_ON : Lexer::T_IDENTIFIER;
+ return (parseModeFlags & Lexer::QmlMode) ? Lexer::T_ON : Lexer::T_IDENTIFIER;
+ }
+ else if (s[1].unicode() == 'f') {
+ return Lexer::T_OF;
}
}
return Lexer::T_IDENTIFIER;
}
-static inline int classify3(const QChar *s, bool qmlMode) {
+static inline int classify3(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 'f') {
if (s[1].unicode() == 'o') {
if (s[2].unicode() == 'r') {
@@ -102,7 +105,7 @@ static inline int classify3(const QChar *s, bool qmlMode) {
else if (s[0].unicode() == 'i') {
if (s[1].unicode() == 'n') {
if (s[2].unicode() == 't') {
- return qmlMode ? int(Lexer::T_INT) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_INT) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -144,12 +147,12 @@ static inline int classify3(const QChar *s, bool qmlMode) {
return Lexer::T_IDENTIFIER;
}
-static inline int classify4(const QChar *s, bool qmlMode) {
+static inline int classify4(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 'b') {
if (s[1].unicode() == 'y') {
if (s[2].unicode() == 't') {
if (s[3].unicode() == 'e') {
- return qmlMode ? int(Lexer::T_BYTE) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_BYTE) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -165,7 +168,7 @@ static inline int classify4(const QChar *s, bool qmlMode) {
else if (s[1].unicode() == 'h') {
if (s[2].unicode() == 'a') {
if (s[3].unicode() == 'r') {
- return qmlMode ? int(Lexer::T_CHAR) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_CHAR) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -181,7 +184,16 @@ static inline int classify4(const QChar *s, bool qmlMode) {
else if (s[1].unicode() == 'n') {
if (s[2].unicode() == 'u') {
if (s[3].unicode() == 'm') {
- return qmlMode ? int(Lexer::T_ENUM) : int(Lexer::T_RESERVED_WORD);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_ENUM) : int(Lexer::T_RESERVED_WORD);
+ }
+ }
+ }
+ }
+ else if (s[0].unicode() == 'f') {
+ if (s[1].unicode() == 'r') {
+ if (s[2].unicode() == 'o') {
+ if (s[3].unicode() == 'm') {
+ return int(Lexer::T_FROM);
}
}
}
@@ -190,7 +202,7 @@ static inline int classify4(const QChar *s, bool qmlMode) {
if (s[1].unicode() == 'o') {
if (s[2].unicode() == 't') {
if (s[3].unicode() == 'o') {
- return qmlMode ? int(Lexer::T_GOTO) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_GOTO) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -199,7 +211,7 @@ static inline int classify4(const QChar *s, bool qmlMode) {
if (s[1].unicode() == 'o') {
if (s[2].unicode() == 'n') {
if (s[3].unicode() == 'g') {
- return qmlMode ? int(Lexer::T_LONG) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_LONG) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -250,7 +262,7 @@ static inline int classify4(const QChar *s, bool qmlMode) {
return Lexer::T_IDENTIFIER;
}
-static inline int classify5(const QChar *s, bool qmlMode) {
+static inline int classify5(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 'b') {
if (s[1].unicode() == 'r') {
if (s[2].unicode() == 'e') {
@@ -305,7 +317,7 @@ static inline int classify5(const QChar *s, bool qmlMode) {
if (s[2].unicode() == 'n') {
if (s[3].unicode() == 'a') {
if (s[4].unicode() == 'l') {
- return qmlMode ? int(Lexer::T_FINAL) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_FINAL) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -314,7 +326,7 @@ static inline int classify5(const QChar *s, bool qmlMode) {
if (s[2].unicode() == 'o') {
if (s[3].unicode() == 'a') {
if (s[4].unicode() == 't') {
- return qmlMode ? int(Lexer::T_FLOAT) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_FLOAT) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -325,7 +337,7 @@ static inline int classify5(const QChar *s, bool qmlMode) {
if (s[2].unicode() == 'o') {
if (s[3].unicode() == 'r') {
if (s[4].unicode() == 't') {
- return qmlMode ? int(Lexer::T_SHORT) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_SHORT) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -334,7 +346,7 @@ static inline int classify5(const QChar *s, bool qmlMode) {
if (s[2].unicode() == 'p') {
if (s[3].unicode() == 'e') {
if (s[4].unicode() == 'r') {
- return qmlMode ? int(Lexer::T_SUPER) : int(Lexer::T_RESERVED_WORD);
+ return int(Lexer::T_SUPER);
}
}
}
@@ -362,10 +374,21 @@ static inline int classify5(const QChar *s, bool qmlMode) {
}
}
}
+ else if (s[0].unicode() == 'y') {
+ if (s[1].unicode() == 'i') {
+ if (s[2].unicode() == 'e') {
+ if (s[3].unicode() == 'l') {
+ if (s[4].unicode() == 'd') {
+ return (parseModeFlags & Lexer::YieldIsKeyword) ? Lexer::T_YIELD : Lexer::T_IDENTIFIER;
+ }
+ }
+ }
+ }
+ }
return Lexer::T_IDENTIFIER;
}
-static inline int classify6(const QChar *s, bool qmlMode) {
+static inline int classify6(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 'd') {
if (s[1].unicode() == 'e') {
if (s[2].unicode() == 'l') {
@@ -383,7 +406,7 @@ static inline int classify6(const QChar *s, bool qmlMode) {
if (s[3].unicode() == 'b') {
if (s[4].unicode() == 'l') {
if (s[5].unicode() == 'e') {
- return qmlMode ? int(Lexer::T_DOUBLE) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_DOUBLE) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -409,7 +432,7 @@ static inline int classify6(const QChar *s, bool qmlMode) {
if (s[3].unicode() == 'o') {
if (s[4].unicode() == 'r') {
if (s[5].unicode() == 't') {
- return qmlMode ? int(Lexer::T_IMPORT) : int(Lexer::T_RESERVED_WORD);
+ return Lexer::T_IMPORT;
}
}
}
@@ -422,7 +445,7 @@ static inline int classify6(const QChar *s, bool qmlMode) {
if (s[3].unicode() == 'i') {
if (s[4].unicode() == 'v') {
if (s[5].unicode() == 'e') {
- return qmlMode ? int(Lexer::T_NATIVE) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_NATIVE) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -435,7 +458,7 @@ static inline int classify6(const QChar *s, bool qmlMode) {
if (s[3].unicode() == 'l') {
if (s[4].unicode() == 'i') {
if (s[5].unicode() == 'c') {
- return qmlMode ? Lexer::T_PUBLIC : Lexer::T_IDENTIFIER;
+ return (parseModeFlags & Lexer::QmlMode) ? Lexer::T_PUBLIC : Lexer::T_IDENTIFIER;
}
}
}
@@ -446,7 +469,7 @@ static inline int classify6(const QChar *s, bool qmlMode) {
if (s[3].unicode() == 'g') {
if (s[4].unicode() == 'm') {
if (s[5].unicode() == 'a') {
- return qmlMode ? Lexer::T_PRAGMA : Lexer::T_IDENTIFIER;
+ return (parseModeFlags & Lexer::QmlMode) ? Lexer::T_PRAGMA : Lexer::T_IDENTIFIER;
}
}
}
@@ -467,7 +490,7 @@ static inline int classify6(const QChar *s, bool qmlMode) {
}
}
else if (s[0].unicode() == 's') {
- if (qmlMode && s[1].unicode() == 'i') {
+ if ((parseModeFlags & Lexer::QmlMode) && s[1].unicode() == 'i') {
if (s[2].unicode() == 'g') {
if (s[3].unicode() == 'n') {
if (s[4].unicode() == 'a') {
@@ -483,7 +506,7 @@ static inline int classify6(const QChar *s, bool qmlMode) {
if (s[3].unicode() == 't') {
if (s[4].unicode() == 'i') {
if (s[5].unicode() == 'c') {
- return qmlMode ? int(Lexer::T_STATIC) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::StaticIsKeyword) ? int(Lexer::T_STATIC) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -507,7 +530,7 @@ static inline int classify6(const QChar *s, bool qmlMode) {
if (s[3].unicode() == 'o') {
if (s[4].unicode() == 'w') {
if (s[5].unicode() == 's') {
- return qmlMode ? int(Lexer::T_THROWS) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_THROWS) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -528,7 +551,7 @@ static inline int classify6(const QChar *s, bool qmlMode) {
return Lexer::T_IDENTIFIER;
}
-static inline int classify7(const QChar *s, bool qmlMode) {
+static inline int classify7(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 'b') {
if (s[1].unicode() == 'o') {
if (s[2].unicode() == 'o') {
@@ -536,7 +559,7 @@ static inline int classify7(const QChar *s, bool qmlMode) {
if (s[4].unicode() == 'e') {
if (s[5].unicode() == 'a') {
if (s[6].unicode() == 'n') {
- return qmlMode ? int(Lexer::T_BOOLEAN) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_BOOLEAN) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -596,7 +619,7 @@ static inline int classify7(const QChar *s, bool qmlMode) {
if (s[4].unicode() == 'a') {
if (s[5].unicode() == 'g') {
if (s[6].unicode() == 'e') {
- return qmlMode ? int(Lexer::T_PACKAGE) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_PACKAGE) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -609,7 +632,7 @@ static inline int classify7(const QChar *s, bool qmlMode) {
if (s[4].unicode() == 'a') {
if (s[5].unicode() == 't') {
if (s[6].unicode() == 'e') {
- return qmlMode ? int(Lexer::T_PRIVATE) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_PRIVATE) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -620,7 +643,7 @@ static inline int classify7(const QChar *s, bool qmlMode) {
return Lexer::T_IDENTIFIER;
}
-static inline int classify8(const QChar *s, bool qmlMode) {
+static inline int classify8(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 'a') {
if (s[1].unicode() == 'b') {
if (s[2].unicode() == 's') {
@@ -629,7 +652,7 @@ static inline int classify8(const QChar *s, bool qmlMode) {
if (s[5].unicode() == 'a') {
if (s[6].unicode() == 'c') {
if (s[7].unicode() == 't') {
- return qmlMode ? int(Lexer::T_ABSTRACT) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_ABSTRACT) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -689,7 +712,7 @@ static inline int classify8(const QChar *s, bool qmlMode) {
}
}
}
- else if (qmlMode && s[0].unicode() == 'p') {
+ else if ((parseModeFlags & Lexer::QmlMode) && s[0].unicode() == 'p') {
if (s[1].unicode() == 'r') {
if (s[2].unicode() == 'o') {
if (s[3].unicode() == 'p') {
@@ -706,7 +729,7 @@ static inline int classify8(const QChar *s, bool qmlMode) {
}
}
}
- else if (qmlMode && s[0].unicode() == 'r') {
+ else if ((parseModeFlags & Lexer::QmlMode) && s[0].unicode() == 'r') {
if (s[1].unicode() == 'e') {
if (s[2].unicode() == 'a') {
if (s[3].unicode() == 'd') {
@@ -731,7 +754,7 @@ static inline int classify8(const QChar *s, bool qmlMode) {
if (s[5].unicode() == 'i') {
if (s[6].unicode() == 'l') {
if (s[7].unicode() == 'e') {
- return qmlMode ? int(Lexer::T_VOLATILE) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_VOLATILE) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -743,7 +766,7 @@ static inline int classify8(const QChar *s, bool qmlMode) {
return Lexer::T_IDENTIFIER;
}
-static inline int classify9(const QChar *s, bool qmlMode) {
+static inline int classify9(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 'i') {
if (s[1].unicode() == 'n') {
if (s[2].unicode() == 't') {
@@ -753,7 +776,7 @@ static inline int classify9(const QChar *s, bool qmlMode) {
if (s[6].unicode() == 'a') {
if (s[7].unicode() == 'c') {
if (s[8].unicode() == 'e') {
- return qmlMode ? int(Lexer::T_INTERFACE) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_INTERFACE) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -772,7 +795,7 @@ static inline int classify9(const QChar *s, bool qmlMode) {
if (s[6].unicode() == 't') {
if (s[7].unicode() == 'e') {
if (s[8].unicode() == 'd') {
- return qmlMode ? int(Lexer::T_PROTECTED) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_PROTECTED) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -791,7 +814,7 @@ static inline int classify9(const QChar *s, bool qmlMode) {
if (s[6].unicode() == 'e') {
if (s[7].unicode() == 'n') {
if (s[8].unicode() == 't') {
- return qmlMode ? int(Lexer::T_TRANSIENT) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_TRANSIENT) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -804,7 +827,7 @@ static inline int classify9(const QChar *s, bool qmlMode) {
return Lexer::T_IDENTIFIER;
}
-static inline int classify10(const QChar *s, bool qmlMode) {
+static inline int classify10(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 'i') {
if (s[1].unicode() == 'm') {
if (s[2].unicode() == 'p') {
@@ -815,7 +838,7 @@ static inline int classify10(const QChar *s, bool qmlMode) {
if (s[7].unicode() == 'n') {
if (s[8].unicode() == 't') {
if (s[9].unicode() == 's') {
- return qmlMode ? int(Lexer::T_IMPLEMENTS) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_IMPLEMENTS) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -848,7 +871,7 @@ static inline int classify10(const QChar *s, bool qmlMode) {
return Lexer::T_IDENTIFIER;
}
-static inline int classify12(const QChar *s, bool qmlMode) {
+static inline int classify12(const QChar *s, int parseModeFlags) {
if (s[0].unicode() == 's') {
if (s[1].unicode() == 'y') {
if (s[2].unicode() == 'n') {
@@ -861,7 +884,7 @@ static inline int classify12(const QChar *s, bool qmlMode) {
if (s[9].unicode() == 'z') {
if (s[10].unicode() == 'e') {
if (s[11].unicode() == 'd') {
- return qmlMode ? int(Lexer::T_SYNCHRONIZED) : int(Lexer::T_IDENTIFIER);
+ return (parseModeFlags & Lexer::QmlMode) ? int(Lexer::T_SYNCHRONIZED) : int(Lexer::T_IDENTIFIER);
}
}
}
@@ -877,18 +900,18 @@ static inline int classify12(const QChar *s, bool qmlMode) {
return Lexer::T_IDENTIFIER;
}
-int Lexer::classify(const QChar *s, int n, bool qmlMode) {
+int Lexer::classify(const QChar *s, int n, int parseModeFlags) {
switch (n) {
- case 2: return classify2(s, qmlMode);
- case 3: return classify3(s, qmlMode);
- case 4: return classify4(s, qmlMode);
- case 5: return classify5(s, qmlMode);
- case 6: return classify6(s, qmlMode);
- case 7: return classify7(s, qmlMode);
- case 8: return classify8(s, qmlMode);
- case 9: return classify9(s, qmlMode);
- case 10: return classify10(s, qmlMode);
- case 12: return classify12(s, qmlMode);
+ case 2: return classify2(s, parseModeFlags);
+ case 3: return classify3(s, parseModeFlags);
+ case 4: return classify4(s, parseModeFlags);
+ case 5: return classify5(s, parseModeFlags);
+ case 6: return classify6(s, parseModeFlags);
+ case 7: return classify7(s, parseModeFlags);
+ case 8: return classify8(s, parseModeFlags);
+ case 9: return classify9(s, parseModeFlags);
+ case 10: return classify10(s, parseModeFlags);
+ case 12: return classify12(s, parseModeFlags);
default: return Lexer::T_IDENTIFIER;
} // switch
}
diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp
index aab9025a08..b98bb8bac5 100644
--- a/src/qml/parser/qqmljslexer.cpp
+++ b/src/qml/parser/qqmljslexer.cpp
@@ -58,6 +58,8 @@ static inline int regExpFlagFromChar(const QChar &ch)
case 'g': return Lexer::RegExp_Global;
case 'i': return Lexer::RegExp_IgnoreCase;
case 'm': return Lexer::RegExp_Multiline;
+ case 'u': return Lexer::RegExp_Unicode;
+ case 'y': return Lexer::RegExp_Sticky;
}
return 0;
}
@@ -77,22 +79,15 @@ static inline QChar convertHex(QChar c1, QChar c2)
return QChar((convertHex(c1.unicode()) << 4) + convertHex(c2.unicode()));
}
-static inline QChar convertUnicode(QChar c1, QChar c2, QChar c3, QChar c4)
-{
- return QChar((convertHex(c3.unicode()) << 4) + convertHex(c4.unicode()),
- (convertHex(c1.unicode()) << 4) + convertHex(c2.unicode()));
-}
-
Lexer::Lexer(Engine *engine)
: _engine(engine)
, _codePtr(nullptr)
, _endPtr(nullptr)
- , _lastLinePtr(nullptr)
- , _tokenLinePtr(nullptr)
, _tokenStartPtr(nullptr)
, _char(QLatin1Char('\n'))
, _errorCode(NoError)
, _currentLineNumber(0)
+ , _currentColumnNumber(0)
, _tokenValue(0)
, _parenthesesState(IgnoreParentheses)
, _parenthesesCount(0)
@@ -101,6 +96,7 @@ Lexer::Lexer(Engine *engine)
, _tokenKind(0)
, _tokenLength(0)
, _tokenLine(0)
+ , _tokenColumn(0)
, _validTokenText(false)
, _prohibitAutomaticSemicolon(false)
, _restrictedKeyword(false)
@@ -137,14 +133,13 @@ void Lexer::setCode(const QString &code, int lineno, bool qmlMode)
_codePtr = code.unicode();
_endPtr = _codePtr + code.length();
- _lastLinePtr = _codePtr;
- _tokenLinePtr = _codePtr;
_tokenStartPtr = _codePtr;
_char = QLatin1Char('\n');
_errorCode = NoError;
_currentLineNumber = lineno;
+ _currentColumnNumber = 0;
_tokenValue = 0;
// parentheses state
@@ -156,6 +151,7 @@ void Lexer::setCode(const QString &code, int lineno, bool qmlMode)
_patternFlags = 0;
_tokenLength = 0;
_tokenLine = lineno;
+ _tokenColumn = 0;
_validTokenText = false;
_prohibitAutomaticSemicolon = false;
@@ -172,9 +168,10 @@ void Lexer::scanChar()
if (sequenceLength == 2)
_char = *_codePtr++;
- if (unsigned sequenceLength = isLineTerminatorSequence()) {
- _lastLinePtr = _codePtr + sequenceLength - 1; // points to the first character after the newline
+ ++_currentColumnNumber;
+ if (isLineTerminator()) {
++_currentLineNumber;
+ _currentColumnNumber = 0;
}
}
@@ -222,12 +219,32 @@ inline bool isBinop(int tok)
return false;
}
}
+
+int hexDigit(QChar c)
+{
+ if (c >= QLatin1Char('0') && c <= QLatin1Char('9'))
+ return c.unicode() - '0';
+ if (c >= QLatin1Char('a') && c <= QLatin1Char('f'))
+ return c.unicode() - 'a' + 10;
+ if (c >= QLatin1Char('A') && c <= QLatin1Char('F'))
+ return c.unicode() - 'A' + 10;
+ return -1;
+}
+
+int octalDigit(QChar c)
+{
+ if (c >= QLatin1Char('0') && c <= QLatin1Char('7'))
+ return c.unicode() - '0';
+ return -1;
+}
+
} // anonymous namespace
int Lexer::lex()
{
const int previousTokenKind = _tokenKind;
+ again:
_tokenSpell = QStringRef();
_tokenKind = scanToken();
_tokenLength = _codePtr - _tokenStartPtr - 1;
@@ -239,6 +256,9 @@ int Lexer::lex()
// update the flags
switch (_tokenKind) {
case T_LBRACE:
+ if (_bracesCount > 0)
+ ++_bracesCount;
+ Q_FALLTHROUGH();
case T_SEMICOLON:
case T_QUESTION:
case T_COLON:
@@ -266,9 +286,15 @@ int Lexer::lex()
case T_CONTINUE:
case T_BREAK:
case T_RETURN:
+ case T_YIELD:
case T_THROW:
_restrictedKeyword = true;
break;
+ case T_RBRACE:
+ if (_bracesCount > 0)
+ --_bracesCount;
+ if (_bracesCount == 0)
+ goto again;
} // switch
// update the parentheses state
@@ -295,39 +321,57 @@ int Lexer::lex()
return _tokenKind;
}
-bool Lexer::isUnicodeEscapeSequence(const QChar *chars)
-{
- if (isHexDigit(chars[0]) && isHexDigit(chars[1]) && isHexDigit(chars[2]) && isHexDigit(chars[3]))
- return true;
-
- return false;
-}
-
-QChar Lexer::decodeUnicodeEscapeCharacter(bool *ok)
+uint Lexer::decodeUnicodeEscapeCharacter(bool *ok)
{
- if (_char == QLatin1Char('u') && isUnicodeEscapeSequence(&_codePtr[0])) {
- scanChar(); // skip u
+ Q_ASSERT(_char == QLatin1Char('u'));
+ scanChar(); // skip u
+ if (_codePtr + 4 <= _endPtr && isHexDigit(_char)) {
+ uint codePoint = 0;
+ for (int i = 0; i < 4; ++i) {
+ int digit = hexDigit(_char);
+ if (digit < 0)
+ goto error;
+ codePoint *= 16;
+ codePoint += digit;
+ scanChar();
+ }
- const QChar c1 = _char;
- scanChar();
+ *ok = true;
+ return codePoint;
+ } else if (_codePtr < _endPtr && _char == QLatin1Char('{')) {
+ scanChar(); // skip '{'
+ uint codePoint = 0;
+ if (!isHexDigit(_char))
+ // need at least one hex digit
+ goto error;
- const QChar c2 = _char;
- scanChar();
+ while (_codePtr <= _endPtr) {
+ int digit = hexDigit(_char);
+ if (digit < 0)
+ break;
+ codePoint *= 16;
+ codePoint += digit;
+ if (codePoint > 0x10ffff)
+ goto error;
+ scanChar();
+ }
- const QChar c3 = _char;
- scanChar();
+ if (_char != QLatin1Char('}'))
+ goto error;
- const QChar c4 = _char;
- scanChar();
+ scanChar(); // skip '}'
- if (ok)
- *ok = true;
- return convertUnicode(c1, c2, c3, c4);
+ *ok = true;
+ return codePoint;
}
+ error:
+ _errorCode = IllegalUnicodeEscapeSequence;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence");
+
*ok = false;
- return QChar();
+ return 0;
}
QChar Lexer::decodeHexEscapeCharacter(bool *ok)
@@ -351,15 +395,15 @@ QChar Lexer::decodeHexEscapeCharacter(bool *ok)
return QChar();
}
-static inline bool isIdentifierStart(QChar ch)
+static inline bool isIdentifierStart(uint ch)
{
// fast path for ascii
- if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') ||
- (ch.unicode() >= 'A' && ch.unicode() <= 'Z') ||
+ if ((ch >= 'a' && ch <= 'z') ||
+ (ch >= 'A' && ch <= 'Z') ||
ch == '$' || ch == '_')
return true;
- switch (ch.category()) {
+ switch (QChar::category(ch)) {
case QChar::Number_Letter:
case QChar::Letter_Uppercase:
case QChar::Letter_Lowercase:
@@ -373,17 +417,17 @@ static inline bool isIdentifierStart(QChar ch)
return false;
}
-static bool isIdentifierPart(QChar ch)
+static bool isIdentifierPart(uint ch)
{
// fast path for ascii
- if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') ||
- (ch.unicode() >= 'A' && ch.unicode() <= 'Z') ||
- (ch.unicode() >= '0' && ch.unicode() <= '9') ||
+ if ((ch >= 'a' && ch <= 'z') ||
+ (ch >= 'A' && ch <= 'Z') ||
+ (ch >= '0' && ch <= '9') ||
ch == '$' || ch == '_' ||
- ch.unicode() == 0x200c /* ZWNJ */ || ch.unicode() == 0x200d /* ZWJ */)
+ ch == 0x200c /* ZWNJ */ || ch == 0x200d /* ZWJ */)
return true;
- switch (ch.category()) {
+ switch (QChar::category(ch)) {
case QChar::Mark_NonSpacing:
case QChar::Mark_SpacingCombining:
@@ -412,19 +456,23 @@ int Lexer::scanToken()
return tk;
}
+ if (_bracesCount == 0) {
+ // we're inside a Template string
+ return scanString(TemplateContinuation);
+ }
+
+
_terminator = false;
again:
_validTokenText = false;
- _tokenLinePtr = _lastLinePtr;
while (_char.isSpace()) {
- if (unsigned sequenceLength = isLineTerminatorSequence()) {
- _tokenLinePtr = _codePtr + sequenceLength - 1;
-
+ if (isLineTerminator()) {
if (_restrictedKeyword) {
// automatic semicolon insertion
_tokenLine = _currentLineNumber;
+ _tokenColumn = _currentColumnNumber;
_tokenStartPtr = _codePtr - 1;
return T_SEMICOLON;
} else {
@@ -438,6 +486,7 @@ again:
_tokenStartPtr = _codePtr - 1;
_tokenLine = _currentLineNumber;
+ _tokenColumn = _currentColumnNumber;
if (_codePtr > _endPtr)
return EOF_SYMBOL;
@@ -501,6 +550,9 @@ again:
return T_EQ_EQ_EQ;
}
return T_EQ_EQ;
+ } else if (_char == QLatin1Char('>')) {
+ scanChar();
+ return T_ARROW;
}
return T_EQ;
@@ -557,50 +609,18 @@ again:
return T_DIVIDE_;
case '.':
- if (_char.isDigit()) {
- QVarLengthArray<char,32> chars;
-
- chars.append(ch.unicode()); // append the `.'
-
- while (_char.isDigit()) {
- chars.append(_char.unicode());
+ if (isDecimalDigit(_char.unicode()))
+ return scanNumber(ch);
+ if (_char == QLatin1Char('.')) {
+ scanChar();
+ if (_char == QLatin1Char('.')) {
scanChar();
- }
-
- if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) {
- if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) &&
- _codePtr[1].isDigit())) {
-
- chars.append(_char.unicode());
- scanChar(); // consume `e'
-
- if (_char == QLatin1Char('+') || _char == QLatin1Char('-')) {
- chars.append(_char.unicode());
- scanChar(); // consume the sign
- }
-
- while (_char.isDigit()) {
- chars.append(_char.unicode());
- scanChar();
- }
- }
- }
-
- chars.append('\0');
-
- const char *begin = chars.constData();
- const char *end = nullptr;
- bool ok = false;
-
- _tokenValue = qstrtod(begin, &end, &ok);
-
- if (end - begin != chars.size() - 1) {
- _errorCode = IllegalExponentIndicator;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal syntax for exponential number");
+ return T_ELLIPSIS;
+ } else {
+ _errorCode = IllegalCharacter;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "Unexpected token '.'");
return T_ERROR;
}
-
- return T_NUMERIC_LITERAL;
}
return T_DOT;
@@ -611,7 +631,7 @@ again:
} else if (_char == QLatin1Char('-')) {
scanChar();
- if (_terminator && !_delimited && !_prohibitAutomaticSemicolon) {
+ if (_terminator && !_delimited && !_prohibitAutomaticSemicolon && _tokenKind != T_LPAREN) {
_stackToken = T_MINUS_MINUS;
return T_SEMICOLON;
}
@@ -629,7 +649,7 @@ again:
} else if (_char == QLatin1Char('+')) {
scanChar();
- if (_terminator && !_delimited && !_prohibitAutomaticSemicolon) {
+ if (_terminator && !_delimited && !_prohibitAutomaticSemicolon && _tokenKind != T_LPAREN) {
_stackToken = T_PLUS_PLUS;
return T_SEMICOLON;
}
@@ -642,6 +662,13 @@ again:
if (_char == QLatin1Char('=')) {
scanChar();
return T_STAR_EQ;
+ } else if (_char == QLatin1Char('*')) {
+ scanChar();
+ if (_char == QLatin1Char('=')) {
+ scanChar();
+ return T_STAR_STAR_EQ;
+ }
+ return T_STAR_STAR;
}
return T_STAR;
@@ -676,141 +703,12 @@ again:
}
return T_NOT;
+ case '`':
+ _outerTemplateBraceCount.push(_bracesCount);
+ Q_FALLTHROUGH();
case '\'':
- case '"': {
- const QChar quote = ch;
- bool multilineStringLiteral = false;
-
- const QChar *startCode = _codePtr;
-
- if (_engine) {
- while (_codePtr <= _endPtr) {
- if (isLineTerminator()) {
- if (qmlMode())
- break;
- _errorCode = IllegalCharacter;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Stray newline in string literal");
- return T_ERROR;
- } else if (_char == QLatin1Char('\\')) {
- break;
- } else if (_char == quote) {
- _tokenSpell = _engine->midRef(startCode - _code.unicode() - 1, _codePtr - startCode);
- scanChar();
-
- return T_STRING_LITERAL;
- }
- scanChar();
- }
- }
-
- _validTokenText = true;
- _tokenText.resize(0);
- startCode--;
- while (startCode != _codePtr - 1)
- _tokenText += *startCode++;
-
- while (_codePtr <= _endPtr) {
- if (unsigned sequenceLength = isLineTerminatorSequence()) {
- multilineStringLiteral = true;
- _tokenText += _char;
- if (sequenceLength == 2)
- _tokenText += *_codePtr;
- scanChar();
- } else if (_char == quote) {
- scanChar();
-
- if (_engine)
- _tokenSpell = _engine->newStringRef(_tokenText);
-
- return multilineStringLiteral ? T_MULTILINE_STRING_LITERAL : T_STRING_LITERAL;
- } else if (_char == QLatin1Char('\\')) {
- scanChar();
- if (_codePtr > _endPtr) {
- _errorCode = IllegalEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "End of file reached at escape sequence");
- return T_ERROR;
- }
-
- QChar u;
-
- switch (_char.unicode()) {
- // unicode escape sequence
- case 'u': {
- bool ok = false;
- u = decodeUnicodeEscapeCharacter(&ok);
- if (! ok) {
- _errorCode = IllegalUnicodeEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence");
- return T_ERROR;
- }
- } break;
-
- // hex escape sequence
- case 'x': {
- bool ok = false;
- u = decodeHexEscapeCharacter(&ok);
- if (!ok) {
- _errorCode = IllegalHexadecimalEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal hexadecimal escape sequence");
- return T_ERROR;
- }
- } break;
-
- // single character escape sequence
- case '\\': u = QLatin1Char('\\'); scanChar(); break;
- case '\'': u = QLatin1Char('\''); scanChar(); break;
- case '\"': u = QLatin1Char('\"'); scanChar(); break;
- case 'b': u = QLatin1Char('\b'); scanChar(); break;
- case 'f': u = QLatin1Char('\f'); scanChar(); break;
- case 'n': u = QLatin1Char('\n'); scanChar(); break;
- case 'r': u = QLatin1Char('\r'); scanChar(); break;
- case 't': u = QLatin1Char('\t'); scanChar(); break;
- case 'v': u = QLatin1Char('\v'); scanChar(); break;
-
- case '0':
- if (! _codePtr->isDigit()) {
- scanChar();
- u = QLatin1Char('\0');
- break;
- }
- Q_FALLTHROUGH();
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- _errorCode = IllegalEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Octal escape sequences are not allowed");
- return T_ERROR;
-
- case '\r':
- case '\n':
- case 0x2028u:
- case 0x2029u:
- scanChar();
- continue;
-
- default:
- // non escape character
- u = _char;
- scanChar();
- }
-
- _tokenText += u;
- } else {
- _tokenText += _char;
- scanChar();
- }
- }
-
- _errorCode = UnclosedStringLiteral;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Unclosed string at end of line");
- return T_ERROR;
- }
+ case '"':
+ return scanString(ScanStringMode(ch.unicode()));
case '0':
case '1':
case '2':
@@ -824,28 +722,36 @@ again:
return scanNumber(ch);
default: {
- QChar c = ch;
+ uint c = ch.unicode();
bool identifierWithEscapeChars = false;
- if (c == QLatin1Char('\\') && _char == QLatin1Char('u')) {
+ if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_char.unicode())) {
+ c = QChar::surrogateToUcs4(ushort(c), _char.unicode());
+ scanChar();
+ } else if (c == '\\' && _char == QLatin1Char('u')) {
identifierWithEscapeChars = true;
bool ok = false;
c = decodeUnicodeEscapeCharacter(&ok);
- if (! ok) {
- _errorCode = IllegalUnicodeEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence");
+ if (!ok)
return T_ERROR;
- }
}
if (isIdentifierStart(c)) {
if (identifierWithEscapeChars) {
_tokenText.resize(0);
- _tokenText += c;
+ if (QChar::requiresSurrogates(c)) {
+ _tokenText += QChar(QChar::highSurrogate(c));
+ _tokenText += QChar(QChar::lowSurrogate(c));
+ } else {
+ _tokenText += QChar(c);
+ }
_validTokenText = true;
}
- while (true) {
- c = _char;
- if (_char == QLatin1Char('\\') && _codePtr[0] == QLatin1Char('u')) {
- if (! identifierWithEscapeChars) {
+ while (_codePtr <= _endPtr) {
+ c = _char.unicode();
+ if (QChar::isHighSurrogate(c) && QChar::isLowSurrogate(_codePtr->unicode())) {
+ scanChar();
+ c = QChar::surrogateToUcs4(ushort(c), _char.unicode());
+ } else if (_char == QLatin1Char('\\') && _codePtr[0] == QLatin1Char('u')) {
+ if (!identifierWithEscapeChars) {
identifierWithEscapeChars = true;
_tokenText.resize(0);
_tokenText.insert(0, _tokenStartPtr, _codePtr - _tokenStartPtr - 1);
@@ -855,38 +761,52 @@ again:
scanChar(); // skip '\\'
bool ok = false;
c = decodeUnicodeEscapeCharacter(&ok);
- if (! ok) {
- _errorCode = IllegalUnicodeEscapeSequence;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence");
+ if (!ok)
return T_ERROR;
- }
- if (isIdentifierPart(c))
- _tokenText += c;
- continue;
- } else if (isIdentifierPart(c)) {
- if (identifierWithEscapeChars)
- _tokenText += c;
- scanChar();
+ if (!isIdentifierPart(c))
+ break;
+
+ if (identifierWithEscapeChars) {
+ if (QChar::requiresSurrogates(c)) {
+ _tokenText += QChar(QChar::highSurrogate(c));
+ _tokenText += QChar(QChar::lowSurrogate(c));
+ } else {
+ _tokenText += QChar(c);
+ }
+ }
continue;
}
- _tokenLength = _codePtr - _tokenStartPtr - 1;
+ if (!isIdentifierPart(c))
+ break;
- int kind = T_IDENTIFIER;
+ if (identifierWithEscapeChars) {
+ if (QChar::requiresSurrogates(c)) {
+ _tokenText += QChar(QChar::highSurrogate(c));
+ _tokenText += QChar(QChar::lowSurrogate(c));
+ } else {
+ _tokenText += QChar(c);
+ }
+ }
+ scanChar();
+ }
+
+ _tokenLength = _codePtr - _tokenStartPtr - 1;
- if (! identifierWithEscapeChars)
- kind = classify(_tokenStartPtr, _tokenLength, _qmlMode);
+ int kind = T_IDENTIFIER;
- if (_engine) {
- if (kind == T_IDENTIFIER && identifierWithEscapeChars)
- _tokenSpell = _engine->newStringRef(_tokenText);
- else
- _tokenSpell = _engine->midRef(_tokenStartPtr - _code.unicode(), _tokenLength);
- }
+ if (!identifierWithEscapeChars)
+ kind = classify(_tokenStartPtr, _tokenLength, parseModeFlags());
- return kind;
+ if (_engine) {
+ if (kind == T_IDENTIFIER && identifierWithEscapeChars)
+ _tokenSpell = _engine->newStringRef(_tokenText);
+ else
+ _tokenSpell = _engine->midRef(_tokenStartPtr - _code.unicode(), _tokenLength);
}
+
+ return kind;
}
}
@@ -896,93 +816,278 @@ again:
return T_ERROR;
}
-int Lexer::scanNumber(QChar ch)
+int Lexer::scanString(ScanStringMode mode)
{
- if (ch != QLatin1Char('0')) {
- QVarLengthArray<char, 64> buf;
- buf += ch.toLatin1();
-
- QChar n = _char;
- const QChar *code = _codePtr;
- while (n.isDigit()) {
- buf += n.toLatin1();
- n = *code++;
- }
+ QChar quote = (mode == TemplateContinuation) ? QChar(TemplateHead) : QChar(mode);
+ bool multilineStringLiteral = false;
- if (n != QLatin1Char('.') && n != QLatin1Char('e') && n != QLatin1Char('E')) {
- if (code != _codePtr) {
- _codePtr = code - 1;
+ const QChar *startCode = _codePtr;
+
+ if (_engine) {
+ while (_codePtr <= _endPtr) {
+ if (isLineTerminator() && quote != QLatin1Char('`')) {
+ if (qmlMode())
+ break;
+ _errorCode = IllegalCharacter;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "Stray newline in string literal");
+ return T_ERROR;
+ } else if (_char == QLatin1Char('\\')) {
+ break;
+ } else if (_char == '$' && quote == QLatin1Char('`')) {
+ break;
+ } else if (_char == quote) {
+ _tokenSpell = _engine->midRef(startCode - _code.unicode() - 1, _codePtr - startCode);
scanChar();
+
+ if (quote == QLatin1Char('`'))
+ _bracesCount = _outerTemplateBraceCount.pop();
+
+ if (mode == TemplateHead)
+ return T_NO_SUBSTITUTION_TEMPLATE;
+ else if (mode == TemplateContinuation)
+ return T_TEMPLATE_TAIL;
+ else
+ return T_STRING_LITERAL;
}
- buf.append('\0');
- _tokenValue = strtod(buf.constData(), nullptr);
- return T_NUMERIC_LITERAL;
+ scanChar();
}
- } else if (_char.isDigit() && !qmlMode()) {
- _errorCode = IllegalCharacter;
- _errorMessage = QCoreApplication::translate("QQmlParser", "Decimal numbers can't start with '0'");
- return T_ERROR;
}
- QVarLengthArray<char,32> chars;
- chars.append(ch.unicode());
+ _validTokenText = true;
+ _tokenText.resize(0);
+ startCode--;
+ while (startCode != _codePtr - 1)
+ _tokenText += *startCode++;
+
+ while (_codePtr <= _endPtr) {
+ if (unsigned sequenceLength = isLineTerminatorSequence()) {
+ multilineStringLiteral = true;
+ _tokenText += _char;
+ if (sequenceLength == 2)
+ _tokenText += *_codePtr;
+ scanChar();
+ } else if (_char == mode) {
+ scanChar();
- if (ch == QLatin1Char('0') && (_char == QLatin1Char('x') || _char == QLatin1Char('X'))) {
- ch = _char; // remember the x or X to use it in the error message below.
+ if (_engine)
+ _tokenSpell = _engine->newStringRef(_tokenText);
- // parse hex integer literal
- chars.append(_char.unicode());
- scanChar(); // consume `x'
+ if (quote == QLatin1Char('`'))
+ _bracesCount = _outerTemplateBraceCount.pop();
- while (isHexDigit(_char)) {
- chars.append(_char.unicode());
+ if (mode == TemplateContinuation)
+ return T_TEMPLATE_TAIL;
+ else if (mode == TemplateHead)
+ return T_NO_SUBSTITUTION_TEMPLATE;
+
+ return multilineStringLiteral ? T_MULTILINE_STRING_LITERAL : T_STRING_LITERAL;
+ } else if (quote == QLatin1Char('`') && _char == QLatin1Char('$') && *_codePtr == '{') {
+ scanChar();
+ scanChar();
+ _bracesCount = 1;
+ if (_engine)
+ _tokenSpell = _engine->newStringRef(_tokenText);
+
+ return (mode == TemplateHead ? T_TEMPLATE_HEAD : T_TEMPLATE_MIDDLE);
+ } else if (_char == QLatin1Char('\\')) {
+ scanChar();
+ if (_codePtr > _endPtr) {
+ _errorCode = IllegalEscapeSequence;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "End of file reached at escape sequence");
+ return T_ERROR;
+ }
+
+ QChar u;
+
+ switch (_char.unicode()) {
+ // unicode escape sequence
+ case 'u': {
+ bool ok = false;
+ uint codePoint = decodeUnicodeEscapeCharacter(&ok);
+ if (!ok)
+ return T_ERROR;
+ if (QChar::requiresSurrogates(codePoint)) {
+ // need to use a surrogate pair
+ _tokenText += QChar(QChar::highSurrogate(codePoint));
+ u = QChar::lowSurrogate(codePoint);
+ } else {
+ u = codePoint;
+ }
+ } break;
+
+ // hex escape sequence
+ case 'x': {
+ bool ok = false;
+ u = decodeHexEscapeCharacter(&ok);
+ if (!ok) {
+ _errorCode = IllegalHexadecimalEscapeSequence;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal hexadecimal escape sequence");
+ return T_ERROR;
+ }
+ } break;
+
+ // single character escape sequence
+ case '\\': u = QLatin1Char('\\'); scanChar(); break;
+ case '\'': u = QLatin1Char('\''); scanChar(); break;
+ case '\"': u = QLatin1Char('\"'); scanChar(); break;
+ case 'b': u = QLatin1Char('\b'); scanChar(); break;
+ case 'f': u = QLatin1Char('\f'); scanChar(); break;
+ case 'n': u = QLatin1Char('\n'); scanChar(); break;
+ case 'r': u = QLatin1Char('\r'); scanChar(); break;
+ case 't': u = QLatin1Char('\t'); scanChar(); break;
+ case 'v': u = QLatin1Char('\v'); scanChar(); break;
+
+ case '0':
+ if (! _codePtr->isDigit()) {
+ scanChar();
+ u = QLatin1Char('\0');
+ break;
+ }
+ Q_FALLTHROUGH();
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ _errorCode = IllegalEscapeSequence;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "Octal escape sequences are not allowed");
+ return T_ERROR;
+
+ case '\r':
+ case '\n':
+ case 0x2028u:
+ case 0x2029u:
+ scanChar();
+ continue;
+
+ default:
+ // non escape character
+ u = _char;
+ scanChar();
+ }
+
+ _tokenText += u;
+ } else {
+ _tokenText += _char;
scanChar();
}
+ }
- if (chars.size() < 3) {
- _errorCode = IllegalHexNumber;
- _errorMessage = QCoreApplication::translate("QQmlParser", "At least one hexadecimal digit is required after '0%1'").arg(ch);
+ _errorCode = UnclosedStringLiteral;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "Unclosed string at end of line");
+ return T_ERROR;
+}
+
+int Lexer::scanNumber(QChar ch)
+{
+ if (ch == QLatin1Char('0')) {
+ if (_char == QLatin1Char('x') || _char == QLatin1Char('X')) {
+ ch = _char; // remember the x or X to use it in the error message below.
+
+ // parse hex integer literal
+ scanChar(); // consume 'x'
+
+ if (!isHexDigit(_char)) {
+ _errorCode = IllegalNumber;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "At least one hexadecimal digit is required after '0%1'").arg(ch);
+ return T_ERROR;
+ }
+
+ double d = 0.;
+ while (1) {
+ int digit = ::hexDigit(_char);
+ if (digit < 0)
+ break;
+ d *= 16;
+ d += digit;
+ scanChar();
+ }
+
+ _tokenValue = d;
+ return T_NUMERIC_LITERAL;
+ } else if (_char == QLatin1Char('o') || _char == QLatin1Char('O')) {
+ ch = _char; // remember the o or O to use it in the error message below.
+
+ // parse octal integer literal
+ scanChar(); // consume 'o'
+
+ if (!isOctalDigit(_char.unicode())) {
+ _errorCode = IllegalNumber;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "At least one octal digit is required after '0%1'").arg(ch);
+ return T_ERROR;
+ }
+
+ double d = 0.;
+ while (1) {
+ int digit = ::octalDigit(_char);
+ if (digit < 0)
+ break;
+ d *= 8;
+ d += digit;
+ scanChar();
+ }
+
+ _tokenValue = d;
+ return T_NUMERIC_LITERAL;
+ } else if (_char == QLatin1Char('b') || _char == QLatin1Char('B')) {
+ ch = _char; // remember the b or B to use it in the error message below.
+
+ // parse binary integer literal
+ scanChar(); // consume 'b'
+
+ if (_char.unicode() != '0' && _char.unicode() != '1') {
+ _errorCode = IllegalNumber;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "At least one binary digit is required after '0%1'").arg(ch);
+ return T_ERROR;
+ }
+
+ double d = 0.;
+ while (1) {
+ int digit = 0;
+ if (_char.unicode() == '1')
+ digit = 1;
+ else if (_char.unicode() != '0')
+ break;
+ d *= 2;
+ d += digit;
+ scanChar();
+ }
+
+ _tokenValue = d;
+ return T_NUMERIC_LITERAL;
+ } else if (_char.isDigit() && !qmlMode()) {
+ _errorCode = IllegalCharacter;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "Decimal numbers can't start with '0'");
return T_ERROR;
}
-
- _tokenValue = integerFromString(chars.constData(), chars.size(), 16);
- return T_NUMERIC_LITERAL;
}
// decimal integer literal
- while (_char.isDigit()) {
- chars.append(_char.unicode());
- scanChar(); // consume the digit
- }
-
- if (_char == QLatin1Char('.')) {
- chars.append(_char.unicode());
- scanChar(); // consume `.'
+ QVarLengthArray<char,32> chars;
+ chars.append(ch.unicode());
+ if (ch != QLatin1Char('.')) {
while (_char.isDigit()) {
chars.append(_char.unicode());
- scanChar();
+ scanChar(); // consume the digit
}
- if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) {
- if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) &&
- _codePtr[1].isDigit())) {
-
- chars.append(_char.unicode());
- scanChar(); // consume `e'
+ if (_char == QLatin1Char('.')) {
+ chars.append(_char.unicode());
+ scanChar(); // consume `.'
+ }
+ }
- if (_char == QLatin1Char('+') || _char == QLatin1Char('-')) {
- chars.append(_char.unicode());
- scanChar(); // consume the sign
- }
+ while (_char.isDigit()) {
+ chars.append(_char.unicode());
+ scanChar();
+ }
- while (_char.isDigit()) {
- chars.append(_char.unicode());
- scanChar();
- }
- }
- }
- } else if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) {
+ if (_char == QLatin1Char('e') || _char == QLatin1Char('E')) {
if (_codePtr[0].isDigit() || ((_codePtr[0] == QLatin1Char('+') || _codePtr[0] == QLatin1Char('-')) &&
_codePtr[1].isDigit())) {
@@ -1001,12 +1106,6 @@ int Lexer::scanNumber(QChar ch)
}
}
- if (chars.length() == 1) {
- // if we ended up with a single digit, then it was a '0'
- _tokenValue = 0;
- return T_NUMERIC_LITERAL;
- }
-
chars.append('\0');
const char *begin = chars.constData();
@@ -1174,16 +1273,6 @@ bool Lexer::isOctalDigit(ushort c)
return (c >= '0' && c <= '7');
}
-int Lexer::tokenEndLine() const
-{
- return _currentLineNumber;
-}
-
-int Lexer::tokenEndColumn() const
-{
- return _codePtr - _lastLinePtr;
-}
-
QString Lexer::tokenText() const
{
if (_validTokenText)
@@ -1256,6 +1345,7 @@ static const int uriTokens[] = {
QQmlJSGrammar::T_FUNCTION,
QQmlJSGrammar::T_IF,
QQmlJSGrammar::T_IN,
+ QQmlJSGrammar::T_OF,
QQmlJSGrammar::T_INSTANCEOF,
QQmlJSGrammar::T_NEW,
QQmlJSGrammar::T_NULL,
@@ -1302,7 +1392,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
lex(); // skip T_DOT
- if (! (_tokenKind == T_IDENTIFIER || _tokenKind == T_RESERVED_WORD))
+ if (! (_tokenKind == T_IDENTIFIER || _tokenKind == T_IMPORT))
return true; // expected a valid QML/JS directive
const QString directiveName = tokenText();
@@ -1396,7 +1486,7 @@ bool Lexer::scanDirectives(Directives *directives, DiagnosticMessage *error)
//
// recognize the mandatory `as' followed by the module name
//
- if (! (lex() == T_IDENTIFIER && tokenText() == QLatin1String("as") && tokenStartLine() == lineNumber)) {
+ if (! (lex() == T_AS && tokenStartLine() == lineNumber)) {
if (fileImport)
error->message = QCoreApplication::translate("QQmlParser", "File import requires a qualifier");
else
diff --git a/src/qml/parser/qqmljslexer_p.h b/src/qml/parser/qqmljslexer_p.h
index 11d8081713..644c5c09aa 100644
--- a/src/qml/parser/qqmljslexer_p.h
+++ b/src/qml/parser/qqmljslexer_p.h
@@ -51,10 +51,11 @@
// We mean it.
//
-#include "qqmljsglobal_p.h"
-#include "qqmljsgrammar_p.h"
+#include <private/qqmljsglobal_p.h>
+#include <private/qqmljsgrammar_p.h>
#include <QtCore/qstring.h>
+#include <QtCore/qstack.h>
QT_QML_BEGIN_NAMESPACE
@@ -62,32 +63,7 @@ namespace QQmlJS {
class Engine;
class DiagnosticMessage;
-
-class QML_PARSER_EXPORT Directives {
-public:
- virtual ~Directives() {}
-
- virtual void pragmaLibrary()
- {
- }
-
- virtual void importFile(const QString &jsfile, const QString &module, int line, int column)
- {
- Q_UNUSED(jsfile);
- Q_UNUSED(module);
- Q_UNUSED(line);
- Q_UNUSED(column);
- }
-
- virtual void importModule(const QString &uri, const QString &version, const QString &module, int line, int column)
- {
- Q_UNUSED(uri);
- Q_UNUSED(version);
- Q_UNUSED(module);
- Q_UNUSED(line);
- Q_UNUSED(column);
- }
-};
+class Directives;
class QML_PARSER_EXPORT Lexer: public QQmlJSGrammar
{
@@ -97,10 +73,7 @@ public:
T_BOOLEAN = T_RESERVED_WORD,
T_BYTE = T_RESERVED_WORD,
T_CHAR = T_RESERVED_WORD,
- T_CLASS = T_RESERVED_WORD,
T_DOUBLE = T_RESERVED_WORD,
- T_EXPORT = T_RESERVED_WORD,
- T_EXTENDS = T_RESERVED_WORD,
T_FINAL = T_RESERVED_WORD,
T_FLOAT = T_RESERVED_WORD,
T_GOTO = T_RESERVED_WORD,
@@ -113,8 +86,6 @@ public:
T_PRIVATE = T_RESERVED_WORD,
T_PROTECTED = T_RESERVED_WORD,
T_SHORT = T_RESERVED_WORD,
- T_STATIC = T_RESERVED_WORD,
- T_SUPER = T_RESERVED_WORD,
T_SYNCHRONIZED = T_RESERVED_WORD,
T_THROWS = T_RESERVED_WORD,
T_TRANSIENT = T_RESERVED_WORD,
@@ -124,7 +95,7 @@ public:
enum Error {
NoError,
IllegalCharacter,
- IllegalHexNumber,
+ IllegalNumber,
UnclosedStringLiteral,
IllegalEscapeSequence,
IllegalUnicodeEscapeSequence,
@@ -142,13 +113,34 @@ public:
enum RegExpFlag {
RegExp_Global = 0x01,
RegExp_IgnoreCase = 0x02,
- RegExp_Multiline = 0x04
+ RegExp_Multiline = 0x04,
+ RegExp_Unicode = 0x08,
+ RegExp_Sticky = 0x10
+ };
+
+ enum ParseModeFlags {
+ QmlMode = 0x1,
+ YieldIsKeyword = 0x2,
+ StaticIsKeyword = 0x4
};
public:
Lexer(Engine *engine);
+ int parseModeFlags() const {
+ int flags = 0;
+ if (qmlMode())
+ flags |= QmlMode|StaticIsKeyword;
+ if (yieldIsKeyWord())
+ flags |= YieldIsKeyword;
+ if (_staticIsKeyword)
+ flags |= StaticIsKeyword;
+ return flags;
+ }
+
bool qmlMode() const;
+ bool yieldIsKeyWord() const { return _generatorLevel != 0; }
+ void setStaticIsKeyword(bool b) { _staticIsKeyword = b; }
QString code() const;
void setCode(const QString &code, int lineno, bool qmlMode = true);
@@ -166,10 +158,7 @@ public:
int tokenLength() const { return _tokenLength; }
int tokenStartLine() const { return _tokenLine; }
- int tokenStartColumn() const { return _tokenStartPtr - _tokenLinePtr + 1; }
-
- int tokenEndLine() const;
- int tokenEndColumn() const;
+ int tokenStartColumn() const { return _tokenColumn; }
inline QStringRef tokenSpell() const { return _tokenSpell; }
double tokenValue() const { return _tokenValue; }
@@ -188,13 +177,23 @@ public:
BalancedParentheses
};
+ void enterGeneratorBody() { ++_generatorLevel; }
+ void leaveGeneratorBody() { --_generatorLevel; }
+
protected:
- int classify(const QChar *s, int n, bool qmlMode);
+ static int classify(const QChar *s, int n, int parseModeFlags);
private:
inline void scanChar();
int scanToken();
int scanNumber(QChar ch);
+ enum ScanStringMode {
+ SingleQuote = '\'',
+ DoubleQuote = '"',
+ TemplateHead = '`',
+ TemplateContinuation = 0
+ };
+ int scanString(ScanStringMode mode);
bool isLineTerminator() const;
unsigned isLineTerminatorSequence() const;
@@ -202,10 +201,9 @@ private:
static bool isDecimalDigit(ushort c);
static bool isHexDigit(QChar c);
static bool isOctalDigit(ushort c);
- static bool isUnicodeEscapeSequence(const QChar *chars);
void syncProhibitAutomaticSemicolon();
- QChar decodeUnicodeEscapeCharacter(bool *ok);
+ uint decodeUnicodeEscapeCharacter(bool *ok);
QChar decodeHexEscapeCharacter(bool *ok);
private:
@@ -218,26 +216,30 @@ private:
const QChar *_codePtr;
const QChar *_endPtr;
- const QChar *_lastLinePtr;
- const QChar *_tokenLinePtr;
const QChar *_tokenStartPtr;
QChar _char;
Error _errorCode;
int _currentLineNumber;
+ int _currentColumnNumber;
double _tokenValue;
// parentheses state
ParenthesesState _parenthesesState;
int _parenthesesCount;
+ // template string stack
+ QStack<int> _outerTemplateBraceCount;
+ int _bracesCount = -1;
+
int _stackToken;
int _patternFlags;
int _tokenKind;
int _tokenLength;
int _tokenLine;
+ int _tokenColumn;
bool _validTokenText;
bool _prohibitAutomaticSemicolon;
@@ -246,6 +248,8 @@ private:
bool _followsClosingBrace;
bool _delimited;
bool _qmlMode;
+ int _generatorLevel = 0;
+ bool _staticIsKeyword = false;
};
} // end of namespace QQmlJS
diff --git a/src/qml/parser/qqmljsmemorypool_p.h b/src/qml/parser/qqmljsmemorypool_p.h
index ef443d96bc..9a480f1224 100644
--- a/src/qml/parser/qqmljsmemorypool_p.h
+++ b/src/qml/parser/qqmljsmemorypool_p.h
@@ -83,6 +83,7 @@ public:
free(_blocks);
}
+ qDeleteAll(strings);
}
inline void *allocate(size_t size)
@@ -104,6 +105,11 @@ public:
template <typename Tp> Tp *New() { return new (this->allocate(sizeof(Tp))) Tp(); }
+ QStringRef newString(const QString &string) {
+ strings.append(new QString(string));
+ return QStringRef(strings.last());
+ }
+
private:
Q_NEVER_INLINE void *allocate_helper(size_t size)
{
@@ -143,6 +149,7 @@ private:
int _blockCount = -1;
char *_ptr = nullptr;
char *_end = nullptr;
+ QVector<QString*> strings;
enum
{
@@ -153,12 +160,10 @@ private:
class QML_PARSER_EXPORT Managed
{
- Managed(const Managed &other);
- void operator = (const Managed &other);
-
+ Q_DISABLE_COPY(Managed)
public:
- Managed() {}
- ~Managed() {}
+ Managed() = default;
+ ~Managed() = default;
void *operator new(size_t size, MemoryPool *pool) { return pool->allocate(size); }
void operator delete(void *) {}
diff --git a/src/qml/parser/qqmljsparser.cpp b/src/qml/parser/qqmljsparser.cpp
deleted file mode 100644
index 24b04b02f9..0000000000
--- a/src/qml/parser/qqmljsparser.cpp
+++ /dev/null
@@ -1,2010 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qqmljsengine_p.h"
-#include "qqmljslexer_p.h"
-#include "qqmljsast_p.h"
-#include "qqmljsmemorypool_p.h"
-
-#include <QtCore/qdebug.h>
-#include <QtCore/qcoreapplication.h>
-
-#include <string.h>
-
-
-
-#include "qqmljsparser_p.h"
-
-#include <QtCore/qvarlengtharray.h>
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is automatically generated from qqmljs.g.
-// Changes should be made to that file, not here. Any change to this file will
-// be lost!
-//
-// To regenerate this file, run:
-// qlalr --no-debug --no-lines --qt qqmljs.g
-//
-
-using namespace QQmlJS;
-
-QT_QML_BEGIN_NAMESPACE
-
-void Parser::reallocateStack()
-{
- if (! stack_size)
- stack_size = 128;
- else
- stack_size <<= 1;
-
- sym_stack = reinterpret_cast<Value*> (realloc(sym_stack, stack_size * sizeof(Value)));
- state_stack = reinterpret_cast<int*> (realloc(state_stack, stack_size * sizeof(int)));
- location_stack = reinterpret_cast<AST::SourceLocation*> (realloc(location_stack, stack_size * sizeof(AST::SourceLocation)));
- string_stack = reinterpret_cast<QStringRef*> (realloc(static_cast<void *>(string_stack), stack_size * sizeof(QStringRef)));
-}
-
-Parser::Parser(Engine *engine):
- driver(engine),
- pool(engine->pool()),
- tos(0),
- stack_size(0),
- sym_stack(nullptr),
- state_stack(nullptr),
- location_stack(nullptr),
- string_stack(nullptr),
- program(nullptr),
- yylval(0),
- first_token(nullptr),
- last_token(nullptr)
-{
-}
-
-Parser::~Parser()
-{
- if (stack_size) {
- free(sym_stack);
- free(state_stack);
- free(location_stack);
- free(string_stack);
- }
-}
-
-static inline AST::SourceLocation location(Lexer *lexer)
-{
- AST::SourceLocation loc;
- loc.offset = lexer->tokenOffset();
- loc.length = lexer->tokenLength();
- loc.startLine = lexer->tokenStartLine();
- loc.startColumn = lexer->tokenStartColumn();
- return loc;
-}
-
-AST::UiQualifiedId *Parser::reparseAsQualifiedId(AST::ExpressionNode *expr)
-{
- QVarLengthArray<QStringRef, 4> nameIds;
- QVarLengthArray<AST::SourceLocation, 4> locations;
-
- AST::ExpressionNode *it = expr;
- while (AST::FieldMemberExpression *m = AST::cast<AST::FieldMemberExpression *>(it)) {
- nameIds.append(m->name);
- locations.append(m->identifierToken);
- it = m->base;
- }
-
- if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(it)) {
- AST::UiQualifiedId *q = new (pool) AST::UiQualifiedId(idExpr->name);
- q->identifierToken = idExpr->identifierToken;
-
- AST::UiQualifiedId *currentId = q;
- for (int i = nameIds.size() - 1; i != -1; --i) {
- currentId = new (pool) AST::UiQualifiedId(currentId, nameIds[i]);
- currentId->identifierToken = locations[i];
- }
-
- return currentId->finish();
- }
-
- return nullptr;
-}
-
-AST::UiQualifiedPragmaId *Parser::reparseAsQualifiedPragmaId(AST::ExpressionNode *expr)
-{
- if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(expr)) {
- AST::UiQualifiedPragmaId *q = new (pool) AST::UiQualifiedPragmaId(idExpr->name);
- q->identifierToken = idExpr->identifierToken;
-
- return q->finish();
- }
-
- return nullptr;
-}
-
-
-bool Parser::parse(int startToken)
-{
- Lexer *lexer = driver->lexer();
- bool hadErrors = false;
- int yytoken = -1;
- int action = 0;
-
- token_buffer[0].token = startToken;
- first_token = &token_buffer[0];
- if (startToken == T_FEED_JS_PROGRAM && !lexer->qmlMode()) {
- Directives ignoreDirectives;
- Directives *directives = driver->directives();
- if (!directives)
- directives = &ignoreDirectives;
- DiagnosticMessage error;
- if (!lexer->scanDirectives(directives, &error)) {
- diagnostic_messages.append(error);
- return false;
- }
- token_buffer[1].token = lexer->tokenKind();
- token_buffer[1].dval = lexer->tokenValue();
- token_buffer[1].loc = location(lexer);
- token_buffer[1].spell = lexer->tokenSpell();
- last_token = &token_buffer[2];
- } else {
- last_token = &token_buffer[1];
- }
-
- tos = -1;
- program = nullptr;
-
- do {
- if (++tos == stack_size)
- reallocateStack();
-
- state_stack[tos] = action;
-
- _Lcheck_token:
- if (yytoken == -1 && -TERMINAL_COUNT != action_index[action]) {
- yyprevlloc = yylloc;
-
- if (first_token == last_token) {
- yytoken = lexer->lex();
- yylval = lexer->tokenValue();
- yytokenspell = lexer->tokenSpell();
- yylloc = location(lexer);
- } else {
- yytoken = first_token->token;
- yylval = first_token->dval;
- yytokenspell = first_token->spell;
- yylloc = first_token->loc;
- ++first_token;
- }
- }
-
- action = t_action(action, yytoken);
- if (action > 0) {
- if (action != ACCEPT_STATE) {
- yytoken = -1;
- sym(1).dval = yylval;
- stringRef(1) = yytokenspell;
- loc(1) = yylloc;
- } else {
- --tos;
- return ! hadErrors;
- }
- } else if (action < 0) {
- const int r = -action - 1;
- tos -= rhs[r];
-
- switch (r) {
-
-case 0: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
-
-case 1: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
-
-case 2: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
-
-case 3: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
-
-case 4: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
-
-case 5: {
- sym(1).Node = sym(2).Node;
- program = sym(1).Node;
-} break;
-
-case 6: {
- sym(1).UiProgram = new (pool) AST::UiProgram(sym(1).UiHeaderItemList,
- sym(2).UiObjectMemberList->finish());
-} break;
-
-case 8: {
- sym(1).Node = sym(1).UiHeaderItemList->finish();
-} break;
-
-case 9: {
- sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiPragma);
-} break;
-
-case 10: {
- sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiImport);
-} break;
-
-case 11: {
- sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiPragma);
-} break;
-
-case 12: {
- sym(1).Node = new (pool) AST::UiHeaderItemList(sym(1).UiHeaderItemList, sym(2).UiImport);
-} break;
-
-case 16: {
- sym(1).UiPragma->semicolonToken = loc(2);
-} break;
-
-case 18: {
- sym(1).UiImport->semicolonToken = loc(2);
-} break;
-
-case 20: {
- sym(1).UiImport->versionToken = loc(2);
- sym(1).UiImport->semicolonToken = loc(3);
-} break;
-
-case 22: {
- sym(1).UiImport->versionToken = loc(2);
- sym(1).UiImport->asToken = loc(3);
- sym(1).UiImport->importIdToken = loc(4);
- sym(1).UiImport->importId = stringRef(4);
- sym(1).UiImport->semicolonToken = loc(5);
-} break;
-
-case 24: {
- sym(1).UiImport->asToken = loc(2);
- sym(1).UiImport->importIdToken = loc(3);
- sym(1).UiImport->importId = stringRef(3);
- sym(1).UiImport->semicolonToken = loc(4);
-} break;
-
-case 25: {
- AST::UiPragma *node = nullptr;
-
- if (AST::UiQualifiedPragmaId *qualifiedId = reparseAsQualifiedPragmaId(sym(2).Expression)) {
- node = new (pool) AST::UiPragma(qualifiedId);
- }
-
- sym(1).Node = node;
-
- if (node) {
- node->pragmaToken = loc(1);
- } else {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
- QLatin1String("Expected a qualified name id")));
-
- return false; // ### remove me
- }
-} break;
-
-case 26: {
- AST::UiImport *node = nullptr;
-
- if (AST::StringLiteral *importIdLiteral = AST::cast<AST::StringLiteral *>(sym(2).Expression)) {
- node = new (pool) AST::UiImport(importIdLiteral->value);
- node->fileNameToken = loc(2);
- } else if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(2).Expression)) {
- node = new (pool) AST::UiImport(qualifiedId);
- node->fileNameToken = loc(2);
- }
-
- sym(1).Node = node;
-
- if (node) {
- node->importToken = loc(1);
- } else {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
- QLatin1String("Expected a qualified name id or a string literal")));
-
- return false; // ### remove me
- }
-} break;
-
-case 27: {
- sym(1).Node = nullptr;
-} break;
-
-case 28: {
- sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember);
-} break;
-
-case 29: {
- sym(1).Node = new (pool) AST::UiObjectMemberList(sym(1).UiObjectMember);
-} break;
-
-case 30: {
- AST::UiObjectMemberList *node = new (pool) AST:: UiObjectMemberList(
- sym(1).UiObjectMemberList, sym(2).UiObjectMember);
- sym(1).Node = node;
-} break;
-
-case 31: {
- sym(1).Node = new (pool) AST::UiArrayMemberList(sym(1).UiObjectMember);
-} break;
-
-case 32: {
- AST::UiArrayMemberList *node = new (pool) AST::UiArrayMemberList(
- sym(1).UiArrayMemberList, sym(3).UiObjectMember);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 33: {
- AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer((AST::UiObjectMemberList*)nullptr);
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 34: {
- AST::UiObjectInitializer *node = new (pool) AST::UiObjectInitializer(sym(2).UiObjectMemberList->finish());
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 35: {
- AST::UiObjectDefinition *node = new (pool) AST::UiObjectDefinition(sym(1).UiQualifiedId,
- sym(2).UiObjectInitializer);
- sym(1).Node = node;
-} break;
-
-case 37: {
- AST::UiArrayBinding *node = new (pool) AST::UiArrayBinding(
- sym(1).UiQualifiedId, sym(4).UiArrayMemberList->finish());
- node->colonToken = loc(2);
- node->lbracketToken = loc(3);
- node->rbracketToken = loc(5);
- sym(1).Node = node;
-} break;
-
-case 38: {
- AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding(
- sym(1).UiQualifiedId, sym(3).UiQualifiedId, sym(4).UiObjectInitializer);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 39: {
- AST::UiObjectBinding *node = new (pool) AST::UiObjectBinding(
- sym(3).UiQualifiedId, sym(1).UiQualifiedId, sym(4).UiObjectInitializer);
- node->colonToken = loc(2);
- node->hasOnToken = true;
- sym(1).Node = node;
-} break;
-
-case 47:
-{
- AST::UiScriptBinding *node = new (pool) AST::UiScriptBinding(
- sym(1).UiQualifiedId, sym(3).Statement);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 48: {
- AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 49: {
- AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 50: {
- AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 51: {
- AST::UiQualifiedId *node = new (pool) AST::UiQualifiedId(sym(1).UiQualifiedId, stringRef(3));
- node->identifierToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 52: {
- sym(1).Node = nullptr;
-} break;
-
-case 53: {
- sym(1).Node = sym(1).UiParameterList->finish ();
-} break;
-
-case 54: {
- AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiQualifiedId->finish(), stringRef(2));
- node->propertyTypeToken = loc(1);
- node->identifierToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 55: {
- AST::UiParameterList *node = new (pool) AST::UiParameterList(sym(1).UiParameterList, sym(3).UiQualifiedId->finish(), stringRef(4));
- node->propertyTypeToken = loc(3);
- node->commaToken = loc(2);
- node->identifierToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 57: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2));
- node->type = AST::UiPublicMember::Signal;
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(2);
- node->parameters = sym(4).UiParameterList;
- node->semicolonToken = loc(6);
- sym(1).Node = node;
-} break;
-
-case 59: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(nullptr, stringRef(2));
- node->type = AST::UiPublicMember::Signal;
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(2);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 61: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6));
- node->typeModifier = stringRef(2);
- node->propertyToken = loc(1);
- node->typeModifierToken = loc(2);
- node->typeToken = loc(4);
- node->identifierToken = loc(6);
- node->semicolonToken = loc(7);
- sym(1).Node = node;
-} break;
-
-case 63: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3));
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(3);
- node->semicolonToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 65: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4));
- node->isDefaultMember = true;
- node->defaultToken = loc(1);
- node->propertyToken = loc(2);
- node->typeToken = loc(3);
- node->identifierToken = loc(4);
- node->semicolonToken = loc(5);
- sym(1).Node = node;
-} break;
-
-case 67: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(5).UiQualifiedId->finish(), stringRef(7));
- node->isDefaultMember = true;
- node->defaultToken = loc(1);
- node->typeModifier = stringRef(3);
- node->propertyToken = loc(2);
- node->typeModifierToken = loc(2);
- node->typeToken = loc(4);
- node->identifierToken = loc(7);
- node->semicolonToken = loc(8);
- sym(1).Node = node;
-} break;
-
-case 68: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3),
- sym(5).Statement);
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(3);
- node->colonToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 69: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4),
- sym(6).Statement);
- node->isReadonlyMember = true;
- node->readonlyToken = loc(1);
- node->propertyToken = loc(2);
- node->typeToken = loc(3);
- node->identifierToken = loc(4);
- node->colonToken = loc(5);
- sym(1).Node = node;
-} break;
-
-case 70: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4),
- sym(6).Statement);
- node->isDefaultMember = true;
- node->defaultToken = loc(1);
- node->propertyToken = loc(2);
- node->typeToken = loc(3);
- node->identifierToken = loc(4);
- node->colonToken = loc(5);
- sym(1).Node = node;
-} break;
-
-case 71: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(4).UiQualifiedId->finish(), stringRef(6));
- node->typeModifier = stringRef(2);
- node->propertyToken = loc(1);
- node->typeModifierToken = loc(2);
- node->typeToken = loc(4);
- node->identifierToken = loc(6);
- node->semicolonToken = loc(7); // insert a fake ';' before ':'
-
- AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(6));
- propertyName->identifierToken = loc(6);
- propertyName->next = nullptr;
-
- AST::UiArrayBinding *binding = new (pool) AST::UiArrayBinding(
- propertyName, sym(9).UiArrayMemberList->finish());
- binding->colonToken = loc(7);
- binding->lbracketToken = loc(8);
- binding->rbracketToken = loc(10);
-
- node->binding = binding;
-
- sym(1).Node = node;
-} break;
-
-case 72: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(2).UiQualifiedId->finish(), stringRef(3));
- node->propertyToken = loc(1);
- node->typeToken = loc(2);
- node->identifierToken = loc(3);
- node->semicolonToken = loc(4); // insert a fake ';' before ':'
-
- AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(3));
- propertyName->identifierToken = loc(3);
- propertyName->next = nullptr;
-
- AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding(
- propertyName, sym(5).UiQualifiedId, sym(6).UiObjectInitializer);
- binding->colonToken = loc(4);
-
- node->binding = binding;
-
- sym(1).Node = node;
-} break;
-
-case 73: {
- AST::UiPublicMember *node = new (pool) AST::UiPublicMember(sym(3).UiQualifiedId->finish(), stringRef(4));
- node->isReadonlyMember = true;
- node->readonlyToken = loc(1);
- node->propertyToken = loc(2);
- node->typeToken = loc(3);
- node->identifierToken = loc(4);
- node->semicolonToken = loc(5); // insert a fake ';' before ':'
-
- AST::UiQualifiedId *propertyName = new (pool) AST::UiQualifiedId(stringRef(4));
- propertyName->identifierToken = loc(4);
- propertyName->next = nullptr;
-
- AST::UiObjectBinding *binding = new (pool) AST::UiObjectBinding(
- propertyName, sym(6).UiQualifiedId, sym(7).UiObjectInitializer);
- binding->colonToken = loc(5);
-
- node->binding = binding;
-
- sym(1).Node = node;
-} break;
-
-case 74: {
- sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node);
-} break;
-
-case 75: {
- sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node);
-} break;
-
-case 76: {
- AST::UiEnumDeclaration *enumDeclaration = new (pool) AST::UiEnumDeclaration(stringRef(2), sym(4).UiEnumMemberList->finish());
- enumDeclaration->enumToken = loc(1);
- enumDeclaration->rbraceToken = loc(5);
- sym(1).Node = enumDeclaration;
- break;
-}
-
-case 77: {
- AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1));
- node->memberToken = loc(1);
- sym(1).Node = node;
- break;
-}
-
-case 78: {
- AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(stringRef(1), sym(3).dval);
- node->memberToken = loc(1);
- node->valueToken = loc(3);
- sym(1).Node = node;
- break;
-}
-
-case 79: {
- AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3));
- node->memberToken = loc(3);
- sym(1).Node = node;
- break;
-}
-
-case 80: {
- AST::UiEnumMemberList *node = new (pool) AST::UiEnumMemberList(sym(1).UiEnumMemberList, stringRef(3), sym(5).dval);
- node->memberToken = loc(3);
- node->valueToken = loc(5);
- sym(1).Node = node;
- break;
-}
-
-case 88: {
- AST::ThisExpression *node = new (pool) AST::ThisExpression();
- node->thisToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 89: {
- AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 90: {
- AST::NullExpression *node = new (pool) AST::NullExpression();
- node->nullToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 91: {
- AST::TrueLiteral *node = new (pool) AST::TrueLiteral();
- node->trueToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 92: {
- AST::FalseLiteral *node = new (pool) AST::FalseLiteral();
- node->falseToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 93: {
- AST::NumericLiteral *node = new (pool) AST::NumericLiteral(sym(1).dval);
- node->literalToken = loc(1);
- sym(1).Node = node;
-} break;
-case 94:
-case 95: {
- AST::StringLiteral *node = new (pool) AST::StringLiteral(stringRef(1));
- node->literalToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 96: {
- bool rx = lexer->scanRegExp(Lexer::NoPrefix);
- if (!rx) {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage()));
- return false; // ### remove me
- }
-
- loc(1).length = lexer->tokenLength();
- yylloc = loc(1); // adjust the location of the current token
-
- AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral(
- driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags());
- node->literalToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 97: {
- bool rx = lexer->scanRegExp(Lexer::EqualPrefix);
- if (!rx) {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage()));
- return false;
- }
-
- loc(1).length = lexer->tokenLength();
- yylloc = loc(1); // adjust the location of the current token
-
- AST::RegExpLiteral *node = new (pool) AST::RegExpLiteral(
- driver->newStringRef(lexer->regExpPattern()), lexer->regExpFlags());
- node->literalToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 98: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral((AST::Elision *) nullptr);
- node->lbracketToken = loc(1);
- node->rbracketToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 99: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).Elision->finish());
- node->lbracketToken = loc(1);
- node->rbracketToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 100: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish ());
- node->lbracketToken = loc(1);
- node->rbracketToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 101: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (),
- (AST::Elision *) nullptr);
- node->lbracketToken = loc(1);
- node->commaToken = loc(3);
- node->rbracketToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 102: {
- AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (),
- sym(4).Elision->finish());
- node->lbracketToken = loc(1);
- node->commaToken = loc(3);
- node->rbracketToken = loc(5);
- sym(1).Node = node;
-} break;
-
-case 103: {
- AST::ObjectLiteral *node = nullptr;
- if (sym(2).Node)
- node = new (pool) AST::ObjectLiteral(
- sym(2).PropertyAssignmentList->finish ());
- else
- node = new (pool) AST::ObjectLiteral();
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 104: {
- AST::ObjectLiteral *node = new (pool) AST::ObjectLiteral(
- sym(2).PropertyAssignmentList->finish ());
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 105: {
- AST::NestedExpression *node = new (pool) AST::NestedExpression(sym(2).Expression);
- node->lparenToken = loc(1);
- node->rparenToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 106: {
- if (AST::ArrayMemberExpression *mem = AST::cast<AST::ArrayMemberExpression *>(sym(1).Expression)) {
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken,
- QLatin1String("Ignored annotation")));
-
- sym(1).Expression = mem->base;
- }
-
- if (AST::UiQualifiedId *qualifiedId = reparseAsQualifiedId(sym(1).Expression)) {
- sym(1).UiQualifiedId = qualifiedId;
- } else {
- sym(1).UiQualifiedId = nullptr;
-
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc(1),
- QLatin1String("Expected a qualified name id")));
-
- return false; // ### recover
- }
-} break;
-
-case 107: {
- sym(1).Node = new (pool) AST::ElementList((AST::Elision *) nullptr, sym(1).Expression);
-} break;
-
-case 108: {
- sym(1).Node = new (pool) AST::ElementList(sym(1).Elision->finish(), sym(2).Expression);
-} break;
-
-case 109: {
- AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList,
- (AST::Elision *) nullptr, sym(3).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 110: {
- AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, sym(3).Elision->finish(),
- sym(4).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 111: {
- AST::Elision *node = new (pool) AST::Elision();
- node->commaToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 112: {
- AST::Elision *node = new (pool) AST::Elision(sym(1).Elision);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 113: {
- AST::PropertyNameAndValue *node = new (pool) AST::PropertyNameAndValue(
- sym(1).PropertyName, sym(3).Expression);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 114: {
- AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter(
- sym(2).PropertyName, sym(6).FunctionBody);
- node->getSetToken = loc(1);
- node->lparenToken = loc(3);
- node->rparenToken = loc(4);
- node->lbraceToken = loc(5);
- node->rbraceToken = loc(7);
- sym(1).Node = node;
-} break;
-
-case 115: {
- AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter(
- sym(2).PropertyName, sym(4).FormalParameterList, sym(7).FunctionBody);
- node->getSetToken = loc(1);
- node->lparenToken = loc(3);
- node->rparenToken = loc(5);
- node->lbraceToken = loc(6);
- node->rbraceToken = loc(8);
- sym(1).Node = node;
-} break;
-
-case 116: {
- sym(1).Node = new (pool) AST::PropertyAssignmentList(sym(1).PropertyAssignment);
-} break;
-
-case 117: {
- AST::PropertyAssignmentList *node = new (pool) AST::PropertyAssignmentList(
- sym(1).PropertyAssignmentList, sym(3).PropertyAssignment);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 118: {
- AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1));
- node->propertyNameToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 119: {
- AST::StringLiteralPropertyName *node = new (pool) AST::StringLiteralPropertyName(stringRef(1));
- node->propertyNameToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 120: {
- AST::NumericLiteralPropertyName *node = new (pool) AST::NumericLiteralPropertyName(sym(1).dval);
- node->propertyNameToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 121: {
- AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1));
- node->propertyNameToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 159: {
- AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression);
- node->lbracketToken = loc(2);
- node->rbracketToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 160: {
- AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3));
- node->dotToken = loc(2);
- node->identifierToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 161: {
- AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList);
- node->newToken = loc(1);
- node->lparenToken = loc(3);
- node->rparenToken = loc(5);
- sym(1).Node = node;
-} break;
-
-case 163: {
- AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression);
- node->newToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 164: {
- AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 165: {
- AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 166: {
- AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression);
- node->lbracketToken = loc(2);
- node->rbracketToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 167: {
- AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3));
- node->dotToken = loc(2);
- node->identifierToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 168: {
- sym(1).Node = nullptr;
-} break;
-
-case 169: {
- sym(1).Node = sym(1).ArgumentList->finish();
-} break;
-
-case 170: {
- sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression);
-} break;
-
-case 171: {
- AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 175: {
- AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression);
- node->incrementToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 176: {
- AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression);
- node->decrementToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 178: {
- AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression);
- node->deleteToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 179: {
- AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression);
- node->voidToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 180: {
- AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression);
- node->typeofToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 181: {
- AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression);
- node->incrementToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 182: {
- AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression);
- node->decrementToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 183: {
- AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression);
- node->plusToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 184: {
- AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression);
- node->minusToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 185: {
- AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression);
- node->tildeToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 186: {
- AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression);
- node->notToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 188: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Mul, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 189: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Div, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 190: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Mod, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 192: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Add, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 193: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Sub, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 195: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::LShift, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 196: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::RShift, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 197: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::URShift, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 199: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Lt, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 200: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Gt, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 201: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Le, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 202: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Ge, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 203: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::InstanceOf, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 204: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::In, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 206: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Lt, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 207: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Gt, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 208: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Le, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 209: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Ge, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 210: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::InstanceOf, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 212: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Equal, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 213: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::NotEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 214: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::StrictEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 215: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::StrictNotEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 217: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Equal, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 218: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::NotEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 219: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::StrictEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 220: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::StrictNotEqual, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 222: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitAnd, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 224: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitAnd, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 226: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitXor, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 228: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitXor, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 230: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitOr, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 232: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::BitOr, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 234: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::And, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 236: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::And, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 238: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Or, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 240: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- QSOperator::Or, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 242: {
- AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression,
- sym(3).Expression, sym(5).Expression);
- node->questionToken = loc(2);
- node->colonToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 244: {
- AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression,
- sym(3).Expression, sym(5).Expression);
- node->questionToken = loc(2);
- node->colonToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 246: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- sym(2).ival, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 248: {
- AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression,
- sym(2).ival, sym(3).Expression);
- node->operatorToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 249: {
- sym(1).ival = QSOperator::Assign;
-} break;
-
-case 250: {
- sym(1).ival = QSOperator::InplaceMul;
-} break;
-
-case 251: {
- sym(1).ival = QSOperator::InplaceDiv;
-} break;
-
-case 252: {
- sym(1).ival = QSOperator::InplaceMod;
-} break;
-
-case 253: {
- sym(1).ival = QSOperator::InplaceAdd;
-} break;
-
-case 254: {
- sym(1).ival = QSOperator::InplaceSub;
-} break;
-
-case 255: {
- sym(1).ival = QSOperator::InplaceLeftShift;
-} break;
-
-case 256: {
- sym(1).ival = QSOperator::InplaceRightShift;
-} break;
-
-case 257: {
- sym(1).ival = QSOperator::InplaceURightShift;
-} break;
-
-case 258: {
- sym(1).ival = QSOperator::InplaceAnd;
-} break;
-
-case 259: {
- sym(1).ival = QSOperator::InplaceXor;
-} break;
-
-case 260: {
- sym(1).ival = QSOperator::InplaceOr;
-} break;
-
-case 262: {
- AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 263: {
- sym(1).Node = nullptr;
-} break;
-
-case 266: {
- AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 267: {
- sym(1).Node = nullptr;
-} break;
-
-case 284: {
- AST::Block *node = new (pool) AST::Block(sym(2).StatementList);
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 285: {
- sym(1).Node = new (pool) AST::StatementList(sym(1).Statement);
-} break;
-
-case 286: {
- sym(1).Node = new (pool) AST::StatementList(sym(1).StatementList, sym(2).Statement);
-} break;
-
-case 287: {
- sym(1).Node = nullptr;
-} break;
-
-case 288: {
- sym(1).Node = sym(1).StatementList->finish ();
-} break;
-
-case 290: {
- AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope;
- if (sym(1).ival == T_LET)
- s = AST::VariableDeclaration::BlockScope;
- else if (sym(1).ival == T_CONST)
- s = AST::VariableDeclaration::ReadOnlyBlockScope;
-
- AST::VariableStatement *node = new (pool) AST::VariableStatement(sym(2).VariableDeclarationList->finish(s));
- node->declarationKindToken = loc(1);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 291: {
- sym(1).ival = T_LET;
-} break;
-
-case 292: {
- sym(1).ival = T_CONST;
-} break;
-
-case 293: {
- sym(1).ival = T_VAR;
-} break;
-
-case 294: {
- sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration);
-} break;
-
-case 295: {
- AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList(
- sym(1).VariableDeclarationList, sym(3).VariableDeclaration);
- node->commaToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 296: {
- sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration);
-} break;
-
-case 297: {
- sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).VariableDeclaration);
-} break;
-
-case 298: {
- AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope;
- AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s);
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 299: {
- AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope;
- AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s);
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 300: {
- // ### TODO: AST for initializer
- sym(1) = sym(2);
-} break;
-
-case 301: {
- sym(1).Node = nullptr;
-} break;
-
-case 303: {
- // ### TODO: AST for initializer
- sym(1) = sym(2);
-} break;
-
-case 304: {
- sym(1).Node = nullptr;
-} break;
-
-case 306: {
- AST::EmptyStatement *node = new (pool) AST::EmptyStatement();
- node->semicolonToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 308: {
- AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression);
- node->semicolonToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 309: {
- AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement);
- node->ifToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- node->elseToken = loc(6);
- sym(1).Node = node;
-} break;
-
-case 310: {
- AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement);
- node->ifToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 313: {
- AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression);
- node->doToken = loc(1);
- node->whileToken = loc(3);
- node->lparenToken = loc(4);
- node->rparenToken = loc(6);
- node->semicolonToken = loc(7);
- sym(1).Node = node;
-} break;
-
-case 314: {
- AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement);
- node->whileToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 315: {
- AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression,
- sym(5).Expression, sym(7).Expression, sym(9).Statement);
- node->forToken = loc(1);
- node->lparenToken = loc(2);
- node->firstSemicolonToken = loc(4);
- node->secondSemicolonToken = loc(6);
- node->rparenToken = loc(8);
- sym(1).Node = node;
-} break;
-
-case 316: {
- AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope;
- AST::LocalForStatement *node = new (pool) AST::LocalForStatement(
- sym(4).VariableDeclarationList->finish(s), sym(6).Expression,
- sym(8).Expression, sym(10).Statement);
- node->forToken = loc(1);
- node->lparenToken = loc(2);
- node->varToken = loc(3);
- node->firstSemicolonToken = loc(5);
- node->secondSemicolonToken = loc(7);
- node->rparenToken = loc(9);
- sym(1).Node = node;
-} break;
-
-case 317: {
- AST:: ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression,
- sym(5).Expression, sym(7).Statement);
- node->forToken = loc(1);
- node->lparenToken = loc(2);
- node->inToken = loc(4);
- node->rparenToken = loc(6);
- sym(1).Node = node;
-} break;
-
-case 318: {
- AST::LocalForEachStatement *node = new (pool) AST::LocalForEachStatement(
- sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement);
- node->forToken = loc(1);
- node->lparenToken = loc(2);
- node->varToken = loc(3);
- node->inToken = loc(5);
- node->rparenToken = loc(7);
- sym(1).Node = node;
-} break;
-
-case 320: {
- AST::ContinueStatement *node = new (pool) AST::ContinueStatement();
- node->continueToken = loc(1);
- node->semicolonToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 322: {
- AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2));
- node->continueToken = loc(1);
- node->identifierToken = loc(2);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 324: {
- AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef());
- node->breakToken = loc(1);
- node->semicolonToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 326: {
- AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2));
- node->breakToken = loc(1);
- node->identifierToken = loc(2);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 328: {
- AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression);
- node->returnToken = loc(1);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 329: {
- AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement);
- node->withToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 330: {
- AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock);
- node->switchToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 331: {
- AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses);
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 332: {
- AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses);
- node->lbraceToken = loc(1);
- node->rbraceToken = loc(5);
- sym(1).Node = node;
-} break;
-
-case 333: {
- sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause);
-} break;
-
-case 334: {
- sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause);
-} break;
-
-case 335: {
- sym(1).Node = nullptr;
-} break;
-
-case 336: {
- sym(1).Node = sym(1).CaseClauses->finish ();
-} break;
-
-case 337: {
- AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList);
- node->caseToken = loc(1);
- node->colonToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 338: {
- AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList);
- node->defaultToken = loc(1);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 339: {
- AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement);
- node->identifierToken = loc(1);
- node->colonToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 341: {
- AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression);
- node->throwToken = loc(1);
- node->semicolonToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 342: {
- AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch);
- node->tryToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 343: {
- AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally);
- node->tryToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 344: {
- AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally);
- node->tryToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 345: {
- AST::Catch *node = new (pool) AST::Catch(stringRef(3), sym(5).Block);
- node->catchToken = loc(1);
- node->lparenToken = loc(2);
- node->identifierToken = loc(3);
- node->rparenToken = loc(4);
- sym(1).Node = node;
-} break;
-
-case 346: {
- AST::Finally *node = new (pool) AST::Finally(sym(2).Block);
- node->finallyToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 348: {
- AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement();
- node->debuggerToken = loc(1);
- node->semicolonToken = loc(2);
- sym(1).Node = node;
-} break;
-
-case 350: {
- AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody);
- node->functionToken = loc(1);
- node->identifierToken = loc(2);
- node->lparenToken = loc(3);
- node->rparenToken = loc(5);
- node->lbraceToken = loc(6);
- node->rbraceToken = loc(8);
- sym(1).Node = node;
-} break;
-
-case 351: {
- AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody);
- node->functionToken = loc(1);
- if (! stringRef(2).isNull())
- node->identifierToken = loc(2);
- node->lparenToken = loc(3);
- node->rparenToken = loc(5);
- node->lbraceToken = loc(6);
- node->rbraceToken = loc(8);
- sym(1).Node = node;
-} break;
-
-case 352: {
- AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(6).FunctionBody);
- node->functionToken = loc(1);
- node->lparenToken = loc(2);
- node->rparenToken = loc(4);
- node->lbraceToken = loc(5);
- node->rbraceToken = loc(7);
- sym(1).Node = node;
-} break;
-
-case 353: {
- AST::FormalParameterList *node = new (pool) AST::FormalParameterList(stringRef(1));
- node->identifierToken = loc(1);
- sym(1).Node = node;
-} break;
-
-case 354: {
- AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, stringRef(3));
- node->commaToken = loc(2);
- node->identifierToken = loc(3);
- sym(1).Node = node;
-} break;
-
-case 355: {
- sym(1).Node = nullptr;
-} break;
-
-case 356: {
- sym(1).Node = sym(1).FormalParameterList->finish ();
-} break;
-
-case 357: {
- sym(1).Node = nullptr;
-} break;
-
-case 359: {
- sym(1).Node = new (pool) AST::FunctionBody(sym(1).SourceElements->finish ());
-} break;
-
-case 361: {
- sym(1).Node = new (pool) AST::Program(sym(1).SourceElements->finish ());
-} break;
-
-case 362: {
- sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElement);
-} break;
-
-case 363: {
- sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElements, sym(2).SourceElement);
-} break;
-
-case 364: {
- sym(1).Node = new (pool) AST::StatementSourceElement(sym(1).Statement);
-} break;
-
-case 365: {
- sym(1).Node = new (pool) AST::FunctionSourceElement(sym(1).FunctionDeclaration);
-} break;
-
-case 366: {
- sym(1).Node = nullptr;
-} break;
-
- } // switch
- action = nt_action(state_stack[tos], lhs[r] - TERMINAL_COUNT);
- } // if
- } while (action != 0);
-
- if (first_token == last_token) {
- const int errorState = state_stack[tos];
-
- // automatic insertion of `;'
- if (yytoken != -1 && ((t_action(errorState, T_AUTOMATIC_SEMICOLON) && lexer->canInsertAutomaticSemicolon(yytoken))
- || t_action(errorState, T_COMPATIBILITY_SEMICOLON))) {
- SavedToken &tk = token_buffer[0];
- tk.token = yytoken;
- tk.dval = yylval;
- tk.spell = yytokenspell;
- tk.loc = yylloc;
-
- yylloc = yyprevlloc;
- yylloc.offset += yylloc.length;
- yylloc.startColumn += yylloc.length;
- yylloc.length = 0;
-
- //const QString msg = QCoreApplication::translate("QQmlParser", "Missing `;'");
- //diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, yylloc, msg));
-
- first_token = &token_buffer[0];
- last_token = &token_buffer[1];
-
- yytoken = T_SEMICOLON;
- yylval = 0;
-
- action = errorState;
-
- goto _Lcheck_token;
- }
-
- hadErrors = true;
-
- token_buffer[0].token = yytoken;
- token_buffer[0].dval = yylval;
- token_buffer[0].spell = yytokenspell;
- token_buffer[0].loc = yylloc;
-
- token_buffer[1].token = yytoken = lexer->lex();
- token_buffer[1].dval = yylval = lexer->tokenValue();
- token_buffer[1].spell = yytokenspell = lexer->tokenSpell();
- token_buffer[1].loc = yylloc = location(lexer);
-
- if (t_action(errorState, yytoken)) {
- QString msg;
- int token = token_buffer[0].token;
- if (token < 0 || token >= TERMINAL_COUNT)
- msg = QCoreApplication::translate("QQmlParser", "Syntax error");
- else
- msg = QCoreApplication::translate("QQmlParser", "Unexpected token `%1'").arg(QLatin1String(spell[token]));
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
-
- action = errorState;
- goto _Lcheck_token;
- }
-
- static int tokens[] = {
- T_PLUS,
- T_EQ,
-
- T_COMMA,
- T_COLON,
- T_SEMICOLON,
-
- T_RPAREN, T_RBRACKET, T_RBRACE,
-
- T_NUMERIC_LITERAL,
- T_IDENTIFIER,
-
- T_LPAREN, T_LBRACKET, T_LBRACE,
-
- EOF_SYMBOL
- };
-
- for (int *tk = tokens; *tk != EOF_SYMBOL; ++tk) {
- int a = t_action(errorState, *tk);
- if (a > 0 && t_action(a, yytoken)) {
- const QString msg = QCoreApplication::translate("QQmlParser", "Expected token `%1'").arg(QLatin1String(spell[*tk]));
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
-
- yytoken = *tk;
- yylval = 0;
- yylloc = token_buffer[0].loc;
- yylloc.length = 0;
-
- first_token = &token_buffer[0];
- last_token = &token_buffer[2];
-
- action = errorState;
- goto _Lcheck_token;
- }
- }
-
- for (int tk = 1; tk < TERMINAL_COUNT; ++tk) {
- if (tk == T_AUTOMATIC_SEMICOLON || tk == T_FEED_UI_PROGRAM ||
- tk == T_FEED_JS_STATEMENT || tk == T_FEED_JS_EXPRESSION ||
- tk == T_FEED_JS_SOURCE_ELEMENT)
- continue;
-
- int a = t_action(errorState, tk);
- if (a > 0 && t_action(a, yytoken)) {
- const QString msg = QCoreApplication::translate("QQmlParser", "Expected token `%1'").arg(QLatin1String(spell[tk]));
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
-
- yytoken = tk;
- yylval = 0;
- yylloc = token_buffer[0].loc;
- yylloc.length = 0;
-
- action = errorState;
- goto _Lcheck_token;
- }
- }
-
- const QString msg = QCoreApplication::translate("QQmlParser", "Syntax error");
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
- }
-
- return false;
-}
-
-QT_QML_END_NAMESPACE
-
-
diff --git a/src/qml/parser/qqmljsparser_p.h b/src/qml/parser/qqmljsparser_p.h
deleted file mode 100644
index b4aecd2f08..0000000000
--- a/src/qml/parser/qqmljsparser_p.h
+++ /dev/null
@@ -1,258 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-
-//
-// 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.
-//
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is automatically generated from qqmljs.g.
-// Changes should be made to that file, not here. Any change to this file will
-// be lost!
-//
-// To regenerate this file, run:
-// qlalr --no-debug --no-lines --qt qqmljs.g
-//
-
-#ifndef QQMLJSPARSER_P_H
-#define QQMLJSPARSER_P_H
-
-#include "qqmljsglobal_p.h"
-#include "qqmljsgrammar_p.h"
-#include "qqmljsast_p.h"
-#include "qqmljsengine_p.h"
-
-#include <QtCore/qlist.h>
-#include <QtCore/qstring.h>
-
-QT_QML_BEGIN_NAMESPACE
-
-namespace QQmlJS {
-
-class Engine;
-
-class QML_PARSER_EXPORT Parser: protected QQmlJSGrammar
-{
-public:
- union Value {
- int ival;
- double dval;
- AST::ArgumentList *ArgumentList;
- AST::CaseBlock *CaseBlock;
- AST::CaseClause *CaseClause;
- AST::CaseClauses *CaseClauses;
- AST::Catch *Catch;
- AST::DefaultClause *DefaultClause;
- AST::ElementList *ElementList;
- AST::Elision *Elision;
- AST::ExpressionNode *Expression;
- AST::Finally *Finally;
- AST::FormalParameterList *FormalParameterList;
- AST::FunctionBody *FunctionBody;
- AST::FunctionDeclaration *FunctionDeclaration;
- AST::Node *Node;
- AST::PropertyName *PropertyName;
- AST::PropertyAssignment *PropertyAssignment;
- AST::PropertyAssignmentList *PropertyAssignmentList;
- AST::SourceElement *SourceElement;
- AST::SourceElements *SourceElements;
- AST::Statement *Statement;
- AST::StatementList *StatementList;
- AST::Block *Block;
- AST::VariableDeclaration *VariableDeclaration;
- AST::VariableDeclarationList *VariableDeclarationList;
-
- AST::UiProgram *UiProgram;
- AST::UiHeaderItemList *UiHeaderItemList;
- AST::UiPragma *UiPragma;
- AST::UiImport *UiImport;
- AST::UiParameterList *UiParameterList;
- AST::UiPublicMember *UiPublicMember;
- AST::UiObjectDefinition *UiObjectDefinition;
- AST::UiObjectInitializer *UiObjectInitializer;
- AST::UiObjectBinding *UiObjectBinding;
- AST::UiScriptBinding *UiScriptBinding;
- AST::UiArrayBinding *UiArrayBinding;
- AST::UiObjectMember *UiObjectMember;
- AST::UiObjectMemberList *UiObjectMemberList;
- AST::UiArrayMemberList *UiArrayMemberList;
- AST::UiQualifiedId *UiQualifiedId;
- AST::UiQualifiedPragmaId *UiQualifiedPragmaId;
- AST::UiEnumMemberList *UiEnumMemberList;
- };
-
-public:
- Parser(Engine *engine);
- ~Parser();
-
- // parse a UI program
- bool parse() { return parse(T_FEED_UI_PROGRAM); }
- bool parseStatement() { return parse(T_FEED_JS_STATEMENT); }
- bool parseExpression() { return parse(T_FEED_JS_EXPRESSION); }
- bool parseSourceElement() { return parse(T_FEED_JS_SOURCE_ELEMENT); }
- bool parseUiObjectMember() { return parse(T_FEED_UI_OBJECT_MEMBER); }
- bool parseProgram() { return parse(T_FEED_JS_PROGRAM); }
-
- AST::UiProgram *ast() const
- { return AST::cast<AST::UiProgram *>(program); }
-
- AST::Statement *statement() const
- {
- if (! program)
- return nullptr;
-
- return program->statementCast();
- }
-
- AST::ExpressionNode *expression() const
- {
- if (! program)
- return nullptr;
-
- return program->expressionCast();
- }
-
- AST::UiObjectMember *uiObjectMember() const
- {
- if (! program)
- return nullptr;
-
- return program->uiObjectMemberCast();
- }
-
- AST::Node *rootNode() const
- { return program; }
-
- QList<DiagnosticMessage> diagnosticMessages() const
- { return diagnostic_messages; }
-
- inline DiagnosticMessage diagnosticMessage() const
- {
- for (const DiagnosticMessage &d : diagnostic_messages) {
- if (d.kind != DiagnosticMessage::Warning)
- return d;
- }
-
- return DiagnosticMessage();
- }
-
- inline QString errorMessage() const
- { return diagnosticMessage().message; }
-
- inline int errorLineNumber() const
- { return diagnosticMessage().loc.startLine; }
-
- inline int errorColumnNumber() const
- { return diagnosticMessage().loc.startColumn; }
-
-protected:
- bool parse(int startToken);
-
- void reallocateStack();
-
- inline Value &sym(int index)
- { return sym_stack [tos + index - 1]; }
-
- inline QStringRef &stringRef(int index)
- { return string_stack [tos + index - 1]; }
-
- inline AST::SourceLocation &loc(int index)
- { return location_stack [tos + index - 1]; }
-
- AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr);
- AST::UiQualifiedPragmaId *reparseAsQualifiedPragmaId(AST::ExpressionNode *expr);
-
-protected:
- Engine *driver;
- MemoryPool *pool;
- int tos;
- int stack_size;
- Value *sym_stack;
- int *state_stack;
- AST::SourceLocation *location_stack;
- QStringRef *string_stack;
-
- AST::Node *program;
-
- // error recovery
- enum { TOKEN_BUFFER_SIZE = 3 };
-
- struct SavedToken {
- int token;
- double dval;
- AST::SourceLocation loc;
- QStringRef spell;
- };
-
- double yylval;
- QStringRef yytokenspell;
- AST::SourceLocation yylloc;
- AST::SourceLocation yyprevlloc;
-
- SavedToken token_buffer[TOKEN_BUFFER_SIZE];
- SavedToken *first_token;
- SavedToken *last_token;
-
- QList<DiagnosticMessage> diagnostic_messages;
-};
-
-} // end of namespace QQmlJS
-
-
-
-#define J_SCRIPT_REGEXPLITERAL_RULE1 96
-
-#define J_SCRIPT_REGEXPLITERAL_RULE2 97
-
-QT_QML_END_NAMESPACE
-
-
-
-#endif // QQMLJSPARSER_P_H
diff --git a/src/qml/qml.pro b/src/qml/qml.pro
index 2137877427..a76a87b153 100644
--- a/src/qml/qml.pro
+++ b/src/qml/qml.pro
@@ -31,7 +31,7 @@ DEFINES += QT_NO_FOREACH
!equals(tag, "$${LITERAL_DOLLAR}Format:%H$${LITERAL_DOLLAR}") {
QML_COMPILE_HASH = $$tag
} else:exists($$PWD/../../.git) {
- commit = $$system(git describe --tags --always --long --dirty)
+ commit = $$system(git rev-parse HEAD)
QML_COMPILE_HASH = $$commit
}
compile_hash_contents = \
@@ -73,7 +73,7 @@ include(jsruntime/jsruntime.pri)
include(jit/jit.pri)
include(qml/qml.pri)
include(debugger/debugger.pri)
-qtConfig(animation) {
+qtConfig(qml-animation) {
include(animations/animations.pri)
}
include(types/types.pri)
diff --git a/src/qml/qml/ftw/ftw.pri b/src/qml/qml/ftw/ftw.pri
index 87d80d04bc..ade05a596b 100644
--- a/src/qml/qml/ftw/ftw.pri
+++ b/src/qml/qml/ftw/ftw.pri
@@ -11,8 +11,7 @@ HEADERS += \
$$PWD/qrecyclepool_p.h \
$$PWD/qflagpointer_p.h \
$$PWD/qlazilyallocated_p.h \
- $$PWD/qqmlnullablevalue_p.h \
- $$PWD/qdeferredcleanup_p.h \
+ $$PWD/qqmlnullablevalue_p.h
SOURCES += \
$$PWD/qintrusivelist.cpp \
diff --git a/src/qml/qml/ftw/qflagpointer_p.h b/src/qml/qml/ftw/qflagpointer_p.h
index 91ce74bec9..71b41cd30b 100644
--- a/src/qml/qml/ftw/qflagpointer_p.h
+++ b/src/qml/qml/ftw/qflagpointer_p.h
@@ -82,6 +82,8 @@ public:
inline T *data() const;
+ inline explicit operator bool() const;
+
private:
quintptr ptr_value = 0;
@@ -230,6 +232,12 @@ T *QFlagPointer<T>::data() const
return (T *)(ptr_value & ~FlagsMask);
}
+template<typename T>
+QFlagPointer<T>::operator bool() const
+{
+ return data() != nullptr;
+}
+
template<typename T, typename T2>
QBiPointer<T, T2>::QBiPointer()
{
diff --git a/src/qml/qml/ftw/qqmlrefcount_p.h b/src/qml/qml/ftw/qqmlrefcount_p.h
index 3cfb345b30..d32a08e0f5 100644
--- a/src/qml/qml/ftw/qqmlrefcount_p.h
+++ b/src/qml/qml/ftw/qqmlrefcount_p.h
@@ -85,19 +85,23 @@ public:
inline QQmlRefPointer();
inline QQmlRefPointer(T *, Mode m = AddRef);
inline QQmlRefPointer(const QQmlRefPointer<T> &);
+ inline QQmlRefPointer(QQmlRefPointer<T> &&);
inline ~QQmlRefPointer();
inline QQmlRefPointer<T> &operator=(const QQmlRefPointer<T> &o);
+ inline QQmlRefPointer<T> &operator=(QQmlRefPointer<T> &&o);
inline bool isNull() const { return !o; }
inline T* operator->() const { return o; }
inline T& operator*() const { return *o; }
- inline operator T*() const { return o; }
+ explicit inline operator bool() const { return o != nullptr; }
inline T* data() const { return o; }
inline QQmlRefPointer<T> &adopt(T *);
+ inline T* take() { T *res = o; o = nullptr; return res; }
+
private:
T *o;
};
@@ -156,6 +160,12 @@ QQmlRefPointer<T>::QQmlRefPointer(const QQmlRefPointer<T> &other)
if (o) o->addref();
}
+template <class T>
+QQmlRefPointer<T>::QQmlRefPointer(QQmlRefPointer<T> &&other)
+ : o(other.take())
+{
+}
+
template<class T>
QQmlRefPointer<T>::~QQmlRefPointer()
{
@@ -171,6 +181,14 @@ QQmlRefPointer<T> &QQmlRefPointer<T>::operator=(const QQmlRefPointer<T> &other)
return *this;
}
+template <class T>
+QQmlRefPointer<T> &QQmlRefPointer<T>::operator=(QQmlRefPointer<T> &&other)
+{
+ QQmlRefPointer<T> m(std::move(other));
+ qSwap(o, m.o);
+ return *this;
+}
+
/*!
Takes ownership of \a other. take() does *not* add a reference, as it assumes ownership
of the callers reference of other.
diff --git a/src/qml/qml/ftw/qqmlthread.cpp b/src/qml/qml/ftw/qqmlthread.cpp
index 96313d7627..2ef1dc7e93 100644
--- a/src/qml/qml/ftw/qqmlthread.cpp
+++ b/src/qml/qml/ftw/qqmlthread.cpp
@@ -316,6 +316,12 @@ void QQmlThread::shutdownThread()
void QQmlThread::internalCallMethodInThread(Message *message)
{
+#if !QT_CONFIG(thread)
+ message->call(this);
+ delete message;
+ return;
+#endif
+
Q_ASSERT(!isThisThread());
d->lock();
Q_ASSERT(d->m_mainThreadWaiting == false);
@@ -376,6 +382,10 @@ void QQmlThread::internalCallMethodInMain(Message *message)
void QQmlThread::internalPostMethodToThread(Message *message)
{
+#if !QT_CONFIG(thread)
+ internalPostMethodToMain(message);
+ return;
+#endif
Q_ASSERT(!isThisThread());
d->lock();
bool wasEmpty = d->threadList.isEmpty();
diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri
index 412dc6cba2..6d69294c17 100644
--- a/src/qml/qml/qml.pri
+++ b/src/qml/qml/qml.pri
@@ -20,7 +20,6 @@ SOURCES += \
$$PWD/qqmlinfo.cpp \
$$PWD/qqmlerror.cpp \
$$PWD/qqmlvaluetype.cpp \
- $$PWD/qqmlxmlhttprequest.cpp \
$$PWD/qqmlcleanup.cpp \
$$PWD/qqmlpropertycache.cpp \
$$PWD/qqmlnotifier.cpp \
@@ -31,7 +30,6 @@ SOURCES += \
$$PWD/qqmlextensionplugin.cpp \
$$PWD/qqmlimport.cpp \
$$PWD/qqmllist.cpp \
- $$PWD/qqmllocale.cpp \
$$PWD/qqmljavascriptexpression.cpp \
$$PWD/qqmlabstractbinding.cpp \
$$PWD/qqmlvaluetypeproxybinding.cpp \
@@ -85,7 +83,6 @@ HEADERS += \
$$PWD/qqmldata_p.h \
$$PWD/qqmlerror.h \
$$PWD/qqmlvaluetype_p.h \
- $$PWD/qqmlxmlhttprequest_p.h \
$$PWD/qqmlcleanup_p.h \
$$PWD/qqmlpropertycache_p.h \
$$PWD/qqmlpropertyindex_p.h \
@@ -99,7 +96,6 @@ HEADERS += \
$$PWD/qqmlimport_p.h \
$$PWD/qqmlextensionplugin.h \
$$PWD/qqmlscriptstring_p.h \
- $$PWD/qqmllocale_p.h \
$$PWD/qqmlcomponentattached_p.h \
$$PWD/qqmljavascriptexpression_p.h \
$$PWD/qqmlabstractbinding_p.h \
@@ -120,5 +116,22 @@ HEADERS += \
$$PWD/qqmldelayedcallqueue_p.h \
$$PWD/qqmlloggingcategory_p.h
+qtConfig(qml-xml-http-request) {
+ HEADERS += \
+ $$PWD/qqmlxmlhttprequest_p.h
+
+ SOURCES += \
+ $$PWD/qqmlxmlhttprequest.cpp
+
+}
+
+qtConfig(qml-locale) {
+ HEADERS += \
+ $$PWD/qqmllocale_p.h
+
+ SOURCES += \
+ $$PWD/qqmllocale.cpp
+}
+
include(ftw/ftw.pri)
include(v8/v8.pri)
diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h
index 213f23cd98..05a9f70247 100644
--- a/src/qml/qml/qqml.h
+++ b/src/qml/qml/qqml.h
@@ -496,6 +496,38 @@ int qmlRegisterCustomType(const char *uri, int versionMajor, int versionMinor,
return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
}
+template<typename T, int metaObjectRevision>
+int qmlRegisterCustomType(const char *uri, int versionMajor, int versionMinor,
+ const char *qmlName, QQmlCustomParser *parser)
+{
+ QML_GETTYPENAMES
+
+ QQmlPrivate::RegisterType type = {
+ 1,
+
+ qRegisterNormalizedMetaType<T *>(pointerName.constData()),
+ qRegisterNormalizedMetaType<QQmlListProperty<T> >(listName.constData()),
+ sizeof(T), QQmlPrivate::createInto<T>,
+ QString(),
+
+ uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject,
+
+ QQmlPrivate::attachedPropertiesFunc<T>(),
+ QQmlPrivate::attachedPropertiesMetaObject<T>(),
+
+ QQmlPrivate::StaticCastSelector<T,QQmlParserStatus>::cast(),
+ QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueSource>::cast(),
+ QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueInterceptor>::cast(),
+
+ nullptr, nullptr,
+
+ parser,
+ metaObjectRevision
+ };
+
+ return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type);
+}
+
template<typename T, typename E>
int qmlRegisterCustomExtendedType(const char *uri, int versionMajor, int versionMinor,
const char *qmlName, QQmlCustomParser *parser)
@@ -645,6 +677,8 @@ inline int qmlRegisterType(const QUrl &url, const char *uri, int versionMajor, i
return QQmlPrivate::qmlregister(QQmlPrivate::CompositeRegistration, &type);
}
+int Q_QML_EXPORT qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
+
QT_END_NAMESPACE
QML_DECLARE_TYPE(QObject)
diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp
index a0517e4558..9ac2100eab 100644
--- a/src/qml/qml/qqmlapplicationengine.cpp
+++ b/src/qml/qml/qqmlapplicationengine.cpp
@@ -74,7 +74,7 @@ void QQmlApplicationEnginePrivate::init()
q->connect(q, &QQmlApplicationEngine::exit, QCoreApplication::instance(), &QCoreApplication::exit);
#if QT_CONFIG(translation)
QTranslator* qtTranslator = new QTranslator;
- if (qtTranslator->load(QLatin1String("qt_") + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
+ if (qtTranslator->load(QLocale(), QLatin1String("qt"), QLatin1String("_"), QLibraryInfo::location(QLibraryInfo::TranslationsPath)))
QCoreApplication::installTranslator(qtTranslator);
translators << qtTranslator;
#endif
@@ -91,7 +91,7 @@ void QQmlApplicationEnginePrivate::loadTranslations(const QUrl &rootFile)
QFileInfo fi(rootFile.toLocalFile());
QTranslator *translator = new QTranslator;
- if (translator->load(QLatin1String("qml_") + QLocale::system().name(), fi.path() + QLatin1String("/i18n"))) {
+ if (translator->load(QLocale(), QLatin1String("qml"), QLatin1String("_"), fi.path() + QLatin1String("/i18n"))) {
QCoreApplication::installTranslator(translator);
translators << translator;
} else {
diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp
index 30a18440a8..e38f379eb0 100644
--- a/src/qml/qml/qqmlbinding.cpp
+++ b/src/qml/qml/qqmlbinding.cpp
@@ -337,7 +337,7 @@ protected:
class QQmlTranslationBinding : public GenericBinding<QMetaType::QString> {
public:
- QQmlTranslationBinding(QV4::CompiledData::CompilationUnit *compilationUnit, const QV4::CompiledData::Binding *binding)
+ QQmlTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
{
setCompilationUnit(compilationUnit);
m_binding = binding;
@@ -358,7 +358,7 @@ public:
if (!isAddedToObject() || hasError())
return;
- const QString result = m_binding->valueAsString(m_compilationUnit->data);
+ const QString result = m_binding->valueAsString(m_compilationUnit.data());
Q_ASSERT(targetObject());
@@ -374,11 +374,13 @@ public:
}
}
+ bool hasDependencies() const override final { return true; }
+
private:
const QV4::CompiledData::Binding *m_binding;
};
-QQmlBinding *QQmlBinding::createTranslationBinding(QV4::CompiledData::CompilationUnit *unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt)
+QQmlBinding *QQmlBinding::createTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, const QV4::CompiledData::Binding *binding, QObject *obj, QQmlContextData *ctxt)
{
QQmlTranslationBinding *b = new QQmlTranslationBinding(unit, binding);
@@ -550,7 +552,7 @@ void QQmlBinding::setEnabled(bool e, QQmlPropertyData::WriteFlags flags)
QString QQmlBinding::expression() const
{
- return QStringLiteral("function() { [code] }");
+ return QStringLiteral("function() { [native code] }");
}
void QQmlBinding::setTarget(const QQmlProperty &prop)
@@ -663,6 +665,11 @@ QVector<QQmlProperty> QQmlBinding::dependencies() const
return dependencies;
}
+bool QQmlBinding::hasDependencies() const
+{
+ return !permanentGuards.isEmpty() || !activeGuards.isEmpty() || translationsCaptured();
+}
+
class QObjectPointerBinding: public QQmlNonbindingBinding
{
QQmlMetaObject targetMetaObject;
diff --git a/src/qml/qml/qqmlbinding_p.h b/src/qml/qml/qqmlbinding_p.h
index a1295bd0ac..f192de4342 100644
--- a/src/qml/qml/qqmlbinding_p.h
+++ b/src/qml/qml/qqmlbinding_p.h
@@ -79,7 +79,7 @@ public:
const QString &url = QString(), quint16 lineNumber = 0);
static QQmlBinding *create(const QQmlPropertyData *property, QV4::Function *function,
QObject *obj, QQmlContextData *ctxt, QV4::ExecutionContext *scope);
- static QQmlBinding *createTranslationBinding(QV4::CompiledData::CompilationUnit *unit, const QV4::CompiledData::Binding *binding,
+ static QQmlBinding *createTranslationBinding(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit, const QV4::CompiledData::Binding *binding,
QObject *obj, QQmlContextData *ctxt);
~QQmlBinding() override;
@@ -118,6 +118,7 @@ public:
* Call this method from the UI thread.
*/
QVector<QQmlProperty> dependencies() const;
+ virtual bool hasDependencies() const;
protected:
virtual void doUpdate(const DeleteWatcher &watcher,
diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp
index d5117c8cec..e5b78591e0 100644
--- a/src/qml/qml/qqmlboundsignal.cpp
+++ b/src/qml/qml/qqmlboundsignal.cpp
@@ -110,46 +110,34 @@ QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index,
m_index(index),
m_target(target)
{
- // If the function is marked as having a nested function, then the user wrote:
- // onSomeSignal: function() { /*....*/ }
- // So take that nested function:
- if (auto closure = function->nestedFunction())
- function = closure;
-
- setupFunction(scope, function);
+ // It's important to call init first, because m_index gets remapped in case of cloned signals.
init(ctxt, scopeObject);
-}
-QQmlBoundSignalExpression::QQmlBoundSignalExpression(QObject *target, int index, QQmlContextData *ctxt, QObject *scope, QV4::Function *runtimeFunction)
- : QQmlJavaScriptExpression(),
- m_index(index),
- m_target(target)
-{
- // It's important to call init first, because m_index gets remapped in case of cloned signals.
- init(ctxt, scope);
+ QV4::ExecutionEngine *engine = ctxt->engine->handle();
// If the function is marked as having a nested function, then the user wrote:
// onSomeSignal: function() { /*....*/ }
// So take that nested function:
- if (auto closure = runtimeFunction->nestedFunction())
- runtimeFunction = closure;
-
- QV4::ExecutionEngine *engine = ctxt->engine->handle();
-
- QList<QByteArray> signalParameters = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).parameterNames();
- if (!signalParameters.isEmpty()) {
- QString error;
- QQmlPropertyCache::signalParameterStringForJS(engine, signalParameters, &error);
- if (!error.isEmpty()) {
- qmlWarning(scopeObject()) << error;
- return;
+ if (auto closure = function->nestedFunction()) {
+ function = closure;
+ } else {
+ QList<QByteArray> signalParameters = QMetaObjectPrivate::signal(m_target->metaObject(), m_index).parameterNames();
+ if (!signalParameters.isEmpty()) {
+ QString error;
+ QQmlPropertyCache::signalParameterStringForJS(engine, signalParameters, &error);
+ if (!error.isEmpty()) {
+ qmlWarning(scopeObject) << error;
+ return;
+ }
+ function->updateInternalClass(engine, signalParameters);
}
- runtimeFunction->updateInternalClass(engine, signalParameters);
}
QV4::Scope valueScope(engine);
- QV4::Scoped<QV4::QmlContext> qmlContext(valueScope, QV4::QmlContext::create(engine->rootContext(), ctxt, scope));
- setupFunction(qmlContext, runtimeFunction);
+ QV4::Scoped<QV4::QmlContext> qmlContext(valueScope, scope);
+ if (!qmlContext)
+ qmlContext = QV4::QmlContext::create(engine->rootContext(), ctxt, scopeObject);
+ setupFunction(qmlContext, function);
}
void QQmlBoundSignalExpression::init(QQmlContextData *ctxt, QObject *scope)
@@ -180,7 +168,7 @@ void QQmlBoundSignalExpression::expressionChanged()
QString QQmlBoundSignalExpression::expression() const
{
if (expressionFunctionValid())
- return QStringLiteral("function() { [code] }");
+ return QStringLiteral("function() { [native code] }");
return QString();
}
@@ -221,12 +209,12 @@ void QQmlBoundSignalExpression::evaluate(void **a)
jsCall->args[ii] = scope.engine->fromVariant(*((QVariant *)a[ii + 1]));
} else if (type == QMetaType::Int) {
//### optimization. Can go away if we switch to metaTypeToJS, or be expanded otherwise
- jsCall->args[ii] = QV4::Primitive::fromInt32(*reinterpret_cast<const int*>(a[ii + 1]));
+ 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::Primitive::nullValue();
+ jsCall->args[ii] = QV4::Value::nullValue();
else
jsCall->args[ii] = QV4::QObjectWrapper::wrap(v4, *reinterpret_cast<QObject* const *>(a[ii + 1]));
} else {
diff --git a/src/qml/qml/qqmlboundsignal_p.h b/src/qml/qml/qqmlboundsignal_p.h
index 01094a11f7..d1ec67210e 100644
--- a/src/qml/qml/qqmlboundsignal_p.h
+++ b/src/qml/qml/qqmlboundsignal_p.h
@@ -73,10 +73,8 @@ public:
const QString &parameterString = QString());
QQmlBoundSignalExpression(QObject *target, int index,
- QQmlContextData *ctxt, QObject *scopeObject, QV4::Function *function, QV4::ExecutionContext *scope);
-
- QQmlBoundSignalExpression(QObject *target, int index,
- QQmlContextData *ctxt, QObject *scope, QV4::Function *runtimeFunction);
+ QQmlContextData *ctxt, QObject *scopeObject, QV4::Function *function,
+ QV4::ExecutionContext *scope = nullptr);
// inherited from QQmlJavaScriptExpression.
QString expressionIdentifier() const override;
diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp
index 8885e69161..a67c5c4a2b 100644
--- a/src/qml/qml/qqmlcomponent.cpp
+++ b/src/qml/qml/qqmlcomponent.cpp
@@ -174,8 +174,6 @@ V4_DEFINE_EXTENSION(QQmlComponentExtension, componentExtension);
}
}
\endcode
-
- Note that the \l {Qt Quick 1} version is named QDeclarativeComponent.
*/
/*!
@@ -334,7 +332,7 @@ void QQmlComponentPrivate::typeDataProgress(QQmlTypeData *, qreal p)
emit q->progressChanged(p);
}
-void QQmlComponentPrivate::fromTypeData(QQmlTypeData *data)
+void QQmlComponentPrivate::fromTypeData(const QQmlRefPointer<QQmlTypeData> &data)
{
url = data->finalUrl();
compilationUnit = data->compilationUnit();
@@ -343,15 +341,12 @@ void QQmlComponentPrivate::fromTypeData(QQmlTypeData *data)
Q_ASSERT(data->isError());
state.errors = data->errors();
}
-
- data->release();
}
void QQmlComponentPrivate::clear()
{
if (typeData) {
typeData->unregisterCallback(this);
- typeData->release();
typeData = nullptr;
}
@@ -387,7 +382,7 @@ QQmlComponent::~QQmlComponent()
if (d->typeData) {
d->typeData->unregisterCallback(d);
- d->typeData->release();
+ d->typeData = nullptr;
}
}
@@ -580,7 +575,7 @@ void QQmlComponent::setData(const QByteArray &data, const QUrl &url)
d->url = url;
- QQmlTypeData *typeData = QQmlEnginePrivate::get(d->engine)->typeLoader.getType(data, url);
+ QQmlRefPointer<QQmlTypeData> typeData = QQmlEnginePrivate::get(d->engine)->typeLoader.getType(data, url);
if (typeData->isCompleteOrError()) {
d->fromTypeData(typeData);
@@ -595,8 +590,8 @@ void QQmlComponent::setData(const QByteArray &data, const QUrl &url)
}
/*!
-Returns the QQmlContext the component was created in. This is only
-valid for components created directly from QML.
+ Returns the QQmlContext the component was created in. This is only
+ valid for components created directly from QML.
*/
QQmlContext *QQmlComponent::creationContext() const
{
@@ -608,6 +603,17 @@ QQmlContext *QQmlComponent::creationContext() const
}
/*!
+ Returns the QQmlEngine of this component.
+
+ \since 5.12
+*/
+QQmlEngine *QQmlComponent::engine() const
+{
+ Q_D(const QQmlComponent);
+ return d->engine;
+}
+
+/*!
Load the QQmlComponent from the provided \a url.
\include qqmlcomponent.qdoc url-note
@@ -667,7 +673,7 @@ void QQmlComponentPrivate::loadUrl(const QUrl &newUrl, QQmlComponent::Compilatio
? QQmlTypeLoader::Asynchronous
: QQmlTypeLoader::PreferSynchronous;
- QQmlTypeData *data = QQmlEnginePrivate::get(engine)->typeLoader.getType(url, loaderMode);
+ QQmlRefPointer<QQmlTypeData> data = QQmlEnginePrivate::get(engine)->typeLoader.getType(url, loaderMode);
if (data->isCompleteOrError()) {
fromTypeData(data);
@@ -848,13 +854,10 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context)
// Do not create infinite recursion in object creation
static const int maxCreationDepth = 10;
- if (++creationDepth.localData() >= maxCreationDepth) {
+ if (creationDepth.localData() >= maxCreationDepth) {
qWarning("QQmlComponent: Component creation is recursing - aborting");
- --creationDepth.localData();
return nullptr;
}
- Q_ASSERT(creationDepth.localData() >= 1);
- depthIncreased = true;
QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine);
@@ -878,10 +881,6 @@ QQmlComponentPrivate::beginCreate(QQmlContextData *context)
ddata->indestructible = true;
ddata->explicitIndestructibleSet = true;
ddata->rootObjectInCreation = false;
- } else {
- Q_ASSERT(creationDepth.localData() >= 1);
- --creationDepth.localData();
- depthIncreased = false;
}
return rv;
@@ -955,14 +954,10 @@ void QQmlComponent::completeCreate()
void QQmlComponentPrivate::completeCreate()
{
if (state.completePending) {
+ ++creationDepth.localData();
QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine);
complete(ep, &state);
- }
-
- if (depthIncreased) {
- Q_ASSERT(creationDepth.localData() >= 1);
--creationDepth.localData();
- depthIncreased = false;
}
}
@@ -1226,7 +1221,7 @@ void QQmlComponentPrivate::setInitialProperties(QV4::ExecutionEngine *engine, QV
QV4::Scope scope(engine);
QV4::ScopedObject object(scope);
QV4::ScopedObject valueMap(scope, v);
- QV4::ObjectIterator it(scope, valueMap, QV4::ObjectIterator::EnumerableOnly|QV4::ObjectIterator::WithProtoChain);
+ QV4::ObjectIterator it(scope, valueMap, QV4::ObjectIterator::EnumerableOnly);
QV4::ScopedString name(scope);
QV4::ScopedValue val(scope);
if (engine->hasException)
@@ -1274,7 +1269,7 @@ void QQmlComponent::createObject(QQmlV4Function *args)
QObject *parent = nullptr;
QV4::ExecutionEngine *v4 = args->v4engine();
QV4::Scope scope(v4);
- QV4::ScopedValue valuemap(scope, QV4::Primitive::undefinedValue());
+ QV4::ScopedValue valuemap(scope, QV4::Value::undefinedValue());
if (args->length() >= 1) {
QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, (*args)[0]);
@@ -1389,7 +1384,7 @@ void QQmlComponent::incubateObject(QQmlV4Function *args)
QV4::Scope scope(v4);
QObject *parent = nullptr;
- QV4::ScopedValue valuemap(scope, QV4::Primitive::undefinedValue());
+ QV4::ScopedValue valuemap(scope, QV4::Value::undefinedValue());
QQmlIncubator::IncubationMode mode = QQmlIncubator::Asynchronous;
if (args->length() >= 1) {
@@ -1421,9 +1416,9 @@ void QQmlComponent::incubateObject(QQmlV4Function *args)
QQmlComponentExtension *e = componentExtension(args->v4engine());
- QV4::Scoped<QV4::QmlIncubatorObject> r(scope, v4->memoryManager->allocObject<QV4::QmlIncubatorObject>(mode));
+ QV4::Scoped<QV4::QmlIncubatorObject> r(scope, v4->memoryManager->allocate<QV4::QmlIncubatorObject>(mode));
QV4::ScopedObject p(scope, e->incubationProto.value());
- r->setPrototype(p);
+ r->setPrototypeOf(p);
if (!valuemap->isUndefined())
r->d()->valuemap.set(scope.engine, valuemap);
@@ -1527,8 +1522,8 @@ QQmlComponentExtension::~QQmlComponentExtension()
void QV4::Heap::QmlIncubatorObject::init(QQmlIncubator::IncubationMode m)
{
Object::init();
- valuemap.set(internalClass->engine, QV4::Primitive::undefinedValue());
- statusChanged.set(internalClass->engine, QV4::Primitive::undefinedValue());
+ valuemap.set(internalClass->engine, QV4::Value::undefinedValue());
+ statusChanged.set(internalClass->engine, QV4::Value::undefinedValue());
parent.init();
qmlContext.set(internalClass->engine, nullptr);
incubator = new QQmlComponentIncubator(this, m);
@@ -1569,7 +1564,7 @@ void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s)
if (f) {
QV4::JSCallData jsCallData(scope, 1);
*jsCallData->thisObject = this;
- jsCallData->args[0] = QV4::Primitive::fromUInt32(s);
+ jsCallData->args[0] = QV4::Value::fromUInt32(s);
f->call(jsCallData);
if (scope.hasException()) {
QQmlError error = scope.engine->catchExceptionAsQmlError();
diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h
index 444b3ec46c..20199d0b21 100644
--- a/src/qml/qml/qqmlcomponent.h
+++ b/src/qml/qml/qqmlcomponent.h
@@ -109,6 +109,7 @@ public:
QQmlContext *forContext = nullptr);
QQmlContext *creationContext() const;
+ QQmlEngine *engine() const;
static QQmlComponentAttached *qmlAttachedProperties(QObject *);
diff --git a/src/qml/qml/qqmlcomponent_p.h b/src/qml/qml/qqmlcomponent_p.h
index 2a8d36f317..4d9e4c6c15 100644
--- a/src/qml/qml/qqmlcomponent_p.h
+++ b/src/qml/qml/qqmlcomponent_p.h
@@ -79,7 +79,7 @@ class Q_QML_PRIVATE_EXPORT QQmlComponentPrivate : public QObjectPrivate, public
public:
QQmlComponentPrivate()
- : typeData(nullptr), progress(0.), start(-1), engine(nullptr), creationContext(nullptr), depthIncreased(false) {}
+ : progress(0.), start(-1), engine(nullptr), creationContext(nullptr) {}
void loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous);
@@ -88,18 +88,18 @@ public:
void initializeObjectWithInitialProperties(QV4::QmlContext *qmlContext, const QV4::Value &valuemap, QObject *toCreate);
static void setInitialProperties(QV4::ExecutionEngine *engine, QV4::QmlContext *qmlContext, const QV4::Value &o, const QV4::Value &v);
- void incubateObject(
+ virtual void incubateObject(
QQmlIncubator *incubationTask,
QQmlComponent *component,
QQmlEngine *engine,
QQmlContextData *context,
QQmlContextData *forContext);
- QQmlTypeData *typeData;
+ QQmlRefPointer<QQmlTypeData> typeData;
void typeDataReady(QQmlTypeData *) override;
void typeDataProgress(QQmlTypeData *, qreal) override;
- void fromTypeData(QQmlTypeData *data);
+ void fromTypeData(const QQmlRefPointer<QQmlTypeData> &data);
QUrl url;
qreal progress;
@@ -136,7 +136,6 @@ public:
QQmlEngine *engine;
QQmlGuardedContextData creationContext;
- bool depthIncreased;
void clear();
diff --git a/src/qml/qml/qqmlcontext.cpp b/src/qml/qml/qqmlcontext.cpp
index 3dcfa92416..cbf5a6e259 100644
--- a/src/qml/qml/qqmlcontext.cpp
+++ b/src/qml/qml/qqmlcontext.cpp
@@ -845,7 +845,7 @@ void QQmlContextData::initFromTypeCompilationUnit(const QQmlRefPointer<QV4::Comp
typeCompilationUnit = unit;
componentObjectIndex = subComponentIndex == -1 ? /*root object*/0 : subComponentIndex;
Q_ASSERT(!idValues);
- idValueCount = typeCompilationUnit->data->objectAt(componentObjectIndex)->nNamedObjectsInComponent;
+ idValueCount = typeCompilationUnit->objectAt(componentObjectIndex)->nNamedObjectsInComponent;
idValues = new ContextGuard[idValueCount];
}
diff --git a/src/qml/qml/qqmlcustomparser_p.h b/src/qml/qml/qqmlcustomparser_p.h
index 2a0f805014..bf28bca447 100644
--- a/src/qml/qml/qqmlcustomparser_p.h
+++ b/src/qml/qml/qqmlcustomparser_p.h
@@ -81,8 +81,8 @@ public:
void clearErrors();
Flags flags() const { return m_flags; }
- virtual void verifyBindings(const QV4::CompiledData::Unit *, const QList<const QV4::CompiledData::Binding *> &) = 0;
- virtual void applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &) = 0;
+ virtual void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) = 0;
+ virtual void applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &) = 0;
QVector<QQmlCompileError> errors() const { return exceptions; }
diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h
index 59fefde893..2468de6857 100644
--- a/src/qml/qml/qqmldata_p.h
+++ b/src/qml/qml/qqmldata_p.h
@@ -232,7 +232,7 @@ public:
QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
QVector<DeferredData *> deferredData;
- void deferData(int objectIndex, QV4::CompiledData::CompilationUnit *, QQmlContextData *);
+ void deferData(int objectIndex, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, QQmlContextData *);
void releaseDeferredData();
QV4::WeakValue jsWrapper;
diff --git a/src/qml/qml/qqmldelayedcallqueue.cpp b/src/qml/qml/qqmldelayedcallqueue.cpp
index 5bcf5cd586..61cb0a9065 100644
--- a/src/qml/qml/qqmldelayedcallqueue.cpp
+++ b/src/qml/qml/qqmldelayedcallqueue.cpp
@@ -71,7 +71,7 @@ void QQmlDelayedCallQueue::DelayedFunctionCall::execute(QV4::ExecutionEngine *en
*jsCallData->thisObject = QV4::Encode::undefined();
for (int i = 0; i < argCount; i++) {
- jsCallData->args[i] = array->getIndexed(i);
+ jsCallData->args[i] = array->get(i);
}
callback->call(jsCallData);
@@ -186,7 +186,7 @@ void QQmlDelayedCallQueue::storeAnyArguments(DelayedFunctionCall &dfc, const QV4
QV4::ScopedArrayObject array(scope, engine->newArrayObject(length));
uint i = 0;
for (int j = offset, ej = argc; j < ej; ++i, ++j)
- array->putIndexed(i, argv[j]);
+ array->put(i, argv[j]);
dfc.m_args.set(engine, array);
}
diff --git a/src/qml/qml/qqmldirparser.cpp b/src/qml/qml/qqmldirparser.cpp
index 4cca8a4d58..d87bf433b8 100644
--- a/src/qml/qml/qqmldirparser.cpp
+++ b/src/qml/qml/qqmldirparser.cpp
@@ -107,6 +107,7 @@ bool QQmlDirParser::parse(const QString &source)
_components.clear();
_scripts.clear();
_designerSupported = false;
+ _className.clear();
quint16 lineNumber = 0;
bool firstLine = true;
@@ -196,7 +197,8 @@ bool QQmlDirParser::parse(const QString &source)
continue;
}
- // Ignore these. qmlimportscanner uses them.
+ _className = sections[1];
+
} else if (sections[0] == QLatin1String("internal")) {
if (sectionCount != 3) {
reportError(lineNumber, 0,
@@ -270,7 +272,7 @@ bool QQmlDirParser::parse(const QString &source)
if (parseVersion(sections[1], &major, &minor)) {
const QString &fileName = sections[2];
- if (fileName.endsWith(QLatin1String(".js"))) {
+ if (fileName.endsWith(QLatin1String(".js")) || fileName.endsWith(QLatin1String(".mjs"))) {
// A 'js' extension indicates a namespaced script import
const Script entry(sections[0], fileName, major, minor);
_scripts.append(entry);
@@ -377,6 +379,11 @@ bool QQmlDirParser::designerSupported() const
return _designerSupported;
}
+QString QQmlDirParser::className() const
+{
+ return _className;
+}
+
QDebug &operator<< (QDebug &debug, const QQmlDirParser::Component &component)
{
const QString output = QStringLiteral("{%1 %2.%3}").
diff --git a/src/qml/qml/qqmldirparser_p.h b/src/qml/qml/qqmldirparser_p.h
index 820c40238d..d7e29813d1 100644
--- a/src/qml/qml/qqmldirparser_p.h
+++ b/src/qml/qml/qqmldirparser_p.h
@@ -135,6 +135,8 @@ public:
QList<TypeInfo> typeInfos() const;
#endif
+ QString className() const;
+
private:
bool maybeAddComponent(const QString &typeName, const QString &fileName, const QString &version, QHash<QString,Component> &hash, int lineNumber = -1, bool multi = true);
void reportError(quint16 line, quint16 column, const QString &message);
@@ -150,6 +152,7 @@ private:
#ifdef QT_CREATOR
QList<TypeInfo> _typeInfos;
#endif
+ QString _className;
};
typedef QHash<QString,QQmlDirParser::Component> QQmlDirComponents;
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index c6b39581a7..f288646ec7 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -48,7 +48,6 @@
#include "qqmlcomponent.h"
#include "qqmlvme_p.h"
#include "qqmlstringconverters_p.h"
-#include "qqmlxmlhttprequest_p.h"
#include "qqmlscriptstring.h"
#include "qqmlglobal_p.h"
#include "qqmlcomponent_p.h"
@@ -79,20 +78,26 @@
#include <private/qobject_p.h>
#include <private/qmetaobject_p.h>
+#if QT_CONFIG(qml_locale)
#include <private/qqmllocale_p.h>
+#endif
#include <private/qqmlbind_p.h>
#include <private/qqmlconnections_p.h>
-#if QT_CONFIG(animation)
+#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>
@@ -217,30 +222,40 @@ void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int
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
- qmlRegisterType<QQmlConnections,1>(uri, versionMajor, (versionMinor < 3 ? 3 : versionMinor), "Connections"); //Only available in >=2.3
- qmlRegisterType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections");
-#if QT_CONFIG(animation)
+ qmlRegisterCustomType<QQmlConnections>(uri, versionMajor, 0, "Connections", new QQmlConnectionsParser);
+ if (!strcmp(uri, "QtQuick"))
+ qmlRegisterCustomType<QQmlConnections,1>(uri, versionMajor, 7, "Connections", new QQmlConnectionsParser); //Only available in QtQuick >=2.7
+ else
+ qmlRegisterCustomType<QQmlConnections,1>(uri, versionMajor, 3, "Connections", new QQmlConnectionsParser); //Only available in QtQml >=2.3
+#if QT_CONFIG(qml_animation)
qmlRegisterType<QQmlTimer>(uri, versionMajor, versionMinor,"Timer");
#endif
qmlRegisterType<QQmlInstantiator>(uri, versionMajor, (versionMinor < 1 ? 1 : versionMinor), "Instantiator"); //Only available in >=2.1
- qmlRegisterCustomType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections", new QQmlConnectionsParser);
qmlRegisterType<QQmlInstanceModel>();
- qmlRegisterType<QQmlLoggingCategory>(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "LoggingCategory"); //Only available in >=2.8
+
+ qmlRegisterType<QQmlLoggingCategory>(uri, versionMajor, 8, "LoggingCategory"); //Only available in >=2.8
+ qmlRegisterType<QQmlLoggingCategory,1>(uri, versionMajor, 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");
#endif
qmlRegisterType<QQmlObjectModel>(uri, versionMajor, versionMinor, "VisualItemModel");
+#endif // < Qt 6
}
void QQmlEnginePrivate::defineQtQuick2Module()
@@ -250,7 +265,9 @@ void QQmlEnginePrivate::defineQtQuick2Module()
// register the QtQuick2 types which are implemented in the QtQml module.
registerQtQuick2Types("QtQuick",2,0);
+#if QT_CONFIG(qml_locale)
qmlRegisterUncreatableType<QQmlLocale>("QtQuick", 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);
@@ -676,7 +693,9 @@ QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e)
#endif
outputWarningsToMsgLog(true),
cleanup(nullptr), erroredBindings(nullptr), inProgressCreations(0),
+#if QT_CONFIG(qml_worker_script)
workerScriptEngine(nullptr),
+#endif
activeObjectCreator(nullptr),
#if QT_CONFIG(qml_network)
networkAccessManager(nullptr), networkAccessManagerFactory(nullptr),
@@ -951,7 +970,9 @@ 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);
@@ -974,6 +995,7 @@ void QQmlEnginePrivate::init()
rootContext = new QQmlContext(q,true);
}
+#if QT_CONFIG(qml_worker_script)
QQuickWorkerScriptEngine *QQmlEnginePrivate::getWorkerScriptEngine()
{
Q_Q(QQmlEngine);
@@ -981,6 +1003,7 @@ QQuickWorkerScriptEngine *QQmlEnginePrivate::getWorkerScriptEngine()
workerScriptEngine = new QQuickWorkerScriptEngine(q);
return workerScriptEngine;
}
+#endif
/*!
\class QQmlEngine
@@ -1010,8 +1033,6 @@ QQuickWorkerScriptEngine *QQmlEnginePrivate::getWorkerScriptEngine()
In this case, the Text item will be created in the engine's
\l {QQmlEngine::rootContext()}{root context}.
- Note that the \l {Qt Quick 1} version is called QDeclarativeEngine.
-
\sa QQmlComponent, QQmlContext, {QML Global Object}
*/
@@ -1365,6 +1386,71 @@ void QQmlEngine::setOutputWarningsToStandardError(bool enabled)
}
/*!
+ \fn template<typename T> T QQmlEngine::singletonInstance(int qmlTypeId)
+
+ Returns the instance of a singleton type that was registered under \a qmlTypeId.
+
+ The template argument \e T may be either QJSValue or a pointer to a QObject-derived
+ type and depends on how the singleton was registered. If no instance of \e T has been
+ created yet, it is created now. If \a qmlTypeId does not represent a valid singleton
+ type, either a default constructed QJSValue or a \c nullptr is returned.
+
+ QObject* example:
+ \code
+ class MySingleton : public QObject {
+ Q_OBJECT
+ static int typeId;
+ // ...
+ };
+
+ // Register with QObject* callback
+ MySingleton::typeId = qmlRegisterSingletonType<MySingleton>(...);
+
+ // Retrieve as QObject*
+ QQmlEngine engine;
+ MySingleton* instance = engine.singletonInstance<MySingleton*>(MySingleton::typeId);
+ \endcode
+
+ QJSValue example:
+ \code
+ // Register with QJSValue callback
+ int typeId = qmlRegisterSingletonType(...);
+
+ // Retrieve as QJSValue
+ QQmlEngine engine;
+ QJSValue instance = engine.singletonInstance<QJSValue>(typeId);
+ \endcode
+
+ It is recommended to store the QML type id during registration, e.g. as a static member
+ in the singleton class. Otherwise, a costly lookup via qmlTypeId() has to be performed
+ at run-time.
+
+ \sa qmlRegisterSingletonType(), qmlTypeId()
+ \since 5.12
+*/
+template<>
+QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId)
+{
+ QQmlType type = QQmlMetaType::qmlType(qmlTypeId, QQmlMetaType::TypeIdCategory::QmlType);
+
+ if (!type.isValid() || !type.isSingleton())
+ return QJSValue();
+
+ QQmlType::SingletonInstanceInfo* info = type.singletonInstanceInfo();
+ info->init(this);
+
+ if (QObject* o = info->qobjectApi(this))
+ return this->newQObject(o);
+ else {
+ QJSValue value = info->scriptApi(this);
+ if (!value.isUndefined())
+ return value;
+ }
+
+ return QJSValue();
+}
+
+/*!
Refreshes all binding expressions that use strings marked for translation.
Call this function after you have installed a new translator with
@@ -1699,7 +1785,7 @@ void QQmlData::NotifyList::layout()
todo = nullptr;
}
-void QQmlData::deferData(int objectIndex, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlContextData *context)
+void QQmlData::deferData(int objectIndex, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlContextData *context)
{
QQmlData::DeferredData *deferData = new QQmlData::DeferredData;
deferData->deferredIdx = objectIndex;
@@ -2030,6 +2116,25 @@ void QQmlEnginePrivate::warning(QQmlEnginePrivate *engine, const QList<QQmlError
dumpwarning(error);
}
+QList<QQmlError> QQmlEnginePrivate::qmlErrorFromDiagnostics(const QString &fileName, const QList<DiagnosticMessage> &diagnosticMessages)
+{
+ QList<QQmlError> errors;
+ for (const DiagnosticMessage &m : diagnosticMessages) {
+ if (m.isWarning()) {
+ qWarning("%s:%d : %s", qPrintable(fileName), m.loc.startLine, qPrintable(m.message));
+ continue;
+ }
+
+ QQmlError error;
+ error.setUrl(QUrl(fileName));
+ error.setDescription(m.message);
+ error.setLine(m.loc.startLine);
+ error.setColumn(m.loc.startColumn);
+ errors << error;
+ }
+ return errors;
+}
+
void QQmlEnginePrivate::cleanupScarceResources()
{
// iterate through the list and release them all.
@@ -2266,7 +2371,7 @@ QQmlMetaObject QQmlEnginePrivate::rawMetaObjectForType(int t) const
Locker locker(this);
auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return QQmlMetaObject((*iter)->rootPropertyCache());
+ return QQmlMetaObject((*iter)->rootPropertyCache().data());
} else {
QQmlType type = QQmlMetaType::qmlType(t);
return QQmlMetaObject(type.baseMetaObject());
@@ -2278,7 +2383,7 @@ QQmlMetaObject QQmlEnginePrivate::metaObjectForType(int t) const
Locker locker(this);
auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return QQmlMetaObject((*iter)->rootPropertyCache());
+ return QQmlMetaObject((*iter)->rootPropertyCache().data());
} else {
QQmlType type = QQmlMetaType::qmlType(t);
return QQmlMetaObject(type.metaObject());
@@ -2290,7 +2395,7 @@ QQmlPropertyCache *QQmlEnginePrivate::propertyCacheForType(int t)
Locker locker(this);
auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return (*iter)->rootPropertyCache();
+ return (*iter)->rootPropertyCache().data();
} else {
QQmlType type = QQmlMetaType::qmlType(t);
locker.unlock();
@@ -2303,7 +2408,7 @@ QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t, int minorVe
Locker locker(this);
auto iter = m_compositeTypes.constFind(t);
if (iter != m_compositeTypes.cend()) {
- return (*iter)->rootPropertyCache();
+ return (*iter)->rootPropertyCache().data();
} else {
QQmlType type = QQmlMetaType::qmlType(t);
locker.unlock();
diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h
index 73ad2754c8..871e6bd9b4 100644
--- a/src/qml/qml/qqmlengine.h
+++ b/src/qml/qml/qqmlengine.h
@@ -143,6 +143,9 @@ public:
bool outputWarningsToStandardError() const;
void setOutputWarningsToStandardError(bool);
+ template<typename T>
+ T singletonInstance(int qmlTypeId);
+
public Q_SLOTS:
void retranslate();
@@ -167,6 +170,19 @@ private:
Q_DECLARE_PRIVATE(QQmlEngine)
};
+template<>
+Q_QML_EXPORT QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId);
+
+template<typename T>
+T QQmlEngine::singletonInstance(int qmlTypeId) {
+ QJSValue instance = singletonInstance<QJSValue>(qmlTypeId);
+ if (!instance.isQObject())
+ return nullptr;
+
+ QObject *object = instance.toQObject();
+ return qobject_cast<T>(object);
+}
+
QT_END_NAMESPACE
#endif // QQMLENGINE_H
diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index da52e01793..3caa6ec138 100644
--- a/src/qml/qml/qqmlengine_p.h
+++ b/src/qml/qml/qqmlengine_p.h
@@ -153,8 +153,10 @@ public:
QV8Engine *v8engine() const { return q_func()->handle()->v8Engine; }
QV4::ExecutionEngine *v4engine() const { return q_func()->handle(); }
+#if QT_CONFIG(qml_worker_script)
QQuickWorkerScriptEngine *getWorkerScriptEngine();
QQuickWorkerScriptEngine *workerScriptEngine;
+#endif
QUrl baseUrl;
@@ -245,6 +247,8 @@ public:
inline static QQmlEngine *get(QQmlEnginePrivate *p);
inline static QQmlEnginePrivate *get(QV4::ExecutionEngine *e);
+ 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();
diff --git a/src/qml/qml/qqmlerror.cpp b/src/qml/qml/qqmlerror.cpp
index fc5b186b29..61e9a3f37e 100644
--- a/src/qml/qml/qqmlerror.cpp
+++ b/src/qml/qml/qqmlerror.cpp
@@ -73,8 +73,6 @@ QT_BEGIN_NAMESPACE
^
\endcode
- Note that the \l {Qt Quick 1} version is named QDeclarativeError
-
\sa QQuickView::errors(), QQmlComponent::errors()
*/
class QQmlErrorPrivate
diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp
index 27d3acb9b7..0c1ffbf3a0 100644
--- a/src/qml/qml/qqmlexpression.cpp
+++ b/src/qml/qml/qqmlexpression.cpp
@@ -110,8 +110,6 @@ void QQmlExpressionPrivate::init(QQmlContextData *ctxt, QV4::Function *runtimeFu
QQmlExpression *expr = new QQmlExpression(engine->rootContext(), myObject, "width * 2");
int result = expr->evaluate().toInt(); // result = 400
\endcode
-
- Note that the \l {Qt Quick 1} version is called QDeclarativeExpression.
*/
/*!
diff --git a/src/qml/qml/qqmlextensionplugin.cpp b/src/qml/qml/qqmlextensionplugin.cpp
index 818ff7c34f..c1c971f0a9 100644
--- a/src/qml/qml/qqmlextensionplugin.cpp
+++ b/src/qml/qml/qqmlextensionplugin.cpp
@@ -55,8 +55,6 @@ QT_BEGIN_NAMESPACE
The \l {Writing QML Extensions with C++} tutorial also contains a chapter
on creating QML plugins.
- Note that the \l {Qt Quick 1} version is called QDeclarativeExtensionPlugin.
-
\sa QQmlEngine::importPlugin(), {How to Create Qt Plugins}
*/
diff --git a/src/qml/qml/qqmlguard_p.h b/src/qml/qml/qqmlguard_p.h
index 808bf4c709..3ac63926a0 100644
--- a/src/qml/qml/qqmlguard_p.h
+++ b/src/qml/qml/qqmlguard_p.h
@@ -106,6 +106,34 @@ protected:
virtual void objectDestroyed(T *) {}
};
+template <typename T>
+class QQmlStrongJSQObjectReference : public QQmlGuard<T>
+{
+public:
+ void setObject(T *o, QObject *parent) {
+ T *old = this->object();
+ if (o == old)
+ return;
+
+ if (m_jsOwnership && old && old->parent() == parent)
+ QQml_setParent_noEvent(old, nullptr);
+
+ this->QQmlGuard<T>::operator=(o);
+
+ if (o && !o->parent() && !QQmlData::keepAliveDuringGarbageCollection(o)) {
+ m_jsOwnership = true;
+ QQml_setParent_noEvent(o, parent);
+ } else {
+ m_jsOwnership = false;
+ }
+ }
+
+private:
+ using QQmlGuard<T>::setObject;
+ using QQmlGuard<T>::operator=;
+ bool m_jsOwnership = false;
+};
+
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QQmlGuard<QObject>)
diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp
index 80ebab5ca3..bc53b98b5b 100644
--- a/src/qml/qml/qqmlimport.cpp
+++ b/src/qml/qml/qqmlimport.cpp
@@ -674,6 +674,7 @@ bool QQmlImportInstance::setQmldirContent(const QString &resolvedUrl, const QQml
{
Q_ASSERT(resolvedUrl.endsWith(Slash));
url = resolvedUrl;
+ localDirectoryPath = QQmlFile::urlToLocalFileOrQrc(url);
qmlDirComponents = qmldir.components();
@@ -737,7 +738,8 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
int *vmajor, int *vminor, QQmlType *type_return, QString *base,
bool *typeRecursionDetected,
QQmlType::RegistrationType registrationType,
- QQmlImport::RecursionRestriction recursionRestriction) const
+ QQmlImport::RecursionRestriction recursionRestriction,
+ QList<QQmlError> *errors) const
{
if (majversion >= 0 && minversion >= 0) {
QQmlType t = QQmlMetaType::qmlType(type, uri, majversion, minversion);
@@ -810,19 +812,32 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt
*type_return = returnType;
return returnType.isValid();
}
- } else if (!isLibrary) {
+ } else if (!isLibrary && !localDirectoryPath.isEmpty()) {
QString qmlUrl;
bool exists = false;
const QString urlsToTry[2] = {
- url + QString::fromRawData(type.constData(), type.length()) + dotqml_string, // Type -> Type.qml
- url + QString::fromRawData(type.constData(), type.length()) + dotuidotqml_string // Type -> Type.ui.qml
+ typeStr + dotqml_string, // Type -> Type.qml
+ typeStr + dotuidotqml_string // Type -> Type.ui.qml
};
for (uint i = 0; i < sizeof(urlsToTry) / sizeof(urlsToTry[0]); ++i) {
- const QString url = urlsToTry[i];
- exists = !typeLoader->absoluteFilePath(QQmlFile::urlToLocalFileOrQrc(url)).isEmpty();
+ exists = typeLoader->fileExists(localDirectoryPath, urlsToTry[i]);
if (exists) {
- qmlUrl = url;
+#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
+ // don't let function.qml confuse the use of "new Function(...)" for example.
+ if (!QQml_isFileCaseCorrect(localDirectoryPath + urlsToTry[i])) {
+ exists = false;
+ if (errors) {
+ QQmlError caseError;
+ caseError.setDescription(QLatin1String("File name case mismatch"));
+ errors->append(caseError);
+ }
+ break;
+ }
+#else
+ Q_UNUSED(errors);
+#endif
+ qmlUrl = url + urlsToTry[i];
break;
}
}
@@ -908,7 +923,7 @@ bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedS
for (int i=0; i<imports.count(); ++i) {
const QQmlImportInstance *import = imports.at(i);
if (import->resolveType(typeLoader, type, vmajor, vminor, type_return, base,
- &typeRecursionDetected, registrationType, recursionRestriction)) {
+ &typeRecursionDetected, registrationType, recursionRestriction, errors)) {
if (qmlCheckTypes()) {
// check for type clashes
for (int j = i+1; j<imports.count(); ++j) {
@@ -1421,6 +1436,7 @@ QQmlImportInstance *QQmlImportsPrivate::addImportToNamespace(QQmlImportNamespace
QQmlImportInstance *import = new QQmlImportInstance;
import->uri = uri;
import->url = url;
+ import->localDirectoryPath = QQmlFile::urlToLocalFileOrQrc(url);
import->majversion = vmaj;
import->minversion = vmin;
import->isLibrary = (type == QV4::CompiledData::Import::ImportLibrary);
@@ -1759,7 +1775,7 @@ QQmlImportDatabase::QQmlImportDatabase(QQmlEngine *e)
filePluginPath << QLatin1String(".");
// Search order is applicationDirPath(), qrc:/qt-project.org/imports, $QML2_IMPORT_PATH, QLibraryInfo::Qml2ImportsPath
- QString installImportsPath = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath);
+ QString installImportsPath = QLibraryInfo::location(QLibraryInfo::Qml2ImportsPath);
addImportPath(installImportsPath);
// env import paths
@@ -2050,7 +2066,7 @@ bool QQmlImportDatabase::registerPluginTypes(QObject *instance, const QString &b
if (QQmlExtensionPlugin *plugin = qobject_cast<QQmlExtensionPlugin *>(instance)) {
// basepath should point to the directory of the module, not the plugin file itself:
- QQmlExtensionPluginPrivate::get(plugin)->baseUrl = QUrl::fromLocalFile(basePath);
+ QQmlExtensionPluginPrivate::get(plugin)->baseUrl = QQmlImports::urlFromLocalFileOrQrcOrUrl(basePath);
}
iface->registerTypes(moduleId);
diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h
index 2437979ef8..f8c01ed876 100644
--- a/src/qml/qml/qqmlimport_p.h
+++ b/src/qml/qml/qqmlimport_p.h
@@ -78,6 +78,7 @@ struct QQmlImportInstance
{
QString uri; // e.g. QtQuick
QString url; // the base path of the import
+ QString localDirectoryPath; // the base path of the import if it's a local file
int majversion; // the major version imported
int minversion; // the minor version imported
bool isLibrary; // true means that this is not a file import
@@ -94,7 +95,8 @@ struct QQmlImportInstance
int *vmajor, int *vminor, QQmlType* type_return,
QString *base = nullptr, bool *typeRecursionDetected = nullptr,
QQmlType::RegistrationType = QQmlType::AnyRegistrationType,
- QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion) const;
+ QQmlImport::RecursionRestriction recursionRestriction = QQmlImport::PreventRecursion,
+ QList<QQmlError> *errors = nullptr) const;
};
class QQmlImportNamespace
diff --git a/src/qml/qml/qqmlincubator.cpp b/src/qml/qml/qqmlincubator.cpp
index e18ce71902..39da550d63 100644
--- a/src/qml/qml/qqmlincubator.cpp
+++ b/src/qml/qml/qqmlincubator.cpp
@@ -282,8 +282,9 @@ void QQmlIncubatorPrivate::incubate(QQmlInstantiationInterrupt &i)
if (!vmeGuard.isOK()) {
QQmlError error;
+ error.setMessageType(QtInfoMsg);
error.setUrl(compilationUnit->url());
- error.setDescription(QQmlComponent::tr("Object destroyed during incubation"));
+ error.setDescription(QQmlComponent::tr("Object or context destroyed during incubation"));
errors << error;
progress = QQmlIncubatorPrivate::Completed;
diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp
index 93ec9421ed..9f2a96d5d9 100644
--- a/src/qml/qml/qqmljavascriptexpression.cpp
+++ b/src/qml/qml/qqmljavascriptexpression.cpp
@@ -93,8 +93,7 @@ void QQmlDelayedError::catchJavaScriptException(QV4::ExecutionEngine *engine)
QQmlJavaScriptExpression::QQmlJavaScriptExpression()
- : m_error(nullptr),
- m_context(nullptr),
+ : m_context(nullptr),
m_prevExpression(nullptr),
m_nextExpression(nullptr),
m_v4Function(nullptr)
@@ -247,6 +246,9 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b
while (QQmlJavaScriptExpressionGuard *g = capture.guards.takeFirst())
g->Delete();
+ if (!watcher.wasDeleted())
+ setTranslationsCaptured(capture.translationCaptured);
+
ep->propertyCapture = lastPropertyCapture;
return result->asReturnedValue();
@@ -392,7 +394,7 @@ QQmlDelayedError *QQmlJavaScriptExpression::delayedError()
{
if (!m_error)
m_error = new QQmlDelayedError;
- return m_error;
+ return m_error.data();
}
QV4::ReturnedValue
@@ -458,7 +460,7 @@ void QQmlJavaScriptExpression::setupFunction(QV4::ExecutionContext *qmlContext,
setCompilationUnit(m_v4Function->compilationUnit);
}
-void QQmlJavaScriptExpression::setCompilationUnit(QV4::CompiledData::CompilationUnit *compilationUnit)
+void QQmlJavaScriptExpression::setCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit)
{
m_compilationUnit = compilationUnit;
}
diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h
index 01af3b89ca..de3fba0774 100644
--- a/src/qml/qml/qqmljavascriptexpression_p.h
+++ b/src/qml/qml/qqmljavascriptexpression_p.h
@@ -162,7 +162,7 @@ protected:
}
void setupFunction(QV4::ExecutionContext *qmlContext, QV4::Function *f);
- void setCompilationUnit(QV4::CompiledData::CompilationUnit *compilationUnit);
+ void setCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit);
// We store some flag bits in the following flag pointers.
// activeGuards:flag1 - notifyOnValueChanged
@@ -171,13 +171,17 @@ protected:
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(); }
+
private:
friend class QQmlContextData;
friend class QQmlPropertyCapture;
friend void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *, void **);
friend class QQmlTranslationBinding;
- QQmlDelayedError *m_error;
+ // m_error:flag1 translationsCapturedDuringEvaluation
+ QFlagPointer<QQmlDelayedError> m_error;
QQmlContextData *m_context;
QQmlJavaScriptExpression **m_prevExpression;
@@ -208,12 +212,14 @@ public:
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 captureTranslation() { translationCaptured = true; }
QQmlEngine *engine;
QQmlJavaScriptExpression *expression;
QQmlJavaScriptExpression::DeleteWatcher *watcher;
QFieldList<QQmlJavaScriptExpressionGuard, &QQmlJavaScriptExpressionGuard::next> guards;
QStringList *errorString;
+ bool translationCaptured = false;
};
QQmlJavaScriptExpression::DeleteWatcher::DeleteWatcher(QQmlJavaScriptExpression *e)
@@ -260,18 +266,17 @@ void QQmlJavaScriptExpression::setScopeObject(QObject *v)
bool QQmlJavaScriptExpression::hasError() const
{
- return m_error && m_error->isValid();
+ return !m_error.isNull() && m_error->isValid();
}
bool QQmlJavaScriptExpression::hasDelayedError() const
{
- return m_error;
+ return !m_error.isNull();
}
inline void QQmlJavaScriptExpression::clearError()
{
- if (m_error)
- delete m_error;
+ delete m_error.data();
m_error = nullptr;
}
diff --git a/src/qml/qml/qqmllist.cpp b/src/qml/qml/qqmllist.cpp
index 918b181195..656a8a470b 100644
--- a/src/qml/qml/qqmllist.cpp
+++ b/src/qml/qml/qqmllist.cpp
@@ -109,8 +109,6 @@ Attempting to add objects of the incorrect type to a list property will fail.
Like with normal lists, when accessing a list element by index, it is the callers responsibility to ensure
that it does not request an out of range element using the count() method before calling at().
-
-The \l {Qt Quick 1} version of this class is named QDeclarativeListReference.
*/
/*!
@@ -367,10 +365,7 @@ Q_PROPERTY(QQmlListProperty<Fruit> fruit READ fruit)
QML list properties are type-safe - in this case \c {Fruit} is a QObject type that
\c {Apple}, \c {Orange} and \c {Banana} all derive from.
-The \l {Qt Quick 1} version of this class is named QDeclarativeListProperty.
-
\sa {Extending QML - Object and List Property Types Example}
-
*/
/*!
diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp
index 3fbe3df2ab..2f769c1aef 100644
--- a/src/qml/qml/qqmllistwrapper.cpp
+++ b/src/qml/qml/qqmllistwrapper.cpp
@@ -74,7 +74,7 @@ ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, QObject *object, i
Scope scope(engine);
- Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocObject<QmlListWrapper>());
+ Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocate<QmlListWrapper>());
r->d()->object = object;
r->d()->propertyType = propType;
void *args[] = { &r->d()->property(), nullptr };
@@ -86,7 +86,7 @@ ReturnedValue QmlListWrapper::create(ExecutionEngine *engine, const QQmlListProp
{
Scope scope(engine);
- Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocObject<QmlListWrapper>());
+ Scoped<QmlListWrapper> r(scope, engine->memoryManager->allocate<QmlListWrapper>());
r->d()->object = prop.object;
r->d()->property() = prop;
r->d()->propertyType = propType;
@@ -102,68 +102,73 @@ QVariant QmlListWrapper::toVariant() const
}
-ReturnedValue QmlListWrapper::get(const Managed *m, String *name, bool *hasProperty)
+ReturnedValue QmlListWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<QmlListWrapper>());
const QmlListWrapper *w = static_cast<const QmlListWrapper *>(m);
QV4::ExecutionEngine *v4 = w->engine();
- if (name->equals(v4->id_length()) && !w->d()->object.isNull()) {
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0;
- return Primitive::fromUInt32(count).asReturnedValue();
- }
-
- uint idx = name->asArrayIndex();
- if (idx != UINT_MAX)
- return getIndexed(m, idx, hasProperty);
-
- return Object::get(m, name, hasProperty);
-}
-
-ReturnedValue QmlListWrapper::getIndexed(const Managed *m, uint index, bool *hasProperty)
-{
- Q_UNUSED(hasProperty);
-
- Q_ASSERT(m->as<QmlListWrapper>());
- const QmlListWrapper *w = static_cast<const QmlListWrapper *>(m);
- QV4::ExecutionEngine *v4 = w->engine();
+ if (index < count && w->d()->property().at) {
+ if (hasProperty)
+ *hasProperty = true;
+ return QV4::QObjectWrapper::wrap(v4, w->d()->property().at(&w->d()->property(), index));
+ }
- quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0;
- if (index < count && w->d()->property().at) {
if (hasProperty)
- *hasProperty = true;
- return QV4::QObjectWrapper::wrap(v4, w->d()->property().at(&w->d()->property(), index));
+ *hasProperty = false;
+ return Value::undefinedValue().asReturnedValue();
+ } else if (id.isString()) {
+ if (id == v4->id_length()->propertyKey() && !w->d()->object.isNull()) {
+ quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0;
+ return Value::fromUInt32(count).asReturnedValue();
+ }
}
- if (hasProperty)
- *hasProperty = false;
- return Primitive::undefinedValue().asReturnedValue();
+ return Object::virtualGet(m, id, receiver, hasProperty);
}
-bool QmlListWrapper::put(Managed *m, String *name, const Value &value)
+bool QmlListWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
// doesn't do anything. Should we throw?
Q_UNUSED(m);
- Q_UNUSED(name);
+ Q_UNUSED(id);
Q_UNUSED(value);
+ Q_UNUSED(receiver);
return false;
}
-void QmlListWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs)
+struct QmlListWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
{
- name->setM(nullptr);
- *index = UINT_MAX;
- Q_ASSERT(m->as<QmlListWrapper>());
- QmlListWrapper *w = static_cast<QmlListWrapper *>(m);
+ ~QmlListWrapperOwnPropertyKeyIterator() override = default;
+ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
+
+};
+
+PropertyKey QmlListWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
+{
+ const QmlListWrapper *w = static_cast<const QmlListWrapper *>(o);
+
quint32 count = w->d()->property().count ? w->d()->property().count(&w->d()->property()) : 0;
- if (it->arrayIndex < count) {
- *index = it->arrayIndex;
- ++it->arrayIndex;
- *attrs = QV4::Attr_Data;
- p->value = QV4::QObjectWrapper::wrap(w->engine(), w->d()->property().at(&w->d()->property(), *index));
- return;
+ if (arrayIndex < count) {
+ uint index = arrayIndex;
+ ++arrayIndex;
+ if (attrs)
+ *attrs = QV4::Attr_Data;
+ if (pd)
+ pd->value = QV4::QObjectWrapper::wrap(w->engine(), w->d()->property().at(&w->d()->property(), index));
+ return PropertyKey::fromArrayIndex(index);
}
- return QV4::Object::advanceIterator(m, it, name, index, p, attrs);
+
+ return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
+}
+
+OwnPropertyKeyIterator *QmlListWrapper::virtualOwnPropertyKeys(const Object *m, Value *target)
+{
+ *target = *m;
+ return new QmlListWrapperOwnPropertyKeyIterator;
}
void PropertyListPrototype::init(ExecutionEngine *)
diff --git a/src/qml/qml/qqmllistwrapper_p.h b/src/qml/qml/qqmllistwrapper_p.h
index e02831c8d1..c185122ad9 100644
--- a/src/qml/qml/qqmllistwrapper_p.h
+++ b/src/qml/qml/qqmllistwrapper_p.h
@@ -93,10 +93,9 @@ struct Q_QML_EXPORT QmlListWrapper : Object
QVariant toVariant() const;
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty);
- static bool put(Managed *m, String *name, const Value &value);
- static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
+ 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 OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
};
struct PropertyListPrototype : Object
diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp
index b4f7092a22..2b17037df0 100644
--- a/src/qml/qml/qqmllocale.cpp
+++ b/src/qml/qml/qqmllocale.cpp
@@ -77,7 +77,7 @@ static bool isLocaleObject(const QV4::Value &val)
void QQmlDateExtension::registerExtension(QV4::ExecutionEngine *engine)
{
- engine->datePrototype()->defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString);
+ engine->datePrototype()->defineDefaultProperty(engine->id_toLocaleString(), method_toLocaleString);
engine->datePrototype()->defineDefaultProperty(QStringLiteral("toLocaleTimeString"), method_toLocaleTimeString);
engine->datePrototype()->defineDefaultProperty(QStringLiteral("toLocaleDateString"), method_toLocaleDateString);
engine->dateCtor()->defineDefaultProperty(QStringLiteral("fromLocaleString"), method_fromLocaleString);
@@ -350,7 +350,7 @@ ReturnedValue QQmlDateExtension::method_timeZoneUpdated(const QV4::FunctionObjec
if (argc != 0)
THROW_ERROR("Locale: Date.timeZoneUpdated(): Invalid arguments");
- QV4::DatePrototype::timezoneUpdated();
+ QV4::DatePrototype::timezoneUpdated(scope.engine);
RETURN_UNDEFINED();
}
@@ -360,7 +360,7 @@ ReturnedValue QQmlDateExtension::method_timeZoneUpdated(const QV4::FunctionObjec
void QQmlNumberExtension::registerExtension(QV4::ExecutionEngine *engine)
{
- engine->numberPrototype()->defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString);
+ engine->numberPrototype()->defineDefaultProperty(engine->id_toLocaleString(), method_toLocaleString);
engine->numberPrototype()->defineDefaultProperty(QStringLiteral("toLocaleCurrencyString"), method_toLocaleCurrencyString);
engine->numberCtor()->defineDefaultProperty(QStringLiteral("fromLocaleString"), method_fromLocaleString);
}
@@ -512,7 +512,7 @@ ReturnedValue QQmlLocaleData::method_get_weekDays(const QV4::FunctionObject *b,
int day = days.at(i);
if (day == 7) // JS Date days in range 0(Sunday) to 6(Saturday)
day = 0;
- result->arrayPut(i, QV4::Primitive::fromInt32(day));
+ result->arrayPut(i, QV4::Value::fromInt32(day));
}
result->setArrayLengthUnchecked(days.size());
@@ -825,10 +825,10 @@ QV4::ReturnedValue QQmlLocale::wrap(ExecutionEngine *v4, const QLocale &locale)
{
QV4::Scope scope(v4);
QV4LocaleDataDeletable *d = localeV4Data(scope.engine);
- QV4::Scoped<QQmlLocaleData> wrapper(scope, v4->memoryManager->allocObject<QQmlLocaleData>());
+ QV4::Scoped<QQmlLocaleData> wrapper(scope, v4->memoryManager->allocate<QQmlLocaleData>());
*wrapper->d()->locale = locale;
QV4::ScopedObject p(scope, d->prototype.value());
- wrapper->setPrototype(p);
+ wrapper->setPrototypeOf(p);
return wrapper.asReturnedValue();
}
diff --git a/src/qml/qml/qqmllocale_p.h b/src/qml/qml/qqmllocale_p.h
index 8341b1f555..859c36e11b 100644
--- a/src/qml/qml/qqmllocale_p.h
+++ b/src/qml/qml/qqmllocale_p.h
@@ -58,6 +58,8 @@
#include <private/qqmlglobal_p.h>
#include <private/qv4object_p.h>
+QT_REQUIRE_CONFIG(qml_locale);
+
QT_BEGIN_NAMESPACE
diff --git a/src/qml/qml/qqmlloggingcategory.cpp b/src/qml/qml/qqmlloggingcategory.cpp
index 597fe458fa..b59a26e17e 100644
--- a/src/qml/qml/qqmlloggingcategory.cpp
+++ b/src/qml/qml/qqmlloggingcategory.cpp
@@ -59,6 +59,7 @@
LoggingCategory {
id: category
name: "com.qt.category"
+ defaultLogLevel: LoggingCategory.Warning
}
Component.onCompleted: {
@@ -84,6 +85,17 @@
\sa QLoggingCategory::categoryName()
*/
+/*!
+ \qmlproperty enumeration QtQml::LoggingCategory::defaultLogLevel
+ \since 5.12
+
+ Holds the default log level of the logging category. By default it is
+ created with the LoggingCategory.Debug log level.
+
+ \note This property needs to be set when declaring the LoggingCategory
+ and cannot be changed later.
+*/
+
QQmlLoggingCategory::QQmlLoggingCategory(QObject *parent)
: QObject(parent)
, m_initialized(false)
@@ -99,6 +111,11 @@ QString QQmlLoggingCategory::name() const
return QString::fromUtf8(m_name);
}
+QQmlLoggingCategory::DefaultLogLevel QQmlLoggingCategory::defaultLogLevel() const
+{
+ return m_defaultLogLevel;
+}
+
QLoggingCategory *QQmlLoggingCategory::category() const
{
return m_category.data();
@@ -111,10 +128,25 @@ void QQmlLoggingCategory::classBegin()
void QQmlLoggingCategory::componentComplete()
{
m_initialized = true;
- if (m_name.isNull())
+ if (m_name.isNull()) {
qmlWarning(this) << QLatin1String("Declaring the name of the LoggingCategory is mandatory and cannot be changed later !");
+ } else {
+ QScopedPointer<QLoggingCategory> category(new QLoggingCategory(m_name.constData(), QtMsgType(m_defaultLogLevel)));
+ m_category.swap(category);
+ }
}
+void QQmlLoggingCategory::setDefaultLogLevel(DefaultLogLevel defaultLogLevel)
+{
+ if (m_initialized) {
+ qmlWarning(this) << QLatin1String("The defaultLogLevel of a LoggingCategory cannot be changed after the Item is created");
+ return;
+ }
+
+ m_defaultLogLevel = defaultLogLevel;
+}
+
+
void QQmlLoggingCategory::setName(const QString &name)
{
if (m_initialized) {
@@ -123,8 +155,6 @@ void QQmlLoggingCategory::setName(const QString &name)
}
m_name = name.toUtf8();
- QScopedPointer<QLoggingCategory> category(new QLoggingCategory(m_name.constData()));
- m_category.swap(category);
}
#include "moc_qqmlloggingcategory_p.cpp"
diff --git a/src/qml/qml/qqmlloggingcategory_p.h b/src/qml/qml/qqmlloggingcategory_p.h
index 544db1fe33..ece06e04b4 100644
--- a/src/qml/qml/qqmlloggingcategory_p.h
+++ b/src/qml/qml/qqmlloggingcategory_p.h
@@ -65,11 +65,23 @@ class QQmlLoggingCategory : public QObject, public QQmlParserStatus
Q_INTERFACES(QQmlParserStatus)
Q_PROPERTY(QString name READ name WRITE setName)
+ Q_PROPERTY(DefaultLogLevel defaultLogLevel READ defaultLogLevel WRITE setDefaultLogLevel REVISION 1)
public:
+ enum DefaultLogLevel {
+ Debug = QtDebugMsg,
+ Info = QtInfoMsg,
+ Warning = QtWarningMsg,
+ Critical = QtCriticalMsg,
+ Fatal = QtFatalMsg
+ };
+ Q_ENUM(DefaultLogLevel);
+
QQmlLoggingCategory(QObject *parent = nullptr);
virtual ~QQmlLoggingCategory();
+ DefaultLogLevel defaultLogLevel() const;
+ void setDefaultLogLevel(DefaultLogLevel defaultLogLevel);
QString name() const;
void setName(const QString &name);
@@ -81,6 +93,7 @@ public:
private:
QByteArray m_name;
QScopedPointer<QLoggingCategory> m_category;
+ DefaultLogLevel m_defaultLogLevel = Debug;
bool m_initialized;
};
diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp
index 2705321e77..e34f7a407f 100644
--- a/src/qml/qml/qqmlmetatype.cpp
+++ b/src/qml/qml/qqmlmetatype.cpp
@@ -137,6 +137,15 @@ private:
QStringList *typeRegistrationFailures = nullptr;
};
+struct EnumInfo {
+ QStringList path;
+ QString metaObjectName;
+ QString enumName;
+ QString enumKey;
+ QString metaEnumScope;
+ bool scoped;
+};
+
class QQmlTypeModulePrivate
{
public:
@@ -218,6 +227,7 @@ public:
int attachedPropertiesId;
int propertyValueSourceCast;
int propertyValueInterceptorCast;
+ bool registerEnumClassesUnscoped;
};
struct QQmlSingletonTypeData
@@ -270,6 +280,9 @@ public:
QVector<PropertyCacheByMinorVersion> propertyCaches;
QQmlPropertyCache *propertyCacheForMinorVersion(int minorVersion) const;
void setPropertyCacheForMinorVersion(int minorVersion, QQmlPropertyCache *cache);
+private:
+ void createListOfPossibleConflictingItems(const QMetaObject *metaObject, QList<EnumInfo> &enumInfoList, QStringList path) const;
+ void createEnumConflictReport(const QMetaObject *metaObject, const QString &conflictingKey) const;
};
void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv)
@@ -288,7 +301,14 @@ void QQmlMetaTypeData::registerType(QQmlTypePrivate *priv)
void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e)
{
if (scriptCallback && scriptApi(e).isUndefined()) {
- setScriptApi(e, scriptCallback(e, e));
+ QJSValue value = scriptCallback(e, e);
+ if (value.isQObject()) {
+ QObject *o = value.toQObject();
+ // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj)
+ // should behave identically to QML singleton types.
+ e->setContextForObject(o, new QQmlContext(e->rootContext(), e));
+ }
+ setScriptApi(e, value);
} else if (qobjectCallback && !qobjectApi(e)) {
QObject *o = qobjectCallback(e, e);
setQObjectApi(e, o);
@@ -297,6 +317,9 @@ void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e)
}
// if this object can use a property cache, create it now
QQmlData::ensurePropertyCache(e, o);
+ // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj)
+ // should behave identically to QML singleton types.
+ e->setContextForObject(o, new QQmlContext(e->rootContext(), e));
} else if (!url.isEmpty() && !qobjectApi(e)) {
QQmlComponent component(e, url, QQmlComponent::PreferSynchronous);
QObject *o = component.beginCreate(e->rootContext());
@@ -359,6 +382,7 @@ QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type)
extraData.cd->attachedPropertiesType = nullptr;
extraData.cd->propertyValueSourceCast = -1;
extraData.cd->propertyValueInterceptorCast = -1;
+ extraData.cd->registerEnumClassesUnscoped = true;
break;
case QQmlType::SingletonType:
case QQmlType::CompositeSingletonType:
@@ -485,9 +509,17 @@ QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQm
d->extraData.cd->propertyValueInterceptorCast = type.valueInterceptorCast;
d->extraData.cd->extFunc = type.extensionObjectCreate;
d->extraData.cd->customParser = type.customParser;
+ d->extraData.cd->registerEnumClassesUnscoped = true;
if (type.extensionMetaObject)
d->extraData.cd->extMetaObject = type.extensionMetaObject;
+
+ // Check if the user wants only scoped enum classes
+ if (d->baseMetaObject) {
+ auto indexOfClassInfo = d->baseMetaObject->indexOfClassInfo("RegisterEnumClassesUnscoped");
+ if (indexOfClassInfo != -1 && QString::fromUtf8(d->baseMetaObject->classInfo(indexOfClassInfo).value()) == QLatin1String("false"))
+ d->extraData.cd->registerEnumClassesUnscoped = false;
+ }
}
QQmlType::QQmlType(QQmlMetaTypeData *data, const QString &elementName, const QQmlPrivate::RegisterCompositeType &type)
@@ -600,7 +632,7 @@ QQmlType QQmlType::resolveCompositeBaseType(QQmlEnginePrivate *engine) const
Q_ASSERT(isComposite());
if (!engine || !d)
return QQmlType();
- QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()), QQmlRefPointer<QQmlTypeData>::Adopt);
+ QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()));
if (td.isNull() || !td->isComplete())
return QQmlType();
QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit();
@@ -614,11 +646,11 @@ QQmlPropertyCache *QQmlType::compositePropertyCache(QQmlEnginePrivate *engine) c
Q_ASSERT(isComposite());
if (!engine)
return nullptr;
- QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()), QQmlRefPointer<QQmlTypeData>::Adopt);
+ QQmlRefPointer<QQmlTypeData> td(engine->typeLoader.getType(sourceUrl()));
if (td.isNull() || !td->isComplete())
return nullptr;
QV4::CompiledData::CompilationUnit *compilationUnit = td->compilationUnit();
- return compilationUnit->rootPropertyCache();
+ return compilationUnit->rootPropertyCache().data();
}
static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo,
@@ -808,7 +840,7 @@ void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const
{
// Add any enum values defined by 'related' classes
if (metaObject->d.relatedMetaObjects) {
- const QMetaObject * const *related = metaObject->d.relatedMetaObjects;
+ const auto *related = metaObject->d.relatedMetaObjects;
if (related) {
while (*related)
insertEnums(*related++);
@@ -824,7 +856,13 @@ void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const
for (int jj = 0; jj < e.keyCount(); ++jj) {
const QString key = QString::fromUtf8(e.key(jj));
const int value = e.value(jj);
- enums.insert(key, value);
+ if (!isScoped || (regType == QQmlType::CppType && extraData.cd->registerEnumClassesUnscoped)) {
+ if (enums.contains(key)) {
+ qWarning("Previously registered enum will be overwritten due to name clash: %s.%s", metaObject->className(), key.toUtf8().constData());
+ createEnumConflictReport(metaObject, key);
+ }
+ enums.insert(key, value);
+ }
if (isScoped)
scoped->insert(key, value);
}
@@ -836,6 +874,59 @@ void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const
}
}
+void QQmlTypePrivate::createListOfPossibleConflictingItems(const QMetaObject *metaObject, QList<EnumInfo> &enumInfoList, QStringList path) const
+{
+ path.append(QString::fromUtf8(metaObject->className()));
+
+ if (metaObject->d.relatedMetaObjects) {
+ const auto *related = metaObject->d.relatedMetaObjects;
+ if (related) {
+ while (*related)
+ createListOfPossibleConflictingItems(*related++, enumInfoList, path);
+ }
+ }
+
+ for (int ii = 0; ii < metaObject->enumeratorCount(); ++ii) {
+ const auto e = metaObject->enumerator(ii);
+
+ for (int jj = 0; jj < e.keyCount(); ++jj) {
+ const QString key = QString::fromUtf8(e.key(jj));
+
+ EnumInfo enumInfo;
+ enumInfo.metaObjectName = QString::fromUtf8(metaObject->className());
+ enumInfo.enumName = QString::fromUtf8(e.name());
+ enumInfo.enumKey = key;
+ enumInfo.scoped = e.isScoped();
+ enumInfo.path = path;
+ enumInfo.metaEnumScope = QString::fromUtf8(e.scope());
+ enumInfoList.append(enumInfo);
+ }
+ }
+}
+
+void QQmlTypePrivate::createEnumConflictReport(const QMetaObject *metaObject, const QString &conflictingKey) const
+{
+ QList<EnumInfo> enumInfoList;
+
+ if (baseMetaObject) // prefer baseMetaObject if available
+ metaObject = baseMetaObject;
+
+ if (!metaObject) { // If there is no metaObject at all return early
+ qWarning() << "No meta object information available. Skipping conflict analysis.";
+ return;
+ }
+
+ createListOfPossibleConflictingItems(metaObject, enumInfoList, QStringList());
+
+ qWarning().noquote() << QLatin1String("Possible conflicting items:");
+ // find items with conflicting key
+ for (const auto i : enumInfoList) {
+ if (i.enumKey == conflictingKey)
+ qWarning().noquote().nospace() << " " << i.metaObjectName << "." << i.enumName << "." << i.enumKey << " from scope "
+ << i.metaEnumScope << " injected by " << i.path.join(QLatin1String("->"));
+ }
+}
+
void QQmlTypePrivate::insertEnumsFromPropertyCache(const QQmlPropertyCache *cache) const
{
const QMetaObject *cppMetaObject = cache->firstCppMetaObject();
@@ -865,7 +956,7 @@ QQmlPropertyCache *QQmlTypePrivate::propertyCacheForMinorVersion(int minorVersio
{
for (int i = 0; i < propertyCaches.count(); ++i)
if (propertyCaches.at(i).minorVersion == minorVersion)
- return propertyCaches.at(i).cache;
+ return propertyCaches.at(i).cache.data();
return nullptr;
}
@@ -1877,6 +1968,23 @@ void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor)
p->maxMinorVersion = qMax(p->maxMinorVersion, versionMinor);
}
+//From qqml.h
+int qmlTypeId(const char *uri, int versionMajor, int versionMinor, const char *qmlName)
+{
+ QMutexLocker lock(metaTypeDataLock());
+ QQmlMetaTypeData *data = metaTypeData();
+
+ QQmlTypeModule *module = getTypeModule(QString::fromUtf8(uri), versionMajor, data);
+ if (!module)
+ return -1;
+
+ QQmlType type = module->type(QHashedStringRef(QString::fromUtf8(qmlName)), versionMinor);
+ if (!type.isValid())
+ return -1;
+
+ return type.index();
+}
+
bool QQmlMetaType::namespaceContainsRegistrations(const QString &uri, int majorVersion)
{
const QQmlMetaTypeData *data = metaTypeData();
@@ -2248,19 +2356,25 @@ QQmlType QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStrin
}
/*!
- Returns the type (if any) that corresponds to the QVariant::Type \a userType.
- Returns null if no type is registered.
+ Returns the type (if any) that corresponds to \a typeId. Depending on \a category, the
+ \a typeId is interpreted either as QVariant::Type or as QML type id returned by one of the
+ qml type registration functions. Returns null if no type is registered.
*/
-QQmlType QQmlMetaType::qmlType(int userType)
+QQmlType QQmlMetaType::qmlType(int typeId, TypeIdCategory category)
{
QMutexLocker lock(metaTypeDataLock());
QQmlMetaTypeData *data = metaTypeData();
- QQmlTypePrivate *type = data->idToType.value(userType);
- if (type && type->typeId == userType)
- return QQmlType(type);
- else
- return QQmlType();
+ if (category == TypeIdCategory::MetaType) {
+ QQmlTypePrivate *type = data->idToType.value(typeId);
+ if (type && type->typeId == typeId)
+ return QQmlType(type);
+ } else if (category == TypeIdCategory::QmlType) {
+ QQmlType type = data->types.value(typeId);
+ if (type.isValid())
+ return type;
+ }
+ return QQmlType();
}
/*!
diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h
index bcaf62d6ba..4a5e4ba266 100644
--- a/src/qml/qml/qqmlmetatype_p.h
+++ b/src/qml/qml/qqmlmetatype_p.h
@@ -91,11 +91,16 @@ public:
static QList<QQmlType> qmlSingletonTypes();
static QList<QQmlType> qmlAllTypes();
+ enum class TypeIdCategory {
+ MetaType,
+ QmlType
+ };
+
static QQmlType qmlType(const QString &qualifiedName, int, int);
static QQmlType qmlType(const QHashedStringRef &name, const QHashedStringRef &module, int, int);
static QQmlType qmlType(const QMetaObject *);
static QQmlType qmlType(const QMetaObject *metaObject, const QHashedStringRef &module, int version_major, int version_minor);
- static QQmlType qmlType(int);
+ static QQmlType qmlType(int typeId, TypeIdCategory category = TypeIdCategory::MetaType);
static QQmlType qmlType(const QUrl &unNormalizedUrl, bool includeNonFileImports = false);
static QQmlPropertyCache *propertyCache(const QMetaObject *metaObject);
diff --git a/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp b/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp
index 1680253d73..c14a91c1fa 100644
--- a/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp
+++ b/src/qml/qml/qqmlnetworkaccessmanagerfactory.cpp
@@ -80,8 +80,6 @@ QT_BEGIN_NAMESPACE
For more information about signals and threads, see
\l {Threads and QObjects} and \l {Signals and Slots Across Threads}.
- The \l {Qt Quick 1} version of this class is named QDeclarativeNetworkAccessManagerFactory.
-
\sa {C++ Extensions: Network Access Manager Factory Example}{Network Access Manager Factory Example}
*/
diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp
index 5aaf79c9e5..c181d791f5 100644
--- a/src/qml/qml/qqmlobjectcreator.cpp
+++ b/src/qml/qml/qqmlobjectcreator.cpp
@@ -71,7 +71,7 @@ struct ActiveOCRestorer
};
}
-QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlContextData *creationContext,
+QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlContextData *creationContext,
QQmlIncubatorPrivate *incubator)
: phase(Startup)
, compilationUnit(compilationUnit)
@@ -99,7 +99,7 @@ QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::Compil
}
}
-QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState)
+QQmlObjectCreator::QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState)
: phase(Startup)
, compilationUnit(compilationUnit)
, resolvedTypes(compilationUnit->resolvedTypes)
@@ -120,7 +120,7 @@ void QQmlObjectCreator::init(QQmlContextData *providedParentContext)
if (compilationUnit && !compilationUnit->engine)
compilationUnit->linkToEngine(v4);
- qmlUnit = compilationUnit->data;
+ qmlUnit = compilationUnit->unitData();
context = nullptr;
_qobject = nullptr;
_scopeObject = nullptr;
@@ -166,7 +166,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
if (subComponentIndex == -1) {
objectToCreate = /*root object*/0;
} else {
- const QV4::CompiledData::Object *compObj = qmlUnit->objectAt(subComponentIndex);
+ const QV4::CompiledData::Object *compObj = compilationUnit->objectAt(subComponentIndex);
objectToCreate = compObj->bindingTable()->value.objectIndex;
}
@@ -193,8 +193,8 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI
context->importedScripts.set(v4, scripts);
QV4::ScopedValue v(scope);
for (int i = 0; i < compilationUnit->dependentScripts.count(); ++i) {
- QQmlScriptData *s = compilationUnit->dependentScripts.at(i);
- scripts->putIndexed(i, (v = s->scriptValueForContext(context)));
+ QQmlRefPointer<QQmlScriptData> s = compilationUnit->dependentScripts.at(i);
+ scripts->put(i, (v = s->scriptValueForContext(context)));
}
} else if (sharedState->creationContext) {
context->importedScripts = sharedState->creationContext->importedScripts;
@@ -252,7 +252,7 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance, QQmlData::
Q_ASSERT(!sharedState->allJavaScriptObjects);
sharedState->allJavaScriptObjects = valueScope.alloc(compilationUnit->totalObjectCount);
- QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc(1));
+ QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc());
qSwap(_qmlContext, qmlContext);
@@ -262,7 +262,7 @@ bool QQmlObjectCreator::populateDeferredProperties(QObject *instance, QQmlData::
int objectIndex = deferredData->deferredIdx;
qSwap(_compiledObjectIndex, objectIndex);
- const QV4::CompiledData::Object *obj = qmlUnit->objectAt(_compiledObjectIndex);
+ const QV4::CompiledData::Object *obj = compilationUnit->objectAt(_compiledObjectIndex);
qSwap(_compiledObject, obj);
qSwap(_ddata, declarativeData);
@@ -312,7 +312,7 @@ bool QQmlObjectCreator::populateDeferredBinding(const QQmlProperty &qmlProperty,
if (!sharedState->allJavaScriptObjects)
sharedState->allJavaScriptObjects = valueScope.alloc(compilationUnit->totalObjectCount);
- QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc(1));
+ QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc());
qSwap(_qmlContext, qmlContext);
@@ -322,7 +322,7 @@ bool QQmlObjectCreator::populateDeferredBinding(const QQmlProperty &qmlProperty,
int objectIndex = deferredData->deferredIdx;
qSwap(_compiledObjectIndex, objectIndex);
- const QV4::CompiledData::Object *obj = qmlUnit->objectAt(_compiledObjectIndex);
+ const QV4::CompiledData::Object *obj = compilationUnit->objectAt(_compiledObjectIndex);
qSwap(_compiledObject, obj);
qSwap(_ddata, declarativeData);
@@ -373,7 +373,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
propertyType = QMetaType::Int;
} else {
// ### This should be resolved earlier at compile time and the binding value should be changed accordingly.
- QVariant value = binding->valueAsString(qmlUnit);
+ QVariant value = binding->valueAsString(compilationUnit.data());
bool ok = QQmlPropertyPrivate::write(_qobject, *property, value, context);
Q_ASSERT(ok);
Q_UNUSED(ok);
@@ -384,10 +384,10 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
switch (propertyType) {
case QMetaType::QVariant: {
if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- double n = binding->valueAsNumber();
+ double n = binding->valueAsNumber(compilationUnit->constants);
if (double(int(n)) == n) {
if (property->isVarProperty()) {
- _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Primitive::fromInt32(int(n)));
+ _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Value::fromInt32(int(n)));
} else {
int i = int(n);
QVariant value(i);
@@ -395,7 +395,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
}
} else {
if (property->isVarProperty()) {
- _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Primitive::fromDouble(n));
+ _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Value::fromDouble(n));
} else {
QVariant value(n);
property->writeProperty(_qobject, &value, propertyWriteFlags);
@@ -403,13 +403,13 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
}
} else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) {
if (property->isVarProperty()) {
- _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Primitive::fromBoolean(binding->valueAsBoolean()));
+ _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Value::fromBoolean(binding->valueAsBoolean()));
} else {
QVariant value(binding->valueAsBoolean());
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
} else {
- QString stringValue = binding->valueAsString(qmlUnit);
+ QString stringValue = binding->valueAsString(compilationUnit.data());
if (property->isVarProperty()) {
QV4::ScopedString s(scope, v4->newString(stringValue));
_vmeMetaObject->setVMEProperty(property->coreIndex(), s);
@@ -422,25 +422,25 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
case QVariant::String: {
Q_ASSERT(binding->evaluatesToString());
- QString value = binding->valueAsString(qmlUnit);
+ QString value = binding->valueAsString(compilationUnit.data());
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::StringList: {
Q_ASSERT(binding->evaluatesToString());
- QStringList value(binding->valueAsString(qmlUnit));
+ QStringList value(binding->valueAsString(compilationUnit.data()));
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::ByteArray: {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String);
- QByteArray value(binding->valueAsString(qmlUnit).toUtf8());
+ QByteArray value(binding->valueAsString(compilationUnit.data()).toUtf8());
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Url: {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String);
- QString string = binding->valueAsString(qmlUnit);
+ QString string = binding->valueAsString(compilationUnit.data());
// Encoded dir-separators defeat QUrl processing - decode them first
string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive);
QUrl value = string.isEmpty() ? QUrl() : compilationUnit->finalUrl().resolved(QUrl(string));
@@ -452,7 +452,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
case QVariant::UInt: {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number);
- double d = binding->valueAsNumber();
+ double d = binding->valueAsNumber(compilationUnit->constants);
uint value = uint(d);
property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
@@ -460,7 +460,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
case QVariant::Int: {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number);
- double d = binding->valueAsNumber();
+ double d = binding->valueAsNumber(compilationUnit->constants);
int value = int(d);
property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
@@ -468,19 +468,19 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
case QMetaType::Float: {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number);
- float value = float(binding->valueAsNumber());
+ float value = float(binding->valueAsNumber(compilationUnit->constants));
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Double: {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number);
- double value = binding->valueAsNumber();
+ double value = binding->valueAsNumber(compilationUnit->constants);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Color: {
bool ok = false;
- uint colorValue = QQmlStringConverters::rgbaFromString(binding->valueAsString(qmlUnit), &ok);
+ uint colorValue = QQmlStringConverters::rgbaFromString(binding->valueAsString(compilationUnit.data()), &ok);
Q_ASSERT(ok);
struct { void *data[4]; } buffer;
if (QQml_valueTypeProvider()->storeValueType(property->propType(), &colorValue, &buffer, sizeof(buffer))) {
@@ -491,21 +491,21 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
#if QT_CONFIG(datestring)
case QVariant::Date: {
bool ok = false;
- QDate value = QQmlStringConverters::dateFromString(binding->valueAsString(qmlUnit), &ok);
+ QDate value = QQmlStringConverters::dateFromString(binding->valueAsString(compilationUnit.data()), &ok);
Q_ASSERT(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Time: {
bool ok = false;
- QTime value = QQmlStringConverters::timeFromString(binding->valueAsString(qmlUnit), &ok);
+ QTime value = QQmlStringConverters::timeFromString(binding->valueAsString(compilationUnit.data()), &ok);
Q_ASSERT(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::DateTime: {
bool ok = false;
- QDateTime value = QQmlStringConverters::dateTimeFromString(binding->valueAsString(qmlUnit), &ok);
+ QDateTime value = QQmlStringConverters::dateTimeFromString(binding->valueAsString(compilationUnit.data()), &ok);
// ### VME compatibility :(
{
const qint64 date = value.date().toJulianDay();
@@ -519,42 +519,42 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
#endif // datestring
case QVariant::Point: {
bool ok = false;
- QPoint value = QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok).toPoint();
+ QPoint value = QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok).toPoint();
Q_ASSERT(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::PointF: {
bool ok = false;
- QPointF value = QQmlStringConverters::pointFFromString(binding->valueAsString(qmlUnit), &ok);
+ QPointF value = QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok);
Q_ASSERT(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Size: {
bool ok = false;
- QSize value = QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok).toSize();
+ QSize value = QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok).toSize();
Q_ASSERT(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::SizeF: {
bool ok = false;
- QSizeF value = QQmlStringConverters::sizeFFromString(binding->valueAsString(qmlUnit), &ok);
+ QSizeF value = QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok);
Q_ASSERT(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::Rect: {
bool ok = false;
- QRect value = QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok).toRect();
+ QRect value = QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok).toRect();
Q_ASSERT(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
break;
case QVariant::RectF: {
bool ok = false;
- QRectF value = QQmlStringConverters::rectFFromString(binding->valueAsString(qmlUnit), &ok);
+ QRectF value = QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok);
Q_ASSERT(ok);
property->writeProperty(_qobject, &value, propertyWriteFlags);
}
@@ -570,7 +570,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
float xp;
float yp;
} vec;
- bool ok = QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(qmlUnit), &vec, sizeof(vec));
+ bool ok = QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec));
Q_ASSERT(ok);
Q_UNUSED(ok);
property->writeProperty(_qobject, &vec, propertyWriteFlags);
@@ -582,7 +582,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
float yp;
float zy;
} vec;
- bool ok = QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(qmlUnit), &vec, sizeof(vec));
+ bool ok = QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec));
Q_ASSERT(ok);
Q_UNUSED(ok);
property->writeProperty(_qobject, &vec, propertyWriteFlags);
@@ -595,7 +595,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
float zy;
float wp;
} vec;
- bool ok = QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(qmlUnit), &vec, sizeof(vec));
+ bool ok = QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec));
Q_ASSERT(ok);
Q_UNUSED(ok);
property->writeProperty(_qobject, &vec, propertyWriteFlags);
@@ -608,7 +608,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
float yp;
float zp;
} vec;
- bool ok = QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(qmlUnit), &vec, sizeof(vec));
+ bool ok = QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec));
Q_ASSERT(ok);
Q_UNUSED(ok);
property->writeProperty(_qobject, &vec, propertyWriteFlags);
@@ -622,12 +622,12 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
if (property->propType() == qMetaTypeId<QList<qreal> >()) {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number);
QList<qreal> value;
- value.append(binding->valueAsNumber());
+ value.append(binding->valueAsNumber(compilationUnit->constants));
property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
} else if (property->propType() == qMetaTypeId<QList<int> >()) {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number);
- double n = binding->valueAsNumber();
+ double n = binding->valueAsNumber(compilationUnit->constants);
QList<int> value;
value.append(int(n));
property->writeProperty(_qobject, &value, propertyWriteFlags);
@@ -640,7 +640,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
break;
} else if (property->propType() == qMetaTypeId<QList<QUrl> >()) {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String);
- QString urlString = binding->valueAsString(qmlUnit);
+ QString urlString = binding->valueAsString(compilationUnit.data());
QUrl u = urlString.isEmpty() ? QUrl()
: compilationUnit->finalUrl().resolved(QUrl(urlString));
QList<QUrl> value;
@@ -650,7 +650,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
} else if (property->propType() == qMetaTypeId<QList<QString> >()) {
Q_ASSERT(binding->evaluatesToString());
QList<QString> value;
- value.append(binding->valueAsString(qmlUnit));
+ value.append(binding->valueAsString(compilationUnit.data()));
property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
} else if (property->propType() == qMetaTypeId<QJSValue>()) {
@@ -658,20 +658,20 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const
if (binding->type == QV4::CompiledData::Binding::Type_Boolean) {
value = QJSValue(binding->valueAsBoolean());
} else if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- double n = binding->valueAsNumber();
+ double n = binding->valueAsNumber(compilationUnit->constants);
if (double(int(n)) == n) {
value = QJSValue(int(n));
} else
value = QJSValue(n);
} else {
- value = QJSValue(binding->valueAsString(qmlUnit));
+ value = QJSValue(binding->valueAsString(compilationUnit.data()));
}
property->writeProperty(_qobject, &value, propertyWriteFlags);
break;
}
// otherwise, try a custom type assignment
- QString stringValue = binding->valueAsString(qmlUnit);
+ QString stringValue = binding->valueAsString(compilationUnit.data());
QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType());
Q_ASSERT(converter);
QVariant value = (*converter)(stringValue);
@@ -784,7 +784,7 @@ void QQmlObjectCreator::setupBindings(bool applyDeferredBindings)
bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProperty, const QV4::CompiledData::Binding *binding)
{
if (binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
- Q_ASSERT(stringAt(qmlUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty());
+ Q_ASSERT(stringAt(compilationUnit->objectAt(binding->value.objectIndex)->inheritedTypeNameIndex).isEmpty());
QV4::CompiledData::ResolvedTypeReference *tr = resolvedTypes.value(binding->propertyNameIndex);
Q_ASSERT(tr);
QQmlType attachedType = tr->type;
@@ -804,13 +804,13 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
// ### resolve this at compile time
if (bindingProperty && bindingProperty->propType() == qMetaTypeId<QQmlScriptString>()) {
- QQmlScriptString ss(binding->valueAsScriptString(qmlUnit), context->asQQmlContext(), _scopeObject);
+ QQmlScriptString ss(binding->valueAsScriptString(compilationUnit.data()), context->asQQmlContext(), _scopeObject);
ss.d.data()->bindingId = binding->type == QV4::CompiledData::Binding::Type_Script ? binding->value.compiledScriptIndex : (quint32)QQmlBinding::Invalid;
ss.d.data()->lineNumber = binding->location.line;
ss.d.data()->columnNumber = binding->location.column;
ss.d.data()->isStringLiteral = binding->type == QV4::CompiledData::Binding::Type_String;
ss.d.data()->isNumberLiteral = binding->type == QV4::CompiledData::Binding::Type_Number;
- ss.d.data()->numberValue = binding->valueAsNumber();
+ ss.d.data()->numberValue = binding->valueAsNumber(compilationUnit->constants);
QQmlPropertyData::WriteFlags propertyWriteFlags = QQmlPropertyData::BypassInterceptor |
QQmlPropertyData::RemoveBindingOnAliasWrite;
@@ -831,7 +831,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
return true;
if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty) {
- const QV4::CompiledData::Object *obj = qmlUnit->objectAt(binding->value.objectIndex);
+ const QV4::CompiledData::Object *obj = compilationUnit->objectAt(binding->value.objectIndex);
if (stringAt(obj->inheritedTypeNameIndex).isEmpty()) {
QObject *groupObject = nullptr;
@@ -876,7 +876,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
&& !_valueTypeProperty)
QQmlPropertyPrivate::removeBinding(_bindingTarget, QQmlPropertyIndex(bindingProperty->coreIndex()));
- if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->containsTranslations()) {
+ if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) {
if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression) {
QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex];
int signalIndex = _propertyCache->methodIndexToSignalIndex(bindingProperty->coreIndex());
@@ -898,7 +898,7 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper
targetProperty = _valueTypeProperty;
subprop = bindingProperty;
}
- if (binding->containsTranslations()) {
+ if (binding->isTranslationBinding()) {
qmlBinding = QQmlBinding::createTranslationBinding(compilationUnit, binding, _scopeObject, context);
} else {
QV4::Function *runtimeFunction = compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex];
@@ -1130,7 +1130,7 @@ void QQmlObjectCreator::createQmlContext()
QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isContextObject)
{
- const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index);
+ const QV4::CompiledData::Object *obj = compilationUnit->objectAt(index);
QQmlObjectCreationProfiler profiler(sharedState->profiler.profiler, obj);
ActiveOCRestorer ocRestorer(this, QQmlEnginePrivate::get(engine));
@@ -1144,9 +1144,9 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
if (obj->flags & QV4::CompiledData::Object::IsComponent) {
isComponent = true;
- QQmlComponent *component = new QQmlComponent(engine, compilationUnit, index, parent);
+ QQmlComponent *component = new QQmlComponent(engine, compilationUnit.data(), index, parent);
Q_QML_OC_PROFILE(sharedState->profiler, profiler.update(
- compilationUnit, obj, QStringLiteral("<component>"), context->url()));
+ compilationUnit.data(), obj, QStringLiteral("<component>"), context->url()));
QQmlComponentPrivate::get(component)->creationContext = context;
instance = component;
ddata = QQmlData::get(instance, /*create*/true);
@@ -1157,7 +1157,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
QQmlType type = typeRef->type;
if (type.isValid()) {
Q_QML_OC_PROFILE(sharedState->profiler, profiler.update(
- compilationUnit, obj, type.qmlTypeName(), context->url()));
+ compilationUnit.data(), obj, type.qmlTypeName(), context->url()));
void *ddataMemory = nullptr;
type.create(&instance, &ddataMemory, sizeof(QQmlData));
@@ -1190,9 +1190,9 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
} else {
Q_ASSERT(typeRef->compilationUnit);
Q_QML_OC_PROFILE(sharedState->profiler, profiler.update(
- compilationUnit, obj, typeRef->compilationUnit->fileName(),
- context->url()));
- if (typeRef->compilationUnit->data->isSingleton())
+ compilationUnit.data(), obj, typeRef->compilationUnit->fileName(),
+ context->url()));
+ if (typeRef->compilationUnit->unitData()->isSingleton())
{
recordError(obj->location, tr("Composite Singleton Type %1 is not creatable").arg(stringAt(obj->inheritedTypeNameIndex)));
return nullptr;
@@ -1254,17 +1254,17 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
if (customParser && obj->flags & QV4::CompiledData::Object::HasCustomParserBindings) {
customParser->engine = QQmlEnginePrivate::get(engine);
- customParser->imports = compilationUnit->typeNameCache;
+ customParser->imports = compilationUnit->typeNameCache.data();
QList<const QV4::CompiledData::Binding *> bindings;
- const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index);
+ const QV4::CompiledData::Object *obj = compilationUnit->objectAt(index);
const QV4::CompiledData::Binding *binding = obj->bindingTable();
for (quint32 i = 0; i < obj->nBindings; ++i, ++binding) {
if (binding->flags & QV4::CompiledData::Binding::IsCustomParserBinding) {
bindings << binding;
}
}
- customParser->applyBindings(instance, compilationUnit, bindings);
+ customParser->applyBindings(instance, compilationUnit.data(), bindings);
customParser->engine = nullptr;
customParser->imports = (QQmlTypeNameCache*)nullptr;
@@ -1280,7 +1280,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
if (installPropertyCache) {
if (ddata->propertyCache)
ddata->propertyCache->release();;
- ddata->propertyCache = cache;
+ ddata->propertyCache = cache.data();
ddata->propertyCache->addref();
}
@@ -1292,7 +1292,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo
++sharedState->allJavaScriptObjects;
QV4::Scope valueScope(v4);
- QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc(1));
+ QV4::QmlContext *qmlContext = static_cast<QV4::QmlContext *>(valueScope.alloc());
qSwap(_qmlContext, qmlContext);
@@ -1344,6 +1344,11 @@ QQmlContextData *QQmlObjectCreator::finalize(QQmlInstantiationInterrupt &interru
data->clearPendingBindingBit(b->targetPropertyIndex().coreIndex());
b->setEnabled(true, QQmlPropertyData::BypassInterceptor |
QQmlPropertyData::DontRemoveBinding);
+ if (!b->isValueTypeProxy()) {
+ QQmlBinding *binding = static_cast<QQmlBinding*>(b.data());
+ if (!binding->hasError() && !binding->hasDependencies())
+ b->removeFromObject();
+ }
if (watcher.hasRecursed() || interrupt.shouldInterrupt())
return nullptr;
@@ -1419,7 +1424,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
qSwap(_qobject, instance);
qSwap(_valueTypeProperty, valueTypeProperty);
qSwap(_compiledObjectIndex, index);
- const QV4::CompiledData::Object *obj = qmlUnit->objectAt(_compiledObjectIndex);
+ const QV4::CompiledData::Object *obj = compilationUnit->objectAt(_compiledObjectIndex);
qSwap(_compiledObject, obj);
qSwap(_ddata, declarativeData);
qSwap(_bindingTarget, bindingTarget);
@@ -1436,7 +1441,7 @@ bool QQmlObjectCreator::populateInstance(int index, QObject *instance, QObject *
vmeMetaObject = new QQmlVMEMetaObject(v4, _qobject, cache, compilationUnit, _compiledObjectIndex);
if (_ddata->propertyCache)
_ddata->propertyCache->release();
- _ddata->propertyCache = cache;
+ _ddata->propertyCache = cache.data();
_ddata->propertyCache->addref();
scopeObjectProtector = _ddata->jsWrapper.value();
} else {
diff --git a/src/qml/qml/qqmlobjectcreator_p.h b/src/qml/qml/qqmlobjectcreator_p.h
index 67a5bdd827..020e673941 100644
--- a/src/qml/qml/qqmlobjectcreator_p.h
+++ b/src/qml/qml/qqmlobjectcreator_p.h
@@ -85,7 +85,7 @@ class Q_QML_PRIVATE_EXPORT QQmlObjectCreator
{
Q_DECLARE_TR_FUNCTIONS(QQmlObjectCreator)
public:
- QQmlObjectCreator(QQmlContextData *parentContext, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator = nullptr);
+ QQmlObjectCreator(QQmlContextData *parentContext, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlContextData *creationContext, QQmlIncubatorPrivate *incubator = nullptr);
~QQmlObjectCreator();
QObject *create(int subComponentIndex = -1, QObject *parent = nullptr, QQmlInstantiationInterrupt *interrupt = nullptr);
@@ -104,7 +104,7 @@ public:
QFiniteStack<QPointer<QObject> > &allCreatedObjects() const { return sharedState->allCreatedObjects; }
private:
- QQmlObjectCreator(QQmlContextData *contextData, QV4::CompiledData::CompilationUnit *compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState);
+ QQmlObjectCreator(QQmlContextData *contextData, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, QQmlObjectCreatorSharedState *inheritedSharedState);
void init(QQmlContextData *parentContext);
@@ -118,7 +118,7 @@ private:
void setPropertyValue(const QQmlPropertyData *property, const QV4::CompiledData::Binding *binding);
void setupFunctions();
- QString stringAt(int idx) const { return qmlUnit->stringAt(idx); }
+ QString stringAt(int idx) const { return compilationUnit->stringAt(idx); }
void recordError(const QV4::CompiledData::Location &location, const QString &description);
void registerObjectWithContextById(const QV4::CompiledData::Object *object, QObject *instance) const;
diff --git a/src/qml/qml/qqmlopenmetaobject.cpp b/src/qml/qml/qqmlopenmetaobject.cpp
index 1b44bbdda3..fc798a2c23 100644
--- a/src/qml/qml/qqmlopenmetaobject.cpp
+++ b/src/qml/qml/qqmlopenmetaobject.cpp
@@ -181,46 +181,75 @@ void QQmlOpenMetaObjectTypePrivate::init(const QMetaObject *metaObj)
class QQmlOpenMetaObjectPrivate
{
public:
- QQmlOpenMetaObjectPrivate(QQmlOpenMetaObject *_q)
- : q(_q), parent(nullptr), type(nullptr), cacheProperties(false) {}
+ QQmlOpenMetaObjectPrivate(QQmlOpenMetaObject *_q, bool _autoCreate, QObject *obj)
+ : q(_q), object(obj), autoCreate(_autoCreate) {}
+
+ struct Property {
+ private:
+ QVariant m_value;
+ QPointer<QObject> qobjectTracker;
+ public:
+ bool valueSet = false;
+
+ QVariant value() const {
+ if (QMetaType::typeFlags(m_value.userType()) & QMetaType::PointerToQObject
+ && qobjectTracker.isNull())
+ return QVariant::fromValue<QObject*>(nullptr);
+ return m_value;
+ }
+ QVariant &valueRef() { return m_value; }
+ void setValue(const QVariant &v) {
+ m_value = v;
+ valueSet = true;
+ if (QMetaType::typeFlags(v.userType()) & QMetaType::PointerToQObject)
+ qobjectTracker = m_value.value<QObject*>();
+ }
+ };
- inline QPair<QVariant, bool> &getDataRef(int idx) {
- while (data.count() <= idx)
- data << QPair<QVariant, bool>(QVariant(), false);
- return data[idx];
+ inline void setPropertyValue(int idx, const QVariant &value) {
+ if (data.count() <= idx)
+ data.resize(idx + 1);
+ data[idx].setValue(value);
}
- inline QVariant &getData(int idx) {
- QPair<QVariant, bool> &prop = getDataRef(idx);
- if (!prop.second) {
- prop.first = q->initialValue(idx);
- prop.second = true;
- }
- return prop.first;
+ inline Property &propertyRef(int idx) {
+ if (data.count() <= idx)
+ data.resize(idx + 1);
+ Property &prop = data[idx];
+ if (!prop.valueSet)
+ prop.setValue(q->initialValue(idx));
+ return prop;
+ }
+
+ inline QVariant propertyValue(int idx) {
+ auto &prop = propertyRef(idx);
+ return prop.value();
+ }
+
+ inline QVariant &propertyValueRef(int idx) {
+ auto &prop = propertyRef(idx);
+ return prop.valueRef();
}
- inline bool hasData(int idx) const {
+ inline bool hasProperty(int idx) const {
if (idx >= data.count())
return false;
- return data[idx].second;
+ return data[idx].valueSet;
}
- bool autoCreate;
QQmlOpenMetaObject *q;
- QAbstractDynamicMetaObject *parent;
- QList<QPair<QVariant, bool> > data;
+ QAbstractDynamicMetaObject *parent = nullptr;
+ QVector<Property> data;
QObject *object;
- QQmlOpenMetaObjectType *type;
- bool cacheProperties;
+ QQmlRefPointer<QQmlOpenMetaObjectType> type;
+ bool autoCreate;
+ bool cacheProperties = false;
};
QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, const QMetaObject *base, bool automatic)
-: d(new QQmlOpenMetaObjectPrivate(this))
+: d(new QQmlOpenMetaObjectPrivate(this, automatic, obj))
{
- d->autoCreate = automatic;
- d->object = obj;
-
- d->type = new QQmlOpenMetaObjectType(base ? base : obj->metaObject(), nullptr);
+ d->type.adopt(new QQmlOpenMetaObjectType(base ? base : obj->metaObject(), nullptr));
d->type->d->referers.insert(this);
QObjectPrivate *op = QObjectPrivate::get(obj);
@@ -230,13 +259,9 @@ QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, const QMetaObject *base, bo
}
QQmlOpenMetaObject::QQmlOpenMetaObject(QObject *obj, QQmlOpenMetaObjectType *type, bool automatic)
-: d(new QQmlOpenMetaObjectPrivate(this))
+: d(new QQmlOpenMetaObjectPrivate(this, automatic, obj))
{
- d->autoCreate = automatic;
- d->object = obj;
-
d->type = type;
- d->type->addref();
d->type->d->referers.insert(this);
QObjectPrivate *op = QObjectPrivate::get(obj);
@@ -250,13 +275,12 @@ QQmlOpenMetaObject::~QQmlOpenMetaObject()
if (d->parent)
delete d->parent;
d->type->d->referers.remove(this);
- d->type->release();
delete d;
}
QQmlOpenMetaObjectType *QQmlOpenMetaObject::type() const
{
- return d->type;
+ return d->type.data();
}
void QQmlOpenMetaObject::emitPropertyNotification(const QByteArray &propertyName)
@@ -276,13 +300,11 @@ int QQmlOpenMetaObject::metaCall(QObject *o, QMetaObject::Call c, int id, void *
int propId = id - d->type->d->propertyOffset;
if (c == QMetaObject::ReadProperty) {
propertyRead(propId);
- *reinterpret_cast<QVariant *>(a[0]) = d->getData(propId);
+ *reinterpret_cast<QVariant *>(a[0]) = d->propertyValue(propId);
} else if (c == QMetaObject::WriteProperty) {
- if (propId >= d->data.count() || d->data.at(propId).first != *reinterpret_cast<QVariant *>(a[0])) {
+ if (propId >= d->data.count() || d->data.at(propId).value() != *reinterpret_cast<QVariant *>(a[0])) {
propertyWrite(propId);
- QPair<QVariant, bool> &prop = d->getDataRef(propId);
- prop.first = propertyWriteValue(propId, *reinterpret_cast<QVariant *>(a[0]));
- prop.second = true;
+ d->setPropertyValue(propId, propertyWriteValue(propId, *reinterpret_cast<QVariant *>(a[0])));
propertyWritten(propId);
activate(o, d->type->d->signalOffset + propId, nullptr);
}
@@ -303,14 +325,12 @@ QAbstractDynamicMetaObject *QQmlOpenMetaObject::parent() const
QVariant QQmlOpenMetaObject::value(int id) const
{
- return d->getData(id);
+ return d->propertyValue(id);
}
void QQmlOpenMetaObject::setValue(int id, const QVariant &value)
{
- QPair<QVariant, bool> &prop = d->getDataRef(id);
- prop.first = propertyWriteValue(id, value);
- prop.second = true;
+ d->setPropertyValue(id, propertyWriteValue(id, value));
activate(d->object, id + d->type->d->signalOffset, nullptr);
}
@@ -320,23 +340,18 @@ QVariant QQmlOpenMetaObject::value(const QByteArray &name) const
if (iter == d->type->d->names.cend())
return QVariant();
- return d->getData(*iter);
+ return d->propertyValue(*iter);
}
-QVariant &QQmlOpenMetaObject::operator[](const QByteArray &name)
+QVariant &QQmlOpenMetaObject::valueRef(const QByteArray &name)
{
QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(name);
Q_ASSERT(iter != d->type->d->names.cend());
- return d->getData(*iter);
-}
-
-QVariant &QQmlOpenMetaObject::operator[](int id)
-{
- return d->getData(id);
+ return d->propertyValueRef(*iter);
}
-bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val)
+bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val, bool force)
{
QHash<QByteArray, int>::ConstIterator iter = d->type->d->names.constFind(name);
@@ -348,11 +363,10 @@ bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val)
}
if (id >= 0) {
- QVariant &dataVal = d->getData(id);
- if (dataVal == val)
+ if (!force && d->propertyValue(id) == val)
return false;
- dataVal = val;
+ d->setPropertyValue(id, val);
activate(d->object, id + d->type->d->signalOffset, nullptr);
return true;
}
@@ -363,7 +377,7 @@ bool QQmlOpenMetaObject::setValue(const QByteArray &name, const QVariant &val)
// returns true if this value has been initialized by a call to either value() or setValue()
bool QQmlOpenMetaObject::hasValue(int id) const
{
- return d->hasData(id);
+ return d->hasProperty(id);
}
void QQmlOpenMetaObject::setCached(bool c)
diff --git a/src/qml/qml/qqmlopenmetaobject_p.h b/src/qml/qml/qqmlopenmetaobject_p.h
index 4905190b75..168a2a6f7f 100644
--- a/src/qml/qml/qqmlopenmetaobject_p.h
+++ b/src/qml/qml/qqmlopenmetaobject_p.h
@@ -100,11 +100,10 @@ public:
~QQmlOpenMetaObject() override;
QVariant value(const QByteArray &) const;
- bool setValue(const QByteArray &, const QVariant &);
+ bool setValue(const QByteArray &, const QVariant &, bool force = false);
QVariant value(int) const;
void setValue(int, const QVariant &);
- QVariant &operator[](const QByteArray &);
- QVariant &operator[](int);
+ QVariant &valueRef(const QByteArray &);
bool hasValue(int) const;
int count() const;
diff --git a/src/qml/qml/qqmlparserstatus.cpp b/src/qml/qml/qqmlparserstatus.cpp
index 1082c318cb..b8f4bb8c19 100644
--- a/src/qml/qml/qqmlparserstatus.cpp
+++ b/src/qml/qml/qqmlparserstatus.cpp
@@ -81,8 +81,6 @@ QT_BEGIN_NAMESPACE
void componentComplete();
}
\endcode
-
- The \l {Qt Quick 1} version of this class is named QDeclarativeParserStatus.
*/
/*! \internal */
diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h
index fabdcacc36..22e46fd7ed 100644
--- a/src/qml/qml/qqmlprivate.h
+++ b/src/qml/qml/qqmlprivate.h
@@ -95,12 +95,20 @@ namespace QQmlPrivate
{
void Q_QML_EXPORT qdeclarativeelement_destructor(QObject *);
template<typename T>
- class QQmlElement : public T
+ class QQmlElement final : public T
{
public:
~QQmlElement() override {
QQmlPrivate::qdeclarativeelement_destructor(this);
}
+ static void operator delete(void *ptr) {
+ // We allocate memory from this class in QQmlType::create
+ // along with some additional memory.
+ // So we override the operator delete in order to avoid the
+ // sized operator delete to be called with a different size than
+ // the size that was allocated.
+ ::operator delete (ptr);
+ }
};
template<typename T>
diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp
index c4487f91a3..000b88ebaa 100644
--- a/src/qml/qml/qqmlproperty.cpp
+++ b/src/qml/qml/qqmlproperty.cpp
@@ -111,8 +111,6 @@ qWarning() << "Current pixel size:" << property.read().toInt();
property.write(24);
qWarning() << "Pixel size should now be 24:" << property.read().toInt();
\endcode
-
-The \l {Qt Quick 1} version of this class was named QDeclarativeProperty.
*/
/*!
@@ -255,7 +253,7 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name)
{
if (!obj) return;
- QQmlTypeNameCache *typeNameCache = context?context->imports:nullptr;
+ QQmlRefPointer<QQmlTypeNameCache> typeNameCache = context?context->imports:nullptr;
QObject *currentObject = obj;
QVector<QStringRef> path;
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 88eda9c020..0388215630 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -1360,19 +1360,21 @@ template <typename StringVisitor>
int visitEnumerations(const QMetaObject &mo, StringVisitor visitString)
{
const QMetaObjectPrivate *const priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
- const int intsPerEnumerator = 4;
+ const int intsPerEnumerator = priv->revision >= 8 ? 5 : 4;
int fieldCount = priv->enumeratorCount * intsPerEnumerator;
for (int i = 0; i < priv->enumeratorCount; ++i) {
const uint *enumeratorData = mo.d.data + priv->enumeratorData + i * intsPerEnumerator;
- const uint keyCount = enumeratorData[2];
+ const uint keyCount = enumeratorData[intsPerEnumerator == 5 ? 3 : 2];
fieldCount += keyCount * 2;
visitString(enumeratorData[0]); // name
+ if (intsPerEnumerator == 5)
+ visitString(enumeratorData[1]); // enum name
- const uint keyOffset = enumeratorData[3];
+ const uint keyOffset = enumeratorData[intsPerEnumerator == 5 ? 4 : 3];
for (uint j = 0; j < keyCount; ++j) {
visitString(mo.d.data[keyOffset + 2 * j]);
@@ -1412,7 +1414,7 @@ bool QQmlPropertyCache::determineMetaObjectSizes(const QMetaObject &mo, int *fie
int *stringCount)
{
const QMetaObjectPrivate *priv = reinterpret_cast<const QMetaObjectPrivate*>(mo.d.data);
- if (priv->revision != 7) {
+ if (priv->revision < 7 || priv->revision > 8) {
return false;
}
diff --git a/src/qml/qml/qqmlpropertycache_p.h b/src/qml/qml/qqmlpropertycache_p.h
index b78a2ddd20..0785910cec 100644
--- a/src/qml/qml/qqmlpropertycache_p.h
+++ b/src/qml/qml/qqmlpropertycache_p.h
@@ -979,13 +979,13 @@ public:
void append(QQmlPropertyCache *cache) { cache->addref(); data.append(cache); }
QQmlPropertyCache *at(int index) const { return data.at(index).data(); }
- void set(int index, QQmlPropertyCache *replacement) {
+ void set(int index, const QQmlRefPointer<QQmlPropertyCache> &replacement) {
if (QQmlPropertyCache *oldCache = data.at(index).data()) {
- if (replacement == oldCache)
+ if (replacement.data() == oldCache)
return;
oldCache->release();
}
- data[index] = replacement;
+ data[index] = replacement.data();
replacement->addref();
}
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index c656fac4ff..0b0c6242ba 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -51,7 +51,7 @@
#include <private/qqmltypecompiler_p.h>
#include <private/qqmlpropertyvalidator_p.h>
#include <private/qqmlpropertycachecreator_p.h>
-#include <private/qdeferredcleanup_p.h>
+#include <private/qv4module_p.h>
#include <QtCore/qdir.h>
#include <QtCore/qfile.h>
@@ -66,6 +66,7 @@
#include <QtCore/qloggingcategory.h>
#include <QtQml/qqmlextensioninterface.h>
#include <QtCore/qcryptographichash.h>
+#include <QtCore/qscopeguard.h>
#include <functional>
@@ -504,11 +505,12 @@ void QQmlDataBlob::addDependency(QQmlDataBlob *blob)
if (!blob ||
blob->status() == Error || blob->status() == Complete ||
- status() == Error || status() == Complete || m_isDone ||
- m_waitingFor.contains(blob))
+ status() == Error || status() == Complete || m_isDone)
return;
- blob->addref();
+ for (auto existingDep: qAsConst(m_waitingFor))
+ if (existingDep.data() == blob)
+ return;
m_data.setStatus(WaitingForDependencies);
@@ -691,13 +693,11 @@ void QQmlDataBlob::tryDone()
void QQmlDataBlob::cancelAllWaitingFor()
{
while (m_waitingFor.count()) {
- QQmlDataBlob *blob = m_waitingFor.takeLast();
+ QQmlRefPointer<QQmlDataBlob> blob = m_waitingFor.takeLast();
Q_ASSERT(blob->m_waitingOnMe.contains(this));
blob->m_waitingOnMe.removeOne(this);
-
- blob->release();
}
}
@@ -706,7 +706,8 @@ void QQmlDataBlob::notifyAllWaitingOnMe()
while (m_waitingOnMe.count()) {
QQmlDataBlob *blob = m_waitingOnMe.takeLast();
- Q_ASSERT(blob->m_waitingFor.contains(this));
+ Q_ASSERT(std::any_of(blob->m_waitingFor.constBegin(), blob->m_waitingFor.constEnd(),
+ [this](const QQmlRefPointer<QQmlDataBlob> &waiting) { return waiting.data() == this; }));
blob->notifyComplete(this);
}
@@ -714,13 +715,19 @@ void QQmlDataBlob::notifyAllWaitingOnMe()
void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob)
{
- Q_ASSERT(m_waitingFor.contains(blob));
Q_ASSERT(blob->status() == Error || blob->status() == Complete);
QQmlCompilingProfiler prof(typeLoader()->profiler(), blob);
m_inCallback = true;
- m_waitingFor.removeOne(blob);
+ QQmlRefPointer<QQmlDataBlob> blobRef;
+ for (int i = 0; i < m_waitingFor.count(); ++i) {
+ if (m_waitingFor.at(i).data() == blob) {
+ blobRef = m_waitingFor.takeAt(i);
+ break;
+ }
+ }
+ Q_ASSERT(blobRef);
if (blob->status() == Error) {
dependencyError(blob);
@@ -728,8 +735,6 @@ void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob)
dependencyComplete(blob);
}
- blob->release();
-
if (!isError() && m_waitingFor.isEmpty())
allDependenciesDone();
@@ -860,13 +865,23 @@ void QQmlTypeLoaderThread::loadWithCachedUnitAsync(QQmlDataBlob *b, const QV4::C
void QQmlTypeLoaderThread::callCompleted(QQmlDataBlob *b)
{
b->addref();
+#if !QT_CONFIG(thread)
+ if (!isThisThread())
+ postMethodToThread(&This::callCompletedMain, b);
+#else
postMethodToMain(&This::callCompletedMain, b);
+#endif
}
void QQmlTypeLoaderThread::callDownloadProgressChanged(QQmlDataBlob *b, qreal p)
{
b->addref();
+#if !QT_CONFIG(thread)
+ if (!isThisThread())
+ postMethodToThread(&This::callDownloadProgressChangedMain, b, p);
+#else
postMethodToMain(&This::callDownloadProgressChangedMain, b, p);
+#endif
}
void QQmlTypeLoaderThread::initializeEngine(QQmlExtensionInterface *iface,
@@ -1170,7 +1185,10 @@ void QQmlTypeLoader::loadThread(QQmlDataBlob *blob)
}
#define DATALOADER_MAXIMUM_REDIRECT_RECURSION 16
+
+#ifndef TYPELOADER_MINIMUM_TRIM_THRESHOLD
#define TYPELOADER_MINIMUM_TRIM_THRESHOLD 64
+#endif
#if QT_CONFIG(qml_network)
void QQmlTypeLoader::networkReplyFinished(QNetworkReply *reply)
@@ -1327,20 +1345,17 @@ QQmlTypeLoader::Blob::Blob(const QUrl &url, QQmlDataBlob::Type type, QQmlTypeLoa
QQmlTypeLoader::Blob::~Blob()
{
- for (int ii = 0; ii < m_qmldirs.count(); ++ii)
- m_qmldirs.at(ii)->release();
}
bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, const QV4::CompiledData::Import *import, int priority, QList<QQmlError> *errors)
{
- QQmlQmldirData *data = typeLoader()->getQmldir(url);
+ QQmlRefPointer<QQmlQmldirData> data = typeLoader()->getQmldir(url);
data->setImport(this, import);
data->setPriority(this, priority);
if (data->status() == Error) {
// This qmldir must not exist - which is not an error
- data->release();
return true;
} else if (data->status() == Complete) {
// This data is already available
@@ -1348,11 +1363,11 @@ bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, const QV4::CompiledData:
}
// Wait for this data to become available
- addDependency(data);
+ addDependency(data.data());
return true;
}
-bool QQmlTypeLoader::Blob::updateQmldir(QQmlQmldirData *data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors)
+bool QQmlTypeLoader::Blob::updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors)
{
QString qmldirIdentifier = data->urlString();
QString qmldirUrl = qmldirIdentifier.left(qmldirIdentifier.lastIndexOf(QLatin1Char('/')) + 1);
@@ -1378,8 +1393,8 @@ bool QQmlTypeLoader::Blob::updateQmldir(QQmlQmldirData *data, const QV4::Compile
const auto qmldirScripts = qmldir.scripts();
for (const QQmlDirParser::Script &script : qmldirScripts) {
QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName));
- QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl);
- addDependency(blob);
+ QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl);
+ addDependency(blob.data());
scriptImported(blob, import->location, script.nameSpace, importQualifier);
}
@@ -1398,8 +1413,8 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL
const QString &importQualifier = stringAt(import->qualifierIndex);
if (import->type == QV4::CompiledData::Import::ImportScript) {
QUrl scriptUrl = finalUrl().resolved(QUrl(importUri));
- QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl);
- addDependency(blob);
+ QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl);
+ addDependency(blob.data());
scriptImported(blob, import->location, importQualifier, QString());
} else if (import->type == QV4::CompiledData::Import::ImportLibrary) {
@@ -1426,8 +1441,8 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL
const auto qmldirScripts = qmldir.scripts();
for (const QQmlDirParser::Script &script : qmldirScripts) {
QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName));
- QQmlScriptBlob *blob = typeLoader()->getScript(scriptUrl);
- addDependency(blob);
+ QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(scriptUrl);
+ addDependency(blob.data());
scriptImported(blob, import->location, script.nameSpace, importQualifier);
}
@@ -1499,14 +1514,6 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL
return true;
}
-void QQmlTypeLoader::Blob::dependencyError(QQmlDataBlob *blob)
-{
- if (blob->type() == QQmlDataBlob::QmldirFile) {
- QQmlQmldirData *data = static_cast<QQmlQmldirData *>(blob);
- data->release();
- }
-}
-
void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob)
{
if (blob->type() == QQmlDataBlob::QmldirFile) {
@@ -1532,7 +1539,7 @@ bool QQmlTypeLoader::Blob::isDebugging() const
return typeLoader()->engine()->handle()->debugger() != nullptr;
}
-bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlError> *errors)
+bool QQmlTypeLoader::Blob::qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &data, QList<QQmlError> *errors)
{
bool resolve = true;
@@ -1552,7 +1559,6 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlE
if (resolve) {
// This is the (current) best resolution for this import
if (!updateQmldir(data, import, errors)) {
- data->release();
return false;
}
@@ -1562,7 +1568,6 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlE
}
}
- data->release();
return true;
}
@@ -1664,7 +1669,7 @@ QUrl QQmlTypeLoader::normalize(const QUrl &unNormalizedUrl)
/*!
Returns a QQmlTypeData for the specified \a url. The QQmlTypeData may be cached.
*/
-QQmlTypeData *QQmlTypeLoader::getType(const QUrl &unNormalizedUrl, Mode mode)
+QQmlRefPointer<QQmlTypeData> QQmlTypeLoader::getType(const QUrl &unNormalizedUrl, Mode mode)
{
Q_ASSERT(!unNormalizedUrl.isRelative() &&
(QQmlFile::urlToLocalFileOrQrc(unNormalizedUrl).isEmpty() ||
@@ -1707,8 +1712,6 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &unNormalizedUrl, Mode mode)
}
}
- typeData->addref();
-
return typeData;
}
@@ -1716,20 +1719,20 @@ QQmlTypeData *QQmlTypeLoader::getType(const QUrl &unNormalizedUrl, Mode mode)
Returns a QQmlTypeData for the given \a data with the provided base \a url. The
QQmlTypeData will not be cached.
*/
-QQmlTypeData *QQmlTypeLoader::getType(const QByteArray &data, const QUrl &url, Mode mode)
+QQmlRefPointer<QQmlTypeData> QQmlTypeLoader::getType(const QByteArray &data, const QUrl &url, Mode mode)
{
LockHolder<QQmlTypeLoader> holder(this);
QQmlTypeData *typeData = new QQmlTypeData(url, this);
QQmlTypeLoader::loadWithStaticData(typeData, data, mode);
- return typeData;
+ return QQmlRefPointer<QQmlTypeData>(typeData, QQmlRefPointer<QQmlTypeData>::Adopt);
}
/*!
Return a QQmlScriptBlob for \a url. The QQmlScriptData may be cached.
*/
-QQmlScriptBlob *QQmlTypeLoader::getScript(const QUrl &unNormalizedUrl)
+QQmlRefPointer<QQmlScriptBlob> QQmlTypeLoader::getScript(const QUrl &unNormalizedUrl)
{
Q_ASSERT(!unNormalizedUrl.isRelative() &&
(QQmlFile::urlToLocalFileOrQrc(unNormalizedUrl).isEmpty() ||
@@ -1754,20 +1757,17 @@ QQmlScriptBlob *QQmlTypeLoader::getScript(const QUrl &unNormalizedUrl)
}
}
- scriptBlob->addref();
-
return scriptBlob;
}
/*!
Returns a QQmlQmldirData for \a url. The QQmlQmldirData may be cached.
*/
-QQmlQmldirData *QQmlTypeLoader::getQmldir(const QUrl &url)
+QQmlRefPointer<QQmlQmldirData> QQmlTypeLoader::getQmldir(const QUrl &url)
{
Q_ASSERT(!url.isRelative() &&
(QQmlFile::urlToLocalFileOrQrc(url).isEmpty() ||
!QDir::isRelativePath(QQmlFile::urlToLocalFileOrQrc(url))));
-
LockHolder<QQmlTypeLoader> holder(this);
QQmlQmldirData *qmldirData = m_qmldirCache.value(url);
@@ -1778,8 +1778,6 @@ QQmlQmldirData *QQmlTypeLoader::getQmldir(const QUrl &url)
QQmlTypeLoader::load(qmldirData);
}
- qmldirData->addref();
-
return qmldirData;
}
@@ -1859,6 +1857,52 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path)
return absoluteFilePath;
}
+bool QQmlTypeLoader::fileExists(const QString &path, const QString &file)
+{
+ if (path.isEmpty())
+ return false;
+ Q_ASSERT(path.endsWith(QLatin1Char('/')));
+ if (path.at(0) == QLatin1Char(':')) {
+ // qrc resource
+ QFileInfo fileInfo(path + file);
+ return fileInfo.isFile();
+ } else if (path.count() > 3 && path.at(3) == QLatin1Char(':') &&
+ path.startsWith(QLatin1String("qrc"), Qt::CaseInsensitive)) {
+ // qrc resource url
+ QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path + file));
+ return fileInfo.isFile();
+ }
+#if defined(Q_OS_ANDROID)
+ else if (path.count() > 7 && path.at(6) == QLatin1Char(':') && path.at(7) == QLatin1Char('/') &&
+ path.startsWith(QLatin1String("assets"), Qt::CaseInsensitive)) {
+ // assets resource url
+ QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path + file));
+ return fileInfo.isFile();
+ }
+#endif
+
+ LockHolder<QQmlTypeLoader> holder(this);
+ if (!m_importDirCache.contains(path)) {
+ bool exists = QDir(path).exists();
+ QCache<QString, bool> *entry = exists ? new QCache<QString, bool> : nullptr;
+ m_importDirCache.insert(path, entry);
+ }
+ QCache<QString, bool> *fileSet = m_importDirCache.object(path);
+ if (!fileSet)
+ return false;
+
+ QString absoluteFilePath;
+
+ bool *value = fileSet->object(file);
+ if (value) {
+ return *value;
+ } else {
+ bool exists = QFile::exists(path + file);
+ fileSet->insert(file, new bool(exists));
+ return exists;
+ }
+}
+
/*!
Returns true if the path is a directory via a directory cache. Cache is
@@ -2073,17 +2117,9 @@ QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager)
QQmlTypeData::~QQmlTypeData()
{
- for (int ii = 0; ii < m_scripts.count(); ++ii)
- m_scripts.at(ii).script->release();
- for (int ii = 0; ii < m_compositeSingletons.count(); ++ii) {
- if (QQmlTypeData *tdata = m_compositeSingletons.at(ii).typeData)
- tdata->release();
- }
- for (auto it = m_resolvedTypes.constBegin(), end = m_resolvedTypes.constEnd();
- it != end; ++it) {
- if (QQmlTypeData *tdata = it->typeData)
- tdata->release();
- }
+ m_scripts.clear();
+ m_compositeSingletons.clear();
+ m_resolvedTypes.clear();
}
const QList<QQmlTypeData::ScriptReference> &QQmlTypeData::resolvedScripts() const
@@ -2130,7 +2166,7 @@ bool QQmlTypeData::tryLoadFromDiskCache()
}
}
- if (unit->data->flags & QV4::CompiledData::Unit::PendingTypeCompilation) {
+ if (unit->unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation) {
restoreIR(unit);
return true;
}
@@ -2152,8 +2188,8 @@ bool QQmlTypeData::tryLoadFromDiskCache()
return false;
// find the implicit import
- for (quint32 i = 0; i < m_compiledData->data->nImports; ++i) {
- const QV4::CompiledData::Import *import = m_compiledData->data->importAt(i);
+ for (quint32 i = 0, count = m_compiledData->importCount(); i < count; ++i) {
+ const QV4::CompiledData::Import *import = m_compiledData->importAt(i);
if (m_compiledData->stringAt(import->uriIndex) == QLatin1String(".")
&& import->qualifierIndex == 0
&& import->majorVersion == -1
@@ -2169,8 +2205,8 @@ bool QQmlTypeData::tryLoadFromDiskCache()
}
}
- for (int i = 0, count = m_compiledData->data->nImports; i < count; ++i) {
- const QV4::CompiledData::Import *import = m_compiledData->data->importAt(i);
+ for (int i = 0, count = m_compiledData->importCount(); i < count; ++i) {
+ const QV4::CompiledData::Import *import = m_compiledData->importAt(i);
QList<QQmlError> errors;
if (!addImport(import, &errors)) {
Q_ASSERT(errors.size());
@@ -2201,7 +2237,7 @@ void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeName
{
QQmlPropertyCacheCreator<QV4::CompiledData::CompilationUnit> propertyCacheCreator(&m_compiledData->propertyCaches,
&pendingGroupPropertyBindings,
- engine, m_compiledData, &m_importCache);
+ engine, m_compiledData.data(), &m_importCache);
QQmlCompileError error = propertyCacheCreator.buildMetaObjects();
if (error.isSet()) {
setError(error);
@@ -2209,7 +2245,7 @@ void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeName
}
}
- QQmlPropertyCacheAliasCreator<QV4::CompiledData::CompilationUnit> aliasCreator(&m_compiledData->propertyCaches, m_compiledData);
+ QQmlPropertyCacheAliasCreator<QV4::CompiledData::CompilationUnit> aliasCreator(&m_compiledData->propertyCaches, m_compiledData.data());
aliasCreator.appendAliasPropertiesToMetaObjects();
pendingGroupPropertyBindings.resolveMissingPropertyCaches(engine, &m_compiledData->propertyCaches);
@@ -2219,8 +2255,8 @@ static bool addTypeReferenceChecksumsToHash(const QList<QQmlTypeData::TypeRefere
{
for (const auto &typeRef: typeRefs) {
if (typeRef.typeData) {
- const auto unit = typeRef.typeData->compilationUnit();
- hash->addData(unit->data->md5Checksum, sizeof(unit->data->md5Checksum));
+ const auto unit = typeRef.typeData->compilationUnit()->unitData();
+ hash->addData(unit->md5Checksum, sizeof(unit->md5Checksum));
} else if (typeRef.type.isValid()) {
const auto propertyCache = QQmlEnginePrivate::get(engine)->cache(typeRef.type.metaObject());
bool ok = false;
@@ -2234,7 +2270,7 @@ static bool addTypeReferenceChecksumsToHash(const QList<QQmlTypeData::TypeRefere
void QQmlTypeData::done()
{
- QDeferredCleanup cleanup([this]{
+ auto cleanup = qScopeGuard([this]{
m_document.reset();
m_typeReferences.clear();
if (isError())
@@ -2354,7 +2390,7 @@ void QQmlTypeData::done()
{
QQmlType type = QQmlMetaType::qmlType(finalUrl(), true);
- if (m_compiledData && m_compiledData->data->flags & QV4::CompiledData::Unit::IsSingleton) {
+ if (m_compiledData && m_compiledData->unitData()->flags & QV4::CompiledData::Unit::IsSingleton) {
if (!type.isValid()) {
QQmlError error;
error.setDescription(QQmlTypeLoader::tr("No matching type found, pragma Singleton files cannot be used by QQmlComponent."));
@@ -2393,8 +2429,7 @@ void QQmlTypeData::done()
}
m_compiledData->typeNameCache->add(qualifier.toString(), scriptIndex, enclosingNamespace);
- QQmlScriptData *scriptData = script.script->scriptData();
- scriptData->addref();
+ QQmlRefPointer<QQmlScriptData> scriptData = script.script->scriptData();
m_compiledData->dependentScripts << scriptData;
}
}
@@ -2501,7 +2536,7 @@ bool QQmlTypeData::loadFromSource()
void QQmlTypeData::restoreIR(QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit)
{
m_document.reset(new QmlIR::Document(isDebugging()));
- QmlIR::IRLoader loader(unit->data, m_document.data());
+ QmlIR::IRLoader loader(unit->unitData(), m_document.data());
loader.load();
m_document->jsModule.fileName = urlString();
m_document->jsModule.finalUrl = finalUrlString();
@@ -2608,7 +2643,7 @@ void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCach
{
Q_ASSERT(m_compiledData.isNull());
- const bool typeRecompilation = m_document && m_document->javaScriptCompilationUnit && m_document->javaScriptCompilationUnit->data->flags & QV4::CompiledData::Unit::PendingTypeCompilation;
+ const bool typeRecompilation = m_document && m_document->javaScriptCompilationUnit && m_document->javaScriptCompilationUnit->unitData()->flags & QV4::CompiledData::Unit::PendingTypeCompilation;
QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine());
QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), typeNameCache, resolvedTypeCache, dependencyHasher);
@@ -2637,8 +2672,8 @@ void QQmlTypeData::resolveTypes()
// Add any imported scripts to our resolved set
const auto resolvedScripts = m_importCache.resolvedScripts();
for (const QQmlImports::ScriptReference &script : resolvedScripts) {
- QQmlScriptBlob *blob = typeLoader()->getScript(script.location);
- addDependency(blob);
+ QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(script.location);
+ addDependency(blob.data());
ScriptReference ref;
//ref.location = ...
@@ -2681,7 +2716,7 @@ void QQmlTypeData::resolveTypes()
// TODO: give an error message? If so, we should record and show the path of the cycle.
continue;
}
- addDependency(ref.typeData);
+ addDependency(ref.typeData.data());
ref.prefix = csRef.prefix;
m_compositeSingletons << ref;
@@ -2711,7 +2746,7 @@ void QQmlTypeData::resolveTypes()
if (ref.type.isComposite()) {
ref.typeData = typeLoader()->getType(ref.type.sourceUrl());
- addDependency(ref.typeData);
+ addDependency(ref.typeData.data());
}
ref.majorVersion = majorVersion;
ref.minorVersion = minorVersion;
@@ -2743,7 +2778,7 @@ QQmlCompileError QQmlTypeData::buildTypeResolutionCaches(
for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons)
(*typeNameCache)->add(singleton.type.qmlTypeName(), singleton.type.sourceUrl(), singleton.prefix);
- m_importCache.populateCache(*typeNameCache);
+ m_importCache.populateCache(typeNameCache->data());
QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine());
@@ -2833,7 +2868,7 @@ bool QQmlTypeData::resolveType(const QString &typeName, int &majorVersion, int &
return true;
}
-void QQmlTypeData::scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &/*nameSpace*/)
+void QQmlTypeData::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &/*nameSpace*/)
{
ScriptReference ref;
ref.script = blob;
@@ -2876,11 +2911,22 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent
return m_value.value();
Q_ASSERT(parentCtxt && parentCtxt->engine);
- QQmlEnginePrivate *ep = QQmlEnginePrivate::get(parentCtxt->engine);
QV4::ExecutionEngine *v4 = parentCtxt->engine->handle();
+
+ if (m_precompiledScript->unitData()->flags & QV4::CompiledData::Unit::IsESModule) {
+ m_loaded = true;
+
+ m_value.set(v4, m_precompiledScript->instantiate(v4));
+ if (!m_value.isNullOrUndefined())
+ m_precompiledScript->evaluate();
+
+ return m_value.value();
+ }
+
+ QQmlEnginePrivate *ep = QQmlEnginePrivate::get(parentCtxt->engine);
QV4::Scope scope(v4);
- bool shared = m_precompiledScript->data->flags & QV4::CompiledData::Unit::IsSharedLibrary;
+ bool shared = m_precompiledScript->unitData()->flags & QV4::CompiledData::Unit::IsSharedLibrary;
QQmlContextData *effectiveCtxt = parentCtxt;
if (shared)
@@ -2921,7 +2967,7 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent
}
QV4::ScopedValue v(scope);
for (int ii = 0; ii < scripts.count(); ++ii)
- scriptsArray->putIndexed(ii, (v = scripts.at(ii)->scriptData()->scriptValueForContext(ctxt)));
+ scriptsArray->put(ii, (v = scripts.at(ii)->scriptData()->scriptValueForContext(ctxt)));
if (!hasEngine())
initialize(parentCtxt->engine);
@@ -2959,8 +3005,6 @@ void QQmlScriptData::clear()
typeNameCache = nullptr;
}
- for (int ii = 0; ii < scripts.count(); ++ii)
- scripts.at(ii)->release();
scripts.clear();
// An addref() was made when the QQmlCleanup was added to the engine.
@@ -2968,19 +3012,16 @@ void QQmlScriptData::clear()
}
QQmlScriptBlob::QQmlScriptBlob(const QUrl &url, QQmlTypeLoader *loader)
-: QQmlTypeLoader::Blob(url, JavaScriptFile, loader), m_scriptData(nullptr)
+ : QQmlTypeLoader::Blob(url, JavaScriptFile, loader)
+ , m_isModule(url.path().endsWith(QLatin1String(".mjs")))
{
}
QQmlScriptBlob::~QQmlScriptBlob()
{
- if (m_scriptData) {
- m_scriptData->release();
- m_scriptData = nullptr;
- }
}
-QQmlScriptData *QQmlScriptBlob::scriptData() const
+QQmlRefPointer<QQmlScriptData> QQmlScriptBlob::scriptData() const
{
return m_scriptData;
}
@@ -3006,9 +3047,6 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
return;
}
- QmlIR::Document irUnit(isDebugging());
-
- irUnit.jsModule.sourceTimeStamp = data.sourceTimeStamp();
QString error;
QString source = data.readAll(&error);
if (!error.isEmpty()) {
@@ -3016,32 +3054,51 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
return;
}
- QmlIR::ScriptDirectivesCollector collector(&irUnit);
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit;
- QList<QQmlError> errors;
- QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = QV4::Script::precompile(
- &irUnit.jsModule, &irUnit.jsGenerator, urlString(), finalUrlString(),
- source, &errors, &collector);
- // No need to addref on unit, it's initial refcount is 1
- source.clear();
- if (!errors.isEmpty()) {
- setError(errors);
- return;
- }
- if (!unit) {
- unit.adopt(new QV4::CompiledData::CompilationUnit);
- }
- irUnit.javaScriptCompilationUnit = unit;
+ if (m_isModule) {
+ QList<QQmlJS::DiagnosticMessage> diagnostics;
+ unit = QV4::ExecutionEngine::compileModule(isDebugging(), urlString(), source, data.sourceTimeStamp(), &diagnostics);
+ QList<QQmlError> errors = QQmlEnginePrivate::qmlErrorFromDiagnostics(urlString(), diagnostics);
+ if (!errors.isEmpty()) {
+ setError(errors);
+ return;
+ }
+ } else {
+ QmlIR::Document irUnit(isDebugging());
+
+ irUnit.jsModule.sourceTimeStamp = data.sourceTimeStamp();
+
+ QmlIR::ScriptDirectivesCollector collector(&irUnit);
+ irUnit.jsParserEngine.setDirectives(&collector);
+
+ QList<QQmlError> errors;
+ unit = QV4::Script::precompile(
+ &irUnit.jsModule, &irUnit.jsParserEngine, &irUnit.jsGenerator, urlString(), finalUrlString(),
+ source, &errors);
+ // No need to addref on unit, it's initial refcount is 1
+ source.clear();
+ if (!errors.isEmpty()) {
+ setError(errors);
+ return;
+ }
+ if (!unit) {
+ unit.adopt(new QV4::CompiledData::CompilationUnit);
+ }
+ irUnit.javaScriptCompilationUnit = unit;
- QmlIR::QmlUnitGenerator qmlGenerator;
- QV4::CompiledData::Unit *unitData = qmlGenerator.generate(irUnit);
- Q_ASSERT(!unit->data);
- // The js unit owns the data and will free the qml unit.
- unit->data = unitData;
+ QmlIR::QmlUnitGenerator qmlGenerator;
+ qmlGenerator.generate(irUnit);
+ }
if ((!disableDiskCache() || forceDiskCache()) && !isDebugging()) {
QString errorString;
- if (!unit->saveToDisk(url(), &errorString)) {
+ if (unit->saveToDisk(url(), &errorString)) {
+ QString error;
+ if (!unit->loadFromDisk(url(), data.sourceTimeStamp(), &error)) {
+ // ignore error, keep using the in-memory compilation unit.
+ }
+ } else {
qCDebug(DBG_DISK_CACHE()) << "Error saving cached version of" << unit->fileName() << "to disk:" << errorString;
}
}
@@ -3052,7 +3109,7 @@ void QQmlScriptBlob::dataReceived(const SourceCodeData &data)
void QQmlScriptBlob::initializeFromCachedUnit(const QV4::CompiledData::Unit *unit)
{
QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit;
- compilationUnit.adopt(new QV4::CompiledData::CompilationUnit(unit));
+ compilationUnit.adopt(new QV4::CompiledData::CompilationUnit(unit, urlString(), finalUrlString()));
initializeFromCompilationUnit(compilationUnit);
}
@@ -3078,33 +3135,36 @@ void QQmlScriptBlob::done()
}
}
- m_scriptData->typeNameCache = new QQmlTypeNameCache(m_importCache);
+ if (!m_isModule) {
+ m_scriptData->typeNameCache = new QQmlTypeNameCache(m_importCache);
- QSet<QString> ns;
+ QSet<QString> ns;
- for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) {
- const ScriptReference &script = m_scripts.at(scriptIndex);
+ for (int scriptIndex = 0; scriptIndex < m_scripts.count(); ++scriptIndex) {
+ const ScriptReference &script = m_scripts.at(scriptIndex);
- m_scriptData->scripts.append(script.script);
+ m_scriptData->scripts.append(script.script);
- if (!script.nameSpace.isNull()) {
- if (!ns.contains(script.nameSpace)) {
- ns.insert(script.nameSpace);
- m_scriptData->typeNameCache->add(script.nameSpace);
+ if (!script.nameSpace.isNull()) {
+ if (!ns.contains(script.nameSpace)) {
+ ns.insert(script.nameSpace);
+ m_scriptData->typeNameCache->add(script.nameSpace);
+ }
}
+ m_scriptData->typeNameCache->add(script.qualifier, scriptIndex, script.nameSpace);
}
- m_scriptData->typeNameCache->add(script.qualifier, scriptIndex, script.nameSpace);
- }
- m_importCache.populateCache(m_scriptData->typeNameCache);
+ m_importCache.populateCache(m_scriptData->typeNameCache);
+ }
+ m_scripts.clear();
}
QString QQmlScriptBlob::stringAt(int index) const
{
- return m_scriptData->m_precompiledScript->data->stringAt(index);
+ return m_scriptData->m_precompiledScript->stringAt(index);
}
-void QQmlScriptBlob::scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace)
+void QQmlScriptBlob::scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace)
{
ScriptReference ref;
ref.script = blob;
@@ -3118,30 +3178,45 @@ void QQmlScriptBlob::scriptImported(QQmlScriptBlob *blob, const QV4::CompiledDat
void QQmlScriptBlob::initializeFromCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit)
{
Q_ASSERT(!m_scriptData);
- m_scriptData = new QQmlScriptData();
+ m_scriptData.adopt(new QQmlScriptData());
m_scriptData->url = finalUrl();
m_scriptData->urlString = finalUrlString();
m_scriptData->m_precompiledScript = unit;
m_importCache.setBaseUrl(finalUrl(), finalUrlString());
- Q_ASSERT(m_scriptData->m_precompiledScript->data->flags & QV4::CompiledData::Unit::IsQml);
- const QV4::CompiledData::Unit *qmlUnit = m_scriptData->m_precompiledScript->data;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> script = m_scriptData->m_precompiledScript;
- QList<QQmlError> errors;
- for (quint32 i = 0; i < qmlUnit->nImports; ++i) {
- const QV4::CompiledData::Import *import = qmlUnit->importAt(i);
- if (!addImport(import, &errors)) {
- Q_ASSERT(errors.size());
- QQmlError error(errors.takeFirst());
- error.setUrl(m_importCache.baseUrl());
- error.setLine(import->location.line);
- error.setColumn(import->location.column);
- errors.prepend(error); // put it back on the list after filling out information.
- setError(errors);
- return;
+ if (!m_isModule) {
+ QList<QQmlError> errors;
+ for (quint32 i = 0, count = script->importCount(); i < count; ++i) {
+ const QV4::CompiledData::Import *import = script->importAt(i);
+ if (!addImport(import, &errors)) {
+ Q_ASSERT(errors.size());
+ QQmlError error(errors.takeFirst());
+ error.setUrl(m_importCache.baseUrl());
+ error.setLine(import->location.line);
+ error.setColumn(import->location.column);
+ errors.prepend(error); // put it back on the list after filling out information.
+ setError(errors);
+ return;
+ }
}
}
+
+ auto *v4 = QQmlEnginePrivate::getV4Engine(typeLoader()->engine());
+
+ v4->injectModule(unit);
+
+ for (const QString &request: unit->moduleRequests()) {
+ if (v4->moduleForUrl(QUrl(request), unit.data()))
+ continue;
+
+ const QUrl absoluteRequest = unit->finalUrl().resolved(QUrl(request));
+ QQmlRefPointer<QQmlScriptBlob> blob = typeLoader()->getScript(absoluteRequest);
+ addDependency(blob.data());
+ scriptImported(blob, /* ### */QV4::CompiledData::Location(), /*qualifier*/QString(), /*namespace*/QString());
+ }
}
QQmlQmldirData::QQmlQmldirData(const QUrl &url, QQmlTypeLoader *loader)
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index e75719866d..031554b330 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -217,7 +217,7 @@ private:
QList<QQmlDataBlob *> m_waitingOnMe;
// List of QQmlDataBlob's that I am waiting for to complete.
- QList<QQmlDataBlob *> m_waitingFor;
+ QVector<QQmlRefPointer<QQmlDataBlob>> m_waitingFor;
int m_redirectCount:30;
bool m_inCallback:1;
@@ -279,14 +279,13 @@ public:
bool addImport(const QV4::CompiledData::Import *import, QList<QQmlError> *errors);
bool fetchQmldir(const QUrl &url, const QV4::CompiledData::Import *import, int priority, QList<QQmlError> *errors);
- bool updateQmldir(QQmlQmldirData *data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors);
+ bool updateQmldir(const QQmlRefPointer<QQmlQmldirData> &data, const QV4::CompiledData::Import *import, QList<QQmlError> *errors);
private:
- virtual bool qmldirDataAvailable(QQmlQmldirData *, QList<QQmlError> *);
+ virtual bool qmldirDataAvailable(const QQmlRefPointer<QQmlQmldirData> &, QList<QQmlError> *);
- virtual void scriptImported(QQmlScriptBlob *, const QV4::CompiledData::Location &, const QString &, const QString &) {}
+ virtual void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &, const QV4::CompiledData::Location &, const QString &, const QString &) {}
- void dependencyError(QQmlDataBlob *) override;
void dependencyComplete(QQmlDataBlob *) override;
protected:
@@ -296,7 +295,7 @@ public:
QQmlImports m_importCache;
QHash<const QV4::CompiledData::Import*, int> m_unresolvedImports;
- QList<QQmlQmldirData *> m_qmldirs;
+ QVector<QQmlRefPointer<QQmlQmldirData>> m_qmldirs;
QQmlMetaType::CachedUnitLookupError m_cachedUnitStatus = QQmlMetaType::CachedUnitLookupError::NoError;
};
@@ -307,13 +306,14 @@ public:
static QUrl normalize(const QUrl &unNormalizedUrl);
- QQmlTypeData *getType(const QUrl &unNormalizedUrl, Mode mode = PreferSynchronous);
- QQmlTypeData *getType(const QByteArray &, const QUrl &url, Mode mode = PreferSynchronous);
+ QQmlRefPointer<QQmlTypeData> getType(const QUrl &unNormalizedUrl, Mode mode = PreferSynchronous);
+ QQmlRefPointer<QQmlTypeData> getType(const QByteArray &, const QUrl &url, Mode mode = PreferSynchronous);
- QQmlScriptBlob *getScript(const QUrl &unNormalizedUrl);
- QQmlQmldirData *getQmldir(const QUrl &);
+ QQmlRefPointer<QQmlScriptBlob> getScript(const QUrl &unNormalizedUrl);
+ QQmlRefPointer<QQmlQmldirData> getQmldir(const QUrl &);
QString absoluteFilePath(const QString &path);
+ bool fileExists(const QString &path, const QString &file);
bool directoryExists(const QString &path);
const QQmlTypeLoaderQmldirContent qmldirContent(const QString &filePath);
@@ -424,13 +424,13 @@ class Q_AUTOTEST_EXPORT QQmlTypeData : public QQmlTypeLoader::Blob
public:
struct TypeReference
{
- TypeReference() : majorVersion(0), minorVersion(0), typeData(nullptr), needsCreation(true) {}
+ TypeReference() : majorVersion(0), minorVersion(0), needsCreation(true) {}
QV4::CompiledData::Location location;
QQmlType type;
int majorVersion;
int minorVersion;
- QQmlTypeData *typeData;
+ QQmlRefPointer<QQmlTypeData> typeData;
QString prefix; // used by CompositeSingleton types
QString qualifiedName() const;
bool needsCreation;
@@ -438,11 +438,9 @@ public:
struct ScriptReference
{
- ScriptReference() : script(nullptr) {}
-
QV4::CompiledData::Location location;
QString qualifier;
- QQmlScriptBlob *script;
+ QQmlRefPointer<QQmlScriptBlob> script;
};
private:
@@ -495,7 +493,7 @@ private:
bool reportErrors = true,
QQmlType::RegistrationType registrationType = QQmlType::AnyRegistrationType);
- void scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override;
+ void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override;
SourceCodeData m_backupSourceCode; // used when cache verification fails.
@@ -541,10 +539,12 @@ public:
QUrl url;
QString urlString;
QQmlTypeNameCache *typeNameCache;
- QList<QQmlScriptBlob *> scripts;
+ QVector<QQmlRefPointer<QQmlScriptBlob>> scripts;
QV4::ReturnedValue scriptValueForContext(QQmlContextData *parentCtxt);
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit() const { return m_precompiledScript; }
+
protected:
void clear() override; // From QQmlCleanup
@@ -571,15 +571,13 @@ public:
struct ScriptReference
{
- ScriptReference() : script(nullptr) {}
-
QV4::CompiledData::Location location;
QString qualifier;
QString nameSpace;
- QQmlScriptBlob *script;
+ QQmlRefPointer<QQmlScriptBlob> script;
};
- QQmlScriptData *scriptData() const;
+ QQmlRefPointer<QQmlScriptData> scriptData() const;
protected:
void dataReceived(const SourceCodeData &) override;
@@ -589,11 +587,12 @@ protected:
QString stringAt(int index) const override;
private:
- void scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override;
+ void scriptImported(const QQmlRefPointer<QQmlScriptBlob> &blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override;
void initializeFromCompilationUnit(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &unit);
QList<ScriptReference> m_scripts;
- QQmlScriptData *m_scriptData;
+ QQmlRefPointer<QQmlScriptData> m_scriptData;
+ const bool m_isModule;
};
class Q_AUTOTEST_EXPORT QQmlQmldirData : public QQmlTypeLoader::Blob
diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp
index ce35e966aa..b58619c0d3 100644
--- a/src/qml/qml/qqmltypewrapper.cpp
+++ b/src/qml/qml/qqmltypewrapper.cpp
@@ -95,12 +95,17 @@ QObject* QQmlTypeWrapper::singletonObject() const
QVariant QQmlTypeWrapper::toVariant() const
{
- QObject *qobjectSingleton = singletonObject();
- if (qobjectSingleton)
+ // Only Singleton type wrappers can be converted to a variant.
+ if (!isSingleton())
+ return QVariant();
+
+ QQmlEngine *e = engine()->qmlEngine();
+ QQmlType::SingletonInstanceInfo *siinfo = d()->type().singletonInstanceInfo();
+ siinfo->init(e);
+ if (QObject *qobjectSingleton = siinfo->qobjectApi(e))
return QVariant::fromValue<QObject*>(qobjectSingleton);
- // only QObject Singleton Type can be converted to a variant.
- return QVariant();
+ return QVariant::fromValue<QJSValue>(siinfo->scriptApi(e));
}
@@ -111,7 +116,7 @@ ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o,
Q_ASSERT(t.isValid());
Scope scope(engine);
- Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocObject<QQmlTypeWrapper>());
+ Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>());
w->d()->mode = mode; w->d()->object = o;
w->d()->typePrivate = t.priv();
QQmlType::refHandle(w->d()->typePrivate);
@@ -120,15 +125,15 @@ ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o,
// Returns a type wrapper for importNamespace (of t) on o. This allows nested resolution of a type in a
// namespace.
-ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, QQmlTypeNameCache *t, const QQmlImportRef *importNamespace,
+ReturnedValue QQmlTypeWrapper::create(QV4::ExecutionEngine *engine, QObject *o, const QQmlRefPointer<QQmlTypeNameCache> &t, const QQmlImportRef *importNamespace,
Heap::QQmlTypeWrapper::TypeNameMode mode)
{
Q_ASSERT(t);
Q_ASSERT(importNamespace);
Scope scope(engine);
- Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocObject<QQmlTypeWrapper>());
- w->d()->mode = mode; w->d()->object = o; w->d()->typeNamespace = t; w->d()->importNamespace = importNamespace;
+ Scoped<QQmlTypeWrapper> w(scope, engine->memoryManager->allocate<QQmlTypeWrapper>());
+ w->d()->mode = mode; w->d()->object = o; w->d()->typeNamespace = t.data(); w->d()->importNamespace = importNamespace;
t->addref();
return w.asReturnedValue();
}
@@ -162,12 +167,16 @@ static ReturnedValue throwLowercaseEnumError(QV4::ExecutionEngine *v4, String *n
return v4->throwTypeError(message);
}
-ReturnedValue QQmlTypeWrapper::get(const Managed *m, String *name, bool *hasProperty)
+ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<QQmlTypeWrapper>());
+ if (!id.isString())
+ return Object::virtualGet(m, id, receiver, hasProperty);
+
QV4::ExecutionEngine *v4 = static_cast<const QQmlTypeWrapper *>(m)->engine();
QV4::Scope scope(v4);
+ ScopedString name(scope, id.asStringOrSymbol());
Scoped<QQmlTypeWrapper> w(scope, static_cast<const QQmlTypeWrapper *>(m));
@@ -196,7 +205,7 @@ ReturnedValue QQmlTypeWrapper::get(const Managed *m, String *name, bool *hasProp
bool ok = false;
const int value = enumForSingleton(v4, name, qobjectSingleton, type, &ok);
if (ok)
- return QV4::Primitive::fromInt32(value).asReturnedValue();
+ return QV4::Value::fromInt32(value).asReturnedValue();
}
// check for property.
@@ -228,11 +237,11 @@ ReturnedValue QQmlTypeWrapper::get(const Managed *m, String *name, bool *hasProp
bool ok = false;
int value = type.enumValue(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok);
if (ok)
- return QV4::Primitive::fromInt32(value).asReturnedValue();
+ return QV4::Value::fromInt32(value).asReturnedValue();
value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok);
if (ok) {
- Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocObject<QQmlScopedEnumWrapper>());
+ Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>());
enumWrapper->d()->typePrivate = type.priv();
QQmlType::refHandle(enumWrapper->d()->typePrivate);
enumWrapper->d()->scopeEnumIndex = value;
@@ -263,7 +272,7 @@ ReturnedValue QQmlTypeWrapper::get(const Managed *m, String *name, bool *hasProp
return create(scope.engine, object, r.type, w->d()->mode);
} else if (r.scriptIndex != -1) {
QV4::ScopedObject scripts(scope, context->importedScripts.valueRef());
- return scripts->getIndexed(r.scriptIndex);
+ return scripts->get(r.scriptIndex);
} else if (r.importNamespace) {
return create(scope.engine, object, context->imports, r.importNamespace);
}
@@ -279,7 +288,7 @@ ReturnedValue QQmlTypeWrapper::get(const Managed *m, String *name, bool *hasProp
}
bool ok = false;
- const ReturnedValue result = Object::get(m, name, &ok);
+ const ReturnedValue result = Object::virtualGet(m, id, receiver, &ok);
if (hasProperty)
*hasProperty = ok;
@@ -295,16 +304,20 @@ ReturnedValue QQmlTypeWrapper::get(const Managed *m, String *name, bool *hasProp
}
-bool QQmlTypeWrapper::put(Managed *m, String *name, const Value &value)
+bool QQmlTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
+ if (!id.isString())
+ return Object::virtualPut(m, id, value, receiver);
+
+
Q_ASSERT(m->as<QQmlTypeWrapper>());
QQmlTypeWrapper *w = static_cast<QQmlTypeWrapper *>(m);
- QV4::ExecutionEngine *v4 = w->engine();
- if (v4->hasException)
+ QV4::Scope scope(w);
+ if (scope.engine->hasException)
return false;
- QV4::Scope scope(v4);
- QQmlContextData *context = v4->callingQmlContext();
+ ScopedString name(scope, id.asStringOrSymbol());
+ QQmlContextData *context = scope.engine->callingQmlContext();
QQmlType type = w->d()->type();
if (type.isValid() && !type.isSingleton() && w->d()->object) {
@@ -312,7 +325,7 @@ bool QQmlTypeWrapper::put(Managed *m, String *name, const Value &value)
QQmlEngine *e = scope.engine->qmlEngine();
QObject *ao = qmlAttachedPropertiesObjectById(type.attachedPropertiesId(QQmlEnginePrivate::get(e)), object);
if (ao)
- return QV4::QObjectWrapper::setQmlProperty(v4, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value);
+ return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value);
return false;
} else if (type.isSingleton()) {
QQmlEngine *e = scope.engine->qmlEngine();
@@ -321,12 +334,12 @@ bool QQmlTypeWrapper::put(Managed *m, String *name, const Value &value)
QObject *qobjectSingleton = siinfo->qobjectApi(e);
if (qobjectSingleton) {
- return QV4::QObjectWrapper::setQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value);
+ return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value);
} else if (!siinfo->scriptApi(e).isUndefined()) {
- QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e)));
+ QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(scope.engine, siinfo->scriptApi(e)));
if (!apiprivate) {
QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"');
- v4->throwError(error);
+ scope.engine->throwError(error);
return false;
} else {
return apiprivate->put(name, value);
@@ -337,15 +350,21 @@ bool QQmlTypeWrapper::put(Managed *m, String *name, const Value &value)
return false;
}
-PropertyAttributes QQmlTypeWrapper::query(const Managed *m, String *name)
+PropertyAttributes QQmlTypeWrapper::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
{
- // ### Implement more efficiently.
- bool hasProperty = false;
- static_cast<Object *>(const_cast<Managed*>(m))->get(name, &hasProperty);
- return hasProperty ? Attr_Data : Attr_Invalid;
+ if (id.isString()) {
+ Scope scope(m);
+ ScopedString n(scope, id.asStringOrSymbol());
+ // ### Implement more efficiently.
+ bool hasProperty = false;
+ static_cast<const Object *>(m)->get(n, &hasProperty);
+ return hasProperty ? Attr_Data : Attr_Invalid;
+ }
+
+ return QV4::Object::virtualGetOwnProperty(m, id, p);
}
-bool QQmlTypeWrapper::isEqualTo(Managed *a, Managed *b)
+bool QQmlTypeWrapper::virtualIsEqualTo(Managed *a, Managed *b)
{
Q_ASSERT(a->as<QV4::QQmlTypeWrapper>());
QV4::QQmlTypeWrapper *qmlTypeWrapperA = static_cast<QV4::QQmlTypeWrapper *>(a);
@@ -357,7 +376,7 @@ bool QQmlTypeWrapper::isEqualTo(Managed *a, Managed *b)
return false;
}
-ReturnedValue QQmlTypeWrapper::instanceOf(const Object *typeObject, const Value &var)
+ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const Value &var)
{
Q_ASSERT(typeObject->as<QV4::QQmlTypeWrapper>());
const QV4::QQmlTypeWrapper *typeWrapper = static_cast<const QV4::QQmlTypeWrapper *>(typeObject);
@@ -385,10 +404,9 @@ ReturnedValue QQmlTypeWrapper::instanceOf(const Object *typeObject, const Value
if (!theirDData->compilationUnit)
return Encode(false);
- QQmlTypeData *td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl());
+ QQmlRefPointer<QQmlTypeData> td = qenginepriv->typeLoader.getType(typeWrapper->d()->type().sourceUrl());
CompiledData::CompilationUnit *cu = td->compilationUnit();
myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId);
- td->release();
} else {
myQmlType = qenginepriv->metaObjectForType(myTypeId);
}
@@ -410,12 +428,16 @@ QQmlType Heap::QQmlScopedEnumWrapper::type() const
return QQmlType(typePrivate);
}
-ReturnedValue QQmlScopedEnumWrapper::get(const Managed *m, String *name, bool *hasProperty)
+ReturnedValue QQmlScopedEnumWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<QQmlScopedEnumWrapper>());
+ if (!id.isString())
+ return Object::virtualGet(m, id, receiver, hasProperty);
+
const QQmlScopedEnumWrapper *resource = static_cast<const QQmlScopedEnumWrapper *>(m);
QV4::ExecutionEngine *v4 = resource->engine();
QV4::Scope scope(v4);
+ ScopedString name(scope, id.asStringOrSymbol());
QQmlType type = resource->d()->type();
int index = resource->d()->scopeEnumIndex;
@@ -425,7 +447,7 @@ ReturnedValue QQmlScopedEnumWrapper::get(const Managed *m, String *name, bool *h
if (hasProperty)
*hasProperty = ok;
if (ok)
- return QV4::Primitive::fromInt32(value).asReturnedValue();
+ return QV4::Value::fromInt32(value).asReturnedValue();
return Encode::undefined();
}
diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h
index 25ff7ba7c8..fa1ad56902 100644
--- a/src/qml/qml/qqmltypewrapper_p.h
+++ b/src/qml/qml/qqmltypewrapper_p.h
@@ -108,15 +108,15 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object
static ReturnedValue create(ExecutionEngine *, QObject *, const QQmlType &,
Heap::QQmlTypeWrapper::TypeNameMode = Heap::QQmlTypeWrapper::IncludeEnums);
- static ReturnedValue create(ExecutionEngine *, QObject *, QQmlTypeNameCache *, const QQmlImportRef *,
+ static ReturnedValue create(ExecutionEngine *, QObject *, const QQmlRefPointer<QQmlTypeNameCache> &, const QQmlImportRef *,
Heap::QQmlTypeWrapper::TypeNameMode = Heap::QQmlTypeWrapper::IncludeEnums);
-
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static bool put(Managed *m, String *name, const Value &value);
- static PropertyAttributes query(const Managed *, String *name);
- static bool isEqualTo(Managed *that, Managed *o);
- static ReturnedValue instanceOf(const Object *typeObject, const Value &var);
+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);
+ static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p);
+ static bool virtualIsEqualTo(Managed *that, Managed *o);
+ static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var);
};
struct Q_QML_EXPORT QQmlScopedEnumWrapper : Object
@@ -124,7 +124,7 @@ struct Q_QML_EXPORT QQmlScopedEnumWrapper : Object
V4_OBJECT2(QQmlScopedEnumWrapper, Object)
V4_NEEDS_DESTROY
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
+ static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
};
}
diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp
index 270414a676..2b21591017 100644
--- a/src/qml/qml/qqmlvaluetype.cpp
+++ b/src/qml/qml/qqmlvaluetype.cpp
@@ -67,8 +67,7 @@ struct QQmlValueTypeFactoryImpl
QQmlValueTypeFactoryImpl::QQmlValueTypeFactoryImpl()
{
- for (unsigned int ii = 0; ii < QVariant::UserType; ++ii)
- valueTypes[ii] = nullptr;
+ std::fill_n(valueTypes, int(QVariant::UserType), nullptr);
// See types wrapped in qqmlmodelindexvaluetype_p.h
qRegisterMetaType<QItemSelectionRange>();
@@ -521,38 +520,33 @@ void QQmlEasingValueType::setBezierCurve(const QVariantList &customCurveVariant)
if (customCurveVariant.isEmpty())
return;
- QVariantList variantList = customCurveVariant;
- if ((variantList.count() % 6) == 0) {
- bool allRealsOk = true;
- QVector<qreal> reals;
- const int variantListCount = variantList.count();
- reals.reserve(variantListCount);
- for (int i = 0; i < variantListCount; i++) {
- bool ok;
- const qreal real = variantList.at(i).toReal(&ok);
- reals.append(real);
- if (!ok)
- allRealsOk = false;
- }
- if (allRealsOk) {
- QEasingCurve newEasingCurve(QEasingCurve::BezierSpline);
- for (int i = 0; i < reals.count() / 6; i++) {
- const qreal c1x = reals.at(i * 6);
- const qreal c1y = reals.at(i * 6 + 1);
- const qreal c2x = reals.at(i * 6 + 2);
- const qreal c2y = reals.at(i * 6 + 3);
- const qreal c3x = reals.at(i * 6 + 4);
- const qreal c3y = reals.at(i * 6 + 5);
-
- const QPointF c1(c1x, c1y);
- const QPointF c2(c2x, c2y);
- const QPointF c3(c3x, c3y);
-
- newEasingCurve.addCubicBezierSegment(c1, c2, c3);
- v = newEasingCurve;
- }
- }
+ if ((customCurveVariant.count() % 6) != 0)
+ return;
+
+ auto convert = [](const QVariant &v, qreal &r) {
+ bool ok;
+ r = v.toReal(&ok);
+ return ok;
+ };
+
+ QEasingCurve newEasingCurve(QEasingCurve::BezierSpline);
+ for (int i = 0, ei = customCurveVariant.size(); i < ei; i += 6) {
+ qreal c1x, c1y, c2x, c2y, c3x, c3y;
+ if (!convert(customCurveVariant.at(i ), c1x)) return;
+ if (!convert(customCurveVariant.at(i + 1), c1y)) return;
+ if (!convert(customCurveVariant.at(i + 2), c2x)) return;
+ if (!convert(customCurveVariant.at(i + 3), c2y)) return;
+ if (!convert(customCurveVariant.at(i + 4), c3x)) return;
+ if (!convert(customCurveVariant.at(i + 5), c3y)) return;
+
+ const QPointF c1(c1x, c1y);
+ const QPointF c2(c2x, c2y);
+ const QPointF c3(c3x, c3y);
+
+ newEasingCurve.addCubicBezierSegment(c1, c2, c3);
}
+
+ v = newEasingCurve;
}
QVariantList QQmlEasingValueType::bezierCurve() const
diff --git a/src/qml/qml/qqmlvaluetypeproxybinding_p.h b/src/qml/qml/qqmlvaluetypeproxybinding_p.h
index ba0d305bd9..35b54c339b 100644
--- a/src/qml/qml/qqmlvaluetypeproxybinding_p.h
+++ b/src/qml/qml/qqmlvaluetypeproxybinding_p.h
@@ -55,7 +55,7 @@
QT_BEGIN_NAMESPACE
-class QQmlValueTypeProxyBinding : public QQmlAbstractBinding
+class Q_AUTOTEST_EXPORT QQmlValueTypeProxyBinding : public QQmlAbstractBinding
{
public:
QQmlValueTypeProxyBinding(QObject *o, QQmlPropertyIndex coreIndex);
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index 6196a09d94..b503d75a47 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -39,7 +39,6 @@
#include "qqmlvaluetypewrapper_p.h"
#include <private/qv8engine_p.h>
-
#include <private/qqmlvaluetype_p.h>
#include <private/qqmlbinding_p.h>
#include <private/qqmlglobal_p.h>
@@ -49,6 +48,7 @@
#include <private/qv4functionobject_p.h>
#include <private/qv4variantobject_p.h>
#include <private/qv4alloca_p.h>
+#include <private/qv4stackframe_p.h>
#include <private/qv4objectiterator_p.h>
#include <private/qv4qobjectwrapper_p.h>
#include <QtCore/qloggingcategory.h>
@@ -186,7 +186,7 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, QObject *obj
Scope scope(engine);
initProto(engine);
- Scoped<QQmlValueTypeReference> r(scope, engine->memoryManager->allocObject<QQmlValueTypeReference>());
+ Scoped<QQmlValueTypeReference> r(scope, engine->memoryManager->allocate<QQmlValueTypeReference>());
r->d()->object = object;
r->d()->property = property;
r->d()->setPropertyCache(QJSEnginePrivate::get(engine)->cache(metaObject));
@@ -200,7 +200,7 @@ ReturnedValue QQmlValueTypeWrapper::create(ExecutionEngine *engine, const QVaria
Scope scope(engine);
initProto(engine);
- Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocObject<QQmlValueTypeWrapper>());
+ Scoped<QQmlValueTypeWrapper> r(scope, engine->memoryManager->allocate<QQmlValueTypeWrapper>());
r->d()->setPropertyCache(QJSEnginePrivate::get(engine)->cache(metaObject));
r->d()->valueType = QQmlValueTypeFactory::valueType(typeId);
r->d()->gadgetPtr = nullptr;
@@ -227,7 +227,7 @@ bool QQmlValueTypeWrapper::toGadget(void *data) const
return true;
}
-bool QQmlValueTypeWrapper::isEqualTo(Managed *m, Managed *other)
+bool QQmlValueTypeWrapper::virtualIsEqualTo(Managed *m, Managed *other)
{
Q_ASSERT(m && m->as<QQmlValueTypeWrapper>() && other);
QV4::QQmlValueTypeWrapper *lv = static_cast<QQmlValueTypeWrapper *>(m);
@@ -241,41 +241,58 @@ bool QQmlValueTypeWrapper::isEqualTo(Managed *m, Managed *other)
return false;
}
-PropertyAttributes QQmlValueTypeWrapper::query(const Managed *m, String *name)
+PropertyAttributes QQmlValueTypeWrapper::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
{
- Q_ASSERT(m->as<const QQmlValueTypeWrapper>());
- const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
+ if (id.isString()) {
+ Scope scope(m);
+ ScopedString n(scope, id.asStringOrSymbol());
+ const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
+ QQmlPropertyData *result = r->d()->propertyCache()->property(n.getPointer(), nullptr, nullptr);
+ return result ? Attr_Data : Attr_Invalid;
+ }
- QQmlPropertyData *result = r->d()->propertyCache()->property(name, nullptr, nullptr);
- return result ? Attr_Data : Attr_Invalid;
+ return QV4::Object::virtualGetOwnProperty(m, id, p);
}
-void QQmlValueTypeWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
+struct QQmlValueTypeWrapperOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
{
- name->setM(nullptr);
- *index = UINT_MAX;
+ int propertyIndex = 0;
+ ~QQmlValueTypeWrapperOwnPropertyKeyIterator() override = default;
+ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
+
+};
- QQmlValueTypeWrapper *that = static_cast<QQmlValueTypeWrapper*>(m);
+PropertyKey QQmlValueTypeWrapperOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) {
+ const QQmlValueTypeWrapper *that = static_cast<const QQmlValueTypeWrapper *>(o);
- if (QQmlValueTypeReference *ref = that->as<QQmlValueTypeReference>()) {
+ if (const QQmlValueTypeReference *ref = that->as<QQmlValueTypeReference>()) {
if (!ref->readReferenceValue())
- return;
+ return PropertyKey::invalid();
}
if (that->d()->propertyCache()) {
const QMetaObject *mo = that->d()->propertyCache()->createMetaObject();
const int propertyCount = mo->propertyCount();
- if (it->arrayIndex < static_cast<uint>(propertyCount)) {
+ if (propertyIndex < propertyCount) {
Scope scope(that->engine());
- ScopedString propName(scope, that->engine()->newString(QString::fromUtf8(mo->property(it->arrayIndex).name())));
- name->setM(propName->d());
- ++it->arrayIndex;
- *attributes = QV4::Attr_Data;
- p->value = that->QV4::Object::get(propName);
- return;
+ ScopedString propName(scope, that->engine()->newString(QString::fromUtf8(mo->property(propertyIndex).name())));
+ ++propertyIndex;
+ if (attrs)
+ *attrs = QV4::Attr_Data;
+ if (pd)
+ pd->value = that->QV4::Object::get(propName);
+ return propName->toPropertyKey();
}
}
- QV4::Object::advanceIterator(m, it, name, index, p, attributes);
+
+ return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
+}
+
+
+OwnPropertyKeyIterator *QQmlValueTypeWrapper::virtualOwnPropertyKeys(const Object *m, Value *target)
+{
+ *target = *m;
+ return new QQmlValueTypeWrapperOwnPropertyKeyIterator;
}
bool QQmlValueTypeWrapper::isEqual(const QVariant& value) const
@@ -355,21 +372,27 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, con
return Encode(b->engine()->newString(result));
}
-ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *hasProperty)
+ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<QQmlValueTypeWrapper>());
+
+ if (!id.isString())
+ return Object::virtualGet(m, id, receiver, hasProperty);
+
const QQmlValueTypeWrapper *r = static_cast<const QQmlValueTypeWrapper *>(m);
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 Primitive::undefinedValue().asReturnedValue();
+ return Value::undefinedValue().asReturnedValue();
}
- QQmlPropertyData *result = r->d()->propertyCache()->property(name, nullptr, nullptr);
+ QQmlPropertyData *result = r->d()->propertyCache()->property(name.getPointer(), nullptr, nullptr);
if (!result)
- return Object::get(m, name, hasProperty);
+ return Object::virtualGet(m, id, receiver, hasProperty);
if (hasProperty)
*hasProperty = true;
@@ -413,8 +436,11 @@ ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *ha
#undef VALUE_TYPE_ACCESSOR
}
-bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value)
+bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
+ if (!id.isString())
+ return Object::virtualPut(m, id, value, receiver);
+
Q_ASSERT(m->as<QQmlValueTypeWrapper>());
ExecutionEngine *v4 = static_cast<QQmlValueTypeWrapper *>(m)->engine();
Scope scope(v4);
@@ -435,8 +461,10 @@ bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value)
writeBackPropertyType = writebackProperty.userType();
}
+ ScopedString name(scope, id.asStringOrSymbol());
+
const QMetaObject *metaObject = r->d()->propertyCache()->metaObject();
- const QQmlPropertyData *pd = r->d()->propertyCache()->property(name, nullptr, nullptr);
+ const QQmlPropertyData *pd = r->d()->propertyCache()->property(name.getPointer(), nullptr, nullptr);
if (!pd)
return false;
diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h
index f99d207d68..8db9474132 100644
--- a/src/qml/qml/qqmlvaluetypewrapper_p.h
+++ b/src/qml/qml/qqmlvaluetypewrapper_p.h
@@ -106,12 +106,11 @@ public:
int typeId() const;
bool write(QObject *target, int propertyIndex) const;
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static bool put(Managed *m, String *name, const Value &value);
- static bool isEqualTo(Managed *m, Managed *other);
- static PropertyAttributes query(const Managed *, String *name);
- static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
-
+ 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 bool virtualIsEqualTo(Managed *m, Managed *other);
+ 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 void initProto(ExecutionEngine *v4);
diff --git a/src/qml/qml/qqmlvme_p.h b/src/qml/qml/qqmlvme_p.h
index 9a94ac6258..13c5524d96 100644
--- a/src/qml/qml/qqmlvme_p.h
+++ b/src/qml/qml/qqmlvme_p.h
@@ -124,6 +124,10 @@ private:
// Used to check that a QQmlVME that is interrupted mid-execution
// is still valid. Checks all the objects and contexts have not been
// deleted.
+//
+// VME stands for Virtual Machine Execution. QML files used to
+// be compiled to a byte code data structure that a virtual machine executed
+// (for constructing the tree of QObjects and setting properties).
class QQmlVMEGuard
{
public:
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index c1d3980b58..6bc469c836 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -106,8 +106,8 @@ void QQmlVMEVariantQObjectPtr::objectDestroyed(QObject *)
QV4::Scope scope(v4);
QV4::Scoped<QV4::MemberData> sp(scope, m_target->propertyAndMethodStorage.value());
if (sp) {
- QV4::MemberData::Index index{ sp->d(), sp->d()->values.values + m_index };
- index.set(v4, QV4::Primitive::nullValue());
+ QV4::PropertyIndex index{ sp->d(), sp->d()->values.values + m_index };
+ index.set(v4, QV4::Value::nullValue());
}
}
@@ -176,7 +176,7 @@ void QQmlVMEMetaObjectEndpoint::tryConnect()
}
-QQmlInterceptorMetaObject::QQmlInterceptorMetaObject(QObject *obj, QQmlPropertyCache *cache)
+QQmlInterceptorMetaObject::QQmlInterceptorMetaObject(QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache)
: object(obj),
cache(cache),
interceptors(nullptr),
@@ -316,7 +316,7 @@ QAbstractDynamicMetaObject *QQmlInterceptorMetaObject::toDynamicMetaObject(QObje
QQmlVMEMetaObject::QQmlVMEMetaObject(QV4::ExecutionEngine *engine,
QObject *obj,
- QQmlPropertyCache *cache, QV4::CompiledData::CompilationUnit *qmlCompilationUnit, int qmlObjectId)
+ const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &qmlCompilationUnit, int qmlObjectId)
: QQmlInterceptorMetaObject(obj, cache),
engine(engine),
ctxt(QQmlData::get(obj, true)->outerContext),
@@ -326,7 +326,7 @@ QQmlVMEMetaObject::QQmlVMEMetaObject(QV4::ExecutionEngine *engine,
QQmlData::get(obj)->hasVMEMetaObject = true;
if (compilationUnit && qmlObjectId >= 0) {
- compiledObject = compilationUnit->data->objectAt(qmlObjectId);
+ compiledObject = compilationUnit->objectAt(qmlObjectId);
if (compiledObject->nProperties || compiledObject->nFunctions) {
uint size = compiledObject->nProperties + compiledObject->nFunctions;
@@ -368,21 +368,21 @@ void QQmlVMEMetaObject::writeProperty(int id, int v)
{
QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
- md->set(engine, id, QV4::Primitive::fromInt32(v));
+ md->set(engine, id, QV4::Value::fromInt32(v));
}
void QQmlVMEMetaObject::writeProperty(int id, bool v)
{
QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
- md->set(engine, id, QV4::Primitive::fromBoolean(v));
+ md->set(engine, id, QV4::Value::fromBoolean(v));
}
void QQmlVMEMetaObject::writeProperty(int id, double v)
{
QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
- md->set(engine, id, QV4::Primitive::fromDouble(v));
+ md->set(engine, id, QV4::Value::fromDouble(v));
}
void QQmlVMEMetaObject::writeProperty(int id, const QString& v)
@@ -1005,7 +1005,7 @@ QV4::ReturnedValue QQmlVMEMetaObject::readVarProperty(int id) const
QV4::MemberData *md = propertyAndMethodStorageAsMemberData();
if (md)
return (md->data() + id)->asReturnedValue();
- return QV4::Primitive::undefinedValue().asReturnedValue();
+ return QV4::Value::undefinedValue().asReturnedValue();
}
QVariant QQmlVMEMetaObject::readPropertyAsVariant(int id) const
@@ -1122,7 +1122,7 @@ QV4::ReturnedValue QQmlVMEMetaObject::vmeMethod(int index) const
return parentVMEMetaObject()->vmeMethod(index);
}
if (!compiledObject)
- return QV4::Primitive::undefinedValue().asReturnedValue();
+ return QV4::Value::undefinedValue().asReturnedValue();
const int plainSignals = compiledObject->nSignals + compiledObject->nProperties + compiledObject->nAliases;
Q_ASSERT(index >= (methodOffset() + plainSignals) && index < (methodOffset() + plainSignals + int(compiledObject->nFunctions)));
return method(index - methodOffset() - plainSignals);
diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h
index 0c82686d47..dbcc9d2884 100644
--- a/src/qml/qml/qqmlvmemetaobject_p.h
+++ b/src/qml/qml/qqmlvmemetaobject_p.h
@@ -94,7 +94,7 @@ public:
class Q_QML_PRIVATE_EXPORT QQmlInterceptorMetaObject : public QAbstractDynamicMetaObject
{
public:
- QQmlInterceptorMetaObject(QObject *obj, QQmlPropertyCache *cache);
+ QQmlInterceptorMetaObject(QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache);
~QQmlInterceptorMetaObject() override;
void registerInterceptor(QQmlPropertyIndex index, QQmlPropertyValueInterceptor *interceptor);
@@ -104,7 +104,7 @@ public:
QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *o) override;
// Used by auto-tests for inspection
- QQmlPropertyCache *propertyCache() const { return cache; }
+ QQmlPropertyCache *propertyCache() const { return cache.data(); }
bool intercepts(QQmlPropertyIndex propertyIndex) const
{
@@ -146,7 +146,7 @@ class QQmlVMEMetaObjectEndpoint;
class Q_QML_PRIVATE_EXPORT QQmlVMEMetaObject : public QQmlInterceptorMetaObject
{
public:
- QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, QQmlPropertyCache *cache, QV4::CompiledData::CompilationUnit *qmlCompilationUnit, int qmlObjectId);
+ QQmlVMEMetaObject(QV4::ExecutionEngine *engine, QObject *obj, const QQmlRefPointer<QQmlPropertyCache> &cache, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &qmlCompilationUnit, int qmlObjectId);
~QQmlVMEMetaObject() override;
bool aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const;
diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp
index 567d83f3ee..9f629f974d 100644
--- a/src/qml/qml/qqmlxmlhttprequest.cpp
+++ b/src/qml/qml/qqmlxmlhttprequest.cpp
@@ -228,8 +228,7 @@ public:
static ReturnedValue create(ExecutionEngine *, NodeImpl *, const QList<NodeImpl *> &);
// JS API
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty);
+ static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
};
void Heap::NamedNodeMap::init(NodeImpl *data, const QList<NodeImpl *> &list)
@@ -250,8 +249,7 @@ public:
V4_NEEDS_DESTROY
// JS API
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty);
+ static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
// C++ API
static ReturnedValue create(ExecutionEngine *, NodeImpl *);
@@ -595,7 +593,7 @@ ReturnedValue NodePrototype::getProto(ExecutionEngine *v4)
Scope scope(v4);
QQmlXMLHttpRequestData *d = xhrdata(v4);
if (d->nodePrototype.isUndefined()) {
- ScopedObject p(scope, v4->memoryManager->allocObject<NodePrototype>());
+ ScopedObject p(scope, v4->memoryManager->allocate<NodePrototype>());
d->nodePrototype.set(v4, p);
v4->v8Engine->freezeObject(p);
}
@@ -606,12 +604,12 @@ ReturnedValue Node::create(ExecutionEngine *v4, NodeImpl *data)
{
Scope scope(v4);
- Scoped<Node> instance(scope, v4->memoryManager->allocObject<Node>(data));
+ Scoped<Node> instance(scope, v4->memoryManager->allocate<Node>(data));
ScopedObject p(scope);
switch (data->type) {
case NodeImpl::Attr:
- instance->setPrototype((p = Attr::prototype(v4)));
+ instance->setPrototypeUnchecked((p = Attr::prototype(v4)));
break;
case NodeImpl::Comment:
case NodeImpl::Document:
@@ -623,13 +621,13 @@ ReturnedValue Node::create(ExecutionEngine *v4, NodeImpl *data)
case NodeImpl::ProcessingInstruction:
return Encode::undefined();
case NodeImpl::CDATA:
- instance->setPrototype((p = CDATA::prototype(v4)));
+ instance->setPrototypeUnchecked((p = CDATA::prototype(v4)));
break;
case NodeImpl::Text:
- instance->setPrototype((p = Text::prototype(v4)));
+ instance->setPrototypeUnchecked((p = Text::prototype(v4)));
break;
case NodeImpl::Element:
- instance->setPrototype((p = Element::prototype(v4)));
+ instance->setPrototypeUnchecked((p = Element::prototype(v4)));
break;
}
@@ -643,7 +641,7 @@ ReturnedValue Element::prototype(ExecutionEngine *engine)
Scope scope(engine);
ScopedObject p(scope, engine->newObject());
ScopedObject pp(scope);
- p->setPrototype((pp = NodePrototype::getProto(engine)));
+ p->setPrototypeUnchecked((pp = NodePrototype::getProto(engine)));
p->defineAccessorProperty(QStringLiteral("tagName"), NodePrototype::method_get_nodeName, nullptr);
d->elementPrototype.set(engine, p);
engine->v8Engine->freezeObject(p);
@@ -658,7 +656,7 @@ ReturnedValue Attr::prototype(ExecutionEngine *engine)
Scope scope(engine);
ScopedObject p(scope, engine->newObject());
ScopedObject pp(scope);
- p->setPrototype((pp = NodePrototype::getProto(engine)));
+ p->setPrototypeUnchecked((pp = NodePrototype::getProto(engine)));
p->defineAccessorProperty(QStringLiteral("name"), method_name, nullptr);
p->defineAccessorProperty(QStringLiteral("value"), method_value, nullptr);
p->defineAccessorProperty(QStringLiteral("ownerElement"), method_ownerElement, nullptr);
@@ -715,7 +713,7 @@ ReturnedValue CharacterData::prototype(ExecutionEngine *v4)
Scope scope(v4);
ScopedObject p(scope, v4->newObject());
ScopedObject pp(scope);
- p->setPrototype((pp = NodePrototype::getProto(v4)));
+ p->setPrototypeUnchecked((pp = NodePrototype::getProto(v4)));
p->defineAccessorProperty(QStringLiteral("data"), NodePrototype::method_get_nodeValue, nullptr);
p->defineAccessorProperty(QStringLiteral("length"), method_length, nullptr);
d->characterDataPrototype.set(v4, p);
@@ -751,7 +749,7 @@ ReturnedValue Text::prototype(ExecutionEngine *v4)
Scope scope(v4);
ScopedObject p(scope, v4->newObject());
ScopedObject pp(scope);
- p->setPrototype((pp = CharacterData::prototype(v4)));
+ p->setPrototypeUnchecked((pp = CharacterData::prototype(v4)));
p->defineAccessorProperty(QStringLiteral("isElementContentWhitespace"), method_isElementContentWhitespace, nullptr);
p->defineAccessorProperty(QStringLiteral("wholeText"), method_wholeText, nullptr);
d->textPrototype.set(v4, p);
@@ -768,7 +766,7 @@ ReturnedValue CDATA::prototype(ExecutionEngine *v4)
Scope scope(v4);
ScopedObject p(scope, v4->newObject());
ScopedObject pp(scope);
- p->setPrototype((pp = Text::prototype(v4)));
+ p->setPrototypeUnchecked((pp = Text::prototype(v4)));
d->cdataPrototype.set(v4, p);
v4->v8Engine->freezeObject(p);
}
@@ -782,7 +780,7 @@ ReturnedValue Document::prototype(ExecutionEngine *v4)
Scope scope(v4);
ScopedObject p(scope, v4->newObject());
ScopedObject pp(scope);
- p->setPrototype((pp = NodePrototype::getProto(v4)));
+ p->setPrototypeUnchecked((pp = NodePrototype::getProto(v4)));
p->defineAccessorProperty(QStringLiteral("xmlVersion"), method_xmlVersion, nullptr);
p->defineAccessorProperty(QStringLiteral("xmlEncoding"), method_xmlEncoding, nullptr);
p->defineAccessorProperty(QStringLiteral("xmlStandalone"), method_xmlStandalone, nullptr);
@@ -876,10 +874,10 @@ ReturnedValue Document::load(ExecutionEngine *v4, const QByteArray &data)
return Encode::null();
}
- ScopedObject instance(scope, v4->memoryManager->allocObject<Node>(document));
+ ScopedObject instance(scope, v4->memoryManager->allocate<Node>(document));
document->release(); // the GC should own the NodeImpl via Node now
ScopedObject p(scope);
- instance->setPrototype((p = Document::prototype(v4)));
+ instance->setPrototypeUnchecked((p = Document::prototype(v4)));
return instance.asReturnedValue();
}
@@ -888,33 +886,33 @@ bool Node::isNull() const
return d()->d == nullptr;
}
-ReturnedValue NamedNodeMap::getIndexed(const Managed *m, uint index, bool *hasProperty)
+ReturnedValue NamedNodeMap::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<NamedNodeMap>());
+
const NamedNodeMap *r = static_cast<const NamedNodeMap *>(m);
QV4::ExecutionEngine *v4 = r->engine();
- if ((int)index < r->d()->list().count()) {
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+
+ if ((int)index < r->d()->list().count()) {
+ if (hasProperty)
+ *hasProperty = true;
+ return Node::create(v4, r->d()->list().at(index));
+ }
if (hasProperty)
- *hasProperty = true;
- return Node::create(v4, r->d()->list().at(index));
+ *hasProperty = false;
+ return Encode::undefined();
}
- if (hasProperty)
- *hasProperty = false;
- return Encode::undefined();
-}
-ReturnedValue NamedNodeMap::get(const Managed *m, String *name, bool *hasProperty)
-{
- Q_ASSERT(m->as<NamedNodeMap>());
- const NamedNodeMap *r = static_cast<const NamedNodeMap *>(m);
- QV4::ExecutionEngine *v4 = r->engine();
+ if (id.isSymbol())
+ return Object::virtualGet(m, id, receiver, hasProperty);
- name->makeIdentifier();
- if (name->equals(v4->id_length()))
- return Primitive::fromInt32(r->d()->list().count()).asReturnedValue();
+ if (id == v4->id_length()->propertyKey())
+ return Value::fromInt32(r->d()->list().count()).asReturnedValue();
- QString str = name->toQString();
+ QString str = id.toQString();
for (int ii = 0; ii < r->d()->list().count(); ++ii) {
if (r->d()->list().at(ii)->name == str) {
if (hasProperty)
@@ -930,41 +928,35 @@ ReturnedValue NamedNodeMap::get(const Managed *m, String *name, bool *hasPropert
ReturnedValue NamedNodeMap::create(ExecutionEngine *v4, NodeImpl *data, const QList<NodeImpl *> &list)
{
- return (v4->memoryManager->allocObject<NamedNodeMap>(data, list))->asReturnedValue();
+ return (v4->memoryManager->allocate<NamedNodeMap>(data, list))->asReturnedValue();
}
-ReturnedValue NodeList::getIndexed(const Managed *m, uint index, bool *hasProperty)
+ReturnedValue NodeList::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
Q_ASSERT(m->as<NodeList>());
const NodeList *r = static_cast<const NodeList *>(m);
QV4::ExecutionEngine *v4 = r->engine();
- if ((int)index < r->d()->d->children.count()) {
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+ if ((int)index < r->d()->d->children.count()) {
+ if (hasProperty)
+ *hasProperty = true;
+ return Node::create(v4, r->d()->d->children.at(index));
+ }
if (hasProperty)
- *hasProperty = true;
- return Node::create(v4, r->d()->d->children.at(index));
+ *hasProperty = false;
+ return Encode::undefined();
}
- if (hasProperty)
- *hasProperty = false;
- return Encode::undefined();
-}
-
-ReturnedValue NodeList::get(const Managed *m, String *name, bool *hasProperty)
-{
- Q_ASSERT(m->as<NodeList>());
- const NodeList *r = static_cast<const NodeList *>(m);
- QV4::ExecutionEngine *v4 = r->engine();
- name->makeIdentifier();
-
- if (name->equals(v4->id_length()))
- return Primitive::fromInt32(r->d()->d->children.count()).asReturnedValue();
- return Object::get(m, name, hasProperty);
+ if (id == v4->id_length()->propertyKey())
+ return Value::fromInt32(r->d()->d->children.count()).asReturnedValue();
+ return Object::virtualGet(m, id, receiver, hasProperty);
}
ReturnedValue NodeList::create(ExecutionEngine *v4, NodeImpl *data)
{
- return (v4->memoryManager->allocObject<NodeList>(data))->asReturnedValue();
+ return (v4->memoryManager->allocate<NodeList>(data))->asReturnedValue();
}
ReturnedValue Document::method_documentElement(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -1080,7 +1072,8 @@ private:
QQmlContextDataRef m_qmlContext;
bool m_wasConstructedWithQmlContext = true;
- static void dispatchCallbackNow(Object *thisObj);
+ void dispatchCallbackNow(Object *thisObj);
+ static void dispatchCallbackNow(Object *thisObj, bool done, bool error);
void dispatchCallbackSafely();
int m_status;
@@ -1561,22 +1554,37 @@ const QByteArray &QQmlXMLHttpRequest::rawResponseBody() const
void QQmlXMLHttpRequest::dispatchCallbackNow(Object *thisObj)
{
+ dispatchCallbackNow(thisObj, m_state == Done, m_errorFlag);
+}
+
+void QQmlXMLHttpRequest::dispatchCallbackNow(Object *thisObj, bool done, bool error)
+{
Q_ASSERT(thisObj);
- QV4::Scope scope(thisObj->engine());
- ScopedString s(scope, scope.engine->newString(QStringLiteral("onreadystatechange")));
- ScopedFunctionObject callback(scope, thisObj->get(s));
- if (!callback) {
- // not an error, but no onreadystatechange function to call.
- return;
- }
+ const auto dispatch = [thisObj](const QString &eventName) {
+ QV4::Scope scope(thisObj->engine());
+ ScopedString s(scope, scope.engine->newString(eventName));
+ ScopedFunctionObject callback(scope, thisObj->get(s));
+ // not an error, but no event handler to call.
+ if (!callback)
+ return;
+
+ QV4::JSCallData jsCallData(scope);
+ callback->call(jsCallData);
- QV4::JSCallData jsCallData(scope);
- callback->call(jsCallData);
+ if (scope.engine->hasException) {
+ QQmlError error = scope.engine->catchExceptionAsQmlError();
+ QQmlEnginePrivate::warning(QQmlEnginePrivate::get(scope.engine->qmlEngine()), error);
+ }
+ };
- if (scope.engine->hasException) {
- QQmlError error = scope.engine->catchExceptionAsQmlError();
- QQmlEnginePrivate::warning(QQmlEnginePrivate::get(scope.engine->qmlEngine()), error);
+ dispatch(QStringLiteral("onreadystatechange"));
+ if (done) {
+ if (error)
+ dispatch(QStringLiteral("onerror"));
+ else
+ dispatch(QStringLiteral("onload"));
+ dispatch(QStringLiteral("onloadend"));
}
}
@@ -1637,19 +1645,19 @@ struct QQmlXMLHttpRequestCtor : public FunctionObject
{
V4_OBJECT2(QQmlXMLHttpRequestCtor, FunctionObject)
- static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *, int)
+ static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *, int, const Value *)
{
Scope scope(f->engine());
const QQmlXMLHttpRequestCtor *ctor = static_cast<const QQmlXMLHttpRequestCtor *>(f);
QQmlXMLHttpRequest *r = new QQmlXMLHttpRequest(scope.engine->v8Engine->networkAccessManager(), scope.engine);
- Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->allocObject<QQmlXMLHttpRequestWrapper>(r));
+ Scoped<QQmlXMLHttpRequestWrapper> w(scope, scope.engine->memoryManager->allocate<QQmlXMLHttpRequestWrapper>(r));
ScopedObject proto(scope, ctor->d()->proto);
- w->setPrototype(proto);
+ w->setPrototypeUnchecked(proto);
return w.asReturnedValue();
}
- static ReturnedValue call(const FunctionObject *, const Value *, const Value *, int) {
+ static ReturnedValue virtualCall(const FunctionObject *, const Value *, const Value *, int) {
return Encode::undefined();
}
@@ -1682,11 +1690,11 @@ void Heap::QQmlXMLHttpRequestCtor::init(ExecutionEngine *engine)
Scope scope(engine);
Scoped<QV4::QQmlXMLHttpRequestCtor> ctor(scope, this);
- ctor->defineReadonlyProperty(QStringLiteral("UNSENT"), Primitive::fromInt32(0));
- ctor->defineReadonlyProperty(QStringLiteral("OPENED"), Primitive::fromInt32(1));
- ctor->defineReadonlyProperty(QStringLiteral("HEADERS_RECEIVED"), Primitive::fromInt32(2));
- ctor->defineReadonlyProperty(QStringLiteral("LOADING"), Primitive::fromInt32(3));
- ctor->defineReadonlyProperty(QStringLiteral("DONE"), Primitive::fromInt32(4));
+ ctor->defineReadonlyProperty(QStringLiteral("UNSENT"), Value::fromInt32(0));
+ ctor->defineReadonlyProperty(QStringLiteral("OPENED"), Value::fromInt32(1));
+ ctor->defineReadonlyProperty(QStringLiteral("HEADERS_RECEIVED"), Value::fromInt32(2));
+ ctor->defineReadonlyProperty(QStringLiteral("LOADING"), Value::fromInt32(3));
+ ctor->defineReadonlyProperty(QStringLiteral("DONE"), Value::fromInt32(4));
if (!ctor->d()->proto)
ctor->setupProto();
ScopedString s(scope, engine->id_prototype());
@@ -1722,11 +1730,11 @@ void QQmlXMLHttpRequestCtor::setupProto()
p->defineAccessorProperty(QStringLiteral("responseType"), method_get_responseType, method_set_responseType);
// State values
- p->defineReadonlyProperty(QStringLiteral("UNSENT"), Primitive::fromInt32(0));
- p->defineReadonlyProperty(QStringLiteral("OPENED"), Primitive::fromInt32(1));
- p->defineReadonlyProperty(QStringLiteral("HEADERS_RECEIVED"), Primitive::fromInt32(2));
- p->defineReadonlyProperty(QStringLiteral("LOADING"), Primitive::fromInt32(3));
- p->defineReadonlyProperty(QStringLiteral("DONE"), Primitive::fromInt32(4));
+ p->defineReadonlyProperty(QStringLiteral("UNSENT"), Value::fromInt32(0));
+ p->defineReadonlyProperty(QStringLiteral("OPENED"), Value::fromInt32(1));
+ p->defineReadonlyProperty(QStringLiteral("HEADERS_RECEIVED"), Value::fromInt32(2));
+ p->defineReadonlyProperty(QStringLiteral("LOADING"), Value::fromInt32(3));
+ p->defineReadonlyProperty(QStringLiteral("DONE"), Value::fromInt32(4));
}
@@ -2049,7 +2057,7 @@ void *qt_add_qmlxmlhttprequest(ExecutionEngine *v4)
{
Scope scope(v4);
- Scoped<QQmlXMLHttpRequestCtor> ctor(scope, v4->memoryManager->allocObject<QQmlXMLHttpRequestCtor>(v4));
+ Scoped<QQmlXMLHttpRequestCtor> ctor(scope, v4->memoryManager->allocate<QQmlXMLHttpRequestCtor>(v4));
ScopedString s(scope, v4->newString(QStringLiteral("XMLHttpRequest")));
v4->globalObject->defineReadonlyProperty(s, ctor);
diff --git a/src/qml/qml/qqmlxmlhttprequest_p.h b/src/qml/qml/qqmlxmlhttprequest_p.h
index f2836d8301..7515ef8dcc 100644
--- a/src/qml/qml/qqmlxmlhttprequest_p.h
+++ b/src/qml/qml/qqmlxmlhttprequest_p.h
@@ -55,7 +55,7 @@
#include <QtCore/qglobal.h>
#include <private/qqmlglobal_p.h>
-#if QT_CONFIG(xmlstreamreader) && QT_CONFIG(qml_network)
+QT_REQUIRE_CONFIG(qml_xml_http_request);
QT_BEGIN_NAMESPACE
@@ -64,7 +64,5 @@ void qt_rem_qmlxmlhttprequest(QV4::ExecutionEngine *engine, void *);
QT_END_NAMESPACE
-#endif // xmlstreamreader && qml_network
-
#endif // QQMLXMLHTTPREQUEST_P_H
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
index f04bee7f19..f713efb289 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp
@@ -44,7 +44,9 @@
#include <private/qqmlcomponent_p.h>
#include <private/qqmlloggingcategory_p.h>
#include <private/qqmlstringconverters_p.h>
+#if QT_CONFIG(qml_locale)
#include <private/qqmllocale_p.h>
+#endif
#include <private/qv8engine_p.h>
#include <private/qqmldelayedcallqueue_p.h>
#include <QFileInfo>
@@ -65,6 +67,7 @@
#include <private/qv4jsonobject_p.h>
#include <private/qv4objectproto_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <private/qv4stackframe_p.h>
#include <QtCore/qstring.h>
#include <QtCore/qdatetime.h>
@@ -107,8 +110,8 @@ void Heap::QtObject::init(QQmlEngine *qmlEngine)
{
ScopedString str(scope);
ScopedValue v(scope);
- o->put((str = scope.engine->newString(QStringLiteral("Asynchronous"))), (v = QV4::Primitive::fromInt32(0)));
- o->put((str = scope.engine->newString(QStringLiteral("Synchronous"))), (v = QV4::Primitive::fromInt32(1)));
+ o->put((str = scope.engine->newString(QStringLiteral("Asynchronous"))), (v = QV4::Value::fromInt32(0)));
+ o->put((str = scope.engine->newString(QStringLiteral("Synchronous"))), (v = QV4::Value::fromInt32(1)));
}
o->defineDefaultProperty(QStringLiteral("include"), QV4Include::method_include);
@@ -138,7 +141,9 @@ void Heap::QtObject::init(QQmlEngine *qmlEngine)
o->defineDefaultProperty(QStringLiteral("btoa"), QV4::QtObject::method_btoa);
o->defineDefaultProperty(QStringLiteral("atob"), QV4::QtObject::method_atob);
o->defineDefaultProperty(QStringLiteral("resolvedUrl"), QV4::QtObject::method_resolvedUrl);
+#if QT_CONFIG(qml_locale)
o->defineDefaultProperty(QStringLiteral("locale"), QV4::QtObject::method_locale);
+#endif
o->defineDefaultProperty(QStringLiteral("binding"), QV4::QtObject::method_binding);
if (qmlEngine) {
@@ -178,7 +183,7 @@ ReturnedValue QtObject::findAndAdd(const QString *name, bool &foundProperty) con
QMetaEnum enumerator = qtMetaObject->enumerator(d()->enumeratorIterator);
for (int keyCount = enumerator.keyCount(); d()->keyIterator < keyCount; ++d()->keyIterator) {
key = scope.engine->newString(QString::fromUtf8(enumerator.key(d()->keyIterator)));
- value = QV4::Primitive::fromInt32(enumerator.value(d()->keyIterator));
+ value = QV4::Value::fromInt32(enumerator.value(d()->keyIterator));
o->put(key, value);
if (name && key->toQString() == *name) {
++d()->keyIterator;
@@ -193,35 +198,34 @@ ReturnedValue QtObject::findAndAdd(const QString *name, bool &foundProperty) con
return Encode::undefined();
}
-ReturnedValue QtObject::get(const Managed *m, String *name, bool *hasProperty)
+ReturnedValue QtObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
bool hasProp = false;
if (hasProperty == nullptr) {
hasProperty = &hasProp;
}
- ReturnedValue ret = QV4::Object::get(m, name, hasProperty);
+ ReturnedValue ret = QV4::Object::virtualGet(m, id, receiver, hasProperty);
if (*hasProperty) {
return ret;
}
auto that = static_cast<const QtObject*>(m);
if (!that->d()->isComplete()) {
- const QString key = name->toQString();
+ const QString key = id.toQString();
ret = that->findAndAdd(&key, *hasProperty);
}
return ret;
}
-void QtObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
+OwnPropertyKeyIterator *QtObject::virtualOwnPropertyKeys(const Object *m, Value *target)
{
- auto that = static_cast<QtObject*>(m);
- if (!that->d()->isComplete()) {
- that->addAll();
- }
+ auto that = static_cast<const QtObject*>(m);
+ if (!that->d()->isComplete())
+ const_cast<QtObject *>(that)->addAll();
- QV4::Object::advanceIterator(m, it, name, index, p, attributes);
+ return Object::virtualOwnPropertyKeys(m, target);
}
/*!
@@ -1102,11 +1106,11 @@ ReturnedValue QtObject::method_createQmlObject(const FunctionObject *b, const Va
const QQmlError &error = errors.at(ii);
errorstr += QLatin1String("\n ") + error.toString();
qmlerror = v4->newObject();
- qmlerror->put((s = v4->newString(QStringLiteral("lineNumber"))), (v = QV4::Primitive::fromInt32(error.line())));
- qmlerror->put((s = v4->newString(QStringLiteral("columnNumber"))), (v = QV4::Primitive::fromInt32(error.column())));
+ qmlerror->put((s = v4->newString(QStringLiteral("lineNumber"))), (v = QV4::Value::fromInt32(error.line())));
+ qmlerror->put((s = v4->newString(QStringLiteral("columnNumber"))), (v = QV4::Value::fromInt32(error.column())));
qmlerror->put((s = v4->newString(QStringLiteral("fileName"))), (v = v4->newString(error.url().toString())));
qmlerror->put((s = v4->newString(QStringLiteral("message"))), (v = v4->newString(error.description())));
- qmlerrors->putIndexed(ii, qmlerror);
+ qmlerrors->put(ii, qmlerror);
}
v = v4->newString(errorstr);
@@ -1152,7 +1156,7 @@ ReturnedValue QtObject::method_createQmlObject(const FunctionObject *b, const Va
if (!parentArg)
THROW_GENERIC_ERROR("Qt.createQmlObject(): Missing parent object");
- QQmlTypeData *typeData = QQmlEnginePrivate::get(engine)->typeLoader.getType(
+ QQmlRefPointer<QQmlTypeData> typeData = QQmlEnginePrivate::get(engine)->typeLoader.getType(
qml.toUtf8(), url, QQmlTypeLoader::Synchronous);
Q_ASSERT(typeData->isCompleteOrError());
QQmlComponent component(engine);
@@ -1309,6 +1313,7 @@ ReturnedValue QtObject::method_createComponent(const FunctionObject *b, const Va
return QV4::QObjectWrapper::wrap(scope.engine, c);
}
+#if QT_CONFIG(qml_locale)
/*!
\qmlmethod Qt::locale(name)
@@ -1343,6 +1348,7 @@ ReturnedValue QtObject::method_locale(const FunctionObject *b, const Value *, co
return QQmlLocale::locale(scope.engine, code);
}
+#endif
void Heap::QQmlBindingFunction::init(const QV4::FunctionObject *bindingFunction)
{
@@ -1401,10 +1407,6 @@ DEFINE_OBJECT_VTABLE(QQmlBindingFunction);
\snippet qml/qtBinding.4.qml 0
- \note In \l {Qt Quick 1}, all function assignments were treated as
- binding assignments. The Qt.binding() function is new to
- \l {Qt Quick}{Qt Quick 2}.
-
\since 5.0
*/
ReturnedValue QtObject::method_binding(const FunctionObject *b, const Value *, const Value *argv, int argc)
@@ -1416,7 +1418,7 @@ ReturnedValue QtObject::method_binding(const FunctionObject *b, const Value *, c
if (!f)
THROW_TYPE_ERROR_WITH_MESSAGE("binding(): argument (binding expression) must be a function");
- return Encode(scope.engine->memoryManager->allocObject<QQmlBindingFunction>(f));
+ return Encode(scope.engine->memoryManager->allocate<QQmlBindingFunction>(f));
}
@@ -1551,7 +1553,7 @@ static ReturnedValue writeToConsole(const FunctionObject *b, const Value *, cons
if (i != start)
result.append(QLatin1Char(' '));
- if (argv[i].as<ArrayObject>())
+ if (argv[i].isManaged() && argv[i].managed()->isArrayLike())
result += QLatin1Char('[') + argv[i].toQStringNoThrow() + QLatin1Char(']');
else
result.append(argv[i].toQStringNoThrow());
@@ -1795,7 +1797,7 @@ void QV4::GlobalExtensions::init(Object *globalObject, QJSEngine::Extensions ext
globalObject->defineDefaultProperty(QStringLiteral("print"), QV4::ConsoleObject::method_log);
- QV4::ScopedObject console(scope, globalObject->engine()->memoryManager->allocObject<QV4::ConsoleObject>());
+ QV4::ScopedObject console(scope, globalObject->engine()->memoryManager->allocate<QV4::ConsoleObject>());
globalObject->defineDefaultProperty(QStringLiteral("console"), console);
}
@@ -1850,6 +1852,10 @@ ReturnedValue GlobalExtensions::method_qsTranslate(const FunctionObject *b, cons
if (argc > i)
n = argv[i].toInt32();
+ if (QQmlEnginePrivate *ep = (scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : nullptr))
+ if (ep->propertyCapture)
+ ep->propertyCapture->captureTranslation();
+
QString result = QCoreApplication::translate(context.toUtf8().constData(),
text.toUtf8().constData(),
comment.toUtf8().constData(),
@@ -1953,6 +1959,10 @@ ReturnedValue GlobalExtensions::method_qsTr(const FunctionObject *b, const Value
if (argc > 2)
n = argv[2].toInt32();
+ if (QQmlEnginePrivate *ep = (scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : nullptr))
+ if (ep->propertyCapture)
+ ep->propertyCapture->captureTranslation();
+
QString result = QCoreApplication::translate(context.toUtf8().constData(), text.toUtf8().constData(),
comment.toUtf8().constData(), n);
@@ -2033,6 +2043,10 @@ ReturnedValue GlobalExtensions::method_qsTrId(const FunctionObject *b, const Val
if (argc > 1)
n = argv[1].toInt32();
+ if (QQmlEnginePrivate *ep = (scope.engine->qmlEngine() ? QQmlEnginePrivate::get(scope.engine->qmlEngine()) : nullptr))
+ if (ep->propertyCapture)
+ ep->propertyCapture->captureTranslation();
+
return Encode(scope.engine->newString(qtTrId(argv[0].toQStringNoThrow().toUtf8().constData(), n)));
}
diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
index ee3b5f7d6e..d87b83ba10 100644
--- a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
+++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h
@@ -93,8 +93,8 @@ struct QtObject : Object
{
V4_OBJECT2(QtObject, Object)
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
+ static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty);
+ static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
static ReturnedValue method_isQtObject(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_rgba(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
@@ -126,7 +126,9 @@ struct QtObject : Object
static ReturnedValue method_resolvedUrl(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_createQmlObject(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_createComponent(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+#if QT_CONFIG(qml_locale)
static ReturnedValue method_locale(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+#endif
static ReturnedValue method_binding(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_platform(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
@@ -186,6 +188,11 @@ struct QQmlBindingFunction : public QV4::FunctionObject
QQmlSourceLocation currentLocation() const; // from caller stack trace
};
+inline bool FunctionObject::isBinding() const
+{
+ return d()->vtable() == QQmlBindingFunction::staticVTable();
+}
+
}
QT_END_NAMESPACE
diff --git a/src/qml/qml/v8/qv4domerrors.cpp b/src/qml/qml/v8/qv4domerrors.cpp
index b680aa07da..7ad37d098f 100644
--- a/src/qml/qml/v8/qv4domerrors.cpp
+++ b/src/qml/qml/v8/qv4domerrors.cpp
@@ -48,23 +48,23 @@ void qt_add_domexceptions(ExecutionEngine *e)
{
Scope scope(e);
ScopedObject domexception(scope, e->newObject());
- domexception->defineReadonlyProperty(QStringLiteral("INDEX_SIZE_ERR"), Primitive::fromInt32(DOMEXCEPTION_INDEX_SIZE_ERR));
- domexception->defineReadonlyProperty(QStringLiteral("DOMSTRING_SIZE_ERR"), Primitive::fromInt32(DOMEXCEPTION_DOMSTRING_SIZE_ERR));
- domexception->defineReadonlyProperty(QStringLiteral("HIERARCHY_REQUEST_ERR"), Primitive::fromInt32(DOMEXCEPTION_HIERARCHY_REQUEST_ERR));
- domexception->defineReadonlyProperty(QStringLiteral("WRONG_DOCUMENT_ERR"), Primitive::fromInt32(DOMEXCEPTION_WRONG_DOCUMENT_ERR));
- domexception->defineReadonlyProperty(QStringLiteral("INVALID_CHARACTER_ERR"), Primitive::fromInt32(DOMEXCEPTION_INVALID_CHARACTER_ERR));
- domexception->defineReadonlyProperty(QStringLiteral("NO_DATA_ALLOWED_ERR"), Primitive::fromInt32(DOMEXCEPTION_NO_DATA_ALLOWED_ERR));
- domexception->defineReadonlyProperty(QStringLiteral("NO_MODIFICATION_ALLOWED_ERR"), Primitive::fromInt32(DOMEXCEPTION_NO_MODIFICATION_ALLOWED_ERR));
- domexception->defineReadonlyProperty(QStringLiteral("NOT_FOUND_ERR"), Primitive::fromInt32(DOMEXCEPTION_NOT_FOUND_ERR));
- domexception->defineReadonlyProperty(QStringLiteral("NOT_SUPPORTED_ERR"), Primitive::fromInt32(DOMEXCEPTION_NOT_SUPPORTED_ERR));
- domexception->defineReadonlyProperty(QStringLiteral("INUSE_ATTRIBUTE_ERR"), Primitive::fromInt32(DOMEXCEPTION_INUSE_ATTRIBUTE_ERR));
- domexception->defineReadonlyProperty(QStringLiteral("INVALID_STATE_ERR"), Primitive::fromInt32(DOMEXCEPTION_INVALID_STATE_ERR));
- domexception->defineReadonlyProperty(QStringLiteral("SYNTAX_ERR"), Primitive::fromInt32(DOMEXCEPTION_SYNTAX_ERR));
- domexception->defineReadonlyProperty(QStringLiteral("INVALID_MODIFICATION_ERR"), Primitive::fromInt32(DOMEXCEPTION_INVALID_MODIFICATION_ERR));
- domexception->defineReadonlyProperty(QStringLiteral("NAMESPACE_ERR"), Primitive::fromInt32(DOMEXCEPTION_NAMESPACE_ERR));
- domexception->defineReadonlyProperty(QStringLiteral("INVALID_ACCESS_ERR"), Primitive::fromInt32(DOMEXCEPTION_INVALID_ACCESS_ERR));
- domexception->defineReadonlyProperty(QStringLiteral("VALIDATION_ERR"), Primitive::fromInt32(DOMEXCEPTION_VALIDATION_ERR));
- domexception->defineReadonlyProperty(QStringLiteral("TYPE_MISMATCH_ERR"), Primitive::fromInt32(DOMEXCEPTION_TYPE_MISMATCH_ERR));
+ domexception->defineReadonlyProperty(QStringLiteral("INDEX_SIZE_ERR"), Value::fromInt32(DOMEXCEPTION_INDEX_SIZE_ERR));
+ domexception->defineReadonlyProperty(QStringLiteral("DOMSTRING_SIZE_ERR"), Value::fromInt32(DOMEXCEPTION_DOMSTRING_SIZE_ERR));
+ domexception->defineReadonlyProperty(QStringLiteral("HIERARCHY_REQUEST_ERR"), Value::fromInt32(DOMEXCEPTION_HIERARCHY_REQUEST_ERR));
+ domexception->defineReadonlyProperty(QStringLiteral("WRONG_DOCUMENT_ERR"), Value::fromInt32(DOMEXCEPTION_WRONG_DOCUMENT_ERR));
+ domexception->defineReadonlyProperty(QStringLiteral("INVALID_CHARACTER_ERR"), Value::fromInt32(DOMEXCEPTION_INVALID_CHARACTER_ERR));
+ domexception->defineReadonlyProperty(QStringLiteral("NO_DATA_ALLOWED_ERR"), Value::fromInt32(DOMEXCEPTION_NO_DATA_ALLOWED_ERR));
+ domexception->defineReadonlyProperty(QStringLiteral("NO_MODIFICATION_ALLOWED_ERR"), Value::fromInt32(DOMEXCEPTION_NO_MODIFICATION_ALLOWED_ERR));
+ domexception->defineReadonlyProperty(QStringLiteral("NOT_FOUND_ERR"), Value::fromInt32(DOMEXCEPTION_NOT_FOUND_ERR));
+ domexception->defineReadonlyProperty(QStringLiteral("NOT_SUPPORTED_ERR"), Value::fromInt32(DOMEXCEPTION_NOT_SUPPORTED_ERR));
+ domexception->defineReadonlyProperty(QStringLiteral("INUSE_ATTRIBUTE_ERR"), Value::fromInt32(DOMEXCEPTION_INUSE_ATTRIBUTE_ERR));
+ domexception->defineReadonlyProperty(QStringLiteral("INVALID_STATE_ERR"), Value::fromInt32(DOMEXCEPTION_INVALID_STATE_ERR));
+ domexception->defineReadonlyProperty(QStringLiteral("SYNTAX_ERR"), Value::fromInt32(DOMEXCEPTION_SYNTAX_ERR));
+ domexception->defineReadonlyProperty(QStringLiteral("INVALID_MODIFICATION_ERR"), Value::fromInt32(DOMEXCEPTION_INVALID_MODIFICATION_ERR));
+ domexception->defineReadonlyProperty(QStringLiteral("NAMESPACE_ERR"), Value::fromInt32(DOMEXCEPTION_NAMESPACE_ERR));
+ domexception->defineReadonlyProperty(QStringLiteral("INVALID_ACCESS_ERR"), Value::fromInt32(DOMEXCEPTION_INVALID_ACCESS_ERR));
+ domexception->defineReadonlyProperty(QStringLiteral("VALIDATION_ERR"), Value::fromInt32(DOMEXCEPTION_VALIDATION_ERR));
+ domexception->defineReadonlyProperty(QStringLiteral("TYPE_MISMATCH_ERR"), Value::fromInt32(DOMEXCEPTION_TYPE_MISMATCH_ERR));
e->globalObject->defineDefaultProperty(QStringLiteral("DOMException"), domexception);
}
diff --git a/src/qml/qml/v8/qv4domerrors_p.h b/src/qml/qml/v8/qv4domerrors_p.h
index 06a70a13e9..1842e46a9c 100644
--- a/src/qml/qml/v8/qv4domerrors_p.h
+++ b/src/qml/qml/v8/qv4domerrors_p.h
@@ -77,7 +77,7 @@ QT_BEGIN_NAMESPACE
#define THROW_DOM(error, string) { \
QV4::ScopedValue v(scope, scope.engine->newString(QStringLiteral(string))); \
QV4::ScopedObject ex(scope, scope.engine->newErrorObject(v)); \
- ex->put(QV4::ScopedString(scope, scope.engine->newIdentifier(QStringLiteral("code"))), QV4::ScopedValue(scope, QV4::Primitive::fromInt32(error))); \
+ ex->put(QV4::ScopedString(scope, scope.engine->newIdentifier(QStringLiteral("code"))), QV4::ScopedValue(scope, QV4::Value::fromInt32(error))); \
return scope.engine->throwError(ex); \
}
diff --git a/src/qml/qml/v8/qv4sqlerrors.cpp b/src/qml/qml/v8/qv4sqlerrors.cpp
index db8d130cef..3f1744a687 100644
--- a/src/qml/qml/v8/qv4sqlerrors.cpp
+++ b/src/qml/qml/v8/qv4sqlerrors.cpp
@@ -49,14 +49,14 @@ void qt_add_sqlexceptions(QV4::ExecutionEngine *engine)
{
Scope scope(engine);
ScopedObject sqlexception(scope, engine->newObject());
- sqlexception->defineReadonlyProperty(QStringLiteral("UNKNOWN_ERR"), Primitive::fromInt32(SQLEXCEPTION_UNKNOWN_ERR));
- sqlexception->defineReadonlyProperty(QStringLiteral("DATABASE_ERR"), Primitive::fromInt32(SQLEXCEPTION_DATABASE_ERR));
- sqlexception->defineReadonlyProperty(QStringLiteral("VERSION_ERR"), Primitive::fromInt32(SQLEXCEPTION_VERSION_ERR));
- sqlexception->defineReadonlyProperty(QStringLiteral("TOO_LARGE_ERR"), Primitive::fromInt32(SQLEXCEPTION_TOO_LARGE_ERR));
- sqlexception->defineReadonlyProperty(QStringLiteral("QUOTA_ERR"), Primitive::fromInt32(SQLEXCEPTION_QUOTA_ERR));
- sqlexception->defineReadonlyProperty(QStringLiteral("SYNTAX_ERR"), Primitive::fromInt32(SQLEXCEPTION_SYNTAX_ERR));
- sqlexception->defineReadonlyProperty(QStringLiteral("CONSTRAINT_ERR"), Primitive::fromInt32(SQLEXCEPTION_CONSTRAINT_ERR));
- sqlexception->defineReadonlyProperty(QStringLiteral("TIMEOUT_ERR"), Primitive::fromInt32(SQLEXCEPTION_TIMEOUT_ERR));
+ sqlexception->defineReadonlyProperty(QStringLiteral("UNKNOWN_ERR"), Value::fromInt32(SQLEXCEPTION_UNKNOWN_ERR));
+ sqlexception->defineReadonlyProperty(QStringLiteral("DATABASE_ERR"), Value::fromInt32(SQLEXCEPTION_DATABASE_ERR));
+ sqlexception->defineReadonlyProperty(QStringLiteral("VERSION_ERR"), Value::fromInt32(SQLEXCEPTION_VERSION_ERR));
+ sqlexception->defineReadonlyProperty(QStringLiteral("TOO_LARGE_ERR"), Value::fromInt32(SQLEXCEPTION_TOO_LARGE_ERR));
+ sqlexception->defineReadonlyProperty(QStringLiteral("QUOTA_ERR"), Value::fromInt32(SQLEXCEPTION_QUOTA_ERR));
+ sqlexception->defineReadonlyProperty(QStringLiteral("SYNTAX_ERR"), Value::fromInt32(SQLEXCEPTION_SYNTAX_ERR));
+ sqlexception->defineReadonlyProperty(QStringLiteral("CONSTRAINT_ERR"), Value::fromInt32(SQLEXCEPTION_CONSTRAINT_ERR));
+ sqlexception->defineReadonlyProperty(QStringLiteral("TIMEOUT_ERR"), Value::fromInt32(SQLEXCEPTION_TIMEOUT_ERR));
engine->globalObject->defineDefaultProperty(QStringLiteral("SQLException"), sqlexception);
}
diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp
index 038a75d50a..d76344b613 100644
--- a/src/qml/qml/v8/qv8engine.cpp
+++ b/src/qml/qml/v8/qv8engine.cpp
@@ -39,14 +39,21 @@
#include "qv8engine_p.h"
+#if QT_CONFIG(qml_sequence_object)
#include "qv4sequenceobject_p.h"
+#endif
+
#include "private/qjsengine_p.h"
#include <private/qqmlbuiltinfunctions_p.h>
#include <private/qqmllist_p.h>
#include <private/qqmlengine_p.h>
+#if QT_CONFIG(qml_xml_http_request)
#include <private/qqmlxmlhttprequest_p.h>
+#endif
+#if QT_CONFIG(qml_locale)
#include <private/qqmllocale_p.h>
+#endif
#include <private/qqmlglobal_p.h>
#include <private/qqmlmemoryprofiler_p.h>
#include <private/qqmlplatform_p.h>
@@ -78,6 +85,7 @@
#include <private/qv4script_p.h>
#include <private/qv4include_p.h>
#include <private/qv4jsonobject_p.h>
+#include <private/qv4identifiertable_p.h>
Q_DECLARE_METATYPE(QList<int>)
@@ -123,17 +131,20 @@ static void restoreJSValue(QDataStream &stream, void *data)
}
}
-QV8Engine::QV8Engine(QJSEngine *qq, QV4::ExecutionEngine *v4)
- : q(qq)
- , m_engine(nullptr)
+QV8Engine::QV8Engine(QV4::ExecutionEngine *v4)
+ : m_engine(nullptr)
, m_v4Engine(v4)
+#if QT_CONFIG(qml_xml_http_request)
, m_xmlHttpRequestData(nullptr)
+#endif
{
+#ifndef Q_OS_WASM // wasm does not have working simd QTBUG-63924
#ifdef Q_PROCESSOR_X86_32
if (!qCpuHasFeature(SSE2)) {
qFatal("This program requires an X86 processor that supports SSE2 extension, at least a Pentium 4 or newer");
}
#endif
+#endif
QML_MEMORY_SCOPE_STRING("QV8Engine::QV8Engine");
qMetaTypeId<QJSValue>();
@@ -157,7 +168,7 @@ QV8Engine::~QV8Engine()
qDeleteAll(m_extensionData);
m_extensionData.clear();
-#if QT_CONFIG(xmlstreamreader) && QT_CONFIG(qml_network)
+#if QT_CONFIG(qml_xml_http_request)
qt_rem_qmlxmlhttprequest(m_v4Engine, m_xmlHttpRequestData);
m_xmlHttpRequestData = nullptr;
#endif
@@ -180,14 +191,16 @@ void QV8Engine::initializeGlobal()
QV4::Scope scope(m_v4Engine);
QV4::GlobalExtensions::init(m_v4Engine->globalObject, QJSEngine::AllExtensions);
- QV4::ScopedObject qt(scope, m_v4Engine->memoryManager->allocObject<QV4::QtObject>(m_engine));
+ QV4::ScopedObject qt(scope, m_v4Engine->memoryManager->allocate<QV4::QtObject>(m_engine));
m_v4Engine->globalObject->defineDefaultProperty(QStringLiteral("Qt"), qt);
+#if QT_CONFIG(qml_locale)
QQmlLocale::registerStringLocaleCompare(m_v4Engine);
QQmlDateExtension::registerExtension(m_v4Engine);
QQmlNumberExtension::registerExtension(m_v4Engine);
+#endif
-#if QT_CONFIG(xmlstreamreader) && QT_CONFIG(qml_network)
+#if QT_CONFIG(qml_xml_http_request)
qt_add_domexceptions(m_v4Engine);
m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(m_v4Engine);
#endif
@@ -196,8 +209,10 @@ void QV8Engine::initializeGlobal()
{
for (uint i = 0; i < m_v4Engine->globalObject->internalClass()->size; ++i) {
- if (m_v4Engine->globalObject->internalClass()->nameMap.at(i))
- m_illegalNames.insert(m_v4Engine->globalObject->internalClass()->nameMap.at(i)->string);
+ if (m_v4Engine->globalObject->internalClass()->nameMap.at(i).isString()) {
+ QV4::PropertyKey id = m_v4Engine->globalObject->internalClass()->nameMap.at(i);
+ m_illegalNames.insert(id.toQString());
+ }
}
}
}
@@ -210,25 +225,25 @@ static void freeze_recursive(QV4::ExecutionEngine *v4, QV4::Object *object)
QV4::Scope scope(v4);
bool instanceOfObject = false;
- QV4::ScopedObject p(scope, object->prototype());
+ QV4::ScopedObject p(scope, object->getPrototypeOf());
while (p) {
if (p->d() == v4->objectPrototype()->d()) {
instanceOfObject = true;
break;
}
- p = p->prototype();
+ p = p->getPrototypeOf();
}
if (!instanceOfObject)
return;
- QV4::InternalClass *frozen = object->internalClass()->propertiesFrozen();
+ QV4::Heap::InternalClass *frozen = object->internalClass()->propertiesFrozen();
if (object->internalClass() == frozen)
return;
object->setInternalClass(frozen);
QV4::ScopedObject o(scope);
for (uint i = 0; i < frozen->size; ++i) {
- if (!frozen->nameMap.at(i))
+ if (!frozen->nameMap.at(i).isStringOrSymbol())
continue;
o = *object->propertyData(i);
if (o)
diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h
index b6a378667e..23559618ef 100644
--- a/src/qml/qml/v8/qv8engine_p.h
+++ b/src/qml/qml/v8/qv8engine_p.h
@@ -67,6 +67,7 @@
#include <private/qv4value_p.h>
#include <private/qv4identifier_p.h>
#include <private/qv4context_p.h>
+#include <private/qv4stackframe_p.h>
#include <private/qqmldelayedcallqueue_p.h>
QT_BEGIN_NAMESPACE
@@ -154,10 +155,9 @@ class Q_QML_PRIVATE_EXPORT QV8Engine
friend class QJSEngine;
public:
// static QJSEngine* get(QV8Engine* d) { Q_ASSERT(d); return d->q; }
- static QV4::ExecutionEngine *getV4(QJSEngine *q) { return q->handle(); }
static QV4::ExecutionEngine *getV4(QV8Engine *d) { return d->m_v4Engine; }
- QV8Engine(QJSEngine* qq, QV4::ExecutionEngine *v4);
+ QV8Engine(QV4::ExecutionEngine *v4);
virtual ~QV8Engine();
// This enum should be in sync with QQmlEngine::ObjectOwnership
@@ -170,10 +170,11 @@ public:
void initQmlGlobalObject();
void setEngine(QQmlEngine *engine);
QQmlEngine *engine() { return m_engine; }
- QJSEngine *publicEngine() { return q; }
QQmlDelayedCallQueue *delayedCallQueue() { return &m_delayedCallQueue; }
+#if QT_CONFIG(qml_xml_http_request)
void *xmlHttpRequestData() const { return m_xmlHttpRequestData; }
+#endif
void freezeObject(const QV4::Value &value);
@@ -202,13 +203,14 @@ public:
int consoleCountHelper(const QString &file, quint16 line, quint16 column);
protected:
- QJSEngine* q;
QQmlEngine *m_engine;
QQmlDelayedCallQueue m_delayedCallQueue;
QV4::ExecutionEngine *m_v4Engine;
+#if QT_CONFIG(qml_xml_http_request)
void *m_xmlHttpRequestData;
+#endif
QVector<Deletable *> m_extensionData;
diff --git a/src/qml/qtqmlglobal.h b/src/qml/qtqmlglobal.h
index 6e92867cf5..e02dfa5ed4 100644
--- a/src/qml/qtqmlglobal.h
+++ b/src/qml/qtqmlglobal.h
@@ -52,6 +52,7 @@
# endif
#else
# define QT_FEATURE_qml_debug -1
+# define QT_FEATURE_qml_sequence_object 1
#endif
QT_BEGIN_NAMESPACE
diff --git a/src/qml/qtqmlglobal_p.h b/src/qml/qtqmlglobal_p.h
index 4c0ba338d8..60988d12e6 100644
--- a/src/qml/qtqmlglobal_p.h
+++ b/src/qml/qtqmlglobal_p.h
@@ -57,6 +57,8 @@
# include <QtQml/private/qtqml-config_p.h>
#endif
+#define Q_QML_PRIVATE_API_VERSION 3
+
#define Q_QML_PRIVATE_EXPORT Q_QML_EXPORT
#if !defined(QT_QMLDEVTOOLS_LIB) && !defined(QT_BUILD_QMLDEVTOOLS_LIB)
diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp
index 183bb1bf74..f601087690 100644
--- a/src/qml/types/qqmlconnections.cpp
+++ b/src/qml/types/qqmlconnections.cpp
@@ -231,11 +231,11 @@ void QQmlConnections::setIgnoreUnknownSignals(bool ignore)
d->ignoreUnknownSignals = ignore;
}
-void QQmlConnectionsParser::verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props)
+void QQmlConnectionsParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props)
{
for (int ii = 0; ii < props.count(); ++ii) {
const QV4::CompiledData::Binding *binding = props.at(ii);
- const QString &propName = qmlUnit->stringAt(binding->propertyNameIndex);
+ const QString &propName = compilationUnit->stringAt(binding->propertyNameIndex);
if (!propName.startsWith(QLatin1String("on")) || (propName.length() < 3 || !propName.at(2).isUpper())) {
error(props.at(ii), QQmlConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName));
@@ -243,8 +243,8 @@ void QQmlConnectionsParser::verifyBindings(const QV4::CompiledData::Unit *qmlUni
}
if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
- const QV4::CompiledData::Object *target = qmlUnit->objectAt(binding->value.objectIndex);
- if (!qmlUnit->stringAt(target->inheritedTypeNameIndex).isEmpty())
+ const QV4::CompiledData::Object *target = compilationUnit->objectAt(binding->value.objectIndex);
+ if (!compilationUnit->stringAt(target->inheritedTypeNameIndex).isEmpty())
error(binding, QQmlConnections::tr("Connections: nested objects not allowed"));
else
error(binding, QQmlConnections::tr("Connections: syntax error"));
@@ -256,7 +256,7 @@ void QQmlConnectionsParser::verifyBindings(const QV4::CompiledData::Unit *qmlUni
}
}
-void QQmlConnectionsParser::applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
+void QQmlConnectionsParser::applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
{
QQmlConnectionsPrivate *p =
static_cast<QQmlConnectionsPrivate *>(QObjectPrivate::get(object));
@@ -276,10 +276,9 @@ void QQmlConnections::connectSignals()
QQmlData *ddata = QQmlData::get(this);
QQmlContextData *ctxtdata = ddata ? ddata->outerContext : nullptr;
- const QV4::CompiledData::Unit *qmlUnit = d->compilationUnit->data;
for (const QV4::CompiledData::Binding *binding : qAsConst(d->bindings)) {
Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Script);
- QString propName = qmlUnit->stringAt(binding->propertyNameIndex);
+ QString propName = d->compilationUnit->stringAt(binding->propertyNameIndex);
QQmlProperty prop(target, propName);
if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) {
diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h
index 50e2c59ac3..bd03d7e152 100644
--- a/src/qml/types/qqmlconnections_p.h
+++ b/src/qml/types/qqmlconnections_p.h
@@ -69,7 +69,7 @@ class Q_AUTOTEST_EXPORT QQmlConnections : public QObject, public QQmlParserStatu
Q_INTERFACES(QQmlParserStatus)
Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged)
- Q_REVISION(1) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged)
+ Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged REVISION 1)
Q_PROPERTY(bool ignoreUnknownSignals READ ignoreUnknownSignals WRITE setIgnoreUnknownSignals)
public:
@@ -98,8 +98,8 @@ private:
class QQmlConnectionsParser : public QQmlCustomParser
{
public:
- void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props) override;
- void applyBindings(QObject *object, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
+ void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) override;
+ void applyBindings(QObject *object, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
};
diff --git a/src/qml/types/qqmldelegatecomponent.cpp b/src/qml/types/qqmldelegatecomponent.cpp
new file mode 100644
index 0000000000..007e8e2ec6
--- /dev/null
+++ b/src/qml/types/qqmldelegatecomponent.cpp
@@ -0,0 +1,298 @@
+/****************************************************************************
+**
+** 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 "qqmldelegatecomponent_p.h"
+#include <QtQml/private/qqmladaptormodel_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlAbstractDelegateComponent::QQmlAbstractDelegateComponent(QObject *parent)
+ : QQmlComponent(parent)
+{
+}
+
+QQmlAbstractDelegateComponent::~QQmlAbstractDelegateComponent()
+{
+}
+
+QVariant QQmlAbstractDelegateComponent::value(QQmlAdaptorModel *adaptorModel, int row, int column, const QString &role) const
+{
+ if (!adaptorModel)
+ return QVariant();
+ return adaptorModel->value(adaptorModel->indexAt(row, column), role);
+}
+
+/*!
+ \qmlmodule Qt.labs.qmlmodels 1.0
+ \title Qt Labs QML Models - QML Types
+ \ingroup qmlmodules
+ \brief The Qt Labs QML Models module provides various model-related types for use with views.
+
+ To use this module, import the module with the following line:
+
+ \code
+ import Qt.labs.qmlmodels 1.0
+ \endcode
+*/
+
+/*!
+ \qmltype DelegateChoice
+ \instantiates QQmlDelegateChoice
+ \inqmlmodule Qt.labs.qmlmodels
+ \brief Encapsulates a delegate and when to use it.
+
+ The DelegateChoice type wraps a delegate and defines the circumstances
+ in which it should be chosen.
+ DelegateChoices can be nested inside a DelegateChooser.
+
+ \sa DelegateChooser
+*/
+
+/*!
+ \qmlproperty string QtQml.Models::DelegateChoice::roleValue
+ This property holds the value used to match the role data for the role provided by \l DefaultDelegateChooser::role.
+*/
+QVariant QQmlDelegateChoice::roleValue() const
+{
+ return m_value;
+}
+
+void QQmlDelegateChoice::setRoleValue(const QVariant &value)
+{
+ if (m_value == value)
+ return;
+ m_value = value;
+ emit roleValueChanged();
+ emit changed();
+}
+
+/*!
+ \qmlproperty index QtQml.Models::DelegateChoice::row
+ This property holds the value used to match the row value of model elements.
+ With models that have only the index property (and thus only one column), this property
+ should be intended as index, and set to the desired index value.
+
+ \note Setting both row and index has undefined behavior. The two are equivalent and only
+ one should be used.
+
+ \sa QtQml.Models::DelegateChoice::index
+*/
+
+/*!
+ \qmlproperty index QtQml.Models::DelegateChoice::index
+ This property holds the value used to match the index value of model elements.
+ This is effectively an alias for \l QtQml.Models::DelegateChoice::row
+
+ \sa QtQml.Models::DelegateChoice::row
+*/
+int QQmlDelegateChoice::row() const
+{
+ return m_row;
+}
+
+void QQmlDelegateChoice::setRow(int r)
+{
+ if (m_row == r)
+ return;
+ m_row = r;
+ emit rowChanged();
+ emit indexChanged();
+ emit changed();
+}
+
+/*!
+ \qmlproperty index QtQml.Models::DelegateChoice::column
+ This property holds the value used to match the column value of model elements.
+*/
+int QQmlDelegateChoice::column() const
+{
+ return m_column;
+}
+
+void QQmlDelegateChoice::setColumn(int c)
+{
+ if (m_column == c)
+ return;
+ m_column = c;
+ emit columnChanged();
+ emit changed();
+}
+
+QQmlComponent *QQmlDelegateChoice::delegate() const
+{
+ return m_delegate;
+}
+
+/*!
+ \qmlproperty Component QtQml.Models::DelegateChoice::delegate
+ This property holds the delegate to use if this choice matches the model item.
+*/
+void QQmlDelegateChoice::setDelegate(QQmlComponent *delegate)
+{
+ if (m_delegate == delegate)
+ return;
+ QQmlAbstractDelegateComponent *adc = static_cast<QQmlAbstractDelegateComponent *>(m_delegate);
+ if (adc)
+ disconnect(adc, &QQmlAbstractDelegateComponent::delegateChanged, this, &QQmlDelegateChoice::delegateChanged);
+ m_delegate = delegate;
+ adc = static_cast<QQmlAbstractDelegateComponent *>(delegate);
+ if (adc)
+ connect(adc, &QQmlAbstractDelegateComponent::delegateChanged, this, &QQmlDelegateChoice::delegateChanged);
+ emit delegateChanged();
+ emit changed();
+}
+
+bool QQmlDelegateChoice::match(int row, int column, const QVariant &value) const
+{
+ if (!m_value.isValid() && m_row < 0 && m_column < 0)
+ return true;
+
+ const bool roleMatched = (m_value.isValid()) ? value == m_value : true;
+ const bool rowMatched = (m_row < 0 ) ? true : m_row == row;
+ const bool columnMatched = (m_column < 0 ) ? true : m_column == column;
+ return roleMatched && rowMatched && columnMatched;
+}
+
+/*!
+ \qmltype QQmlDelegateChooser
+ \instantiates QQmlDelegateChooser
+ \inqmlmodule Qt.labs.qmlmodels
+ \brief Allows a view to use different delegates for different types of items in the model.
+
+ The DelegateChooser is a special \l Component type intended for those scenarios where a Component is required
+ by a view and used as a delegate.
+ DelegateChooser encapsulates a set of \l DelegateChoices.
+ These choices are used determine the delegate that will be instantiated for each
+ item in the model.
+ The selection of the choice is performed based on the value that a model item has for \l role,
+ and also based on index.
+
+ \note This type is intended to transparently work only with TableView and any DelegateModel-based view.
+ Views (including user-defined views) that aren't internally based on a DelegateModel need to explicitly support
+ this type of component to make it function as described.
+
+ \sa DelegateChoice
+*/
+
+/*!
+ \qmlproperty string QtQml.Models::DelegateChooser::role
+ This property holds the role used to determine the delegate for a given model item.
+
+ \sa DelegateChoice
+*/
+void QQmlDelegateChooser::setRole(const QString &role)
+{
+ if (m_role == role)
+ return;
+ m_role = role;
+ emit roleChanged();
+}
+
+/*!
+ \qmlproperty list<DelegateChoice> QtQml.Models::DelegateChooser::choices
+ \default
+ The list of DelegateChoices for the chooser.
+
+ The list is treated as an ordered list, where the first DelegateChoice to match
+ will be used be a view.
+
+ It should not generally be necessary to refer to the \c choices property,
+ as it is the default property for DefaultDelegateChooser and thus all child items are
+ automatically assigned to this property.
+*/
+
+QQmlListProperty<QQmlDelegateChoice> QQmlDelegateChooser::choices()
+{
+ return QQmlListProperty<QQmlDelegateChoice>(this, nullptr,
+ QQmlDelegateChooser::choices_append,
+ QQmlDelegateChooser::choices_count,
+ QQmlDelegateChooser::choices_at,
+ QQmlDelegateChooser::choices_clear);
+}
+
+void QQmlDelegateChooser::choices_append(QQmlListProperty<QQmlDelegateChoice> *prop, QQmlDelegateChoice *choice)
+{
+ QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object);
+ q->m_choices.append(choice);
+ connect(choice, &QQmlDelegateChoice::changed, q, &QQmlAbstractDelegateComponent::delegateChanged);
+ q->delegateChanged();
+}
+
+int QQmlDelegateChooser::choices_count(QQmlListProperty<QQmlDelegateChoice> *prop)
+{
+ QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser*>(prop->object);
+ return q->m_choices.count();
+}
+
+QQmlDelegateChoice *QQmlDelegateChooser::choices_at(QQmlListProperty<QQmlDelegateChoice> *prop, int index)
+{
+ QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser*>(prop->object);
+ return q->m_choices.at(index);
+}
+
+void QQmlDelegateChooser::choices_clear(QQmlListProperty<QQmlDelegateChoice> *prop)
+{
+ QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object);
+ for (QQmlDelegateChoice *choice : q->m_choices)
+ disconnect(choice, &QQmlDelegateChoice::changed, q, &QQmlAbstractDelegateComponent::delegateChanged);
+ q->m_choices.clear();
+ q->delegateChanged();
+}
+
+QQmlComponent *QQmlDelegateChooser::delegate(QQmlAdaptorModel *adaptorModel, int row, int column) const
+{
+ QVariant v;
+ if (!m_role.isNull())
+ v = value(adaptorModel, row, column, m_role);
+ if (!v.isValid()) { // check if the row only has modelData, for example if the row is a QVariantMap
+ v = value(adaptorModel, row, column, QStringLiteral("modelData"));
+ if (v.isValid())
+ v = v.toMap().value(m_role);
+ }
+ // loop through choices, finding first one that fits
+ for (int i = 0; i < m_choices.count(); ++i) {
+ const QQmlDelegateChoice *choice = m_choices.at(i);
+ if (choice->match(row, column, v))
+ return choice->delegate();
+ }
+
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/types/qqmldelegatecomponent_p.h b/src/qml/types/qqmldelegatecomponent_p.h
new file mode 100644
index 0000000000..c925ed9a60
--- /dev/null
+++ b/src/qml/types/qqmldelegatecomponent_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 QQMLDELEGATECOMPONENT_P_H
+#define QQMLDELEGATECOMPONENT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtqmlglobal_p.h>
+#include <qqmlcomponent.h>
+
+QT_REQUIRE_CONFIG(qml_delegate_model);
+
+QT_BEGIN_NAMESPACE
+
+// TODO: consider making QQmlAbstractDelegateComponent public API
+class QQmlAbstractDelegateComponentPrivate;
+class QQmlAdaptorModel;
+class Q_QML_PRIVATE_EXPORT QQmlAbstractDelegateComponent : public QQmlComponent
+{
+ Q_OBJECT
+public:
+ QQmlAbstractDelegateComponent(QObject *parent = nullptr);
+ ~QQmlAbstractDelegateComponent() override;
+
+ virtual QQmlComponent *delegate(QQmlAdaptorModel *adaptorModel, int row, int column = 0) const = 0;
+
+signals:
+ void delegateChanged();
+
+protected:
+ QVariant value(QQmlAdaptorModel *adaptorModel,int row, int column, const QString &role) const;
+
+private:
+ Q_DECLARE_PRIVATE(QQmlAbstractDelegateComponent)
+ Q_DISABLE_COPY(QQmlAbstractDelegateComponent)
+};
+
+class Q_QML_PRIVATE_EXPORT QQmlDelegateChoice : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QVariant roleValue READ roleValue WRITE setRoleValue NOTIFY roleValueChanged)
+ Q_PROPERTY(int row READ row WRITE setRow NOTIFY rowChanged)
+ Q_PROPERTY(int index READ row WRITE setRow NOTIFY indexChanged)
+ Q_PROPERTY(int column READ column WRITE setColumn NOTIFY columnChanged)
+ Q_PROPERTY(QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
+ Q_CLASSINFO("DefaultProperty", "delegate")
+public:
+ QVariant roleValue() const;
+ void setRoleValue(const QVariant &roleValue);
+
+ int row() const;
+ void setRow(int r);
+
+ int column() const;
+ void setColumn(int c);
+
+ QQmlComponent *delegate() const;
+ void setDelegate(QQmlComponent *delegate);
+
+ virtual bool match(int row, int column, const QVariant &value) const;
+
+signals:
+ void roleValueChanged();
+ void rowChanged();
+ void indexChanged();
+ void columnChanged();
+ void delegateChanged();
+ void changed();
+
+private:
+ QVariant m_value;
+ int m_row = -1;
+ int m_column = -1;
+ QQmlComponent *m_delegate = nullptr;
+};
+
+class Q_QML_PRIVATE_EXPORT QQmlDelegateChooser : public QQmlAbstractDelegateComponent
+{
+ Q_OBJECT
+ Q_PROPERTY(QString role READ role WRITE setRole NOTIFY roleChanged)
+ Q_PROPERTY(QQmlListProperty<QQmlDelegateChoice> choices READ choices CONSTANT)
+ Q_CLASSINFO("DefaultProperty", "choices")
+
+public:
+ QString role() const { return m_role; }
+ void setRole(const QString &role);
+
+ virtual QQmlListProperty<QQmlDelegateChoice> choices();
+ static void choices_append(QQmlListProperty<QQmlDelegateChoice> *, QQmlDelegateChoice *);
+ static int choices_count(QQmlListProperty<QQmlDelegateChoice> *);
+ static QQmlDelegateChoice *choices_at(QQmlListProperty<QQmlDelegateChoice> *, int);
+ static void choices_clear(QQmlListProperty<QQmlDelegateChoice> *);
+
+ QQmlComponent *delegate(QQmlAdaptorModel *adaptorModel, int row, int column = -1) const override;
+
+signals:
+ void roleChanged();
+
+private:
+ QString m_role;
+ QList<QQmlDelegateChoice *> m_choices;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQmlDelegateChoice)
+QML_DECLARE_TYPE(QQmlDelegateChooser)
+
+#endif // QQMLDELEGATECOMPONENT_P_H
diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp
index 7a12813f0c..243a7ce749 100644
--- a/src/qml/types/qqmldelegatemodel.cpp
+++ b/src/qml/types/qqmldelegatemodel.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qqmldelegatemodel_p_p.h"
+#include "qqmldelegatecomponent_p.h"
#include <QtQml/qqmlinfo.h>
@@ -93,18 +94,18 @@ struct DelegateModelGroupFunction : QV4::FunctionObject
static Heap::DelegateModelGroupFunction *create(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg))
{
- return scope->engine()->memoryManager->allocObject<DelegateModelGroupFunction>(scope, flag, code);
+ return scope->engine()->memoryManager->allocate<DelegateModelGroupFunction>(scope, flag, code);
}
- static ReturnedValue call(const QV4::FunctionObject *that, const Value *thisObject, const Value *argv, int argc)
+ static ReturnedValue virtualCall(const QV4::FunctionObject *that, const Value *thisObject, const Value *argv, int argc)
{
QV4::Scope scope(that->engine());
QV4::Scoped<DelegateModelGroupFunction> f(scope, static_cast<const DelegateModelGroupFunction *>(that));
QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject);
if (!o)
- return scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"));
+ return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
- QV4::ScopedValue v(scope, argc ? argv[0] : Primitive::undefinedValue());
+ QV4::ScopedValue v(scope, argc ? argv[0] : Value::undefinedValue());
return f->d()->code(o->d()->item, f->d()->flag, v);
}
};
@@ -160,22 +161,6 @@ QQmlDelegateModelParts::QQmlDelegateModelParts(QQmlDelegateModel *parent)
//---------------------------------------------------------------------------
/*!
- \qmltype VisualDataModel
- \instantiates QQmlDelegateModel
- \inqmlmodule QtQuick
- \ingroup qtquick-models
- \brief Encapsulates a model and delegate.
-
- The VisualDataModel type encapsulates a model and the delegate that will
- be instantiated for items in a model.
-
- This type is provided by the \l{Qt QML} module due to compatibility reasons.
- The same implementation is now primarily available as DelegateModel in the
- \l{Qt QML Models QML Types}{Qt QML Models} module.
-
- \sa {QtQml.Models::DelegateModel}
-*/
-/*!
\qmltype DelegateModel
\instantiates QQmlDelegateModel
\inqmlmodule QtQml.Models
@@ -193,14 +178,11 @@ QQmlDelegateModelParts::QQmlDelegateModelParts(QQmlDelegateModel *parent)
The example below illustrates using a DelegateModel with a ListView.
- \snippet delegatemodel/visualdatamodel.qml 0
-
- \note This type is also available as \l VisualDataModel in the \l{Qt QML}
- module due to compatibility reasons.
+ \snippet delegatemodel/delegatemodel.qml 0
*/
QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt)
- : m_delegate(nullptr)
+ : m_delegateChooser(nullptr)
, m_cacheMetaType(nullptr)
, m_context(ctxt)
, m_parts(nullptr)
@@ -228,6 +210,14 @@ QQmlDelegateModelPrivate::~QQmlDelegateModelPrivate()
m_cacheMetaType->release();
}
+int QQmlDelegateModelPrivate::adaptorModelCount() const
+{
+ // QQmlDelegateModel currently only support list models.
+ // So even if a model is a table model, only the first
+ // column will be used.
+ return m_adaptorModel.rowCount();
+}
+
void QQmlDelegateModelPrivate::requestMoreIfNecessary()
{
Q_Q(QQmlDelegateModel);
@@ -263,6 +253,8 @@ QQmlDelegateModel::QQmlDelegateModel(QQmlContext *ctxt, QObject *parent)
QQmlDelegateModel::~QQmlDelegateModel()
{
Q_D(QQmlDelegateModel);
+ d->disconnectFromAbstractItemModel();
+ d->m_adaptorModel.setObject(nullptr, this);
for (QQmlDelegateModelItem *cacheItem : qAsConst(d->m_cache)) {
if (cacheItem->object) {
@@ -336,7 +328,7 @@ void QQmlDelegateModel::componentComplete()
static_cast<QQmlPartsModel *>(d->m_pendingParts.first())->updateFilterGroup();
QVector<Compositor::Insert> inserts;
- d->m_count = d->m_adaptorModel.count();
+ d->m_count = d->adaptorModelCount();
d->m_compositor.append(
&d->m_adaptorModel,
0,
@@ -368,6 +360,54 @@ QVariant QQmlDelegateModel::model() const
return d->m_adaptorModel.model();
}
+void QQmlDelegateModelPrivate::connectToAbstractItemModel()
+{
+ Q_Q(QQmlDelegateModel);
+ if (!m_adaptorModel.adaptsAim())
+ return;
+
+ auto aim = m_adaptorModel.aim();
+
+ qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ q, QQmlDelegateModel, SLOT(_q_rowsInserted(QModelIndex,int,int)));
+ qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ q, QQmlDelegateModel, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
+ qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ q, QQmlDelegateModel, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
+ qmlobject_connect(aim, QAbstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
+ q, QQmlDelegateModel, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>)));
+ qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
+ q, QQmlDelegateModel, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
+ qmlobject_connect(aim, QAbstractItemModel, SIGNAL(modelReset()),
+ q, QQmlDelegateModel, SLOT(_q_modelReset()));
+ qmlobject_connect(aim, QAbstractItemModel, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
+ q, QQmlDelegateModel, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
+}
+
+void QQmlDelegateModelPrivate::disconnectFromAbstractItemModel()
+{
+ Q_Q(QQmlDelegateModel);
+ if (!m_adaptorModel.adaptsAim())
+ return;
+
+ auto aim = m_adaptorModel.aim();
+
+ QObject::disconnect(aim, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ q, SLOT(_q_rowsInserted(QModelIndex,int,int)));
+ QObject::disconnect(aim, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ q, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
+ QObject::disconnect(aim, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ q, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
+ QObject::disconnect(aim, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
+ q, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>)));
+ QObject::disconnect(aim, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
+ q, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
+ QObject::disconnect(aim, SIGNAL(modelReset()),
+ q, SLOT(_q_modelReset()));
+ QObject::disconnect(aim, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
+ q, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
+}
+
void QQmlDelegateModel::setModel(const QVariant &model)
{
Q_D(QQmlDelegateModel);
@@ -375,7 +415,10 @@ void QQmlDelegateModel::setModel(const QVariant &model)
if (d->m_complete)
_q_itemsRemoved(0, d->m_count);
+ d->disconnectFromAbstractItemModel();
d->m_adaptorModel.setModel(model, this, d->m_context->engine());
+ d->connectToAbstractItemModel();
+
d->m_adaptorModel.replaceWatchedRoles(QList<QByteArray>(), d->m_watchedRoles);
for (int i = 0; d->m_parts && i < d->m_parts->models.count(); ++i) {
d->m_adaptorModel.replaceWatchedRoles(
@@ -383,7 +426,7 @@ void QQmlDelegateModel::setModel(const QVariant &model)
}
if (d->m_complete) {
- _q_itemsInserted(0, d->m_adaptorModel.count());
+ _q_itemsInserted(0, d->adaptorModelCount());
d->requestMoreIfNecessary();
}
}
@@ -394,6 +437,8 @@ void QQmlDelegateModel::setModel(const QVariant &model)
The delegate provides a template defining each item instantiated by a view.
The index is exposed as an accessible \c index property. Properties of the
model are also available depending upon the type of \l {qml-data-models}{Data Model}.
+
+ \sa DelegateComponent
*/
QQmlComponent *QQmlDelegateModel::delegate() const
{
@@ -408,22 +453,25 @@ void QQmlDelegateModel::setDelegate(QQmlComponent *delegate)
qmlWarning(this) << tr("The delegate of a DelegateModel cannot be changed within onUpdated.");
return;
}
+ if (d->m_delegate == delegate)
+ return;
bool wasValid = d->m_delegate != nullptr;
- d->m_delegate = delegate;
+ d->m_delegate.setObject(delegate, this);
d->m_delegateValidated = false;
- if (wasValid && d->m_complete) {
- for (int i = 1; i < d->m_groupCount; ++i) {
- QQmlDelegateModelGroupPrivate::get(d->m_groups[i])->changeSet.remove(
- 0, d->m_compositor.count(Compositor::Group(i)));
- }
- }
- if (d->m_complete && d->m_delegate) {
- for (int i = 1; i < d->m_groupCount; ++i) {
- QQmlDelegateModelGroupPrivate::get(d->m_groups[i])->changeSet.insert(
- 0, d->m_compositor.count(Compositor::Group(i)));
+ if (d->m_delegateChooser)
+ QObject::disconnect(d->m_delegateChooserChanged);
+
+ d->m_delegateChooser = nullptr;
+ if (delegate) {
+ QQmlAbstractDelegateComponent *adc =
+ qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
+ if (adc) {
+ d->m_delegateChooser = adc;
+ d->m_delegateChooserChanged = connect(adc, &QQmlAbstractDelegateComponent::delegateChanged,
+ [d](){ d->delegateChanged(); });
}
}
- d->emitChanges();
+ d->delegateChanged(d->m_delegate, wasValid);
}
/*!
@@ -442,10 +490,10 @@ void QQmlDelegateModel::setDelegate(QQmlComponent *delegate)
the new directory's contents.
\c main.cpp:
- \snippet delegatemodel/visualdatamodel_rootindex/main.cpp 0
+ \snippet delegatemodel/delegatemodel_rootindex/main.cpp 0
\c view.qml:
- \snippet delegatemodel/visualdatamodel_rootindex/view.qml 0
+ \snippet delegatemodel/delegatemodel_rootindex/view.qml 0
If the \l model is a QAbstractItemModel subclass, the delegate can also
reference a \c hasModelChildren property (optionally qualified by a
@@ -470,12 +518,16 @@ void QQmlDelegateModel::setRootIndex(const QVariant &root)
if (changed || !d->m_adaptorModel.isValid()) {
const int oldCount = d->m_count;
d->m_adaptorModel.rootIndex = modelIndex;
- if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) // The previous root index was invalidated, so we need to reconnect the model.
+ if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) {
+ // The previous root index was invalidated, so we need to reconnect the model.
+ d->disconnectFromAbstractItemModel();
d->m_adaptorModel.setModel(d->m_adaptorModel.list.list(), this, d->m_context->engine());
+ d->connectToAbstractItemModel();
+ }
if (d->m_adaptorModel.canFetchMore())
d->m_adaptorModel.fetchMore();
if (d->m_complete) {
- const int newCount = d->m_adaptorModel.count();
+ const int newCount = d->adaptorModelCount();
if (oldCount)
_q_itemsRemoved(0, oldCount);
if (newCount)
@@ -536,25 +588,24 @@ int QQmlDelegateModel::count() const
QQmlDelegateModel::ReleaseFlags QQmlDelegateModelPrivate::release(QObject *object)
{
- QQmlDelegateModel::ReleaseFlags stat = nullptr;
if (!object)
- return stat;
-
- if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object)) {
- if (cacheItem->releaseObject()) {
- cacheItem->destroyObject();
- emitDestroyingItem(object);
- if (cacheItem->incubationTask) {
- releaseIncubator(cacheItem->incubationTask);
- cacheItem->incubationTask = nullptr;
- }
- cacheItem->Dispose();
- stat |= QQmlInstanceModel::Destroyed;
- } else {
- stat |= QQmlDelegateModel::Referenced;
- }
+ return QQmlDelegateModel::ReleaseFlags(0);
+
+ QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object);
+ if (!cacheItem)
+ return QQmlDelegateModel::ReleaseFlags(0);
+
+ if (!cacheItem->releaseObject())
+ return QQmlDelegateModel::Referenced;
+
+ cacheItem->destroyObject();
+ emitDestroyingItem(object);
+ if (cacheItem->incubationTask) {
+ releaseIncubator(cacheItem->incubationTask);
+ cacheItem->incubationTask = nullptr;
}
- return stat;
+ cacheItem->Dispose();
+ return QQmlInstanceModel::Destroyed;
}
/*
@@ -649,7 +700,7 @@ QQmlDelegateModelGroup *QQmlDelegateModelPrivate::group_at(
The following example illustrates using groups to select items in a model.
- \snippet delegatemodel/visualdatagroup.qml 0
+ \snippet delegatemodel/delegatemodelgroup.qml 0
*/
QQmlListProperty<QQmlDelegateModelGroup> QQmlDelegateModel::groups()
@@ -667,7 +718,7 @@ QQmlListProperty<QQmlDelegateModelGroup> QQmlDelegateModel::groups()
/*!
\qmlproperty DelegateModelGroup QtQml.Models::DelegateModel::items
- This property holds visual data model's default group to which all new items are added.
+ This property holds default group to which all new items are added.
*/
QQmlDelegateModelGroup *QQmlDelegateModel::items()
@@ -679,7 +730,7 @@ QQmlDelegateModelGroup *QQmlDelegateModel::items()
/*!
\qmlproperty DelegateModelGroup QtQml.Models::DelegateModel::persistedItems
- This property holds visual data model's persisted items group.
+ This property holds delegate model's persisted items group.
Items in this group are not destroyed when released by a view, instead they are persisted
until removed from the group.
@@ -702,9 +753,9 @@ QQmlDelegateModelGroup *QQmlDelegateModel::persistedItems()
/*!
\qmlproperty string QtQml.Models::DelegateModel::filterOnGroup
- This property holds the name of the group used to filter the visual data model.
+ This property holds name of the group that is used to filter the delegate model.
- Only items which belong to this group are visible to a view.
+ Only items that belong to this group are visible to a view.
By default this is the \l items group.
*/
@@ -808,6 +859,12 @@ QObject *QQmlDelegateModel::parts()
return d->m_parts;
}
+const QAbstractItemModel *QQmlDelegateModel::abstractItemModel() const
+{
+ Q_D(const QQmlDelegateModel);
+ return d->m_adaptorModel.adaptsAim() ? d->m_adaptorModel.aim() : nullptr;
+}
+
void QQmlDelegateModelPrivate::emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package)
{
for (int i = 1; i < m_groupCount; ++i)
@@ -862,6 +919,13 @@ void QQmlDelegateModelPrivate::releaseIncubator(QQDMIncubationTask *incubationTa
}
}
+void QQmlDelegateModelPrivate::addCacheItem(QQmlDelegateModelItem *item, Compositor::iterator it)
+{
+ m_cache.insert(it.cacheIndex, item);
+ m_compositor.setFlags(it, 1, Compositor::CacheFlag);
+ Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache));
+}
+
void QQmlDelegateModelPrivate::removeCacheItem(QQmlDelegateModelItem *cacheItem)
{
int cidx = m_cache.lastIndexOf(cacheItem);
@@ -892,7 +956,7 @@ void QQmlDelegateModelPrivate::incubatorStatusChanged(QQDMIncubationTask *incuba
emitCreatedItem(incubationTask, cacheItem->object);
cacheItem->releaseObject();
} else if (status == QQmlIncubator::Error) {
- qmlWarning(m_delegate, incubationTaskErrors + m_delegate->errors()) << "Error creating delegate";
+ qmlInfo(m_delegate, incubationTaskErrors + m_delegate->errors()) << "Cannot create delegate";
}
if (!cacheItem->isObjectReferenced()) {
@@ -951,10 +1015,7 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ
return nullptr;
cacheItem->groups = it->flags;
-
- m_cache.insert(it.cacheIndex, cacheItem);
- m_compositor.setFlags(it, 1, Compositor::CacheFlag);
- Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache));
+ addCacheItem(cacheItem, it);
}
// Bump the reference counts temporarily so neither the content data or the delegate object
@@ -969,7 +1030,18 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ
cacheItem->incubationTask->forceCompletion();
}
} else if (!cacheItem->object) {
- QQmlContext *creationContext = m_delegate->creationContext();
+ QQmlComponent *delegate = m_delegate;
+ if (m_delegateChooser) {
+ QQmlAbstractDelegateComponent *chooser = m_delegateChooser;
+ do {
+ delegate = chooser->delegate(&m_adaptorModel, index);
+ chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
+ } while (chooser);
+ if (!delegate)
+ return nullptr;
+ }
+
+ QQmlContext *creationContext = delegate->creationContext();
cacheItem->scriptRef += 1;
@@ -994,10 +1066,10 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQ
}
}
- QQmlComponentPrivate *cp = QQmlComponentPrivate::get(m_delegate);
+ QQmlComponentPrivate *cp = QQmlComponentPrivate::get(delegate);
cp->incubateObject(
cacheItem->incubationTask,
- m_delegate,
+ delegate,
m_context->engine(),
ctxt,
QQmlContextData::get(m_context));
@@ -1046,7 +1118,10 @@ QQmlIncubator::Status QQmlDelegateModel::incubationStatus(int index)
if (!it->inCache())
return QQmlIncubator::Null;
- return d->m_cache.at(it.cacheIndex)->incubationTask->status();
+ if (auto incubationTask = d->m_cache.at(it.cacheIndex)->incubationTask)
+ return incubationTask->status();
+
+ return QQmlIncubator::Ready;
}
QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index, const QString &name)
@@ -1266,8 +1341,12 @@ void QQmlDelegateModel::_q_itemsInserted(int index, int count)
const QList<QQmlDelegateModelItem *> cache = d->m_cache;
for (int i = 0, c = cache.count(); i < c; ++i) {
QQmlDelegateModelItem *item = cache.at(i);
- if (item->modelIndex() >= index)
- item->setModelIndex(item->modelIndex() + count);
+ if (item->modelIndex() >= index) {
+ const int newIndex = item->modelIndex() + count;
+ const int row = newIndex;
+ const int column = 0;
+ item->setModelIndex(newIndex, row, column);
+ }
}
QVector<Compositor::Insert> inserts;
@@ -1408,10 +1487,14 @@ void QQmlDelegateModel::_q_itemsRemoved(int index, int count)
if (!d->m_cache.contains(item))
continue;
- if (item->modelIndex() >= index + count)
- item->setModelIndex(item->modelIndex() - count);
- else if (item->modelIndex() >= index)
- item->setModelIndex(-1);
+ if (item->modelIndex() >= index + count) {
+ const int newIndex = item->modelIndex() - count;
+ const int row = newIndex;
+ const int column = 0;
+ item->setModelIndex(newIndex, row, column);
+ } else if (item->modelIndex() >= index) {
+ item->setModelIndex(-1, -1, -1);
+ }
}
QVector<Compositor::Remove> removes;
@@ -1456,10 +1539,17 @@ void QQmlDelegateModel::_q_itemsMoved(int from, int to, int count)
const QList<QQmlDelegateModelItem *> cache = d->m_cache;
for (int i = 0, c = cache.count(); i < c; ++i) {
QQmlDelegateModelItem *item = cache.at(i);
- if (item->modelIndex() >= from && item->modelIndex() < from + count)
- item->setModelIndex(item->modelIndex() - from + to);
- else if (item->modelIndex() >= minimum && item->modelIndex() < maximum)
- item->setModelIndex(item->modelIndex() + difference);
+ if (item->modelIndex() >= from && item->modelIndex() < from + count) {
+ const int newIndex = item->modelIndex() - from + to;
+ const int row = newIndex;
+ const int column = 0;
+ item->setModelIndex(newIndex, row, column);
+ } else if (item->modelIndex() >= minimum && item->modelIndex() < maximum) {
+ const int newIndex = item->modelIndex() + difference;
+ const int row = newIndex;
+ const int column = 0;
+ item->setModelIndex(newIndex, row, column);
+ }
}
QVector<Compositor::Remove> removes;
@@ -1477,6 +1567,32 @@ void QQmlDelegateModelPrivate::emitModelUpdated(const QQmlChangeSet &changeSet,
emit q->countChanged();
}
+void QQmlDelegateModelPrivate::delegateChanged(bool add, bool remove)
+{
+ Q_Q(QQmlDelegateModel);
+ if (!m_complete)
+ return;
+
+ if (m_transaction) {
+ qmlWarning(q) << QQmlDelegateModel::tr("The delegates of a DelegateModel cannot be changed within onUpdated.");
+ return;
+ }
+
+ if (remove) {
+ for (int i = 1; i < m_groupCount; ++i) {
+ QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.remove(
+ 0, m_compositor.count(Compositor::Group(i)));
+ }
+ }
+ if (add) {
+ for (int i = 1; i < m_groupCount; ++i) {
+ QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.insert(
+ 0, m_compositor.count(Compositor::Group(i)));
+ }
+ }
+ emitChanges();
+}
+
void QQmlDelegateModelPrivate::emitChanges()
{
if (m_transaction || !m_complete || !m_context || !m_context->isValid())
@@ -1510,13 +1626,13 @@ void QQmlDelegateModel::_q_modelReset()
d->m_adaptorModel.rootIndex = QModelIndex();
if (d->m_complete) {
- d->m_count = d->m_adaptorModel.count();
+ d->m_count = d->adaptorModelCount();
const QList<QQmlDelegateModelItem *> cache = d->m_cache;
for (int i = 0, c = cache.count(); i < c; ++i) {
QQmlDelegateModelItem *item = cache.at(i);
if (item->modelIndex() != -1)
- item->setModelIndex(-1);
+ item->setModelIndex(-1, -1, -1);
}
QVector<Compositor::Remove> removes;
@@ -1552,7 +1668,8 @@ void QQmlDelegateModel::_q_rowsAboutToBeRemoved(const QModelIndex &parent, int b
if (index.parent() == parent && index.row() >= begin && index.row() <= end) {
const int oldCount = d->m_count;
d->m_count = 0;
- d->m_adaptorModel.invalidateModel(this);
+ d->disconnectFromAbstractItemModel();
+ d->m_adaptorModel.invalidateModel();
if (d->m_complete && oldCount > 0) {
QVector<Compositor::Remove> removes;
@@ -1654,7 +1771,7 @@ bool QQmlDelegateModelPrivate::insert(Compositor::insert_iterator &before, const
if (!o)
return false;
- QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly|QV4::ObjectIterator::WithProtoChain);
+ QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly);
QV4::ScopedValue propertyName(scope);
QV4::ScopedValue v(scope);
while (1) {
@@ -1804,7 +1921,7 @@ int QQmlDelegateModelItemMetaType::parseGroups(const QV4::Value &groups) const
QV4::ScopedValue v(scope);
uint arrayLength = array->getLength();
for (uint i = 0; i < arrayLength; ++i) {
- v = array->getIndexed(i);
+ v = array->get(i);
const QString groupName = v->toQString();
int index = groupNames.indexOf(groupName);
if (index != -1)
@@ -1819,7 +1936,7 @@ QV4::ReturnedValue QQmlDelegateModelItem::get_model(const QV4::FunctionObject *b
QV4::Scope scope(b);
QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
if (!o)
- return b->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object"));
+ return b->engine()->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
if (!o->d()->item->metaType->model)
RETURN_UNDEFINED();
@@ -1831,7 +1948,7 @@ QV4::ReturnedValue QQmlDelegateModelItem::get_groups(const QV4::FunctionObject *
QV4::Scope scope(b);
QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
if (!o)
- return scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"));
+ return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
QStringList groups;
for (int i = 1; i < o->d()->item->metaType->groupCount; ++i) {
@@ -1847,7 +1964,7 @@ QV4::ReturnedValue QQmlDelegateModelItem::set_groups(const QV4::FunctionObject *
QV4::Scope scope(b);
QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
if (!o)
- return scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"));
+ return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
if (!argc)
THROW_TYPE_ERROR();
@@ -1906,18 +2023,21 @@ void QV4::Heap::QQmlDelegateModelItemObject::destroy()
}
-QQmlDelegateModelItem::QQmlDelegateModelItem(
- QQmlDelegateModelItemMetaType *metaType, int modelIndex)
+QQmlDelegateModelItem::QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex, int row, int column)
: v4(metaType->v4Engine)
, metaType(metaType)
, contextData(nullptr)
, object(nullptr)
, attached(nullptr)
, incubationTask(nullptr)
+ , delegate(nullptr)
+ , poolTime(0)
, objectRef(0)
, scriptRef(0)
, groups(0)
, index(modelIndex)
+ , row(row)
+ , column(column)
{
metaType->addref();
}
@@ -1952,6 +2072,24 @@ void QQmlDelegateModelItem::Dispose()
delete this;
}
+void QQmlDelegateModelItem::setModelIndex(int idx, int newRow, int newColumn)
+{
+ const int prevIndex = index;
+ const int prevRow = row;
+ const int prevColumn = column;
+
+ index = idx;
+ row = newRow;
+ column = newColumn;
+
+ if (idx != prevIndex)
+ emit modelIndexChanged();
+ if (row != prevRow)
+ emit rowChanged();
+ if (column != prevColumn)
+ emit columnChanged();
+}
+
void QQmlDelegateModelItem::destroyObject()
{
Q_ASSERT(object);
@@ -2080,28 +2218,35 @@ QQmlDelegateModelAttached::QQmlDelegateModelAttached(
, m_previousGroups(cacheItem->groups)
{
QQml_setParent_noEvent(this, parent);
+ resetCurrentIndex();
+ // Let m_previousIndex be equal to m_currentIndex
+ std::copy(std::begin(m_currentIndex), std::end(m_currentIndex), std::begin(m_previousIndex));
+
+ if (!cacheItem->metaType->metaObject)
+ cacheItem->metaType->initializeMetaObject();
+
+ QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject;
+ cacheItem->metaType->metaObject->addref();
+}
+
+void QQmlDelegateModelAttached::resetCurrentIndex()
+{
if (QQDMIncubationTask *incubationTask = m_cacheItem->incubationTask) {
for (int i = 1; i < qMin<int>(m_cacheItem->metaType->groupCount, Compositor::MaximumGroupCount); ++i)
- m_currentIndex[i] = m_previousIndex[i] = incubationTask->index[i];
+ m_currentIndex[i] = incubationTask->index[i];
} else {
QQmlDelegateModelPrivate * const model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model);
Compositor::iterator it = model->m_compositor.find(
Compositor::Cache, model->m_cache.indexOf(m_cacheItem));
for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i)
- m_currentIndex[i] = m_previousIndex[i] = it.index[i];
+ m_currentIndex[i] = it.index[i];
}
-
- if (!cacheItem->metaType->metaObject)
- cacheItem->metaType->initializeMetaObject();
-
- QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject;
- cacheItem->metaType->metaObject->addref();
}
/*!
\qmlattachedproperty int QtQml.Models::DelegateModel::model
- This attached property holds the visual data model this delegate instance belongs to.
+ This attached property holds the data model this delegate instance belongs to.
It is attached to each instance of the delegate.
*/
@@ -2148,7 +2293,7 @@ void QQmlDelegateModelAttached::setGroups(const QStringList &groups)
/*!
\qmlattachedproperty bool QtQml.Models::DelegateModel::isUnresolved
- This attached property holds whether the visual item is bound to a data model index.
+ This attached property indicates whether the visual item is bound to a data model index.
Returns true if the item is not bound to the model, and false if it is.
An unresolved item can be bound to the data model using the DelegateModelGroup::resolve()
@@ -2287,22 +2432,6 @@ void QQmlDelegateModelGroupPrivate::destroyingPackage(QQuickPackage *package)
}
/*!
- \qmltype VisualDataGroup
- \instantiates QQmlDelegateModelGroup
- \inqmlmodule QtQuick
- \ingroup qtquick-models
- \brief Encapsulates a filtered set of visual data items.
-
- The VisualDataGroup type provides a means to address the model data of a
- model's delegate items, as well as sort and filter these delegate items.
-
- This type is provided by the \l{Qt QML} module due to compatibility reasons.
- The same implementation is now primarily available as \l DelegateModelGroup
- in the \l{Qt QML Models QML Types}{Qt QML Models} module.
-
- \sa {QtQml.Models::DelegateModelGroup}
-*/
-/*!
\qmltype DelegateModelGroup
\instantiates QQmlDelegateModelGroup
\inqmlmodule QtQml.Models
@@ -2335,9 +2464,6 @@ void QQmlDelegateModelGroupPrivate::destroyingPackage(QQuickPackage *package)
type or to cherry-pick specific items that should be instantiated irregardless of whether
they're currently within a view's visible area.
- \note This type is also available as \l VisualDataGroup in the \l{Qt QML}
- module due to compatibility reasons.
-
\sa {QML Dynamic View Ordering Tutorial}
*/
QQmlDelegateModelGroup::QQmlDelegateModelGroup(QObject *parent)
@@ -2483,9 +2609,9 @@ QQmlV4Handle QQmlDelegateModelGroup::get(int index)
model->m_cacheMetaType->initializePrototype();
QV4::ExecutionEngine *v4 = model->m_cacheMetaType->v4Engine;
QV4::Scope scope(v4);
- QV4::ScopedObject o(scope, v4->memoryManager->allocObject<QQmlDelegateModelItemObject>(cacheItem));
+ QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(cacheItem));
QV4::ScopedObject p(scope, model->m_cacheMetaType->modelItemProto.value());
- o->setPrototype(p);
+ o->setPrototypeOf(p);
++cacheItem->scriptRef;
return QQmlV4Handle(o);
@@ -3194,7 +3320,10 @@ QQmlIncubator::Status QQmlPartsModel::incubationStatus(int index)
if (!it->inCache())
return QQmlIncubator::Null;
- return model->m_cache.at(it.cacheIndex)->incubationTask->status();
+ if (auto incubationTask = model->m_cache.at(it.cacheIndex)->incubationTask)
+ return incubationTask->status();
+
+ return QQmlIncubator::Ready;
}
int QQmlPartsModel::indexOf(QObject *item, QObject *) const
@@ -3214,7 +3343,10 @@ void QQmlPartsModel::createdPackage(int index, QQuickPackage *package)
void QQmlPartsModel::initPackage(int index, QQuickPackage *package)
{
- emit initItem(index, package->part(m_part));
+ if (m_modelUpdatePending)
+ m_pendingPackageInitializations << index;
+ else
+ emit initItem(index, package->part(m_part));
}
void QQmlPartsModel::destroyingPackage(QQuickPackage *package)
@@ -3226,9 +3358,22 @@ void QQmlPartsModel::destroyingPackage(QQuickPackage *package)
void QQmlPartsModel::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset)
{
+ m_modelUpdatePending = false;
emit modelUpdated(changeSet, reset);
if (changeSet.difference() != 0)
emit countChanged();
+
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model);
+ QVector<int> pendingPackageInitializations;
+ qSwap(pendingPackageInitializations, m_pendingPackageInitializations);
+ for (int index : pendingPackageInitializations) {
+ if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup))
+ continue;
+ QObject *object = model->object(m_compositorGroup, index, QQmlIncubator::Asynchronous);
+ if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object))
+ emit initItem(index, package->part(m_part));
+ model->release(object);
+ }
}
//============================================================================
@@ -3238,7 +3383,7 @@ struct QQmlDelegateModelGroupChange : QV4::Object
V4_OBJECT2(QQmlDelegateModelGroupChange, QV4::Object)
static QV4::Heap::QQmlDelegateModelGroupChange *create(QV4::ExecutionEngine *e) {
- return e->memoryManager->allocObject<QQmlDelegateModelGroupChange>();
+ return e->memoryManager->allocate<QQmlDelegateModelGroupChange>();
}
static QV4::ReturnedValue method_get_index(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) {
@@ -3275,49 +3420,49 @@ struct QQmlDelegateModelGroupChangeArray : public QV4::Object
public:
static QV4::Heap::QQmlDelegateModelGroupChangeArray *create(QV4::ExecutionEngine *engine, const QVector<QQmlChangeSet::Change> &changes)
{
- return engine->memoryManager->allocObject<QQmlDelegateModelGroupChangeArray>(changes);
+ return engine->memoryManager->allocate<QQmlDelegateModelGroupChangeArray>(changes);
}
quint32 count() const { return d()->changes->count(); }
const QQmlChangeSet::Change &at(int index) const { return d()->changes->at(index); }
- static QV4::ReturnedValue getIndexed(const QV4::Managed *m, uint index, bool *hasProperty)
+ static QV4::ReturnedValue virtualGet(const QV4::Managed *m, QV4::PropertyKey id, const QV4::Value *receiver, bool *hasProperty)
{
- Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>());
- QV4::ExecutionEngine *v4 = static_cast<const QQmlDelegateModelGroupChangeArray *>(m)->engine();
- QV4::Scope scope(v4);
- QV4::Scoped<QQmlDelegateModelGroupChangeArray> array(scope, static_cast<const QQmlDelegateModelGroupChangeArray *>(m));
-
- if (index >= array->count()) {
- if (hasProperty)
- *hasProperty = false;
- return QV4::Primitive::undefinedValue().asReturnedValue();
- }
+ if (id.isArrayIndex()) {
+ uint index = id.asArrayIndex();
+ Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>());
+ QV4::ExecutionEngine *v4 = static_cast<const QQmlDelegateModelGroupChangeArray *>(m)->engine();
+ QV4::Scope scope(v4);
+ QV4::Scoped<QQmlDelegateModelGroupChangeArray> array(scope, static_cast<const QQmlDelegateModelGroupChangeArray *>(m));
+
+ if (index >= array->count()) {
+ if (hasProperty)
+ *hasProperty = false;
+ return QV4::Value::undefinedValue().asReturnedValue();
+ }
- const QQmlChangeSet::Change &change = array->at(index);
+ const QQmlChangeSet::Change &change = array->at(index);
- QV4::ScopedObject changeProto(scope, engineData(v4)->changeProto.value());
- QV4::Scoped<QQmlDelegateModelGroupChange> object(scope, QQmlDelegateModelGroupChange::create(v4));
- object->setPrototype(changeProto);
- object->d()->change = change;
+ QV4::ScopedObject changeProto(scope, engineData(v4)->changeProto.value());
+ QV4::Scoped<QQmlDelegateModelGroupChange> object(scope, QQmlDelegateModelGroupChange::create(v4));
+ object->setPrototypeOf(changeProto);
+ object->d()->change = change;
- if (hasProperty)
- *hasProperty = true;
- return object.asReturnedValue();
- }
+ if (hasProperty)
+ *hasProperty = true;
+ return object.asReturnedValue();
+ }
- static QV4::ReturnedValue get(const QV4::Managed *m, QV4::String *name, bool *hasProperty)
- {
Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>());
const QQmlDelegateModelGroupChangeArray *array = static_cast<const QQmlDelegateModelGroupChangeArray *>(m);
- if (name->equals(array->engine()->id_length())) {
+ if (id == array->engine()->id_length()->propertyKey()) {
if (hasProperty)
*hasProperty = true;
return QV4::Encode(array->count());
}
- return Object::get(m, name, hasProperty);
+ return Object::virtualGet(m, id, receiver, hasProperty);
}
};
diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h
index b0786cd088..0ad8939732 100644
--- a/src/qml/types/qqmldelegatemodel_p.h
+++ b/src/qml/types/qqmldelegatemodel_p.h
@@ -67,7 +67,6 @@ QT_REQUIRE_CONFIG(qml_delegate_model);
QT_BEGIN_NAMESPACE
class QQmlChangeSet;
-class QQmlComponent;
class QQuickPackage;
class QQmlV4Function;
class QQmlDelegateModelGroup;
@@ -130,6 +129,8 @@ public:
QQmlListProperty<QQmlDelegateModelGroup> groups();
QObject *parts();
+ const QAbstractItemModel *abstractItemModel() const override;
+
bool event(QEvent *) override;
static QQmlDelegateModelAttached *qmlAttachedProperties(QObject *obj);
@@ -212,6 +213,7 @@ public:
QQmlDelegateModelAttached(QQmlDelegateModelItem *cacheItem, QObject *parent);
~QQmlDelegateModelAttached() {}
+ void resetCurrentIndex();
void setCacheItem(QQmlDelegateModelItem *item);
QQmlDelegateModel *model() const;
diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h
index 18980cfd7c..2d6fdf228e 100644
--- a/src/qml/types/qqmldelegatemodel_p_p.h
+++ b/src/qml/types/qqmldelegatemodel_p_p.h
@@ -67,8 +67,9 @@ QT_BEGIN_NAMESPACE
typedef QQmlListCompositor Compositor;
class QQmlDelegateModelAttachedMetaObject;
+class QQmlAbstractDelegateComponent;
-class QQmlDelegateModelItemMetaType : public QQmlRefCount
+class Q_QML_PRIVATE_EXPORT QQmlDelegateModelItemMetaType : public QQmlRefCount
{
public:
QQmlDelegateModelItemMetaType(QV4::ExecutionEngine *engine, QQmlDelegateModel *model, const QStringList &groupNames);
@@ -95,9 +96,11 @@ class QQmlDelegateModelItem : public QObject
{
Q_OBJECT
Q_PROPERTY(int index READ modelIndex NOTIFY modelIndexChanged)
+ Q_PROPERTY(int row MEMBER row NOTIFY rowChanged)
+ Q_PROPERTY(int column MEMBER column NOTIFY columnChanged)
Q_PROPERTY(QObject *model READ modelObject CONSTANT)
public:
- QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex);
+ QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex, int row, int column);
~QQmlDelegateModelItem();
void referenceObject() { ++objectRef; }
@@ -121,7 +124,7 @@ public:
int groupIndex(Compositor::Group group);
int modelIndex() const { return index; }
- void setModelIndex(int idx) { index = idx; Q_EMIT modelIndexChanged(); }
+ virtual void setModelIndex(int idx, int newRow, int newColumn);
virtual QV4::ReturnedValue get() { return QV4::QObjectWrapper::wrap(v4, this); }
@@ -141,6 +144,8 @@ public:
QPointer<QObject> object;
QPointer<QQmlDelegateModelAttached> attached;
QQDMIncubationTask *incubationTask;
+ QQmlComponent *delegate;
+ int poolTime;
int objectRef;
int scriptRef;
int groups;
@@ -148,9 +153,13 @@ public:
Q_SIGNALS:
void modelIndexChanged();
+ void rowChanged();
+ void columnChanged();
protected:
void objectDestroyed(QObject *);
+ int row;
+ int column;
};
namespace QV4 {
@@ -190,8 +199,8 @@ public:
void statusChanged(Status) override;
void setInitialState(QObject *) override;
- QQmlDelegateModelItem *incubating;
- QQmlDelegateModelPrivate *vdm;
+ QQmlDelegateModelItem *incubating = nullptr;
+ QQmlDelegateModelPrivate *vdm = nullptr;
int index[QQmlListCompositor::MaximumGroupCount];
};
@@ -256,6 +265,8 @@ public:
void init();
void connectModel(QQmlAdaptorModel *model);
+ void connectToAbstractItemModel();
+ void disconnectFromAbstractItemModel();
void requestMoreIfNecessary();
QObject *object(Compositor::Group group, int index, QQmlIncubator::IncubationMode incubationMode);
@@ -269,6 +280,7 @@ public:
Q_EMIT q_func()->initItem(incubationTask->index[m_compositorGroup], item); }
void emitDestroyingPackage(QQuickPackage *package);
void emitDestroyingItem(QObject *item) { Q_EMIT q_func()->destroyingItem(item); }
+ void addCacheItem(QQmlDelegateModelItem *item, Compositor::iterator it);
void removeCacheItem(QQmlDelegateModelItem *cacheItem);
void updateFilterGroup();
@@ -292,9 +304,12 @@ public:
void itemsChanged(const QVector<Compositor::Change> &changes);
void emitChanges();
void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) override;
+ void delegateChanged(bool add = true, bool remove = true);
bool insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups);
+ int adaptorModelCount() const;
+
static void group_append(QQmlListProperty<QQmlDelegateModelGroup> *property, QQmlDelegateModelGroup *group);
static int group_count(QQmlListProperty<QQmlDelegateModelGroup> *property);
static QQmlDelegateModelGroup *group_at(QQmlListProperty<QQmlDelegateModelGroup> *property, int index);
@@ -305,7 +320,9 @@ public:
QQmlAdaptorModel m_adaptorModel;
QQmlListCompositor m_compositor;
- QQmlComponent *m_delegate;
+ QQmlStrongJSQObjectReference<QQmlComponent> m_delegate;
+ QQmlAbstractDelegateComponent *m_delegateChooser;
+ QMetaObject::Connection m_delegateChooserChanged;
QQmlDelegateModelItemMetaType *m_cacheMetaType;
QPointer<QQmlContext> m_context;
QQmlDelegateModelParts *m_parts;
@@ -378,8 +395,10 @@ private:
QString m_part;
QString m_filterGroup;
QList<QByteArray> m_watchedRoles;
+ QVector<int> m_pendingPackageInitializations; // vector holds model indices
Compositor::Group m_compositorGroup;
bool m_inheritGroup;
+ bool m_modelUpdatePending = true;
};
class QMetaPropertyBuilder;
diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp
index 830c2bef5a..debf14df97 100644
--- a/src/qml/types/qqmllistmodel.cpp
+++ b/src/qml/types/qqmllistmodel.cpp
@@ -62,6 +62,8 @@
#include <QtCore/qdatetime.h>
#include <QScopedValueRollback>
+Q_DECLARE_METATYPE(const QV4::CompiledData::Binding*);
+
QT_BEGIN_NAMESPACE
// Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models.
@@ -124,8 +126,8 @@ const ListLayout::Role &ListLayout::getRoleOrCreate(QV4::String *key, Role::Data
const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type)
{
- const int dataSizes[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) };
- const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) };
+ const int dataSizes[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) };
+ const int dataAlignments[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) };
Role *r = new Role;
r->name = key;
@@ -227,6 +229,10 @@ const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QV
data.value<QJSValue>().isCallable()) {
type = Role::Function;
break;
+ } else if (data.userType() == qMetaTypeId<const QV4::CompiledData::Binding*>()
+ && data.value<const QV4::CompiledData::Binding*>()->isTranslationBinding()) {
+ type = Role::String;
+ break;
} else {
type = Role::List;
break;
@@ -261,6 +267,75 @@ const ListLayout::Role *ListLayout::getExistingRole(QV4::String *key) const
return r;
}
+StringOrTranslation::StringOrTranslation(const QString &s)
+{
+ d.setFlag();
+ setString(s);
+}
+
+StringOrTranslation::StringOrTranslation(const QV4::CompiledData::Binding *binding)
+{
+ d.setFlag();
+ clear();
+ d = binding;
+}
+
+StringOrTranslation::~StringOrTranslation()
+{
+ clear();
+}
+
+void StringOrTranslation::setString(const QString &s)
+{
+ d.setFlag();
+ clear();
+ QStringData *stringData = const_cast<QString &>(s).data_ptr();
+ d = stringData;
+ if (stringData)
+ stringData->ref.ref();
+}
+
+void StringOrTranslation::setTranslation(const QV4::CompiledData::Binding *binding)
+{
+ d.setFlag();
+ clear();
+ d = binding;
+}
+
+QString StringOrTranslation::toString(const QQmlListModel *owner) const
+{
+ if (d.isNull())
+ return QString();
+ if (d.isT1()) {
+ QStringDataPtr holder = { d.asT1() };
+ holder.ptr->ref.ref();
+ return QString(holder);
+ }
+ if (!owner)
+ return QString();
+ return d.asT2()->valueAsString(owner->m_compilationUnit.data());
+}
+
+QString StringOrTranslation::asString() const
+{
+ if (d.isNull())
+ return QString();
+ if (!d.isT1())
+ return QString();
+ QStringDataPtr holder = { d.asT1() };
+ holder.ptr->ref.ref();
+ return QString(holder);
+}
+
+void StringOrTranslation::clear()
+{
+ if (QStringData *strData = d.isT1() ? d.asT1() : nullptr) {
+ if (!strData->ref.deref())
+ QStringData::deallocate(strData);
+ }
+ d = static_cast<QStringData *>(nullptr);
+}
+
QObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementIndex)
{
ListElement *e = elements[elementIndex];
@@ -488,7 +563,7 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles)
QV4::Scope scope(v4);
QV4::ScopedObject o(scope);
- QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::WithProtoChain|QV4::ObjectIterator::EnumerableOnly);
+ QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly);
QV4::ScopedString propertyName(scope);
QV4::ScopedValue propertyValue(scope);
while (1) {
@@ -512,7 +587,7 @@ void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles)
int arrayLength = a->getLength();
for (int j=0 ; j < arrayLength ; ++j) {
- o = a->getIndexed(j);
+ o = a->get(j);
subModel->append(o);
}
@@ -567,7 +642,7 @@ void ListModel::set(int elementIndex, QV4::Object *object)
QV4::ExecutionEngine *v4 = object->engine();
QV4::Scope scope(v4);
- QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::WithProtoChain|QV4::ObjectIterator::EnumerableOnly);
+ QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly);
QV4::ScopedString propertyName(scope);
QV4::ScopedValue propertyValue(scope);
QV4::ScopedObject o(scope);
@@ -593,7 +668,7 @@ void ListModel::set(int elementIndex, QV4::Object *object)
int arrayLength = a->getLength();
for (int j=0 ; j < arrayLength ; ++j) {
- o = a->getIndexed(j);
+ o = a->get(j);
subModel->append(o);
}
@@ -717,11 +792,11 @@ ModelNodeMetaObject *ListElement::objectCache()
return ModelNodeMetaObject::get(m_objectCache);
}
-QString *ListElement::getStringProperty(const ListLayout::Role &role)
+StringOrTranslation *ListElement::getStringProperty(const ListLayout::Role &role)
{
char *mem = getPropertyMemory(role);
- QString *s = reinterpret_cast<QString *>(mem);
- return s->data_ptr() ? s : nullptr;
+ StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem);
+ return s;
}
QObject *ListElement::getQObjectProperty(const ListLayout::Role &role)
@@ -806,9 +881,9 @@ QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListMo
break;
case ListLayout::Role::String:
{
- QString *value = reinterpret_cast<QString *>(mem);
- if (value->data_ptr() != nullptr)
- data = *value;
+ StringOrTranslation *value = reinterpret_cast<StringOrTranslation *>(mem);
+ if (value->isSet())
+ data = value->toString(owner);
}
break;
case ListLayout::Role::Bool:
@@ -878,15 +953,13 @@ int ListElement::setStringProperty(const ListLayout::Role &role, const QString &
if (role.type == ListLayout::Role::String) {
char *mem = getPropertyMemory(role);
- QString *c = reinterpret_cast<QString *>(mem);
+ StringOrTranslation *c = reinterpret_cast<StringOrTranslation *>(mem);
bool changed;
- if (c->data_ptr() == nullptr) {
- new (mem) QString(s);
+ if (!c->isSet() || c->isTranslation())
changed = true;
- } else {
- changed = c->compare(s) != 0;
- *c = s;
- }
+ else
+ changed = c->asString().compare(s) != 0;
+ c->setString(s);
if (changed)
roleIndex = role.index;
}
@@ -1048,11 +1121,25 @@ int ListElement::setFunctionProperty(const ListLayout::Role &role, const QJSValu
return roleIndex;
}
+int ListElement::setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b)
+{
+ int roleIndex = -1;
+
+ if (role.type == ListLayout::Role::String) {
+ char *mem = getPropertyMemory(role);
+ StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem);
+ s->setTranslation(b);
+ roleIndex = role.index;
+ }
+
+ return roleIndex;
+}
+
void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s)
{
char *mem = getPropertyMemory(role);
- new (mem) QString(s);
+ new (mem) StringOrTranslation(s);
}
void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d)
@@ -1219,9 +1306,9 @@ void ListElement::destroy(ListLayout *layout)
switch (r.type) {
case ListLayout::Role::String:
{
- QString *string = getStringProperty(r);
+ StringOrTranslation *string = getStringProperty(r);
if (string)
- string->~QString();
+ string->~StringOrTranslation();
}
break;
case ListLayout::Role::List:
@@ -1267,7 +1354,10 @@ void ListElement::destroy(ListLayout *layout)
}
}
- delete m_objectCache;
+ if (m_objectCache) {
+ m_objectCache->~QObject();
+ operator delete(m_objectCache);
+ }
}
if (next)
@@ -1284,7 +1374,10 @@ int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant
roleIndex = setDoubleProperty(role, d.toDouble());
break;
case ListLayout::Role::String:
- roleIndex = setStringProperty(role, d.toString());
+ if (d.userType() == qMetaTypeId<const QV4::CompiledData::Binding *>())
+ roleIndex = setTranslationProperty(role, d.value<const QV4::CompiledData::Binding*>());
+ else
+ roleIndex = setStringProperty(role, d.toString());
break;
case ListLayout::Role::Bool:
roleIndex = setBoolProperty(role, d.toBool());
@@ -1332,7 +1425,7 @@ int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d
ListModel *subModel = new ListModel(role.subLayout, nullptr);
int arrayLength = a->getLength();
for (int j=0 ; j < arrayLength ; ++j) {
- o = a->getIndexed(j);
+ o = a->get(j);
subModel->append(o);
}
roleIndex = setListProperty(role, subModel);
@@ -1446,7 +1539,7 @@ void ModelNodeMetaObject::propertyWritten(int index)
return;
QString propName = QString::fromUtf8(name(index));
- QVariant value = operator[](index);
+ const QVariant value = this->value(index);
QV4::Scope scope(m_model->engine());
QV4::ScopedValue v(scope, scope.engine->fromVariant(value));
@@ -1474,29 +1567,37 @@ void ModelNodeMetaObject::emitDirectNotifies(const int *changedRoles, int roleCo
namespace QV4 {
-bool ModelObject::put(Managed *m, String *name, const Value &value)
+bool ModelObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
{
+ if (!id.isString())
+ return Object::virtualPut(m, id, value, receiver);
+ QString propName = id.toQString();
+
ModelObject *that = static_cast<ModelObject*>(m);
ExecutionEngine *eng = that->engine();
const int elementIndex = that->d()->elementIndex();
- const QString propName = name->toQString();
int roleIndex = that->d()->m_model->m_listModel->setExistingProperty(elementIndex, propName, value, eng);
if (roleIndex != -1)
that->d()->m_model->emitItemsChanged(elementIndex, 1, QVector<int>(1, roleIndex));
ModelNodeMetaObject *mo = ModelNodeMetaObject::get(that->object());
if (mo->initialized())
- mo->emitPropertyNotification(name->toQString().toUtf8());
+ mo->emitPropertyNotification(propName.toUtf8());
return true;
}
-ReturnedValue ModelObject::get(const Managed *m, String *name, bool *hasProperty)
+ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
{
+ if (!id.isString())
+ return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
+
const ModelObject *that = static_cast<const ModelObject*>(m);
+ Scope scope(that);
+ ScopedString name(scope, id.asStringOrSymbol());
const ListLayout::Role *role = that->d()->m_model->m_listModel->getExistingRole(name);
if (!role)
- return QObjectWrapper::get(m, name, hasProperty);
+ return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
if (hasProperty)
*hasProperty = true;
@@ -1512,27 +1613,43 @@ ReturnedValue ModelObject::get(const Managed *m, String *name, bool *hasProperty
return that->engine()->fromVariant(value);
}
-void ModelObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
+struct ModelObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator
{
- ModelObject *that = static_cast<ModelObject*>(m);
+ int roleNameIndex = 0;
+ ~ModelObjectOwnPropertyKeyIterator() override = default;
+ PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
+
+};
+
+PropertyKey ModelObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
+{
+ const ModelObject *that = static_cast<const ModelObject *>(o);
+
ExecutionEngine *v4 = that->engine();
- name->setM(nullptr);
- *index = UINT_MAX;
- if (it->arrayIndex < uint(that->d()->m_model->m_listModel->roleCount())) {
+ if (roleNameIndex < that->listModel()->roleCount()) {
Scope scope(that->engine());
- const ListLayout::Role &role = that->d()->m_model->m_listModel->getExistingRole(it->arrayIndex);
- ++it->arrayIndex;
+ const ListLayout::Role &role = that->listModel()->getExistingRole(roleNameIndex);
+ ++roleNameIndex;
ScopedString roleName(scope, v4->newString(role.name));
- name->setM(roleName->d());
- *attributes = QV4::Attr_Data;
- QVariant value = that->d()->m_model->data(that->d()->elementIndex(), role.index);
- p->value = v4->fromVariant(value);
- return;
+ if (attrs)
+ *attrs = QV4::Attr_Data;
+ if (pd) {
+ QVariant value = that->d()->m_model->data(that->d()->elementIndex(), role.index);
+ pd->value = v4->fromVariant(value);
+ }
+ return roleName->toPropertyKey();
}
+
// Fall back to QV4::Object as opposed to QV4::QObjectWrapper otherwise it will add
// unnecessary entries that relate to the roles used. These just create extra work
// later on as they will just be ignored.
- QV4::Object::advanceIterator(m, it, name, index, p, attributes);
+ return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
+}
+
+OwnPropertyKeyIterator *ModelObject::virtualOwnPropertyKeys(const Object *m, Value *target)
+{
+ *target = *m;
+ return new ModelObjectOwnPropertyKeyIterator;
}
DEFINE_OBJECT_VTABLE(ModelObject);
@@ -1766,13 +1883,13 @@ void DynamicRoleModelNodeMetaObject::propertyWritten(int index)
\snippet ../quick/threading/threadedlistmodel/timedisplay.qml 0
- The included file, \tt dataloader.js, looks like this:
+ The included file, \tt dataloader.mjs, looks like this:
- \snippet ../quick/threading/threadedlistmodel/dataloader.js 0
+ \snippet ../quick/threading/threadedlistmodel/dataloader.mjs 0
The timer in the main example sends messages to the worker script by calling
\l WorkerScript::sendMessage(). When this message is received,
- \c WorkerScript.onMessage() is invoked in \c dataloader.js,
+ \c WorkerScript.onMessage() is invoked in \c dataloader.mjs,
which appends the current time to the list model.
Note the call to sync() from the external thread.
@@ -1809,6 +1926,7 @@ QQmlListModel::QQmlListModel(const QQmlListModel *owner, ListModel *data, QV4::E
m_listModel = data;
m_engine = engine;
+ m_compilationUnit = owner->m_compilationUnit;
}
QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent)
@@ -1828,6 +1946,7 @@ QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agen
ListModel::sync(orig->m_listModel, m_listModel);
m_engine = nullptr;
+ m_compilationUnit = orig->m_compilationUnit;
}
QQmlListModel::~QQmlListModel()
@@ -2244,7 +2363,7 @@ void QQmlListModel::insert(QQmlV4Function *args)
int objectArrayLength = objectArray->getLength();
emitItemsAboutToBeInserted(index, objectArrayLength);
for (int i=0 ; i < objectArrayLength ; ++i) {
- argObject = objectArray->getIndexed(i);
+ argObject = objectArray->get(i);
if (m_dynamicRoles) {
m_modelObjects.insert(index+i, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this));
@@ -2356,7 +2475,7 @@ void QQmlListModel::append(QQmlV4Function *args)
emitItemsAboutToBeInserted(index, objectArrayLength);
for (int i=0 ; i < objectArrayLength ; ++i) {
- argObject = objectArray->getIndexed(i);
+ argObject = objectArray->get(i);
if (m_dynamicRoles) {
m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this));
@@ -2423,7 +2542,7 @@ void QQmlListModel::append(QQmlV4Function *args)
QQmlV4Handle QQmlListModel::get(int index) const
{
QV4::Scope scope(engine());
- QV4::ScopedValue result(scope, QV4::Primitive::undefinedValue());
+ QV4::ScopedValue result(scope, QV4::Value::undefinedValue());
if (index >= 0 && index < count()) {
@@ -2434,7 +2553,7 @@ QQmlV4Handle QQmlListModel::get(int index) const
QObject *object = m_listModel->getOrCreateModelObject(const_cast<QQmlListModel *>(this), index);
QQmlData *ddata = QQmlData::get(object);
if (ddata->jsWrapper.isNullOrUndefined()) {
- result = scope.engine->memoryManager->allocObject<QV4::ModelObject>(object, const_cast<QQmlListModel *>(this));
+ result = scope.engine->memoryManager->allocate<QV4::ModelObject>(object, const_cast<QQmlListModel *>(this));
// Keep track of the QObjectWrapper in persistent value storage
ddata->jsWrapper.set(scope.engine, result);
} else {
@@ -2551,12 +2670,12 @@ void QQmlListModel::sync()
qmlWarning(this) << "List sync() can only be called from a WorkerScript";
}
-bool QQmlListModelParser::verifyProperty(const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding)
+bool QQmlListModelParser::verifyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
{
if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
const quint32 targetObjectIndex = binding->value.objectIndex;
- const QV4::CompiledData::Object *target = qmlUnit->objectAt(targetObjectIndex);
- QString objName = qmlUnit->stringAt(target->inheritedTypeNameIndex);
+ const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex);
+ QString objName = compilationUnit->stringAt(target->inheritedTypeNameIndex);
if (objName != listElementTypeName) {
const QMetaObject *mo = resolveType(objName);
if (mo != &QQmlListElement::staticMetaObject) {
@@ -2566,23 +2685,23 @@ bool QQmlListModelParser::verifyProperty(const QV4::CompiledData::Unit *qmlUnit,
listElementTypeName = objName; // cache right name for next time
}
- if (!qmlUnit->stringAt(target->idNameIndex).isEmpty()) {
+ if (!compilationUnit->stringAt(target->idNameIndex).isEmpty()) {
error(target->locationOfIdProperty, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property"));
return false;
}
const QV4::CompiledData::Binding *binding = target->bindingTable();
for (quint32 i = 0; i < target->nBindings; ++i, ++binding) {
- QString propName = qmlUnit->stringAt(binding->propertyNameIndex);
+ QString propName = compilationUnit->stringAt(binding->propertyNameIndex);
if (propName.isEmpty()) {
error(binding, QQmlListModel::tr("ListElement: cannot contain nested elements"));
return false;
}
- if (!verifyProperty(qmlUnit, binding))
+ if (!verifyProperty(compilationUnit, binding))
return false;
}
} else if (binding->type == QV4::CompiledData::Binding::Type_Script) {
- QString scriptStr = binding->valueAsScriptString(qmlUnit);
+ QString scriptStr = binding->valueAsScriptString(compilationUnit.data());
if (!binding->isFunctionExpression() && !definesEmptyList(scriptStr)) {
QByteArray script = scriptStr.toUtf8();
bool ok;
@@ -2597,14 +2716,14 @@ bool QQmlListModelParser::verifyProperty(const QV4::CompiledData::Unit *qmlUnit,
return true;
}
-bool QQmlListModelParser::applyProperty(QV4::CompiledData::CompilationUnit *compilationUnit, const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex)
+bool QQmlListModelParser::applyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex)
{
- const QString elementName = qmlUnit->stringAt(binding->propertyNameIndex);
+ const QString elementName = compilationUnit->stringAt(binding->propertyNameIndex);
bool roleSet = false;
if (binding->type >= QV4::CompiledData::Binding::Type_Object) {
const quint32 targetObjectIndex = binding->value.objectIndex;
- const QV4::CompiledData::Object *target = qmlUnit->objectAt(targetObjectIndex);
+ const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex);
ListModel *subModel = nullptr;
if (outterElementIndex == -1) {
@@ -2625,20 +2744,22 @@ bool QQmlListModelParser::applyProperty(QV4::CompiledData::CompilationUnit *comp
const QV4::CompiledData::Binding *subBinding = target->bindingTable();
for (quint32 i = 0; i < target->nBindings; ++i, ++subBinding) {
- roleSet |= applyProperty(compilationUnit, qmlUnit, subBinding, subModel, elementIndex);
+ roleSet |= applyProperty(compilationUnit, subBinding, subModel, elementIndex);
}
} else {
QVariant value;
- if (binding->evaluatesToString()) {
- value = binding->valueAsString(qmlUnit);
+ if (binding->isTranslationBinding()) {
+ value = QVariant::fromValue<const QV4::CompiledData::Binding*>(binding);
+ } else if (binding->evaluatesToString()) {
+ value = binding->valueAsString(compilationUnit.data());
} else if (binding->type == QV4::CompiledData::Binding::Type_Number) {
- value = binding->valueAsNumber();
+ value = binding->valueAsNumber(compilationUnit->constants);
} else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) {
value = binding->valueAsBoolean();
} else if (binding->type == QV4::CompiledData::Binding::Type_Script) {
- QString scriptStr = binding->valueAsScriptString(qmlUnit);
+ QString scriptStr = binding->valueAsScriptString(compilationUnit.data());
if (definesEmptyList(scriptStr)) {
const ListLayout::Role &role = model->getOrCreateListRole(elementName);
ListModel *emptyModel = new ListModel(role.subLayout, nullptr);
@@ -2673,35 +2794,34 @@ bool QQmlListModelParser::applyProperty(QV4::CompiledData::CompilationUnit *comp
return roleSet;
}
-void QQmlListModelParser::verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
+void QQmlListModelParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
{
listElementTypeName = QString(); // unknown
for (const QV4::CompiledData::Binding *binding : bindings) {
- QString propName = qmlUnit->stringAt(binding->propertyNameIndex);
+ QString propName = compilationUnit->stringAt(binding->propertyNameIndex);
if (!propName.isEmpty()) { // isn't default property
error(binding, QQmlListModel::tr("ListModel: undefined property '%1'").arg(propName));
return;
}
- if (!verifyProperty(qmlUnit, binding))
+ if (!verifyProperty(compilationUnit, binding))
return;
}
}
-void QQmlListModelParser::applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
+void QQmlListModelParser::applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
{
QQmlListModel *rv = static_cast<QQmlListModel *>(obj);
rv->m_engine = qmlEngine(rv)->handle();
-
- const QV4::CompiledData::Unit *qmlUnit = compilationUnit->data;
+ rv->m_compilationUnit = compilationUnit;
bool setRoles = false;
for (const QV4::CompiledData::Binding *binding : bindings) {
if (binding->type != QV4::CompiledData::Binding::Type_Object)
continue;
- setRoles |= applyProperty(compilationUnit, qmlUnit, binding, rv->m_listModel, /*outter element index*/-1);
+ setRoles |= applyProperty(compilationUnit, binding, rv->m_listModel, /*outter element index*/-1);
}
if (setRoles == false)
diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h
index 0c0859dc80..95b797c898 100644
--- a/src/qml/types/qqmllistmodel_p.h
+++ b/src/qml/types/qqmllistmodel_p.h
@@ -64,6 +64,8 @@
#include <private/qv4engine_p.h>
#include <private/qpodvector_p.h>
+QT_REQUIRE_CONFIG(qml_list_model);
+
QT_BEGIN_NAMESPACE
@@ -122,6 +124,7 @@ private:
friend class ListElement;
friend class DynamicRoleModelNode;
friend class DynamicRoleModelNodeMetaObject;
+ friend struct StringOrTranslation;
// Constructs a flat list model for a worker agent
QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent);
@@ -133,6 +136,7 @@ private:
QQmlListModelWorkerAgent *m_agent;
mutable QV4::ExecutionEngine *m_engine;
+ QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit;
bool m_mainThread;
bool m_primary;
@@ -183,13 +187,13 @@ public:
QQmlListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {}
- void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
- void applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
+ void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
+ void applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
private:
- bool verifyProperty(const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding);
+ bool verifyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding);
// returns true if a role was set
- bool applyProperty(QV4::CompiledData::CompilationUnit *compilationUnit, const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex);
+ bool applyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex);
static bool definesEmptyList(const QString &);
diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h
index ad5e94c909..ff52ee049f 100644
--- a/src/qml/types/qqmllistmodel_p_p.h
+++ b/src/qml/types/qqmllistmodel_p_p.h
@@ -57,6 +57,8 @@
#include <private/qv4qobjectwrapper_p.h>
#include <qqml.h>
+QT_REQUIRE_CONFIG(qml_list_model);
+
QT_BEGIN_NAMESPACE
@@ -142,16 +144,6 @@ protected:
private:
using QQmlOpenMetaObject::setValue;
- void setValue(const QByteArray &name, const QVariant &val, bool force)
- {
- if (force) {
- QVariant existingValue = value(name);
- if (existingValue.isValid()) {
- (*this)[name] = QVariant();
- }
- }
- setValue(name, val);
- }
void emitDirectNotifies(const int *changedRoles, int roleCount);
@@ -181,12 +173,15 @@ struct ModelObject : public QObjectWrapper {
struct ModelObject : public QObjectWrapper
{
- static bool put(Managed *m, String *name, const Value& value);
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
-
V4_OBJECT2(ModelObject, QObjectWrapper)
V4_NEEDS_DESTROY
+
+ ListModel *listModel() const { return d()->m_model->m_listModel; }
+
+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 OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
};
} // namespace QV4
@@ -252,6 +247,22 @@ private:
QStringHash<Role *> roleHash;
};
+struct StringOrTranslation
+{
+ explicit StringOrTranslation(const QString &s);
+ explicit StringOrTranslation(const QV4::CompiledData::Binding *binding);
+ ~StringOrTranslation();
+ bool isSet() const { return d.flag(); }
+ bool isTranslation() const { return d.isT2(); }
+ void setString(const QString &s);
+ void setTranslation(const QV4::CompiledData::Binding *binding);
+ QString toString(const QQmlListModel *owner) const;
+ QString asString() const;
+private:
+ void clear();
+ QBiPointer<QStringData, const QV4::CompiledData::Binding> d;
+};
+
/*!
\internal
*/
@@ -287,6 +298,7 @@ private:
int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m);
int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt);
int setFunctionProperty(const ListLayout::Role &role, const QJSValue &f);
+ int setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b);
void setStringPropertyFast(const ListLayout::Role &role, const QString &s);
void setDoublePropertyFast(const ListLayout::Role &role, double n);
@@ -301,7 +313,7 @@ private:
QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV4::ExecutionEngine *eng);
ListModel *getListProperty(const ListLayout::Role &role);
- QString *getStringProperty(const ListLayout::Role &role);
+ StringOrTranslation *getStringProperty(const ListLayout::Role &role);
QObject *getQObjectProperty(const ListLayout::Role &role);
QPointer<QObject> *getGuardProperty(const ListLayout::Role &role);
QVariantMap *getVariantMapProperty(const ListLayout::Role &role);
diff --git a/src/qml/types/qqmllistmodelworkeragent_p.h b/src/qml/types/qqmllistmodelworkeragent_p.h
index 2120f25744..ae2d4b11e0 100644
--- a/src/qml/types/qqmllistmodelworkeragent_p.h
+++ b/src/qml/types/qqmllistmodelworkeragent_p.h
@@ -59,6 +59,8 @@
#include <private/qv8engine_p.h>
+QT_REQUIRE_CONFIG(qml_list_model);
+
QT_BEGIN_NAMESPACE
diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp
index e217b63c6f..30915d96fd 100644
--- a/src/qml/types/qqmlmodelsmodule.cpp
+++ b/src/qml/types/qqmlmodelsmodule.cpp
@@ -39,9 +39,12 @@
#include "qqmlmodelsmodule_p.h"
#include <QtCore/qitemselectionmodel.h>
+#if QT_CONFIG(qml_list_model)
#include <private/qqmllistmodel_p.h>
+#endif
#if QT_CONFIG(qml_delegate_model)
#include <private/qqmldelegatemodel_p.h>
+#include <private/qqmldelegatecomponent_p.h>
#endif
#include <private/qqmlobjectmodel_p.h>
@@ -51,8 +54,10 @@ void QQmlModelsModule::defineModule()
{
const char uri[] = "QtQml.Models";
+#if QT_CONFIG(qml_list_model)
qmlRegisterType<QQmlListElement>(uri, 2, 1, "ListElement");
qmlRegisterCustomType<QQmlListModel>(uri, 2, 1, "ListModel", new QQmlListModelParser);
+#endif
#if QT_CONFIG(qml_delegate_model)
qmlRegisterType<QQmlDelegateModel>(uri, 2, 1, "DelegateModel");
qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 1, "DelegateModelGroup");
@@ -63,4 +68,13 @@ void QQmlModelsModule::defineModule()
qmlRegisterType<QItemSelectionModel>(uri, 2, 2, "ItemSelectionModel");
}
+void QQmlModelsModule::defineLabsModule()
+{
+ const char uri[] = "Qt.labs.qmlmodels";
+
+ 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");
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/types/qqmlmodelsmodule_p.h b/src/qml/types/qqmlmodelsmodule_p.h
index bac9bea81e..939ecc1500 100644
--- a/src/qml/types/qqmlmodelsmodule_p.h
+++ b/src/qml/types/qqmlmodelsmodule_p.h
@@ -59,6 +59,7 @@ class Q_QML_PRIVATE_EXPORT QQmlModelsModule
{
public:
static void defineModule();
+ static void defineLabsModule();
};
QT_END_NAMESPACE
diff --git a/src/qml/types/qqmlobjectmodel.cpp b/src/qml/types/qqmlobjectmodel.cpp
index d94af64fde..2f4d427430 100644
--- a/src/qml/types/qqmlobjectmodel.cpp
+++ b/src/qml/types/qqmlobjectmodel.cpp
@@ -206,27 +206,10 @@ public:
}
\endcode
- \image visualitemmodel.png
+ \image objectmodel.png
\sa {Qt Quick Examples - Views}
*/
-/*!
- \qmltype VisualItemModel
- \instantiates QQmlObjectModel
- \inqmlmodule QtQuick
- \brief Defines a set of objects to be used as a model.
-
- The VisualItemModel type contains the objects to be used
- as a model.
-
- This element is now primarily available as ObjectModel in the QtQml.Models module.
- VisualItemModel continues to be provided, with the same implementation, in \c QtQuick for
- compatibility reasons.
-
- For full details about the type, see the \l ObjectModel documentation.
-
- \sa {QtQml.Models::ObjectModel}
-*/
QQmlObjectModel::QQmlObjectModel(QObject *parent)
: QQmlInstanceModel(*(new QQmlObjectModelPrivate), parent)
diff --git a/src/qml/types/qqmlobjectmodel_p.h b/src/qml/types/qqmlobjectmodel_p.h
index 267828dcdd..4ac4f1c65b 100644
--- a/src/qml/types/qqmlobjectmodel_p.h
+++ b/src/qml/types/qqmlobjectmodel_p.h
@@ -60,6 +60,7 @@ QT_BEGIN_NAMESPACE
class QObject;
class QQmlChangeSet;
+class QAbstractItemModel;
class Q_QML_PRIVATE_EXPORT QQmlInstanceModel : public QObject
{
@@ -83,6 +84,7 @@ public:
virtual QQmlIncubator::Status incubationStatus(int index) = 0;
virtual int indexOf(QObject *object, QObject *objectContext) const = 0;
+ virtual const QAbstractItemModel *abstractItemModel() const { return nullptr; }
Q_SIGNALS:
void countChanged();
diff --git a/src/qml/types/qqmltableinstancemodel.cpp b/src/qml/types/qqmltableinstancemodel.cpp
new file mode 100644
index 0000000000..1054158dc8
--- /dev/null
+++ b/src/qml/types/qqmltableinstancemodel.cpp
@@ -0,0 +1,542 @@
+/****************************************************************************
+**
+** 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 "qqmltableinstancemodel_p.h"
+#include "qqmldelegatecomponent_p.h"
+
+#include <QtCore/QTimer>
+
+#include <QtQml/private/qqmlincubator_p.h>
+#include <QtQml/private/qqmlchangeset_p.h>
+#include <QtQml/private/qqmlcomponent_p.h>
+
+QT_BEGIN_NAMESPACE
+
+const char* kModelItemTag = "_tableinstancemodel_modelItem";
+
+bool QQmlTableInstanceModel::isDoneIncubating(QQmlDelegateModelItem *modelItem)
+{
+ if (!modelItem->incubationTask)
+ return true;
+
+ const auto status = modelItem->incubationTask->status();
+ return (status == QQmlIncubator::Ready) || (status == QQmlIncubator::Error);
+}
+
+void QQmlTableInstanceModel::deleteModelItemLater(QQmlDelegateModelItem *modelItem)
+{
+ Q_ASSERT(modelItem);
+
+ delete modelItem->object;
+ modelItem->object = nullptr;
+
+ if (modelItem->contextData) {
+ modelItem->contextData->invalidate();
+ Q_ASSERT(modelItem->contextData->refCount == 1);
+ modelItem->contextData = nullptr;
+ }
+
+ modelItem->deleteLater();
+}
+
+QQmlTableInstanceModel::QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent)
+ : QQmlInstanceModel(*(new QObjectPrivate()), parent)
+ , m_qmlContext(qmlContext)
+ , m_metaType(new QQmlDelegateModelItemMetaType(m_qmlContext->engine()->handle(), nullptr, QStringList()))
+{
+}
+
+QQmlTableInstanceModel::~QQmlTableInstanceModel()
+{
+ for (const auto modelItem : m_modelItems) {
+ // No item in m_modelItems should be referenced at this point. The view
+ // should release all its items before it deletes this model. Only model items
+ // that are still being incubated should be left for us to delete.
+ Q_ASSERT(modelItem->objectRef == 0);
+ Q_ASSERT(modelItem->incubationTask);
+ // Check that we are not being deleted while we're
+ // in the process of e.g emitting a created signal.
+ Q_ASSERT(modelItem->scriptRef == 0);
+
+ if (modelItem->object) {
+ delete modelItem->object;
+ modelItem->object = nullptr;
+ modelItem->contextData->invalidate();
+ modelItem->contextData = nullptr;
+ }
+ }
+
+ deleteAllFinishedIncubationTasks();
+ qDeleteAll(m_modelItems);
+ drainReusableItemsPool(0);
+}
+
+QQmlComponent *QQmlTableInstanceModel::resolveDelegate(int index)
+{
+ if (m_delegateChooser) {
+ const int row = m_adaptorModel.rowAt(index);
+ const int column = m_adaptorModel.columnAt(index);
+ QQmlComponent *delegate = nullptr;
+ QQmlAbstractDelegateComponent *chooser = m_delegateChooser;
+ do {
+ delegate = chooser->delegate(&m_adaptorModel, row, column);
+ chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
+ } while (chooser);
+ return delegate;
+ }
+
+ return m_delegate;
+}
+
+QQmlDelegateModelItem *QQmlTableInstanceModel::resolveModelItem(int index)
+{
+ // Check if an item for the given index is already loaded and ready
+ QQmlDelegateModelItem *modelItem = m_modelItems.value(index, nullptr);
+ if (modelItem)
+ return modelItem;
+
+ QQmlComponent *delegate = resolveDelegate(index);
+ if (!delegate)
+ return nullptr;
+
+ // Check if the pool contains an item that can be reused
+ modelItem = takeFromReusableItemsPool(delegate);
+ if (modelItem) {
+ reuseItem(modelItem, index);
+ m_modelItems.insert(index, modelItem);
+ return modelItem;
+ }
+
+ // Create a new item from scratch
+ modelItem = m_adaptorModel.createItem(m_metaType, index);
+ if (modelItem) {
+ modelItem->delegate = delegate;
+ m_modelItems.insert(index, modelItem);
+ return modelItem;
+ }
+
+ qWarning() << Q_FUNC_INFO << "failed creating a model item for index: " << index;
+ return nullptr;
+}
+
+QObject *QQmlTableInstanceModel::object(int index, QQmlIncubator::IncubationMode incubationMode)
+{
+ Q_ASSERT(m_delegate);
+ Q_ASSERT(index >= 0 && index < m_adaptorModel.count());
+ Q_ASSERT(m_qmlContext && m_qmlContext->isValid());
+
+ QQmlDelegateModelItem *modelItem = resolveModelItem(index);
+ if (!modelItem)
+ return nullptr;
+
+ if (modelItem->object) {
+ // The model item has already been incubated. So
+ // just bump the ref-count and return it.
+ modelItem->referenceObject();
+ return modelItem->object;
+ }
+
+ // The object is not ready, and needs to be incubated
+ incubateModelItem(modelItem, incubationMode);
+ if (!isDoneIncubating(modelItem))
+ return nullptr;
+
+ // Incubation is done, so the task should be removed
+ Q_ASSERT(!modelItem->incubationTask);
+
+ if (!modelItem->object) {
+ // The object was incubated synchronously (otherwise we would return above). But since
+ // we have no object, the incubation must have failed. And when we have no object, there
+ // should be no object references either. And there should also not be any internal script
+ // refs at this point. So we delete the model item.
+ Q_ASSERT(!modelItem->isObjectReferenced());
+ Q_ASSERT(!modelItem->isReferenced());
+ m_modelItems.remove(modelItem->index);
+ delete modelItem;
+ return nullptr;
+ }
+
+ // Incubation was completed sync and successful
+ modelItem->referenceObject();
+ return modelItem->object;
+}
+
+QQmlInstanceModel::ReleaseFlags QQmlTableInstanceModel::release(QObject *object, ReusableFlag reusable)
+{
+ Q_ASSERT(object);
+ auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag));
+ Q_ASSERT(modelItem);
+
+ if (!modelItem->releaseObject())
+ return QQmlDelegateModel::Referenced;
+
+ if (modelItem->isReferenced()) {
+ // We still have an internal reference to this object, which means that we are told to release an
+ // object while the createdItem signal for it is still on the stack. This can happen when objects
+ // are e.g delivered async, and the user flicks back and forth quicker than the loading can catch
+ // up with. The view might then find that the object is no longer visible and should be released.
+ // We detect this case in incubatorStatusChanged(), and delete it there instead. But from the callers
+ // point of view, it should consider it destroyed.
+ return QQmlDelegateModel::Destroyed;
+ }
+
+ // The item is not referenced by anyone
+ m_modelItems.remove(modelItem->index);
+
+ if (reusable == Reusable) {
+ insertIntoReusableItemsPool(modelItem);
+ return QQmlInstanceModel::Referenced;
+ }
+
+ // The item is not reused or referenced by anyone, so just delete it
+ modelItem->destroyObject();
+ emit destroyingItem(object);
+
+ delete modelItem;
+ return QQmlInstanceModel::Destroyed;
+}
+
+void QQmlTableInstanceModel::cancel(int index)
+{
+ auto modelItem = m_modelItems.value(index);
+ Q_ASSERT(modelItem);
+
+ // Since the view expects the item to be incubating, there should be
+ // an incubation task. And since the incubation is not done, no-one
+ // should yet have received, and therfore hold a reference to, the object.
+ Q_ASSERT(modelItem->incubationTask);
+ Q_ASSERT(!modelItem->isObjectReferenced());
+
+ m_modelItems.remove(index);
+
+ if (modelItem->object)
+ delete modelItem->object;
+
+ // modelItem->incubationTask will be deleted from the modelItems destructor
+ delete modelItem;
+}
+
+void QQmlTableInstanceModel::insertIntoReusableItemsPool(QQmlDelegateModelItem *modelItem)
+{
+ // Currently, the only way for a view to reuse items is to call QQmlTableInstanceModel::release()
+ // with the second argument explicitly set to QQmlTableInstanceModel::Reusable. If the released
+ // item is no longer referenced, it will be added to the pool. Reusing of items can be specified
+ // per item, in case certain items cannot be recycled.
+ // A QQmlDelegateModelItem knows which delegate its object was created from. So when we are
+ // about to create a new item, we first check if the pool contains an item based on the same
+ // delegate from before. If so, we take it out of the pool (instead of creating a new item), and
+ // update all its context-, and attached properties.
+ // When a view is recycling items, it should call QQmlTableInstanceModel::drainReusableItemsPool()
+ // regularly. As there is currently no logic to 'hibernate' items in the pool, they are only
+ // meant to rest there for a short while, ideally only from the time e.g a row is unloaded
+ // on one side of the view, and until a new row is loaded on the opposite side. In-between
+ // this time, the application will see the item as fully functional and 'alive' (just not
+ // visible on screen). Since this time is supposed to be short, we don't take any action to
+ // notify the application about it, since we don't want to trigger any bindings that can
+ // disturb performance.
+ // A recommended time for calling drainReusableItemsPool() is each time a view has finished
+ // loading e.g a new row or column. If there are more items in the pool after that, it means
+ // that the view most likely doesn't need them anytime soon. Those items should be destroyed to
+ // not consume resources.
+ // Depending on if a view is a list or a table, it can sometimes be performant to keep
+ // items in the pool for a bit longer than one "row out/row in" cycle. E.g for a table, if the
+ // number of visible rows in a view is much larger than the number of visible columns.
+ // In that case, if you flick out a row, and then flick in a column, you would throw away a lot
+ // of items in the pool if completely draining it. The reason is that unloading a row places more
+ // items in the pool than what ends up being recycled when loading a new column. And then, when you
+ // next flick in a new row, you would need to load all those drained items again from scratch. For
+ // that reason, you can specify a maxPoolTime to the drainReusableItemsPool() that allows you to keep
+ // items in the pool for a bit longer, effectively keeping more items in circulation.
+ // A recommended maxPoolTime would be equal to the number of dimenstions in the view, which
+ // means 1 for a list view and 2 for a table view. If you specify 0, all items will be drained.
+ Q_ASSERT(!modelItem->incubationTask);
+ Q_ASSERT(!modelItem->isObjectReferenced());
+ Q_ASSERT(!modelItem->isReferenced());
+ Q_ASSERT(modelItem->object);
+
+ modelItem->poolTime = 0;
+ m_reusableItemsPool.append(modelItem);
+ emit itemPooled(modelItem->index, modelItem->object);
+}
+
+QQmlDelegateModelItem *QQmlTableInstanceModel::takeFromReusableItemsPool(const QQmlComponent *delegate)
+{
+ // Find the oldest item in the pool that was made from the same delegate as
+ // the given argument, remove it from the pool, and return it.
+ if (m_reusableItemsPool.isEmpty())
+ return nullptr;
+
+ for (auto it = m_reusableItemsPool.begin(); it != m_reusableItemsPool.end(); ++it) {
+ if ((*it)->delegate != delegate)
+ continue;
+ auto modelItem = *it;
+ m_reusableItemsPool.erase(it);
+ return modelItem;
+ }
+
+ return nullptr;
+}
+
+void QQmlTableInstanceModel::drainReusableItemsPool(int maxPoolTime)
+{
+ // Rather than releasing all pooled items upon a call to this function, each
+ // item has a poolTime. The poolTime specifies for how many loading cycles an item
+ // has been resting in the pool. And for each invocation of this function, poolTime
+ // will increase. If poolTime is equal to, or exceeds, maxPoolTime, it will be removed
+ // from the pool and released. This way, the view can tweak a bit for how long
+ // items should stay in "circulation", even if they are not recycled right away.
+ for (auto it = m_reusableItemsPool.begin(); it != m_reusableItemsPool.end();) {
+ auto modelItem = *it;
+ modelItem->poolTime++;
+ if (modelItem->poolTime <= maxPoolTime) {
+ ++it;
+ } else {
+ it = m_reusableItemsPool.erase(it);
+ release(modelItem->object, NotReusable);
+ }
+ }
+}
+
+void QQmlTableInstanceModel::reuseItem(QQmlDelegateModelItem *item, int newModelIndex)
+{
+ // Update the context properties index, row and column on
+ // the delegate item, and inform the application about it.
+ const int newRow = m_adaptorModel.rowAt(newModelIndex);
+ const int newColumn = m_adaptorModel.columnAt(newModelIndex);
+ item->setModelIndex(newModelIndex, newRow, newColumn);
+
+ // Notify the application that all 'dynamic'/role-based context data has
+ // changed as well (their getter function will use the updated index).
+ auto const itemAsList = QList<QQmlDelegateModelItem *>() << item;
+ auto const updateAllRoles = QVector<int>();
+ m_adaptorModel.notify(itemAsList, newModelIndex, 1, updateAllRoles);
+
+ // Inform the view that the item is recycled. This will typically result
+ // in the view updating its own attached delegate item properties.
+ emit itemReused(newModelIndex, item->object);
+}
+
+void QQmlTableInstanceModel::incubateModelItem(QQmlDelegateModelItem *modelItem, QQmlIncubator::IncubationMode incubationMode)
+{
+ // Guard the model item temporarily so that it's not deleted from
+ // incubatorStatusChanged(), in case the incubation is done synchronously.
+ modelItem->scriptRef++;
+
+ if (modelItem->incubationTask) {
+ // We're already incubating the model item from a previous request. If the previous call requested
+ // the item async, but the current request needs it sync, we need to force-complete the incubation.
+ const bool sync = (incubationMode == QQmlIncubator::Synchronous || incubationMode == QQmlIncubator::AsynchronousIfNested);
+ if (sync && modelItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous)
+ modelItem->incubationTask->forceCompletion();
+ } else {
+ modelItem->incubationTask = new QQmlTableInstanceModelIncubationTask(this, modelItem, incubationMode);
+
+ QQmlContextData *ctxt = new QQmlContextData;
+ QQmlContext *creationContext = modelItem->delegate->creationContext();
+ ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data()));
+ ctxt->contextObject = modelItem;
+ modelItem->contextData = ctxt;
+
+ QQmlComponentPrivate::get(modelItem->delegate)->incubateObject(
+ modelItem->incubationTask,
+ modelItem->delegate,
+ m_qmlContext->engine(),
+ ctxt,
+ QQmlContextData::get(m_qmlContext));
+ }
+
+ // Remove the temporary guard
+ modelItem->scriptRef--;
+}
+
+void QQmlTableInstanceModel::incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *incubationTask, QQmlIncubator::Status status)
+{
+ QQmlDelegateModelItem *modelItem = incubationTask->modelItemToIncubate;
+ Q_ASSERT(modelItem->incubationTask);
+
+ modelItem->incubationTask = nullptr;
+ incubationTask->modelItemToIncubate = nullptr;
+
+ if (status == QQmlIncubator::Ready) {
+ // Tag the incubated object with the model item for easy retrieval upon release etc.
+ modelItem->object->setProperty(kModelItemTag, QVariant::fromValue(modelItem));
+
+ // Emit that the item has been created. What normally happens next is that the view
+ // upon receiving the signal asks for the model item once more. And since the item is
+ // now in the map, it will be returned directly.
+ Q_ASSERT(modelItem->object);
+ modelItem->scriptRef++;
+ emit createdItem(modelItem->index, modelItem->object);
+ modelItem->scriptRef--;
+ } else if (status == QQmlIncubator::Error) {
+ qWarning() << "Error incubating delegate:" << incubationTask->errors();
+ }
+
+ if (!modelItem->isReferenced() && !modelItem->isObjectReferenced()) {
+ // We have no internal reference to the model item, and the view has no
+ // reference to the incubated object. So just delete the model item.
+ // Note that being here means that the object was incubated _async_
+ // (otherwise modelItem->isReferenced() would be true).
+ m_modelItems.remove(modelItem->index);
+
+ if (modelItem->object) {
+ modelItem->scriptRef++;
+ emit destroyingItem(modelItem->object);
+ modelItem->scriptRef--;
+ Q_ASSERT(!modelItem->isReferenced());
+ }
+
+ deleteModelItemLater(modelItem);
+ }
+
+ deleteIncubationTaskLater(incubationTask);
+}
+
+QQmlIncubator::Status QQmlTableInstanceModel::incubationStatus(int index) {
+ const auto modelItem = m_modelItems.value(index, nullptr);
+ if (!modelItem)
+ return QQmlIncubator::Null;
+
+ if (modelItem->incubationTask)
+ return modelItem->incubationTask->status();
+
+ // Since we clear the incubation task when we're done
+ // incubating, it means that the status is Ready.
+ return QQmlIncubator::Ready;
+}
+
+void QQmlTableInstanceModel::deleteIncubationTaskLater(QQmlIncubator *incubationTask)
+{
+ // We often need to post-delete incubation tasks, since we cannot
+ // delete them while we're in the middle of an incubation change callback.
+ Q_ASSERT(!m_finishedIncubationTasks.contains(incubationTask));
+ m_finishedIncubationTasks.append(incubationTask);
+ if (m_finishedIncubationTasks.count() == 1)
+ QTimer::singleShot(1, this, &QQmlTableInstanceModel::deleteAllFinishedIncubationTasks);
+}
+
+void QQmlTableInstanceModel::deleteAllFinishedIncubationTasks()
+{
+ qDeleteAll(m_finishedIncubationTasks);
+ m_finishedIncubationTasks.clear();
+}
+
+QVariant QQmlTableInstanceModel::model() const
+{
+ return m_adaptorModel.model();
+}
+
+void QQmlTableInstanceModel::setModel(const QVariant &model)
+{
+ // Pooled items are still accessible/alive for the application, and
+ // needs to stay in sync with the model. So we need to drain the pool
+ // completely when the model changes.
+ drainReusableItemsPool(0);
+ if (auto const aim = abstractItemModel())
+ disconnect(aim, &QAbstractItemModel::dataChanged, this, &QQmlTableInstanceModel::dataChangedCallback);
+ m_adaptorModel.setModel(model, this, m_qmlContext->engine());
+ if (auto const aim = abstractItemModel())
+ connect(aim, &QAbstractItemModel::dataChanged, this, &QQmlTableInstanceModel::dataChangedCallback);
+}
+
+void QQmlTableInstanceModel::dataChangedCallback(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles)
+{
+ // This function is called when model data has changed. In that case, we tell the adaptor model
+ // to go through all the items we have created, find the ones that are affected, and notify that
+ // their model data has changed. This will in turn update QML bindings inside the delegate items.
+ int numberOfRowsChanged = end.row() - begin.row() + 1;
+ int numberOfColumnsChanged = end.column() - begin.column() + 1;
+
+ for (int column = 0; column < numberOfColumnsChanged; ++column) {
+ const int columnIndex = begin.column() + column;
+ const int rowIndex = begin.row() + (columnIndex * rows());
+ m_adaptorModel.notify(m_modelItems.values(), rowIndex, numberOfRowsChanged, roles);
+ }
+}
+
+QQmlComponent *QQmlTableInstanceModel::delegate() const
+{
+ return m_delegate;
+}
+
+void QQmlTableInstanceModel::setDelegate(QQmlComponent *delegate)
+{
+ if (m_delegate == delegate)
+ return;
+
+ m_delegateChooser = nullptr;
+ if (delegate) {
+ QQmlAbstractDelegateComponent *adc =
+ qobject_cast<QQmlAbstractDelegateComponent *>(delegate);
+ if (adc)
+ m_delegateChooser = adc;
+ }
+
+ m_delegate = delegate;
+}
+
+const QAbstractItemModel *QQmlTableInstanceModel::abstractItemModel() const
+{
+ return m_adaptorModel.adaptsAim() ? m_adaptorModel.aim() : nullptr;
+}
+
+// --------------------------------------------------------
+
+void QQmlTableInstanceModelIncubationTask::setInitialState(QObject *object)
+{
+ modelItemToIncubate->object = object;
+ emit tableInstanceModel->initItem(modelItemToIncubate->index, object);
+}
+
+void QQmlTableInstanceModelIncubationTask::statusChanged(QQmlIncubator::Status status)
+{
+ if (!QQmlTableInstanceModel::isDoneIncubating(modelItemToIncubate))
+ return;
+
+ // We require the view to cancel any ongoing load
+ // requests before the tableInstanceModel is destructed.
+ Q_ASSERT(tableInstanceModel);
+
+ tableInstanceModel->incubatorStatusChanged(this, status);
+}
+
+#include "moc_qqmltableinstancemodel_p.cpp"
+
+QT_END_NAMESPACE
+
diff --git a/src/qml/types/qqmltableinstancemodel_p.h b/src/qml/types/qqmltableinstancemodel_p.h
new file mode 100644
index 0000000000..03761af326
--- /dev/null
+++ b/src/qml/types/qqmltableinstancemodel_p.h
@@ -0,0 +1,160 @@
+/****************************************************************************
+**
+** 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 QQMLTABLEINSTANCEMODEL_P_H
+#define QQMLTABLEINSTANCEMODEL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQml/private/qqmldelegatemodel_p.h>
+#include <QtQml/private/qqmldelegatemodel_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlTableInstanceModel;
+class QQmlAbstractDelegateComponent;
+
+class QQmlTableInstanceModelIncubationTask : public QQDMIncubationTask
+{
+public:
+ QQmlTableInstanceModelIncubationTask(
+ QQmlTableInstanceModel *tableInstanceModel
+ , QQmlDelegateModelItem* modelItemToIncubate
+ , IncubationMode mode)
+ : QQDMIncubationTask(nullptr, mode)
+ , modelItemToIncubate(modelItemToIncubate)
+ , tableInstanceModel(tableInstanceModel) {
+ clear();
+ }
+
+ void statusChanged(Status status) override;
+ void setInitialState(QObject *object) override;
+
+ QQmlDelegateModelItem *modelItemToIncubate = nullptr;
+ QQmlTableInstanceModel *tableInstanceModel = nullptr;
+};
+
+class Q_QML_PRIVATE_EXPORT QQmlTableInstanceModel : public QQmlInstanceModel
+{
+ Q_OBJECT
+
+public:
+
+ enum ReusableFlag {
+ NotReusable,
+ Reusable
+ };
+
+ QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent = nullptr);
+ ~QQmlTableInstanceModel() override;
+
+ int count() const override { return m_adaptorModel.count(); }
+ int rows() const { return m_adaptorModel.rowCount(); }
+ int columns() const { return m_adaptorModel.columnCount(); }
+
+ bool isValid() const override { return true; }
+
+ QVariant model() const;
+ void setModel(const QVariant &model);
+
+ QQmlComponent *delegate() const;
+ void setDelegate(QQmlComponent *);
+
+ const QAbstractItemModel *abstractItemModel() const override;
+
+ QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override;
+ ReleaseFlags release(QObject *object) override { return release(object, NotReusable); }
+ ReleaseFlags release(QObject *object, ReusableFlag reusable);
+ void cancel(int) override;
+
+ void insertIntoReusableItemsPool(QQmlDelegateModelItem *modelItem);
+ QQmlDelegateModelItem *takeFromReusableItemsPool(const QQmlComponent *delegate);
+ void drainReusableItemsPool(int maxPoolTime);
+ int poolSize() { return m_reusableItemsPool.size(); }
+ void reuseItem(QQmlDelegateModelItem *item, int newModelIndex);
+
+ QQmlIncubator::Status incubationStatus(int index) override;
+
+ QString stringValue(int, const QString &) override { Q_UNREACHABLE(); return QString(); }
+ void setWatchedRoles(const QList<QByteArray> &) override { Q_UNREACHABLE(); }
+ int indexOf(QObject *, QObject *) const override { Q_UNREACHABLE(); return 0; }
+
+Q_SIGNALS:
+ void itemPooled(int index, QObject *object);
+ void itemReused(int index, QObject *object);
+
+private:
+ QQmlComponent *resolveDelegate(int index);
+
+ QQmlAdaptorModel m_adaptorModel;
+ QQmlAbstractDelegateComponent *m_delegateChooser = nullptr;
+ QQmlComponent *m_delegate = nullptr;
+ QPointer<QQmlContext> m_qmlContext;
+ QQmlDelegateModelItemMetaType *m_metaType;
+
+ QHash<int, QQmlDelegateModelItem *> m_modelItems;
+ QList<QQmlDelegateModelItem *> m_reusableItemsPool;
+ QList<QQmlIncubator *> m_finishedIncubationTasks;
+
+ void incubateModelItem(QQmlDelegateModelItem *modelItem, QQmlIncubator::IncubationMode incubationMode);
+ void incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *dmIncubationTask, QQmlIncubator::Status status);
+ void deleteIncubationTaskLater(QQmlIncubator *incubationTask);
+ void deleteAllFinishedIncubationTasks();
+ QQmlDelegateModelItem *resolveModelItem(int index);
+
+ void dataChangedCallback(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles);
+
+ static bool isDoneIncubating(QQmlDelegateModelItem *modelItem);
+ static void deleteModelItemLater(QQmlDelegateModelItem *modelItem);
+
+ friend class QQmlTableInstanceModelIncubationTask;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLTABLEINSTANCEMODEL_P_H
diff --git a/src/qml/types/qqmltimer_p.h b/src/qml/types/qqmltimer_p.h
index d597869994..0160e97a2f 100644
--- a/src/qml/types/qqmltimer_p.h
+++ b/src/qml/types/qqmltimer_p.h
@@ -57,6 +57,8 @@
#include <private/qtqmlglobal_p.h>
+QT_REQUIRE_CONFIG(qml_animation);
+
QT_BEGIN_NAMESPACE
class QQmlTimerPrivate;
diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp
index 98f819337b..fe51f8ec47 100644
--- a/src/qml/types/qquickworkerscript.cpp
+++ b/src/qml/types/qquickworkerscript.cpp
@@ -37,9 +37,12 @@
**
****************************************************************************/
+#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>
@@ -136,49 +139,26 @@ public:
QQuickWorkerScriptEnginePrivate(QQmlEngine *eng);
- class WorkerEngine : public QV8Engine
- {
- public:
- WorkerEngine(QQuickWorkerScriptEnginePrivate *parent);
- ~WorkerEngine();
+ QQmlEngine *qmlengine;
- void init();
+ QMutex m_lock;
+ QWaitCondition m_wait;
+
+ struct WorkerScript : public QV8Engine {
+ WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent);
+ ~WorkerScript() override;
#if QT_CONFIG(qml_network)
QNetworkAccessManager *networkAccessManager() override;
#endif
- QQuickWorkerScriptEnginePrivate *p;
-
- QV4::ReturnedValue sendFunction(int id);
-
- QV4::PersistentValue onmessage;
- private:
- QV4::PersistentValue createsend;
+ QQuickWorkerScriptEnginePrivate *p = nullptr;
+ QUrl source;
+ QQuickWorkerScript *owner = nullptr;
#if QT_CONFIG(qml_network)
- QNetworkAccessManager *accessManager;
+ QScopedPointer<QNetworkAccessManager> accessManager;
#endif
- };
-
- WorkerEngine *workerEngine;
- static QQuickWorkerScriptEnginePrivate *get(QV8Engine *e) {
- return static_cast<WorkerEngine *>(e)->p;
- }
-
- QQmlEngine *qmlengine;
-
- QMutex m_lock;
- QWaitCondition m_wait;
-
- struct WorkerScript {
- WorkerScript();
- ~WorkerScript();
-
- int id;
- QUrl source;
- bool initialized;
- QQuickWorkerScript *owner;
- QV4::PersistentValue qmlContext;
+ int id = -1;
};
QHash<int, WorkerScript *> workers;
@@ -200,97 +180,8 @@ private:
void reportScriptException(WorkerScript *, const QQmlError &error);
};
-QQuickWorkerScriptEnginePrivate::WorkerEngine::WorkerEngine(QQuickWorkerScriptEnginePrivate *parent)
- : QV8Engine(nullptr, new QV4::ExecutionEngine), p(parent)
-#if QT_CONFIG(qml_network)
-, accessManager(nullptr)
-#endif
-{
- m_v4Engine->v8Engine = this;
-}
-
-QQuickWorkerScriptEnginePrivate::WorkerEngine::~WorkerEngine()
-{
-#if QT_CONFIG(qml_network)
- delete accessManager;
-#endif
- delete m_v4Engine;
-}
-
-void QQuickWorkerScriptEnginePrivate::WorkerEngine::init()
-{
- initQmlGlobalObject();
-#define CALL_ONMESSAGE_SCRIPT \
- "(function(object, message) { "\
- "var isfunction = false; "\
- "try { "\
- "isfunction = object.WorkerScript.onMessage instanceof Function; "\
- "} catch (e) {}" \
- "if (isfunction) "\
- "object.WorkerScript.onMessage(message); "\
- "})"
-
-#define SEND_MESSAGE_CREATE_SCRIPT \
- "(function(method, engine) { "\
- "return (function(id) { "\
- "return (function(message) { "\
- "if (arguments.length) method(engine, id, message); "\
- "}); "\
- "}); "\
- "})"
-
- QV4::Scope scope(m_v4Engine);
- QV4::ExecutionContext *globalContext = scope.engine->rootContext();
- onmessage.set(scope.engine, QV4::Script(globalContext, QV4::Compiler::GlobalCode, QString::fromUtf8(CALL_ONMESSAGE_SCRIPT)).run()); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro
- Q_ASSERT(!scope.engine->hasException);
- QV4::Script createsendscript(globalContext, QV4::Compiler::GlobalCode, QString::fromUtf8(SEND_MESSAGE_CREATE_SCRIPT)); // do not use QStringLiteral here, MSVC2012 cannot apply this cleanly to the macro
- QV4::ScopedFunctionObject createsendconstructor(scope, createsendscript.run());
- Q_ASSERT(!scope.engine->hasException);
- QV4::ScopedString name(scope, m_v4Engine->newString(QStringLiteral("sendMessage")));
- QV4::ScopedValue function(scope, QV4::FunctionObject::createBuiltinFunction(globalContext, name,
- QQuickWorkerScriptEnginePrivate::method_sendMessage));
- QV4::JSCallData jsCallData(scope, 1);
- jsCallData->args[0] = function;
- *jsCallData->thisObject = m_v4Engine->global();
- createsend.set(scope.engine, createsendconstructor->call(jsCallData));
-}
-
-// Requires handle and context scope
-QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::WorkerEngine::sendFunction(int id)
-{
- QV4::ExecutionEngine *v4 = createsend.engine();
- if (!v4)
- return QV4::Encode::undefined();
-
- QV4::Scope scope(v4);
- QV4::ScopedFunctionObject f(scope, createsend.value());
-
- QV4::ScopedValue v(scope);
- QV4::JSCallData jsCallData(scope, 1);
- jsCallData->args[0] = QV4::Primitive::fromInt32(id);
- *jsCallData->thisObject = m_v4Engine->global();
- v = f->call(jsCallData);
- if (scope.hasException())
- v = scope.engine->catchException();
- return v->asReturnedValue();
-}
-
-#if QT_CONFIG(qml_network)
-QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerEngine::networkAccessManager()
-{
- if (!accessManager) {
- if (p->qmlengine && p->qmlengine->networkAccessManagerFactory()) {
- accessManager = p->qmlengine->networkAccessManagerFactory()->create(p);
- } else {
- accessManager = new QNetworkAccessManager(p);
- }
- }
- return accessManager;
-}
-#endif
-
QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *engine)
-: workerEngine(nullptr), qmlengine(engine), m_nextId(0)
+: qmlengine(engine), m_nextId(0)
{
}
@@ -298,35 +189,18 @@ QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4
const QV4::Value *, const QV4::Value *argv, int argc)
{
QV4::Scope scope(b);
- WorkerEngine *engine = static_cast<WorkerEngine *>(scope.engine->v8Engine);
-
- int id = argc > 1 ? argv[1].toInt32() : 0;
+ WorkerScript *script = static_cast<WorkerScript *>(scope.engine->v8Engine);
- QV4::ScopedValue v(scope, argc > 2 ? argv[2] : QV4::Primitive::undefinedValue());
+ QV4::ScopedValue v(scope, argc > 0 ? argv[0] : QV4::Value::undefinedValue());
QByteArray data = QV4::Serialize::serialize(v, scope.engine);
- QMutexLocker locker(&engine->p->m_lock);
- WorkerScript *script = engine->p->workers.value(id);
+ QMutexLocker locker(&script->p->m_lock);
if (script && script->owner)
QCoreApplication::postEvent(script->owner, new WorkerDataEvent(0, data));
return QV4::Encode::undefined();
}
-QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::getWorker(WorkerScript *script)
-{
- if (!script->initialized) {
- script->initialized = true;
-
- QV4::ExecutionEngine *v4 = QV8Engine::getV4(workerEngine);
- QV4::Scope scope(v4);
- QV4::ScopedValue v(scope, workerEngine->sendFunction(script->id));
- script->qmlContext.set(v4, QV4::QmlContext::createWorkerContext(v4->rootContext(), script->source, v));
- }
-
- return script->qmlContext.value();
-}
-
bool QQuickWorkerScriptEnginePrivate::event(QEvent *event)
{
if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) {
@@ -341,6 +215,7 @@ bool QQuickWorkerScriptEnginePrivate::event(QEvent *event)
emit stopThread();
return true;
} else if (event->type() == (QEvent::Type)WorkerRemoveEvent::WorkerRemove) {
+ QMutexLocker locker(&m_lock);
WorkerRemoveEvent *workerEvent = static_cast<WorkerRemoveEvent *>(event);
QHash<int, WorkerScript *>::iterator itr = workers.find(workerEvent->workerId());
if (itr != workers.end()) {
@@ -359,19 +234,23 @@ void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &d
if (!script)
return;
- QV4::ExecutionEngine *v4 = QV8Engine::getV4(workerEngine);
+ QV4::ExecutionEngine *v4 = QV8Engine::getV4(script);
QV4::Scope scope(v4);
- QV4::ScopedFunctionObject f(scope, workerEngine->onmessage.value());
+ QV4::ScopedString v(scope);
+ QV4::ScopedObject worker(scope, v4->globalObject->get((v = v4->newString(QStringLiteral("WorkerScript")))));
+ QV4::ScopedFunctionObject onmessage(scope);
+ if (worker)
+ onmessage = worker->get((v = v4->newString(QStringLiteral("onMessage"))));
+
+ if (!onmessage)
+ return;
QV4::ScopedValue value(scope, QV4::Serialize::deserialize(data, v4));
- QV4::Scoped<QV4::QmlContext> qmlContext(scope, script->qmlContext.value());
- Q_ASSERT(!!qmlContext);
- QV4::JSCallData jsCallData(scope, 2);
+ QV4::JSCallData jsCallData(scope, 1);
*jsCallData->thisObject = v4->global();
- jsCallData->args[0] = qmlContext->d()->qml(); // ###
- jsCallData->args[1] = value;
- f->call(jsCallData);
+ jsCallData->args[0] = value;
+ onmessage->call(jsCallData);
if (scope.hasException()) {
QQmlError error = scope.engine->catchExceptionAsQmlError();
reportScriptException(script, error);
@@ -385,29 +264,37 @@ void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url)
QString fileName = QQmlFile::urlToLocalFileOrQrc(url);
- QV4::ExecutionEngine *v4 = QV8Engine::getV4(workerEngine);
- QV4::Scope scope(v4);
- QScopedPointer<QV4::Script> program;
-
WorkerScript *script = workers.value(id);
if (!script)
return;
+
+ QV4::ExecutionEngine *v4 = QV8Engine::getV4(script);
+
script->source = url;
- QV4::Scoped<QV4::QmlContext> qmlContext(scope, getWorker(script));
- Q_ASSERT(!!qmlContext);
+ if (fileName.endsWith(QLatin1String(".mjs"))) {
+ auto moduleUnit = v4->loadModule(url);
+ if (moduleUnit) {
+ if (moduleUnit->instantiate(v4))
+ moduleUnit->evaluate();
+ } else {
+ v4->throwError(QStringLiteral("Could not load module file"));
+ }
+ } else {
+ QString error;
+ QV4::Scope scope(v4);
+ QScopedPointer<QV4::Script> program;
+ program.reset(QV4::Script::createFromFileOrCache(v4, /*qmlContext*/nullptr, fileName, url, &error));
+ if (program.isNull()) {
+ if (!error.isEmpty())
+ qWarning().nospace() << error;
+ return;
+ }
- QString error;
- program.reset(QV4::Script::createFromFileOrCache(v4, qmlContext, fileName, url, &error));
- if (program.isNull()) {
- if (!error.isEmpty())
- qWarning().nospace() << error;
- return;
+ if (!v4->hasException)
+ program->run();
}
- if (!v4->hasException)
- program->run();
-
if (v4->hasException) {
QQmlError error = v4->catchExceptionAsQmlError();
reportScriptException(script, error);
@@ -417,9 +304,7 @@ void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url)
void QQuickWorkerScriptEnginePrivate::reportScriptException(WorkerScript *script,
const QQmlError &error)
{
- QQuickWorkerScriptEnginePrivate *p = QQuickWorkerScriptEnginePrivate::get(workerEngine);
-
- QMutexLocker locker(&p->m_lock);
+ QMutexLocker locker(&script->p->m_lock);
if (script->owner)
QCoreApplication::postEvent(script->owner, new WorkerErrorEvent(error));
}
@@ -508,21 +393,47 @@ QQuickWorkerScriptEngine::~QQuickWorkerScriptEngine()
d->deleteLater();
}
-QQuickWorkerScriptEnginePrivate::WorkerScript::WorkerScript()
-: id(-1), initialized(false), owner(nullptr)
+QQuickWorkerScriptEnginePrivate::WorkerScript::WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent)
+ : QV8Engine(new QV4::ExecutionEngine)
+ , p(parent)
+ , id(id)
{
+ m_v4Engine->v8Engine = this;
+
+ initQmlGlobalObject();
+
+ QV4::Scope scope(m_v4Engine);
+ QV4::ScopedObject api(scope, scope.engine->newObject());
+ QV4::ScopedString name(scope, m_v4Engine->newString(QStringLiteral("sendMessage")));
+ QV4::ScopedValue sendMessage(scope, QV4::FunctionObject::createBuiltinFunction(m_v4Engine, name, method_sendMessage, 1));
+ api->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("sendMessage"))), sendMessage);
+ m_v4Engine->globalObject->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("WorkerScript"))), api);
}
QQuickWorkerScriptEnginePrivate::WorkerScript::~WorkerScript()
{
+ delete m_v4Engine;
}
+#if QT_CONFIG(qml_network)
+QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerScript::networkAccessManager()
+{
+ if (!accessManager) {
+ if (p->qmlengine && p->qmlengine->networkAccessManagerFactory()) {
+ accessManager.reset(p->qmlengine->networkAccessManagerFactory()->create(p));
+ } else {
+ accessManager.reset(new QNetworkAccessManager(p));
+ }
+ }
+ return accessManager.data();
+}
+#endif
+
int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner)
{
typedef QQuickWorkerScriptEnginePrivate::WorkerScript WorkerScript;
- WorkerScript *script = new WorkerScript;
+ WorkerScript *script = new WorkerScript(d->m_nextId++, d);
- script->id = d->m_nextId++;
script->owner = owner;
d->m_lock.lock();
@@ -555,9 +466,6 @@ void QQuickWorkerScriptEngine::run()
{
d->m_lock.lock();
- d->workerEngine = new QQuickWorkerScriptEnginePrivate::WorkerEngine(d);
- d->workerEngine->init();
-
d->m_wait.wakeAll();
d->m_lock.unlock();
@@ -566,8 +474,6 @@ void QQuickWorkerScriptEngine::run()
qDeleteAll(d->workers);
d->workers.clear();
-
- delete d->workerEngine; d->workerEngine = nullptr;
}
@@ -589,22 +495,32 @@ void QQuickWorkerScriptEngine::run()
\snippet qml/workerscript/workerscript.qml 0
- The above worker script specifies a JavaScript file, "script.js", that handles
- the operations to be performed in the new thread. Here is \c script.js:
+ The above worker script specifies a JavaScript file, "script.mjs", that handles
+ the operations to be performed in the new thread. Here is \c script.mjs:
- \quotefile qml/workerscript/script.js
+ \quotefile qml/workerscript/script.mjs
When the user clicks anywhere within the rectangle, \c sendMessage() is
called, triggering the \tt WorkerScript.onMessage() handler in
- \tt script.js. This in turn sends a reply message that is then received
+ \tt script.mjs. This in turn sends a reply message that is then received
by the \tt onMessage() handler of \tt myWorker.
+ The example uses a script that is an ECMAScript module, because it has the ".mjs" extension.
+ It can use import statements to access functionality from other modules and it is run in JavaScript
+ strict mode.
+
+ If a worker script has the extension ".js" instead, then it is considered to contain plain JavaScript
+ statements and it is run in non-strict mode.
+
+ \note Each WorkerScript element will instantiate a separate JavaScript engine to ensure perfect
+ isolation and thread-safety. If the impact of that results in a memory consumption that is too
+ high for your environment, then consider sharing a WorkerScript element.
\section3 Restrictions
Since the \c WorkerScript.onMessage() function is run in a separate thread, the
JavaScript file is evaluated in a context separate from the main QML engine. This means
- that unlike an ordinary JavaScript file that is imported into QML, the \c script.js
+ that unlike an ordinary JavaScript file that is imported into QML, the \c script.mjs
in the above example cannot access the properties, methods or other attributes
of the QML item, nor can it access any context properties set on the QML object
through QQmlContext.
@@ -612,7 +528,8 @@ void QQuickWorkerScriptEngine::run()
Additionally, there are restrictions on the types of values that can be passed to and
from the worker script. See the sendMessage() documentation for details.
- Worker script can not use \l {qtqml-javascript-imports.html}{.import} syntax.
+ Worker scripts that are plain JavaScript sources can not use \l {qtqml-javascript-imports.html}{.import} syntax.
+ Scripts that are ECMAScript modules can freely use import and export statements.
\sa {Qt Quick Examples - Threading},
{Threaded ListModel Example}
@@ -632,6 +549,10 @@ QQuickWorkerScript::~QQuickWorkerScript()
This holds the url of the JavaScript file that implements the
\tt WorkerScript.onMessage() handler for threaded operations.
+
+ If the file name component of the url ends with ".mjs", then the script
+ is parsed as an ECMAScript module and run in strict mode. Otherwise it is considered to be
+ plain script.
*/
QUrl QQuickWorkerScript::source() const
{
@@ -679,7 +600,7 @@ void QQuickWorkerScript::sendMessage(QQmlV4Function *args)
}
QV4::Scope scope(args->v4engine());
- QV4::ScopedValue argument(scope, QV4::Primitive::undefinedValue());
+ QV4::ScopedValue argument(scope, QV4::Value::undefinedValue());
if (args->length() != 0)
argument = (*args)[0];
diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri
index 8bcbd6e544..e74c89b1f1 100644
--- a/src/qml/types/types.pri
+++ b/src/qml/types/types.pri
@@ -1,39 +1,54 @@
SOURCES += \
$$PWD/qqmlbind.cpp \
$$PWD/qqmlconnections.cpp \
- $$PWD/qqmllistmodel.cpp \
- $$PWD/qqmllistmodelworkeragent.cpp \
$$PWD/qqmlmodelsmodule.cpp \
$$PWD/qqmlmodelindexvaluetype.cpp \
$$PWD/qqmlobjectmodel.cpp \
$$PWD/qquickpackage.cpp \
- $$PWD/qquickworkerscript.cpp \
- $$PWD/qqmlinstantiator.cpp
+ $$PWD/qqmlinstantiator.cpp \
+ $$PWD/qqmltableinstancemodel.cpp
HEADERS += \
$$PWD/qqmlbind_p.h \
$$PWD/qqmlconnections_p.h \
- $$PWD/qqmllistmodel_p.h \
- $$PWD/qqmllistmodel_p_p.h \
- $$PWD/qqmllistmodelworkeragent_p.h \
$$PWD/qqmlmodelsmodule_p.h \
$$PWD/qqmlmodelindexvaluetype_p.h \
$$PWD/qqmlobjectmodel_p.h \
$$PWD/qquickpackage_p.h \
- $$PWD/qquickworkerscript_p.h \
$$PWD/qqmlinstantiator_p.h \
- $$PWD/qqmlinstantiator_p_p.h
+ $$PWD/qqmlinstantiator_p_p.h \
+ $$PWD/qqmltableinstancemodel_p.h
+
+qtConfig(qml-worker-script) {
+ SOURCES += \
+ $$PWD/qquickworkerscript.cpp
+ HEADERS += \
+ $$PWD/qquickworkerscript_p.h
+}
+
+qtConfig(qml-list-model) {
+ SOURCES += \
+ $$PWD/qqmllistmodel.cpp \
+ $$PWD/qqmllistmodelworkeragent.cpp
+
+ HEADERS += \
+ $$PWD/qqmllistmodel_p.h \
+ $$PWD/qqmllistmodel_p_p.h \
+ $$PWD/qqmllistmodelworkeragent_p.h
+}
qtConfig(qml-delegate-model) {
SOURCES += \
- $$PWD/qqmldelegatemodel.cpp
+ $$PWD/qqmldelegatemodel.cpp \
+ $$PWD/qqmldelegatecomponent.cpp
HEADERS += \
$$PWD/qqmldelegatemodel_p.h \
- $$PWD/qqmldelegatemodel_p_p.h
+ $$PWD/qqmldelegatemodel_p_p.h \
+ $$PWD/qqmldelegatecomponent_p.h
}
-qtConfig(animation) {
+qtConfig(qml-animation) {
SOURCES += \
$$PWD/qqmltimer.cpp
diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp
index b4bebb9d5d..2b50a0867c 100644
--- a/src/qml/util/qqmladaptormodel.cpp
+++ b/src/qml/util/qqmladaptormodel.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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.
@@ -66,7 +66,7 @@ static QV4::ReturnedValue get_index(const QV4::FunctionObject *f, const QV4::Val
QV4::Scope scope(f);
QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
if (!o)
- RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")));
+ RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")));
RETURN_RESULT(QV4::Encode(o->d()->item->index));
}
@@ -96,7 +96,7 @@ public:
QQmlDMCachedModelData(
QQmlDelegateModelItemMetaType *metaType,
VDMModelDelegateDataType *dataType,
- int index);
+ int index, int row, int column);
int metaCall(QMetaObject::Call call, int id, void **arguments);
@@ -200,7 +200,7 @@ public:
QV4::Scope scope(b);
QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
if (!o)
- RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")));
+ RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")));
const QQmlAdaptorModel *const model = static_cast<QQmlDMCachedModelData *>(o->d()->item)->type->model;
if (o->d()->item->index >= 0 && *model) {
@@ -228,8 +228,8 @@ public:
QV4::ScopedString name(scope, v4->newString(QString::fromUtf8(propertyName)));
QV4::ExecutionContext *global = v4->rootContext();
- QV4::ScopedFunctionObject g(scope, v4->memoryManager->allocObject<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::get_property));
- QV4::ScopedFunctionObject s(scope, v4->memoryManager->allocObject<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::set_property));
+ QV4::ScopedFunctionObject g(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::get_property));
+ QV4::ScopedFunctionObject s(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::set_property));
p->setGetter(g);
p->setSetter(s);
proto->insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable);
@@ -262,9 +262,8 @@ public:
bool hasModelData;
};
-QQmlDMCachedModelData::QQmlDMCachedModelData(
- QQmlDelegateModelItemMetaType *metaType, VDMModelDelegateDataType *dataType, int index)
- : QQmlDelegateModelItem(metaType, index)
+QQmlDMCachedModelData::QQmlDMCachedModelData(QQmlDelegateModelItemMetaType *metaType, VDMModelDelegateDataType *dataType, int index, int row, int column)
+ : QQmlDelegateModelItem(metaType, index, row, column)
, type(dataType)
{
if (index == -1)
@@ -326,13 +325,12 @@ void QQmlDMCachedModelData::setValue(const QString &role, const QVariant &value)
}
}
-bool QQmlDMCachedModelData::resolveIndex(const QQmlAdaptorModel &, int idx)
+bool QQmlDMCachedModelData::resolveIndex(const QQmlAdaptorModel &adaptorModel, int idx)
{
if (index == -1) {
Q_ASSERT(idx >= 0);
- index = idx;
cachedData.clear();
- emit modelIndexChanged();
+ setModelIndex(idx, adaptorModel.rowAt(idx), adaptorModel.columnAt(idx));
const QMetaObject *meta = metaObject();
const int propertyCount = type->propertyRoles.count();
for (int i = 0; i < propertyCount; ++i)
@@ -348,7 +346,7 @@ QV4::ReturnedValue QQmlDMCachedModelData::get_property(const QV4::FunctionObject
QV4::Scope scope(b);
QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
if (!o)
- return scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"));
+ return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
uint propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index;
@@ -370,7 +368,7 @@ QV4::ReturnedValue QQmlDMCachedModelData::set_property(const QV4::FunctionObject
QV4::Scope scope(b);
QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
if (!o)
- return scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object"));
+ return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
if (!argc)
return scope.engine->throwTypeError();
@@ -400,12 +398,13 @@ class QQmlDMAbstractItemModelData : public QQmlDMCachedModelData
{
Q_OBJECT
Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT)
+
public:
QQmlDMAbstractItemModelData(
QQmlDelegateModelItemMetaType *metaType,
VDMModelDelegateDataType *dataType,
- int index)
- : QQmlDMCachedModelData(metaType, dataType, index)
+ int index, int row, int column)
+ : QQmlDMCachedModelData(metaType, dataType, index, row, column)
{
}
@@ -413,7 +412,7 @@ public:
{
if (index >= 0 && *type->model) {
const QAbstractItemModel * const model = type->model->aim();
- return model->hasChildren(model->index(index, 0, type->model->rootIndex));
+ return model->hasChildren(model->index(row, column, type->model->rootIndex));
} else {
return false;
}
@@ -421,13 +420,13 @@ public:
QVariant value(int role) const override
{
- return type->model->aim()->index(index, 0, type->model->rootIndex).data(role);
+ return type->model->aim()->index(row, column, type->model->rootIndex).data(role);
}
void setValue(int role, const QVariant &value) override
{
type->model->aim()->setData(
- type->model->aim()->index(index, 0, type->model->rootIndex), value, role);
+ type->model->aim()->index(row, column, type->model->rootIndex), value, role);
}
QV4::ReturnedValue get() override
@@ -438,8 +437,8 @@ public:
}
QV4::Scope scope(v4);
QV4::ScopedObject proto(scope, type->prototype.value());
- QV4::ScopedObject o(scope, proto->engine()->memoryManager->allocObject<QQmlDelegateModelItemObject>(this));
- o->setPrototype(proto);
+ QV4::ScopedObject o(scope, proto->engine()->memoryManager->allocate<QQmlDelegateModelItemObject>(this));
+ o->setPrototypeOf(proto);
++scriptRef;
return o.asReturnedValue();
}
@@ -453,31 +452,18 @@ public:
{
}
- int count(const QQmlAdaptorModel &model) const override
+ int rowCount(const QQmlAdaptorModel &model) const override
{
return model.aim()->rowCount(model.rootIndex);
}
- void cleanup(QQmlAdaptorModel &model, QQmlDelegateModel *vdm) const override
- {
- QAbstractItemModel * const aim = model.aim();
- if (aim && vdm) {
- QObject::disconnect(aim, SIGNAL(rowsInserted(QModelIndex,int,int)),
- vdm, SLOT(_q_rowsInserted(QModelIndex,int,int)));
- QObject::disconnect(aim, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
- vdm, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
- QObject::disconnect(aim, SIGNAL(rowsRemoved(QModelIndex,int,int)),
- vdm, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
- QObject::disconnect(aim, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
- vdm, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>)));
- QObject::disconnect(aim, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
- vdm, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
- QObject::disconnect(aim, SIGNAL(modelReset()),
- vdm, SLOT(_q_modelReset()));
- QObject::disconnect(aim, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
- vdm, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
- }
+ int columnCount(const QQmlAdaptorModel &model) const override
+ {
+ return model.aim()->columnCount(model.rootIndex);
+ }
+ void cleanup(QQmlAdaptorModel &) const override
+ {
const_cast<VDMAbstractItemModelDataType *>(this)->release();
}
@@ -485,9 +471,9 @@ public:
{
QHash<QByteArray, int>::const_iterator it = roleNames.find(role.toUtf8());
if (it != roleNames.end()) {
- return model.aim()->index(index, 0, model.rootIndex).data(*it);
+ return model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex).data(*it);
} else if (role == QLatin1String("hasModelChildren")) {
- return QVariant(model.aim()->hasChildren(model.aim()->index(index, 0, model.rootIndex)));
+ return QVariant(model.aim()->hasChildren(model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex)));
} else {
return QVariant();
}
@@ -503,7 +489,7 @@ public:
QVariant modelIndex(const QQmlAdaptorModel &model, int index) const override
{
return model
- ? QVariant::fromValue(model.aim()->index(index, 0, model.rootIndex))
+ ? QVariant::fromValue(model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex))
: QVariant();
}
@@ -521,12 +507,12 @@ public:
QQmlDelegateModelItem *createItem(
QQmlAdaptorModel &model,
QQmlDelegateModelItemMetaType *metaType,
- int index) const override
+ int index, int row, int column) const override
{
VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this);
if (!metaObject)
dataType->initializeMetaType(model);
- return new QQmlDMAbstractItemModelData(metaType, dataType, index);
+ return new QQmlDMAbstractItemModelData(metaType, dataType, index, row, column);
}
void initializeMetaType(QQmlAdaptorModel &model)
@@ -567,8 +553,8 @@ class QQmlDMListAccessorData : public QQmlDelegateModelItem
Q_OBJECT
Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
public:
- QQmlDMListAccessorData(QQmlDelegateModelItemMetaType *metaType, int index, const QVariant &value)
- : QQmlDelegateModelItem(metaType, index)
+ QQmlDMListAccessorData(QQmlDelegateModelItemMetaType *metaType, int index, int row, int column, const QVariant &value)
+ : QQmlDelegateModelItem(metaType, index, row, column)
, cachedData(value)
{
}
@@ -580,10 +566,11 @@ public:
void setModelData(const QVariant &data)
{
- if (index == -1 && data != cachedData) {
- cachedData = data;
- emit modelDataChanged();
- }
+ if (data == cachedData)
+ return;
+
+ cachedData = data;
+ emit modelDataChanged();
}
static QV4::ReturnedValue get_modelData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
@@ -591,7 +578,7 @@ public:
QV4::ExecutionEngine *v4 = b->engine();
const QQmlDelegateModelItemObject *o = thisObject->as<QQmlDelegateModelItemObject>();
if (!o)
- return v4->throwTypeError(QStringLiteral("Not a valid VisualData object"));
+ return v4->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
return v4->fromVariant(static_cast<QQmlDMListAccessorData *>(o->d()->item)->cachedData);
}
@@ -601,7 +588,7 @@ public:
QV4::ExecutionEngine *v4 = b->engine();
const QQmlDelegateModelItemObject *o = thisObject->as<QQmlDelegateModelItemObject>();
if (!o)
- return v4->throwTypeError(QStringLiteral("Not a valid VisualData object"));
+ return v4->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
if (!argc)
return v4->throwTypeError();
@@ -613,9 +600,9 @@ public:
{
QQmlAdaptorModelEngineData *data = engineData(v4);
QV4::Scope scope(v4);
- QV4::ScopedObject o(scope, v4->memoryManager->allocObject<QQmlDelegateModelItemObject>(this));
+ QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(this));
QV4::ScopedObject p(scope, data->listItemProto.value());
- o->setPrototype(p);
+ o->setPrototypeOf(p);
++scriptRef;
return o.asReturnedValue();
}
@@ -653,11 +640,16 @@ class VDMListDelegateDataType : public QQmlAdaptorModel::Accessors
public:
inline VDMListDelegateDataType() {}
- int count(const QQmlAdaptorModel &model) const override
+ int rowCount(const QQmlAdaptorModel &model) const override
{
return model.list.count();
}
+ int columnCount(const QQmlAdaptorModel &) const override
+ {
+ return 1;
+ }
+
QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
{
return role == QLatin1String("modelData")
@@ -668,13 +660,27 @@ public:
QQmlDelegateModelItem *createItem(
QQmlAdaptorModel &model,
QQmlDelegateModelItemMetaType *metaType,
- int index) const override
+ int index, int row, int column) const override
{
return new QQmlDMListAccessorData(
metaType,
- index,
+ index, row, column,
index >= 0 && index < model.list.count() ? model.list.at(index) : QVariant());
}
+
+ bool notify(const QQmlAdaptorModel &model, const QList<QQmlDelegateModelItem *> &items, int index, int count, const QVector<int> &) const override
+ {
+ for (auto modelItem : items) {
+ const int modelItemIndex = modelItem->index;
+ if (modelItemIndex < index || modelItemIndex >= index + count)
+ continue;
+
+ auto listModelItem = static_cast<QQmlDMListAccessorData *>(modelItem);
+ QVariant updatedModelData = model.list.at(listModelItem->index);
+ listModelItem->setModelData(updatedModelData);
+ }
+ return true;
+ }
};
//-----------------------------------------------------------------
@@ -685,19 +691,31 @@ class VDMObjectDelegateDataType;
class QQmlDMObjectData : public QQmlDelegateModelItem, public QQmlAdaptorModelProxyInterface
{
Q_OBJECT
- Q_PROPERTY(QObject *modelData READ modelData CONSTANT)
+ Q_PROPERTY(QObject *modelData READ modelData NOTIFY modelDataChanged)
Q_INTERFACES(QQmlAdaptorModelProxyInterface)
public:
QQmlDMObjectData(
QQmlDelegateModelItemMetaType *metaType,
VDMObjectDelegateDataType *dataType,
- int index,
+ int index, int row, int column,
QObject *object);
+ void setModelData(QObject *modelData)
+ {
+ if (modelData == object)
+ return;
+
+ object = modelData;
+ emit modelDataChanged();
+ }
+
QObject *modelData() const { return object; }
QObject *proxiedObject() override { return object; }
QPointer<QObject> object;
+
+Q_SIGNALS:
+ void modelDataChanged();
};
class VDMObjectDelegateDataType : public QQmlRefCount, public QQmlAdaptorModel::Accessors
@@ -737,11 +755,16 @@ public:
free(metaObject);
}
- int count(const QQmlAdaptorModel &model) const override
+ int rowCount(const QQmlAdaptorModel &model) const override
{
return model.list.count();
}
+ int columnCount(const QQmlAdaptorModel &) const override
+ {
+ return 1;
+ }
+
QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
{
if (QObject *object = model.list.at(index).value<QObject *>())
@@ -752,13 +775,13 @@ public:
QQmlDelegateModelItem *createItem(
QQmlAdaptorModel &model,
QQmlDelegateModelItemMetaType *metaType,
- int index) const override
+ int index, int row, int column) const override
{
VDMObjectDelegateDataType *dataType = const_cast<VDMObjectDelegateDataType *>(this);
if (!metaObject)
dataType->initializeMetaType(model);
return index >= 0 && index < model.list.count()
- ? new QQmlDMObjectData(metaType, dataType, index, qvariant_cast<QObject *>(model.list.at(index)))
+ ? new QQmlDMObjectData(metaType, dataType, index, row, column, qvariant_cast<QObject *>(model.list.at(index)))
: nullptr;
}
@@ -769,10 +792,24 @@ public:
metaObject = builder.toMetaObject();
}
- void cleanup(QQmlAdaptorModel &, QQmlDelegateModel *) const override
+ void cleanup(QQmlAdaptorModel &) const override
{
const_cast<VDMObjectDelegateDataType *>(this)->release();
}
+
+ bool notify(const QQmlAdaptorModel &model, const QList<QQmlDelegateModelItem *> &items, int index, int count, const QVector<int> &) const override
+ {
+ for (auto modelItem : items) {
+ const int modelItemIndex = modelItem->index;
+ if (modelItemIndex < index || modelItemIndex >= index + count)
+ continue;
+
+ auto objectModelItem = static_cast<QQmlDMObjectData *>(modelItem);
+ QObject *updatedModelData = qvariant_cast<QObject *>(model.list.at(objectModelItem->index));
+ objectModelItem->setModelData(updatedModelData);
+ }
+ return true;
+ }
};
class QQmlDMObjectDataMetaObject : public QAbstractDynamicMetaObject
@@ -872,12 +909,11 @@ public:
VDMObjectDelegateDataType *m_type;
};
-QQmlDMObjectData::QQmlDMObjectData(
- QQmlDelegateModelItemMetaType *metaType,
+QQmlDMObjectData::QQmlDMObjectData(QQmlDelegateModelItemMetaType *metaType,
VDMObjectDelegateDataType *dataType,
- int index,
+ int index, int row, int column,
QObject *object)
- : QQmlDelegateModelItem(metaType, index)
+ : QQmlDelegateModelItem(metaType, index, row, column)
, object(object)
{
new QQmlDMObjectDataMetaObject(this, dataType);
@@ -904,50 +940,34 @@ QQmlAdaptorModel::~QQmlAdaptorModel()
accessors->cleanup(*this);
}
-void QQmlAdaptorModel::setModel(const QVariant &variant, QQmlDelegateModel *vdm, QQmlEngine *engine)
+void QQmlAdaptorModel::setModel(const QVariant &variant, QObject *parent, QQmlEngine *engine)
{
- accessors->cleanup(*this, vdm);
+ accessors->cleanup(*this);
list.setList(variant, engine);
if (QObject *object = qvariant_cast<QObject *>(list.list())) {
- setObject(object);
- if (QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(object)) {
+ setObject(object, parent);
+ if (qobject_cast<QAbstractItemModel *>(object))
accessors = new VDMAbstractItemModelDataType(this);
-
- qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
- vdm, QQmlDelegateModel, SLOT(_q_rowsInserted(QModelIndex,int,int)));
- qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
- vdm, QQmlDelegateModel, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
- qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
- vdm, QQmlDelegateModel, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
- qmlobject_connect(model, QAbstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
- vdm, QQmlDelegateModel, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>)));
- qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
- vdm, QQmlDelegateModel, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
- qmlobject_connect(model, QAbstractItemModel, SIGNAL(modelReset()),
- vdm, QQmlDelegateModel, SLOT(_q_modelReset()));
- qmlobject_connect(model, QAbstractItemModel, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
- vdm, QQmlDelegateModel, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
- } else {
+ else
accessors = new VDMObjectDelegateDataType;
- }
} else if (list.type() == QQmlListAccessor::ListProperty) {
- setObject(static_cast<const QQmlListReference *>(variant.constData())->object());
+ setObject(static_cast<const QQmlListReference *>(variant.constData())->object(), parent);
accessors = new VDMObjectDelegateDataType;
} else if (list.type() != QQmlListAccessor::Invalid
&& list.type() != QQmlListAccessor::Instance) { // Null QObject
- setObject(nullptr);
+ setObject(nullptr, parent);
accessors = &qt_vdm_list_accessors;
} else {
- setObject(nullptr);
+ setObject(nullptr, parent);
accessors = &qt_vdm_null_accessors;
}
}
-void QQmlAdaptorModel::invalidateModel(QQmlDelegateModel *vdm)
+void QQmlAdaptorModel::invalidateModel()
{
- accessors->cleanup(*this, vdm);
+ accessors->cleanup(*this);
accessors = &qt_vdm_null_accessors;
// Don't clear the model object as we still need the guard to clear the list variant if the
// object is destroyed.
@@ -958,6 +978,38 @@ bool QQmlAdaptorModel::isValid() const
return accessors != &qt_vdm_null_accessors;
}
+int QQmlAdaptorModel::count() const
+{
+ return rowCount() * columnCount();
+}
+
+int QQmlAdaptorModel::rowCount() const
+{
+ return qMax(0, accessors->rowCount(*this));
+}
+
+int QQmlAdaptorModel::columnCount() const
+{
+ return qMax(isValid() ? 1 : 0, accessors->columnCount(*this));
+}
+
+int QQmlAdaptorModel::rowAt(int index) const
+{
+ int count = rowCount();
+ return count <= 0 ? -1 : index % count;
+}
+
+int QQmlAdaptorModel::columnAt(int index) const
+{
+ int count = rowCount();
+ return count <= 0 ? -1 : index / count;
+}
+
+int QQmlAdaptorModel::indexAt(int row, int column) const
+{
+ return column * rowCount() + row;
+}
+
void QQmlAdaptorModel::objectDestroyed(QObject *)
{
setModel(QVariant(), nullptr, nullptr);
diff --git a/src/qml/util/qqmladaptormodel_p.h b/src/qml/util/qqmladaptormodel_p.h
index b152a886a5..b5e0881078 100644
--- a/src/qml/util/qqmladaptormodel_p.h
+++ b/src/qml/util/qqmladaptormodel_p.h
@@ -54,8 +54,9 @@
#include <QtCore/qabstractitemmodel.h>
#include "private/qqmllistaccessor_p.h"
-
+#include <private/qqmlglobal_p.h>
#include <private/qqmlguard_p.h>
+#include <private/qqmlnullablevalue_p.h>
QT_REQUIRE_CONFIG(qml_delegate_model);
@@ -67,7 +68,7 @@ class QQmlDelegateModel;
class QQmlDelegateModelItem;
class QQmlDelegateModelItemMetaType;
-class QQmlAdaptorModel : public QQmlGuard<QObject>
+class Q_QML_PRIVATE_EXPORT QQmlAdaptorModel : public QQmlStrongJSQObjectReference<QObject>
{
public:
class Accessors
@@ -75,8 +76,9 @@ public:
public:
inline Accessors() {}
virtual ~Accessors();
- virtual int count(const QQmlAdaptorModel &) const { return 0; }
- virtual void cleanup(QQmlAdaptorModel &, QQmlDelegateModel * = nullptr) const {}
+ virtual int rowCount(const QQmlAdaptorModel &) const { return 0; }
+ virtual int columnCount(const QQmlAdaptorModel &) const { return 0; }
+ virtual void cleanup(QQmlAdaptorModel &) const {}
virtual QVariant value(const QQmlAdaptorModel &, int, const QString &) const {
return QVariant(); }
@@ -84,7 +86,7 @@ public:
virtual QQmlDelegateModelItem *createItem(
QQmlAdaptorModel &,
QQmlDelegateModelItemMetaType *,
- int) const { return nullptr; }
+ int, int, int) const { return nullptr; }
virtual bool notify(
const QQmlAdaptorModel &,
@@ -112,19 +114,25 @@ public:
~QQmlAdaptorModel();
inline QVariant model() const { return list.list(); }
- void setModel(const QVariant &variant, QQmlDelegateModel *vdm, QQmlEngine *engine);
- void invalidateModel(QQmlDelegateModel *vdm);
+ void setModel(const QVariant &variant, QObject *parent, QQmlEngine *engine);
+ void invalidateModel();
bool isValid() const;
-
+ int count() const;
+ int rowCount() const;
+ int columnCount() const;
+ int rowAt(int index) const;
+ int columnAt(int index) const;
+ int indexAt(int row, int column) const;
+
+ inline bool adaptsAim() const { return qobject_cast<QAbstractItemModel *>(object()); }
inline QAbstractItemModel *aim() { return static_cast<QAbstractItemModel *>(object()); }
inline const QAbstractItemModel *aim() const { return static_cast<const QAbstractItemModel *>(object()); }
- inline int count() const { return qMax(0, accessors->count(*this)); }
inline QVariant value(int index, const QString &role) const {
return accessors->value(*this, index, role); }
inline QQmlDelegateModelItem *createItem(QQmlDelegateModelItemMetaType *metaType, int index) {
- return accessors->createItem(*this, metaType, index); }
+ return accessors->createItem(*this, metaType, index, rowAt(index), columnAt(index)); }
inline bool hasProxyObject() const {
return list.type() == QQmlListAccessor::Instance || list.type() == QQmlListAccessor::ListProperty; }
diff --git a/src/qml/util/qqmllistaccessor.cpp b/src/qml/util/qqmllistaccessor.cpp
index ad55519ad3..46a11e2bc2 100644
--- a/src/qml/util/qqmllistaccessor.cpp
+++ b/src/qml/util/qqmllistaccessor.cpp
@@ -81,7 +81,26 @@ void QQmlListAccessor::setList(const QVariant &v, QQmlEngine *engine)
} else if (d.userType() == QMetaType::QVariantList) {
m_type = VariantList;
} else if (d.canConvert(QVariant::Int)) {
- m_type = Integer;
+ // Here we have to check for an upper limit, because down the line code might (well, will)
+ // allocate memory depending on the number of elements. The upper limit cannot be INT_MAX:
+ // QVector<QPointer<QQuickItem>> something;
+ // something.resize(count());
+ // (See e.g. QQuickRepeater::regenerate())
+ // This will allocate data along the lines of:
+ // sizeof(QPointer<QQuickItem>) * count() + QVector::headerSize
+ // So, doing an approximate round-down-to-nice-number, we get:
+ const int upperLimit = 100 * 1000 * 1000;
+
+ int i = v.toInt();
+ if (i < 0) {
+ qWarning("Model size of %d is less than 0", i);
+ m_type = Invalid;
+ } else if (i > upperLimit) {
+ qWarning("Model size of %d is bigger than the upper limit %d", i, upperLimit);
+ m_type = Invalid;
+ } else {
+ m_type = Integer;
+ }
} else if ((!enginePrivate && QQmlMetaType::isQObject(d.userType())) ||
(enginePrivate && enginePrivate->isQObject(d.userType()))) {
QObject *data = enginePrivate?enginePrivate->toQObject(d):QQmlMetaType::toQObject(d);
diff --git a/src/qml/util/qqmllistcompositor.cpp b/src/qml/util/qqmllistcompositor.cpp
index 8b0b8f48f0..921e86f355 100644
--- a/src/qml/util/qqmllistcompositor.cpp
+++ b/src/qml/util/qqmllistcompositor.cpp
@@ -99,7 +99,7 @@ QT_BEGIN_NAMESPACE
ranges is often quite small, which helps as well. If there is a need for faster random access
then a skip list like index may be an appropriate addition.
- \sa VisualDataModel
+ \sa DelegateModel
*/
#ifdef QT_QML_VERIFY_MINIMAL
diff --git a/src/qml/util/qqmlpropertymap.cpp b/src/qml/util/qqmlpropertymap.cpp
index 578c05086f..3f78ca6b69 100644
--- a/src/qml/util/qqmlpropertymap.cpp
+++ b/src/qml/util/qqmlpropertymap.cpp
@@ -122,7 +122,7 @@ QVariant QQmlPropertyMapMetaObject::propertyWriteValue(int index, const QVariant
void QQmlPropertyMapMetaObject::propertyWritten(int index)
{
- priv->emitChanged(priv->propertyName(index), operator[](index));
+ priv->emitChanged(priv->propertyName(index), value(index));
}
void QQmlPropertyMapMetaObject::propertyCreated(int, QMetaPropertyBuilder &b)
@@ -311,7 +311,7 @@ QVariant &QQmlPropertyMap::operator[](const QString &key)
if (!d->keys.contains(key))
insert(key, QVariant());//force creation -- needed below
- return (*(d->mo))[utf8key];
+ return d->mo->valueRef(utf8key);
}
/*!
diff --git a/src/qmldebug/qmldebug.pro b/src/qmldebug/qmldebug.pro
index 2c1b71797d..0807482d23 100644
--- a/src/qmldebug/qmldebug.pro
+++ b/src/qmldebug/qmldebug.pro
@@ -9,6 +9,7 @@ SOURCES += \
qqmldebugconnection.cpp \
qqmldebugmessageclient.cpp \
qqmlenginecontrolclient.cpp \
+ qqmlpreviewclient.cpp \
qqmlprofilerclient.cpp \
qqmlprofilerevent.cpp \
qqmlprofilereventlocation.cpp \
@@ -22,6 +23,8 @@ HEADERS += \
qqmldebugmessageclient_p.h \
qqmlenginecontrolclient_p.h \
qqmlenginecontrolclient_p_p.h \
+ qqmlpreviewclient_p.h \
+ qqmlpreviewclient_p_p.h \
qqmlprofilerclient_p.h \
qqmlprofilerclient_p_p.h \
qqmlprofilerevent_p.h \
diff --git a/src/qmldebug/qqmldebugclient.cpp b/src/qmldebug/qqmldebugclient.cpp
index d412b7b267..03123cc6e0 100644
--- a/src/qmldebug/qqmldebugclient.cpp
+++ b/src/qmldebug/qqmldebugclient.cpp
@@ -117,11 +117,6 @@ QQmlDebugConnection *QQmlDebugClient::connection() const
return d->connection;
}
-void QQmlDebugClient::stateChanged(QQmlDebugClient::State state)
-{
- Q_UNUSED(state);
-}
-
void QQmlDebugClient::messageReceived(const QByteArray &message)
{
Q_UNUSED(message);
diff --git a/src/qmldebug/qqmldebugclient_p.h b/src/qmldebug/qqmldebugclient_p.h
index 723de5ee43..469b65d4a9 100644
--- a/src/qmldebug/qqmldebugclient_p.h
+++ b/src/qmldebug/qqmldebugclient_p.h
@@ -76,13 +76,14 @@ public:
QQmlDebugConnection *connection() const;
+signals:
+ void stateChanged(State state);
+
protected:
QQmlDebugClient(QQmlDebugClientPrivate &dd);
private:
friend class QQmlDebugConnection;
-
- virtual void stateChanged(State state);
virtual void messageReceived(const QByteArray &message);
};
diff --git a/src/qmldebug/qqmldebugconnection.cpp b/src/qmldebug/qqmldebugconnection.cpp
index 67bad8d812..4e087ee6db 100644
--- a/src/qmldebug/qqmldebugconnection.cpp
+++ b/src/qmldebug/qqmldebugconnection.cpp
@@ -259,7 +259,7 @@ QQmlDebugConnection::~QQmlDebugConnection()
Q_D(QQmlDebugConnection);
QHash<QString, QQmlDebugClient*>::iterator iter = d->plugins.begin();
for (; iter != d->plugins.end(); ++iter)
- iter.value()->stateChanged(QQmlDebugClient::NotConnected);
+ emit iter.value()->stateChanged(QQmlDebugClient::NotConnected);
}
int QQmlDebugConnection::currentDataStreamVersion() const
@@ -295,7 +295,7 @@ void QQmlDebugConnection::close()
QHash<QString, QQmlDebugClient*>::iterator iter = d->plugins.begin();
for (; iter != d->plugins.end(); ++iter)
- iter.value()->stateChanged(QQmlDebugClient::NotConnected);
+ emit iter.value()->stateChanged(QQmlDebugClient::NotConnected);
}
if (d->device) {
diff --git a/src/qmldebug/qqmldebugmessageclient.cpp b/src/qmldebug/qqmldebugmessageclient.cpp
index a03c1f8af2..0892404194 100644
--- a/src/qmldebug/qqmldebugmessageclient.cpp
+++ b/src/qmldebug/qqmldebugmessageclient.cpp
@@ -58,11 +58,6 @@ QQmlDebugMessageClient::QQmlDebugMessageClient(QQmlDebugConnection *client)
{
}
-void QQmlDebugMessageClient::stateChanged(State state)
-{
- emit newState(state);
-}
-
void QQmlDebugMessageClient::messageReceived(const QByteArray &data)
{
QDataStream ds(data);
diff --git a/src/qmldebug/qqmldebugmessageclient_p.h b/src/qmldebug/qqmldebugmessageclient_p.h
index 75c70044e4..a2a7f28f81 100644
--- a/src/qmldebug/qqmldebugmessageclient_p.h
+++ b/src/qmldebug/qqmldebugmessageclient_p.h
@@ -71,15 +71,10 @@ class QQmlDebugMessageClient : public QQmlDebugClient
public:
explicit QQmlDebugMessageClient(QQmlDebugConnection *client);
- virtual void stateChanged(State state) override;
virtual void messageReceived(const QByteArray &) override;
signals:
- void newState(QQmlDebugClient::State);
void message(QtMsgType, const QString &, const QQmlDebugContextInfo &);
-
-private:
- Q_DISABLE_COPY(QQmlDebugMessageClient)
};
QT_END_NAMESPACE
diff --git a/src/qmldebug/qqmlenginecontrolclient.cpp b/src/qmldebug/qqmlenginecontrolclient.cpp
index 45d672d4bc..cdc724ef2f 100644
--- a/src/qmldebug/qqmlenginecontrolclient.cpp
+++ b/src/qmldebug/qqmlenginecontrolclient.cpp
@@ -98,8 +98,8 @@ void QQmlEngineControlClient::messageReceived(const QByteArray &data)
{
Q_D(QQmlEngineControlClient);
QPacket stream(d->connection->currentDataStreamVersion(), data);
- int message;
- int id;
+ qint32 message;
+ qint32 id;
QString name;
stream >> message >> id;
@@ -150,7 +150,7 @@ void QQmlEngineControlClientPrivate::sendCommand(
{
Q_Q(QQmlEngineControlClient);
QPacket stream(connection->currentDataStreamVersion());
- stream << int(command) << engineId;
+ stream << static_cast<qint32>(command) << engineId;
q->sendMessage(stream.data());
}
diff --git a/src/qmldebug/qqmlpreviewclient.cpp b/src/qmldebug/qqmlpreviewclient.cpp
new file mode 100644
index 0000000000..60937b9cfd
--- /dev/null
+++ b/src/qmldebug/qqmlpreviewclient.cpp
@@ -0,0 +1,139 @@
+/****************************************************************************
+**
+** 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 "qqmlpreviewclient_p_p.h"
+#include <private/qpacket_p.h>
+
+#include <QtCore/qurl.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qdir.h>
+#include <QtQml/qqmlfile.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlPreviewClient::QQmlPreviewClient(QQmlDebugConnection *connection)
+ : QQmlDebugClient(*(new QQmlPreviewClientPrivate(connection)))
+{
+}
+
+void QQmlPreviewClient::messageReceived(const QByteArray &message)
+{
+ QPacket packet(connection()->currentDataStreamVersion(), message);
+
+ qint8 command;
+ packet >> command;
+
+ switch (command) {
+ case Error: {
+ QString seviceError;
+ packet >> seviceError;
+ emit error(seviceError);
+ break;
+ }
+ case Request: {
+ QString fileName;
+ packet >> fileName;
+ emit request(fileName);
+ break;
+ }
+ case Fps: {
+ FpsInfo info;
+ packet >> info.numSyncs >> info.minSync >> info.maxSync >> info.totalSync
+ >> info.numRenders >> info.minRender >> info.maxRender >> info.totalRender;
+ emit fps(info);
+ break;
+ }
+ default:
+ emit error(QString::fromLatin1("Unknown command received: %1").arg(command));
+ break;
+ }
+}
+
+void QQmlPreviewClient::sendDirectory(const QString &path, const QStringList &entries)
+{
+ QPacket packet(connection()->currentDataStreamVersion());
+ packet << static_cast<qint8>(Directory) << path << entries;
+ sendMessage(packet.data());
+}
+
+void QQmlPreviewClient::sendFile(const QString &path, const QByteArray &contents)
+{
+ QPacket packet(connection()->currentDataStreamVersion());
+ packet << static_cast<qint8>(File) << path << contents;
+ sendMessage(packet.data());
+}
+
+void QQmlPreviewClient::sendError(const QString &path)
+{
+ QPacket packet(connection()->currentDataStreamVersion());
+ packet << static_cast<qint8>(Error) << path;
+ sendMessage(packet.data());
+}
+
+void QQmlPreviewClient::triggerLoad(const QUrl &url)
+{
+ QPacket packet(connection()->currentDataStreamVersion());
+ packet << static_cast<qint8>(Load) << url;
+ sendMessage(packet.data());
+}
+
+void QQmlPreviewClient::triggerRerun()
+{
+ QPacket packet(connection()->currentDataStreamVersion());
+ packet << static_cast<qint8>(Rerun);
+ sendMessage(packet.data());
+}
+
+void QQmlPreviewClient::triggerZoom(float factor)
+{
+ QPacket packet(connection()->currentDataStreamVersion());
+ packet << static_cast<qint8>(Zoom) << factor;
+ sendMessage(packet.data());
+}
+
+void QQmlPreviewClient::triggerLanguage(const QUrl &url, const QString &locale)
+{
+ QPacket packet(connection()->currentDataStreamVersion());
+ packet << static_cast<qint8>(Language) << url << locale;
+ sendMessage(packet.data());
+}
+
+QT_END_NAMESPACE
diff --git a/src/qmldebug/qqmlpreviewclient_p.h b/src/qmldebug/qqmlpreviewclient_p.h
new file mode 100644
index 0000000000..65661613e9
--- /dev/null
+++ b/src/qmldebug/qqmlpreviewclient_p.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** 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 QQMLPREVIEWCLIENT_P_H
+#define QQMLPREVIEWCLIENT_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/qqmldebugclient_p.h>
+#include <private/qqmldebugconnection_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlPreviewClientPrivate;
+class QQmlPreviewClient : public QQmlDebugClient
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QQmlPreviewClient)
+public:
+ enum Command {
+ File,
+ Load,
+ Request,
+ Error,
+ Rerun,
+ Directory,
+ ClearCache,
+ Zoom,
+ Fps,
+ Language
+ };
+
+ struct FpsInfo {
+ quint16 numSyncs = 0;
+ quint16 minSync = std::numeric_limits<quint16>::max();
+ quint16 maxSync = 0;
+ quint16 totalSync = 0;
+
+ quint16 numRenders = 0;
+ quint16 minRender = std::numeric_limits<quint16>::max();
+ quint16 maxRender = 0;
+ quint16 totalRender = 0;
+ };
+
+ QQmlPreviewClient(QQmlDebugConnection *parent);
+ void messageReceived(const QByteArray &message) override;
+
+ void sendDirectory(const QString &path, const QStringList &entries);
+ void sendFile(const QString &path, const QByteArray &contents);
+ void sendError(const QString &path);
+
+ void triggerLoad(const QUrl &url);
+ void triggerRerun();
+ void triggerZoom(float factor);
+ void triggerLanguage(const QUrl &url, const QString &locale);
+
+signals:
+ void request(const QString &path);
+ void error(const QString &message);
+ void fps(const FpsInfo &info);
+};
+
+QT_END_NAMESPACE
+
+#endif // QQMLPREVIEWCLIENT_P_H
diff --git a/src/qml/qml/ftw/qdeferredcleanup_p.h b/src/qmldebug/qqmlpreviewclient_p_p.h
index 6b59f04a77..8fb2b4b18c 100644
--- a/src/qml/qml/ftw/qdeferredcleanup_p.h
+++ b/src/qmldebug/qqmlpreviewclient_p_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** 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.
@@ -37,8 +37,11 @@
**
****************************************************************************/
-#ifndef QDEFERREDCLEANUP_P_H
-#define QDEFERREDCLEANUP_P_H
+#ifndef QQMLPREVIEWCLIENT_P_P_H
+#define QQMLPREVIEWCLIENT_P_P_H
+
+#include "qqmlpreviewclient_p.h"
+#include "qqmldebugclient_p_p.h"
//
// W A R N I N G
@@ -51,24 +54,17 @@
// We mean it.
//
-#include <QtCore/qglobal.h>
-
-#include <functional>
-
QT_BEGIN_NAMESPACE
-struct QDeferredCleanup
+class QQmlPreviewClientPrivate : public QQmlDebugClientPrivate
{
- std::function<void()> callback;
- template <typename Callback>
- QDeferredCleanup(Callback &&cb)
- : callback(cb)
+ Q_DECLARE_PUBLIC(QQmlPreviewClient)
+public:
+ QQmlPreviewClientPrivate(QQmlDebugConnection *connection)
+ : QQmlDebugClientPrivate(QLatin1String("QmlPreview"), connection)
{}
- ~QDeferredCleanup() { callback(); }
- QDeferredCleanup(const QDeferredCleanup &) = delete;
- QDeferredCleanup &operator=(const QDeferredCleanup &) = delete;
};
QT_END_NAMESPACE
-#endif // QDEFERREDCLEANUP_P_H
+#endif // QQMLPREVIEWCLIENT_P_P_H
diff --git a/src/qmldebug/qqmlprofilerclient.cpp b/src/qmldebug/qqmlprofilerclient.cpp
index 661b43f164..73db2ad94d 100644
--- a/src/qmldebug/qqmlprofilerclient.cpp
+++ b/src/qmldebug/qqmlprofilerclient.cpp
@@ -42,6 +42,10 @@
QT_BEGIN_NAMESPACE
+QQmlProfilerClientPrivate::~QQmlProfilerClientPrivate()
+{
+}
+
int QQmlProfilerClientPrivate::resolveType(const QQmlProfilerTypedEvent &event)
{
int typeIndex = -1;
@@ -165,6 +169,7 @@ QQmlProfilerClient::QQmlProfilerClient(QQmlDebugConnection *connection,
{
Q_D(QQmlProfilerClient);
setRequestedFeatures(features);
+ connect(this, &QQmlDebugClient::stateChanged, this, &QQmlProfilerClient::onStateChanged);
connect(d->engineControl.data(), &QQmlEngineControlClient::engineAboutToBeAdded,
this, &QQmlProfilerClient::sendRecordingStatus);
connect(d->engineControl.data(), &QQmlEngineControlClient::engineAboutToBeRemoved,
@@ -314,7 +319,7 @@ bool QQmlProfilerClientPrivate::updateFeatures(ProfileFeature feature)
return true;
}
-void QQmlProfilerClient::stateChanged(State status)
+void QQmlProfilerClient::onStateChanged(State status)
{
if (status == Enabled) {
sendRecordingStatus(-1);
diff --git a/src/qmldebug/qqmlprofilerclient_p.h b/src/qmldebug/qqmlprofilerclient_p.h
index 7b8e286f5b..89c117a8b5 100644
--- a/src/qmldebug/qqmlprofilerclient_p.h
+++ b/src/qmldebug/qqmlprofilerclient_p.h
@@ -76,7 +76,6 @@ public:
void setRecording(bool);
quint64 recordedFeatures() const;
virtual void messageReceived(const QByteArray &) override;
- virtual void stateChanged(State status) override;
void clearEvents();
void clearAll();
@@ -87,6 +86,7 @@ public:
protected:
QQmlProfilerClient(QQmlProfilerClientPrivate &dd);
+ void onStateChanged(State status);
signals:
void complete(qint64 maximumTime);
diff --git a/src/qmldebug/qqmlprofilerclient_p_p.h b/src/qmldebug/qqmlprofilerclient_p_p.h
index 994c08cafc..df73209858 100644
--- a/src/qmldebug/qqmlprofilerclient_p_p.h
+++ b/src/qmldebug/qqmlprofilerclient_p_p.h
@@ -79,7 +79,7 @@ public:
{
}
- virtual ~QQmlProfilerClientPrivate() override {}
+ virtual ~QQmlProfilerClientPrivate() override;
void sendRecordingStatus(int engineId);
bool updateFeatures(ProfileFeature feature);
@@ -112,4 +112,3 @@ public:
QT_END_NAMESPACE
#endif // QQMLPROFILERCLIENT_P_P_H
-
diff --git a/src/qmldevtools/qmldevtools.pro b/src/qmldevtools/qmldevtools.pro
index 23b7cf651e..05aa6f66f5 100644
--- a/src/qmldevtools/qmldevtools.pro
+++ b/src/qmldevtools/qmldevtools.pro
@@ -1,7 +1,7 @@
option(host_build)
TARGET = QtQmlDevTools
QT = core-private
-CONFIG += minimal_syncqt internal_module qmldevtools_build
+CONFIG += minimal_syncqt internal_module qmldevtools_build generated_privates
MODULE_INCNAME = QtQml
INCLUDEPATH += $$OUT_PWD/../qml
diff --git a/src/qmltest/doc/src/qtquicktest-index.qdoc b/src/qmltest/doc/src/qtquicktest-index.qdoc
index 1f84db405c..700bddc8d5 100644
--- a/src/qmltest/doc/src/qtquicktest-index.qdoc
+++ b/src/qmltest/doc/src/qtquicktest-index.qdoc
@@ -146,12 +146,20 @@
\li Name
\li Purpose
\row
+ \row
+ \li void applicationAvailable()
+ \li Called right after the QApplication object was instantiated.
+ Use this function to setup everything that is not related
+ to QML directly.
\li void qmlEngineAvailable(QQmlEngine*)
\li Called when the QML engine is available.
Any \l {QQmlEngine::addImportPath}{import paths},
\l {QQmlEngine::addPluginPath}{plugin paths},
and \l {QQmlFileSelector::setExtraSelectors}{extra file selectors}
will have been set on the engine by this point.
+ \li void cleanupTestCase()
+ \li Called right after the test execution has finished.
+ Use this function to clean up before everything will start to be destructed.
\endtable
Each function will be called once for each \c tst_*.qml file, so any
diff --git a/src/qmltest/qtestoptions_p.h b/src/qmltest/qtestoptions_p.h
index 68fe09f793..7be5c88590 100644
--- a/src/qmltest/qtestoptions_p.h
+++ b/src/qmltest/qtestoptions_p.h
@@ -51,7 +51,11 @@
// We mean it.
//
-#include <QtTest/qttestglobal.h>
+#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
+# include <QtTest/qtest_global.h>
+#else
+# include <QtTest/qttestglobal.h>
+#endif
#include <QtCore/qstring.h>
#include <QtCore/qstringlist.h>
diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp
index 8b9ff4c5cd..f666bc2284 100644
--- a/src/qmltest/quicktest.cpp
+++ b/src/qmltest/quicktest.cpp
@@ -215,8 +215,8 @@ public:
m_errors += component.errors();
if (component.isReady()) {
- CompilationUnit *rootCompilationUnit = QQmlComponentPrivate::get(&component)->compilationUnit;
- TestCaseEnumerationResult result = enumerateTestCases(rootCompilationUnit);
+ QQmlRefPointer<CompilationUnit> rootCompilationUnit = QQmlComponentPrivate::get(&component)->compilationUnit;
+ TestCaseEnumerationResult result = enumerateTestCases(rootCompilationUnit.data());
m_testCases = result.testCases + result.finalizedPartialTestCases();
m_errors += result.errors;
}
@@ -258,8 +258,8 @@ private:
TestCaseEnumerationResult enumerateTestCases(CompilationUnit *compilationUnit, const Object *object = nullptr)
{
QQmlType testCaseType;
- for (quint32 i = 0; i < compilationUnit->data->nImports; ++i) {
- const Import *import = compilationUnit->data->importAt(i);
+ for (quint32 i = 0, count = compilationUnit->importCount(); i < count; ++i) {
+ const Import *import = compilationUnit->importAt(i);
if (compilationUnit->stringAt(import->uriIndex) != QLatin1Literal("QtTest"))
continue;
@@ -278,7 +278,7 @@ private:
if (!object) // Start at root of compilation unit if not enumerating a specific child
object = compilationUnit->objectAt(0);
- if (CompilationUnit *superTypeUnit = compilationUnit->resolvedTypes.value(object->inheritedTypeNameIndex)->compilationUnit) {
+ if (CompilationUnit *superTypeUnit = compilationUnit->resolvedTypes.value(object->inheritedTypeNameIndex)->compilationUnit.data()) {
// We have a non-C++ super type, which could indicate we're a subtype of a TestCase
if (testCaseType.isValid() && superTypeUnit->url() == testCaseType.sourceUrl())
result.isTestCase = true;
@@ -359,6 +359,11 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch
}
}
+ if (setup) {
+ // Don't check the return value; it's OK if it doesn't exist.
+ QMetaObject::invokeMethod(setup, "applicationAvailable");
+ }
+
// Look for QML-specific command-line options.
// -import dir Specify an import directory.
// -plugins dir Specify a directory where to search for plugins.
@@ -476,6 +481,9 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch
// Register the test object
qmlRegisterSingletonType<QTestRootObject>("Qt.test.qtestroot", 1, 0, "QTestRootObject", testRootObject);
+ QSet<QString> commandLineTestFunctions = QTest::testFunctions.toSet();
+ const bool filteringTestFunctions = !commandLineTestFunctions.isEmpty();
+
// Scan through all of the "tst_*.qml" files and run each of them
// in turn with a separate QQuickView (for test isolation).
for (const QString &file : qAsConst(files)) {
@@ -508,10 +516,10 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch
continue;
}
- static const QSet<QString> commandLineTestFunctions = QTest::testFunctions.toSet();
- if (!commandLineTestFunctions.isEmpty() &&
- !availableTestFunctions.toSet().intersects(commandLineTestFunctions))
+ const QSet<QString> availableTestSet = availableTestFunctions.toSet();
+ if (filteringTestFunctions && !availableTestSet.intersects(commandLineTestFunctions))
continue;
+ commandLineTestFunctions.subtract(availableTestSet);
QQuickView view(&engine, nullptr);
view.setFlags(Qt::Window | Qt::WindowSystemMenuHint
@@ -580,10 +588,23 @@ int quick_test_main_with_setup(int argc, char **argv, const char *name, const ch
}
}
+ if (setup) {
+ // Don't check the return value; it's OK if it doesn't exist.
+ QMetaObject::invokeMethod(setup, "cleanupTestCase");
+ }
+
// Flush the current logging stream.
QuickTestResult::setProgramName(nullptr);
delete app;
+ // Check that all test functions passed on the command line were found
+ if (!commandLineTestFunctions.isEmpty()) {
+ qWarning() << "Could not find the following test functions:";
+ for (const QString &functionName : qAsConst(commandLineTestFunctions))
+ qWarning(" %s()", qUtf8Printable(functionName));
+ return commandLineTestFunctions.count();
+ }
+
// Return the number of failures as the exit code.
return QuickTestResult::exitCode();
}
diff --git a/src/qmltest/quicktestevent.cpp b/src/qmltest/quicktestevent.cpp
index ee375a896c..480811d95c 100644
--- a/src/qmltest/quicktestevent.cpp
+++ b/src/qmltest/quicktestevent.cpp
@@ -144,6 +144,7 @@ namespace QtQuickTest
int lastMouseTimestamp = 0;
+ // TODO should be Qt::MouseButtons buttons in case multiple buttons are pressed
static void mouseEvent(MouseAction action, QWindow *window,
QObject *item, Qt::MouseButton button,
Qt::KeyboardModifiers stateKey, const QPointF &_pos, int delay=-1)
@@ -252,6 +253,7 @@ bool QuickTestEvent::mousePress
QWindow *view = eventWindow(item);
if (!view)
return false;
+ m_pressedButtons.setFlag(Qt::MouseButton(button), true);
QtQuickTest::mouseEvent(QtQuickTest::MousePress, view, item,
Qt::MouseButton(button),
Qt::KeyboardModifiers(modifiers),
@@ -281,6 +283,7 @@ bool QuickTestEvent::mouseRelease
QWindow *view = eventWindow(item);
if (!view)
return false;
+ m_pressedButtons.setFlag(Qt::MouseButton(button), false);
QtQuickTest::mouseEvent(QtQuickTest::MouseRelease, view, item,
Qt::MouseButton(button),
Qt::KeyboardModifiers(modifiers),
@@ -336,8 +339,9 @@ bool QuickTestEvent::mouseMove
QWindow *view = eventWindow(item);
if (!view)
return false;
+ const Qt::MouseButtons effectiveButtons = buttons ? Qt::MouseButtons(buttons) : m_pressedButtons;
QtQuickTest::mouseEvent(QtQuickTest::MouseMove, view, item,
- Qt::MouseButton(buttons), Qt::NoModifier,
+ Qt::MouseButton(int(effectiveButtons)), Qt::NoModifier,
QPointF(x, y), delay);
return true;
}
diff --git a/src/qmltest/quicktestevent_p.h b/src/qmltest/quicktestevent_p.h
index f33f339cdd..5208c03a82 100644
--- a/src/qmltest/quicktestevent_p.h
+++ b/src/qmltest/quicktestevent_p.h
@@ -119,6 +119,8 @@ private:
QWindow *activeWindow();
QTouchDevice *touchDevice();
+ Qt::MouseButtons m_pressedButtons;
+
friend class QQuickTouchEventSequence;
};
diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp
index c4a3280cf6..3b854dfccd 100644
--- a/src/qmltest/quicktestresult.cpp
+++ b/src/qmltest/quicktestresult.cpp
@@ -61,6 +61,7 @@
#include <QtCore/qdebug.h>
#include <QtCore/QUrl>
#include <QtCore/QDir>
+#include <QtCore/qregularexpression.h>
#include <QtQuick/qquickwindow.h>
#include <QtGui/qvector3d.h>
#include <QtGui/qimagewriter.h>
@@ -625,9 +626,18 @@ void QuickTestResult::warn(const QString &message, const QUrl &location, int lin
QTestLog::warn(message.toLatin1().constData(), qtestFixUrl(location).toLatin1().constData(), line);
}
-void QuickTestResult::ignoreWarning(const QString &message)
+void QuickTestResult::ignoreWarning(const QJSValue &message)
{
- QTestLog::ignoreMessage(QtWarningMsg, message.toLatin1().constData());
+ if (message.isRegExp()) {
+ // ### we should probably handle QRegularExpression conversion engine-side
+ QRegExp re = message.toVariant().toRegExp();
+ QRegularExpression::PatternOptions opts = re.caseSensitivity() ==
+ Qt::CaseInsensitive ? QRegularExpression::CaseInsensitiveOption : QRegularExpression::NoPatternOption;
+ QRegularExpression re2(re.pattern(), opts);
+ QTestLog::ignoreMessage(QtWarningMsg, re2);
+ } else {
+ QTestLog::ignoreMessage(QtWarningMsg, message.toString().toLatin1());
+ }
}
void QuickTestResult::wait(int ms)
diff --git a/src/qmltest/quicktestresult_p.h b/src/qmltest/quicktestresult_p.h
index 6e7b72830e..f222cd3e87 100644
--- a/src/qmltest/quicktestresult_p.h
+++ b/src/qmltest/quicktestresult_p.h
@@ -137,7 +137,7 @@ public Q_SLOTS:
const QUrl &location, int line);
void warn(const QString &message, const QUrl &location, int line);
- void ignoreWarning(const QString &message);
+ void ignoreWarning(const QJSValue &message);
void wait(int ms);
void sleep(int ms);
diff --git a/src/quick/configure.json b/src/quick/configure.json
index 2c57c3318f..9ec3531ef4 100644
--- a/src/quick/configure.json
+++ b/src/quick/configure.json
@@ -15,6 +15,7 @@
"quick-flipable": "boolean",
"quick-gridview": "boolean",
"quick-listview": "boolean",
+ "quick-tableview": "boolean",
"quick-path": "boolean",
"quick-pathview": "boolean",
"quick-positioners": "boolean",
@@ -86,7 +87,7 @@
},
"quick-itemview": {
"label": "ItemView item",
- "condition": "features.quick-gridview || features.quick-listview",
+ "condition": "features.quick-gridview || features.quick-listview || features.quick-tableview",
"output": [
"privateFeature"
]
@@ -107,6 +108,14 @@
"privateFeature"
]
},
+ "quick-tableview": {
+ "label": "TableView item",
+ "purpose": "Provides the TableView item.",
+ "section": "Qt Quick",
+ "output": [
+ "privateFeature"
+ ]
+ },
"quick-particles": {
"label": "Particle support",
"purpose": "Provides a particle system.",
@@ -183,6 +192,7 @@
"quick-flipable",
"quick-gridview",
"quick-listview",
+ "quick-tableview",
"quick-path",
"quick-pathview",
"quick-positioners",
diff --git a/src/quick/designer/qqmldesignermetaobject.cpp b/src/quick/designer/qqmldesignermetaobject.cpp
index 09493c30d6..2efcdada8b 100644
--- a/src/quick/designer/qqmldesignermetaobject.cpp
+++ b/src/quick/designer/qqmldesignermetaobject.cpp
@@ -83,7 +83,7 @@ static QQmlPropertyCache *cacheForObject(QObject *object, QQmlEngine *engine)
{
QQmlVMEMetaObject *metaObject = QQmlVMEMetaObject::get(object);
if (metaObject)
- return metaObject->cache;
+ return metaObject->cache.data();
return QQmlEnginePrivate::get(engine)->cache(object);
}
@@ -139,7 +139,7 @@ QQmlDesignerMetaObject::QQmlDesignerMetaObject(QObject *object, QQmlEngine *engi
cache->setParent(ddata->propertyCache);
cache->invalidate(engine, this);
ddata->propertyCache->release();
- ddata->propertyCache = cache;
+ ddata->propertyCache = cache.data();
ddata->propertyCache->addref();
}
diff --git a/src/quick/designer/qquickdesignercustomparserobject.cpp b/src/quick/designer/qquickdesignercustomparserobject.cpp
index 9145b1367a..50a8b6a25b 100644
--- a/src/quick/designer/qquickdesignercustomparserobject.cpp
+++ b/src/quick/designer/qquickdesignercustomparserobject.cpp
@@ -46,12 +46,12 @@ QQuickDesignerCustomParserObject::QQuickDesignerCustomParserObject()
}
-void QQuickDesignerCustomParser::verifyBindings(const QV4::CompiledData::Unit *, const QList<const QV4::CompiledData::Binding *> &)
+void QQuickDesignerCustomParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &)
{
/* Nothing to do we accept anything */
}
-void QQuickDesignerCustomParser::applyBindings(QObject *, QV4::CompiledData::CompilationUnit *, const QList<const QV4::CompiledData::Binding *> &)
+void QQuickDesignerCustomParser::applyBindings(QObject *, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &, const QList<const QV4::CompiledData::Binding *> &)
{
/* Nothing to do we accept anything */
}
diff --git a/src/quick/designer/qquickdesignercustomparserobject_p.h b/src/quick/designer/qquickdesignercustomparserobject_p.h
index e4d0765277..b38417d102 100644
--- a/src/quick/designer/qquickdesignercustomparserobject_p.h
+++ b/src/quick/designer/qquickdesignercustomparserobject_p.h
@@ -70,8 +70,8 @@ public:
QQuickDesignerCustomParser()
: QQmlCustomParser(AcceptsAttachedProperties | AcceptsSignalHandlers) {}
- void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props) override;
- void applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
+ void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) override;
+ void applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
};
QT_END_NAMESPACE
diff --git a/src/quick/doc/images/9BcAYDlpuT8.jpg b/src/quick/doc/images/9BcAYDlpuT8.jpg
new file mode 100644
index 0000000000..0a69ab0034
--- /dev/null
+++ b/src/quick/doc/images/9BcAYDlpuT8.jpg
Binary files differ
diff --git a/src/quick/doc/images/pointDistanceThreshold.png b/src/quick/doc/images/pointDistanceThreshold.png
deleted file mode 100644
index c883f5f8a1..0000000000
--- a/src/quick/doc/images/pointDistanceThreshold.png
+++ /dev/null
Binary files differ
diff --git a/src/quick/doc/images/pointerHandlerMargin.png b/src/quick/doc/images/pointerHandlerMargin.png
new file mode 100644
index 0000000000..e996f57bf1
--- /dev/null
+++ b/src/quick/doc/images/pointerHandlerMargin.png
Binary files differ
diff --git a/src/quick/doc/images/pointDistanceThreshold.svg b/src/quick/doc/images/pointerHandlerMargin.svg
index 30d04e0df6..3e8b05f3f9 100644
--- a/src/quick/doc/images/pointDistanceThreshold.svg
+++ b/src/quick/doc/images/pointerHandlerMargin.svg
@@ -10,17 +10,17 @@
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg6318"
- inkscape:version="0.92.2 5c3e80d, 2017-08-06"
- viewBox="0 0 249.99999 100"
+ inkscape:version="0.92.2 2405546, 2018-03-11"
+ viewBox="0 0 148.46917 100.37263"
version="1.1"
- sodipodi:docname="pointDistanceThreshold.svg"
- width="250"
- height="100"
- inkscape:export-filename="/home/rutledge/dev/qt510/qtdeclarative/src/quick/doc/images/pointDistanceThreshold.png"
+ sodipodi:docname="pointerHandlerMargin.svg"
+ width="148.46918"
+ height="100.37263"
+ inkscape:export-filename="/home/rutledge/dev/qt5/qtdeclarative/src/quick/doc/images/pointerHandlerMargin.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<title
- id="title892">pointDistanceThreshold</title>
+ id="title892">pointerHandlerMargin</title>
<defs
id="defs20">
<marker
@@ -104,18 +104,18 @@
id="base"
bordercolor="#666666"
inkscape:pageshadow="2"
- inkscape:window-y="10"
+ inkscape:window-y="0"
pagecolor="#ffffff"
inkscape:zoom="2.1818696"
- inkscape:window-width="3025"
+ inkscape:window-width="1920"
inkscape:window-maximized="0"
- inkscape:window-x="275"
+ inkscape:window-x="0"
showgrid="false"
borderopacity="1.0"
inkscape:current-layer="layer1"
- inkscape:cx="-180.21887"
- inkscape:cy="-3.6626369"
- inkscape:window-height="2115"
+ inkscape:cx="-22.660169"
+ inkscape:cy="58.210886"
+ inkscape:window-height="1080"
inkscape:pageopacity="0.0"
inkscape:document-units="px"
fit-margin-top="0"
@@ -126,7 +126,7 @@
id="layer1"
inkscape:label="Camada 1"
inkscape:groupmode="layer"
- transform="translate(14.90473,-165.12199)">
+ transform="translate(-86.411864,-164.74934)">
<rect
style="opacity:1;fill:none;fill-opacity:1;stroke:#006eed;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4231"
@@ -174,15 +174,15 @@
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-start:url(#marker1145)" />
<text
id="text1335"
- y="227.02623"
- x="-37.15416"
- style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:125%;font-family:Sans;-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ y="226.88364"
+ x="104.81705"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:125%;font-family:Sans;-inkscape-font-specification:'Sans, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:end;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"
transform="rotate(-6.0579549)"><tspan
- y="227.02623"
- x="-37.15416"
- id="tspan1333"
- sodipodi:role="line">pointDistanceThreshold</tspan></text>
+ y="226.88364"
+ x="104.81705"
+ sodipodi:role="line"
+ id="tspan862">margin</tspan></text>
</g>
<metadata
id="metadata19">
@@ -196,10 +196,10 @@
<dc:publisher>
<cc:Agent
rdf:about="http://openclipart.org/">
- <dc:title></dc:title>
+ <dc:title />
</cc:Agent>
</dc:publisher>
- <dc:title>pointDistanceThreshold</dc:title>
+ <dc:title>pointerHandlerMargin</dc:title>
<dc:creator>
<cc:Agent>
<dc:title>The Qt Company</dc:title>
diff --git a/src/quick/doc/images/qml-item-canvas-lineDash.png b/src/quick/doc/images/qml-item-canvas-lineDash.png
new file mode 100644
index 0000000000..a2a038abe0
--- /dev/null
+++ b/src/quick/doc/images/qml-item-canvas-lineDash.png
Binary files differ
diff --git a/src/quick/doc/qtquick.qdocconf b/src/quick/doc/qtquick.qdocconf
index ad5e47002d..34ec66bf1d 100644
--- a/src/quick/doc/qtquick.qdocconf
+++ b/src/quick/doc/qtquick.qdocconf
@@ -89,3 +89,7 @@ navigation.qmltypespage = "Qt Quick QML Types"
# \svgcolor {#ffdead}
macro.svgcolor.HTML = "<div style=\"padding:10px;color:#fff;background:\1;\"></div>"
+
+# youtube video thumbnails that show up in offline and online docs
+HTML.extraimages += images/9BcAYDlpuT8.jpg
+qhp.QtQuick.extraFiles += images/9BcAYDlpuT8.jpg
diff --git a/src/quick/doc/snippets/pointerHandlers/dragHandler.qml b/src/quick/doc/snippets/pointerHandlers/dragHandler.qml
index 78a7db5b0c..04ea3967a0 100644
--- a/src/quick/doc/snippets/pointerHandlers/dragHandler.qml
+++ b/src/quick/doc/snippets/pointerHandlers/dragHandler.qml
@@ -48,8 +48,7 @@
**
****************************************************************************/
//![0]
-import QtQuick 2.10
-import Qt.labs.handlers 1.0
+import QtQuick 2.12
Rectangle {
width: 100
diff --git a/src/quick/doc/snippets/pointerHandlers/dragHandlerDifferentTarget.qml b/src/quick/doc/snippets/pointerHandlers/dragHandlerDifferentTarget.qml
index 4c4168de83..7e7662539f 100644
--- a/src/quick/doc/snippets/pointerHandlers/dragHandlerDifferentTarget.qml
+++ b/src/quick/doc/snippets/pointerHandlers/dragHandlerDifferentTarget.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -48,8 +48,7 @@
**
****************************************************************************/
//![0]
-import QtQuick 2.10
-import Qt.labs.handlers 1.0
+import QtQuick 2.12
Item {
width: 640
@@ -58,8 +57,8 @@ Item {
Rectangle {
id: feedback
border.color: "red"
- width: Math.max(10, handler.point.ellipseDiameters.width)
- height: Math.max(10, handler.point.ellipseDiameters.height)
+ width: Math.max(10, handler.centroid.ellipseDiameters.width)
+ height: Math.max(10, handler.centroid.ellipseDiameters.height)
radius: Math.max(width, height) / 2
visible: handler.active
}
diff --git a/src/quick/doc/snippets/pointerHandlers/dragHandlerNullTarget.qml b/src/quick/doc/snippets/pointerHandlers/dragHandlerNullTarget.qml
index 09429ec1d2..c33f2b2782 100644
--- a/src/quick/doc/snippets/pointerHandlers/dragHandlerNullTarget.qml
+++ b/src/quick/doc/snippets/pointerHandlers/dragHandlerNullTarget.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -48,8 +48,7 @@
**
****************************************************************************/
//![0]
-import QtQuick 2.10
-import Qt.labs.handlers 1.0
+import QtQuick 2.12
Item {
width: 640
@@ -62,9 +61,9 @@ Item {
Text {
color: handler.active ? "darkgreen" : "black"
- text: handler.point.position.x.toFixed(1) + "," + handler.point.position.y.toFixed(1)
- x: handler.point.position.x - width / 2
- y: handler.point.position.y - height
+ text: handler.centroid.centroid.x.toFixed(1) + "," + handler.centroid.position.y.toFixed(1)
+ x: handler.centroid.position.x - width / 2
+ y: handler.centroid.position.y - height
}
}
//![0]
diff --git a/src/quick/doc/snippets/pointerHandlers/pinchHandler.qml b/src/quick/doc/snippets/pointerHandlers/pinchHandler.qml
index 955047d115..dd0542a953 100644
--- a/src/quick/doc/snippets/pointerHandlers/pinchHandler.qml
+++ b/src/quick/doc/snippets/pointerHandlers/pinchHandler.qml
@@ -48,8 +48,7 @@
**
****************************************************************************/
//![0]
-import QtQuick 2.8
-import Qt.labs.handlers 1.0
+import QtQuick 2.12
Rectangle {
width: 400
diff --git a/src/quick/doc/snippets/pointerHandlers/pinchHandlerDifferentTarget.qml b/src/quick/doc/snippets/pointerHandlers/pinchHandlerDifferentTarget.qml
index a5255a64e3..b071e294af 100644
--- a/src/quick/doc/snippets/pointerHandlers/pinchHandlerDifferentTarget.qml
+++ b/src/quick/doc/snippets/pointerHandlers/pinchHandlerDifferentTarget.qml
@@ -48,8 +48,7 @@
**
****************************************************************************/
//![0]
-import QtQuick 2.8
-import Qt.labs.handlers 1.0
+import QtQuick 2.12
Item {
width: 640
diff --git a/src/quick/doc/snippets/pointerHandlers/pinchHandlerNullTarget.qml b/src/quick/doc/snippets/pointerHandlers/pinchHandlerNullTarget.qml
index 7d21efcb84..ade59d29f0 100644
--- a/src/quick/doc/snippets/pointerHandlers/pinchHandlerNullTarget.qml
+++ b/src/quick/doc/snippets/pointerHandlers/pinchHandlerNullTarget.qml
@@ -48,8 +48,7 @@
**
****************************************************************************/
//![0]
-import QtQuick 2.8
-import Qt.labs.handlers 1.0
+import QtQuick 2.12
Item {
width: 640
diff --git a/src/quick/doc/snippets/pointerHandlers/pointHandler.qml b/src/quick/doc/snippets/pointerHandlers/pointHandler.qml
index 262eb607b6..20be120120 100644
--- a/src/quick/doc/snippets/pointerHandlers/pointHandler.qml
+++ b/src/quick/doc/snippets/pointerHandlers/pointHandler.qml
@@ -48,9 +48,8 @@
**
****************************************************************************/
//![0]
-import QtQuick 2.10
+import QtQuick 2.12
import QtQuick.Window 2.2
-import Qt.labs.handlers 1.0
Window {
width: 480
diff --git a/src/quick/doc/snippets/qml/mousearea/mousearea-snippet.qml b/src/quick/doc/snippets/qml/mousearea/mousearea-snippet.qml
index 0306c2de0f..13ef779d08 100644
--- a/src/quick/doc/snippets/qml/mousearea/mousearea-snippet.qml
+++ b/src/quick/doc/snippets/qml/mousearea/mousearea-snippet.qml
@@ -90,7 +90,7 @@ Rectangle {
Rectangle {
width: 100; height: 100
- //! [mouse handlers]
+ //! [signal handlers]
MouseArea {
anchors.fill: parent
onClicked: console.log("area clicked")
@@ -98,7 +98,7 @@ Rectangle {
onEntered: console.log("mouse entered the area")
onExited: console.log("mouse left the area")
}
- //! [mouse handlers]
+ //! [signal handlers]
}
} //end of column
diff --git a/src/quick/doc/snippets/qml/pathview/pathview.qml b/src/quick/doc/snippets/qml/pathview/pathview.qml
index 1a31f1372b..58d19b1a0c 100644
--- a/src/quick/doc/snippets/qml/pathview/pathview.qml
+++ b/src/quick/doc/snippets/qml/pathview/pathview.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -59,6 +59,7 @@ Rectangle {
id: delegate
Column {
id: wrapper
+ opacity: PathView.isCurrentItem ? 1 : 0.5
Image {
anchors.horizontalCenter: nameText.horizontalCenter
width: 64; height: 64
@@ -68,7 +69,6 @@ Rectangle {
id: nameText
text: name
font.pointSize: 16
- color: wrapper.PathView.isCurrentItem ? "red" : "black"
}
}
}
diff --git a/src/quick/doc/snippets/qml/rectangle/rectangle-gradient.qml b/src/quick/doc/snippets/qml/rectangle/rectangle-gradient.qml
index 031bee960d..e52a0ce0ef 100644
--- a/src/quick/doc/snippets/qml/rectangle/rectangle-gradient.qml
+++ b/src/quick/doc/snippets/qml/rectangle/rectangle-gradient.qml
@@ -80,5 +80,17 @@ Rectangle {
}
}
//! [rectangles]
+
+//! [presets]
+Rectangle {
+ y: 0; width: 80; height: 80
+ gradient: Gradient.NightFade
+}
+
+Rectangle {
+ y: 0; width: 80; height: 80
+ gradient: "NightFade"
+}
+//! [presets]
}
}
diff --git a/src/quick/doc/snippets/qml/tableview/reusabledelegate.qml b/src/quick/doc/snippets/qml/tableview/reusabledelegate.qml
new file mode 100644
index 0000000000..4a49e809cc
--- /dev/null
+++ b/src/quick/doc/snippets/qml/tableview/reusabledelegate.qml
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+
+//![0]
+Component {
+ id: tableViewDelegate
+ Rectangle {
+ implicitWidth: 100
+ implicitHeight: 50
+
+ TableView.onPooled: rotationAnimation.pause()
+ TableView.onReused: rotationAnimation.resume()
+
+ Rectangle {
+ id: rect
+ anchors.centerIn: parent
+ width: 40
+ height: 5
+ color: "green"
+
+ RotationAnimation {
+ id: rotationAnimation
+ target: rect
+ duration: (Math.random() * 2000) + 200
+ from: 0
+ to: 359
+ running: true
+ loops: Animation.Infinite
+ }
+ }
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/qml/tableview/tablemodel.cpp b/src/quick/doc/snippets/qml/tableview/tablemodel.cpp
new file mode 100644
index 0000000000..ea9f76f131
--- /dev/null
+++ b/src/quick/doc/snippets/qml/tableview/tablemodel.cpp
@@ -0,0 +1,103 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2018 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]
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+#include <QAbstractTableModel>
+
+class TableModel : public QAbstractTableModel
+{
+ Q_OBJECT
+
+public:
+
+ int rowCount(const QModelIndex & = QModelIndex()) const override
+ {
+ return 200;
+ }
+
+ int columnCount(const QModelIndex & = QModelIndex()) const override
+ {
+ return 200;
+ }
+
+ QVariant data(const QModelIndex &index, int role) const override
+ {
+ switch (role) {
+ case Qt::DisplayRole:
+ return QString("%1, %2").arg(index.column()).arg(index.row());
+ default:
+ break;
+ }
+
+ return QVariant();
+ }
+
+ QHash<int, QByteArray> roleNames() const override
+ {
+ return { {Qt::DisplayRole, "display"} };
+ }
+};
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ qmlRegisterType<TableModel>("TableModel", 0, 1, "TableModel");
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+
+ return app.exec();
+}
+
+#include "main.moc"
+//![0]
diff --git a/src/quick/doc/snippets/qml/models/visual-model-and-view.qml b/src/quick/doc/snippets/qml/tableview/tablemodel.qml
index baf489f8b9..8a8ec94958 100644
--- a/src/quick/doc/snippets/qml/models/visual-model-and-view.qml
+++ b/src/quick/doc/snippets/qml/tableview/tablemodel.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -47,22 +47,25 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-import QtQuick 2.0
-Rectangle {
- width: 200; height: 200
+//![0]
+import QtQuick 2.12
+import TableModel 0.1
- //! [visual model and view]
- VisualItemModel {
- id: itemModel
- Rectangle { height: 30; width: 80; color: "red" }
- Rectangle { height: 30; width: 80; color: "green" }
- Rectangle { height: 30; width: 80; color: "blue" }
- }
+TableView {
+ anchors.fill: parent
+ columnSpacing: 1
+ rowSpacing: 1
+ clip: true
+
+ model: TableModel {}
- ListView {
- anchors.fill: parent
- model: itemModel
+ delegate: Rectangle {
+ implicitWidth: 100
+ implicitHeight: 50
+ Text {
+ text: display
+ }
}
- //! [visual model and view]
}
+//![0]
diff --git a/src/quick/doc/snippets/qml/tableview/tableviewwithheader.qml b/src/quick/doc/snippets/qml/tableview/tableviewwithheader.qml
new file mode 100644
index 0000000000..2214328ee7
--- /dev/null
+++ b/src/quick/doc/snippets/qml/tableview/tableviewwithheader.qml
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+
+//![0]
+TableView {
+ id: tableView
+
+ topMargin: header.implicitHeight
+
+ Text {
+ id: header
+ text: "A table header"
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/qml/tableview/tableviewwithprovider.qml b/src/quick/doc/snippets/qml/tableview/tableviewwithprovider.qml
new file mode 100644
index 0000000000..028b12ca6c
--- /dev/null
+++ b/src/quick/doc/snippets/qml/tableview/tableviewwithprovider.qml
@@ -0,0 +1,69 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+
+//![0]
+TableView {
+ id: tableView
+
+ property var columnWidths: [100, 50, 80, 150]
+ columnWidthProvider: function (column) { return columnWidths[column] }
+
+ Timer {
+ running: true
+ interval: 2000
+ onTriggered: {
+ tableView.columnWidths[2] = 150
+ tableView.forceLayout();
+ }
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/qml/transition-reversible.qml b/src/quick/doc/snippets/qml/transition-reversible.qml
index 58a88621c4..f74b6bffb3 100644
--- a/src/quick/doc/snippets/qml/transition-reversible.qml
+++ b/src/quick/doc/snippets/qml/transition-reversible.qml
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -47,24 +47,26 @@
** $QT_END_LICENSE$
**
****************************************************************************/
-import QtQuick 2.0
+import QtQuick 2.12
//![0]
Rectangle {
id: rect
width: 100; height: 100
- color: "red"
+ color: "steelblue"
- MouseArea { id: mouseArea; anchors.fill: parent }
+ TapHandler { id: tapHandler }
states: State {
name: "brighter"
- when: mouseArea.pressed
- PropertyChanges { target: rect; color: "yellow"; x: 50 }
+ when: tapHandler.pressed
+ PropertyChanges { target: rect; color: "lightsteelblue"; x: 50 }
}
//! [sequential animations]
transitions: Transition {
+ to: "brighter"
+ reversible: true
SequentialAnimation {
PropertyAnimation { property: "x"; duration: 1000 }
ColorAnimation { duration: 1000 }
diff --git a/src/quick/doc/src/concepts/effects/particles.qdoc b/src/quick/doc/src/concepts/effects/particles.qdoc
index 59f9c0db83..0b87eb4529 100644
--- a/src/quick/doc/src/concepts/effects/particles.qdoc
+++ b/src/quick/doc/src/concepts/effects/particles.qdoc
@@ -26,15 +26,16 @@
****************************************************************************/
/*!
- \qmlmodule QtQuick.Particles 2.11
+ \qmlmodule QtQuick.Particles 2.\QtMinorVersion
\title Qt Quick Particles QML Types
\ingroup qmlmodules
\brief Provides QML types for particle effects
This QML module contains a particle system for Qt Quick. To use these types, import the module with the following line:
- \code
- import QtQuick.Particles 2.11
- \endcode
+
+ \qml \QtMinorVersion
+ import QtQuick.Particles 2.\1
+ \endqml
For a simple overview of how the system can be used, see \l{Using the Qt Quick Particle System}.
@@ -48,10 +49,11 @@
Documentation for all Particle System types can be found on the \l QtQuick.Particles module page.
- Note that to use types from the particles module, you will need to import the types with the following line:
- \code
- import QtQuick.Particles 2.11
- \endcode
+ To use the types from the particles module, import the module with the following line:
+
+ \qml \QtMinorVersion
+ import QtQuick.Particles 2.\1
+ \endqml
\section1 The ParticleSystem
This particle system contains four main types of QML types: ParticleSystem, Painters, Emitters and Affectors.
diff --git a/src/quick/doc/src/concepts/input/mouse.qdoc b/src/quick/doc/src/concepts/input/mouse.qdoc
index 422c326898..cb506cb99a 100644
--- a/src/quick/doc/src/concepts/input/mouse.qdoc
+++ b/src/quick/doc/src/concepts/input/mouse.qdoc
@@ -33,6 +33,13 @@
\tableofcontents
+A more modern way of handling events from all pointing devices, including
+mouse and touchscreen, is via \l {Qt Quick Input Handlers}{Input Handlers}.
+This page describes the original Qt Quick \l MouseArea type, which was
+initially designed to handle mouse input, and later began handling single-touch
+events (in the form of synthetic mouse events) in simple touch-oriented user
+interfaces.
+
\section1 Mouse Types
\list
@@ -44,8 +51,8 @@
QML uses \l{qtqml-syntax-signals.html}{signals and handlers} to
deliver mouse interactions. Specifically, Qt Quick provides the \l MouseArea
-and \l MouseEvent types which allow developers to define signal handlers which
-accept mouse events within a defined area.
+and \l MouseEvent types that allow developers to define JavaScript callbacks
+(also called signal handlers), which accept mouse events within a defined area.
\section1 Defining a Mouse Area
@@ -59,8 +66,8 @@ definable.
\section1 Receiving Events
-The MouseArea type provides
-\l{qtqml-syntax-signals.html}{signals and handlers} to detect different
+The MouseArea type emits
+\l{qtqml-syntax-signals.html}{signals} in response to different
mouse events. The \l MouseArea type documentation describes these
gestures in greater detail:
@@ -76,8 +83,8 @@ gestures in greater detail:
\li released
\endlist
-These signals have signal handlers that are invoked when the signals are emitted.
-\snippet qml/mousearea/mousearea-snippet.qml mouse handlers
+These signals can have callbacks that are invoked when the signals are emitted.
+\snippet qml/mousearea/mousearea-snippet.qml signal handlers
\section1 Enabling Gestures
Some mouse gestures and button clicks need to be enabled before they send or
@@ -101,9 +108,9 @@ Additionally, to disable the whole mouse area, set the MouseArea
\section1 MouseEvent Object
-Signals and their handlers receive a \l MouseEvent object as a parameter. The
-\c mouse object contain information about the mouse event. For example, the
-mouse button that started the event is queried through the
+Signals and their callbacks receive a \l MouseEvent object as a parameter.
+The \c mouse object contains information about the mouse event. For example,
+the mouse button that started the event is queried through the
\l {MouseEvent::button}{mouse.button} property.
The \c MouseEvent object can also ignore a mouse event using its \c accepted
diff --git a/src/quick/doc/src/concepts/input/topic.qdoc b/src/quick/doc/src/concepts/input/topic.qdoc
index c23654cadb..95f4732f46 100644
--- a/src/quick/doc/src/concepts/input/topic.qdoc
+++ b/src/quick/doc/src/concepts/input/topic.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -35,30 +35,20 @@ design. Depending on the use-case that an application solves, and the
form-factor of the device that the application runs on, the best way
to receive user-input may be different.
-\section1 Touch
+\section1 Input from Pointing Devices
Allowing users to physically touch a screen to interact with an application is
a popular user-interface paradigm on portable devices like smartphones and
-tablets.
+tablets. In desktop applications, detecting and reacting to clicks and presses
+according to the mouse cursor position is a fundamental concept in
+user-interface design.
-Qt Quick was designed specifically with touch-driven user-interfaces in mind,
-and thus touch events are supported in various visual object types, from
-\l{Flickable} lists to the generic \l{MultiPointTouchArea} type, as well as
-in the \l{MouseArea} type (which will be documented thoroughly in a proceeding
-section).
+Touch-driven and mouse-driven user interfaces are supported by various
+\l{Qt Quick Input Handlers}{input handler} types, and visual object types
+such as \l{Flickable} and \l{MouseArea}.
-
-\section1 Mouse
-
-Mouse input is another important user input for user interfaces. Detecting
-and reacting to clicks and presses according to the mouse cursor position is a fundamental
-concept in user-interface design.
-
-Qt Quick provides the MouseArea visual object type which automatically receives
-mouse events (including clicks and wheel events) which allows developers to
-create custom user-interface objects to handle mouse input. Please see the
-documentation about \l{qtquick-input-mouseevents.html}
-{mouse events in Qt Quick} for more information on the topic.
+See also the documentation about
+\l{qtquick-input-mouseevents.html} {mouse events in Qt Quick}.
\section1 Keyboard Input and Keyboard Focus
diff --git a/src/quick/doc/src/concepts/pointerhandlers/qtquickhandlers-index.qdoc b/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc
index fc1fd5fbc1..2ac9860e6f 100644
--- a/src/quick/doc/src/concepts/pointerhandlers/qtquickhandlers-index.qdoc
+++ b/src/quick/doc/src/concepts/inputhandlers/qtquickhandlers-index.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -27,35 +27,34 @@
/*!
\page qtquickhandlers-index.html
- \title Qt Quick Pointer Handlers
- \brief A module with a set of QML elements that handle events from pointing devices in a user interface.
+ \title Qt Quick Input Handlers
+ \brief A module with a set of QML elements that handle events from input devices in a user interface.
- Qt Quick Pointer Handlers are a set of QML types used to handle events from
- touch, mouse, and stylus devices in a UI. In contrast to event-handling
- Items, such as \l MouseArea, Pointer Handlers require less memory and are
- intended to be used in greater numbers: one handler per type of
- interaction. Each Pointer Handler handles events on behalf of its \c parent
- Item. One Item can have multiple Pointer Handlers.
+ Qt Quick Input Handlers are a set of QML types used to handle events from
+ keyboard, touch, mouse, and stylus devices in a UI. In contrast to event-handling
+ items, such as \l MouseArea and \l Flickable, input handlers are explicitly non-visual,
+ require less memory and are intended to be used in greater numbers: one
+ handler instance per aspect of interaction. Each input handler instance
+ handles certain events on behalf of its \c parent Item. Thus the visual and
+ behavioral concerns are better separated, and the behavior is built up by
+ finer-grained composition.
- The module is introduced in Qt 5.10.
+ In Qt 5.10, these handlers were introduced in a separate Qt.labs.handlers module.
+ Now they are included with Qt Quick since 5.12. The pre-existing
+ \l Keys attached property is similar in concept, so we refer to the
+ pointing-device-oriented handlers plus \c Keys together as the set of Input Handlers.
+ We expect to offer more attached-property use cases in future versions of Qt.
- \section1 Pointer Handlers
+ \section1 Input Handlers
- \annotatedlist pointerhandlers
-
- \section1 Getting Started
-
- The QML types can be imported into your application using the following import statement in your \c {.qml} file.
-
- \code
- import Qt.labs.handlers 1.0
- \endcode
+ \annotatedlist qtquick-input-handlers
\section1 Key Features
Some of the key features are:
\list
+ \li Handle keystrokes within the focused Item
\li Handle gestures such as tapping or dragging regardless which device it comes from
\li Handle gestures from different classes of devices in different ways
\li Each Item can have unlimited Handlers
@@ -67,7 +66,5 @@
\list
\li \l{Qt Quick}
- \li \l{Qt Quick Pointer Handlers}
- \li \l{Qt Quick Pointer Handlers QML Types}{Qt Quick Pointer Handlers QML Types}
\endlist
*/
diff --git a/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc b/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc
index d82e35fb93..c33fc0cfa3 100644
--- a/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc
+++ b/src/quick/doc/src/concepts/layouts/qtquicklayouts.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
- \qmlmodule QtQuick.Layouts 1.11
+ \qmlmodule QtQuick.Layouts 1.\QtMinorVersion
\title Qt Quick Layouts QML Types
\ingroup qmlmodules
\brief Provides QML types for arranging QML items in a user interface.
@@ -39,8 +39,8 @@
The QML types can be imported into your application using the
following import statement in your .qml file.
- \code
- import QtQuick.Layouts 1.11
- \endcode
+ \qml \QtMinorVersion
+ import QtQuick.Layouts 1.\1
+ \endqml
*/
diff --git a/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc b/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc
index 2705ca5e34..e8265b92d2 100644
--- a/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc
+++ b/src/quick/doc/src/concepts/modelviewsdata/cppmodels.qdoc
@@ -41,9 +41,10 @@ QObjectList or a \l QAbstractItemModel. The first three are useful for exposing
simpler datasets, while QAbstractItemModel provides a more flexible solution for
more complex models.
-For a video tutorial that takes you through the whole process of exposing a C++
-model to QML, see the
-\l {https://youtu.be/9BcAYDlpuT8}{Using C++ Models in QML Tutorial}.
+Here is a video tutorial that takes you through the whole process of exposing a C++
+model to QML:
+
+\youtube 9BcAYDlpuT8
\section2 QStringList-based Model
diff --git a/src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc b/src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc
index 86beb38e6a..89b7929755 100644
--- a/src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc
+++ b/src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc
@@ -278,11 +278,6 @@ To visualize data, bind the view's \c model property to a model and the
}
\endcode
- \note VisualItemModel can also be used, but it is only provided for compatibility
- reasons. VisualItemModel allows a QML item to be provided as a model. This model
- contains both the data and delegate; the child items of a VisualItemModel
- provide the contents of the delegate. The model does not provide any roles.
-
\section2 Integers as Models
An integer can be used as a model that contains a certain number
@@ -307,6 +302,7 @@ To visualize data, bind the view's \c model property to a model and the
}
\endqml
+ \note The limit on the number of items in an integer model is 100,000,000.
\section2 Object Instances as Models
diff --git a/src/quick/doc/src/concepts/pointerhandlers/qtquickhandlers.qdoc b/src/quick/doc/src/concepts/pointerhandlers/qtquickhandlers.qdoc
deleted file mode 100644
index cf87c75e8c..0000000000
--- a/src/quick/doc/src/concepts/pointerhandlers/qtquickhandlers.qdoc
+++ /dev/null
@@ -1,44 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
- \qmlmodule Qt.labs.handlers 1.0
- \title Qt Quick Pointer Handlers QML Types
- \ingroup qmlmodules
- \brief Provides QML types for handling events from pointing devices in a user interface.
-
- The \l{Qt Quick Pointer Handlers} module provides QML types for handling
- events from pointing devices in a UI. These QML types work in conjunction
- with \l{Qt Quick}.
-
- The QML types can be imported into your application using the
- following import statement in your .qml file.
-
- \code
- import Qt.labs.handlers 1.0
- \endcode
-*/
diff --git a/src/quick/doc/src/concepts/positioning/righttoleft.qdoc b/src/quick/doc/src/concepts/positioning/righttoleft.qdoc
index 4e1eca7ac3..7c8cd735b3 100644
--- a/src/quick/doc/src/concepts/positioning/righttoleft.qdoc
+++ b/src/quick/doc/src/concepts/positioning/righttoleft.qdoc
@@ -73,7 +73,7 @@ the effective alignment of the text element that takes the mirroring into accoun
(This applies to the \l Row, \l Grid, \l Flow, \l ListView and \l GridView types.)
-From Qt Quick 1.1 onwards, types used for horizontal positioning and model views have gained a \c layoutDirection
+Types used for horizontal positioning and model views have the \c layoutDirection
property for controlling the horizontal direction of the layouts. Setting \c layoutDirection to
\c Qt.RightToLeft causes items to be laid out from the right to left. By default Qt Quick follows
the left-to-right layout direction.
@@ -132,7 +132,7 @@ aligned and just make sure that text is translated and aligned properly.
(This applies to \l Image, \l BorderImage and \l AnimatedImage types.)
Most images do not need to be mirrored, but some directional icons, such as arrows, may need to be mirrored.
-The painting of these icons can be mirrored with a dedicated \c mirror property introduced in Qt Quick 1.1:
+The painting of these icons can be mirrored with a dedicated \c mirror property:
\snippet qml/righttoleft.qml 5
diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
index 99175ab94e..4c94259139 100644
--- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
+++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
@@ -796,6 +796,14 @@ with multiple windows.
\li Mipmapped Image items are not placed in the global atlas and will
not be batched.
+ \li A bug in the OpenGL driver related to framebuffer object (FBO) readbacks
+ may corrupt rendered glyphs. If you set the \c QML_USE_GLYPHCACHE_WORKAROUND
+ environment variable, Qt keeps an additional copy of the glyph in RAM. This
+ means that performance is slightly lower when drawing glyphs that have not
+ been drawn before, as Qt accesses the extra copy via the CPU. It also means
+ that the glyph cache will use twice as much memory. The quality is not
+ affected by this.
+
\endlist
If an application performs poorly, make sure that rendering is
diff --git a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
index c8bdfa28cd..dd7f21e81a 100644
--- a/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
+++ b/src/quick/doc/src/guidelines/qtquick-bestpractices.qdoc
@@ -28,7 +28,7 @@
/*!
\page qtquick-bestpractices.html
\title Best Practices for QML and Qt Quick
-\brief Lists best practices for working with QML and Qt Quick
+\brief Lists best practices for working with QML and Qt Quick.
\ingroup best-practices
Despite all of the benefits that QML and Qt Quick offer, they can be
@@ -121,7 +121,7 @@ of resources. However, whenever a new file is added to \c RESOURCES using this
approach, it causes \e all of the other files in \c RESOURCES to be recompiled
as well. This can be inefficient, especially for large sets of files.
In this case, a better approach is to separate each type of resource into its
-own \l {Resource Collection Files (.qrc)}{.qrc} file. For example, the snippet
+own \l [Qt Core] {Resource Collection Files}{.qrc} file. For example, the snippet
above could be changed to the following:
\badcode
@@ -197,9 +197,9 @@ so. To explain why, let's take a look at a simplified example.
Suppose we were writing the UI for a settings page:
-\qml
- import QtQuick 2.11
- import QtQuick.Controls 2.4
+\qml \QtMinorVersion
+ import QtQuick 2.\1
+ import QtQuick.Controls 2.\1
Page {
Button {
@@ -296,9 +296,9 @@ into QML:
The QML then calls the C++ slot directly:
-\qml
- import QtQuick 2.11
- import QtQuick.Controls 2.4
+\qml \QtMinorVersion
+ import QtQuick 2.\1
+ import QtQuick.Controls 2.\1
Page {
Button {
diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc
index e787ed31ef..ddf90f88d9 100644
--- a/src/quick/doc/src/qmltypereference.qdoc
+++ b/src/quick/doc/src/qmltypereference.qdoc
@@ -26,7 +26,7 @@
****************************************************************************/
/*!
-\qmlmodule QtQuick 2.11
+\qmlmodule QtQuick 2.\QtMinorVersion
\title Qt Quick QML Types
\ingroup qmlmodules
\brief Provides graphical QML types.
@@ -34,11 +34,11 @@
The \l{Qt Quick} module provides graphical primitive types. These types are only
available in a QML document if that document imports the \c QtQuick namespace.
-The current version of the \c QtQuick module is version 2.11, and thus it may be
-imported via the following statement:
+The current version of the \c QtQuick module is version \QtMinorVersion, and
+thus it may be imported via the following statement:
-\qml
-import QtQuick 2.11
+\qml \QtMinorVersion
+import QtQuick 2.\1
\endqml
Visit the \l {Qt Quick} module documentation for more
@@ -78,7 +78,7 @@ In addition, the \c QtQuick import provides the following basic types:
\section1 Object Types
-All object types provided by the \c QtQuick import are based on the \l{Item}
+Most object types provided by the \c QtQuick import are based on the \l{Item}
type, which itself derives from \l{QtQml::QtObject}{QtObject}. \l{Qt QML QML
Types#Object Types} {QML object types} provided by the Qt QML module (such as
\l{QtQml::QtObject}{QtObject} and \l{QtQml::Component}{Component}) are also
@@ -889,15 +889,16 @@ console.log(c + " " + d); // false true
*/
/*!
-\qmlmodule QtTest 1.11
+\qmlmodule QtTest 1.\QtMinorVersion
\title Qt Quick Test QML Types
\brief This module provides QML types to unit test your QML application
\ingroup qmlmodules
You can import this module using the following statement:
-\code
-import QtTest 1.11
-\endcode
+
+\qml \QtMinorVersion
+import QtTest 1.\1
+\endqml
For more information about how to use these types, see
\l{Qt Quick Test}.
diff --git a/src/quick/handlers/handlers.pri b/src/quick/handlers/handlers.pri
index 8bd74d95da..1258822f40 100644
--- a/src/quick/handlers/handlers.pri
+++ b/src/quick/handlers/handlers.pri
@@ -1,17 +1,22 @@
HEADERS += \
$$PWD/qquickdraghandler_p.h \
- $$PWD/qquickhandlersmodule_p.h \
+ $$PWD/qquickhandlerpoint_p.h \
+ $$PWD/qquickhoverhandler_p.h \
$$PWD/qquickmultipointhandler_p.h \
$$PWD/qquickpinchhandler_p.h \
$$PWD/qquickpointerdevicehandler_p.h \
+ $$PWD/qquickpointerdevicehandler_p_p.h \
$$PWD/qquickpointerhandler_p.h \
+ $$PWD/qquickpointerhandler_p_p.h \
$$PWD/qquickpointhandler_p.h \
$$PWD/qquicksinglepointhandler_p.h \
$$PWD/qquicktaphandler_p.h \
+ $$PWD/qquickdragaxis_p.h
SOURCES += \
$$PWD/qquickdraghandler.cpp \
- $$PWD/qquickhandlersmodule.cpp \
+ $$PWD/qquickhandlerpoint.cpp \
+ $$PWD/qquickhoverhandler.cpp \
$$PWD/qquickmultipointhandler.cpp \
$$PWD/qquickpinchhandler.cpp \
$$PWD/qquickpointerdevicehandler.cpp \
@@ -19,4 +24,4 @@ SOURCES += \
$$PWD/qquickpointhandler.cpp \
$$PWD/qquicksinglepointhandler.cpp \
$$PWD/qquicktaphandler.cpp \
-
+ $$PWD/qquickdragaxis.cpp
diff --git a/src/quick/handlers/qquickdragaxis.cpp b/src/quick/handlers/qquickdragaxis.cpp
new file mode 100644
index 0000000000..5efe19b2fe
--- /dev/null
+++ b/src/quick/handlers/qquickdragaxis.cpp
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qquickdragaxis_p.h"
+#include <limits>
+
+QQuickDragAxis::QQuickDragAxis()
+ : m_minimum(-std::numeric_limits<qreal>::max())
+ , m_maximum(std::numeric_limits<qreal>::max())
+ , m_enabled(true)
+{
+}
+
+void QQuickDragAxis::setMinimum(qreal minimum)
+{
+ if (m_minimum == minimum)
+ return;
+
+ m_minimum = minimum;
+ emit minimumChanged();
+}
+
+void QQuickDragAxis::setMaximum(qreal maximum)
+{
+ if (m_maximum == maximum)
+ return;
+
+ m_maximum = maximum;
+ emit maximumChanged();
+}
+
+void QQuickDragAxis::setEnabled(bool enabled)
+{
+ if (m_enabled == enabled)
+ return;
+
+ m_enabled = enabled;
+ emit enabledChanged();
+}
+
diff --git a/src/quick/handlers/qquickdragaxis_p.h b/src/quick/handlers/qquickdragaxis_p.h
new file mode 100644
index 0000000000..2c2e0a426d
--- /dev/null
+++ b/src/quick/handlers/qquickdragaxis_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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 QQUICKDRAGAXIS_P_H
+#define QQUICKDRAGAXIS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qobject.h>
+#include <QtCore/qglobal.h>
+
+class Q_AUTOTEST_EXPORT QQuickDragAxis : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal minimum READ minimum WRITE setMinimum NOTIFY minimumChanged)
+ Q_PROPERTY(qreal maximum READ maximum WRITE setMaximum NOTIFY maximumChanged)
+ Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
+
+public:
+ QQuickDragAxis();
+
+ qreal minimum() const { return m_minimum; }
+ void setMinimum(qreal minimum);
+
+ qreal maximum() const { return m_maximum; }
+ void setMaximum(qreal maximum);
+
+ bool enabled() const { return m_enabled; }
+ void setEnabled(bool enabled);
+
+signals:
+ void minimumChanged();
+ void maximumChanged();
+ void enabledChanged();
+
+private:
+ qreal m_minimum;
+ qreal m_maximum;
+ bool m_enabled;
+};
+
+#endif // QQUICKDRAGAXIS_P_H
diff --git a/src/quick/handlers/qquickdraghandler.cpp b/src/quick/handlers/qquickdraghandler.cpp
index 5e540b3615..aa66c16007 100644
--- a/src/quick/handlers/qquickdraghandler.cpp
+++ b/src/quick/handlers/qquickdraghandler.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -43,16 +43,20 @@
QT_BEGIN_NAMESPACE
+static const qreal DragAngleToleranceDegrees = 10;
+
+Q_LOGGING_CATEGORY(lcDragHandler, "qt.quick.handler.drag")
+
/*!
\qmltype DragHandler
\instantiates QQuickDragHandler
- \inherits SinglePointHandler
- \inqmlmodule Qt.labs.handlers
- \ingroup qtquick-handlers
+ \inherits MultiPointHandler
+ \inqmlmodule QtQuick
+ \ingroup qtquick-input-handlers
\brief Handler for dragging.
DragHandler is a handler that is used to interactively move an Item.
- Like other Pointer Handlers, by default it is fully functional, and
+ Like other Input Handlers, by default it is fully functional, and
manipulates its \l {PointerHandler::target} {target}.
\snippet pointerHandlers/dragHandler.qml 0
@@ -71,119 +75,173 @@ QT_BEGIN_NAMESPACE
\snippet pointerHandlers/dragHandlerNullTarget.qml 0
+ If minimumPointCount and maximumPointCount are set to values larger than 1,
+ the user will need to drag that many fingers in the same direction to start
+ dragging. A multi-finger drag gesture can be detected independently of both
+ a (default) single-finger DragHandler and a PinchHandler on the same Item,
+ and thus can be used to adjust some other feature independently of the
+ usual pinch behavior: for example adjust a tilt transformation, or adjust
+ some other numeric value, if the \c target is set to null. But if the
+ \l target is an Item, \l centroid is the point at which the drag begins and
+ to which the \c target will be moved (subject to constraints).
+
At this time, drag-and-drop is not yet supported.
\sa Drag, MouseArea
*/
-QQuickDragHandler::QQuickDragHandler(QObject *parent)
- : QQuickSinglePointHandler(parent)
-{
-}
-
-QQuickDragHandler::~QQuickDragHandler()
-{
-}
-
-bool QQuickDragHandler::wantsEventPoint(QQuickEventPoint *point)
+QQuickDragHandler::QQuickDragHandler(QQuickItem *parent)
+ : QQuickMultiPointHandler(parent, 1, 1)
{
- // If we've already been interested in a point, stay interested, even if it has strayed outside bounds.
- return ((point->state() != QQuickEventPoint::Pressed && this->point().id() == point->pointId())
- || QQuickSinglePointHandler::wantsEventPoint(point));
}
-bool QQuickDragHandler::targetContains(QQuickEventPoint *point)
+bool QQuickDragHandler::targetContainsCentroid()
{
Q_ASSERT(parentItem() && target());
- return target()->contains(localTargetPosition(point));
+ return target()->contains(targetCentroidPosition());
}
-QPointF QQuickDragHandler::localTargetPosition(QQuickEventPoint *point)
+QPointF QQuickDragHandler::targetCentroidPosition()
{
- QPointF pos = point->position();
+ QPointF pos = m_centroid.position();
if (target() != parentItem())
pos = parentItem()->mapToItem(target(), pos);
return pos;
}
-void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point)
+void QQuickDragHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point)
{
- if (grabber == this && stateChange == QQuickEventPoint::GrabExclusive) {
+ QQuickMultiPointHandler::onGrabChanged(grabber, transition, point);
+ if (grabber == this && transition == QQuickEventPoint::GrabExclusive) {
// In case the grab got handed over from another grabber, we might not get the Press.
if (!m_pressedInsideTarget) {
if (target())
m_pressTargetPos = QPointF(target()->width(), target()->height()) / 2;
- m_pressScenePos = point->scenePosition();
} else if (m_pressTargetPos.isNull()) {
if (target())
- m_pressTargetPos = localTargetPosition(point);
- m_pressScenePos = point->scenePosition();
+ m_pressTargetPos = targetCentroidPosition();
}
}
- QQuickSinglePointHandler::onGrabChanged(grabber, stateChange, point);
}
void QQuickDragHandler::onActiveChanged()
{
- if (!active()) {
+ QQuickMultiPointHandler::onActiveChanged();
+ if (active()) {
+ if (auto parent = parentItem()) {
+ if (currentEvent()->asPointerTouchEvent())
+ parent->setKeepTouchGrab(true);
+ // tablet and mouse are treated the same by Item's legacy event handling, and
+ // touch becomes synth-mouse for Flickable, so we need to prevent stealing
+ // mouse grab too, whenever dragging occurs in an enabled direction
+ parent->setKeepMouseGrab(true);
+ }
+ } else {
m_pressTargetPos = QPointF();
- m_pressScenePos = m_pressTargetPos;
m_pressedInsideTarget = false;
+ if (auto parent = parentItem()) {
+ parent->setKeepTouchGrab(false);
+ parent->setKeepMouseGrab(false);
+ }
}
}
-void QQuickDragHandler::handleEventPoint(QQuickEventPoint *point)
+void QQuickDragHandler::handlePointerEventImpl(QQuickPointerEvent *event)
{
- point->setAccepted();
- switch (point->state()) {
- case QQuickEventPoint::Pressed:
- if (target()) {
- m_pressedInsideTarget = targetContains(point);
- m_pressTargetPos = localTargetPosition(point);
- }
- m_pressScenePos = point->scenePosition();
- setPassiveGrab(point);
- break;
- case QQuickEventPoint::Updated: {
- QVector2D accumulatedDragDelta = QVector2D(point->scenePosition() - m_pressScenePos);
- if (active()) {
- // update translation property. Make sure axis is respected for it.
- if (!m_xAxis.enabled())
+ QQuickMultiPointHandler::handlePointerEventImpl(event);
+ event->setAccepted(true);
+
+ 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());
+ if (!m_xAxis.enabled())
+ accumulatedDragDelta.setX(0);
+ if (!m_yAxis.enabled())
+ accumulatedDragDelta.setY(0);
+ setTranslation(accumulatedDragDelta);
+ } else {
+ // Check that all points have been dragged past the drag threshold,
+ // to the extent that the constraints allow,
+ // and in approximately the same direction
+ qreal minAngle = 361;
+ qreal maxAngle = -361;
+ bool allOverThreshold = !event->isReleaseEvent();
+ QVector <QQuickEventPoint *> chosenPoints;
+
+ if (event->isPressEvent())
+ m_pressedInsideTarget = target() && m_currentPoints.count() > 0;
+
+ for (const QQuickHandlerPoint &p : m_currentPoints) {
+ if (!allOverThreshold)
+ break;
+ QQuickEventPoint *point = event->pointById(p.id());
+ chosenPoints << point;
+ setPassiveGrab(point);
+ // 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(point->scenePosition() - point->scenePressPosition());
+ if (!m_xAxis.enabled()) {
+ // If horizontal dragging is disallowed, but the user is dragging
+ // mostly horizontally, then don't activate.
+ if (qAbs(accumulatedDragDelta.x()) > qAbs(accumulatedDragDelta.y()))
+ accumulatedDragDelta.setY(0);
accumulatedDragDelta.setX(0);
- if (!m_yAxis.enabled())
+ }
+ if (!m_yAxis.enabled()) {
+ // If vertical dragging is disallowed, but the user is dragging
+ // mostly vertically, then don't activate.
+ if (qAbs(accumulatedDragDelta.y()) > qAbs(accumulatedDragDelta.x()))
+ accumulatedDragDelta.setX(0);
accumulatedDragDelta.setY(0);
- setTranslation(accumulatedDragDelta);
-
- if (target() && target()->parentItem()) {
- const QPointF newTargetTopLeft = localTargetPosition(point) - m_pressTargetPos;
- const QPointF xformOrigin = target()->transformOriginPoint();
- const QPointF targetXformOrigin = newTargetTopLeft + xformOrigin;
- QPointF pos = target()->parentItem()->mapFromItem(target(), targetXformOrigin);
- pos -= xformOrigin;
- QPointF targetItemPos = target()->position();
- if (!m_xAxis.enabled())
- pos.setX(targetItemPos.x());
- if (!m_yAxis.enabled())
- pos.setY(targetItemPos.y());
- enforceAxisConstraints(&pos);
- moveTarget(pos, point);
}
- } else if (!point->exclusiveGrabber() &&
- ((m_xAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(accumulatedDragDelta.x(), Qt::XAxis, point)) ||
- (m_yAxis.enabled() && QQuickWindowPrivate::dragOverThreshold(accumulatedDragDelta.y(), Qt::YAxis, point)))) {
- setExclusiveGrab(point);
- if (auto parent = parentItem()) {
- if (point->pointerEvent()->asPointerTouchEvent())
- parent->setKeepTouchGrab(true);
- // tablet and mouse are treated the same by Item's legacy event handling, and
- // touch becomes synth-mouse for Flickable, so we need to prevent stealing
- // mouse grab too, whenever dragging occurs in an enabled direction
- parent->setKeepMouseGrab(true);
+ qreal angle = std::atan2(accumulatedDragDelta.y(), accumulatedDragDelta.x()) * 180 / M_PI;
+ bool overThreshold = QQuickWindowPrivate::dragOverThreshold(accumulatedDragDelta);
+ qCDebug(lcDragHandler) << "movement" << accumulatedDragDelta << "angle" << angle << "of point" << point
+ << "pressed @" << point->scenePressPosition() << "over threshold?" << overThreshold;
+ minAngle = qMin(angle, minAngle);
+ maxAngle = qMax(angle, maxAngle);
+ if (allOverThreshold && !overThreshold)
+ allOverThreshold = false;
+
+ if (event->isPressEvent()) {
+ // m_pressedInsideTarget should stay true iff ALL points in which DragHandler is interested
+ // have been pressed inside the target() Item. (E.g. in a Slider the parent might be the
+ // whole control while the target is just the knob.)
+ if (target()) {
+ const QPointF localPressPos = target()->mapFromScene(point->scenePressPosition());
+ m_pressedInsideTarget &= target()->contains(localPressPos);
+ m_pressTargetPos = targetCentroidPosition();
+ }
+ // QQuickWindowPrivate::deliverToPassiveGrabbers() skips subsequent delivery if the event is filtered.
+ // (That affects behavior for mouse but not for touch, because Flickable only handles mouse.)
+ // So we have to compensate by accepting the event here to avoid any parent Flickable from
+ // getting the event via direct delivery and grabbing too soon.
+ point->setAccepted(event->asPointerMouseEvent()); // stop propagation iff it's a mouse event
}
}
- } break;
- default:
- break;
+ if (allOverThreshold) {
+ qreal angleDiff = maxAngle - minAngle;
+ if (angleDiff > 180)
+ angleDiff = 360 - angleDiff;
+ qCDebug(lcDragHandler) << "angle min" << minAngle << "max" << maxAngle << "range" << angleDiff;
+ if (angleDiff < DragAngleToleranceDegrees && grabPoints(chosenPoints))
+ setActive(true);
+ }
+ }
+ if (active() && target() && target()->parentItem()) {
+ const QPointF newTargetTopLeft = targetCentroidPosition() - m_pressTargetPos;
+ const QPointF xformOrigin = target()->transformOriginPoint();
+ const QPointF targetXformOrigin = newTargetTopLeft + xformOrigin;
+ QPointF pos = target()->parentItem()->mapFromItem(target(), targetXformOrigin);
+ pos -= xformOrigin;
+ QPointF targetItemPos = target()->position();
+ if (!m_xAxis.enabled())
+ pos.setX(targetItemPos.x());
+ if (!m_yAxis.enabled())
+ pos.setY(targetItemPos.y());
+ enforceAxisConstraints(&pos);
+ moveTarget(pos);
}
}
@@ -243,39 +301,6 @@ void QQuickDragHandler::setTranslation(const QVector2D &trans)
applied to the \l {PointerHandler::target} {target}.
If \c enabled is true, vertical dragging is allowed.
*/
-QQuickDragAxis::QQuickDragAxis()
- : m_minimum(-DBL_MAX)
- , m_maximum(DBL_MAX)
- , m_enabled(true)
-{
-}
-
-void QQuickDragAxis::setMinimum(qreal minimum)
-{
- if (m_minimum == minimum)
- return;
-
- m_minimum = minimum;
- emit minimumChanged();
-}
-
-void QQuickDragAxis::setMaximum(qreal maximum)
-{
- if (m_maximum == maximum)
- return;
-
- m_maximum = maximum;
- emit maximumChanged();
-}
-
-void QQuickDragAxis::setEnabled(bool enabled)
-{
- if (m_enabled == enabled)
- return;
-
- m_enabled = enabled;
- emit enabledChanged();
-}
/*!
\readonly
diff --git a/src/quick/handlers/qquickdraghandler_p.h b/src/quick/handlers/qquickdraghandler_p.h
index 363df31a64..387a81eb43 100644
--- a/src/quick/handlers/qquickdraghandler_p.h
+++ b/src/quick/handlers/qquickdraghandler_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -51,41 +51,12 @@
// We mean it.
//
-#include "qquicksinglepointhandler_p.h"
+#include "qquickmultipointhandler_p.h"
+#include "qquickdragaxis_p.h"
QT_BEGIN_NAMESPACE
-class Q_AUTOTEST_EXPORT QQuickDragAxis : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(qreal minimum READ minimum WRITE setMinimum NOTIFY minimumChanged)
- Q_PROPERTY(qreal maximum READ maximum WRITE setMaximum NOTIFY maximumChanged)
- Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
-
-public:
- QQuickDragAxis();
-
- qreal minimum() const { return m_minimum; }
- void setMinimum(qreal minimum);
-
- qreal maximum() const { return m_maximum; }
- void setMaximum(qreal maximum);
-
- bool enabled() const { return m_enabled; }
- void setEnabled(bool enabled);
-
-signals:
- void minimumChanged();
- void maximumChanged();
- void enabledChanged();
-
-private:
- qreal m_minimum;
- qreal m_maximum;
- bool m_enabled;
-};
-
-class Q_AUTOTEST_EXPORT QQuickDragHandler : public QQuickSinglePointHandler
+class Q_AUTOTEST_EXPORT QQuickDragHandler : public QQuickMultiPointHandler
{
Q_OBJECT
Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT)
@@ -93,10 +64,9 @@ class Q_AUTOTEST_EXPORT QQuickDragHandler : public QQuickSinglePointHandler
Q_PROPERTY(QVector2D translation READ translation NOTIFY translationChanged)
public:
- explicit QQuickDragHandler(QObject *parent = nullptr);
- ~QQuickDragHandler();
+ explicit QQuickDragHandler(QQuickItem *parent = nullptr);
- void handleEventPoint(QQuickEventPoint *point) override;
+ void handlePointerEventImpl(QQuickPointerEvent *event) override;
QQuickDragAxis *xAxis() { return &m_xAxis; }
QQuickDragAxis *yAxis() { return &m_yAxis; }
@@ -104,25 +74,22 @@ public:
QVector2D translation() const { return m_translation; }
void setTranslation(const QVector2D &trans);
- Q_INVOKABLE void enforceConstraints();
+ void enforceConstraints();
Q_SIGNALS:
-// void gestureStarted(QQuickGestureEvent *gesture);
void translationChanged();
protected:
- bool wantsEventPoint(QQuickEventPoint *point) override;
void onActiveChanged() override;
- void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override;
+ void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point) override;
private:
void ungrab();
void enforceAxisConstraints(QPointF *localPos);
- bool targetContains(QQuickEventPoint *point);
- QPointF localTargetPosition(QQuickEventPoint *point);
+ bool targetContainsCentroid();
+ QPointF targetCentroidPosition();
private:
- QPointF m_pressScenePos;
QPointF m_pressTargetPos; // We must also store the local targetPos, because we cannot deduce
// the press target pos from the scene pos in case there was e.g a
// flick in one of the ancestors during the drag.
diff --git a/src/quick/handlers/qquickhandlerpoint.cpp b/src/quick/handlers/qquickhandlerpoint.cpp
new file mode 100644
index 0000000000..6a106b5464
--- /dev/null
+++ b/src/quick/handlers/qquickhandlerpoint.cpp
@@ -0,0 +1,352 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickhandlerpoint_p.h"
+#include "private/qquickevents_p_p.h"
+
+QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(DBG_TOUCH_TARGET)
+
+/*!
+ \qmltype HandlerPoint
+ \instantiates QQuickHandlerPoint
+ \inqmlmodule QtQuick
+ \brief An event point.
+
+ A QML representation of a QQuickEventPoint.
+
+ It's possible to make bindings to properties of a \l SinglePointHandler's
+ current point. For example:
+
+ \snippet pointerHandlers/dragHandlerNullTarget.qml 0
+
+ The point is kept up-to-date when the DragHandler is actively responding to
+ an EventPoint; but when the point is released, or the current point is
+ being handled by a different handler, \c position.x and \c position.y are 0.
+
+ \note This is practically identical to QtQuick::EventPoint; however an
+ EventPoint is a long-lived QObject which is invalidated between gestures
+ and reused for subsequent event deliveries. Continuous bindings to its
+ properties are not possible, and an individual handler cannot rely on it
+ outside the period when that point is part of an active gesture which that
+ handler is handling. HandlerPoint is a Q_GADGET that the handler owns.
+ This allows you to make lifetime bindings to its properties.
+
+ \sa SinglePointHandler::point
+*/
+
+QQuickHandlerPoint::QQuickHandlerPoint()
+{}
+
+void QQuickHandlerPoint::localize(QQuickItem *item)
+{
+ m_pressPosition = item->mapFromScene(m_scenePressPosition);
+}
+
+void QQuickHandlerPoint::reset()
+{
+ m_id = 0;
+ m_uniqueId = QPointingDeviceUniqueId();
+ m_position = QPointF();
+ m_scenePosition = QPointF();
+ m_pressPosition = QPointF();
+ m_scenePressPosition = QPointF();
+ m_sceneGrabPosition = QPointF();
+ m_velocity = QVector2D();
+ m_rotation = 0;
+ m_pressure = 0;
+ m_ellipseDiameters = QSizeF();
+ m_pressedButtons = Qt::NoButton;
+ m_pressedModifiers = Qt::NoModifier;
+}
+
+void QQuickHandlerPoint::reset(const QQuickEventPoint *point)
+{
+ m_id = point->pointId();
+ const QQuickPointerEvent *event = point->pointerEvent();
+ switch (point->state()) {
+ case QQuickEventPoint::Pressed:
+ m_pressPosition = point->position();
+ m_scenePressPosition = point->scenePosition();
+ m_pressedButtons = event->buttons();
+ break;
+ case QQuickEventPoint::Released:
+ if (event->buttons() == Qt::NoButton) {
+ reset();
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ m_scenePressPosition = point->scenePressPosition();
+ m_pressedButtons = event->buttons();
+ m_pressedModifiers = event->modifiers();
+ if (event->asPointerTouchEvent()) {
+ const QQuickEventTouchPoint *tp = static_cast<const QQuickEventTouchPoint *>(point);
+ m_uniqueId = tp->uniqueId();
+ m_rotation = tp->rotation();
+ m_pressure = tp->pressure();
+ m_ellipseDiameters = tp->ellipseDiameters();
+ } else if (event->asPointerTabletEvent()) {
+ // TODO
+ } else {
+ m_uniqueId = event->device()->uniqueId();
+ m_rotation = 0;
+ m_pressure = event->buttons() ? 1 : 0;
+ m_ellipseDiameters = QSizeF();
+ }
+ m_position = point->position();
+ m_scenePosition = point->scenePosition();
+ if (point->state() == QQuickEventPoint::Updated)
+ m_velocity = point->velocity();
+}
+
+void QQuickHandlerPoint::reset(const QVector<QQuickHandlerPoint> &points)
+{
+ if (points.isEmpty()) {
+ qWarning("reset: no points");
+ return;
+ }
+ if (points.count() == 1) {
+ *this = points.first(); // copy all values
+ return;
+ }
+ // all points are required to be from the same event
+ QPointF posSum;
+ QPointF scenePosSum;
+ QPointF pressPosSum;
+ QPointF scenePressPosSum;
+ QVector2D velocitySum;
+ qreal pressureSum = 0;
+ QSizeF ellipseDiameterSum;
+ for (const QQuickHandlerPoint &point : points) {
+ posSum += point.position();
+ scenePosSum += point.scenePosition();
+ pressPosSum += point.pressPosition();
+ scenePressPosSum += point.scenePressPosition();
+ velocitySum += point.velocity();
+ pressureSum += point.pressure();
+ ellipseDiameterSum += point.ellipseDiameters();
+ }
+ m_id = 0;
+ m_uniqueId = QPointingDeviceUniqueId();
+ // all points are required to be from the same event, so pressed buttons and modifiers should be the same
+ m_pressedButtons = points.first().pressedButtons();
+ m_pressedModifiers = points.first().modifiers();
+ m_position = posSum / points.size();
+ m_scenePosition = scenePosSum / points.size();
+ m_pressPosition = pressPosSum / points.size();
+ m_scenePressPosition = scenePressPosSum / points.size();
+ m_velocity = velocitySum / points.size();
+ m_rotation = 0; // averaging the rotations of all the points isn't very sensible
+ m_pressure = pressureSum / points.size();
+ m_ellipseDiameters = ellipseDiameterSum / points.size();
+}
+
+/*!
+ \readonly
+ \qmlproperty int QtQuick::HandlerPoint::id
+ \brief The ID number of the point
+
+ During a touch gesture, from the time that the first finger is pressed
+ until the last finger is released, each touchpoint will have a unique ID
+ number. Likewise, if input from multiple devices occurs (for example
+ simultaneous mouse and touch presses), all the current event points from
+ all the devices will have unique IDs.
+
+ \note Do not assume that id numbers start at zero or that they are
+ sequential. Such an assumption is often false due to the way the underlying
+ drivers work.
+
+ \sa QTouchEvent::TouchPoint::id
+*/
+
+/*!
+ \readonly
+ \qmlproperty PointingDeviceUniqueId QtQuick::HandlerPoint::uniqueId
+ \brief The unique ID of the point, if any
+
+ This is normally empty, because touchscreens cannot uniquely identify fingers.
+
+ On some types of touchscreens, especially those using TUIO drivers,
+ it's possible to use recognizable physical tokens (fiducial objects)
+ in addition to fingers. So if this point is a touch point, and
+ uniqueId is set, it is the identifier for such an object.
+
+ On a graphics tablet, each type of stylus or other tool often has a unique
+ ID or serial number, which can be useful to respond in different ways to
+ different tools.
+
+ Interpreting the contents of this ID requires knowledge of the hardware and
+ drivers in use.
+
+ \sa QTabletEvent::uniqueId, QtQuick::TouchPoint::uniqueId, QtQuick::EventTouchPoint::uniqueId
+*/
+
+/*!
+ \readonly
+ \qmlproperty QPointF QtQuick::HandlerPoint::position
+ \brief The position within the \c parent Item
+
+ This is the position of the event point relative to the bounds of
+ the \l {PointerHandler::parent} {parent}.
+*/
+
+/*!
+ \readonly
+ \qmlproperty QPointF QtQuick::HandlerPoint::scenePosition
+ \brief The position within the scene
+
+ This is the position of the event point relative to the bounds of the Qt
+ Quick scene (typically the whole window).
+*/
+
+/*!
+ \readonly
+ \qmlproperty QPointF QtQuick::HandlerPoint::pressPosition
+ \brief The pressed position within the \c parent Item
+
+ This is the position at which this point was pressed, relative to the
+ bounds of the \l {PointerHandler::parent} {parent}.
+*/
+
+/*!
+ \readonly
+ \qmlproperty QPointF QtQuick::HandlerPoint::scenePressPosition
+ \brief The pressed position within the scene
+
+ This is the position at which this point was pressed, in the coordinate
+ system of the \l {Qt Quick Scene Graph}{scene graph}.
+*/
+
+/*!
+ \readonly
+ \qmlproperty QPointF QtQuick::HandlerPoint::sceneGrabPosition
+ \brief The grabbed position within the scene
+
+ If this point has been grabbed by a Pointer Handler or an Item, it means
+ that object has taken sole responsibility for handling the movement and the
+ release if this point. In that case, this is the position at which the grab
+ occurred, in the coordinate system of the \l {Qt Quick Scene Graph}{scene graph}.
+*/
+
+/*!
+ \readonly
+ \qmlproperty enumeration QtQuick::HandlerPoint::pressedButtons
+ \brief Which mouse or stylus buttons are currently pressed
+
+ \sa MouseArea::pressedButtons
+*/
+
+/*!
+ \readonly
+ \qmlproperty enumeration QtQuick::HandlerPoint::modifiers
+ \brief Which modifier keys are currently pressed
+
+ This property holds the keyboard modifiers that were pressed at the time
+ the event occurred.
+
+ \sa MouseArea::modifiers
+*/
+
+/*!
+ \readonly
+ \qmlproperty QVector2D QtQuick::HandlerPoint::velocity
+ \brief A vector representing the average speed and direction of movement
+
+ This is a velocity vector pointing in the direction of movement, in logical
+ pixels per second. It has x and y components, at least one of which will be
+ nonzero when this point is in motion. It holds the average recent velocity:
+ how fast and in which direction the event point has been moving recently.
+
+ \sa QtQuick::EventPoint::velocity, QtQuick::TouchPoint::velocity, QTouchEvent::TouchPoint::velocity
+*/
+
+/*!
+ \readonly
+ \qmlproperty qreal QtQuick::HandlerPoint::rotation
+
+ This property holds the rotation angle of the stylus on a graphics tablet
+ or the contact patch of a touchpoint on a touchscreen.
+
+ It is valid only with certain tablet stylus devices and touchscreens that
+ can measure the rotation angle. Otherwise, it will be zero.
+*/
+
+/*!
+ \readonly
+ \qmlproperty qreal QtQuick::HandlerPoint::pressure
+
+ This property tells how hard the user is pressing the stylus on a graphics
+ tablet or the finger against a touchscreen, in the range from \c 0 (no
+ measurable pressure) to \c 1.0 (maximum pressure which the device can
+ measure).
+
+ It is valid only with certain tablets and touchscreens that can measure
+ pressure. Otherwise, it will be zero.
+*/
+
+/*!
+ \readonly
+ \qmlproperty size QtQuick::HandlerPoint::ellipseDiameters
+
+ This property holds the diameters of the contact patch, if the event
+ comes from a touchpoint and the device provides this information.
+
+ A touchpoint is modeled as an elliptical area where the finger is pressed
+ against the touchscreen. (In fact, it could also be modeled as a bitmap;
+ but in that case we expect an elliptical bounding estimate to be fitted to
+ the contact patch before the event is sent.) The harder the user presses,
+ the larger the contact patch; so, these diameters provide an alternate way
+ of detecting pressure, in case the device does not include a separate
+ pressure sensor. The ellipse is centered on \l scenePosition (\l position
+ in the PointerHandler's Item's local coordinates). The \l rotation property
+ provides the rotation of the ellipse, if known. It is expected that if the
+ \l rotation is zero, the \l {QSize::height}{height} is the larger dimension
+ (the major axis), because of the usual hand position, reaching upward or
+ outward across the surface.
+
+ If the contact patch is unknown, or the device is not a touchscreen,
+ these values will be zero.
+
+ \sa QtQuick::EventTouchPoint::ellipseDiameters, QtQuick::TouchPoint::ellipseDiameters, QTouchEvent::TouchPoint::ellipseDiameters
+*/
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickhandlerpoint_p.h b/src/quick/handlers/qquickhandlerpoint_p.h
new file mode 100644
index 0000000000..44fd830af2
--- /dev/null
+++ b/src/quick/handlers/qquickhandlerpoint_p.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKHANDLERPOINT_H
+#define QQUICKHANDLERPOINT_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 "qquickpointerdevicehandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickMultiPointHandler;
+class QQuickSinglePointHandler;
+
+class Q_QUICK_PRIVATE_EXPORT QQuickHandlerPoint {
+ Q_GADGET
+ Q_PROPERTY(int id READ id)
+ Q_PROPERTY(QPointingDeviceUniqueId uniqueId READ uniqueId)
+ Q_PROPERTY(QPointF position READ position)
+ Q_PROPERTY(QPointF scenePosition READ scenePosition)
+ Q_PROPERTY(QPointF pressPosition READ pressPosition)
+ Q_PROPERTY(QPointF scenePressPosition READ scenePressPosition)
+ Q_PROPERTY(QPointF sceneGrabPosition READ sceneGrabPosition)
+ Q_PROPERTY(Qt::MouseButtons pressedButtons READ pressedButtons)
+ Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers)
+ Q_PROPERTY(QVector2D velocity READ velocity)
+ Q_PROPERTY(qreal rotation READ rotation)
+ Q_PROPERTY(qreal pressure READ pressure)
+ Q_PROPERTY(QSizeF ellipseDiameters READ ellipseDiameters)
+
+public:
+ QQuickHandlerPoint();
+
+ int id() const { return m_id; }
+ Qt::MouseButtons pressedButtons() const { return m_pressedButtons; }
+ Qt::KeyboardModifiers modifiers() const { return m_pressedModifiers; }
+ QPointF pressPosition() const { return m_pressPosition; }
+ QPointF scenePressPosition() const { return m_scenePressPosition; }
+ QPointF sceneGrabPosition() const { return m_sceneGrabPosition; }
+ QPointF position() const { return m_position; }
+ QPointF scenePosition() const { return m_scenePosition; }
+ QVector2D velocity() const { return m_velocity; }
+ qreal rotation() const { return m_rotation; }
+ qreal pressure() const { return m_pressure; }
+ QSizeF ellipseDiameters() const { return m_ellipseDiameters; }
+ QPointingDeviceUniqueId uniqueId() const { return m_uniqueId; }
+ void localize(QQuickItem *item);
+
+ void reset();
+ void reset(const QQuickEventPoint *point);
+ void reset(const QVector<QQuickHandlerPoint> &points);
+
+private:
+ int m_id = 0;
+ QPointingDeviceUniqueId m_uniqueId;
+ Qt::MouseButtons m_pressedButtons = Qt::NoButton;
+ Qt::KeyboardModifiers m_pressedModifiers = Qt::NoModifier;
+ QPointF m_position;
+ QPointF m_scenePosition;
+ QPointF m_pressPosition;
+ QPointF m_scenePressPosition;
+ QPointF m_sceneGrabPosition;
+ QVector2D m_velocity;
+ qreal m_rotation = 0;
+ qreal m_pressure = 0;
+ QSizeF m_ellipseDiameters;
+ friend class QQuickMultiPointHandler;
+ friend class QQuickSinglePointHandler;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickHandlerPoint)
+
+#endif // QQUICKHANDLERPOINT_H
diff --git a/src/quick/handlers/qquickhandlersmodule.cpp b/src/quick/handlers/qquickhandlersmodule.cpp
deleted file mode 100644
index c9e08fe4f2..0000000000
--- a/src/quick/handlers/qquickhandlersmodule.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQuick module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qquickhandlersmodule_p.h"
-#include "qquickpointerhandler_p.h"
-#include "qquickdraghandler_p.h"
-#include "qquickpinchhandler_p.h"
-#include "qquickpointhandler_p.h"
-#include "qquicktaphandler_p.h"
-
-static void initResources()
-{
-#ifdef QT_STATIC
- Q_INIT_RESOURCE(qmake_Qt_labs_handlers);
-#endif
-}
-
-QT_BEGIN_NAMESPACE
-
-static QQmlPrivate::AutoParentResult handler_autoParent(QObject *obj, QObject *parent)
-{
- if (qmlobject_cast<QQuickItem *>(parent)) {
- QQuickPointerHandler *handler = qmlobject_cast<QQuickPointerHandler *>(obj);
- if (handler) {
- handler->setParent(parent);
- return QQmlPrivate::Parented;
- }
- }
- return QQmlPrivate::IncompatibleObject;
-}
-
-static void qt_quickhandlers_defineModule(const char *uri, int major, int minor)
-{
- QQmlPrivate::RegisterAutoParent autoparent = { 0, &handler_autoParent };
- QQmlPrivate::qmlregister(QQmlPrivate::AutoParentRegistration, &autoparent);
- qmlRegisterUncreatableType<QQuickPointerEvent>(uri, major, minor, "PointerEvent",
- QQuickPointerHandler::tr("PointerEvent is only available as a parameter of several signals in PointerHandler"));
- qmlRegisterUncreatableType<QQuickEventPoint>(uri, major, minor, "EventPoint",
- QQuickPointerHandler::tr("EventPoint is only available as a member of PointerEvent"));
- qmlRegisterUncreatableType<QQuickEventTouchPoint>(uri, major, minor, "EventTouchPoint",
- QQuickPointerHandler::tr("EventTouchPoint is only available as a member of PointerEvent"));
- qmlRegisterUncreatableType<QQuickPointerDevice>(uri, major, minor, "PointerDevice",
- QQuickPointerHandler::tr("PointerDevice is only available as a property of PointerEvent"));
- qRegisterMetaType<QPointingDeviceUniqueId>("QPointingDeviceUniqueId");
- qmlRegisterUncreatableType<QPointingDeviceUniqueId>(uri, major, minor, "PointingDeviceUniqueId",
- QQuickPointerHandler::tr("PointingDeviceUniqueId is only available as a property of PointerEvent"));
-
- qmlRegisterUncreatableType<QQuickPointerHandler>(uri,major,minor,"PointerHandler",
- QQuickPointerHandler::tr("PointerHandler is an abstract base class"));
- qmlRegisterType<QQuickPointHandler>(uri,major,minor,"PointHandler");
- qmlRegisterType<QQuickDragHandler>(uri,major,minor,"DragHandler");
- qmlRegisterUncreatableType<QQuickDragAxis>(uri, major, minor, "DragAxis",
- QQuickDragHandler::tr("DragAxis is only available as a grouped property of DragHandler"));
- qmlRegisterType<QQuickPinchHandler>(uri,major,minor,"PinchHandler");
- qmlRegisterType<QQuickTapHandler>(uri,major,minor,"TapHandler");
- qRegisterMetaType<QQuickHandlerPoint>();
-}
-
-void QQuickHandlersModule::defineModule()
-{
- initResources();
-
- const char uri[] = "Qt.labs.handlers";
- int majorVersion = 1;
- int minorVersion = 0;
-
- qt_quickhandlers_defineModule(uri, majorVersion, minorVersion);
-}
-
-QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickhoverhandler.cpp b/src/quick/handlers/qquickhoverhandler.cpp
new file mode 100644
index 0000000000..61955cad03
--- /dev/null
+++ b/src/quick/handlers/qquickhoverhandler.cpp
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickhoverhandler_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcHoverHandler, "qt.quick.handler.hover")
+
+/*!
+ \qmltype HoverHandler
+ \instantiates QQuickHoverHandler
+ \inqmlmodule QtQuick
+ \ingroup qtquick-input-handlers
+ \brief Handler for mouse and tablet hover.
+
+ HoverHandler detects a hovering cursor. Since touchscreens don't generally
+ offer hover events, in practice it detects a hovering mouse or tablet stylus.
+
+ \sa MouseArea
+*/
+
+QQuickHoverHandler::QQuickHoverHandler(QQuickItem *parent)
+ : QQuickSinglePointHandler(parent)
+{
+ // Rule out the touchscreen for now (can be overridden in QML in case a hover-detecting touchscreen exists)
+ setAcceptedDevices(static_cast<QQuickPointerDevice::DeviceType>(
+ static_cast<int>(QQuickPointerDevice::AllDevices) ^ static_cast<int>(QQuickPointerDevice::TouchScreen)));
+}
+
+QQuickHoverHandler::~QQuickHoverHandler()
+{
+ if (auto parent = parentItem())
+ QQuickItemPrivate::get(parent)->setHasHoverInChild(false);
+}
+
+void QQuickHoverHandler::componentComplete()
+{
+ parentItem()->setAcceptHoverEvents(true);
+ QQuickItemPrivate::get(parentItem())->setHasHoverInChild(true);
+}
+
+bool QQuickHoverHandler::wantsPointerEvent(QQuickPointerEvent *event)
+{
+ QQuickEventPoint *point = event->point(0);
+ if (QQuickPointerDeviceHandler::wantsPointerEvent(event) && wantsEventPoint(point) && parentContains(point)) {
+ // assume this is a mouse event, so there's only one point
+ setPointId(point->pointId());
+ return true;
+ }
+ setHovered(false);
+ return false;
+}
+
+void QQuickHoverHandler::handleEventPoint(QQuickEventPoint *point)
+{
+ setHovered(true);
+ setPassiveGrab(point);
+}
+
+void QQuickHoverHandler::setHovered(bool hovered)
+{
+ if (m_hovered != hovered) {
+ qCDebug(lcHoverHandler) << objectName() << "hovered" << m_hovered << "->" << hovered;
+ m_hovered = hovered;
+ emit hoveredChanged();
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickhoverhandler_p.h b/src/quick/handlers/qquickhoverhandler_p.h
new file mode 100644
index 0000000000..1ee2aeb7e6
--- /dev/null
+++ b/src/quick/handlers/qquickhoverhandler_p.h
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKHOVERHANDLER_H
+#define QQUICKHOVERHANDLER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickitem.h"
+#include "qevent.h"
+#include "qquicksinglepointhandler_p.h"
+#include <QtCore/qbasictimer.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQuickHoverHandler : public QQuickSinglePointHandler
+{
+ Q_OBJECT
+ Q_PROPERTY(bool hovered READ isHovered NOTIFY hoveredChanged)
+
+public:
+ explicit QQuickHoverHandler(QQuickItem *parent = nullptr);
+ ~QQuickHoverHandler();
+
+ bool isHovered() const { return m_hovered; }
+
+Q_SIGNALS:
+ void hoveredChanged();
+
+protected:
+ void componentComplete() override;
+ bool wantsPointerEvent(QQuickPointerEvent *event) override;
+ void handleEventPoint(QQuickEventPoint *point) override;
+
+private:
+ void setHovered(bool hovered);
+
+private:
+ bool m_hovered = false;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickHoverHandler)
+
+#endif // QQUICKHOVERHANDLER_H
diff --git a/src/quick/handlers/qquickmultipointhandler.cpp b/src/quick/handlers/qquickmultipointhandler.cpp
index d595b4c9b4..2f4e9d45e9 100644
--- a/src/quick/handlers/qquickmultipointhandler.cpp
+++ b/src/quick/handlers/qquickmultipointhandler.cpp
@@ -51,23 +51,17 @@ QT_BEGIN_NAMESPACE
\preliminary
\instantiates QQuickMultiPointHandler
\inherits PointerDeviceHandler
- \inqmlmodule Qt.labs.handlers
- \ingroup qtquick-handlers
+ \inqmlmodule QtQuick
\brief Abstract handler for multi-point Pointer Events.
An intermediate class (not registered as a QML type)
for any type of handler which requires and acts upon a specific number
of multiple touchpoints.
*/
-QQuickMultiPointHandler::QQuickMultiPointHandler(QObject *parent, int minimumPointCount)
+QQuickMultiPointHandler::QQuickMultiPointHandler(QQuickItem *parent, int minimumPointCount, int maximumPointCount)
: QQuickPointerDeviceHandler(parent)
, m_minimumPointCount(minimumPointCount)
- , m_maximumPointCount(-1)
- , m_pointDistanceThreshold(0)
-{
-}
-
-QQuickMultiPointHandler::~QQuickMultiPointHandler()
+ , m_maximumPointCount(maximumPointCount)
{
}
@@ -81,34 +75,99 @@ bool QQuickMultiPointHandler::wantsPointerEvent(QQuickPointerEvent *event)
return true;
#endif
- if (sameAsCurrentPoints(event))
- return true;
+ if (event->asPointerScrollEvent())
+ return false;
+ // If points were pressed or released within parentItem, reset stored state
+ // and check eligible points again. This class of handlers is intended to
+ // 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
+ // not want to reshuffle the order either).
const QVector<QQuickEventPoint *> candidatePoints = eligiblePoints(event);
+ if (candidatePoints.count() != m_currentPoints.count()) {
+ m_currentPoints.clear();
+ if (active()) {
+ setActive(false);
+ m_centroid.reset();
+ emit centroidChanged();
+ }
+ } else if (hasCurrentPoints(event)) {
+ return true;
+ }
+
const bool ret = (candidatePoints.size() >= minimumPointCount() && candidatePoints.size() <= maximumPointCount());
- if (ret)
- m_currentPoints = candidatePoints;
+ if (ret) {
+ const int c = candidatePoints.count();
+ m_currentPoints.resize(c);
+ for (int i = 0; i < c; ++i) {
+ m_currentPoints[i].reset(candidatePoints[i]);
+ m_currentPoints[i].localize(parentItem());
+ }
+ } else {
+ m_currentPoints.clear();
+ }
return ret;
}
+void QQuickMultiPointHandler::handlePointerEventImpl(QQuickPointerEvent *event)
+{
+ QQuickPointerHandler::handlePointerEventImpl(event);
+ // event's points can be reordered since the previous event, which is why m_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) {
+ 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
+ emit centroidChanged();
+}
+
+void QQuickMultiPointHandler::onActiveChanged()
+{
+ if (active()) {
+ m_centroid.m_sceneGrabPosition = m_centroid.m_scenePosition;
+ } else {
+ // Don't call m_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;
+ }
+}
+
+void QQuickMultiPointHandler::onGrabChanged(QQuickPointerHandler *, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *)
+{
+ // 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();
+}
+
QVector<QQuickEventPoint *> QQuickMultiPointHandler::eligiblePoints(QQuickPointerEvent *event)
{
QVector<QQuickEventPoint *> ret;
int c = event->pointCount();
- QRectF parentBounds = parentItem()->mapRectToScene(parentItem()->boundingRect())
- .marginsAdded(QMarginsF(m_pointDistanceThreshold, m_pointDistanceThreshold, m_pointDistanceThreshold, m_pointDistanceThreshold));
// If one or more points are newly pressed or released, all non-released points are candidates for this handler.
- // In other cases however, do not steal the grab: that is, if a point has a grabber,
- // it's not a candidate for this handler.
+ // In other cases however, check whether it would be OK to steal the grab if the handler chooses to do that.
bool stealingAllowed = event->isPressEvent() || event->isReleaseEvent();
for (int i = 0; i < c; ++i) {
QQuickEventPoint *p = event->point(i);
if (!stealingAllowed) {
QObject *exclusiveGrabber = p->exclusiveGrabber();
- if (exclusiveGrabber && exclusiveGrabber != this)
+ if (exclusiveGrabber && exclusiveGrabber != this && !canGrab(p))
continue;
}
- if (p->state() != QQuickEventPoint::Released && parentBounds.contains(p->scenePosition()))
+ if (p->state() != QQuickEventPoint::Released && wantsEventPoint(p))
ret << p;
}
return ret;
@@ -122,7 +181,7 @@ QVector<QQuickEventPoint *> QQuickMultiPointHandler::eligiblePoints(QQuickPointe
If a smaller number of touchpoints are in contact with the
\l {PointerHandler::parent}{parent}, they will be ignored.
- Any ignored points are eligible to activate other Pointer Handlers that
+ Any ignored points are eligible to activate other Input Handlers that
have different constraints, on the same Item or on other Items.
The default value is 2.
@@ -148,7 +207,7 @@ void QQuickMultiPointHandler::setMinimumPointCount(int c)
chosen in the order that they are pressed, and the remaining points will
be ignored.
- Any ignored points are eligible to activate other Pointer Handlers that
+ Any ignored points are eligible to activate other Input Handlers that
have different constraints, on the same Item or on other Items.
The default value is the same as \l minimumPointCount.
@@ -162,70 +221,20 @@ void QQuickMultiPointHandler::setMaximumPointCount(int maximumPointCount)
emit maximumPointCountChanged();
}
-/*!
- \qmlproperty real MultiPointHandler::pointDistanceThreshold
-
- The margin beyond the bounds of the \l {PointerHandler::parent}{parent}
- item within which a touch point can activate this handler. For example, on
- a PinchHandler where the \l {PointerHandler::target}{target} is also the
- \c parent, it's useful to set this to a distance at least half the width
- of a typical user's finger, so that if the \c parent has been scaled down
- to a very small size, the pinch gesture is still possible.
-
- The default value is 0.
-
- \image pointDistanceThreshold.png
-*/
-void QQuickMultiPointHandler::setPointDistanceThreshold(qreal pointDistanceThreshold)
-{
- if (m_pointDistanceThreshold == pointDistanceThreshold)
- return;
-
- m_pointDistanceThreshold = pointDistanceThreshold;
- emit pointDistanceThresholdChanged();
-}
-
-bool QQuickMultiPointHandler::sameAsCurrentPoints(QQuickPointerEvent *event)
+bool QQuickMultiPointHandler::hasCurrentPoints(QQuickPointerEvent *event)
{
- bool ret = true;
- int c = event->pointCount();
- if (c != m_currentPoints.size())
+ if (event->pointCount() < m_currentPoints.size() || m_currentPoints.size() == 0)
return false;
// TODO optimize: either ensure the points are sorted,
// or use std::equal with a predicate
- for (int i = 0; ret && i < c; ++i) {
- if (event->point(i)->state() == QQuickEventPoint::Released)
+ for (const QQuickHandlerPoint &p : qAsConst(m_currentPoints)) {
+ const QQuickEventPoint *ep = event->pointById(p.id());
+ if (!ep)
+ return false;
+ if (ep->state() == QQuickEventPoint::Released)
return false;
- bool found = false;
- int pointId = event->point(i)->pointId();
- for (QQuickEventPoint *o : qAsConst(m_currentPoints))
- if (o && pointId == o->pointId())
- found = true;
- if (!found)
- ret = false;
}
- return ret;
-}
-
-// TODO make templates for these functions somehow?
-QPointF QQuickMultiPointHandler::touchPointCentroid()
-{
- QPointF ret;
- if (Q_UNLIKELY(m_currentPoints.size() == 0))
- return ret;
- for (QQuickEventPoint *point : qAsConst(m_currentPoints))
- ret += point->scenePosition();
- return ret / m_currentPoints.size();
-}
-
-QVector2D QQuickMultiPointHandler::touchPointCentroidVelocity()
-{
- QVector2D ret;
- if (Q_UNLIKELY(m_currentPoints.size() == 0))
- return ret;
- for (QQuickEventPoint *point : qAsConst(m_currentPoints))
- ret += point->velocity();
- return ret / m_currentPoints.size();
+ return true;
}
qreal QQuickMultiPointHandler::averageTouchPointDistance(const QPointF &ref)
@@ -233,8 +242,8 @@ qreal QQuickMultiPointHandler::averageTouchPointDistance(const QPointF &ref)
qreal ret = 0;
if (Q_UNLIKELY(m_currentPoints.size() == 0))
return ret;
- for (QQuickEventPoint *point : qAsConst(m_currentPoints))
- ret += QVector2D(point->scenePosition() - ref).length();
+ for (const QQuickHandlerPoint &p : m_currentPoints)
+ ret += QVector2D(p.scenePosition() - ref).length();
return ret / m_currentPoints.size();
}
@@ -244,8 +253,8 @@ qreal QQuickMultiPointHandler::averageStartingDistance(const QPointF &ref)
qreal ret = 0;
if (Q_UNLIKELY(m_currentPoints.size() == 0))
return ret;
- for (QQuickEventPoint *point : qAsConst(m_currentPoints))
- ret += QVector2D(point->sceneGrabPosition() - ref).length();
+ for (const QQuickHandlerPoint &p : m_currentPoints)
+ ret += QVector2D(p.sceneGrabPosition() - ref).length();
return ret / m_currentPoints.size();
}
@@ -253,9 +262,9 @@ QVector<QQuickMultiPointHandler::PointData> QQuickMultiPointHandler::angles(cons
{
QVector<PointData> angles;
angles.reserve(m_currentPoints.count());
- for (QQuickEventPoint *point : qAsConst(m_currentPoints)) {
- qreal angle = QLineF(ref, point->scenePosition()).angle();
- angles.append(PointData(point->pointId(), -angle)); // convert to clockwise, to be consistent with QQuickItem::rotation
+ for (const QQuickHandlerPoint &p : m_currentPoints) {
+ qreal angle = QLineF(ref, p.scenePosition()).angle();
+ angles.append(PointData(p.id(), -angle)); // convert to clockwise, to be consistent with QQuickItem::rotation
}
return angles;
}
@@ -302,7 +311,7 @@ bool QQuickMultiPointHandler::grabPoints(QVector<QQuickEventPoint *> points)
{
bool allowed = true;
for (QQuickEventPoint* point : points) {
- if (!canGrab(point)) {
+ if (point->exclusiveGrabber() != this && !canGrab(point)) {
allowed = false;
break;
}
@@ -314,4 +323,19 @@ bool QQuickMultiPointHandler::grabPoints(QVector<QQuickEventPoint *> points)
return allowed;
}
+void QQuickMultiPointHandler::moveTarget(QPointF pos)
+{
+ target()->setPosition(pos);
+ m_centroid.m_position = target()->mapFromScene(m_centroid.m_scenePosition);
+}
+
+/*!
+ \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.
+*/
+
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickmultipointhandler_p.h b/src/quick/handlers/qquickmultipointhandler_p.h
index 67e550d387..94142013cc 100644
--- a/src/quick/handlers/qquickmultipointhandler_p.h
+++ b/src/quick/handlers/qquickmultipointhandler_p.h
@@ -53,6 +53,7 @@
#include "qquickitem.h"
#include "qevent.h"
+#include "qquickhandlerpoint_p.h"
#include "qquickpointerdevicehandler_p.h"
QT_BEGIN_NAMESPACE
@@ -62,11 +63,10 @@ class Q_AUTOTEST_EXPORT QQuickMultiPointHandler : public QQuickPointerDeviceHand
Q_OBJECT
Q_PROPERTY(int minimumPointCount READ minimumPointCount WRITE setMinimumPointCount NOTIFY minimumPointCountChanged)
Q_PROPERTY(int maximumPointCount READ maximumPointCount WRITE setMaximumPointCount NOTIFY maximumPointCountChanged)
- Q_PROPERTY(qreal pointDistanceThreshold READ pointDistanceThreshold WRITE setPointDistanceThreshold NOTIFY pointDistanceThresholdChanged)
+ Q_PROPERTY(QQuickHandlerPoint centroid READ centroid NOTIFY centroidChanged)
public:
- explicit QQuickMultiPointHandler(QObject *parent = nullptr, int minimumPointCount = 2);
- ~QQuickMultiPointHandler();
+ explicit QQuickMultiPointHandler(QQuickItem *parent = nullptr, int minimumPointCount = 2, int maximumPointCount = -1);
int minimumPointCount() const { return m_minimumPointCount; }
void setMinimumPointCount(int c);
@@ -74,13 +74,13 @@ public:
int maximumPointCount() const { return m_maximumPointCount >= 0 ? m_maximumPointCount : m_minimumPointCount; }
void setMaximumPointCount(int maximumPointCount);
- qreal pointDistanceThreshold() const { return m_pointDistanceThreshold; }
- void setPointDistanceThreshold(qreal pointDistanceThreshold);
+ QQuickHandlerPoint centroid() const { return m_centroid; }
signals:
void minimumPointCountChanged();
void maximumPointCountChanged();
- void pointDistanceThresholdChanged();
+ void marginChanged();
+ void centroidChanged();
protected:
struct PointData {
@@ -91,10 +91,11 @@ protected:
};
bool wantsPointerEvent(QQuickPointerEvent *event) override;
- bool sameAsCurrentPoints(QQuickPointerEvent *event);
+ void handlePointerEventImpl(QQuickPointerEvent *event) override;
+ void onActiveChanged() override;
+ void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point) override;
+ bool hasCurrentPoints(QQuickPointerEvent *event);
QVector<QQuickEventPoint *> eligiblePoints(QQuickPointerEvent *event);
- QPointF touchPointCentroid();
- QVector2D touchPointCentroidVelocity();
qreal averageTouchPointDistance(const QPointF &ref);
qreal averageStartingDistance(const QPointF &ref);
qreal averageTouchPointAngle(const QPointF &ref);
@@ -103,12 +104,13 @@ protected:
static qreal averageAngleDelta(const QVector<PointData> &old, const QVector<PointData> &newAngles);
void acceptPoints(const QVector<QQuickEventPoint *> &points);
bool grabPoints(QVector<QQuickEventPoint *> points);
+ void moveTarget(QPointF pos);
protected:
- QVector<QQuickEventPoint *> m_currentPoints;
+ QVector<QQuickHandlerPoint> m_currentPoints;
+ QQuickHandlerPoint m_centroid;
int m_minimumPointCount;
int m_maximumPointCount;
- qreal m_pointDistanceThreshold;
};
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpinchhandler.cpp b/src/quick/handlers/qquickpinchhandler.cpp
index 57d851ed59..e6442e7258 100644
--- a/src/quick/handlers/qquickpinchhandler.cpp
+++ b/src/quick/handlers/qquickpinchhandler.cpp
@@ -47,6 +47,7 @@
#include <QMouseEvent>
#include <QDebug>
#include <qpa/qplatformnativeinterface.h>
+#include <math.h>
QT_BEGIN_NAMESPACE
@@ -56,12 +57,12 @@ Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch")
\qmltype PinchHandler
\instantiates QQuickPinchHandler
\inherits MultiPointHandler
- \inqmlmodule Qt.labs.handlers
- \ingroup qtquick-handlers
+ \inqmlmodule QtQuick
+ \ingroup qtquick-input-handlers
\brief Handler for pinch gestures.
PinchHandler is a handler that interprets a multi-finger gesture to
- interactively rotate, zoom, and drag an Item. Like other Pointer Handlers,
+ interactively rotate, zoom, and drag an Item. Like other Input Handlers,
by default it is fully functional, and manipulates its \l target,
which is the Item within which it is declared.
@@ -85,26 +86,8 @@ Q_LOGGING_CATEGORY(lcPinchHandler, "qt.quick.handler.pinch")
\sa PinchArea
*/
-QQuickPinchHandler::QQuickPinchHandler(QObject *parent)
+QQuickPinchHandler::QQuickPinchHandler(QQuickItem *parent)
: QQuickMultiPointHandler(parent, 2)
- , m_activeScale(1)
- , m_activeRotation(0)
- , m_activeTranslation(0,0)
- , m_minimumScale(-qInf())
- , m_maximumScale(qInf())
- , m_minimumRotation(-qInf())
- , m_maximumRotation(qInf())
- , m_minimumX(-qInf())
- , m_maximumX(qInf())
- , m_minimumY(-qInf())
- , m_maximumY(qInf())
- , m_pinchOrigin(PinchCenter)
- , m_startScale(1)
- , m_startRotation(0)
-{
-}
-
-QQuickPinchHandler::~QQuickPinchHandler()
{
}
@@ -169,29 +152,6 @@ void QQuickPinchHandler::setMaximumRotation(qreal maximumRotation)
}
/*!
- \qmlproperty real QtQuick::PinchHandler::pinchOrigin
-
- The point to be held in place, around which the \l target is scaled and
- rotated.
-
- \value FirstPoint
- the first touch point, wherever the first finger is pressed
- \value PinchCenter
- the centroid between all the touch points at the time when the
- PinchHandler becomes \l active
- \value TargetCenter
- the center of the \l target
-*/
-void QQuickPinchHandler::setPinchOrigin(QQuickPinchHandler::PinchOrigin pinchOrigin)
-{
- if (m_pinchOrigin == pinchOrigin)
- return;
-
- m_pinchOrigin = pinchOrigin;
- emit pinchOriginChanged();
-}
-
-/*!
\qmlproperty real QtQuick::PinchHandler::minimumX
The minimum acceptable x coordinate of the centroid
@@ -287,11 +247,11 @@ bool QQuickPinchHandler::wantsPointerEvent(QQuickPointerEvent *event)
void QQuickPinchHandler::onActiveChanged()
{
+ QQuickMultiPointHandler::onActiveChanged();
if (active()) {
m_startMatrix = QMatrix4x4();
- m_startCentroid = touchPointCentroid();
- m_startAngles = angles(m_startCentroid);
- m_startDistance = averageTouchPointDistance(m_startCentroid);
+ m_startAngles = angles(m_centroid.sceneGrabPosition());
+ m_startDistance = averageTouchPointDistance(m_centroid.sceneGrabPosition());
m_activeRotation = 0;
m_activeTranslation = QVector2D();
if (const QQuickItem *t = target()) {
@@ -304,7 +264,7 @@ void QQuickPinchHandler::onActiveChanged()
m_startMatrix.rotate(m_startRotation, 0, 0, -1);
m_startMatrix.translate(-xformOrigin);
} else {
- m_startScale = 1;
+ m_startScale = m_accumulatedScale;
m_startRotation = 0;
}
qCInfo(lcPinchHandler) << "activated with starting scale" << m_startScale << "rotation" << m_startRotation;
@@ -316,20 +276,21 @@ void QQuickPinchHandler::onActiveChanged()
void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
{
if (Q_UNLIKELY(lcPinchHandler().isDebugEnabled())) {
- for (QQuickEventPoint *point : qAsConst(m_currentPoints))
- qCDebug(lcPinchHandler) << point->state() << point->sceneGrabPosition() << "->" << point->scenePosition();
+ for (const QQuickHandlerPoint &p : m_currentPoints)
+ qCDebug(lcPinchHandler) << hex << p.id() << p.sceneGrabPosition() << "->" << p.scenePosition();
}
+ QQuickMultiPointHandler::handlePointerEventImpl(event);
qreal dist = 0;
#if QT_CONFIG(gestures)
if (const auto gesture = event->asPointerNativeGestureEvent()) {
+ m_centroid.reset(event->point(0));
switch (gesture->type()) {
case Qt::EndNativeGesture:
m_activeScale = 1;
m_activeRotation = 0;
m_activeTranslation = QVector2D();
- m_centroid = QPointF();
- m_centroidVelocity = QVector2D();
+ m_centroid.reset();
setActive(false);
emit updated();
return;
@@ -344,24 +305,81 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
return;
}
if (!active()) {
- m_centroid = gesture->point(0)->scenePosition();
setActive(true);
- m_startCentroid = m_centroid;
// Native gestures for 2-finger pinch do not allow dragging, so
// the centroid won't move during the gesture, and translation stays at zero
- m_centroidVelocity = QVector2D();
m_activeTranslation = QVector2D();
}
} else
#endif // QT_CONFIG(gestures)
{
- bool containsReleasedPoints = event->isReleaseEvent();
+ const bool containsReleasedPoints = event->isReleaseEvent();
+ QVector<QQuickEventPoint *> chosenPoints;
+ for (const QQuickHandlerPoint &p : m_currentPoints) {
+ QQuickEventPoint *ep = event->pointById(p.id());
+ chosenPoints << ep;
+ }
if (!active()) {
// Verify that at least one of the points has moved beyond threshold needed to activate the handler
- for (QQuickEventPoint *point : qAsConst(m_currentPoints)) {
- if (!containsReleasedPoints && QQuickWindowPrivate::dragOverThreshold(point) && grabPoints(m_currentPoints)) {
- setActive(true);
- break;
+ int numberOfPointsDraggedOverThreshold = 0;
+ QVector2D accumulatedDrag;
+ const QVector2D currentCentroid(m_centroid.scenePosition());
+ const QVector2D pressCentroid(m_centroid.scenePressPosition());
+
+ QStyleHints *styleHints = QGuiApplication::styleHints();
+ const int dragThreshold = styleHints->startDragDistance();
+ const int dragThresholdSquared = dragThreshold * dragThreshold;
+
+ double accumulatedCentroidDistance = 0; // Used to detect scale
+ if (event->isPressEvent())
+ m_accumulatedStartCentroidDistance = 0; // Used to detect scale
+
+ float accumulatedMovementMagnitude = 0;
+
+ for (QQuickEventPoint *point : qAsConst(chosenPoints)) {
+ if (!containsReleasedPoints) {
+ accumulatedDrag += QVector2D(point->scenePressPosition() - point->scenePosition());
+ /*
+ In order to detect a drag, we want to check if all points have moved more or
+ less in the same direction.
+
+ We then take each point, and convert the point to a local coordinate system where
+ the centroid is the origin. This is done both for the press positions and the
+ current positions. We will then have two positions:
+
+ - pressCentroidRelativePosition
+ is the start point relative to the press centroid
+ - currentCentroidRelativePosition
+ is the current point relative to the current centroid
+
+ If those two points are far enough apart, it might not be considered as a drag
+ anymore. (Note that the threshold will matched to the average of the relative
+ movement of all the points). Therefore, a big relative movement will make a big
+ contribution to the average relative movement.
+
+ The algorithm then can be described as:
+ For each point:
+ - Calculate vector pressCentroidRelativePosition (from the press centroid to the press position)
+ - Calculate vector currentCentroidRelativePosition (from the current centroid to the current position)
+ - Calculate the relative movement vector:
+
+ centroidRelativeMovement = currentCentroidRelativePosition - pressCentroidRelativePosition
+
+ and measure its magnitude. Add the magnitude to the accumulatedMovementMagnitude.
+
+ Finally, if the accumulatedMovementMagnitude is below some threshold, it means
+ that the points were stationary or they were moved in parallel (e.g. the hand
+ was moved, but the relative position between each finger remained very much
+ the same). This is then used to rule out if there is a rotation or scale.
+ */
+ QVector2D pressCentroidRelativePosition = QVector2D(point->scenePosition()) - currentCentroid;
+ QVector2D currentCentroidRelativePosition = QVector2D(point->scenePressPosition()) - pressCentroid;
+ QVector2D centroidRelativeMovement = currentCentroidRelativePosition - pressCentroidRelativePosition;
+ accumulatedMovementMagnitude += centroidRelativeMovement.length();
+
+ accumulatedCentroidDistance += pressCentroidRelativePosition.length();
+ if (event->isPressEvent())
+ m_accumulatedStartCentroidDistance += (QVector2D(point->scenePressPosition()) - pressCentroid).length();
} else {
setPassiveGrab(point);
}
@@ -369,47 +387,74 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
point->setAccepted(false); // don't stop propagation
setPassiveGrab(point);
}
+ if (QQuickWindowPrivate::dragOverThreshold(point))
+ ++numberOfPointsDraggedOverThreshold;
+ }
+
+ const bool requiredNumberOfPointsDraggedOverThreshold = numberOfPointsDraggedOverThreshold >= minimumPointCount() && numberOfPointsDraggedOverThreshold <= maximumPointCount();
+ accumulatedMovementMagnitude /= m_currentPoints.count();
+
+ QVector2D avgDrag = accumulatedDrag / m_currentPoints.count();
+ if (!xAxis()->enabled())
+ avgDrag.setX(0);
+ if (!yAxis()->enabled())
+ avgDrag.setY(0);
+
+ const qreal centroidMovementDelta = (currentCentroid - pressCentroid).length();
+
+ qreal distanceToCentroidDelta = qAbs(accumulatedCentroidDistance - m_accumulatedStartCentroidDistance); // Used to detect scale
+ if (numberOfPointsDraggedOverThreshold >= 1) {
+ if (requiredNumberOfPointsDraggedOverThreshold && avgDrag.lengthSquared() >= dragThresholdSquared && accumulatedMovementMagnitude < dragThreshold) {
+ // Drag
+ if (grabPoints(chosenPoints))
+ setActive(true);
+ } else if (distanceToCentroidDelta > dragThreshold) { // all points should in accumulation have been moved beyond threshold (?)
+ // Scale
+ if (grabPoints(chosenPoints))
+ setActive(true);
+ } else if (distanceToCentroidDelta < dragThreshold && (centroidMovementDelta < dragThreshold)) {
+ // Rotate
+ // Since it wasn't a scale and if we exceeded the dragthreshold, and the
+ // centroid didn't moved much, the points must have been moved around the centroid.
+ if (grabPoints(chosenPoints))
+ setActive(true);
+ }
}
if (!active())
return;
}
- // TODO check m_pinchOrigin: right now it acts like it's set to PinchCenter
- m_centroid = touchPointCentroid();
- m_centroidVelocity = touchPointCentroidVelocity();
+
// 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);
+ dist = averageTouchPointDistance(m_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);
+ QVector<PointData> newAngles = angles(m_centroid.scenePosition());
const qreal angleDelta = averageAngleDelta(m_startAngles, newAngles);
m_activeRotation += angleDelta;
m_startAngles = std::move(newAngles);
if (!containsReleasedPoints)
- acceptPoints(m_currentPoints);
+ acceptPoints(chosenPoints);
}
- QPointF centroidParentPos;
- QRectF bounds(m_minimumX, m_minimumY, m_maximumX - m_minimumX, m_maximumY - m_minimumY);
- if (target() && target()->parentItem()) {
- centroidParentPos = target()->parentItem()->mapFromScene(m_centroid);
- centroidParentPos = QPointF(qBound(bounds.left(), centroidParentPos.x(), bounds.right()),
- qBound(bounds.top(), centroidParentPos.y(), bounds.bottom()));
- }
const qreal totalRotation = m_startRotation + m_activeRotation;
const qreal rotation = qBound(m_minimumRotation, totalRotation, m_maximumRotation);
m_activeRotation += (rotation - totalRotation); //adjust for the potential bounding above
- const qreal scale = m_startScale * m_activeScale;
+ m_accumulatedScale = m_startScale * m_activeScale;
if (target() && target()->parentItem()) {
+ const QPointF centroidParentPos = target()->parentItem()->mapFromScene(m_centroid.scenePosition());
// 3. Drag/translate
- const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(m_startCentroid);
+ const QPointF centroidStartParentPos = target()->parentItem()->mapFromScene(m_centroid.sceneGrabPosition());
m_activeTranslation = QVector2D(centroidParentPos - centroidStartParentPos);
-
+ if (!xAxis()->enabled())
+ m_activeTranslation.setX(0);
+ if (!yAxis()->enabled())
+ m_activeTranslation.setY(0);
// apply rotation + scaling around the centroid - then apply translation.
QMatrix4x4 mat;
@@ -426,18 +471,23 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
QPointF pos = mat * xformOriginPoint;
pos -= xformOriginPoint;
+ if (xAxis()->enabled())
+ pos.setX(qBound(xAxis()->minimum(), pos.x(), xAxis()->maximum()));
+ if (yAxis()->enabled())
+ pos.setY(qBound(yAxis()->minimum(), pos.y(), yAxis()->maximum()));
+
target()->setPosition(pos);
target()->setRotation(rotation);
- target()->setScale(scale);
+ target()->setScale(m_accumulatedScale);
// TODO some translation inadvertently happens; try to hold the chosen pinch origin in place
} else {
- m_activeTranslation = QVector2D(m_centroid - m_startCentroid);
+ m_activeTranslation = QVector2D(m_centroid.scenePosition() - m_centroid.scenePressPosition());
}
- qCDebug(lcPinchHandler) << "centroid" << m_startCentroid << "->" << m_centroid
+ qCDebug(lcPinchHandler) << "centroid" << m_centroid.scenePressPosition() << "->" << m_centroid.scenePosition()
<< ", distance" << m_startDistance << "->" << dist
- << ", startScale" << m_startScale << "->" << scale
+ << ", startScale" << m_startScale << "->" << m_accumulatedScale
<< ", activeRotation" << m_activeRotation
<< ", rotation" << rotation
<< " from " << event->device()->type();
@@ -447,31 +497,33 @@ void QQuickPinchHandler::handlePointerEventImpl(QQuickPointerEvent *event)
/*!
\readonly
- \qmlproperty QPointF QtQuick::PinchHandler::centroid
+ \qmlproperty QtQuick::HandlerPoint QtQuick::PinchHandler::centroid
A point exactly in the middle of the currently-pressed touch points.
- If \l pinchOrigin is set to \c PinchCenter, the \l target will be rotated
- around this point.
+ The \l target will be rotated around this point.
*/
/*!
\readonly
- \qmlproperty QVector2D QtQuick::PinchHandler::centroidVelocity
+ \qmlproperty real QtQuick::PinchHandler::scale
- The average velocity of the \l centroid: a vector representing the speed
- and direction of movement of the whole group of touchpoints, in logical
- pixels per second.
+ The scale factor that will automatically be set on the \l target if it is not null.
+ Otherwise, bindings can be used to do arbitrary things with this value.
+ While the pinch gesture is being performed, it is continuously multiplied by
+ \l activeScale; after the gesture ends, it stays the same; and when the next
+ pinch gesture begins, it begins to be multiplied by activeScale again.
*/
/*!
\readonly
- \qmlproperty real QtQuick::PinchHandler::scale
-
- The scale factor. It is 1.0 when the gesture begins, increases as the
- touchpoints are spread apart, and decreases as the touchpoints are brought
- together. If \l target is not null, this will be automatically applied to its
- \l {Item::scale}{scale}. Otherwise, bindings can be used to do arbitrary
- things with this value.
+ \qmlproperty real QtQuick::PinchHandler::activeScale
+
+ The scale factor while the pinch gesture is being performed.
+ It is 1.0 when the gesture begins, increases as the touchpoints are spread
+ apart, and decreases as the touchpoints are brought together.
+ If \l target is not null, its \l {Item::scale}{scale} will be automatically
+ multiplied by this value.
+ Otherwise, bindings can be used to do arbitrary things with this value.
*/
/*!
diff --git a/src/quick/handlers/qquickpinchhandler_p.h b/src/quick/handlers/qquickpinchhandler_p.h
index 9a17971416..8f24d18166 100644
--- a/src/quick/handlers/qquickpinchhandler_p.h
+++ b/src/quick/handlers/qquickpinchhandler_p.h
@@ -55,6 +55,7 @@
#include "qevent.h"
#include "qquickmultipointhandler_p.h"
#include <private/qquicktranslate_p.h>
+#include "qquickdragaxis_p.h"
QT_BEGIN_NAMESPACE
@@ -65,25 +66,19 @@ class Q_AUTOTEST_EXPORT QQuickPinchHandler : public QQuickMultiPointHandler
Q_PROPERTY(qreal maximumScale READ maximumScale WRITE setMaximumScale NOTIFY maximumScaleChanged)
Q_PROPERTY(qreal minimumRotation READ minimumRotation WRITE setMinimumRotation NOTIFY minimumRotationChanged)
Q_PROPERTY(qreal maximumRotation READ maximumRotation WRITE setMaximumRotation NOTIFY maximumRotationChanged)
- Q_PROPERTY(PinchOrigin pinchOrigin READ pinchOrigin WRITE setPinchOrigin NOTIFY pinchOriginChanged)
- Q_PROPERTY(QPointF centroid READ centroid NOTIFY updated)
- Q_PROPERTY(QVector2D centroidVelocity READ centroidVelocity NOTIFY updated)
Q_PROPERTY(qreal scale READ scale NOTIFY updated)
+ Q_PROPERTY(qreal activeScale READ activeScale NOTIFY updated)
Q_PROPERTY(qreal rotation READ rotation NOTIFY updated)
Q_PROPERTY(QVector2D translation READ translation NOTIFY updated)
Q_PROPERTY(qreal minimumX READ minimumX WRITE setMinimumX NOTIFY minimumXChanged)
Q_PROPERTY(qreal maximumX READ maximumX WRITE setMaximumX NOTIFY maximumXChanged)
Q_PROPERTY(qreal minimumY READ minimumY WRITE setMinimumY NOTIFY minimumYChanged)
Q_PROPERTY(qreal maximumY READ maximumY WRITE setMaximumY NOTIFY maximumYChanged)
+ Q_PROPERTY(QQuickDragAxis * xAxis READ xAxis CONSTANT)
+ Q_PROPERTY(QQuickDragAxis * yAxis READ yAxis CONSTANT)
public:
- enum PinchOrigin {
- FirstPoint, PinchCenter, TargetCenter
- };
- Q_ENUM(PinchOrigin)
-
- explicit QQuickPinchHandler(QObject *parent = nullptr);
- ~QQuickPinchHandler();
+ explicit QQuickPinchHandler(QQuickItem *parent = nullptr);
qreal minimumScale() const { return m_minimumScale; }
void setMinimumScale(qreal minimumScale);
@@ -97,15 +92,10 @@ public:
qreal maximumRotation() const { return m_maximumRotation; }
void setMaximumRotation(qreal maximumRotation);
- PinchOrigin pinchOrigin() const { return m_pinchOrigin; }
- void setPinchOrigin(PinchOrigin pinchOrigin);
-
QVector2D translation() const { return m_activeTranslation; }
- qreal scale() const { return m_activeScale; }
+ qreal scale() const { return m_accumulatedScale; }
+ qreal activeScale() const { return m_activeScale; }
qreal rotation() const { return m_activeRotation; }
- QPointF centroid() const { return m_centroid; }
- QVector2D centroidVelocity() const { return m_centroidVelocity; }
-
qreal minimumX() const { return m_minimumX; }
void setMinimumX(qreal minX);
qreal maximumX() const { return m_maximumX; }
@@ -115,6 +105,9 @@ public:
qreal maximumY() const { return m_maximumY; }
void setMaximumY(qreal maxY);
+ QQuickDragAxis *xAxis() { return &m_xAxis; }
+ QQuickDragAxis *yAxis() { return &m_yAxis; }
+
signals:
void minimumScaleChanged();
void maximumScaleChanged();
@@ -124,7 +117,6 @@ signals:
void maximumXChanged();
void minimumYChanged();
void maximumYChanged();
- void pinchOriginChanged();
void updated();
protected:
@@ -134,36 +126,34 @@ protected:
private:
// properties
- qreal m_activeScale;
- qreal m_activeRotation;
- QVector2D m_activeTranslation;
- QPointF m_centroid;
- QVector2D m_centroidVelocity;
-
- qreal m_minimumScale;
- qreal m_maximumScale;
+ qreal m_activeScale = 1;
+ qreal m_accumulatedScale = 1;
+ qreal m_activeRotation = 0;
+ QVector2D m_activeTranslation = QVector2D(0, 0);
- qreal m_minimumRotation;
- qreal m_maximumRotation;
+ qreal m_minimumScale = -qInf();
+ qreal m_maximumScale = qInf();
- qreal m_minimumX;
- qreal m_maximumX;
- qreal m_minimumY;
- qreal m_maximumY;
+ qreal m_minimumRotation = -qInf();
+ qreal m_maximumRotation = qInf();
- PinchOrigin m_pinchOrigin;
+ qreal m_minimumX = -qInf();
+ qreal m_maximumX = qInf();
+ qreal m_minimumY = -qInf();
+ qreal m_maximumY = qInf();
+ QQuickDragAxis m_xAxis;
+ QQuickDragAxis m_yAxis;
// internal
- qreal m_startScale;
- qreal m_startRotation;
- QPointF m_startCentroid;
- qreal m_startDistance;
+ qreal m_startScale = 1;
+ qreal m_startRotation = 0;
+ qreal m_startDistance = 0;
QPointF m_startPos;
+ qreal m_accumulatedStartCentroidDistance = 0;
QVector<PointData> m_startAngles;
QMatrix4x4 m_startMatrix;
QQuickMatrix4x4 m_transform;
-
};
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpointerdevicehandler.cpp b/src/quick/handlers/qquickpointerdevicehandler.cpp
index 06831613b6..096fad2071 100644
--- a/src/quick/handlers/qquickpointerdevicehandler.cpp
+++ b/src/quick/handlers/qquickpointerdevicehandler.cpp
@@ -37,7 +37,7 @@
**
****************************************************************************/
-#include "qquickpointerdevicehandler_p.h"
+#include "qquickpointerdevicehandler_p_p.h"
#include <private/qquickitem_p.h>
#include <QMouseEvent>
#include <QDebug>
@@ -51,27 +51,87 @@ QT_BEGIN_NAMESPACE
\preliminary
\instantiates QQuickPointerDeviceHandler
\inherits PointerHandler
- \inqmlmodule Qt.labs.handlers
- \ingroup qtquick-handlers
+ \inqmlmodule QtQuick
\brief Abstract handler for pointer events with device-specific constraints.
An intermediate class (not registered as a QML type) for handlers which
allow filtering based on device type, pointer type, or keyboard modifiers.
*/
-QQuickPointerDeviceHandler::QQuickPointerDeviceHandler(QObject *parent)
- : QQuickPointerHandler(parent)
- , m_acceptedDevices(QQuickPointerDevice::AllDevices)
- , m_acceptedPointerTypes(QQuickPointerDevice::AllPointerTypes)
- , m_acceptedModifiers(Qt::KeyboardModifierMask)
+QQuickPointerDeviceHandler::QQuickPointerDeviceHandler(QQuickItem *parent)
+ : QQuickPointerHandler(*(new QQuickPointerDeviceHandlerPrivate), parent)
{
}
-QQuickPointerDeviceHandler::~QQuickPointerDeviceHandler()
+QQuickPointerDeviceHandler::QQuickPointerDeviceHandler(QQuickPointerDeviceHandlerPrivate &dd, QQuickItem *parent)
+ : QQuickPointerHandler(dd, parent)
{
}
+QQuickPointerDevice::DeviceTypes QQuickPointerDeviceHandler::acceptedDevices() const
+{
+ Q_D(const QQuickPointerDeviceHandler);
+ return d->acceptedDevices;
+}
+
+QQuickPointerDevice::PointerTypes QQuickPointerDeviceHandler::acceptedPointerTypes() const
+{
+ Q_D(const QQuickPointerDeviceHandler);
+ return d->acceptedPointerTypes;
+}
+
+/*!
+ \qmlproperty flags QtQuick::PointerDeviceHandler::acceptedButtons
+
+ The mouse buttons which can activate this Pointer Handler.
+
+ By default, this property is set to \l {QtQuick::MouseEvent::button} {Qt.LeftButton}.
+ It can be set to an OR combination of mouse buttons, and will ignore events
+ from other buttons.
+
+ For example, a control could be made to respond to left and right clicks
+ in different ways, with two handlers:
+
+ \qml
+ Item {
+ TapHandler {
+ onTapped: console.log("left clicked")
+ }
+ TapHandler {
+ acceptedButtons: Qt.RightButton
+ onTapped: console.log("right clicked")
+ }
+ }
+ \endqml
+
+ \note Tapping on a touchscreen or tapping the stylus on a graphics tablet
+ emulates clicking the left mouse button. This behavior can be altered via
+ \l {PointerDeviceHandler::acceptedDevices}{acceptedDevices} or
+ \l {PointerDeviceHandler::acceptedPointerTypes}{acceptedPointerTypes}.
+*/
+Qt::MouseButtons QQuickPointerDeviceHandler::acceptedButtons() const
+{
+ Q_D(const QQuickPointerDeviceHandler);
+ return d->acceptedButtons;
+}
+
+void QQuickPointerDeviceHandler::setAcceptedButtons(Qt::MouseButtons buttons)
+{
+ Q_D(QQuickPointerDeviceHandler);
+ if (d->acceptedButtons == buttons)
+ return;
+
+ d->acceptedButtons = buttons;
+ emit acceptedButtonsChanged();
+}
+
+Qt::KeyboardModifiers QQuickPointerDeviceHandler::acceptedModifiers() const
+{
+ Q_D(const QQuickPointerDeviceHandler);
+ return d->acceptedModifiers;
+}
+
/*!
- \qmlproperty int PointerDeviceHandler::acceptedDevices
+ \qmlproperty flags PointerDeviceHandler::acceptedDevices
The types of pointing devices that can activate this Pointer Handler.
@@ -98,15 +158,16 @@ QQuickPointerDeviceHandler::~QQuickPointerDeviceHandler()
*/
void QQuickPointerDeviceHandler::setAcceptedDevices(QQuickPointerDevice::DeviceTypes acceptedDevices)
{
- if (m_acceptedDevices == acceptedDevices)
+ Q_D(QQuickPointerDeviceHandler);
+ if (d->acceptedDevices == acceptedDevices)
return;
- m_acceptedDevices = acceptedDevices;
+ d->acceptedDevices = acceptedDevices;
emit acceptedDevicesChanged();
}
/*!
- \qmlproperty int PointerDeviceHandler::acceptedPointerTypes
+ \qmlproperty flags PointerDeviceHandler::acceptedPointerTypes
The types of pointing instruments (finger, stylus, eraser, etc.)
that can activate this Pointer Handler.
@@ -136,15 +197,16 @@ void QQuickPointerDeviceHandler::setAcceptedDevices(QQuickPointerDevice::DeviceT
*/
void QQuickPointerDeviceHandler::setAcceptedPointerTypes(QQuickPointerDevice::PointerTypes acceptedPointerTypes)
{
- if (m_acceptedPointerTypes == acceptedPointerTypes)
+ Q_D(QQuickPointerDeviceHandler);
+ if (d->acceptedPointerTypes == acceptedPointerTypes)
return;
- m_acceptedPointerTypes = acceptedPointerTypes;
+ d->acceptedPointerTypes = acceptedPointerTypes;
emit acceptedPointerTypesChanged();
}
/*!
- \qmlproperty int PointerDeviceHandler::acceptedModifiers
+ \qmlproperty flags PointerDeviceHandler::acceptedModifiers
If this property is set, it will require the given keyboard modifiers to
be pressed in order to react to pointer events, and otherwise ignore them.
@@ -171,26 +233,28 @@ void QQuickPointerDeviceHandler::setAcceptedPointerTypes(QQuickPointerDevice::Po
*/
void QQuickPointerDeviceHandler::setAcceptedModifiers(Qt::KeyboardModifiers acceptedModifiers)
{
- if (m_acceptedModifiers == acceptedModifiers)
+ Q_D(QQuickPointerDeviceHandler);
+ if (d->acceptedModifiers == acceptedModifiers)
return;
- m_acceptedModifiers = acceptedModifiers;
+ d->acceptedModifiers = acceptedModifiers;
emit acceptedModifiersChanged();
}
bool QQuickPointerDeviceHandler::wantsPointerEvent(QQuickPointerEvent *event)
{
+ Q_D(QQuickPointerDeviceHandler);
if (!QQuickPointerHandler::wantsPointerEvent(event))
return false;
qCDebug(lcPointerHandlerDispatch) << objectName()
- << "checking device type" << m_acceptedDevices
- << "pointer type" << m_acceptedPointerTypes
- << "modifiers" << m_acceptedModifiers;
- if ((event->device()->type() & m_acceptedDevices) == 0)
+ << "checking device type" << d->acceptedDevices
+ << "pointer type" << d->acceptedPointerTypes
+ << "modifiers" << d->acceptedModifiers;
+ if ((event->device()->type() & d->acceptedDevices) == 0)
return false;
- if ((event->device()->pointerType() & m_acceptedPointerTypes) == 0)
+ if ((event->device()->pointerType() & d->acceptedPointerTypes) == 0)
return false;
- if (m_acceptedModifiers != Qt::KeyboardModifierMask && event->modifiers() != m_acceptedModifiers)
+ if (d->acceptedModifiers != Qt::KeyboardModifierMask && event->modifiers() != d->acceptedModifiers)
return false;
return true;
}
diff --git a/src/quick/handlers/qquickpointerdevicehandler_p.h b/src/quick/handlers/qquickpointerdevicehandler_p.h
index 1638604ea7..82b24369d3 100644
--- a/src/quick/handlers/qquickpointerdevicehandler_p.h
+++ b/src/quick/handlers/qquickpointerdevicehandler_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -36,6 +36,7 @@
** $QT_END_LICENSE$
**
****************************************************************************/
+#include "qquickpointerhandler_p.h"
#ifndef QQUICKPOINTERDEVICEHANDLER_H
#define QQUICKPOINTERDEVICEHANDLER_H
@@ -51,42 +52,44 @@
// We mean it.
//
-#include "qquickpointerhandler_p.h"
-
QT_BEGIN_NAMESPACE
+class QQuickPointerDeviceHandlerPrivate;
+
class Q_AUTOTEST_EXPORT QQuickPointerDeviceHandler : public QQuickPointerHandler
{
Q_OBJECT
Q_PROPERTY(QQuickPointerDevice::DeviceTypes acceptedDevices READ acceptedDevices WRITE setAcceptedDevices NOTIFY acceptedDevicesChanged)
Q_PROPERTY(QQuickPointerDevice::PointerTypes acceptedPointerTypes READ acceptedPointerTypes WRITE setAcceptedPointerTypes NOTIFY acceptedPointerTypesChanged)
+ Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged)
Q_PROPERTY(Qt::KeyboardModifiers acceptedModifiers READ acceptedModifiers WRITE setAcceptedModifiers NOTIFY acceptedModifiersChanged)
public:
- explicit QQuickPointerDeviceHandler(QObject *parent = nullptr);
- ~QQuickPointerDeviceHandler();
+ explicit QQuickPointerDeviceHandler(QQuickItem *parent = nullptr);
- QQuickPointerDevice::DeviceTypes acceptedDevices() const { return m_acceptedDevices; }
- QQuickPointerDevice::PointerTypes acceptedPointerTypes() const { return m_acceptedPointerTypes; }
- Qt::KeyboardModifiers acceptedModifiers() const { return m_acceptedModifiers; }
+ QQuickPointerDevice::DeviceTypes acceptedDevices() const;
+ QQuickPointerDevice::PointerTypes acceptedPointerTypes() const;
+ Qt::MouseButtons acceptedButtons() const;
+ Qt::KeyboardModifiers acceptedModifiers() const;
-public slots:
+public Q_SLOTS:
void setAcceptedDevices(QQuickPointerDevice::DeviceTypes acceptedDevices);
void setAcceptedPointerTypes(QQuickPointerDevice::PointerTypes acceptedPointerTypes);
+ void setAcceptedButtons(Qt::MouseButtons buttons);
void setAcceptedModifiers(Qt::KeyboardModifiers acceptedModifiers);
Q_SIGNALS:
void acceptedDevicesChanged();
void acceptedPointerTypesChanged();
+ void acceptedButtonsChanged();
void acceptedModifiersChanged();
protected:
+ QQuickPointerDeviceHandler(QQuickPointerDeviceHandlerPrivate &dd, QQuickItem *parent = nullptr);
+
bool wantsPointerEvent(QQuickPointerEvent *event) override;
-protected:
- QQuickPointerDevice::DeviceTypes m_acceptedDevices;
- QQuickPointerDevice::PointerTypes m_acceptedPointerTypes;
- Qt::KeyboardModifiers m_acceptedModifiers;
+ Q_DECLARE_PRIVATE(QQuickPointerDeviceHandler)
};
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpointerdevicehandler_p_p.h b/src/quick/handlers/qquickpointerdevicehandler_p_p.h
new file mode 100644
index 0000000000..6a950590f3
--- /dev/null
+++ b/src/quick/handlers/qquickpointerdevicehandler_p_p.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPOINTERDEVICEHANDLER_P_H
+#define QQUICKPOINTERDEVICEHANDLER_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 "qquickpointerdevicehandler_p.h"
+#include "qquickpointerhandler_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQuickPointerDeviceHandlerPrivate : public QQuickPointerHandlerPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickPointerDeviceHandler)
+
+public:
+ static QQuickPointerDeviceHandlerPrivate* get(QQuickPointerDeviceHandler *q) { return q->d_func(); }
+ static const QQuickPointerDeviceHandlerPrivate* get(const QQuickPointerDeviceHandler *q) { return q->d_func(); }
+
+ QQuickPointerDevice::DeviceTypes acceptedDevices = QQuickPointerDevice::AllDevices;
+ QQuickPointerDevice::PointerTypes acceptedPointerTypes = QQuickPointerDevice::AllPointerTypes;
+ Qt::MouseButtons acceptedButtons = Qt::LeftButton;
+ Qt::KeyboardModifiers acceptedModifiers = Qt::KeyboardModifierMask;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPOINTERDEVICEHANDLER_P_H
diff --git a/src/quick/handlers/qquickpointerhandler.cpp b/src/quick/handlers/qquickpointerhandler.cpp
index e5b1dc8985..12c06aa179 100644
--- a/src/quick/handlers/qquickpointerhandler.cpp
+++ b/src/quick/handlers/qquickpointerhandler.cpp
@@ -38,6 +38,8 @@
****************************************************************************/
#include "qquickpointerhandler_p.h"
+#include "qquickpointerhandler_p_p.h"
+#include <QtQuick/private/qquickitem_p.h>
QT_BEGIN_NAMESPACE
@@ -51,24 +53,20 @@ Q_LOGGING_CATEGORY(lcPointerHandlerActive, "qt.quick.handler.active")
\since 5.10
\preliminary
\instantiates QQuickPointerHandler
- \inqmlmodule Qt.labs.handlers
- \ingroup qtquick-handlers
+ \inqmlmodule QtQuick
\brief Abstract handler for pointer events.
- PointerHandler is the base class handler (not registered as a QML type) for
- pointer events without regard to source (touch, mouse or graphics tablet).
+ PointerHandler is the base class Input Handler (not registered as a QML type) for
+ events from any kind of pointing device (touch, mouse or graphics tablet).
*/
-QQuickPointerHandler::QQuickPointerHandler(QObject *parent)
- : QObject(parent)
- , m_currentEvent(nullptr)
- , m_target(nullptr)
- , m_enabled(true)
- , m_active(false)
- , m_targetExplicitlySet(false)
- , m_hadKeepMouseGrab(false)
- , m_hadKeepTouchGrab(false)
- , m_grabPermissions(CanTakeOverFromItems | CanTakeOverFromHandlersOfDifferentType | ApprovesTakeOverByAnything)
+QQuickPointerHandler::QQuickPointerHandler(QQuickItem *parent)
+ : QObject(*(new QQuickPointerHandlerPrivate), parent)
+{
+}
+
+QQuickPointerHandler::QQuickPointerHandler(QQuickPointerHandlerPrivate &dd, QQuickItem *parent)
+ : QObject(dd, parent)
{
}
@@ -82,25 +80,58 @@ QQuickPointerHandler::~QQuickPointerHandler()
}
/*!
+ \qmlproperty real PointerHandler::margin
+
+ The margin beyond the bounds of the \l {PointerHandler::parent}{parent}
+ item within which an event point can activate this handler. For example, on
+ a PinchHandler where the \l {PointerHandler::target}{target} is also the
+ \c parent, it's useful to set this to a distance at least half the width
+ of a typical user's finger, so that if the \c parent has been scaled down
+ to a very small size, the pinch gesture is still possible. Or, if a
+ TapHandler-based button is placed near the screen edge, it can be used
+ to comply with Fitts's Law: react to mouse clicks at the screen edge
+ even though the button is visually spaced away from the edge by a few pixels.
+
+ The default value is 0.
+
+ \image pointerHandlerMargin.png
+*/
+qreal QQuickPointerHandler::margin() const
+{
+ Q_D(const QQuickPointerHandler);
+ return d->m_margin;
+}
+
+void QQuickPointerHandler::setMargin(qreal pointDistanceThreshold)
+{
+ Q_D(QQuickPointerHandler);
+ if (d->m_margin == pointDistanceThreshold)
+ return;
+
+ d->m_margin = pointDistanceThreshold;
+ emit marginChanged();
+}
+
+/*!
Notification that the grab has changed in some way which is relevant to this handler.
- The \a grabber (subject) will be the PointerHandler whose state is changing,
+ The \a grabber (subject) will be the Input Handler whose state is changing,
or null if the state change regards an Item.
- The \a stateChange (verb) tells what happened.
+ The \a transition (verb) tells what happened.
The \a point (object) is the point that was grabbed or ungrabbed.
EventPoint has the sole responsibility to call this function.
- The PointerHandler must react in whatever way is appropriate, and must
+ The Input Handler must react in whatever way is appropriate, and must
emit the relevant signals (for the benefit of QML code).
A subclass is allowed to override this virtual function, but must always
call its parent class's implementation in addition to (usually after)
whatever custom behavior it implements.
*/
-void QQuickPointerHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point)
+void QQuickPointerHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point)
{
- qCDebug(lcPointerHandlerGrab) << point << stateChange << grabber;
+ qCDebug(lcPointerHandlerGrab) << point << transition << grabber;
Q_ASSERT(point);
if (grabber == this) {
bool wasCanceled = false;
- switch (stateChange) {
+ switch (transition) {
case QQuickEventPoint::GrabPassive:
case QQuickEventPoint::GrabExclusive:
break;
@@ -113,8 +144,9 @@ void QQuickPointerHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEv
setActive(false);
point->setAccepted(false);
if (auto par = parentItem()) {
- par->setKeepMouseGrab(m_hadKeepMouseGrab);
- par->setKeepTouchGrab(m_hadKeepTouchGrab);
+ Q_D(const QQuickPointerHandler);
+ par->setKeepMouseGrab(d->hadKeepMouseGrab);
+ par->setKeepTouchGrab(d->hadKeepTouchGrab);
}
break;
case QQuickEventPoint::OverrideGrabPassive:
@@ -124,19 +156,17 @@ void QQuickPointerHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEv
}
if (wasCanceled)
emit canceled(point);
- else
- emit grabChanged(point);
+ emit grabChanged(transition, point);
}
}
/*!
- \internal
Acquire or give up a passive grab of the given \a point, according to the \a grab state.
- Unlike the exclusive grab, multiple PointerHandlers can have passive grabs
+ Unlike the exclusive grab, multiple Input Handlers can have passive grabs
simultaneously. This means that each of them will receive further events
- when the \a point moves, and when it is finally released. Typically a
- PointerHandler should acquire a passive grab as soon as a point is pressed,
+ when the \a point moves, and when it is finally released. Typically an
+ Input Handler should acquire a passive grab as soon as a point is pressed,
if the handler's constraints do not clearly rule out any interest in that
point. For example, DragHandler needs a passive grab in order to watch the
movement of a point to see whether it will be dragged past the drag
@@ -181,19 +211,20 @@ bool QQuickPointerHandler::canGrab(QQuickEventPoint *point)
*/
bool QQuickPointerHandler::approveGrabTransition(QQuickEventPoint *point, QObject *proposedGrabber)
{
+ Q_D(const QQuickPointerHandler);
bool allowed = false;
if (proposedGrabber == this) {
QObject* existingGrabber = point->exclusiveGrabber();
- allowed = (existingGrabber == nullptr) || ((m_grabPermissions & CanTakeOverFromAnything) == CanTakeOverFromAnything);
+ allowed = (existingGrabber == nullptr) || ((d->grabPermissions & CanTakeOverFromAnything) == CanTakeOverFromAnything);
if (existingGrabber) {
if (QQuickPointerHandler *existingPhGrabber = point->grabberPointerHandler()) {
- if (!allowed && (m_grabPermissions & CanTakeOverFromHandlersOfDifferentType) &&
+ if (!allowed && (d->grabPermissions & CanTakeOverFromHandlersOfDifferentType) &&
existingPhGrabber->metaObject()->className() != metaObject()->className())
allowed = true;
- if (!allowed && (m_grabPermissions & CanTakeOverFromHandlersOfSameType) &&
+ if (!allowed && (d->grabPermissions & CanTakeOverFromHandlersOfSameType) &&
existingPhGrabber->metaObject()->className() == metaObject()->className())
allowed = true;
- } else if ((m_grabPermissions & CanTakeOverFromItems)) {
+ } else if ((d->grabPermissions & CanTakeOverFromItems)) {
QQuickItem * existingItemGrabber = point->grabberItem();
if (existingItemGrabber && !((existingItemGrabber->keepMouseGrab() && point->pointerEvent()->asPointerMouseEvent()) ||
(existingItemGrabber->keepTouchGrab() && point->pointerEvent()->asPointerTouchEvent())))
@@ -203,18 +234,18 @@ bool QQuickPointerHandler::approveGrabTransition(QQuickEventPoint *point, QObjec
} else {
// proposedGrabber is different: that means this instance will lose its grab
if (proposedGrabber) {
- if ((m_grabPermissions & ApprovesTakeOverByAnything) == ApprovesTakeOverByAnything)
+ if ((d->grabPermissions & ApprovesTakeOverByAnything) == ApprovesTakeOverByAnything)
allowed = true;
- if (!allowed && (m_grabPermissions & ApprovesTakeOverByHandlersOfDifferentType) &&
+ if (!allowed && (d->grabPermissions & ApprovesTakeOverByHandlersOfDifferentType) &&
proposedGrabber->metaObject()->className() != metaObject()->className())
allowed = true;
- if (!allowed && (m_grabPermissions & ApprovesTakeOverByHandlersOfSameType) &&
+ if (!allowed && (d->grabPermissions & ApprovesTakeOverByHandlersOfSameType) &&
proposedGrabber->metaObject()->className() == metaObject()->className())
allowed = true;
- if (!allowed && (m_grabPermissions & ApprovesTakeOverByItems) && proposedGrabber->inherits("QQuickItem"))
+ if (!allowed && (d->grabPermissions & ApprovesTakeOverByItems) && proposedGrabber->inherits("QQuickItem"))
allowed = true;
} else {
- if (!allowed && (m_grabPermissions & ApprovesCancellation))
+ if (!allowed && (d->grabPermissions & ApprovesCancellation))
allowed = true;
}
}
@@ -225,23 +256,49 @@ bool QQuickPointerHandler::approveGrabTransition(QQuickEventPoint *point, QObjec
}
/*!
- \qmlproperty bool QtQuick::PointerHandler::grabPermission
+ \qmlproperty flags QtQuick::PointerHandler::grabPermissions
This property specifies the permissions when this handler's logic decides
to take over the exclusive grab, or when it is asked to approve grab
takeover or cancellation by another handler.
+ \value PointerHandler.TakeOverForbidden
+ This handler neither takes from nor gives grab permission to any type of Item or Handler.
+ \value PointerHandler.CanTakeOverFromHandlersOfSameType
+ This handler can take the exclusive grab from another handler of the same class.
+ \value PointerHandler.CanTakeOverFromHandlersOfDifferentType
+ This handler can take the exclusive grab from any kind of handler.
+ \value PointerHandler.CanTakeOverFromAnything
+ This handler can take the exclusive grab from any type of Item or Handler.
+ \value PointerHandler.ApprovesTakeOverByHandlersOfSameType
+ This handler gives permission for another handler of the same class to take the grab.
+ \value PointerHandler.ApprovesTakeOverByHandlersOfDifferentType
+ This handler gives permission for any kind of handler to take the grab.
+ \value PointerHandler.ApprovesTakeOverByItems
+ This handler gives permission for any kind of Item to take the grab.
+ \value PointerHandler.ApprovesCancellation
+ This handler will allow its grab to be set to null.
+ \value PointerHandler.ApprovesTakeOverByAnything
+ This handler gives permission for any any type of Item or Handler to take the grab.
+
The default is
- \c {CanTakeOverFromItems | CanTakeOverFromHandlersOfDifferentType | ApprovesTakeOverByAnything}
+ \c {PointerHandler.CanTakeOverFromItems | PointerHandler.CanTakeOverFromHandlersOfDifferentType | PointerHandler.ApprovesTakeOverByAnything}
which allows most takeover scenarios but avoids e.g. two PinchHandlers fighting
over the same touchpoints.
*/
+QQuickPointerHandler::GrabPermissions QQuickPointerHandler::grabPermissions() const
+{
+ Q_D(const QQuickPointerHandler);
+ return static_cast<QQuickPointerHandler::GrabPermissions>(d->grabPermissions);
+}
+
void QQuickPointerHandler::setGrabPermissions(GrabPermissions grabPermission)
{
- if (m_grabPermissions == grabPermission)
+ Q_D(QQuickPointerHandler);
+ if (d->grabPermissions == grabPermission)
return;
- m_grabPermissions = grabPermission;
+ d->grabPermissions = grabPermission;
emit grabPermissionChanged();
}
@@ -253,8 +310,13 @@ void QQuickPointerHandler::componentComplete()
{
}
+QQuickPointerEvent *QQuickPointerHandler::currentEvent()
+{
+ Q_D(const QQuickPointerHandler);
+ return d->currentEvent;
+}
+
/*!
- \internal
Acquire or give up the exclusive grab of the given \a point, according to
the \a grab state, and subject to the rules: canGrab(), and the rule not to
relinquish another handler's grab. Returns true if permission is granted,
@@ -284,7 +346,6 @@ bool QQuickPointerHandler::setExclusiveGrab(QQuickEventPoint *point, bool grab)
}
/*!
- \internal
Cancel any existing grab of the given \a point.
*/
void QQuickPointerHandler::cancelAllGrabs(QQuickEventPoint *point)
@@ -300,9 +361,19 @@ QPointF QQuickPointerHandler::eventPos(const QQuickEventPoint *point) const
bool QQuickPointerHandler::parentContains(const QQuickEventPoint *point) const
{
- if (point) {
- if (QQuickItem *par = parentItem())
- return par->contains(par->mapFromScene(point->scenePosition()));
+ if (!point)
+ return false;
+ if (QQuickItem *par = parentItem()) {
+ if (par->window()) {
+ QPoint screenPosition = par->window()->mapToGlobal(point->scenePosition().toPoint());
+ if (!par->window()->geometry().contains(screenPosition))
+ return false;
+ }
+ QPointF p = par->mapFromScene(point->scenePosition());
+ qreal m = margin();
+ if (m > 0)
+ return p.x() >= -m && p.y() >= -m && p.x() <= par->width() + m && p.y() <= par->height() + m;
+ return par->contains(p);
}
return false;
}
@@ -313,15 +384,28 @@ bool QQuickPointerHandler::parentContains(const QQuickEventPoint *point) const
If a PointerHandler is disabled, it will reject all events
and no signals will be emitted.
*/
+bool QQuickPointerHandler::enabled() const
+{
+ Q_D(const QQuickPointerHandler);
+ return d->enabled;
+}
+
void QQuickPointerHandler::setEnabled(bool enabled)
{
- if (m_enabled == enabled)
+ Q_D(QQuickPointerHandler);
+ if (d->enabled == enabled)
return;
- m_enabled = enabled;
+ d->enabled = enabled;
emit enabledChanged();
}
+bool QQuickPointerHandler::active() const
+{
+ Q_D(const QQuickPointerHandler);
+ return d->active;
+}
+
/*!
\qmlproperty Item QtQuick::PointerHandler::target
@@ -335,21 +419,28 @@ void QQuickPointerHandler::setEnabled(bool enabled)
*/
void QQuickPointerHandler::setTarget(QQuickItem *target)
{
- m_targetExplicitlySet = true;
- if (m_target == target)
+ Q_D(QQuickPointerHandler);
+ d->targetExplicitlySet = true;
+ if (d->target == target)
return;
- QQuickItem *oldTarget = m_target;
- m_target = target;
+ QQuickItem *oldTarget = d->target;
+ d->target = target;
onTargetChanged(oldTarget);
emit targetChanged();
}
+QQuickItem *QQuickPointerHandler::parentItem() const
+{
+ return static_cast<QQuickItem *>(QObject::parent());
+}
+
QQuickItem *QQuickPointerHandler::target() const
{
- if (!m_targetExplicitlySet)
+ Q_D(const QQuickPointerHandler);
+ if (!d->targetExplicitlySet)
return parentItem();
- return m_target;
+ return d->target;
}
void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event)
@@ -374,15 +465,24 @@ void QQuickPointerHandler::handlePointerEvent(QQuickPointerEvent *event)
bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event)
{
+ Q_D(const QQuickPointerHandler);
Q_UNUSED(event)
- return m_enabled;
+ return d->enabled;
+}
+
+bool QQuickPointerHandler::wantsEventPoint(QQuickEventPoint *point)
+{
+ bool ret = point->exclusiveGrabber() == this || point->passiveGrabbers().contains(this) || parentContains(point);
+ qCDebug(lcPointerHandlerDispatch) << hex << point->pointId() << "@" << point->scenePosition()
+ << metaObject()->className() << objectName() << ret;
+ return ret;
}
/*!
\readonly
\qmlproperty bool QtQuick::PointerHandler::active
- This holds true whenever this PointerHandler has taken sole responsibility
+ This holds true whenever this Input Handler has taken sole responsibility
for handing one or more EventPoints, by successfully taking an exclusive
grab of those points. This means that it is keeping its properties
up-to-date according to the movements of those Event Points and actively
@@ -390,9 +490,10 @@ bool QQuickPointerHandler::wantsPointerEvent(QQuickPointerEvent *event)
*/
void QQuickPointerHandler::setActive(bool active)
{
- if (m_active != active) {
- qCDebug(lcPointerHandlerActive) << this << m_active << "->" << active;
- m_active = active;
+ Q_D(QQuickPointerHandler);
+ if (d->active != active) {
+ qCDebug(lcPointerHandlerActive) << this << d->active << "->" << active;
+ d->active = active;
onActiveChanged();
emit activeChanged();
}
@@ -400,7 +501,8 @@ void QQuickPointerHandler::setActive(bool active)
void QQuickPointerHandler::handlePointerEventImpl(QQuickPointerEvent *event)
{
- m_currentEvent = event;
+ Q_D(QQuickPointerHandler);
+ d->currentEvent = event;
}
/*!
@@ -417,10 +519,13 @@ void QQuickPointerHandler::handlePointerEventImpl(QQuickPointerEvent *event)
*/
/*!
- \qmlsignal QtQuick::PointerHandler::grabChanged(EventPoint point)
+ \qmlsignal QtQuick::PointerHandler::grabChanged(GrabTransition transition, EventPoint point)
- This signal is emitted when this handler has acquired or relinquished a
- passive or exclusive grab of the given \a point.
+ This signal is emitted when the grab has changed in some way which is
+ relevant to this handler.
+
+ The \a transition (verb) tells what happened.
+ The \a point (object) is the point that was grabbed or ungrabbed.
*/
/*!
@@ -430,4 +535,16 @@ void QQuickPointerHandler::handlePointerEventImpl(QQuickPointerEvent *event)
emitted when the grab is stolen by a different Pointer Handler or Item.
*/
+QQuickPointerHandlerPrivate::QQuickPointerHandlerPrivate()
+ : grabPermissions(QQuickPointerHandler::CanTakeOverFromItems |
+ QQuickPointerHandler::CanTakeOverFromHandlersOfDifferentType |
+ QQuickPointerHandler::ApprovesTakeOverByAnything)
+ , enabled(true)
+ , active(false)
+ , targetExplicitlySet(false)
+ , hadKeepMouseGrab(false)
+ , hadKeepTouchGrab(false)
+{
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquickpointerhandler_p.h b/src/quick/handlers/qquickpointerhandler_p.h
index e2bcce8fc9..c600e42491 100644
--- a/src/quick/handlers/qquickpointerhandler_p.h
+++ b/src/quick/handlers/qquickpointerhandler_p.h
@@ -51,8 +51,6 @@
// We mean it.
//
-#include "qevent.h"
-
#include <QtQuick/private/qquickevents_p_p.h>
#include <QtQuick/private/qquickitem_p.h>
@@ -60,6 +58,8 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcPointerHandlerDispatch)
+class QQuickPointerHandlerPrivate;
+
class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandler : public QObject, public QQmlParserStatus
{
Q_OBJECT
@@ -70,10 +70,11 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandler : public QObject, public QQmlP
Q_PROPERTY(QQuickItem * target READ target WRITE setTarget NOTIFY targetChanged)
Q_PROPERTY(QQuickItem * parent READ parentItem CONSTANT)
Q_PROPERTY(GrabPermissions grabPermissions READ grabPermissions WRITE setGrabPermissions NOTIFY grabPermissionChanged)
+ Q_PROPERTY(qreal margin READ margin WRITE setMargin NOTIFY marginChanged)
public:
- explicit QQuickPointerHandler(QObject *parent = nullptr);
- virtual ~QQuickPointerHandler();
+ explicit QQuickPointerHandler(QQuickItem *parent = nullptr);
+ ~QQuickPointerHandler();
enum GrabPermission {
TakeOverForbidden = 0x0,
@@ -91,40 +92,47 @@ public:
Q_FLAG(GrabPermissions)
public:
- bool enabled() const { return m_enabled; }
+ bool enabled() const;
void setEnabled(bool enabled);
- bool active() const { return m_active; }
+ bool active() const;
QQuickItem *target() const;
void setTarget(QQuickItem *target);
- QQuickItem * parentItem() const { return static_cast<QQuickItem *>(QObject::parent()); }
+ QQuickItem * parentItem() const;
void handlePointerEvent(QQuickPointerEvent *event);
- GrabPermissions grabPermissions() const { return static_cast<GrabPermissions>(m_grabPermissions); }
+ GrabPermissions grabPermissions() const;
void setGrabPermissions(GrabPermissions grabPermissions);
+ qreal margin() const;
+ void setMargin(qreal pointDistanceThreshold);
+
Q_SIGNALS:
void enabledChanged();
void activeChanged();
void targetChanged();
- void grabChanged(QQuickEventPoint *point);
+ void marginChanged();
+ void grabChanged(QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point);
void grabPermissionChanged();
void canceled(QQuickEventPoint *point);
protected:
+ QQuickPointerHandler(QQuickPointerHandlerPrivate &dd, QQuickItem *parent);
+
void classBegin() override;
void componentComplete() override;
- QQuickPointerEvent *currentEvent() { return m_currentEvent; }
+ QQuickPointerEvent *currentEvent();
virtual bool wantsPointerEvent(QQuickPointerEvent *event);
+ virtual bool wantsEventPoint(QQuickEventPoint *point);
virtual void handlePointerEventImpl(QQuickPointerEvent *event);
void setActive(bool active);
virtual void onTargetChanged(QQuickItem *oldTarget) { Q_UNUSED(oldTarget); }
virtual void onActiveChanged() { }
- virtual void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point);
+ virtual void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point);
virtual bool canGrab(QQuickEventPoint *point);
virtual bool approveGrabTransition(QQuickEventPoint *point, QObject *proposedGrabber);
void setPassiveGrab(QQuickEventPoint *point, bool grab = true);
@@ -133,19 +141,11 @@ protected:
QPointF eventPos(const QQuickEventPoint *point) const;
bool parentContains(const QQuickEventPoint *point) const;
-private:
- QQuickPointerEvent *m_currentEvent;
- QQuickItem *m_target;
- bool m_enabled : 1;
- bool m_active : 1;
- bool m_targetExplicitlySet : 1;
- bool m_hadKeepMouseGrab : 1; // some handlers override target()->setKeepMouseGrab(); this remembers previous state
- bool m_hadKeepTouchGrab : 1; // some handlers override target()->setKeepTouchGrab(); this remembers previous state
- uint m_reserved : 19;
- uint8_t m_grabPermissions : 8;
-
friend class QQuickEventPoint;
+ friend class QQuickItemPrivate;
friend class QQuickWindowPrivate;
+
+ Q_DECLARE_PRIVATE(QQuickPointerHandler)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPointerHandler::GrabPermissions)
diff --git a/src/quick/handlers/qquickpointerhandler_p_p.h b/src/quick/handlers/qquickpointerhandler_p_p.h
new file mode 100644
index 0000000000..2ea4905643
--- /dev/null
+++ b/src/quick/handlers/qquickpointerhandler_p_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPOINTERHANDLER_P_H
+#define QQUICKPOINTERHANDLER_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 "qevent.h"
+
+#include <QtQuick/private/qquickevents_p_p.h>
+#include <QtQuick/private/qquickpointerhandler_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerHandlerPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickPointerHandler)
+
+public:
+ static QQuickPointerHandlerPrivate* get(QQuickPointerHandler *q) { return q->d_func(); }
+ static const QQuickPointerHandlerPrivate* get(const QQuickPointerHandler *q) { return q->d_func(); }
+
+ QQuickPointerHandlerPrivate();
+
+ QQuickPointerEvent *currentEvent = nullptr;
+ QQuickItem *target = nullptr;
+ qreal m_margin = 0;
+ uint8_t grabPermissions : 8;
+ bool enabled : 1;
+ bool active : 1;
+ bool targetExplicitlySet : 1;
+ bool hadKeepMouseGrab : 1; // some handlers override target()->setKeepMouseGrab(); this remembers previous state
+ bool hadKeepTouchGrab : 1; // some handlers override target()->setKeepTouchGrab(); this remembers previous state
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKPOINTERHANDLER_P_H
diff --git a/src/quick/handlers/qquickpointhandler.cpp b/src/quick/handlers/qquickpointhandler.cpp
index 7e2d40452c..3bc1c06a1a 100644
--- a/src/quick/handlers/qquickpointhandler.cpp
+++ b/src/quick/handlers/qquickpointhandler.cpp
@@ -47,8 +47,8 @@ QT_BEGIN_NAMESPACE
\qmltype PointHandler
\instantiates QQuickPointHandler
\inherits SinglePointHandler
- \inqmlmodule Qt.labs.handlers
- \ingroup qtquick-handlers
+ \inqmlmodule QtQuick
+ \ingroup qtquick-input-handlers
\brief Handler for reacting to a single touchpoint.
PointHandler can be used to show feedback about a touchpoint or the mouse
@@ -97,7 +97,7 @@ QT_BEGIN_NAMESPACE
\snippet pointerHandlers/pointHandler.qml 0
- Like all pointer handlers, a PointHandler has a \l target property, which
+ Like all input handlers, a PointHandler has a \l target property, which
may be used as a convenient place to put a point-tracking Item; but
PointHandler will not automatically manipulate the \c target item in any way.
You need to use bindings to make it react to the \l point.
@@ -114,16 +114,12 @@ QT_BEGIN_NAMESPACE
\sa MultiPointTouchArea
*/
-QQuickPointHandler::QQuickPointHandler(QObject *parent)
+QQuickPointHandler::QQuickPointHandler(QQuickItem *parent)
: QQuickSinglePointHandler(parent)
{
setIgnoreAdditionalPoints();
}
-QQuickPointHandler::~QQuickPointHandler()
-{
-}
-
bool QQuickPointHandler::wantsEventPoint(QQuickEventPoint *pt)
{
// On press, we want it unless a sibling of the same type also does.
@@ -143,11 +139,16 @@ void QQuickPointHandler::handleEventPoint(QQuickEventPoint *point)
{
switch (point->state()) {
case QQuickEventPoint::Pressed:
- setPassiveGrab(point);
- setActive(true);
+ if (point->pointerEvent()->asPointerTouchEvent() ||
+ (point->pointerEvent()->buttons() & acceptedButtons()) != Qt::NoButton) {
+ setPassiveGrab(point);
+ setActive(true);
+ }
break;
case QQuickEventPoint::Released:
- setActive(false);
+ if (point->pointerEvent()->asPointerTouchEvent() ||
+ (point->pointerEvent()->buttons() & acceptedButtons()) == Qt::NoButton)
+ setActive(false);
break;
default:
break;
diff --git a/src/quick/handlers/qquickpointhandler_p.h b/src/quick/handlers/qquickpointhandler_p.h
index 5babab0c4d..380ce1f90f 100644
--- a/src/quick/handlers/qquickpointhandler_p.h
+++ b/src/quick/handlers/qquickpointhandler_p.h
@@ -61,8 +61,7 @@ class Q_AUTOTEST_EXPORT QQuickPointHandler : public QQuickSinglePointHandler
Q_PROPERTY(QVector2D translation READ translation NOTIFY translationChanged)
public:
- explicit QQuickPointHandler(QObject *parent = 0);
- ~QQuickPointHandler();
+ explicit QQuickPointHandler(QQuickItem *parent = nullptr);
QVector2D translation() const;
diff --git a/src/quick/handlers/qquicksinglepointhandler.cpp b/src/quick/handlers/qquicksinglepointhandler.cpp
index 9df20390e4..82e5b1b05d 100644
--- a/src/quick/handlers/qquicksinglepointhandler.cpp
+++ b/src/quick/handlers/qquicksinglepointhandler.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -48,8 +48,7 @@ Q_DECLARE_LOGGING_CATEGORY(DBG_TOUCH_TARGET)
\preliminary
\instantiates QQuickSinglePointHandler
\inherits PointerDeviceHandler
- \inqmlmodule Qt.labs.handlers
- \ingroup qtquick-handlers
+ \inqmlmodule QtQuick
\brief Abstract handler for single-point Pointer Events.
An intermediate class (not registered as a QML type)
@@ -59,10 +58,8 @@ Q_DECLARE_LOGGING_CATEGORY(DBG_TOUCH_TARGET)
Override handleEventPoint() to implement a single-point handler.
*/
-QQuickSinglePointHandler::QQuickSinglePointHandler(QObject *parent)
+QQuickSinglePointHandler::QQuickSinglePointHandler(QQuickItem *parent)
: QQuickPointerDeviceHandler(parent)
- , m_acceptedButtons(Qt::LeftButton)
- , m_ignoreAdditionalPoints(false)
{
}
@@ -71,7 +68,7 @@ bool QQuickSinglePointHandler::wantsPointerEvent(QQuickPointerEvent *event)
if (!QQuickPointerDeviceHandler::wantsPointerEvent(event))
return false;
if (event->device()->pointerType() != QQuickPointerDevice::Finger &&
- (event->buttons() & m_acceptedButtons) == 0 && (event->button() & m_acceptedButtons) == 0)
+ (event->buttons() & acceptedButtons()) == 0 && (event->button() & acceptedButtons()) == 0)
return false;
if (m_pointInfo.m_id) {
@@ -79,16 +76,23 @@ bool QQuickSinglePointHandler::wantsPointerEvent(QQuickPointerEvent *event)
// It's expected to be an update or a release.
// If we no longer want it, cancel the grab.
int candidatePointCount = 0;
+ bool missing = true;
QQuickEventPoint *point = nullptr;
int c = event->pointCount();
for (int i = 0; i < c; ++i) {
QQuickEventPoint *p = event->point(i);
+ const bool found = (p->pointId() == m_pointInfo.m_id);
+ if (found)
+ missing = false;
if (wantsEventPoint(p)) {
++candidatePointCount;
- if (p->pointId() == m_pointInfo.m_id)
+ if (found)
point = p;
}
}
+ if (missing)
+ qCWarning(DBG_TOUCH_TARGET) << this << "pointId" << hex << m_pointInfo.m_id
+ << "is missing from current event, but was neither canceled nor released";
if (point) {
if (candidatePointCount == 1 || (candidatePointCount > 1 && m_ignoreAdditionalPoints)) {
point->setAccepted();
@@ -97,8 +101,6 @@ bool QQuickSinglePointHandler::wantsPointerEvent(QQuickPointerEvent *event)
point->cancelAllGrabs(this);
}
} else {
- qCWarning(DBG_TOUCH_TARGET) << this << "pointId" << hex << m_pointInfo.m_id
- << "is missing from current event, but was neither canceled nor released";
return false;
}
} else {
@@ -115,7 +117,7 @@ bool QQuickSinglePointHandler::wantsPointerEvent(QQuickPointerEvent *event)
}
}
if (chosen && candidatePointCount == 1) {
- m_pointInfo.m_id = chosen->pointId();
+ setPointId(chosen->pointId());
chosen->setAccepted();
}
}
@@ -127,64 +129,28 @@ void QQuickSinglePointHandler::handlePointerEventImpl(QQuickPointerEvent *event)
QQuickPointerDeviceHandler::handlePointerEventImpl(event);
QQuickEventPoint *currentPoint = event->pointById(m_pointInfo.m_id);
Q_ASSERT(currentPoint);
- if (!m_pointInfo.m_id || !currentPoint->isAccepted()) {
+ m_pointInfo.reset(currentPoint);
+ handleEventPoint(currentPoint);
+ if (currentPoint->state() == QQuickEventPoint::Released && (event->buttons() & acceptedButtons()) == Qt::NoButton) {
+ setExclusiveGrab(currentPoint, false);
reset();
- } else {
- if (event->asPointerTouchEvent()) {
- QQuickEventTouchPoint *tp = static_cast<QQuickEventTouchPoint *>(currentPoint);
- m_pointInfo.m_uniqueId = tp->uniqueId();
- m_pointInfo.m_rotation = tp->rotation();
- m_pointInfo.m_pressure = tp->pressure();
- m_pointInfo.m_ellipseDiameters = tp->ellipseDiameters();
- } else if (event->asPointerTabletEvent()) {
- // TODO
- } else {
- m_pointInfo.m_uniqueId = event->device()->uniqueId();
- m_pointInfo.m_rotation = 0;
- m_pointInfo.m_pressure = event->buttons() ? 1 : 0;
- m_pointInfo.m_ellipseDiameters = QSizeF();
- }
- m_pointInfo.m_position = currentPoint->position();
- m_pointInfo.m_scenePosition = currentPoint->scenePosition();
- if (currentPoint->state() == QQuickEventPoint::Updated)
- m_pointInfo.m_velocity = currentPoint->velocity();
- handleEventPoint(currentPoint);
- switch (currentPoint->state()) {
- case QQuickEventPoint::Pressed:
- m_pointInfo.m_pressPosition = currentPoint->position();
- m_pointInfo.m_scenePressPosition = currentPoint->scenePosition();
- m_pointInfo.m_pressedButtons = event->buttons();
- break;
- case QQuickEventPoint::Released:
- setExclusiveGrab(currentPoint, false);
- reset();
- break;
- default:
- m_pointInfo.m_pressedButtons = event->buttons();
- break;
- }
- emit pointChanged();
}
+ emit pointChanged();
}
-bool QQuickSinglePointHandler::wantsEventPoint(QQuickEventPoint *point)
-{
- return parentContains(point);
-}
-
-void QQuickSinglePointHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point)
+void QQuickSinglePointHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point)
{
if (grabber != this)
return;
- switch (stateChange) {
+ switch (transition) {
case QQuickEventPoint::GrabExclusive:
m_pointInfo.m_sceneGrabPosition = point->sceneGrabPosition();
setActive(true);
- QQuickPointerHandler::onGrabChanged(grabber, stateChange, point);
+ QQuickPointerHandler::onGrabChanged(grabber, transition, point);
break;
case QQuickEventPoint::GrabPassive:
m_pointInfo.m_sceneGrabPosition = point->sceneGrabPosition();
- QQuickPointerHandler::onGrabChanged(grabber, stateChange, point);
+ QQuickPointerHandler::onGrabChanged(grabber, transition, point);
break;
case QQuickEventPoint::OverrideGrabPassive:
return; // don't emit
@@ -193,11 +159,10 @@ void QQuickSinglePointHandler::onGrabChanged(QQuickPointerHandler *grabber, QQui
case QQuickEventPoint::CancelGrabPassive:
case QQuickEventPoint::CancelGrabExclusive:
// the grab is lost or relinquished, so the point is no longer relevant
- QQuickPointerHandler::onGrabChanged(grabber, stateChange, point);
+ QQuickPointerHandler::onGrabChanged(grabber, transition, point);
reset();
break;
}
- emit singlePointGrabChanged();
}
void QQuickSinglePointHandler::setIgnoreAdditionalPoints(bool v)
@@ -212,42 +177,9 @@ void QQuickSinglePointHandler::moveTarget(QPointF pos, QQuickEventPoint *point)
m_pointInfo.m_position = target()->mapFromScene(m_pointInfo.m_scenePosition);
}
-/*!
- \qmlproperty int QtQuick::SinglePointHandler::acceptedButtons
-
- The mouse buttons which can activate this Pointer Handler.
-
- By default, this property is set to \l {QtQuick::MouseEvent::button} {Qt.LeftButton}.
- It can be set to an OR combination of mouse buttons, and will ignore events
- from other buttons.
-
- For example, a control could be made to respond to left and right clicks
- in different ways, with two handlers:
-
- \qml
- Item {
- TapHandler {
- onTapped: console.log("left clicked")
- }
- TapHandler {
- acceptedButtons: Qt.RightButton
- onTapped: console.log("right clicked")
- }
- }
- \endqml
-
- \note Tapping on a touchscreen or tapping the stylus on a graphics tablet
- emulates clicking the left mouse button. This behavior can be altered via
- \l {PointerDeviceHandler::acceptedDevices}{acceptedDevices} or
- \l {PointerDeviceHandler::acceptedPointerTypes}{acceptedPointerTypes}.
-*/
-void QQuickSinglePointHandler::setAcceptedButtons(Qt::MouseButtons buttons)
+void QQuickSinglePointHandler::setPointId(int id)
{
- if (m_acceptedButtons == buttons)
- return;
-
- m_acceptedButtons = buttons;
- emit acceptedButtonsChanged();
+ m_pointInfo.m_id = id;
}
void QQuickSinglePointHandler::reset()
@@ -264,213 +196,4 @@ void QQuickSinglePointHandler::reset()
handled, this object is reset to default values (all coordinates are 0).
*/
-/*!
- \qmltype HandlerPoint
- \instantiates QQuickHandlerPoint
- \inqmlmodule Qt.labs.handlers
- \ingroup qtquick-handlers
- \brief An event point.
-
- A QML representation of a QQuickEventPoint.
-
- It's possible to make bindings to properties of a \l SinglePointHandler's
- current point. For example:
-
- \snippet pointerHandlers/dragHandlerNullTarget.qml 0
-
- The point is kept up-to-date when the DragHandler is actively responding to
- an EventPoint; but when the point is released, or the current point is
- being handled by a different handler, \c position.x and \c position.y are 0.
-
- \note This is practically identical to QtQuick::EventPoint; however an
- EventPoint is a long-lived QObject which is invalidated between gestures
- and reused for subsequent event deliveries. Continuous bindings to its
- properties are not possible, and an individual handler cannot rely on it
- outside the period when that point is part of an active gesture which that
- handler is handling. HandlerPoint is a Q_GADGET that the handler owns.
- This allows you to make lifetime bindings to its properties.
-
- \sa SinglePointHandler::point
-*/
-
-QQuickHandlerPoint::QQuickHandlerPoint()
- : m_id(0)
- , m_rotation(0)
- , m_pressure(0)
-{}
-
-void QQuickHandlerPoint::reset()
-{
- m_id = 0;
- m_uniqueId = QPointingDeviceUniqueId();
- m_position = QPointF();
- m_scenePosition = QPointF();
- m_pressPosition = QPointF();
- m_scenePressPosition = QPointF();
- m_sceneGrabPosition = QPointF();
- m_velocity = QVector2D();
- m_rotation = 0;
- m_pressure = 0;
- m_ellipseDiameters = QSizeF();
- m_pressedButtons = Qt::NoButton;
-}
-
-/*!
- \readonly
- \qmlproperty int QtQuick::HandlerPoint::id
- \brief The ID number of the point
-
- During a touch gesture, from the time that the first finger is pressed
- until the last finger is released, each touchpoint will have a unique ID
- number. Likewise, if input from multiple devices occurs (for example
- simultaneous mouse and touch presses), all the current event points from
- all the devices will have unique IDs.
-
- \note Do not assume that id numbers start at zero or that they are
- sequential. Such an assumption is often false due to the way the underlying
- drivers work.
-
- \sa QTouchEvent::TouchPoint::id
-*/
-
-/*!
- \readonly
- \qmlproperty PointingDeviceUniqueId QtQuick::HandlerPoint::uniqueId
- \brief The unique ID of the point, if any
-
- This is normally empty, because touchscreens cannot uniquely identify fingers.
-
- On some types of touchscreens, especially those using TUIO drivers,
- it's possible to use recognizable physical tokens (fiducial objects)
- in addition to fingers. So if this point is a touch point, and
- uniqueId is set, it is the identifier for such an object.
-
- On a graphics tablet, each type of stylus or other tool often has a unique
- ID or serial number, which can be useful to respond in different ways to
- different tools.
-
- Interpreting the contents of this ID requires knowledge of the hardware and
- drivers in use.
-
- \sa QTabletEvent::uniqueId, QtQuick::TouchPoint::uniqueId, QtQuick::EventTouchPoint::uniqueId
-*/
-
-/*!
- \readonly
- \qmlproperty QPointF QtQuick::HandlerPoint::position
- \brief The position within the \c parent Item
-
- This is the position of the event point relative to the bounds of
- the \l {PointerHandler::parent} {parent}.
-*/
-
-/*!
- \readonly
- \qmlproperty QPointF QtQuick::HandlerPoint::scenePosition
- \brief The position within the scene
-
- This is the position of the event point relative to the bounds of the Qt
- Quick scene (typically the whole window).
-*/
-
-/*!
- \readonly
- \qmlproperty QPointF QtQuick::HandlerPoint::pressPosition
- \brief The pressed position within the \c parent Item
-
- This is the position at which this point was pressed, relative to the
- bounds of the \l {PointerHandler::parent} {parent}.
-*/
-
-/*!
- \readonly
- \qmlproperty QPointF QtQuick::HandlerPoint::scenePressPosition
- \brief The pressed position within the scene
-
- This is the position at which this point was pressed, in the coordinate
- system of the \l {Qt Quick Scene Graph}{scene graph}.
-*/
-
-/*!
- \readonly
- \qmlproperty QPointF QtQuick::HandlerPoint::sceneGrabPosition
- \brief The grabbed position within the scene
-
- If this point has been grabbed by a Pointer Handler or an Item, it means
- that object has taken sole responsibility for handling the movement and the
- release if this point. In that case, this is the position at which the grab
- occurred, in the coordinate system of the \l {Qt Quick Scene Graph}{scene graph}.
-*/
-
-/*!
- \readonly
- \qmlproperty enum QtQuick::HandlerPoint::pressedButtons
- \brief Which mouse or stylus buttons are currently pressed
-
- \sa MouseArea::pressedButtons
-*/
-
-/*!
- \readonly
- \qmlproperty QVector2D QtQuick::HandlerPoint::velocity
- \brief A vector representing the average speed and direction of movement
-
- This is a velocity vector pointing in the direction of movement, in logical
- pixels per second. It has x and y components, at least one of which will be
- nonzero when this point is in motion. It holds the average recent velocity:
- how fast and in which direction the event point has been moving recently.
-
- \sa QtQuick::EventPoint::velocity, QtQuick::TouchPoint::velocity, QTouchEvent::TouchPoint::velocity
-*/
-
-/*!
- \readonly
- \qmlproperty qreal QtQuick::HandlerPoint::rotation
-
- This property holds the rotation angle of the stylus on a graphics tablet
- or the contact patch of a touchpoint on a touchscreen.
-
- It is valid only with certain tablet stylus devices and touchscreens that
- can measure the rotation angle. Otherwise, it will be zero.
-*/
-
-/*!
- \readonly
- \qmlproperty qreal QtQuick::HandlerPoint::pressure
-
- This property tells how hard the user is pressing the stylus on a graphics
- tablet or the finger against a touchscreen, in the range from \c 0 (no
- measurable pressure) to \c 1.0 (maximum pressure which the device can
- measure).
-
- It is valid only with certain tablets and touchscreens that can measure
- pressure. Otherwise, it will be zero.
-*/
-
-/*!
- \readonly
- \qmlproperty size QtQuick::HandlerPoint::ellipseDiameters
-
- This property holds the diameters of the contact patch, if the event
- comes from a touchpoint and the device provides this information.
-
- A touchpoint is modeled as an elliptical area where the finger is pressed
- against the touchscreen. (In fact, it could also be modeled as a bitmap;
- but in that case we expect an elliptical bounding estimate to be fitted to
- the contact patch before the event is sent.) The harder the user presses,
- the larger the contact patch; so, these diameters provide an alternate way
- of detecting pressure, in case the device does not include a separate
- pressure sensor. The ellipse is centered on \l scenePosition (\l position
- in the PointerHandler's Item's local coordinates). The \l rotation property
- provides the rotation of the ellipse, if known. It is expected that if the
- \l rotation is zero, the \l {QSize::height}{height} is the larger dimension
- (the major axis), because of the usual hand position, reaching upward or
- outward across the surface.
-
- If the contact patch is unknown, or the device is not a touchscreen,
- these values will be zero.
-
- \sa QtQuick::EventTouchPoint::ellipseDiameters, QtQuick::TouchPoint::ellipseDiameters, QTouchEvent::TouchPoint::ellipseDiameters
-*/
-
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquicksinglepointhandler_p.h b/src/quick/handlers/qquicksinglepointhandler_p.h
index 7606b4f7ba..b9e5b12224 100644
--- a/src/quick/handlers/qquicksinglepointhandler_p.h
+++ b/src/quick/handlers/qquicksinglepointhandler_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -51,104 +51,47 @@
// We mean it.
//
+#include "qquickhandlerpoint_p.h"
#include "qquickpointerdevicehandler_p.h"
QT_BEGIN_NAMESPACE
-class QQuickSinglePointHandler;
-
-class Q_QUICK_PRIVATE_EXPORT QQuickHandlerPoint {
- Q_GADGET
- Q_PROPERTY(int id READ id)
- Q_PROPERTY(QPointingDeviceUniqueId uniqueId READ uniqueId)
- Q_PROPERTY(QPointF position READ position)
- Q_PROPERTY(QPointF scenePosition READ scenePosition)
- Q_PROPERTY(QPointF pressPosition READ pressPosition)
- Q_PROPERTY(QPointF scenePressPosition READ scenePressPosition)
- Q_PROPERTY(QPointF sceneGrabPosition READ sceneGrabPosition)
- Q_PROPERTY(Qt::MouseButtons pressedButtons READ pressedButtons)
- Q_PROPERTY(QVector2D velocity READ velocity)
- Q_PROPERTY(qreal rotation READ rotation)
- Q_PROPERTY(qreal pressure READ pressure)
- Q_PROPERTY(QSizeF ellipseDiameters READ ellipseDiameters)
-
-public:
- QQuickHandlerPoint();
-
- int id() const { return m_id; }
- Qt::MouseButtons pressedButtons() const { return m_pressedButtons; }
- QPointF pressPosition() const { return m_pressPosition; }
- QPointF scenePressPosition() const { return m_scenePressPosition; }
- QPointF sceneGrabPosition() const { return m_sceneGrabPosition; }
- QPointF position() const { return m_position; }
- QPointF scenePosition() const { return m_scenePosition; }
- QVector2D velocity() const { return m_velocity; }
- qreal rotation() const { return m_rotation; }
- qreal pressure() const { return m_pressure; }
- QSizeF ellipseDiameters() const { return m_ellipseDiameters; }
- QPointingDeviceUniqueId uniqueId() const { return m_uniqueId; }
-
-private:
- void reset();
- int m_id;
- QPointingDeviceUniqueId m_uniqueId;
- Qt::MouseButtons m_pressedButtons;
- QPointF m_position;
- QPointF m_scenePosition;
- QPointF m_pressPosition;
- QPointF m_scenePressPosition;
- QPointF m_sceneGrabPosition;
- QVector2D m_velocity;
- qreal m_rotation;
- qreal m_pressure;
- QSizeF m_ellipseDiameters;
- friend class QQuickSinglePointHandler;
-};
-
class Q_QUICK_PRIVATE_EXPORT QQuickSinglePointHandler : public QQuickPointerDeviceHandler
{
Q_OBJECT
- Q_PROPERTY(Qt::MouseButtons acceptedButtons READ acceptedButtons WRITE setAcceptedButtons NOTIFY acceptedButtonsChanged)
Q_PROPERTY(QQuickHandlerPoint point READ point NOTIFY pointChanged)
public:
- explicit QQuickSinglePointHandler(QObject *parent = nullptr);
- virtual ~QQuickSinglePointHandler() { }
-
- Qt::MouseButtons acceptedButtons() const { return m_acceptedButtons; }
- void setAcceptedButtons(Qt::MouseButtons buttons);
+ explicit QQuickSinglePointHandler(QQuickItem *parent = nullptr);
QQuickHandlerPoint point() const { return m_pointInfo; }
Q_SIGNALS:
void pointChanged();
- void singlePointGrabChanged(); // QQuickPointerHandler::grabChanged signal can't be a property notifier here
- void acceptedButtonsChanged();
protected:
bool wantsPointerEvent(QQuickPointerEvent *event) override;
- virtual bool wantsEventPoint(QQuickEventPoint *point);
void handlePointerEventImpl(QQuickPointerEvent *event) override;
virtual void handleEventPoint(QQuickEventPoint *point) = 0;
QQuickEventPoint *currentPoint(QQuickPointerEvent *ev) { return ev->pointById(m_pointInfo.m_id); }
- void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override;
+ void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point) override;
void setIgnoreAdditionalPoints(bool v = true);
void moveTarget(QPointF pos, QQuickEventPoint *point);
+ void setPointId(int id);
+
private:
void reset();
private:
QQuickHandlerPoint m_pointInfo;
- Qt::MouseButtons m_acceptedButtons;
- bool m_ignoreAdditionalPoints : 1;
+ bool m_ignoreAdditionalPoints = false;
};
QT_END_NAMESPACE
-QML_DECLARE_TYPE(QQuickHandlerPoint)
QML_DECLARE_TYPE(QQuickSinglePointHandler)
#endif // QQUICKPOINTERSINGLEHANDLER_H
diff --git a/src/quick/handlers/qquicktaphandler.cpp b/src/quick/handlers/qquicktaphandler.cpp
index 902ff0df10..e97722d6b7 100644
--- a/src/quick/handlers/qquicktaphandler.cpp
+++ b/src/quick/handlers/qquicktaphandler.cpp
@@ -55,8 +55,8 @@ int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1);
\qmltype TapHandler
\instantiates QQuickTapHandler
\inherits SinglePointHandler
- \inqmlmodule Qt.labs.handlers
- \ingroup qtquick-handlers
+ \inqmlmodule QtQuick
+ \ingroup qtquick-input-handlers
\brief Handler for taps and clicks.
TapHandler is a handler for taps on a touchscreen or clicks on a mouse.
@@ -65,7 +65,7 @@ int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1);
value is DragThreshold, which requires the press and release to be close
together in both space and time. In this case, DragHandler is able to
function using only a passive grab, and therefore does not interfere with
- event delivery to any other Items or Pointer Handlers. So the default
+ event delivery to any other Items or Input Handlers. So the default
gesturePolicy is useful when you want to modify behavior of an existing
control or Item by adding a TapHandler with bindings and/or JavaScript
callbacks.
@@ -84,13 +84,8 @@ int QQuickTapHandler::m_touchMultiTapDistanceSquared(-1);
\sa MouseArea
*/
-QQuickTapHandler::QQuickTapHandler(QObject *parent)
+QQuickTapHandler::QQuickTapHandler(QQuickItem *parent)
: QQuickSinglePointHandler(parent)
- , m_pressed(false)
- , m_gesturePolicy(DragThreshold)
- , m_tapCount(0)
- , m_longPressThreshold(-1)
- , m_lastTapTimestamp(0.0)
{
if (m_mouseMultiClickDistanceSquared < 0) {
m_multiTapInterval = qApp->styleHints()->mouseDoubleClickInterval() / 1000.0;
@@ -103,11 +98,7 @@ QQuickTapHandler::QQuickTapHandler(QObject *parent)
}
}
-QQuickTapHandler::~QQuickTapHandler()
-{
-}
-
-static bool dragOverThreshold(QQuickEventPoint *point)
+static bool dragOverThreshold(const QQuickEventPoint *point)
{
QPointF delta = point->scenePosition() - point->scenePressPosition();
return (QQuickWindowPrivate::dragOverThreshold(delta.x(), Qt::XAxis, point) ||
@@ -121,6 +112,11 @@ bool QQuickTapHandler::wantsEventPoint(QQuickEventPoint *point)
// (e.g. DragHandler) gets a chance to take over.
// Don't forget to emit released in case of a cancel.
bool ret = false;
+ bool overThreshold = dragOverThreshold(point);
+ if (overThreshold) {
+ m_longPressTimer.stop();
+ m_holdTimer.invalidate();
+ }
switch (point->state()) {
case QQuickEventPoint::Pressed:
case QQuickEventPoint::Released:
@@ -129,7 +125,7 @@ bool QQuickTapHandler::wantsEventPoint(QQuickEventPoint *point)
case QQuickEventPoint::Updated:
switch (m_gesturePolicy) {
case DragThreshold:
- ret = !dragOverThreshold(point);
+ ret = !overThreshold;
break;
case WithinBounds:
ret = parentContains(point);
@@ -209,19 +205,6 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event)
}
/*!
- \qmlsignal QtQuick::TapHandler::tapped()
-
- This signal is emitted when the pointer device taps the item.
- */
-
-/*!
- \qmlsignal QtQuick::TapHandler::longPressed()
-
- This signal is emitted when a press occurs that is longer than the
- \l {TapHandler::longPressThreshold} {long press threshold}.
- */
-
-/*!
\qmlproperty enumeration QtQuick::TapHandler::gesturePolicy
The spatial constraint for a tap or long press gesture to be recognized,
@@ -237,12 +220,12 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event)
threshold (QStyleHints::startDragDistance), the tap gesture is
canceled, even if the button or finger is still pressed. This policy
can be useful whenever TapHandler needs to cooperate with other
- pointer handlers (for example \l DragHandler) or event-handling Items
+ input handlers (for example \l DragHandler) or event-handling Items
(for example QtQuick Controls), because in this case TapHandler
will not take the exclusive grab, but merely a passive grab.
\value TapHandler.WithinBounds
- If the event point leaves the bounds of the \l target item, the tap
+ If the event point leaves the bounds of the \l parent Item, the tap
gesture is canceled. The TapHandler will take the exclusive grab on
press, but will release the grab as soon as the boundary constraint
is no longer satisfied.
@@ -250,7 +233,7 @@ void QQuickTapHandler::timerEvent(QTimerEvent *event)
\value TapHandler.ReleaseWithinBounds
At the time of release (the mouse button is released or the finger
is lifted), if the event point is outside the bounds of the
- \l target item, a tap gesture is not recognized. This corresponds to
+ \l parent Item, a tap gesture is not recognized. This corresponds to
typical behavior for button widgets: you can cancel a click by
dragging outside the button, and you can also change your mind by
dragging back inside the button before release. Note that it's
@@ -308,12 +291,12 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi
else
m_tapCount = 1;
qCDebug(lcTapHandler) << objectName() << "tapped" << m_tapCount << "times";
- emit tapped();
+ emit tapped(point);
emit tapCountChanged();
if (m_tapCount == 1)
- emit singleTapped();
+ emit singleTapped(point);
else if (m_tapCount == 2)
- emit doubleTapped();
+ emit doubleTapped(point);
m_lastTapTimestamp = ts;
m_lastTapPos = point->scenePosition();
} else {
@@ -330,10 +313,10 @@ void QQuickTapHandler::setPressed(bool press, bool cancel, QQuickEventPoint *poi
}
}
-void QQuickTapHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point)
+void QQuickTapHandler::onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point)
{
- QQuickSinglePointHandler::onGrabChanged(grabber, stateChange, point);
- bool isCanceled = stateChange == QQuickEventPoint::CancelGrabExclusive || stateChange == QQuickEventPoint::CancelGrabPassive;
+ QQuickSinglePointHandler::onGrabChanged(grabber, transition, point);
+ bool isCanceled = transition == QQuickEventPoint::CancelGrabExclusive || transition == QQuickEventPoint::CancelGrabPassive;
if (grabber == this && (isCanceled || point->state() == QQuickEventPoint::Released))
setPressed(false, isCanceled, point);
}
@@ -387,23 +370,45 @@ void QQuickTapHandler::updateTimeHeld()
*/
/*!
- \qmlsignal TapHandler::singleTapped
+ \qmlsignal QtQuick::TapHandler::tapped
+
+ This signal is emitted each time the \l parent Item is tapped.
+
+ That is, if you press and release a touchpoint or button within a time
+ period less than \l longPressThreshold, while any movement does not exceed
+ the drag threshold, then the \c tapped signal will be emitted at the time
+ of release.
+*/
+
+/*!
+ \qmlsignal QtQuick::TapHandler::singleTapped
\since 5.11
- This signal is emitted when the \l target is tapped once. After an amount
- of time greater than QStyleHints::mouseDoubleClickInterval, it can be
- tapped again; but if the time until the next tap is less, \l tapCount
- will increase.
+ This signal is emitted when the \l parent Item is tapped once.
+ After an amount of time greater than QStyleHints::mouseDoubleClickInterval,
+ it can be tapped again; but if the time until the next tap is less,
+ \l tapCount will increase.
*/
/*!
- \qmlsignal TapHandler::doubleTapped
+ \qmlsignal QtQuick::TapHandler::doubleTapped
\since 5.11
- This signal is emitted when the \l target is tapped twice within a short
- span of time (QStyleHints::mouseDoubleClickInterval) and distance
+ This signal is emitted when the \l parent Item is tapped twice within a
+ short span of time (QStyleHints::mouseDoubleClickInterval) and distance
(QPlatformTheme::MouseDoubleClickDistance or
- QPlatformTheme::TouchDoubleTapDistance). This signal always occurs
- after singleTapped, tapped and tapCountChanged.
+ QPlatformTheme::TouchDoubleTapDistance). This signal always occurs after
+ \l singleTapped, \l tapped, and \l tapCountChanged.
*/
+
+/*!
+ \qmlsignal QtQuick::TapHandler::longPressed
+
+ This signal is emitted when the \l parent Item is pressed and held for a
+ time period greater than \l longPressThreshold. That is, if you press and
+ hold a touchpoint or button, while any movement does not exceed the drag
+ threshold, then the \c longPressed signal will be emitted at the time that
+ \l timeHeld exceeds \l longPressThreshold.
+*/
+
QT_END_NAMESPACE
diff --git a/src/quick/handlers/qquicktaphandler_p.h b/src/quick/handlers/qquicktaphandler_p.h
index b7c1895926..6ec5d55227 100644
--- a/src/quick/handlers/qquicktaphandler_p.h
+++ b/src/quick/handlers/qquicktaphandler_p.h
@@ -75,8 +75,7 @@ public:
};
Q_ENUM(GesturePolicy)
- explicit QQuickTapHandler(QObject *parent = nullptr);
- ~QQuickTapHandler();
+ explicit QQuickTapHandler(QQuickItem *parent = nullptr);
bool isPressed() const { return m_pressed; }
@@ -95,13 +94,13 @@ Q_SIGNALS:
void timeHeldChanged();
void longPressThresholdChanged();
void gesturePolicyChanged();
- void tapped();
- void singleTapped();
- void doubleTapped();
+ void tapped(QQuickEventPoint *eventPoint);
+ void singleTapped(QQuickEventPoint *eventPoint);
+ void doubleTapped(QQuickEventPoint *eventPoint);
void longPressed();
protected:
- void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabState stateChange, QQuickEventPoint *point) override;
+ void onGrabChanged(QQuickPointerHandler *grabber, QQuickEventPoint::GrabTransition transition, QQuickEventPoint *point) override;
void timerEvent(QTimerEvent *event) override;
bool wantsEventPoint(QQuickEventPoint *point) override;
void handleEventPoint(QQuickEventPoint *point) override;
@@ -113,14 +112,14 @@ private:
void updateTimeHeld();
private:
- bool m_pressed;
- GesturePolicy m_gesturePolicy;
- int m_tapCount;
- int m_longPressThreshold;
- QBasicTimer m_longPressTimer;
- QElapsedTimer m_holdTimer;
QPointF m_lastTapPos;
- qreal m_lastTapTimestamp;
+ qreal m_lastTapTimestamp = 0;
+ QElapsedTimer m_holdTimer;
+ QBasicTimer m_longPressTimer;
+ int m_tapCount = 0;
+ int m_longPressThreshold = -1;
+ GesturePolicy m_gesturePolicy = GesturePolicy::DragThreshold;
+ bool m_pressed = false;
static qreal m_multiTapInterval;
static int m_mouseMultiClickDistanceSquared;
diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp
index eee64c9663..06dddabb65 100644
--- a/src/quick/items/context2d/qquickcanvasitem.cpp
+++ b/src/quick/items/context2d/qquickcanvasitem.cpp
@@ -735,7 +735,7 @@ void QQuickCanvasItem::updatePolish()
for (auto it = animationCallbacks.cbegin(), end = animationCallbacks.cend(); it != end; ++it) {
function = it.value().value();
- jsCall->args[0] = QV4::Primitive::fromUInt32(QDateTime::currentMSecsSinceEpoch() / 1000);
+ jsCall->args[0] = QV4::Value::fromUInt32(QDateTime::currentMSecsSinceEpoch() / 1000);
function->call(jsCall);
}
}
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index 2c115f4d3c..32336b5baf 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -563,6 +563,8 @@ struct QQuickJSContext2D : public QV4::Object
static QV4::ReturnedValue method_set_lineWidth(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_get_miterLimit(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_set_miterLimit(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
+ static QV4::ReturnedValue method_set_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
+ static QV4::ReturnedValue method_get_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_get_shadowBlur(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_set_shadowBlur(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
@@ -596,7 +598,7 @@ public:
static QV4::Heap::QQuickJSContext2DPrototype *create(QV4::ExecutionEngine *engine)
{
QV4::Scope scope(engine);
- QV4::Scoped<QQuickJSContext2DPrototype> o(scope, engine->memoryManager->allocObject<QQuickJSContext2DPrototype>());
+ QV4::Scoped<QQuickJSContext2DPrototype> o(scope, engine->memoryManager->allocate<QQuickJSContext2DPrototype>());
o->defineDefaultProperty(QStringLiteral("quadraticCurveTo"), method_quadraticCurveTo, 0);
o->defineDefaultProperty(QStringLiteral("restore"), method_restore, 0);
@@ -641,6 +643,8 @@ public:
o->defineDefaultProperty(QStringLiteral("createLinearGradient"), method_createLinearGradient, 0);
o->defineDefaultProperty(QStringLiteral("strokeRect"), method_strokeRect, 0);
o->defineDefaultProperty(QStringLiteral("closePath"), method_closePath, 0);
+ o->defineDefaultProperty(QStringLiteral("setLineDash"), method_setLineDash, 0);
+ o->defineDefaultProperty(QStringLiteral("getLineDash"), method_getLineDash, 0);
o->defineAccessorProperty(QStringLiteral("canvas"), QQuickJSContext2DPrototype::method_get_canvas, nullptr);
return o->d();
@@ -690,6 +694,8 @@ public:
static QV4::ReturnedValue method_createImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_getImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
static QV4::ReturnedValue method_putImageData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
+ static QV4::ReturnedValue method_setLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
+ static QV4::ReturnedValue method_getLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
};
@@ -908,8 +914,8 @@ struct QQuickJSContext2DPixelData : public QV4::Object
V4_OBJECT2(QQuickJSContext2DPixelData, QV4::Object)
V4_NEEDS_DESTROY
- static QV4::ReturnedValue getIndexed(const QV4::Managed *m, uint index, bool *hasProperty);
- static bool putIndexed(QV4::Managed *m, uint index, const QV4::Value &value);
+ static QV4::ReturnedValue virtualGet(const QV4::Managed *m, QV4::PropertyKey id, const QV4::Value *receiver, bool *hasProperty);
+ static bool virtualPut(QV4::Managed *m, QV4::PropertyKey id, const QV4::Value &value, Value *receiver);
static QV4::ReturnedValue proto_get_length(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
};
@@ -938,7 +944,7 @@ struct QQuickJSContext2DImageData : public QV4::Object
void QV4::Heap::QQuickJSContext2DImageData::init()
{
Object::init();
- pixelData = QV4::Primitive::undefinedValue();
+ pixelData = QV4::Value::undefinedValue();
QV4::Scope scope(internalClass->engine);
QV4::ScopedObject o(scope, this);
@@ -954,19 +960,19 @@ static QV4::ReturnedValue qt_create_image_data(qreal w, qreal h, QV4::ExecutionE
{
QV4::Scope scope(v4);
QQuickContext2DEngineData *ed = engineData(scope.engine);
- QV4::Scoped<QQuickJSContext2DPixelData> pixelData(scope, scope.engine->memoryManager->allocObject<QQuickJSContext2DPixelData>());
+ QV4::Scoped<QQuickJSContext2DPixelData> pixelData(scope, scope.engine->memoryManager->allocate<QQuickJSContext2DPixelData>());
QV4::ScopedObject p(scope, ed->pixelArrayProto.value());
- pixelData->setPrototype(p);
+ pixelData->setPrototypeOf(p);
if (image.isNull()) {
*pixelData->d()->image = QImage(w, h, QImage::Format_ARGB32);
pixelData->d()->image->fill(0x00000000);
} else {
- Q_ASSERT(image.width()== qRound(w * image.devicePixelRatio()) && image.height() == qRound(h * image.devicePixelRatio()));
+ Q_ASSERT(image.width()== qRound(w * image.devicePixelRatioF()) && image.height() == qRound(h * image.devicePixelRatioF()));
*pixelData->d()->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32);
}
- QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, scope.engine->memoryManager->allocObject<QQuickJSContext2DImageData>());
+ QV4::Scoped<QQuickJSContext2DImageData> imageData(scope, scope.engine->memoryManager->allocate<QQuickJSContext2DImageData>());
imageData->d()->pixelData = pixelData.asReturnedValue();
return imageData.asReturnedValue();
}
@@ -1406,7 +1412,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_fillStyle(const QV4::FunctionOb
QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
CHECK_CONTEXT_SETTER(r)
- QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Primitive::undefinedValue());
+ QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Value::undefinedValue());
if (value->as<Object>()) {
QColor color = scope.engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>();
@@ -1461,7 +1467,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_fillRule(const QV4::FunctionObj
QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
CHECK_CONTEXT_SETTER(r)
- QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Primitive::undefinedValue());
+ QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Value::undefinedValue());
if ((value->isString() && value->toQString() == QLatin1String("WindingFill"))
|| (value->isInt32() && value->integerValue() == Qt::WindingFill)) {
@@ -1515,7 +1521,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_strokeStyle(const QV4::Function
QV4::Scoped<QQuickJSContext2D> r(scope, thisObject->as<QQuickJSContext2D>());
CHECK_CONTEXT_SETTER(r)
- QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Primitive::undefinedValue());
+ QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Value::undefinedValue());
if (value->as<Object>()) {
QColor color = scope.engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>();
@@ -1582,9 +1588,9 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createLinearGradient(const
}
QQuickContext2DEngineData *ed = engineData(scope.engine);
- QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
+ QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
QV4::ScopedObject p(scope, ed->gradientProto.value());
- gradient->setPrototype(p);
+ gradient->setPrototypeOf(p);
*gradient->d()->brush = QLinearGradient(x0, y0, x1, y1);
RETURN_RESULT(*gradient);
}
@@ -1634,9 +1640,9 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createRadialGradient(const
QQuickContext2DEngineData *ed = engineData(scope.engine);
- QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
+ QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
QV4::ScopedObject p(scope, ed->gradientProto.value());
- gradient->setPrototype(p);
+ gradient->setPrototypeOf(p);
*gradient->d()->brush = QRadialGradient(QPointF(x1, y1), r1, QPointF(x0, y0), r0);
RETURN_RESULT(*gradient);
}
@@ -1678,9 +1684,9 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createConicalGradient(cons
QQuickContext2DEngineData *ed = engineData(scope.engine);
- QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
+ QV4::Scoped<QQuickContext2DStyle> gradient(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
QV4::ScopedObject p(scope, ed->gradientProto.value());
- gradient->setPrototype(p);
+ gradient->setPrototypeOf(p);
*gradient->d()->brush = QConicalGradient(x, y, angle);
RETURN_RESULT(*gradient);
}
@@ -1738,7 +1744,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_createPattern(const QV4::F
CHECK_CONTEXT(r)
if (argc >= 2) {
- QV4::Scoped<QQuickContext2DStyle> pattern(scope, scope.engine->memoryManager->allocObject<QQuickContext2DStyle>());
+ QV4::Scoped<QQuickContext2DStyle> pattern(scope, scope.engine->memoryManager->allocate<QQuickContext2DStyle>());
QColor color = scope.engine->toVariant(argv[0], qMetaTypeId<QColor>()).value<QColor>();
if (color.isValid()) {
@@ -1963,6 +1969,122 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_miterLimit(const QV4::FunctionO
RETURN_UNDEFINED();
}
+/*!
+ \qmlmethod array QtQuick::Context2D::getLineDash()
+ \since QtQuick 2.11
+ Returns an array of qreals representing the dash pattern of the line.
+
+ \sa setLineDash()
+ */
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_getLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+{
+ QV4::Scope scope(b);
+ QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
+ CHECK_CONTEXT(r)
+
+ const QVector<qreal> pattern = r->d()->context->state.lineDash;
+ QV4::ScopedArrayObject array(scope, scope.engine->newArrayObject(pattern.size()));
+ array->arrayReserve(pattern.size());
+ for (int i = 0; i < pattern.size(); i++)
+ array->put(i, QV4::Value::fromDouble(pattern[i]));
+
+ array->setArrayLengthUnchecked(pattern.size());
+
+ RETURN_RESULT(*array);
+}
+
+/*!
+ \qmlmethod QtQuick::Context2D::setLineDash(array pattern)
+ \since QtQuick 2.11
+ Sets the dash pattern to the given pattern
+
+ \a pattern a list of numbers that specifies distances to alternately draw a line and a gap.
+
+ If the number of elements in the array is odd, the elements of the array get copied
+ and concatenated. For example, [5, 15, 25] will become [5, 15, 25, 5, 15, 25].
+
+ \table 100%
+ \row
+ \li \inlineimage qml-item-canvas-lineDash.png
+ \li
+ \code
+ var space = 4
+ ctx.setLineDash([1, space, 3, space, 9, space, 27, space, 9, space])
+ ...
+ ctx.stroke();
+ \endcode
+ \endtable
+
+ \sa setLineDash
+ */
+QV4::ReturnedValue QQuickJSContext2DPrototype::method_setLineDash(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
+{
+ QV4::Scope scope(b);
+ QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
+ CHECK_CONTEXT_SETTER(r)
+
+ if (!argc)
+ RETURN_UNDEFINED();
+
+ QV4::ScopedArrayObject array(scope, argv[0]);
+ if (!array)
+ RETURN_UNDEFINED();
+
+ QV4::ScopedValue v(scope);
+ const uint arrayLength = array->getLength();
+ QVector<qreal> dashes;
+ dashes.reserve(arrayLength);
+ for (uint i = 0; i < arrayLength; ++i) {
+ v = array->get(i);
+ const double number = v->toNumber();
+
+ if (!qt_is_finite(number) || (number < 0))
+ RETURN_UNDEFINED();
+
+ dashes.append(v->toNumber());
+ }
+ if (dashes.size() % 2 != 0) {
+ dashes += dashes;
+ }
+
+ r->d()->context->state.lineDash = dashes;
+ r->d()->context->buffer()->setLineDash(dashes);
+
+ RETURN_UNDEFINED();
+}
+
+/*!
+ \qmlproperty real QtQuick::Context2D::lineDashOffset
+ \since QtQuick 2.11
+
+ Holds the current line dash offset
+ The default line dash ofset value is 0
+ */
+QV4::ReturnedValue QQuickJSContext2D::method_get_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
+{
+ QV4::Scope scope(b);
+ QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
+ CHECK_CONTEXT(r)
+
+ RETURN_RESULT(QV4::Encode(r->d()->context->state.lineDashOffset));
+}
+
+QV4::ReturnedValue QQuickJSContext2D::method_set_lineDashOffset(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
+{
+ QV4::Scope scope(b);
+ QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
+ CHECK_CONTEXT_SETTER(r)
+
+ const qreal offset = argc ? argv[0].toNumber() : -1;
+
+ if (qt_is_finite(offset) && offset != r->d()->context->state.lineDashOffset) {
+ r->d()->context->state.lineDashOffset = offset;
+ r->d()->context->buffer()->setLineDashOffset(offset);
+ }
+ RETURN_UNDEFINED();
+}
+
+
// shadows
/*!
\qmlproperty real QtQuick::Context2D::shadowBlur
@@ -2096,7 +2218,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_path(const QV4::FunctionObject
QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
CHECK_CONTEXT_SETTER(r)
- QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Primitive::undefinedValue());
+ QV4::ScopedValue value(scope, argc ? argv[0] : QV4::Value::undefinedValue());
r->d()->context->beginPath();
QV4::Scoped<QV4::QObjectWrapper> qobjectWrapper(scope, value);
if (!!qobjectWrapper) {
@@ -2593,7 +2715,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_isPointInPath(const QV4::F
bool pointInPath = false;
if (argc >= 2)
pointInPath = r->d()->context->isPointInPath(argv[0].toNumber(), argv[1].toNumber());
- RETURN_RESULT(QV4::Primitive::fromBoolean(pointInPath).asReturnedValue());
+ RETURN_RESULT(QV4::Value::fromBoolean(pointInPath).asReturnedValue());
}
QV4::ReturnedValue QQuickJSContext2DPrototype::method_drawFocusRing(const QV4::FunctionObject *b, const QV4::Value *, const QV4::Value *, int)
@@ -2652,7 +2774,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_font(const QV4::FunctionObject
QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
CHECK_CONTEXT_SETTER(r)
- QV4::ScopedString s(scope, argc ? argv[0] : QV4::Primitive::undefinedValue(), QV4::ScopedString::Convert);
+ QV4::ScopedString s(scope, argc ? argv[0] : QV4::Value::undefinedValue(), QV4::ScopedString::Convert);
if (scope.engine->hasException)
RETURN_UNDEFINED();
QFont font = qt_font_from_string(s->toQString(), r->d()->context->state.font);
@@ -2704,7 +2826,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_textAlign(const QV4::FunctionOb
QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
CHECK_CONTEXT_SETTER(r)
- QV4::ScopedString s(scope, argc ? argv[0] : QV4::Primitive::undefinedValue(), QV4::ScopedString::Convert);
+ QV4::ScopedString s(scope, argc ? argv[0] : QV4::Value::undefinedValue(), QV4::ScopedString::Convert);
if (scope.engine->hasException)
RETURN_UNDEFINED();
QString textAlign = s->toQString();
@@ -2771,7 +2893,7 @@ QV4::ReturnedValue QQuickJSContext2D::method_set_textBaseline(const QV4::Functio
QV4::Scope scope(b);
QV4::Scoped<QQuickJSContext2D> r(scope, *thisObject);
CHECK_CONTEXT_SETTER(r)
- QV4::ScopedString s(scope, argc ? argv[0] : QV4::Primitive::undefinedValue(), QV4::ScopedString::Convert);
+ QV4::ScopedString s(scope, argc ? argv[0] : QV4::Value::undefinedValue(), QV4::ScopedString::Convert);
if (scope.engine->hasException)
RETURN_UNDEFINED();
QString textBaseline = s->toQString();
@@ -2858,7 +2980,7 @@ QV4::ReturnedValue QQuickJSContext2DPrototype::method_measureText(const QV4::Fun
uint width = fm.width(argv[0].toQStringNoThrow());
QV4::ScopedObject tm(scope, scope.engine->newObject());
tm->put(QV4::ScopedString(scope, scope.engine->newIdentifier(QStringLiteral("width"))).getPointer(),
- QV4::ScopedValue(scope, QV4::Primitive::fromDouble(width)));
+ QV4::ScopedValue(scope, QV4::Value::fromDouble(width)));
RETURN_RESULT(*tm);
}
RETURN_UNDEFINED();
@@ -3130,8 +3252,12 @@ QV4::ReturnedValue QQuickJSContext2DPixelData::proto_get_length(const QV4::Funct
RETURN_RESULT(QV4::Encode(r->d()->image->width() * r->d()->image->height() * 4));
}
-QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(const QV4::Managed *m, uint index, bool *hasProperty)
+QV4::ReturnedValue QQuickJSContext2DPixelData::virtualGet(const QV4::Managed *m, QV4::PropertyKey id, const QV4::Value *receiver, bool *hasProperty)
{
+ if (!id.isArrayIndex())
+ return QV4::Object::virtualGet(m, id, receiver, hasProperty);
+
+ uint index = id.asArrayIndex();
Q_ASSERT(m->as<QQuickJSContext2DPixelData>());
QV4::ExecutionEngine *v4 = static_cast<const QQuickJSContext2DPixelData *>(m)->engine();
QV4::Scope scope(v4);
@@ -3156,19 +3282,24 @@ QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(const QV4::Managed *m,
return QV4::Encode(qAlpha(*pixel));
}
}
+
if (hasProperty)
*hasProperty = false;
return QV4::Encode::undefined();
}
-bool QQuickJSContext2DPixelData::putIndexed(QV4::Managed *m, uint index, const QV4::Value &value)
+bool QQuickJSContext2DPixelData::virtualPut(QV4::Managed *m, QV4::PropertyKey id, const QV4::Value &value, QV4::Value *receiver)
{
+ if (!id.isArrayIndex())
+ return Object::virtualPut(m, id, value, receiver);
+
Q_ASSERT(m->as<QQuickJSContext2DPixelData>());
QV4::ExecutionEngine *v4 = static_cast<QQuickJSContext2DPixelData *>(m)->engine();
QV4::Scope scope(v4);
if (scope.hasException())
return false;
+ uint index = id.asArrayIndex();
QV4::Scoped<QQuickJSContext2DPixelData> r(scope, static_cast<QQuickJSContext2DPixelData *>(m));
const int v = value.toInt32();
@@ -4303,6 +4434,7 @@ QQuickContext2DEngineData::QQuickContext2DEngineData(QV4::ExecutionEngine *v4)
proto->defineAccessorProperty(QStringLiteral("lineWidth"), QQuickJSContext2D::method_get_lineWidth, QQuickJSContext2D::method_set_lineWidth);
proto->defineAccessorProperty(QStringLiteral("textAlign"), QQuickJSContext2D::method_get_textAlign, QQuickJSContext2D::method_set_textAlign);
proto->defineAccessorProperty(QStringLiteral("shadowBlur"), QQuickJSContext2D::method_get_shadowBlur, QQuickJSContext2D::method_set_shadowBlur);
+ proto->defineAccessorProperty(QStringLiteral("lineDashOffset"), QQuickJSContext2D::method_get_lineDashOffset, QQuickJSContext2D::method_set_lineDashOffset);
contextPrototype = proto;
proto = scope.engine->newObject();
@@ -4366,6 +4498,10 @@ void QQuickContext2D::popState()
if (newState.shadowOffsetY != state.shadowOffsetY)
buffer()->setShadowOffsetY(newState.shadowOffsetY);
+
+ if (newState.lineDash != state.lineDash)
+ buffer()->setLineDash(newState.lineDash);
+
m_path = state.matrix.map(m_path);
state = newState;
m_path = state.matrix.inverted().map(m_path);
@@ -4399,9 +4535,9 @@ void QQuickContext2D::setV4Engine(QV4::ExecutionEngine *engine)
QQuickContext2DEngineData *ed = engineData(engine);
QV4::Scope scope(engine);
- QV4::Scoped<QQuickJSContext2D> wrapper(scope, engine->memoryManager->allocObject<QQuickJSContext2D>());
+ QV4::Scoped<QQuickJSContext2D> wrapper(scope, engine->memoryManager->allocate<QQuickJSContext2D>());
QV4::ScopedObject p(scope, ed->contextPrototype.value());
- wrapper->setPrototype(p);
+ wrapper->setPrototypeOf(p);
wrapper->d()->context = this;
m_v4value = wrapper;
}
diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h
index 4cc07027b1..1ece6796f3 100644
--- a/src/quick/items/context2d/qquickcontext2d_p.h
+++ b/src/quick/items/context2d/qquickcontext2d_p.h
@@ -112,6 +112,8 @@ public:
LineWidth,
LineCap,
LineJoin,
+ LineDash,
+ LineDashOffset,
MiterLimit,
ShadowOffsetX,
ShadowOffsetY,
@@ -142,6 +144,7 @@ public:
, lineWidth(1)
, lineCap(Qt::FlatCap)
, lineJoin(Qt::MiterJoin)
+ , lineDashOffset(0)
, miterLimit(10)
, shadowOffsetX(0)
, shadowOffsetY(0)
@@ -170,6 +173,8 @@ public:
qreal lineWidth;
Qt::PenCapStyle lineCap;
Qt::PenJoinStyle lineJoin;
+ QVector<qreal> lineDash;
+ qreal lineDashOffset;
qreal miterLimit;
qreal shadowOffsetX;
qreal shadowOffsetY;
diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
index 30895d9b0e..55ebbe907c 100644
--- a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
+++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
@@ -192,6 +192,10 @@ QPen QQuickContext2DCommandBuffer::makePen(const QQuickContext2D::State& state)
pen.setJoinStyle(state.lineJoin);
pen.setMiterLimit(state.miterLimit);
pen.setBrush(state.strokeStyle);
+ if (!state.lineDash.isEmpty()) {
+ pen.setDashPattern(state.lineDash);
+ }
+ pen.setDashOffset(state.lineDashOffset);
return pen;
}
@@ -361,6 +365,28 @@ void QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State& s
p->setPen(nPen);
break;
}
+ case QQuickContext2D::LineDash:
+ {
+ const qreal count = takeReal();
+ QVector<qreal> pattern;
+ pattern.reserve(count);
+ for (uint i = 0; i < count; i++) {
+ pattern.append(takeReal());
+ }
+ state.lineDash = pattern;
+ QPen nPen = p->pen();
+ nPen.setDashPattern(pattern);
+ p->setPen(nPen);
+ break;
+ }
+ case QQuickContext2D::LineDashOffset:
+ {
+ state.lineDashOffset = takeReal();
+ QPen nPen = p->pen();
+ nPen.setDashOffset(state.lineDashOffset);
+ p->setPen(nPen);
+ break;
+ }
case QQuickContext2D::MiterLimit:
{
state.miterLimit = takeMiterLimit();
diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h
index 2a1ac7304e..c3e844c929 100644
--- a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h
+++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h
@@ -201,6 +201,20 @@ public:
ints << join;
}
+ inline void setLineDash(const QVector<qreal> &pattern)
+ {
+ commands << QQuickContext2D::LineDash;
+ reals << pattern.length();
+ for (qreal r : pattern)
+ reals << r;
+ }
+
+ inline void setLineDashOffset( qreal offset)
+ {
+ commands << QQuickContext2D::LineDashOffset;
+ reals << offset;
+ }
+
inline void setMiterLimit( qreal limit)
{
commands << QQuickContext2D::MiterLimit;
diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri
index 1acb3b5265..e649b5429d 100644
--- a/src/quick/items/items.pri
+++ b/src/quick/items/items.pri
@@ -122,9 +122,11 @@ qtConfig(quick-gridview) {
qtConfig(quick-itemview) {
HEADERS += \
+ $$PWD/qquickitemviewfxitem_p_p.h \
$$PWD/qquickitemview_p.h \
$$PWD/qquickitemview_p_p.h
SOURCES += \
+ $$PWD/qquickitemviewfxitem.cpp \
$$PWD/qquickitemview.cpp
}
@@ -142,6 +144,14 @@ qtConfig(quick-listview) {
$$PWD/qquicklistview.cpp
}
+qtConfig(quick-tableview) {
+ HEADERS += \
+ $$PWD/qquicktableview_p.h \
+ $$PWD/qquicktableview_p_p.h
+ SOURCES += \
+ $$PWD/qquicktableview.cpp
+}
+
qtConfig(quick-pathview) {
HEADERS += \
$$PWD/qquickpathview_p.h \
diff --git a/src/quick/items/qquickanchors_p.h b/src/quick/items/qquickanchors_p.h
index 931b963534..c7995cb7a8 100644
--- a/src/quick/items/qquickanchors_p.h
+++ b/src/quick/items/qquickanchors_p.h
@@ -108,6 +108,7 @@ public:
Vertical_Mask = TopAnchor | BottomAnchor | VCenterAnchor | BaselineAnchor
};
Q_DECLARE_FLAGS(Anchors, Anchor)
+ Q_FLAG(Anchors)
QQuickAnchorLine left() const;
void setLeft(const QQuickAnchorLine &edge);
diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp
index 603d5a3120..18adb4e992 100644
--- a/src/quick/items/qquickanimatedsprite.cpp
+++ b/src/quick/items/qquickanimatedsprite.cpp
@@ -268,6 +268,16 @@ QT_BEGIN_NAMESPACE
Stops, then starts the sprite animation.
*/
+/*!
+ \qmlsignal QtQuick::AnimatedSprite::finished()
+ \since 5.12
+
+ This signal is emitted when the sprite has finished animating.
+
+ It is not emitted when running is set to \c false, nor for sprites whose
+ \l loops property is set to \c AnimatedSprite.Infinite.
+*/
+
QQuickAnimatedSprite::QQuickAnimatedSprite(QQuickItem *parent) :
QQuickItem(*(new QQuickAnimatedSpritePrivate), parent)
{
@@ -806,6 +816,7 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGSpriteNode *node)
frameAt = 0;
d->m_running = false;
emit runningChanged(false);
+ emit finished();
maybeUpdate();
}
} else {
diff --git a/src/quick/items/qquickanimatedsprite_p.h b/src/quick/items/qquickanimatedsprite_p.h
index d7e60b909c..ff59591c9f 100644
--- a/src/quick/items/qquickanimatedsprite_p.h
+++ b/src/quick/items/qquickanimatedsprite_p.h
@@ -135,6 +135,8 @@ Q_SIGNALS:
void loopsChanged(int arg);
void currentFrameChanged(int arg);
+ Q_REVISION(12) void finished();
+
public Q_SLOTS:
void start();
void stop();
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp
index d4aba31e28..31c56b7cb7 100644
--- a/src/quick/items/qquickevents.cpp
+++ b/src/quick/items/qquickevents.cpp
@@ -642,6 +642,11 @@ QQuickPointerDevice *QQuickPointerDevice::touchDevice(const QTouchDevice *d)
return dev;
}
+const QTouchDevice *QQuickPointerDevice::qTouchDevice() const
+{
+ return g_touchDevices->key(const_cast<QQuickPointerDevice *>(this));
+}
+
QList<QQuickPointerDevice*> QQuickPointerDevice::touchDevices()
{
return g_touchDevices->values();
@@ -901,7 +906,7 @@ void QQuickEventPoint::setGrabberPointerHandler(QQuickPointerHandler *grabber, b
if (grabber) {
grabber->onGrabChanged(grabber, GrabExclusive, this);
for (QPointer<QQuickPointerHandler> passiveGrabber : m_passiveGrabbers) {
- if (passiveGrabber != grabber)
+ if (!passiveGrabber.isNull() && passiveGrabber != grabber)
passiveGrabber->onGrabChanged(grabber, OverrideGrabPassive, this);
}
}
@@ -1291,7 +1296,7 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event)
Qt::TouchPointState state = Qt::TouchPointStationary;
switch (ev->type()) {
case QEvent::MouseButtonPress:
- m_mousePoint->clearPassiveGrabbers();
+ m_point->clearPassiveGrabbers();
Q_FALLTHROUGH();
case QEvent::MouseButtonDblClick:
state = Qt::TouchPointPressed;
@@ -1305,13 +1310,13 @@ QQuickPointerEvent *QQuickPointerMouseEvent::reset(QEvent *event)
default:
break;
}
- m_mousePoint->reset(state, ev->windowPos(), quint64(1) << 24, ev->timestamp()); // mouse has device ID 1
+ m_point->reset(state, ev->windowPos(), quint64(1) << 24, ev->timestamp()); // mouse has device ID 1
return this;
}
-void QQuickPointerMouseEvent::localize(QQuickItem *target)
+void QQuickSinglePointEvent::localize(QQuickItem *target)
{
- m_mousePoint->localizePosition(target);
+ m_point->localizePosition(target);
}
QQuickPointerEvent *QQuickPointerTouchEvent::reset(QEvent *event)
@@ -1412,35 +1417,54 @@ QQuickPointerEvent *QQuickPointerNativeGestureEvent::reset(QEvent *event)
break;
}
quint64 deviceId = QTouchDevicePrivate::get(const_cast<QTouchDevice *>(ev->device()))->id; // a bit roundabout since QTouchDevice::mTouchDeviceId is protected
- m_gesturePoint->reset(state, ev->windowPos(), deviceId << 24, ev->timestamp());
+ m_point->reset(state, ev->windowPos(), deviceId << 24, ev->timestamp());
return this;
}
-
-void QQuickPointerNativeGestureEvent::localize(QQuickItem *target)
-{
- m_gesturePoint->localizePosition(target);
-}
#endif // QT_CONFIG(gestures)
-QQuickEventPoint *QQuickPointerMouseEvent::point(int i) const {
+QQuickEventPoint *QQuickSinglePointEvent::point(int i) const
+{
if (i == 0)
- return m_mousePoint;
+ return m_point;
return nullptr;
}
-QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const {
- if (i >= 0 && i < m_pointCount)
- return m_touchPoints.at(i);
- return nullptr;
+QQuickPointerEvent *QQuickPointerScrollEvent::reset(QEvent *event)
+{
+ m_event = static_cast<QInputEvent*>(event);
+ if (!event)
+ return this;
+#if QT_CONFIG(wheelevent)
+ if (event->type() == QEvent::Wheel) {
+ auto ev = static_cast<QWheelEvent*>(event);
+ m_device = QQuickPointerDevice::genericMouseDevice();
+ m_device->eventDeliveryTargets().clear();
+ // m_button = Qt::NoButton;
+ m_pressedButtons = ev->buttons();
+ m_angleDelta = QVector2D(ev->angleDelta());
+ m_pixelDelta = QVector2D(ev->pixelDelta());
+ m_phase = ev->phase();
+ m_synthSource = ev->source();
+ m_inverted = ev->inverted();
+
+ m_point->reset(Qt::TouchPointMoved, ev->posF(), quint64(1) << 24, ev->timestamp()); // mouse has device ID 1
+ }
+#endif
+ // TODO else if (event->type() == QEvent::Scroll) ...
+ return this;
}
-#if QT_CONFIG(gestures)
-QQuickEventPoint *QQuickPointerNativeGestureEvent::point(int i) const {
- if (i == 0)
- return m_gesturePoint;
+void QQuickPointerScrollEvent::localize(QQuickItem *target)
+{
+ m_point->localizePosition(target);
+}
+
+QQuickEventPoint *QQuickPointerTouchEvent::point(int i) const
+{
+ if (i >= 0 && i < m_pointCount)
+ return m_touchPoints.at(i);
return nullptr;
}
-#endif // QT_CONFIG(gestures)
QQuickEventPoint::QQuickEventPoint(QQuickPointerEvent *parent)
: QObject(parent), m_pointId(0), m_exclusiveGrabber(nullptr), m_timestamp(0), m_pressTimestamp(0),
@@ -1454,17 +1478,19 @@ QQuickPointerEvent *QQuickEventPoint::pointerEvent() const
return static_cast<QQuickPointerEvent *>(parent());
}
-bool QQuickPointerMouseEvent::allPointsAccepted() const {
- return m_mousePoint->isAccepted();
+bool QQuickSinglePointEvent::allPointsAccepted() const
+{
+ return m_point->isAccepted();
}
-bool QQuickPointerMouseEvent::allUpdatedPointsAccepted() const {
- return m_mousePoint->state() == QQuickEventPoint::Pressed || m_mousePoint->isAccepted();
+bool QQuickSinglePointEvent::allUpdatedPointsAccepted() const
+{
+ return m_point->state() == QQuickEventPoint::Pressed || m_point->isAccepted();
}
-bool QQuickPointerMouseEvent::allPointsGrabbed() const
+bool QQuickSinglePointEvent::allPointsGrabbed() const
{
- return m_mousePoint->exclusiveGrabber() != nullptr;
+ return m_point->exclusiveGrabber() != nullptr;
}
QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) const
@@ -1477,10 +1503,10 @@ QMouseEvent *QQuickPointerMouseEvent::asMouseEvent(const QPointF &localPos) cons
/*!
Returns the exclusive grabber of this event, if any, in a vector.
*/
-QVector<QObject *> QQuickPointerMouseEvent::exclusiveGrabbers() const
+QVector<QObject *> QQuickSinglePointEvent::exclusiveGrabbers() const
{
QVector<QObject *> result;
- if (QObject *grabber = m_mousePoint->exclusiveGrabber())
+ if (QObject *grabber = m_point->exclusiveGrabber())
result << grabber;
return result;
}
@@ -1488,17 +1514,18 @@ QVector<QObject *> QQuickPointerMouseEvent::exclusiveGrabbers() const
/*!
Remove all passive and exclusive grabbers of this event, without notifying.
*/
-void QQuickPointerMouseEvent::clearGrabbers() const {
- m_mousePoint->setGrabberItem(nullptr);
- m_mousePoint->clearPassiveGrabbers();
+void QQuickSinglePointEvent::clearGrabbers() const
+{
+ m_point->setGrabberItem(nullptr);
+ m_point->clearPassiveGrabbers();
}
/*!
Returns whether the given \a handler is the exclusive grabber of this event.
*/
-bool QQuickPointerMouseEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const
+bool QQuickSinglePointEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const
{
- return m_mousePoint->exclusiveGrabber() == handler;
+ return handler && (m_point->exclusiveGrabber() == handler);
}
bool QQuickPointerMouseEvent::isPressEvent() const
@@ -1526,7 +1553,8 @@ bool QQuickPointerMouseEvent::isReleaseEvent() const
return me && me->type() == QEvent::MouseButtonRelease;
}
-bool QQuickPointerTouchEvent::allPointsAccepted() const {
+bool QQuickPointerTouchEvent::allPointsAccepted() const
+{
for (int i = 0; i < m_pointCount; ++i) {
if (!m_touchPoints.at(i)->isAccepted())
return false;
@@ -1534,7 +1562,8 @@ bool QQuickPointerTouchEvent::allPointsAccepted() const {
return true;
}
-bool QQuickPointerTouchEvent::allUpdatedPointsAccepted() const {
+bool QQuickPointerTouchEvent::allUpdatedPointsAccepted() const
+{
for (int i = 0; i < m_pointCount; ++i) {
auto point = m_touchPoints.at(i);
if (point->state() != QQuickEventPoint::Pressed && !point->isAccepted())
@@ -1571,7 +1600,8 @@ QVector<QObject *> QQuickPointerTouchEvent::exclusiveGrabbers() const
Remove all passive and exclusive grabbers of all touchpoints in this event,
without notifying.
*/
-void QQuickPointerTouchEvent::clearGrabbers() const {
+void QQuickPointerTouchEvent::clearGrabbers() const
+{
for (auto point: m_touchPoints) {
point->setGrabberItem(nullptr);
point->clearPassiveGrabbers();
@@ -1629,7 +1659,8 @@ QVector<QPointF> QQuickPointerEvent::unacceptedPressedPointScenePositions() cons
If the touchpoint cannot be found, this returns nullptr.
Ownership of the event is NOT transferred to the caller.
*/
-QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const {
+QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickItem *relativeTo) const
+{
const QTouchEvent::TouchPoint *p = touchPointById(pointID);
if (!p)
return nullptr;
@@ -1669,33 +1700,6 @@ QMouseEvent *QQuickPointerTouchEvent::syntheticMouseEvent(int pointID, QQuickIte
}
#if QT_CONFIG(gestures)
-/*!
- Returns the exclusive grabber of this event, if any, in a vector.
-*/
-QVector<QObject *> QQuickPointerNativeGestureEvent::exclusiveGrabbers() const
-{
- QVector<QObject *> result;
- if (QObject *grabber = m_gesturePoint->exclusiveGrabber())
- result << grabber;
- return result;
-}
-
-/*!
- Remove all passive and exclusive grabbers of this event, without notifying.
-*/
-void QQuickPointerNativeGestureEvent::clearGrabbers() const {
- m_gesturePoint->setGrabberItem(nullptr);
- m_gesturePoint->clearPassiveGrabbers();
-}
-
-/*!
- Returns whether the given \a handler is the exclusive grabber of this event.
-*/
-bool QQuickPointerNativeGestureEvent::hasExclusiveGrabber(const QQuickPointerHandler *handler) const
-{
- return m_gesturePoint->exclusiveGrabber() == handler;
-}
-
bool QQuickPointerNativeGestureEvent::isPressEvent() const
{
return type() == Qt::BeginNativeGesture;
@@ -1729,6 +1733,39 @@ qreal QQuickPointerNativeGestureEvent::value() const
#endif // QT_CONFIG(gestures)
/*!
+ Returns whether the scroll event has Qt::ScrollBegin phase. On touchpads
+ which provide phase information, this is true when the fingers are placed
+ on the touchpad and scrolling begins. On other devices where this
+ information is not available, it remains false.
+*/
+bool QQuickPointerScrollEvent::isPressEvent() const
+{
+ return phase() == Qt::ScrollBegin;
+}
+
+/*!
+ Returns true when the scroll event has Qt::ScrollUpdate phase, or when the
+ phase is unknown. Some multi-touch-capable touchpads and trackpads provide
+ phase information; whereas ordinary mouse wheels and other types of
+ trackpads do not, and in such cases this is always true.
+*/
+bool QQuickPointerScrollEvent::isUpdateEvent() const
+{
+ return phase() == Qt::ScrollUpdate || phase() == Qt::NoScrollPhase;
+}
+
+/*!
+ Returns whether the scroll event has Qt::ScrollBegin phase. On touchpads
+ which provide phase information, this is true when the fingers are lifted
+ from the touchpad. On other devices where this information is not
+ available, it remains false.
+*/
+bool QQuickPointerScrollEvent::isReleaseEvent() const
+{
+ return phase() == Qt::ScrollEnd;
+}
+
+/*!
\internal
Returns a pointer to the QQuickEventPoint which has the \a pointId as
\l {QQuickEventPoint::pointId}{pointId}.
@@ -1736,13 +1773,15 @@ qreal QQuickPointerNativeGestureEvent::value() const
\fn QQuickPointerEvent::pointById(int pointId) const
*/
-QQuickEventPoint *QQuickPointerMouseEvent::pointById(int pointId) const {
- if (m_mousePoint && pointId == m_mousePoint->pointId())
- return m_mousePoint;
+QQuickEventPoint *QQuickSinglePointEvent::pointById(int pointId) const
+{
+ if (m_point && pointId == m_point->pointId())
+ return m_point;
return nullptr;
}
-QQuickEventPoint *QQuickPointerTouchEvent::pointById(int pointId) const {
+QQuickEventPoint *QQuickPointerTouchEvent::pointById(int pointId) const
+{
auto it = std::find_if(m_touchPoints.constBegin(), m_touchPoints.constEnd(),
[pointId](const QQuickEventTouchPoint *tp) { return tp->pointId() == pointId; } );
if (it != m_touchPoints.constEnd())
@@ -1750,21 +1789,14 @@ QQuickEventPoint *QQuickPointerTouchEvent::pointById(int pointId) const {
return nullptr;
}
-#if QT_CONFIG(gestures)
-QQuickEventPoint *QQuickPointerNativeGestureEvent::pointById(int pointId) const {
- if (m_gesturePoint && pointId == m_gesturePoint->pointId())
- return m_gesturePoint;
- return nullptr;
-}
-#endif
-
/*!
\internal
Returns a pointer to the original TouchPoint which has the same
\l {QTouchEvent::TouchPoint::id}{id} as \a pointId, if the original event is a
QTouchEvent, and if that point is found. Otherwise, returns nullptr.
*/
-const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int pointId) const {
+const QTouchEvent::TouchPoint *QQuickPointerTouchEvent::touchPointById(int pointId) const
+{
const QTouchEvent *ev = asTouchEvent();
if (!ev)
return nullptr;
@@ -1875,24 +1907,10 @@ QTouchEvent *QQuickPointerTouchEvent::asTouchEvent() const
return static_cast<QTouchEvent *>(m_event);
}
-#if QT_CONFIG(gestures)
-bool QQuickPointerNativeGestureEvent::allPointsAccepted() const {
- return m_gesturePoint->isAccepted();
-}
-
-bool QQuickPointerNativeGestureEvent::allUpdatedPointsAccepted() const {
- return m_gesturePoint->state() == QQuickEventPoint::Pressed || m_gesturePoint->isAccepted();
-}
-
-bool QQuickPointerNativeGestureEvent::allPointsGrabbed() const
-{
- return m_gesturePoint->exclusiveGrabber() != nullptr;
-}
-#endif // QT_CONFIG(gestures)
-
#ifndef QT_NO_DEBUG_STREAM
-Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *dev) {
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *dev)
+{
QDebugStateSaver saver(dbg);
dbg.nospace();
if (!dev) {
@@ -1914,7 +1932,8 @@ Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerDevice *
return dbg;
}
-Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *event) {
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *event)
+{
QDebugStateSaver saver(dbg);
dbg.nospace();
dbg << "QQuickPointerEvent(";
@@ -1933,7 +1952,8 @@ Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickPointerEvent *e
return dbg;
}
-Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickEventPoint *event) {
+Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QQuickEventPoint *event)
+{
QDebugStateSaver saver(dbg);
dbg.nospace();
dbg << "QQuickEventPoint(accepted:" << event->isAccepted()
diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h
index 4097845ec9..f3ce04d11e 100644
--- a/src/quick/items/qquickevents_p_p.h
+++ b/src/quick/items/qquickevents_p_p.h
@@ -69,6 +69,7 @@ class QQuickPointerMouseEvent;
#if QT_CONFIG(gestures)
class QQuickPointerNativeGestureEvent;
#endif
+class QQuickPointerScrollEvent;
class QQuickPointerTabletEvent;
class QQuickPointerTouchEvent;
class QQuickPointerHandler;
@@ -76,12 +77,12 @@ class QQuickPointerHandler;
class QQuickKeyEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(int key READ key)
- Q_PROPERTY(QString text READ text)
- Q_PROPERTY(int modifiers READ modifiers)
- Q_PROPERTY(bool isAutoRepeat READ isAutoRepeat)
- Q_PROPERTY(int count READ count)
- Q_PROPERTY(quint32 nativeScanCode READ nativeScanCode)
+ Q_PROPERTY(int key READ key CONSTANT)
+ Q_PROPERTY(QString text READ text CONSTANT)
+ Q_PROPERTY(int modifiers READ modifiers CONSTANT)
+ Q_PROPERTY(bool isAutoRepeat READ isAutoRepeat CONSTANT)
+ Q_PROPERTY(int count READ count CONSTANT)
+ Q_PROPERTY(quint32 nativeScanCode READ nativeScanCode CONSTANT)
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
public:
@@ -124,16 +125,16 @@ private:
class Q_QUICK_PRIVATE_EXPORT QQuickMouseEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(qreal x READ x)
- Q_PROPERTY(qreal y READ y)
- Q_PROPERTY(int button READ button)
- Q_PROPERTY(int buttons READ buttons)
- Q_PROPERTY(int modifiers READ modifiers)
- Q_PROPERTY(int source READ source REVISION 7)
- Q_PROPERTY(bool wasHeld READ wasHeld)
- Q_PROPERTY(bool isClick READ isClick)
+ Q_PROPERTY(qreal x READ x CONSTANT)
+ Q_PROPERTY(qreal y READ y CONSTANT)
+ Q_PROPERTY(int button READ button CONSTANT)
+ Q_PROPERTY(int buttons READ buttons CONSTANT)
+ Q_PROPERTY(int modifiers READ modifiers CONSTANT)
+ Q_PROPERTY(int source READ source CONSTANT REVISION 7)
+ Q_PROPERTY(bool wasHeld READ wasHeld CONSTANT)
+ Q_PROPERTY(bool isClick READ isClick CONSTANT)
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
- Q_REVISION(11) Q_PROPERTY(int flags READ flags)
+ Q_PROPERTY(int flags READ flags CONSTANT REVISION 11)
public:
QQuickMouseEvent()
@@ -192,13 +193,13 @@ private:
class QQuickWheelEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(qreal x READ x)
- Q_PROPERTY(qreal y READ y)
- Q_PROPERTY(QPoint angleDelta READ angleDelta)
- Q_PROPERTY(QPoint pixelDelta READ pixelDelta)
- Q_PROPERTY(int buttons READ buttons)
- Q_PROPERTY(int modifiers READ modifiers)
- Q_PROPERTY(bool inverted READ inverted)
+ Q_PROPERTY(qreal x READ x CONSTANT)
+ Q_PROPERTY(qreal y READ y CONSTANT)
+ Q_PROPERTY(QPoint angleDelta READ angleDelta CONSTANT)
+ Q_PROPERTY(QPoint pixelDelta READ pixelDelta CONSTANT)
+ Q_PROPERTY(int buttons READ buttons CONSTANT)
+ Q_PROPERTY(int modifiers READ modifiers CONSTANT)
+ Q_PROPERTY(bool inverted READ inverted CONSTANT)
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
public:
@@ -258,15 +259,15 @@ private:
class Q_QUICK_PRIVATE_EXPORT QQuickEventPoint : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQuickPointerEvent *event READ pointerEvent)
- Q_PROPERTY(QPointF position READ position)
- Q_PROPERTY(QPointF scenePosition READ scenePosition)
- Q_PROPERTY(QPointF scenePressPosition READ scenePressPosition)
- Q_PROPERTY(QPointF sceneGrabPosition READ sceneGrabPosition)
- Q_PROPERTY(State state READ state)
- Q_PROPERTY(int pointId READ pointId)
- Q_PROPERTY(qreal timeHeld READ timeHeld)
- Q_PROPERTY(QVector2D velocity READ velocity)
+ Q_PROPERTY(QQuickPointerEvent *event READ pointerEvent CONSTANT)
+ Q_PROPERTY(QPointF position READ position CONSTANT)
+ Q_PROPERTY(QPointF scenePosition READ scenePosition CONSTANT)
+ Q_PROPERTY(QPointF scenePressPosition READ scenePressPosition CONSTANT)
+ Q_PROPERTY(QPointF sceneGrabPosition READ sceneGrabPosition CONSTANT)
+ Q_PROPERTY(State state READ state CONSTANT)
+ Q_PROPERTY(int pointId READ pointId CONSTANT)
+ Q_PROPERTY(qreal timeHeld READ timeHeld CONSTANT)
+ Q_PROPERTY(QVector2D velocity READ velocity CONSTANT)
Q_PROPERTY(bool accepted READ isAccepted WRITE setAccepted)
Q_PROPERTY(QObject *exclusiveGrabber READ exclusiveGrabber WRITE setExclusiveGrabber)
@@ -280,7 +281,7 @@ public:
Q_DECLARE_FLAGS(States, State)
Q_FLAG(States)
- enum GrabState {
+ enum GrabTransition {
GrabPassive = 0x01,
UngrabPassive = 0x02,
CancelGrabPassive = 0x03,
@@ -289,7 +290,7 @@ public:
UngrabExclusive = 0x20,
CancelGrabExclusive = 0x30,
};
- Q_ENUM(GrabState)
+ Q_ENUM(GrabTransition)
QQuickEventPoint(QQuickPointerEvent *parent);
@@ -385,10 +386,10 @@ private:
class Q_QUICK_PRIVATE_EXPORT QQuickPointerEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQuickPointerDevice *device READ device)
- Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers)
- Q_PROPERTY(Qt::MouseButtons button READ button)
- Q_PROPERTY(Qt::MouseButtons buttons READ buttons)
+ Q_PROPERTY(QQuickPointerDevice *device READ device CONSTANT)
+ Q_PROPERTY(Qt::KeyboardModifiers modifiers READ modifiers CONSTANT)
+ Q_PROPERTY(Qt::MouseButtons button READ button CONSTANT)
+ Q_PROPERTY(Qt::MouseButtons buttons READ buttons CONSTANT)
public:
QQuickPointerEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
@@ -419,16 +420,18 @@ public: // helpers for C++ only (during event delivery)
#if QT_CONFIG(gestures)
virtual QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() { return nullptr; }
#endif
+ virtual QQuickPointerScrollEvent *asPointerScrollEvent() { return nullptr; }
virtual const QQuickPointerMouseEvent *asPointerMouseEvent() const { return nullptr; }
virtual const QQuickPointerTouchEvent *asPointerTouchEvent() const { return nullptr; }
virtual const QQuickPointerTabletEvent *asPointerTabletEvent() const { return nullptr; }
#if QT_CONFIG(gestures)
virtual const QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() const { return nullptr; }
#endif
+ virtual const QQuickPointerScrollEvent *asPointerScrollEvent() const { return nullptr; }
virtual bool allPointsAccepted() const = 0;
virtual bool allUpdatedPointsAccepted() const = 0;
virtual bool allPointsGrabbed() const = 0;
- bool isAccepted() { return m_event->isAccepted(); }
+ bool isAccepted() { return m_event ? m_event->isAccepted() : false; }
void setAccepted(bool accepted) { if (m_event) m_event->setAccepted(accepted); }
QVector<QPointF> unacceptedPressedPointScenePositions() const;
@@ -439,7 +442,7 @@ public: // helpers for C++ only (during event delivery)
virtual void clearGrabbers() const = 0;
virtual bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const = 0;
- ulong timestamp() const { return m_event->timestamp(); }
+ ulong timestamp() const { return m_event ? m_event->timestamp() : 0; }
protected:
QQuickPointerDevice *m_device;
@@ -450,21 +453,14 @@ protected:
Q_DISABLE_COPY(QQuickPointerEvent)
};
-class Q_QUICK_PRIVATE_EXPORT QQuickPointerMouseEvent : public QQuickPointerEvent
+class Q_QUICK_PRIVATE_EXPORT QQuickSinglePointEvent : public QQuickPointerEvent
{
Q_OBJECT
public:
- QQuickPointerMouseEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
- : QQuickPointerEvent(parent, device), m_mousePoint(new QQuickEventPoint(this)) { }
+ QQuickSinglePointEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
+ : QQuickPointerEvent(parent, device), m_point(new QQuickEventPoint(this)) { }
- QQuickPointerEvent *reset(QEvent *) override;
void localize(QQuickItem *target) override;
- bool isPressEvent() const override;
- bool isDoubleClickEvent() const override;
- bool isUpdateEvent() const override;
- bool isReleaseEvent() const override;
- QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; }
- const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; }
int pointCount() const override { return 1; }
QQuickEventPoint *point(int i) const override;
QQuickEventPoint *pointById(int pointId) const override;
@@ -475,10 +471,28 @@ public:
void clearGrabbers() const override;
bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const override;
- QMouseEvent *asMouseEvent(const QPointF& localPos) const;
+protected:
+ QQuickEventPoint *m_point;
-private:
- QQuickEventPoint *m_mousePoint;
+ Q_DISABLE_COPY(QQuickSinglePointEvent)
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerMouseEvent : public QQuickSinglePointEvent
+{
+ Q_OBJECT
+public:
+ QQuickPointerMouseEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
+ : QQuickSinglePointEvent(parent, device) { }
+
+ QQuickPointerEvent *reset(QEvent *) override;
+ bool isPressEvent() const override;
+ bool isDoubleClickEvent() const override;
+ bool isUpdateEvent() const override;
+ bool isReleaseEvent() const override;
+ QQuickPointerMouseEvent *asPointerMouseEvent() override { return this; }
+ const QQuickPointerMouseEvent *asPointerMouseEvent() const override { return this; }
+
+ QMouseEvent *asMouseEvent(const QPointF& localPos) const;
Q_DISABLE_COPY(QQuickPointerMouseEvent)
};
@@ -526,7 +540,7 @@ private:
};
#if QT_CONFIG(gestures)
-class Q_QUICK_PRIVATE_EXPORT QQuickPointerNativeGestureEvent : public QQuickPointerEvent
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerNativeGestureEvent : public QQuickSinglePointEvent
{
Q_OBJECT
Q_PROPERTY(Qt::NativeGestureType type READ type CONSTANT)
@@ -534,34 +548,65 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerNativeGestureEvent : public QQuickPoin
public:
QQuickPointerNativeGestureEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
- : QQuickPointerEvent(parent, device), m_gesturePoint(new QQuickEventPoint(this)) { }
+ : QQuickSinglePointEvent(parent, device) { }
QQuickPointerEvent *reset(QEvent *) override;
- void localize(QQuickItem *target) override;
bool isPressEvent() const override;
bool isUpdateEvent() const override;
bool isReleaseEvent() const override;
QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() override { return this; }
const QQuickPointerNativeGestureEvent *asPointerNativeGestureEvent() const override { return this; }
- int pointCount() const override { return 1; }
- QQuickEventPoint *point(int i) const override;
- QQuickEventPoint *pointById(int pointId) const override;
- bool allPointsAccepted() const override;
- bool allUpdatedPointsAccepted() const override;
- bool allPointsGrabbed() const override;
- QVector<QObject *> exclusiveGrabbers() const override;
- void clearGrabbers() const override;
- bool hasExclusiveGrabber(const QQuickPointerHandler *handler) const override;
Qt::NativeGestureType type() const;
qreal value() const;
-private:
- QQuickEventPoint *m_gesturePoint;
-
Q_DISABLE_COPY(QQuickPointerNativeGestureEvent)
};
#endif // QT_CONFIG(gestures)
+class Q_QUICK_PRIVATE_EXPORT QQuickPointerScrollEvent : public QQuickSinglePointEvent
+{
+ Q_OBJECT
+ Q_PROPERTY(QVector2D angleDelta READ angleDelta CONSTANT)
+ Q_PROPERTY(QVector2D pixelDelta READ pixelDelta CONSTANT)
+ Q_PROPERTY(bool hasAngleDelta READ hasAngleDelta CONSTANT)
+ Q_PROPERTY(bool hasPixelDelta READ hasPixelDelta CONSTANT)
+ Q_PROPERTY(bool inverted READ isInverted CONSTANT)
+
+public:
+ QQuickPointerScrollEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
+ : QQuickSinglePointEvent(parent, device) { }
+
+ QQuickPointerEvent *reset(QEvent *) override;
+ void localize(QQuickItem *target) override;
+ bool isPressEvent() const override;
+ bool isUpdateEvent() const override;
+ bool isReleaseEvent() const override;
+ QQuickPointerScrollEvent *asPointerScrollEvent() override { return this; }
+ const QQuickPointerScrollEvent *asPointerScrollEvent() const override { return this; }
+ QVector2D angleDelta() const { return m_angleDelta; }
+ QVector2D pixelDelta() const { return m_pixelDelta; }
+ bool hasAngleDelta() const { return !angleDelta().isNull(); }
+ bool hasPixelDelta() const { return !pixelDelta().isNull(); }
+ bool isInverted() const { return m_inverted; }
+ Qt::ScrollPhase phase() const { return m_phase; }
+
+private:
+ // TODO add QQuickPointerDevice source() whenever QInputEvent is extended to have a source device
+ // then maybe Qt::MouseEventSource synthSource() will be obsolete... that's why it's not public now
+ Qt::MouseEventSource synthSource() const { return m_synthSource; }
+
+private:
+ QVector2D m_angleDelta;
+ QVector2D m_pixelDelta;
+ Qt::ScrollPhase m_phase = Qt::NoScrollPhase;
+ Qt::MouseEventSource m_synthSource = Qt::MouseEventNotSynthesized;
+ bool m_inverted = false;
+
+ friend class QQuickWindowPrivate;
+
+ Q_DISABLE_COPY(QQuickPointerScrollEvent)
+};
+
// ### Qt 6: move this to qtbase, replace QTouchDevice and the enums in QTabletEvent
class Q_QUICK_PRIVATE_EXPORT QQuickPointerDevice : public QObject
@@ -587,7 +632,6 @@ public:
AllDevices = 0x7FFF
};
Q_DECLARE_FLAGS(DeviceTypes, DeviceType)
- Q_ENUM(DeviceType)
Q_FLAG(DeviceTypes)
enum PointerType : qint16 {
@@ -599,7 +643,6 @@ public:
AllPointerTypes = 0x7FFF
};
Q_DECLARE_FLAGS(PointerTypes, PointerType)
- Q_ENUM(PointerType)
Q_FLAG(PointerTypes)
enum CapabilityFlag : qint16 {
@@ -616,7 +659,6 @@ public:
YTilt = 0x1000
};
Q_DECLARE_FLAGS(Capabilities, CapabilityFlag)
- Q_ENUM(CapabilityFlag)
Q_FLAG(Capabilities)
DeviceType type() const { return m_deviceType; }
@@ -627,6 +669,7 @@ public:
int buttonCount() const { return m_buttonCount; }
QString name() const { return m_name; }
QPointingDeviceUniqueId uniqueId() const { return m_uniqueId; }
+ const QTouchDevice *qTouchDevice() const;
static QQuickPointerDevice *touchDevice(const QTouchDevice *d);
static QList<QQuickPointerDevice *> touchDevices();
diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp
index cc35630fff..85045be411 100644
--- a/src/quick/items/qquickflickable.cpp
+++ b/src/quick/items/qquickflickable.cpp
@@ -69,10 +69,6 @@ static const int FlickThreshold = 15;
// will ensure the Flickable retains the grab on consecutive flicks.
static const int RetainGrabVelocity = 100;
-#ifdef Q_OS_OSX
-static const int MovementEndingTimerInterval = 100;
-#endif
-
// Currently std::round can't be used on Android when using ndk g++, so
// use C version instead. We could just define two versions of Round, one
// for float and one for double, but then only one of them would be used
@@ -248,6 +244,7 @@ QQuickFlickablePrivate::QQuickFlickablePrivate()
, stealMouse(false), pressed(false)
, scrollingPhase(false), interactive(true), calcVelocity(false)
, pixelAligned(false)
+ , syncDrag(false)
, lastPosTime(-1)
, lastPressTime(0)
, deceleration(QML_FLICK_DEFAULTDECELERATION)
@@ -508,7 +505,8 @@ static bool fuzzyLessThanOrEqualTo(qreal a, qreal b)
void QQuickFlickablePrivate::updateBeginningEnd()
{
Q_Q(QQuickFlickable);
- bool atBoundaryChange = false;
+ bool atXBeginningChange = false, atXEndChange = false;
+ bool atYBeginningChange = false, atYEndChange = false;
// Vertical
const qreal maxyextent = -q->maxYExtent();
@@ -519,11 +517,11 @@ void QQuickFlickablePrivate::updateBeginningEnd()
if (atBeginning != vData.atBeginning) {
vData.atBeginning = atBeginning;
- atBoundaryChange = true;
+ atYBeginningChange = true;
}
if (atEnd != vData.atEnd) {
vData.atEnd = atEnd;
- atBoundaryChange = true;
+ atYEndChange = true;
}
// Horizontal
@@ -535,11 +533,11 @@ void QQuickFlickablePrivate::updateBeginningEnd()
if (atBeginning != hData.atBeginning) {
hData.atBeginning = atBeginning;
- atBoundaryChange = true;
+ atXBeginningChange = true;
}
if (atEnd != hData.atEnd) {
hData.atEnd = atEnd;
- atBoundaryChange = true;
+ atXEndChange = true;
}
if (vData.extentsChanged) {
@@ -560,8 +558,16 @@ void QQuickFlickablePrivate::updateBeginningEnd()
}
}
- if (atBoundaryChange)
+ if (atXEndChange || atYEndChange || atXBeginningChange || atYBeginningChange)
emit q->isAtBoundaryChanged();
+ if (atXEndChange)
+ emit q->atXEndChanged();
+ if (atXBeginningChange)
+ emit q->atXBeginningChanged();
+ if (atYEndChange)
+ emit q->atYEndChanged();
+ if (atYBeginningChange)
+ emit q->atYBeginningChanged();
if (visibleArea)
visibleArea->updateVisible();
@@ -984,6 +990,33 @@ void QQuickFlickable::setPixelAligned(bool align)
}
}
+/*!
+ \qmlproperty bool QtQuick::Flickable::synchronousDrag
+ \since 5.12
+
+ If this property is set to true, then when the mouse or touchpoint moves
+ far enough to begin dragging the content, the content will jump, such that
+ the content pixel which was under the cursor or touchpoint when pressed
+ remains under that point.
+
+ The default is \c false, which provides a smoother experience (no jump)
+ at the cost that some of the drag distance is "lost" at the beginning.
+*/
+bool QQuickFlickable::synchronousDrag() const
+{
+ Q_D(const QQuickFlickable);
+ return d->syncDrag;
+}
+
+void QQuickFlickable::setSynchronousDrag(bool v)
+{
+ Q_D(QQuickFlickable);
+ if (v != d->syncDrag) {
+ d->syncDrag = v;
+ emit synchronousDragChanged();
+ }
+}
+
qint64 QQuickFlickablePrivate::computeCurrentTime(QInputEvent *event) const
{
if (0 != event->timestamp())
@@ -1100,7 +1133,7 @@ void QQuickFlickablePrivate::drag(qint64 currentTimestamp, QEvent::Type eventTyp
if (overThreshold || elapsedSincePress > 200) {
if (!vMoved)
vData.dragStartOffset = dy;
- qreal newY = dy + vData.pressPos - vData.dragStartOffset;
+ qreal newY = dy + vData.pressPos - (syncDrag ? 0 : vData.dragStartOffset);
// Recalculate bounds in case margins have changed, but use the content
// size estimate taken at the start of the drag in case the drag causes
// the estimate to be altered
@@ -1176,7 +1209,7 @@ void QQuickFlickablePrivate::drag(qint64 currentTimestamp, QEvent::Type eventTyp
if (overThreshold || elapsedSincePress > 200) {
if (!hMoved)
hData.dragStartOffset = dx;
- qreal newX = dx + hData.pressPos - hData.dragStartOffset;
+ qreal newX = dx + hData.pressPos - (syncDrag ? 0 : hData.dragStartOffset);
const qreal minX = hData.dragMinBound + hData.startMargin;
const qreal maxX = hData.dragMaxBound - hData.endMargin;
if (!(boundsBehavior & QQuickFlickable::DragOverBounds)) {
@@ -1280,7 +1313,7 @@ void QQuickFlickablePrivate::drag(qint64 currentTimestamp, QEvent::Type eventTyp
void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event)
{
Q_Q(QQuickFlickable);
- if (!interactive || lastPosTime == -1)
+ if (!interactive || lastPosTime == -1 || event->buttons() == Qt::NoButton)
return;
qint64 currentTimestamp = computeCurrentTime(event);
@@ -1460,23 +1493,24 @@ void QQuickFlickable::wheelEvent(QWheelEvent *event)
case Qt::ScrollUpdate:
if (d->scrollingPhase)
d->pressed = true;
-#ifdef Q_OS_MACOS
- // TODO eliminate this timer when ScrollMomentum has been added
- d->movementEndingTimer.start(MovementEndingTimerInterval, this);
-#endif
+ break;
+ case Qt::ScrollMomentum:
+ d->pressed = false;
+ d->scrollingPhase = false;
+ d->draggingEnding();
+ event->accept();
+ d->lastPosTime = -1;
break;
case Qt::ScrollEnd:
- // TODO most of this should be done at transition to ScrollMomentum phase,
- // then do what the movementEndingTimer triggers at transition to ScrollEnd phase
d->pressed = false;
d->scrollingPhase = false;
d->draggingEnding();
event->accept();
returnToBounds();
d->lastPosTime = -1;
-#ifdef Q_OS_MACOS
- d->movementEndingTimer.start(MovementEndingTimerInterval, this);
-#endif
+ d->stealMouse = false;
+ if (!d->velocityTimeline.isActive() && !d->timeline.isActive())
+ movementEnding(true, true);
return;
}
@@ -1672,14 +1706,6 @@ void QQuickFlickable::timerEvent(QTimerEvent *event)
if (d->delayedPressEvent) {
d->replayDelayedPress();
}
- } else if (event->timerId() == d->movementEndingTimer.timerId()) {
- d->movementEndingTimer.stop();
- if (!d->scrollingPhase) {
- d->pressed = false;
- d->stealMouse = false;
- if (!d->velocityTimeline.isActive() && !d->timeline.isActive())
- movementEnding(true, true);
- }
}
}
@@ -1821,8 +1847,8 @@ void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity)
d->vData.velocity = yVelocity;
d->hData.vTime = d->vData.vTime = d->timeline.time();
- bool flickedX = d->flickX(xVelocity);
- bool flickedY = d->flickY(yVelocity);
+ const bool flickedX = xflick() && !qFuzzyIsNull(xVelocity) && d->flickX(xVelocity);
+ const bool flickedY = yflick() && !qFuzzyIsNull(yVelocity) && d->flickY(yVelocity);
if (flickedX)
d->hMoved = true;
@@ -2540,7 +2566,7 @@ void QQuickFlickablePrivate::draggingStarting()
void QQuickFlickablePrivate::draggingEnding()
{
Q_Q(QQuickFlickable);
- bool wasDragging = hData.dragging || vData.dragging;
+ const bool wasDragging = hData.dragging || vData.dragging;
if (hData.dragging) {
hData.dragging = false;
emit q->draggingHorizontallyChanged();
@@ -2549,12 +2575,14 @@ void QQuickFlickablePrivate::draggingEnding()
vData.dragging = false;
emit q->draggingVerticallyChanged();
}
- if (wasDragging && !hData.dragging && !vData.dragging) {
- emit q->draggingChanged();
- emit q->dragEnded();
+ if (wasDragging) {
+ if (!hData.dragging && !vData.dragging) {
+ emit q->draggingChanged();
+ emit q->dragEnded();
+ }
+ hData.inRebound = false;
+ vData.inRebound = false;
}
- hData.inRebound = false;
- vData.inRebound = false;
}
bool QQuickFlickablePrivate::isViewMoving() const
diff --git a/src/quick/items/qquickflickable_p.h b/src/quick/items/qquickflickable_p.h
index 939e3af698..1bd8fc1020 100644
--- a/src/quick/items/qquickflickable_p.h
+++ b/src/quick/items/qquickflickable_p.h
@@ -98,14 +98,15 @@ class Q_QUICK_PRIVATE_EXPORT QQuickFlickable : public QQuickItem
Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive NOTIFY interactiveChanged)
Q_PROPERTY(int pressDelay READ pressDelay WRITE setPressDelay NOTIFY pressDelayChanged)
- Q_PROPERTY(bool atXEnd READ isAtXEnd NOTIFY isAtBoundaryChanged)
- Q_PROPERTY(bool atYEnd READ isAtYEnd NOTIFY isAtBoundaryChanged)
- Q_PROPERTY(bool atXBeginning READ isAtXBeginning NOTIFY isAtBoundaryChanged)
- Q_PROPERTY(bool atYBeginning READ isAtYBeginning NOTIFY isAtBoundaryChanged)
+ Q_PROPERTY(bool atXEnd READ isAtXEnd NOTIFY atXEndChanged)
+ Q_PROPERTY(bool atYEnd READ isAtYEnd NOTIFY atYEndChanged)
+ Q_PROPERTY(bool atXBeginning READ isAtXBeginning NOTIFY atXBeginningChanged)
+ Q_PROPERTY(bool atYBeginning READ isAtYBeginning NOTIFY atYBeginningChanged)
Q_PROPERTY(QQuickFlickableVisibleArea *visibleArea READ visibleArea CONSTANT)
Q_PROPERTY(bool pixelAligned READ pixelAligned WRITE setPixelAligned NOTIFY pixelAlignedChanged)
+ Q_PROPERTY(bool synchronousDrag READ synchronousDrag WRITE setSynchronousDrag NOTIFY synchronousDragChanged REVISION 12)
Q_PROPERTY(qreal horizontalOvershoot READ horizontalOvershoot NOTIFY horizontalOvershootChanged REVISION 9)
Q_PROPERTY(qreal verticalOvershoot READ verticalOvershoot NOTIFY verticalOvershootChanged REVISION 9)
@@ -213,6 +214,9 @@ public:
bool pixelAligned() const;
void setPixelAligned(bool align);
+ bool synchronousDrag() const;
+ void setSynchronousDrag(bool v);
+
qreal horizontalOvershoot() const;
qreal verticalOvershoot() const;
@@ -259,9 +263,15 @@ Q_SIGNALS:
void dragStarted();
void dragEnded();
void pixelAlignedChanged();
+ Q_REVISION(12) void synchronousDragChanged();
Q_REVISION(9) void horizontalOvershootChanged();
Q_REVISION(9) void verticalOvershootChanged();
+ Q_REVISION(12) void atXEndChanged();
+ Q_REVISION(12) void atYEndChanged();
+ Q_REVISION(12) void atXBeginningChanged();
+ Q_REVISION(12) void atYBeginningChanged();
+
protected:
bool childMouseEventFilter(QQuickItem *, QEvent *) override;
void mousePressEvent(QMouseEvent *event) override;
diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h
index 08f069e830..c538cf7878 100644
--- a/src/quick/items/qquickflickable_p_p.h
+++ b/src/quick/items/qquickflickable_p_p.h
@@ -221,8 +221,8 @@ public:
bool interactive : 1;
bool calcVelocity : 1;
bool pixelAligned : 1;
+ bool syncDrag : 1;
QElapsedTimer timer;
- QBasicTimer movementEndingTimer;
qint64 lastPosTime;
qint64 lastPressTime;
QPointF lastPos;
diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp
index f2fa66332c..272d4a4df5 100644
--- a/src/quick/items/qquickgridview.cpp
+++ b/src/quick/items/qquickgridview.cpp
@@ -417,11 +417,11 @@ qreal QQuickGridViewPrivate::contentXForPosition(qreal pos) const
if (flow == QQuickGridView::FlowLeftToRight) {
// vertical scroll
if (q->effectiveLayoutDirection() == Qt::LeftToRight) {
- return 0;
+ return -q->leftMargin();
} else {
qreal colSize = cellWidth;
- int columns = q->width()/colSize;
- return -q->width() + (cellWidth * columns);
+ int columns = (q->width() - q->leftMargin() - q->rightMargin()) / colSize;
+ return -q->width() + q->rightMargin() + (cellWidth * columns);
}
} else {
// horizontal scroll
@@ -444,16 +444,18 @@ qreal QQuickGridViewPrivate::contentYForPosition(qreal pos) const
} else {
// horizontal scroll
if (verticalLayoutDirection == QQuickItemView::TopToBottom)
- return 0;
+ return -q->topMargin();
else
- return -q->height();
+ return -q->height() + q->bottomMargin();
}
}
void QQuickGridViewPrivate::resetColumns()
{
Q_Q(QQuickGridView);
- qreal length = flow == QQuickGridView::FlowLeftToRight ? q->width() : q->height();
+ qreal length = flow == QQuickGridView::FlowLeftToRight
+ ? q->width() - q->leftMargin() - q->rightMargin()
+ : q->height() - q->topMargin() - q->bottomMargin();
columns = qMax(1, qFloor(length / colSize()));
}
@@ -914,7 +916,7 @@ void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
qreal tempPosition = isContentFlowReversed() ? -position()-size() : position();
if (snapMode == QQuickGridView::SnapOneRow && moveReason == Mouse) {
// if we've been dragged < rowSize()/2 then bias towards the next row
- qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
+ qreal dist = data.move.value() - data.pressPos;
qreal bias = 0;
if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < rowSize()/2)
bias = rowSize()/2;
@@ -925,13 +927,13 @@ void QQuickGridViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
tempPosition -= bias;
}
FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
- if (strictHighlightRange && currentItem && (!topItem || topItem->index != currentIndex)) {
+ if (strictHighlightRange && currentItem && (!topItem || (topItem->index != currentIndex && fixupMode == Immediate))) {
// StrictlyEnforceRange always keeps an item in range
updateHighlight();
topItem = currentItem;
}
FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
- if (strictHighlightRange && currentItem && (!bottomItem || bottomItem->index != currentIndex)) {
+ if (strictHighlightRange && currentItem && (!bottomItem || (bottomItem->index != currentIndex && fixupMode == Immediate))) {
// StrictlyEnforceRange always keeps an item in range
updateHighlight();
bottomItem = currentItem;
@@ -1013,7 +1015,7 @@ bool QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
if (data.move.value() < minExtent) {
if (snapMode == QQuickGridView::SnapOneRow) {
// if we've been dragged < averageSize/2 then bias towards the next item
- qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
+ qreal dist = data.move.value() - data.pressPos;
qreal bias = dist < rowSize()/2 ? rowSize()/2 : 0;
if (isContentFlowReversed())
bias = -bias;
@@ -1030,7 +1032,7 @@ bool QQuickGridViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
if (data.move.value() > maxExtent) {
if (snapMode == QQuickGridView::SnapOneRow) {
// if we've been dragged < averageSize/2 then bias towards the next item
- qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
+ qreal dist = data.move.value() - data.pressPos;
qreal bias = -dist < rowSize()/2 ? rowSize()/2 : 0;
if (isContentFlowReversed())
bias = -bias;
@@ -1309,13 +1311,14 @@ void QQuickGridView::setHighlightFollowsCurrentItem(bool autoHighlight)
/*!
- \qmlproperty model QtQuick::GridView::model
- This property holds the model providing data for the grid.
+ \qmlproperty model QtQuick::GridView::model
+ This property holds the model providing data for the grid.
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 VisualItemModel, or provided by C++ model classes. If a C++ model class is
- used, it must be a subclass of \l QAbstractItemModel or a simple list.
+ in the view. Models can be created directly in QML using \l ListModel,
+ \l XmlListModel, \l DelegateModel, or \l ObjectModel, or provided by C++
+ model classes. If a C++ model class is used, it must be a subclass of
+ \l QAbstractItemModel or a simple list.
\sa {qml-data-models}{Data Models}
*/
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index 154e3241b1..eba3842c10 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -67,6 +67,7 @@
#include <QtQuick/private/qquickstate_p.h>
#include <private/qquickitem_p.h>
#include <QtQuick/private/qquickaccessibleattached_p.h>
+#include <QtQuick/private/qquickhoverhandler_p.h>
#include <QtQuick/private/qquickpointerhandler_p.h>
#include <private/qv4engine_p.h>
@@ -356,7 +357,7 @@ void QQuickItemKeyFilter::componentComplete()
\qmltype KeyNavigation
\instantiates QQuickKeyNavigationAttached
\inqmlmodule QtQuick
- \ingroup qtquick-input
+ \ingroup qtquick-input-handlers
\brief Supports key navigation by arrow keys.
Key-based user interfaces commonly allow the use of arrow keys to navigate between
@@ -815,7 +816,7 @@ bool QQuickKeysAttached::isConnected(const char *signalName) const
\qmltype Keys
\instantiates QQuickKeysAttached
\inqmlmodule QtQuick
- \ingroup qtquick-input
+ \ingroup qtquick-input-handlers
\brief Provides key handling to Items.
All visual primitives support key handling via the Keys
@@ -2453,20 +2454,19 @@ bool QQuickItemPrivate::canAcceptTabFocus(QQuickItem *item)
return true;
#if QT_CONFIG(accessibility)
- if (QObject *acc = qmlAttachedPropertiesObject<QQuickAccessibleAttached>(item, false)) {
- int role = acc->property("role").toInt();
- if (role == QAccessible::EditableText
- || role == QAccessible::Table
- || role == QAccessible::List
- || role == QAccessible::SpinBox) {
- return true;
- } else if (role == QAccessible::ComboBox) {
- QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(item);
+ QAccessible::Role role = QQuickItemPrivate::get(item)->accessibleRole();
+ if (role == QAccessible::EditableText || role == QAccessible::Table || role == QAccessible::List) {
+ return true;
+ } else if (role == QAccessible::ComboBox || role == QAccessible::SpinBox) {
+ if (QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(item))
return iface->state().editable;
- }
}
#endif
+ QVariant editable = item->property("editable");
+ if (editable.isValid())
+ return editable.toBool();
+
QVariant readonly = item->property("readOnly");
if (readonly.isValid() && !readonly.toBool() && item->property("text").isValid())
return true;
@@ -2565,8 +2565,19 @@ QQuickItem* QQuickItemPrivate::nextPrevItemInTabFocusChain(QQuickItem *item, boo
from = item->parentItem();
}
bool skip = false;
- QQuickItem * startItem = item;
- QQuickItem * firstFromItem = from;
+
+ QQuickItem *startItem = item;
+ // Protect from endless loop:
+ // If we start on an invisible item we will not find it again.
+ // If there is no other item which can become the focus item, we have a forever loop,
+ // since the protection only works if we encounter the first item again.
+ while (startItem && !startItem->isVisible()) {
+ startItem = startItem->parentItem();
+ }
+ if (!startItem)
+ return item;
+
+ QQuickItem *firstFromItem = from;
QQuickItem *current = item;
qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: startItem:" << startItem;
qCDebug(DBG_FOCUS) << "QQuickItemPrivate::nextPrevItemInTabFocusChain: firstFromItem:" << firstFromItem;
@@ -5201,6 +5212,17 @@ void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event)
}
}
+bool QQuickItemPrivate::anyPointerHandlerWants(QQuickEventPoint *point) const
+{
+ if (!hasPointerHandlers())
+ return false;
+ for (QQuickPointerHandler *handler : extra->pointerHandlers) {
+ if (handler->wantsEventPoint(point))
+ return true;
+ }
+ return false;
+}
+
/*!
\internal
Deliver the \a event to all PointerHandlers which are in the pre-determined
@@ -7404,6 +7426,8 @@ void QQuickItemPrivate::setHasHoverInChild(bool hasHover)
QQuickItemPrivate *otherChildPrivate = QQuickItemPrivate::get(otherChild);
if (otherChildPrivate->subtreeHoverEnabled || otherChildPrivate->hoverEnabled)
return; // nope! sorry, something else wants it kept on.
+ if (otherChildPrivate->hasHoverHandlers())
+ return; // nope! sorry, we have pointer handlers which are interested.
}
}
@@ -8177,6 +8201,16 @@ bool QQuickItemPrivate::hasPointerHandlers() const
return extra.isAllocated() && !extra->pointerHandlers.isEmpty();
}
+bool QQuickItemPrivate::hasHoverHandlers() const
+{
+ if (!hasPointerHandlers())
+ return false;
+ for (QQuickPointerHandler *h : extra->pointerHandlers)
+ if (qmlobject_cast<QQuickHoverHandler *>(h))
+ return true;
+ return false;
+}
+
#if QT_CONFIG(quick_shadereffect)
QQuickItemLayer::QQuickItemLayer(QQuickItem *item)
: m_item(item)
@@ -8762,7 +8796,7 @@ void QV4::Heap::QQuickItemWrapper::markObjects(QV4::Heap::Base *that, QV4::MarkS
quint64 QQuickItemPrivate::_q_createJSWrapper(QV4::ExecutionEngine *engine)
{
- return (engine->memoryManager->allocObject<QQuickItemWrapper>(q_func()))->asReturnedValue();
+ return (engine->memoryManager->allocate<QQuickItemWrapper>(q_func()))->asReturnedValue();
}
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h
index cfdb2ad5b7..6f601e0872 100644
--- a/src/quick/items/qquickitem.h
+++ b/src/quick/items/qquickitem.h
@@ -163,6 +163,7 @@ public:
// Remember to increment the size of QQuickItemPrivate::flags
};
Q_DECLARE_FLAGS(Flags, Flag)
+ Q_FLAG(Flags)
enum ItemChange {
ItemChildAddedChange, // value.item
diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h
index 0204ac07c5..11b47114cf 100644
--- a/src/quick/items/qquickitem_p.h
+++ b/src/quick/items/qquickitem_p.h
@@ -280,6 +280,7 @@ public:
QQuickItemLayer *layer() const;
bool hasPointerHandlers() const;
+ bool hasHoverHandlers() const;
// data property
static void data_append(QQmlListProperty<QObject> *, QObject *);
@@ -330,12 +331,26 @@ public:
Q_DECLARE_FLAGS(ChangeTypes, ChangeType)
struct ChangeListener {
- ChangeListener(QQuickItemChangeListener *l = nullptr, QQuickItemPrivate::ChangeTypes t = nullptr) : listener(l), types(t), gTypes(QQuickGeometryChange::All) {}
- ChangeListener(QQuickItemChangeListener *l, QQuickGeometryChange gt) : listener(l), types(Geometry), gTypes(gt) {}
+ using ChangeTypes = QQuickItemPrivate::ChangeTypes;
+
+ ChangeListener(QQuickItemChangeListener *l = nullptr, ChangeTypes t = nullptr)
+ : listener(l)
+ , types(t)
+ , gTypes(QQuickGeometryChange::All)
+ {}
+
+ ChangeListener(QQuickItemChangeListener *l, QQuickGeometryChange gt)
+ : listener(l)
+ , types(Geometry)
+ , gTypes(gt)
+ {}
+
+ bool operator==(const ChangeListener &other) const
+ { return listener == other.listener && types == other.types; }
+
QQuickItemChangeListener *listener;
- QQuickItemPrivate::ChangeTypes types;
+ ChangeTypes types;
QQuickGeometryChange gTypes; //NOTE: not used for ==
- bool operator==(const ChangeListener &other) const { return listener == other.listener && types == other.types; }
};
struct ExtraData {
@@ -582,6 +597,7 @@ public:
#endif
void deliverShortcutOverrideEvent(QKeyEvent *);
+ bool anyPointerHandlerWants(QQuickEventPoint *point) const;
virtual bool handlePointerEvent(QQuickPointerEvent *, bool avoidExclusiveGrabber = false);
virtual void setVisible(bool visible);
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index 51a91e1f7a..a20150e3b9 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -64,6 +64,9 @@
#if QT_CONFIG(quick_pathview)
#include "qquickpathview_p.h"
#endif
+#if QT_CONFIG(quick_tableview)
+#include "qquicktableview_p.h"
+#endif
#if QT_CONFIG(quick_viewtransitions)
#include "qquickitemviewtransition_p.h"
#endif
@@ -113,6 +116,12 @@
#include <private/qqmlmetatype_p.h>
#include <QtQuick/private/qquickaccessibleattached_p.h>
+#include "handlers/qquickdraghandler_p.h"
+#include "handlers/qquickhoverhandler_p.h"
+#include "handlers/qquickpinchhandler_p.h"
+#include "handlers/qquickpointhandler_p.h"
+#include "handlers/qquicktaphandler_p.h"
+
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcTransient)
QT_END_NAMESPACE
@@ -135,6 +144,9 @@ static QQmlPrivate::AutoParentResult qquickitem_autoParent(QObject *obj, QObject
win->setTransientParent(parentItem->window());
return QQmlPrivate::Parented;
}
+ } else if (QQuickPointerHandler *handler = qmlobject_cast<QQuickPointerHandler *>(obj)) {
+ handler->setParent(parent);
+ return QQmlPrivate::Parented;
}
return QQmlPrivate::IncompatibleObject;
} else if (QQuickWindow *parentWindow = qmlobject_cast<QQuickWindow *>(parent)) {
@@ -422,6 +434,42 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickAnimatedImage, 11>(uri, 2, 11,"AnimatedImage");
#endif
qmlRegisterType<QQuickItem, 11>(uri, 2, 11,"Item");
+ qmlRegisterType<QQuickFlickable, 12>(uri, 2, 12, "Flickable");
+
+ // classes related to Input Handlers which are newly exposed since 5.12
+ qmlRegisterUncreatableType<QQuickPointerEvent>(uri, 2, 12, "PointerEvent",
+ QQuickPointerHandler::tr("PointerEvent is only available as a parameter of several signals in PointerHandler"));
+ qmlRegisterUncreatableType<QQuickPointerMouseEvent>(uri, 2, 12, "PointerMouseEvent",
+ QQuickPointerHandler::tr("PointerMouseEvent is only available as a parameter of several signals in PointerHandler"));
+ qmlRegisterUncreatableType<QQuickPointerTouchEvent>(uri, 2, 12, "PointerTouchEvent",
+ QQuickPointerHandler::tr("PointerTouchEvent is only available as a parameter of several signals in PointerHandler"));
+ qmlRegisterUncreatableType<QQuickEventPoint>(uri, 2, 12, "EventPoint",
+ QQuickPointerHandler::tr("EventPoint is only available as a member of PointerEvent"));
+ qmlRegisterUncreatableType<QQuickEventTouchPoint>(uri, 2, 12, "EventTouchPoint",
+ QQuickPointerHandler::tr("EventTouchPoint is only available as a member of PointerEvent"));
+ qmlRegisterUncreatableType<QQuickPointerDevice>(uri, 2, 12, "PointerDevice",
+ QQuickPointerHandler::tr("PointerDevice is only available as a property of PointerEvent"));
+
+ // Input Handlers are part of QtQuick, not a separate module, since 5.12
+ qmlRegisterUncreatableType<QQuickPointerHandler>(uri, 2, 12, "PointerHandler",
+ QQuickPointerHandler::tr("PointerHandler is an abstract base class"));
+ qmlRegisterType<QQuickPointHandler>(uri, 2, 12, "PointHandler");
+ qmlRegisterType<QQuickDragHandler>(uri, 2, 12, "DragHandler");
+ qmlRegisterUncreatableType<QQuickDragAxis>(uri, 2, 12, "DragAxis",
+ QQuickDragHandler::tr("DragAxis is only available as a grouped property of DragHandler"));
+ qmlRegisterType<QQuickHoverHandler>(uri, 2, 12, "HoverHandler");
+ qmlRegisterType<QQuickPinchHandler>(uri, 2, 12, "PinchHandler");
+ qmlRegisterType<QQuickTapHandler>(uri, 2, 12, "TapHandler");
+ qRegisterMetaType<QQuickHandlerPoint>();
+
+ // The rest of the 5.12 revisions
+ qmlRegisterType<QQuickAnimatedSprite, 12>("QtQuick", 2, 12, "AnimatedSprite");
+ qmlRegisterType<QQuickGradient, 12>(uri, 2, 12, "Gradient");
+ qmlRegisterType<QQuickFlickable, 12>(uri, 2, 12, "Flickable");
+ qmlRegisterType<QQuickText, 12>(uri, 2, 12, "Text");
+#if QT_CONFIG(quick_tableview)
+ qmlRegisterType<QQuickTableView>(uri, 2, 12, "TableView");
+#endif
}
static void initResources()
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index f2e055e874..7493bdafda 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qquickitemview_p_p.h"
+#include "qquickitemviewfxitem_p_p.h"
#include <QtQuick/private/qquicktransition_p.h>
#include <QtQml/QQmlInfo>
#include "qplatformdefs.h"
@@ -52,117 +53,14 @@ Q_LOGGING_CATEGORY(lcItemViewDelegateLifecycle, "qt.quick.itemview.lifecycle")
#endif
FxViewItem::FxViewItem(QQuickItem *i, QQuickItemView *v, bool own, QQuickItemViewAttached *attached)
- : item(i)
+ : QQuickItemViewFxItem(i, own, QQuickItemViewPrivate::get(v))
, view(v)
- , transitionableItem(nullptr)
, attached(attached)
- , ownItem(own)
- , releaseAfterTransition(false)
- , trackGeom(false)
{
if (attached) // can be null for default components (see createComponentItem)
attached->setView(view);
}
-FxViewItem::~FxViewItem()
-{
- delete transitionableItem;
- if (ownItem && item) {
- trackGeometry(false);
- item->setParentItem(nullptr);
- item->deleteLater();
- item = nullptr;
- }
-}
-
-qreal FxViewItem::itemX() const
-{
- return transitionableItem ? transitionableItem->itemX() : (item ? item->x() : 0);
-}
-
-qreal FxViewItem::itemY() const
-{
- return transitionableItem ? transitionableItem->itemY() : (item ? item->y() : 0);
-}
-
-void FxViewItem::moveTo(const QPointF &pos, bool immediate)
-{
- if (transitionableItem)
- transitionableItem->moveTo(pos, immediate);
- else if (item)
- item->setPosition(pos);
-}
-
-void FxViewItem::setVisible(bool visible)
-{
- if (!visible && transitionableItem && transitionableItem->transitionScheduledOrRunning())
- return;
- if (item)
- QQuickItemPrivate::get(item)->setCulled(!visible);
-}
-
-void FxViewItem::trackGeometry(bool track)
-{
- if (track) {
- if (!trackGeom) {
- if (item) {
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
- itemPrivate->addItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
- }
- trackGeom = true;
- }
- } else {
- if (trackGeom) {
- if (item) {
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
- itemPrivate->removeItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
- }
- trackGeom = false;
- }
- }
-}
-
-QQuickItemViewTransitioner::TransitionType FxViewItem::scheduledTransitionType() const
-{
- return transitionableItem ? transitionableItem->nextTransitionType : QQuickItemViewTransitioner::NoTransition;
-}
-
-bool FxViewItem::transitionScheduledOrRunning() const
-{
- return transitionableItem ? transitionableItem->transitionScheduledOrRunning() : false;
-}
-
-bool FxViewItem::transitionRunning() const
-{
- return transitionableItem ? transitionableItem->transitionRunning() : false;
-}
-
-bool FxViewItem::isPendingRemoval() const
-{
- return transitionableItem ? transitionableItem->isPendingRemoval() : false;
-}
-
-void FxViewItem::transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget)
-{
- if (!transitioner)
- return;
- if (!transitionableItem)
- transitionableItem = new QQuickItemViewTransitionableItem(item);
- transitioner->transitionNextReposition(transitionableItem, type, asTarget);
-}
-
-bool FxViewItem::prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds)
-{
- return transitionableItem ? transitionableItem->prepareTransition(transitioner, index, viewBounds) : false;
-}
-
-void FxViewItem::startTransition(QQuickItemViewTransitioner *transitioner)
-{
- if (transitionableItem)
- transitionableItem->startTransition(transitioner, index);
-}
-
-
QQuickItemViewChangeSet::QQuickItemViewChangeSet()
: active(false)
{
@@ -1329,6 +1227,12 @@ void QQuickItemView::trackedPositionChanged()
Q_D(QQuickItemView);
if (!d->trackedItem || !d->currentItem)
return;
+
+ if (d->inLayout) {
+ polish();
+ return;
+ }
+
if (d->moveReason == QQuickItemViewPrivate::SetIndex) {
qreal trackedPos = d->trackedItem->position();
qreal trackedSize = d->trackedItem->size();
diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h
index e250cf0ccb..ea5b5df9c6 100644
--- a/src/quick/items/qquickitemview_p_p.h
+++ b/src/quick/items/qquickitemview_p_p.h
@@ -56,6 +56,7 @@
QT_REQUIRE_CONFIG(quick_itemview);
#include "qquickitemview_p.h"
+#include "qquickitemviewfxitem_p_p.h"
#include "qquickitemviewtransition_p.h"
#include "qquickflickable_p_p.h"
#include <QtQml/private/qqmlobjectmodel_p.h>
@@ -65,47 +66,13 @@ QT_REQUIRE_CONFIG(quick_itemview);
QT_BEGIN_NAMESPACE
-
-class Q_AUTOTEST_EXPORT FxViewItem
+class Q_AUTOTEST_EXPORT FxViewItem : public QQuickItemViewFxItem
{
public:
FxViewItem(QQuickItem *, QQuickItemView *, bool own, QQuickItemViewAttached *attached);
- virtual ~FxViewItem();
-
- qreal itemX() const;
- qreal itemY() const;
- inline qreal itemWidth() const { return item ? item->width() : 0; }
- inline qreal itemHeight() const { return item ? item->height() : 0; }
-
- void moveTo(const QPointF &pos, bool immediate);
- void setVisible(bool visible);
- void trackGeometry(bool track);
-
- QQuickItemViewTransitioner::TransitionType scheduledTransitionType() const;
- bool transitionScheduledOrRunning() const;
- bool transitionRunning() const;
- bool isPendingRemoval() const;
-
- void transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget);
- bool prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds);
- void startTransition(QQuickItemViewTransitioner *transitioner);
-
- // these are positions and sizes along the current direction of scrolling/flicking
- virtual qreal position() const = 0;
- virtual qreal endPosition() const = 0;
- virtual qreal size() const = 0;
- virtual qreal sectionSize() const = 0;
-
- virtual bool contains(qreal x, qreal y) const = 0;
- QPointer<QQuickItem> item;
QQuickItemView *view;
- QQuickItemViewTransitionableItem *transitionableItem;
QQuickItemViewAttached *attached;
- int index;
- bool ownItem;
- bool releaseAfterTransition;
- bool trackGeom;
};
diff --git a/src/quick/items/qquickitemviewfxitem.cpp b/src/quick/items/qquickitemviewfxitem.cpp
new file mode 100644
index 0000000000..f9c65967ea
--- /dev/null
+++ b/src/quick/items/qquickitemviewfxitem.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickitemviewfxitem_p_p.h"
+#include "qquickitem_p.h"
+#include "qquickitemview_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QQuickItemViewFxItem::QQuickItemViewFxItem(QQuickItem *item, bool ownItem, QQuickItemChangeListener* changeListener)
+ : item(item)
+ , ownItem(ownItem)
+ , changeListener(changeListener)
+ , transitionableItem(nullptr)
+ , releaseAfterTransition(false)
+ , trackGeom(false)
+{
+}
+
+QQuickItemViewFxItem::~QQuickItemViewFxItem()
+{
+ delete transitionableItem;
+ if (ownItem && item) {
+ trackGeometry(false);
+ item->setParentItem(0);
+ item->deleteLater();
+ }
+}
+
+qreal QQuickItemViewFxItem::itemX() const
+{
+ return transitionableItem ? transitionableItem->itemX() : (item ? item->x() : 0);
+}
+
+qreal QQuickItemViewFxItem::itemY() const
+{
+ return transitionableItem ? transitionableItem->itemY() : (item ? item->y() : 0);
+}
+
+void QQuickItemViewFxItem::moveTo(const QPointF &pos, bool immediate)
+{
+ if (transitionableItem)
+ transitionableItem->moveTo(pos, immediate);
+ else if (item)
+ item->setPosition(pos);
+}
+
+void QQuickItemViewFxItem::setVisible(bool visible)
+{
+ if (!visible && transitionableItem && transitionableItem->transitionScheduledOrRunning())
+ return;
+ if (item)
+ QQuickItemPrivate::get(item)->setCulled(!visible);
+}
+
+void QQuickItemViewFxItem::trackGeometry(bool track)
+{
+ if (track) {
+ if (!trackGeom) {
+ if (item) {
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ itemPrivate->addItemChangeListener(changeListener, QQuickItemPrivate::Geometry);
+ }
+ trackGeom = true;
+ }
+ } else {
+ if (trackGeom) {
+ if (item) {
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ itemPrivate->removeItemChangeListener(changeListener, QQuickItemPrivate::Geometry);
+ }
+ trackGeom = false;
+ }
+ }
+}
+
+QRectF QQuickItemViewFxItem::geometry() const
+{
+ return QRectF(item->position(), item->size());
+}
+
+void QQuickItemViewFxItem::setGeometry(const QRectF &geometry)
+{
+ item->setPosition(geometry.topLeft());
+ item->setSize(geometry.size());
+}
+
+QQuickItemViewTransitioner::TransitionType QQuickItemViewFxItem::scheduledTransitionType() const
+{
+ return transitionableItem ? transitionableItem->nextTransitionType : QQuickItemViewTransitioner::NoTransition;
+}
+
+bool QQuickItemViewFxItem::transitionScheduledOrRunning() const
+{
+ return transitionableItem ? transitionableItem->transitionScheduledOrRunning() : false;
+}
+
+bool QQuickItemViewFxItem::transitionRunning() const
+{
+ return transitionableItem ? transitionableItem->transitionRunning() : false;
+}
+
+bool QQuickItemViewFxItem::isPendingRemoval() const
+{
+ return transitionableItem ? transitionableItem->isPendingRemoval() : false;
+}
+
+void QQuickItemViewFxItem::transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget)
+{
+ if (!transitioner)
+ return;
+ if (!transitionableItem)
+ transitionableItem = new QQuickItemViewTransitionableItem(item);
+ transitioner->transitionNextReposition(transitionableItem, type, asTarget);
+}
+
+bool QQuickItemViewFxItem::prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds)
+{
+ return transitionableItem ? transitionableItem->prepareTransition(transitioner, index, viewBounds) : false;
+}
+
+void QQuickItemViewFxItem::startTransition(QQuickItemViewTransitioner *transitioner)
+{
+ if (transitionableItem)
+ transitionableItem->startTransition(transitioner, index);
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/quick/items/qquickitemviewfxitem_p_p.h b/src/quick/items/qquickitemviewfxitem_p_p.h
new file mode 100644
index 0000000000..48ffe248bc
--- /dev/null
+++ b/src/quick/items/qquickitemviewfxitem_p_p.h
@@ -0,0 +1,108 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKFXVIEWITEM_P_P_H
+#define QQUICKFXVIEWITEM_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtQuick/private/qtquickglobal_p.h>
+#include <QtQuick/private/qquickitem_p.h>
+#include <QtQuick/private/qquickitemviewtransition_p.h>
+
+QT_REQUIRE_CONFIG(quick_itemview);
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQuickItemViewFxItem
+{
+public:
+ QQuickItemViewFxItem(QQuickItem *item, bool ownItem, QQuickItemChangeListener *changeListener);
+ virtual ~QQuickItemViewFxItem();
+
+ qreal itemX() const;
+ qreal itemY() const;
+ inline qreal itemWidth() const { return item ? item->width() : 0; }
+ inline qreal itemHeight() const { return item ? item->height() : 0; }
+
+ void moveTo(const QPointF &pos, bool immediate);
+ void setVisible(bool visible);
+ void trackGeometry(bool track);
+
+ QRectF geometry() const;
+ void setGeometry(const QRectF &geometry);
+
+ QQuickItemViewTransitioner::TransitionType scheduledTransitionType() const;
+ bool transitionScheduledOrRunning() const;
+ bool transitionRunning() const;
+ bool isPendingRemoval() const;
+
+ void transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget);
+ bool prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds);
+ void startTransition(QQuickItemViewTransitioner *transitioner);
+
+ // these are positions and sizes along the current direction of scrolling/flicking
+ virtual qreal position() const = 0;
+ virtual qreal endPosition() const = 0;
+ virtual qreal size() const = 0;
+ virtual qreal sectionSize() const = 0;
+
+ virtual bool contains(qreal x, qreal y) const = 0;
+
+ int index = -1;
+ QPointer<QQuickItem> item;
+ bool ownItem;
+ QQuickItemChangeListener *changeListener;
+ QQuickItemViewTransitionableItem *transitionableItem;
+ bool releaseAfterTransition;
+ bool trackGeom;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKFXVIEWITEM_P_P_H
diff --git a/src/quick/items/qquickitemviewtransition.cpp b/src/quick/items/qquickitemviewtransition.cpp
index c06dcce0d9..0fde0beb75 100644
--- a/src/quick/items/qquickitemviewtransition.cpp
+++ b/src/quick/items/qquickitemviewtransition.cpp
@@ -500,6 +500,8 @@ void QQuickItemViewTransitionableItem::startTransition(QQuickItemViewTransitione
}
if (!transition || transition->m_type != nextTransitionType || transition->m_isTarget != isTransitionTarget) {
+ if (transition)
+ transition->cancel();
delete transition;
transition = new QQuickItemViewTransitionJob;
}
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index 19033e03f1..540805ac28 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -1501,7 +1501,7 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
qreal tempPosition = isContentFlowReversed() ? -position()-size() : position();
if (snapMode == QQuickListView::SnapOneItem && moveReason == Mouse) {
// if we've been dragged < averageSize/2 then bias towards the next item
- qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
+ qreal dist = data.move.value() - data.pressPos;
qreal bias = 0;
if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2)
bias = averageSize/2;
@@ -1512,13 +1512,13 @@ void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExte
tempPosition -= bias;
}
FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
- if (strictHighlightRange && currentItem && (!topItem || topItem->index != currentIndex)) {
+ if (strictHighlightRange && currentItem && (!topItem || (topItem->index != currentIndex && fixupMode == Immediate))) {
// StrictlyEnforceRange always keeps an item in range
updateHighlight();
topItem = currentItem;
}
FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
- if (strictHighlightRange && currentItem && (!bottomItem || bottomItem->index != currentIndex)) {
+ if (strictHighlightRange && currentItem && (!bottomItem || (bottomItem->index != currentIndex && fixupMode == Immediate))) {
// StrictlyEnforceRange always keeps an item in range
updateHighlight();
bottomItem = currentItem;
@@ -1599,7 +1599,7 @@ bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
if (data.move.value() < minExtent) {
if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
// if we've been dragged < averageSize/2 then bias towards the next item
- qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
+ qreal dist = data.move.value() - data.pressPos;
qreal bias = dist < averageSize/2 ? averageSize/2 : 0;
if (isContentFlowReversed())
bias = -bias;
@@ -1616,7 +1616,7 @@ bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExte
if (data.move.value() > maxExtent) {
if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
// if we've been dragged < averageSize/2 then bias towards the next item
- qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
+ qreal dist = data.move.value() - data.pressPos;
qreal bias = -dist < averageSize/2 ? averageSize/2 : 0;
if (isContentFlowReversed())
bias = -bias;
@@ -1982,7 +1982,7 @@ QQuickListView::~QQuickListView()
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 VisualItemModel, or provided by C++ model classes. If a C++ model class is
+ or \l ObjectModel, or provided by C++ model classes. If a C++ model class is
used, it must be a subclass of \l QAbstractItemModel or a simple list.
\sa {qml-data-models}{Data Models}
@@ -2521,7 +2521,11 @@ void QQuickListView::setHighlightResizeDuration(int duration)
the view.
\li ListView.SnapOneItem - the view settles no more than one item away from the first
visible item at the time the mouse button is released. This mode is particularly
- useful for moving one page at a time.
+ useful for moving one page at a time. When SnapOneItem is enabled, the ListView will
+ show a stronger affinity to neighboring items when movement occurs. For example, a
+ short drag that snaps back to the current item with SnapToItem might snap to a
+ neighboring item with SnapOneItem.
+
\endlist
\c snapMode does not affect the \l currentIndex. To update the
diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp
index b5ae41daef..d0e29c204e 100644
--- a/src/quick/items/qquickloader.cpp
+++ b/src/quick/items/qquickloader.cpp
@@ -54,7 +54,7 @@ static const QQuickItemPrivate::ChangeTypes watchedChanges
= QQuickItemPrivate::Geometry | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight;
QQuickLoaderPrivate::QQuickLoaderPrivate()
- : item(nullptr), object(nullptr), component(nullptr), itemContext(nullptr), incubator(nullptr), updatingSize(false),
+ : item(nullptr), object(nullptr), itemContext(nullptr), incubator(nullptr), updatingSize(false),
active(true), loadingFromSource(false), asynchronous(false)
{
}
@@ -111,11 +111,10 @@ void QQuickLoaderPrivate::clear()
QObject::disconnect(component, SIGNAL(progressChanged(qreal)),
q, SIGNAL(progressChanged()));
component->deleteLater();
- component = nullptr;
+ component.setObject(nullptr, q);
} else if (component) {
- component = nullptr;
+ component.setObject(nullptr, q);
}
- componentStrongReference.clear();
source = QUrl();
if (item) {
@@ -441,7 +440,7 @@ void QQuickLoader::loadFromSource()
if (isComponentComplete()) {
QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
if (!d->component)
- d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
+ d->component.setObject(new QQmlComponent(qmlEngine(this), d->source, mode, this), this);
d->load();
}
}
@@ -484,11 +483,7 @@ void QQuickLoader::setSourceComponent(QQmlComponent *comp)
d->clear();
- d->component = comp;
- if (comp) {
- if (QQmlData *ddata = QQmlData::get(comp))
- d->componentStrongReference = ddata->jsWrapper;
- }
+ d->component.setObject(comp, this);
d->loadingFromSource = false;
if (d->active)
@@ -832,7 +827,7 @@ void QQuickLoader::componentComplete()
if (d->loadingFromSource) {
QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
if (!d->component)
- d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
+ d->component.setObject(new QQmlComponent(qmlEngine(this), d->source, mode, this), this);
}
d->load();
}
@@ -1007,7 +1002,7 @@ QUrl QQuickLoaderPrivate::resolveSourceUrl(QQmlV4Function *args)
QV4::ReturnedValue QQuickLoaderPrivate::extractInitialPropertyValues(QQmlV4Function *args, QObject *loader, bool *error)
{
QV4::Scope scope(args->v4engine());
- QV4::ScopedValue valuemap(scope, QV4::Primitive::undefinedValue());
+ QV4::ScopedValue valuemap(scope, QV4::Encode::undefined());
if (args->length() >= 2) {
QV4::ScopedValue v(scope, (*args)[1]);
if (!v->isObject() || v->as<QV4::ArrayObject>()) {
diff --git a/src/quick/items/qquickloader_p_p.h b/src/quick/items/qquickloader_p_p.h
index 7492527401..349b5c6c06 100644
--- a/src/quick/items/qquickloader_p_p.h
+++ b/src/quick/items/qquickloader_p_p.h
@@ -103,8 +103,7 @@ public:
QUrl source;
QQuickItem *item;
QObject *object;
- QQmlComponent *component;
- QV4::PersistentValue componentStrongReference; // To ensure GC doesn't delete components created by Qt.createComponent
+ QQmlStrongJSQObjectReference<QQmlComponent> component;
QQmlContext *itemContext;
QQuickLoaderIncubator *incubator;
QV4::PersistentValue initialPropertyValues;
diff --git a/src/quick/items/qquickmultipointtoucharea.cpp b/src/quick/items/qquickmultipointtoucharea.cpp
index b1438d7541..3eca535a67 100644
--- a/src/quick/items/qquickmultipointtoucharea.cpp
+++ b/src/quick/items/qquickmultipointtoucharea.cpp
@@ -610,6 +610,9 @@ void QQuickMultiPointTouchArea::updateTouchData(QEvent *event)
else { // QEvent::MouseButtonPress
addTouchPoint(me);
started = true;
+ _mouseQpaTouchPoint.setStartPos(me->localPos());
+ _mouseQpaTouchPoint.setStartScenePos(me->windowPos());
+ _mouseQpaTouchPoint.setStartScreenPos(me->screenPos());
_mouseQpaTouchPoint.setState(Qt::TouchPointPressed);
}
touchPoints << _mouseQpaTouchPoint;
diff --git a/src/quick/items/qquickmultipointtoucharea_p.h b/src/quick/items/qquickmultipointtoucharea_p.h
index f1550b4ac6..634ea1c2e2 100644
--- a/src/quick/items/qquickmultipointtoucharea_p.h
+++ b/src/quick/items/qquickmultipointtoucharea_p.h
@@ -185,8 +185,8 @@ private:
class QQuickGrabGestureEvent : public QObject
{
Q_OBJECT
- Q_PROPERTY(QQmlListProperty<QObject> touchPoints READ touchPoints)
- Q_PROPERTY(qreal dragThreshold READ dragThreshold)
+ Q_PROPERTY(QQmlListProperty<QObject> touchPoints READ touchPoints CONSTANT)
+ Q_PROPERTY(qreal dragThreshold READ dragThreshold CONSTANT)
public:
QQuickGrabGestureEvent() : _dragThreshold(QGuiApplication::styleHints()->startDragDistance()) {}
diff --git a/src/quick/items/qquickpainteditem.h b/src/quick/items/qquickpainteditem.h
index b057f4295d..ddc1fd99d9 100644
--- a/src/quick/items/qquickpainteditem.h
+++ b/src/quick/items/qquickpainteditem.h
@@ -71,6 +71,7 @@ public:
FastFBOResizing = 0x1
};
Q_DECLARE_FLAGS(PerformanceHints, PerformanceHint)
+ Q_FLAG(PerformanceHints)
void update(const QRect &rect = QRect());
diff --git a/src/quick/items/qquickpositioners.cpp b/src/quick/items/qquickpositioners.cpp
index 55bafd9f83..512f59d799 100644
--- a/src/quick/items/qquickpositioners.cpp
+++ b/src/quick/items/qquickpositioners.cpp
@@ -900,11 +900,7 @@ void QQuickPositionerAttached::setIsLastItem(bool isLastItem)
cases, these lists will be empty. See the \l ViewTransition documentation for more details
and examples on using these transitions.
- \note In \l {Qt Quick 1}, this transition was applied to all items that were part of the
- positioner at the time of its creation. From \l {Qt Quick}{Qt Quick 2} onwards, positioners apply the
- \l populate transition to these items instead.
-
- \sa add, ViewTransition, {Qt Quick Examples - Positioners}
+ \sa add, populate, ViewTransition, {Qt Quick Examples - Positioners}
*/
/*!
\qmlproperty real QtQuick::Column::spacing
@@ -1089,11 +1085,7 @@ void QQuickColumn::reportConflictingAnchors()
cases, these lists will be empty. See the \l ViewTransition documentation for more details
and examples on using these transitions.
- \note In \l {Qt Quick 1}, this transition was applied to all items that were part of the
- positioner at the time of its creation. From \l {Qt Quick}{QtQuick 2} onwards, positioners apply the
- \l populate transition to these items instead.
-
- \sa add, ViewTransition, {Qt Quick Examples - Positioners}
+ \sa add, populate, ViewTransition, {Qt Quick Examples - Positioners}
*/
/*!
\qmlproperty real QtQuick::Row::spacing
@@ -1378,11 +1370,7 @@ void QQuickRow::reportConflictingAnchors()
cases, these lists will be empty. See the \l ViewTransition documentation for more details
and examples on using these transitions.
- \note In \l {Qt Quick 1}, this transition was applied to all items that were part of the
- positioner at the time of its creation. From \l {Qt Quick}{QtQuick 2} onwards, positioners apply the
- \l populate transition to these items instead.
-
- \sa add, ViewTransition, {Qt Quick Examples - Positioners}
+ \sa add, populate, ViewTransition, {Qt Quick Examples - Positioners}
*/
/*!
\qmlproperty qreal QtQuick::Grid::spacing
@@ -1974,11 +1962,7 @@ void QQuickGrid::reportConflictingAnchors()
cases, these lists will be empty. See the \l ViewTransition documentation for more details
and examples on using these transitions.
- \note In \l {Qt Quick 1}, this transition was applied to all items that were part of the
- positioner at the time of its creation. From \l {Qt Quick}{QtQuick 2} onwards, positioners apply the
- \l populate transition to these items instead.
-
- \sa add, ViewTransition, {Qt Quick Examples - Positioners}
+ \sa add, populate, ViewTransition, {Qt Quick Examples - Positioners}
*/
/*!
\qmlproperty real QtQuick::Flow::spacing
diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp
index bf030b9d80..5e217dcd0c 100644
--- a/src/quick/items/qquickrectangle.cpp
+++ b/src/quick/items/qquickrectangle.cpp
@@ -40,9 +40,13 @@
#include "qquickrectangle_p.h"
#include "qquickrectangle_p_p.h"
+#include <QtQml/qqmlinfo.h>
+
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qsgadaptationlayer_p.h>
+#include <private/qqmlmetatype_p.h>
+
#include <QtGui/qpixmapcache.h>
#include <QtCore/qmath.h>
#include <QtCore/qmetaobject.h>
@@ -219,10 +223,11 @@ void QQuickGradientStop::updateGradient()
of solid color fills or images. Consider using gradients for static items
in a user interface.
- In Qt 5.0, only vertical, linear gradients can be applied to items. If you
- need to apply different orientations of gradients, a combination of rotation
- and clipping will need to be applied to the relevant items. This can
- introduce additional performance requirements for your application.
+ Since Qt 5.12, vertical and horizontal linear gradients can be applied to items.
+ If you need to apply angled gradients, a combination of rotation and clipping
+ can be applied to the relevant items. Alternatively, consider using
+ QtQuick.Shapes::LinearGradient or QtGraphicalEffects::LinearGradient. These
+ approaches can all introduce additional performance requirements for your application.
The use of animations involving gradient stops may not give the desired
result. An alternative way to animate gradients is to use pre-generated
@@ -255,6 +260,28 @@ QQmlListProperty<QQuickGradientStop> QQuickGradient::stops()
return QQmlListProperty<QQuickGradientStop>(this, m_stops);
}
+/*!
+ \qmlproperty enumeration QtQuick::Gradient::orientation
+ \since 5.12
+
+ Set this property to define the direction of the gradient.
+ \list
+ \li Gradient.Vertical - a vertical gradient
+ \li Gradient.Horizontal - a horizontal gradient
+ \endlist
+
+ The default is Gradient.Vertical.
+*/
+void QQuickGradient::setOrientation(Orientation orientation)
+{
+ if (m_orientation == orientation)
+ return;
+
+ m_orientation = orientation;
+ emit orientationChanged();
+ emit updated();
+}
+
QGradientStops QQuickGradient::gradientStops() const
{
QGradientStops stops;
@@ -370,11 +397,11 @@ QQuickPen *QQuickRectangle::border()
}
/*!
- \qmlproperty Gradient QtQuick::Rectangle::gradient
+ \qmlproperty any QtQuick::Rectangle::gradient
The gradient to use to fill the rectangle.
- This property allows for the construction of simple vertical gradients.
+ This property allows for the construction of simple vertical or horizontal gradients.
Other gradients may be formed by adding rotation to the rectangle.
\div {class="float-left"}
@@ -384,37 +411,66 @@ QQuickPen *QQuickRectangle::border()
\snippet qml/rectangle/rectangle-gradient.qml rectangles
\clearfloat
+ The property also accepts gradient presets from QGradient::Preset. Note however
+ that due to Rectangle only supporting simple vertical or horizontal gradients,
+ any preset with an unsupported angle will revert to the closest representation.
+
+ \snippet qml/rectangle/rectangle-gradient.qml presets
+ \clearfloat
+
If both a gradient and a color are specified, the gradient will be used.
\sa Gradient, color
*/
-QQuickGradient *QQuickRectangle::gradient() const
+QJSValue QQuickRectangle::gradient() const
{
Q_D(const QQuickRectangle);
return d->gradient;
}
-void QQuickRectangle::setGradient(QQuickGradient *gradient)
+void QQuickRectangle::setGradient(const QJSValue &gradient)
{
Q_D(QQuickRectangle);
- if (d->gradient == gradient)
+ if (d->gradient.equals(gradient))
return;
- static int updatedSignalIdx = -1;
- if (updatedSignalIdx < 0)
- updatedSignalIdx = QMetaMethod::fromSignal(&QQuickGradient::updated).methodIndex();
+
+ static int updatedSignalIdx = QMetaMethod::fromSignal(&QQuickGradient::updated).methodIndex();
if (d->doUpdateSlotIdx < 0)
d->doUpdateSlotIdx = QQuickRectangle::staticMetaObject.indexOfSlot("doUpdate()");
- if (d->gradient)
- QMetaObject::disconnect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
- d->gradient = gradient;
- if (d->gradient)
- QMetaObject::connect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
+
+ if (auto oldGradient = qobject_cast<QQuickGradient*>(d->gradient.toQObject()))
+ QMetaObject::disconnect(oldGradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
+
+ if (gradient.isQObject()) {
+ if (auto newGradient = qobject_cast<QQuickGradient*>(gradient.toQObject())) {
+ d->gradient = gradient;
+ QMetaObject::connect(newGradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
+ } else {
+ qmlWarning(this) << "Can't assign "
+ << QQmlMetaType::prettyTypeName(gradient.toQObject()) << " to gradient property";
+ d->gradient = QJSValue();
+ }
+ } else if (gradient.isNumber() || gradient.isString()) {
+ QGradient preset(gradient.toVariant().value<QGradient::Preset>());
+ if (preset.type() != QGradient::NoGradient) {
+ d->gradient = gradient;
+ } else {
+ qmlWarning(this) << "No such gradient preset '" << gradient.toString() << "'";
+ d->gradient = QJSValue();
+ }
+ } else if (gradient.isNull() || gradient.isUndefined()) {
+ d->gradient = gradient;
+ } else {
+ qmlWarning(this) << "Unknown gradient type. Expected int, string, or Gradient";
+ d->gradient = QJSValue();
+ }
+
update();
}
void QQuickRectangle::resetGradient()
{
- setGradient(nullptr);
+ setGradient(QJSValue());
}
/*!
@@ -510,10 +566,35 @@ QSGNode *QQuickRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData
rectangle->setAntialiasing(antialiasing());
QGradientStops stops;
- if (d->gradient) {
- stops = d->gradient->gradientStops();
+ bool vertical = true;
+ if (d->gradient.isQObject()) {
+ auto gradient = qobject_cast<QQuickGradient*>(d->gradient.toQObject());
+ Q_ASSERT(gradient);
+ stops = gradient->gradientStops();
+ vertical = gradient->orientation() == QQuickGradient::Vertical;
+ } else if (d->gradient.isNumber() || d->gradient.isString()) {
+ QGradient preset(d->gradient.toVariant().value<QGradient::Preset>());
+ if (preset.type() == QGradient::LinearGradient) {
+ auto linearGradient = static_cast<QLinearGradient&>(preset);
+ const QPointF start = linearGradient.start();
+ const QPointF end = linearGradient.finalStop();
+ vertical = qAbs(start.y() - end.y()) >= qAbs(start.x() - end.x());
+ stops = linearGradient.stops();
+ if ((vertical && start.y() > end.y()) || (!vertical && start.x() > end.x())) {
+ // QSGInternalRectangleNode doesn't support stops in the wrong order,
+ // so we need to manually reverse them here.
+ QGradientStops reverseStops;
+ for (auto it = stops.rbegin(); it != stops.rend(); ++it) {
+ auto stop = *it;
+ stop.first = 1 - stop.first;
+ reverseStops.append(stop);
+ }
+ stops = reverseStops;
+ }
+ }
}
rectangle->setGradientStops(stops);
+ rectangle->setGradientVertical(vertical);
rectangle->update();
diff --git a/src/quick/items/qquickrectangle_p.h b/src/quick/items/qquickrectangle_p.h
index c07ad835fb..d56a03d22d 100644
--- a/src/quick/items/qquickrectangle_p.h
+++ b/src/quick/items/qquickrectangle_p.h
@@ -119,24 +119,35 @@ class Q_QUICK_PRIVATE_EXPORT QQuickGradient : public QObject
Q_OBJECT
Q_PROPERTY(QQmlListProperty<QQuickGradientStop> stops READ stops)
+ Q_PROPERTY(Orientation orientation READ orientation WRITE setOrientation NOTIFY orientationChanged REVISION 12)
Q_CLASSINFO("DefaultProperty", "stops")
+ Q_ENUMS(QGradient::Preset)
public:
QQuickGradient(QObject *parent=nullptr);
~QQuickGradient() override;
+ enum Orientation { Vertical = Qt::Vertical,
+ Horizontal = Qt::Horizontal };
+ Q_ENUM(Orientation)
+
QQmlListProperty<QQuickGradientStop> stops();
+ Orientation orientation() const { return m_orientation; }
+ void setOrientation(Orientation orientation);
+
QGradientStops gradientStops() const;
Q_SIGNALS:
void updated();
+ void orientationChanged();
private:
void doUpdate();
private:
QList<QQuickGradientStop *> m_stops;
+ Orientation m_orientation = Vertical;
friend class QQuickRectangle;
friend class QQuickGradientStop;
};
@@ -147,7 +158,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickRectangle : public QQuickItem
Q_OBJECT
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
- Q_PROPERTY(QQuickGradient *gradient READ gradient WRITE setGradient RESET resetGradient)
+ Q_PROPERTY(QJSValue gradient READ gradient WRITE setGradient RESET resetGradient)
Q_PROPERTY(QQuickPen * border READ border CONSTANT)
Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged)
public:
@@ -158,8 +169,8 @@ public:
QQuickPen *border();
- QQuickGradient *gradient() const;
- void setGradient(QQuickGradient *gradient);
+ QJSValue gradient() const;
+ void setGradient(const QJSValue &gradient);
void resetGradient();
qreal radius() const;
diff --git a/src/quick/items/qquickrectangle_p_p.h b/src/quick/items/qquickrectangle_p_p.h
index 3c1aaf7661..c7c5293f9b 100644
--- a/src/quick/items/qquickrectangle_p_p.h
+++ b/src/quick/items/qquickrectangle_p_p.h
@@ -73,7 +73,7 @@ public:
}
QColor color;
- QQuickGradient *gradient;
+ QJSValue gradient;
QQuickPen *pen;
qreal radius;
static int doUpdateSlotIdx;
diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp
index fb1c6366f2..ec74660d96 100644
--- a/src/quick/items/qquickrendercontrol.cpp
+++ b/src/quick/items/qquickrendercontrol.cpp
@@ -187,7 +187,6 @@ void QQuickRenderControlPrivate::windowDestroyed()
{
if (window) {
rc->invalidate();
- QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
delete QQuickWindowPrivate::get(window)->animationController;
QQuickWindowPrivate::get(window)->animationController = nullptr;
@@ -392,6 +391,7 @@ QImage QQuickRenderControl::grab()
grabContent.setDevicePixelRatio(d->window->effectiveDevicePixelRatio());
}
#endif
+#if QT_CONFIG(thread)
} else if (d->window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) {
QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window);
cd->polishItems();
@@ -409,6 +409,7 @@ QImage QQuickRenderControl::grab()
render();
softwareRenderer->setCurrentPaintDevice(prevDev);
}
+#endif
} else {
qWarning("QQuickRenderControl: grabs are not supported with the current Qt Quick backend");
}
diff --git a/src/quick/items/qquickscalegrid.cpp b/src/quick/items/qquickscalegrid.cpp
index d7a0f1b681..23f179be1d 100644
--- a/src/quick/items/qquickscalegrid.cpp
+++ b/src/quick/items/qquickscalegrid.cpp
@@ -66,6 +66,7 @@ void QQuickScaleGrid::setLeft(int pos)
{
if (_left != pos) {
_left = pos;
+ emit leftBorderChanged();
emit borderChanged();
}
}
@@ -74,6 +75,7 @@ void QQuickScaleGrid::setTop(int pos)
{
if (_top != pos) {
_top = pos;
+ emit topBorderChanged();
emit borderChanged();
}
}
@@ -82,6 +84,7 @@ void QQuickScaleGrid::setRight(int pos)
{
if (_right != pos) {
_right = pos;
+ emit rightBorderChanged();
emit borderChanged();
}
}
@@ -90,6 +93,7 @@ void QQuickScaleGrid::setBottom(int pos)
{
if (_bottom != pos) {
_bottom = pos;
+ emit bottomBorderChanged();
emit borderChanged();
}
}
diff --git a/src/quick/items/qquickscalegrid_p_p.h b/src/quick/items/qquickscalegrid_p_p.h
index 5752f61e3f..f5187a8eea 100644
--- a/src/quick/items/qquickscalegrid_p_p.h
+++ b/src/quick/items/qquickscalegrid_p_p.h
@@ -65,10 +65,10 @@ class Q_AUTOTEST_EXPORT QQuickScaleGrid : public QObject
{
Q_OBJECT
- Q_PROPERTY(int left READ left WRITE setLeft NOTIFY borderChanged)
- Q_PROPERTY(int top READ top WRITE setTop NOTIFY borderChanged)
- Q_PROPERTY(int right READ right WRITE setRight NOTIFY borderChanged)
- Q_PROPERTY(int bottom READ bottom WRITE setBottom NOTIFY borderChanged)
+ Q_PROPERTY(int left READ left WRITE setLeft NOTIFY leftBorderChanged)
+ Q_PROPERTY(int top READ top WRITE setTop NOTIFY topBorderChanged)
+ Q_PROPERTY(int right READ right WRITE setRight NOTIFY rightBorderChanged)
+ Q_PROPERTY(int bottom READ bottom WRITE setBottom NOTIFY bottomBorderChanged)
public:
QQuickScaleGrid(QObject *parent=nullptr);
@@ -90,6 +90,10 @@ public:
Q_SIGNALS:
void borderChanged();
+ void leftBorderChanged();
+ void topBorderChanged();
+ void rightBorderChanged();
+ void bottomBorderChanged();
private:
int _left;
diff --git a/src/quick/items/qquickscreen.cpp b/src/quick/items/qquickscreen.cpp
index aea7e44a65..b057fd9d8f 100644
--- a/src/quick/items/qquickscreen.cpp
+++ b/src/quick/items/qquickscreen.cpp
@@ -396,24 +396,24 @@ void QQuickScreenInfo::setWrappedScreen(QScreen *screen)
if (!oldScreen || screen->devicePixelRatio() != oldScreen->devicePixelRatio())
emit devicePixelRatioChanged();
- connect(screen, SIGNAL(geometryChanged(QRect)),
- this, SIGNAL(widthChanged()));
- connect(screen, SIGNAL(geometryChanged(QRect)),
- this, SIGNAL(heightChanged()));
- connect(screen, SIGNAL(geometryChanged(QRect)),
- this, SIGNAL(virtualXChanged()));
- connect(screen, SIGNAL(geometryChanged(QRect)),
- this, SIGNAL(virtualYChanged()));
- connect(screen, SIGNAL(orientationChanged(Qt::ScreenOrientation)),
- this, SIGNAL(orientationChanged()));
- connect(screen, SIGNAL(primaryOrientationChanged(Qt::ScreenOrientation)),
- this, SIGNAL(primaryOrientationChanged()));
- connect(screen, SIGNAL(virtualGeometryChanged(QRect)),
- this, SIGNAL(desktopGeometryChanged()));
- connect(screen, SIGNAL(logicalDotsPerInchChanged(qreal)),
- this, SIGNAL(logicalPixelDensityChanged()));
- connect(screen, SIGNAL(physicalDotsPerInchChanged(qreal)),
- this, SIGNAL(pixelDensityChanged()));
+ qmlobject_connect(screen, QScreen, SIGNAL(geometryChanged(QRect)),
+ this, QQuickScreenInfo, SIGNAL(widthChanged()));
+ qmlobject_connect(screen, QScreen, SIGNAL(geometryChanged(QRect)),
+ this, QQuickScreenInfo, SIGNAL(heightChanged()));
+ qmlobject_connect(screen, QScreen, SIGNAL(geometryChanged(QRect)),
+ this, QQuickScreenInfo, SIGNAL(virtualXChanged()));
+ qmlobject_connect(screen, QScreen, SIGNAL(geometryChanged(QRect)),
+ this, QQuickScreenInfo, SIGNAL(virtualYChanged()));
+ qmlobject_connect(screen, QScreen, SIGNAL(orientationChanged(Qt::ScreenOrientation)),
+ this, QQuickScreenInfo, SIGNAL(orientationChanged()));
+ qmlobject_connect(screen, QScreen, SIGNAL(primaryOrientationChanged(Qt::ScreenOrientation)),
+ this, QQuickScreenInfo, SIGNAL(primaryOrientationChanged()));
+ qmlobject_connect(screen, QScreen, SIGNAL(virtualGeometryChanged(QRect)),
+ this, QQuickScreenInfo, SIGNAL(desktopGeometryChanged()));
+ qmlobject_connect(screen, QScreen, SIGNAL(logicalDotsPerInchChanged(qreal)),
+ this, QQuickScreenInfo, SIGNAL(logicalPixelDensityChanged()));
+ qmlobject_connect(screen, QScreen, SIGNAL(physicalDotsPerInchChanged(qreal)),
+ this, QQuickScreenInfo, SIGNAL(pixelDensityChanged()));
}
QScreen *QQuickScreenInfo::wrappedScreen() const
@@ -473,11 +473,11 @@ int QQuickScreenAttached::angleBetween(int a, int b)
void QQuickScreenAttached::windowChanged(QQuickWindow* c)
{
if (m_window)
- disconnect(m_window, SIGNAL(screenChanged(QScreen*)), this, SLOT(screenChanged(QScreen*)));
+ qmlobject_disconnect(m_window, QQuickWindow, SIGNAL(screenChanged(QScreen*)), this, QQuickScreenAttached, SLOT(screenChanged(QScreen*)));
m_window = c;
screenChanged(c ? c->screen() : nullptr);
if (c)
- connect(c, SIGNAL(screenChanged(QScreen*)), this, SLOT(screenChanged(QScreen*)));
+ qmlobject_connect(c, QQuickWindow, SIGNAL(screenChanged(QScreen*)), this, QQuickScreenAttached, SLOT(screenChanged(QScreen*)));
}
void QQuickScreenAttached::screenChanged(QScreen *screen)
diff --git a/src/quick/items/qquickshadereffectmesh.cpp b/src/quick/items/qquickshadereffectmesh.cpp
index 77b7cbc78d..804f548d21 100644
--- a/src/quick/items/qquickshadereffectmesh.cpp
+++ b/src/quick/items/qquickshadereffectmesh.cpp
@@ -64,6 +64,11 @@ QQuickShaderEffectMesh::QQuickShaderEffectMesh(QObject *parent)
{
}
+QQuickShaderEffectMesh::QQuickShaderEffectMesh(QObjectPrivate &dd, QObject *parent)
+ : QObject(dd, parent)
+{
+}
+
/*!
\qmltype GridMesh
\instantiates QQuickGridMesh
diff --git a/src/quick/items/qquickshadereffectmesh_p.h b/src/quick/items/qquickshadereffectmesh_p.h
index 5d6641429a..62a9798e40 100644
--- a/src/quick/items/qquickshadereffectmesh_p.h
+++ b/src/quick/items/qquickshadereffectmesh_p.h
@@ -72,7 +72,7 @@ const char *qtTexCoordAttributeName();
class QSGGeometry;
class QRectF;
-class QQuickShaderEffectMesh : public QObject
+class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectMesh : public QObject
{
Q_OBJECT
public:
@@ -87,9 +87,12 @@ public:
Q_SIGNALS:
// Emitted when the geometry needs to be updated.
void geometryChanged();
+
+protected:
+ QQuickShaderEffectMesh(QObjectPrivate &dd, QObject *parent = nullptr);
};
-class QQuickGridMesh : public QQuickShaderEffectMesh
+class Q_QUICK_PRIVATE_EXPORT QQuickGridMesh : public QQuickShaderEffectMesh
{
Q_OBJECT
Q_PROPERTY(QSize resolution READ resolution WRITE setResolution NOTIFY resolutionChanged)
diff --git a/src/quick/items/qquickspriteengine_p.h b/src/quick/items/qquickspriteengine_p.h
index da917683b6..d3944b4620 100644
--- a/src/quick/items/qquickspriteengine_p.h
+++ b/src/quick/items/qquickspriteengine_p.h
@@ -109,7 +109,7 @@ public:
virtual int variedDuration() const
{
- return qMax(qreal(0.0) , m_duration
+ return qMax(0.0 , m_duration
+ (m_durationVariation * QRandomGenerator::global()->bounded(2.0))
- m_durationVariation);
}
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
new file mode 100644
index 0000000000..68d55d98ea
--- /dev/null
+++ b/src/quick/items/qquicktableview.cpp
@@ -0,0 +1,2024 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquicktableview_p.h"
+#include "qquicktableview_p_p.h"
+
+#include <QtCore/qtimer.h>
+#include <QtCore/qdir.h>
+#include <QtQml/private/qqmldelegatemodel_p.h>
+#include <QtQml/private/qqmldelegatemodel_p_p.h>
+#include <QtQml/private/qqmlincubator_p.h>
+#include <QtQml/private/qqmlchangeset_p.h>
+#include <QtQml/qqmlinfo.h>
+
+#include <QtQuick/private/qquickflickable_p_p.h>
+#include <QtQuick/private/qquickitemviewfxitem_p_p.h>
+
+/*!
+ \qmltype TableView
+ \instantiates QQuickTableView
+ \inqmlmodule QtQuick
+ \ingroup qtquick-views
+ \inherits Flickable
+ \brief Provides a table view of items provided by the 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.
+
+ TableView inherits \l Flickable. This means that while the model can have
+ any number of rows and columns, only a subsection of the table is usually
+ visible inside the viewport. As soon as you flick, new rows and columns
+ enter the viewport, while old ones exit and are removed from the viewport.
+ The rows and columns that move out are reused for building the rows and columns
+ that move into the viewport. As such, the TableView support models of any
+ size without affecting performance.
+
+ 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.
+
+ \section1 Example Usage
+
+ The following example shows how to create a model from C++ with multiple
+ columns:
+
+ \snippet qml/tableview/tablemodel.cpp 0
+
+ And then how to use it from QML:
+
+ \snippet qml/tableview/tablemodel.qml 0
+
+ \section1 Reusing items
+
+ TableView recycles delegate items by default, instead of instantiating from
+ the \l delegate whenever new rows and columns are flicked into view. This
+ can give a huge performance boost, depending on the complexity of the
+ delegate.
+
+ When an item is flicked out, it moves to the \e{reuse pool}, which is an
+ internal cache of unused items. When this happens, the \l TableView::pooled
+ signal is emitted to inform the item about it. Likewise, when the item is
+ moved back from the pool, the \l TableView::reused signal is emitted.
+
+ Any item properties that come from the model are updated when the
+ item is reused. This includes \c index, \c row, and \c column, but also
+ any model roles.
+
+ \note Avoid storing any state inside a delegate. If you do, reset it
+ manually on receiving the \l TableView::reused signal.
+
+ If an item has timers or animations, consider pausing them on receiving
+ the \l TableView::pooled signal. That way you avoid using the CPU resources
+ for items that are not visible. Likewise, if an item has resources that
+ cannot be reused, they could be freed up.
+
+ If you don't want to reuse items or if the \l delegate cannot support it,
+ you can set the \l reuseItems property to \c false.
+
+ \note While an item is in the pool, it might still be alive and respond
+ to connected signals and bindings.
+
+ The following example shows a delegate that animates a spinning rectangle. When
+ it is pooled, the animation is temporarily paused:
+
+ \snippet qml/tableview/reusabledelegate.qml 0
+
+ \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
+ containing any number of rows and columns. Instead, it will ask the
+ application whenever it needs to know.
+
+ TableView uses the largest \c implicitWidth among the items as the column
+ width, unless the \l columnWidthProvider property is explicitly set. Once
+ the column width is found, all other items in the same column are resized
+ to this width, even if new items that are flicked in later have larger
+ \c implicitWidth. Setting an explicit \l width on an item is ignored and
+ overwritten.
+
+ \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
+ \l columnWidthProvider. The same logic applies for the row height
+ calculation.
+
+ If you change the values that a \l rowHeightProvider or a
+ \l columnWidthProvider return for rows and columns inside the viewport, you
+ must call \l forceLayout. This informs TableView that it needs to use the
+ provider functions again to recalculate and update the layout.
+
+ \note The size of a row or column should be a whole number to avoid
+ sub-pixel alignment of items.
+
+ The following example shows how to set a simple \c columnWidthProvider
+ together with a timer that modifies the values the function returns. When
+ the array is modified, \l forceLayout is called to let the changes
+ take effect:
+
+ \snippet qml/tableview/tableviewwithprovider.qml 0
+
+ \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.
+
+ 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:
+
+ \snippet qml/tableview/tableviewwithheader.qml 0
+*/
+
+/*!
+ \qmlproperty int QtQuick::TableView::rows
+
+ This property holds the number of rows in the table. This is
+ equal to the number of rows in the model.
+
+ This property is read only.
+*/
+
+/*!
+ \qmlproperty int QtQuick::TableView::columns
+
+ 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.
+
+ This property is read only.
+*/
+
+/*!
+ \qmlproperty real QtQuick::TableView::rowSpacing
+
+ This property holds the spacing between the rows.
+
+ The default value is 0.
+*/
+
+/*!
+ \qmlproperty real QtQuick::TableView::columnSpacing
+
+ This property holds the spacing between the columns.
+
+ The default value is 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.
+
+ \note The height of a row must always be greater than \c 0.
+
+ \sa columnWidthProvider, {Row heights and column widths}
+*/
+
+/*!
+ \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.
+
+ \note The width of a column must always be greater than \c 0.
+
+ \sa rowHeightProvider, {Row heights and column widths}
+*/
+
+/*!
+ \qmlproperty model QtQuick::TableView::model
+ 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
+ or a simple list.
+
+ \sa {qml-data-models}{Data Models}
+*/
+
+/*!
+ \qmlproperty Component QtQuick::TableView::delegate
+
+ The delegate provides a template defining each cell item instantiated by the
+ view. The model index is exposed as an accessible \c index property. The same
+ 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 implicitWidth and \l implicitHeight.
+ The TableView lays out the items based on that information. Explicit \l width or
+ \l 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
+ should therefore avoid storing state information in the delegates.
+
+ \sa {Row heights and column widths}, {Reusing items}
+*/
+
+/*!
+ \qmlproperty bool QtQuick::TableView::reuseItems
+
+ This property holds whether or not items instantiated from the \l delegate
+ should be reused. If set to \c false, any currently pooled items
+ are destroyed.
+
+ \sa {Reusing items}, TableView::pooled, TableView::reused
+*/
+
+/*!
+ \qmlproperty real QtQuick::TableView::contentWidth
+
+ This property holds the width of the \l contentView, 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.
+
+ 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.
+
+ \sa contentHeight
+*/
+
+/*!
+ \qmlproperty real QtQuick::TableView::contentHeight
+
+ This property holds the height of the \l contentView, 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.
+
+ 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.
+
+ \sa contentWidth
+*/
+
+/*!
+ \qmlmethod real QtQuick::TableView::forceLayout
+
+ 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 leftMargin.
+
+ This method forces the TableView to immediately update the layout so
+ that any recent changes take effect.
+
+ Calling this function re-evaluates the size and position of each visible
+ row and column. This is needed if the functions assigned to
+ \l rowHeightProvider or \l columnWidthProvider return different values than
+ what is already assigned.
+*/
+
+/*!
+ \qmlattachedproperty TableView QtQuick::TableView::view
+
+ This attached property holds the view that manages the delegate instance.
+ It is attached to each instance of the delegate.
+*/
+
+/*!
+ \qmlattachedsignal QtQuick::TableView::pooled
+
+ This signal is emitted after an item has been added to the reuse
+ pool. You can use it to pause ongoing timers or animations inside
+ the item, or free up resources that cannot be reused.
+
+ This signal is emitted only if the \l reuseItems property is \c true.
+
+ \sa {Reusing items}, reuseItems, reused
+*/
+
+/*!
+ \qmlattachedsignal QtQuick::TableView::reused
+
+ This signal is emitted after an item has been reused. At this point, the
+ 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.
+
+ This signal is emitted when the item is reused, and not the first time the
+ item is created.
+
+ This signal is emitted only if the \l reuseItems property is \c true.
+
+ \sa {Reusing items}, reuseItems, pooled
+*/
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcTableViewDelegateLifecycle, "qt.quick.tableview.lifecycle")
+
+#define Q_TABLEVIEW_UNREACHABLE(output) { dumpTable(); qWarning() << "output:" << output; Q_UNREACHABLE(); }
+#define Q_TABLEVIEW_ASSERT(cond, output) Q_ASSERT((cond) || [&](){ dumpTable(); qWarning() << "output:" << output; return false;}())
+
+static const Qt::Edge allTableEdges[] = { Qt::LeftEdge, Qt::RightEdge, Qt::TopEdge, Qt::BottomEdge };
+
+static QLine rectangleEdge(const QRect &rect, Qt::Edge tableEdge)
+{
+ switch (tableEdge) {
+ case Qt::LeftEdge:
+ return QLine(rect.topLeft(), rect.bottomLeft());
+ case Qt::RightEdge:
+ return QLine(rect.topRight(), rect.bottomRight());
+ case Qt::TopEdge:
+ return QLine(rect.topLeft(), rect.topRight());
+ case Qt::BottomEdge:
+ return QLine(rect.bottomLeft(), rect.bottomRight());
+ }
+ return QLine();
+}
+
+static QRect expandedRect(const QRect &rect, Qt::Edge edge, int increment)
+{
+ switch (edge) {
+ case Qt::LeftEdge:
+ return rect.adjusted(-increment, 0, 0, 0);
+ case Qt::RightEdge:
+ return rect.adjusted(0, 0, increment, 0);
+ case Qt::TopEdge:
+ return rect.adjusted(0, -increment, 0, 0);
+ case Qt::BottomEdge:
+ return rect.adjusted(0, 0, 0, increment);
+ }
+ return QRect();
+}
+
+const QPoint QQuickTableViewPrivate::kLeft = QPoint(-1, 0);
+const QPoint QQuickTableViewPrivate::kRight = QPoint(1, 0);
+const QPoint QQuickTableViewPrivate::kUp = QPoint(0, -1);
+const QPoint QQuickTableViewPrivate::kDown = QPoint(0, 1);
+
+QQuickTableViewPrivate::QQuickTableViewPrivate()
+ : QQuickFlickablePrivate()
+{
+}
+
+QQuickTableViewPrivate::~QQuickTableViewPrivate()
+{
+ releaseLoadedItems(QQmlTableInstanceModel::NotReusable);
+ if (tableModel)
+ delete tableModel;
+}
+
+QString QQuickTableViewPrivate::tableLayoutToString() const
+{
+ return QString(QLatin1String("table cells: (%1,%2) -> (%3,%4), item count: %5, table rect: %6,%7 x %8,%9"))
+ .arg(loadedTable.topLeft().x()).arg(loadedTable.topLeft().y())
+ .arg(loadedTable.bottomRight().x()).arg(loadedTable.bottomRight().y())
+ .arg(loadedItems.count())
+ .arg(loadedTableOuterRect.x())
+ .arg(loadedTableOuterRect.y())
+ .arg(loadedTableOuterRect.width())
+ .arg(loadedTableOuterRect.height());
+}
+
+void QQuickTableViewPrivate::dumpTable() const
+{
+ auto listCopy = loadedItems.values();
+ std::stable_sort(listCopy.begin(), listCopy.end(),
+ [](const FxTableItem *lhs, const FxTableItem *rhs)
+ { return lhs->index < rhs->index; });
+
+ qWarning() << QStringLiteral("******* TABLE DUMP *******");
+ for (int i = 0; i < listCopy.count(); ++i)
+ qWarning() << static_cast<FxTableItem *>(listCopy.at(i))->cell;
+ qWarning() << tableLayoutToString();
+
+ const QString filename = QStringLiteral("QQuickTableView_dumptable_capture.png");
+ const QString path = QDir::current().absoluteFilePath(filename);
+ if (q_func()->window() && q_func()->window()->grabWindow().save(path))
+ qWarning() << "Window capture saved to:" << path;
+}
+
+QQuickTableViewAttached *QQuickTableViewPrivate::getAttachedObject(const QObject *object) const
+{
+ QObject *attachedObject = qmlAttachedPropertiesObject<QQuickTableView>(object);
+ return static_cast<QQuickTableViewAttached *>(attachedObject);
+}
+
+int QQuickTableViewPrivate::modelIndexAtCell(const QPoint &cell) const
+{
+ int availableRows = tableSize.height();
+ int modelIndex = cell.y() + (cell.x() * availableRows);
+ Q_TABLEVIEW_ASSERT(modelIndex < model->count(),
+ "modelIndex:" << modelIndex << "cell:" << cell << "count:" << model->count());
+ return modelIndex;
+}
+
+QPoint QQuickTableViewPrivate::cellAtModelIndex(int modelIndex) const
+{
+ int availableRows = tableSize.height();
+ Q_TABLEVIEW_ASSERT(availableRows > 0, availableRows);
+ int column = int(modelIndex / availableRows);
+ int row = modelIndex % availableRows;
+ return QPoint(column, row);
+}
+
+void QQuickTableViewPrivate::updateContentWidth()
+{
+ Q_Q(QQuickTableView);
+
+ if (explicitContentWidth.isValid()) {
+ // Don't calculate contentWidth when it
+ // was set explicitly by the application.
+ return;
+ }
+
+ const qreal thresholdBeforeAdjust = 0.1;
+ int currentRightColumn = loadedTable.right();
+
+ if (currentRightColumn > contentSizeBenchMarkPoint.x()) {
+ contentSizeBenchMarkPoint.setX(currentRightColumn);
+
+ const qreal spacing = currentRightColumn * cellSpacing.width();
+ qreal currentWidth = loadedTableOuterRect.right();
+ const qreal averageCellWidth = (currentWidth - spacing) / (currentRightColumn + 1);
+ qreal estimatedWidth = (tableSize.width() * (averageCellWidth + cellSpacing.width())) - cellSpacing.width();
+
+ if (currentRightColumn >= tableSize.width() - 1) {
+ // We are at the last column, and can set the exact width
+ if (!qFuzzyCompare(currentWidth, q->implicitWidth()))
+ q->QQuickFlickable::setContentWidth(currentWidth);
+ } else if (currentWidth >= q->implicitWidth()) {
+ // We are at the estimated width, but there are still more columns
+ q->QQuickFlickable::setContentWidth(estimatedWidth);
+ } else {
+ // Only set a new width if the new estimate is substantially different
+ qreal diff = 1 - (estimatedWidth / q->implicitWidth());
+ if (qAbs(diff) > thresholdBeforeAdjust)
+ q->QQuickFlickable::setContentWidth(estimatedWidth);
+ }
+ }
+}
+
+void QQuickTableViewPrivate::updateContentHeight()
+{
+ Q_Q(QQuickTableView);
+
+ if (explicitContentHeight.isValid()) {
+ // Don't calculate contentHeight when it
+ // was set explicitly by the application.
+ return;
+ }
+
+ const qreal thresholdBeforeAdjust = 0.1;
+ int currentBottomRow = loadedTable.bottom();
+
+ if (currentBottomRow > contentSizeBenchMarkPoint.y()) {
+ contentSizeBenchMarkPoint.setY(currentBottomRow);
+
+ const qreal spacing = currentBottomRow * cellSpacing.height();
+ qreal currentHeight = loadedTableOuterRect.bottom();
+ const qreal averageCellHeight = (currentHeight - spacing) / (currentBottomRow + 1);
+ qreal estimatedHeight = (tableSize.height() * (averageCellHeight + cellSpacing.height())) - cellSpacing.height();
+
+ if (currentBottomRow >= tableSize.height() - 1) {
+ // We are at the last row, and can set the exact height
+ if (!qFuzzyCompare(currentHeight, q->implicitHeight()))
+ q->QQuickFlickable::setContentHeight(currentHeight);
+ } else if (currentHeight >= q->implicitHeight()) {
+ // We are at the estimated height, but there are still more rows
+ q->QQuickFlickable::setContentHeight(estimatedHeight);
+ } else {
+ // Only set a new height if the new estimate is substantially different
+ qreal diff = 1 - (estimatedHeight / q->implicitHeight());
+ if (qAbs(diff) > thresholdBeforeAdjust)
+ q->QQuickFlickable::setContentHeight(estimatedHeight);
+ }
+ }
+}
+
+void QQuickTableViewPrivate::enforceTableAtOrigin()
+{
+ // Gaps before the first row/column can happen if rows/columns
+ // changes size while flicking e.g because of spacing changes or
+ // changes to a column maxWidth/row maxHeight. Check for this, and
+ // move the whole table rect accordingly.
+ bool layoutNeeded = false;
+ const qreal flickMargin = 50;
+
+ if (loadedTable.x() == 0 && loadedTableOuterRect.x() > 0) {
+ // The table is at the beginning, but not at the edge of the
+ // content view. So move the table to origin.
+ loadedTableOuterRect.moveLeft(0);
+ layoutNeeded = true;
+ } else if (loadedTableOuterRect.x() < 0) {
+ // The table is outside the beginning of the content view. Move
+ // the whole table inside, and make some room for flicking.
+ loadedTableOuterRect.moveLeft(loadedTable.x() == 0 ? 0 : flickMargin);
+ layoutNeeded = true;
+ }
+
+ if (loadedTable.y() == 0 && loadedTableOuterRect.y() > 0) {
+ loadedTableOuterRect.moveTop(0);
+ layoutNeeded = true;
+ } else if (loadedTableOuterRect.y() < 0) {
+ loadedTableOuterRect.moveTop(loadedTable.y() == 0 ? 0 : flickMargin);
+ layoutNeeded = true;
+ }
+
+ if (layoutNeeded) {
+ qCDebug(lcTableViewDelegateLifecycle);
+ relayoutTableItems();
+ }
+}
+
+void QQuickTableViewPrivate::updateAverageEdgeSize()
+{
+ int bottomCell = loadedTable.bottom();
+ int rightCell = loadedTable.right();
+ qreal accRowSpacing = bottomCell * cellSpacing.height();
+ qreal accColumnSpacing = rightCell * cellSpacing.width();
+ averageEdgeSize.setHeight((loadedTableOuterRect.bottom() - accRowSpacing) / (bottomCell + 1));
+ averageEdgeSize.setWidth((loadedTableOuterRect.right() - accColumnSpacing) / (rightCell + 1));
+}
+
+void QQuickTableViewPrivate::syncLoadedTableRectFromLoadedTable()
+{
+ QRectF topLeftRect = loadedTableItem(loadedTable.topLeft())->geometry();
+ QRectF bottomRightRect = loadedTableItem(loadedTable.bottomRight())->geometry();
+ loadedTableOuterRect = topLeftRect.united(bottomRightRect);
+ loadedTableInnerRect = QRectF(topLeftRect.bottomRight(), bottomRightRect.topLeft());
+}
+
+void QQuickTableViewPrivate::syncLoadedTableFromLoadRequest()
+{
+ if (loadRequest.edge() == Qt::Edge(0)) {
+ // No edge means we're loading the top-left item
+ loadedTable = QRect(loadRequest.firstCell(), loadRequest.lastCell());
+ return;
+ }
+
+ switch (loadRequest.edge()) {
+ case Qt::LeftEdge:
+ case Qt::TopEdge:
+ loadedTable.setTopLeft(loadRequest.firstCell());
+ break;
+ case Qt::RightEdge:
+ case Qt::BottomEdge:
+ loadedTable.setBottomRight(loadRequest.lastCell());
+ break;
+ }
+}
+
+FxTableItem *QQuickTableViewPrivate::itemNextTo(const FxTableItem *fxTableItem, const QPoint &direction) const
+{
+ return loadedTableItem(fxTableItem->cell + direction);
+}
+
+FxTableItem *QQuickTableViewPrivate::loadedTableItem(const QPoint &cell) const
+{
+ const int modelIndex = modelIndexAtCell(cell);
+ Q_TABLEVIEW_ASSERT(loadedItems.contains(modelIndex), modelIndex << cell);
+ return loadedItems.value(modelIndex);
+}
+
+FxTableItem *QQuickTableViewPrivate::createFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode)
+{
+ Q_Q(QQuickTableView);
+
+ bool ownItem = false;
+ int modelIndex = modelIndexAtCell(cell);
+
+ QObject* object = model->object(modelIndex, incubationMode);
+ if (!object) {
+ if (model->incubationStatus(modelIndex) == QQmlIncubator::Loading) {
+ // Item is incubating. Return nullptr for now, and let the table call this
+ // function again once we get a callback to itemCreatedCallback().
+ return nullptr;
+ }
+
+ qWarning() << "TableView: failed loading index:" << modelIndex;
+ object = new QQuickItem();
+ ownItem = true;
+ }
+
+ QQuickItem *item = qmlobject_cast<QQuickItem*>(object);
+ if (!item) {
+ // The model could not provide an QQuickItem for the
+ // given index, so we create a placeholder instead.
+ qWarning() << "TableView: delegate is not an item:" << modelIndex;
+ model->release(object);
+ item = new QQuickItem();
+ ownItem = true;
+ } else {
+ QQuickAnchors *anchors = QQuickItemPrivate::get(item)->_anchors;
+ if (anchors && anchors->activeDirections())
+ qmlWarning(item) << "TableView: detected anchors on delegate with index: " << modelIndex
+ << ". Use implicitWidth and implicitHeight instead.";
+ }
+
+ if (ownItem) {
+ // Parent item is normally set early on from initItemCallback (to
+ // allow bindings to the parent property). But if we created the item
+ // within this function, we need to set it explicit.
+ item->setImplicitWidth(kDefaultColumnWidth);
+ item->setImplicitHeight(kDefaultRowHeight);
+ item->setParentItem(q->contentItem());
+ }
+ Q_TABLEVIEW_ASSERT(item->parentItem() == q->contentItem(), item->parentItem());
+
+ FxTableItem *fxTableItem = new FxTableItem(item, q, ownItem);
+ fxTableItem->setVisible(false);
+ fxTableItem->cell = cell;
+ fxTableItem->index = modelIndex;
+ return fxTableItem;
+}
+
+FxTableItem *QQuickTableViewPrivate::loadFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode)
+{
+#ifdef QT_DEBUG
+ // Since TableView needs to work flawlessly when e.g incubating inside an async
+ // loader, being able to override all loading to async while debugging can be helpful.
+ static const bool forcedAsync = forcedIncubationMode == QLatin1String("async");
+ if (forcedAsync)
+ incubationMode = QQmlIncubator::Asynchronous;
+#endif
+
+ // Note that even if incubation mode is asynchronous, the item might
+ // be ready immediately since the model has a cache of items.
+ QBoolBlocker guard(blockItemCreatedCallback);
+ auto item = createFxTableItem(cell, incubationMode);
+ qCDebug(lcTableViewDelegateLifecycle) << cell << "ready?" << bool(item);
+ return item;
+}
+
+void QQuickTableViewPrivate::releaseLoadedItems(QQmlTableInstanceModel::ReusableFlag reusableFlag) {
+ // Make a copy and clear the list of items first to avoid destroyed
+ // items being accessed during the loop (QTBUG-61294)
+ auto const tmpList = loadedItems;
+ loadedItems.clear();
+ for (FxTableItem *item : tmpList)
+ releaseItem(item, reusableFlag);
+}
+
+void QQuickTableViewPrivate::releaseItem(FxTableItem *fxTableItem, QQmlTableInstanceModel::ReusableFlag reusableFlag)
+{
+ Q_Q(QQuickTableView);
+ auto item = fxTableItem->item;
+ Q_TABLEVIEW_ASSERT(item, fxTableItem->index);
+
+ if (fxTableItem->ownItem) {
+ delete item;
+ } else {
+ // Only QQmlTableInstanceModel supports reusing items
+ auto releaseFlag = tableModel ?
+ tableModel->release(item, reusableFlag) :
+ model->release(item);
+
+ if (releaseFlag != QQmlInstanceModel::Destroyed) {
+ // When items are not destroyed, it typically means that the
+ // item is reused, or that the model is an ObjectModel. If
+ // so, we just hide the item instead.
+ fxTableItem->setVisible(false);
+
+ // If the item (or a descendant) has focus, remove it, so
+ // that the item doesn't enter with focus when it's reused.
+ if (QQuickWindow *window = item->window()) {
+ const auto focusItem = qobject_cast<QQuickItem *>(window->focusObject());
+ if (focusItem) {
+ const bool hasFocus = item == focusItem || item->isAncestorOf(focusItem);
+ if (hasFocus) {
+ const auto focusChild = QQuickItemPrivate::get(q)->subFocusItem;
+ QQuickWindowPrivate::get(window)->clearFocusInScope(q, focusChild, Qt::OtherFocusReason);
+ }
+ }
+ }
+ }
+ }
+
+ delete fxTableItem;
+}
+
+void QQuickTableViewPrivate::unloadItem(const QPoint &cell)
+{
+ const int modelIndex = modelIndexAtCell(cell);
+ Q_TABLEVIEW_ASSERT(loadedItems.contains(modelIndex), modelIndex << cell);
+ releaseItem(loadedItems.take(modelIndex), reusableFlag);
+}
+
+void QQuickTableViewPrivate::unloadItems(const QLine &items)
+{
+ qCDebug(lcTableViewDelegateLifecycle) << items;
+
+ if (items.dx()) {
+ int y = items.p1().y();
+ for (int x = items.p1().x(); x <= items.p2().x(); ++x)
+ unloadItem(QPoint(x, y));
+ } else {
+ int x = items.p1().x();
+ for (int y = items.p1().y(); y <= items.p2().y(); ++y)
+ unloadItem(QPoint(x, y));
+ }
+}
+
+bool QQuickTableViewPrivate::canLoadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const
+{
+ switch (tableEdge) {
+ case Qt::LeftEdge:
+ if (loadedTable.topLeft().x() == 0)
+ return false;
+ return loadedTableOuterRect.left() > fillRect.left() + cellSpacing.width();
+ case Qt::RightEdge:
+ if (loadedTable.bottomRight().x() >= tableSize.width() - 1)
+ return false;
+ return loadedTableOuterRect.right() < fillRect.right() - cellSpacing.width();
+ case Qt::TopEdge:
+ if (loadedTable.topLeft().y() == 0)
+ return false;
+ return loadedTableOuterRect.top() > fillRect.top() + cellSpacing.height();
+ case Qt::BottomEdge:
+ if (loadedTable.bottomRight().y() >= tableSize.height() - 1)
+ return false;
+ return loadedTableOuterRect.bottom() < fillRect.bottom() - cellSpacing.height();
+ }
+
+ return false;
+}
+
+bool QQuickTableViewPrivate::canUnloadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const
+{
+ // Note: if there is only one row or column left, we cannot unload, since
+ // they are needed as anchor point for further layouting.
+ switch (tableEdge) {
+ case Qt::LeftEdge:
+ if (loadedTable.width() <= 1)
+ return false;
+ return loadedTableInnerRect.left() <= fillRect.left();
+ case Qt::RightEdge:
+ if (loadedTable.width() <= 1)
+ return false;
+ return loadedTableInnerRect.right() >= fillRect.right();
+ case Qt::TopEdge:
+ if (loadedTable.height() <= 1)
+ return false;
+ return loadedTableInnerRect.top() <= fillRect.top();
+ case Qt::BottomEdge:
+ if (loadedTable.height() <= 1)
+ return false;
+ return loadedTableInnerRect.bottom() >= fillRect.bottom();
+ }
+ Q_TABLEVIEW_UNREACHABLE(tableEdge);
+ return false;
+}
+
+Qt::Edge QQuickTableViewPrivate::nextEdgeToLoad(const QRectF rect)
+{
+ for (Qt::Edge edge : allTableEdges) {
+ if (canLoadTableEdge(edge, rect))
+ return edge;
+ }
+ return Qt::Edge(0);
+}
+
+Qt::Edge QQuickTableViewPrivate::nextEdgeToUnload(const QRectF rect)
+{
+ for (Qt::Edge edge : allTableEdges) {
+ if (canUnloadTableEdge(edge, rect))
+ return edge;
+ }
+ return Qt::Edge(0);
+}
+
+qreal QQuickTableViewPrivate::cellWidth(const QPoint& cell)
+{
+ // Using an items width directly is not an option, since we change
+ // it during layout (which would also cause problems when recycling items).
+ auto const cellItem = loadedTableItem(cell)->item;
+ return cellItem->implicitWidth();
+}
+
+qreal QQuickTableViewPrivate::cellHeight(const QPoint& cell)
+{
+ // Using an items height directly is not an option, since we change
+ // it during layout (which would also cause problems when recycling items).
+ auto const cellItem = loadedTableItem(cell)->item;
+ return cellItem->implicitHeight();
+}
+
+qreal QQuickTableViewPrivate::sizeHintForColumn(int column)
+{
+ // Find the widest cell in the column, and return its width
+ qreal columnWidth = 0;
+ for (int row = loadedTable.top(); row <= loadedTable.bottom(); ++row)
+ columnWidth = qMax(columnWidth, cellWidth(QPoint(column, row)));
+
+ return columnWidth;
+}
+
+qreal QQuickTableViewPrivate::sizeHintForRow(int row)
+{
+ // Find the highest cell in the row, and return its height
+ qreal rowHeight = 0;
+ for (int column = loadedTable.left(); column <= loadedTable.right(); ++column)
+ rowHeight = qMax(rowHeight, cellHeight(QPoint(column, row)));
+
+ return rowHeight;
+}
+
+void QQuickTableViewPrivate::calculateTableSize()
+{
+ // tableSize is the same as row and column count, and will always
+ // be the same as the number of rows and columns in the model.
+ Q_Q(QQuickTableView);
+ QSize prevTableSize = tableSize;
+
+ if (tableModel)
+ tableSize = QSize(tableModel->columns(), tableModel->rows());
+ else if (model)
+ tableSize = QSize(1, model->count());
+ else
+ tableSize = QSize(0, 0);
+
+ if (prevTableSize.width() != tableSize.width())
+ emit q->columnsChanged();
+ if (prevTableSize.height() != tableSize.height())
+ emit q->rowsChanged();
+}
+
+qreal QQuickTableViewPrivate::resolveColumnWidth(int column)
+{
+ Q_TABLEVIEW_ASSERT(column >= loadedTable.left() && column <= loadedTable.right(), column);
+ qreal columnWidth = -1;
+
+ if (!columnWidthProvider.isUndefined()) {
+ if (columnWidthProvider.isCallable()) {
+ auto const columnAsArgument = QJSValueList() << QJSValue(column);
+ columnWidth = columnWidthProvider.call(columnAsArgument).toNumber();
+ if (qIsNaN(columnWidth) || columnWidth <= 0) {
+ // The column width needs to be greater than 0, otherwise we never reach the edge
+ // while loading/refilling columns. This would cause the application to hang.
+ if (!layoutWarningIssued) {
+ layoutWarningIssued = true;
+ qmlWarning(q_func()) << "columnWidthProvider did not return a valid width for column: " << column;
+ }
+ columnWidth = kDefaultColumnWidth;
+ }
+ } else {
+ if (!layoutWarningIssued) {
+ layoutWarningIssued = true;
+ qmlWarning(q_func()) << "columnWidthProvider doesn't contain a function";
+ }
+ columnWidth = kDefaultColumnWidth;
+ }
+ } else {
+ // If columnWidthProvider is left unspecified, we just iterate over the currently visible items in
+ // the column. The downside of doing that, is that the column width will then only be based
+ // on the implicit width of the currently loaded items (which can be different depending on
+ // which row you're at when the column is flicked in). The upshot is that you don't have to
+ // bother setting columnWidthProvider for small tables, or if the implicit width doesn't vary.
+ columnWidth = sizeHintForColumn(column);
+ if (qIsNaN(columnWidth) || columnWidth <= 0) {
+ // The column width needs to be greater than 0, otherwise we never reach the edge
+ // while loading/refilling columns. This would cause the application to hang.
+ if (!layoutWarningIssued) {
+ layoutWarningIssued = true;
+ qmlWarning(q_func()) << "the delegate's implicitWidth needs to be greater than zero";
+ }
+ columnWidth = kDefaultColumnWidth;
+ }
+ }
+
+ return columnWidth;
+}
+
+qreal QQuickTableViewPrivate::resolveRowHeight(int row)
+{
+ Q_TABLEVIEW_ASSERT(row >= loadedTable.top() && row <= loadedTable.bottom(), row);
+ qreal rowHeight = -1;
+
+ if (!rowHeightProvider.isUndefined()) {
+ if (rowHeightProvider.isCallable()) {
+ auto const rowAsArgument = QJSValueList() << QJSValue(row);
+ rowHeight = rowHeightProvider.call(rowAsArgument).toNumber();
+ if (qIsNaN(rowHeight) || rowHeight <= 0) {
+ // The row height needs to be greater than 0, otherwise we never reach the edge
+ // while loading/refilling rows. This would cause the application to hang.
+ if (!layoutWarningIssued) {
+ layoutWarningIssued = true;
+ qmlWarning(q_func()) << "rowHeightProvider did not return a valid height for row: " << row;
+ }
+ rowHeight = kDefaultRowHeight;
+ }
+ } else {
+ if (!layoutWarningIssued) {
+ layoutWarningIssued = true;
+ qmlWarning(q_func()) << "rowHeightProvider doesn't contain a function";
+ }
+ rowHeight = kDefaultRowHeight;
+ }
+ } else {
+ // If rowHeightProvider is left unspecified, we just iterate over the currently visible items in
+ // the row. The downside of doing that, is that the row height will then only be based
+ // on the implicit height of the currently loaded items (which can be different depending on
+ // which column you're at when the row is flicked in). The upshot is that you don't have to
+ // bother setting rowHeightProvider for small tables, or if the implicit height doesn't vary.
+ rowHeight = sizeHintForRow(row);
+ if (qIsNaN(rowHeight) || rowHeight <= 0) {
+ if (!layoutWarningIssued) {
+ layoutWarningIssued = true;
+ qmlWarning(q_func()) << "the delegate's implicitHeight needs to be greater than zero";
+ }
+ rowHeight = kDefaultRowHeight;
+ }
+ }
+
+ return rowHeight;
+}
+
+void QQuickTableViewPrivate::relayoutTable()
+{
+ relayoutTableItems();
+ syncLoadedTableRectFromLoadedTable();
+ enforceTableAtOrigin();
+ contentSizeBenchMarkPoint = QPoint(-1, -1);
+ updateContentWidth();
+ updateContentHeight();
+ // Return back to updatePolish to loadAndUnloadVisibleEdges()
+ // since the re-layout might have caused some edges to be pushed
+ // out, while others might have been pushed in.
+}
+
+void QQuickTableViewPrivate::relayoutTableItems()
+{
+ qCDebug(lcTableViewDelegateLifecycle);
+ columnRowPositionsInvalid = false;
+
+ qreal nextColumnX = loadedTableOuterRect.x();
+ qreal nextRowY = loadedTableOuterRect.y();
+
+ for (int column = loadedTable.left(); column <= loadedTable.right(); ++column) {
+ // Adjust the geometry of all cells in the current column
+ const qreal width = resolveColumnWidth(column);
+
+ for (int row = loadedTable.top(); row <= loadedTable.bottom(); ++row) {
+ auto item = loadedTableItem(QPoint(column, row));
+ QRectF geometry = item->geometry();
+ geometry.moveLeft(nextColumnX);
+ geometry.setWidth(width);
+ item->setGeometry(geometry);
+ }
+
+ nextColumnX += width + cellSpacing.width();
+ }
+
+ for (int row = loadedTable.top(); row <= loadedTable.bottom(); ++row) {
+ // Adjust the geometry of all cells in the current row
+ const qreal height = resolveRowHeight(row);
+
+ for (int column = loadedTable.left(); column <= loadedTable.right(); ++column) {
+ auto item = loadedTableItem(QPoint(column, row));
+ QRectF geometry = item->geometry();
+ geometry.moveTop(nextRowY);
+ geometry.setHeight(height);
+ item->setGeometry(geometry);
+ }
+
+ nextRowY += height + cellSpacing.height();
+ }
+
+ if (Q_UNLIKELY(lcTableViewDelegateLifecycle().isDebugEnabled())) {
+ for (int column = loadedTable.left(); column <= loadedTable.right(); ++column) {
+ for (int row = loadedTable.top(); row <= loadedTable.bottom(); ++row) {
+ QPoint cell = QPoint(column, row);
+ qCDebug(lcTableViewDelegateLifecycle()) << "relayout item:" << cell << loadedTableItem(cell)->geometry();
+ }
+ }
+ }
+}
+
+void QQuickTableViewPrivate::layoutVerticalEdge(Qt::Edge tableEdge)
+{
+ int column = (tableEdge == Qt::LeftEdge) ? loadedTable.left() : loadedTable.right();
+ QPoint neighbourDirection = (tableEdge == Qt::LeftEdge) ? kRight : kLeft;
+ qreal width = resolveColumnWidth(column);
+
+ for (int row = loadedTable.top(); row <= loadedTable.bottom(); ++row) {
+ auto fxTableItem = loadedTableItem(QPoint(column, row));
+ auto const neighbourItem = itemNextTo(fxTableItem, neighbourDirection);
+
+ QRectF geometry = fxTableItem->geometry();
+ geometry.setWidth(width);
+ geometry.setHeight(neighbourItem->geometry().height());
+ qreal left = tableEdge == Qt::LeftEdge ?
+ neighbourItem->geometry().left() - cellSpacing.width() - geometry.width() :
+ neighbourItem->geometry().right() + cellSpacing.width();
+
+ geometry.moveLeft(left);
+ geometry.moveTop(neighbourItem->geometry().top());
+
+ fxTableItem->setGeometry(geometry);
+ fxTableItem->setVisible(true);
+
+ qCDebug(lcTableViewDelegateLifecycle()) << "layout item:" << QPoint(column, row) << fxTableItem->geometry();
+ }
+}
+
+void QQuickTableViewPrivate::layoutHorizontalEdge(Qt::Edge tableEdge)
+{
+ int row = (tableEdge == Qt::TopEdge) ? loadedTable.top() : loadedTable.bottom();
+ QPoint neighbourDirection = (tableEdge == Qt::TopEdge) ? kDown : kUp;
+ qreal height = resolveRowHeight(row);
+
+ for (int column = loadedTable.left(); column <= loadedTable.right(); ++column) {
+ auto fxTableItem = loadedTableItem(QPoint(column, row));
+ auto const neighbourItem = itemNextTo(fxTableItem, neighbourDirection);
+
+ QRectF geometry = fxTableItem->geometry();
+ geometry.setWidth(neighbourItem->geometry().width());
+ geometry.setHeight(height);
+ qreal top = tableEdge == Qt::TopEdge ?
+ neighbourItem->geometry().top() - cellSpacing.height() - geometry.height() :
+ neighbourItem->geometry().bottom() + cellSpacing.height();
+
+ geometry.moveTop(top);
+ geometry.moveLeft(neighbourItem->geometry().left());
+
+ fxTableItem->setGeometry(geometry);
+ fxTableItem->setVisible(true);
+
+ qCDebug(lcTableViewDelegateLifecycle()) << "layout item:" << QPoint(column, row) << fxTableItem->geometry();
+ }
+}
+
+void QQuickTableViewPrivate::layoutTopLeftItem()
+{
+ const QPoint cell = loadRequest.firstCell();
+ auto topLeftItem = loadedTableItem(cell);
+ auto item = topLeftItem->item;
+
+ item->setPosition(loadRequest.startPosition());
+ item->setSize(QSizeF(resolveColumnWidth(cell.x()), resolveRowHeight(cell.y())));
+ topLeftItem->setVisible(true);
+ qCDebug(lcTableViewDelegateLifecycle) << "geometry:" << topLeftItem->geometry();
+}
+
+void QQuickTableViewPrivate::layoutTableEdgeFromLoadRequest()
+{
+ if (loadRequest.edge() == Qt::Edge(0)) {
+ // No edge means we're loading the top-left item
+ layoutTopLeftItem();
+ return;
+ }
+
+ switch (loadRequest.edge()) {
+ case Qt::LeftEdge:
+ case Qt::RightEdge:
+ layoutVerticalEdge(loadRequest.edge());
+ break;
+ case Qt::TopEdge:
+ case Qt::BottomEdge:
+ layoutHorizontalEdge(loadRequest.edge());
+ break;
+ }
+}
+
+void QQuickTableViewPrivate::cancelLoadRequest()
+{
+ loadRequest.markAsDone();
+ model->cancel(modelIndexAtCell(loadRequest.currentCell()));
+
+ if (rebuildScheduled) {
+ // No reason to rollback already loaded edge items
+ // since we anyway are about to reload all items.
+ return;
+ }
+
+ if (loadRequest.atBeginning()) {
+ // No items have yet been loaded, so nothing to unload
+ return;
+ }
+
+ QLine rollbackItems;
+ rollbackItems.setP1(loadRequest.firstCell());
+ rollbackItems.setP2(loadRequest.previousCell());
+ qCDebug(lcTableViewDelegateLifecycle()) << "rollback:" << rollbackItems << tableLayoutToString();
+ unloadItems(rollbackItems);
+}
+
+void QQuickTableViewPrivate::processLoadRequest()
+{
+ Q_TABLEVIEW_ASSERT(loadRequest.isActive(), "");
+
+ while (loadRequest.hasCurrentCell()) {
+ QPoint cell = loadRequest.currentCell();
+ FxTableItem *fxTableItem = loadFxTableItem(cell, loadRequest.incubationMode());
+
+ if (!fxTableItem) {
+ // Requested item is not yet ready. Just leave, and wait for this
+ // function to be called again when the item is ready.
+ return;
+ }
+
+ loadedItems.insert(modelIndexAtCell(cell), fxTableItem);
+ loadRequest.moveToNextCell();
+ }
+
+ qCDebug(lcTableViewDelegateLifecycle()) << "all items loaded!";
+
+ syncLoadedTableFromLoadRequest();
+ layoutTableEdgeFromLoadRequest();
+ syncLoadedTableRectFromLoadedTable();
+
+ if (rebuildState == RebuildState::Done) {
+ enforceTableAtOrigin();
+ updateContentWidth();
+ updateContentHeight();
+ drainReusePoolAfterLoadRequest();
+ }
+
+ loadRequest.markAsDone();
+
+ qCDebug(lcTableViewDelegateLifecycle()) << "request completed! Table:" << tableLayoutToString();
+}
+
+void QQuickTableViewPrivate::processRebuildTable()
+{
+ moveToNextRebuildState();
+
+ if (rebuildState == RebuildState::LoadInitalTable) {
+ beginRebuildTable();
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::VerifyTable) {
+ if (loadedItems.isEmpty()) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "no items loaded, meaning empty model or no delegate";
+ rebuildState = RebuildState::Done;
+ return;
+ }
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::LayoutTable) {
+ layoutAfterLoadingInitialTable();
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::LoadAndUnloadAfterLayout) {
+ loadAndUnloadVisibleEdges();
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ const bool preload = (rebuildOptions & RebuildOption::All
+ && reusableFlag == QQmlTableInstanceModel::Reusable);
+
+ if (rebuildState == RebuildState::PreloadColumns) {
+ if (preload && loadedTable.right() < tableSize.width() - 1)
+ loadEdge(Qt::RightEdge, QQmlIncubator::AsynchronousIfNested);
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::PreloadRows) {
+ if (preload && loadedTable.bottom() < tableSize.height() - 1)
+ loadEdge(Qt::BottomEdge, QQmlIncubator::AsynchronousIfNested);
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ if (rebuildState == RebuildState::MovePreloadedItemsToPool) {
+ while (Qt::Edge edge = nextEdgeToUnload(viewportRect))
+ unloadEdge(edge);
+ if (!moveToNextRebuildState())
+ return;
+ }
+
+ Q_TABLEVIEW_ASSERT(rebuildState == RebuildState::Done, int(rebuildState));
+}
+
+bool QQuickTableViewPrivate::moveToNextRebuildState()
+{
+ if (loadRequest.isActive()) {
+ // Items are still loading async, which means
+ // that the current state is not yet done.
+ return false;
+ }
+ rebuildState = RebuildState(int(rebuildState) + 1);
+ qCDebug(lcTableViewDelegateLifecycle()) << int(rebuildState);
+ return true;
+}
+
+void QQuickTableViewPrivate::beginRebuildTable()
+{
+ if (loadRequest.isActive())
+ cancelLoadRequest();
+
+ calculateTableSize();
+
+ QPoint topLeft;
+ QPointF topLeftPos;
+
+ if (rebuildOptions & RebuildOption::All) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::All";
+ releaseLoadedItems(QQmlTableInstanceModel::NotReusable);
+ } else if (rebuildOptions & RebuildOption::ViewportOnly) {
+ qCDebug(lcTableViewDelegateLifecycle()) << "RebuildOption::ViewportOnly";
+ releaseLoadedItems(reusableFlag);
+
+ if (rebuildOptions & RebuildOption::CalculateNewTopLeftRow) {
+ const int newRow = int(viewportRect.y() / (averageEdgeSize.height() + cellSpacing.height()));
+ topLeft.ry() = qBound(0, newRow, tableSize.height() - 1);
+ topLeftPos.ry() = topLeft.y() * (averageEdgeSize.height() + cellSpacing.height());
+ } else {
+ topLeft.ry() = qBound(0, loadedTable.topLeft().y(), tableSize.height() - 1);
+ topLeftPos.ry() = loadedTableOuterRect.topLeft().y();
+ }
+ if (rebuildOptions & RebuildOption::CalculateNewTopLeftColumn) {
+ const int newColumn = int(viewportRect.x() / (averageEdgeSize.width() + cellSpacing.width()));
+ topLeft.rx() = qBound(0, newColumn, tableSize.width() - 1);
+ topLeftPos.rx() = topLeft.x() * (averageEdgeSize.width() + cellSpacing.width());
+ } else {
+ topLeft.rx() = qBound(0, loadedTable.topLeft().x(), tableSize.width() - 1);
+ topLeftPos.rx() = loadedTableOuterRect.topLeft().x();
+ }
+ } else {
+ Q_TABLEVIEW_UNREACHABLE(rebuildOptions);
+ }
+
+ loadedTable = QRect();
+ loadedTableOuterRect = QRect();
+ loadedTableInnerRect = QRect();
+ contentSizeBenchMarkPoint = QPoint(-1, -1);
+ columnRowPositionsInvalid = false;
+
+ loadInitialTopLeftItem(topLeft, topLeftPos);
+ loadAndUnloadVisibleEdges();
+}
+
+void QQuickTableViewPrivate::layoutAfterLoadingInitialTable()
+{
+ if (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.
+ relayoutTable();
+ }
+
+ updateAverageEdgeSize();
+ updateContentWidth();
+ updateContentHeight();
+}
+
+void QQuickTableViewPrivate::loadInitialTopLeftItem(const QPoint &cell, const QPointF &pos)
+{
+ Q_TABLEVIEW_ASSERT(loadedItems.isEmpty(), "");
+
+ if (tableSize.isEmpty())
+ return;
+
+ if (model->count() == 0)
+ return;
+
+ 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)
+{
+ unloadItems(rectangleEdge(loadedTable, edge));
+ loadedTable = expandedRect(loadedTable, edge, -1);
+ syncLoadedTableRectFromLoadedTable();
+ qCDebug(lcTableViewDelegateLifecycle) << tableLayoutToString();
+}
+
+void QQuickTableViewPrivate::loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMode incubationMode)
+{
+ QLine cellsToLoad = rectangleEdge(expandedRect(loadedTable, edge, 1), edge);
+ loadRequest.begin(cellsToLoad, edge, incubationMode);
+ processLoadRequest();
+}
+
+void QQuickTableViewPrivate::loadAndUnloadVisibleEdges()
+{
+ // Unload table edges that have been moved outside the visible part of the
+ // table (including buffer area), and load new edges that has been moved inside.
+ // Note: an important point is that we always keep the table rectangular
+ // and without holes to reduce complexity (we never leave the table in
+ // a half-loaded state, or keep track of multiple patches).
+ // We load only one edge (row or column) at a time. This is especially
+ // important when loading into the buffer, since we need to be able to
+ // cancel the buffering quickly if the user starts to flick, and then
+ // focus all further loading on the edges that are flicked into view.
+
+ if (loadRequest.isActive()) {
+ // Don't start loading more edges while we're
+ // already waiting for another one to load.
+ return;
+ }
+
+ if (loadedItems.isEmpty()) {
+ // We need at least the top-left item to be loaded before we can
+ // start loading edges around it. Not having a top-left item at
+ // this point means that the model is empty (or no delegate).
+ return;
+ }
+
+ bool tableModified;
+
+ do {
+ tableModified = false;
+
+ if (Qt::Edge edge = nextEdgeToUnload(viewportRect)) {
+ tableModified = true;
+ unloadEdge(edge);
+ }
+
+ if (Qt::Edge edge = nextEdgeToLoad(viewportRect)) {
+ tableModified = true;
+ loadEdge(edge, QQmlIncubator::AsynchronousIfNested);
+ if (loadRequest.isActive())
+ return;
+ }
+ } while (tableModified);
+
+}
+
+void QQuickTableViewPrivate::drainReusePoolAfterLoadRequest()
+{
+ Q_Q(QQuickTableView);
+
+ if (reusableFlag == QQmlTableInstanceModel::NotReusable || !tableModel)
+ return;
+
+ if (!qFuzzyIsNull(q->verticalOvershoot()) || !qFuzzyIsNull(q->horizontalOvershoot())) {
+ // Don't drain while we're overshooting, since this will fill up the
+ // pool, but we expect to reuse them all once the content item moves back.
+ return;
+ }
+
+ // When loading edges, we don't want to drain the reuse pool too aggressively. Normally,
+ // all the items in the pool are reused rapidly as the content view is flicked around
+ // anyway. Even if the table is temporarily flicked to a section that contains fewer
+ // cells than what used to be (e.g if the flicked-in rows are taller than average), it
+ // still makes sense to keep all the items in circulation; Chances are, that soon enough,
+ // thinner rows are flicked back in again (meaning that we can fit more items into the
+ // view). But at the same time, if a delegate chooser is in use, the pool might contain
+ // items created from different delegates. And some of those delegates might be used only
+ // occasionally. So to avoid situations where an item ends up in the pool for too long, we
+ // call drain after each load request, but with a sufficiently large pool time. (If an item
+ // in the pool has a large pool time, it means that it hasn't been reused for an equal
+ // amount of load cycles, and should be released).
+ //
+ // We calculate an appropriate pool time by figuring out what the minimum time must be to
+ // not disturb frequently reused items. Since the number of items in a row might be higher
+ // than in a column (or vice versa), the minimum pool time should take into account that
+ // you might be flicking out a single row (filling up the pool), before you continue
+ // flicking in several new columns (taking them out again, but now in smaller chunks). This
+ // will increase the number of load cycles items are kept in the pool (poolTime), but still,
+ // we shouldn't release them, as they are still being reused frequently.
+ // To get a flexible maxValue (that e.g tolerates rows and columns being flicked
+ // in with varying sizes, causing some items not to be resued immediately), we multiply the
+ // value by 2. Note that we also add an extra +1 to the column count, because the number of
+ // visible columns will fluctuate between +1/-1 while flicking.
+ const int w = loadedTable.width();
+ const int h = loadedTable.height();
+ const int minTime = int(std::ceil(w > h ? qreal(w + 1) / h : qreal(h + 1) / w));
+ const int maxTime = minTime * 2;
+ tableModel->drainReusableItemsPool(maxTime);
+}
+
+void QQuickTableViewPrivate::scheduleRebuildTable(RebuildOptions options) {
+ if (!q_func()->isComponentComplete()) {
+ // We'll rebuild the table once complete anyway
+ 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
+ // new value, model changes etc, this function will end up being called. Here
+ // we check what needs to be done, and load/unload cells accordingly.
+
+ Q_TABLEVIEW_ASSERT(!polishing, "recursive updatePolish() calls are not allowed!");
+ QBoolBlocker polishGuard(polishing, true);
+
+ if (loadRequest.isActive()) {
+ // We're currently loading items async to build a new edge in the table. We see the loading
+ // as an atomic operation, which means that we don't continue doing anything else until all
+ // items have been received and laid out. Note that updatePolish is then called once more
+ // after the loadRequest has completed to handle anything that might have occurred in-between.
+ return;
+ }
+
+ if (rebuildState != RebuildState::Done) {
+ processRebuildTable();
+ return;
+ }
+
+ syncWithPendingChanges();
+
+ if (rebuildState == RebuildState::Begin) {
+ processRebuildTable();
+ return;
+ }
+
+ if (loadedItems.isEmpty())
+ return;
+
+ if (columnRowPositionsInvalid)
+ relayoutTable();
+
+ loadAndUnloadVisibleEdges();
+}
+
+void QQuickTableViewPrivate::fixup(QQuickFlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent)
+{
+ if (rebuildScheduled || rebuildState != RebuildState::Done)
+ return;
+
+ QQuickFlickablePrivate::fixup(data, minExtent, maxExtent);
+}
+
+void QQuickTableViewPrivate::createWrapperModel()
+{
+ Q_Q(QQuickTableView);
+ // When the assigned model is not an instance model, we create a wrapper
+ // model (QQmlTableInstanceModel) that keeps a pointer to both the
+ // assigned model and the assigned delegate. This model will give us a
+ // common interface to any kind of model (js arrays, QAIM, number etc), and
+ // help us create delegate instances.
+ tableModel = new QQmlTableInstanceModel(qmlContext(q));
+ model = tableModel;
+}
+
+void QQuickTableViewPrivate::itemCreatedCallback(int modelIndex, QObject*)
+{
+ if (blockItemCreatedCallback)
+ return;
+
+ qCDebug(lcTableViewDelegateLifecycle) << "item done loading:"
+ << cellAtModelIndex(modelIndex);
+
+ // Since the item we waited for has finished incubating, we can
+ // continue with the load request. processLoadRequest will
+ // ask the model for the requested item once more, which will be
+ // quick since the model has cached it.
+ processLoadRequest();
+ loadAndUnloadVisibleEdges();
+ updatePolish();
+}
+
+void QQuickTableViewPrivate::initItemCallback(int modelIndex, QObject *object)
+{
+ Q_UNUSED(modelIndex);
+ Q_Q(QQuickTableView);
+
+ if (auto item = qmlobject_cast<QQuickItem*>(object)) {
+ item->setParentItem(q->contentItem());
+ item->setZ(1);
+ }
+
+ if (auto attached = getAttachedObject(object))
+ attached->setView(q);
+}
+
+void QQuickTableViewPrivate::itemPooledCallback(int modelIndex, QObject *object)
+{
+ Q_UNUSED(modelIndex);
+
+ if (auto attached = getAttachedObject(object))
+ emit attached->pooled();
+}
+
+void QQuickTableViewPrivate::itemReusedCallback(int modelIndex, QObject *object)
+{
+ Q_UNUSED(modelIndex);
+
+ if (auto attached = getAttachedObject(object))
+ emit attached->reused();
+}
+
+void QQuickTableViewPrivate::syncWithPendingChanges()
+{
+ // The application can change properties like the model or the delegate while
+ // we're e.g in the middle of e.g loading a new row. Since this will lead to
+ // unpredicted behavior, and possibly a crash, we need to postpone taking
+ // such assignments into effect until we're in a state that allows it.
+ Q_Q(QQuickTableView);
+ viewportRect = QRectF(q->contentX(), q->contentY(), q->width(), q->height());
+ syncRebuildOptions();
+ syncModel();
+ syncDelegate();
+}
+
+void QQuickTableViewPrivate::syncRebuildOptions()
+{
+ if (!rebuildScheduled)
+ return;
+
+ rebuildState = RebuildState::Begin;
+ rebuildOptions = scheduledRebuildOptions;
+ scheduledRebuildOptions = RebuildOption::None;
+ rebuildScheduled = false;
+}
+
+void QQuickTableViewPrivate::syncDelegate()
+{
+ if (tableModel && assignedDelegate == tableModel->delegate())
+ return;
+
+ if (!tableModel)
+ createWrapperModel();
+
+ tableModel->setDelegate(assignedDelegate);
+}
+
+void QQuickTableViewPrivate::syncModel()
+{
+ if (modelVariant == assignedModel)
+ return;
+
+ if (model)
+ disconnectFromModel();
+
+ modelVariant = assignedModel;
+ QVariant effectiveModelVariant = modelVariant;
+ if (effectiveModelVariant.userType() == qMetaTypeId<QJSValue>())
+ effectiveModelVariant = effectiveModelVariant.value<QJSValue>().toVariant();
+
+ const auto instanceModel = qobject_cast<QQmlInstanceModel *>(qvariant_cast<QObject*>(effectiveModelVariant));
+
+ if (instanceModel) {
+ if (tableModel) {
+ delete tableModel;
+ tableModel = nullptr;
+ }
+ model = instanceModel;
+ } else {
+ if (!tableModel)
+ createWrapperModel();
+ tableModel->setModel(effectiveModelVariant);
+ }
+
+ connectToModel();
+}
+
+void QQuickTableViewPrivate::connectToModel()
+{
+ Q_TABLEVIEW_ASSERT(model, "");
+
+ QObjectPrivate::connect(model, &QQmlInstanceModel::createdItem, this, &QQuickTableViewPrivate::itemCreatedCallback);
+ QObjectPrivate::connect(model, &QQmlInstanceModel::initItem, this, &QQuickTableViewPrivate::initItemCallback);
+
+ if (tableModel) {
+ QObjectPrivate::connect(tableModel, &QQmlTableInstanceModel::itemPooled, this, &QQuickTableViewPrivate::itemPooledCallback);
+ QObjectPrivate::connect(tableModel, &QQmlTableInstanceModel::itemReused, this, &QQuickTableViewPrivate::itemReusedCallback);
+ }
+
+ if (auto const aim = model->abstractItemModel()) {
+ // When the model exposes a QAIM, we connect to it directly. This means that if the current model is
+ // a QQmlDelegateModel, we just ignore all the change sets it emits. In most cases, the model will instead
+ // be our own QQmlTableInstanceModel, which doesn't bother creating change sets at all. For models that are
+ // not based on QAIM (like QQmlObjectModel, QQmlListModel, javascript arrays etc), there is currently no way
+ // to modify the model at runtime without also re-setting the model on the view.
+ connect(aim, &QAbstractItemModel::rowsMoved, this, &QQuickTableViewPrivate::rowsMovedCallback);
+ connect(aim, &QAbstractItemModel::columnsMoved, this, &QQuickTableViewPrivate::columnsMovedCallback);
+ connect(aim, &QAbstractItemModel::rowsInserted, this, &QQuickTableViewPrivate::rowsInsertedCallback);
+ connect(aim, &QAbstractItemModel::rowsRemoved, this, &QQuickTableViewPrivate::rowsRemovedCallback);
+ connect(aim, &QAbstractItemModel::columnsInserted, this, &QQuickTableViewPrivate::columnsInsertedCallback);
+ connect(aim, &QAbstractItemModel::columnsRemoved, this, &QQuickTableViewPrivate::columnsRemovedCallback);
+ connect(aim, &QAbstractItemModel::modelReset, this, &QQuickTableViewPrivate::modelResetCallback);
+ } else {
+ QObjectPrivate::connect(model, &QQmlInstanceModel::modelUpdated, this, &QQuickTableViewPrivate::modelUpdated);
+ }
+}
+
+void QQuickTableViewPrivate::disconnectFromModel()
+{
+ Q_TABLEVIEW_ASSERT(model, "");
+
+ QObjectPrivate::disconnect(model, &QQmlInstanceModel::createdItem, this, &QQuickTableViewPrivate::itemCreatedCallback);
+ QObjectPrivate::disconnect(model, &QQmlInstanceModel::initItem, this, &QQuickTableViewPrivate::initItemCallback);
+
+ if (tableModel) {
+ QObjectPrivate::disconnect(tableModel, &QQmlTableInstanceModel::itemPooled, this, &QQuickTableViewPrivate::itemPooledCallback);
+ QObjectPrivate::disconnect(tableModel, &QQmlTableInstanceModel::itemReused, this, &QQuickTableViewPrivate::itemReusedCallback);
+ }
+
+ if (auto const aim = model->abstractItemModel()) {
+ disconnect(aim, &QAbstractItemModel::rowsMoved, this, &QQuickTableViewPrivate::rowsMovedCallback);
+ disconnect(aim, &QAbstractItemModel::columnsMoved, this, &QQuickTableViewPrivate::columnsMovedCallback);
+ disconnect(aim, &QAbstractItemModel::rowsInserted, this, &QQuickTableViewPrivate::rowsInsertedCallback);
+ disconnect(aim, &QAbstractItemModel::rowsRemoved, this, &QQuickTableViewPrivate::rowsRemovedCallback);
+ disconnect(aim, &QAbstractItemModel::columnsInserted, this, &QQuickTableViewPrivate::columnsInsertedCallback);
+ disconnect(aim, &QAbstractItemModel::columnsRemoved, this, &QQuickTableViewPrivate::columnsRemovedCallback);
+ disconnect(aim, &QAbstractItemModel::modelReset, this, &QQuickTableViewPrivate::modelResetCallback);
+ } else {
+ QObjectPrivate::disconnect(model, &QQmlInstanceModel::modelUpdated, this, &QQuickTableViewPrivate::modelUpdated);
+ }
+}
+
+void QQuickTableViewPrivate::modelUpdated(const QQmlChangeSet &changeSet, bool reset)
+{
+ Q_UNUSED(changeSet);
+ Q_UNUSED(reset);
+
+ Q_TABLEVIEW_ASSERT(!model->abstractItemModel(), "");
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+}
+
+void QQuickTableViewPrivate::rowsMovedCallback(const QModelIndex &parent, int, int, const QModelIndex &, int )
+{
+ if (parent != QModelIndex())
+ return;
+
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+}
+
+void QQuickTableViewPrivate::columnsMovedCallback(const QModelIndex &parent, int, int, const QModelIndex &, int)
+{
+ if (parent != QModelIndex())
+ return;
+
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+}
+
+void QQuickTableViewPrivate::rowsInsertedCallback(const QModelIndex &parent, int, int)
+{
+ if (parent != QModelIndex())
+ return;
+
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+}
+
+void QQuickTableViewPrivate::rowsRemovedCallback(const QModelIndex &parent, int, int)
+{
+ if (parent != QModelIndex())
+ return;
+
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+}
+
+void QQuickTableViewPrivate::columnsInsertedCallback(const QModelIndex &parent, int, int)
+{
+ if (parent != QModelIndex())
+ return;
+
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+}
+
+void QQuickTableViewPrivate::columnsRemovedCallback(const QModelIndex &parent, int, int)
+{
+ if (parent != QModelIndex())
+ return;
+
+ scheduleRebuildTable(RebuildOption::ViewportOnly);
+}
+
+void QQuickTableViewPrivate::modelResetCallback()
+{
+ scheduleRebuildTable(RebuildOption::All);
+}
+
+QQuickTableView::QQuickTableView(QQuickItem *parent)
+ : QQuickFlickable(*(new QQuickTableViewPrivate), parent)
+{
+ setFlag(QQuickItem::ItemIsFocusScope);
+}
+
+int QQuickTableView::rows() const
+{
+ return d_func()->tableSize.height();
+}
+
+int QQuickTableView::columns() const
+{
+ return d_func()->tableSize.width();
+}
+
+qreal QQuickTableView::rowSpacing() const
+{
+ return d_func()->cellSpacing.height();
+}
+
+void QQuickTableView::setRowSpacing(qreal spacing)
+{
+ Q_D(QQuickTableView);
+ if (qt_is_nan(spacing) || !qt_is_finite(spacing) || spacing < 0)
+ return;
+ if (qFuzzyCompare(d->cellSpacing.height(), spacing))
+ return;
+
+ d->cellSpacing.setHeight(spacing);
+ d->invalidateColumnRowPositions();
+ emit rowSpacingChanged();
+}
+
+qreal QQuickTableView::columnSpacing() const
+{
+ return d_func()->cellSpacing.width();
+}
+
+void QQuickTableView::setColumnSpacing(qreal spacing)
+{
+ Q_D(QQuickTableView);
+ if (qt_is_nan(spacing) || !qt_is_finite(spacing) || spacing < 0)
+ return;
+ if (qFuzzyCompare(d->cellSpacing.width(), spacing))
+ return;
+
+ d->cellSpacing.setWidth(spacing);
+ d->invalidateColumnRowPositions();
+ emit columnSpacingChanged();
+}
+
+QJSValue QQuickTableView::rowHeightProvider() const
+{
+ return d_func()->rowHeightProvider;
+}
+
+void QQuickTableView::setRowHeightProvider(QJSValue provider)
+{
+ Q_D(QQuickTableView);
+ if (provider.strictlyEquals(d->rowHeightProvider))
+ return;
+
+ d->rowHeightProvider = provider;
+ d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::ViewportOnly);
+ emit rowHeightProviderChanged();
+}
+
+QJSValue QQuickTableView::columnWidthProvider() const
+{
+ return d_func()->columnWidthProvider;
+}
+
+void QQuickTableView::setColumnWidthProvider(QJSValue provider)
+{
+ Q_D(QQuickTableView);
+ if (provider.strictlyEquals(d->columnWidthProvider))
+ return;
+
+ d->columnWidthProvider = provider;
+ d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::ViewportOnly);
+ emit columnWidthProviderChanged();
+}
+
+QVariant QQuickTableView::model() const
+{
+ return d_func()->assignedModel;
+}
+
+void QQuickTableView::setModel(const QVariant &newModel)
+{
+ Q_D(QQuickTableView);
+ if (newModel == d->assignedModel)
+ return;
+
+ d->assignedModel = newModel;
+ d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::All);
+ emit modelChanged();
+}
+
+QQmlComponent *QQuickTableView::delegate() const
+{
+ return d_func()->assignedDelegate;
+}
+
+void QQuickTableView::setDelegate(QQmlComponent *newDelegate)
+{
+ Q_D(QQuickTableView);
+ if (newDelegate == d->assignedDelegate)
+ return;
+
+ d->assignedDelegate = newDelegate;
+ d->scheduleRebuildTable(QQuickTableViewPrivate::RebuildOption::All);
+
+ emit delegateChanged();
+}
+
+bool QQuickTableView::reuseItems() const
+{
+ return bool(d_func()->reusableFlag == QQmlTableInstanceModel::Reusable);
+}
+
+void QQuickTableView::setReuseItems(bool reuse)
+{
+ Q_D(QQuickTableView);
+ if (reuseItems() == reuse)
+ return;
+
+ d->reusableFlag = reuse ? QQmlTableInstanceModel::Reusable : QQmlTableInstanceModel::NotReusable;
+
+ if (!reuse && d->tableModel) {
+ // When we're told to not reuse items, we
+ // immediately, as documented, drain the pool.
+ d->tableModel->drainReusableItemsPool(0);
+ }
+
+ emit reuseItemsChanged();
+}
+
+void QQuickTableView::setContentWidth(qreal width)
+{
+ Q_D(QQuickTableView);
+ d->explicitContentWidth = width;
+ QQuickFlickable::setContentWidth(width);
+}
+
+void QQuickTableView::setContentHeight(qreal height)
+{
+ Q_D(QQuickTableView);
+ d->explicitContentHeight = height;
+ QQuickFlickable::setContentHeight(height);
+}
+
+void QQuickTableView::forceLayout()
+{
+ Q_D(QQuickTableView);
+ d->columnRowPositionsInvalid = true;
+
+ if (d->polishing) {
+ qWarning() << "TableView::forceLayout(): Cannot do an immediate re-layout during an ongoing layout!";
+ polish();
+ return;
+ }
+
+ d->updatePolish();
+}
+
+QQuickTableViewAttached *QQuickTableView::qmlAttachedProperties(QObject *obj)
+{
+ return new QQuickTableViewAttached(obj);
+}
+
+void QQuickTableView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
+{
+ Q_D(QQuickTableView);
+ QQuickFlickable::geometryChanged(newGeometry, oldGeometry);
+
+ if (d->tableModel) {
+ // When the view changes size, we force the pool to
+ // shrink by releasing all pooled items.
+ d->tableModel->drainReusableItemsPool(0);
+ }
+
+ polish();
+}
+
+void QQuickTableView::viewportMoved(Qt::Orientations orientation)
+{
+ Q_D(QQuickTableView);
+ QQuickFlickable::viewportMoved(orientation);
+
+ QQuickTableViewPrivate::RebuildOptions options = QQuickTableViewPrivate::RebuildOption::None;
+
+ // Check the viewport moved more than one page vertically
+ if (!d->viewportRect.intersects(QRectF(d->viewportRect.x(), contentY(), 1, height())))
+ options |= QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftRow;
+ // Check the viewport moved more than one page horizontally
+ if (!d->viewportRect.intersects(QRectF(contentX(), d->viewportRect.y(), width(), 1)))
+ options |= QQuickTableViewPrivate::RebuildOption::CalculateNewTopLeftColumn;
+
+ if (options) {
+ // When the viewport has moved more than one page vertically or horizontally, we switch
+ // strategy from refilling edges around the current table to instead rebuild the table
+ // from scratch inside the new viewport. This will greatly improve performance when flicking
+ // a long distance in one go, which can easily happen when dragging on scrollbars.
+ options |= QQuickTableViewPrivate::RebuildOption::ViewportOnly;
+ d->scheduleRebuildTable(options);
+ }
+
+ if (d->rebuildScheduled) {
+ // No reason to do anything, since we're about to rebuild the whole table anyway.
+ // Besides, calling updatePolish, which will start the rebuild, can easily cause
+ // binding loops to happen since we usually end up modifying the geometry of the
+ // viewport (contentItem) as well.
+ return;
+ }
+
+ // Calling polish() will schedule a polish event. But while the user is flicking, several
+ // mouse events will be handled before we get an updatePolish() call. And the updatePolish()
+ // call will only see the last mouse position. This results in a stuttering flick experience
+ // (especially on windows). We improve on this by calling updatePolish() directly. But this
+ // has the pitfall that we open up for recursive callbacks. E.g while inside updatePolish(), we
+ // load/unload items, and emit signals. The application can listen to those signals and set a
+ // new contentX/Y on the flickable. So we need to guard for this, to avoid unexpected behavior.
+ if (d->polishing)
+ polish();
+ else
+ d->updatePolish();
+}
+
+void QQuickTableViewPrivate::_q_componentFinalized()
+{
+ // Now that all bindings are evaluated, and we know
+ // our final geometery, we can build the table.
+ qCDebug(lcTableViewDelegateLifecycle);
+ updatePolish();
+}
+
+void QQuickTableViewPrivate::registerCallbackWhenBindingsAreEvaluated()
+{
+ // componentComplete() is called on us after all static values have been assigned, but
+ // before bindings to any anchestors has been evaluated. Especially this means that
+ // if our size is bound to the parents size, it will still be empty at that point.
+ // And we cannot build the table without knowing our own size. We could wait until we
+ // got the first updatePolish() callback, but at that time, any asynchronous loaders that we
+ // might be inside have already finished loading, which means that we would load all
+ // the delegate items synchronously instead of asynchronously. We therefore add a componentFinalized
+ // function that gets called after all the bindings we rely on has been evaluated.
+ // When receiving this call, we load the delegate items (and build the table).
+ Q_Q(QQuickTableView);
+ QQmlEnginePrivate *engPriv = QQmlEnginePrivate::get(qmlEngine(q));
+ static int finalizedIdx = -1;
+ if (finalizedIdx < 0)
+ finalizedIdx = q->metaObject()->indexOfSlot("_q_componentFinalized()");
+ engPriv->registerFinalizeCallback(q, finalizedIdx);
+}
+
+void QQuickTableView::componentComplete()
+{
+ QQuickFlickable::componentComplete();
+ d_func()->registerCallbackWhenBindingsAreEvaluated();
+}
+
+#include "moc_qquicktableview_p.cpp"
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/qquicktableview_p.h b/src/quick/items/qquicktableview_p.h
new file mode 100644
index 0000000000..9fcd4c6c17
--- /dev/null
+++ b/src/quick/items/qquicktableview_p.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKTABLEVIEW_P_H
+#define QQUICKTABLEVIEW_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtquickglobal_p.h>
+QT_REQUIRE_CONFIG(quick_tableview);
+
+#include <QtCore/qpointer.h>
+#include <QtQuick/private/qtquickglobal_p.h>
+#include <QtQuick/private/qquickflickable_p.h>
+#include <QtQml/private/qqmlnullablevalue_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickTableViewAttached;
+class QQuickTableViewPrivate;
+
+class Q_QUICK_PRIVATE_EXPORT QQuickTableView : public QQuickFlickable
+{
+ Q_OBJECT
+
+ Q_PROPERTY(int rows READ rows NOTIFY rowsChanged)
+ Q_PROPERTY(int columns READ columns NOTIFY columnsChanged)
+ Q_PROPERTY(qreal rowSpacing READ rowSpacing WRITE setRowSpacing NOTIFY rowSpacingChanged)
+ Q_PROPERTY(qreal columnSpacing READ columnSpacing WRITE setColumnSpacing NOTIFY columnSpacingChanged)
+ Q_PROPERTY(QJSValue rowHeightProvider READ rowHeightProvider WRITE setRowHeightProvider NOTIFY rowHeightProviderChanged)
+ Q_PROPERTY(QJSValue columnWidthProvider READ columnWidthProvider WRITE setColumnWidthProvider NOTIFY columnWidthProviderChanged)
+ Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged)
+ Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
+ Q_PROPERTY(bool reuseItems READ reuseItems WRITE setReuseItems NOTIFY reuseItemsChanged)
+ Q_PROPERTY(qreal contentWidth READ contentWidth WRITE setContentWidth NOTIFY contentWidthChanged)
+ Q_PROPERTY(qreal contentHeight READ contentHeight WRITE setContentWidth NOTIFY contentHeightChanged)
+
+public:
+ QQuickTableView(QQuickItem *parent = nullptr);
+
+ int rows() const;
+ int columns() const;
+
+ qreal rowSpacing() const;
+ void setRowSpacing(qreal spacing);
+
+ qreal columnSpacing() const;
+ void setColumnSpacing(qreal spacing);
+
+ QJSValue rowHeightProvider() const;
+ void setRowHeightProvider(QJSValue provider);
+
+ QJSValue columnWidthProvider() const;
+ void setColumnWidthProvider(QJSValue provider);
+
+ QVariant model() const;
+ void setModel(const QVariant &newModel);
+
+ QQmlComponent *delegate() const;
+ void setDelegate(QQmlComponent *);
+
+ bool reuseItems() const;
+ void setReuseItems(bool reuseItems);
+
+ void setContentWidth(qreal width);
+ void setContentHeight(qreal height);
+
+ Q_INVOKABLE void forceLayout();
+
+ static QQuickTableViewAttached *qmlAttachedProperties(QObject *);
+
+Q_SIGNALS:
+ void rowsChanged();
+ void columnsChanged();
+ void rowSpacingChanged();
+ void columnSpacingChanged();
+ void rowHeightProviderChanged();
+ void columnWidthProviderChanged();
+ void modelChanged();
+ void delegateChanged();
+ void reuseItemsChanged();
+
+protected:
+ void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
+ void viewportMoved(Qt::Orientations orientation) override;
+ void componentComplete() override;
+
+private:
+ Q_DISABLE_COPY(QQuickTableView)
+ Q_DECLARE_PRIVATE(QQuickTableView)
+
+ Q_PRIVATE_SLOT(d_func(), void _q_componentFinalized())
+};
+
+class Q_QUICK_PRIVATE_EXPORT QQuickTableViewAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQuickTableView *view READ view NOTIFY viewChanged)
+
+public:
+ QQuickTableViewAttached(QObject *parent)
+ : QObject(parent) {}
+
+ QQuickTableView *view() const { return m_view; }
+ void setView(QQuickTableView *newTableView) {
+ if (newTableView == m_view)
+ return;
+ m_view = newTableView;
+ Q_EMIT viewChanged();
+ }
+
+Q_SIGNALS:
+ void viewChanged();
+ void pooled();
+ void reused();
+
+private:
+ QPointer<QQuickTableView> m_view;
+
+ friend class QQuickTableViewPrivate;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickTableView)
+QML_DECLARE_TYPEINFO(QQuickTableView, QML_HAS_ATTACHED_PROPERTIES)
+
+#endif // QQUICKTABLEVIEW_P_H
diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h
new file mode 100644
index 0000000000..a4f829addd
--- /dev/null
+++ b/src/quick/items/qquicktableview_p_p.h
@@ -0,0 +1,387 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKTABLEVIEW_P_P_H
+#define QQUICKTABLEVIEW_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquicktableview_p.h"
+
+#include <QtCore/qtimer.h>
+#include <QtQml/private/qqmltableinstancemodel_p.h>
+#include <QtQml/private/qqmlincubator_p.h>
+#include <QtQml/private/qqmlchangeset_p.h>
+#include <QtQml/qqmlinfo.h>
+
+#include <QtQuick/private/qquickflickable_p_p.h>
+#include <QtQuick/private/qquickitemviewfxitem_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcTableViewDelegateLifecycle)
+
+static const qreal kDefaultRowHeight = 50;
+static const qreal kDefaultColumnWidth = 50;
+
+class FxTableItem;
+
+class Q_QML_AUTOTEST_EXPORT QQuickTableViewPrivate : public QQuickFlickablePrivate
+{
+ Q_DECLARE_PUBLIC(QQuickTableView)
+
+public:
+ class TableEdgeLoadRequest
+ {
+ // Whenever we need to load new rows or columns in the
+ // table, we fill out a TableEdgeLoadRequest.
+ // TableEdgeLoadRequest is just a struct that keeps track
+ // of which cells that needs to be loaded, and which cell
+ // the table is currently loading. The loading itself is
+ // done by QQuickTableView.
+
+ public:
+ void begin(const QPoint &cell, const QPointF &pos, QQmlIncubator::IncubationMode incubationMode)
+ {
+ Q_ASSERT(!active);
+ active = true;
+ tableEdge = Qt::Edge(0);
+ tableCells = QLine(cell, cell);
+ mode = incubationMode;
+ cellCount = 1;
+ currentIndex = 0;
+ startPos = pos;
+ qCDebug(lcTableViewDelegateLifecycle()) << "begin top-left:" << toString();
+ }
+
+ void begin(const QLine cellsToLoad, Qt::Edge edgeToLoad, QQmlIncubator::IncubationMode incubationMode)
+ {
+ Q_ASSERT(!active);
+ active = true;
+ tableEdge = edgeToLoad;
+ tableCells = cellsToLoad;
+ mode = incubationMode;
+ cellCount = tableCells.x2() - tableCells.x1() + tableCells.y2() - tableCells.y1() + 1;
+ currentIndex = 0;
+ qCDebug(lcTableViewDelegateLifecycle()) << "begin:" << toString();
+ }
+
+ inline void markAsDone() { active = false; }
+ inline bool isActive() { return active; }
+
+ inline QPoint firstCell() { return tableCells.p1(); }
+ inline QPoint lastCell() { return tableCells.p2(); }
+ inline QPoint currentCell() { return cellAt(currentIndex); }
+ inline QPoint previousCell() { return cellAt(currentIndex - 1); }
+
+ inline bool atBeginning() { return currentIndex == 0; }
+ inline bool hasCurrentCell() { return currentIndex < cellCount; }
+ inline void moveToNextCell() { ++currentIndex; }
+
+ inline Qt::Edge edge() { return tableEdge; }
+ inline QQmlIncubator::IncubationMode incubationMode() { return mode; }
+
+ inline QPointF startPosition() { return startPos; }
+
+ QString toString()
+ {
+ QString str;
+ QDebug dbg(&str);
+ dbg.nospace() << "TableSectionLoadRequest(" << "edge:"
+ << tableEdge << " cells:" << tableCells << " incubation:";
+
+ switch (mode) {
+ case QQmlIncubator::Asynchronous:
+ dbg << "Asynchronous";
+ break;
+ case QQmlIncubator::AsynchronousIfNested:
+ dbg << "AsynchronousIfNested";
+ break;
+ case QQmlIncubator::Synchronous:
+ dbg << "Synchronous";
+ break;
+ }
+
+ return str;
+ }
+
+ private:
+ Qt::Edge tableEdge = Qt::Edge(0);
+ QLine tableCells;
+ int currentIndex = 0;
+ int cellCount = 0;
+ bool active = false;
+ QQmlIncubator::IncubationMode mode = QQmlIncubator::AsynchronousIfNested;
+ QPointF startPos;
+
+ QPoint cellAt(int index)
+ {
+ int x = tableCells.p1().x() + (tableCells.dx() ? index : 0);
+ int y = tableCells.p1().y() + (tableCells.dy() ? index : 0);
+ return QPoint(x, y);
+ }
+ };
+
+ enum class RebuildState {
+ Begin = 0,
+ LoadInitalTable,
+ VerifyTable,
+ LayoutTable,
+ LoadAndUnloadAfterLayout,
+ PreloadColumns,
+ PreloadRows,
+ MovePreloadedItemsToPool,
+ Done
+ };
+
+ enum class RebuildOption {
+ None = 0,
+ ViewportOnly = 0x1,
+ CalculateNewTopLeftRow = 0x2,
+ CalculateNewTopLeftColumn = 0x4,
+ All = 0x8,
+ };
+ Q_DECLARE_FLAGS(RebuildOptions, RebuildOption)
+
+public:
+ QQuickTableViewPrivate();
+ ~QQuickTableViewPrivate() override;
+
+ static inline QQuickTableViewPrivate *get(QQuickTableView *q) { return q->d_func(); }
+
+ void updatePolish() override;
+ void fixup(AxisData &data, qreal minExtent, qreal maxExtent) override;
+
+public:
+ QHash<int, FxTableItem *> loadedItems;
+
+ // model, tableModel and modelVariant all point to the same model. modelVariant
+ // is the model assigned by the user. And tableModel is the wrapper model we create
+ // around it. But if the model is an instance model directly, we cannot wrap it, so
+ // we need a pointer for that case as well.
+ QQmlInstanceModel* model = nullptr;
+ QPointer<QQmlTableInstanceModel> tableModel = nullptr;
+ QVariant modelVariant;
+
+ // When the applications assignes a new model or delegate to the view, we keep them
+ // around until we're ready to take them into use (syncWithPendingChanges).
+ QVariant assignedModel = QVariant(int(0));
+ QQmlComponent *assignedDelegate = nullptr;
+
+ // loadedTable describes the table cells that are currently loaded (from top left
+ // row/column to bottom right row/column). loadedTableOuterRect describes the actual
+ // pixels that those cells cover, and is matched agains the viewport to determine when
+ // we need to fill up with more rows/columns. loadedTableInnerRect describes the pixels
+ // that the loaded table covers if you remove one row/column on each side of the table, and
+ // is used to determine rows/columns that are no longer visible and can be unloaded.
+ QRect loadedTable;
+ QRectF loadedTableOuterRect;
+ QRectF loadedTableInnerRect;
+
+ QRectF viewportRect = QRectF(0, 0, -1, -1);
+
+ QSize tableSize;
+
+ RebuildState rebuildState = RebuildState::Done;
+ RebuildOptions rebuildOptions = RebuildOption::All;
+ RebuildOptions scheduledRebuildOptions = RebuildOption::All;
+
+ TableEdgeLoadRequest loadRequest;
+
+ QPoint contentSizeBenchMarkPoint = QPoint(-1, -1);
+ QSizeF cellSpacing = QSizeF(0, 0);
+
+ QQmlTableInstanceModel::ReusableFlag reusableFlag = QQmlTableInstanceModel::Reusable;
+
+ bool blockItemCreatedCallback = false;
+ bool columnRowPositionsInvalid = false;
+ bool layoutWarningIssued = false;
+ bool polishing = false;
+ bool rebuildScheduled = true;
+
+ QJSValue rowHeightProvider;
+ QJSValue columnWidthProvider;
+
+ // TableView uses contentWidth/height to report the size of the table (this
+ // will e.g make scrollbars written for Flickable work out of the box). This
+ // value is continuously calculated, and will change/improve as more columns
+ // are loaded into view. At the same time, we want to open up for the
+ // possibility that the application can set the content width explicitly, in
+ // case it knows what the exact width should be from the start. We therefore
+ // override the contentWidth/height properties from QQuickFlickable, to be able
+ // to implement this combined behavior. This also lets us lazy build the table
+ // if the application needs to know the content size early on.
+ QQmlNullableValue<qreal> explicitContentWidth;
+ QQmlNullableValue<qreal> explicitContentHeight;
+
+ QSizeF averageEdgeSize;
+
+ const static QPoint kLeft;
+ const static QPoint kRight;
+ const static QPoint kUp;
+ const static QPoint kDown;
+
+#ifdef QT_DEBUG
+ QString forcedIncubationMode = qEnvironmentVariable("QT_TABLEVIEW_INCUBATION_MODE");
+#endif
+
+public:
+ QQuickTableViewAttached *getAttachedObject(const QObject *object) const;
+
+ int modelIndexAtCell(const QPoint &cell) const;
+ QPoint cellAtModelIndex(int modelIndex) const;
+
+ qreal sizeHintForColumn(int column);
+ qreal sizeHintForRow(int row);
+ void calculateTableSize();
+
+ qreal resolveColumnWidth(int column);
+ qreal resolveRowHeight(int row);
+
+ void relayoutTable();
+ void relayoutTableItems();
+
+ void layoutVerticalEdge(Qt::Edge tableEdge);
+ void layoutHorizontalEdge(Qt::Edge tableEdge);
+ void layoutTopLeftItem();
+ void layoutTableEdgeFromLoadRequest();
+
+ void updateContentWidth();
+ void updateContentHeight();
+ void updateAverageEdgeSize();
+
+ void enforceTableAtOrigin();
+ void syncLoadedTableRectFromLoadedTable();
+ void syncLoadedTableFromLoadRequest();
+
+ bool canLoadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const;
+ bool canUnloadTableEdge(Qt::Edge tableEdge, const QRectF fillRect) const;
+ Qt::Edge nextEdgeToLoad(const QRectF rect);
+ Qt::Edge nextEdgeToUnload(const QRectF rect);
+
+ qreal cellWidth(const QPoint &cell);
+ qreal cellHeight(const QPoint &cell);
+
+ FxTableItem *loadedTableItem(const QPoint &cell) const;
+ FxTableItem *itemNextTo(const FxTableItem *fxTableItem, const QPoint &direction) const;
+ FxTableItem *createFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode);
+ FxTableItem *loadFxTableItem(const QPoint &cell, QQmlIncubator::IncubationMode incubationMode);
+
+ void releaseItem(FxTableItem *fxTableItem, QQmlTableInstanceModel::ReusableFlag reusableFlag);
+ void releaseLoadedItems(QQmlTableInstanceModel::ReusableFlag reusableFlag);
+
+ void unloadItem(const QPoint &cell);
+ void unloadItems(const QLine &items);
+
+ void loadInitialTopLeftItem(const QPoint &cell, const QPointF &pos);
+ void loadEdge(Qt::Edge edge, QQmlIncubator::IncubationMode incubationMode);
+ void unloadEdge(Qt::Edge edge);
+ void loadAndUnloadVisibleEdges();
+ void drainReusePoolAfterLoadRequest();
+ void cancelLoadRequest();
+ void processLoadRequest();
+
+ void processRebuildTable();
+ bool moveToNextRebuildState();
+ void beginRebuildTable();
+ void layoutAfterLoadingInitialTable();
+
+ void scheduleRebuildTable(QQuickTableViewPrivate::RebuildOptions options);
+ void invalidateColumnRowPositions();
+
+ void createWrapperModel();
+
+ void initItemCallback(int modelIndex, QObject *item);
+ void itemCreatedCallback(int modelIndex, QObject *object);
+ void itemPooledCallback(int modelIndex, QObject *object);
+ void itemReusedCallback(int modelIndex, QObject *object);
+ void modelUpdated(const QQmlChangeSet &changeSet, bool reset);
+
+ inline void syncWithPendingChanges();
+ inline void syncDelegate();
+ inline void syncModel();
+ inline void syncRebuildOptions();
+
+ void connectToModel();
+ void disconnectFromModel();
+
+ void rowsMovedCallback(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int row);
+ void columnsMovedCallback(const QModelIndex &parent, int start, int end, const QModelIndex &destination, int column);
+ void rowsInsertedCallback(const QModelIndex &parent, int begin, int end);
+ void rowsRemovedCallback(const QModelIndex &parent, int begin, int end);
+ void columnsInsertedCallback(const QModelIndex &parent, int begin, int end);
+ void columnsRemovedCallback(const QModelIndex &parent, int begin, int end);
+ void modelResetCallback();
+
+ void _q_componentFinalized();
+ void registerCallbackWhenBindingsAreEvaluated();
+
+ inline QString tableLayoutToString() const;
+ void dumpTable() const;
+};
+
+class FxTableItem : public QQuickItemViewFxItem
+{
+public:
+ FxTableItem(QQuickItem *item, QQuickTableView *table, bool own)
+ : QQuickItemViewFxItem(item, own, QQuickTableViewPrivate::get(table))
+ {
+ }
+
+ qreal position() const override { return 0; }
+ qreal endPosition() const override { return 0; }
+ qreal size() const override { return 0; }
+ qreal sectionSize() const override { return 0; }
+ bool contains(qreal, qreal) const override { return false; }
+
+ QPoint cell;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index 76720e5c5d..3cce30aaf6 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -88,6 +88,7 @@ QQuickTextPrivate::QQuickTextPrivate()
, truncated(false), hAlignImplicit(true), rightToLeftText(false)
, layoutTextElided(false), textHasChanged(true), needToUpdateLayout(false), formatModifiesFontSize(false)
, polishSize(false)
+ , updateSizeRecursionGuard(false)
{
implicitAntialiasing = true;
}
@@ -442,6 +443,7 @@ void QQuickTextPrivate::updateSize()
//### need to confirm cost of always setting these for richText
internalWidthUpdate = true;
+ qreal oldWidth = q->width();
qreal iWidth = -1;
if (!q->widthValid())
iWidth = size.width();
@@ -449,30 +451,45 @@ void QQuickTextPrivate::updateSize()
q->setImplicitSize(iWidth + hPadding, size.height() + vPadding);
internalWidthUpdate = false;
- if (iWidth == -1)
- q->setImplicitHeight(size.height() + vPadding);
-
- QTextBlock firstBlock = extra->doc->firstBlock();
- while (firstBlock.layout()->lineCount() == 0)
- firstBlock = firstBlock.next();
-
- QTextBlock lastBlock = extra->doc->lastBlock();
- while (lastBlock.layout()->lineCount() == 0)
- lastBlock = lastBlock.previous();
-
- if (firstBlock.lineCount() > 0 && lastBlock.lineCount() > 0) {
- QTextLine firstLine = firstBlock.layout()->lineAt(0);
- QTextLine lastLine = lastBlock.layout()->lineAt(lastBlock.layout()->lineCount() - 1);
- advance = QSizeF(lastLine.horizontalAdvance(),
- (lastLine.y() + lastBlock.layout()->position().y()) - (firstLine.y() + firstBlock.layout()->position().y()));
+ // If the implicit width update caused a recursive change of the width,
+ // we will have skipped integral parts of the layout due to the
+ // internalWidthUpdate recursion guard. To make sure everything is up
+ // to date, we need to run a second pass over the layout when updateSize()
+ // is done.
+ if (!qFuzzyCompare(q->width(), oldWidth) && !updateSizeRecursionGuard) {
+ updateSizeRecursionGuard = true;
+ updateSize();
+ updateSizeRecursionGuard = false;
} else {
- advance = QSizeF();
+ if (iWidth == -1)
+ q->setImplicitHeight(size.height() + vPadding);
+
+ QTextBlock firstBlock = extra->doc->firstBlock();
+ while (firstBlock.layout()->lineCount() == 0)
+ firstBlock = firstBlock.next();
+
+ QTextBlock lastBlock = extra->doc->lastBlock();
+ while (lastBlock.layout()->lineCount() == 0)
+ lastBlock = lastBlock.previous();
+
+ if (firstBlock.lineCount() > 0 && lastBlock.lineCount() > 0) {
+ QTextLine firstLine = firstBlock.layout()->lineAt(0);
+ QTextLine lastLine = lastBlock.layout()->lineAt(lastBlock.layout()->lineCount() - 1);
+ advance = QSizeF(lastLine.horizontalAdvance(),
+ (lastLine.y() + lastBlock.layout()->position().y()) - (firstLine.y() + firstBlock.layout()->position().y()));
+ } else {
+ advance = QSizeF();
+ }
}
}
if (layedOutTextRect.size() != previousSize)
emit q->contentSizeChanged();
+ if (layedOutTextRect.width() != previousSize.width())
+ emit q->contentWidthChanged(layedOutTextRect.width());
+ if (layedOutTextRect.height() != previousSize.height())
+ emit q->contentHeightChanged(layedOutTextRect.height());
updateType = UpdatePaintNode;
q->update();
}
diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h
index 15e989c13d..f4e7fa7046 100644
--- a/src/quick/items/qquicktext_p.h
+++ b/src/quick/items/qquicktext_p.h
@@ -79,10 +79,10 @@ class Q_QUICK_PRIVATE_EXPORT QQuickText : public QQuickImplicitSizeItem
Q_PROPERTY(TextFormat textFormat READ textFormat WRITE setTextFormat NOTIFY textFormatChanged)
Q_PROPERTY(TextElideMode elide READ elideMode WRITE setElideMode NOTIFY elideModeChanged) //### elideMode?
- Q_PROPERTY(qreal contentWidth READ contentWidth NOTIFY contentSizeChanged)
- Q_PROPERTY(qreal contentHeight READ contentHeight NOTIFY contentSizeChanged)
- Q_PROPERTY(qreal paintedWidth READ contentWidth NOTIFY contentSizeChanged) // Compatibility
- Q_PROPERTY(qreal paintedHeight READ contentHeight NOTIFY contentSizeChanged)
+ Q_PROPERTY(qreal contentWidth READ contentWidth NOTIFY contentWidthChanged)
+ Q_PROPERTY(qreal contentHeight READ contentHeight NOTIFY contentHeightChanged)
+ Q_PROPERTY(qreal paintedWidth READ contentWidth NOTIFY contentWidthChanged) // Compatibility
+ Q_PROPERTY(qreal paintedHeight READ contentHeight NOTIFY contentHeightChanged)
Q_PROPERTY(qreal lineHeight READ lineHeight WRITE setLineHeight NOTIFY lineHeightChanged)
Q_PROPERTY(LineHeightMode lineHeightMode READ lineHeightMode WRITE setLineHeightMode NOTIFY lineHeightModeChanged)
Q_PROPERTY(QUrl baseUrl READ baseUrl WRITE setBaseUrl RESET resetBaseUrl NOTIFY baseUrlChanged)
@@ -272,6 +272,8 @@ Q_SIGNALS:
void textFormatChanged(QQuickText::TextFormat textFormat);
void elideModeChanged(QQuickText::TextElideMode mode);
void contentSizeChanged();
+ Q_REVISION(12) void contentWidthChanged(qreal contentWidth);
+ Q_REVISION(12) void contentHeightChanged(qreal contentHeight);
void lineHeightChanged(qreal lineHeight);
void lineHeightModeChanged(LineHeightMode mode);
void fontSizeModeChanged();
diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h
index b0b1492d57..fd26d966c8 100644
--- a/src/quick/items/qquicktext_p_p.h
+++ b/src/quick/items/qquicktext_p_p.h
@@ -174,6 +174,7 @@ public:
bool needToUpdateLayout:1;
bool formatModifiesFontSize:1;
bool polishSize:1; // Workaround for problem with polish called after updateSize (QTBUG-42636)
+ bool updateSizeRecursionGuard:1;
static const QChar elideChar;
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 1c44afc594..059886f41c 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -46,6 +46,7 @@
#include "qquickevents_p_p.h"
#include <private/qquickdrag_p.h>
+#include <private/qquickhoverhandler_p.h>
#include <private/qquickpointerhandler_p.h>
#include <QtQuick/private/qsgrenderer_p.h>
@@ -88,6 +89,8 @@ Q_LOGGING_CATEGORY(DBG_TOUCH, "qt.quick.touch")
Q_LOGGING_CATEGORY(DBG_TOUCH_TARGET, "qt.quick.touch.target")
Q_LOGGING_CATEGORY(DBG_MOUSE, "qt.quick.mouse")
Q_LOGGING_CATEGORY(DBG_MOUSE_TARGET, "qt.quick.mouse.target")
+Q_LOGGING_CATEGORY(lcWheelTarget, "qt.quick.wheel.target")
+Q_LOGGING_CATEGORY(lcGestureTarget, "qt.quick.gesture.target")
Q_LOGGING_CATEGORY(DBG_HOVER_TRACE, "qt.quick.hover.trace")
Q_LOGGING_CATEGORY(DBG_FOCUS, "qt.quick.focus")
Q_LOGGING_CATEGORY(DBG_DIRTY, "qt.quick.dirty")
@@ -256,14 +259,16 @@ void QQuickWindow::hideEvent(QHideEvent *)
void QQuickWindow::focusOutEvent(QFocusEvent *ev)
{
Q_D(QQuickWindow);
- d->contentItem->setFocus(false, ev->reason());
+ if (d->contentItem)
+ d->contentItem->setFocus(false, ev->reason());
}
/*! \reimp */
void QQuickWindow::focusInEvent(QFocusEvent *ev)
{
Q_D(QQuickWindow);
- d->contentItem->setFocus(true, ev->reason());
+ if (d->contentItem)
+ d->contentItem->setFocus(true, ev->reason());
d->updateFocusItemTransform();
}
@@ -610,6 +615,7 @@ QQmlListProperty<QObject> QQuickWindowPrivate::data()
static QMouseEvent *touchToMouseEvent(QEvent::Type type, const QTouchEvent::TouchPoint &p, QTouchEvent *event, QQuickItem *item, bool transformNeeded = true)
{
+ Q_ASSERT(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents));
// The touch point local position and velocity are not yet transformed.
QMouseEvent *me = new QMouseEvent(type, transformNeeded ? item->mapFromScene(p.scenePos()) : p.pos(), p.scenePos(), p.screenPos(),
Qt::LeftButton, (type == QEvent::MouseButtonRelease ? Qt::NoButton : Qt::LeftButton), event->modifiers());
@@ -651,6 +657,7 @@ bool QQuickWindowPrivate::checkIfDoubleClicked(ulong newPressEventTimestamp)
bool QQuickWindowPrivate::deliverTouchAsMouse(QQuickItem *item, QQuickPointerEvent *pointerEvent)
{
+ Q_ASSERT(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents));
Q_Q(QQuickWindow);
auto device = pointerEvent->device();
@@ -1315,7 +1322,9 @@ QQuickWindow::~QQuickWindow()
#if QT_CONFIG(draganddrop)
delete d->dragGrabber; d->dragGrabber = nullptr;
#endif
- delete d->contentItem; d->contentItem = nullptr;
+ QQuickRootItem *root = d->contentItem;
+ d->contentItem = nullptr;
+ delete root;
qDeleteAll(d->pointerEventInstances);
d->pointerEventInstances.clear();
@@ -1540,8 +1549,20 @@ bool QQuickWindowPrivate::clearHover(ulong timestamp)
QPointF pos = q->mapFromGlobal(QGuiApplicationPrivate::lastCursorPosition.toPoint());
bool accepted = false;
- for (QQuickItem* item : qAsConst(hoverItems))
+ for (QQuickItem* item : qAsConst(hoverItems)) {
accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), timestamp, true) || accepted;
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ if (itemPrivate->hasPointerHandlers()) {
+ pos = q->mapFromGlobal(QCursor::pos());
+ QQuickPointerEvent *pointerEvent = pointerEventInstance(QQuickPointerDevice::genericMouseDevice(), QEvent::MouseMove);
+ pointerEvent->point(0)->reset(Qt::TouchPointMoved, pos, quint64(1) << 24 /* mouse has device ID 1 */, timestamp, QVector2D());
+ pointerEvent->point(0)->setAccepted(true);
+ pointerEvent->localize(item);
+ for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers)
+ if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h))
+ hh->handlePointerEvent(pointerEvent);
+ }
+ }
hoverItems.clear();
return accepted;
}
@@ -1571,6 +1592,8 @@ bool QQuickWindow::event(QEvent *e)
return d->deliverTouchCancelEvent(static_cast<QTouchEvent*>(e));
break;
case QEvent::Enter: {
+ if (!d->contentItem)
+ return false;
QEnterEvent *enter = static_cast<QEnterEvent*>(e);
bool accepted = enter->isAccepted();
bool delivered = d->deliverHoverEvent(d->contentItem, enter->windowPos(), d->lastMousePosition,
@@ -1592,7 +1615,8 @@ bool QQuickWindow::event(QEvent *e)
break;
#endif
case QEvent::WindowDeactivate:
- contentItem()->windowDeactivateEvent();
+ if (d->contentItem)
+ d->contentItem->windowDeactivateEvent();
break;
case QEvent::Close: {
// TOOD Qt 6 (binary incompatible)
@@ -1617,7 +1641,7 @@ bool QQuickWindow::event(QEvent *e)
}
#if QT_CONFIG(gestures)
case QEvent::NativeGesture:
- d->deliverNativeGestureEvent(d->contentItem, static_cast<QNativeGestureEvent*>(e));
+ d->deliverSinglePointEventUntilAccepted(d->pointerEventInstance(e));
break;
#endif
case QEvent::ShortcutOverride:
@@ -1684,6 +1708,7 @@ void QQuickWindowPrivate::deliverToPassiveGrabbers(const QVector<QPointer <QQuic
{
const QVector<QQuickPointerHandler *> &eventDeliveryTargets = pointerEvent->device()->eventDeliveryTargets();
QVarLengthArray<QPair<QQuickItem *, bool>, 4> sendFilteredPointerEventResult;
+ hasFiltered.clear();
for (auto handler : passiveGrabbers) {
// a null pointer in passiveGrabbers is unlikely, unless the grabbing handler was deleted dynamically
if (Q_LIKELY(handler) && !eventDeliveryTargets.contains(handler)) {
@@ -1723,6 +1748,7 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven
if (grabberItem) {
bool handled = false;
+ hasFiltered.clear();
if (sendFilteredPointerEvent(pointerEvent, grabberItem))
handled = true;
// if the grabber is an Item:
@@ -1747,8 +1773,10 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven
// release event: ungrab if no buttons are pressed anymore
if (mouseIsReleased)
removeGrabber(grabberItem, true, isDeliveringTouchAsMouse());
- } else if (auto handler = point->grabberPointerHandler()) {
+ deliverToPassiveGrabbers(point->passiveGrabbers(), pointerEvent);
+ } else if (auto handler = point->grabberPointerHandler()) {
pointerEvent->localize(handler->parentItem());
+ hasFiltered.clear();
if (!sendFilteredPointerEvent(pointerEvent, handler->parentItem()))
handler->handlePointerEvent(pointerEvent);
if (mouseIsReleased)
@@ -1767,12 +1795,13 @@ void QQuickWindowPrivate::deliverMouseEvent(QQuickPointerMouseEvent *pointerEven
// If some points weren't grabbed, deliver to non-grabber PointerHandlers in reverse paint order
if (!pointerEvent->allPointsGrabbed() && pointerEvent->buttons()) {
- QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point->scenePosition(), false, false);
+ QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point, false, false);
for (QQuickItem *item : targetItems) {
QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
if (!itemPrivate->extra.isAllocated() || itemPrivate->extra->pointerHandlers.isEmpty())
continue;
pointerEvent->localize(item);
+ hasFiltered.clear();
if (!sendFilteredPointerEvent(pointerEvent, item)) {
if (itemPrivate->handlePointerEvent(pointerEvent, true)) // avoid re-delivering to grabbers
delivered = true;
@@ -1834,6 +1863,16 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
}
}
+ if (itemPrivate->hasPointerHandlers()) {
+ QQuickPointerEvent *pointerEvent = pointerEventInstance(QQuickPointerDevice::genericMouseDevice(), QEvent::MouseMove);
+ pointerEvent->point(0)->reset(Qt::TouchPointMoved, scenePos, quint64(1) << 24 /* mouse has device ID 1 */, timestamp, QVector2D());
+ pointerEvent->point(0)->setAccepted(true);
+ pointerEvent->localize(item);
+ for (QQuickPointerHandler *h : itemPrivate->extra->pointerHandlers)
+ if (QQuickHoverHandler *hh = qmlobject_cast<QQuickHoverHandler *>(h))
+ hh->handlePointerEvent(pointerEvent);
+ }
+
if (itemPrivate->hoverEnabled) {
QPointF p = item->mapFromScene(scenePos);
if (item->contains(p)) {
@@ -1886,43 +1925,57 @@ bool QQuickWindowPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &sce
return false;
}
-#if QT_CONFIG(wheelevent)
-bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event)
+// Simple delivery of non-mouse, non-touch Pointer Events: visit the items and handlers
+// in the usual reverse-paint-order until propagation is stopped
+bool QQuickWindowPrivate::deliverSinglePointEventUntilAccepted(QQuickPointerEvent *event)
{
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
-
- if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
- QPointF p = item->mapFromScene(event->posF());
- if (!item->contains(p))
- return false;
- }
+ Q_ASSERT(event->pointCount() == 1);
+ QQuickEventPoint *point = event->point(0);
+ QVector<QQuickItem *> targetItems = pointerTargets(contentItem, point, false, false);
- QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
- for (int ii = children.count() - 1; ii >= 0; --ii) {
- QQuickItem *child = children.at(ii);
- if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
- continue;
- if (deliverWheelEvent(child, event))
- return true;
- }
-
- QPointF p = item->mapFromScene(event->posF());
-
- if (item->contains(p)) {
- QWheelEvent wheel(p, event->globalPosF(), event->pixelDelta(), event->angleDelta(), event->delta(),
- event->orientation(), event->buttons(), event->modifiers(), event->phase(), event->source(), event->inverted());
- wheel.setTimestamp(event->timestamp());
- wheel.accept();
- QCoreApplication::sendEvent(item, &wheel);
- if (wheel.isAccepted()) {
- event->accept();
+ for (QQuickItem *item : targetItems) {
+ QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
+ event->localize(item);
+ // Let Pointer Handlers have the first shot
+ itemPrivate->handlePointerEvent(event);
+ if (point->isAccepted())
return true;
+ QPointF g = item->window()->mapToGlobal(point->scenePosition().toPoint());
+#if QT_CONFIG(wheelevent)
+ // Let the Item have a chance to handle it
+ if (QQuickPointerScrollEvent *pse = event->asPointerScrollEvent()) {
+ QWheelEvent wheel(point->position(), g, pse->pixelDelta().toPoint(), pse->angleDelta().toPoint(),
+ pse->buttons(), pse->modifiers(), pse->phase(),
+ pse->isInverted(), pse->synthSource());
+ wheel.setTimestamp(pse->timestamp());
+ wheel.accept();
+ QCoreApplication::sendEvent(item, &wheel);
+ if (wheel.isAccepted()) {
+ qCDebug(lcWheelTarget) << &wheel << "->" << item;
+ event->setAccepted(true);
+ return true;
+ }
}
+#endif
+#if QT_CONFIG(gestures)
+ if (QQuickPointerNativeGestureEvent *pnge = event->asPointerNativeGestureEvent()) {
+ QNativeGestureEvent nge(pnge->type(), pnge->device()->qTouchDevice(), point->position(), point->scenePosition(), g,
+ pnge->value(), 0L, 0L); // TODO can't copy things I can't access
+ nge.accept();
+ QCoreApplication::sendEvent(item, &nge);
+ if (nge.isAccepted()) {
+ qCDebug(lcGestureTarget) << &nge << "->" << item;
+ event->setAccepted(true);
+ return true;
+ }
+ }
+#endif // gestures
}
- return false;
+ return false; // it wasn't handled
}
+#if QT_CONFIG(wheelevent)
/*! \reimp */
void QQuickWindow::wheelEvent(QWheelEvent *event)
{
@@ -1937,55 +1990,11 @@ void QQuickWindow::wheelEvent(QWheelEvent *event)
return;
event->ignore();
- d->deliverWheelEvent(d->contentItem, event);
+ d->deliverPointerEvent(d->pointerEventInstance(event));
d->lastWheelEventAccepted = event->isAccepted();
}
#endif // wheelevent
-#if QT_CONFIG(gestures)
-bool QQuickWindowPrivate::deliverNativeGestureEvent(QQuickItem *item, QNativeGestureEvent *event)
-{
- QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
-
- QPointF p = item->mapFromScene(event->windowPos());
- if ((itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) && !item->contains(p))
- return false;
-
- QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
- for (int ii = children.count() - 1; ii >= 0; --ii) {
- QQuickItem *child = children.at(ii);
- if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(child)->culled)
- continue;
- if (deliverNativeGestureEvent(child, event))
- return true;
- }
-
- // Try the Item's pointer handlers first
- QQuickPointerEvent *pointerEvent = pointerEventInstance(event);
- pointerEvent->localize(item);
- if (itemPrivate->handlePointerEvent(pointerEvent, false)) {
- if (pointerEvent->allPointsAccepted()) {
- event->accept();
- return true;
- }
- }
-
- // If still not accepted, try direct delivery to the item
- if (item->contains(p)) {
- QNativeGestureEvent copy(event->gestureType(), event->device(), p, event->windowPos(), event->screenPos(),
- event->value(), 0L, 0L); // TODO can't copy things I can't access
- event->accept();
- item->event(&copy);
- if (copy.isAccepted()) {
- event->accept();
- return true;
- }
- }
-
- return false;
-}
-#endif // gestures
-
bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
{
qCDebug(DBG_TOUCH) << event;
@@ -2229,6 +2238,8 @@ QQuickPointerEvent *QQuickWindowPrivate::queryPointerEventInstance(QQuickPointer
if (eventType == QEvent::NativeGesture && !qobject_cast<QQuickPointerNativeGestureEvent*>(e))
continue;
#endif
+ if (eventType == QEvent::Wheel && !qobject_cast<QQuickPointerScrollEvent*>(e))
+ continue;
// Otherwise we assume there's only one event type per device.
// More disambiguation tests might need to be added above if that changes later.
if (e->device() == device)
@@ -2248,7 +2259,10 @@ QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QQuickPointerDevic
// QWindowSystemInterface::handleMouseEvent() does not take a device parameter:
// we assume all mouse events come from one mouse (the "core pointer").
// So when the event is a mouse event, device == QQuickPointerDevice::genericMouseDevice()
- ev = new QQuickPointerMouseEvent(q, device);
+ if (eventType == QEvent::Wheel)
+ ev = new QQuickPointerScrollEvent(q, device);
+ else
+ ev = new QQuickPointerMouseEvent(q, device);
break;
case QQuickPointerDevice::TouchPad:
case QQuickPointerDevice::TouchScreen:
@@ -2282,6 +2296,7 @@ QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QEvent *event) con
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
+ case QEvent::Wheel:
dev = QQuickPointerDevice::genericMouseDevice();
break;
case QEvent::TouchBegin:
@@ -2324,7 +2339,7 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event)
} else if (event->asPointerTouchEvent()) {
deliverTouchEvent(event->asPointerTouchEvent());
} else {
- Q_ASSERT(false);
+ deliverSinglePointEventUntilAccepted(event);
}
event->reset(nullptr);
@@ -2332,16 +2347,16 @@ void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event)
--pointerEventRecursionGuard;
}
-// check if item or any of its child items contain the point
+// check if item or any of its child items contain the point, or if any pointer handler "wants" the point
// FIXME: should this be iterative instead of recursive?
// If checkMouseButtons is true, it means we are finding targets for a mouse event, so no item for which acceptedMouseButtons() is NoButton will be added.
// If checkAcceptsTouch is true, it means we are finding targets for a touch event, so either acceptTouchEvents() must return true OR
// it must accept a synth. mouse event, thus if acceptTouchEvents() returns false but acceptedMouseButtons() is true, gets added; if not, it doesn't.
-QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, const QPointF &scenePos, bool checkMouseButtons, bool checkAcceptsTouch) const
+QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, QQuickEventPoint *point, bool checkMouseButtons, bool checkAcceptsTouch) const
{
QVector<QQuickItem *> targets;
auto itemPrivate = QQuickItemPrivate::get(item);
- QPointF itemPos = item->mapFromScene(scenePos);
+ QPointF itemPos = item->mapFromScene(point->scenePosition());
// if the item clips, we can potentially return early
if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
if (!item->contains(itemPos))
@@ -2355,11 +2370,15 @@ QVector<QQuickItem *> QQuickWindowPrivate::pointerTargets(QQuickItem *item, cons
auto childPrivate = QQuickItemPrivate::get(child);
if (!child->isVisible() || !child->isEnabled() || childPrivate->culled)
continue;
- targets << pointerTargets(child, scenePos, checkMouseButtons, checkAcceptsTouch);
+ targets << pointerTargets(child, point, checkMouseButtons, checkAcceptsTouch);
}
bool relevant = item->contains(itemPos);
- if (!(itemPrivate->hasPointerHandlers())) {
+ if (itemPrivate->hasPointerHandlers()) {
+ if (!relevant)
+ if (itemPrivate->anyPointerHandlerWants(point))
+ relevant = true;
+ } else {
if (relevant && checkMouseButtons && item->acceptedMouseButtons() == Qt::NoButton)
relevant = false;
if (relevant && checkAcceptsTouch && !(item->acceptTouchEvents() || item->acceptedMouseButtons()))
@@ -2440,6 +2459,7 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve
// The grabber is not an item? It's a handler then. Let it have the event first.
QQuickPointerHandler *handler = static_cast<QQuickPointerHandler *>(grabber);
receiver = static_cast<QQuickPointerHandler *>(grabber)->parentItem();
+ hasFiltered.clear();
if (sendFilteredPointerEvent(event, receiver))
done = true;
event->localize(receiver);
@@ -2469,7 +2489,7 @@ void QQuickWindowPrivate::deliverUpdatedTouchPoints(QQuickPointerTouchEvent *eve
QQuickEventPoint *point = event->point(i);
if (point->state() == QQuickEventPoint::Pressed)
continue; // presses were delivered earlier; not the responsibility of deliverUpdatedTouchPoints
- QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePosition(), false, false);
+ QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point, false, false);
if (targetItems.count()) {
targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
} else {
@@ -2494,12 +2514,20 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event,
int pointCount = event->pointCount();
QVector<QQuickItem *> targetItems;
bool isTouchEvent = (event->asPointerTouchEvent() != nullptr);
+ if (isTouchEvent && event->isPressEvent()) {
+ // When a second point is pressed, we're starting over with delivery, so
+ // don't let prior conception of which one is acting as a mouse interfere
+ touchMouseId = -1;
+ touchMouseDevice = nullptr;
+ }
for (int i = 0; i < pointCount; ++i) {
auto point = event->point(i);
+ if (point->state() == QQuickEventPoint::Pressed && !event->isDoubleClickEvent())
+ point->clearPassiveGrabbers();
point->setAccepted(false); // because otherwise touchEventForItem will ignore it
if (point->grabberPointerHandler() && point->state() == QQuickEventPoint::Released)
point->setGrabberPointerHandler(nullptr, true);
- QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point->scenePosition(), !isTouchEvent, isTouchEvent);
+ QVector<QQuickItem *> targetItemsForPoint = pointerTargets(contentItem, point, !isTouchEvent, isTouchEvent);
if (targetItems.count()) {
targetItems = mergePointerTargets(targetItems, targetItemsForPoint);
} else {
@@ -2508,6 +2536,7 @@ bool QQuickWindowPrivate::deliverPressOrReleaseEvent(QQuickPointerEvent *event,
}
for (QQuickItem *item : targetItems) {
+ hasFiltered.clear();
if (!handlersOnly && sendFilteredPointerEvent(event, item)) {
if (event->isAccepted()) {
for (int i = 0; i < event->pointCount(); ++i)
@@ -2559,7 +2588,6 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
// synthetic events - flickable sends one when setPressDelay is used.
auto oldMouseGrabber = q->mouseGrabberItem();
QPointF localPos = item->mapFromScene(point->scenePosition());
- Q_ASSERT(item->contains(localPos)); // transform is checked already
QMouseEvent *me = event->asMouseEvent(localPos);
me->accept();
QCoreApplication::sendEvent(item, me);
@@ -2587,6 +2615,7 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
bool eventAccepted = false;
// If any parent filters the event, we're done.
+ hasFiltered.clear();
if (sendFilteredPointerEvent(pointerEvent, item))
return;
@@ -2596,10 +2625,12 @@ void QQuickWindowPrivate::deliverMatchingPointsToItem(QQuickItem *item, QQuickPo
eventAccepted = touchEvent->isAccepted();
// If the touch event wasn't accepted, synthesize a mouse event and see if the item wants it.
- if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
- // send mouse event
- if (deliverTouchAsMouse(item, ptEvent))
- eventAccepted = true;
+ if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) {
+ if (!eventAccepted && (itemPrivate->acceptedMouseButtons() & Qt::LeftButton)) {
+ // send mouse event
+ if (deliverTouchAsMouse(item, ptEvent))
+ eventAccepted = true;
+ }
}
if (eventAccepted) {
@@ -2803,7 +2834,6 @@ QQuickItem *QQuickWindowPrivate::findCursorItem(QQuickItem *item, const QPointF
bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent)
{
- hasFiltered.clear();
return sendFilteredPointerEventImpl(event, receiver, filteringParent ? filteringParent : receiver->parentItem());
}
@@ -2864,7 +2894,7 @@ bool QQuickWindowPrivate::sendFilteredPointerEventImpl(QQuickPointerEvent *event
pt->setGrabberItem(filteringParent);
}
return true;
- } else {
+ } else if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents))) {
// filteringParent didn't filter the touch event. Give it a chance to filter a synthetic mouse event.
for (int i = 0; i < filteringParentTouchEvent->touchPoints().size(); ++i) {
const QTouchEvent::TouchPoint &tp = filteringParentTouchEvent->touchPoints().at(i);
@@ -2963,6 +2993,12 @@ bool QQuickWindowPrivate::dragOverThreshold(qreal d, Qt::Axis axis, QMouseEvent
return overThreshold;
}
+bool QQuickWindowPrivate::dragOverThreshold(QVector2D delta)
+{
+ int threshold = qApp->styleHints()->startDragDistance();
+ return qAbs(delta.x()) > threshold || qAbs(delta.y()) > threshold;
+}
+
/*!
\qmlproperty list<Object> Window::data
\default
diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h
index 817178fdac..79e8a11aa8 100644
--- a/src/quick/items/qquickwindow.h
+++ b/src/quick/items/qquickwindow.h
@@ -95,6 +95,7 @@ public:
};
Q_DECLARE_FLAGS(CreateTextureOptions, CreateTextureOption)
+ Q_FLAG(CreateTextureOptions)
enum SceneGraphError {
ContextNotAvailable = 1
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index b5e3a2c1eb..10c8477417 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -152,12 +152,7 @@ public:
bool sendFilteredMouseEvent(QEvent *event, QQuickItem *receiver, QQuickItem *filteringParent);
bool sendFilteredPointerEvent(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent = nullptr);
bool sendFilteredPointerEventImpl(QQuickPointerEvent *event, QQuickItem *receiver, QQuickItem *filteringParent);
-#if QT_CONFIG(wheelevent)
- bool deliverWheelEvent(QQuickItem *, QWheelEvent *);
-#endif
-#if QT_CONFIG(gestures)
- bool deliverNativeGestureEvent(QQuickItem *, QNativeGestureEvent *);
-#endif
+ bool deliverSinglePointEventUntilAccepted(QQuickPointerEvent *);
// entry point of events to the window
void handleTouchEvent(QTouchEvent *);
@@ -180,7 +175,7 @@ public:
void deliverUpdatedTouchPoints(QQuickPointerTouchEvent *event);
void deliverMatchingPointsToItem(QQuickItem *item, QQuickPointerEvent *pointerEvent, bool handlersOnly = false);
- QVector<QQuickItem *> pointerTargets(QQuickItem *, const QPointF &, bool checkMouseButtons, bool checkAcceptsTouch) const;
+ QVector<QQuickItem *> pointerTargets(QQuickItem *, QQuickEventPoint *point, bool checkMouseButtons, bool checkAcceptsTouch) const;
QVector<QQuickItem *> mergePointerTargets(const QVector<QQuickItem *> &list1, const QVector<QQuickItem *> &list2) const;
// hover delivery
@@ -307,6 +302,7 @@ public:
QQuickWindowPrivate::dragOverThreshold(delta.y(), Qt::YAxis, point));
}
+ static bool dragOverThreshold(QVector2D delta);
// data property
static void data_append(QQmlListProperty<QObject> *, QObject *);
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
index d715d900ba..2e5fdbbe6b 100644
--- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
@@ -77,6 +77,11 @@ QSGSoftwareRenderableNode *QSGAbstractSoftwareRenderer::renderableNode(QSGNode *
return m_nodes.value(node, nullptr);
}
+const QLinkedList<QSGSoftwareRenderableNode*> &QSGAbstractSoftwareRenderer::renderableNodes() const
+{
+ return m_renderableNodes;
+}
+
void QSGAbstractSoftwareRenderer::addNodeMapping(QSGNode *node, QSGSoftwareRenderableNode *renderableNode)
{
m_nodes.insert(node, renderableNode);
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
index f6594d931a..99204ef25e 100644
--- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer_p.h
@@ -88,6 +88,7 @@ protected:
QRect backgroundRect();
// only known after calling optimizeRenderList()
bool isOpaque() const { return m_isOpaque; }
+ const QLinkedList<QSGSoftwareRenderableNode*> &renderableNodes() const;
private:
void nodeAdded(QSGNode *node);
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp
index a8b5944974..c33144344f 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareadaptation.cpp
@@ -74,6 +74,7 @@ QSGContextFactoryInterface::Flags QSGSoftwareAdaptation::flags(const QString &)
QSGRenderLoop *QSGSoftwareAdaptation::createWindowManager()
{
+#if QT_CONFIG(thread)
static bool threaded = false;
static bool envChecked = false;
if (!envChecked) {
@@ -83,6 +84,7 @@ QSGRenderLoop *QSGSoftwareAdaptation::createWindowManager()
if (threaded)
return new QSGSoftwareThreadedRenderLoop;
+#endif
return new QSGSoftwareRenderLoop();
}
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp
index 21f20c66cd..dd789b78c7 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareglyphnode.cpp
@@ -49,12 +49,40 @@ QSGSoftwareGlyphNode::QSGSoftwareGlyphNode()
setGeometry(&m_geometry);
}
+namespace {
+QRectF calculateBoundingRect(const QPointF &position, const QGlyphRun &glyphs)
+{
+ qreal minX = 0;
+ qreal minY = 0;
+ qreal maxX = 0;
+ qreal maxY = 0;
+
+ for (int i = 0, n = qMin(glyphs.glyphIndexes().size(), glyphs.positions().size()); i < n; ++i) {
+ QRectF glyphRect = glyphs.rawFont().boundingRect(glyphs.glyphIndexes()[i]);
+ glyphRect.translate(glyphs.positions()[i]);
+
+ if (i == 0) {
+ minX = glyphRect.left();
+ minY = glyphRect.top();
+ maxX = glyphRect.right();
+ maxY = glyphRect.bottom();
+ } else {
+ minX = qMin(glyphRect.left(), minX);
+ minY = qMin(glyphRect.top(), minY);
+ maxX = qMax(glyphRect.right(),maxX);
+ maxY = qMax(glyphRect.bottom(), maxY);
+ }
+ }
+ QRectF boundingRect(QPointF(minX, minY), QPointF(maxX, maxY));
+ return boundingRect.translated(position - QPointF(0.0, glyphs.rawFont().ascent()));
+}
+}
void QSGSoftwareGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
{
m_position = position;
m_glyphRun = glyphs;
- m_bounding_rect = glyphs.boundingRect().translated(m_position - QPointF(0.0, glyphs.rawFont().ascent()));
+ m_bounding_rect = calculateBoundingRect(position, glyphs);
}
void QSGSoftwareGlyphNode::setColor(const QColor &color)
@@ -91,8 +119,8 @@ void QSGSoftwareGlyphNode::paint(QPainter *painter)
QPointF pos = m_position - QPointF(0, m_glyphRun.rawFont().ascent());
qreal offset = 1.0;
- if (painter->device()->devicePixelRatio() != 0)
- offset = 1.0 / painter->device()->devicePixelRatio();
+ if (painter->device()->devicePixelRatioF() > 0.0)
+ offset = 1.0 / painter->device()->devicePixelRatioF();
switch (m_style) {
case QQuickText::Normal: break;
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp
index bf3141bc32..2c361e03e2 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp
@@ -47,6 +47,7 @@ QT_BEGIN_NAMESPACE
QSGSoftwareInternalRectangleNode::QSGSoftwareInternalRectangleNode()
: m_penWidth(0)
, m_radius(0)
+ , m_vertical(true)
, m_cornerPixmapIsDirty(true)
, m_devicePixelRatio(1)
{
@@ -186,6 +187,15 @@ void QSGSoftwareInternalRectangleNode::setGradientStops(const QGradientStops &st
markDirty(DirtyMaterial);
}
+void QSGSoftwareInternalRectangleNode::setGradientVertical(bool vertical)
+{
+ if (m_vertical != vertical) {
+ m_vertical = vertical;
+ m_cornerPixmapIsDirty = true;
+ markDirty(DirtyMaterial);
+ }
+}
+
void QSGSoftwareInternalRectangleNode::setRadius(qreal radius)
{
if (m_radius != radius) {
@@ -209,7 +219,7 @@ void QSGSoftwareInternalRectangleNode::update()
}
if (!m_stops.isEmpty()) {
- QLinearGradient gradient(QPoint(0,0), QPoint(0,1));
+ QLinearGradient gradient(QPoint(0,0), QPoint(m_vertical ? 0 : 1, m_vertical ? 1 : 0));
gradient.setStops(m_stops);
gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
m_brush = QBrush(gradient);
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h
index 1f87424d2a..125520de26 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode_p.h
@@ -69,6 +69,7 @@ public:
void setPenColor(const QColor &color) override;
void setPenWidth(qreal width) override;
void setGradientStops(const QGradientStops &stops) override;
+ void setGradientVertical(bool vertical) override;
void setRadius(qreal radius) override;
void setAntialiasing(bool antialiasing) override { Q_UNUSED(antialiasing) }
void setAligned(bool aligned) override;
@@ -91,6 +92,7 @@ private:
double m_radius;
QPen m_pen;
QBrush m_brush;
+ bool m_vertical;
bool m_cornerPixmapIsDirty;
QPixmap m_cornerPixmap;
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
index 1463681fa3..471624d3f8 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qsgsoftwarepublicnodes_p.h"
+#include "qsgsoftwarelayer_p.h"
#include "qsgsoftwarepixmaptexture_p.h"
#include "qsgsoftwareinternalimagenode_p.h"
@@ -104,6 +105,9 @@ void QSGSoftwareImageNode::paint(QPainter *painter)
} else if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture *>(m_texture)) {
const QPixmap &pm = pt->pixmap();
painter->drawPixmap(m_rect, pm, m_sourceRect);
+ } else if (QSGSoftwareLayer *pt = qobject_cast<QSGSoftwareLayer *>(m_texture)) {
+ const QPixmap &pm = pt->pixmap();
+ painter->drawPixmap(m_rect, pm, m_sourceRect);
} else if (QSGPlainTexture *pt = qobject_cast<QSGPlainTexture *>(m_texture)) {
const QImage &im = pt->image();
painter->drawImage(m_rect, im, m_sourceRect);
@@ -115,7 +119,6 @@ void QSGSoftwareImageNode::updateCachedMirroredPixmap()
if (m_transformMode == NoTransform) {
m_cachedPixmap = QPixmap();
} else {
-
if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture *>(m_texture)) {
QTransform mirrorTransform;
if (m_transformMode.testFlag(MirrorVertically))
@@ -123,6 +126,13 @@ void QSGSoftwareImageNode::updateCachedMirroredPixmap()
if (m_transformMode.testFlag(MirrorHorizontally))
mirrorTransform = mirrorTransform.scale(-1, 1);
m_cachedPixmap = pt->pixmap().transformed(mirrorTransform);
+ } else if (QSGSoftwareLayer *pt = qobject_cast<QSGSoftwareLayer *>(m_texture)) {
+ QTransform mirrorTransform;
+ if (m_transformMode.testFlag(MirrorVertically))
+ mirrorTransform = mirrorTransform.scale(1, -1);
+ if (m_transformMode.testFlag(MirrorHorizontally))
+ mirrorTransform = mirrorTransform.scale(-1, 1);
+ m_cachedPixmap = pt->pixmap().transformed(mirrorTransform);
} else if (QSGPlainTexture *pt = qobject_cast<QSGPlainTexture *>(m_texture)) {
m_cachedPixmap = QPixmap::fromImage(pt->image().mirrored(m_transformMode.testFlag(MirrorHorizontally), m_transformMode.testFlag(MirrorVertically)));
} else {
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
index 8fc87db179..b20d0a1828 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
@@ -72,7 +72,7 @@ class QSGSoftwareNinePatchNode;
class QSGSoftwareSpriteNode;
class QSGRenderNode;
-class QSGSoftwareRenderableNode
+class Q_QUICK_PRIVATE_EXPORT QSGSoftwareRenderableNode
{
public:
enum NodeType {
@@ -104,6 +104,7 @@ public:
bool isOpaque() const { return m_isOpaque; }
bool isDirty() const { return m_isDirty; }
bool isDirtyRegionEmpty() const;
+ QSGNode *handle() const { return m_handle.node; }
void setTransform(const QTransform &transform);
void setClipRegion(const QRegion &clipRegion, bool hasClipRegion = true);
@@ -123,6 +124,7 @@ public:
private:
union RenderableNodeHandle {
+ QSGNode *node;
QSGSimpleRectNode *simpleRectNode;
QSGSimpleTextureNode *simpleTextureNode;
QSGSoftwareInternalImageNode *imageNode;
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
index ffcee5f56e..e9ed52d428 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp
@@ -113,8 +113,8 @@ void QSGSoftwareRenderer::render()
setBackgroundColor(clearColor());
setBackgroundRect(QRect(0, 0,
- m_paintDevice->width() / m_paintDevice->devicePixelRatio(),
- m_paintDevice->height() / m_paintDevice->devicePixelRatio()));
+ m_paintDevice->width() / m_paintDevice->devicePixelRatioF(),
+ m_paintDevice->height() / m_paintDevice->devicePixelRatioF()));
// Build Renderlist
// The renderlist is created by visiting each node in the tree and when a
diff --git a/src/quick/scenegraph/adaptations/software/software.pri b/src/quick/scenegraph/adaptations/software/software.pri
index de5f01cdee..278dbe7944 100644
--- a/src/quick/scenegraph/adaptations/software/software.pri
+++ b/src/quick/scenegraph/adaptations/software/software.pri
@@ -18,8 +18,7 @@ SOURCES += \
$$PWD/qsgsoftwarerenderlistbuilder.cpp \
$$PWD/qsgsoftwarerenderloop.cpp \
$$PWD/qsgsoftwarelayer.cpp \
- $$PWD/qsgsoftwareadaptation.cpp \
- $$PWD/qsgsoftwarethreadedrenderloop.cpp
+ $$PWD/qsgsoftwareadaptation.cpp
HEADERS += \
$$PWD/qsgsoftwarecontext_p.h \
@@ -37,8 +36,7 @@ HEADERS += \
$$PWD/qsgsoftwarerenderlistbuilder_p.h \
$$PWD/qsgsoftwarerenderloop_p.h \
$$PWD/qsgsoftwarelayer_p.h \
- $$PWD/qsgsoftwareadaptation_p.h \
- $$PWD/qsgsoftwarethreadedrenderloop_p.h
+ $$PWD/qsgsoftwareadaptation_p.h
qtConfig(quick-sprite) {
SOURCES += \
@@ -46,3 +44,10 @@ qtConfig(quick-sprite) {
HEADERS += \
$$PWD/qsgsoftwarespritenode_p.h
}
+
+qtConfig(thread) {
+ SOURCES += \
+ $$PWD/qsgsoftwarethreadedrenderloop.cpp
+ HEADERS +=\
+ $$PWD/qsgsoftwarethreadedrenderloop_p.h
+}
diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp
index e868a4380e..796bc870de 100644
--- a/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp
+++ b/src/quick/scenegraph/compressedtexture/qsgcompressedatlastexture.cpp
@@ -56,7 +56,6 @@
#include <private/qquickprofiler_p.h>
#include <private/qsgtexture_p.h>
#include <private/qsgcompressedtexture_p.h>
-#include <private/qsgpkmhandler_p.h>
QT_BEGIN_NAMESPACE
@@ -154,13 +153,12 @@ QSGTexture *Texture::removedFromAtlas() const
}
if (!m_data.isEmpty()) {
- QSGCompressedTexture::DataPtr texData(QSGCompressedTexture::DataPtr::create());
- texData->data = m_data;
- texData->size = m_size;
- texData->format = static_cast<Atlas*>(m_atlas)->format();
- texData->hasAlpha = hasAlphaChannel();
- texData->dataLength = m_dataLength;
- texData->dataOffset = m_dataOffset;
+ QTextureFileData texData;
+ texData.setData(m_data);
+ texData.setSize(m_size);
+ texData.setGLInternalFormat(static_cast<Atlas*>(m_atlas)->format());
+ texData.setDataLength(m_dataLength);
+ texData.setDataOffset(m_dataOffset);
m_nonatlas_texture = new QSGCompressedTexture(texData);
m_nonatlas_texture->setMipmapFiltering(mipmapFiltering());
m_nonatlas_texture->setFiltering(filtering());
diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp
index 839c562989..d3310bc11c 100644
--- a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp
+++ b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture.cpp
@@ -49,50 +49,11 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(QSG_LOG_TEXTUREIO, "qt.scenegraph.textureio");
-bool QSGCompressedTextureData::isValid() const
-{
- if (data.isNull() || size.isEmpty() || !format)
- return false;
- if (dataLength < 0 || dataOffset < 0 || dataOffset >= data.length())
- return false;
- if (dataLength > 0 && qint64(dataOffset) + qint64(dataLength) > qint64(data.length()))
- return false;
-
- return true;
-}
-
-int QSGCompressedTextureData::sizeInBytes() const
-{
- if (!isValid())
- return 0;
- return dataLength > 0 ? dataLength : data.length() - dataOffset;
-}
-
-Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QSGCompressedTextureData *d)
-{
- QDebugStateSaver saver(dbg);
-
- dbg.nospace() << "QSGCompressedTextureData(";
- if (d) {
- dbg << d->logName << ' ';
- dbg << static_cast<QOpenGLTexture::TextureFormat>(d->format)
- << "[0x" << hex << d->format << dec << "]";
- dbg.space() << (d->hasAlpha ? "with" : "no") << "alpha" << d->size
- << "databuffer" << d->data.size() << "offset" << d->dataOffset << "length";
- dbg.nospace() << d->dataLength << ")";
- } else {
- dbg << "null)";
- }
- return dbg;
-}
-
-QSGCompressedTexture::QSGCompressedTexture(const DataPtr& texData)
+QSGCompressedTexture::QSGCompressedTexture(const QTextureFileData &texData)
: m_textureData(texData)
{
- if (m_textureData) {
- m_size = m_textureData->size;
- m_hasAlpha = m_textureData->hasAlpha;
- }
+ m_size = m_textureData.size();
+ m_hasAlpha = !formatIsOpaque(m_textureData.glInternalFormat());
}
QSGCompressedTexture::~QSGCompressedTexture()
@@ -155,37 +116,40 @@ void QSGCompressedTexture::bind()
if (m_uploaded)
return;
- QByteArray logName(m_textureData ? m_textureData->logName : QByteArrayLiteral("(unset)"));
-
- if (!m_textureData || !m_textureData->isValid()) {
- qCDebug(QSG_LOG_TEXTUREIO, "Invalid texture data for %s", logName.constData());
+ if (!m_textureData.isValid()) {
+ qCDebug(QSG_LOG_TEXTUREIO, "Invalid texture data for %s", m_textureData.logName().constData());
funcs->glBindTexture(GL_TEXTURE_2D, 0);
return;
}
if (Q_UNLIKELY(QSG_LOG_TEXTUREIO().isDebugEnabled())) {
- qCDebug(QSG_LOG_TEXTUREIO) << "Uploading texture" << m_textureData.data();
+ qCDebug(QSG_LOG_TEXTUREIO) << "Uploading texture" << m_textureData;
while (funcs->glGetError() != GL_NO_ERROR);
}
- funcs->glCompressedTexImage2D(GL_TEXTURE_2D, 0, m_textureData->format,
- m_size.width(), m_size.height(), 0, m_textureData->sizeInBytes(),
- m_textureData->data.constData() + m_textureData->dataOffset);
+ funcs->glCompressedTexImage2D(GL_TEXTURE_2D, 0, m_textureData.glInternalFormat(),
+ m_size.width(), m_size.height(), 0, m_textureData.dataLength(),
+ m_textureData.data().constData() + m_textureData.dataOffset());
if (Q_UNLIKELY(QSG_LOG_TEXTUREIO().isDebugEnabled())) {
GLuint error = funcs->glGetError();
if (error != GL_NO_ERROR) {
- qCDebug(QSG_LOG_TEXTUREIO, "glCompressedTexImage2D failed for %s, error 0x%x", logName.constData(), error);
+ qCDebug(QSG_LOG_TEXTUREIO, "glCompressedTexImage2D failed for %s, error 0x%x", m_textureData.logName().constData(), error);
}
}
- m_textureData.clear(); // Release this memory, not needed anymore
+ m_textureData = QTextureFileData(); // Release this memory, not needed anymore
updateBindOptions(true);
m_uploaded = true;
#endif // QT_CONFIG(opengl)
}
+QTextureFileData QSGCompressedTexture::textureData() const
+{
+ return m_textureData;
+}
+
bool QSGCompressedTexture::formatIsOpaque(quint32 glTextureFormat)
{
switch (glTextureFormat) {
@@ -211,14 +175,14 @@ bool QSGCompressedTexture::formatIsOpaque(quint32 glTextureFormat)
}
}
-QSGCompressedTextureFactory::QSGCompressedTextureFactory(const QSGCompressedTexture::DataPtr &texData)
+QSGCompressedTextureFactory::QSGCompressedTextureFactory(const QTextureFileData &texData)
: m_textureData(texData)
{
}
QSGTexture *QSGCompressedTextureFactory::createTexture(QQuickWindow *window) const
{
- if (!m_textureData || !m_textureData->isValid())
+ if (!m_textureData.isValid())
return nullptr;
// attempt to atlas the texture
@@ -232,15 +196,12 @@ QSGTexture *QSGCompressedTextureFactory::createTexture(QQuickWindow *window) con
int QSGCompressedTextureFactory::textureByteCount() const
{
- return m_textureData ? m_textureData->sizeInBytes() : 0;
+ return qMax(0, m_textureData.data().size() - m_textureData.dataOffset());
}
-
QSize QSGCompressedTextureFactory::textureSize() const
{
- if (m_textureData && m_textureData->isValid())
- return m_textureData->size;
- return QSize();
+ return m_textureData.size();
}
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h
index aa87316809..c3b58a2389 100644
--- a/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h
+++ b/src/quick/scenegraph/compressedtexture/qsgcompressedtexture_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <private/qtexturefiledata_p.h>
#include <QSGTexture>
#include <QtQuick/private/qsgcontext_p.h>
#include <QQuickTextureFactory>
@@ -58,30 +59,11 @@
QT_BEGIN_NAMESPACE
-struct Q_QUICK_PRIVATE_EXPORT QSGCompressedTextureData
-{
- QByteArray logName;
- QByteArray data;
- QSize size;
- uint format = 0;
- int dataOffset = 0;
- int dataLength = 0;
- bool hasAlpha = false;
-
- bool isValid() const;
- int sizeInBytes() const;
-};
-
-Q_QUICK_PRIVATE_EXPORT QDebug operator<<(QDebug, const QSGCompressedTextureData *);
-
-
class Q_QUICK_PRIVATE_EXPORT QSGCompressedTexture : public QSGTexture
{
Q_OBJECT
public:
- typedef QSharedPointer<QSGCompressedTextureData> DataPtr;
-
- QSGCompressedTexture(const DataPtr& texData);
+ QSGCompressedTexture(const QTextureFileData& texData);
virtual ~QSGCompressedTexture();
int textureId() const override;
@@ -91,12 +73,12 @@ public:
void bind() override;
- const QSGCompressedTextureData *textureData();
+ QTextureFileData textureData() const;
static bool formatIsOpaque(quint32 glTextureFormat);
protected:
- DataPtr m_textureData;
+ QTextureFileData m_textureData;
QSize m_size;
mutable uint m_textureId = 0;
bool m_hasAlpha = false;
@@ -110,13 +92,14 @@ namespace QSGAtlasTexture {
class Q_QUICK_PRIVATE_EXPORT QSGCompressedTextureFactory : public QQuickTextureFactory
{
public:
- QSGCompressedTextureFactory(const QSGCompressedTexture::DataPtr& texData);
+ QSGCompressedTextureFactory(const QTextureFileData& texData);
QSGTexture *createTexture(QQuickWindow *) const override;
int textureByteCount() const override;
QSize textureSize() const override;
protected:
- QSGCompressedTexture::DataPtr m_textureData;
+ QTextureFileData m_textureData;
+
private:
friend class QSGAtlasTexture::Manager;
};
diff --git a/src/quick/scenegraph/compressedtexture/qsgktxhandler.cpp b/src/quick/scenegraph/compressedtexture/qsgktxhandler.cpp
deleted file mode 100644
index e3e4ca6824..0000000000
--- a/src/quick/scenegraph/compressedtexture/qsgktxhandler.cpp
+++ /dev/null
@@ -1,186 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQuick module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsgktxhandler_p.h"
-#include "qsgcompressedtexture_p.h"
-#include <QOpenGLTexture>
-#include <QtEndian>
-
-//#define KTX_DEBUG
-
-QT_BEGIN_NAMESPACE
-
-#define KTX_IDENTIFIER_LENGTH 12
-static const char ktxIdentifier[KTX_IDENTIFIER_LENGTH] = { '\xAB', 'K', 'T', 'X', ' ', '1', '1', '\xBB', '\r', '\n', '\x1A', '\n' };
-static const quint32 platformEndianIdentifier = 0x04030201;
-static const quint32 inversePlatformEndianIdentifier = 0x01020304;
-
-struct KTXHeader {
- quint8 identifier[KTX_IDENTIFIER_LENGTH]; //Must match ktxIdentifier
- quint32 endianness; //Either platformEndianIdentifier or inversePlatformEndianIdentifier, other values not allowed.
- quint32 glType;
- quint32 glTypeSize;
- quint32 glFormat;
- quint32 glInternalFormat;
- quint32 glBaseInternalFormat;
- quint32 pixelWidth;
- quint32 pixelHeight;
- quint32 pixelDepth;
- quint32 numberOfArrayElements;
- quint32 numberOfFaces;
- quint32 numberOfMipmapLevels;
- quint32 bytesOfKeyValueData;
-};
-
-static const int headerSize = sizeof(KTXHeader);
-
-// Currently unused, declared for future reference
-struct KTXKeyValuePairItem {
- quint32 keyAndValueByteSize;
- /*
- quint8 keyAndValue[keyAndValueByteSize];
- quint8 valuePadding[3 - ((keyAndValueByteSize + 3) % 4)];
- */
-};
-
-struct KTXMipmapLevel {
- quint32 imageSize;
- /*
- for each array_element in numberOfArrayElements*
- for each face in numberOfFaces
- for each z_slice in pixelDepth*
- for each row or row_of_blocks in pixelHeight*
- for each pixel or block_of_pixels in pixelWidth
- Byte data[format-specific-number-of-bytes]**
- end
- end
- end
- Byte cubePadding[0-3]
- end
- end
- quint8 mipPadding[3 - ((imageSize + 3) % 4)]
- */
-};
-
-bool QSGKtxHandler::canRead(const QByteArray &suffix, const QByteArray &block)
-{
- Q_UNUSED(suffix)
-
- return (qstrncmp(block.constData(), ktxIdentifier, KTX_IDENTIFIER_LENGTH) == 0);
-}
-
-QQuickTextureFactory *QSGKtxHandler::read()
-{
- if (!device())
- return nullptr;
-
- QByteArray buf = device()->readAll();
- if (buf.size() < headerSize || !canRead(QByteArray(), buf)) {
- qCDebug(QSG_LOG_TEXTUREIO, "Invalid KTX file %s", logName().constData());
- return nullptr;
- }
-
- const KTXHeader *header = reinterpret_cast<const KTXHeader *>(buf.constData());
- if (!checkHeader(*header)) {
- qCDebug(QSG_LOG_TEXTUREIO, "Unsupported KTX file format in %s", logName().constData());
- return nullptr;
- }
-
- QSGCompressedTexture::DataPtr texData(QSGCompressedTexture::DataPtr::create());
-
- texData->size = QSize(decode(header->pixelWidth), decode(header->pixelHeight));
- texData->format = decode(header->glInternalFormat);
- texData->hasAlpha = !QSGCompressedTexture::formatIsOpaque(texData->format);
-
- // For now, ignore any additional mipmap levels
- int preambleSize = headerSize + decode(header->bytesOfKeyValueData);
- if (buf.size() >= preambleSize + int(sizeof(KTXMipmapLevel))) {
- texData->data = buf;
- texData->dataOffset = preambleSize + sizeof(quint32); // for the imageSize
- const KTXMipmapLevel *level = reinterpret_cast<const KTXMipmapLevel *>(buf.constData() + preambleSize);
- texData->dataLength = decode(level->imageSize);
- }
-
- if (!texData->isValid()) {
- qCDebug(QSG_LOG_TEXTUREIO, "Invalid values in header of KTX file %s", logName().constData());
- return nullptr;
- }
-
- texData->logName = logName();
-#ifdef KTX_DEBUG
- qDebug() << "KTX file handler read" << texData.data();
-#endif
-
- return new QSGCompressedTextureFactory(texData);
-}
-
-bool QSGKtxHandler::checkHeader(const KTXHeader &header)
-{
- if (header.endianness != platformEndianIdentifier && header.endianness != inversePlatformEndianIdentifier)
- return false;
- inverseEndian = (header.endianness == inversePlatformEndianIdentifier);
-#ifdef KTX_DEBUG
- QMetaEnum tfme = QMetaEnum::fromType<QOpenGLTexture::TextureFormat>();
- QMetaEnum ptme = QMetaEnum::fromType<QOpenGLTexture::PixelType>();
- qDebug("Header of %s:", logName().constData());
- qDebug(" glType: 0x%x (%s)", decode(header.glType), ptme.valueToKey(decode(header.glType)));
- qDebug(" glTypeSize: %u", decode(header.glTypeSize));
- qDebug(" glFormat: 0x%x (%s)", decode(header.glFormat), tfme.valueToKey(decode(header.glFormat)));
- qDebug(" glInternalFormat: 0x%x (%s)", decode(header.glInternalFormat), tfme.valueToKey(decode(header.glInternalFormat)));
- qDebug(" glBaseInternalFormat: 0x%x (%s)", decode(header.glBaseInternalFormat), tfme.valueToKey(decode(header.glBaseInternalFormat)));
- qDebug(" pixelWidth: %u", decode(header.pixelWidth));
- qDebug(" pixelHeight: %u", decode(header.pixelHeight));
- qDebug(" pixelDepth: %u", decode(header.pixelDepth));
- qDebug(" numberOfArrayElements: %u", decode(header.numberOfArrayElements));
- qDebug(" numberOfFaces: %u", decode(header.numberOfFaces));
- qDebug(" numberOfMipmapLevels: %u", decode(header.numberOfMipmapLevels));
- qDebug(" bytesOfKeyValueData: %u", decode(header.bytesOfKeyValueData));
-#endif
- return ((decode(header.glType) == 0) &&
- (decode(header.glFormat) == 0) &&
- (decode(header.pixelDepth) == 0) &&
- (decode(header.numberOfFaces) == 1));
-}
-
-quint32 QSGKtxHandler::decode(quint32 val)
-{
- return inverseEndian ? qbswap<quint32>(val) : val;
-}
-
-QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/compressedtexture/qsgpkmhandler.cpp b/src/quick/scenegraph/compressedtexture/qsgpkmhandler.cpp
deleted file mode 100644
index 618c0db045..0000000000
--- a/src/quick/scenegraph/compressedtexture/qsgpkmhandler.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtQuick module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsgpkmhandler_p.h"
-#include "qsgcompressedtexture_p.h"
-
-#include <QFile>
-#include <QDebug>
-#include <qendian.h>
-#include <qopenglfunctions.h>
-#include <qqmlfile.h>
-#include <QOpenGLTexture>
-
-//#define ETC_DEBUG
-
-QT_BEGIN_NAMESPACE
-
-static const int headerSize = 16;
-
-static unsigned int typeMap[5] = {
- QOpenGLTexture::RGB8_ETC1, // GL_ETC1_RGB8_OES,
- QOpenGLTexture::RGB8_ETC2, // GL_COMPRESSED_RGB8_ETC2,
- 0, // unused (obsolete)
- QOpenGLTexture::RGBA8_ETC2_EAC, // GL_COMPRESSED_RGBA8_ETC2_EAC,
- QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2 // GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2
-};
-
-bool QSGPkmHandler::canRead(const QByteArray &suffix, const QByteArray &block)
-{
- Q_UNUSED(suffix)
-
- return block.startsWith("PKM ");
-}
-
-QQuickTextureFactory *QSGPkmHandler::read()
-{
- if (!device())
- return nullptr;
-
- QSGCompressedTexture::DataPtr texData(QSGCompressedTexture::DataPtr::create());
-
- texData->data = device()->readAll();
- if (texData->data.size() < headerSize || !canRead(QByteArray(), texData->data)) {
- qCDebug(QSG_LOG_TEXTUREIO, "Invalid PKM file %s", logName().constData());
- return nullptr;
- }
-
- const char *rawData = texData->data.constData();
-
- // ignore version (rawData + 4 & 5)
-
- // texture type
- quint16 type = qFromBigEndian<quint16>(rawData + 6);
- if (type > sizeof(typeMap)/sizeof(typeMap[0])) {
- qCDebug(QSG_LOG_TEXTUREIO, "Unknown compression format in PKM file %s", logName().constData());
- return nullptr;
- }
- texData->format = typeMap[type];
- texData->hasAlpha = !QSGCompressedTexture::formatIsOpaque(texData->format);
-
- // texture size
- const int bpb = (texData->format == QOpenGLTexture::RGBA8_ETC2_EAC) ? 16 : 8;
- QSize paddedSize(qFromBigEndian<quint16>(rawData + 8), qFromBigEndian<quint16>(rawData + 10));
- texData->dataLength = (paddedSize.width() / 4) * (paddedSize.height() / 4) * bpb;
- QSize texSize(qFromBigEndian<quint16>(rawData + 12), qFromBigEndian<quint16>(rawData + 14));
- texData->size = texSize;
-
- texData->dataOffset = headerSize;
-
- if (!texData->isValid()) {
- qCDebug(QSG_LOG_TEXTUREIO, "Invalid values in header of PKM file %s", logName().constData());
- return nullptr;
- }
-
- texData->logName = logName();
-#ifdef ETC_DEBUG
- qDebug() << "PKM file handler read" << texData.data();
-#endif
- return new QSGCompressedTextureFactory(texData);
-}
-
-QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgabstractrenderer.h b/src/quick/scenegraph/coreapi/qsgabstractrenderer.h
index ab6fb4f317..b9805f9db6 100644
--- a/src/quick/scenegraph/coreapi/qsgabstractrenderer.h
+++ b/src/quick/scenegraph/coreapi/qsgabstractrenderer.h
@@ -61,6 +61,7 @@ public:
ClearStencilBuffer = 0x0004
};
Q_DECLARE_FLAGS(ClearMode, ClearModeBit)
+ Q_FLAG(ClearMode)
~QSGAbstractRenderer() override;
diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp
index c64360f955..c99e149aa5 100644
--- a/src/quick/scenegraph/qsgadaptationlayer.cpp
+++ b/src/quick/scenegraph/qsgadaptationlayer.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -82,19 +82,26 @@ QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache()
{
}
+QSGDistanceFieldGlyphCache::GlyphData &QSGDistanceFieldGlyphCache::emptyData(glyph_t glyph)
+{
+ GlyphData gd;
+ gd.texture = &s_emptyTexture;
+ QHash<glyph_t, GlyphData>::iterator it = m_glyphsData.insert(glyph, gd);
+ return it.value();
+}
+
QSGDistanceFieldGlyphCache::GlyphData &QSGDistanceFieldGlyphCache::glyphData(glyph_t glyph)
{
QHash<glyph_t, GlyphData>::iterator data = m_glyphsData.find(glyph);
if (data == m_glyphsData.end()) {
- GlyphData gd;
- gd.texture = &s_emptyTexture;
+ GlyphData &gd = emptyData(glyph);
gd.path = m_referenceFont.pathForGlyph(glyph);
// need bounding rect in base font size scale
qreal scaleFactor = qreal(1) / QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution);
QTransform scaleDown;
scaleDown.scale(scaleFactor, scaleFactor);
gd.boundingRect = scaleDown.mapRect(gd.path.boundingRect());
- data = m_glyphsData.insert(glyph, gd);
+ return gd;
}
return data.value();
}
@@ -234,10 +241,8 @@ void QSGDistanceFieldGlyphCache::setGlyphsPosition(const QList<GlyphPosition> &g
}
if (!invalidatedGlyphs.isEmpty()) {
- QLinkedList<QSGDistanceFieldGlyphConsumer *>::iterator it = m_registeredNodes.begin();
- while (it != m_registeredNodes.end()) {
- (*it)->invalidateGlyphs(invalidatedGlyphs);
- ++it;
+ for (QSGDistanceFieldGlyphConsumerList::iterator iter = m_registeredNodes.begin(); iter != m_registeredNodes.end(); ++iter) {
+ iter->invalidateGlyphs(invalidatedGlyphs);
}
}
}
@@ -280,10 +285,8 @@ void QSGDistanceFieldGlyphCache::setGlyphsTexture(const QVector<glyph_t> &glyphs
}
if (!invalidatedGlyphs.isEmpty()) {
- QLinkedList<QSGDistanceFieldGlyphConsumer*>::iterator it = m_registeredNodes.begin();
- while (it != m_registeredNodes.end()) {
- (*it)->invalidateGlyphs(invalidatedGlyphs);
- ++it;
+ for (QSGDistanceFieldGlyphConsumerList::iterator iter = m_registeredNodes.begin(); iter != m_registeredNodes.end(); ++iter) {
+ iter->invalidateGlyphs(invalidatedGlyphs);
}
}
}
diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h
index 9c88b8272c..ba5c4353b2 100644
--- a/src/quick/scenegraph/qsgadaptationlayer_p.h
+++ b/src/quick/scenegraph/qsgadaptationlayer_p.h
@@ -64,6 +64,7 @@
#include <QtGui/private/qdatabuffer_p.h>
#include <private/qopenglcontext_p.h>
#include <private/qdistancefield_p.h>
+#include <private/qintrusivelist_p.h>
// ### remove
#include <QtQuick/private/qquicktext_p.h>
@@ -82,6 +83,7 @@ class QSGGlyphNode;
class QSGRootNode;
class QSGSpriteNode;
class QSGRenderNode;
+class QSGRenderContext;
class Q_QUICK_PRIVATE_EXPORT QSGNodeVisitorEx
{
@@ -136,6 +138,7 @@ public:
virtual void setPenColor(const QColor &color) = 0;
virtual void setPenWidth(qreal width) = 0;
virtual void setGradientStops(const QGradientStops &stops) = 0;
+ virtual void setGradientVertical(bool vertical) = 0;
virtual void setRadius(qreal radius) = 0;
virtual void setAntialiasing(bool antialiasing) { Q_UNUSED(antialiasing) }
virtual void setAligned(bool aligned) = 0;
@@ -403,7 +406,9 @@ public:
virtual ~QSGDistanceFieldGlyphConsumer() {}
virtual void invalidateGlyphs(const QVector<quint32> &glyphs) = 0;
+ QIntrusiveListNode node;
};
+typedef QIntrusiveList<QSGDistanceFieldGlyphConsumer, &QSGDistanceFieldGlyphConsumer::node> QSGDistanceFieldGlyphConsumerList;
class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldGlyphCache
{
@@ -464,8 +469,8 @@ public:
void update();
- void registerGlyphNode(QSGDistanceFieldGlyphConsumer *node) { m_registeredNodes.append(node); }
- void unregisterGlyphNode(QSGDistanceFieldGlyphConsumer *node) { m_registeredNodes.removeOne(node); }
+ void registerGlyphNode(QSGDistanceFieldGlyphConsumer *node) { m_registeredNodes.insert(node); }
+ void unregisterGlyphNode(QSGDistanceFieldGlyphConsumer *node) { m_registeredNodes.remove(node); }
virtual void registerOwnerElement(QQuickItem *ownerElement);
virtual void unregisterOwnerElement(QQuickItem *ownerElement);
@@ -503,6 +508,7 @@ protected:
uint textureIdForGlyph(glyph_t glyph) const;
GlyphData &glyphData(glyph_t glyph);
+ GlyphData &emptyData(glyph_t glyph);
#if defined(QSG_DISTANCEFIELD_CACHE_DEBUG)
void saveTexture(GLuint textureId, int width, int height) const;
@@ -510,18 +516,21 @@ protected:
inline bool isCoreProfile() const { return m_coreProfile; }
-private:
+ bool m_doubleGlyphResolution;
+
+protected:
QRawFont m_referenceFont;
+
+private:
int m_glyphCount;
- bool m_doubleGlyphResolution;
bool m_coreProfile;
QList<Texture> m_textures;
QHash<glyph_t, GlyphData> m_glyphsData;
QDataBuffer<glyph_t> m_pendingGlyphs;
QSet<glyph_t> m_populatingGlyphs;
- QLinkedList<QSGDistanceFieldGlyphConsumer*> m_registeredNodes;
+ QSGDistanceFieldGlyphConsumerList m_registeredNodes;
static Texture s_emptyTexture;
};
diff --git a/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp b/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp
index df124cce35..91317ee2d7 100644
--- a/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp
+++ b/src/quick/scenegraph/qsgbasicinternalrectanglenode.cpp
@@ -68,19 +68,30 @@ namespace
{
float x, y;
Color4ub color;
- void set(float nx, float ny, Color4ub ncolor)
+
+ void set(float primary, float secondary, Color4ub ncolor, bool vertical)
{
- x = nx; y = ny; color = ncolor;
+ if (vertical) {
+ x = secondary; y = primary;
+ } else {
+ x = primary; y = secondary;
+ }
+ color = ncolor;
}
};
struct SmoothVertex : public Vertex
{
float dx, dy;
- void set(float nx, float ny, Color4ub ncolor, float ndx, float ndy)
+
+ void set(float primary, float secondary, Color4ub ncolor, float dPrimary, float dSecondary, bool vertical)
{
- Vertex::set(nx, ny, ncolor);
- dx = ndx; dy = ndy;
+ Vertex::set(primary, secondary, ncolor, vertical);
+ if (vertical) {
+ dx = dSecondary; dy = dPrimary;
+ } else {
+ dx = dPrimary; dy = dSecondary;
+ }
}
};
@@ -103,6 +114,7 @@ QSGBasicInternalRectangleNode::QSGBasicInternalRectangleNode()
, m_antialiasing(false)
, m_gradient_is_opaque(true)
, m_dirty_geometry(false)
+ , m_gradient_is_vertical(true)
, m_geometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0)
{
setGeometry(&m_geometry);
@@ -160,6 +172,15 @@ void QSGBasicInternalRectangleNode::setGradientStops(const QGradientStops &stops
m_dirty_geometry = true;
}
+void QSGBasicInternalRectangleNode::setGradientVertical(bool vertical)
+{
+ if (vertical == m_gradient_is_vertical)
+ return;
+ m_gradient_is_vertical = vertical;
+ m_dirty_geometry = true;
+}
+
+
void QSGBasicInternalRectangleNode::setRadius(qreal radius)
{
if (radius == m_radius)
@@ -230,12 +251,15 @@ void QSGBasicInternalRectangleNode::updateGeometry()
Color4ub transparent = { 0, 0, 0, 0 };
const QGradientStops &stops = m_gradient_stops;
+ float length = (m_gradient_is_vertical ? height : width);
+ float secondaryLength = (m_gradient_is_vertical ? width : height);
+
int nextGradientStop = 0;
- float gradientPos = penWidth / height;
+ float gradientPos = penWidth / length;
while (nextGradientStop < stops.size() && stops.at(nextGradientStop).first <= gradientPos)
++nextGradientStop;
int lastGradientStop = stops.size() - 1;
- float lastGradientPos = 1.0f - penWidth / height;
+ float lastGradientPos = 1.0f - penWidth / length;
while (lastGradientStop >= nextGradientStop && stops.at(lastGradientStop).first >= lastGradientPos)
--lastGradientStop;
int gradientIntersections = (lastGradientStop - nextGradientStop + 1);
@@ -319,40 +343,46 @@ void QSGBasicInternalRectangleNode::updateGeometry()
quint16 *indices = g->indexDataAsUShort();
quint16 index = 0;
- float py = 0; // previous inner y-coordinate.
- float plx = 0; // previous inner left x-coordinate.
- float prx = 0; // previous inner right x-coordinate.
+ float pp = 0; // previous inner primary coordinate.
+ float pss = 0; // previous inner secondary start coordinate.
+ float pse = 0; // previous inner secondary end coordinate.
float angle = 0.5f * float(M_PI) / segments;
float cosStep = qFastCos(angle);
float sinStep = qFastSin(angle);
+ float innerStart = (m_gradient_is_vertical ? innerRect.top() : innerRect.left());
+ float innerEnd = (m_gradient_is_vertical ? innerRect.bottom() : innerRect.right());
+ float innerLength = (m_gradient_is_vertical ? innerRect.height() : innerRect.width());
+ float innerSecondaryStart = (m_gradient_is_vertical ? innerRect.left() : innerRect.top());
+ float innerSecondaryEnd = (m_gradient_is_vertical ? innerRect.right() : innerRect.bottom());
+
for (int part = 0; part < 2; ++part) {
float c = 1 - part;
float s = part;
for (int i = 0; i <= segments; ++i) {
- float y, lx, rx;
+ float p, ss, se;
if (innerRadius > 0) {
- y = (part ? innerRect.bottom() : innerRect.top()) - innerRadius * c; // current inner y-coordinate.
- lx = innerRect.left() - innerRadius * s; // current inner left x-coordinate.
- rx = innerRect.right() + innerRadius * s; // current inner right x-coordinate.
- gradientPos = ((part ? innerRect.height() : 0) + radius - innerRadius * c) / height;
+ p = (part ? innerEnd : innerStart) - innerRadius * c; // current inner primary coordinate.
+ ss = innerSecondaryStart - innerRadius * s; // current inner secondary start coordinate.
+ se = innerSecondaryEnd + innerRadius * s; // current inner secondary end coordinate.
+ gradientPos = ((part ? innerLength : 0) + radius - innerRadius * c) / length;
} else {
- y = (part ? innerRect.bottom() + innerRadius : innerRect.top() - innerRadius); // current inner y-coordinate.
- lx = innerRect.left() - innerRadius; // current inner left x-coordinate.
- rx = innerRect.right() + innerRadius; // current inner right x-coordinate.
- gradientPos = ((part ? innerRect.height() + innerRadius : -innerRadius) + radius) / height;
+ p = (part ? innerEnd + innerRadius : innerStart - innerRadius); // current inner primary coordinate.
+ ss = innerSecondaryStart - innerRadius; // current inner secondary start coordinate.
+ se = innerSecondaryEnd + innerRadius; // current inner secondary end coordinate.
+ gradientPos = ((part ? innerLength + innerRadius : -innerRadius) + radius) / length;
}
- float Y = (part ? innerRect.bottom() : innerRect.top()) - outerRadius * c; // current outer y-coordinate.
- float lX = innerRect.left() - outerRadius * s; // current outer left x-coordinate.
- float rX = innerRect.right() + outerRadius * s; // current outer right x-coordinate.
+ float outerEdge = (part ? innerEnd : innerStart) - outerRadius * c; // current outer primary coordinate.
+ float outerSecondaryStart = innerSecondaryStart - outerRadius * s; // current outer secondary start coordinate.
+ float outerSecondaryEnd = innerSecondaryEnd + outerRadius * s; // current outer secondary end coordinate.
while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) {
// Insert vertices at gradient stops.
- float gy = (innerRect.top() - radius) + stops.at(nextGradientStop).first * height;
- float t = (gy - py) / (y - py);
- float glx = plx * (1 - t) + t * lx;
- float grx = prx * (1 - t) + t * rx;
+ float gp = (innerStart - radius) + stops.at(nextGradientStop).first * length;
+ float t = (gp - pp) / (p - pp);
+ float gis = pss * (1 - t) + t * ss; // gradient inner start
+ float gie = pse * (1 - t) + t * se; // gradient inner end
fillColor = colorToColor4ub(stops.at(nextGradientStop).second);
@@ -377,23 +407,23 @@ void QSGBasicInternalRectangleNode::updateGeometry()
indices[innerAATail++] = index + 3;
bool lower = stops.at(nextGradientStop).first > 0.5f;
- float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy);
- smoothVertices[index++].set(grx, gy, fillColor, width - grx - delta, dy);
- smoothVertices[index++].set(glx, gy, fillColor, delta - glx, dy);
+ float dp = lower ? qMin(0.0f, length - gp - delta) : qMax(0.0f, delta - gp);
+ smoothVertices[index++].set(gp, gie, fillColor, dp, secondaryLength - gie - delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(gp, gis, fillColor, dp, delta - gis, m_gradient_is_vertical);
if (penWidth) {
- smoothVertices[index++].set(grx, gy, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c);
- smoothVertices[index++].set(glx, gy, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c);
+ smoothVertices[index++].set(gp, gie, borderColor, -0.49f * penWidth * c, 0.49f * penWidth * s, m_gradient_is_vertical);
+ smoothVertices[index++].set(gp, gis, borderColor, -0.49f * penWidth * c, -0.49f * penWidth * s, m_gradient_is_vertical);
} else {
- dy = lower ? delta : -delta;
- smoothVertices[index++].set(grx, gy, transparent, delta, dy);
- smoothVertices[index++].set(glx, gy, transparent, -delta, dy);
+ dp = lower ? delta : -delta;
+ smoothVertices[index++].set(gp, gie, transparent, dp, delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(gp, gis, transparent, dp, -delta, m_gradient_is_vertical);
}
} else {
- vertices[index++].set(grx, gy, fillColor);
- vertices[index++].set(glx, gy, fillColor);
+ vertices[index++].set(gp, gie, fillColor, m_gradient_is_vertical);
+ vertices[index++].set(gp, gis, fillColor, m_gradient_is_vertical);
if (penWidth) {
- vertices[index++].set(grx, gy, borderColor);
- vertices[index++].set(glx, gy, borderColor);
+ vertices[index++].set(gp, gie, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(gp, gis, borderColor, m_gradient_is_vertical);
}
}
++nextGradientStop;
@@ -430,41 +460,41 @@ void QSGBasicInternalRectangleNode::updateGeometry()
indices[innerAATail++] = index + 1;
indices[innerAATail++] = index + 3;
- float dy = part ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y);
- smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy);
- smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy);
+ float dp = part ? qMin(0.0f, length - p - delta) : qMax(0.0f, delta - p);
+ smoothVertices[index++].set(p, se, fillColor, dp, secondaryLength - se - delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(p, ss, fillColor, dp, delta - ss, m_gradient_is_vertical);
- dy = part ? delta : -delta;
+ dp = part ? delta : -delta;
if (penWidth) {
- smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth * s, -0.49f * penWidth * c);
- smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth * s, -0.49f * penWidth * c);
- smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth * s, 0.49f * penWidth * c);
- smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth * s, 0.49f * penWidth * c);
- smoothVertices[index++].set(rX, Y, transparent, delta, dy);
- smoothVertices[index++].set(lX, Y, transparent, -delta, dy);
+ smoothVertices[index++].set(p, se, borderColor, -0.49f * penWidth * c, 0.49f * penWidth * s, m_gradient_is_vertical);
+ smoothVertices[index++].set(p, ss, borderColor, -0.49f * penWidth * c, -0.49f * penWidth * s, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerEdge, outerSecondaryEnd, borderColor, 0.49f * penWidth * c, -0.49f * penWidth * s, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerEdge, outerSecondaryStart, borderColor, 0.49f * penWidth * c, 0.49f * penWidth * s, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerEdge, outerSecondaryEnd, transparent, dp, delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerEdge, outerSecondaryStart, transparent, dp, -delta, m_gradient_is_vertical);
indices[--outerAAHead] = index - 2;
indices[--outerAAHead] = index - 4;
indices[outerAATail++] = index - 3;
indices[outerAATail++] = index - 1;
} else {
- smoothVertices[index++].set(rx, y, transparent, delta, dy);
- smoothVertices[index++].set(lx, y, transparent, -delta, dy);
+ smoothVertices[index++].set(p, se, transparent, dp, delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(p, ss, transparent, dp, -delta, m_gradient_is_vertical);
}
} else {
- vertices[index++].set(rx, y, fillColor);
- vertices[index++].set(lx, y, fillColor);
+ vertices[index++].set(p, se, fillColor, m_gradient_is_vertical);
+ vertices[index++].set(p, ss, fillColor, m_gradient_is_vertical);
if (penWidth) {
- vertices[index++].set(rx, y, borderColor);
- vertices[index++].set(lx, y, borderColor);
- vertices[index++].set(rX, Y, borderColor);
- vertices[index++].set(lX, Y, borderColor);
+ vertices[index++].set(p, se, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(p, ss, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(outerEdge, outerSecondaryEnd, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(outerEdge, outerSecondaryStart, borderColor, m_gradient_is_vertical);
}
}
- py = y;
- plx = lx;
- prx = rx;
+ pp = p;
+ pss = ss;
+ pse = se;
// Rotate
qreal tmp = c;
@@ -543,19 +573,24 @@ void QSGBasicInternalRectangleNode::updateGeometry()
quint16 *indices = g->indexDataAsUShort();
quint16 index = 0;
- float lx = innerRect.left();
- float rx = innerRect.right();
- float lX = outerRect.left();
- float rX = outerRect.right();
+ float innerStart = (m_gradient_is_vertical ? innerRect.top() : innerRect.left());
+ float innerEnd = (m_gradient_is_vertical ? innerRect.bottom() : innerRect.right());
+ float outerStart = (m_gradient_is_vertical ? outerRect.top() : outerRect.left());
+ float outerEnd = (m_gradient_is_vertical ? outerRect.bottom() : outerRect.right());
+
+ float innerSecondaryStart = (m_gradient_is_vertical ? innerRect.left() : innerRect.top());
+ float innerSecondaryEnd = (m_gradient_is_vertical ? innerRect.right() : innerRect.bottom());
+ float outerSecondaryStart = (m_gradient_is_vertical ? outerRect.left() : outerRect.top());
+ float outerSecondaryEnd = (m_gradient_is_vertical ? outerRect.right() : outerRect.bottom());
for (int part = -1; part <= 1; part += 2) {
- float y = (part == 1 ? innerRect.bottom() : innerRect.top());
- float Y = (part == 1 ? outerRect.bottom() : outerRect.top());
- gradientPos = (y - innerRect.top() + penWidth) / height;
+ float innerEdge = (part == 1 ? innerEnd : innerStart);
+ float outerEdge = (part == 1 ? outerEnd : outerStart);
+ gradientPos = (innerEdge - innerStart + penWidth) / length;
while (nextGradientStop <= lastGradientStop && stops.at(nextGradientStop).first <= gradientPos) {
// Insert vertices at gradient stops.
- float gy = (innerRect.top() - penWidth) + stops.at(nextGradientStop).first * height;
+ float gp = (innerStart - penWidth) + stops.at(nextGradientStop).first * length;
fillColor = colorToColor4ub(stops.at(nextGradientStop).second);
@@ -580,22 +615,22 @@ void QSGBasicInternalRectangleNode::updateGeometry()
indices[innerAATail++] = index + 3;
bool lower = stops.at(nextGradientStop).first > 0.5f;
- float dy = lower ? qMin(0.0f, height - gy - delta) : qMax(0.0f, delta - gy);
- smoothVertices[index++].set(rx, gy, fillColor, width - rx - delta, dy);
- smoothVertices[index++].set(lx, gy, fillColor, delta - lx, dy);
+ float dp = lower ? qMin(0.0f, length - gp - delta) : qMax(0.0f, delta - gp);
+ smoothVertices[index++].set(gp, innerSecondaryEnd, fillColor, dp, secondaryLength - innerSecondaryEnd - delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(gp, innerSecondaryStart, fillColor, dp, delta - innerSecondaryStart, m_gradient_is_vertical);
if (penWidth) {
- smoothVertices[index++].set(rx, gy, borderColor, 0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth);
- smoothVertices[index++].set(lx, gy, borderColor, -0.49f * penWidth, (lower ? 0.49f : -0.49f) * penWidth);
+ smoothVertices[index++].set(gp, innerSecondaryEnd, borderColor, (lower ? 0.49f : -0.49f) * penWidth, 0.49f * penWidth, m_gradient_is_vertical);
+ smoothVertices[index++].set(gp, innerSecondaryStart, borderColor, (lower ? 0.49f : -0.49f) * penWidth, -0.49f * penWidth, m_gradient_is_vertical);
} else {
- smoothVertices[index++].set(rx, gy, transparent, delta, lower ? delta : -delta);
- smoothVertices[index++].set(lx, gy, transparent, -delta, lower ? delta : -delta);
+ smoothVertices[index++].set(gp, innerSecondaryEnd, transparent, lower ? delta : -delta, delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(gp, innerSecondaryStart, transparent, lower ? delta : -delta, -delta, m_gradient_is_vertical);
}
} else {
- vertices[index++].set(rx, gy, fillColor);
- vertices[index++].set(lx, gy, fillColor);
+ vertices[index++].set(gp, innerSecondaryEnd, fillColor, m_gradient_is_vertical);
+ vertices[index++].set(gp, innerSecondaryStart, fillColor, m_gradient_is_vertical);
if (penWidth) {
- vertices[index++].set(rx, gy, borderColor);
- vertices[index++].set(lx, gy, borderColor);
+ vertices[index++].set(gp, innerSecondaryEnd, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(gp, innerSecondaryStart, borderColor, m_gradient_is_vertical);
}
}
++nextGradientStop;
@@ -632,34 +667,34 @@ void QSGBasicInternalRectangleNode::updateGeometry()
indices[innerAATail++] = index + 1;
indices[innerAATail++] = index + 3;
- float dy = part == 1 ? qMin(0.0f, height - y - delta) : qMax(0.0f, delta - y);
- smoothVertices[index++].set(rx, y, fillColor, width - rx - delta, dy);
- smoothVertices[index++].set(lx, y, fillColor, delta - lx, dy);
+ float dp = part == 1 ? qMin(0.0f, length - innerEdge - delta) : qMax(0.0f, delta - innerEdge);
+ smoothVertices[index++].set(innerEdge, innerSecondaryEnd, fillColor, dp, secondaryLength - innerSecondaryEnd - delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(innerEdge, innerSecondaryStart, fillColor, dp, delta - innerSecondaryStart, m_gradient_is_vertical);
if (penWidth) {
- smoothVertices[index++].set(rx, y, borderColor, 0.49f * penWidth, 0.49f * penWidth * part);
- smoothVertices[index++].set(lx, y, borderColor, -0.49f * penWidth, 0.49f * penWidth * part);
- smoothVertices[index++].set(rX, Y, borderColor, -0.49f * penWidth, -0.49f * penWidth * part);
- smoothVertices[index++].set(lX, Y, borderColor, 0.49f * penWidth, -0.49f * penWidth * part);
- smoothVertices[index++].set(rX, Y, transparent, delta, delta * part);
- smoothVertices[index++].set(lX, Y, transparent, -delta, delta * part);
+ smoothVertices[index++].set(innerEdge, innerSecondaryEnd, borderColor, 0.49f * penWidth * part, 0.49f * penWidth, m_gradient_is_vertical);
+ smoothVertices[index++].set(innerEdge, innerSecondaryStart, borderColor, 0.49f * penWidth * part, -0.49f * penWidth, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerEdge, outerSecondaryEnd, borderColor, -0.49f * penWidth * part, -0.49f * penWidth, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerEdge, outerSecondaryStart, borderColor, -0.49f * penWidth * part, 0.49f * penWidth, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerEdge, outerSecondaryEnd, transparent, delta * part, delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(outerEdge, outerSecondaryStart, transparent, delta * part, -delta, m_gradient_is_vertical);
indices[--outerAAHead] = index - 2;
indices[--outerAAHead] = index - 4;
indices[outerAATail++] = index - 3;
indices[outerAATail++] = index - 1;
} else {
- smoothVertices[index++].set(rx, y, transparent, delta, delta * part);
- smoothVertices[index++].set(lx, y, transparent, -delta, delta * part);
+ smoothVertices[index++].set(innerEdge, innerSecondaryEnd, transparent, delta * part, delta, m_gradient_is_vertical);
+ smoothVertices[index++].set(innerEdge, innerSecondaryStart, transparent, delta * part, -delta, m_gradient_is_vertical);
}
} else {
- vertices[index++].set(rx, y, fillColor);
- vertices[index++].set(lx, y, fillColor);
+ vertices[index++].set(innerEdge, innerSecondaryEnd, fillColor, m_gradient_is_vertical);
+ vertices[index++].set(innerEdge, innerSecondaryStart, fillColor, m_gradient_is_vertical);
if (penWidth) {
- vertices[index++].set(rx, y, borderColor);
- vertices[index++].set(lx, y, borderColor);
- vertices[index++].set(rX, Y, borderColor);
- vertices[index++].set(lX, Y, borderColor);
+ vertices[index++].set(innerEdge, innerSecondaryEnd, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(innerEdge, innerSecondaryStart, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(outerEdge, outerSecondaryEnd, borderColor, m_gradient_is_vertical);
+ vertices[index++].set(outerEdge, outerSecondaryStart, borderColor, m_gradient_is_vertical);
}
}
}
diff --git a/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h b/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h
index 98e53669ce..99f26b9aed 100644
--- a/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h
+++ b/src/quick/scenegraph/qsgbasicinternalrectanglenode_p.h
@@ -66,6 +66,7 @@ public:
void setPenColor(const QColor &color) override;
void setPenWidth(qreal width) override;
void setGradientStops(const QGradientStops &stops) override;
+ void setGradientVertical(bool vertical) override;
void setRadius(qreal radius) override;
void setAntialiasing(bool antialiasing) override;
void setAligned(bool aligned) override;
@@ -90,6 +91,7 @@ protected:
uint m_antialiasing : 1;
uint m_gradient_is_opaque : 1;
uint m_dirty_geometry : 1;
+ uint m_gradient_is_vertical : 1;
QSGGeometry m_geometry;
};
diff --git a/src/quick/scenegraph/qsgcontextplugin.cpp b/src/quick/scenegraph/qsgcontextplugin.cpp
index 628619fa46..66add51c55 100644
--- a/src/quick/scenegraph/qsgcontextplugin.cpp
+++ b/src/quick/scenegraph/qsgcontextplugin.cpp
@@ -73,6 +73,8 @@ Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
struct QSGAdaptationBackendData
{
QSGAdaptationBackendData();
+ ~QSGAdaptationBackendData();
+ Q_DISABLE_COPY(QSGAdaptationBackendData)
bool tried = false;
QSGContextFactoryInterface *factory = nullptr;
@@ -91,6 +93,11 @@ QSGAdaptationBackendData::QSGAdaptationBackendData()
builtIns.append(new QSGSoftwareAdaptation);
}
+QSGAdaptationBackendData::~QSGAdaptationBackendData()
+{
+ qDeleteAll(builtIns);
+}
+
Q_GLOBAL_STATIC(QSGAdaptationBackendData, qsg_adaptation_data)
// This only works when the backend is loaded (contextFactory() was called),
diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
index ef189ba461..ccc57b0b86 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
+++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -39,12 +39,18 @@
#include "qsgdefaultdistancefieldglyphcache_p.h"
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qbuffer.h>
+#include <QtQml/qqmlfile.h>
+
#include <QtGui/private/qdistancefield_p.h>
#include <QtGui/private/qopenglcontext_p.h>
#include <QtQml/private/qqmlglobal_p.h>
#include <qopenglfunctions.h>
#include <qopenglframebufferobject.h>
#include <qmath.h>
+#include "qsgcontext_p.h"
+
#if !defined(QT_OPENGL_ES_2)
#include <QtGui/qopenglfunctions_3_2_core.h>
@@ -59,10 +65,12 @@ DEFINE_BOOL_CONFIG_OPTION(qsgPreferFullSizeGlyphCacheTextures, QSG_PREFER_FULLSI
# define QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING 2
#endif
-QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font)
+QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c,
+ const QRawFont &font)
: QSGDistanceFieldGlyphCache(c, font)
, m_maxTextureSize(0)
, m_maxTextureCount(3)
+ , m_areaAllocator(nullptr)
, m_blitProgram(nullptr)
, m_blitBuffer(QOpenGLBuffer::VertexBuffer)
, m_fboGuard(nullptr)
@@ -81,7 +89,8 @@ QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLCont
qWarning("Buffer creation failed");
}
- m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), m_maxTextureCount * maxTextureSize()));
+ // Load a pregenerated cache if the font contains one
+ loadPregeneratedCache(font);
}
QSGDefaultDistanceFieldGlyphCache::~QSGDefaultDistanceFieldGlyphCache()
@@ -101,6 +110,9 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyph
QList<GlyphPosition> glyphPositions;
QVector<glyph_t> glyphsToRender;
+ if (m_areaAllocator == nullptr)
+ m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), m_maxTextureCount * maxTextureSize()));
+
for (QSet<glyph_t>::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) {
glyph_t glyphIndex = *it;
@@ -237,10 +249,23 @@ void QSGDefaultDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyph
m_unusedGlyphs += glyphs;
}
-void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, int width, int height)
+void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
+ int width,
+ int height)
+{
+ QByteArray zeroBuf(width * height, 0);
+ createTexture(texInfo, width, height, zeroBuf.constData());
+}
+
+void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
+ int width,
+ int height,
+ const void *pixels)
{
- if (useTextureResizeWorkaround() && texInfo->image.isNull())
+ if (useTextureResizeWorkaround() && texInfo->image.isNull()) {
texInfo->image = QDistanceField(width, height);
+ memcpy(texInfo->image.bits(), pixels, width * height);
+ }
while (m_funcs->glGetError() != GL_NO_ERROR) { }
@@ -261,8 +286,7 @@ void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, int
const GLenum format = GL_ALPHA;
#endif
- QByteArray zeroBuf(width * height, 0);
- m_funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, zeroBuf.constData());
+ m_funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, pixels);
texInfo->size = QSize(width, height);
@@ -520,4 +544,261 @@ int QSGDefaultDistanceFieldGlyphCache::maxTextureSize() const
return m_maxTextureSize;
}
+namespace {
+ struct Qtdf {
+ // We need these structs to be tightly packed, but some compilers we use do not
+ // support #pragma pack(1), so we need to hardcode the offsets/sizes in the
+ // file format
+ enum TableSize {
+ HeaderSize = 14,
+ GlyphRecordSize = 46,
+ TextureRecordSize = 17
+ };
+
+ enum Offset {
+ // Header
+ majorVersion = 0,
+ minorVersion = 1,
+ pixelSize = 2,
+ textureSize = 4,
+ flags = 8,
+ headerPadding = 9,
+ numGlyphs = 10,
+
+ // Glyph record
+ glyphIndex = 0,
+ textureOffsetX = 4,
+ textureOffsetY = 8,
+ textureWidth = 12,
+ textureHeight = 16,
+ xMargin = 20,
+ yMargin = 24,
+ boundingRectX = 28,
+ boundingRectY = 32,
+ boundingRectWidth = 36,
+ boundingRectHeight = 40,
+ textureIndex = 44,
+
+ // Texture record
+ allocatedX = 0,
+ allocatedY = 4,
+ allocatedWidth = 8,
+ allocatedHeight = 12,
+ texturePadding = 16
+
+ };
+
+ template <typename T>
+ static inline T fetch(const char *data, Offset offset)
+ {
+ return qFromBigEndian<T>(data + int(offset));
+ }
+ };
+}
+
+bool QSGDefaultDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
+{
+ // The pregenerated data must be loaded first, otherwise the area allocator
+ // will be wrong
+ if (m_areaAllocator != nullptr) {
+ qWarning("Font cache must be loaded before cache is used");
+ return false;
+ }
+
+ static QElapsedTimer timer;
+
+ bool profile = QSG_LOG_TIME_GLYPH().isDebugEnabled();
+ if (profile)
+ timer.start();
+
+ QByteArray qtdfTable = font.fontTable("qtdf");
+ if (qtdfTable.isEmpty())
+ return false;
+
+ typedef QHash<TextureInfo *, QVector<glyph_t> > GlyphTextureHash;
+
+ GlyphTextureHash glyphTextures;
+
+ if (uint(qtdfTable.size()) < Qtdf::HeaderSize) {
+ qWarning("Invalid qtdf table in font '%s'",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ const char *qtdfTableStart = qtdfTable.constData();
+ const char *qtdfTableEnd = qtdfTableStart + qtdfTable.size();
+
+ int padding = 0;
+ int textureCount = 0;
+ {
+ quint8 majorVersion = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::majorVersion);
+ quint8 minorVersion = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::minorVersion);
+ if (majorVersion != 5 || minorVersion != 12) {
+ qWarning("Invalid version of qtdf table %d.%d in font '%s'",
+ majorVersion,
+ minorVersion,
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ qreal pixelSize = qreal(Qtdf::fetch<quint16>(qtdfTableStart, Qtdf::pixelSize));
+ m_maxTextureSize = Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::textureSize);
+ m_doubleGlyphResolution = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::flags) == 1;
+ padding = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::headerPadding);
+
+ if (pixelSize <= 0.0) {
+ qWarning("Invalid pixel size in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ if (m_maxTextureSize <= 0) {
+ qWarning("Invalid texture size in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ int systemMaxTextureSize;
+ m_funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &systemMaxTextureSize);
+
+ if (m_maxTextureSize > systemMaxTextureSize) {
+ qWarning("System maximum texture size is %d. This is lower than the value in '%s', which is %d",
+ systemMaxTextureSize,
+ qPrintable(font.familyName()),
+ m_maxTextureSize);
+ }
+
+ if (padding != QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING) {
+ qWarning("Padding mismatch in '%s'. Font requires %d, but Qt is compiled with %d.",
+ qPrintable(font.familyName()),
+ padding,
+ QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING);
+ }
+
+ m_referenceFont.setPixelSize(pixelSize);
+
+ quint32 glyphCount = Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::numGlyphs);
+ m_unusedGlyphs.reserve(glyphCount);
+
+ const char *allocatorData = qtdfTableStart + Qtdf::HeaderSize;
+ {
+ m_areaAllocator = new QSGAreaAllocator(QSize(0, 0));
+ allocatorData = m_areaAllocator->deserialize(allocatorData, qtdfTableEnd - allocatorData);
+ if (allocatorData == nullptr)
+ return false;
+ }
+
+ if (m_areaAllocator->size().height() % m_maxTextureSize != 0) {
+ qWarning("Area allocator size mismatch in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ textureCount = m_areaAllocator->size().height() / m_maxTextureSize;
+ m_maxTextureCount = qMax(m_maxTextureCount, textureCount);
+
+ const char *textureRecord = allocatorData;
+ for (int i = 0; i < textureCount; ++i, textureRecord += Qtdf::TextureRecordSize) {
+ if (textureRecord + Qtdf::TextureRecordSize > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ TextureInfo *tex = textureInfo(i);
+ tex->allocatedArea.setX(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedX));
+ tex->allocatedArea.setY(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedY));
+ tex->allocatedArea.setWidth(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedWidth));
+ tex->allocatedArea.setHeight(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedHeight));
+ tex->padding = Qtdf::fetch<quint8>(textureRecord, Qtdf::texturePadding);
+ }
+
+ const char *glyphRecord = textureRecord;
+ for (quint32 i = 0; i < glyphCount; ++i, glyphRecord += Qtdf::GlyphRecordSize) {
+ if (glyphRecord + Qtdf::GlyphRecordSize > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ glyph_t glyph = Qtdf::fetch<quint32>(glyphRecord, Qtdf::glyphIndex);
+ m_unusedGlyphs.insert(glyph);
+
+ GlyphData &glyphData = emptyData(glyph);
+
+#define FROM_FIXED_POINT(value) \
+(((qreal)value)/(qreal)65536)
+
+ glyphData.texCoord.x = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureOffsetX));
+ glyphData.texCoord.y = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureOffsetY));
+ glyphData.texCoord.width = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureWidth));
+ glyphData.texCoord.height = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureHeight));
+ glyphData.texCoord.xMargin = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::xMargin));
+ glyphData.texCoord.yMargin = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::yMargin));
+ glyphData.boundingRect.setX(FROM_FIXED_POINT(Qtdf::fetch<qint32>(glyphRecord, Qtdf::boundingRectX)));
+ glyphData.boundingRect.setY(FROM_FIXED_POINT(Qtdf::fetch<qint32>(glyphRecord, Qtdf::boundingRectY)));
+ glyphData.boundingRect.setWidth(FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::boundingRectWidth)));
+ glyphData.boundingRect.setHeight(FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::boundingRectHeight)));
+
+#undef FROM_FIXED_POINT
+
+ int textureIndex = Qtdf::fetch<quint16>(glyphRecord, Qtdf::textureIndex);
+ if (textureIndex < 0 || textureIndex >= textureCount) {
+ qWarning("Invalid texture index %d (texture count == %d) in '%s'",
+ textureIndex,
+ textureCount,
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+
+ TextureInfo *texInfo = textureInfo(textureIndex);
+ m_glyphsTexture.insert(glyph, texInfo);
+
+ glyphTextures[texInfo].append(glyph);
+ }
+
+ GLint alignment = 4; // default value
+ m_funcs->glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment);
+
+ m_funcs->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ const uchar *textureData = reinterpret_cast<const uchar *>(glyphRecord);
+ for (int i = 0; i < textureCount; ++i) {
+
+ TextureInfo *texInfo = textureInfo(i);
+
+ int width = texInfo->allocatedArea.width();
+ int height = texInfo->allocatedArea.height();
+ qint64 size = width * height;
+ if (reinterpret_cast<const char *>(textureData + size) > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ createTexture(texInfo, width, height, textureData);
+
+ QVector<glyph_t> glyphs = glyphTextures.value(texInfo);
+
+ Texture t;
+ t.textureId = texInfo->texture;
+ t.size = texInfo->size;
+
+ setGlyphsTexture(glyphs, t);
+
+ textureData += size;
+ }
+
+ m_funcs->glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
+ }
+
+ if (profile) {
+ quint64 now = timer.elapsed();
+ qCDebug(QSG_LOG_TIME_GLYPH,
+ "distancefield: %d pre-generated glyphs loaded in %dms",
+ m_unusedGlyphs.size(),
+ (int) now);
+ }
+
+ return true;
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
index 76c0d20647..a0e4387af9 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
+++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
@@ -85,7 +85,10 @@ public:
void setMaxTextureCount(int max) { m_maxTextureCount = max; }
int maxTextureCount() const { return m_maxTextureCount; }
+
private:
+ bool loadPregeneratedCache(const QRawFont &font);
+
struct TextureInfo {
GLuint texture;
QSize size;
@@ -96,6 +99,7 @@ private:
TextureInfo(const QRect &preallocRect = QRect()) : texture(0), allocatedArea(preallocRect) { }
};
+ void createTexture(TextureInfo * texInfo, int width, int height, const void *pixels);
void createTexture(TextureInfo * texInfo, int width, int height);
void resizeTexture(TextureInfo * texInfo, int width, int height);
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
index 22e97a2dc9..73b79c6300 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp
+++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
@@ -325,7 +325,8 @@ bool QSGDefaultRenderContext::separateIndexBuffer() const
// lifetime. An attempt to bind a buffer object to the other
// target will generate an INVALID_OPERATION error, and the
// current binding will remain untouched.
- static const bool isWebGL = qGuiApp->platformName().compare(QLatin1String("webgl")) == 0;
+ static const bool isWebGL = (qGuiApp->platformName().compare(QLatin1String("webgl")) == 0
+ || qGuiApp->platformName().compare(QLatin1String("wasm")) == 0);
return isWebGL;
}
diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp
index 05f635cf1d..3d579fde46 100644
--- a/src/quick/scenegraph/qsgrenderloop.cpp
+++ b/src/quick/scenegraph/qsgrenderloop.cpp
@@ -221,10 +221,12 @@ QSGRenderLoop *QSGRenderLoop::instance()
}
switch (loopType) {
+#if QT_CONFIG(thread)
case ThreadedRenderLoop:
qCDebug(QSG_LOG_INFO, "threaded render loop");
s_instance = new QSGThreadedRenderLoop();
break;
+#endif
case WindowsRenderLoop:
qCDebug(QSG_LOG_INFO, "windows render loop");
s_instance = new QSGWindowsRenderLoop();
diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri
index 4f98638487..ddd7fb7f4c 100644
--- a/src/quick/scenegraph/scenegraph.pri
+++ b/src/quick/scenegraph/scenegraph.pri
@@ -115,7 +115,6 @@ qtConfig(opengl(es1|es2)?) {
$$PWD/util/qsgdefaultimagenode.cpp \
$$PWD/util/qsgdefaultninepatchnode.cpp \
$$PWD/qsgdefaultlayer.cpp \
- $$PWD/qsgthreadedrenderloop.cpp \
$$PWD/qsgwindowsrenderloop.cpp
HEADERS += \
$$PWD/qsgdefaultglyphnode_p.h \
@@ -132,9 +131,15 @@ qtConfig(opengl(es1|es2)?) {
$$PWD/util/qsgdefaultimagenode_p.h \
$$PWD/util/qsgdefaultninepatchnode_p.h \
$$PWD/qsgdefaultlayer_p.h \
- $$PWD/qsgthreadedrenderloop_p.h \
$$PWD/qsgwindowsrenderloop_p.h
+ qtConfig(thread) {
+ SOURCES += \
+ $$PWD/qsgthreadedrenderloop.cpp
+ HEADERS += \
+ $$PWD/qsgthreadedrenderloop_p.h
+ }
+
qtConfig(quick-sprite) {
SOURCES += \
$$PWD/qsgdefaultspritenode.cpp
@@ -230,14 +235,9 @@ SOURCES += \
qtConfig(opengl(es1|es2)?) {
HEADERS += \
$$PWD/compressedtexture/qsgcompressedatlastexture_p.h \
- $$PWD/compressedtexture/qsgcompressedtexture_p.h \
- $$PWD/compressedtexture/qsgtexturefilehandler_p.h \
- $$PWD/compressedtexture/qsgpkmhandler_p.h \
- $$PWD/compressedtexture/qsgktxhandler_p.h
+ $$PWD/compressedtexture/qsgcompressedtexture_p.h
SOURCES += \
$$PWD/compressedtexture/qsgcompressedatlastexture.cpp \
- $$PWD/compressedtexture/qsgcompressedtexture.cpp \
- $$PWD/compressedtexture/qsgpkmhandler.cpp \
- $$PWD/compressedtexture/qsgktxhandler.cpp
+ $$PWD/compressedtexture/qsgcompressedtexture.cpp
}
diff --git a/src/quick/scenegraph/util/qsgareaallocator.cpp b/src/quick/scenegraph/util/qsgareaallocator.cpp
index cd270a1d63..9a8c8e333b 100644
--- a/src/quick/scenegraph/util/qsgareaallocator.cpp
+++ b/src/quick/scenegraph/util/qsgareaallocator.cpp
@@ -42,6 +42,9 @@
#include <QtCore/qglobal.h>
#include <QtCore/qrect.h>
#include <QtCore/qpoint.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/qstack.h>
+#include <QtCore/qendian.h>
QT_BEGIN_NAMESPACE
@@ -285,4 +288,145 @@ void QSGAreaAllocator::mergeNodeWithNeighbors(QSGAreaAllocatorNode *node)
} // end while(!done)
}
+namespace {
+ struct AreaAllocatorTable
+ {
+ enum TableSize {
+ HeaderSize = 10,
+ NodeSize = 9
+ };
+
+ enum Offset {
+ // Header
+ majorVersion = 0,
+ minorVersion = 1,
+ width = 2,
+ height = 6,
+
+ // Node
+ split = 0,
+ splitType = 4,
+ flags = 8
+ };
+
+ enum Flags {
+ IsOccupied = 1,
+ HasLeft = 2,
+ HasRight = 4
+ };
+
+ template <typename T>
+ static inline T fetch(const char *data, Offset offset)
+ {
+ return qFromBigEndian<T>(data + int(offset));
+ }
+
+ template <typename T>
+ static inline void put(char *data, Offset offset, T value)
+ {
+ qToBigEndian(value, data + int(offset));
+ }
+ };
+}
+
+QByteArray QSGAreaAllocator::serialize()
+{
+ QVarLengthArray<QSGAreaAllocatorNode *> nodesToProcess;
+
+ QStack<QSGAreaAllocatorNode *> nodes;
+ nodes.push(m_root);
+ while (!nodes.isEmpty()) {
+ QSGAreaAllocatorNode *node = nodes.pop();
+
+ nodesToProcess.append(node);
+ if (node->left != nullptr)
+ nodes.push(node->left);
+ if (node->right != nullptr)
+ nodes.push(node->right);
+ }
+
+ QByteArray ret;
+ ret.resize(AreaAllocatorTable::HeaderSize + AreaAllocatorTable::NodeSize * nodesToProcess.size());
+
+ char *data = ret.data();
+ AreaAllocatorTable::put(data, AreaAllocatorTable::majorVersion, quint8(5));
+ AreaAllocatorTable::put(data, AreaAllocatorTable::minorVersion, quint8(12));
+ AreaAllocatorTable::put(data, AreaAllocatorTable::width, quint32(m_size.width()));
+ AreaAllocatorTable::put(data, AreaAllocatorTable::height, quint32(m_size.height()));
+
+ data += AreaAllocatorTable::HeaderSize;
+ for (QSGAreaAllocatorNode *node : nodesToProcess) {
+ AreaAllocatorTable::put(data, AreaAllocatorTable::split, qint32(node->split));
+ AreaAllocatorTable::put(data, AreaAllocatorTable::splitType, quint32(node->splitType));
+
+ quint8 flags =
+ (node->isOccupied ? AreaAllocatorTable::IsOccupied : 0)
+ | (node->left != nullptr ? AreaAllocatorTable::HasLeft : 0)
+ | (node->right != nullptr ? AreaAllocatorTable::HasRight : 0);
+ AreaAllocatorTable::put(data, AreaAllocatorTable::flags, flags);
+ data += AreaAllocatorTable::NodeSize;
+ }
+
+ return ret;
+}
+
+const char *QSGAreaAllocator::deserialize(const char *data, int size)
+{
+ if (uint(size) < AreaAllocatorTable::HeaderSize) {
+ qWarning("QSGAreaAllocator::deserialize: Data not long enough to fit header");
+ return nullptr;
+ }
+
+ const char *end = data + size;
+
+ quint8 majorVersion = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::majorVersion);
+ quint8 minorVersion = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::minorVersion);
+ if (majorVersion != 5 || minorVersion != 12) {
+ qWarning("Unrecognized version %d.%d of QSGAreaAllocator",
+ majorVersion,
+ minorVersion);
+ return nullptr;
+ }
+
+ m_size = QSize(AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::width),
+ AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::height));
+
+ Q_ASSERT(m_root != nullptr);
+ Q_ASSERT(m_root->left == nullptr);
+ Q_ASSERT(m_root->right == nullptr);
+
+ QStack<QSGAreaAllocatorNode *> nodes;
+ nodes.push(m_root);
+
+ data += AreaAllocatorTable::HeaderSize;
+ while (!nodes.isEmpty()) {
+ if (data + AreaAllocatorTable::NodeSize > end) {
+ qWarning("QSGAreaAllocator::deseriable: Data not long enough for nodes");
+ return nullptr;
+ }
+
+ QSGAreaAllocatorNode *node = nodes.pop();
+
+ node->split = AreaAllocatorTable::fetch<qint32>(data, AreaAllocatorTable::split);
+ node->splitType = SplitType(AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::splitType));
+
+ quint8 flags = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::flags);
+ node->isOccupied = flags & AreaAllocatorTable::IsOccupied;
+
+ if (flags & AreaAllocatorTable::HasLeft) {
+ node->left = new QSGAreaAllocatorNode(node);
+ nodes.push(node->left);
+ }
+
+ if (flags & AreaAllocatorTable::HasRight) {
+ node->right = new QSGAreaAllocatorNode(node);
+ nodes.push(node->right);
+ }
+
+ data += AreaAllocatorTable::NodeSize;
+ }
+
+ return data;
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgareaallocator_p.h b/src/quick/scenegraph/util/qsgareaallocator_p.h
index 8bc05a5a5b..300a8128c0 100644
--- a/src/quick/scenegraph/util/qsgareaallocator_p.h
+++ b/src/quick/scenegraph/util/qsgareaallocator_p.h
@@ -69,6 +69,10 @@ public:
bool deallocate(const QRect &rect);
bool isEmpty() const { return m_root == nullptr; }
QSize size() const { return m_size; }
+
+ QByteArray serialize();
+ const char *deserialize(const char *data, int size);
+
private:
bool allocateInNode(const QSize &size, QPoint &result, const QRect &currentRect, QSGAreaAllocatorNode *node);
bool deallocateInNode(const QPoint &pos, QSGAreaAllocatorNode *node);
diff --git a/src/quick/scenegraph/util/qsgatlastexture.cpp b/src/quick/scenegraph/util/qsgatlastexture.cpp
index 7608a81ddc..66c6d3a882 100644
--- a/src/quick/scenegraph/util/qsgatlastexture.cpp
+++ b/src/quick/scenegraph/util/qsgatlastexture.cpp
@@ -143,11 +143,11 @@ QSGTexture *Manager::create(const QImage &image, bool hasAlphaChannel)
QSGTexture *Manager::create(const QSGCompressedTextureFactory *factory)
{
QSGTexture *t = nullptr;
- if (!qsgEnableCompressedAtlas() || !factory->m_textureData || !factory->m_textureData->isValid())
+ if (!qsgEnableCompressedAtlas() || !factory->m_textureData.isValid())
return t;
// TODO: further abstract the atlas and remove this restriction
- unsigned int format = factory->m_textureData->format;
+ unsigned int format = factory->m_textureData.glInternalFormat();
switch (format) {
case QOpenGLTexture::RGB8_ETC1:
case QOpenGLTexture::RGB8_ETC2:
@@ -158,15 +158,15 @@ QSGTexture *Manager::create(const QSGCompressedTextureFactory *factory)
return t;
}
- QSize size = factory->m_textureData->size;
+ QSize size = factory->m_textureData.size();
if (size.width() < m_atlas_size_limit && size.height() < m_atlas_size_limit) {
QHash<unsigned int, QSGCompressedAtlasTexture::Atlas*>::iterator i = m_atlases.find(format);
if (i == m_atlases.end())
i = m_atlases.insert(format, new QSGCompressedAtlasTexture::Atlas(m_atlas_size, format));
// must be multiple of 4
QSize paddedSize(((size.width() + 3) / 4) * 4, ((size.height() + 3) / 4) * 4);
- QByteArray data = factory->m_textureData->data;
- t = i.value()->create(data, factory->m_textureData->sizeInBytes(), factory->m_textureData->dataOffset, size, paddedSize);
+ QByteArray data = factory->m_textureData.data();
+ t = i.value()->create(data, factory->m_textureData.dataLength(), factory->m_textureData.dataOffset(), size, paddedSize);
}
return t;
}
@@ -351,19 +351,6 @@ Texture *Atlas::create(const QImage &image)
return nullptr;
}
-static void swizzleBGRAToRGBA(QImage *image)
-{
- const int width = image->width();
- const int height = image->height();
- uint *p = (uint *) image->bits();
- int stride = image->bytesPerLine() / 4;
- for (int i = 0; i < height; ++i) {
- for (int x = 0; x < width; ++x)
- p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00);
- p += stride;
- }
-}
-
void Atlas::upload(Texture *texture)
{
const QImage &image = texture->image();
@@ -395,7 +382,7 @@ void Atlas::upload(Texture *texture)
}
if (m_externalFormat == GL_RGBA)
- swizzleBGRAToRGBA(&tmp);
+ tmp = std::move(tmp).convertToFormat(QImage::Format_RGBA8888_Premultiplied);
QOpenGLContext::currentContext()->functions()->glTexSubImage2D(GL_TEXTURE_2D, 0,
r.x(), r.y(), r.width(), r.height(),
m_externalFormat, GL_UNSIGNED_BYTE, tmp.constBits());
diff --git a/src/quick/scenegraph/util/qsgengine.h b/src/quick/scenegraph/util/qsgengine.h
index e9e01dc710..e48b7784ae 100644
--- a/src/quick/scenegraph/util/qsgengine.h
+++ b/src/quick/scenegraph/util/qsgengine.h
@@ -66,6 +66,7 @@ public:
TextureIsOpaque = 0x0010
};
Q_DECLARE_FLAGS(CreateTextureOptions, CreateTextureOption)
+ Q_FLAG(CreateTextureOptions)
explicit QSGEngine(QObject *parent = nullptr);
~QSGEngine() override;
diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp
index fea92a5121..982d05691d 100644
--- a/src/quick/scenegraph/util/qsgtexture.cpp
+++ b/src/quick/scenegraph/util/qsgtexture.cpp
@@ -659,17 +659,6 @@ QSGPlainTexture::~QSGPlainTexture()
#endif
}
-void qsg_swizzleBGRAToRGBA(QImage *image)
-{
- const int width = image->width();
- const int height = image->height();
- for (int i = 0; i < height; ++i) {
- uint *p = (uint *) image->scanLine(i);
- for (int x = 0; x < width; ++x)
- p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00);
- }
-}
-
void QSGPlainTexture::setImage(const QImage &image)
{
m_image = image;
@@ -841,7 +830,7 @@ void QSGPlainTexture::bind()
internalFormat = GL_RGBA;
#endif
} else {
- qsg_swizzleBGRAToRGBA(&tmp);
+ tmp = std::move(tmp).convertToFormat(QImage::Format_RGBA8888_Premultiplied);
}
qint64 swizzleTime = 0;
diff --git a/src/quick/scenegraph/util/qsgtexturereader.cpp b/src/quick/scenegraph/util/qsgtexturereader.cpp
index 8e95f27120..5e12ca4035 100644
--- a/src/quick/scenegraph/util/qsgtexturereader.cpp
+++ b/src/quick/scenegraph/util/qsgtexturereader.cpp
@@ -38,36 +38,40 @@
****************************************************************************/
#include "qsgtexturereader_p.h"
-
-#include <private/qtquickglobal_p.h>
-
-#include <private/qsgtexturefilehandler_p.h>
+#include <private/qtexturefilereader_p.h>
#if QT_CONFIG(opengl)
-#include <private/qsgpkmhandler_p.h>
-#include <private/qsgktxhandler_p.h>
+#include <private/qsgcompressedtexture_p.h>
#endif
-#include <QFileInfo>
-
QT_BEGIN_NAMESPACE
QSGTextureReader::QSGTextureReader(QIODevice *device, const QString &fileName)
- : m_device(device), m_fileInfo(fileName)
{
+#if QT_CONFIG(opengl)
+ m_reader = new QTextureFileReader(device, fileName);
+#else
+ Q_UNUSED(device);
+ Q_UNUSED(fileName);
+#endif
}
QSGTextureReader::~QSGTextureReader()
{
- delete m_handler;
+ delete m_reader;
}
QQuickTextureFactory *QSGTextureReader::read()
{
#if QT_CONFIG(opengl)
- if (!isTexture())
+ if (!m_reader)
+ return nullptr;
+
+ QTextureFileData texData = m_reader->read();
+ if (!texData.isValid())
return nullptr;
- return m_handler->read();
+
+ return new QSGCompressedTextureFactory(texData);
#else
return nullptr;
#endif
@@ -75,41 +79,12 @@ QQuickTextureFactory *QSGTextureReader::read()
bool QSGTextureReader::isTexture()
{
-#if QT_CONFIG(opengl)
- if (!checked) {
- checked = true;
- if (!init())
- return false;
-
- QByteArray headerBlock = m_device->peek(64);
- QByteArray suffix = m_fileInfo.suffix().toLower().toLatin1();
- QByteArray logName = m_fileInfo.fileName().toUtf8();
-
- // Currently the handlers are hardcoded; later maybe a list of plugins
- if (QSGPkmHandler::canRead(suffix, headerBlock)) {
- m_handler = new QSGPkmHandler(m_device, logName);
- } else if (QSGKtxHandler::canRead(suffix, headerBlock)) {
- m_handler = new QSGKtxHandler(m_device, logName);
- }
- // else if OtherHandler::canRead() ...etc.
- }
- return (m_handler != nullptr);
-#else
- return false;
-#endif
+ return m_reader ? m_reader->canRead() : false;
}
QList<QByteArray> QSGTextureReader::supportedFileFormats()
{
- // Hardcoded for now
- return {QByteArrayLiteral("pkm"), QByteArrayLiteral("ktx")};
-}
-
-bool QSGTextureReader::init()
-{
- if (!m_device)
- return false;
- return m_device->isReadable();
+ return QTextureFileReader::supportedFileFormats();
}
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgtexturereader_p.h b/src/quick/scenegraph/util/qsgtexturereader_p.h
index 19e33bf5c3..20c17fce50 100644
--- a/src/quick/scenegraph/util/qsgtexturereader_p.h
+++ b/src/quick/scenegraph/util/qsgtexturereader_p.h
@@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE
class QIODevice;
class QQuickTextureFactory;
-class QSGTextureFileHandler;
+class QTextureFileReader;
class QSGTextureReader
{
@@ -75,11 +75,7 @@ public:
static QList<QByteArray> supportedFileFormats();
private:
- bool init();
- QIODevice *m_device = nullptr;
- QFileInfo m_fileInfo;
- QSGTextureFileHandler *m_handler = nullptr;
- bool checked = false;
+ QTextureFileReader *m_reader = nullptr;
};
QT_END_NAMESPACE
diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp
index 9ee1a323e5..02be9daac0 100644
--- a/src/quick/util/qquickanimation.cpp
+++ b/src/quick/util/qquickanimation.cpp
@@ -234,6 +234,27 @@ QQmlProperty QQuickAbstractAnimationPrivate::createProperty(QObject *obj, const
The corresponding handler is \c onStopped.
*/
+/*!
+ \qmlsignal QtQuick::Animation::finished()
+ \since 5.12
+
+ This signal is emitted when the animation has finished naturally.
+
+ It is not emitted when \l running is set to \c false, nor for animations whose
+ \l loops property is set to \c Animation.Infinite.
+
+ In addition, it is only emitted for top-level, standalone animations. It
+ will not be emitted for animations in a Behavior or Transition, or
+ animations that are part of an animation group.
+
+ If \l alwaysRunToEnd is true, this signal will not be emitted until the
+ animation has completed its current iteration.
+
+ The corresponding handler is \c onFinished.
+
+ \sa stopped(), started(), running
+*/
+
void QQuickAbstractAnimation::setRunning(bool r)
{
Q_D(QQuickAbstractAnimation);
@@ -656,6 +677,7 @@ void QQuickAbstractAnimationPrivate::animationFinished(QAbstractAnimationJob*)
if (loopCount != 1)
animationInstance->setLoopCount(loopCount);
}
+ emit q->finished();
}
QQuickAbstractAnimation::ThreadingModel QQuickAbstractAnimation::threadingModel() const
diff --git a/src/quick/util/qquickanimation_p.h b/src/quick/util/qquickanimation_p.h
index 2293f2597f..746cb938bd 100644
--- a/src/quick/util/qquickanimation_p.h
+++ b/src/quick/util/qquickanimation_p.h
@@ -126,6 +126,7 @@ Q_SIGNALS:
void pausedChanged(bool);
void alwaysRunToEndChanged(bool);
void loopCountChanged(int);
+ Q_REVISION(12) void finished();
public Q_SLOTS:
void restart();
diff --git a/src/quick/util/qquickglobal.cpp b/src/quick/util/qquickglobal.cpp
index 7fa20636ec..5337bfd640 100644
--- a/src/quick/util/qquickglobal.cpp
+++ b/src/quick/util/qquickglobal.cpp
@@ -388,7 +388,7 @@ public:
float matVals[16];
QV4::ScopedValue v(scope);
for (quint32 i = 0; i < 16; ++i) {
- v = array->getIndexed(i);
+ v = array->get(i);
if (!v->isNumber())
return QMatrix4x4();
matVals[i] = v->asDouble();
diff --git a/src/quick/util/qquickimageprovider.cpp b/src/quick/util/qquickimageprovider.cpp
index f7c8724318..ced981de3e 100644
--- a/src/quick/util/qquickimageprovider.cpp
+++ b/src/quick/util/qquickimageprovider.cpp
@@ -39,8 +39,10 @@
#include "qquickimageprovider.h"
+#include "qquickimageprovider_p.h"
#include "qquickpixmapcache_p.h"
#include <QtQuick/private/qsgcontext_p.h>
+#include <private/qqmlglobal_p.h>
QT_BEGIN_NAMESPACE
@@ -160,7 +162,10 @@ QQuickTextureFactory *QQuickTextureFactory::textureFactoryForImage(const QImage
Constructs the image response
*/
QQuickImageResponse::QQuickImageResponse()
+ : QObject(*(new QQuickImageResponsePrivate))
{
+ qmlobject_connect(this, QQuickImageResponse, SIGNAL(finished()),
+ this, QQuickImageResponse, SLOT(_q_finished()));
}
/*!
@@ -329,8 +334,6 @@ void QQuickImageResponse::cancel()
\c cache property to \c false for the relevant \l Image, \l BorderImage or
\l AnimatedImage object.
- The \l {Qt Quick 1} version of this class is named QDeclarativeImageProvider.
-
\sa QQmlEngine::addImageProvider()
*/
diff --git a/src/quick/util/qquickimageprovider.h b/src/quick/util/qquickimageprovider.h
index 4f8193789a..82d0501697 100644
--- a/src/quick/util/qquickimageprovider.h
+++ b/src/quick/util/qquickimageprovider.h
@@ -69,6 +69,8 @@ public:
static QQuickTextureFactory *textureFactoryForImage(const QImage &image);
};
+class QQuickImageResponsePrivate;
+
class Q_QUICK_EXPORT QQuickImageResponse : public QObject
{
Q_OBJECT
@@ -84,6 +86,10 @@ public Q_SLOTS:
Q_SIGNALS:
void finished();
+
+private:
+ Q_DECLARE_PRIVATE(QQuickImageResponse)
+ Q_PRIVATE_SLOT(d_func(), void _q_finished())
};
class Q_QUICK_EXPORT QQuickImageProvider : public QQmlImageProviderBase
diff --git a/src/quick/scenegraph/compressedtexture/qsgpkmhandler_p.h b/src/quick/util/qquickimageprovider_p.h
index 6154c51b84..b5baf79319 100644
--- a/src/quick/scenegraph/compressedtexture/qsgpkmhandler_p.h
+++ b/src/quick/util/qquickimageprovider_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2017 The Qt Company Ltd.
+** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QSGPKMHANDLER_H
-#define QSGPKMHANDLER_H
+#ifndef QQUICKQQUICKIMAGEPROVIDER_P_H
+#define QQUICKQQUICKIMAGEPROVIDER_P_H
//
// W A R N I N G
@@ -51,20 +51,22 @@
// We mean it.
//
-#include "qsgtexturefilehandler_p.h"
+#include <private/qtquickglobal_p.h>
+#include <private/qobject_p.h>
+#include <qquickimageprovider.h>
QT_BEGIN_NAMESPACE
-class QSGPkmHandler : public QSGTextureFileHandler
+class QQuickImageResponsePrivate : public QObjectPrivate
{
+ Q_DECLARE_PUBLIC(QQuickImageResponse)
public:
- using QSGTextureFileHandler::QSGTextureFileHandler;
+ bool finished = false;
- static bool canRead(const QByteArray &suffix, const QByteArray &block);
-
- QQuickTextureFactory *read() override;
+ void _q_finished() { finished = true; }
};
+
QT_END_NAMESPACE
-#endif // QSGPKMHANDLER_H
+#endif // QQUICKQQUICKIMAGEPROVIDER_P_H
diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp
index 4237ec3edf..0dd2a88ca1 100644
--- a/src/quick/util/qquickpixmapcache.cpp
+++ b/src/quick/util/qquickpixmapcache.cpp
@@ -39,6 +39,7 @@
#include "qquickpixmapcache_p.h"
#include <qquickimageprovider.h>
+#include "qquickimageprovider_p.h"
#include <qqmlengine.h>
#include <private/qqmlglobal_p.h>
@@ -179,6 +180,8 @@ public:
QQuickPixmapReaderThreadObject(QQuickPixmapReader *);
void processJobs();
bool event(QEvent *e) override;
+public slots:
+ void asyncResponseFinished(QQuickImageResponse *response);
private slots:
void networkRequestDone();
void asyncResponseFinished();
@@ -613,10 +616,15 @@ void QQuickPixmapReaderThreadObject::networkRequestDone()
#endif
}
+void QQuickPixmapReaderThreadObject::asyncResponseFinished(QQuickImageResponse *response)
+{
+ reader->asyncResponseFinished(response);
+}
+
void QQuickPixmapReaderThreadObject::asyncResponseFinished()
{
QQuickImageResponse *response = static_cast<QQuickImageResponse *>(sender());
- reader->asyncResponseFinished(response);
+ asyncResponseFinished(response);
}
void QQuickPixmapReader::processJobs()
@@ -800,7 +808,14 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u
response = asyncProvider->requestImageResponse(imageId(url), runningJob->requestSize);
}
- QObject::connect(response, SIGNAL(finished()), threadObject, SLOT(asyncResponseFinished()));
+ // Might be that the async provider was so quick it emitted the signal before we
+ // could connect to it.
+ if (static_cast<QQuickImageResponsePrivate*>(QObjectPrivate::get(response))->finished) {
+ QMetaObject::invokeMethod(threadObject, "asyncResponseFinished",
+ Qt::QueuedConnection, Q_ARG(QQuickImageResponse*, response));
+ } else {
+ QObject::connect(response, SIGNAL(finished()), threadObject, SLOT(asyncResponseFinished()));
+ }
asyncResponses.insert(response, runningJob);
break;
diff --git a/src/quick/util/qquickprofiler.cpp b/src/quick/util/qquickprofiler.cpp
index bd9f04e562..decc6eac31 100644
--- a/src/quick/util/qquickprofiler.cpp
+++ b/src/quick/util/qquickprofiler.cpp
@@ -116,9 +116,8 @@ void QQuickProfiler::stopProfilingImpl()
m_data.clear();
}
-void QQuickProfiler::reportDataImpl(bool trackLocations)
+void QQuickProfiler::reportDataImpl()
{
- Q_UNUSED(trackLocations);
QMutexLocker lock(&m_dataMutex);
emit dataReady(m_data);
m_data.clear();
diff --git a/src/quick/util/qquickprofiler_p.h b/src/quick/util/qquickprofiler_p.h
index d699ddf371..28b058c2e8 100644
--- a/src/quick/util/qquickprofiler_p.h
+++ b/src/quick/util/qquickprofiler_p.h
@@ -357,7 +357,7 @@ protected:
void startProfilingImpl(quint64 features);
void stopProfilingImpl();
- void reportDataImpl(bool trackLocations);
+ void reportDataImpl();
void setTimer(const QElapsedTimer &t);
};
diff --git a/src/quick/util/qquickpropertychanges.cpp b/src/quick/util/qquickpropertychanges.cpp
index a3a598621f..8e2ac32ace 100644
--- a/src/quick/util/qquickpropertychanges.cpp
+++ b/src/quick/util/qquickpropertychanges.cpp
@@ -208,7 +208,7 @@ public:
bool isExplicit : 1;
void decode();
- void decodeBinding(const QString &propertyPrefix, const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding);
+ void decodeBinding(const QString &propertyPrefix, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &qmlUnit, const QV4::CompiledData::Binding *binding);
class ExpressionChange {
public:
@@ -236,19 +236,19 @@ public:
QQmlProperty property(const QString &);
};
-void QQuickPropertyChangesParser::verifyList(const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding)
+void QQuickPropertyChangesParser::verifyList(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
{
if (binding->type == QV4::CompiledData::Binding::Type_Object) {
- error(qmlUnit->objectAt(binding->value.objectIndex), QQuickPropertyChanges::tr("PropertyChanges does not support creating state-specific objects."));
+ error(compilationUnit->objectAt(binding->value.objectIndex), QQuickPropertyChanges::tr("PropertyChanges does not support creating state-specific objects."));
return;
}
if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty
|| binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
- const QV4::CompiledData::Object *subObj = qmlUnit->objectAt(binding->value.objectIndex);
+ const QV4::CompiledData::Object *subObj = compilationUnit->objectAt(binding->value.objectIndex);
const QV4::CompiledData::Binding *subBinding = subObj->bindingTable();
for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding) {
- verifyList(qmlUnit, subBinding);
+ verifyList(compilationUnit, subBinding);
}
}
}
@@ -259,26 +259,26 @@ void QQuickPropertyChangesPrivate::decode()
return;
for (const QV4::CompiledData::Binding *binding : qAsConst(bindings))
- decodeBinding(QString(), compilationUnit->data, binding);
+ decodeBinding(QString(), compilationUnit, binding);
bindings.clear();
decoded = true;
}
-void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix, const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding)
+void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
{
Q_Q(QQuickPropertyChanges);
- QString propertyName = propertyPrefix + qmlUnit->stringAt(binding->propertyNameIndex);
+ QString propertyName = propertyPrefix + compilationUnit->stringAt(binding->propertyNameIndex);
if (binding->type == QV4::CompiledData::Binding::Type_GroupProperty
|| binding->type == QV4::CompiledData::Binding::Type_AttachedProperty) {
QString pre = propertyName + QLatin1Char('.');
- const QV4::CompiledData::Object *subObj = qmlUnit->objectAt(binding->value.objectIndex);
+ const QV4::CompiledData::Object *subObj = compilationUnit->objectAt(binding->value.objectIndex);
const QV4::CompiledData::Binding *subBinding = subObj->bindingTable();
for (quint32 i = 0; i < subObj->nBindings; ++i, ++subBinding) {
- decodeBinding(pre, qmlUnit, subBinding);
+ decodeBinding(pre, compilationUnit, subBinding);
}
return;
}
@@ -298,7 +298,7 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
}
}
- if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->containsTranslations()) {
+ if (binding->type == QV4::CompiledData::Binding::Type_Script || binding->isTranslationBinding()) {
QUrl url = QUrl();
int line = -1;
int column = -1;
@@ -313,8 +313,8 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
QString expression;
QQmlBinding::Identifier id = QQmlBinding::Invalid;
- if (!binding->containsTranslations()) {
- expression = binding->valueAsString(qmlUnit);
+ if (!binding->isTranslationBinding()) {
+ expression = binding->valueAsString(compilationUnit.data());
id = binding->value.compiledScriptIndex;
}
expressions << ExpressionChange(propertyName, binding, id, expression, url, line, column);
@@ -328,10 +328,10 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
case QV4::CompiledData::Binding::Type_TranslationById:
Q_UNREACHABLE();
case QV4::CompiledData::Binding::Type_String:
- var = binding->valueAsString(qmlUnit);
+ var = binding->valueAsString(compilationUnit.data());
break;
case QV4::CompiledData::Binding::Type_Number:
- var = binding->valueAsNumber();
+ var = binding->valueAsNumber(compilationUnit->constants);
break;
case QV4::CompiledData::Binding::Type_Boolean:
var = binding->valueAsBoolean();
@@ -343,13 +343,13 @@ void QQuickPropertyChangesPrivate::decodeBinding(const QString &propertyPrefix,
properties << qMakePair(propertyName, var);
}
-void QQuickPropertyChangesParser::verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props)
+void QQuickPropertyChangesParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props)
{
for (int ii = 0; ii < props.count(); ++ii)
- verifyList(qmlUnit, props.at(ii));
+ verifyList(compilationUnit, props.at(ii));
}
-void QQuickPropertyChangesParser::applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
+void QQuickPropertyChangesParser::applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings)
{
QQuickPropertyChangesPrivate *p =
static_cast<QQuickPropertyChangesPrivate *>(QObjectPrivate::get(obj));
@@ -468,7 +468,7 @@ QQuickPropertyChanges::ActionList QQuickPropertyChanges::actions()
QQmlContextData *context = QQmlContextData::get(qmlContext(this));
QQmlBinding *newBinding = nullptr;
- if (e.binding && e.binding->containsTranslations()) {
+ if (e.binding && e.binding->isTranslationBinding()) {
newBinding = QQmlBinding::createTranslationBinding(d->compilationUnit, e.binding, object(), context);
} else if (e.id != QQmlBinding::Invalid) {
QV4::Scope scope(qmlEngine(this)->handle());
diff --git a/src/quick/util/qquickpropertychanges_p.h b/src/quick/util/qquickpropertychanges_p.h
index 35b37e84cb..74fe511d6e 100644
--- a/src/quick/util/qquickpropertychanges_p.h
+++ b/src/quick/util/qquickpropertychanges_p.h
@@ -101,10 +101,10 @@ public:
QQuickPropertyChangesParser()
: QQmlCustomParser(AcceptsAttachedProperties) {}
- void verifyList(const QV4::CompiledData::Unit *qmlUnit, const QV4::CompiledData::Binding *binding);
+ void verifyList(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding);
- void verifyBindings(const QV4::CompiledData::Unit *qmlUnit, const QList<const QV4::CompiledData::Binding *> &props) override;
- void applyBindings(QObject *obj, QV4::CompiledData::CompilationUnit *compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
+ void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) override;
+ void applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
};
diff --git a/src/quick/util/qquickstate.cpp b/src/quick/util/qquickstate.cpp
index b953ad1c7f..3ca6440784 100644
--- a/src/quick/util/qquickstate.cpp
+++ b/src/quick/util/qquickstate.cpp
@@ -222,7 +222,7 @@ bool QQuickState::isWhenKnown() const
QQmlBinding *QQuickState::when() const
{
Q_D(const QQuickState);
- return d->when;
+ return d->when.data();
}
void QQuickState::setWhen(QQmlBinding *when)
diff --git a/src/quick/util/qquickstate_p_p.h b/src/quick/util/qquickstate_p_p.h
index f1bc24c558..61472b4d06 100644
--- a/src/quick/util/qquickstate_p_p.h
+++ b/src/quick/util/qquickstate_p_p.h
@@ -203,12 +203,12 @@ class QQuickStatePrivate : public QObjectPrivate
public:
QQuickStatePrivate()
- : when(nullptr), named(false), inState(false), group(nullptr) {}
+ : named(false), inState(false), group(nullptr) {}
typedef QList<QQuickSimpleAction> SimpleActionList;
QString name;
- QQmlBinding *when;
+ QQmlBinding::Ptr when;
bool named;
struct OperationGuard : public QQmlGuard<QQuickStateOperation>
diff --git a/src/quick/util/qquicksvgparser.cpp b/src/quick/util/qquicksvgparser.cpp
index 2bf9c121d3..0687913565 100644
--- a/src/quick/util/qquicksvgparser.cpp
+++ b/src/quick/util/qquicksvgparser.cpp
@@ -280,11 +280,8 @@ bool QQuickSvgParser::parsePathDataFast(const QString &dataStr, QPainterPath &pa
++str;
QChar pathElem = *str;
++str;
- QChar endc = *end;
- *const_cast<QChar *>(end) = 0; // parseNumbersArray requires 0-termination that QStringRef cannot guarantee
QVarLengthArray<qreal, 8> arg;
parseNumbersArray(str, arg);
- *const_cast<QChar *>(end) = endc;
if (pathElem == QLatin1Char('z') || pathElem == QLatin1Char('Z'))
arg.append(0);//dummy
const qreal *num = arg.constData();
diff --git a/src/quick/util/qquicktransitionmanager.cpp b/src/quick/util/qquicktransitionmanager.cpp
index a1367249c6..e51de1a02a 100644
--- a/src/quick/util/qquicktransitionmanager.cpp
+++ b/src/quick/util/qquicktransitionmanager.cpp
@@ -91,10 +91,10 @@ void QQuickTransitionManager::complete()
{
d->applyBindings();
- for (int ii = 0; ii < d->completeList.count(); ++ii) {
- const QQmlProperty &prop = d->completeList.at(ii).property();
- prop.write(d->completeList.at(ii).value());
- }
+ // Explicitly take a copy in case the write action triggers a script that modifies the list.
+ QQuickTransitionManagerPrivate::SimpleActionList completeListCopy = d->completeList;
+ for (const QQuickSimpleAction &action : qAsConst(completeListCopy))
+ action.property().write(action.value());
d->completeList.clear();
@@ -131,7 +131,9 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
{
cancel();
+ // The copy below is ON PURPOSE, because firing actions might involve scripts that modify the list.
QQuickStateOperation::ActionList applyList = list;
+
// Determine which actions are binding changes and disable any current bindings
for (const QQuickStateAction &action : qAsConst(applyList)) {
if (action.toBinding)
@@ -156,8 +158,7 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
if (transition && !d->bindingsList.isEmpty()) {
// Apply all the property and binding changes
- for (int ii = 0; ii < applyList.size(); ++ii) {
- const QQuickStateAction &action = applyList.at(ii);
+ for (const QQuickStateAction &action : qAsConst(applyList)) {
if (action.toBinding) {
QQmlPropertyPrivate::setBinding(action.toBinding.data(), QQmlPropertyPrivate::None, QQmlPropertyData::BypassInterceptor | QQmlPropertyData::DontRemoveBinding);
} else if (!action.event) {
@@ -170,17 +171,15 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
}
}
- // Read all the end values for binding changes
- for (int ii = 0; ii < applyList.size(); ++ii) {
- QQuickStateAction *action = &applyList[ii];
- if (action->event) {
- action->event->saveTargetValues();
+ // Read all the end values for binding changes.
+ for (auto it = applyList.begin(), eit = applyList.end(); it != eit; ++it) {
+ if (it->event) {
+ it->event->saveTargetValues();
continue;
}
- const QQmlProperty &prop = action->property;
- if (action->toBinding || !action->toValue.isValid()) {
- action->toValue = prop.read();
- }
+ const QQmlProperty &prop = it->property;
+ if (it->toBinding || !it->toValue.isValid())
+ it->toValue = prop.read();
}
// Revert back to the original values
@@ -210,29 +209,20 @@ void QQuickTransitionManager::transition(const QList<QQuickStateAction> &list,
delete oldInstance;
// Modify the action list to remove actions handled in the transition
- for (int ii = 0; ii < applyList.count(); ++ii) {
- const QQuickStateAction &action = applyList.at(ii);
-
+ auto isHandledInTransition = [this, touched](const QQuickStateAction &action) {
if (action.event) {
-
- if (action.actionDone) {
- applyList.removeAt(ii);
- --ii;
- }
-
+ return action.actionDone;
} else {
-
if (touched.contains(action.property)) {
if (action.toValue != action.fromValue)
- d->completeList <<
- QQuickSimpleAction(action, QQuickSimpleAction::EndState);
-
- applyList.removeAt(ii);
- --ii;
+ d->completeList << QQuickSimpleAction(action, QQuickSimpleAction::EndState);
+ return true;
}
-
}
- }
+ return false;
+ };
+ auto newEnd = std::remove_if(applyList.begin(), applyList.end(), isHandledInTransition);
+ applyList.erase(newEnd, applyList.end());
}
// Any actions remaining have not been handled by the transition and should
@@ -270,12 +260,9 @@ void QQuickTransitionManager::cancel()
if (d->transitionInstance && d->transitionInstance->isRunning())
d->transitionInstance->stop();
- for(int i = 0; i < d->bindingsList.count(); ++i) {
- QQuickStateAction action = d->bindingsList[i];
+ for (const QQuickStateAction &action : qAsConst(d->bindingsList)) {
if (action.toBinding && action.deletableToBinding) {
QQmlPropertyPrivate::removeBinding(action.property);
- action.toBinding = nullptr;
- action.deletableToBinding = false;
} else if (action.event) {
//### what do we do here?
}
diff --git a/src/quick/util/qquickutilmodule.cpp b/src/quick/util/qquickutilmodule.cpp
index b2e5b84cf4..31d4d4c437 100644
--- a/src/quick/util/qquickutilmodule.cpp
+++ b/src/quick/util/qquickutilmodule.cpp
@@ -132,4 +132,7 @@ void QQuickUtilModule::defineModule()
qmlRegisterType<QQuickShortcut,9>("QtQuick", 2, 9, "Shortcut");
#endif
+
+ qmlRegisterUncreatableType<QQuickAbstractAnimation, 12>("QtQuick", 2, 12, "Animation",
+ QQuickAbstractAnimation::tr("Animation is an abstract class"));
}
diff --git a/src/quick/util/util.pri b/src/quick/util/util.pri
index edcb268cd9..c51f082d03 100644
--- a/src/quick/util/util.pri
+++ b/src/quick/util/util.pri
@@ -53,6 +53,7 @@ HEADERS += \
$$PWD/qquickfontloader_p.h \
$$PWD/qquickstyledtext_p.h \
$$PWD/qquickimageprovider.h \
+ $$PWD/qquickimageprovider_p.h \
$$PWD/qquicksvgparser_p.h \
$$PWD/qquickvaluetypes_p.h \
$$PWD/qquickanimator_p.h \
diff --git a/src/imports/shapes/qquicknvprfunctions.cpp b/src/quickshapes/qquicknvprfunctions.cpp
index 409a59be7f..409a59be7f 100644
--- a/src/imports/shapes/qquicknvprfunctions.cpp
+++ b/src/quickshapes/qquicknvprfunctions.cpp
diff --git a/src/imports/shapes/qquicknvprfunctions_p.h b/src/quickshapes/qquicknvprfunctions_p.h
index 342e92cbc3..92246cf4c8 100644
--- a/src/imports/shapes/qquicknvprfunctions_p.h
+++ b/src/quickshapes/qquicknvprfunctions_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtQuickShapes/private/qquickshapesglobal_p.h>
#include <qopengl.h>
#include <QtGui/qsurfaceformat.h>
diff --git a/src/imports/shapes/qquicknvprfunctions_p_p.h b/src/quickshapes/qquicknvprfunctions_p_p.h
index 6df20566af..3d9ca0de9f 100644
--- a/src/imports/shapes/qquicknvprfunctions_p_p.h
+++ b/src/quickshapes/qquicknvprfunctions_p_p.h
@@ -51,7 +51,8 @@
// We mean it.
//
-#include "qquicknvprfunctions_p.h"
+#include <QtQuickShapes/private/qquickshapesglobal_p.h>
+#include <QtQuickShapes/private/qquicknvprfunctions_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/imports/shapes/qquickshape.cpp b/src/quickshapes/qquickshape.cpp
index 067a54736f..0c159ec21b 100644
--- a/src/imports/shapes/qquickshape.cpp
+++ b/src/quickshapes/qquickshape.cpp
@@ -577,10 +577,10 @@ void QQuickShapePath::resetFillGradient()
\li When running with the default, OpenGL backend of Qt Quick, both the
generic, triangulation-based and the NVIDIA-specific
- \c{GL_NV_path_rendering} methods are available. The choice is made at
- runtime, depending on the graphics driver's capabilities. When this is not
- desired, applications can force using the generic method by setting the
- Shape.vendorExtensionsEnabled property to \c false.
+ \c{GL_NV_path_rendering} methods are available. By default only the generic
+ approach is used. Setting Shape.vendorExtensionsEnabled property to \c true
+ leads to using NV_path_rendering on NVIDIA systems, and the generic method
+ on others.
\li The \c software backend is fully supported. The path is rendered via
QPainter::strokePath() and QPainter::fillPath() in this case.
@@ -624,9 +624,8 @@ void QQuickShapePath::resetFillGradient()
\li As a general rule, scenes should avoid using separate Shape items when
it is not absolutely necessary. Prefer using one Shape item with multiple
- ShapePath elements over multiple Shape items. Scenes that cannot avoid
- using a large number of individual Shape items should consider setting
- Shape.vendorExtensionsEnabled to \c false.
+ ShapePath elements over multiple Shape items.
+
\endlist
\sa {Qt Quick Examples - Shapes}, Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg
@@ -658,9 +657,22 @@ void QQuickShapePrivate::setStatus(QQuickShape::Status newStatus)
}
}
+struct QQuickShapeResourceInitializer
+{
+ QQuickShapeResourceInitializer()
+ {
+#if defined(QT_STATIC)
+ Q_INIT_RESOURCE(qtquickshapes);
+#endif
+ }
+};
+
+Q_GLOBAL_STATIC(QQuickShapeResourceInitializer, initQQuickShapeResources)
+
QQuickShape::QQuickShape(QQuickItem *parent)
: QQuickItem(*(new QQuickShapePrivate), parent)
{
+ initQQuickShapeResources();
setFlag(ItemHasContents);
}
@@ -737,11 +749,18 @@ void QQuickShape::setAsynchronous(bool async)
\qmlproperty bool QtQuick.Shapes::Shape::vendorExtensionsEnabled
This property controls the usage of non-standard OpenGL extensions like
- \c GL_NV_path_rendering. To disable Shape.NvprRenderer and force a uniform
- behavior regardless of the graphics card and drivers, set this property to
- \c false.
+ \c GL_NV_path_rendering.
+
+ The default value is \c false.
- The default value is \c true.
+ As of Qt 5.12 Shape.NvprRenderer is disabled by default and a uniform
+ behavior, based on triangulating the path and generating QSGGeometryNode
+ instances, is used regardless of the graphics card and drivers. To enable
+ using vendor-specific path rendering approaches set the value to \c true.
+ Depending on the platform and content, this can lead to improved
+ performance. Setting the value to \c true is safe in any case since
+ rendering falls back to the default method when the vendor-specific
+ approach, such as \c GL_NV_path_rendering, is not supported at run time.
*/
bool QQuickShape::vendorExtensionsEnabled() const
diff --git a/src/imports/shapes/qquickshape_p.h b/src/quickshapes/qquickshape_p.h
index 1dfeaf9228..cd242cafc3 100644
--- a/src/imports/shapes/qquickshape_p.h
+++ b/src/quickshapes/qquickshape_p.h
@@ -51,7 +51,8 @@
// We mean it.
//
-#include "qquickitem.h"
+#include <QtQuickShapes/private/qquickshapesglobal_p.h>
+#include <QtQuick/qquickitem.h>
#include <private/qtquickglobal_p.h>
#include <private/qquickpath_p_p.h>
@@ -62,7 +63,7 @@ QT_BEGIN_NAMESPACE
class QQuickShapePathPrivate;
class QQuickShapePrivate;
-class QQuickShapeGradient : public QQuickGradient
+class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapeGradient : public QQuickGradient
{
Q_OBJECT
Q_PROPERTY(SpreadMode spread READ spread WRITE setSpread NOTIFY spreadChanged)
@@ -88,7 +89,7 @@ private:
SpreadMode m_spread;
};
-class QQuickShapeLinearGradient : public QQuickShapeGradient
+class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapeLinearGradient : public QQuickShapeGradient
{
Q_OBJECT
Q_PROPERTY(qreal x1 READ x1 WRITE setX1 NOTIFY x1Changed)
@@ -120,7 +121,7 @@ private:
QPointF m_end;
};
-class QQuickShapeRadialGradient : public QQuickShapeGradient
+class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapeRadialGradient : public QQuickShapeGradient
{
Q_OBJECT
Q_PROPERTY(qreal centerX READ centerX WRITE setCenterX NOTIFY centerXChanged)
@@ -167,7 +168,7 @@ private:
qreal m_focalRadius = 0;
};
-class QQuickShapeConicalGradient : public QQuickShapeGradient
+class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapeConicalGradient : public QQuickShapeGradient
{
Q_OBJECT
Q_PROPERTY(qreal centerX READ centerX WRITE setCenterX NOTIFY centerXChanged)
@@ -197,7 +198,7 @@ private:
qreal m_angle = 0;
};
-class QQuickShapePath : public QQuickPath
+class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapePath : public QQuickPath
{
Q_OBJECT
@@ -296,7 +297,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged())
};
-class QQuickShape : public QQuickItem
+class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShape : public QQuickItem
{
Q_OBJECT
Q_PROPERTY(RendererType rendererType READ rendererType NOTIFY rendererChanged)
diff --git a/src/imports/shapes/qquickshape_p_p.h b/src/quickshapes/qquickshape_p_p.h
index ef2775885e..bc06c8fde8 100644
--- a/src/imports/shapes/qquickshape_p_p.h
+++ b/src/quickshapes/qquickshape_p_p.h
@@ -51,7 +51,8 @@
// We mean it.
//
-#include "qquickshape_p.h"
+#include <QtQuickShapes/private/qquickshapesglobal_p.h>
+#include <QtQuickShapes/private/qquickshape_p.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QPainterPath>
#include <QColor>
@@ -122,7 +123,7 @@ struct QQuickShapeStrokeFillParams
QQuickShapeGradient *fillGradient;
};
-class QQuickShapePathPrivate : public QQuickPathPrivate
+class Q_QUICKSHAPES_PRIVATE_EXPORT QQuickShapePathPrivate : public QQuickPathPrivate
{
Q_DECLARE_PUBLIC(QQuickShapePath)
@@ -181,7 +182,7 @@ public:
QQuickShape::ContainsMode containsMode = QQuickShape::BoundingRectContains;
bool spChanged = false;
bool async = false;
- bool enableVendorExts = true;
+ bool enableVendorExts = false;
bool syncTimingActive = false;
};
diff --git a/src/imports/shapes/qquickshapegenericrenderer.cpp b/src/quickshapes/qquickshapegenericrenderer.cpp
index 8a4785a83a..604da2a889 100644
--- a/src/imports/shapes/qquickshapegenericrenderer.cpp
+++ b/src/quickshapes/qquickshapegenericrenderer.cpp
@@ -40,7 +40,10 @@
#include "qquickshapegenericrenderer_p.h"
#include <QtGui/private/qtriangulator_p.h>
#include <QtGui/private/qtriangulatingstroker_p.h>
+
+#if QT_CONFIG(thread)
#include <QThreadPool>
+#endif
#if QT_CONFIG(opengl)
#include <QSGVertexColorMaterial>
@@ -278,6 +281,7 @@ void QQuickShapeGenericRenderer::setAsyncCallback(void (*callback)(void *), void
m_asyncCallbackData = data;
}
+#if QT_CONFIG(thread)
static QThreadPool *pathWorkThreadPool = nullptr;
static void deletePathWorkThreadPool()
@@ -285,9 +289,16 @@ static void deletePathWorkThreadPool()
delete pathWorkThreadPool;
pathWorkThreadPool = nullptr;
}
+#endif
void QQuickShapeGenericRenderer::endSync(bool async)
{
+#if !QT_CONFIG(thread)
+ // Force synchronous mode for the no-thread configuration due
+ // to lack of QThreadPool.
+ async = false;
+#endif
+
bool didKickOffAsync = false;
for (int i = 0; i < m_sp.count(); ++i) {
@@ -311,13 +322,14 @@ void QQuickShapeGenericRenderer::endSync(bool async)
continue;
}
+#if QT_CONFIG(thread)
if (async && !pathWorkThreadPool) {
qAddPostRoutine(deletePathWorkThreadPool);
pathWorkThreadPool = new QThreadPool;
const int idealCount = QThread::idealThreadCount();
pathWorkThreadPool->setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4);
}
-
+#endif
if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) {
d.path.setFillRule(d.fillRule);
if (m_api == QSGRendererInterface::Unknown)
@@ -348,7 +360,9 @@ void QQuickShapeGenericRenderer::endSync(bool async)
r->deleteLater();
});
didKickOffAsync = true;
+#if QT_CONFIG(thread)
pathWorkThreadPool->start(r);
+#endif
} else {
triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices, &d.indexType, q_supportsElementIndexUint(m_api));
}
@@ -376,7 +390,9 @@ void QQuickShapeGenericRenderer::endSync(bool async)
r->deleteLater();
});
didKickOffAsync = true;
+#if QT_CONFIG(thread)
pathWorkThreadPool->start(r);
+#endif
} else {
triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices,
QSize(m_item->width(), m_item->height()));
diff --git a/src/imports/shapes/qquickshapegenericrenderer_p.h b/src/quickshapes/qquickshapegenericrenderer_p.h
index 11070ae7dc..9928d7ab72 100644
--- a/src/imports/shapes/qquickshapegenericrenderer_p.h
+++ b/src/quickshapes/qquickshapegenericrenderer_p.h
@@ -51,7 +51,8 @@
// We mean it.
//
-#include "qquickshape_p_p.h"
+#include <QtQuickShapes/private/qquickshapesglobal_p.h>
+#include <QtQuickShapes/private/qquickshape_p_p.h>
#include <qsgnode.h>
#include <qsggeometry.h>
#include <qsgmaterial.h>
diff --git a/src/imports/shapes/qquickshapenvprrenderer.cpp b/src/quickshapes/qquickshapenvprrenderer.cpp
index 51af0d8961..51af0d8961 100644
--- a/src/imports/shapes/qquickshapenvprrenderer.cpp
+++ b/src/quickshapes/qquickshapenvprrenderer.cpp
diff --git a/src/imports/shapes/qquickshapenvprrenderer_p.h b/src/quickshapes/qquickshapenvprrenderer_p.h
index f6c9fc169e..d40eb1bce9 100644
--- a/src/imports/shapes/qquickshapenvprrenderer_p.h
+++ b/src/quickshapes/qquickshapenvprrenderer_p.h
@@ -51,8 +51,9 @@
// We mean it.
//
-#include "qquickshape_p_p.h"
-#include "qquicknvprfunctions_p.h"
+#include <QtQuickShapes/private/qquickshapesglobal_p.h>
+#include <QtQuickShapes/private/qquickshape_p_p.h>
+#include <QtQuickShapes/private/qquicknvprfunctions_p.h>
#include <qsgrendernode.h>
#include <QColor>
#include <QVector4D>
diff --git a/src/quickshapes/qquickshapesglobal.h b/src/quickshapes/qquickshapesglobal.h
new file mode 100644
index 0000000000..eb279c5d14
--- /dev/null
+++ b/src/quickshapes/qquickshapesglobal.h
@@ -0,0 +1,56 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL3$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free
+** Software Foundation and appearing in the file LICENSE.GPL included in
+** the packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 2.0 requirements will be
+** met: http://www.gnu.org/licenses/gpl-2.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QQUICKSHAPESGLOBAL_H
+#define QQUICKSHAPESGLOBAL_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_STATIC
+# if defined(QT_BUILD_QUICKSHAPES_LIB)
+# define Q_QUICKSHAPES_EXPORT Q_DECL_EXPORT
+# else
+# define Q_QUICKSHAPES_EXPORT Q_DECL_IMPORT
+# endif
+#else
+# define Q_QUICKSHAPES_EXPORT
+#endif
+
+QT_END_NAMESPACE
+
+#endif // QQUICKSHAPESGLOBAL_H
+
diff --git a/src/quick/handlers/qquickhandlersmodule_p.h b/src/quickshapes/qquickshapesglobal_p.h
index 7eb8d39b98..2f559b45a0 100644
--- a/src/quick/handlers/qquickhandlersmodule_p.h
+++ b/src/quickshapes/qquickshapesglobal_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -37,8 +37,8 @@
**
****************************************************************************/
-#ifndef QQUICKHANDLERSMODULE_P_H
-#define QQUICKHANDLERSMODULE_P_H
+#ifndef QQUICKSHAPESGLOBAL_P_H
+#define QQUICKSHAPESGLOBAL_P_H
//
// W A R N I N G
@@ -51,18 +51,13 @@
// We mean it.
//
-#include <qqml.h>
-#include <private/qtquickglobal_p.h>
+#include "qquickshapesglobal.h"
QT_BEGIN_NAMESPACE
-class Q_QUICK_PRIVATE_EXPORT QQuickHandlersModule
-{
-public:
- static void defineModule();
-};
+#define Q_QUICKSHAPES_PRIVATE_EXPORT Q_QUICKSHAPES_EXPORT
QT_END_NAMESPACE
-#endif // QQUICKHANDLERSMODULE_P_H
+#endif // QQUICKSHAPESGLOBAL_P_H
diff --git a/src/imports/shapes/qquickshapesoftwarerenderer.cpp b/src/quickshapes/qquickshapesoftwarerenderer.cpp
index 0f5c3604b5..0f5c3604b5 100644
--- a/src/imports/shapes/qquickshapesoftwarerenderer.cpp
+++ b/src/quickshapes/qquickshapesoftwarerenderer.cpp
diff --git a/src/imports/shapes/qquickshapesoftwarerenderer_p.h b/src/quickshapes/qquickshapesoftwarerenderer_p.h
index 0abc2e37b0..11e658b4b7 100644
--- a/src/imports/shapes/qquickshapesoftwarerenderer_p.h
+++ b/src/quickshapes/qquickshapesoftwarerenderer_p.h
@@ -51,7 +51,8 @@
// We mean it.
//
-#include "qquickshape_p_p.h"
+#include <QtQuickShapes/private/qquickshapesglobal_p.h>
+#include <QtQuickShapes/private/qquickshape_p_p.h>
#include <qsgrendernode.h>
#include <QPen>
#include <QBrush>
diff --git a/src/imports/shapes/qtquickshapesplugin.qrc b/src/quickshapes/qtquickshapes.qrc
index f139861693..f139861693 100644
--- a/src/imports/shapes/qtquickshapesplugin.qrc
+++ b/src/quickshapes/qtquickshapes.qrc
diff --git a/src/quickshapes/quickshapes.pro b/src/quickshapes/quickshapes.pro
new file mode 100644
index 0000000000..5a59dec18e
--- /dev/null
+++ b/src/quickshapes/quickshapes.pro
@@ -0,0 +1,33 @@
+TARGET = QtQuickShapes
+
+QT = core gui-private qml quick-private
+
+CONFIG += simd optimize_full internal_module
+
+HEADERS += \
+ qquickshapesglobal.h \
+ qquickshapesglobal_p.h \
+ qquickshape_p.h \
+ qquickshape_p_p.h \
+ qquickshapegenericrenderer_p.h \
+ qquickshapesoftwarerenderer_p.h
+
+SOURCES += \
+ qquickshape.cpp \
+ qquickshapegenericrenderer.cpp \
+ qquickshapesoftwarerenderer.cpp
+
+qtConfig(opengl) {
+ HEADERS += \
+ qquicknvprfunctions_p.h \
+ qquicknvprfunctions_p_p.h \
+ qquickshapenvprrenderer_p.h
+
+ SOURCES += \
+ qquicknvprfunctions.cpp \
+ qquickshapenvprrenderer.cpp
+}
+
+RESOURCES += qtquickshapes.qrc
+
+load(qt_module)
diff --git a/src/imports/shapes/shaders/blit.frag b/src/quickshapes/shaders/blit.frag
index 505f0db179..505f0db179 100644
--- a/src/imports/shapes/shaders/blit.frag
+++ b/src/quickshapes/shaders/blit.frag
diff --git a/src/imports/shapes/shaders/blit.vert b/src/quickshapes/shaders/blit.vert
index f8306bd945..f8306bd945 100644
--- a/src/imports/shapes/shaders/blit.vert
+++ b/src/quickshapes/shaders/blit.vert
diff --git a/src/imports/shapes/shaders/blit_core.frag b/src/quickshapes/shaders/blit_core.frag
index 7073808fba..7073808fba 100644
--- a/src/imports/shapes/shaders/blit_core.frag
+++ b/src/quickshapes/shaders/blit_core.frag
diff --git a/src/imports/shapes/shaders/blit_core.vert b/src/quickshapes/shaders/blit_core.vert
index 5246441da3..5246441da3 100644
--- a/src/imports/shapes/shaders/blit_core.vert
+++ b/src/quickshapes/shaders/blit_core.vert
diff --git a/src/imports/shapes/shaders/conicalgradient.frag b/src/quickshapes/shaders/conicalgradient.frag
index af5fdd5ee0..af5fdd5ee0 100644
--- a/src/imports/shapes/shaders/conicalgradient.frag
+++ b/src/quickshapes/shaders/conicalgradient.frag
diff --git a/src/imports/shapes/shaders/conicalgradient.vert b/src/quickshapes/shaders/conicalgradient.vert
index 3350b0675a..3350b0675a 100644
--- a/src/imports/shapes/shaders/conicalgradient.vert
+++ b/src/quickshapes/shaders/conicalgradient.vert
diff --git a/src/imports/shapes/shaders/conicalgradient_core.frag b/src/quickshapes/shaders/conicalgradient_core.frag
index e18b80159a..e18b80159a 100644
--- a/src/imports/shapes/shaders/conicalgradient_core.frag
+++ b/src/quickshapes/shaders/conicalgradient_core.frag
diff --git a/src/imports/shapes/shaders/conicalgradient_core.vert b/src/quickshapes/shaders/conicalgradient_core.vert
index f94a56401b..f94a56401b 100644
--- a/src/imports/shapes/shaders/conicalgradient_core.vert
+++ b/src/quickshapes/shaders/conicalgradient_core.vert
diff --git a/src/imports/shapes/shaders/lineargradient.frag b/src/quickshapes/shaders/lineargradient.frag
index 7f4a739109..7f4a739109 100644
--- a/src/imports/shapes/shaders/lineargradient.frag
+++ b/src/quickshapes/shaders/lineargradient.frag
diff --git a/src/imports/shapes/shaders/lineargradient.vert b/src/quickshapes/shaders/lineargradient.vert
index eb21b8886b..eb21b8886b 100644
--- a/src/imports/shapes/shaders/lineargradient.vert
+++ b/src/quickshapes/shaders/lineargradient.vert
diff --git a/src/imports/shapes/shaders/lineargradient_core.frag b/src/quickshapes/shaders/lineargradient_core.frag
index 5908acfa67..5908acfa67 100644
--- a/src/imports/shapes/shaders/lineargradient_core.frag
+++ b/src/quickshapes/shaders/lineargradient_core.frag
diff --git a/src/imports/shapes/shaders/lineargradient_core.vert b/src/quickshapes/shaders/lineargradient_core.vert
index 60b56f38e3..60b56f38e3 100644
--- a/src/imports/shapes/shaders/lineargradient_core.vert
+++ b/src/quickshapes/shaders/lineargradient_core.vert
diff --git a/src/imports/shapes/shaders/radialgradient.frag b/src/quickshapes/shaders/radialgradient.frag
index 0f503bc0f7..0f503bc0f7 100644
--- a/src/imports/shapes/shaders/radialgradient.frag
+++ b/src/quickshapes/shaders/radialgradient.frag
diff --git a/src/imports/shapes/shaders/radialgradient.vert b/src/quickshapes/shaders/radialgradient.vert
index 3350b0675a..3350b0675a 100644
--- a/src/imports/shapes/shaders/radialgradient.vert
+++ b/src/quickshapes/shaders/radialgradient.vert
diff --git a/src/imports/shapes/shaders/radialgradient_core.frag b/src/quickshapes/shaders/radialgradient_core.frag
index 706ce53e4d..706ce53e4d 100644
--- a/src/imports/shapes/shaders/radialgradient_core.frag
+++ b/src/quickshapes/shaders/radialgradient_core.frag
diff --git a/src/imports/shapes/shaders/radialgradient_core.vert b/src/quickshapes/shaders/radialgradient_core.vert
index f94a56401b..f94a56401b 100644
--- a/src/imports/shapes/shaders/radialgradient_core.vert
+++ b/src/quickshapes/shaders/radialgradient_core.vert
diff --git a/src/src.pro b/src/src.pro
index 5177dfc743..1b2b4ef6f8 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -2,15 +2,18 @@ TEMPLATE = subdirs
CONFIG += ordered
include($$OUT_PWD/qml/qtqml-config.pri)
include($$OUT_PWD/quick/qtquick-config.pri)
-QT_FOR_CONFIG += qml quick-private
+QT_FOR_CONFIG += qml qml-private quick-private
SUBDIRS += \
qml
-qtHaveModule(gui):qtConfig(animation) {
+qtHaveModule(gui):qtConfig(qml-animation) {
SUBDIRS += \
- quick
+ quick \
+ quickshapes
+
+ qtConfig(testlib): \
+ SUBDIRS += qmltest
- qtConfig(testlib): SUBDIRS += qmltest
qtConfig(quick-particles): \
SUBDIRS += particles
qtHaveModule(widgets): SUBDIRS += quickwidgets
@@ -18,12 +21,15 @@ qtHaveModule(gui):qtConfig(animation) {
SUBDIRS += \
plugins \
- imports \
- qmldevtools
+ imports
+
+qtConfig(qml-devtools): SUBDIRS += qmldevtools
qmldevtools.depends = qml
qtConfig(qml-network) {
QT_FOR_CONFIG += network
- qtConfig(localserver):qtConfig(qml-debug): SUBDIRS += qmldebug
+ qtConfig(thread):qtConfig(localserver):qtConfig(qml-debug): SUBDIRS += qmldebug
}
+
+DISTFILES += sync.profile configure.json