aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs')
-rw-r--r--src/libs/3rdparty/CMakeLists.txt2
-rw-r--r--src/libs/3rdparty/cplusplus/TranslationUnit.cpp28
-rw-r--r--src/libs/3rdparty/cplusplus/TranslationUnit.h6
m---------src/libs/3rdparty/googletest0
-rw-r--r--src/libs/3rdparty/libptyqt/LICENSE-CONPTY21
-rw-r--r--src/libs/3rdparty/libptyqt/conptyprocess.cpp785
-rw-r--r--src/libs/3rdparty/libptyqt/unixptyprocess.cpp17
-rw-r--r--src/libs/3rdparty/lua/CMakeLists.txt75
-rw-r--r--src/libs/3rdparty/lua/LICENSE16
-rw-r--r--src/libs/3rdparty/lua/README6
-rw-r--r--src/libs/3rdparty/lua/doc/contents.html678
-rw-r--r--src/libs/3rdparty/lua/doc/index.css21
-rw-r--r--src/libs/3rdparty/lua/doc/logo.gifbin0 -> 9893 bytes
-rw-r--r--src/libs/3rdparty/lua/doc/lua.1155
-rw-r--r--src/libs/3rdparty/lua/doc/lua.css161
-rw-r--r--src/libs/3rdparty/lua/doc/luac.1118
-rw-r--r--src/libs/3rdparty/lua/doc/manual.css21
-rw-r--r--src/libs/3rdparty/lua/doc/manual.html12046
-rw-r--r--src/libs/3rdparty/lua/doc/osi-certified-72x60.pngbin0 -> 3774 bytes
-rw-r--r--src/libs/3rdparty/lua/doc/readme.html337
-rw-r--r--src/libs/3rdparty/lua/lua.qbs81
-rw-r--r--src/libs/3rdparty/lua/src/lapi.c1463
-rw-r--r--src/libs/3rdparty/lua/src/lapi.h52
-rw-r--r--src/libs/3rdparty/lua/src/lauxlib.c1112
-rw-r--r--src/libs/3rdparty/lua/src/lauxlib.h301
-rw-r--r--src/libs/3rdparty/lua/src/lbaselib.c549
-rw-r--r--src/libs/3rdparty/lua/src/lcode.c1871
-rw-r--r--src/libs/3rdparty/lua/src/lcode.h104
-rw-r--r--src/libs/3rdparty/lua/src/lcorolib.c210
-rw-r--r--src/libs/3rdparty/lua/src/lctype.c64
-rw-r--r--src/libs/3rdparty/lua/src/lctype.h101
-rw-r--r--src/libs/3rdparty/lua/src/ldblib.c483
-rw-r--r--src/libs/3rdparty/lua/src/ldebug.c924
-rw-r--r--src/libs/3rdparty/lua/src/ldebug.h63
-rw-r--r--src/libs/3rdparty/lua/src/ldo.c1024
-rw-r--r--src/libs/3rdparty/lua/src/ldo.h88
-rw-r--r--src/libs/3rdparty/lua/src/ldump.c230
-rw-r--r--src/libs/3rdparty/lua/src/lfunc.c294
-rw-r--r--src/libs/3rdparty/lua/src/lfunc.h64
-rw-r--r--src/libs/3rdparty/lua/src/lgc.c1739
-rw-r--r--src/libs/3rdparty/lua/src/lgc.h202
-rw-r--r--src/libs/3rdparty/lua/src/linit.c65
-rw-r--r--src/libs/3rdparty/lua/src/liolib.c828
-rw-r--r--src/libs/3rdparty/lua/src/ljumptab.h112
-rw-r--r--src/libs/3rdparty/lua/src/llex.c581
-rw-r--r--src/libs/3rdparty/lua/src/llex.h91
-rw-r--r--src/libs/3rdparty/lua/src/llimits.h380
-rw-r--r--src/libs/3rdparty/lua/src/lmathlib.c764
-rw-r--r--src/libs/3rdparty/lua/src/lmem.c215
-rw-r--r--src/libs/3rdparty/lua/src/lmem.h93
-rw-r--r--src/libs/3rdparty/lua/src/loadlib.c767
-rw-r--r--src/libs/3rdparty/lua/src/lobject.c602
-rw-r--r--src/libs/3rdparty/lua/src/lobject.h815
-rw-r--r--src/libs/3rdparty/lua/src/lopcodes.c104
-rw-r--r--src/libs/3rdparty/lua/src/lopcodes.h405
-rw-r--r--src/libs/3rdparty/lua/src/lopnames.h103
-rw-r--r--src/libs/3rdparty/lua/src/loslib.c428
-rw-r--r--src/libs/3rdparty/lua/src/lparser.c1967
-rw-r--r--src/libs/3rdparty/lua/src/lparser.h171
-rw-r--r--src/libs/3rdparty/lua/src/lprefix.h45
-rw-r--r--src/libs/3rdparty/lua/src/lstate.c445
-rw-r--r--src/libs/3rdparty/lua/src/lstate.h409
-rw-r--r--src/libs/3rdparty/lua/src/lstring.c273
-rw-r--r--src/libs/3rdparty/lua/src/lstring.h57
-rw-r--r--src/libs/3rdparty/lua/src/lstrlib.c1874
-rw-r--r--src/libs/3rdparty/lua/src/ltable.c980
-rw-r--r--src/libs/3rdparty/lua/src/ltable.h65
-rw-r--r--src/libs/3rdparty/lua/src/ltablib.c430
-rw-r--r--src/libs/3rdparty/lua/src/ltm.c271
-rw-r--r--src/libs/3rdparty/lua/src/ltm.h104
-rw-r--r--src/libs/3rdparty/lua/src/lua.c679
-rw-r--r--src/libs/3rdparty/lua/src/lua.h523
-rw-r--r--src/libs/3rdparty/lua/src/lua.hpp9
-rw-r--r--src/libs/3rdparty/lua/src/luac.c723
-rw-r--r--src/libs/3rdparty/lua/src/luaconf.h793
-rw-r--r--src/libs/3rdparty/lua/src/lualib.h52
-rw-r--r--src/libs/3rdparty/lua/src/lundump.c335
-rw-r--r--src/libs/3rdparty/lua/src/lundump.h36
-rw-r--r--src/libs/3rdparty/lua/src/lutf8lib.c291
-rw-r--r--src/libs/3rdparty/lua/src/lvm.c1901
-rw-r--r--src/libs/3rdparty/lua/src/lvm.h141
-rw-r--r--src/libs/3rdparty/lua/src/lzio.c68
-rw-r--r--src/libs/3rdparty/lua/src/lzio.h66
-rw-r--r--src/libs/3rdparty/sol2/CMakeLists.txt12
-rw-r--r--src/libs/3rdparty/sol2/LICENSE.txt20
-rw-r--r--src/libs/3rdparty/sol2/include/sol/config.hpp57
-rw-r--r--src/libs/3rdparty/sol2/include/sol/forward.hpp1340
-rw-r--r--src/libs/3rdparty/sol2/include/sol/sol.hpp29202
-rw-r--r--src/libs/3rdparty/sol2/sol2.qbs17
-rw-r--r--src/libs/3rdparty/sqlite/carray.c5
-rw-r--r--src/libs/3rdparty/sqlite/sqlite3.c20449
-rw-r--r--src/libs/3rdparty/sqlite/sqlite3.h457
-rw-r--r--src/libs/3rdparty/sqlite/sqlite3ext.h6
-rw-r--r--src/libs/advanceddockingsystem/dockmanager.cpp74
-rw-r--r--src/libs/advanceddockingsystem/dockmanager.h5
-rw-r--r--src/libs/advanceddockingsystem/workspaceview.cpp2
-rw-r--r--src/libs/cplusplus/CppDocument.cpp9
-rw-r--r--src/libs/cplusplus/CppDocument.h14
-rw-r--r--src/libs/cplusplus/FastPreprocessor.cpp6
-rw-r--r--src/libs/cplusplus/FastPreprocessor.h2
-rw-r--r--src/libs/cplusplus/Macro.h7
-rw-r--r--src/libs/cplusplus/PreprocessorClient.h2
-rw-r--r--src/libs/cplusplus/ResolveExpression.cpp8
-rw-r--r--src/libs/cplusplus/pp-engine.cpp21
-rw-r--r--src/libs/cplusplus/pp-engine.h1
-rw-r--r--src/libs/extensionsystem/CMakeLists.txt10
-rw-r--r--src/libs/extensionsystem/extensionsystem.qbs4
-rw-r--r--src/libs/extensionsystem/invoker.h2
-rw-r--r--src/libs/extensionsystem/optionsparser.cpp19
-rw-r--r--src/libs/extensionsystem/plugindetailsview.cpp10
-rw-r--r--src/libs/extensionsystem/pluginmanager.cpp303
-rw-r--r--src/libs/extensionsystem/pluginmanager.h13
-rw-r--r--src/libs/extensionsystem/pluginmanager_p.h15
-rw-r--r--src/libs/extensionsystem/pluginspec.cpp582
-rw-r--r--src/libs/extensionsystem/pluginspec.h192
-rw-r--r--src/libs/extensionsystem/pluginspec_p.h107
-rw-r--r--src/libs/extensionsystem/pluginview.cpp23
-rw-r--r--src/libs/languageserverprotocol/CMakeLists.txt1
-rw-r--r--src/libs/languageserverprotocol/callhierarchy.cpp5
-rw-r--r--src/libs/languageserverprotocol/callhierarchy.h2
-rw-r--r--src/libs/languageserverprotocol/clientcapabilities.cpp14
-rw-r--r--src/libs/languageserverprotocol/clientcapabilities.h25
-rw-r--r--src/libs/languageserverprotocol/jsonkeys.h4
-rw-r--r--src/libs/languageserverprotocol/jsonobject.h18
-rw-r--r--src/libs/languageserverprotocol/jsonrpcmessages.h14
-rw-r--r--src/libs/languageserverprotocol/languagefeatures.cpp10
-rw-r--r--src/libs/languageserverprotocol/languagefeatures.h3
-rw-r--r--src/libs/languageserverprotocol/languageserverprotocol.qbs2
-rw-r--r--src/libs/languageserverprotocol/lsptypes.cpp32
-rw-r--r--src/libs/languageserverprotocol/lsptypes.h13
-rw-r--r--src/libs/languageserverprotocol/lsputils.h21
-rw-r--r--src/libs/languageserverprotocol/progresssupport.cpp6
-rw-r--r--src/libs/languageserverprotocol/servercapabilities.cpp30
-rw-r--r--src/libs/languageserverprotocol/servercapabilities.h4
-rw-r--r--src/libs/languageserverprotocol/typehierarchy.cpp28
-rw-r--r--src/libs/languageserverprotocol/typehierarchy.h85
-rw-r--r--src/libs/languageserverprotocol/workspace.cpp5
-rw-r--r--src/libs/languageutils/componentversion.cpp69
-rw-r--r--src/libs/languageutils/componentversion.h61
-rw-r--r--src/libs/languageutils/fakemetaobject.cpp120
-rw-r--r--src/libs/libs.qbs2
-rw-r--r--src/libs/modelinglib/CMakeLists.txt4
-rw-r--r--src/libs/modelinglib/modelinglib.qbs8
-rw-r--r--src/libs/modelinglib/qmt/config/configcontroller.cpp33
-rw-r--r--src/libs/modelinglib/qmt/config/configcontroller.h4
-rw-r--r--src/libs/modelinglib/qmt/config/stereotypedefinitionparser.cpp145
-rw-r--r--src/libs/modelinglib/qmt/config/stereotypedefinitionparser.h3
-rw-r--r--src/libs/modelinglib/qmt/controller/namecontroller.cpp41
-rw-r--r--src/libs/modelinglib/qmt/controller/namecontroller.h10
-rw-r--r--src/libs/modelinglib/qmt/diagram/dobject.cpp34
-rw-r--r--src/libs/modelinglib/qmt/diagram/dobject.h13
-rw-r--r--src/libs/modelinglib/qmt/diagram/drelation.cpp25
-rw-r--r--src/libs/modelinglib/qmt/diagram/drelation.h32
-rw-r--r--src/libs/modelinglib/qmt/diagram_controller/dflatassignmentvisitor.cpp5
-rw-r--r--src/libs/modelinglib/qmt/diagram_controller/dupdatevisitor.cpp5
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/capabilities/windable.h2
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/diagramscenemodel.cpp81
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/diagramscenemodel.h2
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/items/classitem.cpp65
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/items/classitem.h1
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/items/componentitem.cpp46
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/items/componentitem.h1
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.cpp42
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.h1
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/items/itemitem.cpp43
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/items/itemitem.h1
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/items/objectitem.cpp184
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/items/objectitem.h9
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/items/packageitem.cpp40
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/items/packageitem.h1
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/items/relationitem.cpp52
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/items/relationitem.h2
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/items/stereotypedisplayvisitor.cpp4
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/parts/arrowitem.cpp24
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.cpp86
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.h5
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/parts/editabletextitem.cpp14
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/parts/editabletextitem.h2
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/parts/pathselectionitem.cpp10
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/parts/pathselectionitem.h3
-rw-r--r--src/libs/modelinglib/qmt/diagram_scene/parts/relationstarter.cpp2
-rw-r--r--src/libs/modelinglib/qmt/diagram_widgets_ui/diagramview.cpp45
-rw-r--r--src/libs/modelinglib/qmt/diagram_widgets_ui/diagramview.h7
-rw-r--r--src/libs/modelinglib/qmt/document_controller/documentcontroller.cpp6
-rw-r--r--src/libs/modelinglib/qmt/document_controller/documentcontroller.h6
-rw-r--r--src/libs/modelinglib/qmt/infrastructure/ioexceptions.cpp16
-rw-r--r--src/libs/modelinglib/qmt/infrastructure/ioexceptions.h20
-rw-r--r--src/libs/modelinglib/qmt/model/mdiagram.h2
-rw-r--r--src/libs/modelinglib/qmt/model/mobject.cpp9
-rw-r--r--src/libs/modelinglib/qmt/model/mobject.h5
-rw-r--r--src/libs/modelinglib/qmt/model_controller/mflatassignmentvisitor.cpp1
-rw-r--r--src/libs/modelinglib/qmt/model_ui/modeltreefilterdata.cpp43
-rw-r--r--src/libs/modelinglib/qmt/model_ui/modeltreefilterdata.h72
-rw-r--r--src/libs/modelinglib/qmt/model_ui/sortedtreemodel.cpp220
-rw-r--r--src/libs/modelinglib/qmt/model_ui/sortedtreemodel.h7
-rw-r--r--src/libs/modelinglib/qmt/model_ui/treemodel.cpp4
-rw-r--r--src/libs/modelinglib/qmt/model_ui/treemodel.h7
-rw-r--r--src/libs/modelinglib/qmt/model_widgets_ui/addrelatedelementsdialog.cpp426
-rw-r--r--src/libs/modelinglib/qmt/model_widgets_ui/addrelatedelementsdialog.h43
-rw-r--r--src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.cpp173
-rw-r--r--src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.h39
-rw-r--r--src/libs/modelinglib/qmt/model_widgets_ui/modeltreeview.cpp50
-rw-r--r--src/libs/modelinglib/qmt/model_widgets_ui/modeltreeview.h4
-rw-r--r--src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.cpp281
-rw-r--r--src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.h143
-rw-r--r--src/libs/modelinglib/qmt/project/project.cpp6
-rw-r--r--src/libs/modelinglib/qmt/project/project.h14
-rw-r--r--src/libs/modelinglib/qmt/project_controller/projectcontroller.cpp8
-rw-r--r--src/libs/modelinglib/qmt/project_controller/projectcontroller.h10
-rw-r--r--src/libs/modelinglib/qmt/serializer/diagramserializer.cpp31
-rw-r--r--src/libs/modelinglib/qmt/serializer/diagramserializer.h3
-rw-r--r--src/libs/modelinglib/qmt/serializer/infrastructureserializer.h18
-rw-r--r--src/libs/modelinglib/qmt/serializer/modelserializer.cpp1
-rw-r--r--src/libs/modelinglib/qmt/serializer/projectserializer.cpp10
-rw-r--r--src/libs/modelinglib/qmt/serializer/projectserializer.h6
-rw-r--r--src/libs/modelinglib/qmt/stereotype/customrelation.cpp5
-rw-r--r--src/libs/modelinglib/qmt/stereotype/customrelation.h8
-rw-r--r--src/libs/modelinglib/qmt/stereotype/shapevalue.cpp2
-rw-r--r--src/libs/modelinglib/qmt/stereotype/stereotypecontroller.cpp21
-rw-r--r--src/libs/modelinglib/qmt/stereotype/stereotypecontroller.h13
-rw-r--r--src/libs/modelinglib/qmt/stereotype/stereotypeicon.cpp15
-rw-r--r--src/libs/modelinglib/qmt/stereotype/stereotypeicon.h25
-rw-r--r--src/libs/modelinglib/qmt/style/defaultstyleengine.cpp201
-rw-r--r--src/libs/modelinglib/qmt/style/defaultstyleengine.h8
-rw-r--r--src/libs/modelinglib/qmt/style/relationvisuals.cpp58
-rw-r--r--src/libs/modelinglib/qmt/style/relationvisuals.h42
-rw-r--r--src/libs/modelinglib/qmt/style/stylecontroller.cpp6
-rw-r--r--src/libs/modelinglib/qmt/style/stylecontroller.h4
-rw-r--r--src/libs/modelinglib/qmt/style/styledrelation.cpp6
-rw-r--r--src/libs/modelinglib/qmt/style/styledrelation.h6
-rw-r--r--src/libs/modelinglib/qmt/style/styleengine.h4
-rw-r--r--src/libs/modelinglib/qmt/tasks/diagramscenecontroller.cpp56
-rw-r--r--src/libs/modelinglib/qmt/tasks/diagramscenecontroller.h7
-rw-r--r--src/libs/modelinglib/qmt/tasks/ielementtasks.h7
-rw-r--r--src/libs/modelinglib/qmt/tasks/voidelementtasks.cpp20
-rw-r--r--src/libs/modelinglib/qmt/tasks/voidelementtasks.h7
-rw-r--r--src/libs/modelinglib/qtserialization/inc/qark/serialize_container.h1
-rw-r--r--src/libs/nanotrace/CMakeLists.txt12
-rw-r--r--src/libs/nanotrace/nanotraceglobals.h18
-rw-r--r--src/libs/nanotrace/nanotracehr.cpp89
-rw-r--r--src/libs/nanotrace/nanotracehr.h636
-rw-r--r--src/libs/nanotrace/staticstring.h116
-rw-r--r--src/libs/qmljs/CMakeLists.txt1
-rw-r--r--src/libs/qmljs/jsoncheck.cpp14
-rw-r--r--src/libs/qmljs/qmljs.qbs1
-rw-r--r--src/libs/qmljs/qmljsbind.cpp17
-rw-r--r--src/libs/qmljs/qmljsbind.h1
-rw-r--r--src/libs/qmljs/qmljscheck.cpp26
-rw-r--r--src/libs/qmljs/qmljscheck.h2
-rw-r--r--src/libs/qmljs/qmljsicons.cpp5
-rw-r--r--src/libs/qmljs/qmljsicons.h1
-rw-r--r--src/libs/qmljs/qmljsimportdependencies.cpp16
-rw-r--r--src/libs/qmljs/qmljsindenter.cpp603
-rw-r--r--src/libs/qmljs/qmljsindenter.h51
-rw-r--r--src/libs/qmljs/qmljsinterpreter.cpp51
-rw-r--r--src/libs/qmljs/qmljsinterpreter.h29
-rw-r--r--src/libs/qmljs/qmljsplugindumper.cpp2
-rw-r--r--src/libs/qmljs/qmljsstaticanalysismessage.cpp7
-rw-r--r--src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h1
-rw-r--r--src/libs/qmlpuppetcommunication/commands/view3dactioncommand.cpp11
-rw-r--r--src/libs/qmlpuppetcommunication/container/imagecontainer.cpp9
-rw-r--r--src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h11
-rw-r--r--src/libs/qmlpuppetcommunication/types/enumeration.h13
-rw-r--r--src/libs/qtcreatorcdbext/CMakeLists.txt2
-rw-r--r--src/libs/qtcreatorcdbext/pytype.cpp22
-rw-r--r--src/libs/qtcreatorcdbext/pyvalue.cpp17
-rw-r--r--src/libs/solutions/spinner/spinner.cpp31
-rw-r--r--src/libs/solutions/spinner/spinner.h1
-rw-r--r--src/libs/solutions/tasking/barrier.cpp26
-rw-r--r--src/libs/solutions/tasking/barrier.h14
-rw-r--r--src/libs/solutions/tasking/concurrentcall.h16
-rw-r--r--src/libs/solutions/tasking/networkquery.cpp9
-rw-r--r--src/libs/solutions/tasking/networkquery.h17
-rw-r--r--src/libs/solutions/tasking/qprocesstask.cpp30
-rw-r--r--src/libs/solutions/tasking/qprocesstask.h24
-rw-r--r--src/libs/solutions/tasking/tasking_global.h13
-rw-r--r--src/libs/solutions/tasking/tasktree.cpp110
-rw-r--r--src/libs/solutions/tasking/tasktree.h57
-rw-r--r--src/libs/solutions/tasking/tasktreerunner.cpp5
-rw-r--r--src/libs/solutions/tasking/tasktreerunner.h14
-rw-r--r--src/libs/sqlite/CMakeLists.txt4
-rw-r--r--src/libs/sqlite/sqlitebasestatement.cpp204
-rw-r--r--src/libs/sqlite/sqlitebasestatement.h93
-rw-r--r--src/libs/sqlite/sqliteexception.cpp30
-rw-r--r--src/libs/sqlite/sqliteexception.h9
-rw-r--r--src/libs/sqlite/sqliteids.h32
-rw-r--r--src/libs/sqlite/sqliteindex.h19
-rw-r--r--src/libs/sqlite/sqlitetracing.cpp38
-rw-r--r--src/libs/sqlite/sqlitetracing.h29
-rw-r--r--src/libs/sqlite/sqlitevalue.h76
-rw-r--r--src/libs/tracing/CMakeLists.txt4
-rw-r--r--src/libs/tracing/timelineitemsrenderpass.cpp4
-rw-r--r--src/libs/tracing/timelinenotesrenderpass.cpp6
-rw-r--r--src/libs/utils/CMakeLists.txt6
-rw-r--r--src/libs/utils/algorithm.h21
-rw-r--r--src/libs/utils/aspects.cpp437
-rw-r--r--src/libs/utils/aspects.h92
-rw-r--r--src/libs/utils/async.cpp11
-rw-r--r--src/libs/utils/async.h5
-rw-r--r--src/libs/utils/buildablehelperlibrary.cpp2
-rw-r--r--src/libs/utils/changeset.cpp35
-rw-r--r--src/libs/utils/changeset.h6
-rw-r--r--src/libs/utils/checkablemessagebox.cpp5
-rw-r--r--src/libs/utils/clangutils.cpp4
-rw-r--r--src/libs/utils/codegeneration.cpp17
-rw-r--r--src/libs/utils/codegeneration.h3
-rw-r--r--src/libs/utils/commandline.cpp13
-rw-r--r--src/libs/utils/commandline.h16
-rw-r--r--src/libs/utils/crumblepath.cpp4
-rw-r--r--src/libs/utils/datafromprocess.h148
-rw-r--r--src/libs/utils/detailsbutton.cpp4
-rw-r--r--src/libs/utils/detailswidget.cpp2
-rw-r--r--src/libs/utils/devicefileaccess.cpp26
-rw-r--r--src/libs/utils/devicefileaccess.h9
-rw-r--r--src/libs/utils/deviceshell.cpp2
-rw-r--r--src/libs/utils/dropsupport.cpp4
-rw-r--r--src/libs/utils/environment.cpp92
-rw-r--r--src/libs/utils/environmentmodel.cpp3
-rw-r--r--src/libs/utils/execmenu.cpp3
-rw-r--r--src/libs/utils/expected.h9
-rw-r--r--src/libs/utils/externalterminalprocessimpl.cpp6
-rw-r--r--src/libs/utils/fancylineedit.cpp28
-rw-r--r--src/libs/utils/filepath.cpp80
-rw-r--r--src/libs/utils/filepath.h6
-rw-r--r--src/libs/utils/filestreamer.cpp2
-rw-r--r--src/libs/utils/fileutils.cpp15
-rw-r--r--src/libs/utils/fileutils.h4
-rw-r--r--src/libs/utils/fsengine/fsengine_impl.cpp8
-rw-r--r--src/libs/utils/fsengine/fsengine_impl.h5
-rw-r--r--src/libs/utils/fsengine/fsenginehandler.cpp31
-rw-r--r--src/libs/utils/fsengine/rootinjectfsengine.h38
-rw-r--r--src/libs/utils/futuresynchronizer.cpp17
-rw-r--r--src/libs/utils/futuresynchronizer.h2
-rw-r--r--src/libs/utils/guiutils.cpp12
-rw-r--r--src/libs/utils/guiutils.h5
-rw-r--r--src/libs/utils/highlightingitemdelegate.cpp2
-rw-r--r--src/libs/utils/historycompleter.cpp6
-rw-r--r--src/libs/utils/hostosinfo.cpp32
-rw-r--r--src/libs/utils/hostosinfo.h4
-rw-r--r--src/libs/utils/icon.cpp15
-rw-r--r--src/libs/utils/iconbutton.cpp2
-rw-r--r--src/libs/utils/images/debugger_overlay_small.pngbin158 -> 166 bytes
-rw-r--r--src/libs/utils/images/debugger_overlay_small@2x.pngbin175 -> 171 bytes
-rw-r--r--src/libs/utils/infobar.cpp4
-rw-r--r--src/libs/utils/infolabel.cpp5
-rw-r--r--src/libs/utils/launcherpackets.cpp6
-rw-r--r--src/libs/utils/launcherpackets.h1
-rw-r--r--src/libs/utils/launchersocket.cpp1
-rw-r--r--src/libs/utils/layoutbuilder.cpp1070
-rw-r--r--src/libs/utils/layoutbuilder.h570
-rw-r--r--src/libs/utils/lua.cpp30
-rw-r--r--src/libs/utils/lua.h34
-rw-r--r--src/libs/utils/macroexpander.cpp2
-rw-r--r--src/libs/utils/macroexpander.h1
-rw-r--r--src/libs/utils/namevalueitem.cpp11
-rw-r--r--src/libs/utils/namevalueitem.h2
-rw-r--r--src/libs/utils/namevaluesdialog.cpp5
-rw-r--r--src/libs/utils/osspecificaspects.h26
-rw-r--r--src/libs/utils/outputformatter.cpp79
-rw-r--r--src/libs/utils/outputformatter.h9
-rw-r--r--src/libs/utils/overlaywidget.cpp27
-rw-r--r--src/libs/utils/overlaywidget.h10
-rw-r--r--src/libs/utils/passworddialog.cpp8
-rw-r--r--src/libs/utils/pathchooser.cpp73
-rw-r--r--src/libs/utils/pathchooser.h2
-rw-r--r--src/libs/utils/persistentsettings.cpp30
-rw-r--r--src/libs/utils/processhandle_mac.mm3
-rw-r--r--src/libs/utils/processhelper.cpp11
-rw-r--r--src/libs/utils/processhelper.h3
-rw-r--r--src/libs/utils/processinfo.cpp2
-rw-r--r--src/libs/utils/processinterface.h1
-rw-r--r--src/libs/utils/projectintropage.cpp33
-rw-r--r--src/libs/utils/projectintropage.h1
-rw-r--r--src/libs/utils/qtcolorbutton.cpp4
-rw-r--r--src/libs/utils/qtcprocess.cpp (renamed from src/libs/utils/process.cpp)19
-rw-r--r--src/libs/utils/qtcprocess.h (renamed from src/libs/utils/process.h)11
-rw-r--r--src/libs/utils/ranges.h2
-rw-r--r--src/libs/utils/smallstring.h134
-rw-r--r--src/libs/utils/smallstringview.h2
-rw-r--r--src/libs/utils/span.h9
-rw-r--r--src/libs/utils/store.cpp6
-rw-r--r--src/libs/utils/stringutils.cpp8
-rw-r--r--src/libs/utils/stringutils.h2
-rw-r--r--src/libs/utils/stylehelper.cpp139
-rw-r--r--src/libs/utils/stylehelper.h4
-rw-r--r--src/libs/utils/terminalhooks.cpp2
-rw-r--r--src/libs/utils/terminalinterface.cpp18
-rw-r--r--src/libs/utils/theme/theme.cpp44
-rw-r--r--src/libs/utils/theme/theme.h7
-rw-r--r--src/libs/utils/threadutils.cpp5
-rw-r--r--src/libs/utils/treemodel.cpp6
-rw-r--r--src/libs/utils/unarchiver.cpp44
-rw-r--r--src/libs/utils/unarchiver.h8
-rw-r--r--src/libs/utils/utility.h14
-rw-r--r--src/libs/utils/utils.qbs11
-rw-r--r--src/libs/utils/wizard.cpp35
-rw-r--r--src/libs/utils/wizard.h4
-rw-r--r--src/libs/utils/wizardpage.h4
398 files changed, 97010 insertions, 11076 deletions
diff --git a/src/libs/3rdparty/CMakeLists.txt b/src/libs/3rdparty/CMakeLists.txt
index 8445a0d01b..364c9a425e 100644
--- a/src/libs/3rdparty/CMakeLists.txt
+++ b/src/libs/3rdparty/CMakeLists.txt
@@ -4,6 +4,8 @@ add_subdirectory(libvterm)
add_subdirectory(libptyqt)
add_subdirectory(qrcodegen)
add_subdirectory(qtkeychain)
+add_subdirectory(lua)
+add_subdirectory(sol2)
if(WIN32)
add_subdirectory(winpty)
diff --git a/src/libs/3rdparty/cplusplus/TranslationUnit.cpp b/src/libs/3rdparty/cplusplus/TranslationUnit.cpp
index e680ee2660..3e51352b14 100644
--- a/src/libs/3rdparty/cplusplus/TranslationUnit.cpp
+++ b/src/libs/3rdparty/cplusplus/TranslationUnit.cpp
@@ -27,7 +27,7 @@
#include "Literals.h"
#include "DiagnosticClient.h"
-#include "cppassert.h"
+#include <utils/qtcassert.h>
#include <utils/textutils.h>
#include <stack>
@@ -88,7 +88,7 @@ int TranslationUnit::sourceLength() const
void TranslationUnit::setSource(const char *source, int size)
{
- CPP_CHECK(source);
+ QTC_ASSERT(source, return);
_firstSourceChar = source;
_lastSourceChar = source + size;
}
@@ -191,6 +191,8 @@ void TranslationUnit::tokenize()
int lineColumnIdx = 0;
Token tk;
+ int macroOffset = -1;
+ int macroLength = -1;
do {
lex(&tk);
@@ -209,17 +211,12 @@ recognize:
lex(&tk);
// Gather where the expansion happens and its length.
- //int macroOffset = static_cast<int>(strtoul(tk.spell(), 0, 0));
+ macroOffset = static_cast<int>(strtoul(tk.spell(), 0, 0));
lex(&tk);
lex(&tk); // Skip the separating comma
- //int macroLength = static_cast<int>(strtoul(tk.spell(), 0, 0));
+ macroLength = static_cast<int>(strtoul(tk.spell(), 0, 0));
lex(&tk);
- // NOTE: We are currently not using the macro offset and length. They
- // are kept here for now because of future use.
- //Q_UNUSED(macroOffset)
- //Q_UNUSED(macroLength)
-
// Now we need to gather the real line and columns from the upcoming
// tokens. But notice this is only relevant for tokens which are expanded
// but not generated.
@@ -307,6 +304,11 @@ recognize:
tk.f.generated = currentGenerated;
_tokens->push_back(tk);
+
+ if (currentExpanded) {
+ QTC_ASSERT(macroOffset != -1 && macroLength != -1, continue);
+ _expansionPositions[int(_tokens->size()) - 1] = std::make_pair(macroOffset, macroLength);
+ }
} while (tk.kind());
for (; ! braces.empty(); braces.pop()) {
@@ -462,6 +464,14 @@ int TranslationUnit::getTokenEndPositionInDocument(const Token &token,
return Utils::Text::positionInText(doc, line, column);
}
+std::pair<int, int> TranslationUnit::getExpansionPosition(int tokenIndex) const
+{
+ QTC_ASSERT(tokenIndex < int(_tokens->size()) && tokenAt(tokenIndex).generated(), return {});
+ const auto it = _expansionPositions.find(tokenIndex);
+ QTC_ASSERT(it != _expansionPositions.end(), return {});
+ return it->second;
+}
+
void TranslationUnit::getPosition(int utf16charOffset,
int *line,
int *column,
diff --git a/src/libs/3rdparty/cplusplus/TranslationUnit.h b/src/libs/3rdparty/cplusplus/TranslationUnit.h
index 40f79d0091..1332549ffd 100644
--- a/src/libs/3rdparty/cplusplus/TranslationUnit.h
+++ b/src/libs/3rdparty/cplusplus/TranslationUnit.h
@@ -133,6 +133,7 @@ public:
const StringLiteral **fileName = nullptr) const;
int getTokenPositionInDocument(const Token token, const QTextDocument *doc) const;
int getTokenEndPositionInDocument(const Token &token, const QTextDocument *doc) const;
+ std::pair<int, int> getExpansionPosition(int tokenIndex) const;
void pushLineOffset(int offset);
void pushPreprocessorLine(int utf16charOffset,
@@ -183,6 +184,11 @@ private:
std::vector<Token> *_comments;
std::vector<int> _lineOffsets;
std::vector<PPLine> _ppLines;
+
+ // Offset and length. Note that in contrast to token offsets, this is a raw file offset
+ // with no preprocessor prefix.
+ std::unordered_map<int, std::pair<int, int>> _expansionPositions;
+
typedef std::unordered_map<unsigned, std::pair<int, int> > TokenLineColumn;
TokenLineColumn _expandedLineColumn;
MemoryPool *_pool;
diff --git a/src/libs/3rdparty/googletest b/src/libs/3rdparty/googletest
-Subproject b796f7d44681514f58a683a3a71ff17c94edb0c
+Subproject f8d7d77c06936315286eb55f8de22cd23c18857
diff --git a/src/libs/3rdparty/libptyqt/LICENSE-CONPTY b/src/libs/3rdparty/libptyqt/LICENSE-CONPTY
new file mode 100644
index 0000000000..8cb179cdb6
--- /dev/null
+++ b/src/libs/3rdparty/libptyqt/LICENSE-CONPTY
@@ -0,0 +1,21 @@
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/src/libs/3rdparty/libptyqt/conptyprocess.cpp b/src/libs/3rdparty/libptyqt/conptyprocess.cpp
index 6464dbeb34..94bb53b583 100644
--- a/src/libs/3rdparty/libptyqt/conptyprocess.cpp
+++ b/src/libs/3rdparty/libptyqt/conptyprocess.cpp
@@ -10,8 +10,740 @@
#include <qt_windows.h>
+#ifdef QTCREATOR_PCH_H
+#ifndef IN
+#define IN
+#endif
+#ifndef OUT
+#define OUT
+#endif
+#endif
+
+#include <winternl.h>
+
#define READ_INTERVAL_MSEC 500
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT license.
+
+#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
+
+////////////////////////////////////////////////////////////////////////////////////////
+/// Ported from wil/result_macros.h
+
+#define FAILED_NTSTATUS(status) (((NTSTATUS)(status)) < 0)
+#define SUCCEEDED_NTSTATUS(status) (((NTSTATUS)(status)) >= 0)
+
+#define RETURN_IF_NTSTATUS_FAILED(call) if(FAILED_NTSTATUS(call)) return E_FAIL;
+#define RETURN_IF_WIN32_BOOL_FALSE(call) if((call) == FALSE) return E_FAIL;
+#define RETURN_IF_NULL_ALLOC(call) if((call) == nullptr) return E_OUTOFMEMORY;
+#define RETURN_IF_FAILED(call) {HRESULT hr = (call); if(hr != S_OK)return hr; }
+
+//! Set zero or more bitflags specified by `flags` in the variable `var`.
+#define WI_SetAllFlags(var, flags) ((var) |= (flags))
+//! Set a single compile-time constant `flag` in the variable `var`.
+#define WI_SetFlag(var, flag) WI_SetAllFlags(var,flag)
+
+////////////////////////////////////////////////////////////////////////////////////////
+/// Ported from wil
+class unique_hmodule : public std::unique_ptr<std::remove_pointer_t<HMODULE>, decltype(&FreeLibrary)>
+{
+public:
+ unique_hmodule() : std::unique_ptr<std::remove_pointer_t<HMODULE>, decltype(&FreeLibrary)>(nullptr, FreeLibrary) {}
+ unique_hmodule(HMODULE module) : std::unique_ptr<std::remove_pointer_t<HMODULE>, decltype(&FreeLibrary)>(module, FreeLibrary) {}
+};
+
+class unique_handle : public std::unique_ptr<std::remove_pointer_t<HANDLE>, decltype(&CloseHandle)>
+{
+public:
+ unique_handle() : std::unique_ptr<std::remove_pointer_t<HANDLE>, decltype(&CloseHandle)>(nullptr, CloseHandle) {}
+ unique_handle(HANDLE module) : std::unique_ptr<std::remove_pointer_t<HANDLE>, decltype(&CloseHandle)>(module, CloseHandle) {}
+
+
+ class AddressOf {
+ public:
+ AddressOf(unique_handle &h) : m_h(h) {}
+ ~AddressOf() {
+ m_h.reset(m_dest);
+ }
+
+ operator PHANDLE() {return &m_dest;}
+
+ HANDLE m_dest{INVALID_HANDLE_VALUE};
+ unique_handle &m_h;
+ };
+
+ AddressOf addressof() {
+ return AddressOf(*this);
+ }
+};
+
+class unique_process_information : public PROCESS_INFORMATION
+{
+public:
+ unique_process_information() {
+ hProcess = 0;
+ hThread = 0;
+ dwProcessId = 0;
+ dwThreadId = 0;
+ }
+ ~unique_process_information() {
+ if (hProcess)
+ {
+ CloseHandle(hProcess);
+ }
+
+ if (hThread)
+ {
+ CloseHandle(hThread);
+ }
+ }
+
+ PROCESS_INFORMATION* addressof() {
+ return this;
+ }
+};
+
+template <typename TLambda>
+class on_scope_exit {
+public:
+ TLambda m_func;
+ bool m_call{true};
+ on_scope_exit(TLambda &&func) : m_func(std::move(func)) {}
+ ~on_scope_exit() {if(m_call)m_func();}
+
+ void release() {m_call = false;}
+};
+
+template <typename TLambda>
+[[nodiscard]] inline auto scope_exit(TLambda&& lambda) noexcept
+{
+ return on_scope_exit<TLambda>(std::forward<TLambda>(lambda));
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+
+
+class WinNTControl
+{
+public:
+ [[nodiscard]] static NTSTATUS NtOpenFile(_Out_ PHANDLE FileHandle,
+ _In_ ACCESS_MASK DesiredAccess,
+ _In_ POBJECT_ATTRIBUTES ObjectAttributes,
+ _Out_ PIO_STATUS_BLOCK IoStatusBlock,
+ _In_ ULONG ShareAccess,
+ _In_ ULONG OpenOptions);
+
+private:
+ WinNTControl();
+
+ WinNTControl(WinNTControl const&) = delete;
+ void operator=(WinNTControl const&) = delete;
+
+ static WinNTControl& GetInstance();
+
+ unique_hmodule const _NtDllDll;
+
+ typedef NTSTATUS(NTAPI* PfnNtOpenFile)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, ULONG, ULONG);
+ PfnNtOpenFile const _NtOpenFile;
+};
+
+WinNTControl::WinNTControl() :
+ // NOTE: Use LoadLibraryExW with LOAD_LIBRARY_SEARCH_SYSTEM32 flag below to avoid unneeded directory traversal.
+ // This has triggered CPG boot IO warnings in the past.
+ _NtDllDll(/*THROW_LAST_ERROR_IF_NULL*/(LoadLibraryExW(L"ntdll.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32))),
+ _NtOpenFile(reinterpret_cast<PfnNtOpenFile>(/*THROW_LAST_ERROR_IF_NULL*/(GetProcAddress(_NtDllDll.get(), "NtOpenFile"))))
+{
+}
+
+// Routine Description:
+// - Provides the singleton pattern for WinNT control. Stores the single instance and returns it.
+// Arguments:
+// - <none>
+// Return Value:
+// - Reference to the single instance of NTDLL.dll wrapped methods.
+WinNTControl& WinNTControl::GetInstance()
+{
+ static WinNTControl Instance;
+ return Instance;
+}
+
+// Routine Description:
+// - Provides access to the NtOpenFile method documented at:
+// https://msdn.microsoft.com/en-us/library/bb432381(v=vs.85).aspx
+// Arguments:
+// - See definitions at MSDN
+// Return Value:
+// - See definitions at MSDN
+[[nodiscard]] NTSTATUS WinNTControl::NtOpenFile(_Out_ PHANDLE FileHandle,
+ _In_ ACCESS_MASK DesiredAccess,
+ _In_ POBJECT_ATTRIBUTES ObjectAttributes,
+ _Out_ PIO_STATUS_BLOCK IoStatusBlock,
+ _In_ ULONG ShareAccess,
+ _In_ ULONG OpenOptions)
+{
+ return GetInstance()._NtOpenFile(FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, ShareAccess, OpenOptions);
+}
+
+namespace DeviceHandle {
+
+/*++
+Routine Description:
+- This routine opens a handle to the console driver.
+
+Arguments:
+- Handle - Receives the handle.
+- DeviceName - Supplies the name to be used to open the console driver.
+- DesiredAccess - Supplies the desired access mask.
+- Parent - Optionally supplies the parent object.
+- Inheritable - Supplies a boolean indicating if the new handle is to be made inheritable.
+- OpenOptions - Supplies the open options to be passed to NtOpenFile. A common
+ option for clients is FILE_SYNCHRONOUS_IO_NONALERT, to make the handle
+ synchronous.
+
+Return Value:
+- NTSTATUS indicating if the handle was successfully created.
+--*/
+[[nodiscard]] NTSTATUS
+_CreateHandle(
+ _Out_ PHANDLE Handle,
+ _In_ PCWSTR DeviceName,
+ _In_ ACCESS_MASK DesiredAccess,
+ _In_opt_ HANDLE Parent,
+ _In_ BOOLEAN Inheritable,
+ _In_ ULONG OpenOptions)
+
+{
+ ULONG Flags = OBJ_CASE_INSENSITIVE;
+
+ if (Inheritable)
+ {
+ WI_SetFlag(Flags, OBJ_INHERIT);
+ }
+
+ UNICODE_STRING Name;
+#pragma warning(suppress : 26492) // const_cast is prohibited, but we can't avoid it for filling UNICODE_STRING.
+ Name.Buffer = const_cast<wchar_t*>(DeviceName);
+ //Name.Length = gsl::narrow_cast<USHORT>((wcslen(DeviceName) * sizeof(wchar_t)));
+ Name.Length = static_cast<unsigned short>((wcslen(DeviceName) * sizeof(wchar_t)));
+ Name.MaximumLength = Name.Length + sizeof(wchar_t);
+
+ OBJECT_ATTRIBUTES ObjectAttributes;
+#pragma warning(suppress : 26477) // The QOS part of this macro in the define is 0. Can't fix that.
+ InitializeObjectAttributes(&ObjectAttributes,
+ &Name,
+ Flags,
+ Parent,
+ nullptr);
+
+ IO_STATUS_BLOCK IoStatus;
+ return WinNTControl::NtOpenFile(Handle,
+ DesiredAccess,
+ &ObjectAttributes,
+ &IoStatus,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ OpenOptions);
+}
+
+/*++
+Routine Description:
+- This routine creates a handle to an input or output client of the given
+ server. No control io is sent to the server as this request must be coming
+ from the server itself.
+
+Arguments:
+- Handle - Receives a handle to the new client.
+- ServerHandle - Supplies a handle to the server to which to attach the
+ newly created client.
+- Name - Supplies the name of the client object.
+- Inheritable - Supplies a flag indicating if the handle must be inheritable.
+
+Return Value:
+- NTSTATUS indicating if the client was successfully created.
+--*/
+[[nodiscard]] NTSTATUS
+CreateClientHandle(
+ _Out_ PHANDLE Handle,
+ _In_ HANDLE ServerHandle,
+ _In_ PCWSTR Name,
+ _In_ BOOLEAN Inheritable)
+{
+ return _CreateHandle(Handle,
+ Name,
+ GENERIC_WRITE | GENERIC_READ | SYNCHRONIZE,
+ ServerHandle,
+ Inheritable,
+ FILE_SYNCHRONOUS_IO_NONALERT);
+}
+
+/*++
+Routine Description:
+- This routine creates a new server on the driver and returns a handle to it.
+
+Arguments:
+- Handle - Receives a handle to the new server.
+- Inheritable - Supplies a flag indicating if the handle must be inheritable.
+
+Return Value:
+- NTSTATUS indicating if the console was successfully created.
+--*/
+[[nodiscard]] NTSTATUS
+CreateServerHandle(
+ _Out_ PHANDLE Handle,
+ _In_ BOOLEAN Inheritable)
+{
+ return _CreateHandle(Handle,
+ L"\\Device\\ConDrv\\Server",
+ GENERIC_ALL,
+ nullptr,
+ Inheritable,
+ 0);
+}
+
+
+} // namespace DeviceHandle
+
+[[nodiscard]] static inline NTSTATUS CreateClientHandle(PHANDLE Handle, HANDLE ServerHandle, PCWSTR Name, BOOLEAN Inheritable)
+{
+ return DeviceHandle::CreateClientHandle(Handle, ServerHandle, Name, Inheritable);
+}
+
+[[nodiscard]] static inline NTSTATUS CreateServerHandle(PHANDLE Handle, BOOLEAN Inheritable)
+{
+ return DeviceHandle::CreateServerHandle(Handle, Inheritable);
+}
+
+typedef struct _PseudoConsole
+{
+ HANDLE hSignal;
+ HANDLE hPtyReference;
+ HANDLE hConPtyProcess;
+} PseudoConsole;
+
+// Signals
+// These are not defined publicly, but are used for controlling the conpty via
+// the signal pipe.
+#define PTY_SIGNAL_CLEAR_WINDOW (2u)
+#define PTY_SIGNAL_RESIZE_WINDOW (8u)
+
+// CreatePseudoConsole Flags
+// The other flag (PSEUDOCONSOLE_INHERIT_CURSOR) is actually defined in consoleapi.h in the OS repo
+#ifndef PSEUDOCONSOLE_INHERIT_CURSOR
+#define PSEUDOCONSOLE_INHERIT_CURSOR (0x1)
+#endif
+#define PSEUDOCONSOLE_RESIZE_QUIRK (0x2)
+#define PSEUDOCONSOLE_WIN32_INPUT_MODE (0x4)
+
+static QString qSystemDirectory()
+{
+ static const QString result = []() -> QString {
+ QVarLengthArray<wchar_t, MAX_PATH> fullPath = {};
+ UINT retLen = ::GetSystemDirectoryW(fullPath.data(), MAX_PATH);
+ if (retLen > MAX_PATH) {
+ fullPath.resize(retLen);
+ retLen = ::GetSystemDirectoryW(fullPath.data(), retLen);
+ }
+ // in some rare cases retLen might be 0
+ return QString::fromWCharArray(fullPath.constData(), int(retLen));
+ }();
+ return result;
+}
+
+// Function Description:
+// - Returns the path to conhost.exe as a process heap string.
+static QString _InboxConsoleHostPath()
+{
+ return QString("\\\\?\\%1\\conhost.exe").arg(qSystemDirectory());
+}
+
+// Function Description:
+// - Returns the path to either conhost.exe or the side-by-side OpenConsole, depending on whether this
+// module is building with Windows and OpenConsole could be found.
+// Return Value:
+// - A pointer to permanent storage containing the path to the console host.
+static const wchar_t* _ConsoleHostPath()
+{
+ // Use the magic of magic statics to only calculate this once.
+ static QString consoleHostPath = _InboxConsoleHostPath();
+ return reinterpret_cast<const wchar_t*>(consoleHostPath.utf16());
+}
+
+static bool _HandleIsValid(HANDLE h) noexcept
+{
+ return (h != INVALID_HANDLE_VALUE) && (h != nullptr);
+}
+
+
+
+HRESULT _CreatePseudoConsole(const HANDLE hToken,
+ const COORD size,
+ const HANDLE hInput,
+ const HANDLE hOutput,
+ const DWORD dwFlags,
+ _Inout_ PseudoConsole* pPty)
+{
+ if (pPty == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+ if (size.X == 0 || size.Y == 0)
+ {
+ return E_INVALIDARG;
+ }
+
+ unique_handle serverHandle;
+ RETURN_IF_NTSTATUS_FAILED(CreateServerHandle(serverHandle.addressof(), TRUE));
+
+ unique_handle signalPipeConhostSide;
+ unique_handle signalPipeOurSide;
+
+ SECURITY_ATTRIBUTES sa;
+ sa.nLength = sizeof(sa);
+ // Mark inheritable for signal handle when creating. It'll have the same value on the other side.
+ sa.bInheritHandle = FALSE;
+ sa.lpSecurityDescriptor = nullptr;
+
+ RETURN_IF_WIN32_BOOL_FALSE(CreatePipe(signalPipeConhostSide.addressof(), signalPipeOurSide.addressof(), &sa, 0));
+ RETURN_IF_WIN32_BOOL_FALSE(SetHandleInformation(signalPipeConhostSide.get(), HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT));
+
+ // GH4061: Ensure that the path to executable in the format is escaped so C:\Program.exe cannot collide with C:\Program Files
+ const wchar_t* pwszFormat = L"\"%s\" --headless %s%s%s--width %hu --height %hu --signal 0x%x --server 0x%x";
+ // This is plenty of space to hold the formatted string
+ wchar_t cmd[MAX_PATH]{};
+ const BOOL bInheritCursor = (dwFlags & PSEUDOCONSOLE_INHERIT_CURSOR) == PSEUDOCONSOLE_INHERIT_CURSOR;
+ const BOOL bResizeQuirk = (dwFlags & PSEUDOCONSOLE_RESIZE_QUIRK) == PSEUDOCONSOLE_RESIZE_QUIRK;
+ const BOOL bWin32InputMode = (dwFlags & PSEUDOCONSOLE_WIN32_INPUT_MODE) == PSEUDOCONSOLE_WIN32_INPUT_MODE;
+ swprintf_s(cmd,
+ MAX_PATH,
+ pwszFormat,
+ _ConsoleHostPath(),
+ bInheritCursor ? L"--inheritcursor " : L"",
+ bWin32InputMode ? L"--win32input " : L"",
+ bResizeQuirk ? L"--resizeQuirk " : L"",
+ size.X,
+ size.Y,
+ signalPipeConhostSide.get(),
+ serverHandle.get());
+
+ STARTUPINFOEXW siEx{ 0 };
+ siEx.StartupInfo.cb = sizeof(STARTUPINFOEXW);
+ siEx.StartupInfo.hStdInput = hInput;
+ siEx.StartupInfo.hStdOutput = hOutput;
+ siEx.StartupInfo.hStdError = hOutput;
+ siEx.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
+
+ // Only pass the handles we actually want the conhost to know about to it:
+ const size_t INHERITED_HANDLES_COUNT = 4;
+ HANDLE inheritedHandles[INHERITED_HANDLES_COUNT];
+ inheritedHandles[0] = serverHandle.get();
+ inheritedHandles[1] = hInput;
+ inheritedHandles[2] = hOutput;
+ inheritedHandles[3] = signalPipeConhostSide.get();
+
+ // Get the size of the attribute list. We need one attribute, the handle list.
+ SIZE_T listSize = 0;
+ InitializeProcThreadAttributeList(nullptr, 1, 0, &listSize);
+
+ // I have to use a HeapAlloc here because kernelbase can't link new[] or delete[]
+ PPROC_THREAD_ATTRIBUTE_LIST attrList = static_cast<PPROC_THREAD_ATTRIBUTE_LIST>(HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, listSize));
+ RETURN_IF_NULL_ALLOC(attrList);
+ auto attrListDelete = scope_exit([&]() noexcept {
+ HeapFree(GetProcessHeap(), 0, attrList);
+ });
+
+ siEx.lpAttributeList = attrList;
+ RETURN_IF_WIN32_BOOL_FALSE(InitializeProcThreadAttributeList(siEx.lpAttributeList, 1, 0, &listSize));
+ // Set cleanup data for ProcThreadAttributeList when successful.
+ auto cleanupProcThreadAttribute = scope_exit([&]() noexcept {
+ DeleteProcThreadAttributeList(siEx.lpAttributeList);
+ });
+ RETURN_IF_WIN32_BOOL_FALSE(UpdateProcThreadAttribute(siEx.lpAttributeList,
+ 0,
+ PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+ inheritedHandles,
+ (INHERITED_HANDLES_COUNT * sizeof(HANDLE)),
+ nullptr,
+ nullptr));
+ unique_process_information pi;
+ { // wow64 disabled filesystem redirection scope
+ if (hToken == INVALID_HANDLE_VALUE || hToken == nullptr)
+ {
+ // Call create process
+ RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(_ConsoleHostPath(),
+ cmd,
+ nullptr,
+ nullptr,
+ TRUE,
+ EXTENDED_STARTUPINFO_PRESENT,
+ nullptr,
+ nullptr,
+ &siEx.StartupInfo,
+ pi.addressof()));
+ }
+ else
+ {
+ // Call create process
+ RETURN_IF_WIN32_BOOL_FALSE(CreateProcessAsUserW(hToken,
+ _ConsoleHostPath(),
+ cmd,
+ nullptr,
+ nullptr,
+ TRUE,
+ EXTENDED_STARTUPINFO_PRESENT,
+ nullptr,
+ nullptr,
+ &siEx.StartupInfo,
+ pi.addressof()));
+ }
+ }
+
+ // Move the process handle out of the PROCESS_INFORMATION into out Pseudoconsole
+ pPty->hConPtyProcess = pi.hProcess;
+ pi.hProcess = nullptr;
+
+ RETURN_IF_NTSTATUS_FAILED(CreateClientHandle(&pPty->hPtyReference,
+ serverHandle.get(),
+ L"\\Reference",
+ FALSE));
+
+ pPty->hSignal = signalPipeOurSide.release();
+
+ return S_OK;
+}
+
+// Function Description:
+// - Resizes the conpty
+// Arguments:
+// - hSignal: A signal pipe as returned by CreateConPty.
+// - size: The new dimensions of the conpty, in characters.
+// Return Value:
+// - S_OK if the call succeeded, else an appropriate HRESULT for failing to
+// write the resize message to the pty.
+HRESULT _ResizePseudoConsole(_In_ const PseudoConsole* const pPty, _In_ const COORD size)
+{
+ if (pPty == nullptr || size.X < 0 || size.Y < 0)
+ {
+ return E_INVALIDARG;
+ }
+
+ unsigned short signalPacket[3];
+ signalPacket[0] = PTY_SIGNAL_RESIZE_WINDOW;
+ signalPacket[1] = size.X;
+ signalPacket[2] = size.Y;
+
+ const BOOL fSuccess = WriteFile(pPty->hSignal, signalPacket, sizeof(signalPacket), nullptr, nullptr);
+ return fSuccess ? S_OK : HRESULT_FROM_WIN32(GetLastError());
+}
+
+// Function Description:
+// - Clears the conpty
+// Arguments:
+// - hSignal: A signal pipe as returned by CreateConPty.
+// Return Value:
+// - S_OK if the call succeeded, else an appropriate HRESULT for failing to
+// write the clear message to the pty.
+HRESULT _ClearPseudoConsole(_In_ const PseudoConsole* const pPty)
+{
+ if (pPty == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+
+ unsigned short signalPacket[1];
+ signalPacket[0] = PTY_SIGNAL_CLEAR_WINDOW;
+
+ const BOOL fSuccess = WriteFile(pPty->hSignal, signalPacket, sizeof(signalPacket), nullptr, nullptr);
+ return fSuccess ? S_OK : HRESULT_FROM_WIN32(GetLastError());
+}
+
+// Function Description:
+// - This closes each of the members of a PseudoConsole. It does not free the
+// data associated with the PseudoConsole. This is helpful for testing,
+// where we might stack allocate a PseudoConsole (instead of getting a
+// HPCON via the API).
+// Arguments:
+// - pPty: A pointer to a PseudoConsole struct.
+// Return Value:
+// - <none>
+void _ClosePseudoConsoleMembers(_In_ PseudoConsole* pPty)
+{
+ if (pPty != nullptr)
+ {
+ // See MSFT:19918626
+ // First break the signal pipe - this will trigger conhost to tear itself down
+ if (_HandleIsValid(pPty->hSignal))
+ {
+ CloseHandle(pPty->hSignal);
+ pPty->hSignal = nullptr;
+ }
+ // Then, wait on the conhost process before killing it.
+ // We do this to make sure the conhost finishes flushing any output it
+ // has yet to send before we hard kill it.
+ if (_HandleIsValid(pPty->hConPtyProcess))
+ {
+ // If the conhost is already dead, then that's fine. Presumably
+ // it's finished flushing it's output already.
+ DWORD dwExit = 0;
+ // If GetExitCodeProcess failed, it's likely conhost is already dead
+ // If so, skip waiting regardless of whatever error
+ // GetExitCodeProcess returned.
+ // We'll just go straight to killing conhost.
+ if (GetExitCodeProcess(pPty->hConPtyProcess, &dwExit) && dwExit == STILL_ACTIVE)
+ {
+ WaitForSingleObject(pPty->hConPtyProcess, INFINITE);
+ }
+
+ TerminateProcess(pPty->hConPtyProcess, 0);
+ CloseHandle(pPty->hConPtyProcess);
+ pPty->hConPtyProcess = nullptr;
+ }
+ // Then take care of the reference handle.
+ // TODO GH#1810: Closing the reference handle late leaves conhost thinking
+ // that we have an outstanding connected client.
+ if (_HandleIsValid(pPty->hPtyReference))
+ {
+ CloseHandle(pPty->hPtyReference);
+ pPty->hPtyReference = nullptr;
+ }
+ }
+}
+
+// Function Description:
+// - This closes each of the members of a PseudoConsole, and HeapFree's the
+// memory allocated to it. This should be used to cleanup any
+// PseudoConsoles that were created with CreatePseudoConsole.
+// Arguments:
+// - pPty: A pointer to a PseudoConsole struct.
+// Return Value:
+// - <none>
+VOID _ClosePseudoConsole(_In_ PseudoConsole* pPty)
+{
+ if (pPty != nullptr)
+ {
+ _ClosePseudoConsoleMembers(pPty);
+ HeapFree(GetProcessHeap(), 0, pPty);
+ }
+}
+
+extern "C" HRESULT ConptyCreatePseudoConsoleAsUser(_In_ HANDLE hToken,
+ _In_ COORD size,
+ _In_ HANDLE hInput,
+ _In_ HANDLE hOutput,
+ _In_ DWORD dwFlags,
+ _Out_ HPCON* phPC)
+{
+ if (phPC == nullptr)
+ {
+ return E_INVALIDARG;
+ }
+ *phPC = nullptr;
+ if ((!_HandleIsValid(hInput)) && (!_HandleIsValid(hOutput)))
+ {
+ return E_INVALIDARG;
+ }
+
+ PseudoConsole* pPty = (PseudoConsole*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PseudoConsole));
+ RETURN_IF_NULL_ALLOC(pPty);
+ auto cleanupPty = scope_exit([&]() noexcept {
+ _ClosePseudoConsole(pPty);
+ });
+
+ unique_handle duplicatedInput;
+ unique_handle duplicatedOutput;
+ RETURN_IF_WIN32_BOOL_FALSE(DuplicateHandle(GetCurrentProcess(), hInput, GetCurrentProcess(), duplicatedInput.addressof(), 0, TRUE, DUPLICATE_SAME_ACCESS));
+ RETURN_IF_WIN32_BOOL_FALSE(DuplicateHandle(GetCurrentProcess(), hOutput, GetCurrentProcess(), duplicatedOutput.addressof(), 0, TRUE, DUPLICATE_SAME_ACCESS));
+
+ RETURN_IF_FAILED(_CreatePseudoConsole(hToken, size, duplicatedInput.get(), duplicatedOutput.get(), dwFlags, pPty));
+
+ *phPC = (HPCON)pPty;
+ cleanupPty.release();
+
+ return S_OK;
+}
+
+// These functions are defined in the console l1 apiset, which is generated from
+// the consoleapi.apx file in minkernel\apiset\libs\Console.
+
+// Function Description:
+// Creates a "Pseudo-console" (conpty) with dimensions (in characters)
+// provided by the `size` parameter. The caller should provide two handles:
+// - `hInput` is used for writing input to the pty, encoded as UTF-8 and VT sequences.
+// - `hOutput` is used for reading the output of the pty, encoded as UTF-8 and VT sequences.
+// Once the call completes, `phPty` will receive a token value to identify this
+// conpty object. This value should be used in conjunction with the other
+// Pseudoconsole API's.
+// `dwFlags` is used to specify optional behavior to the created pseudoconsole.
+// The flags can be combinations of the following values:
+// INHERIT_CURSOR: This will cause the created conpty to attempt to inherit the
+// cursor position of the parent terminal application. This can be useful
+// for applications like `ssh`, where ssh (currently running in a terminal)
+// might want to create a pseudoterminal session for an child application
+// and the child inherit the cursor position of ssh.
+// The created conpty will immediately emit a "Device Status Request" VT
+// sequence to hOutput, that should be replied to on hInput in the format
+// "\x1b[<r>;<c>R", where `<r>` is the row and `<c>` is the column of the
+// cursor position.
+// This requires a cooperating terminal application - if a caller does not
+// reply to this message, the conpty will not process any input until it
+// does. Most *nix terminals and the Windows Console (after Windows 10
+// Anniversary Update) will be able to handle such a message.
+
+extern "C" HRESULT WINAPI ConptyCreatePseudoConsole(_In_ COORD size,
+ _In_ HANDLE hInput,
+ _In_ HANDLE hOutput,
+ _In_ DWORD dwFlags,
+ _Out_ HPCON* phPC)
+{
+ return ConptyCreatePseudoConsoleAsUser(INVALID_HANDLE_VALUE, size, hInput, hOutput, dwFlags, phPC);
+}
+
+// Function Description:
+// Resizes the given conpty to the specified size, in characters.
+extern "C" HRESULT WINAPI ConptyResizePseudoConsole(_In_ HPCON hPC, _In_ COORD size)
+{
+ const PseudoConsole* const pPty = (PseudoConsole*)hPC;
+ HRESULT hr = pPty == nullptr ? E_INVALIDARG : S_OK;
+ if (SUCCEEDED(hr))
+ {
+ hr = _ResizePseudoConsole(pPty, size);
+ }
+ return hr;
+}
+
+// Function Description:
+// - Clear the contents of the conpty buffer, leaving the cursor row at the top
+// of the viewport.
+// - This is used exclusively by ConPTY to support GH#1193, GH#1882. This allows
+// a terminal to clear the contents of the ConPTY buffer, which is important
+// if the user would like to be able to clear the terminal-side buffer.
+extern "C" HRESULT WINAPI ConptyClearPseudoConsole(_In_ HPCON hPC)
+{
+ const PseudoConsole* const pPty = (PseudoConsole*)hPC;
+ HRESULT hr = pPty == nullptr ? E_INVALIDARG : S_OK;
+ if (SUCCEEDED(hr))
+ {
+ hr = _ClearPseudoConsole(pPty);
+ }
+ return hr;
+}
+
+// Function Description:
+// Closes the conpty and all associated state.
+// Client applications attached to the conpty will also behave as though the
+// console window they were running in was closed.
+// This can fail if the conhost hosting the pseudoconsole failed to be
+// terminated, or if the pseudoconsole was already terminated.
+extern "C" VOID WINAPI ConptyClosePseudoConsole(_In_ HPCON hPC)
+{
+ PseudoConsole* const pPty = (PseudoConsole*)hPC;
+ if (pPty != nullptr)
+ {
+ _ClosePseudoConsole(pPty);
+ }
+}
+
+
//ConPTY is available only on Windows 10 released after 1903 (19H1) Windows release
class WindowsContext
{
@@ -38,30 +770,9 @@ public:
bool init()
{
- //already initialized
- if (createPseudoConsole)
- return true;
-
- //try to load symbols from library
- //if it fails -> we can't use ConPty API
- HANDLE kernel32Handle = LoadLibraryExW(L"kernel32.dll", 0, 0);
-
- if (kernel32Handle != nullptr)
- {
- createPseudoConsole = (CreatePseudoConsolePtr)GetProcAddress((HMODULE)kernel32Handle, "CreatePseudoConsole");
- resizePseudoConsole = (ResizePseudoConsolePtr)GetProcAddress((HMODULE)kernel32Handle, "ResizePseudoConsole");
- closePseudoConsole = (ClosePseudoConsolePtr)GetProcAddress((HMODULE)kernel32Handle, "ClosePseudoConsole");
- if (createPseudoConsole == NULL || resizePseudoConsole == NULL || closePseudoConsole == NULL)
- {
- m_lastError = QString("WindowsContext/ConPty error: %1").arg("Invalid on load API functions");
- return false;
- }
- }
- else
- {
- m_lastError = QString("WindowsContext/ConPty error: %1").arg("Unable to load kernel32");
- return false;
- }
+ createPseudoConsole = (CreatePseudoConsolePtr)ConptyCreatePseudoConsole;
+ resizePseudoConsole = (ResizePseudoConsolePtr)ConptyResizePseudoConsole;
+ closePseudoConsole = (ClosePseudoConsolePtr)ConptyClosePseudoConsole;
return true;
}
@@ -81,6 +792,30 @@ private:
QString m_lastError;
};
+static bool checkConHostHasResizeQuirkOption()
+{
+ static bool hasResizeQuirk = std::invoke([](){
+ QFile f(_InboxConsoleHostPath());
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning() << "couldn't open conhost.exe, assuming no resizeQuirk.";
+ return false;
+ }
+ // Conhost.exe should be around 1 MB
+ if (f.size() > 5 * 1024 * 1024) {
+ qWarning() << "conhost.exe is > 5MB, assuming no resizeQuirk.";
+ return false;
+ }
+ QByteArray content = f.readAll();
+ QString searchString("--resizeQuirk");
+ QByteArrayView v((const char*)searchString.data(), searchString.length()*2);
+ bool result = content.contains(v);
+ if (!result)
+ qDebug() << "No resizeQuirk option found in conhost.";
+ return result;
+ });
+
+ return hasResizeQuirk;
+}
HRESULT ConPtyProcess::createPseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn, HANDLE* phPipeOut, qint16 cols, qint16 rows)
{
@@ -93,7 +828,7 @@ HRESULT ConPtyProcess::createPseudoConsoleAndPipes(HPCON* phPC, HANDLE* phPipeIn
CreatePipe(phPipeIn, &hPipePTYOut, NULL, 0))
{
// Create the Pseudo Console of the required size, attached to the PTY-end of the pipes
- hr = WindowsContext::instance().createPseudoConsole({cols, rows}, hPipePTYIn, hPipePTYOut, 0, phPC);
+ hr = WindowsContext::instance().createPseudoConsole({cols, rows}, hPipePTYIn, hPipePTYOut, checkConHostHasResizeQuirkOption() ? PSEUDOCONSOLE_RESIZE_QUIRK : 0, phPC);
// Note: We can close the handles to the PTY-end of the pipes here
// because the handles are dup'ed into the ConHost and will be released
diff --git a/src/libs/3rdparty/libptyqt/unixptyprocess.cpp b/src/libs/3rdparty/libptyqt/unixptyprocess.cpp
index e9ec1d590f..d76349a49e 100644
--- a/src/libs/3rdparty/libptyqt/unixptyprocess.cpp
+++ b/src/libs/3rdparty/libptyqt/unixptyprocess.cpp
@@ -190,16 +190,17 @@ bool UnixPtyProcess::startProcess(const QString &shellPath,
m_readMasterNotify->disconnect();
});
- QStringList varNames;
- for (const QString &line : std::as_const(environment))
- varNames.append(line.split("=").first());
-
- QProcessEnvironment envFormat;
- for (const QString &line : std::as_const(environment))
- envFormat.insert(line.split("=").first(), line.split("=").last());
+ QProcessEnvironment env;
+ for (const QString &envEntry : environment) {
+ const int idx = envEntry.indexOf('=');
+ if (idx != -1)
+ env.insert(envEntry.left(idx), envEntry.mid(idx + 1));
+ else
+ env.insert(envEntry, QString());
+ }
m_shellProcess.setWorkingDirectory(workingDir);
- m_shellProcess.setProcessEnvironment(envFormat);
+ m_shellProcess.setProcessEnvironment(env);
m_shellProcess.setReadChannel(QProcess::StandardOutput);
m_shellProcess.start(m_shellPath, arguments);
if (!m_shellProcess.waitForStarted())
diff --git a/src/libs/3rdparty/lua/CMakeLists.txt b/src/libs/3rdparty/lua/CMakeLists.txt
new file mode 100644
index 0000000000..4501f540c8
--- /dev/null
+++ b/src/libs/3rdparty/lua/CMakeLists.txt
@@ -0,0 +1,75 @@
+
+add_qtc_library(lua546
+ PROPERTIES QT_COMPILE_OPTIONS_DISABLE_WARNINGS ON
+ PUBLIC_INCLUDES src
+ STATIC
+ SOURCES
+ src/lapi.c
+ src/lapi.h
+ src/lauxlib.c
+ src/lauxlib.h
+ src/lbaselib.c
+ src/lcode.c
+ src/lcode.h
+ src/lcorolib.c
+ src/lctype.c
+ src/lctype.h
+ src/ldblib.c
+ src/ldebug.c
+ src/ldebug.h
+ src/ldo.c
+ src/ldo.h
+ src/ldump.c
+ src/lfunc.c
+ src/lfunc.h
+ src/lgc.c
+ src/lgc.h
+ src/linit.c
+ src/liolib.c
+ src/llex.c
+ src/llex.h
+ src/lmathlib.c
+ src/lmem.c
+ src/lmem.h
+ src/loadlib.c
+ src/lobject.c
+ src/lobject.h
+ src/lopcodes.c
+ src/lopcodes.h
+ src/loslib.c
+ src/lparser.c
+ src/lparser.h
+ src/lstate.c
+ src/lstate.h
+ src/lstring.c
+ src/lstring.h
+ src/lstrlib.c
+ src/ltable.c
+ src/ltable.h
+ src/ltablib.c
+ src/ltm.c
+ src/ltm.h
+ src/lua.c
+ src/lua.h
+ src/luaconf.h
+ src/lundump.c
+ src/lundump.h
+ src/lutf8lib.c
+ src/lvm.c
+ src/lvm.h
+ src/lzio.c
+ src/lzio.h
+)
+
+extend_qtc_library(lua546
+ CONDITION LINUX
+ PUBLIC_DEFINES LUA_USE_LINUX
+)
+extend_qtc_library(lua546
+ CONDITION WIN32
+ PUBLIC_DEFINES LUA_USE_WINDOWS
+)
+extend_qtc_library(lua546
+ CONDITION APPLE
+ PUBLIC_DEFINES LUA_USE_MACOSX
+)
diff --git a/src/libs/3rdparty/lua/LICENSE b/src/libs/3rdparty/lua/LICENSE
new file mode 100644
index 0000000000..441cc1981c
--- /dev/null
+++ b/src/libs/3rdparty/lua/LICENSE
@@ -0,0 +1,16 @@
+Copyright © 1994–2023 Lua.org, PUC-Rio.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to deal in the Software without restriction,
+including without limitation the rights to use, copy, modify, merge, publish, distribute,
+sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial
+portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file
diff --git a/src/libs/3rdparty/lua/README b/src/libs/3rdparty/lua/README
new file mode 100644
index 0000000000..1ae97165ba
--- /dev/null
+++ b/src/libs/3rdparty/lua/README
@@ -0,0 +1,6 @@
+
+This is Lua 5.4.6, released on 02 May 2023.
+
+For installation instructions, license details, and
+further information about Lua, see doc/readme.html.
+
diff --git a/src/libs/3rdparty/lua/doc/contents.html b/src/libs/3rdparty/lua/doc/contents.html
new file mode 100644
index 0000000000..1231e6d248
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/contents.html
@@ -0,0 +1,678 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML>
+<HEAD>
+<TITLE>Lua 5.4 Reference Manual - contents</TITLE>
+<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
+<LINK REL="stylesheet" TYPE="text/css" HREF="index.css">
+<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=iso-8859-1">
+</HEAD>
+
+<BODY>
+
+<H1>
+<A HREF="http://www.lua.org/"><IMG SRC="logo.gif" ALT="Lua"></A>
+Lua 5.4 Reference Manual
+</H1>
+
+<P>
+The reference manual is the official definition of the Lua language.
+<BR>
+For a complete introduction to Lua programming, see the book
+<A HREF="http://www.lua.org/pil/">Programming in Lua</A>.
+
+<DIV CLASS="menubar">
+<A HREF="manual.html">start</A>
+&middot;
+<A HREF="#contents">contents</A>
+&middot;
+<A HREF="#index">index</A>
+&middot;
+<A HREF="http://www.lua.org/manual/">other versions</A>
+</DIV>
+
+<P>
+<SMALL>
+Copyright &copy; 2020&ndash;2023 Lua.org, PUC-Rio.
+Freely available under the terms of the
+<A HREF="http://www.lua.org/license.html">Lua license</A>.
+</SMALL>
+
+<H2><A NAME="contents">Contents</A></H2>
+<UL CLASS="contents menubar">
+<LI><A HREF="manual.html">1 &ndash; Introduction</A>
+<P>
+<LI><A HREF="manual.html#2">2 &ndash; Basic Concepts</A>
+<UL>
+<LI><A HREF="manual.html#2.1">2.1 &ndash; Values and Types</A>
+<LI><A HREF="manual.html#2.2">2.2 &ndash; Environments and the Global Environment</A>
+<LI><A HREF="manual.html#2.3">2.3 &ndash; Error Handling</A>
+<LI><A HREF="manual.html#2.4">2.4 &ndash; Metatables and Metamethods</A>
+<LI><A HREF="manual.html#2.5">2.5 &ndash; Garbage Collection</A>
+<UL>
+<LI><A HREF="manual.html#2.5.1">2.5.1 &ndash; Incremental Garbage Collection</A>
+<LI><A HREF="manual.html#2.5.2">2.5.2 &ndash; Generational Garbage Collection</A>
+<LI><A HREF="manual.html#2.5.3">2.5.3 &ndash; Garbage-Collection Metamethods</A>
+<LI><A HREF="manual.html#2.5.4">2.5.4 &ndash; Weak Tables</A>
+</UL>
+<LI><A HREF="manual.html#2.6">2.6 &ndash; Coroutines</A>
+</UL>
+<P>
+<LI><A HREF="manual.html#3">3 &ndash; The Language</A>
+<UL>
+<LI><A HREF="manual.html#3.1">3.1 &ndash; Lexical Conventions</A>
+<LI><A HREF="manual.html#3.2">3.2 &ndash; Variables</A>
+<LI><A HREF="manual.html#3.3">3.3 &ndash; Statements</A>
+<UL>
+<LI><A HREF="manual.html#3.3.1">3.3.1 &ndash; Blocks</A>
+<LI><A HREF="manual.html#3.3.2">3.3.2 &ndash; Chunks</A>
+<LI><A HREF="manual.html#3.3.3">3.3.3 &ndash; Assignment</A>
+<LI><A HREF="manual.html#3.3.4">3.3.4 &ndash; Control Structures</A>
+<LI><A HREF="manual.html#3.3.5">3.3.5 &ndash; For Statement</A>
+<LI><A HREF="manual.html#3.3.6">3.3.6 &ndash; Function Calls as Statements</A>
+<LI><A HREF="manual.html#3.3.7">3.3.7 &ndash; Local Declarations</A>
+<LI><A HREF="manual.html#3.3.8">3.3.8 &ndash; To-be-closed Variables</A>
+</UL>
+<LI><A HREF="manual.html#3.4">3.4 &ndash; Expressions</A>
+<UL>
+<LI><A HREF="manual.html#3.4.1">3.4.1 &ndash; Arithmetic Operators</A>
+<LI><A HREF="manual.html#3.4.2">3.4.2 &ndash; Bitwise Operators</A>
+<LI><A HREF="manual.html#3.4.3">3.4.3 &ndash; Coercions and Conversions</A>
+<LI><A HREF="manual.html#3.4.4">3.4.4 &ndash; Relational Operators</A>
+<LI><A HREF="manual.html#3.4.5">3.4.5 &ndash; Logical Operators</A>
+<LI><A HREF="manual.html#3.4.6">3.4.6 &ndash; Concatenation</A>
+<LI><A HREF="manual.html#3.4.7">3.4.7 &ndash; The Length Operator</A>
+<LI><A HREF="manual.html#3.4.8">3.4.8 &ndash; Precedence</A>
+<LI><A HREF="manual.html#3.4.9">3.4.9 &ndash; Table Constructors</A>
+<LI><A HREF="manual.html#3.4.10">3.4.10 &ndash; Function Calls</A>
+<LI><A HREF="manual.html#3.4.11">3.4.11 &ndash; Function Definitions</A>
+<LI><A HREF="manual.html#3.4.12">3.4.12 &ndash; Lists of expressions, multiple results, and adjustment<A>
+
+</UL>
+<LI><A HREF="manual.html#3.5">3.5 &ndash; Visibility Rules</A>
+</UL>
+<P>
+<LI><A HREF="manual.html#4">4 &ndash; The Application Program Interface</A>
+<UL>
+<LI><A HREF="manual.html#4.1">4.1 &ndash; The Stack</A>
+<UL>
+<LI><A HREF="manual.html#4.1.1">4.1.1 &ndash; Stack Size</A>
+<LI><A HREF="manual.html#4.1.2">4.1.2 &ndash; Valid and Acceptable Indices</A>
+<LI><A HREF="manual.html#4.1.3">4.1.3 &ndash; Pointers to strings</A>
+</UL>
+<LI><A HREF="manual.html#4.2">4.2 &ndash; C Closures</A>
+<LI><A HREF="manual.html#4.3">4.3 &ndash; Registry</A>
+<LI><A HREF="manual.html#4.4">4.4 &ndash; Error Handling in C</A>
+<UL>
+<LI><A HREF="manual.html#4.4.1">4.4.1 &ndash; Status Codes</A>
+</UL>
+<LI><A HREF="manual.html#4.5">4.5 &ndash; Handling Yields in C</A>
+<LI><A HREF="manual.html#4.6">4.6 &ndash; Functions and Types</A>
+<LI><A HREF="manual.html#4.7">4.7 &ndash; The Debug Interface</A>
+</UL>
+<P>
+<LI><A HREF="manual.html#5">5 &ndash; The Auxiliary Library</A>
+<UL>
+<LI><A HREF="manual.html#5.1">5.1 &ndash; Functions and Types</A>
+</UL>
+<P>
+<LI><A HREF="manual.html#6">6 &ndash; The Standard Libraries</A>
+<UL>
+<LI><A HREF="manual.html#6.1">6.1 &ndash; Basic Functions</A>
+<LI><A HREF="manual.html#6.2">6.2 &ndash; Coroutine Manipulation</A>
+<LI><A HREF="manual.html#6.3">6.3 &ndash; Modules</A>
+<LI><A HREF="manual.html#6.4">6.4 &ndash; String Manipulation</A>
+<UL>
+<LI><A HREF="manual.html#6.4.1">6.4.1 &ndash; Patterns</A>
+<LI><A HREF="manual.html#6.4.2">6.4.2 &ndash; Format Strings for Pack and Unpack</A>
+</UL>
+<LI><A HREF="manual.html#6.5">6.5 &ndash; UTF-8 Support</A>
+<LI><A HREF="manual.html#6.6">6.6 &ndash; Table Manipulation</A>
+<LI><A HREF="manual.html#6.7">6.7 &ndash; Mathematical Functions</A>
+<LI><A HREF="manual.html#6.8">6.8 &ndash; Input and Output Facilities</A>
+<LI><A HREF="manual.html#6.9">6.9 &ndash; Operating System Facilities</A>
+<LI><A HREF="manual.html#6.10">6.10 &ndash; The Debug Library</A>
+</UL>
+<P>
+<LI><A HREF="manual.html#7">7 &ndash; Lua Standalone</A>
+<P>
+<LI><A HREF="manual.html#8">8 &ndash; Incompatibilities with the Previous Version</A>
+<UL>
+<LI><A HREF="manual.html#8.1">8.1 &ndash; Incompatibilities in the Language</A>
+<LI><A HREF="manual.html#8.2">8.2 &ndash; Incompatibilities in the Libraries</A>
+<LI><A HREF="manual.html#8.3">8.3 &ndash; Incompatibilities in the API</A>
+</UL>
+<P>
+<LI><A HREF="manual.html#9">9 &ndash; The Complete Syntax of Lua</A>
+</UL>
+
+<H2><A NAME="index">Index</A></H2>
+<TABLE CLASS="menubar" WIDTH="100%">
+<TR>
+<TD>
+<H3><A NAME="functions">Lua functions</A></H3>
+<P>
+<A HREF="manual.html#6.1">basic</A><BR>
+<A HREF="manual.html#pdf-_G">_G</A><BR>
+<A HREF="manual.html#pdf-_VERSION">_VERSION</A><BR>
+<A HREF="manual.html#pdf-assert">assert</A><BR>
+<A HREF="manual.html#pdf-collectgarbage">collectgarbage</A><BR>
+<A HREF="manual.html#pdf-dofile">dofile</A><BR>
+<A HREF="manual.html#pdf-error">error</A><BR>
+<A HREF="manual.html#pdf-getmetatable">getmetatable</A><BR>
+<A HREF="manual.html#pdf-ipairs">ipairs</A><BR>
+<A HREF="manual.html#pdf-load">load</A><BR>
+<A HREF="manual.html#pdf-loadfile">loadfile</A><BR>
+<A HREF="manual.html#pdf-next">next</A><BR>
+<A HREF="manual.html#pdf-pairs">pairs</A><BR>
+<A HREF="manual.html#pdf-pcall">pcall</A><BR>
+<A HREF="manual.html#pdf-print">print</A><BR>
+<A HREF="manual.html#pdf-rawequal">rawequal</A><BR>
+<A HREF="manual.html#pdf-rawget">rawget</A><BR>
+<A HREF="manual.html#pdf-rawlen">rawlen</A><BR>
+<A HREF="manual.html#pdf-rawset">rawset</A><BR>
+<A HREF="manual.html#pdf-require">require</A><BR>
+<A HREF="manual.html#pdf-select">select</A><BR>
+<A HREF="manual.html#pdf-setmetatable">setmetatable</A><BR>
+<A HREF="manual.html#pdf-tonumber">tonumber</A><BR>
+<A HREF="manual.html#pdf-tostring">tostring</A><BR>
+<A HREF="manual.html#pdf-type">type</A><BR>
+<A HREF="manual.html#pdf-warn">warn</A><BR>
+<A HREF="manual.html#pdf-xpcall">xpcall</A><BR>
+
+<P>
+<A HREF="manual.html#6.2">coroutine</A><BR>
+<A HREF="manual.html#pdf-coroutine.close">coroutine.close</A><BR>
+<A HREF="manual.html#pdf-coroutine.create">coroutine.create</A><BR>
+<A HREF="manual.html#pdf-coroutine.isyieldable">coroutine.isyieldable</A><BR>
+<A HREF="manual.html#pdf-coroutine.resume">coroutine.resume</A><BR>
+<A HREF="manual.html#pdf-coroutine.running">coroutine.running</A><BR>
+<A HREF="manual.html#pdf-coroutine.status">coroutine.status</A><BR>
+<A HREF="manual.html#pdf-coroutine.wrap">coroutine.wrap</A><BR>
+<A HREF="manual.html#pdf-coroutine.yield">coroutine.yield</A><BR>
+
+<P>
+<A HREF="manual.html#6.10">debug</A><BR>
+<A HREF="manual.html#pdf-debug.debug">debug.debug</A><BR>
+<A HREF="manual.html#pdf-debug.gethook">debug.gethook</A><BR>
+<A HREF="manual.html#pdf-debug.getinfo">debug.getinfo</A><BR>
+<A HREF="manual.html#pdf-debug.getlocal">debug.getlocal</A><BR>
+<A HREF="manual.html#pdf-debug.getmetatable">debug.getmetatable</A><BR>
+<A HREF="manual.html#pdf-debug.getregistry">debug.getregistry</A><BR>
+<A HREF="manual.html#pdf-debug.getupvalue">debug.getupvalue</A><BR>
+<A HREF="manual.html#pdf-debug.getuservalue">debug.getuservalue</A><BR>
+<A HREF="manual.html#pdf-debug.sethook">debug.sethook</A><BR>
+<A HREF="manual.html#pdf-debug.setlocal">debug.setlocal</A><BR>
+<A HREF="manual.html#pdf-debug.setmetatable">debug.setmetatable</A><BR>
+<A HREF="manual.html#pdf-debug.setupvalue">debug.setupvalue</A><BR>
+<A HREF="manual.html#pdf-debug.setuservalue">debug.setuservalue</A><BR>
+<A HREF="manual.html#pdf-debug.traceback">debug.traceback</A><BR>
+<A HREF="manual.html#pdf-debug.upvalueid">debug.upvalueid</A><BR>
+<A HREF="manual.html#pdf-debug.upvaluejoin">debug.upvaluejoin</A><BR>
+
+<P>
+<A HREF="manual.html#6.8">io</A><BR>
+<A HREF="manual.html#pdf-io.close">io.close</A><BR>
+<A HREF="manual.html#pdf-io.flush">io.flush</A><BR>
+<A HREF="manual.html#pdf-io.input">io.input</A><BR>
+<A HREF="manual.html#pdf-io.lines">io.lines</A><BR>
+<A HREF="manual.html#pdf-io.open">io.open</A><BR>
+<A HREF="manual.html#pdf-io.output">io.output</A><BR>
+<A HREF="manual.html#pdf-io.popen">io.popen</A><BR>
+<A HREF="manual.html#pdf-io.read">io.read</A><BR>
+<A HREF="manual.html#pdf-io.stderr">io.stderr</A><BR>
+<A HREF="manual.html#pdf-io.stdin">io.stdin</A><BR>
+<A HREF="manual.html#pdf-io.stdout">io.stdout</A><BR>
+<A HREF="manual.html#pdf-io.tmpfile">io.tmpfile</A><BR>
+<A HREF="manual.html#pdf-io.type">io.type</A><BR>
+<A HREF="manual.html#pdf-io.write">io.write</A><BR>
+
+<A HREF="manual.html#pdf-file:close">file:close</A><BR>
+<A HREF="manual.html#pdf-file:flush">file:flush</A><BR>
+<A HREF="manual.html#pdf-file:lines">file:lines</A><BR>
+<A HREF="manual.html#pdf-file:read">file:read</A><BR>
+<A HREF="manual.html#pdf-file:seek">file:seek</A><BR>
+<A HREF="manual.html#pdf-file:setvbuf">file:setvbuf</A><BR>
+<A HREF="manual.html#pdf-file:write">file:write</A><BR>
+
+</TD>
+<TD>
+<H3>&nbsp;</H3>
+<P>
+<A HREF="manual.html#6.7">math</A><BR>
+<A HREF="manual.html#pdf-math.abs">math.abs</A><BR>
+<A HREF="manual.html#pdf-math.acos">math.acos</A><BR>
+<A HREF="manual.html#pdf-math.asin">math.asin</A><BR>
+<A HREF="manual.html#pdf-math.atan">math.atan</A><BR>
+<A HREF="manual.html#pdf-math.ceil">math.ceil</A><BR>
+<A HREF="manual.html#pdf-math.cos">math.cos</A><BR>
+<A HREF="manual.html#pdf-math.deg">math.deg</A><BR>
+<A HREF="manual.html#pdf-math.exp">math.exp</A><BR>
+<A HREF="manual.html#pdf-math.floor">math.floor</A><BR>
+<A HREF="manual.html#pdf-math.fmod">math.fmod</A><BR>
+<A HREF="manual.html#pdf-math.huge">math.huge</A><BR>
+<A HREF="manual.html#pdf-math.log">math.log</A><BR>
+<A HREF="manual.html#pdf-math.max">math.max</A><BR>
+<A HREF="manual.html#pdf-math.maxinteger">math.maxinteger</A><BR>
+<A HREF="manual.html#pdf-math.min">math.min</A><BR>
+<A HREF="manual.html#pdf-math.mininteger">math.mininteger</A><BR>
+<A HREF="manual.html#pdf-math.modf">math.modf</A><BR>
+<A HREF="manual.html#pdf-math.pi">math.pi</A><BR>
+<A HREF="manual.html#pdf-math.rad">math.rad</A><BR>
+<A HREF="manual.html#pdf-math.random">math.random</A><BR>
+<A HREF="manual.html#pdf-math.randomseed">math.randomseed</A><BR>
+<A HREF="manual.html#pdf-math.sin">math.sin</A><BR>
+<A HREF="manual.html#pdf-math.sqrt">math.sqrt</A><BR>
+<A HREF="manual.html#pdf-math.tan">math.tan</A><BR>
+<A HREF="manual.html#pdf-math.tointeger">math.tointeger</A><BR>
+<A HREF="manual.html#pdf-math.type">math.type</A><BR>
+<A HREF="manual.html#pdf-math.ult">math.ult</A><BR>
+
+<P>
+<A HREF="manual.html#6.9">os</A><BR>
+<A HREF="manual.html#pdf-os.clock">os.clock</A><BR>
+<A HREF="manual.html#pdf-os.date">os.date</A><BR>
+<A HREF="manual.html#pdf-os.difftime">os.difftime</A><BR>
+<A HREF="manual.html#pdf-os.execute">os.execute</A><BR>
+<A HREF="manual.html#pdf-os.exit">os.exit</A><BR>
+<A HREF="manual.html#pdf-os.getenv">os.getenv</A><BR>
+<A HREF="manual.html#pdf-os.remove">os.remove</A><BR>
+<A HREF="manual.html#pdf-os.rename">os.rename</A><BR>
+<A HREF="manual.html#pdf-os.setlocale">os.setlocale</A><BR>
+<A HREF="manual.html#pdf-os.time">os.time</A><BR>
+<A HREF="manual.html#pdf-os.tmpname">os.tmpname</A><BR>
+
+<P>
+<A HREF="manual.html#6.3">package</A><BR>
+<A HREF="manual.html#pdf-package.config">package.config</A><BR>
+<A HREF="manual.html#pdf-package.cpath">package.cpath</A><BR>
+<A HREF="manual.html#pdf-package.loaded">package.loaded</A><BR>
+<A HREF="manual.html#pdf-package.loadlib">package.loadlib</A><BR>
+<A HREF="manual.html#pdf-package.path">package.path</A><BR>
+<A HREF="manual.html#pdf-package.preload">package.preload</A><BR>
+<A HREF="manual.html#pdf-package.searchers">package.searchers</A><BR>
+<A HREF="manual.html#pdf-package.searchpath">package.searchpath</A><BR>
+
+<P>
+<A HREF="manual.html#6.4">string</A><BR>
+<A HREF="manual.html#pdf-string.byte">string.byte</A><BR>
+<A HREF="manual.html#pdf-string.char">string.char</A><BR>
+<A HREF="manual.html#pdf-string.dump">string.dump</A><BR>
+<A HREF="manual.html#pdf-string.find">string.find</A><BR>
+<A HREF="manual.html#pdf-string.format">string.format</A><BR>
+<A HREF="manual.html#pdf-string.gmatch">string.gmatch</A><BR>
+<A HREF="manual.html#pdf-string.gsub">string.gsub</A><BR>
+<A HREF="manual.html#pdf-string.len">string.len</A><BR>
+<A HREF="manual.html#pdf-string.lower">string.lower</A><BR>
+<A HREF="manual.html#pdf-string.match">string.match</A><BR>
+<A HREF="manual.html#pdf-string.pack">string.pack</A><BR>
+<A HREF="manual.html#pdf-string.packsize">string.packsize</A><BR>
+<A HREF="manual.html#pdf-string.rep">string.rep</A><BR>
+<A HREF="manual.html#pdf-string.reverse">string.reverse</A><BR>
+<A HREF="manual.html#pdf-string.sub">string.sub</A><BR>
+<A HREF="manual.html#pdf-string.unpack">string.unpack</A><BR>
+<A HREF="manual.html#pdf-string.upper">string.upper</A><BR>
+
+<P>
+<A HREF="manual.html#6.6">table</A><BR>
+<A HREF="manual.html#pdf-table.concat">table.concat</A><BR>
+<A HREF="manual.html#pdf-table.insert">table.insert</A><BR>
+<A HREF="manual.html#pdf-table.move">table.move</A><BR>
+<A HREF="manual.html#pdf-table.pack">table.pack</A><BR>
+<A HREF="manual.html#pdf-table.remove">table.remove</A><BR>
+<A HREF="manual.html#pdf-table.sort">table.sort</A><BR>
+<A HREF="manual.html#pdf-table.unpack">table.unpack</A><BR>
+
+<P>
+<A HREF="manual.html#6.5">utf8</A><BR>
+<A HREF="manual.html#pdf-utf8.char">utf8.char</A><BR>
+<A HREF="manual.html#pdf-utf8.charpattern">utf8.charpattern</A><BR>
+<A HREF="manual.html#pdf-utf8.codepoint">utf8.codepoint</A><BR>
+<A HREF="manual.html#pdf-utf8.codes">utf8.codes</A><BR>
+<A HREF="manual.html#pdf-utf8.len">utf8.len</A><BR>
+<A HREF="manual.html#pdf-utf8.offset">utf8.offset</A><BR>
+
+<H3><A NAME="metamethods">metamethods</A></H3>
+<P>
+<A HREF="manual.html#2.4">__add</A><BR>
+<A HREF="manual.html#2.4">__band</A><BR>
+<A HREF="manual.html#2.4">__bnot</A><BR>
+<A HREF="manual.html#2.4">__bor</A><BR>
+<A HREF="manual.html#2.4">__bxor</A><BR>
+<A HREF="manual.html#2.4">__call</A><BR>
+<A HREF="manual.html#3.3.8">__close</A><BR>
+<A HREF="manual.html#2.4">__concat</A><BR>
+<A HREF="manual.html#2.4">__div</A><BR>
+<A HREF="manual.html#2.4">__eq</A><BR>
+<A HREF="manual.html#2.5.3">__gc</A><BR>
+<A HREF="manual.html#2.4">__idiv</A><BR>
+<A HREF="manual.html#2.4">__index</A><BR>
+<A HREF="manual.html#2.4">__le</A><BR>
+<A HREF="manual.html#2.4">__len</A><BR>
+<A HREF="manual.html#2.4">__lt</A><BR>
+<A HREF="manual.html#pdf-getmetatable">__metatable</A><BR>
+<A HREF="manual.html#2.4">__mod</A><BR>
+<A HREF="manual.html#2.5.4">__mode</A><BR>
+<A HREF="manual.html#2.4">__mul</A><BR>
+<A HREF="manual.html#luaL_newmetatable">__name</A><BR>
+<A HREF="manual.html#2.4">__newindex</A><BR>
+<A HREF="manual.html#pdf-pairs">__pairs</A><BR>
+<A HREF="manual.html#2.4">__pow</A><BR>
+<A HREF="manual.html#2.4">__shl</A><BR>
+<A HREF="manual.html#2.4">__shr</A><BR>
+<A HREF="manual.html#2.4">__sub</A><BR>
+<A HREF="manual.html#pdf-tostring">__tostring</A><BR>
+<A HREF="manual.html#2.4">__unm</A><BR>
+
+<H3><A NAME="env">environment<BR>variables</A></H3>
+<P>
+<A HREF="manual.html#pdf-LUA_CPATH">LUA_CPATH</A><BR>
+<A HREF="manual.html#pdf-LUA_CPATH_5_4">LUA_CPATH_5_4</A><BR>
+<A HREF="manual.html#pdf-LUA_INIT">LUA_INIT</A><BR>
+<A HREF="manual.html#pdf-LUA_INIT_5_4">LUA_INIT_5_4</A><BR>
+<A HREF="manual.html#pdf-LUA_PATH">LUA_PATH</A><BR>
+<A HREF="manual.html#pdf-LUA_PATH_5_4">LUA_PATH_5_4</A><BR>
+
+</TD>
+<TD>
+<H3><A NAME="api">C API</A></H3>
+<P>
+<A HREF="manual.html#lua_Alloc">lua_Alloc</A><BR>
+<A HREF="manual.html#lua_CFunction">lua_CFunction</A><BR>
+<A HREF="manual.html#lua_Debug">lua_Debug</A><BR>
+<A HREF="manual.html#lua_Hook">lua_Hook</A><BR>
+<A HREF="manual.html#lua_Integer">lua_Integer</A><BR>
+<A HREF="manual.html#lua_KContext">lua_KContext</A><BR>
+<A HREF="manual.html#lua_KFunction">lua_KFunction</A><BR>
+<A HREF="manual.html#lua_Number">lua_Number</A><BR>
+<A HREF="manual.html#lua_Reader">lua_Reader</A><BR>
+<A HREF="manual.html#lua_State">lua_State</A><BR>
+<A HREF="manual.html#lua_Unsigned">lua_Unsigned</A><BR>
+<A HREF="manual.html#lua_WarnFunction">lua_WarnFunction</A><BR>
+<A HREF="manual.html#lua_Writer">lua_Writer</A><BR>
+
+<P>
+<A HREF="manual.html#lua_absindex">lua_absindex</A><BR>
+<A HREF="manual.html#lua_arith">lua_arith</A><BR>
+<A HREF="manual.html#lua_atpanic">lua_atpanic</A><BR>
+<A HREF="manual.html#lua_call">lua_call</A><BR>
+<A HREF="manual.html#lua_callk">lua_callk</A><BR>
+<A HREF="manual.html#lua_checkstack">lua_checkstack</A><BR>
+<A HREF="manual.html#lua_close">lua_close</A><BR>
+<A HREF="manual.html#lua_closeslot">lua_closeslot</A><BR>
+<A HREF="manual.html#lua_compare">lua_compare</A><BR>
+<A HREF="manual.html#lua_concat">lua_concat</A><BR>
+<A HREF="manual.html#lua_copy">lua_copy</A><BR>
+<A HREF="manual.html#lua_createtable">lua_createtable</A><BR>
+<A HREF="manual.html#lua_dump">lua_dump</A><BR>
+<A HREF="manual.html#lua_error">lua_error</A><BR>
+<A HREF="manual.html#lua_gc">lua_gc</A><BR>
+<A HREF="manual.html#lua_getallocf">lua_getallocf</A><BR>
+<A HREF="manual.html#lua_getextraspace">lua_getextraspace</A><BR>
+<A HREF="manual.html#lua_getfield">lua_getfield</A><BR>
+<A HREF="manual.html#lua_getglobal">lua_getglobal</A><BR>
+<A HREF="manual.html#lua_gethook">lua_gethook</A><BR>
+<A HREF="manual.html#lua_gethookcount">lua_gethookcount</A><BR>
+<A HREF="manual.html#lua_gethookmask">lua_gethookmask</A><BR>
+<A HREF="manual.html#lua_geti">lua_geti</A><BR>
+<A HREF="manual.html#lua_getinfo">lua_getinfo</A><BR>
+<A HREF="manual.html#lua_getiuservalue">lua_getiuservalue</A><BR>
+<A HREF="manual.html#lua_getlocal">lua_getlocal</A><BR>
+<A HREF="manual.html#lua_getmetatable">lua_getmetatable</A><BR>
+<A HREF="manual.html#lua_getstack">lua_getstack</A><BR>
+<A HREF="manual.html#lua_gettable">lua_gettable</A><BR>
+<A HREF="manual.html#lua_gettop">lua_gettop</A><BR>
+<A HREF="manual.html#lua_getupvalue">lua_getupvalue</A><BR>
+<A HREF="manual.html#lua_insert">lua_insert</A><BR>
+<A HREF="manual.html#lua_isboolean">lua_isboolean</A><BR>
+<A HREF="manual.html#lua_iscfunction">lua_iscfunction</A><BR>
+<A HREF="manual.html#lua_isfunction">lua_isfunction</A><BR>
+<A HREF="manual.html#lua_isinteger">lua_isinteger</A><BR>
+<A HREF="manual.html#lua_islightuserdata">lua_islightuserdata</A><BR>
+<A HREF="manual.html#lua_isnil">lua_isnil</A><BR>
+<A HREF="manual.html#lua_isnone">lua_isnone</A><BR>
+<A HREF="manual.html#lua_isnoneornil">lua_isnoneornil</A><BR>
+<A HREF="manual.html#lua_isnumber">lua_isnumber</A><BR>
+<A HREF="manual.html#lua_isstring">lua_isstring</A><BR>
+<A HREF="manual.html#lua_istable">lua_istable</A><BR>
+<A HREF="manual.html#lua_isthread">lua_isthread</A><BR>
+<A HREF="manual.html#lua_isuserdata">lua_isuserdata</A><BR>
+<A HREF="manual.html#lua_isyieldable">lua_isyieldable</A><BR>
+<A HREF="manual.html#lua_len">lua_len</A><BR>
+<A HREF="manual.html#lua_load">lua_load</A><BR>
+<A HREF="manual.html#lua_newstate">lua_newstate</A><BR>
+<A HREF="manual.html#lua_newtable">lua_newtable</A><BR>
+<A HREF="manual.html#lua_newthread">lua_newthread</A><BR>
+<A HREF="manual.html#lua_newuserdatauv">lua_newuserdatauv</A><BR>
+<A HREF="manual.html#lua_next">lua_next</A><BR>
+<A HREF="manual.html#lua_numbertointeger">lua_numbertointeger</A><BR>
+<A HREF="manual.html#lua_pcall">lua_pcall</A><BR>
+<A HREF="manual.html#lua_pcallk">lua_pcallk</A><BR>
+<A HREF="manual.html#lua_pop">lua_pop</A><BR>
+<A HREF="manual.html#lua_pushboolean">lua_pushboolean</A><BR>
+<A HREF="manual.html#lua_pushcclosure">lua_pushcclosure</A><BR>
+<A HREF="manual.html#lua_pushcfunction">lua_pushcfunction</A><BR>
+<A HREF="manual.html#lua_pushfstring">lua_pushfstring</A><BR>
+<A HREF="manual.html#lua_pushglobaltable">lua_pushglobaltable</A><BR>
+<A HREF="manual.html#lua_pushinteger">lua_pushinteger</A><BR>
+<A HREF="manual.html#lua_pushlightuserdata">lua_pushlightuserdata</A><BR>
+<A HREF="manual.html#lua_pushliteral">lua_pushliteral</A><BR>
+<A HREF="manual.html#lua_pushlstring">lua_pushlstring</A><BR>
+<A HREF="manual.html#lua_pushnil">lua_pushnil</A><BR>
+<A HREF="manual.html#lua_pushnumber">lua_pushnumber</A><BR>
+<A HREF="manual.html#lua_pushstring">lua_pushstring</A><BR>
+<A HREF="manual.html#lua_pushthread">lua_pushthread</A><BR>
+<A HREF="manual.html#lua_pushvalue">lua_pushvalue</A><BR>
+<A HREF="manual.html#lua_pushvfstring">lua_pushvfstring</A><BR>
+<A HREF="manual.html#lua_rawequal">lua_rawequal</A><BR>
+<A HREF="manual.html#lua_rawget">lua_rawget</A><BR>
+<A HREF="manual.html#lua_rawgeti">lua_rawgeti</A><BR>
+<A HREF="manual.html#lua_rawgetp">lua_rawgetp</A><BR>
+<A HREF="manual.html#lua_rawlen">lua_rawlen</A><BR>
+<A HREF="manual.html#lua_rawset">lua_rawset</A><BR>
+<A HREF="manual.html#lua_rawseti">lua_rawseti</A><BR>
+<A HREF="manual.html#lua_rawsetp">lua_rawsetp</A><BR>
+<A HREF="manual.html#lua_register">lua_register</A><BR>
+<A HREF="manual.html#lua_remove">lua_remove</A><BR>
+<A HREF="manual.html#lua_replace">lua_replace</A><BR>
+<A HREF="manual.html#lua_resetthread">lua_resetthread</A><BR>
+<A HREF="manual.html#lua_resume">lua_resume</A><BR>
+<A HREF="manual.html#lua_rotate">lua_rotate</A><BR>
+<A HREF="manual.html#lua_setallocf">lua_setallocf</A><BR>
+<A HREF="manual.html#lua_setfield">lua_setfield</A><BR>
+<A HREF="manual.html#lua_setglobal">lua_setglobal</A><BR>
+<A HREF="manual.html#lua_sethook">lua_sethook</A><BR>
+<A HREF="manual.html#lua_seti">lua_seti</A><BR>
+<A HREF="manual.html#lua_setiuservalue">lua_setiuservalue</A><BR>
+<A HREF="manual.html#lua_setlocal">lua_setlocal</A><BR>
+<A HREF="manual.html#lua_setmetatable">lua_setmetatable</A><BR>
+<A HREF="manual.html#lua_settable">lua_settable</A><BR>
+<A HREF="manual.html#lua_settop">lua_settop</A><BR>
+<A HREF="manual.html#lua_setupvalue">lua_setupvalue</A><BR>
+<A HREF="manual.html#lua_setwarnf">lua_setwarnf</A><BR>
+<A HREF="manual.html#lua_status">lua_status</A><BR>
+<A HREF="manual.html#lua_stringtonumber">lua_stringtonumber</A><BR>
+<A HREF="manual.html#lua_toboolean">lua_toboolean</A><BR>
+<A HREF="manual.html#lua_tocfunction">lua_tocfunction</A><BR>
+<A HREF="manual.html#lua_toclose">lua_toclose</A><BR>
+<A HREF="manual.html#lua_tointeger">lua_tointeger</A><BR>
+<A HREF="manual.html#lua_tointegerx">lua_tointegerx</A><BR>
+<A HREF="manual.html#lua_tolstring">lua_tolstring</A><BR>
+<A HREF="manual.html#lua_tonumber">lua_tonumber</A><BR>
+<A HREF="manual.html#lua_tonumberx">lua_tonumberx</A><BR>
+<A HREF="manual.html#lua_topointer">lua_topointer</A><BR>
+<A HREF="manual.html#lua_tostring">lua_tostring</A><BR>
+<A HREF="manual.html#lua_tothread">lua_tothread</A><BR>
+<A HREF="manual.html#lua_touserdata">lua_touserdata</A><BR>
+<A HREF="manual.html#lua_type">lua_type</A><BR>
+<A HREF="manual.html#lua_typename">lua_typename</A><BR>
+<A HREF="manual.html#lua_upvalueid">lua_upvalueid</A><BR>
+<A HREF="manual.html#lua_upvalueindex">lua_upvalueindex</A><BR>
+<A HREF="manual.html#lua_upvaluejoin">lua_upvaluejoin</A><BR>
+<A HREF="manual.html#lua_version">lua_version</A><BR>
+<A HREF="manual.html#lua_warning">lua_warning</A><BR>
+<A HREF="manual.html#lua_xmove">lua_xmove</A><BR>
+<A HREF="manual.html#lua_yield">lua_yield</A><BR>
+<A HREF="manual.html#lua_yieldk">lua_yieldk</A><BR>
+
+</TD>
+<TD>
+<H3><A NAME="auxlib">auxiliary library</A></H3>
+<P>
+<A HREF="manual.html#luaL_Buffer">luaL_Buffer</A><BR>
+<A HREF="manual.html#luaL_Reg">luaL_Reg</A><BR>
+<A HREF="manual.html#luaL_Stream">luaL_Stream</A><BR>
+
+<P>
+<A HREF="manual.html#luaL_addchar">luaL_addchar</A><BR>
+<A HREF="manual.html#luaL_addgsub">luaL_addgsub</A><BR>
+<A HREF="manual.html#luaL_addlstring">luaL_addlstring</A><BR>
+<A HREF="manual.html#luaL_addsize">luaL_addsize</A><BR>
+<A HREF="manual.html#luaL_addstring">luaL_addstring</A><BR>
+<A HREF="manual.html#luaL_addvalue">luaL_addvalue</A><BR>
+<A HREF="manual.html#luaL_argcheck">luaL_argcheck</A><BR>
+<A HREF="manual.html#luaL_argerror">luaL_argerror</A><BR>
+<A HREF="manual.html#luaL_argexpected">luaL_argexpected</A><BR>
+<A HREF="manual.html#luaL_buffaddr">luaL_buffaddr</A><BR>
+<A HREF="manual.html#luaL_buffinit">luaL_buffinit</A><BR>
+<A HREF="manual.html#luaL_buffinitsize">luaL_buffinitsize</A><BR>
+<A HREF="manual.html#luaL_bufflen">luaL_bufflen</A><BR>
+<A HREF="manual.html#luaL_buffsub">luaL_buffsub</A><BR>
+<A HREF="manual.html#luaL_callmeta">luaL_callmeta</A><BR>
+<A HREF="manual.html#luaL_checkany">luaL_checkany</A><BR>
+<A HREF="manual.html#luaL_checkinteger">luaL_checkinteger</A><BR>
+<A HREF="manual.html#luaL_checklstring">luaL_checklstring</A><BR>
+<A HREF="manual.html#luaL_checknumber">luaL_checknumber</A><BR>
+<A HREF="manual.html#luaL_checkoption">luaL_checkoption</A><BR>
+<A HREF="manual.html#luaL_checkstack">luaL_checkstack</A><BR>
+<A HREF="manual.html#luaL_checkstring">luaL_checkstring</A><BR>
+<A HREF="manual.html#luaL_checktype">luaL_checktype</A><BR>
+<A HREF="manual.html#luaL_checkudata">luaL_checkudata</A><BR>
+<A HREF="manual.html#luaL_checkversion">luaL_checkversion</A><BR>
+<A HREF="manual.html#luaL_dofile">luaL_dofile</A><BR>
+<A HREF="manual.html#luaL_dostring">luaL_dostring</A><BR>
+<A HREF="manual.html#luaL_error">luaL_error</A><BR>
+<A HREF="manual.html#luaL_execresult">luaL_execresult</A><BR>
+<A HREF="manual.html#luaL_fileresult">luaL_fileresult</A><BR>
+<A HREF="manual.html#luaL_getmetafield">luaL_getmetafield</A><BR>
+<A HREF="manual.html#luaL_getmetatable">luaL_getmetatable</A><BR>
+<A HREF="manual.html#luaL_getsubtable">luaL_getsubtable</A><BR>
+<A HREF="manual.html#luaL_gsub">luaL_gsub</A><BR>
+<A HREF="manual.html#luaL_len">luaL_len</A><BR>
+<A HREF="manual.html#luaL_loadbuffer">luaL_loadbuffer</A><BR>
+<A HREF="manual.html#luaL_loadbufferx">luaL_loadbufferx</A><BR>
+<A HREF="manual.html#luaL_loadfile">luaL_loadfile</A><BR>
+<A HREF="manual.html#luaL_loadfilex">luaL_loadfilex</A><BR>
+<A HREF="manual.html#luaL_loadstring">luaL_loadstring</A><BR>
+<A HREF="manual.html#luaL_newlib">luaL_newlib</A><BR>
+<A HREF="manual.html#luaL_newlibtable">luaL_newlibtable</A><BR>
+<A HREF="manual.html#luaL_newmetatable">luaL_newmetatable</A><BR>
+<A HREF="manual.html#luaL_newstate">luaL_newstate</A><BR>
+<A HREF="manual.html#luaL_openlibs">luaL_openlibs</A><BR>
+<A HREF="manual.html#luaL_opt">luaL_opt</A><BR>
+<A HREF="manual.html#luaL_optinteger">luaL_optinteger</A><BR>
+<A HREF="manual.html#luaL_optlstring">luaL_optlstring</A><BR>
+<A HREF="manual.html#luaL_optnumber">luaL_optnumber</A><BR>
+<A HREF="manual.html#luaL_optstring">luaL_optstring</A><BR>
+<A HREF="manual.html#luaL_prepbuffer">luaL_prepbuffer</A><BR>
+<A HREF="manual.html#luaL_prepbuffsize">luaL_prepbuffsize</A><BR>
+<A HREF="manual.html#luaL_pushfail">luaL_pushfail</A><BR>
+<A HREF="manual.html#luaL_pushresult">luaL_pushresult</A><BR>
+<A HREF="manual.html#luaL_pushresultsize">luaL_pushresultsize</A><BR>
+<A HREF="manual.html#luaL_ref">luaL_ref</A><BR>
+<A HREF="manual.html#luaL_requiref">luaL_requiref</A><BR>
+<A HREF="manual.html#luaL_setfuncs">luaL_setfuncs</A><BR>
+<A HREF="manual.html#luaL_setmetatable">luaL_setmetatable</A><BR>
+<A HREF="manual.html#luaL_testudata">luaL_testudata</A><BR>
+<A HREF="manual.html#luaL_tolstring">luaL_tolstring</A><BR>
+<A HREF="manual.html#luaL_traceback">luaL_traceback</A><BR>
+<A HREF="manual.html#luaL_typeerror">luaL_typeerror</A><BR>
+<A HREF="manual.html#luaL_typename">luaL_typename</A><BR>
+<A HREF="manual.html#luaL_unref">luaL_unref</A><BR>
+<A HREF="manual.html#luaL_where">luaL_where</A><BR>
+
+<H3><A NAME="library">standard library</A></H3>
+<P>
+<A HREF="manual.html#pdf-luaopen_base">luaopen_base</A><BR>
+<A HREF="manual.html#pdf-luaopen_coroutine">luaopen_coroutine</A><BR>
+<A HREF="manual.html#pdf-luaopen_debug">luaopen_debug</A><BR>
+<A HREF="manual.html#pdf-luaopen_io">luaopen_io</A><BR>
+<A HREF="manual.html#pdf-luaopen_math">luaopen_math</A><BR>
+<A HREF="manual.html#pdf-luaopen_os">luaopen_os</A><BR>
+<A HREF="manual.html#pdf-luaopen_package">luaopen_package</A><BR>
+<A HREF="manual.html#pdf-luaopen_string">luaopen_string</A><BR>
+<A HREF="manual.html#pdf-luaopen_table">luaopen_table</A><BR>
+<A HREF="manual.html#pdf-luaopen_utf8">luaopen_utf8</A><BR>
+
+<H3><A NAME="constants">constants</A></H3>
+<P>
+<A HREF="manual.html#pdf-LUA_ERRERR">LUA_ERRERR</A><BR>
+<A HREF="manual.html#pdf-LUA_ERRFILE">LUA_ERRFILE</A><BR>
+<A HREF="manual.html#pdf-LUA_ERRMEM">LUA_ERRMEM</A><BR>
+<A HREF="manual.html#pdf-LUA_ERRRUN">LUA_ERRRUN</A><BR>
+<A HREF="manual.html#pdf-LUA_ERRSYNTAX">LUA_ERRSYNTAX</A><BR>
+<A HREF="manual.html#pdf-LUA_HOOKCALL">LUA_HOOKCALL</A><BR>
+<A HREF="manual.html#pdf-LUA_HOOKCOUNT">LUA_HOOKCOUNT</A><BR>
+<A HREF="manual.html#pdf-LUA_HOOKLINE">LUA_HOOKLINE</A><BR>
+<A HREF="manual.html#pdf-LUA_HOOKRET">LUA_HOOKRET</A><BR>
+<A HREF="manual.html#pdf-LUA_HOOKTAILCALL">LUA_HOOKTAILCALL</A><BR>
+<A HREF="manual.html#pdf-LUA_LOADED_TABLE">LUA_LOADED_TABLE</A><BR>
+<A HREF="manual.html#pdf-LUA_MASKCALL">LUA_MASKCALL</A><BR>
+<A HREF="manual.html#pdf-LUA_MASKCOUNT">LUA_MASKCOUNT</A><BR>
+<A HREF="manual.html#pdf-LUA_MASKLINE">LUA_MASKLINE</A><BR>
+<A HREF="manual.html#pdf-LUA_MASKRET">LUA_MASKRET</A><BR>
+<A HREF="manual.html#pdf-LUA_MAXINTEGER">LUA_MAXINTEGER</A><BR>
+<A HREF="manual.html#pdf-LUA_MININTEGER">LUA_MININTEGER</A><BR>
+<A HREF="manual.html#pdf-LUA_MINSTACK">LUA_MINSTACK</A><BR>
+<A HREF="manual.html#pdf-LUA_MULTRET">LUA_MULTRET</A><BR>
+<A HREF="manual.html#pdf-LUA_NOREF">LUA_NOREF</A><BR>
+<A HREF="manual.html#pdf-LUA_OK">LUA_OK</A><BR>
+<A HREF="manual.html#pdf-LUA_OPADD">LUA_OPADD</A><BR>
+<A HREF="manual.html#pdf-LUA_OPBAND">LUA_OPBAND</A><BR>
+<A HREF="manual.html#pdf-LUA_OPBNOT">LUA_OPBNOT</A><BR>
+<A HREF="manual.html#pdf-LUA_OPBOR">LUA_OPBOR</A><BR>
+<A HREF="manual.html#pdf-LUA_OPBXOR">LUA_OPBXOR</A><BR>
+<A HREF="manual.html#pdf-LUA_OPDIV">LUA_OPDIV</A><BR>
+<A HREF="manual.html#pdf-LUA_OPEQ">LUA_OPEQ</A><BR>
+<A HREF="manual.html#pdf-LUA_OPIDIV">LUA_OPIDIV</A><BR>
+<A HREF="manual.html#pdf-LUA_OPLE">LUA_OPLE</A><BR>
+<A HREF="manual.html#pdf-LUA_OPLT">LUA_OPLT</A><BR>
+<A HREF="manual.html#pdf-LUA_OPMOD">LUA_OPMOD</A><BR>
+<A HREF="manual.html#pdf-LUA_OPMUL">LUA_OPMUL</A><BR>
+<A HREF="manual.html#pdf-LUA_OPPOW">LUA_OPPOW</A><BR>
+<A HREF="manual.html#pdf-LUA_OPSHL">LUA_OPSHL</A><BR>
+<A HREF="manual.html#pdf-LUA_OPSHR">LUA_OPSHR</A><BR>
+<A HREF="manual.html#pdf-LUA_OPSUB">LUA_OPSUB</A><BR>
+<A HREF="manual.html#pdf-LUA_OPUNM">LUA_OPUNM</A><BR>
+<A HREF="manual.html#pdf-LUA_PRELOAD_TABLE">LUA_PRELOAD_TABLE</A><BR>
+<A HREF="manual.html#pdf-LUA_REFNIL">LUA_REFNIL</A><BR>
+<A HREF="manual.html#pdf-LUA_REGISTRYINDEX">LUA_REGISTRYINDEX</A><BR>
+<A HREF="manual.html#pdf-LUA_RIDX_GLOBALS">LUA_RIDX_GLOBALS</A><BR>
+<A HREF="manual.html#pdf-LUA_RIDX_MAINTHREAD">LUA_RIDX_MAINTHREAD</A><BR>
+<A HREF="manual.html#pdf-LUA_TBOOLEAN">LUA_TBOOLEAN</A><BR>
+<A HREF="manual.html#pdf-LUA_TFUNCTION">LUA_TFUNCTION</A><BR>
+<A HREF="manual.html#pdf-LUA_TLIGHTUSERDATA">LUA_TLIGHTUSERDATA</A><BR>
+<A HREF="manual.html#pdf-LUA_TNIL">LUA_TNIL</A><BR>
+<A HREF="manual.html#pdf-LUA_TNONE">LUA_TNONE</A><BR>
+<A HREF="manual.html#pdf-LUA_TNUMBER">LUA_TNUMBER</A><BR>
+<A HREF="manual.html#pdf-LUA_TSTRING">LUA_TSTRING</A><BR>
+<A HREF="manual.html#pdf-LUA_TTABLE">LUA_TTABLE</A><BR>
+<A HREF="manual.html#pdf-LUA_TTHREAD">LUA_TTHREAD</A><BR>
+<A HREF="manual.html#pdf-LUA_TUSERDATA">LUA_TUSERDATA</A><BR>
+<A HREF="manual.html#pdf-LUA_USE_APICHECK">LUA_USE_APICHECK</A><BR>
+<A HREF="manual.html#pdf-LUA_YIELD">LUA_YIELD</A><BR>
+<A HREF="manual.html#pdf-LUAL_BUFFERSIZE">LUAL_BUFFERSIZE</A><BR>
+
+</TD>
+</TR>
+</TABLE>
+
+<P CLASS="footer">
+Last update:
+Sat Apr 1 17:57:05 UTC 2023
+</P>
+<!--
+Last change: revised for Lua 5.4.5
+-->
+
+</BODY>
+</HTML>
diff --git a/src/libs/3rdparty/lua/doc/index.css b/src/libs/3rdparty/lua/doc/index.css
new file mode 100644
index 0000000000..c961835731
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/index.css
@@ -0,0 +1,21 @@
+ul {
+ list-style-type: none ;
+}
+
+ul.contents {
+ padding: 0 ;
+}
+
+table {
+ border: none ;
+ border-spacing: 0 ;
+ border-collapse: collapse ;
+}
+
+td {
+ vertical-align: top ;
+ padding: 0 ;
+ text-align: left ;
+ line-height: 1.25 ;
+ width: 15% ;
+}
diff --git a/src/libs/3rdparty/lua/doc/logo.gif b/src/libs/3rdparty/lua/doc/logo.gif
new file mode 100644
index 0000000000..5c77eacc3b
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/logo.gif
Binary files differ
diff --git a/src/libs/3rdparty/lua/doc/lua.1 b/src/libs/3rdparty/lua/doc/lua.1
new file mode 100644
index 0000000000..3f472fd81f
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/lua.1
@@ -0,0 +1,155 @@
+.\" $Id: lua.man,v 1.14 2022/09/23 09:06:36 lhf Exp $
+.TH LUA 1 "$Date: 2022/09/23 09:06:36 $"
+.SH NAME
+lua \- Lua interpreter
+.SH SYNOPSIS
+.B lua
+[
+.I options
+]
+[
+.I script
+[
+.I args
+]
+]
+.SH DESCRIPTION
+.B lua
+is the standalone Lua interpreter.
+It loads and executes Lua programs,
+either in textual source form or
+in precompiled binary form.
+(Precompiled binaries are output by
+.BR luac ,
+the Lua compiler.)
+.B lua
+can be used as a batch interpreter and also interactively.
+.LP
+After handling the
+.IR options ,
+the Lua program in file
+.I script
+is loaded and executed.
+The
+.I args
+are available to
+.I script
+as strings in a global table named
+.B arg
+and also as arguments to its main function.
+When called without arguments,
+.B lua
+behaves as
+.B "lua \-v \-i"
+if the standard input is a terminal,
+and as
+.B "lua \-"
+otherwise.
+.LP
+In interactive mode,
+.B lua
+prompts the user,
+reads lines from the standard input,
+and executes them as they are read.
+If the line contains an expression,
+then the line is evaluated and the result is printed.
+If a line does not contain a complete statement,
+then a secondary prompt is displayed and
+lines are read until a complete statement is formed or
+a syntax error is found.
+.LP
+Before handling command line options and scripts,
+.B lua
+checks the contents of the environment variables
+.B LUA_INIT_5_4
+and
+.BR LUA_INIT ,
+in that order.
+If the contents are of the form
+.RI '@ filename ',
+then
+.I filename
+is executed.
+Otherwise, the contents are assumed to be a Lua statement and is executed.
+When
+.B LUA_INIT_5_4
+is defined,
+.B LUA_INIT
+is ignored.
+.SH OPTIONS
+.TP
+.BI \-e " stat"
+execute statement
+.IR stat .
+.TP
+.B \-i
+enter interactive mode after executing
+.IR script .
+.TP
+.BI \-l " mod"
+require library
+.I mod
+into global
+.IR mod .
+.TP
+.BI \-l " g=mod"
+require library
+.I mod
+into global
+.IR g .
+.TP
+.B \-v
+show version information.
+.TP
+.B \-E
+ignore environment variables.
+.TP
+.B \-W
+turn warnings on.
+.TP
+.B \-\-
+stop handling options.
+.TP
+.B \-
+stop handling options and execute the standard input as a file.
+.SH ENVIRONMENT VARIABLES
+The following environment variables affect the execution of
+.BR lua .
+When defined,
+the version-specific variants take priority
+and the version-neutral variants are ignored.
+.TP
+.B LUA_INIT, LUA_INIT_5_4
+Code to be executed before command line options and scripts.
+.TP
+.B LUA_PATH, LUA_PATH_5_4
+Initial value of package.cpath,
+the path used by require to search for Lua loaders.
+.TP
+.B LUA_CPATH, LUA_CPATH_5_4
+Initial value of package.cpath,
+the path used by require to search for C loaders.
+.SH EXIT STATUS
+If a script calls os.exit,
+then
+.B lua
+exits with the given exit status.
+Otherwise,
+.B lua
+exits
+with EXIT_SUCCESS (0 on POSIX systems) if there were no errors
+and
+with EXIT_FAILURE (1 on POSIX systems) if there were errors.
+Errors raised in interactive mode do not cause exits.
+.SH DIAGNOSTICS
+Error messages should be self explanatory.
+.SH "SEE ALSO"
+.BR luac (1)
+.br
+The documentation at lua.org,
+especially section 7 of the reference manual.
+.SH AUTHORS
+R. Ierusalimschy,
+L. H. de Figueiredo,
+W. Celes
+.\" EOF
diff --git a/src/libs/3rdparty/lua/doc/lua.css b/src/libs/3rdparty/lua/doc/lua.css
new file mode 100644
index 0000000000..cbd0799d15
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/lua.css
@@ -0,0 +1,161 @@
+html {
+ background-color: #F8F8F8 ;
+}
+
+body {
+ background-color: #FFFFFF ;
+ color: #000000 ;
+ font-family: Helvetica, Arial, sans-serif ;
+ text-align: justify ;
+ line-height: 1.25 ;
+ margin: 16px auto ;
+ padding: 32px ;
+ border: solid #ccc 1px ;
+ border-radius: 20px ;
+ max-width: 70em ;
+ width: 90% ;
+}
+
+h1, h2, h3, h4 {
+ color: #000080 ;
+ font-family: Verdana, Geneva, sans-serif ;
+ font-weight: normal ;
+ font-style: normal ;
+ text-align: left ;
+}
+
+h1 {
+ font-size: 28pt ;
+}
+
+h1 img {
+ vertical-align: text-bottom ;
+}
+
+h2:before {
+ content: "\2756" ;
+ padding-right: 0.5em ;
+}
+
+a {
+ text-decoration: none ;
+}
+
+a:link {
+ color: #000080 ;
+}
+
+a:link:hover, a:visited:hover {
+ background-color: #D0D0FF ;
+ color: #000080 ;
+ border-radius: 4px ;
+}
+
+a:link:active, a:visited:active {
+ color: #FF0000 ;
+}
+
+div.menubar {
+ padding-bottom: 0.5em ;
+}
+
+p.menubar {
+ margin-left: 2.5em ;
+}
+
+.menubar a:hover {
+ margin: -3px -3px -3px -3px ;
+ padding: 3px 3px 3px 3px ;
+ border-radius: 4px ;
+}
+
+:target {
+ background-color: #F0F0F0 ;
+ margin: -8px ;
+ padding: 8px ;
+ border-radius: 8px ;
+ outline: none ;
+}
+
+hr {
+ display: none ;
+}
+
+table hr {
+ background-color: #a0a0a0 ;
+ color: #a0a0a0 ;
+ border: 0 ;
+ height: 1px ;
+ display: block ;
+}
+
+.footer {
+ color: gray ;
+ font-size: x-small ;
+ text-transform: lowercase ;
+}
+
+input[type=text] {
+ border: solid #a0a0a0 2px ;
+ border-radius: 2em ;
+ background-image: url('images/search.png') ;
+ background-repeat: no-repeat ;
+ background-position: 4px center ;
+ padding-left: 20px ;
+ height: 2em ;
+}
+
+pre.session {
+ background-color: #F8F8F8 ;
+ padding: 1em ;
+ border-radius: 8px ;
+}
+
+table {
+ border: none ;
+ border-spacing: 0 ;
+ border-collapse: collapse ;
+}
+
+td {
+ padding: 0 ;
+ margin: 0 ;
+}
+
+td.gutter {
+ width: 4% ;
+}
+
+table.columns td {
+ vertical-align: top ;
+ padding-bottom: 1em ;
+ text-align: justify ;
+ line-height: 1.25 ;
+}
+
+table.book td {
+ vertical-align: top ;
+}
+
+table.book td.cover {
+ padding-right: 1em ;
+}
+
+table.book img {
+ border: solid #000080 1px ;
+}
+
+table.book span {
+ font-size: small ;
+ text-align: left ;
+ display: block ;
+ margin-top: 0.25em ;
+}
+
+p.logos a:link:hover, p.logos a:visited:hover {
+ background-color: inherit ;
+}
+
+img {
+ background-color: white ;
+}
diff --git a/src/libs/3rdparty/lua/doc/luac.1 b/src/libs/3rdparty/lua/doc/luac.1
new file mode 100644
index 0000000000..33a4ed00ac
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/luac.1
@@ -0,0 +1,118 @@
+.\" $Id: luac.man,v 1.29 2011/11/16 13:53:40 lhf Exp $
+.TH LUAC 1 "$Date: 2011/11/16 13:53:40 $"
+.SH NAME
+luac \- Lua compiler
+.SH SYNOPSIS
+.B luac
+[
+.I options
+] [
+.I filenames
+]
+.SH DESCRIPTION
+.B luac
+is the Lua compiler.
+It translates programs written in the Lua programming language
+into binary files containing precompiled chunks
+that can be later loaded and executed.
+.LP
+The main advantages of precompiling chunks are:
+faster loading,
+protecting source code from accidental user changes,
+and
+off-line syntax checking.
+Precompiling does not imply faster execution
+because in Lua chunks are always compiled into bytecodes before being executed.
+.B luac
+simply allows those bytecodes to be saved in a file for later execution.
+Precompiled chunks are not necessarily smaller than the corresponding source.
+The main goal in precompiling is faster loading.
+.LP
+In the command line,
+you can mix
+text files containing Lua source and
+binary files containing precompiled chunks.
+.B luac
+produces a single output file containing the combined bytecodes
+for all files given.
+Executing the combined file is equivalent to executing the given files.
+By default,
+the output file is named
+.BR luac.out ,
+but you can change this with the
+.B \-o
+option.
+.LP
+Precompiled chunks are
+.I not
+portable across different architectures.
+Moreover,
+the internal format of precompiled chunks
+is likely to change when a new version of Lua is released.
+Make sure you save the source files of all Lua programs that you precompile.
+.LP
+.SH OPTIONS
+.TP
+.B \-l
+produce a listing of the compiled bytecode for Lua's virtual machine.
+Listing bytecodes is useful to learn about Lua's virtual machine.
+If no files are given, then
+.B luac
+loads
+.B luac.out
+and lists its contents.
+Use
+.B \-l \-l
+for a full listing.
+.TP
+.BI \-o " file"
+output to
+.IR file ,
+instead of the default
+.BR luac.out .
+(You can use
+.B "'\-'"
+for standard output,
+but not on platforms that open standard output in text mode.)
+The output file may be one of the given files because
+all files are loaded before the output file is written.
+Be careful not to overwrite precious files.
+.TP
+.B \-p
+load files but do not generate any output file.
+Used mainly for syntax checking and for testing precompiled chunks:
+corrupted files will probably generate errors when loaded.
+If no files are given, then
+.B luac
+loads
+.B luac.out
+and tests its contents.
+No messages are displayed if the file loads without errors.
+.TP
+.B \-s
+strip debug information before writing the output file.
+This saves some space in very large chunks,
+but if errors occur when running a stripped chunk,
+then the error messages may not contain the full information they usually do.
+In particular,
+line numbers and names of local variables are lost.
+.TP
+.B \-v
+show version information.
+.TP
+.B \-\-
+stop handling options.
+.TP
+.B \-
+stop handling options and process standard input.
+.SH "SEE ALSO"
+.BR lua (1)
+.br
+The documentation at lua.org.
+.SH DIAGNOSTICS
+Error messages should be self explanatory.
+.SH AUTHORS
+R. Ierusalimschy,
+L. H. de Figueiredo,
+W. Celes
+.\" EOF
diff --git a/src/libs/3rdparty/lua/doc/manual.css b/src/libs/3rdparty/lua/doc/manual.css
new file mode 100644
index 0000000000..aa0e677dd5
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/manual.css
@@ -0,0 +1,21 @@
+h3 code {
+ font-family: inherit ;
+ font-size: inherit ;
+}
+
+pre, code {
+ font-size: 12pt ;
+}
+
+span.apii {
+ color: gray ;
+ float: right ;
+ font-family: inherit ;
+ font-style: normal ;
+ font-size: small ;
+}
+
+h2:before {
+ content: "" ;
+ padding-right: 0em ;
+}
diff --git a/src/libs/3rdparty/lua/doc/manual.html b/src/libs/3rdparty/lua/doc/manual.html
new file mode 100644
index 0000000000..0af688b343
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/manual.html
@@ -0,0 +1,12046 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML>
+<HEAD>
+<TITLE>Lua 5.4 Reference Manual</TITLE>
+<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
+<LINK REL="stylesheet" TYPE="text/css" HREF="manual.css">
+<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=iso-8859-1">
+</HEAD>
+
+<BODY>
+
+<H1>
+<A HREF="http://www.lua.org/"><IMG SRC="logo.gif" ALT="Lua"></A>
+Lua 5.4 Reference Manual
+</H1>
+
+<P>
+by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes
+
+<P>
+<SMALL>
+Copyright &copy; 2020&ndash;2023 Lua.org, PUC-Rio.
+Freely available under the terms of the
+<a href="http://www.lua.org/license.html">Lua license</a>.
+</SMALL>
+
+<DIV CLASS="menubar">
+<A HREF="contents.html#contents">contents</A>
+&middot;
+<A HREF="contents.html#index">index</A>
+&middot;
+<A HREF="http://www.lua.org/manual/">other versions</A>
+</DIV>
+
+<!-- ====================================================================== -->
+<p>
+
+<!-- $Id: manual.of $ -->
+
+
+
+
+<h1>1 &ndash; <a name="1">Introduction</a></h1>
+
+<p>
+Lua is a powerful, efficient, lightweight, embeddable scripting language.
+It supports procedural programming,
+object-oriented programming, functional programming,
+data-driven programming, and data description.
+
+
+<p>
+Lua combines simple procedural syntax with powerful data description
+constructs based on associative arrays and extensible semantics.
+Lua is dynamically typed,
+runs by interpreting bytecode with a register-based
+virtual machine,
+and has automatic memory management with
+a generational garbage collection,
+making it ideal for configuration, scripting,
+and rapid prototyping.
+
+
+<p>
+Lua is implemented as a library, written in <em>clean C</em>,
+the common subset of standard&nbsp;C and C++.
+The Lua distribution includes a host program called <code>lua</code>,
+which uses the Lua library to offer a complete,
+standalone Lua interpreter,
+for interactive or batch use.
+Lua is intended to be used both as a powerful, lightweight,
+embeddable scripting language for any program that needs one,
+and as a powerful but lightweight and efficient stand-alone language.
+
+
+<p>
+As an extension language, Lua has no notion of a "main" program:
+it works <em>embedded</em> in a host client,
+called the <em>embedding program</em> or simply the <em>host</em>.
+(Frequently, this host is the stand-alone <code>lua</code> program.)
+The host program can invoke functions to execute a piece of Lua code,
+can write and read Lua variables,
+and can register C&nbsp;functions to be called by Lua code.
+Through the use of C&nbsp;functions, Lua can be augmented to cope with
+a wide range of different domains,
+thus creating customized programming languages sharing a syntactical framework.
+
+
+<p>
+Lua is free software,
+and is provided as usual with no guarantees,
+as stated in its license.
+The implementation described in this manual is available
+at Lua's official web site, <code>www.lua.org</code>.
+
+
+<p>
+Like any other reference manual,
+this document is dry in places.
+For a discussion of the decisions behind the design of Lua,
+see the technical papers available at Lua's web site.
+For a detailed introduction to programming in Lua,
+see Roberto's book, <em>Programming in Lua</em>.
+
+
+
+<h1>2 &ndash; <a name="2">Basic Concepts</a></h1>
+
+
+
+<p>
+This section describes the basic concepts of the language.
+
+
+
+
+
+<h2>2.1 &ndash; <a name="2.1">Values and Types</a></h2>
+
+<p>
+Lua is a dynamically typed language.
+This means that
+variables do not have types; only values do.
+There are no type definitions in the language.
+All values carry their own type.
+
+
+<p>
+All values in Lua are first-class values.
+This means that all values can be stored in variables,
+passed as arguments to other functions, and returned as results.
+
+
+<p>
+There are eight basic types in Lua:
+<em>nil</em>, <em>boolean</em>, <em>number</em>,
+<em>string</em>, <em>function</em>, <em>userdata</em>,
+<em>thread</em>, and <em>table</em>.
+The type <em>nil</em> has one single value, <b>nil</b>,
+whose main property is to be different from any other value;
+it often represents the absence of a useful value.
+The type <em>boolean</em> has two values, <b>false</b> and <b>true</b>.
+Both <b>nil</b> and <b>false</b> make a condition false;
+they are collectively called <em>false values</em>.
+Any other value makes a condition true.
+Despite its name,
+<b>false</b> is frequently used as an alternative to <b>nil</b>,
+with the key difference that <b>false</b> behaves
+like a regular value in a table,
+while a <b>nil</b> in a table represents an absent key.
+
+
+<p>
+The type <em>number</em> represents both
+integer numbers and real (floating-point) numbers,
+using two subtypes: <em>integer</em> and <em>float</em>.
+Standard Lua uses 64-bit integers and double-precision (64-bit) floats,
+but you can also compile Lua so that it
+uses 32-bit integers and/or single-precision (32-bit) floats.
+The option with 32 bits for both integers and floats
+is particularly attractive
+for small machines and embedded systems.
+(See macro <code>LUA_32BITS</code> in file <code>luaconf.h</code>.)
+
+
+<p>
+Unless stated otherwise,
+any overflow when manipulating integer values <em>wrap around</em>,
+according to the usual rules of two-complement arithmetic.
+(In other words,
+the actual result is the unique representable integer
+that is equal modulo <em>2<sup>n</sup></em> to the mathematical result,
+where <em>n</em> is the number of bits of the integer type.)
+
+
+<p>
+Lua has explicit rules about when each subtype is used,
+but it also converts between them automatically as needed (see <a href="#3.4.3">&sect;3.4.3</a>).
+Therefore,
+the programmer may choose to mostly ignore the difference
+between integers and floats
+or to assume complete control over the representation of each number.
+
+
+<p>
+The type <em>string</em> represents immutable sequences of bytes.
+
+Lua is 8-bit clean:
+strings can contain any 8-bit value,
+including embedded zeros ('<code>\0</code>').
+Lua is also encoding-agnostic;
+it makes no assumptions about the contents of a string.
+The length of any string in Lua must fit in a Lua integer.
+
+
+<p>
+Lua can call (and manipulate) functions written in Lua and
+functions written in C (see <a href="#3.4.10">&sect;3.4.10</a>).
+Both are represented by the type <em>function</em>.
+
+
+<p>
+The type <em>userdata</em> is provided to allow arbitrary C&nbsp;data to
+be stored in Lua variables.
+A userdata value represents a block of raw memory.
+There are two kinds of userdata:
+<em>full userdata</em>,
+which is an object with a block of memory managed by Lua,
+and <em>light userdata</em>,
+which is simply a C&nbsp;pointer value.
+Userdata has no predefined operations in Lua,
+except assignment and identity test.
+By using <em>metatables</em>,
+the programmer can define operations for full userdata values
+(see <a href="#2.4">&sect;2.4</a>).
+Userdata values cannot be created or modified in Lua,
+only through the C&nbsp;API.
+This guarantees the integrity of data owned by
+the host program and C&nbsp;libraries.
+
+
+<p>
+The type <em>thread</em> represents independent threads of execution
+and it is used to implement coroutines (see <a href="#2.6">&sect;2.6</a>).
+Lua threads are not related to operating-system threads.
+Lua supports coroutines on all systems,
+even those that do not support threads natively.
+
+
+<p>
+The type <em>table</em> implements associative arrays,
+that is, arrays that can have as indices not only numbers,
+but any Lua value except <b>nil</b> and NaN.
+(<em>Not a Number</em> is a special floating-point value
+used by the IEEE 754 standard to represent
+undefined numerical results, such as <code>0/0</code>.)
+Tables can be <em>heterogeneous</em>;
+that is, they can contain values of all types (except <b>nil</b>).
+Any key associated to the value <b>nil</b> is not considered part of the table.
+Conversely, any key that is not part of a table has
+an associated value <b>nil</b>.
+
+
+<p>
+Tables are the sole data-structuring mechanism in Lua;
+they can be used to represent ordinary arrays, lists,
+symbol tables, sets, records, graphs, trees, etc.
+To represent records, Lua uses the field name as an index.
+The language supports this representation by
+providing <code>a.name</code> as syntactic sugar for <code>a["name"]</code>.
+There are several convenient ways to create tables in Lua
+(see <a href="#3.4.9">&sect;3.4.9</a>).
+
+
+<p>
+Like indices,
+the values of table fields can be of any type.
+In particular,
+because functions are first-class values,
+table fields can contain functions.
+Thus tables can also carry <em>methods</em> (see <a href="#3.4.11">&sect;3.4.11</a>).
+
+
+<p>
+The indexing of tables follows
+the definition of raw equality in the language.
+The expressions <code>a[i]</code> and <code>a[j]</code>
+denote the same table element
+if and only if <code>i</code> and <code>j</code> are raw equal
+(that is, equal without metamethods).
+In particular, floats with integral values
+are equal to their respective integers
+(e.g., <code>1.0 == 1</code>).
+To avoid ambiguities,
+any float used as a key that is equal to an integer
+is converted to that integer.
+For instance, if you write <code>a[2.0] = true</code>,
+the actual key inserted into the table will be the integer <code>2</code>.
+
+
+<p>
+Tables, functions, threads, and (full) userdata values are <em>objects</em>:
+variables do not actually <em>contain</em> these values,
+only <em>references</em> to them.
+Assignment, parameter passing, and function returns
+always manipulate references to such values;
+these operations do not imply any kind of copy.
+
+
+<p>
+The library function <a href="#pdf-type"><code>type</code></a> returns a string describing the type
+of a given value (see <a href="#pdf-type"><code>type</code></a>).
+
+
+
+
+
+<h2>2.2 &ndash; <a name="2.2">Environments and the Global Environment</a></h2>
+
+<p>
+As we will discuss further in <a href="#3.2">&sect;3.2</a> and <a href="#3.3.3">&sect;3.3.3</a>,
+any reference to a free name
+(that is, a name not bound to any declaration) <code>var</code>
+is syntactically translated to <code>_ENV.var</code>.
+Moreover, every chunk is compiled in the scope of
+an external local variable named <code>_ENV</code> (see <a href="#3.3.2">&sect;3.3.2</a>),
+so <code>_ENV</code> itself is never a free name in a chunk.
+
+
+<p>
+Despite the existence of this external <code>_ENV</code> variable and
+the translation of free names,
+<code>_ENV</code> is a completely regular name.
+In particular,
+you can define new variables and parameters with that name.
+Each reference to a free name uses the <code>_ENV</code> that is
+visible at that point in the program,
+following the usual visibility rules of Lua (see <a href="#3.5">&sect;3.5</a>).
+
+
+<p>
+Any table used as the value of <code>_ENV</code> is called an <em>environment</em>.
+
+
+<p>
+Lua keeps a distinguished environment called the <em>global environment</em>.
+This value is kept at a special index in the C registry (see <a href="#4.3">&sect;4.3</a>).
+In Lua, the global variable <a href="#pdf-_G"><code>_G</code></a> is initialized with this same value.
+(<a href="#pdf-_G"><code>_G</code></a> is never used internally,
+so changing its value will affect only your own code.)
+
+
+<p>
+When Lua loads a chunk,
+the default value for its <code>_ENV</code> variable
+is the global environment (see <a href="#pdf-load"><code>load</code></a>).
+Therefore, by default,
+free names in Lua code refer to entries in the global environment
+and, therefore, they are also called <em>global variables</em>.
+Moreover, all standard libraries are loaded in the global environment
+and some functions there operate on that environment.
+You can use <a href="#pdf-load"><code>load</code></a> (or <a href="#pdf-loadfile"><code>loadfile</code></a>)
+to load a chunk with a different environment.
+(In C, you have to load the chunk and then change the value
+of its first upvalue; see <a href="#lua_setupvalue"><code>lua_setupvalue</code></a>.)
+
+
+
+
+
+<h2>2.3 &ndash; <a name="2.3">Error Handling</a></h2>
+
+<p>
+Several operations in Lua can <em>raise</em> an error.
+An error interrupts the normal flow of the program,
+which can continue by <em>catching</em> the error.
+
+
+<p>
+Lua code can explicitly raise an error by calling the
+<a href="#pdf-error"><code>error</code></a> function.
+(This function never returns.)
+
+
+<p>
+To catch errors in Lua,
+you can do a <em>protected call</em>,
+using <a href="#pdf-pcall"><code>pcall</code></a> (or <a href="#pdf-xpcall"><code>xpcall</code></a>).
+The function <a href="#pdf-pcall"><code>pcall</code></a> calls a given function in <em>protected mode</em>.
+Any error while running the function stops its execution,
+and control returns immediately to <code>pcall</code>,
+which returns a status code.
+
+
+<p>
+Because Lua is an embedded extension language,
+Lua code starts running by a call
+from C&nbsp;code in the host program.
+(When you use Lua standalone,
+the <code>lua</code> application is the host program.)
+Usually, this call is protected;
+so, when an otherwise unprotected error occurs during
+the compilation or execution of a Lua chunk,
+control returns to the host,
+which can take appropriate measures,
+such as printing an error message.
+
+
+<p>
+Whenever there is an error,
+an <em>error object</em>
+is propagated with information about the error.
+Lua itself only generates errors whose error object is a string,
+but programs may generate errors with
+any value as the error object.
+It is up to the Lua program or its host to handle such error objects.
+For historical reasons,
+an error object is often called an <em>error message</em>,
+even though it does not have to be a string.
+
+
+<p>
+When you use <a href="#pdf-xpcall"><code>xpcall</code></a> (or <a href="#lua_pcall"><code>lua_pcall</code></a>, in C)
+you may give a <em>message handler</em>
+to be called in case of errors.
+This function is called with the original error object
+and returns a new error object.
+It is called before the error unwinds the stack,
+so that it can gather more information about the error,
+for instance by inspecting the stack and creating a stack traceback.
+This message handler is still protected by the protected call;
+so, an error inside the message handler
+will call the message handler again.
+If this loop goes on for too long,
+Lua breaks it and returns an appropriate message.
+The message handler is called only for regular runtime errors.
+It is not called for memory-allocation errors
+nor for errors while running finalizers or other message handlers.
+
+
+<p>
+Lua also offers a system of <em>warnings</em> (see <a href="#pdf-warn"><code>warn</code></a>).
+Unlike errors, warnings do not interfere
+in any way with program execution.
+They typically only generate a message to the user,
+although this behavior can be adapted from C (see <a href="#lua_setwarnf"><code>lua_setwarnf</code></a>).
+
+
+
+
+
+<h2>2.4 &ndash; <a name="2.4">Metatables and Metamethods</a></h2>
+
+<p>
+Every value in Lua can have a <em>metatable</em>.
+This <em>metatable</em> is an ordinary Lua table
+that defines the behavior of the original value
+under certain events.
+You can change several aspects of the behavior
+of a value by setting specific fields in its metatable.
+For instance, when a non-numeric value is the operand of an addition,
+Lua checks for a function in the field <code>__add</code> of the value's metatable.
+If it finds one,
+Lua calls this function to perform the addition.
+
+
+<p>
+The key for each event in a metatable is a string
+with the event name prefixed by two underscores;
+the corresponding value is called a <em>metavalue</em>.
+For most events, the metavalue must be a function,
+which is then called a <em>metamethod</em>.
+In the previous example, the key is the string "<code>__add</code>"
+and the metamethod is the function that performs the addition.
+Unless stated otherwise,
+a metamethod may in fact be any callable value,
+which is either a function or a value with a <code>__call</code> metamethod.
+
+
+<p>
+You can query the metatable of any value
+using the <a href="#pdf-getmetatable"><code>getmetatable</code></a> function.
+Lua queries metamethods in metatables using a raw access (see <a href="#pdf-rawget"><code>rawget</code></a>).
+
+
+<p>
+You can replace the metatable of tables
+using the <a href="#pdf-setmetatable"><code>setmetatable</code></a> function.
+You cannot change the metatable of other types from Lua code,
+except by using the debug library (<a href="#6.10">&sect;6.10</a>).
+
+
+<p>
+Tables and full userdata have individual metatables,
+although multiple tables and userdata can share their metatables.
+Values of all other types share one single metatable per type;
+that is, there is one single metatable for all numbers,
+one for all strings, etc.
+By default, a value has no metatable,
+but the string library sets a metatable for the string type (see <a href="#6.4">&sect;6.4</a>).
+
+
+<p>
+A detailed list of operations controlled by metatables is given next.
+Each event is identified by its corresponding key.
+By convention, all metatable keys used by Lua are composed by
+two underscores followed by lowercase Latin letters.
+
+
+
+<ul>
+
+<li><b><code>__add</code>: </b>
+the addition (<code>+</code>) operation.
+If any operand for an addition is not a number,
+Lua will try to call a metamethod.
+It starts by checking the first operand (even if it is a number);
+if that operand does not define a metamethod for <code>__add</code>,
+then Lua will check the second operand.
+If Lua can find a metamethod,
+it calls the metamethod with the two operands as arguments,
+and the result of the call
+(adjusted to one value)
+is the result of the operation.
+Otherwise, if no metamethod is found,
+Lua raises an error.
+</li>
+
+<li><b><code>__sub</code>: </b>
+the subtraction (<code>-</code>) operation.
+Behavior similar to the addition operation.
+</li>
+
+<li><b><code>__mul</code>: </b>
+the multiplication (<code>*</code>) operation.
+Behavior similar to the addition operation.
+</li>
+
+<li><b><code>__div</code>: </b>
+the division (<code>/</code>) operation.
+Behavior similar to the addition operation.
+</li>
+
+<li><b><code>__mod</code>: </b>
+the modulo (<code>%</code>) operation.
+Behavior similar to the addition operation.
+</li>
+
+<li><b><code>__pow</code>: </b>
+the exponentiation (<code>^</code>) operation.
+Behavior similar to the addition operation.
+</li>
+
+<li><b><code>__unm</code>: </b>
+the negation (unary <code>-</code>) operation.
+Behavior similar to the addition operation.
+</li>
+
+<li><b><code>__idiv</code>: </b>
+the floor division (<code>//</code>) operation.
+Behavior similar to the addition operation.
+</li>
+
+<li><b><code>__band</code>: </b>
+the bitwise AND (<code>&amp;</code>) operation.
+Behavior similar to the addition operation,
+except that Lua will try a metamethod
+if any operand is neither an integer
+nor a float coercible to an integer (see <a href="#3.4.3">&sect;3.4.3</a>).
+</li>
+
+<li><b><code>__bor</code>: </b>
+the bitwise OR (<code>|</code>) operation.
+Behavior similar to the bitwise AND operation.
+</li>
+
+<li><b><code>__bxor</code>: </b>
+the bitwise exclusive OR (binary <code>~</code>) operation.
+Behavior similar to the bitwise AND operation.
+</li>
+
+<li><b><code>__bnot</code>: </b>
+the bitwise NOT (unary <code>~</code>) operation.
+Behavior similar to the bitwise AND operation.
+</li>
+
+<li><b><code>__shl</code>: </b>
+the bitwise left shift (<code>&lt;&lt;</code>) operation.
+Behavior similar to the bitwise AND operation.
+</li>
+
+<li><b><code>__shr</code>: </b>
+the bitwise right shift (<code>&gt;&gt;</code>) operation.
+Behavior similar to the bitwise AND operation.
+</li>
+
+<li><b><code>__concat</code>: </b>
+the concatenation (<code>..</code>) operation.
+Behavior similar to the addition operation,
+except that Lua will try a metamethod
+if any operand is neither a string nor a number
+(which is always coercible to a string).
+</li>
+
+<li><b><code>__len</code>: </b>
+the length (<code>#</code>) operation.
+If the object is not a string,
+Lua will try its metamethod.
+If there is a metamethod,
+Lua calls it with the object as argument,
+and the result of the call
+(always adjusted to one value)
+is the result of the operation.
+If there is no metamethod but the object is a table,
+then Lua uses the table length operation (see <a href="#3.4.7">&sect;3.4.7</a>).
+Otherwise, Lua raises an error.
+</li>
+
+<li><b><code>__eq</code>: </b>
+the equal (<code>==</code>) operation.
+Behavior similar to the addition operation,
+except that Lua will try a metamethod only when the values
+being compared are either both tables or both full userdata
+and they are not primitively equal.
+The result of the call is always converted to a boolean.
+</li>
+
+<li><b><code>__lt</code>: </b>
+the less than (<code>&lt;</code>) operation.
+Behavior similar to the addition operation,
+except that Lua will try a metamethod only when the values
+being compared are neither both numbers nor both strings.
+Moreover, the result of the call is always converted to a boolean.
+</li>
+
+<li><b><code>__le</code>: </b>
+the less equal (<code>&lt;=</code>) operation.
+Behavior similar to the less than operation.
+</li>
+
+<li><b><code>__index</code>: </b>
+The indexing access operation <code>table[key]</code>.
+This event happens when <code>table</code> is not a table or
+when <code>key</code> is not present in <code>table</code>.
+The metavalue is looked up in the metatable of <code>table</code>.
+
+
+<p>
+The metavalue for this event can be either a function, a table,
+or any value with an <code>__index</code> metavalue.
+If it is a function,
+it is called with <code>table</code> and <code>key</code> as arguments,
+and the result of the call
+(adjusted to one value)
+is the result of the operation.
+Otherwise,
+the final result is the result of indexing this metavalue with <code>key</code>.
+This indexing is regular, not raw,
+and therefore can trigger another <code>__index</code> metavalue.
+</li>
+
+<li><b><code>__newindex</code>: </b>
+The indexing assignment <code>table[key] = value</code>.
+Like the index event,
+this event happens when <code>table</code> is not a table or
+when <code>key</code> is not present in <code>table</code>.
+The metavalue is looked up in the metatable of <code>table</code>.
+
+
+<p>
+Like with indexing,
+the metavalue for this event can be either a function, a table,
+or any value with an <code>__newindex</code> metavalue.
+If it is a function,
+it is called with <code>table</code>, <code>key</code>, and <code>value</code> as arguments.
+Otherwise,
+Lua repeats the indexing assignment over this metavalue
+with the same key and value.
+This assignment is regular, not raw,
+and therefore can trigger another <code>__newindex</code> metavalue.
+
+
+<p>
+Whenever a <code>__newindex</code> metavalue is invoked,
+Lua does not perform the primitive assignment.
+If needed,
+the metamethod itself can call <a href="#pdf-rawset"><code>rawset</code></a>
+to do the assignment.
+</li>
+
+<li><b><code>__call</code>: </b>
+The call operation <code>func(args)</code>.
+This event happens when Lua tries to call a non-function value
+(that is, <code>func</code> is not a function).
+The metamethod is looked up in <code>func</code>.
+If present,
+the metamethod is called with <code>func</code> as its first argument,
+followed by the arguments of the original call (<code>args</code>).
+All results of the call
+are the results of the operation.
+This is the only metamethod that allows multiple results.
+</li>
+
+</ul>
+
+<p>
+In addition to the previous list,
+the interpreter also respects the following keys in metatables:
+<code>__gc</code> (see <a href="#2.5.3">&sect;2.5.3</a>),
+<code>__close</code> (see <a href="#3.3.8">&sect;3.3.8</a>),
+<code>__mode</code> (see <a href="#2.5.4">&sect;2.5.4</a>),
+and <code>__name</code>.
+(The entry <code>__name</code>,
+when it contains a string,
+may be used by <a href="#pdf-tostring"><code>tostring</code></a> and in error messages.)
+
+
+<p>
+For the unary operators (negation, length, and bitwise NOT),
+the metamethod is computed and called with a dummy second operand,
+equal to the first one.
+This extra operand is only to simplify Lua's internals
+(by making these operators behave like a binary operation)
+and may be removed in future versions.
+For most uses this extra operand is irrelevant.
+
+
+<p>
+Because metatables are regular tables,
+they can contain arbitrary fields,
+not only the event names defined above.
+Some functions in the standard library
+(e.g., <a href="#pdf-tostring"><code>tostring</code></a>)
+use other fields in metatables for their own purposes.
+
+
+<p>
+It is a good practice to add all needed metamethods to a table
+before setting it as a metatable of some object.
+In particular, the <code>__gc</code> metamethod works only when this order
+is followed (see <a href="#2.5.3">&sect;2.5.3</a>).
+It is also a good practice to set the metatable of an object
+right after its creation.
+
+
+
+
+
+<h2>2.5 &ndash; <a name="2.5">Garbage Collection</a></h2>
+
+
+
+<p>
+Lua performs automatic memory management.
+This means that
+you do not have to worry about allocating memory for new objects
+or freeing it when the objects are no longer needed.
+Lua manages memory automatically by running
+a <em>garbage collector</em> to collect all <em>dead</em> objects.
+All memory used by Lua is subject to automatic management:
+strings, tables, userdata, functions, threads, internal structures, etc.
+
+
+<p>
+An object is considered <em>dead</em>
+as soon as the collector can be sure the object
+will not be accessed again in the normal execution of the program.
+("Normal execution" here excludes finalizers,
+which can resurrect dead objects (see <a href="#2.5.3">&sect;2.5.3</a>),
+and excludes also operations using the debug library.)
+Note that the time when the collector can be sure that an object
+is dead may not coincide with the programmer's expectations.
+The only guarantees are that Lua will not collect an object
+that may still be accessed in the normal execution of the program,
+and it will eventually collect an object
+that is inaccessible from Lua.
+(Here,
+<em>inaccessible from Lua</em> means that neither a variable nor
+another live object refer to the object.)
+Because Lua has no knowledge about C&nbsp;code,
+it never collects objects accessible through the registry (see <a href="#4.3">&sect;4.3</a>),
+which includes the global environment (see <a href="#2.2">&sect;2.2</a>).
+
+
+<p>
+The garbage collector (GC) in Lua can work in two modes:
+incremental and generational.
+
+
+<p>
+The default GC mode with the default parameters
+are adequate for most uses.
+However, programs that waste a large proportion of their time
+allocating and freeing memory can benefit from other settings.
+Keep in mind that the GC behavior is non-portable
+both across platforms and across different Lua releases;
+therefore, optimal settings are also non-portable.
+
+
+<p>
+You can change the GC mode and parameters by calling
+<a href="#lua_gc"><code>lua_gc</code></a> in&nbsp;C
+or <a href="#pdf-collectgarbage"><code>collectgarbage</code></a> in Lua.
+You can also use these functions to control
+the collector directly (e.g., to stop and restart it).
+
+
+
+
+
+<h3>2.5.1 &ndash; <a name="2.5.1">Incremental Garbage Collection</a></h3>
+
+<p>
+In incremental mode,
+each GC cycle performs a mark-and-sweep collection in small steps
+interleaved with the program's execution.
+In this mode,
+the collector uses three numbers to control its garbage-collection cycles:
+the <em>garbage-collector pause</em>,
+the <em>garbage-collector step multiplier</em>,
+and the <em>garbage-collector step size</em>.
+
+
+<p>
+The garbage-collector pause
+controls how long the collector waits before starting a new cycle.
+The collector starts a new cycle when the use of memory
+hits <em>n%</em> of the use after the previous collection.
+Larger values make the collector less aggressive.
+Values equal to or less than 100 mean the collector will not wait to
+start a new cycle.
+A value of 200 means that the collector waits for the total memory in use
+to double before starting a new cycle.
+The default value is 200; the maximum value is 1000.
+
+
+<p>
+The garbage-collector step multiplier
+controls the speed of the collector relative to
+memory allocation,
+that is,
+how many elements it marks or sweeps for each
+kilobyte of memory allocated.
+Larger values make the collector more aggressive but also increase
+the size of each incremental step.
+You should not use values less than 100,
+because they make the collector too slow and
+can result in the collector never finishing a cycle.
+The default value is 100; the maximum value is 1000.
+
+
+<p>
+The garbage-collector step size controls the
+size of each incremental step,
+specifically how many bytes the interpreter allocates
+before performing a step.
+This parameter is logarithmic:
+A value of <em>n</em> means the interpreter will allocate <em>2<sup>n</sup></em>
+bytes between steps and perform equivalent work during the step.
+A large value (e.g., 60) makes the collector a stop-the-world
+(non-incremental) collector.
+The default value is 13,
+which means steps of approximately 8&nbsp;Kbytes.
+
+
+
+
+
+<h3>2.5.2 &ndash; <a name="2.5.2">Generational Garbage Collection</a></h3>
+
+<p>
+In generational mode,
+the collector does frequent <em>minor</em> collections,
+which traverses only objects recently created.
+If after a minor collection the use of memory is still above a limit,
+the collector does a stop-the-world <em>major</em> collection,
+which traverses all objects.
+The generational mode uses two parameters:
+the <em>minor multiplier</em> and the <em>the major multiplier</em>.
+
+
+<p>
+The minor multiplier controls the frequency of minor collections.
+For a minor multiplier <em>x</em>,
+a new minor collection will be done when memory
+grows <em>x%</em> larger than the memory in use after the previous major
+collection.
+For instance, for a multiplier of 20,
+the collector will do a minor collection when the use of memory
+gets 20% larger than the use after the previous major collection.
+The default value is 20; the maximum value is 200.
+
+
+<p>
+The major multiplier controls the frequency of major collections.
+For a major multiplier <em>x</em>,
+a new major collection will be done when memory
+grows <em>x%</em> larger than the memory in use after the previous major
+collection.
+For instance, for a multiplier of 100,
+the collector will do a major collection when the use of memory
+gets larger than twice the use after the previous collection.
+The default value is 100; the maximum value is 1000.
+
+
+
+
+
+<h3>2.5.3 &ndash; <a name="2.5.3">Garbage-Collection Metamethods</a></h3>
+
+<p>
+You can set garbage-collector metamethods for tables
+and, using the C&nbsp;API,
+for full userdata (see <a href="#2.4">&sect;2.4</a>).
+These metamethods, called <em>finalizers</em>,
+are called when the garbage collector detects that the
+corresponding table or userdata is dead.
+Finalizers allow you to coordinate Lua's garbage collection
+with external resource management such as closing files,
+network or database connections,
+or freeing your own memory.
+
+
+<p>
+For an object (table or userdata) to be finalized when collected,
+you must <em>mark</em> it for finalization.
+
+You mark an object for finalization when you set its metatable
+and the metatable has a <code>__gc</code> metamethod.
+Note that if you set a metatable without a <code>__gc</code> field
+and later create that field in the metatable,
+the object will not be marked for finalization.
+
+
+<p>
+When a marked object becomes dead,
+it is not collected immediately by the garbage collector.
+Instead, Lua puts it in a list.
+After the collection,
+Lua goes through that list.
+For each object in the list,
+it checks the object's <code>__gc</code> metamethod:
+If it is present,
+Lua calls it with the object as its single argument.
+
+
+<p>
+At the end of each garbage-collection cycle,
+the finalizers are called in
+the reverse order that the objects were marked for finalization,
+among those collected in that cycle;
+that is, the first finalizer to be called is the one associated
+with the object marked last in the program.
+The execution of each finalizer may occur at any point during
+the execution of the regular code.
+
+
+<p>
+Because the object being collected must still be used by the finalizer,
+that object (and other objects accessible only through it)
+must be <em>resurrected</em> by Lua.
+Usually, this resurrection is transient,
+and the object memory is freed in the next garbage-collection cycle.
+However, if the finalizer stores the object in some global place
+(e.g., a global variable),
+then the resurrection is permanent.
+Moreover, if the finalizer marks a finalizing object for finalization again,
+its finalizer will be called again in the next cycle where the
+object is dead.
+In any case,
+the object memory is freed only in a GC cycle where
+the object is dead and not marked for finalization.
+
+
+<p>
+When you close a state (see <a href="#lua_close"><code>lua_close</code></a>),
+Lua calls the finalizers of all objects marked for finalization,
+following the reverse order that they were marked.
+If any finalizer marks objects for collection during that phase,
+these marks have no effect.
+
+
+<p>
+Finalizers cannot yield nor run the garbage collector.
+Because they can run in unpredictable times,
+it is good practice to restrict each finalizer
+to the minimum necessary to properly release
+its associated resource.
+
+
+<p>
+Any error while running a finalizer generates a warning;
+the error is not propagated.
+
+
+
+
+
+<h3>2.5.4 &ndash; <a name="2.5.4">Weak Tables</a></h3>
+
+<p>
+A <em>weak table</em> is a table whose elements are
+<em>weak references</em>.
+A weak reference is ignored by the garbage collector.
+In other words,
+if the only references to an object are weak references,
+then the garbage collector will collect that object.
+
+
+<p>
+A weak table can have weak keys, weak values, or both.
+A table with weak values allows the collection of its values,
+but prevents the collection of its keys.
+A table with both weak keys and weak values allows the collection of
+both keys and values.
+In any case, if either the key or the value is collected,
+the whole pair is removed from the table.
+The weakness of a table is controlled by the
+<code>__mode</code> field of its metatable.
+This metavalue, if present, must be one of the following strings:
+"<code>k</code>", for a table with weak keys;
+"<code>v</code>", for a table with weak values;
+or "<code>kv</code>", for a table with both weak keys and values.
+
+
+<p>
+A table with weak keys and strong values
+is also called an <em>ephemeron table</em>.
+In an ephemeron table,
+a value is considered reachable only if its key is reachable.
+In particular,
+if the only reference to a key comes through its value,
+the pair is removed.
+
+
+<p>
+Any change in the weakness of a table may take effect only
+at the next collect cycle.
+In particular, if you change the weakness to a stronger mode,
+Lua may still collect some items from that table
+before the change takes effect.
+
+
+<p>
+Only objects that have an explicit construction
+are removed from weak tables.
+Values, such as numbers and light C&nbsp;functions,
+are not subject to garbage collection,
+and therefore are not removed from weak tables
+(unless their associated values are collected).
+Although strings are subject to garbage collection,
+they do not have an explicit construction and
+their equality is by value;
+they behave more like values than like objects.
+Therefore, they are not removed from weak tables.
+
+
+<p>
+Resurrected objects
+(that is, objects being finalized
+and objects accessible only through objects being finalized)
+have a special behavior in weak tables.
+They are removed from weak values before running their finalizers,
+but are removed from weak keys only in the next collection
+after running their finalizers, when such objects are actually freed.
+This behavior allows the finalizer to access properties
+associated with the object through weak tables.
+
+
+<p>
+If a weak table is among the resurrected objects in a collection cycle,
+it may not be properly cleared until the next cycle.
+
+
+
+
+
+
+
+<h2>2.6 &ndash; <a name="2.6">Coroutines</a></h2>
+
+<p>
+Lua supports coroutines,
+also called <em>collaborative multithreading</em>.
+A coroutine in Lua represents an independent thread of execution.
+Unlike threads in multithread systems, however,
+a coroutine only suspends its execution by explicitly calling
+a yield function.
+
+
+<p>
+You create a coroutine by calling <a href="#pdf-coroutine.create"><code>coroutine.create</code></a>.
+Its sole argument is a function
+that is the main function of the coroutine.
+The <code>create</code> function only creates a new coroutine and
+returns a handle to it (an object of type <em>thread</em>);
+it does not start the coroutine.
+
+
+<p>
+You execute a coroutine by calling <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a>.
+When you first call <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a>,
+passing as its first argument
+a thread returned by <a href="#pdf-coroutine.create"><code>coroutine.create</code></a>,
+the coroutine starts its execution by
+calling its main function.
+Extra arguments passed to <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a> are passed
+as arguments to that function.
+After the coroutine starts running,
+it runs until it terminates or <em>yields</em>.
+
+
+<p>
+A coroutine can terminate its execution in two ways:
+normally, when its main function returns
+(explicitly or implicitly, after the last instruction);
+and abnormally, if there is an unprotected error.
+In case of normal termination,
+<a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a> returns <b>true</b>,
+plus any values returned by the coroutine main function.
+In case of errors, <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a> returns <b>false</b>
+plus the error object.
+In this case, the coroutine does not unwind its stack,
+so that it is possible to inspect it after the error
+with the debug API.
+
+
+<p>
+A coroutine yields by calling <a href="#pdf-coroutine.yield"><code>coroutine.yield</code></a>.
+When a coroutine yields,
+the corresponding <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a> returns immediately,
+even if the yield happens inside nested function calls
+(that is, not in the main function,
+but in a function directly or indirectly called by the main function).
+In the case of a yield, <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a> also returns <b>true</b>,
+plus any values passed to <a href="#pdf-coroutine.yield"><code>coroutine.yield</code></a>.
+The next time you resume the same coroutine,
+it continues its execution from the point where it yielded,
+with the call to <a href="#pdf-coroutine.yield"><code>coroutine.yield</code></a> returning any extra
+arguments passed to <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a>.
+
+
+<p>
+Like <a href="#pdf-coroutine.create"><code>coroutine.create</code></a>,
+the <a href="#pdf-coroutine.wrap"><code>coroutine.wrap</code></a> function also creates a coroutine,
+but instead of returning the coroutine itself,
+it returns a function that, when called, resumes the coroutine.
+Any arguments passed to this function
+go as extra arguments to <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a>.
+<a href="#pdf-coroutine.wrap"><code>coroutine.wrap</code></a> returns all the values returned by <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a>,
+except the first one (the boolean error code).
+Unlike <a href="#pdf-coroutine.resume"><code>coroutine.resume</code></a>,
+the function created by <a href="#pdf-coroutine.wrap"><code>coroutine.wrap</code></a>
+propagates any error to the caller.
+In this case,
+the function also closes the coroutine (see <a href="#pdf-coroutine.close"><code>coroutine.close</code></a>).
+
+
+<p>
+As an example of how coroutines work,
+consider the following code:
+
+<pre>
+ function foo (a)
+ print("foo", a)
+ return coroutine.yield(2*a)
+ end
+
+ co = coroutine.create(function (a,b)
+ print("co-body", a, b)
+ local r = foo(a+1)
+ print("co-body", r)
+ local r, s = coroutine.yield(a+b, a-b)
+ print("co-body", r, s)
+ return b, "end"
+ end)
+
+ print("main", coroutine.resume(co, 1, 10))
+ print("main", coroutine.resume(co, "r"))
+ print("main", coroutine.resume(co, "x", "y"))
+ print("main", coroutine.resume(co, "x", "y"))
+</pre><p>
+When you run it, it produces the following output:
+
+<pre>
+ co-body 1 10
+ foo 2
+ main true 4
+ co-body r
+ main true 11 -9
+ co-body x y
+ main true 10 end
+ main false cannot resume dead coroutine
+</pre>
+
+<p>
+You can also create and manipulate coroutines through the C API:
+see functions <a href="#lua_newthread"><code>lua_newthread</code></a>, <a href="#lua_resume"><code>lua_resume</code></a>,
+and <a href="#lua_yield"><code>lua_yield</code></a>.
+
+
+
+
+
+<h1>3 &ndash; <a name="3">The Language</a></h1>
+
+
+
+<p>
+This section describes the lexis, the syntax, and the semantics of Lua.
+In other words,
+this section describes
+which tokens are valid,
+how they can be combined,
+and what their combinations mean.
+
+
+<p>
+Language constructs will be explained using the usual extended BNF notation,
+in which
+{<em>a</em>}&nbsp;means&nbsp;0 or more <em>a</em>'s, and
+[<em>a</em>]&nbsp;means an optional <em>a</em>.
+Non-terminals are shown like non-terminal,
+keywords are shown like <b>kword</b>,
+and other terminal symbols are shown like &lsquo;<b>=</b>&rsquo;.
+The complete syntax of Lua can be found in <a href="#9">&sect;9</a>
+at the end of this manual.
+
+
+
+
+
+<h2>3.1 &ndash; <a name="3.1">Lexical Conventions</a></h2>
+
+<p>
+Lua is a free-form language.
+It ignores spaces and comments between lexical elements (tokens),
+except as delimiters between two tokens.
+In source code,
+Lua recognizes as spaces the standard ASCII whitespace
+characters space, form feed, newline,
+carriage return, horizontal tab, and vertical tab.
+
+
+<p>
+<em>Names</em>
+(also called <em>identifiers</em>)
+in Lua can be any string of Latin letters,
+Arabic-Indic digits, and underscores,
+not beginning with a digit and
+not being a reserved word.
+Identifiers are used to name variables, table fields, and labels.
+
+
+<p>
+The following <em>keywords</em> are reserved
+and cannot be used as names:
+
+
+<pre>
+ and break do else elseif end
+ false for function goto if in
+ local nil not or repeat return
+ then true until while
+</pre>
+
+<p>
+Lua is a case-sensitive language:
+<code>and</code> is a reserved word, but <code>And</code> and <code>AND</code>
+are two different, valid names.
+As a convention,
+programs should avoid creating
+names that start with an underscore followed by
+one or more uppercase letters (such as <a href="#pdf-_VERSION"><code>_VERSION</code></a>).
+
+
+<p>
+The following strings denote other tokens:
+
+<pre>
+ + - * / % ^ #
+ &amp; ~ | &lt;&lt; &gt;&gt; //
+ == ~= &lt;= &gt;= &lt; &gt; =
+ ( ) { } [ ] ::
+ ; : , . .. ...
+</pre>
+
+<p>
+A <em>short literal string</em>
+can be delimited by matching single or double quotes,
+and can contain the following C-like escape sequences:
+'<code>\a</code>' (bell),
+'<code>\b</code>' (backspace),
+'<code>\f</code>' (form feed),
+'<code>\n</code>' (newline),
+'<code>\r</code>' (carriage return),
+'<code>\t</code>' (horizontal tab),
+'<code>\v</code>' (vertical tab),
+'<code>\\</code>' (backslash),
+'<code>\"</code>' (quotation mark [double quote]),
+and '<code>\'</code>' (apostrophe [single quote]).
+A backslash followed by a line break
+results in a newline in the string.
+The escape sequence '<code>\z</code>' skips the following span
+of whitespace characters,
+including line breaks;
+it is particularly useful to break and indent a long literal string
+into multiple lines without adding the newlines and spaces
+into the string contents.
+A short literal string cannot contain unescaped line breaks
+nor escapes not forming a valid escape sequence.
+
+
+<p>
+We can specify any byte in a short literal string,
+including embedded zeros,
+by its numeric value.
+This can be done
+with the escape sequence <code>\x<em>XX</em></code>,
+where <em>XX</em> is a sequence of exactly two hexadecimal digits,
+or with the escape sequence <code>\<em>ddd</em></code>,
+where <em>ddd</em> is a sequence of up to three decimal digits.
+(Note that if a decimal escape sequence is to be followed by a digit,
+it must be expressed using exactly three digits.)
+
+
+<p>
+The UTF-8 encoding of a Unicode character
+can be inserted in a literal string with
+the escape sequence <code>\u{<em>XXX</em>}</code>
+(with mandatory enclosing braces),
+where <em>XXX</em> is a sequence of one or more hexadecimal digits
+representing the character code point.
+This code point can be any value less than <em>2<sup>31</sup></em>.
+(Lua uses the original UTF-8 specification here,
+which is not restricted to valid Unicode code points.)
+
+
+<p>
+Literal strings can also be defined using a long format
+enclosed by <em>long brackets</em>.
+We define an <em>opening long bracket of level <em>n</em></em> as an opening
+square bracket followed by <em>n</em> equal signs followed by another
+opening square bracket.
+So, an opening long bracket of level&nbsp;0 is written as <code>[[</code>,
+an opening long bracket of level&nbsp;1 is written as <code>[=[</code>,
+and so on.
+A <em>closing long bracket</em> is defined similarly;
+for instance,
+a closing long bracket of level&nbsp;4 is written as <code>]====]</code>.
+A <em>long literal</em> starts with an opening long bracket of any level and
+ends at the first closing long bracket of the same level.
+It can contain any text except a closing bracket of the same level.
+Literals in this bracketed form can run for several lines,
+do not interpret any escape sequences,
+and ignore long brackets of any other level.
+Any kind of end-of-line sequence
+(carriage return, newline, carriage return followed by newline,
+or newline followed by carriage return)
+is converted to a simple newline.
+When the opening long bracket is immediately followed by a newline,
+the newline is not included in the string.
+
+
+<p>
+As an example, in a system using ASCII
+(in which '<code>a</code>' is coded as&nbsp;97,
+newline is coded as&nbsp;10, and '<code>1</code>' is coded as&nbsp;49),
+the five literal strings below denote the same string:
+
+<pre>
+ a = 'alo\n123"'
+ a = "alo\n123\""
+ a = '\97lo\10\04923"'
+ a = [[alo
+ 123"]]
+ a = [==[
+ alo
+ 123"]==]
+</pre>
+
+<p>
+Any byte in a literal string not
+explicitly affected by the previous rules represents itself.
+However, Lua opens files for parsing in text mode,
+and the system's file functions may have problems with
+some control characters.
+So, it is safer to represent
+binary data as a quoted literal with
+explicit escape sequences for the non-text characters.
+
+
+<p>
+A <em>numeric constant</em> (or <em>numeral</em>)
+can be written with an optional fractional part
+and an optional decimal exponent,
+marked by a letter '<code>e</code>' or '<code>E</code>'.
+Lua also accepts hexadecimal constants,
+which start with <code>0x</code> or <code>0X</code>.
+Hexadecimal constants also accept an optional fractional part
+plus an optional binary exponent,
+marked by a letter '<code>p</code>' or '<code>P</code>' and written in decimal.
+(For instance, <code>0x1.fp10</code> denotes 1984,
+which is <em>0x1f / 16</em> multiplied by <em>2<sup>10</sup></em>.)
+
+
+<p>
+A numeric constant with a radix point or an exponent
+denotes a float;
+otherwise,
+if its value fits in an integer or it is a hexadecimal constant,
+it denotes an integer;
+otherwise (that is, a decimal integer numeral that overflows),
+it denotes a float.
+Hexadecimal numerals with neither a radix point nor an exponent
+always denote an integer value;
+if the value overflows, it <em>wraps around</em>
+to fit into a valid integer.
+
+
+<p>
+Examples of valid integer constants are
+
+<pre>
+ 3 345 0xff 0xBEBADA
+</pre><p>
+Examples of valid float constants are
+
+<pre>
+ 3.0 3.1416 314.16e-2 0.31416E1 34e1
+ 0x0.1E 0xA23p-4 0X1.921FB54442D18P+1
+</pre>
+
+<p>
+A <em>comment</em> starts with a double hyphen (<code>--</code>)
+anywhere outside a string.
+If the text immediately after <code>--</code> is not an opening long bracket,
+the comment is a <em>short comment</em>,
+which runs until the end of the line.
+Otherwise, it is a <em>long comment</em>,
+which runs until the corresponding closing long bracket.
+
+
+
+
+
+<h2>3.2 &ndash; <a name="3.2">Variables</a></h2>
+
+<p>
+Variables are places that store values.
+There are three kinds of variables in Lua:
+global variables, local variables, and table fields.
+
+
+<p>
+A single name can denote a global variable or a local variable
+(or a function's formal parameter,
+which is a particular kind of local variable):
+
+<pre>
+ var ::= Name
+</pre><p>
+Name denotes identifiers (see <a href="#3.1">&sect;3.1</a>).
+
+
+<p>
+Any variable name is assumed to be global unless explicitly declared
+as a local (see <a href="#3.3.7">&sect;3.3.7</a>).
+Local variables are <em>lexically scoped</em>:
+local variables can be freely accessed by functions
+defined inside their scope (see <a href="#3.5">&sect;3.5</a>).
+
+
+<p>
+Before the first assignment to a variable, its value is <b>nil</b>.
+
+
+<p>
+Square brackets are used to index a table:
+
+<pre>
+ var ::= prefixexp &lsquo;<b>[</b>&rsquo; exp &lsquo;<b>]</b>&rsquo;
+</pre><p>
+The meaning of accesses to table fields can be changed via metatables
+(see <a href="#2.4">&sect;2.4</a>).
+
+
+<p>
+The syntax <code>var.Name</code> is just syntactic sugar for
+<code>var["Name"]</code>:
+
+<pre>
+ var ::= prefixexp &lsquo;<b>.</b>&rsquo; Name
+</pre>
+
+<p>
+An access to a global variable <code>x</code>
+is equivalent to <code>_ENV.x</code>.
+Due to the way that chunks are compiled,
+the variable <code>_ENV</code> itself is never global (see <a href="#2.2">&sect;2.2</a>).
+
+
+
+
+
+<h2>3.3 &ndash; <a name="3.3">Statements</a></h2>
+
+
+
+<p>
+Lua supports an almost conventional set of statements,
+similar to those in other conventional languages.
+This set includes
+blocks, assignments, control structures, function calls,
+and variable declarations.
+
+
+
+
+
+<h3>3.3.1 &ndash; <a name="3.3.1">Blocks</a></h3>
+
+<p>
+A block is a list of statements,
+which are executed sequentially:
+
+<pre>
+ block ::= {stat}
+</pre><p>
+Lua has <em>empty statements</em>
+that allow you to separate statements with semicolons,
+start a block with a semicolon
+or write two semicolons in sequence:
+
+<pre>
+ stat ::= &lsquo;<b>;</b>&rsquo;
+</pre>
+
+<p>
+Both function calls and assignments
+can start with an open parenthesis.
+This possibility leads to an ambiguity in Lua's grammar.
+Consider the following fragment:
+
+<pre>
+ a = b + c
+ (print or io.write)('done')
+</pre><p>
+The grammar could see this fragment in two ways:
+
+<pre>
+ a = b + c(print or io.write)('done')
+
+ a = b + c; (print or io.write)('done')
+</pre><p>
+The current parser always sees such constructions
+in the first way,
+interpreting the open parenthesis
+as the start of the arguments to a call.
+To avoid this ambiguity,
+it is a good practice to always precede with a semicolon
+statements that start with a parenthesis:
+
+<pre>
+ ;(print or io.write)('done')
+</pre>
+
+<p>
+A block can be explicitly delimited to produce a single statement:
+
+<pre>
+ stat ::= <b>do</b> block <b>end</b>
+</pre><p>
+Explicit blocks are useful
+to control the scope of variable declarations.
+Explicit blocks are also sometimes used to
+add a <b>return</b> statement in the middle
+of another block (see <a href="#3.3.4">&sect;3.3.4</a>).
+
+
+
+
+
+<h3>3.3.2 &ndash; <a name="3.3.2">Chunks</a></h3>
+
+<p>
+The unit of compilation of Lua is called a <em>chunk</em>.
+Syntactically,
+a chunk is simply a block:
+
+<pre>
+ chunk ::= block
+</pre>
+
+<p>
+Lua handles a chunk as the body of an anonymous function
+with a variable number of arguments
+(see <a href="#3.4.11">&sect;3.4.11</a>).
+As such, chunks can define local variables,
+receive arguments, and return values.
+Moreover, such anonymous function is compiled as in the
+scope of an external local variable called <code>_ENV</code> (see <a href="#2.2">&sect;2.2</a>).
+The resulting function always has <code>_ENV</code> as its only external variable,
+even if it does not use that variable.
+
+
+<p>
+A chunk can be stored in a file or in a string inside the host program.
+To execute a chunk,
+Lua first <em>loads</em> it,
+precompiling the chunk's code into instructions for a virtual machine,
+and then Lua executes the compiled code
+with an interpreter for the virtual machine.
+
+
+<p>
+Chunks can also be precompiled into binary form;
+see the program <code>luac</code> and the function <a href="#pdf-string.dump"><code>string.dump</code></a> for details.
+Programs in source and compiled forms are interchangeable;
+Lua automatically detects the file type and acts accordingly (see <a href="#pdf-load"><code>load</code></a>).
+
+
+
+
+
+<h3>3.3.3 &ndash; <a name="3.3.3">Assignment</a></h3>
+
+<p>
+Lua allows multiple assignments.
+Therefore, the syntax for assignment
+defines a list of variables on the left side
+and a list of expressions on the right side.
+The elements in both lists are separated by commas:
+
+<pre>
+ stat ::= varlist &lsquo;<b>=</b>&rsquo; explist
+ varlist ::= var {&lsquo;<b>,</b>&rsquo; var}
+ explist ::= exp {&lsquo;<b>,</b>&rsquo; exp}
+</pre><p>
+Expressions are discussed in <a href="#3.4">&sect;3.4</a>.
+
+
+<p>
+Before the assignment,
+the list of values is <em>adjusted</em> to the length of
+the list of variables (see <a href="#3.4.12">&sect;3.4.12</a>).
+
+
+<p>
+If a variable is both assigned and read
+inside a multiple assignment,
+Lua ensures that all reads get the value of the variable
+before the assignment.
+Thus the code
+
+<pre>
+ i = 3
+ i, a[i] = i+1, 20
+</pre><p>
+sets <code>a[3]</code> to 20, without affecting <code>a[4]</code>
+because the <code>i</code> in <code>a[i]</code> is evaluated (to 3)
+before it is assigned&nbsp;4.
+Similarly, the line
+
+<pre>
+ x, y = y, x
+</pre><p>
+exchanges the values of <code>x</code> and <code>y</code>,
+and
+
+<pre>
+ x, y, z = y, z, x
+</pre><p>
+cyclically permutes the values of <code>x</code>, <code>y</code>, and <code>z</code>.
+
+
+<p>
+Note that this guarantee covers only accesses
+syntactically inside the assignment statement.
+If a function or a metamethod called during the assignment
+changes the value of a variable,
+Lua gives no guarantees about the order of that access.
+
+
+<p>
+An assignment to a global name <code>x = val</code>
+is equivalent to the assignment
+<code>_ENV.x = val</code> (see <a href="#2.2">&sect;2.2</a>).
+
+
+<p>
+The meaning of assignments to table fields and
+global variables (which are actually table fields, too)
+can be changed via metatables (see <a href="#2.4">&sect;2.4</a>).
+
+
+
+
+
+<h3>3.3.4 &ndash; <a name="3.3.4">Control Structures</a></h3><p>
+The control structures
+<b>if</b>, <b>while</b>, and <b>repeat</b> have the usual meaning and
+familiar syntax:
+
+
+
+
+<pre>
+ stat ::= <b>while</b> exp <b>do</b> block <b>end</b>
+ stat ::= <b>repeat</b> block <b>until</b> exp
+ stat ::= <b>if</b> exp <b>then</b> block {<b>elseif</b> exp <b>then</b> block} [<b>else</b> block] <b>end</b>
+</pre><p>
+Lua also has a <b>for</b> statement, in two flavors (see <a href="#3.3.5">&sect;3.3.5</a>).
+
+
+<p>
+The condition expression of a
+control structure can return any value.
+Both <b>false</b> and <b>nil</b> test false.
+All values different from <b>nil</b> and <b>false</b> test true.
+In particular, the number 0 and the empty string also test true.
+
+
+<p>
+In the <b>repeat</b>&ndash;<b>until</b> loop,
+the inner block does not end at the <b>until</b> keyword,
+but only after the condition.
+So, the condition can refer to local variables
+declared inside the loop block.
+
+
+<p>
+The <b>goto</b> statement transfers the program control to a label.
+For syntactical reasons,
+labels in Lua are considered statements too:
+
+
+
+<pre>
+ stat ::= <b>goto</b> Name
+ stat ::= label
+ label ::= &lsquo;<b>::</b>&rsquo; Name &lsquo;<b>::</b>&rsquo;
+</pre>
+
+<p>
+A label is visible in the entire block where it is defined,
+except inside nested functions.
+A goto may jump to any visible label as long as it does not
+enter into the scope of a local variable.
+A label should not be declared
+where a label with the same name is visible,
+even if this other label has been declared in an enclosing block.
+
+
+<p>
+The <b>break</b> statement terminates the execution of a
+<b>while</b>, <b>repeat</b>, or <b>for</b> loop,
+skipping to the next statement after the loop:
+
+
+<pre>
+ stat ::= <b>break</b>
+</pre><p>
+A <b>break</b> ends the innermost enclosing loop.
+
+
+<p>
+The <b>return</b> statement is used to return values
+from a function or a chunk
+(which is handled as an anonymous function).
+
+Functions can return more than one value,
+so the syntax for the <b>return</b> statement is
+
+<pre>
+ stat ::= <b>return</b> [explist] [&lsquo;<b>;</b>&rsquo;]
+</pre>
+
+<p>
+The <b>return</b> statement can only be written
+as the last statement of a block.
+If it is necessary to <b>return</b> in the middle of a block,
+then an explicit inner block can be used,
+as in the idiom <code>do return end</code>,
+because now <b>return</b> is the last statement in its (inner) block.
+
+
+
+
+
+<h3>3.3.5 &ndash; <a name="3.3.5">For Statement</a></h3>
+
+<p>
+
+The <b>for</b> statement has two forms:
+one numerical and one generic.
+
+
+
+<h4>The numerical <b>for</b> loop</h4>
+
+<p>
+The numerical <b>for</b> loop repeats a block of code while a
+control variable goes through an arithmetic progression.
+It has the following syntax:
+
+<pre>
+ stat ::= <b>for</b> Name &lsquo;<b>=</b>&rsquo; exp &lsquo;<b>,</b>&rsquo; exp [&lsquo;<b>,</b>&rsquo; exp] <b>do</b> block <b>end</b>
+</pre><p>
+The given identifier (Name) defines the control variable,
+which is a new variable local to the loop body (<em>block</em>).
+
+
+<p>
+The loop starts by evaluating once the three control expressions.
+Their values are called respectively
+the <em>initial value</em>, the <em>limit</em>, and the <em>step</em>.
+If the step is absent, it defaults to&nbsp;1.
+
+
+<p>
+If both the initial value and the step are integers,
+the loop is done with integers;
+note that the limit may not be an integer.
+Otherwise, the three values are converted to
+floats and the loop is done with floats.
+Beware of floating-point accuracy in this case.
+
+
+<p>
+After that initialization,
+the loop body is repeated with the value of the control variable
+going through an arithmetic progression,
+starting at the initial value,
+with a common difference given by the step.
+A negative step makes a decreasing sequence;
+a step equal to zero raises an error.
+The loop continues while the value is less than
+or equal to the limit
+(greater than or equal to for a negative step).
+If the initial value is already greater than the limit
+(or less than, if the step is negative),
+the body is not executed.
+
+
+<p>
+For integer loops,
+the control variable never wraps around;
+instead, the loop ends in case of an overflow.
+
+
+<p>
+You should not change the value of the control variable
+during the loop.
+If you need its value after the loop,
+assign it to another variable before exiting the loop.
+
+
+
+
+
+<h4>The generic <b>for</b> loop</h4>
+
+<p>
+The generic <b>for</b> statement works over functions,
+called <em>iterators</em>.
+On each iteration, the iterator function is called to produce a new value,
+stopping when this new value is <b>nil</b>.
+The generic <b>for</b> loop has the following syntax:
+
+<pre>
+ stat ::= <b>for</b> namelist <b>in</b> explist <b>do</b> block <b>end</b>
+ namelist ::= Name {&lsquo;<b>,</b>&rsquo; Name}
+</pre><p>
+A <b>for</b> statement like
+
+<pre>
+ for <em>var_1</em>, &middot;&middot;&middot;, <em>var_n</em> in <em>explist</em> do <em>body</em> end
+</pre><p>
+works as follows.
+
+
+<p>
+The names <em>var_i</em> declare loop variables local to the loop body.
+The first of these variables is the <em>control variable</em>.
+
+
+<p>
+The loop starts by evaluating <em>explist</em>
+to produce four values:
+an <em>iterator function</em>,
+a <em>state</em>,
+an initial value for the control variable,
+and a <em>closing value</em>.
+
+
+<p>
+Then, at each iteration,
+Lua calls the iterator function with two arguments:
+the state and the control variable.
+The results from this call are then assigned to the loop variables,
+following the rules of multiple assignments (see <a href="#3.3.3">&sect;3.3.3</a>).
+If the control variable becomes <b>nil</b>,
+the loop terminates.
+Otherwise, the body is executed and the loop goes
+to the next iteration.
+
+
+<p>
+The closing value behaves like a
+to-be-closed variable (see <a href="#3.3.8">&sect;3.3.8</a>),
+which can be used to release resources when the loop ends.
+Otherwise, it does not interfere with the loop.
+
+
+<p>
+You should not change the value of the control variable
+during the loop.
+
+
+
+
+
+
+
+<h3>3.3.6 &ndash; <a name="3.3.6">Function Calls as Statements</a></h3><p>
+To allow possible side-effects,
+function calls can be executed as statements:
+
+<pre>
+ stat ::= functioncall
+</pre><p>
+In this case, all returned values are thrown away.
+Function calls are explained in <a href="#3.4.10">&sect;3.4.10</a>.
+
+
+
+
+
+<h3>3.3.7 &ndash; <a name="3.3.7">Local Declarations</a></h3><p>
+Local variables can be declared anywhere inside a block.
+The declaration can include an initialization:
+
+<pre>
+ stat ::= <b>local</b> attnamelist [&lsquo;<b>=</b>&rsquo; explist]
+ attnamelist ::= Name attrib {&lsquo;<b>,</b>&rsquo; Name attrib}
+</pre><p>
+If present, an initial assignment has the same semantics
+of a multiple assignment (see <a href="#3.3.3">&sect;3.3.3</a>).
+Otherwise, all variables are initialized with <b>nil</b>.
+
+
+<p>
+Each variable name may be postfixed by an attribute
+(a name between angle brackets):
+
+<pre>
+ attrib ::= [&lsquo;<b>&lt;</b>&rsquo; Name &lsquo;<b>&gt;</b>&rsquo;]
+</pre><p>
+There are two possible attributes:
+<code>const</code>, which declares a constant variable,
+that is, a variable that cannot be assigned to
+after its initialization;
+and <code>close</code>, which declares a to-be-closed variable (see <a href="#3.3.8">&sect;3.3.8</a>).
+A list of variables can contain at most one to-be-closed variable.
+
+
+<p>
+A chunk is also a block (see <a href="#3.3.2">&sect;3.3.2</a>),
+and so local variables can be declared in a chunk outside any explicit block.
+
+
+<p>
+The visibility rules for local variables are explained in <a href="#3.5">&sect;3.5</a>.
+
+
+
+
+
+<h3>3.3.8 &ndash; <a name="3.3.8">To-be-closed Variables</a></h3>
+
+<p>
+A to-be-closed variable behaves like a constant local variable,
+except that its value is <em>closed</em> whenever the variable
+goes out of scope, including normal block termination,
+exiting its block by <b>break</b>/<b>goto</b>/<b>return</b>,
+or exiting by an error.
+
+
+<p>
+Here, to <em>close</em> a value means
+to call its <code>__close</code> metamethod.
+When calling the metamethod,
+the value itself is passed as the first argument
+and the error object that caused the exit (if any)
+is passed as a second argument;
+if there was no error, the second argument is <b>nil</b>.
+
+
+<p>
+The value assigned to a to-be-closed variable
+must have a <code>__close</code> metamethod
+or be a false value.
+(<b>nil</b> and <b>false</b> are ignored as to-be-closed values.)
+
+
+<p>
+If several to-be-closed variables go out of scope at the same event,
+they are closed in the reverse order that they were declared.
+
+
+<p>
+If there is any error while running a closing method,
+that error is handled like an error in the regular code
+where the variable was defined.
+After an error,
+the other pending closing methods will still be called.
+
+
+<p>
+If a coroutine yields and is never resumed again,
+some variables may never go out of scope,
+and therefore they will never be closed.
+(These variables are the ones created inside the coroutine
+and in scope at the point where the coroutine yielded.)
+Similarly, if a coroutine ends with an error,
+it does not unwind its stack,
+so it does not close any variable.
+In both cases,
+you can either use finalizers
+or call <a href="#pdf-coroutine.close"><code>coroutine.close</code></a> to close the variables.
+However, if the coroutine was created
+through <a href="#pdf-coroutine.wrap"><code>coroutine.wrap</code></a>,
+then its corresponding function will close the coroutine
+in case of errors.
+
+
+
+
+
+
+
+<h2>3.4 &ndash; <a name="3.4">Expressions</a></h2>
+
+
+
+<p>
+The basic expressions in Lua are the following:
+
+<pre>
+ exp ::= prefixexp
+ exp ::= <b>nil</b> | <b>false</b> | <b>true</b>
+ exp ::= Numeral
+ exp ::= LiteralString
+ exp ::= functiondef
+ exp ::= tableconstructor
+ exp ::= &lsquo;<b>...</b>&rsquo;
+ exp ::= exp binop exp
+ exp ::= unop exp
+ prefixexp ::= var | functioncall | &lsquo;<b>(</b>&rsquo; exp &lsquo;<b>)</b>&rsquo;
+</pre>
+
+<p>
+Numerals and literal strings are explained in <a href="#3.1">&sect;3.1</a>;
+variables are explained in <a href="#3.2">&sect;3.2</a>;
+function definitions are explained in <a href="#3.4.11">&sect;3.4.11</a>;
+function calls are explained in <a href="#3.4.10">&sect;3.4.10</a>;
+table constructors are explained in <a href="#3.4.9">&sect;3.4.9</a>.
+Vararg expressions,
+denoted by three dots ('<code>...</code>'), can only be used when
+directly inside a variadic function;
+they are explained in <a href="#3.4.11">&sect;3.4.11</a>.
+
+
+<p>
+Binary operators comprise arithmetic operators (see <a href="#3.4.1">&sect;3.4.1</a>),
+bitwise operators (see <a href="#3.4.2">&sect;3.4.2</a>),
+relational operators (see <a href="#3.4.4">&sect;3.4.4</a>), logical operators (see <a href="#3.4.5">&sect;3.4.5</a>),
+and the concatenation operator (see <a href="#3.4.6">&sect;3.4.6</a>).
+Unary operators comprise the unary minus (see <a href="#3.4.1">&sect;3.4.1</a>),
+the unary bitwise NOT (see <a href="#3.4.2">&sect;3.4.2</a>),
+the unary logical <b>not</b> (see <a href="#3.4.5">&sect;3.4.5</a>),
+and the unary <em>length operator</em> (see <a href="#3.4.7">&sect;3.4.7</a>).
+
+
+
+
+
+<h3>3.4.1 &ndash; <a name="3.4.1">Arithmetic Operators</a></h3><p>
+Lua supports the following arithmetic operators:
+
+<ul>
+<li><b><code>+</code>: </b>addition</li>
+<li><b><code>-</code>: </b>subtraction</li>
+<li><b><code>*</code>: </b>multiplication</li>
+<li><b><code>/</code>: </b>float division</li>
+<li><b><code>//</code>: </b>floor division</li>
+<li><b><code>%</code>: </b>modulo</li>
+<li><b><code>^</code>: </b>exponentiation</li>
+<li><b><code>-</code>: </b>unary minus</li>
+</ul>
+
+<p>
+With the exception of exponentiation and float division,
+the arithmetic operators work as follows:
+If both operands are integers,
+the operation is performed over integers and the result is an integer.
+Otherwise, if both operands are numbers,
+then they are converted to floats,
+the operation is performed following the machine's rules
+for floating-point arithmetic
+(usually the IEEE 754 standard),
+and the result is a float.
+(The string library coerces strings to numbers in
+arithmetic operations; see <a href="#3.4.3">&sect;3.4.3</a> for details.)
+
+
+<p>
+Exponentiation and float division (<code>/</code>)
+always convert their operands to floats
+and the result is always a float.
+Exponentiation uses the ISO&nbsp;C function <code>pow</code>,
+so that it works for non-integer exponents too.
+
+
+<p>
+Floor division (<code>//</code>) is a division
+that rounds the quotient towards minus infinity,
+resulting in the floor of the division of its operands.
+
+
+<p>
+Modulo is defined as the remainder of a division
+that rounds the quotient towards minus infinity (floor division).
+
+
+<p>
+In case of overflows in integer arithmetic,
+all operations <em>wrap around</em>.
+
+
+
+<h3>3.4.2 &ndash; <a name="3.4.2">Bitwise Operators</a></h3><p>
+Lua supports the following bitwise operators:
+
+<ul>
+<li><b><code>&amp;</code>: </b>bitwise AND</li>
+<li><b><code>&#124;</code>: </b>bitwise OR</li>
+<li><b><code>~</code>: </b>bitwise exclusive OR</li>
+<li><b><code>&gt;&gt;</code>: </b>right shift</li>
+<li><b><code>&lt;&lt;</code>: </b>left shift</li>
+<li><b><code>~</code>: </b>unary bitwise NOT</li>
+</ul>
+
+<p>
+All bitwise operations convert its operands to integers
+(see <a href="#3.4.3">&sect;3.4.3</a>),
+operate on all bits of those integers,
+and result in an integer.
+
+
+<p>
+Both right and left shifts fill the vacant bits with zeros.
+Negative displacements shift to the other direction;
+displacements with absolute values equal to or higher than
+the number of bits in an integer
+result in zero (as all bits are shifted out).
+
+
+
+
+
+<h3>3.4.3 &ndash; <a name="3.4.3">Coercions and Conversions</a></h3><p>
+Lua provides some automatic conversions between some
+types and representations at run time.
+Bitwise operators always convert float operands to integers.
+Exponentiation and float division
+always convert integer operands to floats.
+All other arithmetic operations applied to mixed numbers
+(integers and floats) convert the integer operand to a float.
+The C API also converts both integers to floats and
+floats to integers, as needed.
+Moreover, string concatenation accepts numbers as arguments,
+besides strings.
+
+
+<p>
+In a conversion from integer to float,
+if the integer value has an exact representation as a float,
+that is the result.
+Otherwise,
+the conversion gets the nearest higher or
+the nearest lower representable value.
+This kind of conversion never fails.
+
+
+<p>
+The conversion from float to integer
+checks whether the float has an exact representation as an integer
+(that is, the float has an integral value and
+it is in the range of integer representation).
+If it does, that representation is the result.
+Otherwise, the conversion fails.
+
+
+<p>
+Several places in Lua coerce strings to numbers when necessary.
+In particular,
+the string library sets metamethods that try to coerce
+strings to numbers in all arithmetic operations.
+If the conversion fails,
+the library calls the metamethod of the other operand
+(if present) or it raises an error.
+Note that bitwise operators do not do this coercion.
+
+
+<p>
+It is always a good practice not to rely on the
+implicit coercions from strings to numbers,
+as they are not always applied;
+in particular, <code>"1"==1</code> is false and <code>"1"&lt;1</code> raises an error
+(see <a href="#3.4.4">&sect;3.4.4</a>).
+These coercions exist mainly for compatibility and may be removed
+in future versions of the language.
+
+
+<p>
+A string is converted to an integer or a float
+following its syntax and the rules of the Lua lexer.
+The string may have also leading and trailing whitespaces and a sign.
+All conversions from strings to numbers
+accept both a dot and the current locale mark
+as the radix character.
+(The Lua lexer, however, accepts only a dot.)
+If the string is not a valid numeral,
+the conversion fails.
+If necessary, the result of this first step is then converted
+to a specific number subtype following the previous rules
+for conversions between floats and integers.
+
+
+<p>
+The conversion from numbers to strings uses a
+non-specified human-readable format.
+To convert numbers to strings in any specific way,
+use the function <a href="#pdf-string.format"><code>string.format</code></a>.
+
+
+
+
+
+<h3>3.4.4 &ndash; <a name="3.4.4">Relational Operators</a></h3><p>
+Lua supports the following relational operators:
+
+<ul>
+<li><b><code>==</code>: </b>equality</li>
+<li><b><code>~=</code>: </b>inequality</li>
+<li><b><code>&lt;</code>: </b>less than</li>
+<li><b><code>&gt;</code>: </b>greater than</li>
+<li><b><code>&lt;=</code>: </b>less or equal</li>
+<li><b><code>&gt;=</code>: </b>greater or equal</li>
+</ul><p>
+These operators always result in <b>false</b> or <b>true</b>.
+
+
+<p>
+Equality (<code>==</code>) first compares the type of its operands.
+If the types are different, then the result is <b>false</b>.
+Otherwise, the values of the operands are compared.
+Strings are equal if they have the same byte content.
+Numbers are equal if they denote the same mathematical value.
+
+
+<p>
+Tables, userdata, and threads
+are compared by reference:
+two objects are considered equal only if they are the same object.
+Every time you create a new object
+(a table, a userdata, or a thread),
+this new object is different from any previously existing object.
+A function is always equal to itself.
+Functions with any detectable difference
+(different behavior, different definition) are always different.
+Functions created at different times but with no detectable differences
+may be classified as equal or not
+(depending on internal caching details).
+
+
+<p>
+You can change the way that Lua compares tables and userdata
+by using the <code>__eq</code> metamethod (see <a href="#2.4">&sect;2.4</a>).
+
+
+<p>
+Equality comparisons do not convert strings to numbers
+or vice versa.
+Thus, <code>"0"==0</code> evaluates to <b>false</b>,
+and <code>t[0]</code> and <code>t["0"]</code> denote different
+entries in a table.
+
+
+<p>
+The operator <code>~=</code> is exactly the negation of equality (<code>==</code>).
+
+
+<p>
+The order operators work as follows.
+If both arguments are numbers,
+then they are compared according to their mathematical values,
+regardless of their subtypes.
+Otherwise, if both arguments are strings,
+then their values are compared according to the current locale.
+Otherwise, Lua tries to call the <code>__lt</code> or the <code>__le</code>
+metamethod (see <a href="#2.4">&sect;2.4</a>).
+A comparison <code>a &gt; b</code> is translated to <code>b &lt; a</code>
+and <code>a &gt;= b</code> is translated to <code>b &lt;= a</code>.
+
+
+<p>
+Following the IEEE 754 standard,
+the special value NaN is considered neither less than,
+nor equal to, nor greater than any value, including itself.
+
+
+
+
+
+<h3>3.4.5 &ndash; <a name="3.4.5">Logical Operators</a></h3><p>
+The logical operators in Lua are
+<b>and</b>, <b>or</b>, and <b>not</b>.
+Like the control structures (see <a href="#3.3.4">&sect;3.3.4</a>),
+all logical operators consider both <b>false</b> and <b>nil</b> as false
+and anything else as true.
+
+
+<p>
+The negation operator <b>not</b> always returns <b>false</b> or <b>true</b>.
+The conjunction operator <b>and</b> returns its first argument
+if this value is <b>false</b> or <b>nil</b>;
+otherwise, <b>and</b> returns its second argument.
+The disjunction operator <b>or</b> returns its first argument
+if this value is different from <b>nil</b> and <b>false</b>;
+otherwise, <b>or</b> returns its second argument.
+Both <b>and</b> and <b>or</b> use short-circuit evaluation;
+that is,
+the second operand is evaluated only if necessary.
+Here are some examples:
+
+<pre>
+ 10 or 20 --&gt; 10
+ 10 or error() --&gt; 10
+ nil or "a" --&gt; "a"
+ nil and 10 --&gt; nil
+ false and error() --&gt; false
+ false and nil --&gt; false
+ false or nil --&gt; nil
+ 10 and 20 --&gt; 20
+</pre>
+
+
+
+
+<h3>3.4.6 &ndash; <a name="3.4.6">Concatenation</a></h3><p>
+The string concatenation operator in Lua is
+denoted by two dots ('<code>..</code>').
+If both operands are strings or numbers,
+then the numbers are converted to strings
+in a non-specified format (see <a href="#3.4.3">&sect;3.4.3</a>).
+Otherwise, the <code>__concat</code> metamethod is called (see <a href="#2.4">&sect;2.4</a>).
+
+
+
+
+
+<h3>3.4.7 &ndash; <a name="3.4.7">The Length Operator</a></h3>
+
+<p>
+The length operator is denoted by the unary prefix operator <code>#</code>.
+
+
+<p>
+The length of a string is its number of bytes.
+(That is the usual meaning of string length when each
+character is one byte.)
+
+
+<p>
+The length operator applied on a table
+returns a border in that table.
+A <em>border</em> in a table <code>t</code> is any non-negative integer
+that satisfies the following condition:
+
+<pre>
+ (border == 0 or t[border] ~= nil) and
+ (t[border + 1] == nil or border == math.maxinteger)
+</pre><p>
+In words,
+a border is any positive integer index present in the table
+that is followed by an absent index,
+plus two limit cases:
+zero, when index 1 is absent;
+and the maximum value for an integer, when that index is present.
+Note that keys that are not positive integers
+do not interfere with borders.
+
+
+<p>
+A table with exactly one border is called a <em>sequence</em>.
+For instance, the table <code>{10, 20, 30, 40, 50}</code> is a sequence,
+as it has only one border (5).
+The table <code>{10, 20, 30, nil, 50}</code> has two borders (3 and 5),
+and therefore it is not a sequence.
+(The <b>nil</b> at index 4 is called a <em>hole</em>.)
+The table <code>{nil, 20, 30, nil, nil, 60, nil}</code>
+has three borders (0, 3, and 6),
+so it is not a sequence, too.
+The table <code>{}</code> is a sequence with border 0.
+
+
+<p>
+When <code>t</code> is a sequence,
+<code>#t</code> returns its only border,
+which corresponds to the intuitive notion of the length of the sequence.
+When <code>t</code> is not a sequence,
+<code>#t</code> can return any of its borders.
+(The exact one depends on details of
+the internal representation of the table,
+which in turn can depend on how the table was populated and
+the memory addresses of its non-numeric keys.)
+
+
+<p>
+The computation of the length of a table
+has a guaranteed worst time of <em>O(log n)</em>,
+where <em>n</em> is the largest integer key in the table.
+
+
+<p>
+A program can modify the behavior of the length operator for
+any value but strings through the <code>__len</code> metamethod (see <a href="#2.4">&sect;2.4</a>).
+
+
+
+
+
+<h3>3.4.8 &ndash; <a name="3.4.8">Precedence</a></h3><p>
+Operator precedence in Lua follows the table below,
+from lower to higher priority:
+
+<pre>
+ or
+ and
+ &lt; &gt; &lt;= &gt;= ~= ==
+ |
+ ~
+ &amp;
+ &lt;&lt; &gt;&gt;
+ ..
+ + -
+ * / // %
+ unary operators (not # - ~)
+ ^
+</pre><p>
+As usual,
+you can use parentheses to change the precedences of an expression.
+The concatenation ('<code>..</code>') and exponentiation ('<code>^</code>')
+operators are right associative.
+All other binary operators are left associative.
+
+
+
+
+
+<h3>3.4.9 &ndash; <a name="3.4.9">Table Constructors</a></h3><p>
+Table constructors are expressions that create tables.
+Every time a constructor is evaluated, a new table is created.
+A constructor can be used to create an empty table
+or to create a table and initialize some of its fields.
+The general syntax for constructors is
+
+<pre>
+ tableconstructor ::= &lsquo;<b>{</b>&rsquo; [fieldlist] &lsquo;<b>}</b>&rsquo;
+ fieldlist ::= field {fieldsep field} [fieldsep]
+ field ::= &lsquo;<b>[</b>&rsquo; exp &lsquo;<b>]</b>&rsquo; &lsquo;<b>=</b>&rsquo; exp | Name &lsquo;<b>=</b>&rsquo; exp | exp
+ fieldsep ::= &lsquo;<b>,</b>&rsquo; | &lsquo;<b>;</b>&rsquo;
+</pre>
+
+<p>
+Each field of the form <code>[exp1] = exp2</code> adds to the new table an entry
+with key <code>exp1</code> and value <code>exp2</code>.
+A field of the form <code>name = exp</code> is equivalent to
+<code>["name"] = exp</code>.
+Fields of the form <code>exp</code> are equivalent to
+<code>[i] = exp</code>, where <code>i</code> are consecutive integers
+starting with 1;
+fields in the other formats do not affect this counting.
+For example,
+
+<pre>
+ a = { [f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45 }
+</pre><p>
+is equivalent to
+
+<pre>
+ do
+ local t = {}
+ t[f(1)] = g
+ t[1] = "x" -- 1st exp
+ t[2] = "y" -- 2nd exp
+ t.x = 1 -- t["x"] = 1
+ t[3] = f(x) -- 3rd exp
+ t[30] = 23
+ t[4] = 45 -- 4th exp
+ a = t
+ end
+</pre>
+
+<p>
+The order of the assignments in a constructor is undefined.
+(This order would be relevant only when there are repeated keys.)
+
+
+<p>
+If the last field in the list has the form <code>exp</code>
+and the expression is a multires expression,
+then all values returned by this expression enter the list consecutively
+(see <a href="#3.4.12">&sect;3.4.12</a>).
+
+
+<p>
+The field list can have an optional trailing separator,
+as a convenience for machine-generated code.
+
+
+
+
+
+<h3>3.4.10 &ndash; <a name="3.4.10">Function Calls</a></h3><p>
+A function call in Lua has the following syntax:
+
+<pre>
+ functioncall ::= prefixexp args
+</pre><p>
+In a function call,
+first prefixexp and args are evaluated.
+If the value of prefixexp has type <em>function</em>,
+then this function is called
+with the given arguments.
+Otherwise, if present,
+the prefixexp <code>__call</code> metamethod is called:
+its first argument is the value of prefixexp,
+followed by the original call arguments
+(see <a href="#2.4">&sect;2.4</a>).
+
+
+<p>
+The form
+
+<pre>
+ functioncall ::= prefixexp &lsquo;<b>:</b>&rsquo; Name args
+</pre><p>
+can be used to emulate methods.
+A call <code>v:name(<em>args</em>)</code>
+is syntactic sugar for <code>v.name(v,<em>args</em>)</code>,
+except that <code>v</code> is evaluated only once.
+
+
+<p>
+Arguments have the following syntax:
+
+<pre>
+ args ::= &lsquo;<b>(</b>&rsquo; [explist] &lsquo;<b>)</b>&rsquo;
+ args ::= tableconstructor
+ args ::= LiteralString
+</pre><p>
+All argument expressions are evaluated before the call.
+A call of the form <code>f{<em>fields</em>}</code> is
+syntactic sugar for <code>f({<em>fields</em>})</code>;
+that is, the argument list is a single new table.
+A call of the form <code>f'<em>string</em>'</code>
+(or <code>f"<em>string</em>"</code> or <code>f[[<em>string</em>]]</code>)
+is syntactic sugar for <code>f('<em>string</em>')</code>;
+that is, the argument list is a single literal string.
+
+
+<p>
+A call of the form <code>return <em>functioncall</em></code> not in the
+scope of a to-be-closed variable is called a <em>tail call</em>.
+Lua implements <em>proper tail calls</em>
+(or <em>proper tail recursion</em>):
+In a tail call,
+the called function reuses the stack entry of the calling function.
+Therefore, there is no limit on the number of nested tail calls that
+a program can execute.
+However, a tail call erases any debug information about the
+calling function.
+Note that a tail call only happens with a particular syntax,
+where the <b>return</b> has one single function call as argument,
+and it is outside the scope of any to-be-closed variable.
+This syntax makes the calling function return exactly
+the returns of the called function,
+without any intervening action.
+So, none of the following examples are tail calls:
+
+<pre>
+ return (f(x)) -- results adjusted to 1
+ return 2 * f(x) -- result multiplied by 2
+ return x, f(x) -- additional results
+ f(x); return -- results discarded
+ return x or f(x) -- results adjusted to 1
+</pre>
+
+
+
+
+<h3>3.4.11 &ndash; <a name="3.4.11">Function Definitions</a></h3>
+
+<p>
+The syntax for function definition is
+
+<pre>
+ functiondef ::= <b>function</b> funcbody
+ funcbody ::= &lsquo;<b>(</b>&rsquo; [parlist] &lsquo;<b>)</b>&rsquo; block <b>end</b>
+</pre>
+
+<p>
+The following syntactic sugar simplifies function definitions:
+
+<pre>
+ stat ::= <b>function</b> funcname funcbody
+ stat ::= <b>local</b> <b>function</b> Name funcbody
+ funcname ::= Name {&lsquo;<b>.</b>&rsquo; Name} [&lsquo;<b>:</b>&rsquo; Name]
+</pre><p>
+The statement
+
+<pre>
+ function f () <em>body</em> end
+</pre><p>
+translates to
+
+<pre>
+ f = function () <em>body</em> end
+</pre><p>
+The statement
+
+<pre>
+ function t.a.b.c.f () <em>body</em> end
+</pre><p>
+translates to
+
+<pre>
+ t.a.b.c.f = function () <em>body</em> end
+</pre><p>
+The statement
+
+<pre>
+ local function f () <em>body</em> end
+</pre><p>
+translates to
+
+<pre>
+ local f; f = function () <em>body</em> end
+</pre><p>
+not to
+
+<pre>
+ local f = function () <em>body</em> end
+</pre><p>
+(This only makes a difference when the body of the function
+contains references to <code>f</code>.)
+
+
+<p>
+A function definition is an executable expression,
+whose value has type <em>function</em>.
+When Lua precompiles a chunk,
+all its function bodies are precompiled too,
+but they are not created yet.
+Then, whenever Lua executes the function definition,
+the function is <em>instantiated</em> (or <em>closed</em>).
+This function instance, or <em>closure</em>,
+is the final value of the expression.
+
+
+<p>
+Parameters act as local variables that are
+initialized with the argument values:
+
+<pre>
+ parlist ::= namelist [&lsquo;<b>,</b>&rsquo; &lsquo;<b>...</b>&rsquo;] | &lsquo;<b>...</b>&rsquo;
+</pre><p>
+When a Lua function is called,
+it adjusts its list of arguments to
+the length of its list of parameters (see <a href="#3.4.12">&sect;3.4.12</a>),
+unless the function is a <em>variadic function</em>,
+which is indicated by three dots ('<code>...</code>')
+at the end of its parameter list.
+A variadic function does not adjust its argument list;
+instead, it collects all extra arguments and supplies them
+to the function through a <em>vararg expression</em>,
+which is also written as three dots.
+The value of this expression is a list of all actual extra arguments,
+similar to a function with multiple results (see <a href="#3.4.12">&sect;3.4.12</a>).
+
+
+<p>
+As an example, consider the following definitions:
+
+<pre>
+ function f(a, b) end
+ function g(a, b, ...) end
+ function r() return 1,2,3 end
+</pre><p>
+Then, we have the following mapping from arguments to parameters and
+to the vararg expression:
+
+<pre>
+ CALL PARAMETERS
+
+ f(3) a=3, b=nil
+ f(3, 4) a=3, b=4
+ f(3, 4, 5) a=3, b=4
+ f(r(), 10) a=1, b=10
+ f(r()) a=1, b=2
+
+ g(3) a=3, b=nil, ... --&gt; (nothing)
+ g(3, 4) a=3, b=4, ... --&gt; (nothing)
+ g(3, 4, 5, 8) a=3, b=4, ... --&gt; 5 8
+ g(5, r()) a=5, b=1, ... --&gt; 2 3
+</pre>
+
+<p>
+Results are returned using the <b>return</b> statement (see <a href="#3.3.4">&sect;3.3.4</a>).
+If control reaches the end of a function
+without encountering a <b>return</b> statement,
+then the function returns with no results.
+
+
+<p>
+
+There is a system-dependent limit on the number of values
+that a function may return.
+This limit is guaranteed to be greater than 1000.
+
+
+<p>
+The <em>colon</em> syntax
+is used to emulate <em>methods</em>,
+adding an implicit extra parameter <code>self</code> to the function.
+Thus, the statement
+
+<pre>
+ function t.a.b.c:f (<em>params</em>) <em>body</em> end
+</pre><p>
+is syntactic sugar for
+
+<pre>
+ t.a.b.c.f = function (self, <em>params</em>) <em>body</em> end
+</pre>
+
+
+
+
+<h3>3.4.12 &ndash; <a name="3.4.12">Lists of expressions, multiple results,
+and adjustment</a></h3>
+
+<p>
+Both function calls and vararg expressions can result in multiple values.
+These expressions are called <em>multires expressions</em>.
+
+
+<p>
+When a multires expression is used as the last element
+of a list of expressions,
+all results from the expression are added to the
+list of values produced by the list of expressions.
+Note that a single expression
+in a place that expects a list of expressions
+is the last expression in that (singleton) list.
+
+
+<p>
+These are the places where Lua expects a list of expressions:
+
+<ul>
+
+<li>A <b>return</b> statement,
+for instance <code>return e1, e2, e3</code> (see <a href="#3.3.4">&sect;3.3.4</a>).</li>
+
+<li>A table constructor,
+for instance <code>{e1, e2, e3}</code> (see <a href="#3.4.9">&sect;3.4.9</a>).</li>
+
+<li>The arguments of a function call,
+for instance <code>foo(e1, e2, e3)</code> (see <a href="#3.4.10">&sect;3.4.10</a>).</li>
+
+<li>A multiple assignment,
+for instance <code>a , b, c = e1, e2, e3</code> (see <a href="#3.3.3">&sect;3.3.3</a>).</li>
+
+<li>A local declaration,
+for instance <code>local a , b, c = e1, e2, e3</code> (see <a href="#3.3.7">&sect;3.3.7</a>).</li>
+
+<li>The initial values in a generic <b>for</b> loop,
+for instance <code>for k in e1, e2, e3 do ... end</code> (see <a href="#3.3.5">&sect;3.3.5</a>).</li>
+
+</ul><p>
+In the last four cases,
+the list of values from the list of expressions
+must be <em>adjusted</em> to a specific length:
+the number of parameters in a call to a non-variadic function
+(see <a href="#3.4.11">&sect;3.4.11</a>),
+the number of variables in a multiple assignment or
+a local declaration,
+and exactly four values for a generic <b>for</b> loop.
+The <em>adjustment</em> follows these rules:
+If there are more values than needed,
+the extra values are thrown away;
+if there are fewer values than needed,
+the list is extended with <b>nil</b>'s.
+When the list of expressions ends with a multires expression,
+all results from that expression enter the list of values
+before the adjustment.
+
+
+<p>
+When a multires expression is used
+in a list of expressions without being the last element,
+or in a place where the syntax expects a single expression,
+Lua adjusts the result list of that expression to one element.
+As a particular case,
+the syntax expects a single expression inside a parenthesized expression;
+therefore, adding parentheses around a multires expression
+forces it to produce exactly one result.
+
+
+<p>
+We seldom need to use a vararg expression in a place
+where the syntax expects a single expression.
+(Usually it is simpler to add a regular parameter before
+the variadic part and use that parameter.)
+When there is such a need,
+we recommend assigning the vararg expression
+to a single variable and using that variable
+in its place.
+
+
+<p>
+Here are some examples of uses of mutlres expressions.
+In all cases, when the construction needs
+"the n-th result" and there is no such result,
+it uses a <b>nil</b>.
+
+<pre>
+ print(x, f()) -- prints x and all results from f().
+ print(x, (f())) -- prints x and the first result from f().
+ print(f(), x) -- prints the first result from f() and x.
+ print(1 + f()) -- prints 1 added to the first result from f().
+ local x = ... -- x gets the first vararg argument.
+ x,y = ... -- x gets the first vararg argument,
+ -- y gets the second vararg argument.
+ x,y,z = w, f() -- x gets w, y gets the first result from f(),
+ -- z gets the second result from f().
+ x,y,z = f() -- x gets the first result from f(),
+ -- y gets the second result from f(),
+ -- z gets the third result from f().
+ x,y,z = f(), g() -- x gets the first result from f(),
+ -- y gets the first result from g(),
+ -- z gets the second result from g().
+ x,y,z = (f()) -- x gets the first result from f(), y and z get nil.
+ return f() -- returns all results from f().
+ return x, ... -- returns x and all received vararg arguments.
+ return x,y,f() -- returns x, y, and all results from f().
+ {f()} -- creates a list with all results from f().
+ {...} -- creates a list with all vararg arguments.
+ {f(), 5} -- creates a list with the first result from f() and 5.
+</pre>
+
+
+
+
+
+
+<h2>3.5 &ndash; <a name="3.5">Visibility Rules</a></h2>
+
+<p>
+
+Lua is a lexically scoped language.
+The scope of a local variable begins at the first statement after
+its declaration and lasts until the last non-void statement
+of the innermost block that includes the declaration.
+(<em>Void statements</em> are labels and empty statements.)
+Consider the following example:
+
+<pre>
+ x = 10 -- global variable
+ do -- new block
+ local x = x -- new 'x', with value 10
+ print(x) --&gt; 10
+ x = x+1
+ do -- another block
+ local x = x+1 -- another 'x'
+ print(x) --&gt; 12
+ end
+ print(x) --&gt; 11
+ end
+ print(x) --&gt; 10 (the global one)
+</pre>
+
+<p>
+Notice that, in a declaration like <code>local x = x</code>,
+the new <code>x</code> being declared is not in scope yet,
+and so the second <code>x</code> refers to the outside variable.
+
+
+<p>
+Because of the lexical scoping rules,
+local variables can be freely accessed by functions
+defined inside their scope.
+A local variable used by an inner function is called an <em>upvalue</em>
+(or <em>external local variable</em>, or simply <em>external variable</em>)
+inside the inner function.
+
+
+<p>
+Notice that each execution of a <b>local</b> statement
+defines new local variables.
+Consider the following example:
+
+<pre>
+ a = {}
+ local x = 20
+ for i = 1, 10 do
+ local y = 0
+ a[i] = function () y = y + 1; return x + y end
+ end
+</pre><p>
+The loop creates ten closures
+(that is, ten instances of the anonymous function).
+Each of these closures uses a different <code>y</code> variable,
+while all of them share the same <code>x</code>.
+
+
+
+
+
+<h1>4 &ndash; <a name="4">The Application Program Interface</a></h1>
+
+
+
+<p>
+
+This section describes the C&nbsp;API for Lua, that is,
+the set of C&nbsp;functions available to the host program to communicate
+with Lua.
+All API functions and related types and constants
+are declared in the header file <a name="pdf-lua.h"><code>lua.h</code></a>.
+
+
+<p>
+Even when we use the term "function",
+any facility in the API may be provided as a macro instead.
+Except where stated otherwise,
+all such macros use each of their arguments exactly once
+(except for the first argument, which is always a Lua state),
+and so do not generate any hidden side-effects.
+
+
+<p>
+As in most C&nbsp;libraries,
+the Lua API functions do not check their arguments
+for validity or consistency.
+However, you can change this behavior by compiling Lua
+with the macro <a name="pdf-LUA_USE_APICHECK"><code>LUA_USE_APICHECK</code></a> defined.
+
+
+<p>
+The Lua library is fully reentrant:
+it has no global variables.
+It keeps all information it needs in a dynamic structure,
+called the <em>Lua state</em>.
+
+
+<p>
+Each Lua state has one or more threads,
+which correspond to independent, cooperative lines of execution.
+The type <a href="#lua_State"><code>lua_State</code></a> (despite its name) refers to a thread.
+(Indirectly, through the thread, it also refers to the
+Lua state associated to the thread.)
+
+
+<p>
+A pointer to a thread must be passed as the first argument to
+every function in the library, except to <a href="#lua_newstate"><code>lua_newstate</code></a>,
+which creates a Lua state from scratch and returns a pointer
+to the <em>main thread</em> in the new state.
+
+
+
+
+
+<h2>4.1 &ndash; <a name="4.1">The Stack</a></h2>
+
+
+
+<p>
+Lua uses a <em>virtual stack</em> to pass values to and from C.
+Each element in this stack represents a Lua value
+(<b>nil</b>, number, string, etc.).
+Functions in the API can access this stack through the
+Lua state parameter that they receive.
+
+
+<p>
+Whenever Lua calls C, the called function gets a new stack,
+which is independent of previous stacks and of stacks of
+C&nbsp;functions that are still active.
+This stack initially contains any arguments to the C&nbsp;function
+and it is where the C&nbsp;function can store temporary
+Lua values and must push its results
+to be returned to the caller (see <a href="#lua_CFunction"><code>lua_CFunction</code></a>).
+
+
+<p>
+For convenience,
+most query operations in the API do not follow a strict stack discipline.
+Instead, they can refer to any element in the stack
+by using an <em>index</em>:
+A positive index represents an absolute stack position,
+starting at&nbsp;1 as the bottom of the stack;
+a negative index represents an offset relative to the top of the stack.
+More specifically, if the stack has <em>n</em> elements,
+then index&nbsp;1 represents the first element
+(that is, the element that was pushed onto the stack first)
+and
+index&nbsp;<em>n</em> represents the last element;
+index&nbsp;-1 also represents the last element
+(that is, the element at the&nbsp;top)
+and index <em>-n</em> represents the first element.
+
+
+
+
+
+<h3>4.1.1 &ndash; <a name="4.1.1">Stack Size</a></h3>
+
+<p>
+When you interact with the Lua API,
+you are responsible for ensuring consistency.
+In particular,
+<em>you are responsible for controlling stack overflow</em>.
+When you call any API function,
+you must ensure the stack has enough room to accommodate the results.
+
+
+<p>
+There is one exception to the above rule:
+When you call a Lua function
+without a fixed number of results (see <a href="#lua_call"><code>lua_call</code></a>),
+Lua ensures that the stack has enough space for all results.
+However, it does not ensure any extra space.
+So, before pushing anything on the stack after such a call
+you should use <a href="#lua_checkstack"><code>lua_checkstack</code></a>.
+
+
+<p>
+Whenever Lua calls C,
+it ensures that the stack has space for
+at least <a name="pdf-LUA_MINSTACK"><code>LUA_MINSTACK</code></a> extra elements;
+that is, you can safely push up to <code>LUA_MINSTACK</code> values into it.
+<code>LUA_MINSTACK</code> is defined as 20,
+so that usually you do not have to worry about stack space
+unless your code has loops pushing elements onto the stack.
+Whenever necessary,
+you can use the function <a href="#lua_checkstack"><code>lua_checkstack</code></a>
+to ensure that the stack has enough space for pushing new elements.
+
+
+
+
+
+<h3>4.1.2 &ndash; <a name="4.1.2">Valid and Acceptable Indices</a></h3>
+
+<p>
+Any function in the API that receives stack indices
+works only with <em>valid indices</em> or <em>acceptable indices</em>.
+
+
+<p>
+A <em>valid index</em> is an index that refers to a
+position that stores a modifiable Lua value.
+It comprises stack indices between&nbsp;1 and the stack top
+(<code>1 &le; abs(index) &le; top</code>)
+
+plus <em>pseudo-indices</em>,
+which represent some positions that are accessible to C&nbsp;code
+but that are not in the stack.
+Pseudo-indices are used to access the registry (see <a href="#4.3">&sect;4.3</a>)
+and the upvalues of a C&nbsp;function (see <a href="#4.2">&sect;4.2</a>).
+
+
+<p>
+Functions that do not need a specific mutable position,
+but only a value (e.g., query functions),
+can be called with acceptable indices.
+An <em>acceptable index</em> can be any valid index,
+but it also can be any positive index after the stack top
+within the space allocated for the stack,
+that is, indices up to the stack size.
+(Note that 0 is never an acceptable index.)
+Indices to upvalues (see <a href="#4.2">&sect;4.2</a>) greater than the real number
+of upvalues in the current C&nbsp;function are also acceptable (but invalid).
+Except when noted otherwise,
+functions in the API work with acceptable indices.
+
+
+<p>
+Acceptable indices serve to avoid extra tests
+against the stack top when querying the stack.
+For instance, a C&nbsp;function can query its third argument
+without the need to check whether there is a third argument,
+that is, without the need to check whether 3 is a valid index.
+
+
+<p>
+For functions that can be called with acceptable indices,
+any non-valid index is treated as if it
+contains a value of a virtual type <a name="pdf-LUA_TNONE"><code>LUA_TNONE</code></a>,
+which behaves like a nil value.
+
+
+
+
+
+<h3>4.1.3 &ndash; <a name="4.1.3">Pointers to strings</a></h3>
+
+<p>
+Several functions in the API return pointers (<code>const char*</code>)
+to Lua strings in the stack.
+(See <a href="#lua_pushfstring"><code>lua_pushfstring</code></a>, <a href="#lua_pushlstring"><code>lua_pushlstring</code></a>,
+<a href="#lua_pushstring"><code>lua_pushstring</code></a>, and <a href="#lua_tolstring"><code>lua_tolstring</code></a>.
+See also <a href="#luaL_checklstring"><code>luaL_checklstring</code></a>, <a href="#luaL_checkstring"><code>luaL_checkstring</code></a>,
+and <a href="#luaL_tolstring"><code>luaL_tolstring</code></a> in the auxiliary library.)
+
+
+<p>
+In general,
+Lua's garbage collection can free or move internal memory
+and then invalidate pointers to internal strings.
+To allow a safe use of these pointers,
+the API guarantees that any pointer to a string in a stack index
+is valid while the string value at that index is not removed from the stack.
+(It can be moved to another index, though.)
+When the index is a pseudo-index (referring to an upvalue),
+the pointer is valid while the corresponding call is active and
+the corresponding upvalue is not modified.
+
+
+<p>
+Some functions in the debug interface
+also return pointers to strings,
+namely <a href="#lua_getlocal"><code>lua_getlocal</code></a>, <a href="#lua_getupvalue"><code>lua_getupvalue</code></a>,
+<a href="#lua_setlocal"><code>lua_setlocal</code></a>, and <a href="#lua_setupvalue"><code>lua_setupvalue</code></a>.
+For these functions, the pointer is guaranteed to
+be valid while the caller function is active and
+the given closure (if one was given) is in the stack.
+
+
+<p>
+Except for these guarantees,
+the garbage collector is free to invalidate
+any pointer to internal strings.
+
+
+
+
+
+
+
+<h2>4.2 &ndash; <a name="4.2">C Closures</a></h2>
+
+<p>
+When a C&nbsp;function is created,
+it is possible to associate some values with it,
+thus creating a <em>C&nbsp;closure</em>
+(see <a href="#lua_pushcclosure"><code>lua_pushcclosure</code></a>);
+these values are called <em>upvalues</em> and are
+accessible to the function whenever it is called.
+
+
+<p>
+Whenever a C&nbsp;function is called,
+its upvalues are located at specific pseudo-indices.
+These pseudo-indices are produced by the macro
+<a href="#lua_upvalueindex"><code>lua_upvalueindex</code></a>.
+The first upvalue associated with a function is at index
+<code>lua_upvalueindex(1)</code>, and so on.
+Any access to <code>lua_upvalueindex(<em>n</em>)</code>,
+where <em>n</em> is greater than the number of upvalues of the
+current function
+(but not greater than 256,
+which is one plus the maximum number of upvalues in a closure),
+produces an acceptable but invalid index.
+
+
+<p>
+A C&nbsp;closure can also change the values
+of its corresponding upvalues.
+
+
+
+
+
+<h2>4.3 &ndash; <a name="4.3">Registry</a></h2>
+
+<p>
+Lua provides a <em>registry</em>,
+a predefined table that can be used by any C&nbsp;code to
+store whatever Lua values it needs to store.
+The registry table is always accessible at pseudo-index
+<a name="pdf-LUA_REGISTRYINDEX"><code>LUA_REGISTRYINDEX</code></a>.
+Any C&nbsp;library can store data into this table,
+but it must take care to choose keys
+that are different from those used
+by other libraries, to avoid collisions.
+Typically, you should use as key a string containing your library name,
+or a light userdata with the address of a C&nbsp;object in your code,
+or any Lua object created by your code.
+As with variable names,
+string keys starting with an underscore followed by
+uppercase letters are reserved for Lua.
+
+
+<p>
+The integer keys in the registry are used
+by the reference mechanism (see <a href="#luaL_ref"><code>luaL_ref</code></a>)
+and by some predefined values.
+Therefore, integer keys in the registry
+must not be used for other purposes.
+
+
+<p>
+When you create a new Lua state,
+its registry comes with some predefined values.
+These predefined values are indexed with integer keys
+defined as constants in <code>lua.h</code>.
+The following constants are defined:
+
+<ul>
+<li><b><a name="pdf-LUA_RIDX_MAINTHREAD"><code>LUA_RIDX_MAINTHREAD</code></a>: </b> At this index the registry has
+the main thread of the state.
+(The main thread is the one created together with the state.)
+</li>
+
+<li><b><a name="pdf-LUA_RIDX_GLOBALS"><code>LUA_RIDX_GLOBALS</code></a>: </b> At this index the registry has
+the global environment.
+</li>
+</ul>
+
+
+
+
+<h2>4.4 &ndash; <a name="4.4">Error Handling in C</a></h2>
+
+
+
+<p>
+Internally, Lua uses the C <code>longjmp</code> facility to handle errors.
+(Lua will use exceptions if you compile it as C++;
+search for <code>LUAI_THROW</code> in the source code for details.)
+When Lua faces any error,
+such as a memory allocation error or a type error,
+it <em>raises</em> an error;
+that is, it does a long jump.
+A <em>protected environment</em> uses <code>setjmp</code>
+to set a recovery point;
+any error jumps to the most recent active recovery point.
+
+
+<p>
+Inside a C&nbsp;function you can raise an error explicitly
+by calling <a href="#lua_error"><code>lua_error</code></a>.
+
+
+<p>
+Most functions in the API can raise an error,
+for instance due to a memory allocation error.
+The documentation for each function indicates whether
+it can raise errors.
+
+
+<p>
+If an error happens outside any protected environment,
+Lua calls a <em>panic function</em> (see <a href="#lua_atpanic"><code>lua_atpanic</code></a>)
+and then calls <code>abort</code>,
+thus exiting the host application.
+Your panic function can avoid this exit by
+never returning
+(e.g., doing a long jump to your own recovery point outside Lua).
+
+
+<p>
+The panic function,
+as its name implies,
+is a mechanism of last resort.
+Programs should avoid it.
+As a general rule,
+when a C&nbsp;function is called by Lua with a Lua state,
+it can do whatever it wants on that Lua state,
+as it should be already protected.
+However,
+when C code operates on other Lua states
+(e.g., a Lua-state argument to the function,
+a Lua state stored in the registry, or
+the result of <a href="#lua_newthread"><code>lua_newthread</code></a>),
+it should use them only in API calls that cannot raise errors.
+
+
+<p>
+The panic function runs as if it were a message handler (see <a href="#2.3">&sect;2.3</a>);
+in particular, the error object is on the top of the stack.
+However, there is no guarantee about stack space.
+To push anything on the stack,
+the panic function must first check the available space (see <a href="#4.1.1">&sect;4.1.1</a>).
+
+
+
+
+
+<h3>4.4.1 &ndash; <a name="4.4.1">Status Codes</a></h3>
+
+<p>
+Several functions that report errors in the API use the following
+status codes to indicate different kinds of errors or other conditions:
+
+<ul>
+
+<li><b><a name="pdf-LUA_OK"><code>LUA_OK</code></a> (0): </b> no errors.</li>
+
+<li><b><a name="pdf-LUA_ERRRUN"><code>LUA_ERRRUN</code></a>: </b> a runtime error.</li>
+
+<li><b><a name="pdf-LUA_ERRMEM"><code>LUA_ERRMEM</code></a>: </b>
+memory allocation error.
+For such errors, Lua does not call the message handler.
+</li>
+
+<li><b><a name="pdf-LUA_ERRERR"><code>LUA_ERRERR</code></a>: </b> error while running the message handler.</li>
+
+<li><b><a name="pdf-LUA_ERRSYNTAX"><code>LUA_ERRSYNTAX</code></a>: </b> syntax error during precompilation.</li>
+
+<li><b><a name="pdf-LUA_YIELD"><code>LUA_YIELD</code></a>: </b> the thread (coroutine) yields.</li>
+
+<li><b><a name="pdf-LUA_ERRFILE"><code>LUA_ERRFILE</code></a>: </b> a file-related error;
+e.g., it cannot open or read the file.</li>
+
+</ul><p>
+These constants are defined in the header file <code>lua.h</code>.
+
+
+
+
+
+
+
+<h2>4.5 &ndash; <a name="4.5">Handling Yields in C</a></h2>
+
+<p>
+Internally, Lua uses the C <code>longjmp</code> facility to yield a coroutine.
+Therefore, if a C&nbsp;function <code>foo</code> calls an API function
+and this API function yields
+(directly or indirectly by calling another function that yields),
+Lua cannot return to <code>foo</code> any more,
+because the <code>longjmp</code> removes its frame from the C&nbsp;stack.
+
+
+<p>
+To avoid this kind of problem,
+Lua raises an error whenever it tries to yield across an API call,
+except for three functions:
+<a href="#lua_yieldk"><code>lua_yieldk</code></a>, <a href="#lua_callk"><code>lua_callk</code></a>, and <a href="#lua_pcallk"><code>lua_pcallk</code></a>.
+All those functions receive a <em>continuation function</em>
+(as a parameter named <code>k</code>) to continue execution after a yield.
+
+
+<p>
+We need to set some terminology to explain continuations.
+We have a C&nbsp;function called from Lua which we will call
+the <em>original function</em>.
+This original function then calls one of those three functions in the C API,
+which we will call the <em>callee function</em>,
+that then yields the current thread.
+This can happen when the callee function is <a href="#lua_yieldk"><code>lua_yieldk</code></a>,
+or when the callee function is either <a href="#lua_callk"><code>lua_callk</code></a> or <a href="#lua_pcallk"><code>lua_pcallk</code></a>
+and the function called by them yields.
+
+
+<p>
+Suppose the running thread yields while executing the callee function.
+After the thread resumes,
+it eventually will finish running the callee function.
+However,
+the callee function cannot return to the original function,
+because its frame in the C&nbsp;stack was destroyed by the yield.
+Instead, Lua calls a <em>continuation function</em>,
+which was given as an argument to the callee function.
+As the name implies,
+the continuation function should continue the task
+of the original function.
+
+
+<p>
+As an illustration, consider the following function:
+
+<pre>
+ int original_function (lua_State *L) {
+ ... /* code 1 */
+ status = lua_pcall(L, n, m, h); /* calls Lua */
+ ... /* code 2 */
+ }
+</pre><p>
+Now we want to allow
+the Lua code being run by <a href="#lua_pcall"><code>lua_pcall</code></a> to yield.
+First, we can rewrite our function like here:
+
+<pre>
+ int k (lua_State *L, int status, lua_KContext ctx) {
+ ... /* code 2 */
+ }
+
+ int original_function (lua_State *L) {
+ ... /* code 1 */
+ return k(L, lua_pcall(L, n, m, h), ctx);
+ }
+</pre><p>
+In the above code,
+the new function <code>k</code> is a
+<em>continuation function</em> (with type <a href="#lua_KFunction"><code>lua_KFunction</code></a>),
+which should do all the work that the original function
+was doing after calling <a href="#lua_pcall"><code>lua_pcall</code></a>.
+Now, we must inform Lua that it must call <code>k</code> if the Lua code
+being executed by <a href="#lua_pcall"><code>lua_pcall</code></a> gets interrupted in some way
+(errors or yielding),
+so we rewrite the code as here,
+replacing <a href="#lua_pcall"><code>lua_pcall</code></a> by <a href="#lua_pcallk"><code>lua_pcallk</code></a>:
+
+<pre>
+ int original_function (lua_State *L) {
+ ... /* code 1 */
+ return k(L, lua_pcallk(L, n, m, h, ctx2, k), ctx1);
+ }
+</pre><p>
+Note the external, explicit call to the continuation:
+Lua will call the continuation only if needed, that is,
+in case of errors or resuming after a yield.
+If the called function returns normally without ever yielding,
+<a href="#lua_pcallk"><code>lua_pcallk</code></a> (and <a href="#lua_callk"><code>lua_callk</code></a>) will also return normally.
+(Of course, instead of calling the continuation in that case,
+you can do the equivalent work directly inside the original function.)
+
+
+<p>
+Besides the Lua state,
+the continuation function has two other parameters:
+the final status of the call and the context value (<code>ctx</code>) that
+was passed originally to <a href="#lua_pcallk"><code>lua_pcallk</code></a>.
+Lua does not use this context value;
+it only passes this value from the original function to the
+continuation function.
+For <a href="#lua_pcallk"><code>lua_pcallk</code></a>,
+the status is the same value that would be returned by <a href="#lua_pcallk"><code>lua_pcallk</code></a>,
+except that it is <a href="#pdf-LUA_YIELD"><code>LUA_YIELD</code></a> when being executed after a yield
+(instead of <a href="#pdf-LUA_OK"><code>LUA_OK</code></a>).
+For <a href="#lua_yieldk"><code>lua_yieldk</code></a> and <a href="#lua_callk"><code>lua_callk</code></a>,
+the status is always <a href="#pdf-LUA_YIELD"><code>LUA_YIELD</code></a> when Lua calls the continuation.
+(For these two functions,
+Lua will not call the continuation in case of errors,
+because they do not handle errors.)
+Similarly, when using <a href="#lua_callk"><code>lua_callk</code></a>,
+you should call the continuation function
+with <a href="#pdf-LUA_OK"><code>LUA_OK</code></a> as the status.
+(For <a href="#lua_yieldk"><code>lua_yieldk</code></a>, there is not much point in calling
+directly the continuation function,
+because <a href="#lua_yieldk"><code>lua_yieldk</code></a> usually does not return.)
+
+
+<p>
+Lua treats the continuation function as if it were the original function.
+The continuation function receives the same Lua stack
+from the original function,
+in the same state it would be if the callee function had returned.
+(For instance,
+after a <a href="#lua_callk"><code>lua_callk</code></a> the function and its arguments are
+removed from the stack and replaced by the results from the call.)
+It also has the same upvalues.
+Whatever it returns is handled by Lua as if it were the return
+of the original function.
+
+
+
+
+
+<h2>4.6 &ndash; <a name="4.6">Functions and Types</a></h2>
+
+<p>
+Here we list all functions and types from the C&nbsp;API in
+alphabetical order.
+Each function has an indicator like this:
+<span class="apii">[-o, +p, <em>x</em>]</span>
+
+
+<p>
+The first field, <code>o</code>,
+is how many elements the function pops from the stack.
+The second field, <code>p</code>,
+is how many elements the function pushes onto the stack.
+(Any function always pushes its results after popping its arguments.)
+A field in the form <code>x|y</code> means the function can push (or pop)
+<code>x</code> or <code>y</code> elements,
+depending on the situation;
+an interrogation mark '<code>?</code>' means that
+we cannot know how many elements the function pops/pushes
+by looking only at its arguments.
+(For instance, they may depend on what is in the stack.)
+The third field, <code>x</code>,
+tells whether the function may raise errors:
+'<code>-</code>' means the function never raises any error;
+'<code>m</code>' means the function may raise only out-of-memory errors;
+'<code>v</code>' means the function may raise the errors explained in the text;
+'<code>e</code>' means the function can run arbitrary Lua code,
+either directly or through metamethods,
+and therefore may raise any errors.
+
+
+
+<hr><h3><a name="lua_absindex"><code>lua_absindex</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_absindex (lua_State *L, int idx);</pre>
+
+<p>
+Converts the acceptable index <code>idx</code>
+into an equivalent absolute index
+(that is, one that does not depend on the stack size).
+
+
+
+
+
+<hr><h3><a name="lua_Alloc"><code>lua_Alloc</code></a></h3>
+<pre>typedef void * (*lua_Alloc) (void *ud,
+ void *ptr,
+ size_t osize,
+ size_t nsize);</pre>
+
+<p>
+The type of the memory-allocation function used by Lua states.
+The allocator function must provide a
+functionality similar to <code>realloc</code>,
+but not exactly the same.
+Its arguments are
+<code>ud</code>, an opaque pointer passed to <a href="#lua_newstate"><code>lua_newstate</code></a>;
+<code>ptr</code>, a pointer to the block being allocated/reallocated/freed;
+<code>osize</code>, the original size of the block or some code about what
+is being allocated;
+and <code>nsize</code>, the new size of the block.
+
+
+<p>
+When <code>ptr</code> is not <code>NULL</code>,
+<code>osize</code> is the size of the block pointed by <code>ptr</code>,
+that is, the size given when it was allocated or reallocated.
+
+
+<p>
+When <code>ptr</code> is <code>NULL</code>,
+<code>osize</code> encodes the kind of object that Lua is allocating.
+<code>osize</code> is any of
+<a href="#pdf-LUA_TSTRING"><code>LUA_TSTRING</code></a>, <a href="#pdf-LUA_TTABLE"><code>LUA_TTABLE</code></a>, <a href="#pdf-LUA_TFUNCTION"><code>LUA_TFUNCTION</code></a>,
+<a href="#pdf-LUA_TUSERDATA"><code>LUA_TUSERDATA</code></a>, or <a href="#pdf-LUA_TTHREAD"><code>LUA_TTHREAD</code></a> when (and only when)
+Lua is creating a new object of that type.
+When <code>osize</code> is some other value,
+Lua is allocating memory for something else.
+
+
+<p>
+Lua assumes the following behavior from the allocator function:
+
+
+<p>
+When <code>nsize</code> is zero,
+the allocator must behave like <code>free</code>
+and then return <code>NULL</code>.
+
+
+<p>
+When <code>nsize</code> is not zero,
+the allocator must behave like <code>realloc</code>.
+In particular, the allocator returns <code>NULL</code>
+if and only if it cannot fulfill the request.
+
+
+<p>
+Here is a simple implementation for the allocator function.
+It is used in the auxiliary library by <a href="#luaL_newstate"><code>luaL_newstate</code></a>.
+
+<pre>
+ static void *l_alloc (void *ud, void *ptr, size_t osize,
+ size_t nsize) {
+ (void)ud; (void)osize; /* not used */
+ if (nsize == 0) {
+ free(ptr);
+ return NULL;
+ }
+ else
+ return realloc(ptr, nsize);
+ }
+</pre><p>
+Note that ISO&nbsp;C ensures
+that <code>free(NULL)</code> has no effect and that
+<code>realloc(NULL,size)</code> is equivalent to <code>malloc(size)</code>.
+
+
+
+
+
+<hr><h3><a name="lua_arith"><code>lua_arith</code></a></h3><p>
+<span class="apii">[-(2|1), +1, <em>e</em>]</span>
+<pre>void lua_arith (lua_State *L, int op);</pre>
+
+<p>
+Performs an arithmetic or bitwise operation over the two values
+(or one, in the case of negations)
+at the top of the stack,
+with the value on the top being the second operand,
+pops these values, and pushes the result of the operation.
+The function follows the semantics of the corresponding Lua operator
+(that is, it may call metamethods).
+
+
+<p>
+The value of <code>op</code> must be one of the following constants:
+
+<ul>
+
+<li><b><a name="pdf-LUA_OPADD"><code>LUA_OPADD</code></a>: </b> performs addition (<code>+</code>)</li>
+<li><b><a name="pdf-LUA_OPSUB"><code>LUA_OPSUB</code></a>: </b> performs subtraction (<code>-</code>)</li>
+<li><b><a name="pdf-LUA_OPMUL"><code>LUA_OPMUL</code></a>: </b> performs multiplication (<code>*</code>)</li>
+<li><b><a name="pdf-LUA_OPDIV"><code>LUA_OPDIV</code></a>: </b> performs float division (<code>/</code>)</li>
+<li><b><a name="pdf-LUA_OPIDIV"><code>LUA_OPIDIV</code></a>: </b> performs floor division (<code>//</code>)</li>
+<li><b><a name="pdf-LUA_OPMOD"><code>LUA_OPMOD</code></a>: </b> performs modulo (<code>%</code>)</li>
+<li><b><a name="pdf-LUA_OPPOW"><code>LUA_OPPOW</code></a>: </b> performs exponentiation (<code>^</code>)</li>
+<li><b><a name="pdf-LUA_OPUNM"><code>LUA_OPUNM</code></a>: </b> performs mathematical negation (unary <code>-</code>)</li>
+<li><b><a name="pdf-LUA_OPBNOT"><code>LUA_OPBNOT</code></a>: </b> performs bitwise NOT (<code>~</code>)</li>
+<li><b><a name="pdf-LUA_OPBAND"><code>LUA_OPBAND</code></a>: </b> performs bitwise AND (<code>&amp;</code>)</li>
+<li><b><a name="pdf-LUA_OPBOR"><code>LUA_OPBOR</code></a>: </b> performs bitwise OR (<code>|</code>)</li>
+<li><b><a name="pdf-LUA_OPBXOR"><code>LUA_OPBXOR</code></a>: </b> performs bitwise exclusive OR (<code>~</code>)</li>
+<li><b><a name="pdf-LUA_OPSHL"><code>LUA_OPSHL</code></a>: </b> performs left shift (<code>&lt;&lt;</code>)</li>
+<li><b><a name="pdf-LUA_OPSHR"><code>LUA_OPSHR</code></a>: </b> performs right shift (<code>&gt;&gt;</code>)</li>
+
+</ul>
+
+
+
+
+<hr><h3><a name="lua_atpanic"><code>lua_atpanic</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf);</pre>
+
+<p>
+Sets a new panic function and returns the old one (see <a href="#4.4">&sect;4.4</a>).
+
+
+
+
+
+<hr><h3><a name="lua_call"><code>lua_call</code></a></h3><p>
+<span class="apii">[-(nargs+1), +nresults, <em>e</em>]</span>
+<pre>void lua_call (lua_State *L, int nargs, int nresults);</pre>
+
+<p>
+Calls a function.
+Like regular Lua calls,
+<code>lua_call</code> respects the <code>__call</code> metamethod.
+So, here the word "function"
+means any callable value.
+
+
+<p>
+To do a call you must use the following protocol:
+first, the function to be called is pushed onto the stack;
+then, the arguments to the call are pushed
+in direct order;
+that is, the first argument is pushed first.
+Finally you call <a href="#lua_call"><code>lua_call</code></a>;
+<code>nargs</code> is the number of arguments that you pushed onto the stack.
+When the function returns,
+all arguments and the function value are popped
+and the call results are pushed onto the stack.
+The number of results is adjusted to <code>nresults</code>,
+unless <code>nresults</code> is <a name="pdf-LUA_MULTRET"><code>LUA_MULTRET</code></a>.
+In this case, all results from the function are pushed;
+Lua takes care that the returned values fit into the stack space,
+but it does not ensure any extra space in the stack.
+The function results are pushed onto the stack in direct order
+(the first result is pushed first),
+so that after the call the last result is on the top of the stack.
+
+
+<p>
+Any error while calling and running the function is propagated upwards
+(with a <code>longjmp</code>).
+
+
+<p>
+The following example shows how the host program can do the
+equivalent to this Lua code:
+
+<pre>
+ a = f("how", t.x, 14)
+</pre><p>
+Here it is in&nbsp;C:
+
+<pre>
+ lua_getglobal(L, "f"); /* function to be called */
+ lua_pushliteral(L, "how"); /* 1st argument */
+ lua_getglobal(L, "t"); /* table to be indexed */
+ lua_getfield(L, -1, "x"); /* push result of t.x (2nd arg) */
+ lua_remove(L, -2); /* remove 't' from the stack */
+ lua_pushinteger(L, 14); /* 3rd argument */
+ lua_call(L, 3, 1); /* call 'f' with 3 arguments and 1 result */
+ lua_setglobal(L, "a"); /* set global 'a' */
+</pre><p>
+Note that the code above is <em>balanced</em>:
+at its end, the stack is back to its original configuration.
+This is considered good programming practice.
+
+
+
+
+
+<hr><h3><a name="lua_callk"><code>lua_callk</code></a></h3><p>
+<span class="apii">[-(nargs + 1), +nresults, <em>e</em>]</span>
+<pre>void lua_callk (lua_State *L,
+ int nargs,
+ int nresults,
+ lua_KContext ctx,
+ lua_KFunction k);</pre>
+
+<p>
+This function behaves exactly like <a href="#lua_call"><code>lua_call</code></a>,
+but allows the called function to yield (see <a href="#4.5">&sect;4.5</a>).
+
+
+
+
+
+<hr><h3><a name="lua_CFunction"><code>lua_CFunction</code></a></h3>
+<pre>typedef int (*lua_CFunction) (lua_State *L);</pre>
+
+<p>
+Type for C&nbsp;functions.
+
+
+<p>
+In order to communicate properly with Lua,
+a C&nbsp;function must use the following protocol,
+which defines the way parameters and results are passed:
+a C&nbsp;function receives its arguments from Lua in its stack
+in direct order (the first argument is pushed first).
+So, when the function starts,
+<code>lua_gettop(L)</code> returns the number of arguments received by the function.
+The first argument (if any) is at index 1
+and its last argument is at index <code>lua_gettop(L)</code>.
+To return values to Lua, a C&nbsp;function just pushes them onto the stack,
+in direct order (the first result is pushed first),
+and returns in C the number of results.
+Any other value in the stack below the results will be properly
+discarded by Lua.
+Like a Lua function, a C&nbsp;function called by Lua can also return
+many results.
+
+
+<p>
+As an example, the following function receives a variable number
+of numeric arguments and returns their average and their sum:
+
+<pre>
+ static int foo (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ lua_Number sum = 0.0;
+ int i;
+ for (i = 1; i &lt;= n; i++) {
+ if (!lua_isnumber(L, i)) {
+ lua_pushliteral(L, "incorrect argument");
+ lua_error(L);
+ }
+ sum += lua_tonumber(L, i);
+ }
+ lua_pushnumber(L, sum/n); /* first result */
+ lua_pushnumber(L, sum); /* second result */
+ return 2; /* number of results */
+ }
+</pre>
+
+
+
+
+<hr><h3><a name="lua_checkstack"><code>lua_checkstack</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_checkstack (lua_State *L, int n);</pre>
+
+<p>
+Ensures that the stack has space for at least <code>n</code> extra elements,
+that is, that you can safely push up to <code>n</code> values into it.
+It returns false if it cannot fulfill the request,
+either because it would cause the stack
+to be greater than a fixed maximum size
+(typically at least several thousand elements) or
+because it cannot allocate memory for the extra space.
+This function never shrinks the stack;
+if the stack already has space for the extra elements,
+it is left unchanged.
+
+
+
+
+
+<hr><h3><a name="lua_close"><code>lua_close</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void lua_close (lua_State *L);</pre>
+
+<p>
+Close all active to-be-closed variables in the main thread,
+release all objects in the given Lua state
+(calling the corresponding garbage-collection metamethods, if any),
+and frees all dynamic memory used by this state.
+
+
+<p>
+On several platforms, you may not need to call this function,
+because all resources are naturally released when the host program ends.
+On the other hand, long-running programs that create multiple states,
+such as daemons or web servers,
+will probably need to close states as soon as they are not needed.
+
+
+
+
+
+<hr><h3><a name="lua_closeslot"><code>lua_closeslot</code></a></h3><p>
+<span class="apii">[-0, +0, <em>e</em>]</span>
+<pre>void lua_closeslot (lua_State *L, int index);</pre>
+
+<p>
+Close the to-be-closed slot at the given index and set its value to <b>nil</b>.
+The index must be the last index previously marked to be closed
+(see <a href="#lua_toclose"><code>lua_toclose</code></a>) that is still active (that is, not closed yet).
+
+
+<p>
+A <code>__close</code> metamethod cannot yield
+when called through this function.
+
+
+<p>
+(This function was introduced in release&nbsp;5.4.3.)
+
+
+
+
+
+<hr><h3><a name="lua_closethread"><code>lua_closethread</code></a></h3><p>
+<span class="apii">[-0, +?, &ndash;]</span>
+<pre>int lua_closethread (lua_State *L, lua_State *from);</pre>
+
+<p>
+Resets a thread, cleaning its call stack and closing all pending
+to-be-closed variables.
+Returns a status code:
+<a href="#pdf-LUA_OK"><code>LUA_OK</code></a> for no errors in the thread
+(either the original error that stopped the thread or
+errors in closing methods),
+or an error status otherwise.
+In case of error,
+leaves the error object on the top of the stack.
+
+
+<p>
+The parameter <code>from</code> represents the coroutine that is resetting <code>L</code>.
+If there is no such coroutine,
+this parameter can be <code>NULL</code>.
+
+
+<p>
+(This function was introduced in release&nbsp;5.4.6.)
+
+
+
+
+
+<hr><h3><a name="lua_compare"><code>lua_compare</code></a></h3><p>
+<span class="apii">[-0, +0, <em>e</em>]</span>
+<pre>int lua_compare (lua_State *L, int index1, int index2, int op);</pre>
+
+<p>
+Compares two Lua values.
+Returns 1 if the value at index <code>index1</code> satisfies <code>op</code>
+when compared with the value at index <code>index2</code>,
+following the semantics of the corresponding Lua operator
+(that is, it may call metamethods).
+Otherwise returns&nbsp;0.
+Also returns&nbsp;0 if any of the indices is not valid.
+
+
+<p>
+The value of <code>op</code> must be one of the following constants:
+
+<ul>
+
+<li><b><a name="pdf-LUA_OPEQ"><code>LUA_OPEQ</code></a>: </b> compares for equality (<code>==</code>)</li>
+<li><b><a name="pdf-LUA_OPLT"><code>LUA_OPLT</code></a>: </b> compares for less than (<code>&lt;</code>)</li>
+<li><b><a name="pdf-LUA_OPLE"><code>LUA_OPLE</code></a>: </b> compares for less or equal (<code>&lt;=</code>)</li>
+
+</ul>
+
+
+
+
+<hr><h3><a name="lua_concat"><code>lua_concat</code></a></h3><p>
+<span class="apii">[-n, +1, <em>e</em>]</span>
+<pre>void lua_concat (lua_State *L, int n);</pre>
+
+<p>
+Concatenates the <code>n</code> values at the top of the stack,
+pops them, and leaves the result on the top.
+If <code>n</code>&nbsp;is&nbsp;1, the result is the single value on the stack
+(that is, the function does nothing);
+if <code>n</code> is 0, the result is the empty string.
+Concatenation is performed following the usual semantics of Lua
+(see <a href="#3.4.6">&sect;3.4.6</a>).
+
+
+
+
+
+<hr><h3><a name="lua_copy"><code>lua_copy</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void lua_copy (lua_State *L, int fromidx, int toidx);</pre>
+
+<p>
+Copies the element at index <code>fromidx</code>
+into the valid index <code>toidx</code>,
+replacing the value at that position.
+Values at other positions are not affected.
+
+
+
+
+
+<hr><h3><a name="lua_createtable"><code>lua_createtable</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void lua_createtable (lua_State *L, int narr, int nrec);</pre>
+
+<p>
+Creates a new empty table and pushes it onto the stack.
+Parameter <code>narr</code> is a hint for how many elements the table
+will have as a sequence;
+parameter <code>nrec</code> is a hint for how many other elements
+the table will have.
+Lua may use these hints to preallocate memory for the new table.
+This preallocation may help performance when you know in advance
+how many elements the table will have.
+Otherwise you can use the function <a href="#lua_newtable"><code>lua_newtable</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_dump"><code>lua_dump</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_dump (lua_State *L,
+ lua_Writer writer,
+ void *data,
+ int strip);</pre>
+
+<p>
+Dumps a function as a binary chunk.
+Receives a Lua function on the top of the stack
+and produces a binary chunk that,
+if loaded again,
+results in a function equivalent to the one dumped.
+As it produces parts of the chunk,
+<a href="#lua_dump"><code>lua_dump</code></a> calls function <code>writer</code> (see <a href="#lua_Writer"><code>lua_Writer</code></a>)
+with the given <code>data</code>
+to write them.
+
+
+<p>
+If <code>strip</code> is true,
+the binary representation may not include all debug information
+about the function,
+to save space.
+
+
+<p>
+The value returned is the error code returned by the last
+call to the writer;
+0&nbsp;means no errors.
+
+
+<p>
+This function does not pop the Lua function from the stack.
+
+
+
+
+
+<hr><h3><a name="lua_error"><code>lua_error</code></a></h3><p>
+<span class="apii">[-1, +0, <em>v</em>]</span>
+<pre>int lua_error (lua_State *L);</pre>
+
+<p>
+Raises a Lua error,
+using the value on the top of the stack as the error object.
+This function does a long jump,
+and therefore never returns
+(see <a href="#luaL_error"><code>luaL_error</code></a>).
+
+
+
+
+
+<hr><h3><a name="lua_gc"><code>lua_gc</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_gc (lua_State *L, int what, ...);</pre>
+
+<p>
+Controls the garbage collector.
+
+
+<p>
+This function performs several tasks,
+according to the value of the parameter <code>what</code>.
+For options that need extra arguments,
+they are listed after the option.
+
+<ul>
+
+<li><b><code>LUA_GCCOLLECT</code>: </b>
+Performs a full garbage-collection cycle.
+</li>
+
+<li><b><code>LUA_GCSTOP</code>: </b>
+Stops the garbage collector.
+</li>
+
+<li><b><code>LUA_GCRESTART</code>: </b>
+Restarts the garbage collector.
+</li>
+
+<li><b><code>LUA_GCCOUNT</code>: </b>
+Returns the current amount of memory (in Kbytes) in use by Lua.
+</li>
+
+<li><b><code>LUA_GCCOUNTB</code>: </b>
+Returns the remainder of dividing the current amount of bytes of
+memory in use by Lua by 1024.
+</li>
+
+<li><b><code>LUA_GCSTEP</code> <code>(int stepsize)</code>: </b>
+Performs an incremental step of garbage collection,
+corresponding to the allocation of <code>stepsize</code> Kbytes.
+</li>
+
+<li><b><code>LUA_GCISRUNNING</code>: </b>
+Returns a boolean that tells whether the collector is running
+(i.e., not stopped).
+</li>
+
+<li><b><code>LUA_GCINC</code> (int pause, int stepmul, stepsize): </b>
+Changes the collector to incremental mode
+with the given parameters (see <a href="#2.5.1">&sect;2.5.1</a>).
+Returns the previous mode (<code>LUA_GCGEN</code> or <code>LUA_GCINC</code>).
+</li>
+
+<li><b><code>LUA_GCGEN</code> (int minormul, int majormul): </b>
+Changes the collector to generational mode
+with the given parameters (see <a href="#2.5.2">&sect;2.5.2</a>).
+Returns the previous mode (<code>LUA_GCGEN</code> or <code>LUA_GCINC</code>).
+</li>
+
+</ul><p>
+For more details about these options,
+see <a href="#pdf-collectgarbage"><code>collectgarbage</code></a>.
+
+
+<p>
+This function should not be called by a finalizer.
+
+
+
+
+
+<hr><h3><a name="lua_getallocf"><code>lua_getallocf</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_Alloc lua_getallocf (lua_State *L, void **ud);</pre>
+
+<p>
+Returns the memory-allocation function of a given state.
+If <code>ud</code> is not <code>NULL</code>, Lua stores in <code>*ud</code> the
+opaque pointer given when the memory-allocator function was set.
+
+
+
+
+
+<hr><h3><a name="lua_getfield"><code>lua_getfield</code></a></h3><p>
+<span class="apii">[-0, +1, <em>e</em>]</span>
+<pre>int lua_getfield (lua_State *L, int index, const char *k);</pre>
+
+<p>
+Pushes onto the stack the value <code>t[k]</code>,
+where <code>t</code> is the value at the given index.
+As in Lua, this function may trigger a metamethod
+for the "index" event (see <a href="#2.4">&sect;2.4</a>).
+
+
+<p>
+Returns the type of the pushed value.
+
+
+
+
+
+<hr><h3><a name="lua_getextraspace"><code>lua_getextraspace</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void *lua_getextraspace (lua_State *L);</pre>
+
+<p>
+Returns a pointer to a raw memory area associated with the
+given Lua state.
+The application can use this area for any purpose;
+Lua does not use it for anything.
+
+
+<p>
+Each new thread has this area initialized with a copy
+of the area of the main thread.
+
+
+<p>
+By default, this area has the size of a pointer to void,
+but you can recompile Lua with a different size for this area.
+(See <code>LUA_EXTRASPACE</code> in <code>luaconf.h</code>.)
+
+
+
+
+
+<hr><h3><a name="lua_getglobal"><code>lua_getglobal</code></a></h3><p>
+<span class="apii">[-0, +1, <em>e</em>]</span>
+<pre>int lua_getglobal (lua_State *L, const char *name);</pre>
+
+<p>
+Pushes onto the stack the value of the global <code>name</code>.
+Returns the type of that value.
+
+
+
+
+
+<hr><h3><a name="lua_geti"><code>lua_geti</code></a></h3><p>
+<span class="apii">[-0, +1, <em>e</em>]</span>
+<pre>int lua_geti (lua_State *L, int index, lua_Integer i);</pre>
+
+<p>
+Pushes onto the stack the value <code>t[i]</code>,
+where <code>t</code> is the value at the given index.
+As in Lua, this function may trigger a metamethod
+for the "index" event (see <a href="#2.4">&sect;2.4</a>).
+
+
+<p>
+Returns the type of the pushed value.
+
+
+
+
+
+<hr><h3><a name="lua_getmetatable"><code>lua_getmetatable</code></a></h3><p>
+<span class="apii">[-0, +(0|1), &ndash;]</span>
+<pre>int lua_getmetatable (lua_State *L, int index);</pre>
+
+<p>
+If the value at the given index has a metatable,
+the function pushes that metatable onto the stack and returns&nbsp;1.
+Otherwise,
+the function returns&nbsp;0 and pushes nothing on the stack.
+
+
+
+
+
+<hr><h3><a name="lua_gettable"><code>lua_gettable</code></a></h3><p>
+<span class="apii">[-1, +1, <em>e</em>]</span>
+<pre>int lua_gettable (lua_State *L, int index);</pre>
+
+<p>
+Pushes onto the stack the value <code>t[k]</code>,
+where <code>t</code> is the value at the given index
+and <code>k</code> is the value on the top of the stack.
+
+
+<p>
+This function pops the key from the stack,
+pushing the resulting value in its place.
+As in Lua, this function may trigger a metamethod
+for the "index" event (see <a href="#2.4">&sect;2.4</a>).
+
+
+<p>
+Returns the type of the pushed value.
+
+
+
+
+
+<hr><h3><a name="lua_gettop"><code>lua_gettop</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_gettop (lua_State *L);</pre>
+
+<p>
+Returns the index of the top element in the stack.
+Because indices start at&nbsp;1,
+this result is equal to the number of elements in the stack;
+in particular, 0&nbsp;means an empty stack.
+
+
+
+
+
+<hr><h3><a name="lua_getiuservalue"><code>lua_getiuservalue</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>int lua_getiuservalue (lua_State *L, int index, int n);</pre>
+
+<p>
+Pushes onto the stack the <code>n</code>-th user value associated with the
+full userdata at the given index and
+returns the type of the pushed value.
+
+
+<p>
+If the userdata does not have that value,
+pushes <b>nil</b> and returns <a href="#pdf-LUA_TNONE"><code>LUA_TNONE</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_insert"><code>lua_insert</code></a></h3><p>
+<span class="apii">[-1, +1, &ndash;]</span>
+<pre>void lua_insert (lua_State *L, int index);</pre>
+
+<p>
+Moves the top element into the given valid index,
+shifting up the elements above this index to open space.
+This function cannot be called with a pseudo-index,
+because a pseudo-index is not an actual stack position.
+
+
+
+
+
+<hr><h3><a name="lua_Integer"><code>lua_Integer</code></a></h3>
+<pre>typedef ... lua_Integer;</pre>
+
+<p>
+The type of integers in Lua.
+
+
+<p>
+By default this type is <code>long long</code>,
+(usually a 64-bit two-complement integer),
+but that can be changed to <code>long</code> or <code>int</code>
+(usually a 32-bit two-complement integer).
+(See <code>LUA_INT_TYPE</code> in <code>luaconf.h</code>.)
+
+
+<p>
+Lua also defines the constants
+<a name="pdf-LUA_MININTEGER"><code>LUA_MININTEGER</code></a> and <a name="pdf-LUA_MAXINTEGER"><code>LUA_MAXINTEGER</code></a>,
+with the minimum and the maximum values that fit in this type.
+
+
+
+
+
+<hr><h3><a name="lua_isboolean"><code>lua_isboolean</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isboolean (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is a boolean,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_iscfunction"><code>lua_iscfunction</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_iscfunction (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is a C&nbsp;function,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isfunction"><code>lua_isfunction</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isfunction (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is a function
+(either C or Lua), and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isinteger"><code>lua_isinteger</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isinteger (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is an integer
+(that is, the value is a number and is represented as an integer),
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_islightuserdata"><code>lua_islightuserdata</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_islightuserdata (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is a light userdata,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isnil"><code>lua_isnil</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isnil (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is <b>nil</b>,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isnone"><code>lua_isnone</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isnone (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the given index is not valid,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isnoneornil"><code>lua_isnoneornil</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isnoneornil (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the given index is not valid
+or if the value at this index is <b>nil</b>,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isnumber"><code>lua_isnumber</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isnumber (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is a number
+or a string convertible to a number,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isstring"><code>lua_isstring</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isstring (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is a string
+or a number (which is always convertible to a string),
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_istable"><code>lua_istable</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_istable (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is a table,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isthread"><code>lua_isthread</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isthread (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is a thread,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isuserdata"><code>lua_isuserdata</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isuserdata (lua_State *L, int index);</pre>
+
+<p>
+Returns 1 if the value at the given index is a userdata
+(either full or light), and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_isyieldable"><code>lua_isyieldable</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_isyieldable (lua_State *L);</pre>
+
+<p>
+Returns 1 if the given coroutine can yield,
+and 0&nbsp;otherwise.
+
+
+
+
+
+<hr><h3><a name="lua_KContext"><code>lua_KContext</code></a></h3>
+<pre>typedef ... lua_KContext;</pre>
+
+<p>
+The type for continuation-function contexts.
+It must be a numeric type.
+This type is defined as <code>intptr_t</code>
+when <code>intptr_t</code> is available,
+so that it can store pointers too.
+Otherwise, it is defined as <code>ptrdiff_t</code>.
+
+
+
+
+
+<hr><h3><a name="lua_KFunction"><code>lua_KFunction</code></a></h3>
+<pre>typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx);</pre>
+
+<p>
+Type for continuation functions (see <a href="#4.5">&sect;4.5</a>).
+
+
+
+
+
+<hr><h3><a name="lua_len"><code>lua_len</code></a></h3><p>
+<span class="apii">[-0, +1, <em>e</em>]</span>
+<pre>void lua_len (lua_State *L, int index);</pre>
+
+<p>
+Returns the length of the value at the given index.
+It is equivalent to the '<code>#</code>' operator in Lua (see <a href="#3.4.7">&sect;3.4.7</a>) and
+may trigger a metamethod for the "length" event (see <a href="#2.4">&sect;2.4</a>).
+The result is pushed on the stack.
+
+
+
+
+
+<hr><h3><a name="lua_load"><code>lua_load</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>int lua_load (lua_State *L,
+ lua_Reader reader,
+ void *data,
+ const char *chunkname,
+ const char *mode);</pre>
+
+<p>
+Loads a Lua chunk without running it.
+If there are no errors,
+<code>lua_load</code> pushes the compiled chunk as a Lua
+function on top of the stack.
+Otherwise, it pushes an error message.
+
+
+<p>
+The <code>lua_load</code> function uses a user-supplied <code>reader</code> function
+to read the chunk (see <a href="#lua_Reader"><code>lua_Reader</code></a>).
+The <code>data</code> argument is an opaque value passed to the reader function.
+
+
+<p>
+The <code>chunkname</code> argument gives a name to the chunk,
+which is used for error messages and in debug information (see <a href="#4.7">&sect;4.7</a>).
+
+
+<p>
+<code>lua_load</code> automatically detects whether the chunk is text or binary
+and loads it accordingly (see program <code>luac</code>).
+The string <code>mode</code> works as in function <a href="#pdf-load"><code>load</code></a>,
+with the addition that
+a <code>NULL</code> value is equivalent to the string "<code>bt</code>".
+
+
+<p>
+<code>lua_load</code> uses the stack internally,
+so the reader function must always leave the stack
+unmodified when returning.
+
+
+<p>
+<code>lua_load</code> can return
+<a href="#pdf-LUA_OK"><code>LUA_OK</code></a>, <a href="#pdf-LUA_ERRSYNTAX"><code>LUA_ERRSYNTAX</code></a>, or <a href="#pdf-LUA_ERRMEM"><code>LUA_ERRMEM</code></a>.
+The function may also return other values corresponding to
+errors raised by the read function (see <a href="#4.4.1">&sect;4.4.1</a>).
+
+
+<p>
+If the resulting function has upvalues,
+its first upvalue is set to the value of the global environment
+stored at index <code>LUA_RIDX_GLOBALS</code> in the registry (see <a href="#4.3">&sect;4.3</a>).
+When loading main chunks,
+this upvalue will be the <code>_ENV</code> variable (see <a href="#2.2">&sect;2.2</a>).
+Other upvalues are initialized with <b>nil</b>.
+
+
+
+
+
+<hr><h3><a name="lua_newstate"><code>lua_newstate</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_State *lua_newstate (lua_Alloc f, void *ud);</pre>
+
+<p>
+Creates a new independent state and returns its main thread.
+Returns <code>NULL</code> if it cannot create the state
+(due to lack of memory).
+The argument <code>f</code> is the allocator function;
+Lua will do all memory allocation for this state
+through this function (see <a href="#lua_Alloc"><code>lua_Alloc</code></a>).
+The second argument, <code>ud</code>, is an opaque pointer that Lua
+passes to the allocator in every call.
+
+
+
+
+
+<hr><h3><a name="lua_newtable"><code>lua_newtable</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void lua_newtable (lua_State *L);</pre>
+
+<p>
+Creates a new empty table and pushes it onto the stack.
+It is equivalent to <code>lua_createtable(L, 0, 0)</code>.
+
+
+
+
+
+<hr><h3><a name="lua_newthread"><code>lua_newthread</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>lua_State *lua_newthread (lua_State *L);</pre>
+
+<p>
+Creates a new thread, pushes it on the stack,
+and returns a pointer to a <a href="#lua_State"><code>lua_State</code></a> that represents this new thread.
+The new thread returned by this function shares with the original thread
+its global environment,
+but has an independent execution stack.
+
+
+<p>
+Threads are subject to garbage collection,
+like any Lua object.
+
+
+
+
+
+<hr><h3><a name="lua_newuserdatauv"><code>lua_newuserdatauv</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue);</pre>
+
+<p>
+This function creates and pushes on the stack a new full userdata,
+with <code>nuvalue</code> associated Lua values, called <code>user values</code>,
+plus an associated block of raw memory with <code>size</code> bytes.
+(The user values can be set and read with the functions
+<a href="#lua_setiuservalue"><code>lua_setiuservalue</code></a> and <a href="#lua_getiuservalue"><code>lua_getiuservalue</code></a>.)
+
+
+<p>
+The function returns the address of the block of memory.
+Lua ensures that this address is valid as long as
+the corresponding userdata is alive (see <a href="#2.5">&sect;2.5</a>).
+Moreover, if the userdata is marked for finalization (see <a href="#2.5.3">&sect;2.5.3</a>),
+its address is valid at least until the call to its finalizer.
+
+
+
+
+
+<hr><h3><a name="lua_next"><code>lua_next</code></a></h3><p>
+<span class="apii">[-1, +(2|0), <em>v</em>]</span>
+<pre>int lua_next (lua_State *L, int index);</pre>
+
+<p>
+Pops a key from the stack,
+and pushes a key&ndash;value pair from the table at the given index,
+the "next" pair after the given key.
+If there are no more elements in the table,
+then <a href="#lua_next"><code>lua_next</code></a> returns&nbsp;0 and pushes nothing.
+
+
+<p>
+A typical table traversal looks like this:
+
+<pre>
+ /* table is in the stack at index 't' */
+ lua_pushnil(L); /* first key */
+ while (lua_next(L, t) != 0) {
+ /* uses 'key' (at index -2) and 'value' (at index -1) */
+ printf("%s - %s\n",
+ lua_typename(L, lua_type(L, -2)),
+ lua_typename(L, lua_type(L, -1)));
+ /* removes 'value'; keeps 'key' for next iteration */
+ lua_pop(L, 1);
+ }
+</pre>
+
+<p>
+While traversing a table,
+avoid calling <a href="#lua_tolstring"><code>lua_tolstring</code></a> directly on a key,
+unless you know that the key is actually a string.
+Recall that <a href="#lua_tolstring"><code>lua_tolstring</code></a> may change
+the value at the given index;
+this confuses the next call to <a href="#lua_next"><code>lua_next</code></a>.
+
+
+<p>
+This function may raise an error if the given key
+is neither <b>nil</b> nor present in the table.
+See function <a href="#pdf-next"><code>next</code></a> for the caveats of modifying
+the table during its traversal.
+
+
+
+
+
+<hr><h3><a name="lua_Number"><code>lua_Number</code></a></h3>
+<pre>typedef ... lua_Number;</pre>
+
+<p>
+The type of floats in Lua.
+
+
+<p>
+By default this type is double,
+but that can be changed to a single float or a long double.
+(See <code>LUA_FLOAT_TYPE</code> in <code>luaconf.h</code>.)
+
+
+
+
+
+<hr><h3><a name="lua_numbertointeger"><code>lua_numbertointeger</code></a></h3>
+<pre>int lua_numbertointeger (lua_Number n, lua_Integer *p);</pre>
+
+<p>
+Tries to convert a Lua float to a Lua integer;
+the float <code>n</code> must have an integral value.
+If that value is within the range of Lua integers,
+it is converted to an integer and assigned to <code>*p</code>.
+The macro results in a boolean indicating whether the
+conversion was successful.
+(Note that this range test can be tricky to do
+correctly without this macro, due to rounding.)
+
+
+<p>
+This macro may evaluate its arguments more than once.
+
+
+
+
+
+<hr><h3><a name="lua_pcall"><code>lua_pcall</code></a></h3><p>
+<span class="apii">[-(nargs + 1), +(nresults|1), &ndash;]</span>
+<pre>int lua_pcall (lua_State *L, int nargs, int nresults, int msgh);</pre>
+
+<p>
+Calls a function (or a callable object) in protected mode.
+
+
+<p>
+Both <code>nargs</code> and <code>nresults</code> have the same meaning as
+in <a href="#lua_call"><code>lua_call</code></a>.
+If there are no errors during the call,
+<a href="#lua_pcall"><code>lua_pcall</code></a> behaves exactly like <a href="#lua_call"><code>lua_call</code></a>.
+However, if there is any error,
+<a href="#lua_pcall"><code>lua_pcall</code></a> catches it,
+pushes a single value on the stack (the error object),
+and returns an error code.
+Like <a href="#lua_call"><code>lua_call</code></a>,
+<a href="#lua_pcall"><code>lua_pcall</code></a> always removes the function
+and its arguments from the stack.
+
+
+<p>
+If <code>msgh</code> is 0,
+then the error object returned on the stack
+is exactly the original error object.
+Otherwise, <code>msgh</code> is the stack index of a
+<em>message handler</em>.
+(This index cannot be a pseudo-index.)
+In case of runtime errors,
+this handler will be called with the error object
+and its return value will be the object
+returned on the stack by <a href="#lua_pcall"><code>lua_pcall</code></a>.
+
+
+<p>
+Typically, the message handler is used to add more debug
+information to the error object, such as a stack traceback.
+Such information cannot be gathered after the return of <a href="#lua_pcall"><code>lua_pcall</code></a>,
+since by then the stack has unwound.
+
+
+<p>
+The <a href="#lua_pcall"><code>lua_pcall</code></a> function returns one of the following status codes:
+<a href="#pdf-LUA_OK"><code>LUA_OK</code></a>, <a href="#pdf-LUA_ERRRUN"><code>LUA_ERRRUN</code></a>, <a href="#pdf-LUA_ERRMEM"><code>LUA_ERRMEM</code></a>, or <a href="#pdf-LUA_ERRERR"><code>LUA_ERRERR</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_pcallk"><code>lua_pcallk</code></a></h3><p>
+<span class="apii">[-(nargs + 1), +(nresults|1), &ndash;]</span>
+<pre>int lua_pcallk (lua_State *L,
+ int nargs,
+ int nresults,
+ int msgh,
+ lua_KContext ctx,
+ lua_KFunction k);</pre>
+
+<p>
+This function behaves exactly like <a href="#lua_pcall"><code>lua_pcall</code></a>,
+except that it allows the called function to yield (see <a href="#4.5">&sect;4.5</a>).
+
+
+
+
+
+<hr><h3><a name="lua_pop"><code>lua_pop</code></a></h3><p>
+<span class="apii">[-n, +0, <em>e</em>]</span>
+<pre>void lua_pop (lua_State *L, int n);</pre>
+
+<p>
+Pops <code>n</code> elements from the stack.
+It is implemented as a macro over <a href="#lua_settop"><code>lua_settop</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_pushboolean"><code>lua_pushboolean</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>void lua_pushboolean (lua_State *L, int b);</pre>
+
+<p>
+Pushes a boolean value with value <code>b</code> onto the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushcclosure"><code>lua_pushcclosure</code></a></h3><p>
+<span class="apii">[-n, +1, <em>m</em>]</span>
+<pre>void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);</pre>
+
+<p>
+Pushes a new C&nbsp;closure onto the stack.
+This function receives a pointer to a C&nbsp;function
+and pushes onto the stack a Lua value of type <code>function</code> that,
+when called, invokes the corresponding C&nbsp;function.
+The parameter <code>n</code> tells how many upvalues this function will have
+(see <a href="#4.2">&sect;4.2</a>).
+
+
+<p>
+Any function to be callable by Lua must
+follow the correct protocol to receive its parameters
+and return its results (see <a href="#lua_CFunction"><code>lua_CFunction</code></a>).
+
+
+<p>
+When a C&nbsp;function is created,
+it is possible to associate some values with it,
+the so called upvalues;
+these upvalues are then accessible to the function whenever it is called.
+This association is called a C&nbsp;closure (see <a href="#4.2">&sect;4.2</a>).
+To create a C&nbsp;closure,
+first the initial values for its upvalues must be pushed onto the stack.
+(When there are multiple upvalues, the first value is pushed first.)
+Then <a href="#lua_pushcclosure"><code>lua_pushcclosure</code></a>
+is called to create and push the C&nbsp;function onto the stack,
+with the argument <code>n</code> telling how many values will be
+associated with the function.
+<a href="#lua_pushcclosure"><code>lua_pushcclosure</code></a> also pops these values from the stack.
+
+
+<p>
+The maximum value for <code>n</code> is 255.
+
+
+<p>
+When <code>n</code> is zero,
+this function creates a <em>light C&nbsp;function</em>,
+which is just a pointer to the C&nbsp;function.
+In that case, it never raises a memory error.
+
+
+
+
+
+<hr><h3><a name="lua_pushcfunction"><code>lua_pushcfunction</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>void lua_pushcfunction (lua_State *L, lua_CFunction f);</pre>
+
+<p>
+Pushes a C&nbsp;function onto the stack.
+This function is equivalent to <a href="#lua_pushcclosure"><code>lua_pushcclosure</code></a> with no upvalues.
+
+
+
+
+
+<hr><h3><a name="lua_pushfstring"><code>lua_pushfstring</code></a></h3><p>
+<span class="apii">[-0, +1, <em>v</em>]</span>
+<pre>const char *lua_pushfstring (lua_State *L, const char *fmt, ...);</pre>
+
+<p>
+Pushes onto the stack a formatted string
+and returns a pointer to this string (see <a href="#4.1.3">&sect;4.1.3</a>).
+It is similar to the ISO&nbsp;C function <code>sprintf</code>,
+but has two important differences.
+First,
+you do not have to allocate space for the result;
+the result is a Lua string and Lua takes care of memory allocation
+(and deallocation, through garbage collection).
+Second,
+the conversion specifiers are quite restricted.
+There are no flags, widths, or precisions.
+The conversion specifiers can only be
+'<code>%%</code>' (inserts the character '<code>%</code>'),
+'<code>%s</code>' (inserts a zero-terminated string, with no size restrictions),
+'<code>%f</code>' (inserts a <a href="#lua_Number"><code>lua_Number</code></a>),
+'<code>%I</code>' (inserts a <a href="#lua_Integer"><code>lua_Integer</code></a>),
+'<code>%p</code>' (inserts a pointer),
+'<code>%d</code>' (inserts an <code>int</code>),
+'<code>%c</code>' (inserts an <code>int</code> as a one-byte character), and
+'<code>%U</code>' (inserts a <code>long int</code> as a UTF-8 byte sequence).
+
+
+<p>
+This function may raise errors due to memory overflow
+or an invalid conversion specifier.
+
+
+
+
+
+<hr><h3><a name="lua_pushglobaltable"><code>lua_pushglobaltable</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>void lua_pushglobaltable (lua_State *L);</pre>
+
+<p>
+Pushes the global environment onto the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushinteger"><code>lua_pushinteger</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>void lua_pushinteger (lua_State *L, lua_Integer n);</pre>
+
+<p>
+Pushes an integer with value <code>n</code> onto the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushlightuserdata"><code>lua_pushlightuserdata</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>void lua_pushlightuserdata (lua_State *L, void *p);</pre>
+
+<p>
+Pushes a light userdata onto the stack.
+
+
+<p>
+Userdata represent C&nbsp;values in Lua.
+A <em>light userdata</em> represents a pointer, a <code>void*</code>.
+It is a value (like a number):
+you do not create it, it has no individual metatable,
+and it is not collected (as it was never created).
+A light userdata is equal to "any"
+light userdata with the same C&nbsp;address.
+
+
+
+
+
+<hr><h3><a name="lua_pushliteral"><code>lua_pushliteral</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>const char *lua_pushliteral (lua_State *L, const char *s);</pre>
+
+<p>
+This macro is equivalent to <a href="#lua_pushstring"><code>lua_pushstring</code></a>,
+but should be used only when <code>s</code> is a literal string.
+(Lua may optimize this case.)
+
+
+
+
+
+<hr><h3><a name="lua_pushlstring"><code>lua_pushlstring</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>const char *lua_pushlstring (lua_State *L, const char *s, size_t len);</pre>
+
+<p>
+Pushes the string pointed to by <code>s</code> with size <code>len</code>
+onto the stack.
+Lua will make or reuse an internal copy of the given string,
+so the memory at <code>s</code> can be freed or reused immediately after
+the function returns.
+The string can contain any binary data,
+including embedded zeros.
+
+
+<p>
+Returns a pointer to the internal copy of the string (see <a href="#4.1.3">&sect;4.1.3</a>).
+
+
+
+
+
+<hr><h3><a name="lua_pushnil"><code>lua_pushnil</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>void lua_pushnil (lua_State *L);</pre>
+
+<p>
+Pushes a nil value onto the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushnumber"><code>lua_pushnumber</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>void lua_pushnumber (lua_State *L, lua_Number n);</pre>
+
+<p>
+Pushes a float with value <code>n</code> onto the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushstring"><code>lua_pushstring</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>const char *lua_pushstring (lua_State *L, const char *s);</pre>
+
+<p>
+Pushes the zero-terminated string pointed to by <code>s</code>
+onto the stack.
+Lua will make or reuse an internal copy of the given string,
+so the memory at <code>s</code> can be freed or reused immediately after
+the function returns.
+
+
+<p>
+Returns a pointer to the internal copy of the string (see <a href="#4.1.3">&sect;4.1.3</a>).
+
+
+<p>
+If <code>s</code> is <code>NULL</code>, pushes <b>nil</b> and returns <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_pushthread"><code>lua_pushthread</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>int lua_pushthread (lua_State *L);</pre>
+
+<p>
+Pushes the thread represented by <code>L</code> onto the stack.
+Returns 1 if this thread is the main thread of its state.
+
+
+
+
+
+<hr><h3><a name="lua_pushvalue"><code>lua_pushvalue</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>void lua_pushvalue (lua_State *L, int index);</pre>
+
+<p>
+Pushes a copy of the element at the given index
+onto the stack.
+
+
+
+
+
+<hr><h3><a name="lua_pushvfstring"><code>lua_pushvfstring</code></a></h3><p>
+<span class="apii">[-0, +1, <em>v</em>]</span>
+<pre>const char *lua_pushvfstring (lua_State *L,
+ const char *fmt,
+ va_list argp);</pre>
+
+<p>
+Equivalent to <a href="#lua_pushfstring"><code>lua_pushfstring</code></a>, except that it receives a <code>va_list</code>
+instead of a variable number of arguments.
+
+
+
+
+
+<hr><h3><a name="lua_rawequal"><code>lua_rawequal</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_rawequal (lua_State *L, int index1, int index2);</pre>
+
+<p>
+Returns 1 if the two values in indices <code>index1</code> and
+<code>index2</code> are primitively equal
+(that is, equal without calling the <code>__eq</code> metamethod).
+Otherwise returns&nbsp;0.
+Also returns&nbsp;0 if any of the indices are not valid.
+
+
+
+
+
+<hr><h3><a name="lua_rawget"><code>lua_rawget</code></a></h3><p>
+<span class="apii">[-1, +1, &ndash;]</span>
+<pre>int lua_rawget (lua_State *L, int index);</pre>
+
+<p>
+Similar to <a href="#lua_gettable"><code>lua_gettable</code></a>, but does a raw access
+(i.e., without metamethods).
+The value at <code>index</code> must be a table.
+
+
+
+
+
+<hr><h3><a name="lua_rawgeti"><code>lua_rawgeti</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>int lua_rawgeti (lua_State *L, int index, lua_Integer n);</pre>
+
+<p>
+Pushes onto the stack the value <code>t[n]</code>,
+where <code>t</code> is the table at the given index.
+The access is raw,
+that is, it does not use the <code>__index</code> metavalue.
+
+
+<p>
+Returns the type of the pushed value.
+
+
+
+
+
+<hr><h3><a name="lua_rawgetp"><code>lua_rawgetp</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>int lua_rawgetp (lua_State *L, int index, const void *p);</pre>
+
+<p>
+Pushes onto the stack the value <code>t[k]</code>,
+where <code>t</code> is the table at the given index and
+<code>k</code> is the pointer <code>p</code> represented as a light userdata.
+The access is raw;
+that is, it does not use the <code>__index</code> metavalue.
+
+
+<p>
+Returns the type of the pushed value.
+
+
+
+
+
+<hr><h3><a name="lua_rawlen"><code>lua_rawlen</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_Unsigned lua_rawlen (lua_State *L, int index);</pre>
+
+<p>
+Returns the raw "length" of the value at the given index:
+for strings, this is the string length;
+for tables, this is the result of the length operator ('<code>#</code>')
+with no metamethods;
+for userdata, this is the size of the block of memory allocated
+for the userdata.
+For other values, this call returns&nbsp;0.
+
+
+
+
+
+<hr><h3><a name="lua_rawset"><code>lua_rawset</code></a></h3><p>
+<span class="apii">[-2, +0, <em>m</em>]</span>
+<pre>void lua_rawset (lua_State *L, int index);</pre>
+
+<p>
+Similar to <a href="#lua_settable"><code>lua_settable</code></a>, but does a raw assignment
+(i.e., without metamethods).
+The value at <code>index</code> must be a table.
+
+
+
+
+
+<hr><h3><a name="lua_rawseti"><code>lua_rawseti</code></a></h3><p>
+<span class="apii">[-1, +0, <em>m</em>]</span>
+<pre>void lua_rawseti (lua_State *L, int index, lua_Integer i);</pre>
+
+<p>
+Does the equivalent of <code>t[i] = v</code>,
+where <code>t</code> is the table at the given index
+and <code>v</code> is the value on the top of the stack.
+
+
+<p>
+This function pops the value from the stack.
+The assignment is raw,
+that is, it does not use the <code>__newindex</code> metavalue.
+
+
+
+
+
+<hr><h3><a name="lua_rawsetp"><code>lua_rawsetp</code></a></h3><p>
+<span class="apii">[-1, +0, <em>m</em>]</span>
+<pre>void lua_rawsetp (lua_State *L, int index, const void *p);</pre>
+
+<p>
+Does the equivalent of <code>t[p] = v</code>,
+where <code>t</code> is the table at the given index,
+<code>p</code> is encoded as a light userdata,
+and <code>v</code> is the value on the top of the stack.
+
+
+<p>
+This function pops the value from the stack.
+The assignment is raw,
+that is, it does not use the <code>__newindex</code> metavalue.
+
+
+
+
+
+<hr><h3><a name="lua_Reader"><code>lua_Reader</code></a></h3>
+<pre>typedef const char * (*lua_Reader) (lua_State *L,
+ void *data,
+ size_t *size);</pre>
+
+<p>
+The reader function used by <a href="#lua_load"><code>lua_load</code></a>.
+Every time <a href="#lua_load"><code>lua_load</code></a> needs another piece of the chunk,
+it calls the reader,
+passing along its <code>data</code> parameter.
+The reader must return a pointer to a block of memory
+with a new piece of the chunk
+and set <code>size</code> to the block size.
+The block must exist until the reader function is called again.
+To signal the end of the chunk,
+the reader must return <code>NULL</code> or set <code>size</code> to zero.
+The reader function may return pieces of any size greater than zero.
+
+
+
+
+
+<hr><h3><a name="lua_register"><code>lua_register</code></a></h3><p>
+<span class="apii">[-0, +0, <em>e</em>]</span>
+<pre>void lua_register (lua_State *L, const char *name, lua_CFunction f);</pre>
+
+<p>
+Sets the C&nbsp;function <code>f</code> as the new value of global <code>name</code>.
+It is defined as a macro:
+
+<pre>
+ #define lua_register(L,n,f) \
+ (lua_pushcfunction(L, f), lua_setglobal(L, n))
+</pre>
+
+
+
+
+<hr><h3><a name="lua_remove"><code>lua_remove</code></a></h3><p>
+<span class="apii">[-1, +0, &ndash;]</span>
+<pre>void lua_remove (lua_State *L, int index);</pre>
+
+<p>
+Removes the element at the given valid index,
+shifting down the elements above this index to fill the gap.
+This function cannot be called with a pseudo-index,
+because a pseudo-index is not an actual stack position.
+
+
+
+
+
+<hr><h3><a name="lua_replace"><code>lua_replace</code></a></h3><p>
+<span class="apii">[-1, +0, &ndash;]</span>
+<pre>void lua_replace (lua_State *L, int index);</pre>
+
+<p>
+Moves the top element into the given valid index
+without shifting any element
+(therefore replacing the value at that given index),
+and then pops the top element.
+
+
+
+
+
+<hr><h3><a name="lua_resetthread"><code>lua_resetthread</code></a></h3><p>
+<span class="apii">[-0, +?, &ndash;]</span>
+<pre>int lua_resetthread (lua_State *L);</pre>
+
+<p>
+This function is deprecated;
+it is equivalent to <a href="#lua_closethread"><code>lua_closethread</code></a> with
+<code>from</code> being <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_resume"><code>lua_resume</code></a></h3><p>
+<span class="apii">[-?, +?, &ndash;]</span>
+<pre>int lua_resume (lua_State *L, lua_State *from, int nargs,
+ int *nresults);</pre>
+
+<p>
+Starts and resumes a coroutine in the given thread <code>L</code>.
+
+
+<p>
+To start a coroutine,
+you push the main function plus any arguments
+onto the empty stack of the thread.
+then you call <a href="#lua_resume"><code>lua_resume</code></a>,
+with <code>nargs</code> being the number of arguments.
+This call returns when the coroutine suspends or finishes its execution.
+When it returns,
+<code>*nresults</code> is updated and
+the top of the stack contains
+the <code>*nresults</code> values passed to <a href="#lua_yield"><code>lua_yield</code></a>
+or returned by the body function.
+<a href="#lua_resume"><code>lua_resume</code></a> returns
+<a href="#pdf-LUA_YIELD"><code>LUA_YIELD</code></a> if the coroutine yields,
+<a href="#pdf-LUA_OK"><code>LUA_OK</code></a> if the coroutine finishes its execution
+without errors,
+or an error code in case of errors (see <a href="#4.4.1">&sect;4.4.1</a>).
+In case of errors,
+the error object is on the top of the stack.
+
+
+<p>
+To resume a coroutine,
+you remove the <code>*nresults</code> yielded values from its stack,
+push the values to be passed as results from <code>yield</code>,
+and then call <a href="#lua_resume"><code>lua_resume</code></a>.
+
+
+<p>
+The parameter <code>from</code> represents the coroutine that is resuming <code>L</code>.
+If there is no such coroutine,
+this parameter can be <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_rotate"><code>lua_rotate</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void lua_rotate (lua_State *L, int idx, int n);</pre>
+
+<p>
+Rotates the stack elements between the valid index <code>idx</code>
+and the top of the stack.
+The elements are rotated <code>n</code> positions in the direction of the top,
+for a positive <code>n</code>,
+or <code>-n</code> positions in the direction of the bottom,
+for a negative <code>n</code>.
+The absolute value of <code>n</code> must not be greater than the size
+of the slice being rotated.
+This function cannot be called with a pseudo-index,
+because a pseudo-index is not an actual stack position.
+
+
+
+
+
+<hr><h3><a name="lua_setallocf"><code>lua_setallocf</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);</pre>
+
+<p>
+Changes the allocator function of a given state to <code>f</code>
+with user data <code>ud</code>.
+
+
+
+
+
+<hr><h3><a name="lua_setfield"><code>lua_setfield</code></a></h3><p>
+<span class="apii">[-1, +0, <em>e</em>]</span>
+<pre>void lua_setfield (lua_State *L, int index, const char *k);</pre>
+
+<p>
+Does the equivalent to <code>t[k] = v</code>,
+where <code>t</code> is the value at the given index
+and <code>v</code> is the value on the top of the stack.
+
+
+<p>
+This function pops the value from the stack.
+As in Lua, this function may trigger a metamethod
+for the "newindex" event (see <a href="#2.4">&sect;2.4</a>).
+
+
+
+
+
+<hr><h3><a name="lua_setglobal"><code>lua_setglobal</code></a></h3><p>
+<span class="apii">[-1, +0, <em>e</em>]</span>
+<pre>void lua_setglobal (lua_State *L, const char *name);</pre>
+
+<p>
+Pops a value from the stack and
+sets it as the new value of global <code>name</code>.
+
+
+
+
+
+<hr><h3><a name="lua_seti"><code>lua_seti</code></a></h3><p>
+<span class="apii">[-1, +0, <em>e</em>]</span>
+<pre>void lua_seti (lua_State *L, int index, lua_Integer n);</pre>
+
+<p>
+Does the equivalent to <code>t[n] = v</code>,
+where <code>t</code> is the value at the given index
+and <code>v</code> is the value on the top of the stack.
+
+
+<p>
+This function pops the value from the stack.
+As in Lua, this function may trigger a metamethod
+for the "newindex" event (see <a href="#2.4">&sect;2.4</a>).
+
+
+
+
+
+<hr><h3><a name="lua_setiuservalue"><code>lua_setiuservalue</code></a></h3><p>
+<span class="apii">[-1, +0, &ndash;]</span>
+<pre>int lua_setiuservalue (lua_State *L, int index, int n);</pre>
+
+<p>
+Pops a value from the stack and sets it as
+the new <code>n</code>-th user value associated to the
+full userdata at the given index.
+Returns 0 if the userdata does not have that value.
+
+
+
+
+
+<hr><h3><a name="lua_setmetatable"><code>lua_setmetatable</code></a></h3><p>
+<span class="apii">[-1, +0, &ndash;]</span>
+<pre>int lua_setmetatable (lua_State *L, int index);</pre>
+
+<p>
+Pops a table or <b>nil</b> from the stack and
+sets that value as the new metatable for the value at the given index.
+(<b>nil</b> means no metatable.)
+
+
+<p>
+(For historical reasons, this function returns an <code>int</code>,
+which now is always 1.)
+
+
+
+
+
+<hr><h3><a name="lua_settable"><code>lua_settable</code></a></h3><p>
+<span class="apii">[-2, +0, <em>e</em>]</span>
+<pre>void lua_settable (lua_State *L, int index);</pre>
+
+<p>
+Does the equivalent to <code>t[k] = v</code>,
+where <code>t</code> is the value at the given index,
+<code>v</code> is the value on the top of the stack,
+and <code>k</code> is the value just below the top.
+
+
+<p>
+This function pops both the key and the value from the stack.
+As in Lua, this function may trigger a metamethod
+for the "newindex" event (see <a href="#2.4">&sect;2.4</a>).
+
+
+
+
+
+<hr><h3><a name="lua_settop"><code>lua_settop</code></a></h3><p>
+<span class="apii">[-?, +?, <em>e</em>]</span>
+<pre>void lua_settop (lua_State *L, int index);</pre>
+
+<p>
+Accepts any index, or&nbsp;0,
+and sets the stack top to this index.
+If the new top is greater than the old one,
+then the new elements are filled with <b>nil</b>.
+If <code>index</code> is&nbsp;0, then all stack elements are removed.
+
+
+<p>
+This function can run arbitrary code when removing an index
+marked as to-be-closed from the stack.
+
+
+
+
+
+<hr><h3><a name="lua_setwarnf"><code>lua_setwarnf</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud);</pre>
+
+<p>
+Sets the warning function to be used by Lua to emit warnings
+(see <a href="#lua_WarnFunction"><code>lua_WarnFunction</code></a>).
+The <code>ud</code> parameter sets the value <code>ud</code> passed to
+the warning function.
+
+
+
+
+
+<hr><h3><a name="lua_State"><code>lua_State</code></a></h3>
+<pre>typedef struct lua_State lua_State;</pre>
+
+<p>
+An opaque structure that points to a thread and indirectly
+(through the thread) to the whole state of a Lua interpreter.
+The Lua library is fully reentrant:
+it has no global variables.
+All information about a state is accessible through this structure.
+
+
+<p>
+A pointer to this structure must be passed as the first argument to
+every function in the library, except to <a href="#lua_newstate"><code>lua_newstate</code></a>,
+which creates a Lua state from scratch.
+
+
+
+
+
+<hr><h3><a name="lua_status"><code>lua_status</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_status (lua_State *L);</pre>
+
+<p>
+Returns the status of the thread <code>L</code>.
+
+
+<p>
+The status can be <a href="#pdf-LUA_OK"><code>LUA_OK</code></a> for a normal thread,
+an error code if the thread finished the execution
+of a <a href="#lua_resume"><code>lua_resume</code></a> with an error,
+or <a href="#pdf-LUA_YIELD"><code>LUA_YIELD</code></a> if the thread is suspended.
+
+
+<p>
+You can call functions only in threads with status <a href="#pdf-LUA_OK"><code>LUA_OK</code></a>.
+You can resume threads with status <a href="#pdf-LUA_OK"><code>LUA_OK</code></a>
+(to start a new coroutine) or <a href="#pdf-LUA_YIELD"><code>LUA_YIELD</code></a>
+(to resume a coroutine).
+
+
+
+
+
+<hr><h3><a name="lua_stringtonumber"><code>lua_stringtonumber</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>size_t lua_stringtonumber (lua_State *L, const char *s);</pre>
+
+<p>
+Converts the zero-terminated string <code>s</code> to a number,
+pushes that number into the stack,
+and returns the total size of the string,
+that is, its length plus one.
+The conversion can result in an integer or a float,
+according to the lexical conventions of Lua (see <a href="#3.1">&sect;3.1</a>).
+The string may have leading and trailing whitespaces and a sign.
+If the string is not a valid numeral,
+returns 0 and pushes nothing.
+(Note that the result can be used as a boolean,
+true if the conversion succeeds.)
+
+
+
+
+
+<hr><h3><a name="lua_toboolean"><code>lua_toboolean</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_toboolean (lua_State *L, int index);</pre>
+
+<p>
+Converts the Lua value at the given index to a C&nbsp;boolean
+value (0&nbsp;or&nbsp;1).
+Like all tests in Lua,
+<a href="#lua_toboolean"><code>lua_toboolean</code></a> returns true for any Lua value
+different from <b>false</b> and <b>nil</b>;
+otherwise it returns false.
+(If you want to accept only actual boolean values,
+use <a href="#lua_isboolean"><code>lua_isboolean</code></a> to test the value's type.)
+
+
+
+
+
+<hr><h3><a name="lua_tocfunction"><code>lua_tocfunction</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_CFunction lua_tocfunction (lua_State *L, int index);</pre>
+
+<p>
+Converts a value at the given index to a C&nbsp;function.
+That value must be a C&nbsp;function;
+otherwise, returns <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_toclose"><code>lua_toclose</code></a></h3><p>
+<span class="apii">[-0, +0, <em>m</em>]</span>
+<pre>void lua_toclose (lua_State *L, int index);</pre>
+
+<p>
+Marks the given index in the stack as a
+to-be-closed slot (see <a href="#3.3.8">&sect;3.3.8</a>).
+Like a to-be-closed variable in Lua,
+the value at that slot in the stack will be closed
+when it goes out of scope.
+Here, in the context of a C function,
+to go out of scope means that the running function returns to Lua,
+or there is an error,
+or the slot is removed from the stack through
+<a href="#lua_settop"><code>lua_settop</code></a> or <a href="#lua_pop"><code>lua_pop</code></a>,
+or there is a call to <a href="#lua_closeslot"><code>lua_closeslot</code></a>.
+A slot marked as to-be-closed should not be removed from the stack
+by any other function in the API except <a href="#lua_settop"><code>lua_settop</code></a> or <a href="#lua_pop"><code>lua_pop</code></a>,
+unless previously deactivated by <a href="#lua_closeslot"><code>lua_closeslot</code></a>.
+
+
+<p>
+This function should not be called for an index
+that is equal to or below an active to-be-closed slot.
+
+
+<p>
+Note that, both in case of errors and of a regular return,
+by the time the <code>__close</code> metamethod runs,
+the C&nbsp;stack was already unwound,
+so that any automatic C&nbsp;variable declared in the calling function
+(e.g., a buffer) will be out of scope.
+
+
+
+
+
+<hr><h3><a name="lua_tointeger"><code>lua_tointeger</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_Integer lua_tointeger (lua_State *L, int index);</pre>
+
+<p>
+Equivalent to <a href="#lua_tointegerx"><code>lua_tointegerx</code></a> with <code>isnum</code> equal to <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_tointegerx"><code>lua_tointegerx</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_Integer lua_tointegerx (lua_State *L, int index, int *isnum);</pre>
+
+<p>
+Converts the Lua value at the given index
+to the signed integral type <a href="#lua_Integer"><code>lua_Integer</code></a>.
+The Lua value must be an integer,
+or a number or string convertible to an integer (see <a href="#3.4.3">&sect;3.4.3</a>);
+otherwise, <code>lua_tointegerx</code> returns&nbsp;0.
+
+
+<p>
+If <code>isnum</code> is not <code>NULL</code>,
+its referent is assigned a boolean value that
+indicates whether the operation succeeded.
+
+
+
+
+
+<hr><h3><a name="lua_tolstring"><code>lua_tolstring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>m</em>]</span>
+<pre>const char *lua_tolstring (lua_State *L, int index, size_t *len);</pre>
+
+<p>
+Converts the Lua value at the given index to a C&nbsp;string.
+If <code>len</code> is not <code>NULL</code>,
+it sets <code>*len</code> with the string length.
+The Lua value must be a string or a number;
+otherwise, the function returns <code>NULL</code>.
+If the value is a number,
+then <code>lua_tolstring</code> also
+<em>changes the actual value in the stack to a string</em>.
+(This change confuses <a href="#lua_next"><code>lua_next</code></a>
+when <code>lua_tolstring</code> is applied to keys during a table traversal.)
+
+
+<p>
+<code>lua_tolstring</code> returns a pointer
+to a string inside the Lua state (see <a href="#4.1.3">&sect;4.1.3</a>).
+This string always has a zero ('<code>\0</code>')
+after its last character (as in&nbsp;C),
+but can contain other zeros in its body.
+
+
+
+
+
+<hr><h3><a name="lua_tonumber"><code>lua_tonumber</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_Number lua_tonumber (lua_State *L, int index);</pre>
+
+<p>
+Equivalent to <a href="#lua_tonumberx"><code>lua_tonumberx</code></a> with <code>isnum</code> equal to <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_tonumberx"><code>lua_tonumberx</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_Number lua_tonumberx (lua_State *L, int index, int *isnum);</pre>
+
+<p>
+Converts the Lua value at the given index
+to the C&nbsp;type <a href="#lua_Number"><code>lua_Number</code></a> (see <a href="#lua_Number"><code>lua_Number</code></a>).
+The Lua value must be a number or a string convertible to a number
+(see <a href="#3.4.3">&sect;3.4.3</a>);
+otherwise, <a href="#lua_tonumberx"><code>lua_tonumberx</code></a> returns&nbsp;0.
+
+
+<p>
+If <code>isnum</code> is not <code>NULL</code>,
+its referent is assigned a boolean value that
+indicates whether the operation succeeded.
+
+
+
+
+
+<hr><h3><a name="lua_topointer"><code>lua_topointer</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>const void *lua_topointer (lua_State *L, int index);</pre>
+
+<p>
+Converts the value at the given index to a generic
+C&nbsp;pointer (<code>void*</code>).
+The value can be a userdata, a table, a thread, a string, or a function;
+otherwise, <code>lua_topointer</code> returns <code>NULL</code>.
+Different objects will give different pointers.
+There is no way to convert the pointer back to its original value.
+
+
+<p>
+Typically this function is used only for hashing and debug information.
+
+
+
+
+
+<hr><h3><a name="lua_tostring"><code>lua_tostring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>m</em>]</span>
+<pre>const char *lua_tostring (lua_State *L, int index);</pre>
+
+<p>
+Equivalent to <a href="#lua_tolstring"><code>lua_tolstring</code></a> with <code>len</code> equal to <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_tothread"><code>lua_tothread</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_State *lua_tothread (lua_State *L, int index);</pre>
+
+<p>
+Converts the value at the given index to a Lua thread
+(represented as <code>lua_State*</code>).
+This value must be a thread;
+otherwise, the function returns <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_touserdata"><code>lua_touserdata</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void *lua_touserdata (lua_State *L, int index);</pre>
+
+<p>
+If the value at the given index is a full userdata,
+returns its memory-block address.
+If the value is a light userdata,
+returns its value (a pointer).
+Otherwise, returns <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="lua_type"><code>lua_type</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_type (lua_State *L, int index);</pre>
+
+<p>
+Returns the type of the value in the given valid index,
+or <code>LUA_TNONE</code> for a non-valid but acceptable index.
+The types returned by <a href="#lua_type"><code>lua_type</code></a> are coded by the following constants
+defined in <code>lua.h</code>:
+<a name="pdf-LUA_TNIL"><code>LUA_TNIL</code></a>,
+<a name="pdf-LUA_TNUMBER"><code>LUA_TNUMBER</code></a>,
+<a name="pdf-LUA_TBOOLEAN"><code>LUA_TBOOLEAN</code></a>,
+<a name="pdf-LUA_TSTRING"><code>LUA_TSTRING</code></a>,
+<a name="pdf-LUA_TTABLE"><code>LUA_TTABLE</code></a>,
+<a name="pdf-LUA_TFUNCTION"><code>LUA_TFUNCTION</code></a>,
+<a name="pdf-LUA_TUSERDATA"><code>LUA_TUSERDATA</code></a>,
+<a name="pdf-LUA_TTHREAD"><code>LUA_TTHREAD</code></a>,
+and
+<a name="pdf-LUA_TLIGHTUSERDATA"><code>LUA_TLIGHTUSERDATA</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_typename"><code>lua_typename</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>const char *lua_typename (lua_State *L, int tp);</pre>
+
+<p>
+Returns the name of the type encoded by the value <code>tp</code>,
+which must be one the values returned by <a href="#lua_type"><code>lua_type</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_Unsigned"><code>lua_Unsigned</code></a></h3>
+<pre>typedef ... lua_Unsigned;</pre>
+
+<p>
+The unsigned version of <a href="#lua_Integer"><code>lua_Integer</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_upvalueindex"><code>lua_upvalueindex</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_upvalueindex (int i);</pre>
+
+<p>
+Returns the pseudo-index that represents the <code>i</code>-th upvalue of
+the running function (see <a href="#4.2">&sect;4.2</a>).
+<code>i</code> must be in the range <em>[1,256]</em>.
+
+
+
+
+
+<hr><h3><a name="lua_version"><code>lua_version</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_Number lua_version (lua_State *L);</pre>
+
+<p>
+Returns the version number of this core.
+
+
+
+
+
+<hr><h3><a name="lua_WarnFunction"><code>lua_WarnFunction</code></a></h3>
+<pre>typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont);</pre>
+
+<p>
+The type of warning functions, called by Lua to emit warnings.
+The first parameter is an opaque pointer
+set by <a href="#lua_setwarnf"><code>lua_setwarnf</code></a>.
+The second parameter is the warning message.
+The third parameter is a boolean that
+indicates whether the message is
+to be continued by the message in the next call.
+
+
+<p>
+See <a href="#pdf-warn"><code>warn</code></a> for more details about warnings.
+
+
+
+
+
+<hr><h3><a name="lua_warning"><code>lua_warning</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void lua_warning (lua_State *L, const char *msg, int tocont);</pre>
+
+<p>
+Emits a warning with the given message.
+A message in a call with <code>tocont</code> true should be
+continued in another call to this function.
+
+
+<p>
+See <a href="#pdf-warn"><code>warn</code></a> for more details about warnings.
+
+
+
+
+
+<hr><h3><a name="lua_Writer"><code>lua_Writer</code></a></h3>
+<pre>typedef int (*lua_Writer) (lua_State *L,
+ const void* p,
+ size_t sz,
+ void* ud);</pre>
+
+<p>
+The type of the writer function used by <a href="#lua_dump"><code>lua_dump</code></a>.
+Every time <a href="#lua_dump"><code>lua_dump</code></a> produces another piece of chunk,
+it calls the writer,
+passing along the buffer to be written (<code>p</code>),
+its size (<code>sz</code>),
+and the <code>ud</code> parameter supplied to <a href="#lua_dump"><code>lua_dump</code></a>.
+
+
+<p>
+The writer returns an error code:
+0&nbsp;means no errors;
+any other value means an error and stops <a href="#lua_dump"><code>lua_dump</code></a> from
+calling the writer again.
+
+
+
+
+
+<hr><h3><a name="lua_xmove"><code>lua_xmove</code></a></h3><p>
+<span class="apii">[-?, +?, &ndash;]</span>
+<pre>void lua_xmove (lua_State *from, lua_State *to, int n);</pre>
+
+<p>
+Exchange values between different threads of the same state.
+
+
+<p>
+This function pops <code>n</code> values from the stack <code>from</code>,
+and pushes them onto the stack <code>to</code>.
+
+
+
+
+
+<hr><h3><a name="lua_yield"><code>lua_yield</code></a></h3><p>
+<span class="apii">[-?, +?, <em>v</em>]</span>
+<pre>int lua_yield (lua_State *L, int nresults);</pre>
+
+<p>
+This function is equivalent to <a href="#lua_yieldk"><code>lua_yieldk</code></a>,
+but it has no continuation (see <a href="#4.5">&sect;4.5</a>).
+Therefore, when the thread resumes,
+it continues the function that called
+the function calling <code>lua_yield</code>.
+To avoid surprises,
+this function should be called only in a tail call.
+
+
+
+
+
+<hr><h3><a name="lua_yieldk"><code>lua_yieldk</code></a></h3><p>
+<span class="apii">[-?, +?, <em>v</em>]</span>
+<pre>int lua_yieldk (lua_State *L,
+ int nresults,
+ lua_KContext ctx,
+ lua_KFunction k);</pre>
+
+<p>
+Yields a coroutine (thread).
+
+
+<p>
+When a C&nbsp;function calls <a href="#lua_yieldk"><code>lua_yieldk</code></a>,
+the running coroutine suspends its execution,
+and the call to <a href="#lua_resume"><code>lua_resume</code></a> that started this coroutine returns.
+The parameter <code>nresults</code> is the number of values from the stack
+that will be passed as results to <a href="#lua_resume"><code>lua_resume</code></a>.
+
+
+<p>
+When the coroutine is resumed again,
+Lua calls the given continuation function <code>k</code> to continue
+the execution of the C&nbsp;function that yielded (see <a href="#4.5">&sect;4.5</a>).
+This continuation function receives the same stack
+from the previous function,
+with the <code>n</code> results removed and
+replaced by the arguments passed to <a href="#lua_resume"><code>lua_resume</code></a>.
+Moreover,
+the continuation function receives the value <code>ctx</code>
+that was passed to <a href="#lua_yieldk"><code>lua_yieldk</code></a>.
+
+
+<p>
+Usually, this function does not return;
+when the coroutine eventually resumes,
+it continues executing the continuation function.
+However, there is one special case,
+which is when this function is called
+from inside a line or a count hook (see <a href="#4.7">&sect;4.7</a>).
+In that case, <code>lua_yieldk</code> should be called with no continuation
+(probably in the form of <a href="#lua_yield"><code>lua_yield</code></a>) and no results,
+and the hook should return immediately after the call.
+Lua will yield and,
+when the coroutine resumes again,
+it will continue the normal execution
+of the (Lua) function that triggered the hook.
+
+
+<p>
+This function can raise an error if it is called from a thread
+with a pending C call with no continuation function
+(what is called a <em>C-call boundary</em>),
+or it is called from a thread that is not running inside a resume
+(typically the main thread).
+
+
+
+
+
+
+
+<h2>4.7 &ndash; <a name="4.7">The Debug Interface</a></h2>
+
+<p>
+Lua has no built-in debugging facilities.
+Instead, it offers a special interface
+by means of functions and <em>hooks</em>.
+This interface allows the construction of different
+kinds of debuggers, profilers, and other tools
+that need "inside information" from the interpreter.
+
+
+
+<hr><h3><a name="lua_Debug"><code>lua_Debug</code></a></h3>
+<pre>typedef struct lua_Debug {
+ int event;
+ const char *name; /* (n) */
+ const char *namewhat; /* (n) */
+ const char *what; /* (S) */
+ const char *source; /* (S) */
+ size_t srclen; /* (S) */
+ int currentline; /* (l) */
+ int linedefined; /* (S) */
+ int lastlinedefined; /* (S) */
+ unsigned char nups; /* (u) number of upvalues */
+ unsigned char nparams; /* (u) number of parameters */
+ char isvararg; /* (u) */
+ char istailcall; /* (t) */
+ unsigned short ftransfer; /* (r) index of first value transferred */
+ unsigned short ntransfer; /* (r) number of transferred values */
+ char short_src[LUA_IDSIZE]; /* (S) */
+ /* private part */
+ <em>other fields</em>
+} lua_Debug;</pre>
+
+<p>
+A structure used to carry different pieces of
+information about a function or an activation record.
+<a href="#lua_getstack"><code>lua_getstack</code></a> fills only the private part
+of this structure, for later use.
+To fill the other fields of <a href="#lua_Debug"><code>lua_Debug</code></a> with useful information,
+you must call <a href="#lua_getinfo"><code>lua_getinfo</code></a> with an appropriate parameter.
+(Specifically, to get a field,
+you must add the letter between parentheses in the field's comment
+to the parameter <code>what</code> of <a href="#lua_getinfo"><code>lua_getinfo</code></a>.)
+
+
+<p>
+The fields of <a href="#lua_Debug"><code>lua_Debug</code></a> have the following meaning:
+
+<ul>
+
+<li><b><code>source</code>: </b>
+the source of the chunk that created the function.
+If <code>source</code> starts with a '<code>@</code>',
+it means that the function was defined in a file where
+the file name follows the '<code>@</code>'.
+If <code>source</code> starts with a '<code>=</code>',
+the remainder of its contents describes the source in a user-dependent manner.
+Otherwise,
+the function was defined in a string where
+<code>source</code> is that string.
+</li>
+
+<li><b><code>srclen</code>: </b>
+The length of the string <code>source</code>.
+</li>
+
+<li><b><code>short_src</code>: </b>
+a "printable" version of <code>source</code>, to be used in error messages.
+</li>
+
+<li><b><code>linedefined</code>: </b>
+the line number where the definition of the function starts.
+</li>
+
+<li><b><code>lastlinedefined</code>: </b>
+the line number where the definition of the function ends.
+</li>
+
+<li><b><code>what</code>: </b>
+the string <code>"Lua"</code> if the function is a Lua function,
+<code>"C"</code> if it is a C&nbsp;function,
+<code>"main"</code> if it is the main part of a chunk.
+</li>
+
+<li><b><code>currentline</code>: </b>
+the current line where the given function is executing.
+When no line information is available,
+<code>currentline</code> is set to -1.
+</li>
+
+<li><b><code>name</code>: </b>
+a reasonable name for the given function.
+Because functions in Lua are first-class values,
+they do not have a fixed name:
+some functions can be the value of multiple global variables,
+while others can be stored only in a table field.
+The <code>lua_getinfo</code> function checks how the function was
+called to find a suitable name.
+If it cannot find a name,
+then <code>name</code> is set to <code>NULL</code>.
+</li>
+
+<li><b><code>namewhat</code>: </b>
+explains the <code>name</code> field.
+The value of <code>namewhat</code> can be
+<code>"global"</code>, <code>"local"</code>, <code>"method"</code>,
+<code>"field"</code>, <code>"upvalue"</code>, or <code>""</code> (the empty string),
+according to how the function was called.
+(Lua uses the empty string when no other option seems to apply.)
+</li>
+
+<li><b><code>istailcall</code>: </b>
+true if this function invocation was called by a tail call.
+In this case, the caller of this level is not in the stack.
+</li>
+
+<li><b><code>nups</code>: </b>
+the number of upvalues of the function.
+</li>
+
+<li><b><code>nparams</code>: </b>
+the number of parameters of the function
+(always 0&nbsp;for C&nbsp;functions).
+</li>
+
+<li><b><code>isvararg</code>: </b>
+true if the function is a variadic function
+(always true for C&nbsp;functions).
+</li>
+
+<li><b><code>ftransfer</code>: </b>
+the index in the stack of the first value being "transferred",
+that is, parameters in a call or return values in a return.
+(The other values are in consecutive indices.)
+Using this index, you can access and modify these values
+through <a href="#lua_getlocal"><code>lua_getlocal</code></a> and <a href="#lua_setlocal"><code>lua_setlocal</code></a>.
+This field is only meaningful during a
+call hook, denoting the first parameter,
+or a return hook, denoting the first value being returned.
+(For call hooks, this value is always 1.)
+</li>
+
+<li><b><code>ntransfer</code>: </b>
+The number of values being transferred (see previous item).
+(For calls of Lua functions,
+this value is always equal to <code>nparams</code>.)
+</li>
+
+</ul>
+
+
+
+
+<hr><h3><a name="lua_gethook"><code>lua_gethook</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_Hook lua_gethook (lua_State *L);</pre>
+
+<p>
+Returns the current hook function.
+
+
+
+
+
+<hr><h3><a name="lua_gethookcount"><code>lua_gethookcount</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_gethookcount (lua_State *L);</pre>
+
+<p>
+Returns the current hook count.
+
+
+
+
+
+<hr><h3><a name="lua_gethookmask"><code>lua_gethookmask</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_gethookmask (lua_State *L);</pre>
+
+<p>
+Returns the current hook mask.
+
+
+
+
+
+<hr><h3><a name="lua_getinfo"><code>lua_getinfo</code></a></h3><p>
+<span class="apii">[-(0|1), +(0|1|2), <em>m</em>]</span>
+<pre>int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);</pre>
+
+<p>
+Gets information about a specific function or function invocation.
+
+
+<p>
+To get information about a function invocation,
+the parameter <code>ar</code> must be a valid activation record that was
+filled by a previous call to <a href="#lua_getstack"><code>lua_getstack</code></a> or
+given as argument to a hook (see <a href="#lua_Hook"><code>lua_Hook</code></a>).
+
+
+<p>
+To get information about a function, you push it onto the stack
+and start the <code>what</code> string with the character '<code>&gt;</code>'.
+(In that case,
+<code>lua_getinfo</code> pops the function from the top of the stack.)
+For instance, to know in which line a function <code>f</code> was defined,
+you can write the following code:
+
+<pre>
+ lua_Debug ar;
+ lua_getglobal(L, "f"); /* get global 'f' */
+ lua_getinfo(L, "&gt;S", &amp;ar);
+ printf("%d\n", ar.linedefined);
+</pre>
+
+<p>
+Each character in the string <code>what</code>
+selects some fields of the structure <code>ar</code> to be filled or
+a value to be pushed on the stack.
+(These characters are also documented in the declaration of
+the structure <a href="#lua_Debug"><code>lua_Debug</code></a>,
+between parentheses in the comments following each field.)
+
+<ul>
+
+<li><b>'<code>f</code>': </b>
+pushes onto the stack the function that is
+running at the given level;
+</li>
+
+<li><b>'<code>l</code>': </b> fills in the field <code>currentline</code>;
+</li>
+
+<li><b>'<code>n</code>': </b> fills in the fields <code>name</code> and <code>namewhat</code>;
+</li>
+
+<li><b>'<code>r</code>': </b> fills in the fields <code>ftransfer</code> and <code>ntransfer</code>;
+</li>
+
+<li><b>'<code>S</code>': </b>
+fills in the fields <code>source</code>, <code>short_src</code>,
+<code>linedefined</code>, <code>lastlinedefined</code>, and <code>what</code>;
+</li>
+
+<li><b>'<code>t</code>': </b> fills in the field <code>istailcall</code>;
+</li>
+
+<li><b>'<code>u</code>': </b> fills in the fields
+<code>nups</code>, <code>nparams</code>, and <code>isvararg</code>;
+</li>
+
+<li><b>'<code>L</code>': </b>
+pushes onto the stack a table whose indices are
+the lines on the function with some associated code,
+that is, the lines where you can put a break point.
+(Lines with no code include empty lines and comments.)
+If this option is given together with option '<code>f</code>',
+its table is pushed after the function.
+This is the only option that can raise a memory error.
+</li>
+
+</ul>
+
+<p>
+This function returns 0 to signal an invalid option in <code>what</code>;
+even then the valid options are handled correctly.
+
+
+
+
+
+<hr><h3><a name="lua_getlocal"><code>lua_getlocal</code></a></h3><p>
+<span class="apii">[-0, +(0|1), &ndash;]</span>
+<pre>const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);</pre>
+
+<p>
+Gets information about a local variable or a temporary value
+of a given activation record or a given function.
+
+
+<p>
+In the first case,
+the parameter <code>ar</code> must be a valid activation record that was
+filled by a previous call to <a href="#lua_getstack"><code>lua_getstack</code></a> or
+given as argument to a hook (see <a href="#lua_Hook"><code>lua_Hook</code></a>).
+The index <code>n</code> selects which local variable to inspect;
+see <a href="#pdf-debug.getlocal"><code>debug.getlocal</code></a> for details about variable indices
+and names.
+
+
+<p>
+<a href="#lua_getlocal"><code>lua_getlocal</code></a> pushes the variable's value onto the stack
+and returns its name.
+
+
+<p>
+In the second case, <code>ar</code> must be <code>NULL</code> and the function
+to be inspected must be on the top of the stack.
+In this case, only parameters of Lua functions are visible
+(as there is no information about what variables are active)
+and no values are pushed onto the stack.
+
+
+<p>
+Returns <code>NULL</code> (and pushes nothing)
+when the index is greater than
+the number of active local variables.
+
+
+
+
+
+<hr><h3><a name="lua_getstack"><code>lua_getstack</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>int lua_getstack (lua_State *L, int level, lua_Debug *ar);</pre>
+
+<p>
+Gets information about the interpreter runtime stack.
+
+
+<p>
+This function fills parts of a <a href="#lua_Debug"><code>lua_Debug</code></a> structure with
+an identification of the <em>activation record</em>
+of the function executing at a given level.
+Level&nbsp;0 is the current running function,
+whereas level <em>n+1</em> is the function that has called level <em>n</em>
+(except for tail calls, which do not count in the stack).
+When called with a level greater than the stack depth,
+<a href="#lua_getstack"><code>lua_getstack</code></a> returns 0;
+otherwise it returns 1.
+
+
+
+
+
+<hr><h3><a name="lua_getupvalue"><code>lua_getupvalue</code></a></h3><p>
+<span class="apii">[-0, +(0|1), &ndash;]</span>
+<pre>const char *lua_getupvalue (lua_State *L, int funcindex, int n);</pre>
+
+<p>
+Gets information about the <code>n</code>-th upvalue
+of the closure at index <code>funcindex</code>.
+It pushes the upvalue's value onto the stack
+and returns its name.
+Returns <code>NULL</code> (and pushes nothing)
+when the index <code>n</code> is greater than the number of upvalues.
+
+
+<p>
+See <a href="#pdf-debug.getupvalue"><code>debug.getupvalue</code></a> for more information about upvalues.
+
+
+
+
+
+<hr><h3><a name="lua_Hook"><code>lua_Hook</code></a></h3>
+<pre>typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);</pre>
+
+<p>
+Type for debugging hook functions.
+
+
+<p>
+Whenever a hook is called, its <code>ar</code> argument has its field
+<code>event</code> set to the specific event that triggered the hook.
+Lua identifies these events with the following constants:
+<a name="pdf-LUA_HOOKCALL"><code>LUA_HOOKCALL</code></a>, <a name="pdf-LUA_HOOKRET"><code>LUA_HOOKRET</code></a>,
+<a name="pdf-LUA_HOOKTAILCALL"><code>LUA_HOOKTAILCALL</code></a>, <a name="pdf-LUA_HOOKLINE"><code>LUA_HOOKLINE</code></a>,
+and <a name="pdf-LUA_HOOKCOUNT"><code>LUA_HOOKCOUNT</code></a>.
+Moreover, for line events, the field <code>currentline</code> is also set.
+To get the value of any other field in <code>ar</code>,
+the hook must call <a href="#lua_getinfo"><code>lua_getinfo</code></a>.
+
+
+<p>
+For call events, <code>event</code> can be <code>LUA_HOOKCALL</code>,
+the normal value, or <code>LUA_HOOKTAILCALL</code>, for a tail call;
+in this case, there will be no corresponding return event.
+
+
+<p>
+While Lua is running a hook, it disables other calls to hooks.
+Therefore, if a hook calls back Lua to execute a function or a chunk,
+this execution occurs without any calls to hooks.
+
+
+<p>
+Hook functions cannot have continuations,
+that is, they cannot call <a href="#lua_yieldk"><code>lua_yieldk</code></a>,
+<a href="#lua_pcallk"><code>lua_pcallk</code></a>, or <a href="#lua_callk"><code>lua_callk</code></a> with a non-null <code>k</code>.
+
+
+<p>
+Hook functions can yield under the following conditions:
+Only count and line events can yield;
+to yield, a hook function must finish its execution
+calling <a href="#lua_yield"><code>lua_yield</code></a> with <code>nresults</code> equal to zero
+(that is, with no values).
+
+
+
+
+
+<hr><h3><a name="lua_sethook"><code>lua_sethook</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void lua_sethook (lua_State *L, lua_Hook f, int mask, int count);</pre>
+
+<p>
+Sets the debugging hook function.
+
+
+<p>
+Argument <code>f</code> is the hook function.
+<code>mask</code> specifies on which events the hook will be called:
+it is formed by a bitwise OR of the constants
+<a name="pdf-LUA_MASKCALL"><code>LUA_MASKCALL</code></a>,
+<a name="pdf-LUA_MASKRET"><code>LUA_MASKRET</code></a>,
+<a name="pdf-LUA_MASKLINE"><code>LUA_MASKLINE</code></a>,
+and <a name="pdf-LUA_MASKCOUNT"><code>LUA_MASKCOUNT</code></a>.
+The <code>count</code> argument is only meaningful when the mask
+includes <code>LUA_MASKCOUNT</code>.
+For each event, the hook is called as explained below:
+
+<ul>
+
+<li><b>The call hook: </b> is called when the interpreter calls a function.
+The hook is called just after Lua enters the new function.
+</li>
+
+<li><b>The return hook: </b> is called when the interpreter returns from a function.
+The hook is called just before Lua leaves the function.
+</li>
+
+<li><b>The line hook: </b> is called when the interpreter is about to
+start the execution of a new line of code,
+or when it jumps back in the code (even to the same line).
+This event only happens while Lua is executing a Lua function.
+</li>
+
+<li><b>The count hook: </b> is called after the interpreter executes every
+<code>count</code> instructions.
+This event only happens while Lua is executing a Lua function.
+</li>
+
+</ul>
+
+<p>
+Hooks are disabled by setting <code>mask</code> to zero.
+
+
+
+
+
+<hr><h3><a name="lua_setlocal"><code>lua_setlocal</code></a></h3><p>
+<span class="apii">[-(0|1), +0, &ndash;]</span>
+<pre>const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);</pre>
+
+<p>
+Sets the value of a local variable of a given activation record.
+It assigns the value on the top of the stack
+to the variable and returns its name.
+It also pops the value from the stack.
+
+
+<p>
+Returns <code>NULL</code> (and pops nothing)
+when the index is greater than
+the number of active local variables.
+
+
+<p>
+Parameters <code>ar</code> and <code>n</code> are as in the function <a href="#lua_getlocal"><code>lua_getlocal</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_setupvalue"><code>lua_setupvalue</code></a></h3><p>
+<span class="apii">[-(0|1), +0, &ndash;]</span>
+<pre>const char *lua_setupvalue (lua_State *L, int funcindex, int n);</pre>
+
+<p>
+Sets the value of a closure's upvalue.
+It assigns the value on the top of the stack
+to the upvalue and returns its name.
+It also pops the value from the stack.
+
+
+<p>
+Returns <code>NULL</code> (and pops nothing)
+when the index <code>n</code> is greater than the number of upvalues.
+
+
+<p>
+Parameters <code>funcindex</code> and <code>n</code> are as in
+the function <a href="#lua_getupvalue"><code>lua_getupvalue</code></a>.
+
+
+
+
+
+<hr><h3><a name="lua_upvalueid"><code>lua_upvalueid</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void *lua_upvalueid (lua_State *L, int funcindex, int n);</pre>
+
+<p>
+Returns a unique identifier for the upvalue numbered <code>n</code>
+from the closure at index <code>funcindex</code>.
+
+
+<p>
+These unique identifiers allow a program to check whether different
+closures share upvalues.
+Lua closures that share an upvalue
+(that is, that access a same external local variable)
+will return identical ids for those upvalue indices.
+
+
+<p>
+Parameters <code>funcindex</code> and <code>n</code> are as in
+the function <a href="#lua_getupvalue"><code>lua_getupvalue</code></a>,
+but <code>n</code> cannot be greater than the number of upvalues.
+
+
+
+
+
+<hr><h3><a name="lua_upvaluejoin"><code>lua_upvaluejoin</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void lua_upvaluejoin (lua_State *L, int funcindex1, int n1,
+ int funcindex2, int n2);</pre>
+
+<p>
+Make the <code>n1</code>-th upvalue of the Lua closure at index <code>funcindex1</code>
+refer to the <code>n2</code>-th upvalue of the Lua closure at index <code>funcindex2</code>.
+
+
+
+
+
+
+
+<h1>5 &ndash; <a name="5">The Auxiliary Library</a></h1>
+
+
+
+<p>
+
+The <em>auxiliary library</em> provides several convenient functions
+to interface C with Lua.
+While the basic API provides the primitive functions for all
+interactions between C and Lua,
+the auxiliary library provides higher-level functions for some
+common tasks.
+
+
+<p>
+All functions and types from the auxiliary library
+are defined in header file <code>lauxlib.h</code> and
+have a prefix <code>luaL_</code>.
+
+
+<p>
+All functions in the auxiliary library are built on
+top of the basic API,
+and so they provide nothing that cannot be done with that API.
+Nevertheless, the use of the auxiliary library ensures
+more consistency to your code.
+
+
+<p>
+Several functions in the auxiliary library use internally some
+extra stack slots.
+When a function in the auxiliary library uses less than five slots,
+it does not check the stack size;
+it simply assumes that there are enough slots.
+
+
+<p>
+Several functions in the auxiliary library are used to
+check C&nbsp;function arguments.
+Because the error message is formatted for arguments
+(e.g., "<code>bad argument #1</code>"),
+you should not use these functions for other stack values.
+
+
+<p>
+Functions called <code>luaL_check*</code>
+always raise an error if the check is not satisfied.
+
+
+
+
+
+<h2>5.1 &ndash; <a name="5.1">Functions and Types</a></h2>
+
+<p>
+Here we list all functions and types from the auxiliary library
+in alphabetical order.
+
+
+
+<hr><h3><a name="luaL_addchar"><code>luaL_addchar</code></a></h3><p>
+<span class="apii">[-?, +?, <em>m</em>]</span>
+<pre>void luaL_addchar (luaL_Buffer *B, char c);</pre>
+
+<p>
+Adds the byte <code>c</code> to the buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_addgsub"><code>luaL_addgsub</code></a></h3><p>
+<span class="apii">[-?, +?, <em>m</em>]</span>
+<pre>const void luaL_addgsub (luaL_Buffer *B, const char *s,
+ const char *p, const char *r);</pre>
+
+<p>
+Adds a copy of the string <code>s</code> to the buffer <code>B</code> (see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>),
+replacing any occurrence of the string <code>p</code>
+with the string <code>r</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_addlstring"><code>luaL_addlstring</code></a></h3><p>
+<span class="apii">[-?, +?, <em>m</em>]</span>
+<pre>void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l);</pre>
+
+<p>
+Adds the string pointed to by <code>s</code> with length <code>l</code> to
+the buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+The string can contain embedded zeros.
+
+
+
+
+
+<hr><h3><a name="luaL_addsize"><code>luaL_addsize</code></a></h3><p>
+<span class="apii">[-?, +?, &ndash;]</span>
+<pre>void luaL_addsize (luaL_Buffer *B, size_t n);</pre>
+
+<p>
+Adds to the buffer <code>B</code>
+a string of length <code>n</code> previously copied to the
+buffer area (see <a href="#luaL_prepbuffer"><code>luaL_prepbuffer</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_addstring"><code>luaL_addstring</code></a></h3><p>
+<span class="apii">[-?, +?, <em>m</em>]</span>
+<pre>void luaL_addstring (luaL_Buffer *B, const char *s);</pre>
+
+<p>
+Adds the zero-terminated string pointed to by <code>s</code>
+to the buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_addvalue"><code>luaL_addvalue</code></a></h3><p>
+<span class="apii">[-?, +?, <em>m</em>]</span>
+<pre>void luaL_addvalue (luaL_Buffer *B);</pre>
+
+<p>
+Adds the value on the top of the stack
+to the buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+Pops the value.
+
+
+<p>
+This is the only function on string buffers that can (and must)
+be called with an extra element on the stack,
+which is the value to be added to the buffer.
+
+
+
+
+
+<hr><h3><a name="luaL_argcheck"><code>luaL_argcheck</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void luaL_argcheck (lua_State *L,
+ int cond,
+ int arg,
+ const char *extramsg);</pre>
+
+<p>
+Checks whether <code>cond</code> is true.
+If it is not, raises an error with a standard message (see <a href="#luaL_argerror"><code>luaL_argerror</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_argerror"><code>luaL_argerror</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>int luaL_argerror (lua_State *L, int arg, const char *extramsg);</pre>
+
+<p>
+Raises an error reporting a problem with argument <code>arg</code>
+of the C&nbsp;function that called it,
+using a standard message
+that includes <code>extramsg</code> as a comment:
+
+<pre>
+ bad argument #<em>arg</em> to '<em>funcname</em>' (<em>extramsg</em>)
+</pre><p>
+This function never returns.
+
+
+
+
+
+<hr><h3><a name="luaL_argexpected"><code>luaL_argexpected</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void luaL_argexpected (lua_State *L,
+ int cond,
+ int arg,
+ const char *tname);</pre>
+
+<p>
+Checks whether <code>cond</code> is true.
+If it is not, raises an error about the type of the argument <code>arg</code>
+with a standard message (see <a href="#luaL_typeerror"><code>luaL_typeerror</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_Buffer"><code>luaL_Buffer</code></a></h3>
+<pre>typedef struct luaL_Buffer luaL_Buffer;</pre>
+
+<p>
+Type for a <em>string buffer</em>.
+
+
+<p>
+A string buffer allows C&nbsp;code to build Lua strings piecemeal.
+Its pattern of use is as follows:
+
+<ul>
+
+<li>First declare a variable <code>b</code> of type <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>.</li>
+
+<li>Then initialize it with a call <code>luaL_buffinit(L, &amp;b)</code>.</li>
+
+<li>
+Then add string pieces to the buffer calling any of
+the <code>luaL_add*</code> functions.
+</li>
+
+<li>
+Finish by calling <code>luaL_pushresult(&amp;b)</code>.
+This call leaves the final string on the top of the stack.
+</li>
+
+</ul>
+
+<p>
+If you know beforehand the maximum size of the resulting string,
+you can use the buffer like this:
+
+<ul>
+
+<li>First declare a variable <code>b</code> of type <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>.</li>
+
+<li>Then initialize it and preallocate a space of
+size <code>sz</code> with a call <code>luaL_buffinitsize(L, &amp;b, sz)</code>.</li>
+
+<li>Then produce the string into that space.</li>
+
+<li>
+Finish by calling <code>luaL_pushresultsize(&amp;b, sz)</code>,
+where <code>sz</code> is the total size of the resulting string
+copied into that space (which may be less than or
+equal to the preallocated size).
+</li>
+
+</ul>
+
+<p>
+During its normal operation,
+a string buffer uses a variable number of stack slots.
+So, while using a buffer, you cannot assume that you know where
+the top of the stack is.
+You can use the stack between successive calls to buffer operations
+as long as that use is balanced;
+that is,
+when you call a buffer operation,
+the stack is at the same level
+it was immediately after the previous buffer operation.
+(The only exception to this rule is <a href="#luaL_addvalue"><code>luaL_addvalue</code></a>.)
+After calling <a href="#luaL_pushresult"><code>luaL_pushresult</code></a>,
+the stack is back to its level when the buffer was initialized,
+plus the final string on its top.
+
+
+
+
+
+<hr><h3><a name="luaL_buffaddr"><code>luaL_buffaddr</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>char *luaL_buffaddr (luaL_Buffer *B);</pre>
+
+<p>
+Returns the address of the current content of buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+Note that any addition to the buffer may invalidate this address.
+
+
+
+
+
+<hr><h3><a name="luaL_buffinit"><code>luaL_buffinit</code></a></h3><p>
+<span class="apii">[-0, +?, &ndash;]</span>
+<pre>void luaL_buffinit (lua_State *L, luaL_Buffer *B);</pre>
+
+<p>
+Initializes a buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+This function does not allocate any space;
+the buffer must be declared as a variable.
+
+
+
+
+
+<hr><h3><a name="luaL_bufflen"><code>luaL_bufflen</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>size_t luaL_bufflen (luaL_Buffer *B);</pre>
+
+<p>
+Returns the length of the current content of buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_buffinitsize"><code>luaL_buffinitsize</code></a></h3><p>
+<span class="apii">[-?, +?, <em>m</em>]</span>
+<pre>char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz);</pre>
+
+<p>
+Equivalent to the sequence
+<a href="#luaL_buffinit"><code>luaL_buffinit</code></a>, <a href="#luaL_prepbuffsize"><code>luaL_prepbuffsize</code></a>.
+
+
+
+
+
+<hr><h3><a name="luaL_buffsub"><code>luaL_buffsub</code></a></h3><p>
+<span class="apii">[-?, +?, &ndash;]</span>
+<pre>void luaL_buffsub (luaL_Buffer *B, int n);</pre>
+
+<p>
+Removes <code>n</code> bytes from the buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+The buffer must have at least that many bytes.
+
+
+
+
+
+<hr><h3><a name="luaL_callmeta"><code>luaL_callmeta</code></a></h3><p>
+<span class="apii">[-0, +(0|1), <em>e</em>]</span>
+<pre>int luaL_callmeta (lua_State *L, int obj, const char *e);</pre>
+
+<p>
+Calls a metamethod.
+
+
+<p>
+If the object at index <code>obj</code> has a metatable and this
+metatable has a field <code>e</code>,
+this function calls this field passing the object as its only argument.
+In this case this function returns true and pushes onto the
+stack the value returned by the call.
+If there is no metatable or no metamethod,
+this function returns false without pushing any value on the stack.
+
+
+
+
+
+<hr><h3><a name="luaL_checkany"><code>luaL_checkany</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void luaL_checkany (lua_State *L, int arg);</pre>
+
+<p>
+Checks whether the function has an argument
+of any type (including <b>nil</b>) at position <code>arg</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_checkinteger"><code>luaL_checkinteger</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>lua_Integer luaL_checkinteger (lua_State *L, int arg);</pre>
+
+<p>
+Checks whether the function argument <code>arg</code> is an integer
+(or can be converted to an integer)
+and returns this integer.
+
+
+
+
+
+<hr><h3><a name="luaL_checklstring"><code>luaL_checklstring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>const char *luaL_checklstring (lua_State *L, int arg, size_t *l);</pre>
+
+<p>
+Checks whether the function argument <code>arg</code> is a string
+and returns this string;
+if <code>l</code> is not <code>NULL</code> fills its referent
+with the string's length.
+
+
+<p>
+This function uses <a href="#lua_tolstring"><code>lua_tolstring</code></a> to get its result,
+so all conversions and caveats of that function apply here.
+
+
+
+
+
+<hr><h3><a name="luaL_checknumber"><code>luaL_checknumber</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>lua_Number luaL_checknumber (lua_State *L, int arg);</pre>
+
+<p>
+Checks whether the function argument <code>arg</code> is a number
+and returns this number converted to a <code>lua_Number</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_checkoption"><code>luaL_checkoption</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>int luaL_checkoption (lua_State *L,
+ int arg,
+ const char *def,
+ const char *const lst[]);</pre>
+
+<p>
+Checks whether the function argument <code>arg</code> is a string and
+searches for this string in the array <code>lst</code>
+(which must be NULL-terminated).
+Returns the index in the array where the string was found.
+Raises an error if the argument is not a string or
+if the string cannot be found.
+
+
+<p>
+If <code>def</code> is not <code>NULL</code>,
+the function uses <code>def</code> as a default value when
+there is no argument <code>arg</code> or when this argument is <b>nil</b>.
+
+
+<p>
+This is a useful function for mapping strings to C&nbsp;enums.
+(The usual convention in Lua libraries is
+to use strings instead of numbers to select options.)
+
+
+
+
+
+<hr><h3><a name="luaL_checkstack"><code>luaL_checkstack</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void luaL_checkstack (lua_State *L, int sz, const char *msg);</pre>
+
+<p>
+Grows the stack size to <code>top + sz</code> elements,
+raising an error if the stack cannot grow to that size.
+<code>msg</code> is an additional text to go into the error message
+(or <code>NULL</code> for no additional text).
+
+
+
+
+
+<hr><h3><a name="luaL_checkstring"><code>luaL_checkstring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>const char *luaL_checkstring (lua_State *L, int arg);</pre>
+
+<p>
+Checks whether the function argument <code>arg</code> is a string
+and returns this string.
+
+
+<p>
+This function uses <a href="#lua_tolstring"><code>lua_tolstring</code></a> to get its result,
+so all conversions and caveats of that function apply here.
+
+
+
+
+
+<hr><h3><a name="luaL_checktype"><code>luaL_checktype</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void luaL_checktype (lua_State *L, int arg, int t);</pre>
+
+<p>
+Checks whether the function argument <code>arg</code> has type <code>t</code>.
+See <a href="#lua_type"><code>lua_type</code></a> for the encoding of types for <code>t</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_checkudata"><code>luaL_checkudata</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void *luaL_checkudata (lua_State *L, int arg, const char *tname);</pre>
+
+<p>
+Checks whether the function argument <code>arg</code> is a userdata
+of the type <code>tname</code> (see <a href="#luaL_newmetatable"><code>luaL_newmetatable</code></a>) and
+returns the userdata's memory-block address (see <a href="#lua_touserdata"><code>lua_touserdata</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_checkversion"><code>luaL_checkversion</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>void luaL_checkversion (lua_State *L);</pre>
+
+<p>
+Checks whether the code making the call and the Lua library being called
+are using the same version of Lua and the same numeric types.
+
+
+
+
+
+<hr><h3><a name="luaL_dofile"><code>luaL_dofile</code></a></h3><p>
+<span class="apii">[-0, +?, <em>m</em>]</span>
+<pre>int luaL_dofile (lua_State *L, const char *filename);</pre>
+
+<p>
+Loads and runs the given file.
+It is defined as the following macro:
+
+<pre>
+ (luaL_loadfile(L, filename) || lua_pcall(L, 0, LUA_MULTRET, 0))
+</pre><p>
+It returns&nbsp;0 (<a href="#pdf-LUA_OK"><code>LUA_OK</code></a>) if there are no errors,
+or 1 in case of errors.
+
+
+
+
+
+<hr><h3><a name="luaL_dostring"><code>luaL_dostring</code></a></h3><p>
+<span class="apii">[-0, +?, &ndash;]</span>
+<pre>int luaL_dostring (lua_State *L, const char *str);</pre>
+
+<p>
+Loads and runs the given string.
+It is defined as the following macro:
+
+<pre>
+ (luaL_loadstring(L, str) || lua_pcall(L, 0, LUA_MULTRET, 0))
+</pre><p>
+It returns&nbsp;0 (<a href="#pdf-LUA_OK"><code>LUA_OK</code></a>) if there are no errors,
+or 1 in case of errors.
+
+
+
+
+
+<hr><h3><a name="luaL_error"><code>luaL_error</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>int luaL_error (lua_State *L, const char *fmt, ...);</pre>
+
+<p>
+Raises an error.
+The error message format is given by <code>fmt</code>
+plus any extra arguments,
+following the same rules of <a href="#lua_pushfstring"><code>lua_pushfstring</code></a>.
+It also adds at the beginning of the message the file name and
+the line number where the error occurred,
+if this information is available.
+
+
+<p>
+This function never returns,
+but it is an idiom to use it in C&nbsp;functions
+as <code>return luaL_error(<em>args</em>)</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_execresult"><code>luaL_execresult</code></a></h3><p>
+<span class="apii">[-0, +3, <em>m</em>]</span>
+<pre>int luaL_execresult (lua_State *L, int stat);</pre>
+
+<p>
+This function produces the return values for
+process-related functions in the standard library
+(<a href="#pdf-os.execute"><code>os.execute</code></a> and <a href="#pdf-io.close"><code>io.close</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_fileresult"><code>luaL_fileresult</code></a></h3><p>
+<span class="apii">[-0, +(1|3), <em>m</em>]</span>
+<pre>int luaL_fileresult (lua_State *L, int stat, const char *fname);</pre>
+
+<p>
+This function produces the return values for
+file-related functions in the standard library
+(<a href="#pdf-io.open"><code>io.open</code></a>, <a href="#pdf-os.rename"><code>os.rename</code></a>, <a href="#pdf-file:seek"><code>file:seek</code></a>, etc.).
+
+
+
+
+
+<hr><h3><a name="luaL_getmetafield"><code>luaL_getmetafield</code></a></h3><p>
+<span class="apii">[-0, +(0|1), <em>m</em>]</span>
+<pre>int luaL_getmetafield (lua_State *L, int obj, const char *e);</pre>
+
+<p>
+Pushes onto the stack the field <code>e</code> from the metatable
+of the object at index <code>obj</code> and returns the type of the pushed value.
+If the object does not have a metatable,
+or if the metatable does not have this field,
+pushes nothing and returns <code>LUA_TNIL</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_getmetatable"><code>luaL_getmetatable</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>int luaL_getmetatable (lua_State *L, const char *tname);</pre>
+
+<p>
+Pushes onto the stack the metatable associated with the name <code>tname</code>
+in the registry (see <a href="#luaL_newmetatable"><code>luaL_newmetatable</code></a>),
+or <b>nil</b> if there is no metatable associated with that name.
+Returns the type of the pushed value.
+
+
+
+
+
+<hr><h3><a name="luaL_getsubtable"><code>luaL_getsubtable</code></a></h3><p>
+<span class="apii">[-0, +1, <em>e</em>]</span>
+<pre>int luaL_getsubtable (lua_State *L, int idx, const char *fname);</pre>
+
+<p>
+Ensures that the value <code>t[fname]</code>,
+where <code>t</code> is the value at index <code>idx</code>,
+is a table,
+and pushes that table onto the stack.
+Returns true if it finds a previous table there
+and false if it creates a new table.
+
+
+
+
+
+<hr><h3><a name="luaL_gsub"><code>luaL_gsub</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>const char *luaL_gsub (lua_State *L,
+ const char *s,
+ const char *p,
+ const char *r);</pre>
+
+<p>
+Creates a copy of string <code>s</code>,
+replacing any occurrence of the string <code>p</code>
+with the string <code>r</code>.
+Pushes the resulting string on the stack and returns it.
+
+
+
+
+
+<hr><h3><a name="luaL_len"><code>luaL_len</code></a></h3><p>
+<span class="apii">[-0, +0, <em>e</em>]</span>
+<pre>lua_Integer luaL_len (lua_State *L, int index);</pre>
+
+<p>
+Returns the "length" of the value at the given index
+as a number;
+it is equivalent to the '<code>#</code>' operator in Lua (see <a href="#3.4.7">&sect;3.4.7</a>).
+Raises an error if the result of the operation is not an integer.
+(This case can only happen through metamethods.)
+
+
+
+
+
+<hr><h3><a name="luaL_loadbuffer"><code>luaL_loadbuffer</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>int luaL_loadbuffer (lua_State *L,
+ const char *buff,
+ size_t sz,
+ const char *name);</pre>
+
+<p>
+Equivalent to <a href="#luaL_loadbufferx"><code>luaL_loadbufferx</code></a> with <code>mode</code> equal to <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_loadbufferx"><code>luaL_loadbufferx</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>int luaL_loadbufferx (lua_State *L,
+ const char *buff,
+ size_t sz,
+ const char *name,
+ const char *mode);</pre>
+
+<p>
+Loads a buffer as a Lua chunk.
+This function uses <a href="#lua_load"><code>lua_load</code></a> to load the chunk in the
+buffer pointed to by <code>buff</code> with size <code>sz</code>.
+
+
+<p>
+This function returns the same results as <a href="#lua_load"><code>lua_load</code></a>.
+<code>name</code> is the chunk name,
+used for debug information and error messages.
+The string <code>mode</code> works as in the function <a href="#lua_load"><code>lua_load</code></a>.
+
+
+
+
+
+<hr><h3><a name="luaL_loadfile"><code>luaL_loadfile</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>int luaL_loadfile (lua_State *L, const char *filename);</pre>
+
+<p>
+Equivalent to <a href="#luaL_loadfilex"><code>luaL_loadfilex</code></a> with <code>mode</code> equal to <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_loadfilex"><code>luaL_loadfilex</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>int luaL_loadfilex (lua_State *L, const char *filename,
+ const char *mode);</pre>
+
+<p>
+Loads a file as a Lua chunk.
+This function uses <a href="#lua_load"><code>lua_load</code></a> to load the chunk in the file
+named <code>filename</code>.
+If <code>filename</code> is <code>NULL</code>,
+then it loads from the standard input.
+The first line in the file is ignored if it starts with a <code>#</code>.
+
+
+<p>
+The string <code>mode</code> works as in the function <a href="#lua_load"><code>lua_load</code></a>.
+
+
+<p>
+This function returns the same results as <a href="#lua_load"><code>lua_load</code></a>
+or <a href="#pdf-LUA_ERRFILE"><code>LUA_ERRFILE</code></a> for file-related errors.
+
+
+<p>
+As <a href="#lua_load"><code>lua_load</code></a>, this function only loads the chunk;
+it does not run it.
+
+
+
+
+
+<hr><h3><a name="luaL_loadstring"><code>luaL_loadstring</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>int luaL_loadstring (lua_State *L, const char *s);</pre>
+
+<p>
+Loads a string as a Lua chunk.
+This function uses <a href="#lua_load"><code>lua_load</code></a> to load the chunk in
+the zero-terminated string <code>s</code>.
+
+
+<p>
+This function returns the same results as <a href="#lua_load"><code>lua_load</code></a>.
+
+
+<p>
+Also as <a href="#lua_load"><code>lua_load</code></a>, this function only loads the chunk;
+it does not run it.
+
+
+
+
+
+<hr><h3><a name="luaL_newlib"><code>luaL_newlib</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void luaL_newlib (lua_State *L, const luaL_Reg l[]);</pre>
+
+<p>
+Creates a new table and registers there
+the functions in the list <code>l</code>.
+
+
+<p>
+It is implemented as the following macro:
+
+<pre>
+ (luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
+</pre><p>
+The array <code>l</code> must be the actual array,
+not a pointer to it.
+
+
+
+
+
+<hr><h3><a name="luaL_newlibtable"><code>luaL_newlibtable</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void luaL_newlibtable (lua_State *L, const luaL_Reg l[]);</pre>
+
+<p>
+Creates a new table with a size optimized
+to store all entries in the array <code>l</code>
+(but does not actually store them).
+It is intended to be used in conjunction with <a href="#luaL_setfuncs"><code>luaL_setfuncs</code></a>
+(see <a href="#luaL_newlib"><code>luaL_newlib</code></a>).
+
+
+<p>
+It is implemented as a macro.
+The array <code>l</code> must be the actual array,
+not a pointer to it.
+
+
+
+
+
+<hr><h3><a name="luaL_newmetatable"><code>luaL_newmetatable</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>int luaL_newmetatable (lua_State *L, const char *tname);</pre>
+
+<p>
+If the registry already has the key <code>tname</code>,
+returns 0.
+Otherwise,
+creates a new table to be used as a metatable for userdata,
+adds to this new table the pair <code>__name = tname</code>,
+adds to the registry the pair <code>[tname] = new table</code>,
+and returns 1.
+
+
+<p>
+In both cases,
+the function pushes onto the stack the final value associated
+with <code>tname</code> in the registry.
+
+
+
+
+
+<hr><h3><a name="luaL_newstate"><code>luaL_newstate</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>lua_State *luaL_newstate (void);</pre>
+
+<p>
+Creates a new Lua state.
+It calls <a href="#lua_newstate"><code>lua_newstate</code></a> with an
+allocator based on the ISO&nbsp;C allocation functions
+and then sets a warning function and a panic function (see <a href="#4.4">&sect;4.4</a>)
+that print messages to the standard error output.
+
+
+<p>
+Returns the new state,
+or <code>NULL</code> if there is a memory allocation error.
+
+
+
+
+
+<hr><h3><a name="luaL_openlibs"><code>luaL_openlibs</code></a></h3><p>
+<span class="apii">[-0, +0, <em>e</em>]</span>
+<pre>void luaL_openlibs (lua_State *L);</pre>
+
+<p>
+Opens all standard Lua libraries into the given state.
+
+
+
+
+
+<hr><h3><a name="luaL_opt"><code>luaL_opt</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>T luaL_opt (L, func, arg, dflt);</pre>
+
+<p>
+This macro is defined as follows:
+
+<pre>
+ (lua_isnoneornil(L,(arg)) ? (dflt) : func(L,(arg)))
+</pre><p>
+In words, if the argument <code>arg</code> is nil or absent,
+the macro results in the default <code>dflt</code>.
+Otherwise, it results in the result of calling <code>func</code>
+with the state <code>L</code> and the argument index <code>arg</code> as
+arguments.
+Note that it evaluates the expression <code>dflt</code> only if needed.
+
+
+
+
+
+<hr><h3><a name="luaL_optinteger"><code>luaL_optinteger</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>lua_Integer luaL_optinteger (lua_State *L,
+ int arg,
+ lua_Integer d);</pre>
+
+<p>
+If the function argument <code>arg</code> is an integer
+(or it is convertible to an integer),
+returns this integer.
+If this argument is absent or is <b>nil</b>,
+returns <code>d</code>.
+Otherwise, raises an error.
+
+
+
+
+
+<hr><h3><a name="luaL_optlstring"><code>luaL_optlstring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>const char *luaL_optlstring (lua_State *L,
+ int arg,
+ const char *d,
+ size_t *l);</pre>
+
+<p>
+If the function argument <code>arg</code> is a string,
+returns this string.
+If this argument is absent or is <b>nil</b>,
+returns <code>d</code>.
+Otherwise, raises an error.
+
+
+<p>
+If <code>l</code> is not <code>NULL</code>,
+fills its referent with the result's length.
+If the result is <code>NULL</code>
+(only possible when returning <code>d</code> and <code>d == NULL</code>),
+its length is considered zero.
+
+
+<p>
+This function uses <a href="#lua_tolstring"><code>lua_tolstring</code></a> to get its result,
+so all conversions and caveats of that function apply here.
+
+
+
+
+
+<hr><h3><a name="luaL_optnumber"><code>luaL_optnumber</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>lua_Number luaL_optnumber (lua_State *L, int arg, lua_Number d);</pre>
+
+<p>
+If the function argument <code>arg</code> is a number,
+returns this number as a <code>lua_Number</code>.
+If this argument is absent or is <b>nil</b>,
+returns <code>d</code>.
+Otherwise, raises an error.
+
+
+
+
+
+<hr><h3><a name="luaL_optstring"><code>luaL_optstring</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>const char *luaL_optstring (lua_State *L,
+ int arg,
+ const char *d);</pre>
+
+<p>
+If the function argument <code>arg</code> is a string,
+returns this string.
+If this argument is absent or is <b>nil</b>,
+returns <code>d</code>.
+Otherwise, raises an error.
+
+
+
+
+
+<hr><h3><a name="luaL_prepbuffer"><code>luaL_prepbuffer</code></a></h3><p>
+<span class="apii">[-?, +?, <em>m</em>]</span>
+<pre>char *luaL_prepbuffer (luaL_Buffer *B);</pre>
+
+<p>
+Equivalent to <a href="#luaL_prepbuffsize"><code>luaL_prepbuffsize</code></a>
+with the predefined size <a name="pdf-LUAL_BUFFERSIZE"><code>LUAL_BUFFERSIZE</code></a>.
+
+
+
+
+
+<hr><h3><a name="luaL_prepbuffsize"><code>luaL_prepbuffsize</code></a></h3><p>
+<span class="apii">[-?, +?, <em>m</em>]</span>
+<pre>char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz);</pre>
+
+<p>
+Returns an address to a space of size <code>sz</code>
+where you can copy a string to be added to buffer <code>B</code>
+(see <a href="#luaL_Buffer"><code>luaL_Buffer</code></a>).
+After copying the string into this space you must call
+<a href="#luaL_addsize"><code>luaL_addsize</code></a> with the size of the string to actually add
+it to the buffer.
+
+
+
+
+
+<hr><h3><a name="luaL_pushfail"><code>luaL_pushfail</code></a></h3><p>
+<span class="apii">[-0, +1, &ndash;]</span>
+<pre>void luaL_pushfail (lua_State *L);</pre>
+
+<p>
+Pushes the <b>fail</b> value onto the stack (see <a href="#6">&sect;6</a>).
+
+
+
+
+
+<hr><h3><a name="luaL_pushresult"><code>luaL_pushresult</code></a></h3><p>
+<span class="apii">[-?, +1, <em>m</em>]</span>
+<pre>void luaL_pushresult (luaL_Buffer *B);</pre>
+
+<p>
+Finishes the use of buffer <code>B</code> leaving the final string on
+the top of the stack.
+
+
+
+
+
+<hr><h3><a name="luaL_pushresultsize"><code>luaL_pushresultsize</code></a></h3><p>
+<span class="apii">[-?, +1, <em>m</em>]</span>
+<pre>void luaL_pushresultsize (luaL_Buffer *B, size_t sz);</pre>
+
+<p>
+Equivalent to the sequence <a href="#luaL_addsize"><code>luaL_addsize</code></a>, <a href="#luaL_pushresult"><code>luaL_pushresult</code></a>.
+
+
+
+
+
+<hr><h3><a name="luaL_ref"><code>luaL_ref</code></a></h3><p>
+<span class="apii">[-1, +0, <em>m</em>]</span>
+<pre>int luaL_ref (lua_State *L, int t);</pre>
+
+<p>
+Creates and returns a <em>reference</em>,
+in the table at index <code>t</code>,
+for the object on the top of the stack (and pops the object).
+
+
+<p>
+A reference is a unique integer key.
+As long as you do not manually add integer keys into the table <code>t</code>,
+<a href="#luaL_ref"><code>luaL_ref</code></a> ensures the uniqueness of the key it returns.
+You can retrieve an object referred by the reference <code>r</code>
+by calling <code>lua_rawgeti(L, t, r)</code>.
+The function <a href="#luaL_unref"><code>luaL_unref</code></a> frees a reference.
+
+
+<p>
+If the object on the top of the stack is <b>nil</b>,
+<a href="#luaL_ref"><code>luaL_ref</code></a> returns the constant <a name="pdf-LUA_REFNIL"><code>LUA_REFNIL</code></a>.
+The constant <a name="pdf-LUA_NOREF"><code>LUA_NOREF</code></a> is guaranteed to be different
+from any reference returned by <a href="#luaL_ref"><code>luaL_ref</code></a>.
+
+
+
+
+
+<hr><h3><a name="luaL_Reg"><code>luaL_Reg</code></a></h3>
+<pre>typedef struct luaL_Reg {
+ const char *name;
+ lua_CFunction func;
+} luaL_Reg;</pre>
+
+<p>
+Type for arrays of functions to be registered by
+<a href="#luaL_setfuncs"><code>luaL_setfuncs</code></a>.
+<code>name</code> is the function name and <code>func</code> is a pointer to
+the function.
+Any array of <a href="#luaL_Reg"><code>luaL_Reg</code></a> must end with a sentinel entry
+in which both <code>name</code> and <code>func</code> are <code>NULL</code>.
+
+
+
+
+
+<hr><h3><a name="luaL_requiref"><code>luaL_requiref</code></a></h3><p>
+<span class="apii">[-0, +1, <em>e</em>]</span>
+<pre>void luaL_requiref (lua_State *L, const char *modname,
+ lua_CFunction openf, int glb);</pre>
+
+<p>
+If <code>package.loaded[modname]</code> is not true,
+calls the function <code>openf</code> with the string <code>modname</code> as an argument
+and sets the call result to <code>package.loaded[modname]</code>,
+as if that function has been called through <a href="#pdf-require"><code>require</code></a>.
+
+
+<p>
+If <code>glb</code> is true,
+also stores the module into the global <code>modname</code>.
+
+
+<p>
+Leaves a copy of the module on the stack.
+
+
+
+
+
+<hr><h3><a name="luaL_setfuncs"><code>luaL_setfuncs</code></a></h3><p>
+<span class="apii">[-nup, +0, <em>m</em>]</span>
+<pre>void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);</pre>
+
+<p>
+Registers all functions in the array <code>l</code>
+(see <a href="#luaL_Reg"><code>luaL_Reg</code></a>) into the table on the top of the stack
+(below optional upvalues, see next).
+
+
+<p>
+When <code>nup</code> is not zero,
+all functions are created with <code>nup</code> upvalues,
+initialized with copies of the <code>nup</code> values
+previously pushed on the stack
+on top of the library table.
+These values are popped from the stack after the registration.
+
+
+<p>
+A function with a <code>NULL</code> value represents a placeholder,
+which is filled with <b>false</b>.
+
+
+
+
+
+<hr><h3><a name="luaL_setmetatable"><code>luaL_setmetatable</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void luaL_setmetatable (lua_State *L, const char *tname);</pre>
+
+<p>
+Sets the metatable of the object on the top of the stack
+as the metatable associated with name <code>tname</code>
+in the registry (see <a href="#luaL_newmetatable"><code>luaL_newmetatable</code></a>).
+
+
+
+
+
+<hr><h3><a name="luaL_Stream"><code>luaL_Stream</code></a></h3>
+<pre>typedef struct luaL_Stream {
+ FILE *f;
+ lua_CFunction closef;
+} luaL_Stream;</pre>
+
+<p>
+The standard representation for file handles
+used by the standard I/O library.
+
+
+<p>
+A file handle is implemented as a full userdata,
+with a metatable called <code>LUA_FILEHANDLE</code>
+(where <code>LUA_FILEHANDLE</code> is a macro with the actual metatable's name).
+The metatable is created by the I/O library
+(see <a href="#luaL_newmetatable"><code>luaL_newmetatable</code></a>).
+
+
+<p>
+This userdata must start with the structure <code>luaL_Stream</code>;
+it can contain other data after this initial structure.
+The field <code>f</code> points to the corresponding C stream
+(or it can be <code>NULL</code> to indicate an incompletely created handle).
+The field <code>closef</code> points to a Lua function
+that will be called to close the stream
+when the handle is closed or collected;
+this function receives the file handle as its sole argument and
+must return either a true value, in case of success,
+or a false value plus an error message, in case of error.
+Once Lua calls this field,
+it changes the field value to <code>NULL</code>
+to signal that the handle is closed.
+
+
+
+
+
+<hr><h3><a name="luaL_testudata"><code>luaL_testudata</code></a></h3><p>
+<span class="apii">[-0, +0, <em>m</em>]</span>
+<pre>void *luaL_testudata (lua_State *L, int arg, const char *tname);</pre>
+
+<p>
+This function works like <a href="#luaL_checkudata"><code>luaL_checkudata</code></a>,
+except that, when the test fails,
+it returns <code>NULL</code> instead of raising an error.
+
+
+
+
+
+<hr><h3><a name="luaL_tolstring"><code>luaL_tolstring</code></a></h3><p>
+<span class="apii">[-0, +1, <em>e</em>]</span>
+<pre>const char *luaL_tolstring (lua_State *L, int idx, size_t *len);</pre>
+
+<p>
+Converts any Lua value at the given index to a C&nbsp;string
+in a reasonable format.
+The resulting string is pushed onto the stack and also
+returned by the function (see <a href="#4.1.3">&sect;4.1.3</a>).
+If <code>len</code> is not <code>NULL</code>,
+the function also sets <code>*len</code> with the string length.
+
+
+<p>
+If the value has a metatable with a <code>__tostring</code> field,
+then <code>luaL_tolstring</code> calls the corresponding metamethod
+with the value as argument,
+and uses the result of the call as its result.
+
+
+
+
+
+<hr><h3><a name="luaL_traceback"><code>luaL_traceback</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void luaL_traceback (lua_State *L, lua_State *L1, const char *msg,
+ int level);</pre>
+
+<p>
+Creates and pushes a traceback of the stack <code>L1</code>.
+If <code>msg</code> is not <code>NULL</code>, it is appended
+at the beginning of the traceback.
+The <code>level</code> parameter tells at which level
+to start the traceback.
+
+
+
+
+
+<hr><h3><a name="luaL_typeerror"><code>luaL_typeerror</code></a></h3><p>
+<span class="apii">[-0, +0, <em>v</em>]</span>
+<pre>int luaL_typeerror (lua_State *L, int arg, const char *tname);</pre>
+
+<p>
+Raises a type error for the argument <code>arg</code>
+of the C&nbsp;function that called it,
+using a standard message;
+<code>tname</code> is a "name" for the expected type.
+This function never returns.
+
+
+
+
+
+<hr><h3><a name="luaL_typename"><code>luaL_typename</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>const char *luaL_typename (lua_State *L, int index);</pre>
+
+<p>
+Returns the name of the type of the value at the given index.
+
+
+
+
+
+<hr><h3><a name="luaL_unref"><code>luaL_unref</code></a></h3><p>
+<span class="apii">[-0, +0, &ndash;]</span>
+<pre>void luaL_unref (lua_State *L, int t, int ref);</pre>
+
+<p>
+Releases the reference <code>ref</code> from the table at index <code>t</code>
+(see <a href="#luaL_ref"><code>luaL_ref</code></a>).
+The entry is removed from the table,
+so that the referred object can be collected.
+The reference <code>ref</code> is also freed to be used again.
+
+
+<p>
+If <code>ref</code> is <a href="#pdf-LUA_NOREF"><code>LUA_NOREF</code></a> or <a href="#pdf-LUA_REFNIL"><code>LUA_REFNIL</code></a>,
+<a href="#luaL_unref"><code>luaL_unref</code></a> does nothing.
+
+
+
+
+
+<hr><h3><a name="luaL_where"><code>luaL_where</code></a></h3><p>
+<span class="apii">[-0, +1, <em>m</em>]</span>
+<pre>void luaL_where (lua_State *L, int lvl);</pre>
+
+<p>
+Pushes onto the stack a string identifying the current position
+of the control at level <code>lvl</code> in the call stack.
+Typically this string has the following format:
+
+<pre>
+ <em>chunkname</em>:<em>currentline</em>:
+</pre><p>
+Level&nbsp;0 is the running function,
+level&nbsp;1 is the function that called the running function,
+etc.
+
+
+<p>
+This function is used to build a prefix for error messages.
+
+
+
+
+
+
+
+<h1>6 &ndash; <a name="6">The Standard Libraries</a></h1>
+
+
+
+<p>
+The standard Lua libraries provide useful functions
+that are implemented in&nbsp;C through the C&nbsp;API.
+Some of these functions provide essential services to the language
+(e.g., <a href="#pdf-type"><code>type</code></a> and <a href="#pdf-getmetatable"><code>getmetatable</code></a>);
+others provide access to outside services (e.g., I/O);
+and others could be implemented in Lua itself,
+but that for different reasons
+deserve an implementation in C (e.g., <a href="#pdf-table.sort"><code>table.sort</code></a>).
+
+
+<p>
+All libraries are implemented through the official C&nbsp;API
+and are provided as separate C&nbsp;modules.
+Unless otherwise noted,
+these library functions do not adjust its number of arguments
+to its expected parameters.
+For instance, a function documented as <code>foo(arg)</code>
+should not be called without an argument.
+
+
+<p>
+The notation <b>fail</b> means a false value representing
+some kind of failure.
+(Currently, <b>fail</b> is equal to <b>nil</b>,
+but that may change in future versions.
+The recommendation is to always test the success of these functions
+with <code>(not status)</code>, instead of <code>(status == nil)</code>.)
+
+
+<p>
+Currently, Lua has the following standard libraries:
+
+<ul>
+
+<li>basic library (<a href="#6.1">&sect;6.1</a>);</li>
+
+<li>coroutine library (<a href="#6.2">&sect;6.2</a>);</li>
+
+<li>package library (<a href="#6.3">&sect;6.3</a>);</li>
+
+<li>string manipulation (<a href="#6.4">&sect;6.4</a>);</li>
+
+<li>basic UTF-8 support (<a href="#6.5">&sect;6.5</a>);</li>
+
+<li>table manipulation (<a href="#6.6">&sect;6.6</a>);</li>
+
+<li>mathematical functions (<a href="#6.7">&sect;6.7</a>) (sin, log, etc.);</li>
+
+<li>input and output (<a href="#6.8">&sect;6.8</a>);</li>
+
+<li>operating system facilities (<a href="#6.9">&sect;6.9</a>);</li>
+
+<li>debug facilities (<a href="#6.10">&sect;6.10</a>).</li>
+
+</ul><p>
+Except for the basic and the package libraries,
+each library provides all its functions as fields of a global table
+or as methods of its objects.
+
+
+<p>
+To have access to these libraries,
+the C&nbsp;host program should call the <a href="#luaL_openlibs"><code>luaL_openlibs</code></a> function,
+which opens all standard libraries.
+Alternatively,
+the host program can open them individually by using
+<a href="#luaL_requiref"><code>luaL_requiref</code></a> to call
+<a name="pdf-luaopen_base"><code>luaopen_base</code></a> (for the basic library),
+<a name="pdf-luaopen_package"><code>luaopen_package</code></a> (for the package library),
+<a name="pdf-luaopen_coroutine"><code>luaopen_coroutine</code></a> (for the coroutine library),
+<a name="pdf-luaopen_string"><code>luaopen_string</code></a> (for the string library),
+<a name="pdf-luaopen_utf8"><code>luaopen_utf8</code></a> (for the UTF-8 library),
+<a name="pdf-luaopen_table"><code>luaopen_table</code></a> (for the table library),
+<a name="pdf-luaopen_math"><code>luaopen_math</code></a> (for the mathematical library),
+<a name="pdf-luaopen_io"><code>luaopen_io</code></a> (for the I/O library),
+<a name="pdf-luaopen_os"><code>luaopen_os</code></a> (for the operating system library),
+and <a name="pdf-luaopen_debug"><code>luaopen_debug</code></a> (for the debug library).
+These functions are declared in <a name="pdf-lualib.h"><code>lualib.h</code></a>.
+
+
+
+
+
+<h2>6.1 &ndash; <a name="6.1">Basic Functions</a></h2>
+
+<p>
+The basic library provides core functions to Lua.
+If you do not include this library in your application,
+you should check carefully whether you need to provide
+implementations for some of its facilities.
+
+
+<p>
+<hr><h3><a name="pdf-assert"><code>assert (v [, message])</code></a></h3>
+
+
+<p>
+Raises an error if
+the value of its argument <code>v</code> is false (i.e., <b>nil</b> or <b>false</b>);
+otherwise, returns all its arguments.
+In case of error,
+<code>message</code> is the error object;
+when absent, it defaults to "<code>assertion failed!</code>"
+
+
+
+
+<p>
+<hr><h3><a name="pdf-collectgarbage"><code>collectgarbage ([opt [, arg]])</code></a></h3>
+
+
+<p>
+This function is a generic interface to the garbage collector.
+It performs different functions according to its first argument, <code>opt</code>:
+
+<ul>
+
+<li><b>"<code>collect</code>": </b>
+Performs a full garbage-collection cycle.
+This is the default option.
+</li>
+
+<li><b>"<code>stop</code>": </b>
+Stops automatic execution of the garbage collector.
+The collector will run only when explicitly invoked,
+until a call to restart it.
+</li>
+
+<li><b>"<code>restart</code>": </b>
+Restarts automatic execution of the garbage collector.
+</li>
+
+<li><b>"<code>count</code>": </b>
+Returns the total memory in use by Lua in Kbytes.
+The value has a fractional part,
+so that it multiplied by 1024
+gives the exact number of bytes in use by Lua.
+</li>
+
+<li><b>"<code>step</code>": </b>
+Performs a garbage-collection step.
+The step "size" is controlled by <code>arg</code>.
+With a zero value,
+the collector will perform one basic (indivisible) step.
+For non-zero values,
+the collector will perform as if that amount of memory
+(in Kbytes) had been allocated by Lua.
+Returns <b>true</b> if the step finished a collection cycle.
+</li>
+
+<li><b>"<code>isrunning</code>": </b>
+Returns a boolean that tells whether the collector is running
+(i.e., not stopped).
+</li>
+
+<li><b>"<code>incremental</code>": </b>
+Change the collector mode to incremental.
+This option can be followed by three numbers:
+the garbage-collector pause,
+the step multiplier,
+and the step size (see <a href="#2.5.1">&sect;2.5.1</a>).
+A zero means to not change that value.
+</li>
+
+<li><b>"<code>generational</code>": </b>
+Change the collector mode to generational.
+This option can be followed by two numbers:
+the garbage-collector minor multiplier
+and the major multiplier (see <a href="#2.5.2">&sect;2.5.2</a>).
+A zero means to not change that value.
+</li>
+
+</ul><p>
+See <a href="#2.5">&sect;2.5</a> for more details about garbage collection
+and some of these options.
+
+
+<p>
+This function should not be called by a finalizer.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-dofile"><code>dofile ([filename])</code></a></h3>
+Opens the named file and executes its content as a Lua chunk.
+When called without arguments,
+<code>dofile</code> executes the content of the standard input (<code>stdin</code>).
+Returns all values returned by the chunk.
+In case of errors, <code>dofile</code> propagates the error
+to its caller.
+(That is, <code>dofile</code> does not run in protected mode.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-error"><code>error (message [, level])</code></a></h3>
+Raises an error (see <a href="#2.3">&sect;2.3</a>) with <code>message</code> as the error object.
+This function never returns.
+
+
+<p>
+Usually, <code>error</code> adds some information about the error position
+at the beginning of the message, if the message is a string.
+The <code>level</code> argument specifies how to get the error position.
+With level&nbsp;1 (the default), the error position is where the
+<code>error</code> function was called.
+Level&nbsp;2 points the error to where the function
+that called <code>error</code> was called; and so on.
+Passing a level&nbsp;0 avoids the addition of error position information
+to the message.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-_G"><code>_G</code></a></h3>
+A global variable (not a function) that
+holds the global environment (see <a href="#2.2">&sect;2.2</a>).
+Lua itself does not use this variable;
+changing its value does not affect any environment,
+nor vice versa.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-getmetatable"><code>getmetatable (object)</code></a></h3>
+
+
+<p>
+If <code>object</code> does not have a metatable, returns <b>nil</b>.
+Otherwise,
+if the object's metatable has a <code>__metatable</code> field,
+returns the associated value.
+Otherwise, returns the metatable of the given object.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-ipairs"><code>ipairs (t)</code></a></h3>
+
+
+<p>
+Returns three values (an iterator function, the table <code>t</code>, and 0)
+so that the construction
+
+<pre>
+ for i,v in ipairs(t) do <em>body</em> end
+</pre><p>
+will iterate over the key&ndash;value pairs
+(<code>1,t[1]</code>), (<code>2,t[2]</code>), ...,
+up to the first absent index.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-load"><code>load (chunk [, chunkname [, mode [, env]]])</code></a></h3>
+
+
+<p>
+Loads a chunk.
+
+
+<p>
+If <code>chunk</code> is a string, the chunk is this string.
+If <code>chunk</code> is a function,
+<code>load</code> calls it repeatedly to get the chunk pieces.
+Each call to <code>chunk</code> must return a string that concatenates
+with previous results.
+A return of an empty string, <b>nil</b>, or no value signals the end of the chunk.
+
+
+<p>
+If there are no syntactic errors,
+<code>load</code> returns the compiled chunk as a function;
+otherwise, it returns <b>fail</b> plus the error message.
+
+
+<p>
+When you load a main chunk,
+the resulting function will always have exactly one upvalue,
+the <code>_ENV</code> variable (see <a href="#2.2">&sect;2.2</a>).
+However,
+when you load a binary chunk created from a function (see <a href="#pdf-string.dump"><code>string.dump</code></a>),
+the resulting function can have an arbitrary number of upvalues,
+and there is no guarantee that its first upvalue will be
+the <code>_ENV</code> variable.
+(A non-main function may not even have an <code>_ENV</code> upvalue.)
+
+
+<p>
+Regardless, if the resulting function has any upvalues,
+its first upvalue is set to the value of <code>env</code>,
+if that parameter is given,
+or to the value of the global environment.
+Other upvalues are initialized with <b>nil</b>.
+All upvalues are fresh, that is,
+they are not shared with any other function.
+
+
+<p>
+<code>chunkname</code> is used as the name of the chunk for error messages
+and debug information (see <a href="#4.7">&sect;4.7</a>).
+When absent,
+it defaults to <code>chunk</code>, if <code>chunk</code> is a string,
+or to "<code>=(load)</code>" otherwise.
+
+
+<p>
+The string <code>mode</code> controls whether the chunk can be text or binary
+(that is, a precompiled chunk).
+It may be the string "<code>b</code>" (only binary chunks),
+"<code>t</code>" (only text chunks),
+or "<code>bt</code>" (both binary and text).
+The default is "<code>bt</code>".
+
+
+<p>
+It is safe to load malformed binary chunks;
+<code>load</code> signals an appropriate error.
+However,
+Lua does not check the consistency of the code inside binary chunks;
+running maliciously crafted bytecode can crash the interpreter.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-loadfile"><code>loadfile ([filename [, mode [, env]]])</code></a></h3>
+
+
+<p>
+Similar to <a href="#pdf-load"><code>load</code></a>,
+but gets the chunk from file <code>filename</code>
+or from the standard input,
+if no file name is given.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-next"><code>next (table [, index])</code></a></h3>
+
+
+<p>
+Allows a program to traverse all fields of a table.
+Its first argument is a table and its second argument
+is an index in this table.
+A call to <code>next</code> returns the next index of the table
+and its associated value.
+When called with <b>nil</b> as its second argument,
+<code>next</code> returns an initial index
+and its associated value.
+When called with the last index,
+or with <b>nil</b> in an empty table,
+<code>next</code> returns <b>nil</b>.
+If the second argument is absent, then it is interpreted as <b>nil</b>.
+In particular,
+you can use <code>next(t)</code> to check whether a table is empty.
+
+
+<p>
+The order in which the indices are enumerated is not specified,
+<em>even for numeric indices</em>.
+(To traverse a table in numerical order,
+use a numerical <b>for</b>.)
+
+
+<p>
+You should not assign any value to a non-existent field in a table
+during its traversal.
+You may however modify existing fields.
+In particular, you may set existing fields to nil.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-pairs"><code>pairs (t)</code></a></h3>
+
+
+<p>
+If <code>t</code> has a metamethod <code>__pairs</code>,
+calls it with <code>t</code> as argument and returns the first three
+results from the call.
+
+
+<p>
+Otherwise,
+returns three values: the <a href="#pdf-next"><code>next</code></a> function, the table <code>t</code>, and <b>nil</b>,
+so that the construction
+
+<pre>
+ for k,v in pairs(t) do <em>body</em> end
+</pre><p>
+will iterate over all key&ndash;value pairs of table <code>t</code>.
+
+
+<p>
+See function <a href="#pdf-next"><code>next</code></a> for the caveats of modifying
+the table during its traversal.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-pcall"><code>pcall (f [, arg1, &middot;&middot;&middot;])</code></a></h3>
+
+
+<p>
+Calls the function <code>f</code> with
+the given arguments in <em>protected mode</em>.
+This means that any error inside&nbsp;<code>f</code> is not propagated;
+instead, <code>pcall</code> catches the error
+and returns a status code.
+Its first result is the status code (a boolean),
+which is <b>true</b> if the call succeeds without errors.
+In such case, <code>pcall</code> also returns all results from the call,
+after this first result.
+In case of any error, <code>pcall</code> returns <b>false</b> plus the error object.
+Note that errors caught by <code>pcall</code> do not call a message handler.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-print"><code>print (&middot;&middot;&middot;)</code></a></h3>
+Receives any number of arguments
+and prints their values to <code>stdout</code>,
+converting each argument to a string
+following the same rules of <a href="#pdf-tostring"><code>tostring</code></a>.
+
+
+<p>
+The function <code>print</code> is not intended for formatted output,
+but only as a quick way to show a value,
+for instance for debugging.
+For complete control over the output,
+use <a href="#pdf-string.format"><code>string.format</code></a> and <a href="#pdf-io.write"><code>io.write</code></a>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-rawequal"><code>rawequal (v1, v2)</code></a></h3>
+Checks whether <code>v1</code> is equal to <code>v2</code>,
+without invoking the <code>__eq</code> metamethod.
+Returns a boolean.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-rawget"><code>rawget (table, index)</code></a></h3>
+Gets the real value of <code>table[index]</code>,
+without using the <code>__index</code> metavalue.
+<code>table</code> must be a table;
+<code>index</code> may be any value.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-rawlen"><code>rawlen (v)</code></a></h3>
+Returns the length of the object <code>v</code>,
+which must be a table or a string,
+without invoking the <code>__len</code> metamethod.
+Returns an integer.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-rawset"><code>rawset (table, index, value)</code></a></h3>
+Sets the real value of <code>table[index]</code> to <code>value</code>,
+without using the <code>__newindex</code> metavalue.
+<code>table</code> must be a table,
+<code>index</code> any value different from <b>nil</b> and NaN,
+and <code>value</code> any Lua value.
+
+
+<p>
+This function returns <code>table</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-select"><code>select (index, &middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+If <code>index</code> is a number,
+returns all arguments after argument number <code>index</code>;
+a negative number indexes from the end (-1 is the last argument).
+Otherwise, <code>index</code> must be the string <code>"#"</code>,
+and <code>select</code> returns the total number of extra arguments it received.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-setmetatable"><code>setmetatable (table, metatable)</code></a></h3>
+
+
+<p>
+Sets the metatable for the given table.
+If <code>metatable</code> is <b>nil</b>,
+removes the metatable of the given table.
+If the original metatable has a <code>__metatable</code> field,
+raises an error.
+
+
+<p>
+This function returns <code>table</code>.
+
+
+<p>
+To change the metatable of other types from Lua code,
+you must use the debug library (<a href="#6.10">&sect;6.10</a>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-tonumber"><code>tonumber (e [, base])</code></a></h3>
+
+
+<p>
+When called with no <code>base</code>,
+<code>tonumber</code> tries to convert its argument to a number.
+If the argument is already a number or
+a string convertible to a number,
+then <code>tonumber</code> returns this number;
+otherwise, it returns <b>fail</b>.
+
+
+<p>
+The conversion of strings can result in integers or floats,
+according to the lexical conventions of Lua (see <a href="#3.1">&sect;3.1</a>).
+The string may have leading and trailing spaces and a sign.
+
+
+<p>
+When called with <code>base</code>,
+then <code>e</code> must be a string to be interpreted as
+an integer numeral in that base.
+The base may be any integer between 2 and 36, inclusive.
+In bases above&nbsp;10, the letter '<code>A</code>' (in either upper or lower case)
+represents&nbsp;10, '<code>B</code>' represents&nbsp;11, and so forth,
+with '<code>Z</code>' representing 35.
+If the string <code>e</code> is not a valid numeral in the given base,
+the function returns <b>fail</b>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-tostring"><code>tostring (v)</code></a></h3>
+
+
+<p>
+Receives a value of any type and
+converts it to a string in a human-readable format.
+
+
+<p>
+If the metatable of <code>v</code> has a <code>__tostring</code> field,
+then <code>tostring</code> calls the corresponding value
+with <code>v</code> as argument,
+and uses the result of the call as its result.
+Otherwise, if the metatable of <code>v</code> has a <code>__name</code> field
+with a string value,
+<code>tostring</code> may use that string in its final result.
+
+
+<p>
+For complete control of how numbers are converted,
+use <a href="#pdf-string.format"><code>string.format</code></a>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-type"><code>type (v)</code></a></h3>
+
+
+<p>
+Returns the type of its only argument, coded as a string.
+The possible results of this function are
+"<code>nil</code>" (a string, not the value <b>nil</b>),
+"<code>number</code>",
+"<code>string</code>",
+"<code>boolean</code>",
+"<code>table</code>",
+"<code>function</code>",
+"<code>thread</code>",
+and "<code>userdata</code>".
+
+
+
+
+<p>
+<hr><h3><a name="pdf-_VERSION"><code>_VERSION</code></a></h3>
+
+
+<p>
+A global variable (not a function) that
+holds a string containing the running Lua version.
+The current value of this variable is "<code>Lua 5.4</code>".
+
+
+
+
+<p>
+<hr><h3><a name="pdf-warn"><code>warn (msg1, &middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Emits a warning with a message composed by the concatenation
+of all its arguments (which should be strings).
+
+
+<p>
+By convention,
+a one-piece message starting with '<code>@</code>'
+is intended to be a <em>control message</em>,
+which is a message to the warning system itself.
+In particular, the standard warning function in Lua
+recognizes the control messages "<code>@off</code>",
+to stop the emission of warnings,
+and "<code>@on</code>", to (re)start the emission;
+it ignores unknown control messages.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-xpcall"><code>xpcall (f, msgh [, arg1, &middot;&middot;&middot;])</code></a></h3>
+
+
+<p>
+This function is similar to <a href="#pdf-pcall"><code>pcall</code></a>,
+except that it sets a new message handler <code>msgh</code>.
+
+
+
+
+
+
+
+<h2>6.2 &ndash; <a name="6.2">Coroutine Manipulation</a></h2>
+
+<p>
+This library comprises the operations to manipulate coroutines,
+which come inside the table <a name="pdf-coroutine"><code>coroutine</code></a>.
+See <a href="#2.6">&sect;2.6</a> for a general description of coroutines.
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.close"><code>coroutine.close (co)</code></a></h3>
+
+
+<p>
+Closes coroutine <code>co</code>,
+that is,
+closes all its pending to-be-closed variables
+and puts the coroutine in a dead state.
+The given coroutine must be dead or suspended.
+In case of error
+(either the original error that stopped the coroutine or
+errors in closing methods),
+returns <b>false</b> plus the error object;
+otherwise returns <b>true</b>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.create"><code>coroutine.create (f)</code></a></h3>
+
+
+<p>
+Creates a new coroutine, with body <code>f</code>.
+<code>f</code> must be a function.
+Returns this new coroutine,
+an object with type <code>"thread"</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.isyieldable"><code>coroutine.isyieldable ([co])</code></a></h3>
+
+
+<p>
+Returns <b>true</b> when the coroutine <code>co</code> can yield.
+The default for <code>co</code> is the running coroutine.
+
+
+<p>
+A coroutine is yieldable if it is not the main thread and
+it is not inside a non-yieldable C&nbsp;function.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.resume"><code>coroutine.resume (co [, val1, &middot;&middot;&middot;])</code></a></h3>
+
+
+<p>
+Starts or continues the execution of coroutine <code>co</code>.
+The first time you resume a coroutine,
+it starts running its body.
+The values <code>val1</code>, ... are passed
+as the arguments to the body function.
+If the coroutine has yielded,
+<code>resume</code> restarts it;
+the values <code>val1</code>, ... are passed
+as the results from the yield.
+
+
+<p>
+If the coroutine runs without any errors,
+<code>resume</code> returns <b>true</b> plus any values passed to <code>yield</code>
+(when the coroutine yields) or any values returned by the body function
+(when the coroutine terminates).
+If there is any error,
+<code>resume</code> returns <b>false</b> plus the error message.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.running"><code>coroutine.running ()</code></a></h3>
+
+
+<p>
+Returns the running coroutine plus a boolean,
+<b>true</b> when the running coroutine is the main one.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.status"><code>coroutine.status (co)</code></a></h3>
+
+
+<p>
+Returns the status of the coroutine <code>co</code>, as a string:
+<code>"running"</code>,
+if the coroutine is running
+(that is, it is the one that called <code>status</code>);
+<code>"suspended"</code>, if the coroutine is suspended in a call to <code>yield</code>,
+or if it has not started running yet;
+<code>"normal"</code> if the coroutine is active but not running
+(that is, it has resumed another coroutine);
+and <code>"dead"</code> if the coroutine has finished its body function,
+or if it has stopped with an error.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.wrap"><code>coroutine.wrap (f)</code></a></h3>
+
+
+<p>
+Creates a new coroutine, with body <code>f</code>;
+<code>f</code> must be a function.
+Returns a function that resumes the coroutine each time it is called.
+Any arguments passed to this function behave as the
+extra arguments to <code>resume</code>.
+The function returns the same values returned by <code>resume</code>,
+except the first boolean.
+In case of error,
+the function closes the coroutine and propagates the error.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-coroutine.yield"><code>coroutine.yield (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Suspends the execution of the calling coroutine.
+Any arguments to <code>yield</code> are passed as extra results to <code>resume</code>.
+
+
+
+
+
+
+
+<h2>6.3 &ndash; <a name="6.3">Modules</a></h2>
+
+<p>
+The package library provides basic
+facilities for loading modules in Lua.
+It exports one function directly in the global environment:
+<a href="#pdf-require"><code>require</code></a>.
+Everything else is exported in the table <a name="pdf-package"><code>package</code></a>.
+
+
+<p>
+<hr><h3><a name="pdf-require"><code>require (modname)</code></a></h3>
+
+
+<p>
+Loads the given module.
+The function starts by looking into the <a href="#pdf-package.loaded"><code>package.loaded</code></a> table
+to determine whether <code>modname</code> is already loaded.
+If it is, then <code>require</code> returns the value stored
+at <code>package.loaded[modname]</code>.
+(The absence of a second result in this case
+signals that this call did not have to load the module.)
+Otherwise, it tries to find a <em>loader</em> for the module.
+
+
+<p>
+To find a loader,
+<code>require</code> is guided by the table <a href="#pdf-package.searchers"><code>package.searchers</code></a>.
+Each item in this table is a search function,
+that searches for the module in a particular way.
+By changing this table,
+we can change how <code>require</code> looks for a module.
+The following explanation is based on the default configuration
+for <a href="#pdf-package.searchers"><code>package.searchers</code></a>.
+
+
+<p>
+First <code>require</code> queries <code>package.preload[modname]</code>.
+If it has a value,
+this value (which must be a function) is the loader.
+Otherwise <code>require</code> searches for a Lua loader using the
+path stored in <a href="#pdf-package.path"><code>package.path</code></a>.
+If that also fails, it searches for a C&nbsp;loader using the
+path stored in <a href="#pdf-package.cpath"><code>package.cpath</code></a>.
+If that also fails,
+it tries an <em>all-in-one</em> loader (see <a href="#pdf-package.searchers"><code>package.searchers</code></a>).
+
+
+<p>
+Once a loader is found,
+<code>require</code> calls the loader with two arguments:
+<code>modname</code> and an extra value,
+a <em>loader data</em>,
+also returned by the searcher.
+The loader data can be any value useful to the module;
+for the default searchers,
+it indicates where the loader was found.
+(For instance, if the loader came from a file,
+this extra value is the file path.)
+If the loader returns any non-nil value,
+<code>require</code> assigns the returned value to <code>package.loaded[modname]</code>.
+If the loader does not return a non-nil value and
+has not assigned any value to <code>package.loaded[modname]</code>,
+then <code>require</code> assigns <b>true</b> to this entry.
+In any case, <code>require</code> returns the
+final value of <code>package.loaded[modname]</code>.
+Besides that value, <code>require</code> also returns as a second result
+the loader data returned by the searcher,
+which indicates how <code>require</code> found the module.
+
+
+<p>
+If there is any error loading or running the module,
+or if it cannot find any loader for the module,
+then <code>require</code> raises an error.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.config"><code>package.config</code></a></h3>
+
+
+<p>
+A string describing some compile-time configurations for packages.
+This string is a sequence of lines:
+
+<ul>
+
+<li>The first line is the directory separator string.
+Default is '<code>\</code>' for Windows and '<code>/</code>' for all other systems.</li>
+
+<li>The second line is the character that separates templates in a path.
+Default is '<code>;</code>'.</li>
+
+<li>The third line is the string that marks the
+substitution points in a template.
+Default is '<code>?</code>'.</li>
+
+<li>The fourth line is a string that, in a path in Windows,
+is replaced by the executable's directory.
+Default is '<code>!</code>'.</li>
+
+<li>The fifth line is a mark to ignore all text after it
+when building the <code>luaopen_</code> function name.
+Default is '<code>-</code>'.</li>
+
+</ul>
+
+
+
+<p>
+<hr><h3><a name="pdf-package.cpath"><code>package.cpath</code></a></h3>
+
+
+<p>
+A string with the path used by <a href="#pdf-require"><code>require</code></a>
+to search for a C&nbsp;loader.
+
+
+<p>
+Lua initializes the C&nbsp;path <a href="#pdf-package.cpath"><code>package.cpath</code></a> in the same way
+it initializes the Lua path <a href="#pdf-package.path"><code>package.path</code></a>,
+using the environment variable <a name="pdf-LUA_CPATH_5_4"><code>LUA_CPATH_5_4</code></a>,
+or the environment variable <a name="pdf-LUA_CPATH"><code>LUA_CPATH</code></a>,
+or a default path defined in <code>luaconf.h</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.loaded"><code>package.loaded</code></a></h3>
+
+
+<p>
+A table used by <a href="#pdf-require"><code>require</code></a> to control which
+modules are already loaded.
+When you require a module <code>modname</code> and
+<code>package.loaded[modname]</code> is not false,
+<a href="#pdf-require"><code>require</code></a> simply returns the value stored there.
+
+
+<p>
+This variable is only a reference to the real table;
+assignments to this variable do not change the
+table used by <a href="#pdf-require"><code>require</code></a>.
+The real table is stored in the C registry (see <a href="#4.3">&sect;4.3</a>),
+indexed by the key <a name="pdf-LUA_LOADED_TABLE"><code>LUA_LOADED_TABLE</code></a>, a string.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.loadlib"><code>package.loadlib (libname, funcname)</code></a></h3>
+
+
+<p>
+Dynamically links the host program with the C&nbsp;library <code>libname</code>.
+
+
+<p>
+If <code>funcname</code> is "<code>*</code>",
+then it only links with the library,
+making the symbols exported by the library
+available to other dynamically linked libraries.
+Otherwise,
+it looks for a function <code>funcname</code> inside the library
+and returns this function as a C&nbsp;function.
+So, <code>funcname</code> must follow the <a href="#lua_CFunction"><code>lua_CFunction</code></a> prototype
+(see <a href="#lua_CFunction"><code>lua_CFunction</code></a>).
+
+
+<p>
+This is a low-level function.
+It completely bypasses the package and module system.
+Unlike <a href="#pdf-require"><code>require</code></a>,
+it does not perform any path searching and
+does not automatically adds extensions.
+<code>libname</code> must be the complete file name of the C&nbsp;library,
+including if necessary a path and an extension.
+<code>funcname</code> must be the exact name exported by the C&nbsp;library
+(which may depend on the C&nbsp;compiler and linker used).
+
+
+<p>
+This functionality is not supported by ISO&nbsp;C.
+As such, it is only available on some platforms
+(Windows, Linux, Mac OS X, Solaris, BSD,
+plus other Unix systems that support the <code>dlfcn</code> standard).
+
+
+<p>
+This function is inherently insecure,
+as it allows Lua to call any function in any readable dynamic
+library in the system.
+(Lua calls any function assuming the function
+has a proper prototype and respects a proper protocol
+(see <a href="#lua_CFunction"><code>lua_CFunction</code></a>).
+Therefore,
+calling an arbitrary function in an arbitrary dynamic library
+more often than not results in an access violation.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.path"><code>package.path</code></a></h3>
+
+
+<p>
+A string with the path used by <a href="#pdf-require"><code>require</code></a>
+to search for a Lua loader.
+
+
+<p>
+At start-up, Lua initializes this variable with
+the value of the environment variable <a name="pdf-LUA_PATH_5_4"><code>LUA_PATH_5_4</code></a> or
+the environment variable <a name="pdf-LUA_PATH"><code>LUA_PATH</code></a> or
+with a default path defined in <code>luaconf.h</code>,
+if those environment variables are not defined.
+A "<code>;;</code>" in the value of the environment variable
+is replaced by the default path.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.preload"><code>package.preload</code></a></h3>
+
+
+<p>
+A table to store loaders for specific modules
+(see <a href="#pdf-require"><code>require</code></a>).
+
+
+<p>
+This variable is only a reference to the real table;
+assignments to this variable do not change the
+table used by <a href="#pdf-require"><code>require</code></a>.
+The real table is stored in the C registry (see <a href="#4.3">&sect;4.3</a>),
+indexed by the key <a name="pdf-LUA_PRELOAD_TABLE"><code>LUA_PRELOAD_TABLE</code></a>, a string.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.searchers"><code>package.searchers</code></a></h3>
+
+
+<p>
+A table used by <a href="#pdf-require"><code>require</code></a> to control how to find modules.
+
+
+<p>
+Each entry in this table is a <em>searcher function</em>.
+When looking for a module,
+<a href="#pdf-require"><code>require</code></a> calls each of these searchers in ascending order,
+with the module name (the argument given to <a href="#pdf-require"><code>require</code></a>) as its
+sole argument.
+If the searcher finds the module,
+it returns another function, the module <em>loader</em>,
+plus an extra value, a <em>loader data</em>,
+that will be passed to that loader and
+returned as a second result by <a href="#pdf-require"><code>require</code></a>.
+If it cannot find the module,
+it returns a string explaining why
+(or <b>nil</b> if it has nothing to say).
+
+
+<p>
+Lua initializes this table with four searcher functions.
+
+
+<p>
+The first searcher simply looks for a loader in the
+<a href="#pdf-package.preload"><code>package.preload</code></a> table.
+
+
+<p>
+The second searcher looks for a loader as a Lua library,
+using the path stored at <a href="#pdf-package.path"><code>package.path</code></a>.
+The search is done as described in function <a href="#pdf-package.searchpath"><code>package.searchpath</code></a>.
+
+
+<p>
+The third searcher looks for a loader as a C&nbsp;library,
+using the path given by the variable <a href="#pdf-package.cpath"><code>package.cpath</code></a>.
+Again,
+the search is done as described in function <a href="#pdf-package.searchpath"><code>package.searchpath</code></a>.
+For instance,
+if the C&nbsp;path is the string
+
+<pre>
+ "./?.so;./?.dll;/usr/local/?/init.so"
+</pre><p>
+the searcher for module <code>foo</code>
+will try to open the files <code>./foo.so</code>, <code>./foo.dll</code>,
+and <code>/usr/local/foo/init.so</code>, in that order.
+Once it finds a C&nbsp;library,
+this searcher first uses a dynamic link facility to link the
+application with the library.
+Then it tries to find a C&nbsp;function inside the library to
+be used as the loader.
+The name of this C&nbsp;function is the string "<code>luaopen_</code>"
+concatenated with a copy of the module name where each dot
+is replaced by an underscore.
+Moreover, if the module name has a hyphen,
+its suffix after (and including) the first hyphen is removed.
+For instance, if the module name is <code>a.b.c-v2.1</code>,
+the function name will be <code>luaopen_a_b_c</code>.
+
+
+<p>
+The fourth searcher tries an <em>all-in-one loader</em>.
+It searches the C&nbsp;path for a library for
+the root name of the given module.
+For instance, when requiring <code>a.b.c</code>,
+it will search for a C&nbsp;library for <code>a</code>.
+If found, it looks into it for an open function for
+the submodule;
+in our example, that would be <code>luaopen_a_b_c</code>.
+With this facility, a package can pack several C&nbsp;submodules
+into one single library,
+with each submodule keeping its original open function.
+
+
+<p>
+All searchers except the first one (preload) return as the extra value
+the file path where the module was found,
+as returned by <a href="#pdf-package.searchpath"><code>package.searchpath</code></a>.
+The first searcher always returns the string "<code>:preload:</code>".
+
+
+<p>
+Searchers should raise no errors and have no side effects in Lua.
+(They may have side effects in C,
+for instance by linking the application with a library.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-package.searchpath"><code>package.searchpath (name, path [, sep [, rep]])</code></a></h3>
+
+
+<p>
+Searches for the given <code>name</code> in the given <code>path</code>.
+
+
+<p>
+A path is a string containing a sequence of
+<em>templates</em> separated by semicolons.
+For each template,
+the function replaces each interrogation mark (if any)
+in the template with a copy of <code>name</code>
+wherein all occurrences of <code>sep</code>
+(a dot, by default)
+were replaced by <code>rep</code>
+(the system's directory separator, by default),
+and then tries to open the resulting file name.
+
+
+<p>
+For instance, if the path is the string
+
+<pre>
+ "./?.lua;./?.lc;/usr/local/?/init.lua"
+</pre><p>
+the search for the name <code>foo.a</code>
+will try to open the files
+<code>./foo/a.lua</code>, <code>./foo/a.lc</code>, and
+<code>/usr/local/foo/a/init.lua</code>, in that order.
+
+
+<p>
+Returns the resulting name of the first file that it can
+open in read mode (after closing the file),
+or <b>fail</b> plus an error message if none succeeds.
+(This error message lists all file names it tried to open.)
+
+
+
+
+
+
+
+<h2>6.4 &ndash; <a name="6.4">String Manipulation</a></h2>
+
+
+
+<p>
+This library provides generic functions for string manipulation,
+such as finding and extracting substrings, and pattern matching.
+When indexing a string in Lua, the first character is at position&nbsp;1
+(not at&nbsp;0, as in C).
+Indices are allowed to be negative and are interpreted as indexing backwards,
+from the end of the string.
+Thus, the last character is at position -1, and so on.
+
+
+<p>
+The string library provides all its functions inside the table
+<a name="pdf-string"><code>string</code></a>.
+It also sets a metatable for strings
+where the <code>__index</code> field points to the <code>string</code> table.
+Therefore, you can use the string functions in object-oriented style.
+For instance, <code>string.byte(s,i)</code>
+can be written as <code>s:byte(i)</code>.
+
+
+<p>
+The string library assumes one-byte character encodings.
+
+
+<p>
+<hr><h3><a name="pdf-string.byte"><code>string.byte (s [, i [, j]])</code></a></h3>
+Returns the internal numeric codes of the characters <code>s[i]</code>,
+<code>s[i+1]</code>, ..., <code>s[j]</code>.
+The default value for <code>i</code> is&nbsp;1;
+the default value for <code>j</code> is&nbsp;<code>i</code>.
+These indices are corrected
+following the same rules of function <a href="#pdf-string.sub"><code>string.sub</code></a>.
+
+
+<p>
+Numeric codes are not necessarily portable across platforms.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.char"><code>string.char (&middot;&middot;&middot;)</code></a></h3>
+Receives zero or more integers.
+Returns a string with length equal to the number of arguments,
+in which each character has the internal numeric code equal
+to its corresponding argument.
+
+
+<p>
+Numeric codes are not necessarily portable across platforms.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.dump"><code>string.dump (function [, strip])</code></a></h3>
+
+
+<p>
+Returns a string containing a binary representation
+(a <em>binary chunk</em>)
+of the given function,
+so that a later <a href="#pdf-load"><code>load</code></a> on this string returns
+a copy of the function (but with new upvalues).
+If <code>strip</code> is a true value,
+the binary representation may not include all debug information
+about the function,
+to save space.
+
+
+<p>
+Functions with upvalues have only their number of upvalues saved.
+When (re)loaded,
+those upvalues receive fresh instances.
+(See the <a href="#pdf-load"><code>load</code></a> function for details about
+how these upvalues are initialized.
+You can use the debug library to serialize
+and reload the upvalues of a function
+in a way adequate to your needs.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.find"><code>string.find (s, pattern [, init [, plain]])</code></a></h3>
+
+
+<p>
+Looks for the first match of
+<code>pattern</code> (see <a href="#6.4.1">&sect;6.4.1</a>) in the string <code>s</code>.
+If it finds a match, then <code>find</code> returns the indices of&nbsp;<code>s</code>
+where this occurrence starts and ends;
+otherwise, it returns <b>fail</b>.
+A third, optional numeric argument <code>init</code> specifies
+where to start the search;
+its default value is&nbsp;1 and can be negative.
+A <b>true</b> as a fourth, optional argument <code>plain</code>
+turns off the pattern matching facilities,
+so the function does a plain "find substring" operation,
+with no characters in <code>pattern</code> being considered magic.
+
+
+<p>
+If the pattern has captures,
+then in a successful match
+the captured values are also returned,
+after the two indices.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.format"><code>string.format (formatstring, &middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Returns a formatted version of its variable number of arguments
+following the description given in its first argument,
+which must be a string.
+The format string follows the same rules as the ISO&nbsp;C function <code>sprintf</code>.
+The only differences are that the conversion specifiers and modifiers
+<code>F</code>, <code>n</code>, <code>*</code>, <code>h</code>, <code>L</code>, and <code>l</code> are not supported
+and that there is an extra specifier, <code>q</code>.
+Both width and precision, when present,
+are limited to two digits.
+
+
+<p>
+The specifier <code>q</code> formats booleans, nil, numbers, and strings
+in a way that the result is a valid constant in Lua source code.
+Booleans and nil are written in the obvious way
+(<code>true</code>, <code>false</code>, <code>nil</code>).
+Floats are written in hexadecimal,
+to preserve full precision.
+A string is written between double quotes,
+using escape sequences when necessary to ensure that
+it can safely be read back by the Lua interpreter.
+For instance, the call
+
+<pre>
+ string.format('%q', 'a string with "quotes" and \n new line')
+</pre><p>
+may produce the string:
+
+<pre>
+ "a string with \"quotes\" and \
+ new line"
+</pre><p>
+This specifier does not support modifiers (flags, width, precision).
+
+
+<p>
+The conversion specifiers
+<code>A</code>, <code>a</code>, <code>E</code>, <code>e</code>, <code>f</code>,
+<code>G</code>, and <code>g</code> all expect a number as argument.
+The specifiers <code>c</code>, <code>d</code>,
+<code>i</code>, <code>o</code>, <code>u</code>, <code>X</code>, and <code>x</code>
+expect an integer.
+When Lua is compiled with a C89 compiler,
+the specifiers <code>A</code> and <code>a</code> (hexadecimal floats)
+do not support modifiers.
+
+
+<p>
+The specifier <code>s</code> expects a string;
+if its argument is not a string,
+it is converted to one following the same rules of <a href="#pdf-tostring"><code>tostring</code></a>.
+If the specifier has any modifier,
+the corresponding string argument should not contain embedded zeros.
+
+
+<p>
+The specifier <code>p</code> formats the pointer
+returned by <a href="#lua_topointer"><code>lua_topointer</code></a>.
+That gives a unique string identifier for tables, userdata,
+threads, strings, and functions.
+For other values (numbers, nil, booleans),
+this specifier results in a string representing
+the pointer <code>NULL</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.gmatch"><code>string.gmatch (s, pattern [, init])</code></a></h3>
+Returns an iterator function that,
+each time it is called,
+returns the next captures from <code>pattern</code> (see <a href="#6.4.1">&sect;6.4.1</a>)
+over the string <code>s</code>.
+If <code>pattern</code> specifies no captures,
+then the whole match is produced in each call.
+A third, optional numeric argument <code>init</code> specifies
+where to start the search;
+its default value is&nbsp;1 and can be negative.
+
+
+<p>
+As an example, the following loop
+will iterate over all the words from string <code>s</code>,
+printing one per line:
+
+<pre>
+ s = "hello world from Lua"
+ for w in string.gmatch(s, "%a+") do
+ print(w)
+ end
+</pre><p>
+The next example collects all pairs <code>key=value</code> from the
+given string into a table:
+
+<pre>
+ t = {}
+ s = "from=world, to=Lua"
+ for k, v in string.gmatch(s, "(%w+)=(%w+)") do
+ t[k] = v
+ end
+</pre>
+
+<p>
+For this function, a caret '<code>^</code>' at the start of a pattern does not
+work as an anchor, as this would prevent the iteration.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.gsub"><code>string.gsub (s, pattern, repl [, n])</code></a></h3>
+Returns a copy of <code>s</code>
+in which all (or the first <code>n</code>, if given)
+occurrences of the <code>pattern</code> (see <a href="#6.4.1">&sect;6.4.1</a>) have been
+replaced by a replacement string specified by <code>repl</code>,
+which can be a string, a table, or a function.
+<code>gsub</code> also returns, as its second value,
+the total number of matches that occurred.
+The name <code>gsub</code> comes from <em>Global SUBstitution</em>.
+
+
+<p>
+If <code>repl</code> is a string, then its value is used for replacement.
+The character&nbsp;<code>%</code> works as an escape character:
+any sequence in <code>repl</code> of the form <code>%<em>d</em></code>,
+with <em>d</em> between 1 and 9,
+stands for the value of the <em>d</em>-th captured substring;
+the sequence <code>%0</code> stands for the whole match;
+the sequence <code>%%</code> stands for a single&nbsp;<code>%</code>.
+
+
+<p>
+If <code>repl</code> is a table, then the table is queried for every match,
+using the first capture as the key.
+
+
+<p>
+If <code>repl</code> is a function, then this function is called every time a
+match occurs, with all captured substrings passed as arguments,
+in order.
+
+
+<p>
+In any case,
+if the pattern specifies no captures,
+then it behaves as if the whole pattern was inside a capture.
+
+
+<p>
+If the value returned by the table query or by the function call
+is a string or a number,
+then it is used as the replacement string;
+otherwise, if it is <b>false</b> or <b>nil</b>,
+then there is no replacement
+(that is, the original match is kept in the string).
+
+
+<p>
+Here are some examples:
+
+<pre>
+ x = string.gsub("hello world", "(%w+)", "%1 %1")
+ --&gt; x="hello hello world world"
+
+ x = string.gsub("hello world", "%w+", "%0 %0", 1)
+ --&gt; x="hello hello world"
+
+ x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1")
+ --&gt; x="world hello Lua from"
+
+ x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv)
+ --&gt; x="home = /home/roberto, user = roberto"
+
+ x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s)
+ return load(s)()
+ end)
+ --&gt; x="4+5 = 9"
+
+ local t = {name="lua", version="5.4"}
+ x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
+ --&gt; x="lua-5.4.tar.gz"
+</pre>
+
+
+
+<p>
+<hr><h3><a name="pdf-string.len"><code>string.len (s)</code></a></h3>
+
+
+<p>
+Receives a string and returns its length.
+The empty string <code>""</code> has length 0.
+Embedded zeros are counted,
+so <code>"a\000bc\000"</code> has length 5.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.lower"><code>string.lower (s)</code></a></h3>
+
+
+<p>
+Receives a string and returns a copy of this string with all
+uppercase letters changed to lowercase.
+All other characters are left unchanged.
+The definition of what an uppercase letter is depends on the current locale.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.match"><code>string.match (s, pattern [, init])</code></a></h3>
+
+
+<p>
+Looks for the first <em>match</em> of
+the <code>pattern</code> (see <a href="#6.4.1">&sect;6.4.1</a>) in the string <code>s</code>.
+If it finds one, then <code>match</code> returns
+the captures from the pattern;
+otherwise it returns <b>fail</b>.
+If <code>pattern</code> specifies no captures,
+then the whole match is returned.
+A third, optional numeric argument <code>init</code> specifies
+where to start the search;
+its default value is&nbsp;1 and can be negative.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.pack"><code>string.pack (fmt, v1, v2, &middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Returns a binary string containing the values <code>v1</code>, <code>v2</code>, etc.
+serialized in binary form (packed)
+according to the format string <code>fmt</code> (see <a href="#6.4.2">&sect;6.4.2</a>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.packsize"><code>string.packsize (fmt)</code></a></h3>
+
+
+<p>
+Returns the length of a string resulting from <a href="#pdf-string.pack"><code>string.pack</code></a>
+with the given format.
+The format string cannot have the variable-length options
+'<code>s</code>' or '<code>z</code>' (see <a href="#6.4.2">&sect;6.4.2</a>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.rep"><code>string.rep (s, n [, sep])</code></a></h3>
+
+
+<p>
+Returns a string that is the concatenation of <code>n</code> copies of
+the string <code>s</code> separated by the string <code>sep</code>.
+The default value for <code>sep</code> is the empty string
+(that is, no separator).
+Returns the empty string if <code>n</code> is not positive.
+
+
+<p>
+(Note that it is very easy to exhaust the memory of your machine
+with a single call to this function.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.reverse"><code>string.reverse (s)</code></a></h3>
+
+
+<p>
+Returns a string that is the string <code>s</code> reversed.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.sub"><code>string.sub (s, i [, j])</code></a></h3>
+
+
+<p>
+Returns the substring of <code>s</code> that
+starts at <code>i</code> and continues until <code>j</code>;
+<code>i</code> and <code>j</code> can be negative.
+If <code>j</code> is absent, then it is assumed to be equal to -1
+(which is the same as the string length).
+In particular,
+the call <code>string.sub(s,1,j)</code> returns a prefix of <code>s</code>
+with length <code>j</code>,
+and <code>string.sub(s, -i)</code> (for a positive <code>i</code>)
+returns a suffix of <code>s</code>
+with length <code>i</code>.
+
+
+<p>
+If, after the translation of negative indices,
+<code>i</code> is less than 1,
+it is corrected to 1.
+If <code>j</code> is greater than the string length,
+it is corrected to that length.
+If, after these corrections,
+<code>i</code> is greater than <code>j</code>,
+the function returns the empty string.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.unpack"><code>string.unpack (fmt, s [, pos])</code></a></h3>
+
+
+<p>
+Returns the values packed in string <code>s</code> (see <a href="#pdf-string.pack"><code>string.pack</code></a>)
+according to the format string <code>fmt</code> (see <a href="#6.4.2">&sect;6.4.2</a>).
+An optional <code>pos</code> marks where
+to start reading in <code>s</code> (default is 1).
+After the read values,
+this function also returns the index of the first unread byte in <code>s</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-string.upper"><code>string.upper (s)</code></a></h3>
+
+
+<p>
+Receives a string and returns a copy of this string with all
+lowercase letters changed to uppercase.
+All other characters are left unchanged.
+The definition of what a lowercase letter is depends on the current locale.
+
+
+
+
+
+
+
+<h3>6.4.1 &ndash; <a name="6.4.1">Patterns</a></h3>
+
+
+
+<p>
+Patterns in Lua are described by regular strings,
+which are interpreted as patterns by the pattern-matching functions
+<a href="#pdf-string.find"><code>string.find</code></a>,
+<a href="#pdf-string.gmatch"><code>string.gmatch</code></a>,
+<a href="#pdf-string.gsub"><code>string.gsub</code></a>,
+and <a href="#pdf-string.match"><code>string.match</code></a>.
+This section describes the syntax and the meaning
+(that is, what they match) of these strings.
+
+
+
+
+
+<h4>Character Class:</h4><p>
+A <em>character class</em> is used to represent a set of characters.
+The following combinations are allowed in describing a character class:
+
+<ul>
+
+<li><b><em>x</em>: </b>
+(where <em>x</em> is not one of the <em>magic characters</em>
+<code>^$()%.[]*+-?</code>)
+represents the character <em>x</em> itself.
+</li>
+
+<li><b><code>.</code>: </b> (a dot) represents all characters.</li>
+
+<li><b><code>%a</code>: </b> represents all letters.</li>
+
+<li><b><code>%c</code>: </b> represents all control characters.</li>
+
+<li><b><code>%d</code>: </b> represents all digits.</li>
+
+<li><b><code>%g</code>: </b> represents all printable characters except space.</li>
+
+<li><b><code>%l</code>: </b> represents all lowercase letters.</li>
+
+<li><b><code>%p</code>: </b> represents all punctuation characters.</li>
+
+<li><b><code>%s</code>: </b> represents all space characters.</li>
+
+<li><b><code>%u</code>: </b> represents all uppercase letters.</li>
+
+<li><b><code>%w</code>: </b> represents all alphanumeric characters.</li>
+
+<li><b><code>%x</code>: </b> represents all hexadecimal digits.</li>
+
+<li><b><code>%<em>x</em></code>: </b> (where <em>x</em> is any non-alphanumeric character)
+represents the character <em>x</em>.
+This is the standard way to escape the magic characters.
+Any non-alphanumeric character
+(including all punctuation characters, even the non-magical)
+can be preceded by a '<code>%</code>' to represent itself in a pattern.
+</li>
+
+<li><b><code>[<em>set</em>]</code>: </b>
+represents the class which is the union of all
+characters in <em>set</em>.
+A range of characters can be specified by
+separating the end characters of the range,
+in ascending order, with a '<code>-</code>'.
+All classes <code>%</code><em>x</em> described above can also be used as
+components in <em>set</em>.
+All other characters in <em>set</em> represent themselves.
+For example, <code>[%w_]</code> (or <code>[_%w]</code>)
+represents all alphanumeric characters plus the underscore,
+<code>[0-7]</code> represents the octal digits,
+and <code>[0-7%l%-]</code> represents the octal digits plus
+the lowercase letters plus the '<code>-</code>' character.
+
+
+<p>
+You can put a closing square bracket in a set
+by positioning it as the first character in the set.
+You can put a hyphen in a set
+by positioning it as the first or the last character in the set.
+(You can also use an escape for both cases.)
+
+
+<p>
+The interaction between ranges and classes is not defined.
+Therefore, patterns like <code>[%a-z]</code> or <code>[a-%%]</code>
+have no meaning.
+</li>
+
+<li><b><code>[^<em>set</em>]</code>: </b>
+represents the complement of <em>set</em>,
+where <em>set</em> is interpreted as above.
+</li>
+
+</ul><p>
+For all classes represented by single letters (<code>%a</code>, <code>%c</code>, etc.),
+the corresponding uppercase letter represents the complement of the class.
+For instance, <code>%S</code> represents all non-space characters.
+
+
+<p>
+The definitions of letter, space, and other character groups
+depend on the current locale.
+In particular, the class <code>[a-z]</code> may not be equivalent to <code>%l</code>.
+
+
+
+
+
+<h4>Pattern Item:</h4><p>
+A <em>pattern item</em> can be
+
+<ul>
+
+<li>
+a single character class,
+which matches any single character in the class;
+</li>
+
+<li>
+a single character class followed by '<code>*</code>',
+which matches sequences of zero or more characters in the class.
+These repetition items will always match the longest possible sequence;
+</li>
+
+<li>
+a single character class followed by '<code>+</code>',
+which matches sequences of one or more characters in the class.
+These repetition items will always match the longest possible sequence;
+</li>
+
+<li>
+a single character class followed by '<code>-</code>',
+which also matches sequences of zero or more characters in the class.
+Unlike '<code>*</code>',
+these repetition items will always match the shortest possible sequence;
+</li>
+
+<li>
+a single character class followed by '<code>?</code>',
+which matches zero or one occurrence of a character in the class.
+It always matches one occurrence if possible;
+</li>
+
+<li>
+<code>%<em>n</em></code>, for <em>n</em> between 1 and 9;
+such item matches a substring equal to the <em>n</em>-th captured string
+(see below);
+</li>
+
+<li>
+<code>%b<em>xy</em></code>, where <em>x</em> and <em>y</em> are two distinct characters;
+such item matches strings that start with&nbsp;<em>x</em>, end with&nbsp;<em>y</em>,
+and where the <em>x</em> and <em>y</em> are <em>balanced</em>.
+This means that, if one reads the string from left to right,
+counting <em>+1</em> for an <em>x</em> and <em>-1</em> for a <em>y</em>,
+the ending <em>y</em> is the first <em>y</em> where the count reaches 0.
+For instance, the item <code>%b()</code> matches expressions with
+balanced parentheses.
+</li>
+
+<li>
+<code>%f[<em>set</em>]</code>, a <em>frontier pattern</em>;
+such item matches an empty string at any position such that
+the next character belongs to <em>set</em>
+and the previous character does not belong to <em>set</em>.
+The set <em>set</em> is interpreted as previously described.
+The beginning and the end of the subject are handled as if
+they were the character '<code>\0</code>'.
+</li>
+
+</ul>
+
+
+
+
+<h4>Pattern:</h4><p>
+A <em>pattern</em> is a sequence of pattern items.
+A caret '<code>^</code>' at the beginning of a pattern anchors the match at the
+beginning of the subject string.
+A '<code>$</code>' at the end of a pattern anchors the match at the
+end of the subject string.
+At other positions,
+'<code>^</code>' and '<code>$</code>' have no special meaning and represent themselves.
+
+
+
+
+
+<h4>Captures:</h4><p>
+A pattern can contain sub-patterns enclosed in parentheses;
+they describe <em>captures</em>.
+When a match succeeds, the substrings of the subject string
+that match captures are stored (<em>captured</em>) for future use.
+Captures are numbered according to their left parentheses.
+For instance, in the pattern <code>"(a*(.)%w(%s*))"</code>,
+the part of the string matching <code>"a*(.)%w(%s*)"</code> is
+stored as the first capture, and therefore has number&nbsp;1;
+the character matching "<code>.</code>" is captured with number&nbsp;2,
+and the part matching "<code>%s*</code>" has number&nbsp;3.
+
+
+<p>
+As a special case, the capture <code>()</code> captures
+the current string position (a number).
+For instance, if we apply the pattern <code>"()aa()"</code> on the
+string <code>"flaaap"</code>, there will be two captures: 3&nbsp;and&nbsp;5.
+
+
+
+
+
+<h4>Multiple matches:</h4><p>
+The function <a href="#pdf-string.gsub"><code>string.gsub</code></a> and the iterator <a href="#pdf-string.gmatch"><code>string.gmatch</code></a>
+match multiple occurrences of the given pattern in the subject.
+For these functions,
+a new match is considered valid only
+if it ends at least one byte after the end of the previous match.
+In other words, the pattern machine never accepts the
+empty string as a match immediately after another match.
+As an example,
+consider the results of the following code:
+
+<pre>
+ &gt; string.gsub("abc", "()a*()", print);
+ --&gt; 1 2
+ --&gt; 3 3
+ --&gt; 4 4
+</pre><p>
+The second and third results come from Lua matching an empty
+string after '<code>b</code>' and another one after '<code>c</code>'.
+Lua does not match an empty string after '<code>a</code>',
+because it would end at the same position of the previous match.
+
+
+
+
+
+
+
+<h3>6.4.2 &ndash; <a name="6.4.2">Format Strings for Pack and Unpack</a></h3>
+
+<p>
+The first argument to <a href="#pdf-string.pack"><code>string.pack</code></a>,
+<a href="#pdf-string.packsize"><code>string.packsize</code></a>, and <a href="#pdf-string.unpack"><code>string.unpack</code></a>
+is a format string,
+which describes the layout of the structure being created or read.
+
+
+<p>
+A format string is a sequence of conversion options.
+The conversion options are as follows:
+
+<ul>
+<li><b><code>&lt;</code>: </b>sets little endian</li>
+<li><b><code>&gt;</code>: </b>sets big endian</li>
+<li><b><code>=</code>: </b>sets native endian</li>
+<li><b><code>![<em>n</em>]</code>: </b>sets maximum alignment to <code>n</code>
+(default is native alignment)</li>
+<li><b><code>b</code>: </b>a signed byte (<code>char</code>)</li>
+<li><b><code>B</code>: </b>an unsigned byte (<code>char</code>)</li>
+<li><b><code>h</code>: </b>a signed <code>short</code> (native size)</li>
+<li><b><code>H</code>: </b>an unsigned <code>short</code> (native size)</li>
+<li><b><code>l</code>: </b>a signed <code>long</code> (native size)</li>
+<li><b><code>L</code>: </b>an unsigned <code>long</code> (native size)</li>
+<li><b><code>j</code>: </b>a <code>lua_Integer</code></li>
+<li><b><code>J</code>: </b>a <code>lua_Unsigned</code></li>
+<li><b><code>T</code>: </b>a <code>size_t</code> (native size)</li>
+<li><b><code>i[<em>n</em>]</code>: </b>a signed <code>int</code> with <code>n</code> bytes
+(default is native size)</li>
+<li><b><code>I[<em>n</em>]</code>: </b>an unsigned <code>int</code> with <code>n</code> bytes
+(default is native size)</li>
+<li><b><code>f</code>: </b>a <code>float</code> (native size)</li>
+<li><b><code>d</code>: </b>a <code>double</code> (native size)</li>
+<li><b><code>n</code>: </b>a <code>lua_Number</code></li>
+<li><b><code>c<em>n</em></code>: </b>a fixed-sized string with <code>n</code> bytes</li>
+<li><b><code>z</code>: </b>a zero-terminated string</li>
+<li><b><code>s[<em>n</em>]</code>: </b>a string preceded by its length
+coded as an unsigned integer with <code>n</code> bytes
+(default is a <code>size_t</code>)</li>
+<li><b><code>x</code>: </b>one byte of padding</li>
+<li><b><code>X<em>op</em></code>: </b>an empty item that aligns
+according to option <code>op</code>
+(which is otherwise ignored)</li>
+<li><b>'<code> </code>': </b>(space) ignored</li>
+</ul><p>
+(A "<code>[<em>n</em>]</code>" means an optional integral numeral.)
+Except for padding, spaces, and configurations
+(options "<code>xX &lt;=&gt;!</code>"),
+each option corresponds to an argument in <a href="#pdf-string.pack"><code>string.pack</code></a>
+or a result in <a href="#pdf-string.unpack"><code>string.unpack</code></a>.
+
+
+<p>
+For options "<code>!<em>n</em></code>", "<code>s<em>n</em></code>", "<code>i<em>n</em></code>", and "<code>I<em>n</em></code>",
+<code>n</code> can be any integer between 1 and 16.
+All integral options check overflows;
+<a href="#pdf-string.pack"><code>string.pack</code></a> checks whether the given value fits in the given size;
+<a href="#pdf-string.unpack"><code>string.unpack</code></a> checks whether the read value fits in a Lua integer.
+For the unsigned options,
+Lua integers are treated as unsigned values too.
+
+
+<p>
+Any format string starts as if prefixed by "<code>!1=</code>",
+that is,
+with maximum alignment of 1 (no alignment)
+and native endianness.
+
+
+<p>
+Native endianness assumes that the whole system is
+either big or little endian.
+The packing functions will not emulate correctly the behavior
+of mixed-endian formats.
+
+
+<p>
+Alignment works as follows:
+For each option,
+the format gets extra padding until the data starts
+at an offset that is a multiple of the minimum between the
+option size and the maximum alignment;
+this minimum must be a power of 2.
+Options "<code>c</code>" and "<code>z</code>" are not aligned;
+option "<code>s</code>" follows the alignment of its starting integer.
+
+
+<p>
+All padding is filled with zeros by <a href="#pdf-string.pack"><code>string.pack</code></a>
+and ignored by <a href="#pdf-string.unpack"><code>string.unpack</code></a>.
+
+
+
+
+
+
+
+<h2>6.5 &ndash; <a name="6.5">UTF-8 Support</a></h2>
+
+<p>
+This library provides basic support for UTF-8 encoding.
+It provides all its functions inside the table <a name="pdf-utf8"><code>utf8</code></a>.
+This library does not provide any support for Unicode other
+than the handling of the encoding.
+Any operation that needs the meaning of a character,
+such as character classification, is outside its scope.
+
+
+<p>
+Unless stated otherwise,
+all functions that expect a byte position as a parameter
+assume that the given position is either the start of a byte sequence
+or one plus the length of the subject string.
+As in the string library,
+negative indices count from the end of the string.
+
+
+<p>
+Functions that create byte sequences
+accept all values up to <code>0x7FFFFFFF</code>,
+as defined in the original UTF-8 specification;
+that implies byte sequences of up to six bytes.
+
+
+<p>
+Functions that interpret byte sequences only accept
+valid sequences (well formed and not overlong).
+By default, they only accept byte sequences
+that result in valid Unicode code points,
+rejecting values greater than <code>10FFFF</code> and surrogates.
+A boolean argument <code>lax</code>, when available,
+lifts these checks,
+so that all values up to <code>0x7FFFFFFF</code> are accepted.
+(Not well formed and overlong sequences are still rejected.)
+
+
+<p>
+<hr><h3><a name="pdf-utf8.char"><code>utf8.char (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Receives zero or more integers,
+converts each one to its corresponding UTF-8 byte sequence
+and returns a string with the concatenation of all these sequences.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-utf8.charpattern"><code>utf8.charpattern</code></a></h3>
+
+
+<p>
+The pattern (a string, not a function) "<code>[\0-\x7F\xC2-\xFD][\x80-\xBF]*</code>"
+(see <a href="#6.4.1">&sect;6.4.1</a>),
+which matches exactly one UTF-8 byte sequence,
+assuming that the subject is a valid UTF-8 string.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-utf8.codes"><code>utf8.codes (s [, lax])</code></a></h3>
+
+
+<p>
+Returns values so that the construction
+
+<pre>
+ for p, c in utf8.codes(s) do <em>body</em> end
+</pre><p>
+will iterate over all UTF-8 characters in string <code>s</code>,
+with <code>p</code> being the position (in bytes) and <code>c</code> the code point
+of each character.
+It raises an error if it meets any invalid byte sequence.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-utf8.codepoint"><code>utf8.codepoint (s [, i [, j [, lax]]])</code></a></h3>
+
+
+<p>
+Returns the code points (as integers) from all characters in <code>s</code>
+that start between byte position <code>i</code> and <code>j</code> (both included).
+The default for <code>i</code> is 1 and for <code>j</code> is <code>i</code>.
+It raises an error if it meets any invalid byte sequence.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-utf8.len"><code>utf8.len (s [, i [, j [, lax]]])</code></a></h3>
+
+
+<p>
+Returns the number of UTF-8 characters in string <code>s</code>
+that start between positions <code>i</code> and <code>j</code> (both inclusive).
+The default for <code>i</code> is 1 and for <code>j</code> is -1.
+If it finds any invalid byte sequence,
+returns <b>fail</b> plus the position of the first invalid byte.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-utf8.offset"><code>utf8.offset (s, n [, i])</code></a></h3>
+
+
+<p>
+Returns the position (in bytes) where the encoding of the
+<code>n</code>-th character of <code>s</code>
+(counting from position <code>i</code>) starts.
+A negative <code>n</code> gets characters before position <code>i</code>.
+The default for <code>i</code> is 1 when <code>n</code> is non-negative
+and <code>#s + 1</code> otherwise,
+so that <code>utf8.offset(s, -n)</code> gets the offset of the
+<code>n</code>-th character from the end of the string.
+If the specified character is neither in the subject
+nor right after its end,
+the function returns <b>fail</b>.
+
+
+<p>
+As a special case,
+when <code>n</code> is 0 the function returns the start of the encoding
+of the character that contains the <code>i</code>-th byte of <code>s</code>.
+
+
+<p>
+This function assumes that <code>s</code> is a valid UTF-8 string.
+
+
+
+
+
+
+
+<h2>6.6 &ndash; <a name="6.6">Table Manipulation</a></h2>
+
+<p>
+This library provides generic functions for table manipulation.
+It provides all its functions inside the table <a name="pdf-table"><code>table</code></a>.
+
+
+<p>
+Remember that, whenever an operation needs the length of a table,
+all caveats about the length operator apply (see <a href="#3.4.7">&sect;3.4.7</a>).
+All functions ignore non-numeric keys
+in the tables given as arguments.
+
+
+<p>
+<hr><h3><a name="pdf-table.concat"><code>table.concat (list [, sep [, i [, j]]])</code></a></h3>
+
+
+<p>
+Given a list where all elements are strings or numbers,
+returns the string <code>list[i]..sep..list[i+1] &middot;&middot;&middot; sep..list[j]</code>.
+The default value for <code>sep</code> is the empty string,
+the default for <code>i</code> is 1,
+and the default for <code>j</code> is <code>#list</code>.
+If <code>i</code> is greater than <code>j</code>, returns the empty string.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-table.insert"><code>table.insert (list, [pos,] value)</code></a></h3>
+
+
+<p>
+Inserts element <code>value</code> at position <code>pos</code> in <code>list</code>,
+shifting up the elements
+<code>list[pos], list[pos+1], &middot;&middot;&middot;, list[#list]</code>.
+The default value for <code>pos</code> is <code>#list+1</code>,
+so that a call <code>table.insert(t,x)</code> inserts <code>x</code> at the end
+of the list <code>t</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-table.move"><code>table.move (a1, f, e, t [,a2])</code></a></h3>
+
+
+<p>
+Moves elements from the table <code>a1</code> to the table <code>a2</code>,
+performing the equivalent to the following
+multiple assignment:
+<code>a2[t],&middot;&middot;&middot; = a1[f],&middot;&middot;&middot;,a1[e]</code>.
+The default for <code>a2</code> is <code>a1</code>.
+The destination range can overlap with the source range.
+The number of elements to be moved must fit in a Lua integer.
+
+
+<p>
+Returns the destination table <code>a2</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-table.pack"><code>table.pack (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Returns a new table with all arguments stored into keys 1, 2, etc.
+and with a field "<code>n</code>" with the total number of arguments.
+Note that the resulting table may not be a sequence,
+if some arguments are <b>nil</b>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-table.remove"><code>table.remove (list [, pos])</code></a></h3>
+
+
+<p>
+Removes from <code>list</code> the element at position <code>pos</code>,
+returning the value of the removed element.
+When <code>pos</code> is an integer between 1 and <code>#list</code>,
+it shifts down the elements
+<code>list[pos+1], list[pos+2], &middot;&middot;&middot;, list[#list]</code>
+and erases element <code>list[#list]</code>;
+The index <code>pos</code> can also be 0 when <code>#list</code> is 0,
+or <code>#list + 1</code>.
+
+
+<p>
+The default value for <code>pos</code> is <code>#list</code>,
+so that a call <code>table.remove(l)</code> removes the last element
+of the list <code>l</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-table.sort"><code>table.sort (list [, comp])</code></a></h3>
+
+
+<p>
+Sorts the list elements in a given order, <em>in-place</em>,
+from <code>list[1]</code> to <code>list[#list]</code>.
+If <code>comp</code> is given,
+then it must be a function that receives two list elements
+and returns true when the first element must come
+before the second in the final order,
+so that, after the sort,
+<code>i &lt;= j</code> implies <code>not comp(list[j],list[i])</code>.
+If <code>comp</code> is not given,
+then the standard Lua operator <code>&lt;</code> is used instead.
+
+
+<p>
+The <code>comp</code> function must define a consistent order;
+more formally, the function must define a strict weak order.
+(A weak order is similar to a total order,
+but it can equate different elements for comparison purposes.)
+
+
+<p>
+The sort algorithm is not stable:
+Different elements considered equal by the given order
+may have their relative positions changed by the sort.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-table.unpack"><code>table.unpack (list [, i [, j]])</code></a></h3>
+
+
+<p>
+Returns the elements from the given list.
+This function is equivalent to
+
+<pre>
+ return list[i], list[i+1], &middot;&middot;&middot;, list[j]
+</pre><p>
+By default, <code>i</code> is&nbsp;1 and <code>j</code> is <code>#list</code>.
+
+
+
+
+
+
+
+<h2>6.7 &ndash; <a name="6.7">Mathematical Functions</a></h2>
+
+<p>
+This library provides basic mathematical functions.
+It provides all its functions and constants inside the table <a name="pdf-math"><code>math</code></a>.
+Functions with the annotation "<code>integer/float</code>" give
+integer results for integer arguments
+and float results for non-integer arguments.
+The rounding functions
+<a href="#pdf-math.ceil"><code>math.ceil</code></a>, <a href="#pdf-math.floor"><code>math.floor</code></a>, and <a href="#pdf-math.modf"><code>math.modf</code></a>
+return an integer when the result fits in the range of an integer,
+or a float otherwise.
+
+
+<p>
+<hr><h3><a name="pdf-math.abs"><code>math.abs (x)</code></a></h3>
+
+
+<p>
+Returns the maximum value between <code>x</code> and <code>-x</code>. (integer/float)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.acos"><code>math.acos (x)</code></a></h3>
+
+
+<p>
+Returns the arc cosine of <code>x</code> (in radians).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.asin"><code>math.asin (x)</code></a></h3>
+
+
+<p>
+Returns the arc sine of <code>x</code> (in radians).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.atan"><code>math.atan (y [, x])</code></a></h3>
+
+
+<p>
+
+Returns the arc tangent of <code>y/x</code> (in radians),
+using the signs of both arguments to find the
+quadrant of the result.
+It also handles correctly the case of <code>x</code> being zero.
+
+
+<p>
+The default value for <code>x</code> is 1,
+so that the call <code>math.atan(y)</code>
+returns the arc tangent of <code>y</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.ceil"><code>math.ceil (x)</code></a></h3>
+
+
+<p>
+Returns the smallest integral value greater than or equal to <code>x</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.cos"><code>math.cos (x)</code></a></h3>
+
+
+<p>
+Returns the cosine of <code>x</code> (assumed to be in radians).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.deg"><code>math.deg (x)</code></a></h3>
+
+
+<p>
+Converts the angle <code>x</code> from radians to degrees.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.exp"><code>math.exp (x)</code></a></h3>
+
+
+<p>
+Returns the value <em>e<sup>x</sup></em>
+(where <code>e</code> is the base of natural logarithms).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.floor"><code>math.floor (x)</code></a></h3>
+
+
+<p>
+Returns the largest integral value less than or equal to <code>x</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.fmod"><code>math.fmod (x, y)</code></a></h3>
+
+
+<p>
+Returns the remainder of the division of <code>x</code> by <code>y</code>
+that rounds the quotient towards zero. (integer/float)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.huge"><code>math.huge</code></a></h3>
+
+
+<p>
+The float value <code>HUGE_VAL</code>,
+a value greater than any other numeric value.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.log"><code>math.log (x [, base])</code></a></h3>
+
+
+<p>
+Returns the logarithm of <code>x</code> in the given base.
+The default for <code>base</code> is <em>e</em>
+(so that the function returns the natural logarithm of <code>x</code>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.max"><code>math.max (x, &middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Returns the argument with the maximum value,
+according to the Lua operator <code>&lt;</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.maxinteger"><code>math.maxinteger</code></a></h3>
+An integer with the maximum value for an integer.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.min"><code>math.min (x, &middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Returns the argument with the minimum value,
+according to the Lua operator <code>&lt;</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.mininteger"><code>math.mininteger</code></a></h3>
+An integer with the minimum value for an integer.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.modf"><code>math.modf (x)</code></a></h3>
+
+
+<p>
+Returns the integral part of <code>x</code> and the fractional part of <code>x</code>.
+Its second result is always a float.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.pi"><code>math.pi</code></a></h3>
+
+
+<p>
+The value of <em>&pi;</em>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.rad"><code>math.rad (x)</code></a></h3>
+
+
+<p>
+Converts the angle <code>x</code> from degrees to radians.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.random"><code>math.random ([m [, n]])</code></a></h3>
+
+
+<p>
+When called without arguments,
+returns a pseudo-random float with uniform distribution
+in the range <em>[0,1)</em>.
+When called with two integers <code>m</code> and <code>n</code>,
+<code>math.random</code> returns a pseudo-random integer
+with uniform distribution in the range <em>[m, n]</em>.
+The call <code>math.random(n)</code>, for a positive <code>n</code>,
+is equivalent to <code>math.random(1,n)</code>.
+The call <code>math.random(0)</code> produces an integer with
+all bits (pseudo)random.
+
+
+<p>
+This function uses the <code>xoshiro256**</code> algorithm to produce
+pseudo-random 64-bit integers,
+which are the results of calls with argument&nbsp;0.
+Other results (ranges and floats)
+are unbiased extracted from these integers.
+
+
+<p>
+Lua initializes its pseudo-random generator with the equivalent of
+a call to <a href="#pdf-math.randomseed"><code>math.randomseed</code></a> with no arguments,
+so that <code>math.random</code> should generate
+different sequences of results each time the program runs.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.randomseed"><code>math.randomseed ([x [, y]])</code></a></h3>
+
+
+<p>
+When called with at least one argument,
+the integer parameters <code>x</code> and <code>y</code> are
+joined into a 128-bit <em>seed</em> that
+is used to reinitialize the pseudo-random generator;
+equal seeds produce equal sequences of numbers.
+The default for <code>y</code> is zero.
+
+
+<p>
+When called with no arguments,
+Lua generates a seed with
+a weak attempt for randomness.
+
+
+<p>
+This function returns the two seed components
+that were effectively used,
+so that setting them again repeats the sequence.
+
+
+<p>
+To ensure a required level of randomness to the initial state
+(or contrarily, to have a deterministic sequence,
+for instance when debugging a program),
+you should call <a href="#pdf-math.randomseed"><code>math.randomseed</code></a> with explicit arguments.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.sin"><code>math.sin (x)</code></a></h3>
+
+
+<p>
+Returns the sine of <code>x</code> (assumed to be in radians).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.sqrt"><code>math.sqrt (x)</code></a></h3>
+
+
+<p>
+Returns the square root of <code>x</code>.
+(You can also use the expression <code>x^0.5</code> to compute this value.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.tan"><code>math.tan (x)</code></a></h3>
+
+
+<p>
+Returns the tangent of <code>x</code> (assumed to be in radians).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.tointeger"><code>math.tointeger (x)</code></a></h3>
+
+
+<p>
+If the value <code>x</code> is convertible to an integer,
+returns that integer.
+Otherwise, returns <b>fail</b>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.type"><code>math.type (x)</code></a></h3>
+
+
+<p>
+Returns "<code>integer</code>" if <code>x</code> is an integer,
+"<code>float</code>" if it is a float,
+or <b>fail</b> if <code>x</code> is not a number.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-math.ult"><code>math.ult (m, n)</code></a></h3>
+
+
+<p>
+Returns a boolean,
+<b>true</b> if and only if integer <code>m</code> is below integer <code>n</code> when
+they are compared as unsigned integers.
+
+
+
+
+
+
+
+<h2>6.8 &ndash; <a name="6.8">Input and Output Facilities</a></h2>
+
+<p>
+The I/O library provides two different styles for file manipulation.
+The first one uses implicit file handles;
+that is, there are operations to set a default input file and a
+default output file,
+and all input/output operations are done over these default files.
+The second style uses explicit file handles.
+
+
+<p>
+When using implicit file handles,
+all operations are supplied by table <a name="pdf-io"><code>io</code></a>.
+When using explicit file handles,
+the operation <a href="#pdf-io.open"><code>io.open</code></a> returns a file handle
+and then all operations are supplied as methods of the file handle.
+
+
+<p>
+The metatable for file handles provides metamethods
+for <code>__gc</code> and <code>__close</code> that try
+to close the file when called.
+
+
+<p>
+The table <code>io</code> also provides
+three predefined file handles with their usual meanings from C:
+<a name="pdf-io.stdin"><code>io.stdin</code></a>, <a name="pdf-io.stdout"><code>io.stdout</code></a>, and <a name="pdf-io.stderr"><code>io.stderr</code></a>.
+The I/O library never closes these files.
+
+
+<p>
+Unless otherwise stated,
+all I/O functions return <b>fail</b> on failure,
+plus an error message as a second result and
+a system-dependent error code as a third result,
+and some non-false value on success.
+On non-POSIX systems,
+the computation of the error message and error code
+in case of errors
+may be not thread safe,
+because they rely on the global C variable <code>errno</code>.
+
+
+<p>
+<hr><h3><a name="pdf-io.close"><code>io.close ([file])</code></a></h3>
+
+
+<p>
+Equivalent to <code>file:close()</code>.
+Without a <code>file</code>, closes the default output file.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.flush"><code>io.flush ()</code></a></h3>
+
+
+<p>
+Equivalent to <code>io.output():flush()</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.input"><code>io.input ([file])</code></a></h3>
+
+
+<p>
+When called with a file name, it opens the named file (in text mode),
+and sets its handle as the default input file.
+When called with a file handle,
+it simply sets this file handle as the default input file.
+When called without arguments,
+it returns the current default input file.
+
+
+<p>
+In case of errors this function raises the error,
+instead of returning an error code.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.lines"><code>io.lines ([filename, &middot;&middot;&middot;])</code></a></h3>
+
+
+<p>
+Opens the given file name in read mode
+and returns an iterator function that
+works like <code>file:lines(&middot;&middot;&middot;)</code> over the opened file.
+When the iterator function fails to read any value,
+it automatically closes the file.
+Besides the iterator function,
+<code>io.lines</code> returns three other values:
+two <b>nil</b> values as placeholders,
+plus the created file handle.
+Therefore, when used in a generic <b>for</b> loop,
+the file is closed also if the loop is interrupted by an
+error or a <b>break</b>.
+
+
+<p>
+The call <code>io.lines()</code> (with no file name) is equivalent
+to <code>io.input():lines("l")</code>;
+that is, it iterates over the lines of the default input file.
+In this case, the iterator does not close the file when the loop ends.
+
+
+<p>
+In case of errors opening the file,
+this function raises the error,
+instead of returning an error code.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.open"><code>io.open (filename [, mode])</code></a></h3>
+
+
+<p>
+This function opens a file,
+in the mode specified in the string <code>mode</code>.
+In case of success,
+it returns a new file handle.
+
+
+<p>
+The <code>mode</code> string can be any of the following:
+
+<ul>
+<li><b>"<code>r</code>": </b> read mode (the default);</li>
+<li><b>"<code>w</code>": </b> write mode;</li>
+<li><b>"<code>a</code>": </b> append mode;</li>
+<li><b>"<code>r+</code>": </b> update mode, all previous data is preserved;</li>
+<li><b>"<code>w+</code>": </b> update mode, all previous data is erased;</li>
+<li><b>"<code>a+</code>": </b> append update mode, previous data is preserved,
+ writing is only allowed at the end of file.</li>
+</ul><p>
+The <code>mode</code> string can also have a '<code>b</code>' at the end,
+which is needed in some systems to open the file in binary mode.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.output"><code>io.output ([file])</code></a></h3>
+
+
+<p>
+Similar to <a href="#pdf-io.input"><code>io.input</code></a>, but operates over the default output file.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.popen"><code>io.popen (prog [, mode])</code></a></h3>
+
+
+<p>
+This function is system dependent and is not available
+on all platforms.
+
+
+<p>
+Starts the program <code>prog</code> in a separated process and returns
+a file handle that you can use to read data from this program
+(if <code>mode</code> is <code>"r"</code>, the default)
+or to write data to this program
+(if <code>mode</code> is <code>"w"</code>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.read"><code>io.read (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Equivalent to <code>io.input():read(&middot;&middot;&middot;)</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.tmpfile"><code>io.tmpfile ()</code></a></h3>
+
+
+<p>
+In case of success,
+returns a handle for a temporary file.
+This file is opened in update mode
+and it is automatically removed when the program ends.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.type"><code>io.type (obj)</code></a></h3>
+
+
+<p>
+Checks whether <code>obj</code> is a valid file handle.
+Returns the string <code>"file"</code> if <code>obj</code> is an open file handle,
+<code>"closed file"</code> if <code>obj</code> is a closed file handle,
+or <b>fail</b> if <code>obj</code> is not a file handle.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-io.write"><code>io.write (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Equivalent to <code>io.output():write(&middot;&middot;&middot;)</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:close"><code>file:close ()</code></a></h3>
+
+
+<p>
+Closes <code>file</code>.
+Note that files are automatically closed when
+their handles are garbage collected,
+but that takes an unpredictable amount of time to happen.
+
+
+<p>
+When closing a file handle created with <a href="#pdf-io.popen"><code>io.popen</code></a>,
+<a href="#pdf-file:close"><code>file:close</code></a> returns the same values
+returned by <a href="#pdf-os.execute"><code>os.execute</code></a>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:flush"><code>file:flush ()</code></a></h3>
+
+
+<p>
+Saves any written data to <code>file</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:lines"><code>file:lines (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Returns an iterator function that,
+each time it is called,
+reads the file according to the given formats.
+When no format is given,
+uses "<code>l</code>" as a default.
+As an example, the construction
+
+<pre>
+ for c in file:lines(1) do <em>body</em> end
+</pre><p>
+will iterate over all characters of the file,
+starting at the current position.
+Unlike <a href="#pdf-io.lines"><code>io.lines</code></a>, this function does not close the file
+when the loop ends.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:read"><code>file:read (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Reads the file <code>file</code>,
+according to the given formats, which specify what to read.
+For each format,
+the function returns a string or a number with the characters read,
+or <b>fail</b> if it cannot read data with the specified format.
+(In this latter case,
+the function does not read subsequent formats.)
+When called without arguments,
+it uses a default format that reads the next line
+(see below).
+
+
+<p>
+The available formats are
+
+<ul>
+
+<li><b>"<code>n</code>": </b>
+reads a numeral and returns it as a float or an integer,
+following the lexical conventions of Lua.
+(The numeral may have leading whitespaces and a sign.)
+This format always reads the longest input sequence that
+is a valid prefix for a numeral;
+if that prefix does not form a valid numeral
+(e.g., an empty string, "<code>0x</code>", or "<code>3.4e-</code>")
+or it is too long (more than 200 characters),
+it is discarded and the format returns <b>fail</b>.
+</li>
+
+<li><b>"<code>a</code>": </b>
+reads the whole file, starting at the current position.
+On end of file, it returns the empty string;
+this format never fails.
+</li>
+
+<li><b>"<code>l</code>": </b>
+reads the next line skipping the end of line,
+returning <b>fail</b> on end of file.
+This is the default format.
+</li>
+
+<li><b>"<code>L</code>": </b>
+reads the next line keeping the end-of-line character (if present),
+returning <b>fail</b> on end of file.
+</li>
+
+<li><b><em>number</em>: </b>
+reads a string with up to this number of bytes,
+returning <b>fail</b> on end of file.
+If <code>number</code> is zero,
+it reads nothing and returns an empty string,
+or <b>fail</b> on end of file.
+</li>
+
+</ul><p>
+The formats "<code>l</code>" and "<code>L</code>" should be used only for text files.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:seek"><code>file:seek ([whence [, offset]])</code></a></h3>
+
+
+<p>
+Sets and gets the file position,
+measured from the beginning of the file,
+to the position given by <code>offset</code> plus a base
+specified by the string <code>whence</code>, as follows:
+
+<ul>
+<li><b>"<code>set</code>": </b> base is position 0 (beginning of the file);</li>
+<li><b>"<code>cur</code>": </b> base is current position;</li>
+<li><b>"<code>end</code>": </b> base is end of file;</li>
+</ul><p>
+In case of success, <code>seek</code> returns the final file position,
+measured in bytes from the beginning of the file.
+If <code>seek</code> fails, it returns <b>fail</b>,
+plus a string describing the error.
+
+
+<p>
+The default value for <code>whence</code> is <code>"cur"</code>,
+and for <code>offset</code> is 0.
+Therefore, the call <code>file:seek()</code> returns the current
+file position, without changing it;
+the call <code>file:seek("set")</code> sets the position to the
+beginning of the file (and returns 0);
+and the call <code>file:seek("end")</code> sets the position to the
+end of the file, and returns its size.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:setvbuf"><code>file:setvbuf (mode [, size])</code></a></h3>
+
+
+<p>
+Sets the buffering mode for a file.
+There are three available modes:
+
+<ul>
+<li><b>"<code>no</code>": </b> no buffering.</li>
+<li><b>"<code>full</code>": </b> full buffering.</li>
+<li><b>"<code>line</code>": </b> line buffering.</li>
+</ul>
+
+<p>
+For the last two cases,
+<code>size</code> is a hint for the size of the buffer, in bytes.
+The default is an appropriate size.
+
+
+<p>
+The specific behavior of each mode is non portable;
+check the underlying ISO&nbsp;C function <code>setvbuf</code> in your platform for
+more details.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-file:write"><code>file:write (&middot;&middot;&middot;)</code></a></h3>
+
+
+<p>
+Writes the value of each of its arguments to <code>file</code>.
+The arguments must be strings or numbers.
+
+
+<p>
+In case of success, this function returns <code>file</code>.
+
+
+
+
+
+
+
+<h2>6.9 &ndash; <a name="6.9">Operating System Facilities</a></h2>
+
+<p>
+This library is implemented through table <a name="pdf-os"><code>os</code></a>.
+
+
+<p>
+<hr><h3><a name="pdf-os.clock"><code>os.clock ()</code></a></h3>
+
+
+<p>
+Returns an approximation of the amount in seconds of CPU time
+used by the program,
+as returned by the underlying ISO&nbsp;C function <code>clock</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.date"><code>os.date ([format [, time]])</code></a></h3>
+
+
+<p>
+Returns a string or a table containing date and time,
+formatted according to the given string <code>format</code>.
+
+
+<p>
+If the <code>time</code> argument is present,
+this is the time to be formatted
+(see the <a href="#pdf-os.time"><code>os.time</code></a> function for a description of this value).
+Otherwise, <code>date</code> formats the current time.
+
+
+<p>
+If <code>format</code> starts with '<code>!</code>',
+then the date is formatted in Coordinated Universal Time.
+After this optional character,
+if <code>format</code> is the string "<code>*t</code>",
+then <code>date</code> returns a table with the following fields:
+<code>year</code>, <code>month</code> (1&ndash;12), <code>day</code> (1&ndash;31),
+<code>hour</code> (0&ndash;23), <code>min</code> (0&ndash;59),
+<code>sec</code> (0&ndash;61, due to leap seconds),
+<code>wday</code> (weekday, 1&ndash;7, Sunday is&nbsp;1),
+<code>yday</code> (day of the year, 1&ndash;366),
+and <code>isdst</code> (daylight saving flag, a boolean).
+This last field may be absent
+if the information is not available.
+
+
+<p>
+If <code>format</code> is not "<code>*t</code>",
+then <code>date</code> returns the date as a string,
+formatted according to the same rules as the ISO&nbsp;C function <code>strftime</code>.
+
+
+<p>
+If <code>format</code> is absent, it defaults to "<code>%c</code>",
+which gives a human-readable date and time representation
+using the current locale.
+
+
+<p>
+On non-POSIX systems,
+this function may be not thread safe
+because of its reliance on C&nbsp;function <code>gmtime</code> and C&nbsp;function <code>localtime</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.difftime"><code>os.difftime (t2, t1)</code></a></h3>
+
+
+<p>
+Returns the difference, in seconds,
+from time <code>t1</code> to time <code>t2</code>
+(where the times are values returned by <a href="#pdf-os.time"><code>os.time</code></a>).
+In POSIX, Windows, and some other systems,
+this value is exactly <code>t2</code><em>-</em><code>t1</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.execute"><code>os.execute ([command])</code></a></h3>
+
+
+<p>
+This function is equivalent to the ISO&nbsp;C function <code>system</code>.
+It passes <code>command</code> to be executed by an operating system shell.
+Its first result is <b>true</b>
+if the command terminated successfully,
+or <b>fail</b> otherwise.
+After this first result
+the function returns a string plus a number,
+as follows:
+
+<ul>
+
+<li><b>"<code>exit</code>": </b>
+the command terminated normally;
+the following number is the exit status of the command.
+</li>
+
+<li><b>"<code>signal</code>": </b>
+the command was terminated by a signal;
+the following number is the signal that terminated the command.
+</li>
+
+</ul>
+
+<p>
+When called without a <code>command</code>,
+<code>os.execute</code> returns a boolean that is true if a shell is available.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.exit"><code>os.exit ([code [, close]])</code></a></h3>
+
+
+<p>
+Calls the ISO&nbsp;C function <code>exit</code> to terminate the host program.
+If <code>code</code> is <b>true</b>,
+the returned status is <code>EXIT_SUCCESS</code>;
+if <code>code</code> is <b>false</b>,
+the returned status is <code>EXIT_FAILURE</code>;
+if <code>code</code> is a number,
+the returned status is this number.
+The default value for <code>code</code> is <b>true</b>.
+
+
+<p>
+If the optional second argument <code>close</code> is true,
+the function closes the Lua state before exiting (see <a href="#lua_close"><code>lua_close</code></a>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.getenv"><code>os.getenv (varname)</code></a></h3>
+
+
+<p>
+Returns the value of the process environment variable <code>varname</code>
+or <b>fail</b> if the variable is not defined.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.remove"><code>os.remove (filename)</code></a></h3>
+
+
+<p>
+Deletes the file (or empty directory, on POSIX systems)
+with the given name.
+If this function fails, it returns <b>fail</b>
+plus a string describing the error and the error code.
+Otherwise, it returns true.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.rename"><code>os.rename (oldname, newname)</code></a></h3>
+
+
+<p>
+Renames the file or directory named <code>oldname</code> to <code>newname</code>.
+If this function fails, it returns <b>fail</b>,
+plus a string describing the error and the error code.
+Otherwise, it returns true.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.setlocale"><code>os.setlocale (locale [, category])</code></a></h3>
+
+
+<p>
+Sets the current locale of the program.
+<code>locale</code> is a system-dependent string specifying a locale;
+<code>category</code> is an optional string describing which category to change:
+<code>"all"</code>, <code>"collate"</code>, <code>"ctype"</code>,
+<code>"monetary"</code>, <code>"numeric"</code>, or <code>"time"</code>;
+the default category is <code>"all"</code>.
+The function returns the name of the new locale,
+or <b>fail</b> if the request cannot be honored.
+
+
+<p>
+If <code>locale</code> is the empty string,
+the current locale is set to an implementation-defined native locale.
+If <code>locale</code> is the string "<code>C</code>",
+the current locale is set to the standard C locale.
+
+
+<p>
+When called with <b>nil</b> as the first argument,
+this function only returns the name of the current locale
+for the given category.
+
+
+<p>
+This function may be not thread safe
+because of its reliance on C&nbsp;function <code>setlocale</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.time"><code>os.time ([table])</code></a></h3>
+
+
+<p>
+Returns the current time when called without arguments,
+or a time representing the local date and time specified by the given table.
+This table must have fields <code>year</code>, <code>month</code>, and <code>day</code>,
+and may have fields
+<code>hour</code> (default is 12),
+<code>min</code> (default is 0),
+<code>sec</code> (default is 0),
+and <code>isdst</code> (default is <b>nil</b>).
+Other fields are ignored.
+For a description of these fields, see the <a href="#pdf-os.date"><code>os.date</code></a> function.
+
+
+<p>
+When the function is called,
+the values in these fields do not need to be inside their valid ranges.
+For instance, if <code>sec</code> is -10,
+it means 10 seconds before the time specified by the other fields;
+if <code>hour</code> is 1000,
+it means 1000 hours after the time specified by the other fields.
+
+
+<p>
+The returned value is a number, whose meaning depends on your system.
+In POSIX, Windows, and some other systems,
+this number counts the number
+of seconds since some given start time (the "epoch").
+In other systems, the meaning is not specified,
+and the number returned by <code>time</code> can be used only as an argument to
+<a href="#pdf-os.date"><code>os.date</code></a> and <a href="#pdf-os.difftime"><code>os.difftime</code></a>.
+
+
+<p>
+When called with a table,
+<code>os.time</code> also normalizes all the fields
+documented in the <a href="#pdf-os.date"><code>os.date</code></a> function,
+so that they represent the same time as before the call
+but with values inside their valid ranges.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-os.tmpname"><code>os.tmpname ()</code></a></h3>
+
+
+<p>
+Returns a string with a file name that can
+be used for a temporary file.
+The file must be explicitly opened before its use
+and explicitly removed when no longer needed.
+
+
+<p>
+In POSIX systems,
+this function also creates a file with that name,
+to avoid security risks.
+(Someone else might create the file with wrong permissions
+in the time between getting the name and creating the file.)
+You still have to open the file to use it
+and to remove it (even if you do not use it).
+
+
+<p>
+When possible,
+you may prefer to use <a href="#pdf-io.tmpfile"><code>io.tmpfile</code></a>,
+which automatically removes the file when the program ends.
+
+
+
+
+
+
+
+<h2>6.10 &ndash; <a name="6.10">The Debug Library</a></h2>
+
+<p>
+This library provides
+the functionality of the debug interface (<a href="#4.7">&sect;4.7</a>) to Lua programs.
+You should exert care when using this library.
+Several of its functions
+violate basic assumptions about Lua code
+(e.g., that variables local to a function
+cannot be accessed from outside;
+that userdata metatables cannot be changed by Lua code;
+that Lua programs do not crash)
+and therefore can compromise otherwise secure code.
+Moreover, some functions in this library may be slow.
+
+
+<p>
+All functions in this library are provided
+inside the <a name="pdf-debug"><code>debug</code></a> table.
+All functions that operate over a thread
+have an optional first argument which is the
+thread to operate over.
+The default is always the current thread.
+
+
+<p>
+<hr><h3><a name="pdf-debug.debug"><code>debug.debug ()</code></a></h3>
+
+
+<p>
+Enters an interactive mode with the user,
+running each string that the user enters.
+Using simple commands and other debug facilities,
+the user can inspect global and local variables,
+change their values, evaluate expressions, and so on.
+A line containing only the word <code>cont</code> finishes this function,
+so that the caller continues its execution.
+
+
+<p>
+Note that commands for <code>debug.debug</code> are not lexically nested
+within any function and so have no direct access to local variables.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.gethook"><code>debug.gethook ([thread])</code></a></h3>
+
+
+<p>
+Returns the current hook settings of the thread, as three values:
+the current hook function, the current hook mask,
+and the current hook count,
+as set by the <a href="#pdf-debug.sethook"><code>debug.sethook</code></a> function.
+
+
+<p>
+Returns <b>fail</b> if there is no active hook.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.getinfo"><code>debug.getinfo ([thread,] f [, what])</code></a></h3>
+
+
+<p>
+Returns a table with information about a function.
+You can give the function directly
+or you can give a number as the value of <code>f</code>,
+which means the function running at level <code>f</code> of the call stack
+of the given thread:
+level&nbsp;0 is the current function (<code>getinfo</code> itself);
+level&nbsp;1 is the function that called <code>getinfo</code>
+(except for tail calls, which do not count in the stack);
+and so on.
+If <code>f</code> is a number greater than the number of active functions,
+then <code>getinfo</code> returns <b>fail</b>.
+
+
+<p>
+The returned table can contain all the fields returned by <a href="#lua_getinfo"><code>lua_getinfo</code></a>,
+with the string <code>what</code> describing which fields to fill in.
+The default for <code>what</code> is to get all information available,
+except the table of valid lines.
+If present,
+the option '<code>f</code>'
+adds a field named <code>func</code> with the function itself.
+If present,
+the option '<code>L</code>'
+adds a field named <code>activelines</code> with the table of
+valid lines.
+
+
+<p>
+For instance, the expression <code>debug.getinfo(1,"n").name</code> returns
+a name for the current function,
+if a reasonable name can be found,
+and the expression <code>debug.getinfo(print)</code>
+returns a table with all available information
+about the <a href="#pdf-print"><code>print</code></a> function.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.getlocal"><code>debug.getlocal ([thread,] f, local)</code></a></h3>
+
+
+<p>
+This function returns the name and the value of the local variable
+with index <code>local</code> of the function at level <code>f</code> of the stack.
+This function accesses not only explicit local variables,
+but also parameters and temporary values.
+
+
+<p>
+The first parameter or local variable has index&nbsp;1, and so on,
+following the order that they are declared in the code,
+counting only the variables that are active
+in the current scope of the function.
+Compile-time constants may not appear in this listing,
+if they were optimized away by the compiler.
+Negative indices refer to vararg arguments;
+-1 is the first vararg argument.
+The function returns <b>fail</b>
+if there is no variable with the given index,
+and raises an error when called with a level out of range.
+(You can call <a href="#pdf-debug.getinfo"><code>debug.getinfo</code></a> to check whether the level is valid.)
+
+
+<p>
+Variable names starting with '<code>(</code>' (open parenthesis)
+represent variables with no known names
+(internal variables such as loop control variables,
+and variables from chunks saved without debug information).
+
+
+<p>
+The parameter <code>f</code> may also be a function.
+In that case, <code>getlocal</code> returns only the name of function parameters.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.getmetatable"><code>debug.getmetatable (value)</code></a></h3>
+
+
+<p>
+Returns the metatable of the given <code>value</code>
+or <b>nil</b> if it does not have a metatable.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.getregistry"><code>debug.getregistry ()</code></a></h3>
+
+
+<p>
+Returns the registry table (see <a href="#4.3">&sect;4.3</a>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.getupvalue"><code>debug.getupvalue (f, up)</code></a></h3>
+
+
+<p>
+This function returns the name and the value of the upvalue
+with index <code>up</code> of the function <code>f</code>.
+The function returns <b>fail</b>
+if there is no upvalue with the given index.
+
+
+<p>
+(For Lua functions,
+upvalues are the external local variables that the function uses,
+and that are consequently included in its closure.)
+
+
+<p>
+For C&nbsp;functions, this function uses the empty string <code>""</code>
+as a name for all upvalues.
+
+
+<p>
+Variable name '<code>?</code>' (interrogation mark)
+represents variables with no known names
+(variables from chunks saved without debug information).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.getuservalue"><code>debug.getuservalue (u, n)</code></a></h3>
+
+
+<p>
+Returns the <code>n</code>-th user value associated
+to the userdata <code>u</code> plus a boolean,
+<b>false</b> if the userdata does not have that value.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.sethook"><code>debug.sethook ([thread,] hook, mask [, count])</code></a></h3>
+
+
+<p>
+Sets the given function as the debug hook.
+The string <code>mask</code> and the number <code>count</code> describe
+when the hook will be called.
+The string mask may have any combination of the following characters,
+with the given meaning:
+
+<ul>
+<li><b>'<code>c</code>': </b> the hook is called every time Lua calls a function;</li>
+<li><b>'<code>r</code>': </b> the hook is called every time Lua returns from a function;</li>
+<li><b>'<code>l</code>': </b> the hook is called every time Lua enters a new line of code.</li>
+</ul><p>
+Moreover,
+with a <code>count</code> different from zero,
+the hook is called also after every <code>count</code> instructions.
+
+
+<p>
+When called without arguments,
+<a href="#pdf-debug.sethook"><code>debug.sethook</code></a> turns off the hook.
+
+
+<p>
+When the hook is called, its first parameter is a string
+describing the event that has triggered its call:
+<code>"call"</code>, <code>"tail call"</code>, <code>"return"</code>,
+<code>"line"</code>, and <code>"count"</code>.
+For line events,
+the hook also gets the new line number as its second parameter.
+Inside a hook,
+you can call <code>getinfo</code> with level&nbsp;2 to get more information about
+the running function.
+(Level&nbsp;0 is the <code>getinfo</code> function,
+and level&nbsp;1 is the hook function.)
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.setlocal"><code>debug.setlocal ([thread,] level, local, value)</code></a></h3>
+
+
+<p>
+This function assigns the value <code>value</code> to the local variable
+with index <code>local</code> of the function at level <code>level</code> of the stack.
+The function returns <b>fail</b> if there is no local
+variable with the given index,
+and raises an error when called with a <code>level</code> out of range.
+(You can call <code>getinfo</code> to check whether the level is valid.)
+Otherwise, it returns the name of the local variable.
+
+
+<p>
+See <a href="#pdf-debug.getlocal"><code>debug.getlocal</code></a> for more information about
+variable indices and names.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.setmetatable"><code>debug.setmetatable (value, table)</code></a></h3>
+
+
+<p>
+Sets the metatable for the given <code>value</code> to the given <code>table</code>
+(which can be <b>nil</b>).
+Returns <code>value</code>.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.setupvalue"><code>debug.setupvalue (f, up, value)</code></a></h3>
+
+
+<p>
+This function assigns the value <code>value</code> to the upvalue
+with index <code>up</code> of the function <code>f</code>.
+The function returns <b>fail</b> if there is no upvalue
+with the given index.
+Otherwise, it returns the name of the upvalue.
+
+
+<p>
+See <a href="#pdf-debug.getupvalue"><code>debug.getupvalue</code></a> for more information about upvalues.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.setuservalue"><code>debug.setuservalue (udata, value, n)</code></a></h3>
+
+
+<p>
+Sets the given <code>value</code> as
+the <code>n</code>-th user value associated to the given <code>udata</code>.
+<code>udata</code> must be a full userdata.
+
+
+<p>
+Returns <code>udata</code>,
+or <b>fail</b> if the userdata does not have that value.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.traceback"><code>debug.traceback ([thread,] [message [, level]])</code></a></h3>
+
+
+<p>
+If <code>message</code> is present but is neither a string nor <b>nil</b>,
+this function returns <code>message</code> without further processing.
+Otherwise,
+it returns a string with a traceback of the call stack.
+The optional <code>message</code> string is appended
+at the beginning of the traceback.
+An optional <code>level</code> number tells at which level
+to start the traceback
+(default is 1, the function calling <code>traceback</code>).
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.upvalueid"><code>debug.upvalueid (f, n)</code></a></h3>
+
+
+<p>
+Returns a unique identifier (as a light userdata)
+for the upvalue numbered <code>n</code>
+from the given function.
+
+
+<p>
+These unique identifiers allow a program to check whether different
+closures share upvalues.
+Lua closures that share an upvalue
+(that is, that access a same external local variable)
+will return identical ids for those upvalue indices.
+
+
+
+
+<p>
+<hr><h3><a name="pdf-debug.upvaluejoin"><code>debug.upvaluejoin (f1, n1, f2, n2)</code></a></h3>
+
+
+<p>
+Make the <code>n1</code>-th upvalue of the Lua closure <code>f1</code>
+refer to the <code>n2</code>-th upvalue of the Lua closure <code>f2</code>.
+
+
+
+
+
+
+
+<h1>7 &ndash; <a name="7">Lua Standalone</a></h1>
+
+<p>
+Although Lua has been designed as an extension language,
+to be embedded in a host C&nbsp;program,
+it is also frequently used as a standalone language.
+An interpreter for Lua as a standalone language,
+called simply <code>lua</code>,
+is provided with the standard distribution.
+The standalone interpreter includes
+all standard libraries.
+Its usage is:
+
+<pre>
+ lua [options] [script [args]]
+</pre><p>
+The options are:
+
+<ul>
+<li><b><code>-e <em>stat</em></code>: </b> execute string <em>stat</em>;</li>
+<li><b><code>-i</code>: </b> enter interactive mode after running <em>script</em>;</li>
+<li><b><code>-l <em>mod</em></code>: </b> "require" <em>mod</em> and assign the
+ result to global <em>mod</em>;</li>
+<li><b><code>-l <em>g=mod</em></code>: </b> "require" <em>mod</em> and assign the
+ result to global <em>g</em>;</li>
+<li><b><code>-v</code>: </b> print version information;</li>
+<li><b><code>-E</code>: </b> ignore environment variables;</li>
+<li><b><code>-W</code>: </b> turn warnings on;</li>
+<li><b><code>--</code>: </b> stop handling options;</li>
+<li><b><code>-</code>: </b> execute <code>stdin</code> as a file and stop handling options.</li>
+</ul><p>
+(The form <code>-l <em>g=mod</em></code> was introduced in release&nbsp;5.4.4.)
+
+
+<p>
+After handling its options, <code>lua</code> runs the given <em>script</em>.
+When called without arguments,
+<code>lua</code> behaves as <code>lua -v -i</code>
+when the standard input (<code>stdin</code>) is a terminal,
+and as <code>lua -</code> otherwise.
+
+
+<p>
+When called without the option <code>-E</code>,
+the interpreter checks for an environment variable <a name="pdf-LUA_INIT_5_4"><code>LUA_INIT_5_4</code></a>
+(or <a name="pdf-LUA_INIT"><code>LUA_INIT</code></a> if the versioned name is not defined)
+before running any argument.
+If the variable content has the format <code>@<em>filename</em></code>,
+then <code>lua</code> executes the file.
+Otherwise, <code>lua</code> executes the string itself.
+
+
+<p>
+When called with the option <code>-E</code>,
+Lua does not consult any environment variables.
+In particular,
+the values of <a href="#pdf-package.path"><code>package.path</code></a> and <a href="#pdf-package.cpath"><code>package.cpath</code></a>
+are set with the default paths defined in <code>luaconf.h</code>.
+
+
+<p>
+The options <code>-e</code>, <code>-l</code>, and <code>-W</code> are handled in
+the order they appear.
+For instance, an invocation like
+
+<pre>
+ $ lua -e 'a=1' -llib1 script.lua
+</pre><p>
+will first set <code>a</code> to 1, then require the library <code>lib1</code>,
+and finally run the file <code>script.lua</code> with no arguments.
+(Here <code>$</code> is the shell prompt. Your prompt may be different.)
+
+
+<p>
+Before running any code,
+<code>lua</code> collects all command-line arguments
+in a global table called <code>arg</code>.
+The script name goes to index 0,
+the first argument after the script name goes to index 1,
+and so on.
+Any arguments before the script name
+(that is, the interpreter name plus its options)
+go to negative indices.
+For instance, in the call
+
+<pre>
+ $ lua -la b.lua t1 t2
+</pre><p>
+the table is like this:
+
+<pre>
+ arg = { [-2] = "lua", [-1] = "-la",
+ [0] = "b.lua",
+ [1] = "t1", [2] = "t2" }
+</pre><p>
+If there is no script in the call,
+the interpreter name goes to index 0,
+followed by the other arguments.
+For instance, the call
+
+<pre>
+ $ lua -e "print(arg[1])"
+</pre><p>
+will print "<code>-e</code>".
+If there is a script,
+the script is called with arguments
+<code>arg[1]</code>, &middot;&middot;&middot;, <code>arg[#arg]</code>.
+Like all chunks in Lua,
+the script is compiled as a variadic function.
+
+
+<p>
+In interactive mode,
+Lua repeatedly prompts and waits for a line.
+After reading a line,
+Lua first try to interpret the line as an expression.
+If it succeeds, it prints its value.
+Otherwise, it interprets the line as a statement.
+If you write an incomplete statement,
+the interpreter waits for its completion
+by issuing a different prompt.
+
+
+<p>
+If the global variable <a name="pdf-_PROMPT"><code>_PROMPT</code></a> contains a string,
+then its value is used as the prompt.
+Similarly, if the global variable <a name="pdf-_PROMPT2"><code>_PROMPT2</code></a> contains a string,
+its value is used as the secondary prompt
+(issued during incomplete statements).
+
+
+<p>
+In case of unprotected errors in the script,
+the interpreter reports the error to the standard error stream.
+If the error object is not a string but
+has a metamethod <code>__tostring</code>,
+the interpreter calls this metamethod to produce the final message.
+Otherwise, the interpreter converts the error object to a string
+and adds a stack traceback to it.
+When warnings are on,
+they are simply printed in the standard error output.
+
+
+<p>
+When finishing normally,
+the interpreter closes its main Lua state
+(see <a href="#lua_close"><code>lua_close</code></a>).
+The script can avoid this step by
+calling <a href="#pdf-os.exit"><code>os.exit</code></a> to terminate.
+
+
+<p>
+To allow the use of Lua as a
+script interpreter in Unix systems,
+Lua skips the first line of a file chunk if it starts with <code>#</code>.
+Therefore, Lua scripts can be made into executable programs
+by using <code>chmod +x</code> and the&nbsp;<code>#!</code> form,
+as in
+
+<pre>
+ #!/usr/local/bin/lua
+</pre><p>
+Of course,
+the location of the Lua interpreter may be different in your machine.
+If <code>lua</code> is in your <code>PATH</code>,
+then
+
+<pre>
+ #!/usr/bin/env lua
+</pre><p>
+is a more portable solution.
+
+
+
+<h1>8 &ndash; <a name="8">Incompatibilities with the Previous Version</a></h1>
+
+
+
+<p>
+Here we list the incompatibilities that you may find when moving a program
+from Lua&nbsp;5.3 to Lua&nbsp;5.4.
+
+
+<p>
+You can avoid some incompatibilities by compiling Lua with
+appropriate options (see file <code>luaconf.h</code>).
+However,
+all these compatibility options will be removed in the future.
+More often than not,
+compatibility issues arise when these compatibility options
+are removed.
+So, whenever you have the chance,
+you should try to test your code with a version of Lua compiled
+with all compatibility options turned off.
+That will ease transitions to newer versions of Lua.
+
+
+<p>
+Lua versions can always change the C API in ways that
+do not imply source-code changes in a program,
+such as the numeric values for constants
+or the implementation of functions as macros.
+Therefore,
+you should never assume that binaries are compatible between
+different Lua versions.
+Always recompile clients of the Lua API when
+using a new version.
+
+
+<p>
+Similarly, Lua versions can always change the internal representation
+of precompiled chunks;
+precompiled chunks are not compatible between different Lua versions.
+
+
+<p>
+The standard paths in the official distribution may
+change between versions.
+
+
+
+
+
+<h2>8.1 &ndash; <a name="8.1">Incompatibilities in the Language</a></h2>
+<ul>
+
+<li>
+The coercion of strings to numbers in
+arithmetic and bitwise operations
+has been removed from the core language.
+The string library does a similar job
+for arithmetic (but not for bitwise) operations
+using the string metamethods.
+However, unlike in previous versions,
+the new implementation preserves the implicit type of the numeral
+in the string.
+For instance, the result of <code>"1" + "2"</code> now is an integer,
+not a float.
+</li>
+
+<li>
+Literal decimal integer constants that overflow are read as floats,
+instead of wrapping around.
+You can use hexadecimal notation for such constants if you
+want the old behavior
+(reading them as integers with wrap around).
+</li>
+
+<li>
+The use of the <code>__lt</code> metamethod to emulate <code>__le</code>
+has been removed.
+When needed, this metamethod must be explicitly defined.
+</li>
+
+<li>
+The semantics of the numerical <b>for</b> loop
+over integers changed in some details.
+In particular, the control variable never wraps around.
+</li>
+
+<li>
+A label for a <b>goto</b> cannot be declared where a label with the same
+name is visible, even if this other label is declared in an enclosing
+block.
+</li>
+
+<li>
+When finalizing an object,
+Lua does not ignore <code>__gc</code> metamethods that are not functions.
+Any value will be called, if present.
+(Non-callable values will generate a warning,
+like any other error when calling a finalizer.)
+</li>
+
+</ul>
+
+
+
+
+<h2>8.2 &ndash; <a name="8.2">Incompatibilities in the Libraries</a></h2>
+<ul>
+
+<li>
+The function <a href="#pdf-print"><code>print</code></a> does not call <a href="#pdf-tostring"><code>tostring</code></a>
+to format its arguments;
+instead, it has this functionality hardwired.
+You should use <code>__tostring</code> to modify how values are printed.
+</li>
+
+<li>
+The pseudo-random number generator used by the function <a href="#pdf-math.random"><code>math.random</code></a>
+now starts with a somewhat random seed.
+Moreover, it uses a different algorithm.
+</li>
+
+<li>
+By default, the decoding functions in the <a href="#pdf-utf8"><code>utf8</code></a> library
+do not accept surrogates as valid code points.
+An extra parameter in these functions makes them more permissive.
+</li>
+
+<li>
+The options "<code>setpause</code>" and "<code>setstepmul</code>"
+of the function <a href="#pdf-collectgarbage"><code>collectgarbage</code></a> are deprecated.
+You should use the new option "<code>incremental</code>" to set them.
+</li>
+
+<li>
+The function <a href="#pdf-io.lines"><code>io.lines</code></a> now returns four values,
+instead of just one.
+That can be a problem when it is used as the sole
+argument to another function that has optional parameters,
+such as in <code>load(io.lines(filename, "L"))</code>.
+To fix that issue,
+you can wrap the call into parentheses,
+to adjust its number of results to one.
+</li>
+
+</ul>
+
+
+
+
+<h2>8.3 &ndash; <a name="8.3">Incompatibilities in the API</a></h2>
+
+
+<ul>
+
+<li>
+Full userdata now has an arbitrary number of associated user values.
+Therefore, the functions <code>lua_newuserdata</code>,
+<code>lua_setuservalue</code>, and <code>lua_getuservalue</code> were
+replaced by <a href="#lua_newuserdatauv"><code>lua_newuserdatauv</code></a>,
+<a href="#lua_setiuservalue"><code>lua_setiuservalue</code></a>, and <a href="#lua_getiuservalue"><code>lua_getiuservalue</code></a>,
+which have an extra argument.
+
+
+<p>
+For compatibility, the old names still work as macros assuming
+one single user value.
+Note, however, that userdata with zero user values
+are more efficient memory-wise.
+</li>
+
+<li>
+The function <a href="#lua_resume"><code>lua_resume</code></a> has an extra parameter.
+This out parameter returns the number of values on
+the top of the stack that were yielded or returned by the coroutine.
+(In previous versions,
+those values were the entire stack.)
+</li>
+
+<li>
+The function <a href="#lua_version"><code>lua_version</code></a> returns the version number,
+instead of an address of the version number.
+The Lua core should work correctly with libraries using their
+own static copies of the same core,
+so there is no need to check whether they are using the same
+address space.
+</li>
+
+<li>
+The constant <code>LUA_ERRGCMM</code> was removed.
+Errors in finalizers are never propagated;
+instead, they generate a warning.
+</li>
+
+<li>
+The options <code>LUA_GCSETPAUSE</code> and <code>LUA_GCSETSTEPMUL</code>
+of the function <a href="#lua_gc"><code>lua_gc</code></a> are deprecated.
+You should use the new option <code>LUA_GCINC</code> to set them.
+</li>
+
+</ul>
+
+
+
+
+<h1>9 &ndash; <a name="9">The Complete Syntax of Lua</a></h1>
+
+<p>
+Here is the complete syntax of Lua in extended BNF.
+As usual in extended BNF,
+{A} means 0 or more As,
+and [A] means an optional A.
+(For operator precedences, see <a href="#3.4.8">&sect;3.4.8</a>;
+for a description of the terminals
+Name, Numeral,
+and LiteralString, see <a href="#3.1">&sect;3.1</a>.)
+
+
+
+
+<pre>
+
+ chunk ::= block
+
+ block ::= {stat} [retstat]
+
+ stat ::= &lsquo;<b>;</b>&rsquo; |
+ varlist &lsquo;<b>=</b>&rsquo; explist |
+ functioncall |
+ label |
+ <b>break</b> |
+ <b>goto</b> Name |
+ <b>do</b> block <b>end</b> |
+ <b>while</b> exp <b>do</b> block <b>end</b> |
+ <b>repeat</b> block <b>until</b> exp |
+ <b>if</b> exp <b>then</b> block {<b>elseif</b> exp <b>then</b> block} [<b>else</b> block] <b>end</b> |
+ <b>for</b> Name &lsquo;<b>=</b>&rsquo; exp &lsquo;<b>,</b>&rsquo; exp [&lsquo;<b>,</b>&rsquo; exp] <b>do</b> block <b>end</b> |
+ <b>for</b> namelist <b>in</b> explist <b>do</b> block <b>end</b> |
+ <b>function</b> funcname funcbody |
+ <b>local</b> <b>function</b> Name funcbody |
+ <b>local</b> attnamelist [&lsquo;<b>=</b>&rsquo; explist]
+
+ attnamelist ::= Name attrib {&lsquo;<b>,</b>&rsquo; Name attrib}
+
+ attrib ::= [&lsquo;<b>&lt;</b>&rsquo; Name &lsquo;<b>&gt;</b>&rsquo;]
+
+ retstat ::= <b>return</b> [explist] [&lsquo;<b>;</b>&rsquo;]
+
+ label ::= &lsquo;<b>::</b>&rsquo; Name &lsquo;<b>::</b>&rsquo;
+
+ funcname ::= Name {&lsquo;<b>.</b>&rsquo; Name} [&lsquo;<b>:</b>&rsquo; Name]
+
+ varlist ::= var {&lsquo;<b>,</b>&rsquo; var}
+
+ var ::= Name | prefixexp &lsquo;<b>[</b>&rsquo; exp &lsquo;<b>]</b>&rsquo; | prefixexp &lsquo;<b>.</b>&rsquo; Name
+
+ namelist ::= Name {&lsquo;<b>,</b>&rsquo; Name}
+
+ explist ::= exp {&lsquo;<b>,</b>&rsquo; exp}
+
+ exp ::= <b>nil</b> | <b>false</b> | <b>true</b> | Numeral | LiteralString | &lsquo;<b>...</b>&rsquo; | functiondef |
+ prefixexp | tableconstructor | exp binop exp | unop exp
+
+ prefixexp ::= var | functioncall | &lsquo;<b>(</b>&rsquo; exp &lsquo;<b>)</b>&rsquo;
+
+ functioncall ::= prefixexp args | prefixexp &lsquo;<b>:</b>&rsquo; Name args
+
+ args ::= &lsquo;<b>(</b>&rsquo; [explist] &lsquo;<b>)</b>&rsquo; | tableconstructor | LiteralString
+
+ functiondef ::= <b>function</b> funcbody
+
+ funcbody ::= &lsquo;<b>(</b>&rsquo; [parlist] &lsquo;<b>)</b>&rsquo; block <b>end</b>
+
+ parlist ::= namelist [&lsquo;<b>,</b>&rsquo; &lsquo;<b>...</b>&rsquo;] | &lsquo;<b>...</b>&rsquo;
+
+ tableconstructor ::= &lsquo;<b>{</b>&rsquo; [fieldlist] &lsquo;<b>}</b>&rsquo;
+
+ fieldlist ::= field {fieldsep field} [fieldsep]
+
+ field ::= &lsquo;<b>[</b>&rsquo; exp &lsquo;<b>]</b>&rsquo; &lsquo;<b>=</b>&rsquo; exp | Name &lsquo;<b>=</b>&rsquo; exp | exp
+
+ fieldsep ::= &lsquo;<b>,</b>&rsquo; | &lsquo;<b>;</b>&rsquo;
+
+ binop ::= &lsquo;<b>+</b>&rsquo; | &lsquo;<b>-</b>&rsquo; | &lsquo;<b>*</b>&rsquo; | &lsquo;<b>/</b>&rsquo; | &lsquo;<b>//</b>&rsquo; | &lsquo;<b>^</b>&rsquo; | &lsquo;<b>%</b>&rsquo; |
+ &lsquo;<b>&amp;</b>&rsquo; | &lsquo;<b>~</b>&rsquo; | &lsquo;<b>|</b>&rsquo; | &lsquo;<b>&gt;&gt;</b>&rsquo; | &lsquo;<b>&lt;&lt;</b>&rsquo; | &lsquo;<b>..</b>&rsquo; |
+ &lsquo;<b>&lt;</b>&rsquo; | &lsquo;<b>&lt;=</b>&rsquo; | &lsquo;<b>&gt;</b>&rsquo; | &lsquo;<b>&gt;=</b>&rsquo; | &lsquo;<b>==</b>&rsquo; | &lsquo;<b>~=</b>&rsquo; |
+ <b>and</b> | <b>or</b>
+
+ unop ::= &lsquo;<b>-</b>&rsquo; | <b>not</b> | &lsquo;<b>#</b>&rsquo; | &lsquo;<b>~</b>&rsquo;
+
+</pre>
+
+<p>
+
+
+
+
+
+
+
+<P CLASS="footer">
+Last update:
+Tue May 2 20:09:38 UTC 2023
+</P>
+<!--
+Last change: revised for Lua 5.4.6
+-->
+
+</body></html>
+
diff --git a/src/libs/3rdparty/lua/doc/osi-certified-72x60.png b/src/libs/3rdparty/lua/doc/osi-certified-72x60.png
new file mode 100644
index 0000000000..07df5f6ee7
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/osi-certified-72x60.png
Binary files differ
diff --git a/src/libs/3rdparty/lua/doc/readme.html b/src/libs/3rdparty/lua/doc/readme.html
new file mode 100644
index 0000000000..918ec8ed93
--- /dev/null
+++ b/src/libs/3rdparty/lua/doc/readme.html
@@ -0,0 +1,337 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML>
+<HEAD>
+<TITLE>Lua 5.4 readme</TITLE>
+<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
+<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=iso-8859-1">
+<STYLE TYPE="text/css">
+blockquote, .display {
+ border: solid #a0a0a0 2px ;
+ border-radius: 8px ;
+ padding: 1em ;
+ margin: 0px ;
+}
+
+.display {
+ word-spacing: 0.25em ;
+}
+
+dl.display dd {
+ padding-bottom: 0.2em ;
+}
+
+tt, kbd, code {
+ font-size: 12pt ;
+}
+</STYLE>
+</HEAD>
+
+<BODY>
+
+<H1>
+<A HREF="http://www.lua.org/"><IMG SRC="logo.gif" ALT="Lua"></A>
+Welcome to Lua 5.4
+</H1>
+
+<DIV CLASS="menubar">
+<A HREF="#about">about</A>
+&middot;
+<A HREF="#install">installation</A>
+&middot;
+<A HREF="#changes">changes</A>
+&middot;
+<A HREF="#license">license</A>
+&middot;
+<A HREF="contents.html">reference manual</A>
+</DIV>
+
+<H2><A NAME="about">About Lua</A></H2>
+<P>
+Lua is a powerful, efficient, lightweight, embeddable scripting language
+developed by a
+<A HREF="http://www.lua.org/authors.html">team</A>
+at
+<A HREF="http://www.puc-rio.br/">PUC-Rio</A>,
+the Pontifical Catholic University of Rio de Janeiro in Brazil.
+Lua is
+<A HREF="#license">free software</A>
+used in
+<A HREF="http://www.lua.org/uses.html">many products and projects</A>
+around the world.
+
+<P>
+Lua's
+<A HREF="http://www.lua.org/">official web site</A>
+provides complete information
+about Lua,
+including
+an
+<A HREF="http://www.lua.org/about.html">executive summary</A>
+and
+updated
+<A HREF="http://www.lua.org/docs.html">documentation</A>,
+especially the
+<A HREF="http://www.lua.org/manual/5.4/">reference manual</A>,
+which may differ slightly from the
+<A HREF="contents.html">local copy</A>
+distributed in this package.
+
+<H2><A NAME="install">Installing Lua</A></H2>
+<P>
+Lua is distributed in
+<A HREF="http://www.lua.org/ftp/">source</A>
+form.
+You need to build it before using it.
+Building Lua should be straightforward
+because
+Lua is implemented in pure ANSI C and compiles unmodified in all known
+platforms that have an ANSI C compiler.
+Lua also compiles unmodified as C++.
+The instructions given below for building Lua are for Unix-like platforms,
+such as Linux and Mac OS X.
+See also
+<A HREF="#other">instructions for other systems</A>
+and
+<A HREF="#customization">customization options</A>.
+
+<P>
+If you don't have the time or the inclination to compile Lua yourself,
+get a binary from
+<A HREF="http://lua-users.org/wiki/LuaBinaries">LuaBinaries</A>.
+
+<H3>Building Lua</H3>
+<P>
+In most common Unix-like platforms, simply do "<KBD>make</KBD>".
+Here are the details.
+
+<OL>
+<LI>
+Open a terminal window and move to
+the top-level directory, which is named <TT>lua-5.4.6</TT>.
+The <TT>Makefile</TT> there controls both the build process and the installation process.
+<P>
+<LI>
+ Do "<KBD>make</KBD>". The <TT>Makefile</TT> will guess your platform and build Lua for it.
+<P>
+<LI>
+ If the guess failed, do "<KBD>make help</KBD>" and see if your platform is listed.
+ The platforms currently supported are:
+<P>
+<P CLASS="display">
+ guess aix bsd c89 freebsd generic ios linux linux-readline macosx mingw posix solaris
+</P>
+<P>
+ If your platform is listed, just do "<KBD>make xxx</KBD>", where xxx
+ is your platform name.
+<P>
+ If your platform is not listed, try the closest one or posix, generic,
+ c89, in this order.
+<P>
+<LI>
+The compilation takes only a few moments
+and produces three files in the <TT>src</TT> directory:
+lua (the interpreter),
+luac (the compiler),
+and liblua.a (the library).
+<P>
+<LI>
+ To check that Lua has been built correctly, do "<KBD>make test</KBD>"
+ after building Lua. This will run the interpreter and print its version.
+</OL>
+<P>
+If you're running Linux, try "<KBD>make linux-readline</KBD>" to build the interactive Lua interpreter with handy line-editing and history capabilities.
+If you get compilation errors,
+make sure you have installed the <TT>readline</TT> development package
+(which is probably named <TT>libreadline-dev</TT> or <TT>readline-devel</TT>).
+If you get link errors after that,
+then try "<KBD>make linux-readline MYLIBS=-ltermcap</KBD>".
+
+<H3>Installing Lua</H3>
+<P>
+ Once you have built Lua, you may want to install it in an official
+ place in your system. In this case, do "<KBD>make install</KBD>". The official
+ place and the way to install files are defined in the <TT>Makefile</TT>. You'll
+ probably need the right permissions to install files, and so may need to do "<KBD>sudo make install</KBD>".
+
+<P>
+ To build and install Lua in one step, do "<KBD>make all install</KBD>",
+ or "<KBD>make xxx install</KBD>",
+ where xxx is your platform name.
+
+<P>
+ To install Lua locally after building it, do "<KBD>make local</KBD>".
+ This will create a directory <TT>install</TT> with subdirectories
+ <TT>bin</TT>, <TT>include</TT>, <TT>lib</TT>, <TT>man</TT>, <TT>share</TT>,
+ and install Lua as listed below.
+
+ To install Lua locally, but in some other directory, do
+ "<KBD>make install INSTALL_TOP=xxx</KBD>", where xxx is your chosen directory.
+ The installation starts in the <TT>src</TT> and <TT>doc</TT> directories,
+ so take care if <TT>INSTALL_TOP</TT> is not an absolute path.
+
+<DL CLASS="display">
+<DT>
+ bin:
+<DD>
+ lua luac
+<DT>
+ include:
+<DD>
+ lua.h luaconf.h lualib.h lauxlib.h lua.hpp
+<DT>
+ lib:
+<DD>
+ liblua.a
+<DT>
+ man/man1:
+<DD>
+ lua.1 luac.1
+</DL>
+
+<P>
+ These are the only directories you need for development.
+ If you only want to run Lua programs,
+ you only need the files in <TT>bin</TT> and <TT>man</TT>.
+ The files in <TT>include</TT> and <TT>lib</TT> are needed for
+ embedding Lua in C or C++ programs.
+
+<H3><A NAME="customization">Customization</A></H3>
+<P>
+ Three kinds of things can be customized by editing a file:
+<UL>
+ <LI> Where and how to install Lua &mdash; edit <TT>Makefile</TT>.
+ <LI> How to build Lua &mdash; edit <TT>src/Makefile</TT>.
+ <LI> Lua features &mdash; edit <TT>src/luaconf.h</TT>.
+</UL>
+
+<P>
+ You don't actually need to edit the Makefiles because you may set the
+ relevant variables in the command line when invoking make.
+ Nevertheless, it's probably best to edit and save the Makefiles to
+ record the changes you've made.
+
+<P>
+ On the other hand, if you need to customize some Lua features, you'll need
+ to edit <TT>src/luaconf.h</TT> before building and installing Lua.
+ The edited file will be the one installed, and
+ it will be used by any Lua clients that you build, to ensure consistency.
+ Further customization is available to experts by editing the Lua sources.
+
+<H3><A NAME="other">Building Lua on other systems</A></H3>
+<P>
+ If you're not using the usual Unix tools, then the instructions for
+ building Lua depend on the compiler you use. You'll need to create
+ projects (or whatever your compiler uses) for building the library,
+ the interpreter, and the compiler, as follows:
+
+<DL CLASS="display">
+<DT>
+library:
+<DD>
+lapi.c lcode.c lctype.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c ltable.c ltm.c lundump.c lvm.c lzio.c
+lauxlib.c lbaselib.c lcorolib.c ldblib.c liolib.c lmathlib.c loadlib.c loslib.c lstrlib.c ltablib.c lutf8lib.c linit.c
+<DT>
+interpreter:
+<DD>
+ library, lua.c
+<DT>
+compiler:
+<DD>
+ library, luac.c
+</DL>
+
+<P>
+ To use Lua as a library in your own programs, you'll need to know how to
+ create and use libraries with your compiler. Moreover, to dynamically load
+ C libraries for Lua, you'll need to know how to create dynamic libraries
+ and you'll need to make sure that the Lua API functions are accessible to
+ those dynamic libraries &mdash; but <EM>don't</EM> link the Lua library
+ into each dynamic library. For Unix, we recommend that the Lua library
+ be linked statically into the host program and its symbols exported for
+ dynamic linking; <TT>src/Makefile</TT> does this for the Lua interpreter.
+ For Windows, we recommend that the Lua library be a DLL.
+ In all cases, the compiler luac should be linked statically.
+
+<P>
+ As mentioned above, you may edit <TT>src/luaconf.h</TT> to customize
+ some features before building Lua.
+
+<H2><A NAME="changes">Changes since Lua 5.3</A></H2>
+<P>
+Here are the main changes introduced in Lua 5.4.
+The
+<A HREF="contents.html">reference manual</A>
+lists the
+<A HREF="manual.html#8">incompatibilities</A> that had to be introduced.
+
+<H3>Main changes</H3>
+<UL>
+<LI> new generational mode for garbage collection
+<LI> to-be-closed variables
+<LI> const variables
+<LI> userdata can have multiple user values
+<LI> new implementation for math.random
+<LI> warning system
+<LI> debug information about function arguments and returns
+<LI> new semantics for the integer 'for' loop
+<LI> optional 'init' argument to 'string.gmatch'
+<LI> new functions 'lua_resetthread' and 'coroutine.close'
+<LI> string-to-number coercions moved to the string library
+<LI> allocation function allowed to fail when shrinking a memory block
+<LI> new format '%p' in 'string.format'
+<LI> utf8 library accepts codepoints up to 2^31
+</UL>
+
+<H2><A NAME="license">License</A></H2>
+<P>
+<A HREF="http://www.opensource.org/docs/definition.php">
+<IMG SRC="osi-certified-72x60.png" ALIGN="right" ALT="[osi certified]" STYLE="padding-left: 30px ;">
+</A>
+Lua is free software distributed under the terms of the
+<A HREF="http://www.opensource.org/licenses/mit-license.html">MIT license</A>
+reproduced below;
+it may be used for any purpose, including commercial purposes,
+at absolutely no cost without having to ask us.
+
+The only requirement is that if you do use Lua,
+then you should give us credit by including the appropriate copyright notice somewhere in your product or its documentation.
+
+For details, see
+<A HREF="http://www.lua.org/license.html">this</A>.
+
+<BLOCKQUOTE STYLE="padding-bottom: 0em">
+Copyright &copy; 1994&ndash;2023 Lua.org, PUC-Rio.
+
+<P>
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+<P>
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+<P>
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+</BLOCKQUOTE>
+<P>
+
+<P CLASS="footer">
+Last update:
+Tue May 2 20:08:55 UTC 2023
+</P>
+<!--
+Last change: revised for Lua 5.4.6
+-->
+
+</BODY>
+</HTML>
diff --git a/src/libs/3rdparty/lua/lua.qbs b/src/libs/3rdparty/lua/lua.qbs
new file mode 100644
index 0000000000..73d61b039c
--- /dev/null
+++ b/src/libs/3rdparty/lua/lua.qbs
@@ -0,0 +1,81 @@
+QtcLibrary {
+ name: "lua546"
+ type: "staticlibrary"
+
+ cpp.defines: {
+ var defines = base;
+ if (qbs.targetOS.contains("windows"))
+ defines.push("LUA_USE_WINDOWS");
+ else if (qbs.targetOS.contains("macos"))
+ defines.push("LUA_USE_MACOSX");
+ else if (qbs.targetOS.contains("linux"))
+ defines.push("LUA_USE_LINUX");
+ return defines;
+ }
+
+ Group {
+ name: "Sources"
+ prefix: "src/"
+
+ files: [
+ "lapi.c",
+ "lapi.h",
+ "lauxlib.c",
+ "lauxlib.h",
+ "lbaselib.c",
+ "lcode.c",
+ "lcode.h",
+ "lcorolib.c",
+ "lctype.c",
+ "lctype.h",
+ "ldblib.c",
+ "ldebug.c",
+ "ldebug.h",
+ "ldo.c",
+ "ldo.h",
+ "ldump.c",
+ "lfunc.c",
+ "lfunc.h",
+ "lgc.c",
+ "lgc.h",
+ "linit.c",
+ "liolib.c",
+ "llex.c",
+ "llex.h",
+ "lmathlib.c",
+ "lmem.c",
+ "lmem.h",
+ "loadlib.c",
+ "lobject.c",
+ "lobject.h",
+ "lopcodes.c",
+ "lopcodes.h",
+ "loslib.c",
+ "lparser.c",
+ "lparser.h",
+ "lstate.c",
+ "lstate.h",
+ "lstring.c",
+ "lstring.h",
+ "lstrlib.c",
+ "ltable.c",
+ "ltable.h",
+ "ltablib.c",
+ "ltm.c",
+ "ltm.h",
+ "lua.c",
+ "lua.h",
+ "lundump.c",
+ "lundump.h",
+ "lutf8lib.c",
+ "lvm.c",
+ "lvm.h",
+ "lzio.c",
+ "lzio.h",
+ ]
+ }
+
+ Export {
+ cpp.includePaths: project.ide_source_tree + "/src/libs/3rdparty/lua/src"
+ }
+}
diff --git a/src/libs/3rdparty/lua/src/lapi.c b/src/libs/3rdparty/lua/src/lapi.c
new file mode 100644
index 0000000000..34e64af142
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lapi.c
@@ -0,0 +1,1463 @@
+/*
+** $Id: lapi.c $
+** Lua API
+** See Copyright Notice in lua.h
+*/
+
+#define lapi_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <limits.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lundump.h"
+#include "lvm.h"
+
+
+
+const char lua_ident[] =
+ "$LuaVersion: " LUA_COPYRIGHT " $"
+ "$LuaAuthors: " LUA_AUTHORS " $";
+
+
+
+/*
+** Test for a valid index (one that is not the 'nilvalue').
+** '!ttisnil(o)' implies 'o != &G(L)->nilvalue', so it is not needed.
+** However, it covers the most common cases in a faster way.
+*/
+#define isvalid(L, o) (!ttisnil(o) || o != &G(L)->nilvalue)
+
+
+/* test for pseudo index */
+#define ispseudo(i) ((i) <= LUA_REGISTRYINDEX)
+
+/* test for upvalue */
+#define isupvalue(i) ((i) < LUA_REGISTRYINDEX)
+
+
+/*
+** Convert an acceptable index to a pointer to its respective value.
+** Non-valid indices return the special nil value 'G(L)->nilvalue'.
+*/
+static TValue *index2value (lua_State *L, int idx) {
+ CallInfo *ci = L->ci;
+ if (idx > 0) {
+ StkId o = ci->func.p + idx;
+ api_check(L, idx <= ci->top.p - (ci->func.p + 1), "unacceptable index");
+ if (o >= L->top.p) return &G(L)->nilvalue;
+ else return s2v(o);
+ }
+ else if (!ispseudo(idx)) { /* negative index */
+ api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1),
+ "invalid index");
+ return s2v(L->top.p + idx);
+ }
+ else if (idx == LUA_REGISTRYINDEX)
+ return &G(L)->l_registry;
+ else { /* upvalues */
+ idx = LUA_REGISTRYINDEX - idx;
+ api_check(L, idx <= MAXUPVAL + 1, "upvalue index too large");
+ if (ttisCclosure(s2v(ci->func.p))) { /* C closure? */
+ CClosure *func = clCvalue(s2v(ci->func.p));
+ return (idx <= func->nupvalues) ? &func->upvalue[idx-1]
+ : &G(L)->nilvalue;
+ }
+ else { /* light C function or Lua function (through a hook)?) */
+ api_check(L, ttislcf(s2v(ci->func.p)), "caller not a C function");
+ return &G(L)->nilvalue; /* no upvalues */
+ }
+ }
+}
+
+
+
+/*
+** Convert a valid actual index (not a pseudo-index) to its address.
+*/
+l_sinline StkId index2stack (lua_State *L, int idx) {
+ CallInfo *ci = L->ci;
+ if (idx > 0) {
+ StkId o = ci->func.p + idx;
+ api_check(L, o < L->top.p, "invalid index");
+ return o;
+ }
+ else { /* non-positive index */
+ api_check(L, idx != 0 && -idx <= L->top.p - (ci->func.p + 1),
+ "invalid index");
+ api_check(L, !ispseudo(idx), "invalid index");
+ return L->top.p + idx;
+ }
+}
+
+
+LUA_API int lua_checkstack (lua_State *L, int n) {
+ int res;
+ CallInfo *ci;
+ lua_lock(L);
+ ci = L->ci;
+ api_check(L, n >= 0, "negative 'n'");
+ if (L->stack_last.p - L->top.p > n) /* stack large enough? */
+ res = 1; /* yes; check is OK */
+ else /* need to grow stack */
+ res = luaD_growstack(L, n, 0);
+ if (res && ci->top.p < L->top.p + n)
+ ci->top.p = L->top.p + n; /* adjust frame top */
+ lua_unlock(L);
+ return res;
+}
+
+
+LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) {
+ int i;
+ if (from == to) return;
+ lua_lock(to);
+ api_checknelems(from, n);
+ api_check(from, G(from) == G(to), "moving among independent states");
+ api_check(from, to->ci->top.p - to->top.p >= n, "stack overflow");
+ from->top.p -= n;
+ for (i = 0; i < n; i++) {
+ setobjs2s(to, to->top.p, from->top.p + i);
+ to->top.p++; /* stack already checked by previous 'api_check' */
+ }
+ lua_unlock(to);
+}
+
+
+LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) {
+ lua_CFunction old;
+ lua_lock(L);
+ old = G(L)->panic;
+ G(L)->panic = panicf;
+ lua_unlock(L);
+ return old;
+}
+
+
+LUA_API lua_Number lua_version (lua_State *L) {
+ UNUSED(L);
+ return LUA_VERSION_NUM;
+}
+
+
+
+/*
+** basic stack manipulation
+*/
+
+
+/*
+** convert an acceptable stack index into an absolute index
+*/
+LUA_API int lua_absindex (lua_State *L, int idx) {
+ return (idx > 0 || ispseudo(idx))
+ ? idx
+ : cast_int(L->top.p - L->ci->func.p) + idx;
+}
+
+
+LUA_API int lua_gettop (lua_State *L) {
+ return cast_int(L->top.p - (L->ci->func.p + 1));
+}
+
+
+LUA_API void lua_settop (lua_State *L, int idx) {
+ CallInfo *ci;
+ StkId func, newtop;
+ ptrdiff_t diff; /* difference for new top */
+ lua_lock(L);
+ ci = L->ci;
+ func = ci->func.p;
+ if (idx >= 0) {
+ api_check(L, idx <= ci->top.p - (func + 1), "new top too large");
+ diff = ((func + 1) + idx) - L->top.p;
+ for (; diff > 0; diff--)
+ setnilvalue(s2v(L->top.p++)); /* clear new slots */
+ }
+ else {
+ api_check(L, -(idx+1) <= (L->top.p - (func + 1)), "invalid new top");
+ diff = idx + 1; /* will "subtract" index (as it is negative) */
+ }
+ api_check(L, L->tbclist.p < L->top.p, "previous pop of an unclosed slot");
+ newtop = L->top.p + diff;
+ if (diff < 0 && L->tbclist.p >= newtop) {
+ lua_assert(hastocloseCfunc(ci->nresults));
+ newtop = luaF_close(L, newtop, CLOSEKTOP, 0);
+ }
+ L->top.p = newtop; /* correct top only after closing any upvalue */
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_closeslot (lua_State *L, int idx) {
+ StkId level;
+ lua_lock(L);
+ level = index2stack(L, idx);
+ api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist.p == level,
+ "no variable to close at given level");
+ level = luaF_close(L, level, CLOSEKTOP, 0);
+ setnilvalue(s2v(level));
+ lua_unlock(L);
+}
+
+
+/*
+** Reverse the stack segment from 'from' to 'to'
+** (auxiliary to 'lua_rotate')
+** Note that we move(copy) only the value inside the stack.
+** (We do not move additional fields that may exist.)
+*/
+l_sinline void reverse (lua_State *L, StkId from, StkId to) {
+ for (; from < to; from++, to--) {
+ TValue temp;
+ setobj(L, &temp, s2v(from));
+ setobjs2s(L, from, to);
+ setobj2s(L, to, &temp);
+ }
+}
+
+
+/*
+** Let x = AB, where A is a prefix of length 'n'. Then,
+** rotate x n == BA. But BA == (A^r . B^r)^r.
+*/
+LUA_API void lua_rotate (lua_State *L, int idx, int n) {
+ StkId p, t, m;
+ lua_lock(L);
+ t = L->top.p - 1; /* end of stack segment being rotated */
+ p = index2stack(L, idx); /* start of segment */
+ api_check(L, (n >= 0 ? n : -n) <= (t - p + 1), "invalid 'n'");
+ m = (n >= 0 ? t - n : p - n - 1); /* end of prefix */
+ reverse(L, p, m); /* reverse the prefix with length 'n' */
+ reverse(L, m + 1, t); /* reverse the suffix */
+ reverse(L, p, t); /* reverse the entire segment */
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) {
+ TValue *fr, *to;
+ lua_lock(L);
+ fr = index2value(L, fromidx);
+ to = index2value(L, toidx);
+ api_check(L, isvalid(L, to), "invalid index");
+ setobj(L, to, fr);
+ if (isupvalue(toidx)) /* function upvalue? */
+ luaC_barrier(L, clCvalue(s2v(L->ci->func.p)), fr);
+ /* LUA_REGISTRYINDEX does not need gc barrier
+ (collector revisits it before finishing collection) */
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushvalue (lua_State *L, int idx) {
+ lua_lock(L);
+ setobj2s(L, L->top.p, index2value(L, idx));
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+
+/*
+** access functions (stack -> C)
+*/
+
+
+LUA_API int lua_type (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ return (isvalid(L, o) ? ttype(o) : LUA_TNONE);
+}
+
+
+LUA_API const char *lua_typename (lua_State *L, int t) {
+ UNUSED(L);
+ api_check(L, LUA_TNONE <= t && t < LUA_NUMTYPES, "invalid type");
+ return ttypename(t);
+}
+
+
+LUA_API int lua_iscfunction (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ return (ttislcf(o) || (ttisCclosure(o)));
+}
+
+
+LUA_API int lua_isinteger (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ return ttisinteger(o);
+}
+
+
+LUA_API int lua_isnumber (lua_State *L, int idx) {
+ lua_Number n;
+ const TValue *o = index2value(L, idx);
+ return tonumber(o, &n);
+}
+
+
+LUA_API int lua_isstring (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ return (ttisstring(o) || cvt2str(o));
+}
+
+
+LUA_API int lua_isuserdata (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ return (ttisfulluserdata(o) || ttislightuserdata(o));
+}
+
+
+LUA_API int lua_rawequal (lua_State *L, int index1, int index2) {
+ const TValue *o1 = index2value(L, index1);
+ const TValue *o2 = index2value(L, index2);
+ return (isvalid(L, o1) && isvalid(L, o2)) ? luaV_rawequalobj(o1, o2) : 0;
+}
+
+
+LUA_API void lua_arith (lua_State *L, int op) {
+ lua_lock(L);
+ if (op != LUA_OPUNM && op != LUA_OPBNOT)
+ api_checknelems(L, 2); /* all other operations expect two operands */
+ else { /* for unary operations, add fake 2nd operand */
+ api_checknelems(L, 1);
+ setobjs2s(L, L->top.p, L->top.p - 1);
+ api_incr_top(L);
+ }
+ /* first operand at top - 2, second at top - 1; result go to top - 2 */
+ luaO_arith(L, op, s2v(L->top.p - 2), s2v(L->top.p - 1), L->top.p - 2);
+ L->top.p--; /* remove second operand */
+ lua_unlock(L);
+}
+
+
+LUA_API int lua_compare (lua_State *L, int index1, int index2, int op) {
+ const TValue *o1;
+ const TValue *o2;
+ int i = 0;
+ lua_lock(L); /* may call tag method */
+ o1 = index2value(L, index1);
+ o2 = index2value(L, index2);
+ if (isvalid(L, o1) && isvalid(L, o2)) {
+ switch (op) {
+ case LUA_OPEQ: i = luaV_equalobj(L, o1, o2); break;
+ case LUA_OPLT: i = luaV_lessthan(L, o1, o2); break;
+ case LUA_OPLE: i = luaV_lessequal(L, o1, o2); break;
+ default: api_check(L, 0, "invalid option");
+ }
+ }
+ lua_unlock(L);
+ return i;
+}
+
+
+LUA_API size_t lua_stringtonumber (lua_State *L, const char *s) {
+ size_t sz = luaO_str2num(s, s2v(L->top.p));
+ if (sz != 0)
+ api_incr_top(L);
+ return sz;
+}
+
+
+LUA_API lua_Number lua_tonumberx (lua_State *L, int idx, int *pisnum) {
+ lua_Number n = 0;
+ const TValue *o = index2value(L, idx);
+ int isnum = tonumber(o, &n);
+ if (pisnum)
+ *pisnum = isnum;
+ return n;
+}
+
+
+LUA_API lua_Integer lua_tointegerx (lua_State *L, int idx, int *pisnum) {
+ lua_Integer res = 0;
+ const TValue *o = index2value(L, idx);
+ int isnum = tointeger(o, &res);
+ if (pisnum)
+ *pisnum = isnum;
+ return res;
+}
+
+
+LUA_API int lua_toboolean (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ return !l_isfalse(o);
+}
+
+
+LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) {
+ TValue *o;
+ lua_lock(L);
+ o = index2value(L, idx);
+ if (!ttisstring(o)) {
+ if (!cvt2str(o)) { /* not convertible? */
+ if (len != NULL) *len = 0;
+ lua_unlock(L);
+ return NULL;
+ }
+ luaO_tostring(L, o);
+ luaC_checkGC(L);
+ o = index2value(L, idx); /* previous call may reallocate the stack */
+ }
+ if (len != NULL)
+ *len = vslen(o);
+ lua_unlock(L);
+ return svalue(o);
+}
+
+
+LUA_API lua_Unsigned lua_rawlen (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ switch (ttypetag(o)) {
+ case LUA_VSHRSTR: return tsvalue(o)->shrlen;
+ case LUA_VLNGSTR: return tsvalue(o)->u.lnglen;
+ case LUA_VUSERDATA: return uvalue(o)->len;
+ case LUA_VTABLE: return luaH_getn(hvalue(o));
+ default: return 0;
+ }
+}
+
+
+LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ if (ttislcf(o)) return fvalue(o);
+ else if (ttisCclosure(o))
+ return clCvalue(o)->f;
+ else return NULL; /* not a C function */
+}
+
+
+l_sinline void *touserdata (const TValue *o) {
+ switch (ttype(o)) {
+ case LUA_TUSERDATA: return getudatamem(uvalue(o));
+ case LUA_TLIGHTUSERDATA: return pvalue(o);
+ default: return NULL;
+ }
+}
+
+
+LUA_API void *lua_touserdata (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ return touserdata(o);
+}
+
+
+LUA_API lua_State *lua_tothread (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ return (!ttisthread(o)) ? NULL : thvalue(o);
+}
+
+
+/*
+** Returns a pointer to the internal representation of an object.
+** Note that ANSI C does not allow the conversion of a pointer to
+** function to a 'void*', so the conversion here goes through
+** a 'size_t'. (As the returned pointer is only informative, this
+** conversion should not be a problem.)
+*/
+LUA_API const void *lua_topointer (lua_State *L, int idx) {
+ const TValue *o = index2value(L, idx);
+ switch (ttypetag(o)) {
+ case LUA_VLCF: return cast_voidp(cast_sizet(fvalue(o)));
+ case LUA_VUSERDATA: case LUA_VLIGHTUSERDATA:
+ return touserdata(o);
+ default: {
+ if (iscollectable(o))
+ return gcvalue(o);
+ else
+ return NULL;
+ }
+ }
+}
+
+
+
+/*
+** push functions (C -> stack)
+*/
+
+
+LUA_API void lua_pushnil (lua_State *L) {
+ lua_lock(L);
+ setnilvalue(s2v(L->top.p));
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushnumber (lua_State *L, lua_Number n) {
+ lua_lock(L);
+ setfltvalue(s2v(L->top.p), n);
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) {
+ lua_lock(L);
+ setivalue(s2v(L->top.p), n);
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+/*
+** Pushes on the stack a string with given length. Avoid using 's' when
+** 'len' == 0 (as 's' can be NULL in that case), due to later use of
+** 'memcmp' and 'memcpy'.
+*/
+LUA_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) {
+ TString *ts;
+ lua_lock(L);
+ ts = (len == 0) ? luaS_new(L, "") : luaS_newlstr(L, s, len);
+ setsvalue2s(L, L->top.p, ts);
+ api_incr_top(L);
+ luaC_checkGC(L);
+ lua_unlock(L);
+ return getstr(ts);
+}
+
+
+LUA_API const char *lua_pushstring (lua_State *L, const char *s) {
+ lua_lock(L);
+ if (s == NULL)
+ setnilvalue(s2v(L->top.p));
+ else {
+ TString *ts;
+ ts = luaS_new(L, s);
+ setsvalue2s(L, L->top.p, ts);
+ s = getstr(ts); /* internal copy's address */
+ }
+ api_incr_top(L);
+ luaC_checkGC(L);
+ lua_unlock(L);
+ return s;
+}
+
+
+LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt,
+ va_list argp) {
+ const char *ret;
+ lua_lock(L);
+ ret = luaO_pushvfstring(L, fmt, argp);
+ luaC_checkGC(L);
+ lua_unlock(L);
+ return ret;
+}
+
+
+LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) {
+ const char *ret;
+ va_list argp;
+ lua_lock(L);
+ va_start(argp, fmt);
+ ret = luaO_pushvfstring(L, fmt, argp);
+ va_end(argp);
+ luaC_checkGC(L);
+ lua_unlock(L);
+ return ret;
+}
+
+
+LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
+ lua_lock(L);
+ if (n == 0) {
+ setfvalue(s2v(L->top.p), fn);
+ api_incr_top(L);
+ }
+ else {
+ CClosure *cl;
+ api_checknelems(L, n);
+ api_check(L, n <= MAXUPVAL, "upvalue index too large");
+ cl = luaF_newCclosure(L, n);
+ cl->f = fn;
+ L->top.p -= n;
+ while (n--) {
+ setobj2n(L, &cl->upvalue[n], s2v(L->top.p + n));
+ /* does not need barrier because closure is white */
+ lua_assert(iswhite(cl));
+ }
+ setclCvalue(L, s2v(L->top.p), cl);
+ api_incr_top(L);
+ luaC_checkGC(L);
+ }
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushboolean (lua_State *L, int b) {
+ lua_lock(L);
+ if (b)
+ setbtvalue(s2v(L->top.p));
+ else
+ setbfvalue(s2v(L->top.p));
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_pushlightuserdata (lua_State *L, void *p) {
+ lua_lock(L);
+ setpvalue(s2v(L->top.p), p);
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API int lua_pushthread (lua_State *L) {
+ lua_lock(L);
+ setthvalue(L, s2v(L->top.p), L);
+ api_incr_top(L);
+ lua_unlock(L);
+ return (G(L)->mainthread == L);
+}
+
+
+
+/*
+** get functions (Lua -> stack)
+*/
+
+
+l_sinline int auxgetstr (lua_State *L, const TValue *t, const char *k) {
+ const TValue *slot;
+ TString *str = luaS_new(L, k);
+ if (luaV_fastget(L, t, str, slot, luaH_getstr)) {
+ setobj2s(L, L->top.p, slot);
+ api_incr_top(L);
+ }
+ else {
+ setsvalue2s(L, L->top.p, str);
+ api_incr_top(L);
+ luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot);
+ }
+ lua_unlock(L);
+ return ttype(s2v(L->top.p - 1));
+}
+
+
+/*
+** Get the global table in the registry. Since all predefined
+** indices in the registry were inserted right when the registry
+** was created and never removed, they must always be in the array
+** part of the registry.
+*/
+#define getGtable(L) \
+ (&hvalue(&G(L)->l_registry)->array[LUA_RIDX_GLOBALS - 1])
+
+
+LUA_API int lua_getglobal (lua_State *L, const char *name) {
+ const TValue *G;
+ lua_lock(L);
+ G = getGtable(L);
+ return auxgetstr(L, G, name);
+}
+
+
+LUA_API int lua_gettable (lua_State *L, int idx) {
+ const TValue *slot;
+ TValue *t;
+ lua_lock(L);
+ t = index2value(L, idx);
+ if (luaV_fastget(L, t, s2v(L->top.p - 1), slot, luaH_get)) {
+ setobj2s(L, L->top.p - 1, slot);
+ }
+ else
+ luaV_finishget(L, t, s2v(L->top.p - 1), L->top.p - 1, slot);
+ lua_unlock(L);
+ return ttype(s2v(L->top.p - 1));
+}
+
+
+LUA_API int lua_getfield (lua_State *L, int idx, const char *k) {
+ lua_lock(L);
+ return auxgetstr(L, index2value(L, idx), k);
+}
+
+
+LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) {
+ TValue *t;
+ const TValue *slot;
+ lua_lock(L);
+ t = index2value(L, idx);
+ if (luaV_fastgeti(L, t, n, slot)) {
+ setobj2s(L, L->top.p, slot);
+ }
+ else {
+ TValue aux;
+ setivalue(&aux, n);
+ luaV_finishget(L, t, &aux, L->top.p, slot);
+ }
+ api_incr_top(L);
+ lua_unlock(L);
+ return ttype(s2v(L->top.p - 1));
+}
+
+
+l_sinline int finishrawget (lua_State *L, const TValue *val) {
+ if (isempty(val)) /* avoid copying empty items to the stack */
+ setnilvalue(s2v(L->top.p));
+ else
+ setobj2s(L, L->top.p, val);
+ api_incr_top(L);
+ lua_unlock(L);
+ return ttype(s2v(L->top.p - 1));
+}
+
+
+static Table *gettable (lua_State *L, int idx) {
+ TValue *t = index2value(L, idx);
+ api_check(L, ttistable(t), "table expected");
+ return hvalue(t);
+}
+
+
+LUA_API int lua_rawget (lua_State *L, int idx) {
+ Table *t;
+ const TValue *val;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ t = gettable(L, idx);
+ val = luaH_get(t, s2v(L->top.p - 1));
+ L->top.p--; /* remove key */
+ return finishrawget(L, val);
+}
+
+
+LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) {
+ Table *t;
+ lua_lock(L);
+ t = gettable(L, idx);
+ return finishrawget(L, luaH_getint(t, n));
+}
+
+
+LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) {
+ Table *t;
+ TValue k;
+ lua_lock(L);
+ t = gettable(L, idx);
+ setpvalue(&k, cast_voidp(p));
+ return finishrawget(L, luaH_get(t, &k));
+}
+
+
+LUA_API void lua_createtable (lua_State *L, int narray, int nrec) {
+ Table *t;
+ lua_lock(L);
+ t = luaH_new(L);
+ sethvalue2s(L, L->top.p, t);
+ api_incr_top(L);
+ if (narray > 0 || nrec > 0)
+ luaH_resize(L, t, narray, nrec);
+ luaC_checkGC(L);
+ lua_unlock(L);
+}
+
+
+LUA_API int lua_getmetatable (lua_State *L, int objindex) {
+ const TValue *obj;
+ Table *mt;
+ int res = 0;
+ lua_lock(L);
+ obj = index2value(L, objindex);
+ switch (ttype(obj)) {
+ case LUA_TTABLE:
+ mt = hvalue(obj)->metatable;
+ break;
+ case LUA_TUSERDATA:
+ mt = uvalue(obj)->metatable;
+ break;
+ default:
+ mt = G(L)->mt[ttype(obj)];
+ break;
+ }
+ if (mt != NULL) {
+ sethvalue2s(L, L->top.p, mt);
+ api_incr_top(L);
+ res = 1;
+ }
+ lua_unlock(L);
+ return res;
+}
+
+
+LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) {
+ TValue *o;
+ int t;
+ lua_lock(L);
+ o = index2value(L, idx);
+ api_check(L, ttisfulluserdata(o), "full userdata expected");
+ if (n <= 0 || n > uvalue(o)->nuvalue) {
+ setnilvalue(s2v(L->top.p));
+ t = LUA_TNONE;
+ }
+ else {
+ setobj2s(L, L->top.p, &uvalue(o)->uv[n - 1].uv);
+ t = ttype(s2v(L->top.p));
+ }
+ api_incr_top(L);
+ lua_unlock(L);
+ return t;
+}
+
+
+/*
+** set functions (stack -> Lua)
+*/
+
+/*
+** t[k] = value at the top of the stack (where 'k' is a string)
+*/
+static void auxsetstr (lua_State *L, const TValue *t, const char *k) {
+ const TValue *slot;
+ TString *str = luaS_new(L, k);
+ api_checknelems(L, 1);
+ if (luaV_fastget(L, t, str, slot, luaH_getstr)) {
+ luaV_finishfastset(L, t, slot, s2v(L->top.p - 1));
+ L->top.p--; /* pop value */
+ }
+ else {
+ setsvalue2s(L, L->top.p, str); /* push 'str' (to make it a TValue) */
+ api_incr_top(L);
+ luaV_finishset(L, t, s2v(L->top.p - 1), s2v(L->top.p - 2), slot);
+ L->top.p -= 2; /* pop value and key */
+ }
+ lua_unlock(L); /* lock done by caller */
+}
+
+
+LUA_API void lua_setglobal (lua_State *L, const char *name) {
+ const TValue *G;
+ lua_lock(L); /* unlock done in 'auxsetstr' */
+ G = getGtable(L);
+ auxsetstr(L, G, name);
+}
+
+
+LUA_API void lua_settable (lua_State *L, int idx) {
+ TValue *t;
+ const TValue *slot;
+ lua_lock(L);
+ api_checknelems(L, 2);
+ t = index2value(L, idx);
+ if (luaV_fastget(L, t, s2v(L->top.p - 2), slot, luaH_get)) {
+ luaV_finishfastset(L, t, slot, s2v(L->top.p - 1));
+ }
+ else
+ luaV_finishset(L, t, s2v(L->top.p - 2), s2v(L->top.p - 1), slot);
+ L->top.p -= 2; /* pop index and value */
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_setfield (lua_State *L, int idx, const char *k) {
+ lua_lock(L); /* unlock done in 'auxsetstr' */
+ auxsetstr(L, index2value(L, idx), k);
+}
+
+
+LUA_API void lua_seti (lua_State *L, int idx, lua_Integer n) {
+ TValue *t;
+ const TValue *slot;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ t = index2value(L, idx);
+ if (luaV_fastgeti(L, t, n, slot)) {
+ luaV_finishfastset(L, t, slot, s2v(L->top.p - 1));
+ }
+ else {
+ TValue aux;
+ setivalue(&aux, n);
+ luaV_finishset(L, t, &aux, s2v(L->top.p - 1), slot);
+ }
+ L->top.p--; /* pop value */
+ lua_unlock(L);
+}
+
+
+static void aux_rawset (lua_State *L, int idx, TValue *key, int n) {
+ Table *t;
+ lua_lock(L);
+ api_checknelems(L, n);
+ t = gettable(L, idx);
+ luaH_set(L, t, key, s2v(L->top.p - 1));
+ invalidateTMcache(t);
+ luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1));
+ L->top.p -= n;
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_rawset (lua_State *L, int idx) {
+ aux_rawset(L, idx, s2v(L->top.p - 2), 2);
+}
+
+
+LUA_API void lua_rawsetp (lua_State *L, int idx, const void *p) {
+ TValue k;
+ setpvalue(&k, cast_voidp(p));
+ aux_rawset(L, idx, &k, 1);
+}
+
+
+LUA_API void lua_rawseti (lua_State *L, int idx, lua_Integer n) {
+ Table *t;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ t = gettable(L, idx);
+ luaH_setint(L, t, n, s2v(L->top.p - 1));
+ luaC_barrierback(L, obj2gco(t), s2v(L->top.p - 1));
+ L->top.p--;
+ lua_unlock(L);
+}
+
+
+LUA_API int lua_setmetatable (lua_State *L, int objindex) {
+ TValue *obj;
+ Table *mt;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ obj = index2value(L, objindex);
+ if (ttisnil(s2v(L->top.p - 1)))
+ mt = NULL;
+ else {
+ api_check(L, ttistable(s2v(L->top.p - 1)), "table expected");
+ mt = hvalue(s2v(L->top.p - 1));
+ }
+ switch (ttype(obj)) {
+ case LUA_TTABLE: {
+ hvalue(obj)->metatable = mt;
+ if (mt) {
+ luaC_objbarrier(L, gcvalue(obj), mt);
+ luaC_checkfinalizer(L, gcvalue(obj), mt);
+ }
+ break;
+ }
+ case LUA_TUSERDATA: {
+ uvalue(obj)->metatable = mt;
+ if (mt) {
+ luaC_objbarrier(L, uvalue(obj), mt);
+ luaC_checkfinalizer(L, gcvalue(obj), mt);
+ }
+ break;
+ }
+ default: {
+ G(L)->mt[ttype(obj)] = mt;
+ break;
+ }
+ }
+ L->top.p--;
+ lua_unlock(L);
+ return 1;
+}
+
+
+LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) {
+ TValue *o;
+ int res;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ o = index2value(L, idx);
+ api_check(L, ttisfulluserdata(o), "full userdata expected");
+ if (!(cast_uint(n) - 1u < cast_uint(uvalue(o)->nuvalue)))
+ res = 0; /* 'n' not in [1, uvalue(o)->nuvalue] */
+ else {
+ setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top.p - 1));
+ luaC_barrierback(L, gcvalue(o), s2v(L->top.p - 1));
+ res = 1;
+ }
+ L->top.p--;
+ lua_unlock(L);
+ return res;
+}
+
+
+/*
+** 'load' and 'call' functions (run Lua code)
+*/
+
+
+#define checkresults(L,na,nr) \
+ api_check(L, (nr) == LUA_MULTRET \
+ || (L->ci->top.p - L->top.p >= (nr) - (na)), \
+ "results from function overflow current stack size")
+
+
+LUA_API void lua_callk (lua_State *L, int nargs, int nresults,
+ lua_KContext ctx, lua_KFunction k) {
+ StkId func;
+ lua_lock(L);
+ api_check(L, k == NULL || !isLua(L->ci),
+ "cannot use continuations inside hooks");
+ api_checknelems(L, nargs+1);
+ api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread");
+ checkresults(L, nargs, nresults);
+ func = L->top.p - (nargs+1);
+ if (k != NULL && yieldable(L)) { /* need to prepare continuation? */
+ L->ci->u.c.k = k; /* save continuation */
+ L->ci->u.c.ctx = ctx; /* save context */
+ luaD_call(L, func, nresults); /* do the call */
+ }
+ else /* no continuation or no yieldable */
+ luaD_callnoyield(L, func, nresults); /* just do the call */
+ adjustresults(L, nresults);
+ lua_unlock(L);
+}
+
+
+
+/*
+** Execute a protected call.
+*/
+struct CallS { /* data to 'f_call' */
+ StkId func;
+ int nresults;
+};
+
+
+static void f_call (lua_State *L, void *ud) {
+ struct CallS *c = cast(struct CallS *, ud);
+ luaD_callnoyield(L, c->func, c->nresults);
+}
+
+
+
+LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc,
+ lua_KContext ctx, lua_KFunction k) {
+ struct CallS c;
+ int status;
+ ptrdiff_t func;
+ lua_lock(L);
+ api_check(L, k == NULL || !isLua(L->ci),
+ "cannot use continuations inside hooks");
+ api_checknelems(L, nargs+1);
+ api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread");
+ checkresults(L, nargs, nresults);
+ if (errfunc == 0)
+ func = 0;
+ else {
+ StkId o = index2stack(L, errfunc);
+ api_check(L, ttisfunction(s2v(o)), "error handler must be a function");
+ func = savestack(L, o);
+ }
+ c.func = L->top.p - (nargs+1); /* function to be called */
+ if (k == NULL || !yieldable(L)) { /* no continuation or no yieldable? */
+ c.nresults = nresults; /* do a 'conventional' protected call */
+ status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
+ }
+ else { /* prepare continuation (call is already protected by 'resume') */
+ CallInfo *ci = L->ci;
+ ci->u.c.k = k; /* save continuation */
+ ci->u.c.ctx = ctx; /* save context */
+ /* save information for error recovery */
+ ci->u2.funcidx = cast_int(savestack(L, c.func));
+ ci->u.c.old_errfunc = L->errfunc;
+ L->errfunc = func;
+ setoah(ci->callstatus, L->allowhook); /* save value of 'allowhook' */
+ ci->callstatus |= CIST_YPCALL; /* function can do error recovery */
+ luaD_call(L, c.func, nresults); /* do the call */
+ ci->callstatus &= ~CIST_YPCALL;
+ L->errfunc = ci->u.c.old_errfunc;
+ status = LUA_OK; /* if it is here, there were no errors */
+ }
+ adjustresults(L, nresults);
+ lua_unlock(L);
+ return status;
+}
+
+
+LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
+ const char *chunkname, const char *mode) {
+ ZIO z;
+ int status;
+ lua_lock(L);
+ if (!chunkname) chunkname = "?";
+ luaZ_init(L, &z, reader, data);
+ status = luaD_protectedparser(L, &z, chunkname, mode);
+ if (status == LUA_OK) { /* no errors? */
+ LClosure *f = clLvalue(s2v(L->top.p - 1)); /* get new function */
+ if (f->nupvalues >= 1) { /* does it have an upvalue? */
+ /* get global table from registry */
+ const TValue *gt = getGtable(L);
+ /* set global table as 1st upvalue of 'f' (may be LUA_ENV) */
+ setobj(L, f->upvals[0]->v.p, gt);
+ luaC_barrier(L, f->upvals[0], gt);
+ }
+ }
+ lua_unlock(L);
+ return status;
+}
+
+
+LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data, int strip) {
+ int status;
+ TValue *o;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ o = s2v(L->top.p - 1);
+ if (isLfunction(o))
+ status = luaU_dump(L, getproto(o), writer, data, strip);
+ else
+ status = 1;
+ lua_unlock(L);
+ return status;
+}
+
+
+LUA_API int lua_status (lua_State *L) {
+ return L->status;
+}
+
+
+/*
+** Garbage-collection function
+*/
+LUA_API int lua_gc (lua_State *L, int what, ...) {
+ va_list argp;
+ int res = 0;
+ global_State *g = G(L);
+ if (g->gcstp & GCSTPGC) /* internal stop? */
+ return -1; /* all options are invalid when stopped */
+ lua_lock(L);
+ va_start(argp, what);
+ switch (what) {
+ case LUA_GCSTOP: {
+ g->gcstp = GCSTPUSR; /* stopped by the user */
+ break;
+ }
+ case LUA_GCRESTART: {
+ luaE_setdebt(g, 0);
+ g->gcstp = 0; /* (GCSTPGC must be already zero here) */
+ break;
+ }
+ case LUA_GCCOLLECT: {
+ luaC_fullgc(L, 0);
+ break;
+ }
+ case LUA_GCCOUNT: {
+ /* GC values are expressed in Kbytes: #bytes/2^10 */
+ res = cast_int(gettotalbytes(g) >> 10);
+ break;
+ }
+ case LUA_GCCOUNTB: {
+ res = cast_int(gettotalbytes(g) & 0x3ff);
+ break;
+ }
+ case LUA_GCSTEP: {
+ int data = va_arg(argp, int);
+ l_mem debt = 1; /* =1 to signal that it did an actual step */
+ lu_byte oldstp = g->gcstp;
+ g->gcstp = 0; /* allow GC to run (GCSTPGC must be zero here) */
+ if (data == 0) {
+ luaE_setdebt(g, 0); /* do a basic step */
+ luaC_step(L);
+ }
+ else { /* add 'data' to total debt */
+ debt = cast(l_mem, data) * 1024 + g->GCdebt;
+ luaE_setdebt(g, debt);
+ luaC_checkGC(L);
+ }
+ g->gcstp = oldstp; /* restore previous state */
+ if (debt > 0 && g->gcstate == GCSpause) /* end of cycle? */
+ res = 1; /* signal it */
+ break;
+ }
+ case LUA_GCSETPAUSE: {
+ int data = va_arg(argp, int);
+ res = getgcparam(g->gcpause);
+ setgcparam(g->gcpause, data);
+ break;
+ }
+ case LUA_GCSETSTEPMUL: {
+ int data = va_arg(argp, int);
+ res = getgcparam(g->gcstepmul);
+ setgcparam(g->gcstepmul, data);
+ break;
+ }
+ case LUA_GCISRUNNING: {
+ res = gcrunning(g);
+ break;
+ }
+ case LUA_GCGEN: {
+ int minormul = va_arg(argp, int);
+ int majormul = va_arg(argp, int);
+ res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC;
+ if (minormul != 0)
+ g->genminormul = minormul;
+ if (majormul != 0)
+ setgcparam(g->genmajormul, majormul);
+ luaC_changemode(L, KGC_GEN);
+ break;
+ }
+ case LUA_GCINC: {
+ int pause = va_arg(argp, int);
+ int stepmul = va_arg(argp, int);
+ int stepsize = va_arg(argp, int);
+ res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC;
+ if (pause != 0)
+ setgcparam(g->gcpause, pause);
+ if (stepmul != 0)
+ setgcparam(g->gcstepmul, stepmul);
+ if (stepsize != 0)
+ g->gcstepsize = stepsize;
+ luaC_changemode(L, KGC_INC);
+ break;
+ }
+ default: res = -1; /* invalid option */
+ }
+ va_end(argp);
+ lua_unlock(L);
+ return res;
+}
+
+
+
+/*
+** miscellaneous functions
+*/
+
+
+LUA_API int lua_error (lua_State *L) {
+ TValue *errobj;
+ lua_lock(L);
+ errobj = s2v(L->top.p - 1);
+ api_checknelems(L, 1);
+ /* error object is the memory error message? */
+ if (ttisshrstring(errobj) && eqshrstr(tsvalue(errobj), G(L)->memerrmsg))
+ luaM_error(L); /* raise a memory error */
+ else
+ luaG_errormsg(L); /* raise a regular error */
+ /* code unreachable; will unlock when control actually leaves the kernel */
+ return 0; /* to avoid warnings */
+}
+
+
+LUA_API int lua_next (lua_State *L, int idx) {
+ Table *t;
+ int more;
+ lua_lock(L);
+ api_checknelems(L, 1);
+ t = gettable(L, idx);
+ more = luaH_next(L, t, L->top.p - 1);
+ if (more) {
+ api_incr_top(L);
+ }
+ else /* no more elements */
+ L->top.p -= 1; /* remove key */
+ lua_unlock(L);
+ return more;
+}
+
+
+LUA_API void lua_toclose (lua_State *L, int idx) {
+ int nresults;
+ StkId o;
+ lua_lock(L);
+ o = index2stack(L, idx);
+ nresults = L->ci->nresults;
+ api_check(L, L->tbclist.p < o, "given index below or equal a marked one");
+ luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */
+ if (!hastocloseCfunc(nresults)) /* function not marked yet? */
+ L->ci->nresults = codeNresults(nresults); /* mark it */
+ lua_assert(hastocloseCfunc(L->ci->nresults));
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_concat (lua_State *L, int n) {
+ lua_lock(L);
+ api_checknelems(L, n);
+ if (n > 0)
+ luaV_concat(L, n);
+ else { /* nothing to concatenate */
+ setsvalue2s(L, L->top.p, luaS_newlstr(L, "", 0)); /* push empty string */
+ api_incr_top(L);
+ }
+ luaC_checkGC(L);
+ lua_unlock(L);
+}
+
+
+LUA_API void lua_len (lua_State *L, int idx) {
+ TValue *t;
+ lua_lock(L);
+ t = index2value(L, idx);
+ luaV_objlen(L, L->top.p, t);
+ api_incr_top(L);
+ lua_unlock(L);
+}
+
+
+LUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) {
+ lua_Alloc f;
+ lua_lock(L);
+ if (ud) *ud = G(L)->ud;
+ f = G(L)->frealloc;
+ lua_unlock(L);
+ return f;
+}
+
+
+LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) {
+ lua_lock(L);
+ G(L)->ud = ud;
+ G(L)->frealloc = f;
+ lua_unlock(L);
+}
+
+
+void lua_setwarnf (lua_State *L, lua_WarnFunction f, void *ud) {
+ lua_lock(L);
+ G(L)->ud_warn = ud;
+ G(L)->warnf = f;
+ lua_unlock(L);
+}
+
+
+void lua_warning (lua_State *L, const char *msg, int tocont) {
+ lua_lock(L);
+ luaE_warning(L, msg, tocont);
+ lua_unlock(L);
+}
+
+
+
+LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) {
+ Udata *u;
+ lua_lock(L);
+ api_check(L, 0 <= nuvalue && nuvalue < USHRT_MAX, "invalid value");
+ u = luaS_newudata(L, size, nuvalue);
+ setuvalue(L, s2v(L->top.p), u);
+ api_incr_top(L);
+ luaC_checkGC(L);
+ lua_unlock(L);
+ return getudatamem(u);
+}
+
+
+
+static const char *aux_upvalue (TValue *fi, int n, TValue **val,
+ GCObject **owner) {
+ switch (ttypetag(fi)) {
+ case LUA_VCCL: { /* C closure */
+ CClosure *f = clCvalue(fi);
+ if (!(cast_uint(n) - 1u < cast_uint(f->nupvalues)))
+ return NULL; /* 'n' not in [1, f->nupvalues] */
+ *val = &f->upvalue[n-1];
+ if (owner) *owner = obj2gco(f);
+ return "";
+ }
+ case LUA_VLCL: { /* Lua closure */
+ LClosure *f = clLvalue(fi);
+ TString *name;
+ Proto *p = f->p;
+ if (!(cast_uint(n) - 1u < cast_uint(p->sizeupvalues)))
+ return NULL; /* 'n' not in [1, p->sizeupvalues] */
+ *val = f->upvals[n-1]->v.p;
+ if (owner) *owner = obj2gco(f->upvals[n - 1]);
+ name = p->upvalues[n-1].name;
+ return (name == NULL) ? "(no name)" : getstr(name);
+ }
+ default: return NULL; /* not a closure */
+ }
+}
+
+
+LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) {
+ const char *name;
+ TValue *val = NULL; /* to avoid warnings */
+ lua_lock(L);
+ name = aux_upvalue(index2value(L, funcindex), n, &val, NULL);
+ if (name) {
+ setobj2s(L, L->top.p, val);
+ api_incr_top(L);
+ }
+ lua_unlock(L);
+ return name;
+}
+
+
+LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
+ const char *name;
+ TValue *val = NULL; /* to avoid warnings */
+ GCObject *owner = NULL; /* to avoid warnings */
+ TValue *fi;
+ lua_lock(L);
+ fi = index2value(L, funcindex);
+ api_checknelems(L, 1);
+ name = aux_upvalue(fi, n, &val, &owner);
+ if (name) {
+ L->top.p--;
+ setobj(L, val, s2v(L->top.p));
+ luaC_barrier(L, owner, val);
+ }
+ lua_unlock(L);
+ return name;
+}
+
+
+static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) {
+ static const UpVal *const nullup = NULL;
+ LClosure *f;
+ TValue *fi = index2value(L, fidx);
+ api_check(L, ttisLclosure(fi), "Lua function expected");
+ f = clLvalue(fi);
+ if (pf) *pf = f;
+ if (1 <= n && n <= f->p->sizeupvalues)
+ return &f->upvals[n - 1]; /* get its upvalue pointer */
+ else
+ return (UpVal**)&nullup;
+}
+
+
+LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) {
+ TValue *fi = index2value(L, fidx);
+ switch (ttypetag(fi)) {
+ case LUA_VLCL: { /* lua closure */
+ return *getupvalref(L, fidx, n, NULL);
+ }
+ case LUA_VCCL: { /* C closure */
+ CClosure *f = clCvalue(fi);
+ if (1 <= n && n <= f->nupvalues)
+ return &f->upvalue[n - 1];
+ /* else */
+ } /* FALLTHROUGH */
+ case LUA_VLCF:
+ return NULL; /* light C functions have no upvalues */
+ default: {
+ api_check(L, 0, "function expected");
+ return NULL;
+ }
+ }
+}
+
+
+LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1,
+ int fidx2, int n2) {
+ LClosure *f1;
+ UpVal **up1 = getupvalref(L, fidx1, n1, &f1);
+ UpVal **up2 = getupvalref(L, fidx2, n2, NULL);
+ api_check(L, *up1 != NULL && *up2 != NULL, "invalid upvalue index");
+ *up1 = *up2;
+ luaC_objbarrier(L, f1, *up1);
+}
+
+
diff --git a/src/libs/3rdparty/lua/src/lapi.h b/src/libs/3rdparty/lua/src/lapi.h
new file mode 100644
index 0000000000..a742427cdc
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lapi.h
@@ -0,0 +1,52 @@
+/*
+** $Id: lapi.h $
+** Auxiliary functions from Lua API
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lapi_h
+#define lapi_h
+
+
+#include "llimits.h"
+#include "lstate.h"
+
+
+/* Increments 'L->top.p', checking for stack overflows */
+#define api_incr_top(L) {L->top.p++; \
+ api_check(L, L->top.p <= L->ci->top.p, \
+ "stack overflow");}
+
+
+/*
+** If a call returns too many multiple returns, the callee may not have
+** stack space to accommodate all results. In this case, this macro
+** increases its stack space ('L->ci->top.p').
+*/
+#define adjustresults(L,nres) \
+ { if ((nres) <= LUA_MULTRET && L->ci->top.p < L->top.p) \
+ L->ci->top.p = L->top.p; }
+
+
+/* Ensure the stack has at least 'n' elements */
+#define api_checknelems(L,n) \
+ api_check(L, (n) < (L->top.p - L->ci->func.p), \
+ "not enough elements in the stack")
+
+
+/*
+** To reduce the overhead of returning from C functions, the presence of
+** to-be-closed variables in these functions is coded in the CallInfo's
+** field 'nresults', in a way that functions with no to-be-closed variables
+** with zero, one, or "all" wanted results have no overhead. Functions
+** with other number of wanted results, as well as functions with
+** variables to be closed, have an extra check.
+*/
+
+#define hastocloseCfunc(n) ((n) < LUA_MULTRET)
+
+/* Map [-1, inf) (range of 'nresults') into (-inf, -2] */
+#define codeNresults(n) (-(n) - 3)
+#define decodeNresults(n) (-(n) - 3)
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lauxlib.c b/src/libs/3rdparty/lua/src/lauxlib.c
new file mode 100644
index 0000000000..4ca6c65488
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lauxlib.c
@@ -0,0 +1,1112 @@
+/*
+** $Id: lauxlib.c $
+** Auxiliary functions for building Lua libraries
+** See Copyright Notice in lua.h
+*/
+
+#define lauxlib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+/*
+** This file uses only the official API of Lua.
+** Any function declared here could be written as an application function.
+*/
+
+#include "lua.h"
+
+#include "lauxlib.h"
+
+
+#if !defined(MAX_SIZET)
+/* maximum value for size_t */
+#define MAX_SIZET ((size_t)(~(size_t)0))
+#endif
+
+
+/*
+** {======================================================
+** Traceback
+** =======================================================
+*/
+
+
+#define LEVELS1 10 /* size of the first part of the stack */
+#define LEVELS2 11 /* size of the second part of the stack */
+
+
+
+/*
+** Search for 'objidx' in table at index -1. ('objidx' must be an
+** absolute index.) Return 1 + string at top if it found a good name.
+*/
+static int findfield (lua_State *L, int objidx, int level) {
+ if (level == 0 || !lua_istable(L, -1))
+ return 0; /* not found */
+ lua_pushnil(L); /* start 'next' loop */
+ while (lua_next(L, -2)) { /* for each pair in table */
+ if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */
+ if (lua_rawequal(L, objidx, -1)) { /* found object? */
+ lua_pop(L, 1); /* remove value (but keep name) */
+ return 1;
+ }
+ else if (findfield(L, objidx, level - 1)) { /* try recursively */
+ /* stack: lib_name, lib_table, field_name (top) */
+ lua_pushliteral(L, "."); /* place '.' between the two names */
+ lua_replace(L, -3); /* (in the slot occupied by table) */
+ lua_concat(L, 3); /* lib_name.field_name */
+ return 1;
+ }
+ }
+ lua_pop(L, 1); /* remove value */
+ }
+ return 0; /* not found */
+}
+
+
+/*
+** Search for a name for a function in all loaded modules
+*/
+static int pushglobalfuncname (lua_State *L, lua_Debug *ar) {
+ int top = lua_gettop(L);
+ lua_getinfo(L, "f", ar); /* push function */
+ lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
+ if (findfield(L, top + 1, 2)) {
+ const char *name = lua_tostring(L, -1);
+ if (strncmp(name, LUA_GNAME ".", 3) == 0) { /* name start with '_G.'? */
+ lua_pushstring(L, name + 3); /* push name without prefix */
+ lua_remove(L, -2); /* remove original name */
+ }
+ lua_copy(L, -1, top + 1); /* copy name to proper place */
+ lua_settop(L, top + 1); /* remove table "loaded" and name copy */
+ return 1;
+ }
+ else {
+ lua_settop(L, top); /* remove function and global table */
+ return 0;
+ }
+}
+
+
+static void pushfuncname (lua_State *L, lua_Debug *ar) {
+ if (pushglobalfuncname(L, ar)) { /* try first a global name */
+ lua_pushfstring(L, "function '%s'", lua_tostring(L, -1));
+ lua_remove(L, -2); /* remove name */
+ }
+ else if (*ar->namewhat != '\0') /* is there a name from code? */
+ lua_pushfstring(L, "%s '%s'", ar->namewhat, ar->name); /* use it */
+ else if (*ar->what == 'm') /* main? */
+ lua_pushliteral(L, "main chunk");
+ else if (*ar->what != 'C') /* for Lua functions, use <file:line> */
+ lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined);
+ else /* nothing left... */
+ lua_pushliteral(L, "?");
+}
+
+
+static int lastlevel (lua_State *L) {
+ lua_Debug ar;
+ int li = 1, le = 1;
+ /* find an upper bound */
+ while (lua_getstack(L, le, &ar)) { li = le; le *= 2; }
+ /* do a binary search */
+ while (li < le) {
+ int m = (li + le)/2;
+ if (lua_getstack(L, m, &ar)) li = m + 1;
+ else le = m;
+ }
+ return le - 1;
+}
+
+
+LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1,
+ const char *msg, int level) {
+ luaL_Buffer b;
+ lua_Debug ar;
+ int last = lastlevel(L1);
+ int limit2show = (last - level > LEVELS1 + LEVELS2) ? LEVELS1 : -1;
+ luaL_buffinit(L, &b);
+ if (msg) {
+ luaL_addstring(&b, msg);
+ luaL_addchar(&b, '\n');
+ }
+ luaL_addstring(&b, "stack traceback:");
+ while (lua_getstack(L1, level++, &ar)) {
+ if (limit2show-- == 0) { /* too many levels? */
+ int n = last - level - LEVELS2 + 1; /* number of levels to skip */
+ lua_pushfstring(L, "\n\t...\t(skipping %d levels)", n);
+ luaL_addvalue(&b); /* add warning about skip */
+ level += n; /* and skip to last levels */
+ }
+ else {
+ lua_getinfo(L1, "Slnt", &ar);
+ if (ar.currentline <= 0)
+ lua_pushfstring(L, "\n\t%s: in ", ar.short_src);
+ else
+ lua_pushfstring(L, "\n\t%s:%d: in ", ar.short_src, ar.currentline);
+ luaL_addvalue(&b);
+ pushfuncname(L, &ar);
+ luaL_addvalue(&b);
+ if (ar.istailcall)
+ luaL_addstring(&b, "\n\t(...tail calls...)");
+ }
+ }
+ luaL_pushresult(&b);
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Error-report functions
+** =======================================================
+*/
+
+LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) {
+ lua_Debug ar;
+ if (!lua_getstack(L, 0, &ar)) /* no stack frame? */
+ return luaL_error(L, "bad argument #%d (%s)", arg, extramsg);
+ lua_getinfo(L, "n", &ar);
+ if (strcmp(ar.namewhat, "method") == 0) {
+ arg--; /* do not count 'self' */
+ if (arg == 0) /* error is in the self argument itself? */
+ return luaL_error(L, "calling '%s' on bad self (%s)",
+ ar.name, extramsg);
+ }
+ if (ar.name == NULL)
+ ar.name = (pushglobalfuncname(L, &ar)) ? lua_tostring(L, -1) : "?";
+ return luaL_error(L, "bad argument #%d to '%s' (%s)",
+ arg, ar.name, extramsg);
+}
+
+
+LUALIB_API int luaL_typeerror (lua_State *L, int arg, const char *tname) {
+ const char *msg;
+ const char *typearg; /* name for the type of the actual argument */
+ if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING)
+ typearg = lua_tostring(L, -1); /* use the given type name */
+ else if (lua_type(L, arg) == LUA_TLIGHTUSERDATA)
+ typearg = "light userdata"; /* special name for messages */
+ else
+ typearg = luaL_typename(L, arg); /* standard name */
+ msg = lua_pushfstring(L, "%s expected, got %s", tname, typearg);
+ return luaL_argerror(L, arg, msg);
+}
+
+
+static void tag_error (lua_State *L, int arg, int tag) {
+ luaL_typeerror(L, arg, lua_typename(L, tag));
+}
+
+
+/*
+** The use of 'lua_pushfstring' ensures this function does not
+** need reserved stack space when called.
+*/
+LUALIB_API void luaL_where (lua_State *L, int level) {
+ lua_Debug ar;
+ if (lua_getstack(L, level, &ar)) { /* check function at level */
+ lua_getinfo(L, "Sl", &ar); /* get info about it */
+ if (ar.currentline > 0) { /* is there info? */
+ lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline);
+ return;
+ }
+ }
+ lua_pushfstring(L, ""); /* else, no information available... */
+}
+
+
+/*
+** Again, the use of 'lua_pushvfstring' ensures this function does
+** not need reserved stack space when called. (At worst, it generates
+** an error with "stack overflow" instead of the given message.)
+*/
+LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) {
+ va_list argp;
+ va_start(argp, fmt);
+ luaL_where(L, 1);
+ lua_pushvfstring(L, fmt, argp);
+ va_end(argp);
+ lua_concat(L, 2);
+ return lua_error(L);
+}
+
+
+LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) {
+ int en = errno; /* calls to Lua API may change this value */
+ if (stat) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ else {
+ luaL_pushfail(L);
+ if (fname)
+ lua_pushfstring(L, "%s: %s", fname, strerror(en));
+ else
+ lua_pushstring(L, strerror(en));
+ lua_pushinteger(L, en);
+ return 3;
+ }
+}
+
+
+#if !defined(l_inspectstat) /* { */
+
+#if defined(LUA_USE_POSIX)
+
+#include <sys/wait.h>
+
+/*
+** use appropriate macros to interpret 'pclose' return status
+*/
+#define l_inspectstat(stat,what) \
+ if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \
+ else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; }
+
+#else
+
+#define l_inspectstat(stat,what) /* no op */
+
+#endif
+
+#endif /* } */
+
+
+LUALIB_API int luaL_execresult (lua_State *L, int stat) {
+ if (stat != 0 && errno != 0) /* error with an 'errno'? */
+ return luaL_fileresult(L, 0, NULL);
+ else {
+ const char *what = "exit"; /* type of termination */
+ l_inspectstat(stat, what); /* interpret result */
+ if (*what == 'e' && stat == 0) /* successful termination? */
+ lua_pushboolean(L, 1);
+ else
+ luaL_pushfail(L);
+ lua_pushstring(L, what);
+ lua_pushinteger(L, stat);
+ return 3; /* return true/fail,what,code */
+ }
+}
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** Userdata's metatable manipulation
+** =======================================================
+*/
+
+LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) {
+ if (luaL_getmetatable(L, tname) != LUA_TNIL) /* name already in use? */
+ return 0; /* leave previous value on top, but return 0 */
+ lua_pop(L, 1);
+ lua_createtable(L, 0, 2); /* create metatable */
+ lua_pushstring(L, tname);
+ lua_setfield(L, -2, "__name"); /* metatable.__name = tname */
+ lua_pushvalue(L, -1);
+ lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */
+ return 1;
+}
+
+
+LUALIB_API void luaL_setmetatable (lua_State *L, const char *tname) {
+ luaL_getmetatable(L, tname);
+ lua_setmetatable(L, -2);
+}
+
+
+LUALIB_API void *luaL_testudata (lua_State *L, int ud, const char *tname) {
+ void *p = lua_touserdata(L, ud);
+ if (p != NULL) { /* value is a userdata? */
+ if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
+ luaL_getmetatable(L, tname); /* get correct metatable */
+ if (!lua_rawequal(L, -1, -2)) /* not the same? */
+ p = NULL; /* value is a userdata with wrong metatable */
+ lua_pop(L, 2); /* remove both metatables */
+ return p;
+ }
+ }
+ return NULL; /* value is not a userdata with a metatable */
+}
+
+
+LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) {
+ void *p = luaL_testudata(L, ud, tname);
+ luaL_argexpected(L, p != NULL, ud, tname);
+ return p;
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Argument check functions
+** =======================================================
+*/
+
+LUALIB_API int luaL_checkoption (lua_State *L, int arg, const char *def,
+ const char *const lst[]) {
+ const char *name = (def) ? luaL_optstring(L, arg, def) :
+ luaL_checkstring(L, arg);
+ int i;
+ for (i=0; lst[i]; i++)
+ if (strcmp(lst[i], name) == 0)
+ return i;
+ return luaL_argerror(L, arg,
+ lua_pushfstring(L, "invalid option '%s'", name));
+}
+
+
+/*
+** Ensures the stack has at least 'space' extra slots, raising an error
+** if it cannot fulfill the request. (The error handling needs a few
+** extra slots to format the error message. In case of an error without
+** this extra space, Lua will generate the same 'stack overflow' error,
+** but without 'msg'.)
+*/
+LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) {
+ if (l_unlikely(!lua_checkstack(L, space))) {
+ if (msg)
+ luaL_error(L, "stack overflow (%s)", msg);
+ else
+ luaL_error(L, "stack overflow");
+ }
+}
+
+
+LUALIB_API void luaL_checktype (lua_State *L, int arg, int t) {
+ if (l_unlikely(lua_type(L, arg) != t))
+ tag_error(L, arg, t);
+}
+
+
+LUALIB_API void luaL_checkany (lua_State *L, int arg) {
+ if (l_unlikely(lua_type(L, arg) == LUA_TNONE))
+ luaL_argerror(L, arg, "value expected");
+}
+
+
+LUALIB_API const char *luaL_checklstring (lua_State *L, int arg, size_t *len) {
+ const char *s = lua_tolstring(L, arg, len);
+ if (l_unlikely(!s)) tag_error(L, arg, LUA_TSTRING);
+ return s;
+}
+
+
+LUALIB_API const char *luaL_optlstring (lua_State *L, int arg,
+ const char *def, size_t *len) {
+ if (lua_isnoneornil(L, arg)) {
+ if (len)
+ *len = (def ? strlen(def) : 0);
+ return def;
+ }
+ else return luaL_checklstring(L, arg, len);
+}
+
+
+LUALIB_API lua_Number luaL_checknumber (lua_State *L, int arg) {
+ int isnum;
+ lua_Number d = lua_tonumberx(L, arg, &isnum);
+ if (l_unlikely(!isnum))
+ tag_error(L, arg, LUA_TNUMBER);
+ return d;
+}
+
+
+LUALIB_API lua_Number luaL_optnumber (lua_State *L, int arg, lua_Number def) {
+ return luaL_opt(L, luaL_checknumber, arg, def);
+}
+
+
+static void interror (lua_State *L, int arg) {
+ if (lua_isnumber(L, arg))
+ luaL_argerror(L, arg, "number has no integer representation");
+ else
+ tag_error(L, arg, LUA_TNUMBER);
+}
+
+
+LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int arg) {
+ int isnum;
+ lua_Integer d = lua_tointegerx(L, arg, &isnum);
+ if (l_unlikely(!isnum)) {
+ interror(L, arg);
+ }
+ return d;
+}
+
+
+LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int arg,
+ lua_Integer def) {
+ return luaL_opt(L, luaL_checkinteger, arg, def);
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Generic Buffer manipulation
+** =======================================================
+*/
+
+/* userdata to box arbitrary data */
+typedef struct UBox {
+ void *box;
+ size_t bsize;
+} UBox;
+
+
+static void *resizebox (lua_State *L, int idx, size_t newsize) {
+ void *ud;
+ lua_Alloc allocf = lua_getallocf(L, &ud);
+ UBox *box = (UBox *)lua_touserdata(L, idx);
+ void *temp = allocf(ud, box->box, box->bsize, newsize);
+ if (l_unlikely(temp == NULL && newsize > 0)) { /* allocation error? */
+ lua_pushliteral(L, "not enough memory");
+ lua_error(L); /* raise a memory error */
+ }
+ box->box = temp;
+ box->bsize = newsize;
+ return temp;
+}
+
+
+static int boxgc (lua_State *L) {
+ resizebox(L, 1, 0);
+ return 0;
+}
+
+
+static const luaL_Reg boxmt[] = { /* box metamethods */
+ {"__gc", boxgc},
+ {"__close", boxgc},
+ {NULL, NULL}
+};
+
+
+static void newbox (lua_State *L) {
+ UBox *box = (UBox *)lua_newuserdatauv(L, sizeof(UBox), 0);
+ box->box = NULL;
+ box->bsize = 0;
+ if (luaL_newmetatable(L, "_UBOX*")) /* creating metatable? */
+ luaL_setfuncs(L, boxmt, 0); /* set its metamethods */
+ lua_setmetatable(L, -2);
+}
+
+
+/*
+** check whether buffer is using a userdata on the stack as a temporary
+** buffer
+*/
+#define buffonstack(B) ((B)->b != (B)->init.b)
+
+
+/*
+** Whenever buffer is accessed, slot 'idx' must either be a box (which
+** cannot be NULL) or it is a placeholder for the buffer.
+*/
+#define checkbufferlevel(B,idx) \
+ lua_assert(buffonstack(B) ? lua_touserdata(B->L, idx) != NULL \
+ : lua_touserdata(B->L, idx) == (void*)B)
+
+
+/*
+** Compute new size for buffer 'B', enough to accommodate extra 'sz'
+** bytes. (The test for "not big enough" also gets the case when the
+** computation of 'newsize' overflows.)
+*/
+static size_t newbuffsize (luaL_Buffer *B, size_t sz) {
+ size_t newsize = (B->size / 2) * 3; /* buffer size * 1.5 */
+ if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */
+ return luaL_error(B->L, "buffer too large");
+ if (newsize < B->n + sz) /* not big enough? */
+ newsize = B->n + sz;
+ return newsize;
+}
+
+
+/*
+** Returns a pointer to a free area with at least 'sz' bytes in buffer
+** 'B'. 'boxidx' is the relative position in the stack where is the
+** buffer's box or its placeholder.
+*/
+static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) {
+ checkbufferlevel(B, boxidx);
+ if (B->size - B->n >= sz) /* enough space? */
+ return B->b + B->n;
+ else {
+ lua_State *L = B->L;
+ char *newbuff;
+ size_t newsize = newbuffsize(B, sz);
+ /* create larger buffer */
+ if (buffonstack(B)) /* buffer already has a box? */
+ newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */
+ else { /* no box yet */
+ lua_remove(L, boxidx); /* remove placeholder */
+ newbox(L); /* create a new box */
+ lua_insert(L, boxidx); /* move box to its intended position */
+ lua_toclose(L, boxidx);
+ newbuff = (char *)resizebox(L, boxidx, newsize);
+ memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */
+ }
+ B->b = newbuff;
+ B->size = newsize;
+ return newbuff + B->n;
+ }
+}
+
+/*
+** returns a pointer to a free area with at least 'sz' bytes
+*/
+LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) {
+ return prepbuffsize(B, sz, -1);
+}
+
+
+LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) {
+ if (l > 0) { /* avoid 'memcpy' when 's' can be NULL */
+ char *b = prepbuffsize(B, l, -1);
+ memcpy(b, s, l * sizeof(char));
+ luaL_addsize(B, l);
+ }
+}
+
+
+LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) {
+ luaL_addlstring(B, s, strlen(s));
+}
+
+
+LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
+ lua_State *L = B->L;
+ checkbufferlevel(B, -1);
+ lua_pushlstring(L, B->b, B->n);
+ if (buffonstack(B))
+ lua_closeslot(L, -2); /* close the box */
+ lua_remove(L, -2); /* remove box or placeholder from the stack */
+}
+
+
+LUALIB_API void luaL_pushresultsize (luaL_Buffer *B, size_t sz) {
+ luaL_addsize(B, sz);
+ luaL_pushresult(B);
+}
+
+
+/*
+** 'luaL_addvalue' is the only function in the Buffer system where the
+** box (if existent) is not on the top of the stack. So, instead of
+** calling 'luaL_addlstring', it replicates the code using -2 as the
+** last argument to 'prepbuffsize', signaling that the box is (or will
+** be) below the string being added to the buffer. (Box creation can
+** trigger an emergency GC, so we should not remove the string from the
+** stack before we have the space guaranteed.)
+*/
+LUALIB_API void luaL_addvalue (luaL_Buffer *B) {
+ lua_State *L = B->L;
+ size_t len;
+ const char *s = lua_tolstring(L, -1, &len);
+ char *b = prepbuffsize(B, len, -2);
+ memcpy(b, s, len * sizeof(char));
+ luaL_addsize(B, len);
+ lua_pop(L, 1); /* pop string */
+}
+
+
+LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) {
+ B->L = L;
+ B->b = B->init.b;
+ B->n = 0;
+ B->size = LUAL_BUFFERSIZE;
+ lua_pushlightuserdata(L, (void*)B); /* push placeholder */
+}
+
+
+LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) {
+ luaL_buffinit(L, B);
+ return prepbuffsize(B, sz, -1);
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Reference system
+** =======================================================
+*/
+
+/* index of free-list header (after the predefined values) */
+#define freelist (LUA_RIDX_LAST + 1)
+
+/*
+** The previously freed references form a linked list:
+** t[freelist] is the index of a first free index, or zero if list is
+** empty; t[t[freelist]] is the index of the second element; etc.
+*/
+LUALIB_API int luaL_ref (lua_State *L, int t) {
+ int ref;
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1); /* remove from stack */
+ return LUA_REFNIL; /* 'nil' has a unique fixed reference */
+ }
+ t = lua_absindex(L, t);
+ if (lua_rawgeti(L, t, freelist) == LUA_TNIL) { /* first access? */
+ ref = 0; /* list is empty */
+ lua_pushinteger(L, 0); /* initialize as an empty list */
+ lua_rawseti(L, t, freelist); /* ref = t[freelist] = 0 */
+ }
+ else { /* already initialized */
+ lua_assert(lua_isinteger(L, -1));
+ ref = (int)lua_tointeger(L, -1); /* ref = t[freelist] */
+ }
+ lua_pop(L, 1); /* remove element from stack */
+ if (ref != 0) { /* any free element? */
+ lua_rawgeti(L, t, ref); /* remove it from list */
+ lua_rawseti(L, t, freelist); /* (t[freelist] = t[ref]) */
+ }
+ else /* no free elements */
+ ref = (int)lua_rawlen(L, t) + 1; /* get a new reference */
+ lua_rawseti(L, t, ref);
+ return ref;
+}
+
+
+LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
+ if (ref >= 0) {
+ t = lua_absindex(L, t);
+ lua_rawgeti(L, t, freelist);
+ lua_assert(lua_isinteger(L, -1));
+ lua_rawseti(L, t, ref); /* t[ref] = t[freelist] */
+ lua_pushinteger(L, ref);
+ lua_rawseti(L, t, freelist); /* t[freelist] = ref */
+ }
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Load functions
+** =======================================================
+*/
+
+typedef struct LoadF {
+ int n; /* number of pre-read characters */
+ FILE *f; /* file being read */
+ char buff[BUFSIZ]; /* area for reading file */
+} LoadF;
+
+
+static const char *getF (lua_State *L, void *ud, size_t *size) {
+ LoadF *lf = (LoadF *)ud;
+ (void)L; /* not used */
+ if (lf->n > 0) { /* are there pre-read characters to be read? */
+ *size = lf->n; /* return them (chars already in buffer) */
+ lf->n = 0; /* no more pre-read characters */
+ }
+ else { /* read a block from file */
+ /* 'fread' can return > 0 *and* set the EOF flag. If next call to
+ 'getF' called 'fread', it might still wait for user input.
+ The next check avoids this problem. */
+ if (feof(lf->f)) return NULL;
+ *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */
+ }
+ return lf->buff;
+}
+
+
+static int errfile (lua_State *L, const char *what, int fnameindex) {
+ const char *serr = strerror(errno);
+ const char *filename = lua_tostring(L, fnameindex) + 1;
+ lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr);
+ lua_remove(L, fnameindex);
+ return LUA_ERRFILE;
+}
+
+
+/*
+** Skip an optional BOM at the start of a stream. If there is an
+** incomplete BOM (the first character is correct but the rest is
+** not), returns the first character anyway to force an error
+** (as no chunk can start with 0xEF).
+*/
+static int skipBOM (FILE *f) {
+ int c = getc(f); /* read first character */
+ if (c == 0xEF && getc(f) == 0xBB && getc(f) == 0xBF) /* correct BOM? */
+ return getc(f); /* ignore BOM and return next char */
+ else /* no (valid) BOM */
+ return c; /* return first character */
+}
+
+
+/*
+** reads the first character of file 'f' and skips an optional BOM mark
+** in its beginning plus its first line if it starts with '#'. Returns
+** true if it skipped the first line. In any case, '*cp' has the
+** first "valid" character of the file (after the optional BOM and
+** a first-line comment).
+*/
+static int skipcomment (FILE *f, int *cp) {
+ int c = *cp = skipBOM(f);
+ if (c == '#') { /* first line is a comment (Unix exec. file)? */
+ do { /* skip first line */
+ c = getc(f);
+ } while (c != EOF && c != '\n');
+ *cp = getc(f); /* next character after comment, if present */
+ return 1; /* there was a comment */
+ }
+ else return 0; /* no comment */
+}
+
+
+LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
+ const char *mode) {
+ LoadF lf;
+ int status, readstatus;
+ int c;
+ int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
+ if (filename == NULL) {
+ lua_pushliteral(L, "=stdin");
+ lf.f = stdin;
+ }
+ else {
+ lua_pushfstring(L, "@%s", filename);
+ lf.f = fopen(filename, "r");
+ if (lf.f == NULL) return errfile(L, "open", fnameindex);
+ }
+ lf.n = 0;
+ if (skipcomment(lf.f, &c)) /* read initial portion */
+ lf.buff[lf.n++] = '\n'; /* add newline to correct line numbers */
+ if (c == LUA_SIGNATURE[0]) { /* binary file? */
+ lf.n = 0; /* remove possible newline */
+ if (filename) { /* "real" file? */
+ lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
+ if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
+ skipcomment(lf.f, &c); /* re-read initial portion */
+ }
+ }
+ if (c != EOF)
+ lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */
+ status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode);
+ readstatus = ferror(lf.f);
+ if (filename) fclose(lf.f); /* close file (even in case of errors) */
+ if (readstatus) {
+ lua_settop(L, fnameindex); /* ignore results from 'lua_load' */
+ return errfile(L, "read", fnameindex);
+ }
+ lua_remove(L, fnameindex);
+ return status;
+}
+
+
+typedef struct LoadS {
+ const char *s;
+ size_t size;
+} LoadS;
+
+
+static const char *getS (lua_State *L, void *ud, size_t *size) {
+ LoadS *ls = (LoadS *)ud;
+ (void)L; /* not used */
+ if (ls->size == 0) return NULL;
+ *size = ls->size;
+ ls->size = 0;
+ return ls->s;
+}
+
+
+LUALIB_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t size,
+ const char *name, const char *mode) {
+ LoadS ls;
+ ls.s = buff;
+ ls.size = size;
+ return lua_load(L, getS, &ls, name, mode);
+}
+
+
+LUALIB_API int luaL_loadstring (lua_State *L, const char *s) {
+ return luaL_loadbuffer(L, s, strlen(s), s);
+}
+
+/* }====================================================== */
+
+
+
+LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) {
+ if (!lua_getmetatable(L, obj)) /* no metatable? */
+ return LUA_TNIL;
+ else {
+ int tt;
+ lua_pushstring(L, event);
+ tt = lua_rawget(L, -2);
+ if (tt == LUA_TNIL) /* is metafield nil? */
+ lua_pop(L, 2); /* remove metatable and metafield */
+ else
+ lua_remove(L, -2); /* remove only metatable */
+ return tt; /* return metafield type */
+ }
+}
+
+
+LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) {
+ obj = lua_absindex(L, obj);
+ if (luaL_getmetafield(L, obj, event) == LUA_TNIL) /* no metafield? */
+ return 0;
+ lua_pushvalue(L, obj);
+ lua_call(L, 1, 1);
+ return 1;
+}
+
+
+LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) {
+ lua_Integer l;
+ int isnum;
+ lua_len(L, idx);
+ l = lua_tointegerx(L, -1, &isnum);
+ if (l_unlikely(!isnum))
+ luaL_error(L, "object length is not an integer");
+ lua_pop(L, 1); /* remove object */
+ return l;
+}
+
+
+LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
+ idx = lua_absindex(L,idx);
+ if (luaL_callmeta(L, idx, "__tostring")) { /* metafield? */
+ if (!lua_isstring(L, -1))
+ luaL_error(L, "'__tostring' must return a string");
+ }
+ else {
+ switch (lua_type(L, idx)) {
+ case LUA_TNUMBER: {
+ if (lua_isinteger(L, idx))
+ lua_pushfstring(L, "%I", (LUAI_UACINT)lua_tointeger(L, idx));
+ else
+ lua_pushfstring(L, "%f", (LUAI_UACNUMBER)lua_tonumber(L, idx));
+ break;
+ }
+ case LUA_TSTRING:
+ lua_pushvalue(L, idx);
+ break;
+ case LUA_TBOOLEAN:
+ lua_pushstring(L, (lua_toboolean(L, idx) ? "true" : "false"));
+ break;
+ case LUA_TNIL:
+ lua_pushliteral(L, "nil");
+ break;
+ default: {
+ int tt = luaL_getmetafield(L, idx, "__name"); /* try name */
+ const char *kind = (tt == LUA_TSTRING) ? lua_tostring(L, -1) :
+ luaL_typename(L, idx);
+ lua_pushfstring(L, "%s: %p", kind, lua_topointer(L, idx));
+ if (tt != LUA_TNIL)
+ lua_remove(L, -2); /* remove '__name' */
+ break;
+ }
+ }
+ }
+ return lua_tolstring(L, -1, len);
+}
+
+
+/*
+** set functions from list 'l' into table at top - 'nup'; each
+** function gets the 'nup' elements at the top as upvalues.
+** Returns with only the table at the stack.
+*/
+LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
+ luaL_checkstack(L, nup, "too many upvalues");
+ for (; l->name != NULL; l++) { /* fill the table with given functions */
+ if (l->func == NULL) /* place holder? */
+ lua_pushboolean(L, 0);
+ else {
+ int i;
+ for (i = 0; i < nup; i++) /* copy upvalues to the top */
+ lua_pushvalue(L, -nup);
+ lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
+ }
+ lua_setfield(L, -(nup + 2), l->name);
+ }
+ lua_pop(L, nup); /* remove upvalues */
+}
+
+
+/*
+** ensure that stack[idx][fname] has a table and push that table
+** into the stack
+*/
+LUALIB_API int luaL_getsubtable (lua_State *L, int idx, const char *fname) {
+ if (lua_getfield(L, idx, fname) == LUA_TTABLE)
+ return 1; /* table already there */
+ else {
+ lua_pop(L, 1); /* remove previous result */
+ idx = lua_absindex(L, idx);
+ lua_newtable(L);
+ lua_pushvalue(L, -1); /* copy to be left at top */
+ lua_setfield(L, idx, fname); /* assign new table to field */
+ return 0; /* false, because did not find table there */
+ }
+}
+
+
+/*
+** Stripped-down 'require': After checking "loaded" table, calls 'openf'
+** to open a module, registers the result in 'package.loaded' table and,
+** if 'glb' is true, also registers the result in the global table.
+** Leaves resulting module on the top.
+*/
+LUALIB_API void luaL_requiref (lua_State *L, const char *modname,
+ lua_CFunction openf, int glb) {
+ luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
+ lua_getfield(L, -1, modname); /* LOADED[modname] */
+ if (!lua_toboolean(L, -1)) { /* package not already loaded? */
+ lua_pop(L, 1); /* remove field */
+ lua_pushcfunction(L, openf);
+ lua_pushstring(L, modname); /* argument to open function */
+ lua_call(L, 1, 1); /* call 'openf' to open module */
+ lua_pushvalue(L, -1); /* make copy of module (call result) */
+ lua_setfield(L, -3, modname); /* LOADED[modname] = module */
+ }
+ lua_remove(L, -2); /* remove LOADED table */
+ if (glb) {
+ lua_pushvalue(L, -1); /* copy of module */
+ lua_setglobal(L, modname); /* _G[modname] = module */
+ }
+}
+
+
+LUALIB_API void luaL_addgsub (luaL_Buffer *b, const char *s,
+ const char *p, const char *r) {
+ const char *wild;
+ size_t l = strlen(p);
+ while ((wild = strstr(s, p)) != NULL) {
+ luaL_addlstring(b, s, wild - s); /* push prefix */
+ luaL_addstring(b, r); /* push replacement in place of pattern */
+ s = wild + l; /* continue after 'p' */
+ }
+ luaL_addstring(b, s); /* push last suffix */
+}
+
+
+LUALIB_API const char *luaL_gsub (lua_State *L, const char *s,
+ const char *p, const char *r) {
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ luaL_addgsub(&b, s, p, r);
+ luaL_pushresult(&b);
+ return lua_tostring(L, -1);
+}
+
+
+static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
+ (void)ud; (void)osize; /* not used */
+ if (nsize == 0) {
+ free(ptr);
+ return NULL;
+ }
+ else
+ return realloc(ptr, nsize);
+}
+
+
+static int panic (lua_State *L) {
+ const char *msg = lua_tostring(L, -1);
+ if (msg == NULL) msg = "error object is not a string";
+ lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n",
+ msg);
+ return 0; /* return to Lua to abort */
+}
+
+
+/*
+** Warning functions:
+** warnfoff: warning system is off
+** warnfon: ready to start a new message
+** warnfcont: previous message is to be continued
+*/
+static void warnfoff (void *ud, const char *message, int tocont);
+static void warnfon (void *ud, const char *message, int tocont);
+static void warnfcont (void *ud, const char *message, int tocont);
+
+
+/*
+** Check whether message is a control message. If so, execute the
+** control or ignore it if unknown.
+*/
+static int checkcontrol (lua_State *L, const char *message, int tocont) {
+ if (tocont || *(message++) != '@') /* not a control message? */
+ return 0;
+ else {
+ if (strcmp(message, "off") == 0)
+ lua_setwarnf(L, warnfoff, L); /* turn warnings off */
+ else if (strcmp(message, "on") == 0)
+ lua_setwarnf(L, warnfon, L); /* turn warnings on */
+ return 1; /* it was a control message */
+ }
+}
+
+
+static void warnfoff (void *ud, const char *message, int tocont) {
+ checkcontrol((lua_State *)ud, message, tocont);
+}
+
+
+/*
+** Writes the message and handle 'tocont', finishing the message
+** if needed and setting the next warn function.
+*/
+static void warnfcont (void *ud, const char *message, int tocont) {
+ lua_State *L = (lua_State *)ud;
+ lua_writestringerror("%s", message); /* write message */
+ if (tocont) /* not the last part? */
+ lua_setwarnf(L, warnfcont, L); /* to be continued */
+ else { /* last part */
+ lua_writestringerror("%s", "\n"); /* finish message with end-of-line */
+ lua_setwarnf(L, warnfon, L); /* next call is a new message */
+ }
+}
+
+
+static void warnfon (void *ud, const char *message, int tocont) {
+ if (checkcontrol((lua_State *)ud, message, tocont)) /* control message? */
+ return; /* nothing else to be done */
+ lua_writestringerror("%s", "Lua warning: "); /* start a new warning */
+ warnfcont(ud, message, tocont); /* finish processing */
+}
+
+
+LUALIB_API lua_State *luaL_newstate (void) {
+ lua_State *L = lua_newstate(l_alloc, NULL);
+ if (l_likely(L)) {
+ lua_atpanic(L, &panic);
+ lua_setwarnf(L, warnfoff, L); /* default is warnings off */
+ }
+ return L;
+}
+
+
+LUALIB_API void luaL_checkversion_ (lua_State *L, lua_Number ver, size_t sz) {
+ lua_Number v = lua_version(L);
+ if (sz != LUAL_NUMSIZES) /* check numeric types */
+ luaL_error(L, "core and library have incompatible numeric types");
+ else if (v != ver)
+ luaL_error(L, "version mismatch: app. needs %f, Lua core provides %f",
+ (LUAI_UACNUMBER)ver, (LUAI_UACNUMBER)v);
+}
+
diff --git a/src/libs/3rdparty/lua/src/lauxlib.h b/src/libs/3rdparty/lua/src/lauxlib.h
new file mode 100644
index 0000000000..5b977e2a39
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lauxlib.h
@@ -0,0 +1,301 @@
+/*
+** $Id: lauxlib.h $
+** Auxiliary functions for building Lua libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lauxlib_h
+#define lauxlib_h
+
+
+#include <stddef.h>
+#include <stdio.h>
+
+#include "luaconf.h"
+#include "lua.h"
+
+
+/* global table */
+#define LUA_GNAME "_G"
+
+
+typedef struct luaL_Buffer luaL_Buffer;
+
+
+/* extra error code for 'luaL_loadfilex' */
+#define LUA_ERRFILE (LUA_ERRERR+1)
+
+
+/* key, in the registry, for table of loaded modules */
+#define LUA_LOADED_TABLE "_LOADED"
+
+
+/* key, in the registry, for table of preloaded loaders */
+#define LUA_PRELOAD_TABLE "_PRELOAD"
+
+
+typedef struct luaL_Reg {
+ const char *name;
+ lua_CFunction func;
+} luaL_Reg;
+
+
+#define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number))
+
+LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz);
+#define luaL_checkversion(L) \
+ luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES)
+
+LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
+LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
+LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len);
+LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg);
+LUALIB_API int (luaL_typeerror) (lua_State *L, int arg, const char *tname);
+LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg,
+ size_t *l);
+LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg,
+ const char *def, size_t *l);
+LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg);
+LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def);
+
+LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg);
+LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg,
+ lua_Integer def);
+
+LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
+LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t);
+LUALIB_API void (luaL_checkany) (lua_State *L, int arg);
+
+LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
+LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname);
+LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
+LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
+
+LUALIB_API void (luaL_where) (lua_State *L, int lvl);
+LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
+
+LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def,
+ const char *const lst[]);
+
+LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname);
+LUALIB_API int (luaL_execresult) (lua_State *L, int stat);
+
+
+/* predefined references */
+#define LUA_NOREF (-2)
+#define LUA_REFNIL (-1)
+
+LUALIB_API int (luaL_ref) (lua_State *L, int t);
+LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
+
+LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename,
+ const char *mode);
+
+#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL)
+
+LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz,
+ const char *name, const char *mode);
+LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
+
+LUALIB_API lua_State *(luaL_newstate) (void);
+
+LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx);
+
+LUALIB_API void (luaL_addgsub) (luaL_Buffer *b, const char *s,
+ const char *p, const char *r);
+LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s,
+ const char *p, const char *r);
+
+LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup);
+
+LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname);
+
+LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1,
+ const char *msg, int level);
+
+LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname,
+ lua_CFunction openf, int glb);
+
+/*
+** ===============================================================
+** some useful macros
+** ===============================================================
+*/
+
+
+#define luaL_newlibtable(L,l) \
+ lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)
+
+#define luaL_newlib(L,l) \
+ (luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
+
+#define luaL_argcheck(L, cond,arg,extramsg) \
+ ((void)(luai_likely(cond) || luaL_argerror(L, (arg), (extramsg))))
+
+#define luaL_argexpected(L,cond,arg,tname) \
+ ((void)(luai_likely(cond) || luaL_typeerror(L, (arg), (tname))))
+
+#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
+#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
+
+#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
+
+#define luaL_dofile(L, fn) \
+ (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
+
+#define luaL_dostring(L, s) \
+ (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
+
+#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
+
+#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
+
+#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL)
+
+
+/*
+** Perform arithmetic operations on lua_Integer values with wrap-around
+** semantics, as the Lua core does.
+*/
+#define luaL_intop(op,v1,v2) \
+ ((lua_Integer)((lua_Unsigned)(v1) op (lua_Unsigned)(v2)))
+
+
+/* push the value used to represent failure/error */
+#define luaL_pushfail(L) lua_pushnil(L)
+
+
+/*
+** Internal assertions for in-house debugging
+*/
+#if !defined(lua_assert)
+
+#if defined LUAI_ASSERT
+ #include <assert.h>
+ #define lua_assert(c) assert(c)
+#else
+ #define lua_assert(c) ((void)0)
+#endif
+
+#endif
+
+
+
+/*
+** {======================================================
+** Generic Buffer manipulation
+** =======================================================
+*/
+
+struct luaL_Buffer {
+ char *b; /* buffer address */
+ size_t size; /* buffer size */
+ size_t n; /* number of characters in buffer */
+ lua_State *L;
+ union {
+ LUAI_MAXALIGN; /* ensure maximum alignment for buffer */
+ char b[LUAL_BUFFERSIZE]; /* initial buffer */
+ } init;
+};
+
+
+#define luaL_bufflen(bf) ((bf)->n)
+#define luaL_buffaddr(bf) ((bf)->b)
+
+
+#define luaL_addchar(B,c) \
+ ((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \
+ ((B)->b[(B)->n++] = (c)))
+
+#define luaL_addsize(B,s) ((B)->n += (s))
+
+#define luaL_buffsub(B,s) ((B)->n -= (s))
+
+LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
+LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz);
+LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
+LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
+LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
+LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
+LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz);
+LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz);
+
+#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE)
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** File handles for IO library
+** =======================================================
+*/
+
+/*
+** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and
+** initial structure 'luaL_Stream' (it may contain other fields
+** after that initial structure).
+*/
+
+#define LUA_FILEHANDLE "FILE*"
+
+
+typedef struct luaL_Stream {
+ FILE *f; /* stream (NULL for incompletely created streams) */
+ lua_CFunction closef; /* to close stream (NULL for closed streams) */
+} luaL_Stream;
+
+/* }====================================================== */
+
+/*
+** {==================================================================
+** "Abstraction Layer" for basic report of messages and errors
+** ===================================================================
+*/
+
+/* print a string */
+#if !defined(lua_writestring)
+#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout)
+#endif
+
+/* print a newline and flush the output */
+#if !defined(lua_writeline)
+#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout))
+#endif
+
+/* print an error message */
+#if !defined(lua_writestringerror)
+#define lua_writestringerror(s,p) \
+ (fprintf(stderr, (s), (p)), fflush(stderr))
+#endif
+
+/* }================================================================== */
+
+
+/*
+** {============================================================
+** Compatibility with deprecated conversions
+** =============================================================
+*/
+#if defined(LUA_COMPAT_APIINTCASTS)
+
+#define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a))
+#define luaL_optunsigned(L,a,d) \
+ ((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d)))
+
+#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
+#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
+
+#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
+#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
+
+#endif
+/* }============================================================ */
+
+
+
+#endif
+
+
diff --git a/src/libs/3rdparty/lua/src/lbaselib.c b/src/libs/3rdparty/lua/src/lbaselib.c
new file mode 100644
index 0000000000..1d60c9dede
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lbaselib.c
@@ -0,0 +1,549 @@
+/*
+** $Id: lbaselib.c $
+** Basic library
+** See Copyright Notice in lua.h
+*/
+
+#define lbaselib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+static int luaB_print (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int i;
+ for (i = 1; i <= n; i++) { /* for each argument */
+ size_t l;
+ const char *s = luaL_tolstring(L, i, &l); /* convert it to string */
+ if (i > 1) /* not the first element? */
+ lua_writestring("\t", 1); /* add a tab before it */
+ lua_writestring(s, l); /* print it */
+ lua_pop(L, 1); /* pop result */
+ }
+ lua_writeline();
+ return 0;
+}
+
+
+/*
+** Creates a warning with all given arguments.
+** Check first for errors; otherwise an error may interrupt
+** the composition of a warning, leaving it unfinished.
+*/
+static int luaB_warn (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int i;
+ luaL_checkstring(L, 1); /* at least one argument */
+ for (i = 2; i <= n; i++)
+ luaL_checkstring(L, i); /* make sure all arguments are strings */
+ for (i = 1; i < n; i++) /* compose warning */
+ lua_warning(L, lua_tostring(L, i), 1);
+ lua_warning(L, lua_tostring(L, n), 0); /* close warning */
+ return 0;
+}
+
+
+#define SPACECHARS " \f\n\r\t\v"
+
+static const char *b_str2int (const char *s, int base, lua_Integer *pn) {
+ lua_Unsigned n = 0;
+ int neg = 0;
+ s += strspn(s, SPACECHARS); /* skip initial spaces */
+ if (*s == '-') { s++; neg = 1; } /* handle sign */
+ else if (*s == '+') s++;
+ if (!isalnum((unsigned char)*s)) /* no digit? */
+ return NULL;
+ do {
+ int digit = (isdigit((unsigned char)*s)) ? *s - '0'
+ : (toupper((unsigned char)*s) - 'A') + 10;
+ if (digit >= base) return NULL; /* invalid numeral */
+ n = n * base + digit;
+ s++;
+ } while (isalnum((unsigned char)*s));
+ s += strspn(s, SPACECHARS); /* skip trailing spaces */
+ *pn = (lua_Integer)((neg) ? (0u - n) : n);
+ return s;
+}
+
+
+static int luaB_tonumber (lua_State *L) {
+ if (lua_isnoneornil(L, 2)) { /* standard conversion? */
+ if (lua_type(L, 1) == LUA_TNUMBER) { /* already a number? */
+ lua_settop(L, 1); /* yes; return it */
+ return 1;
+ }
+ else {
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ if (s != NULL && lua_stringtonumber(L, s) == l + 1)
+ return 1; /* successful conversion to number */
+ /* else not a number */
+ luaL_checkany(L, 1); /* (but there must be some parameter) */
+ }
+ }
+ else {
+ size_t l;
+ const char *s;
+ lua_Integer n = 0; /* to avoid warnings */
+ lua_Integer base = luaL_checkinteger(L, 2);
+ luaL_checktype(L, 1, LUA_TSTRING); /* no numbers as strings */
+ s = lua_tolstring(L, 1, &l);
+ luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range");
+ if (b_str2int(s, (int)base, &n) == s + l) {
+ lua_pushinteger(L, n);
+ return 1;
+ } /* else not a number */
+ } /* else not a number */
+ luaL_pushfail(L); /* not a number */
+ return 1;
+}
+
+
+static int luaB_error (lua_State *L) {
+ int level = (int)luaL_optinteger(L, 2, 1);
+ lua_settop(L, 1);
+ if (lua_type(L, 1) == LUA_TSTRING && level > 0) {
+ luaL_where(L, level); /* add extra information */
+ lua_pushvalue(L, 1);
+ lua_concat(L, 2);
+ }
+ return lua_error(L);
+}
+
+
+static int luaB_getmetatable (lua_State *L) {
+ luaL_checkany(L, 1);
+ if (!lua_getmetatable(L, 1)) {
+ lua_pushnil(L);
+ return 1; /* no metatable */
+ }
+ luaL_getmetafield(L, 1, "__metatable");
+ return 1; /* returns either __metatable field (if present) or metatable */
+}
+
+
+static int luaB_setmetatable (lua_State *L) {
+ int t = lua_type(L, 2);
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
+ if (l_unlikely(luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL))
+ return luaL_error(L, "cannot change a protected metatable");
+ lua_settop(L, 2);
+ lua_setmetatable(L, 1);
+ return 1;
+}
+
+
+static int luaB_rawequal (lua_State *L) {
+ luaL_checkany(L, 1);
+ luaL_checkany(L, 2);
+ lua_pushboolean(L, lua_rawequal(L, 1, 2));
+ return 1;
+}
+
+
+static int luaB_rawlen (lua_State *L) {
+ int t = lua_type(L, 1);
+ luaL_argexpected(L, t == LUA_TTABLE || t == LUA_TSTRING, 1,
+ "table or string");
+ lua_pushinteger(L, lua_rawlen(L, 1));
+ return 1;
+}
+
+
+static int luaB_rawget (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checkany(L, 2);
+ lua_settop(L, 2);
+ lua_rawget(L, 1);
+ return 1;
+}
+
+static int luaB_rawset (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ luaL_checkany(L, 2);
+ luaL_checkany(L, 3);
+ lua_settop(L, 3);
+ lua_rawset(L, 1);
+ return 1;
+}
+
+
+static int pushmode (lua_State *L, int oldmode) {
+ if (oldmode == -1)
+ luaL_pushfail(L); /* invalid call to 'lua_gc' */
+ else
+ lua_pushstring(L, (oldmode == LUA_GCINC) ? "incremental"
+ : "generational");
+ return 1;
+}
+
+
+/*
+** check whether call to 'lua_gc' was valid (not inside a finalizer)
+*/
+#define checkvalres(res) { if (res == -1) break; }
+
+static int luaB_collectgarbage (lua_State *L) {
+ static const char *const opts[] = {"stop", "restart", "collect",
+ "count", "step", "setpause", "setstepmul",
+ "isrunning", "generational", "incremental", NULL};
+ static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,
+ LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL,
+ LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC};
+ int o = optsnum[luaL_checkoption(L, 1, "collect", opts)];
+ switch (o) {
+ case LUA_GCCOUNT: {
+ int k = lua_gc(L, o);
+ int b = lua_gc(L, LUA_GCCOUNTB);
+ checkvalres(k);
+ lua_pushnumber(L, (lua_Number)k + ((lua_Number)b/1024));
+ return 1;
+ }
+ case LUA_GCSTEP: {
+ int step = (int)luaL_optinteger(L, 2, 0);
+ int res = lua_gc(L, o, step);
+ checkvalres(res);
+ lua_pushboolean(L, res);
+ return 1;
+ }
+ case LUA_GCSETPAUSE:
+ case LUA_GCSETSTEPMUL: {
+ int p = (int)luaL_optinteger(L, 2, 0);
+ int previous = lua_gc(L, o, p);
+ checkvalres(previous);
+ lua_pushinteger(L, previous);
+ return 1;
+ }
+ case LUA_GCISRUNNING: {
+ int res = lua_gc(L, o);
+ checkvalres(res);
+ lua_pushboolean(L, res);
+ return 1;
+ }
+ case LUA_GCGEN: {
+ int minormul = (int)luaL_optinteger(L, 2, 0);
+ int majormul = (int)luaL_optinteger(L, 3, 0);
+ return pushmode(L, lua_gc(L, o, minormul, majormul));
+ }
+ case LUA_GCINC: {
+ int pause = (int)luaL_optinteger(L, 2, 0);
+ int stepmul = (int)luaL_optinteger(L, 3, 0);
+ int stepsize = (int)luaL_optinteger(L, 4, 0);
+ return pushmode(L, lua_gc(L, o, pause, stepmul, stepsize));
+ }
+ default: {
+ int res = lua_gc(L, o);
+ checkvalres(res);
+ lua_pushinteger(L, res);
+ return 1;
+ }
+ }
+ luaL_pushfail(L); /* invalid call (inside a finalizer) */
+ return 1;
+}
+
+
+static int luaB_type (lua_State *L) {
+ int t = lua_type(L, 1);
+ luaL_argcheck(L, t != LUA_TNONE, 1, "value expected");
+ lua_pushstring(L, lua_typename(L, t));
+ return 1;
+}
+
+
+static int luaB_next (lua_State *L) {
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_settop(L, 2); /* create a 2nd argument if there isn't one */
+ if (lua_next(L, 1))
+ return 2;
+ else {
+ lua_pushnil(L);
+ return 1;
+ }
+}
+
+
+static int pairscont (lua_State *L, int status, lua_KContext k) {
+ (void)L; (void)status; (void)k; /* unused */
+ return 3;
+}
+
+static int luaB_pairs (lua_State *L) {
+ luaL_checkany(L, 1);
+ if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */
+ lua_pushcfunction(L, luaB_next); /* will return generator, */
+ lua_pushvalue(L, 1); /* state, */
+ lua_pushnil(L); /* and initial value */
+ }
+ else {
+ lua_pushvalue(L, 1); /* argument 'self' to metamethod */
+ lua_callk(L, 1, 3, 0, pairscont); /* get 3 values from metamethod */
+ }
+ return 3;
+}
+
+
+/*
+** Traversal function for 'ipairs'
+*/
+static int ipairsaux (lua_State *L) {
+ lua_Integer i = luaL_checkinteger(L, 2);
+ i = luaL_intop(+, i, 1);
+ lua_pushinteger(L, i);
+ return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2;
+}
+
+
+/*
+** 'ipairs' function. Returns 'ipairsaux', given "table", 0.
+** (The given "table" may not be a table.)
+*/
+static int luaB_ipairs (lua_State *L) {
+ luaL_checkany(L, 1);
+ lua_pushcfunction(L, ipairsaux); /* iteration function */
+ lua_pushvalue(L, 1); /* state */
+ lua_pushinteger(L, 0); /* initial value */
+ return 3;
+}
+
+
+static int load_aux (lua_State *L, int status, int envidx) {
+ if (l_likely(status == LUA_OK)) {
+ if (envidx != 0) { /* 'env' parameter? */
+ lua_pushvalue(L, envidx); /* environment for loaded function */
+ if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */
+ lua_pop(L, 1); /* remove 'env' if not used by previous call */
+ }
+ return 1;
+ }
+ else { /* error (message is on top of the stack) */
+ luaL_pushfail(L);
+ lua_insert(L, -2); /* put before error message */
+ return 2; /* return fail plus error message */
+ }
+}
+
+
+static int luaB_loadfile (lua_State *L) {
+ const char *fname = luaL_optstring(L, 1, NULL);
+ const char *mode = luaL_optstring(L, 2, NULL);
+ int env = (!lua_isnone(L, 3) ? 3 : 0); /* 'env' index or 0 if no 'env' */
+ int status = luaL_loadfilex(L, fname, mode);
+ return load_aux(L, status, env);
+}
+
+
+/*
+** {======================================================
+** Generic Read function
+** =======================================================
+*/
+
+
+/*
+** reserved slot, above all arguments, to hold a copy of the returned
+** string to avoid it being collected while parsed. 'load' has four
+** optional arguments (chunk, source name, mode, and environment).
+*/
+#define RESERVEDSLOT 5
+
+
+/*
+** Reader for generic 'load' function: 'lua_load' uses the
+** stack for internal stuff, so the reader cannot change the
+** stack top. Instead, it keeps its resulting string in a
+** reserved slot inside the stack.
+*/
+static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
+ (void)(ud); /* not used */
+ luaL_checkstack(L, 2, "too many nested functions");
+ lua_pushvalue(L, 1); /* get function */
+ lua_call(L, 0, 1); /* call it */
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1); /* pop result */
+ *size = 0;
+ return NULL;
+ }
+ else if (l_unlikely(!lua_isstring(L, -1)))
+ luaL_error(L, "reader function must return a string");
+ lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */
+ return lua_tolstring(L, RESERVEDSLOT, size);
+}
+
+
+static int luaB_load (lua_State *L) {
+ int status;
+ size_t l;
+ const char *s = lua_tolstring(L, 1, &l);
+ const char *mode = luaL_optstring(L, 3, "bt");
+ int env = (!lua_isnone(L, 4) ? 4 : 0); /* 'env' index or 0 if no 'env' */
+ if (s != NULL) { /* loading a string? */
+ const char *chunkname = luaL_optstring(L, 2, s);
+ status = luaL_loadbufferx(L, s, l, chunkname, mode);
+ }
+ else { /* loading from a reader function */
+ const char *chunkname = luaL_optstring(L, 2, "=(load)");
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ lua_settop(L, RESERVEDSLOT); /* create reserved slot */
+ status = lua_load(L, generic_reader, NULL, chunkname, mode);
+ }
+ return load_aux(L, status, env);
+}
+
+/* }====================================================== */
+
+
+static int dofilecont (lua_State *L, int d1, lua_KContext d2) {
+ (void)d1; (void)d2; /* only to match 'lua_Kfunction' prototype */
+ return lua_gettop(L) - 1;
+}
+
+
+static int luaB_dofile (lua_State *L) {
+ const char *fname = luaL_optstring(L, 1, NULL);
+ lua_settop(L, 1);
+ if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK))
+ return lua_error(L);
+ lua_callk(L, 0, LUA_MULTRET, 0, dofilecont);
+ return dofilecont(L, 0, 0);
+}
+
+
+static int luaB_assert (lua_State *L) {
+ if (l_likely(lua_toboolean(L, 1))) /* condition is true? */
+ return lua_gettop(L); /* return all arguments */
+ else { /* error */
+ luaL_checkany(L, 1); /* there must be a condition */
+ lua_remove(L, 1); /* remove it */
+ lua_pushliteral(L, "assertion failed!"); /* default message */
+ lua_settop(L, 1); /* leave only message (default if no other one) */
+ return luaB_error(L); /* call 'error' */
+ }
+}
+
+
+static int luaB_select (lua_State *L) {
+ int n = lua_gettop(L);
+ if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') {
+ lua_pushinteger(L, n-1);
+ return 1;
+ }
+ else {
+ lua_Integer i = luaL_checkinteger(L, 1);
+ if (i < 0) i = n + i;
+ else if (i > n) i = n;
+ luaL_argcheck(L, 1 <= i, 1, "index out of range");
+ return n - (int)i;
+ }
+}
+
+
+/*
+** Continuation function for 'pcall' and 'xpcall'. Both functions
+** already pushed a 'true' before doing the call, so in case of success
+** 'finishpcall' only has to return everything in the stack minus
+** 'extra' values (where 'extra' is exactly the number of items to be
+** ignored).
+*/
+static int finishpcall (lua_State *L, int status, lua_KContext extra) {
+ if (l_unlikely(status != LUA_OK && status != LUA_YIELD)) { /* error? */
+ lua_pushboolean(L, 0); /* first result (false) */
+ lua_pushvalue(L, -2); /* error message */
+ return 2; /* return false, msg */
+ }
+ else
+ return lua_gettop(L) - (int)extra; /* return all results */
+}
+
+
+static int luaB_pcall (lua_State *L) {
+ int status;
+ luaL_checkany(L, 1);
+ lua_pushboolean(L, 1); /* first result if no errors */
+ lua_insert(L, 1); /* put it in place */
+ status = lua_pcallk(L, lua_gettop(L) - 2, LUA_MULTRET, 0, 0, finishpcall);
+ return finishpcall(L, status, 0);
+}
+
+
+/*
+** Do a protected call with error handling. After 'lua_rotate', the
+** stack will have <f, err, true, f, [args...]>; so, the function passes
+** 2 to 'finishpcall' to skip the 2 first values when returning results.
+*/
+static int luaB_xpcall (lua_State *L) {
+ int status;
+ int n = lua_gettop(L);
+ luaL_checktype(L, 2, LUA_TFUNCTION); /* check error function */
+ lua_pushboolean(L, 1); /* first result */
+ lua_pushvalue(L, 1); /* function */
+ lua_rotate(L, 3, 2); /* move them below function's arguments */
+ status = lua_pcallk(L, n - 2, LUA_MULTRET, 2, 2, finishpcall);
+ return finishpcall(L, status, 2);
+}
+
+
+static int luaB_tostring (lua_State *L) {
+ luaL_checkany(L, 1);
+ luaL_tolstring(L, 1, NULL);
+ return 1;
+}
+
+
+static const luaL_Reg base_funcs[] = {
+ {"assert", luaB_assert},
+ {"collectgarbage", luaB_collectgarbage},
+ {"dofile", luaB_dofile},
+ {"error", luaB_error},
+ {"getmetatable", luaB_getmetatable},
+ {"ipairs", luaB_ipairs},
+ {"loadfile", luaB_loadfile},
+ {"load", luaB_load},
+ {"next", luaB_next},
+ {"pairs", luaB_pairs},
+ {"pcall", luaB_pcall},
+ {"print", luaB_print},
+ {"warn", luaB_warn},
+ {"rawequal", luaB_rawequal},
+ {"rawlen", luaB_rawlen},
+ {"rawget", luaB_rawget},
+ {"rawset", luaB_rawset},
+ {"select", luaB_select},
+ {"setmetatable", luaB_setmetatable},
+ {"tonumber", luaB_tonumber},
+ {"tostring", luaB_tostring},
+ {"type", luaB_type},
+ {"xpcall", luaB_xpcall},
+ /* placeholders */
+ {LUA_GNAME, NULL},
+ {"_VERSION", NULL},
+ {NULL, NULL}
+};
+
+
+LUAMOD_API int luaopen_base (lua_State *L) {
+ /* open lib into global table */
+ lua_pushglobaltable(L);
+ luaL_setfuncs(L, base_funcs, 0);
+ /* set global _G */
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, LUA_GNAME);
+ /* set global _VERSION */
+ lua_pushliteral(L, LUA_VERSION);
+ lua_setfield(L, -2, "_VERSION");
+ return 1;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lcode.c b/src/libs/3rdparty/lua/src/lcode.c
new file mode 100644
index 0000000000..1a371ca943
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lcode.c
@@ -0,0 +1,1871 @@
+/*
+** $Id: lcode.c $
+** Code generator for Lua
+** See Copyright Notice in lua.h
+*/
+
+#define lcode_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <stdlib.h>
+
+#include "lua.h"
+
+#include "lcode.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lgc.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "lvm.h"
+
+
+/* Maximum number of registers in a Lua function (must fit in 8 bits) */
+#define MAXREGS 255
+
+
+#define hasjumps(e) ((e)->t != (e)->f)
+
+
+static int codesJ (FuncState *fs, OpCode o, int sj, int k);
+
+
+
+/* semantic error */
+l_noret luaK_semerror (LexState *ls, const char *msg) {
+ ls->t.token = 0; /* remove "near <token>" from final message */
+ luaX_syntaxerror(ls, msg);
+}
+
+
+/*
+** If expression is a numeric constant, fills 'v' with its value
+** and returns 1. Otherwise, returns 0.
+*/
+static int tonumeral (const expdesc *e, TValue *v) {
+ if (hasjumps(e))
+ return 0; /* not a numeral */
+ switch (e->k) {
+ case VKINT:
+ if (v) setivalue(v, e->u.ival);
+ return 1;
+ case VKFLT:
+ if (v) setfltvalue(v, e->u.nval);
+ return 1;
+ default: return 0;
+ }
+}
+
+
+/*
+** Get the constant value from a constant expression
+*/
+static TValue *const2val (FuncState *fs, const expdesc *e) {
+ lua_assert(e->k == VCONST);
+ return &fs->ls->dyd->actvar.arr[e->u.info].k;
+}
+
+
+/*
+** If expression is a constant, fills 'v' with its value
+** and returns 1. Otherwise, returns 0.
+*/
+int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v) {
+ if (hasjumps(e))
+ return 0; /* not a constant */
+ switch (e->k) {
+ case VFALSE:
+ setbfvalue(v);
+ return 1;
+ case VTRUE:
+ setbtvalue(v);
+ return 1;
+ case VNIL:
+ setnilvalue(v);
+ return 1;
+ case VKSTR: {
+ setsvalue(fs->ls->L, v, e->u.strval);
+ return 1;
+ }
+ case VCONST: {
+ setobj(fs->ls->L, v, const2val(fs, e));
+ return 1;
+ }
+ default: return tonumeral(e, v);
+ }
+}
+
+
+/*
+** Return the previous instruction of the current code. If there
+** may be a jump target between the current instruction and the
+** previous one, return an invalid instruction (to avoid wrong
+** optimizations).
+*/
+static Instruction *previousinstruction (FuncState *fs) {
+ static const Instruction invalidinstruction = ~(Instruction)0;
+ if (fs->pc > fs->lasttarget)
+ return &fs->f->code[fs->pc - 1]; /* previous instruction */
+ else
+ return cast(Instruction*, &invalidinstruction);
+}
+
+
+/*
+** Create a OP_LOADNIL instruction, but try to optimize: if the previous
+** instruction is also OP_LOADNIL and ranges are compatible, adjust
+** range of previous instruction instead of emitting a new one. (For
+** instance, 'local a; local b' will generate a single opcode.)
+*/
+void luaK_nil (FuncState *fs, int from, int n) {
+ int l = from + n - 1; /* last register to set nil */
+ Instruction *previous = previousinstruction(fs);
+ if (GET_OPCODE(*previous) == OP_LOADNIL) { /* previous is LOADNIL? */
+ int pfrom = GETARG_A(*previous); /* get previous range */
+ int pl = pfrom + GETARG_B(*previous);
+ if ((pfrom <= from && from <= pl + 1) ||
+ (from <= pfrom && pfrom <= l + 1)) { /* can connect both? */
+ if (pfrom < from) from = pfrom; /* from = min(from, pfrom) */
+ if (pl > l) l = pl; /* l = max(l, pl) */
+ SETARG_A(*previous, from);
+ SETARG_B(*previous, l - from);
+ return;
+ } /* else go through */
+ }
+ luaK_codeABC(fs, OP_LOADNIL, from, n - 1, 0); /* else no optimization */
+}
+
+
+/*
+** Gets the destination address of a jump instruction. Used to traverse
+** a list of jumps.
+*/
+static int getjump (FuncState *fs, int pc) {
+ int offset = GETARG_sJ(fs->f->code[pc]);
+ if (offset == NO_JUMP) /* point to itself represents end of list */
+ return NO_JUMP; /* end of list */
+ else
+ return (pc+1)+offset; /* turn offset into absolute position */
+}
+
+
+/*
+** Fix jump instruction at position 'pc' to jump to 'dest'.
+** (Jump addresses are relative in Lua)
+*/
+static void fixjump (FuncState *fs, int pc, int dest) {
+ Instruction *jmp = &fs->f->code[pc];
+ int offset = dest - (pc + 1);
+ lua_assert(dest != NO_JUMP);
+ if (!(-OFFSET_sJ <= offset && offset <= MAXARG_sJ - OFFSET_sJ))
+ luaX_syntaxerror(fs->ls, "control structure too long");
+ lua_assert(GET_OPCODE(*jmp) == OP_JMP);
+ SETARG_sJ(*jmp, offset);
+}
+
+
+/*
+** Concatenate jump-list 'l2' into jump-list 'l1'
+*/
+void luaK_concat (FuncState *fs, int *l1, int l2) {
+ if (l2 == NO_JUMP) return; /* nothing to concatenate? */
+ else if (*l1 == NO_JUMP) /* no original list? */
+ *l1 = l2; /* 'l1' points to 'l2' */
+ else {
+ int list = *l1;
+ int next;
+ while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */
+ list = next;
+ fixjump(fs, list, l2); /* last element links to 'l2' */
+ }
+}
+
+
+/*
+** Create a jump instruction and return its position, so its destination
+** can be fixed later (with 'fixjump').
+*/
+int luaK_jump (FuncState *fs) {
+ return codesJ(fs, OP_JMP, NO_JUMP, 0);
+}
+
+
+/*
+** Code a 'return' instruction
+*/
+void luaK_ret (FuncState *fs, int first, int nret) {
+ OpCode op;
+ switch (nret) {
+ case 0: op = OP_RETURN0; break;
+ case 1: op = OP_RETURN1; break;
+ default: op = OP_RETURN; break;
+ }
+ luaK_codeABC(fs, op, first, nret + 1, 0);
+}
+
+
+/*
+** Code a "conditional jump", that is, a test or comparison opcode
+** followed by a jump. Return jump position.
+*/
+static int condjump (FuncState *fs, OpCode op, int A, int B, int C, int k) {
+ luaK_codeABCk(fs, op, A, B, C, k);
+ return luaK_jump(fs);
+}
+
+
+/*
+** returns current 'pc' and marks it as a jump target (to avoid wrong
+** optimizations with consecutive instructions not in the same basic block).
+*/
+int luaK_getlabel (FuncState *fs) {
+ fs->lasttarget = fs->pc;
+ return fs->pc;
+}
+
+
+/*
+** Returns the position of the instruction "controlling" a given
+** jump (that is, its condition), or the jump itself if it is
+** unconditional.
+*/
+static Instruction *getjumpcontrol (FuncState *fs, int pc) {
+ Instruction *pi = &fs->f->code[pc];
+ if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1))))
+ return pi-1;
+ else
+ return pi;
+}
+
+
+/*
+** Patch destination register for a TESTSET instruction.
+** If instruction in position 'node' is not a TESTSET, return 0 ("fails").
+** Otherwise, if 'reg' is not 'NO_REG', set it as the destination
+** register. Otherwise, change instruction to a simple 'TEST' (produces
+** no register value)
+*/
+static int patchtestreg (FuncState *fs, int node, int reg) {
+ Instruction *i = getjumpcontrol(fs, node);
+ if (GET_OPCODE(*i) != OP_TESTSET)
+ return 0; /* cannot patch other instructions */
+ if (reg != NO_REG && reg != GETARG_B(*i))
+ SETARG_A(*i, reg);
+ else {
+ /* no register to put value or register already has the value;
+ change instruction to simple test */
+ *i = CREATE_ABCk(OP_TEST, GETARG_B(*i), 0, 0, GETARG_k(*i));
+ }
+ return 1;
+}
+
+
+/*
+** Traverse a list of tests ensuring no one produces a value
+*/
+static void removevalues (FuncState *fs, int list) {
+ for (; list != NO_JUMP; list = getjump(fs, list))
+ patchtestreg(fs, list, NO_REG);
+}
+
+
+/*
+** Traverse a list of tests, patching their destination address and
+** registers: tests producing values jump to 'vtarget' (and put their
+** values in 'reg'), other tests jump to 'dtarget'.
+*/
+static void patchlistaux (FuncState *fs, int list, int vtarget, int reg,
+ int dtarget) {
+ while (list != NO_JUMP) {
+ int next = getjump(fs, list);
+ if (patchtestreg(fs, list, reg))
+ fixjump(fs, list, vtarget);
+ else
+ fixjump(fs, list, dtarget); /* jump to default target */
+ list = next;
+ }
+}
+
+
+/*
+** Path all jumps in 'list' to jump to 'target'.
+** (The assert means that we cannot fix a jump to a forward address
+** because we only know addresses once code is generated.)
+*/
+void luaK_patchlist (FuncState *fs, int list, int target) {
+ lua_assert(target <= fs->pc);
+ patchlistaux(fs, list, target, NO_REG, target);
+}
+
+
+void luaK_patchtohere (FuncState *fs, int list) {
+ int hr = luaK_getlabel(fs); /* mark "here" as a jump target */
+ luaK_patchlist(fs, list, hr);
+}
+
+
+/* limit for difference between lines in relative line info. */
+#define LIMLINEDIFF 0x80
+
+
+/*
+** Save line info for a new instruction. If difference from last line
+** does not fit in a byte, of after that many instructions, save a new
+** absolute line info; (in that case, the special value 'ABSLINEINFO'
+** in 'lineinfo' signals the existence of this absolute information.)
+** Otherwise, store the difference from last line in 'lineinfo'.
+*/
+static void savelineinfo (FuncState *fs, Proto *f, int line) {
+ int linedif = line - fs->previousline;
+ int pc = fs->pc - 1; /* last instruction coded */
+ if (abs(linedif) >= LIMLINEDIFF || fs->iwthabs++ >= MAXIWTHABS) {
+ luaM_growvector(fs->ls->L, f->abslineinfo, fs->nabslineinfo,
+ f->sizeabslineinfo, AbsLineInfo, MAX_INT, "lines");
+ f->abslineinfo[fs->nabslineinfo].pc = pc;
+ f->abslineinfo[fs->nabslineinfo++].line = line;
+ linedif = ABSLINEINFO; /* signal that there is absolute information */
+ fs->iwthabs = 1; /* restart counter */
+ }
+ luaM_growvector(fs->ls->L, f->lineinfo, pc, f->sizelineinfo, ls_byte,
+ MAX_INT, "opcodes");
+ f->lineinfo[pc] = linedif;
+ fs->previousline = line; /* last line saved */
+}
+
+
+/*
+** Remove line information from the last instruction.
+** If line information for that instruction is absolute, set 'iwthabs'
+** above its max to force the new (replacing) instruction to have
+** absolute line info, too.
+*/
+static void removelastlineinfo (FuncState *fs) {
+ Proto *f = fs->f;
+ int pc = fs->pc - 1; /* last instruction coded */
+ if (f->lineinfo[pc] != ABSLINEINFO) { /* relative line info? */
+ fs->previousline -= f->lineinfo[pc]; /* correct last line saved */
+ fs->iwthabs--; /* undo previous increment */
+ }
+ else { /* absolute line information */
+ lua_assert(f->abslineinfo[fs->nabslineinfo - 1].pc == pc);
+ fs->nabslineinfo--; /* remove it */
+ fs->iwthabs = MAXIWTHABS + 1; /* force next line info to be absolute */
+ }
+}
+
+
+/*
+** Remove the last instruction created, correcting line information
+** accordingly.
+*/
+static void removelastinstruction (FuncState *fs) {
+ removelastlineinfo(fs);
+ fs->pc--;
+}
+
+
+/*
+** Emit instruction 'i', checking for array sizes and saving also its
+** line information. Return 'i' position.
+*/
+int luaK_code (FuncState *fs, Instruction i) {
+ Proto *f = fs->f;
+ /* put new instruction in code array */
+ luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction,
+ MAX_INT, "opcodes");
+ f->code[fs->pc++] = i;
+ savelineinfo(fs, f, fs->ls->lastline);
+ return fs->pc - 1; /* index of new instruction */
+}
+
+
+/*
+** Format and emit an 'iABC' instruction. (Assertions check consistency
+** of parameters versus opcode.)
+*/
+int luaK_codeABCk (FuncState *fs, OpCode o, int a, int b, int c, int k) {
+ lua_assert(getOpMode(o) == iABC);
+ lua_assert(a <= MAXARG_A && b <= MAXARG_B &&
+ c <= MAXARG_C && (k & ~1) == 0);
+ return luaK_code(fs, CREATE_ABCk(o, a, b, c, k));
+}
+
+
+/*
+** Format and emit an 'iABx' instruction.
+*/
+int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) {
+ lua_assert(getOpMode(o) == iABx);
+ lua_assert(a <= MAXARG_A && bc <= MAXARG_Bx);
+ return luaK_code(fs, CREATE_ABx(o, a, bc));
+}
+
+
+/*
+** Format and emit an 'iAsBx' instruction.
+*/
+int luaK_codeAsBx (FuncState *fs, OpCode o, int a, int bc) {
+ unsigned int b = bc + OFFSET_sBx;
+ lua_assert(getOpMode(o) == iAsBx);
+ lua_assert(a <= MAXARG_A && b <= MAXARG_Bx);
+ return luaK_code(fs, CREATE_ABx(o, a, b));
+}
+
+
+/*
+** Format and emit an 'isJ' instruction.
+*/
+static int codesJ (FuncState *fs, OpCode o, int sj, int k) {
+ unsigned int j = sj + OFFSET_sJ;
+ lua_assert(getOpMode(o) == isJ);
+ lua_assert(j <= MAXARG_sJ && (k & ~1) == 0);
+ return luaK_code(fs, CREATE_sJ(o, j, k));
+}
+
+
+/*
+** Emit an "extra argument" instruction (format 'iAx')
+*/
+static int codeextraarg (FuncState *fs, int a) {
+ lua_assert(a <= MAXARG_Ax);
+ return luaK_code(fs, CREATE_Ax(OP_EXTRAARG, a));
+}
+
+
+/*
+** Emit a "load constant" instruction, using either 'OP_LOADK'
+** (if constant index 'k' fits in 18 bits) or an 'OP_LOADKX'
+** instruction with "extra argument".
+*/
+static int luaK_codek (FuncState *fs, int reg, int k) {
+ if (k <= MAXARG_Bx)
+ return luaK_codeABx(fs, OP_LOADK, reg, k);
+ else {
+ int p = luaK_codeABx(fs, OP_LOADKX, reg, 0);
+ codeextraarg(fs, k);
+ return p;
+ }
+}
+
+
+/*
+** Check register-stack level, keeping track of its maximum size
+** in field 'maxstacksize'
+*/
+void luaK_checkstack (FuncState *fs, int n) {
+ int newstack = fs->freereg + n;
+ if (newstack > fs->f->maxstacksize) {
+ if (newstack >= MAXREGS)
+ luaX_syntaxerror(fs->ls,
+ "function or expression needs too many registers");
+ fs->f->maxstacksize = cast_byte(newstack);
+ }
+}
+
+
+/*
+** Reserve 'n' registers in register stack
+*/
+void luaK_reserveregs (FuncState *fs, int n) {
+ luaK_checkstack(fs, n);
+ fs->freereg += n;
+}
+
+
+/*
+** Free register 'reg', if it is neither a constant index nor
+** a local variable.
+)
+*/
+static void freereg (FuncState *fs, int reg) {
+ if (reg >= luaY_nvarstack(fs)) {
+ fs->freereg--;
+ lua_assert(reg == fs->freereg);
+ }
+}
+
+
+/*
+** Free two registers in proper order
+*/
+static void freeregs (FuncState *fs, int r1, int r2) {
+ if (r1 > r2) {
+ freereg(fs, r1);
+ freereg(fs, r2);
+ }
+ else {
+ freereg(fs, r2);
+ freereg(fs, r1);
+ }
+}
+
+
+/*
+** Free register used by expression 'e' (if any)
+*/
+static void freeexp (FuncState *fs, expdesc *e) {
+ if (e->k == VNONRELOC)
+ freereg(fs, e->u.info);
+}
+
+
+/*
+** Free registers used by expressions 'e1' and 'e2' (if any) in proper
+** order.
+*/
+static void freeexps (FuncState *fs, expdesc *e1, expdesc *e2) {
+ int r1 = (e1->k == VNONRELOC) ? e1->u.info : -1;
+ int r2 = (e2->k == VNONRELOC) ? e2->u.info : -1;
+ freeregs(fs, r1, r2);
+}
+
+
+/*
+** Add constant 'v' to prototype's list of constants (field 'k').
+** Use scanner's table to cache position of constants in constant list
+** and try to reuse constants. Because some values should not be used
+** as keys (nil cannot be a key, integer keys can collapse with float
+** keys), the caller must provide a useful 'key' for indexing the cache.
+** Note that all functions share the same table, so entering or exiting
+** a function can make some indices wrong.
+*/
+static int addk (FuncState *fs, TValue *key, TValue *v) {
+ TValue val;
+ lua_State *L = fs->ls->L;
+ Proto *f = fs->f;
+ const TValue *idx = luaH_get(fs->ls->h, key); /* query scanner table */
+ int k, oldsize;
+ if (ttisinteger(idx)) { /* is there an index there? */
+ k = cast_int(ivalue(idx));
+ /* correct value? (warning: must distinguish floats from integers!) */
+ if (k < fs->nk && ttypetag(&f->k[k]) == ttypetag(v) &&
+ luaV_rawequalobj(&f->k[k], v))
+ return k; /* reuse index */
+ }
+ /* constant not found; create a new entry */
+ oldsize = f->sizek;
+ k = fs->nk;
+ /* numerical value does not need GC barrier;
+ table has no metatable, so it does not need to invalidate cache */
+ setivalue(&val, k);
+ luaH_finishset(L, fs->ls->h, key, idx, &val);
+ luaM_growvector(L, f->k, k, f->sizek, TValue, MAXARG_Ax, "constants");
+ while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
+ setobj(L, &f->k[k], v);
+ fs->nk++;
+ luaC_barrier(L, f, v);
+ return k;
+}
+
+
+/*
+** Add a string to list of constants and return its index.
+*/
+static int stringK (FuncState *fs, TString *s) {
+ TValue o;
+ setsvalue(fs->ls->L, &o, s);
+ return addk(fs, &o, &o); /* use string itself as key */
+}
+
+
+/*
+** Add an integer to list of constants and return its index.
+*/
+static int luaK_intK (FuncState *fs, lua_Integer n) {
+ TValue o;
+ setivalue(&o, n);
+ return addk(fs, &o, &o); /* use integer itself as key */
+}
+
+/*
+** Add a float to list of constants and return its index. Floats
+** with integral values need a different key, to avoid collision
+** with actual integers. To that, we add to the number its smaller
+** power-of-two fraction that is still significant in its scale.
+** For doubles, that would be 1/2^52.
+** (This method is not bulletproof: there may be another float
+** with that value, and for floats larger than 2^53 the result is
+** still an integer. At worst, this only wastes an entry with
+** a duplicate.)
+*/
+static int luaK_numberK (FuncState *fs, lua_Number r) {
+ TValue o;
+ lua_Integer ik;
+ setfltvalue(&o, r);
+ if (!luaV_flttointeger(r, &ik, F2Ieq)) /* not an integral value? */
+ return addk(fs, &o, &o); /* use number itself as key */
+ else { /* must build an alternative key */
+ const int nbm = l_floatatt(MANT_DIG);
+ const lua_Number q = l_mathop(ldexp)(l_mathop(1.0), -nbm + 1);
+ const lua_Number k = (ik == 0) ? q : r + r*q; /* new key */
+ TValue kv;
+ setfltvalue(&kv, k);
+ /* result is not an integral value, unless value is too large */
+ lua_assert(!luaV_flttointeger(k, &ik, F2Ieq) ||
+ l_mathop(fabs)(r) >= l_mathop(1e6));
+ return addk(fs, &kv, &o);
+ }
+}
+
+
+/*
+** Add a false to list of constants and return its index.
+*/
+static int boolF (FuncState *fs) {
+ TValue o;
+ setbfvalue(&o);
+ return addk(fs, &o, &o); /* use boolean itself as key */
+}
+
+
+/*
+** Add a true to list of constants and return its index.
+*/
+static int boolT (FuncState *fs) {
+ TValue o;
+ setbtvalue(&o);
+ return addk(fs, &o, &o); /* use boolean itself as key */
+}
+
+
+/*
+** Add nil to list of constants and return its index.
+*/
+static int nilK (FuncState *fs) {
+ TValue k, v;
+ setnilvalue(&v);
+ /* cannot use nil as key; instead use table itself to represent nil */
+ sethvalue(fs->ls->L, &k, fs->ls->h);
+ return addk(fs, &k, &v);
+}
+
+
+/*
+** Check whether 'i' can be stored in an 'sC' operand. Equivalent to
+** (0 <= int2sC(i) && int2sC(i) <= MAXARG_C) but without risk of
+** overflows in the hidden addition inside 'int2sC'.
+*/
+static int fitsC (lua_Integer i) {
+ return (l_castS2U(i) + OFFSET_sC <= cast_uint(MAXARG_C));
+}
+
+
+/*
+** Check whether 'i' can be stored in an 'sBx' operand.
+*/
+static int fitsBx (lua_Integer i) {
+ return (-OFFSET_sBx <= i && i <= MAXARG_Bx - OFFSET_sBx);
+}
+
+
+void luaK_int (FuncState *fs, int reg, lua_Integer i) {
+ if (fitsBx(i))
+ luaK_codeAsBx(fs, OP_LOADI, reg, cast_int(i));
+ else
+ luaK_codek(fs, reg, luaK_intK(fs, i));
+}
+
+
+static void luaK_float (FuncState *fs, int reg, lua_Number f) {
+ lua_Integer fi;
+ if (luaV_flttointeger(f, &fi, F2Ieq) && fitsBx(fi))
+ luaK_codeAsBx(fs, OP_LOADF, reg, cast_int(fi));
+ else
+ luaK_codek(fs, reg, luaK_numberK(fs, f));
+}
+
+
+/*
+** Convert a constant in 'v' into an expression description 'e'
+*/
+static void const2exp (TValue *v, expdesc *e) {
+ switch (ttypetag(v)) {
+ case LUA_VNUMINT:
+ e->k = VKINT; e->u.ival = ivalue(v);
+ break;
+ case LUA_VNUMFLT:
+ e->k = VKFLT; e->u.nval = fltvalue(v);
+ break;
+ case LUA_VFALSE:
+ e->k = VFALSE;
+ break;
+ case LUA_VTRUE:
+ e->k = VTRUE;
+ break;
+ case LUA_VNIL:
+ e->k = VNIL;
+ break;
+ case LUA_VSHRSTR: case LUA_VLNGSTR:
+ e->k = VKSTR; e->u.strval = tsvalue(v);
+ break;
+ default: lua_assert(0);
+ }
+}
+
+
+/*
+** Fix an expression to return the number of results 'nresults'.
+** 'e' must be a multi-ret expression (function call or vararg).
+*/
+void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) {
+ Instruction *pc = &getinstruction(fs, e);
+ if (e->k == VCALL) /* expression is an open function call? */
+ SETARG_C(*pc, nresults + 1);
+ else {
+ lua_assert(e->k == VVARARG);
+ SETARG_C(*pc, nresults + 1);
+ SETARG_A(*pc, fs->freereg);
+ luaK_reserveregs(fs, 1);
+ }
+}
+
+
+/*
+** Convert a VKSTR to a VK
+*/
+static void str2K (FuncState *fs, expdesc *e) {
+ lua_assert(e->k == VKSTR);
+ e->u.info = stringK(fs, e->u.strval);
+ e->k = VK;
+}
+
+
+/*
+** Fix an expression to return one result.
+** If expression is not a multi-ret expression (function call or
+** vararg), it already returns one result, so nothing needs to be done.
+** Function calls become VNONRELOC expressions (as its result comes
+** fixed in the base register of the call), while vararg expressions
+** become VRELOC (as OP_VARARG puts its results where it wants).
+** (Calls are created returning one result, so that does not need
+** to be fixed.)
+*/
+void luaK_setoneret (FuncState *fs, expdesc *e) {
+ if (e->k == VCALL) { /* expression is an open function call? */
+ /* already returns 1 value */
+ lua_assert(GETARG_C(getinstruction(fs, e)) == 2);
+ e->k = VNONRELOC; /* result has fixed position */
+ e->u.info = GETARG_A(getinstruction(fs, e));
+ }
+ else if (e->k == VVARARG) {
+ SETARG_C(getinstruction(fs, e), 2);
+ e->k = VRELOC; /* can relocate its simple result */
+ }
+}
+
+
+/*
+** Ensure that expression 'e' is not a variable (nor a <const>).
+** (Expression still may have jump lists.)
+*/
+void luaK_dischargevars (FuncState *fs, expdesc *e) {
+ switch (e->k) {
+ case VCONST: {
+ const2exp(const2val(fs, e), e);
+ break;
+ }
+ case VLOCAL: { /* already in a register */
+ e->u.info = e->u.var.ridx;
+ e->k = VNONRELOC; /* becomes a non-relocatable value */
+ break;
+ }
+ case VUPVAL: { /* move value to some (pending) register */
+ e->u.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.info, 0);
+ e->k = VRELOC;
+ break;
+ }
+ case VINDEXUP: {
+ e->u.info = luaK_codeABC(fs, OP_GETTABUP, 0, e->u.ind.t, e->u.ind.idx);
+ e->k = VRELOC;
+ break;
+ }
+ case VINDEXI: {
+ freereg(fs, e->u.ind.t);
+ e->u.info = luaK_codeABC(fs, OP_GETI, 0, e->u.ind.t, e->u.ind.idx);
+ e->k = VRELOC;
+ break;
+ }
+ case VINDEXSTR: {
+ freereg(fs, e->u.ind.t);
+ e->u.info = luaK_codeABC(fs, OP_GETFIELD, 0, e->u.ind.t, e->u.ind.idx);
+ e->k = VRELOC;
+ break;
+ }
+ case VINDEXED: {
+ freeregs(fs, e->u.ind.t, e->u.ind.idx);
+ e->u.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.ind.t, e->u.ind.idx);
+ e->k = VRELOC;
+ break;
+ }
+ case VVARARG: case VCALL: {
+ luaK_setoneret(fs, e);
+ break;
+ }
+ default: break; /* there is one value available (somewhere) */
+ }
+}
+
+
+/*
+** Ensure expression value is in register 'reg', making 'e' a
+** non-relocatable expression.
+** (Expression still may have jump lists.)
+*/
+static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
+ luaK_dischargevars(fs, e);
+ switch (e->k) {
+ case VNIL: {
+ luaK_nil(fs, reg, 1);
+ break;
+ }
+ case VFALSE: {
+ luaK_codeABC(fs, OP_LOADFALSE, reg, 0, 0);
+ break;
+ }
+ case VTRUE: {
+ luaK_codeABC(fs, OP_LOADTRUE, reg, 0, 0);
+ break;
+ }
+ case VKSTR: {
+ str2K(fs, e);
+ } /* FALLTHROUGH */
+ case VK: {
+ luaK_codek(fs, reg, e->u.info);
+ break;
+ }
+ case VKFLT: {
+ luaK_float(fs, reg, e->u.nval);
+ break;
+ }
+ case VKINT: {
+ luaK_int(fs, reg, e->u.ival);
+ break;
+ }
+ case VRELOC: {
+ Instruction *pc = &getinstruction(fs, e);
+ SETARG_A(*pc, reg); /* instruction will put result in 'reg' */
+ break;
+ }
+ case VNONRELOC: {
+ if (reg != e->u.info)
+ luaK_codeABC(fs, OP_MOVE, reg, e->u.info, 0);
+ break;
+ }
+ default: {
+ lua_assert(e->k == VJMP);
+ return; /* nothing to do... */
+ }
+ }
+ e->u.info = reg;
+ e->k = VNONRELOC;
+}
+
+
+/*
+** Ensure expression value is in a register, making 'e' a
+** non-relocatable expression.
+** (Expression still may have jump lists.)
+*/
+static void discharge2anyreg (FuncState *fs, expdesc *e) {
+ if (e->k != VNONRELOC) { /* no fixed register yet? */
+ luaK_reserveregs(fs, 1); /* get a register */
+ discharge2reg(fs, e, fs->freereg-1); /* put value there */
+ }
+}
+
+
+static int code_loadbool (FuncState *fs, int A, OpCode op) {
+ luaK_getlabel(fs); /* those instructions may be jump targets */
+ return luaK_codeABC(fs, op, A, 0, 0);
+}
+
+
+/*
+** check whether list has any jump that do not produce a value
+** or produce an inverted value
+*/
+static int need_value (FuncState *fs, int list) {
+ for (; list != NO_JUMP; list = getjump(fs, list)) {
+ Instruction i = *getjumpcontrol(fs, list);
+ if (GET_OPCODE(i) != OP_TESTSET) return 1;
+ }
+ return 0; /* not found */
+}
+
+
+/*
+** Ensures final expression result (which includes results from its
+** jump lists) is in register 'reg'.
+** If expression has jumps, need to patch these jumps either to
+** its final position or to "load" instructions (for those tests
+** that do not produce values).
+*/
+static void exp2reg (FuncState *fs, expdesc *e, int reg) {
+ discharge2reg(fs, e, reg);
+ if (e->k == VJMP) /* expression itself is a test? */
+ luaK_concat(fs, &e->t, e->u.info); /* put this jump in 't' list */
+ if (hasjumps(e)) {
+ int final; /* position after whole expression */
+ int p_f = NO_JUMP; /* position of an eventual LOAD false */
+ int p_t = NO_JUMP; /* position of an eventual LOAD true */
+ if (need_value(fs, e->t) || need_value(fs, e->f)) {
+ int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs);
+ p_f = code_loadbool(fs, reg, OP_LFALSESKIP); /* skip next inst. */
+ p_t = code_loadbool(fs, reg, OP_LOADTRUE);
+ /* jump around these booleans if 'e' is not a test */
+ luaK_patchtohere(fs, fj);
+ }
+ final = luaK_getlabel(fs);
+ patchlistaux(fs, e->f, final, reg, p_f);
+ patchlistaux(fs, e->t, final, reg, p_t);
+ }
+ e->f = e->t = NO_JUMP;
+ e->u.info = reg;
+ e->k = VNONRELOC;
+}
+
+
+/*
+** Ensures final expression result is in next available register.
+*/
+void luaK_exp2nextreg (FuncState *fs, expdesc *e) {
+ luaK_dischargevars(fs, e);
+ freeexp(fs, e);
+ luaK_reserveregs(fs, 1);
+ exp2reg(fs, e, fs->freereg - 1);
+}
+
+
+/*
+** Ensures final expression result is in some (any) register
+** and return that register.
+*/
+int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
+ luaK_dischargevars(fs, e);
+ if (e->k == VNONRELOC) { /* expression already has a register? */
+ if (!hasjumps(e)) /* no jumps? */
+ return e->u.info; /* result is already in a register */
+ if (e->u.info >= luaY_nvarstack(fs)) { /* reg. is not a local? */
+ exp2reg(fs, e, e->u.info); /* put final result in it */
+ return e->u.info;
+ }
+ /* else expression has jumps and cannot change its register
+ to hold the jump values, because it is a local variable.
+ Go through to the default case. */
+ }
+ luaK_exp2nextreg(fs, e); /* default: use next available register */
+ return e->u.info;
+}
+
+
+/*
+** Ensures final expression result is either in a register
+** or in an upvalue.
+*/
+void luaK_exp2anyregup (FuncState *fs, expdesc *e) {
+ if (e->k != VUPVAL || hasjumps(e))
+ luaK_exp2anyreg(fs, e);
+}
+
+
+/*
+** Ensures final expression result is either in a register
+** or it is a constant.
+*/
+void luaK_exp2val (FuncState *fs, expdesc *e) {
+ if (hasjumps(e))
+ luaK_exp2anyreg(fs, e);
+ else
+ luaK_dischargevars(fs, e);
+}
+
+
+/*
+** Try to make 'e' a K expression with an index in the range of R/K
+** indices. Return true iff succeeded.
+*/
+static int luaK_exp2K (FuncState *fs, expdesc *e) {
+ if (!hasjumps(e)) {
+ int info;
+ switch (e->k) { /* move constants to 'k' */
+ case VTRUE: info = boolT(fs); break;
+ case VFALSE: info = boolF(fs); break;
+ case VNIL: info = nilK(fs); break;
+ case VKINT: info = luaK_intK(fs, e->u.ival); break;
+ case VKFLT: info = luaK_numberK(fs, e->u.nval); break;
+ case VKSTR: info = stringK(fs, e->u.strval); break;
+ case VK: info = e->u.info; break;
+ default: return 0; /* not a constant */
+ }
+ if (info <= MAXINDEXRK) { /* does constant fit in 'argC'? */
+ e->k = VK; /* make expression a 'K' expression */
+ e->u.info = info;
+ return 1;
+ }
+ }
+ /* else, expression doesn't fit; leave it unchanged */
+ return 0;
+}
+
+
+/*
+** Ensures final expression result is in a valid R/K index
+** (that is, it is either in a register or in 'k' with an index
+** in the range of R/K indices).
+** Returns 1 iff expression is K.
+*/
+int luaK_exp2RK (FuncState *fs, expdesc *e) {
+ if (luaK_exp2K(fs, e))
+ return 1;
+ else { /* not a constant in the right range: put it in a register */
+ luaK_exp2anyreg(fs, e);
+ return 0;
+ }
+}
+
+
+static void codeABRK (FuncState *fs, OpCode o, int a, int b,
+ expdesc *ec) {
+ int k = luaK_exp2RK(fs, ec);
+ luaK_codeABCk(fs, o, a, b, ec->u.info, k);
+}
+
+
+/*
+** Generate code to store result of expression 'ex' into variable 'var'.
+*/
+void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
+ switch (var->k) {
+ case VLOCAL: {
+ freeexp(fs, ex);
+ exp2reg(fs, ex, var->u.var.ridx); /* compute 'ex' into proper place */
+ return;
+ }
+ case VUPVAL: {
+ int e = luaK_exp2anyreg(fs, ex);
+ luaK_codeABC(fs, OP_SETUPVAL, e, var->u.info, 0);
+ break;
+ }
+ case VINDEXUP: {
+ codeABRK(fs, OP_SETTABUP, var->u.ind.t, var->u.ind.idx, ex);
+ break;
+ }
+ case VINDEXI: {
+ codeABRK(fs, OP_SETI, var->u.ind.t, var->u.ind.idx, ex);
+ break;
+ }
+ case VINDEXSTR: {
+ codeABRK(fs, OP_SETFIELD, var->u.ind.t, var->u.ind.idx, ex);
+ break;
+ }
+ case VINDEXED: {
+ codeABRK(fs, OP_SETTABLE, var->u.ind.t, var->u.ind.idx, ex);
+ break;
+ }
+ default: lua_assert(0); /* invalid var kind to store */
+ }
+ freeexp(fs, ex);
+}
+
+
+/*
+** Emit SELF instruction (convert expression 'e' into 'e:key(e,').
+*/
+void luaK_self (FuncState *fs, expdesc *e, expdesc *key) {
+ int ereg;
+ luaK_exp2anyreg(fs, e);
+ ereg = e->u.info; /* register where 'e' was placed */
+ freeexp(fs, e);
+ e->u.info = fs->freereg; /* base register for op_self */
+ e->k = VNONRELOC; /* self expression has a fixed register */
+ luaK_reserveregs(fs, 2); /* function and 'self' produced by op_self */
+ codeABRK(fs, OP_SELF, e->u.info, ereg, key);
+ freeexp(fs, key);
+}
+
+
+/*
+** Negate condition 'e' (where 'e' is a comparison).
+*/
+static void negatecondition (FuncState *fs, expdesc *e) {
+ Instruction *pc = getjumpcontrol(fs, e->u.info);
+ lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET &&
+ GET_OPCODE(*pc) != OP_TEST);
+ SETARG_k(*pc, (GETARG_k(*pc) ^ 1));
+}
+
+
+/*
+** Emit instruction to jump if 'e' is 'cond' (that is, if 'cond'
+** is true, code will jump if 'e' is true.) Return jump position.
+** Optimize when 'e' is 'not' something, inverting the condition
+** and removing the 'not'.
+*/
+static int jumponcond (FuncState *fs, expdesc *e, int cond) {
+ if (e->k == VRELOC) {
+ Instruction ie = getinstruction(fs, e);
+ if (GET_OPCODE(ie) == OP_NOT) {
+ removelastinstruction(fs); /* remove previous OP_NOT */
+ return condjump(fs, OP_TEST, GETARG_B(ie), 0, 0, !cond);
+ }
+ /* else go through */
+ }
+ discharge2anyreg(fs, e);
+ freeexp(fs, e);
+ return condjump(fs, OP_TESTSET, NO_REG, e->u.info, 0, cond);
+}
+
+
+/*
+** Emit code to go through if 'e' is true, jump otherwise.
+*/
+void luaK_goiftrue (FuncState *fs, expdesc *e) {
+ int pc; /* pc of new jump */
+ luaK_dischargevars(fs, e);
+ switch (e->k) {
+ case VJMP: { /* condition? */
+ negatecondition(fs, e); /* jump when it is false */
+ pc = e->u.info; /* save jump position */
+ break;
+ }
+ case VK: case VKFLT: case VKINT: case VKSTR: case VTRUE: {
+ pc = NO_JUMP; /* always true; do nothing */
+ break;
+ }
+ default: {
+ pc = jumponcond(fs, e, 0); /* jump when false */
+ break;
+ }
+ }
+ luaK_concat(fs, &e->f, pc); /* insert new jump in false list */
+ luaK_patchtohere(fs, e->t); /* true list jumps to here (to go through) */
+ e->t = NO_JUMP;
+}
+
+
+/*
+** Emit code to go through if 'e' is false, jump otherwise.
+*/
+void luaK_goiffalse (FuncState *fs, expdesc *e) {
+ int pc; /* pc of new jump */
+ luaK_dischargevars(fs, e);
+ switch (e->k) {
+ case VJMP: {
+ pc = e->u.info; /* already jump if true */
+ break;
+ }
+ case VNIL: case VFALSE: {
+ pc = NO_JUMP; /* always false; do nothing */
+ break;
+ }
+ default: {
+ pc = jumponcond(fs, e, 1); /* jump if true */
+ break;
+ }
+ }
+ luaK_concat(fs, &e->t, pc); /* insert new jump in 't' list */
+ luaK_patchtohere(fs, e->f); /* false list jumps to here (to go through) */
+ e->f = NO_JUMP;
+}
+
+
+/*
+** Code 'not e', doing constant folding.
+*/
+static void codenot (FuncState *fs, expdesc *e) {
+ switch (e->k) {
+ case VNIL: case VFALSE: {
+ e->k = VTRUE; /* true == not nil == not false */
+ break;
+ }
+ case VK: case VKFLT: case VKINT: case VKSTR: case VTRUE: {
+ e->k = VFALSE; /* false == not "x" == not 0.5 == not 1 == not true */
+ break;
+ }
+ case VJMP: {
+ negatecondition(fs, e);
+ break;
+ }
+ case VRELOC:
+ case VNONRELOC: {
+ discharge2anyreg(fs, e);
+ freeexp(fs, e);
+ e->u.info = luaK_codeABC(fs, OP_NOT, 0, e->u.info, 0);
+ e->k = VRELOC;
+ break;
+ }
+ default: lua_assert(0); /* cannot happen */
+ }
+ /* interchange true and false lists */
+ { int temp = e->f; e->f = e->t; e->t = temp; }
+ removevalues(fs, e->f); /* values are useless when negated */
+ removevalues(fs, e->t);
+}
+
+
+/*
+** Check whether expression 'e' is a small literal string
+*/
+static int isKstr (FuncState *fs, expdesc *e) {
+ return (e->k == VK && !hasjumps(e) && e->u.info <= MAXARG_B &&
+ ttisshrstring(&fs->f->k[e->u.info]));
+}
+
+/*
+** Check whether expression 'e' is a literal integer.
+*/
+int luaK_isKint (expdesc *e) {
+ return (e->k == VKINT && !hasjumps(e));
+}
+
+
+/*
+** Check whether expression 'e' is a literal integer in
+** proper range to fit in register C
+*/
+static int isCint (expdesc *e) {
+ return luaK_isKint(e) && (l_castS2U(e->u.ival) <= l_castS2U(MAXARG_C));
+}
+
+
+/*
+** Check whether expression 'e' is a literal integer in
+** proper range to fit in register sC
+*/
+static int isSCint (expdesc *e) {
+ return luaK_isKint(e) && fitsC(e->u.ival);
+}
+
+
+/*
+** Check whether expression 'e' is a literal integer or float in
+** proper range to fit in a register (sB or sC).
+*/
+static int isSCnumber (expdesc *e, int *pi, int *isfloat) {
+ lua_Integer i;
+ if (e->k == VKINT)
+ i = e->u.ival;
+ else if (e->k == VKFLT && luaV_flttointeger(e->u.nval, &i, F2Ieq))
+ *isfloat = 1;
+ else
+ return 0; /* not a number */
+ if (!hasjumps(e) && fitsC(i)) {
+ *pi = int2sC(cast_int(i));
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+/*
+** Create expression 't[k]'. 't' must have its final result already in a
+** register or upvalue. Upvalues can only be indexed by literal strings.
+** Keys can be literal strings in the constant table or arbitrary
+** values in registers.
+*/
+void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
+ if (k->k == VKSTR)
+ str2K(fs, k);
+ lua_assert(!hasjumps(t) &&
+ (t->k == VLOCAL || t->k == VNONRELOC || t->k == VUPVAL));
+ if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */
+ luaK_exp2anyreg(fs, t); /* put it in a register */
+ if (t->k == VUPVAL) {
+ t->u.ind.t = t->u.info; /* upvalue index */
+ t->u.ind.idx = k->u.info; /* literal string */
+ t->k = VINDEXUP;
+ }
+ else {
+ /* register index of the table */
+ t->u.ind.t = (t->k == VLOCAL) ? t->u.var.ridx: t->u.info;
+ if (isKstr(fs, k)) {
+ t->u.ind.idx = k->u.info; /* literal string */
+ t->k = VINDEXSTR;
+ }
+ else if (isCint(k)) {
+ t->u.ind.idx = cast_int(k->u.ival); /* int. constant in proper range */
+ t->k = VINDEXI;
+ }
+ else {
+ t->u.ind.idx = luaK_exp2anyreg(fs, k); /* register */
+ t->k = VINDEXED;
+ }
+ }
+}
+
+
+/*
+** Return false if folding can raise an error.
+** Bitwise operations need operands convertible to integers; division
+** operations cannot have 0 as divisor.
+*/
+static int validop (int op, TValue *v1, TValue *v2) {
+ switch (op) {
+ case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR:
+ case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* conversion errors */
+ lua_Integer i;
+ return (luaV_tointegerns(v1, &i, LUA_FLOORN2I) &&
+ luaV_tointegerns(v2, &i, LUA_FLOORN2I));
+ }
+ case LUA_OPDIV: case LUA_OPIDIV: case LUA_OPMOD: /* division by 0 */
+ return (nvalue(v2) != 0);
+ default: return 1; /* everything else is valid */
+ }
+}
+
+
+/*
+** Try to "constant-fold" an operation; return 1 iff successful.
+** (In this case, 'e1' has the final result.)
+*/
+static int constfolding (FuncState *fs, int op, expdesc *e1,
+ const expdesc *e2) {
+ TValue v1, v2, res;
+ if (!tonumeral(e1, &v1) || !tonumeral(e2, &v2) || !validop(op, &v1, &v2))
+ return 0; /* non-numeric operands or not safe to fold */
+ luaO_rawarith(fs->ls->L, op, &v1, &v2, &res); /* does operation */
+ if (ttisinteger(&res)) {
+ e1->k = VKINT;
+ e1->u.ival = ivalue(&res);
+ }
+ else { /* folds neither NaN nor 0.0 (to avoid problems with -0.0) */
+ lua_Number n = fltvalue(&res);
+ if (luai_numisnan(n) || n == 0)
+ return 0;
+ e1->k = VKFLT;
+ e1->u.nval = n;
+ }
+ return 1;
+}
+
+
+/*
+** Convert a BinOpr to an OpCode (ORDER OPR - ORDER OP)
+*/
+l_sinline OpCode binopr2op (BinOpr opr, BinOpr baser, OpCode base) {
+ lua_assert(baser <= opr &&
+ ((baser == OPR_ADD && opr <= OPR_SHR) ||
+ (baser == OPR_LT && opr <= OPR_LE)));
+ return cast(OpCode, (cast_int(opr) - cast_int(baser)) + cast_int(base));
+}
+
+
+/*
+** Convert a UnOpr to an OpCode (ORDER OPR - ORDER OP)
+*/
+l_sinline OpCode unopr2op (UnOpr opr) {
+ return cast(OpCode, (cast_int(opr) - cast_int(OPR_MINUS)) +
+ cast_int(OP_UNM));
+}
+
+
+/*
+** Convert a BinOpr to a tag method (ORDER OPR - ORDER TM)
+*/
+l_sinline TMS binopr2TM (BinOpr opr) {
+ lua_assert(OPR_ADD <= opr && opr <= OPR_SHR);
+ return cast(TMS, (cast_int(opr) - cast_int(OPR_ADD)) + cast_int(TM_ADD));
+}
+
+
+/*
+** Emit code for unary expressions that "produce values"
+** (everything but 'not').
+** Expression to produce final result will be encoded in 'e'.
+*/
+static void codeunexpval (FuncState *fs, OpCode op, expdesc *e, int line) {
+ int r = luaK_exp2anyreg(fs, e); /* opcodes operate only on registers */
+ freeexp(fs, e);
+ e->u.info = luaK_codeABC(fs, op, 0, r, 0); /* generate opcode */
+ e->k = VRELOC; /* all those operations are relocatable */
+ luaK_fixline(fs, line);
+}
+
+
+/*
+** Emit code for binary expressions that "produce values"
+** (everything but logical operators 'and'/'or' and comparison
+** operators).
+** Expression to produce final result will be encoded in 'e1'.
+*/
+static void finishbinexpval (FuncState *fs, expdesc *e1, expdesc *e2,
+ OpCode op, int v2, int flip, int line,
+ OpCode mmop, TMS event) {
+ int v1 = luaK_exp2anyreg(fs, e1);
+ int pc = luaK_codeABCk(fs, op, 0, v1, v2, 0);
+ freeexps(fs, e1, e2);
+ e1->u.info = pc;
+ e1->k = VRELOC; /* all those operations are relocatable */
+ luaK_fixline(fs, line);
+ luaK_codeABCk(fs, mmop, v1, v2, event, flip); /* to call metamethod */
+ luaK_fixline(fs, line);
+}
+
+
+/*
+** Emit code for binary expressions that "produce values" over
+** two registers.
+*/
+static void codebinexpval (FuncState *fs, BinOpr opr,
+ expdesc *e1, expdesc *e2, int line) {
+ OpCode op = binopr2op(opr, OPR_ADD, OP_ADD);
+ int v2 = luaK_exp2anyreg(fs, e2); /* make sure 'e2' is in a register */
+ /* 'e1' must be already in a register or it is a constant */
+ lua_assert((VNIL <= e1->k && e1->k <= VKSTR) ||
+ e1->k == VNONRELOC || e1->k == VRELOC);
+ lua_assert(OP_ADD <= op && op <= OP_SHR);
+ finishbinexpval(fs, e1, e2, op, v2, 0, line, OP_MMBIN, binopr2TM(opr));
+}
+
+
+/*
+** Code binary operators with immediate operands.
+*/
+static void codebini (FuncState *fs, OpCode op,
+ expdesc *e1, expdesc *e2, int flip, int line,
+ TMS event) {
+ int v2 = int2sC(cast_int(e2->u.ival)); /* immediate operand */
+ lua_assert(e2->k == VKINT);
+ finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINI, event);
+}
+
+
+/*
+** Code binary operators with K operand.
+*/
+static void codebinK (FuncState *fs, BinOpr opr,
+ expdesc *e1, expdesc *e2, int flip, int line) {
+ TMS event = binopr2TM(opr);
+ int v2 = e2->u.info; /* K index */
+ OpCode op = binopr2op(opr, OPR_ADD, OP_ADDK);
+ finishbinexpval(fs, e1, e2, op, v2, flip, line, OP_MMBINK, event);
+}
+
+
+/* Try to code a binary operator negating its second operand.
+** For the metamethod, 2nd operand must keep its original value.
+*/
+static int finishbinexpneg (FuncState *fs, expdesc *e1, expdesc *e2,
+ OpCode op, int line, TMS event) {
+ if (!luaK_isKint(e2))
+ return 0; /* not an integer constant */
+ else {
+ lua_Integer i2 = e2->u.ival;
+ if (!(fitsC(i2) && fitsC(-i2)))
+ return 0; /* not in the proper range */
+ else { /* operating a small integer constant */
+ int v2 = cast_int(i2);
+ finishbinexpval(fs, e1, e2, op, int2sC(-v2), 0, line, OP_MMBINI, event);
+ /* correct metamethod argument */
+ SETARG_B(fs->f->code[fs->pc - 1], int2sC(v2));
+ return 1; /* successfully coded */
+ }
+ }
+}
+
+
+static void swapexps (expdesc *e1, expdesc *e2) {
+ expdesc temp = *e1; *e1 = *e2; *e2 = temp; /* swap 'e1' and 'e2' */
+}
+
+
+/*
+** Code binary operators with no constant operand.
+*/
+static void codebinNoK (FuncState *fs, BinOpr opr,
+ expdesc *e1, expdesc *e2, int flip, int line) {
+ if (flip)
+ swapexps(e1, e2); /* back to original order */
+ codebinexpval(fs, opr, e1, e2, line); /* use standard operators */
+}
+
+
+/*
+** Code arithmetic operators ('+', '-', ...). If second operand is a
+** constant in the proper range, use variant opcodes with K operands.
+*/
+static void codearith (FuncState *fs, BinOpr opr,
+ expdesc *e1, expdesc *e2, int flip, int line) {
+ if (tonumeral(e2, NULL) && luaK_exp2K(fs, e2)) /* K operand? */
+ codebinK(fs, opr, e1, e2, flip, line);
+ else /* 'e2' is neither an immediate nor a K operand */
+ codebinNoK(fs, opr, e1, e2, flip, line);
+}
+
+
+/*
+** Code commutative operators ('+', '*'). If first operand is a
+** numeric constant, change order of operands to try to use an
+** immediate or K operator.
+*/
+static void codecommutative (FuncState *fs, BinOpr op,
+ expdesc *e1, expdesc *e2, int line) {
+ int flip = 0;
+ if (tonumeral(e1, NULL)) { /* is first operand a numeric constant? */
+ swapexps(e1, e2); /* change order */
+ flip = 1;
+ }
+ if (op == OPR_ADD && isSCint(e2)) /* immediate operand? */
+ codebini(fs, OP_ADDI, e1, e2, flip, line, TM_ADD);
+ else
+ codearith(fs, op, e1, e2, flip, line);
+}
+
+
+/*
+** Code bitwise operations; they are all commutative, so the function
+** tries to put an integer constant as the 2nd operand (a K operand).
+*/
+static void codebitwise (FuncState *fs, BinOpr opr,
+ expdesc *e1, expdesc *e2, int line) {
+ int flip = 0;
+ if (e1->k == VKINT) {
+ swapexps(e1, e2); /* 'e2' will be the constant operand */
+ flip = 1;
+ }
+ if (e2->k == VKINT && luaK_exp2K(fs, e2)) /* K operand? */
+ codebinK(fs, opr, e1, e2, flip, line);
+ else /* no constants */
+ codebinNoK(fs, opr, e1, e2, flip, line);
+}
+
+
+/*
+** Emit code for order comparisons. When using an immediate operand,
+** 'isfloat' tells whether the original value was a float.
+*/
+static void codeorder (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
+ int r1, r2;
+ int im;
+ int isfloat = 0;
+ OpCode op;
+ if (isSCnumber(e2, &im, &isfloat)) {
+ /* use immediate operand */
+ r1 = luaK_exp2anyreg(fs, e1);
+ r2 = im;
+ op = binopr2op(opr, OPR_LT, OP_LTI);
+ }
+ else if (isSCnumber(e1, &im, &isfloat)) {
+ /* transform (A < B) to (B > A) and (A <= B) to (B >= A) */
+ r1 = luaK_exp2anyreg(fs, e2);
+ r2 = im;
+ op = binopr2op(opr, OPR_LT, OP_GTI);
+ }
+ else { /* regular case, compare two registers */
+ r1 = luaK_exp2anyreg(fs, e1);
+ r2 = luaK_exp2anyreg(fs, e2);
+ op = binopr2op(opr, OPR_LT, OP_LT);
+ }
+ freeexps(fs, e1, e2);
+ e1->u.info = condjump(fs, op, r1, r2, isfloat, 1);
+ e1->k = VJMP;
+}
+
+
+/*
+** Emit code for equality comparisons ('==', '~=').
+** 'e1' was already put as RK by 'luaK_infix'.
+*/
+static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
+ int r1, r2;
+ int im;
+ int isfloat = 0; /* not needed here, but kept for symmetry */
+ OpCode op;
+ if (e1->k != VNONRELOC) {
+ lua_assert(e1->k == VK || e1->k == VKINT || e1->k == VKFLT);
+ swapexps(e1, e2);
+ }
+ r1 = luaK_exp2anyreg(fs, e1); /* 1st expression must be in register */
+ if (isSCnumber(e2, &im, &isfloat)) {
+ op = OP_EQI;
+ r2 = im; /* immediate operand */
+ }
+ else if (luaK_exp2RK(fs, e2)) { /* 2nd expression is constant? */
+ op = OP_EQK;
+ r2 = e2->u.info; /* constant index */
+ }
+ else {
+ op = OP_EQ; /* will compare two registers */
+ r2 = luaK_exp2anyreg(fs, e2);
+ }
+ freeexps(fs, e1, e2);
+ e1->u.info = condjump(fs, op, r1, r2, isfloat, (opr == OPR_EQ));
+ e1->k = VJMP;
+}
+
+
+/*
+** Apply prefix operation 'op' to expression 'e'.
+*/
+void luaK_prefix (FuncState *fs, UnOpr opr, expdesc *e, int line) {
+ static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP};
+ luaK_dischargevars(fs, e);
+ switch (opr) {
+ case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */
+ if (constfolding(fs, opr + LUA_OPUNM, e, &ef))
+ break;
+ /* else */ /* FALLTHROUGH */
+ case OPR_LEN:
+ codeunexpval(fs, unopr2op(opr), e, line);
+ break;
+ case OPR_NOT: codenot(fs, e); break;
+ default: lua_assert(0);
+ }
+}
+
+
+/*
+** Process 1st operand 'v' of binary operation 'op' before reading
+** 2nd operand.
+*/
+void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
+ luaK_dischargevars(fs, v);
+ switch (op) {
+ case OPR_AND: {
+ luaK_goiftrue(fs, v); /* go ahead only if 'v' is true */
+ break;
+ }
+ case OPR_OR: {
+ luaK_goiffalse(fs, v); /* go ahead only if 'v' is false */
+ break;
+ }
+ case OPR_CONCAT: {
+ luaK_exp2nextreg(fs, v); /* operand must be on the stack */
+ break;
+ }
+ case OPR_ADD: case OPR_SUB:
+ case OPR_MUL: case OPR_DIV: case OPR_IDIV:
+ case OPR_MOD: case OPR_POW:
+ case OPR_BAND: case OPR_BOR: case OPR_BXOR:
+ case OPR_SHL: case OPR_SHR: {
+ if (!tonumeral(v, NULL))
+ luaK_exp2anyreg(fs, v);
+ /* else keep numeral, which may be folded or used as an immediate
+ operand */
+ break;
+ }
+ case OPR_EQ: case OPR_NE: {
+ if (!tonumeral(v, NULL))
+ luaK_exp2RK(fs, v);
+ /* else keep numeral, which may be an immediate operand */
+ break;
+ }
+ case OPR_LT: case OPR_LE:
+ case OPR_GT: case OPR_GE: {
+ int dummy, dummy2;
+ if (!isSCnumber(v, &dummy, &dummy2))
+ luaK_exp2anyreg(fs, v);
+ /* else keep numeral, which may be an immediate operand */
+ break;
+ }
+ default: lua_assert(0);
+ }
+}
+
+/*
+** Create code for '(e1 .. e2)'.
+** For '(e1 .. e2.1 .. e2.2)' (which is '(e1 .. (e2.1 .. e2.2))',
+** because concatenation is right associative), merge both CONCATs.
+*/
+static void codeconcat (FuncState *fs, expdesc *e1, expdesc *e2, int line) {
+ Instruction *ie2 = previousinstruction(fs);
+ if (GET_OPCODE(*ie2) == OP_CONCAT) { /* is 'e2' a concatenation? */
+ int n = GETARG_B(*ie2); /* # of elements concatenated in 'e2' */
+ lua_assert(e1->u.info + 1 == GETARG_A(*ie2));
+ freeexp(fs, e2);
+ SETARG_A(*ie2, e1->u.info); /* correct first element ('e1') */
+ SETARG_B(*ie2, n + 1); /* will concatenate one more element */
+ }
+ else { /* 'e2' is not a concatenation */
+ luaK_codeABC(fs, OP_CONCAT, e1->u.info, 2, 0); /* new concat opcode */
+ freeexp(fs, e2);
+ luaK_fixline(fs, line);
+ }
+}
+
+
+/*
+** Finalize code for binary operation, after reading 2nd operand.
+*/
+void luaK_posfix (FuncState *fs, BinOpr opr,
+ expdesc *e1, expdesc *e2, int line) {
+ luaK_dischargevars(fs, e2);
+ if (foldbinop(opr) && constfolding(fs, opr + LUA_OPADD, e1, e2))
+ return; /* done by folding */
+ switch (opr) {
+ case OPR_AND: {
+ lua_assert(e1->t == NO_JUMP); /* list closed by 'luaK_infix' */
+ luaK_concat(fs, &e2->f, e1->f);
+ *e1 = *e2;
+ break;
+ }
+ case OPR_OR: {
+ lua_assert(e1->f == NO_JUMP); /* list closed by 'luaK_infix' */
+ luaK_concat(fs, &e2->t, e1->t);
+ *e1 = *e2;
+ break;
+ }
+ case OPR_CONCAT: { /* e1 .. e2 */
+ luaK_exp2nextreg(fs, e2);
+ codeconcat(fs, e1, e2, line);
+ break;
+ }
+ case OPR_ADD: case OPR_MUL: {
+ codecommutative(fs, opr, e1, e2, line);
+ break;
+ }
+ case OPR_SUB: {
+ if (finishbinexpneg(fs, e1, e2, OP_ADDI, line, TM_SUB))
+ break; /* coded as (r1 + -I) */
+ /* ELSE */
+ } /* FALLTHROUGH */
+ case OPR_DIV: case OPR_IDIV: case OPR_MOD: case OPR_POW: {
+ codearith(fs, opr, e1, e2, 0, line);
+ break;
+ }
+ case OPR_BAND: case OPR_BOR: case OPR_BXOR: {
+ codebitwise(fs, opr, e1, e2, line);
+ break;
+ }
+ case OPR_SHL: {
+ if (isSCint(e1)) {
+ swapexps(e1, e2);
+ codebini(fs, OP_SHLI, e1, e2, 1, line, TM_SHL); /* I << r2 */
+ }
+ else if (finishbinexpneg(fs, e1, e2, OP_SHRI, line, TM_SHL)) {
+ /* coded as (r1 >> -I) */;
+ }
+ else /* regular case (two registers) */
+ codebinexpval(fs, opr, e1, e2, line);
+ break;
+ }
+ case OPR_SHR: {
+ if (isSCint(e2))
+ codebini(fs, OP_SHRI, e1, e2, 0, line, TM_SHR); /* r1 >> I */
+ else /* regular case (two registers) */
+ codebinexpval(fs, opr, e1, e2, line);
+ break;
+ }
+ case OPR_EQ: case OPR_NE: {
+ codeeq(fs, opr, e1, e2);
+ break;
+ }
+ case OPR_GT: case OPR_GE: {
+ /* '(a > b)' <=> '(b < a)'; '(a >= b)' <=> '(b <= a)' */
+ swapexps(e1, e2);
+ opr = cast(BinOpr, (opr - OPR_GT) + OPR_LT);
+ } /* FALLTHROUGH */
+ case OPR_LT: case OPR_LE: {
+ codeorder(fs, opr, e1, e2);
+ break;
+ }
+ default: lua_assert(0);
+ }
+}
+
+
+/*
+** Change line information associated with current position, by removing
+** previous info and adding it again with new line.
+*/
+void luaK_fixline (FuncState *fs, int line) {
+ removelastlineinfo(fs);
+ savelineinfo(fs, fs->f, line);
+}
+
+
+void luaK_settablesize (FuncState *fs, int pc, int ra, int asize, int hsize) {
+ Instruction *inst = &fs->f->code[pc];
+ int rb = (hsize != 0) ? luaO_ceillog2(hsize) + 1 : 0; /* hash size */
+ int extra = asize / (MAXARG_C + 1); /* higher bits of array size */
+ int rc = asize % (MAXARG_C + 1); /* lower bits of array size */
+ int k = (extra > 0); /* true iff needs extra argument */
+ *inst = CREATE_ABCk(OP_NEWTABLE, ra, rb, rc, k);
+ *(inst + 1) = CREATE_Ax(OP_EXTRAARG, extra);
+}
+
+
+/*
+** Emit a SETLIST instruction.
+** 'base' is register that keeps table;
+** 'nelems' is #table plus those to be stored now;
+** 'tostore' is number of values (in registers 'base + 1',...) to add to
+** table (or LUA_MULTRET to add up to stack top).
+*/
+void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) {
+ lua_assert(tostore != 0 && tostore <= LFIELDS_PER_FLUSH);
+ if (tostore == LUA_MULTRET)
+ tostore = 0;
+ if (nelems <= MAXARG_C)
+ luaK_codeABC(fs, OP_SETLIST, base, tostore, nelems);
+ else {
+ int extra = nelems / (MAXARG_C + 1);
+ nelems %= (MAXARG_C + 1);
+ luaK_codeABCk(fs, OP_SETLIST, base, tostore, nelems, 1);
+ codeextraarg(fs, extra);
+ }
+ fs->freereg = base + 1; /* free registers with list values */
+}
+
+
+/*
+** return the final target of a jump (skipping jumps to jumps)
+*/
+static int finaltarget (Instruction *code, int i) {
+ int count;
+ for (count = 0; count < 100; count++) { /* avoid infinite loops */
+ Instruction pc = code[i];
+ if (GET_OPCODE(pc) != OP_JMP)
+ break;
+ else
+ i += GETARG_sJ(pc) + 1;
+ }
+ return i;
+}
+
+
+/*
+** Do a final pass over the code of a function, doing small peephole
+** optimizations and adjustments.
+*/
+void luaK_finish (FuncState *fs) {
+ int i;
+ Proto *p = fs->f;
+ for (i = 0; i < fs->pc; i++) {
+ Instruction *pc = &p->code[i];
+ lua_assert(i == 0 || isOT(*(pc - 1)) == isIT(*pc));
+ switch (GET_OPCODE(*pc)) {
+ case OP_RETURN0: case OP_RETURN1: {
+ if (!(fs->needclose || p->is_vararg))
+ break; /* no extra work */
+ /* else use OP_RETURN to do the extra work */
+ SET_OPCODE(*pc, OP_RETURN);
+ } /* FALLTHROUGH */
+ case OP_RETURN: case OP_TAILCALL: {
+ if (fs->needclose)
+ SETARG_k(*pc, 1); /* signal that it needs to close */
+ if (p->is_vararg)
+ SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */
+ break;
+ }
+ case OP_JMP: {
+ int target = finaltarget(p->code, i);
+ fixjump(fs, i, target);
+ break;
+ }
+ default: break;
+ }
+ }
+}
diff --git a/src/libs/3rdparty/lua/src/lcode.h b/src/libs/3rdparty/lua/src/lcode.h
new file mode 100644
index 0000000000..3265824452
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lcode.h
@@ -0,0 +1,104 @@
+/*
+** $Id: lcode.h $
+** Code generator for Lua
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lcode_h
+#define lcode_h
+
+#include "llex.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+
+
+/*
+** Marks the end of a patch list. It is an invalid value both as an absolute
+** address, and as a list link (would link an element to itself).
+*/
+#define NO_JUMP (-1)
+
+
+/*
+** grep "ORDER OPR" if you change these enums (ORDER OP)
+*/
+typedef enum BinOpr {
+ /* arithmetic operators */
+ OPR_ADD, OPR_SUB, OPR_MUL, OPR_MOD, OPR_POW,
+ OPR_DIV, OPR_IDIV,
+ /* bitwise operators */
+ OPR_BAND, OPR_BOR, OPR_BXOR,
+ OPR_SHL, OPR_SHR,
+ /* string operator */
+ OPR_CONCAT,
+ /* comparison operators */
+ OPR_EQ, OPR_LT, OPR_LE,
+ OPR_NE, OPR_GT, OPR_GE,
+ /* logical operators */
+ OPR_AND, OPR_OR,
+ OPR_NOBINOPR
+} BinOpr;
+
+
+/* true if operation is foldable (that is, it is arithmetic or bitwise) */
+#define foldbinop(op) ((op) <= OPR_SHR)
+
+
+#define luaK_codeABC(fs,o,a,b,c) luaK_codeABCk(fs,o,a,b,c,0)
+
+
+typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
+
+
+/* get (pointer to) instruction of given 'expdesc' */
+#define getinstruction(fs,e) ((fs)->f->code[(e)->u.info])
+
+
+#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET)
+
+#define luaK_jumpto(fs,t) luaK_patchlist(fs, luaK_jump(fs), t)
+
+LUAI_FUNC int luaK_code (FuncState *fs, Instruction i);
+LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx);
+LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx);
+LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A,
+ int B, int C, int k);
+LUAI_FUNC int luaK_isKint (expdesc *e);
+LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v);
+LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
+LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
+LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
+LUAI_FUNC void luaK_checkstack (FuncState *fs, int n);
+LUAI_FUNC void luaK_int (FuncState *fs, int reg, lua_Integer n);
+LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e);
+LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e);
+LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key);
+LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k);
+LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_goiffalse (FuncState *fs, expdesc *e);
+LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e);
+LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults);
+LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);
+LUAI_FUNC int luaK_jump (FuncState *fs);
+LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret);
+LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target);
+LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list);
+LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2);
+LUAI_FUNC int luaK_getlabel (FuncState *fs);
+LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line);
+LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v);
+LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1,
+ expdesc *v2, int line);
+LUAI_FUNC void luaK_settablesize (FuncState *fs, int pc,
+ int ra, int asize, int hsize);
+LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore);
+LUAI_FUNC void luaK_finish (FuncState *fs);
+LUAI_FUNC l_noret luaK_semerror (LexState *ls, const char *msg);
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lcorolib.c b/src/libs/3rdparty/lua/src/lcorolib.c
new file mode 100644
index 0000000000..c64adf08a8
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lcorolib.c
@@ -0,0 +1,210 @@
+/*
+** $Id: lcorolib.c $
+** Coroutine Library
+** See Copyright Notice in lua.h
+*/
+
+#define lcorolib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <stdlib.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+static lua_State *getco (lua_State *L) {
+ lua_State *co = lua_tothread(L, 1);
+ luaL_argexpected(L, co, 1, "thread");
+ return co;
+}
+
+
+/*
+** Resumes a coroutine. Returns the number of results for non-error
+** cases or -1 for errors.
+*/
+static int auxresume (lua_State *L, lua_State *co, int narg) {
+ int status, nres;
+ if (l_unlikely(!lua_checkstack(co, narg))) {
+ lua_pushliteral(L, "too many arguments to resume");
+ return -1; /* error flag */
+ }
+ lua_xmove(L, co, narg);
+ status = lua_resume(co, L, narg, &nres);
+ if (l_likely(status == LUA_OK || status == LUA_YIELD)) {
+ if (l_unlikely(!lua_checkstack(L, nres + 1))) {
+ lua_pop(co, nres); /* remove results anyway */
+ lua_pushliteral(L, "too many results to resume");
+ return -1; /* error flag */
+ }
+ lua_xmove(co, L, nres); /* move yielded values */
+ return nres;
+ }
+ else {
+ lua_xmove(co, L, 1); /* move error message */
+ return -1; /* error flag */
+ }
+}
+
+
+static int luaB_coresume (lua_State *L) {
+ lua_State *co = getco(L);
+ int r;
+ r = auxresume(L, co, lua_gettop(L) - 1);
+ if (l_unlikely(r < 0)) {
+ lua_pushboolean(L, 0);
+ lua_insert(L, -2);
+ return 2; /* return false + error message */
+ }
+ else {
+ lua_pushboolean(L, 1);
+ lua_insert(L, -(r + 1));
+ return r + 1; /* return true + 'resume' returns */
+ }
+}
+
+
+static int luaB_auxwrap (lua_State *L) {
+ lua_State *co = lua_tothread(L, lua_upvalueindex(1));
+ int r = auxresume(L, co, lua_gettop(L));
+ if (l_unlikely(r < 0)) { /* error? */
+ int stat = lua_status(co);
+ if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */
+ stat = lua_closethread(co, L); /* close its tbc variables */
+ lua_assert(stat != LUA_OK);
+ lua_xmove(co, L, 1); /* move error message to the caller */
+ }
+ if (stat != LUA_ERRMEM && /* not a memory error and ... */
+ lua_type(L, -1) == LUA_TSTRING) { /* ... error object is a string? */
+ luaL_where(L, 1); /* add extra info, if available */
+ lua_insert(L, -2);
+ lua_concat(L, 2);
+ }
+ return lua_error(L); /* propagate error */
+ }
+ return r;
+}
+
+
+static int luaB_cocreate (lua_State *L) {
+ lua_State *NL;
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ NL = lua_newthread(L);
+ lua_pushvalue(L, 1); /* move function to top */
+ lua_xmove(L, NL, 1); /* move function from L to NL */
+ return 1;
+}
+
+
+static int luaB_cowrap (lua_State *L) {
+ luaB_cocreate(L);
+ lua_pushcclosure(L, luaB_auxwrap, 1);
+ return 1;
+}
+
+
+static int luaB_yield (lua_State *L) {
+ return lua_yield(L, lua_gettop(L));
+}
+
+
+#define COS_RUN 0
+#define COS_DEAD 1
+#define COS_YIELD 2
+#define COS_NORM 3
+
+
+static const char *const statname[] =
+ {"running", "dead", "suspended", "normal"};
+
+
+static int auxstatus (lua_State *L, lua_State *co) {
+ if (L == co) return COS_RUN;
+ else {
+ switch (lua_status(co)) {
+ case LUA_YIELD:
+ return COS_YIELD;
+ case LUA_OK: {
+ lua_Debug ar;
+ if (lua_getstack(co, 0, &ar)) /* does it have frames? */
+ return COS_NORM; /* it is running */
+ else if (lua_gettop(co) == 0)
+ return COS_DEAD;
+ else
+ return COS_YIELD; /* initial state */
+ }
+ default: /* some error occurred */
+ return COS_DEAD;
+ }
+ }
+}
+
+
+static int luaB_costatus (lua_State *L) {
+ lua_State *co = getco(L);
+ lua_pushstring(L, statname[auxstatus(L, co)]);
+ return 1;
+}
+
+
+static int luaB_yieldable (lua_State *L) {
+ lua_State *co = lua_isnone(L, 1) ? L : getco(L);
+ lua_pushboolean(L, lua_isyieldable(co));
+ return 1;
+}
+
+
+static int luaB_corunning (lua_State *L) {
+ int ismain = lua_pushthread(L);
+ lua_pushboolean(L, ismain);
+ return 2;
+}
+
+
+static int luaB_close (lua_State *L) {
+ lua_State *co = getco(L);
+ int status = auxstatus(L, co);
+ switch (status) {
+ case COS_DEAD: case COS_YIELD: {
+ status = lua_closethread(co, L);
+ if (status == LUA_OK) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ else {
+ lua_pushboolean(L, 0);
+ lua_xmove(co, L, 1); /* move error message */
+ return 2;
+ }
+ }
+ default: /* normal or running coroutine */
+ return luaL_error(L, "cannot close a %s coroutine", statname[status]);
+ }
+}
+
+
+static const luaL_Reg co_funcs[] = {
+ {"create", luaB_cocreate},
+ {"resume", luaB_coresume},
+ {"running", luaB_corunning},
+ {"status", luaB_costatus},
+ {"wrap", luaB_cowrap},
+ {"yield", luaB_yield},
+ {"isyieldable", luaB_yieldable},
+ {"close", luaB_close},
+ {NULL, NULL}
+};
+
+
+
+LUAMOD_API int luaopen_coroutine (lua_State *L) {
+ luaL_newlib(L, co_funcs);
+ return 1;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lctype.c b/src/libs/3rdparty/lua/src/lctype.c
new file mode 100644
index 0000000000..9542280942
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lctype.c
@@ -0,0 +1,64 @@
+/*
+** $Id: lctype.c $
+** 'ctype' functions for Lua
+** See Copyright Notice in lua.h
+*/
+
+#define lctype_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include "lctype.h"
+
+#if !LUA_USE_CTYPE /* { */
+
+#include <limits.h>
+
+
+#if defined (LUA_UCID) /* accept UniCode IDentifiers? */
+/* consider all non-ascii codepoints to be alphabetic */
+#define NONA 0x01
+#else
+#define NONA 0x00 /* default */
+#endif
+
+
+LUAI_DDEF const lu_byte luai_ctype_[UCHAR_MAX + 2] = {
+ 0x00, /* EOZ */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0. */
+ 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 1. */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, /* 2. */
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, 0x16, /* 3. */
+ 0x16, 0x16, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 4. */
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 5. */
+ 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x05,
+ 0x04, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x05, /* 6. */
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, /* 7. */
+ 0x05, 0x05, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00,
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 8. */
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* 9. */
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* a. */
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* b. */
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
+ 0x00, 0x00, NONA, NONA, NONA, NONA, NONA, NONA, /* c. */
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* d. */
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA, /* e. */
+ NONA, NONA, NONA, NONA, NONA, NONA, NONA, NONA,
+ NONA, NONA, NONA, NONA, NONA, 0x00, 0x00, 0x00, /* f. */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+#endif /* } */
diff --git a/src/libs/3rdparty/lua/src/lctype.h b/src/libs/3rdparty/lua/src/lctype.h
new file mode 100644
index 0000000000..864e190188
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lctype.h
@@ -0,0 +1,101 @@
+/*
+** $Id: lctype.h $
+** 'ctype' functions for Lua
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lctype_h
+#define lctype_h
+
+#include "lua.h"
+
+
+/*
+** WARNING: the functions defined here do not necessarily correspond
+** to the similar functions in the standard C ctype.h. They are
+** optimized for the specific needs of Lua.
+*/
+
+#if !defined(LUA_USE_CTYPE)
+
+#if 'A' == 65 && '0' == 48
+/* ASCII case: can use its own tables; faster and fixed */
+#define LUA_USE_CTYPE 0
+#else
+/* must use standard C ctype */
+#define LUA_USE_CTYPE 1
+#endif
+
+#endif
+
+
+#if !LUA_USE_CTYPE /* { */
+
+#include <limits.h>
+
+#include "llimits.h"
+
+
+#define ALPHABIT 0
+#define DIGITBIT 1
+#define PRINTBIT 2
+#define SPACEBIT 3
+#define XDIGITBIT 4
+
+
+#define MASK(B) (1 << (B))
+
+
+/*
+** add 1 to char to allow index -1 (EOZ)
+*/
+#define testprop(c,p) (luai_ctype_[(c)+1] & (p))
+
+/*
+** 'lalpha' (Lua alphabetic) and 'lalnum' (Lua alphanumeric) both include '_'
+*/
+#define lislalpha(c) testprop(c, MASK(ALPHABIT))
+#define lislalnum(c) testprop(c, (MASK(ALPHABIT) | MASK(DIGITBIT)))
+#define lisdigit(c) testprop(c, MASK(DIGITBIT))
+#define lisspace(c) testprop(c, MASK(SPACEBIT))
+#define lisprint(c) testprop(c, MASK(PRINTBIT))
+#define lisxdigit(c) testprop(c, MASK(XDIGITBIT))
+
+
+/*
+** In ASCII, this 'ltolower' is correct for alphabetic characters and
+** for '.'. That is enough for Lua needs. ('check_exp' ensures that
+** the character either is an upper-case letter or is unchanged by
+** the transformation, which holds for lower-case letters and '.'.)
+*/
+#define ltolower(c) \
+ check_exp(('A' <= (c) && (c) <= 'Z') || (c) == ((c) | ('A' ^ 'a')), \
+ (c) | ('A' ^ 'a'))
+
+
+/* one entry for each character and for -1 (EOZ) */
+LUAI_DDEC(const lu_byte luai_ctype_[UCHAR_MAX + 2];)
+
+
+#else /* }{ */
+
+/*
+** use standard C ctypes
+*/
+
+#include <ctype.h>
+
+
+#define lislalpha(c) (isalpha(c) || (c) == '_')
+#define lislalnum(c) (isalnum(c) || (c) == '_')
+#define lisdigit(c) (isdigit(c))
+#define lisspace(c) (isspace(c))
+#define lisprint(c) (isprint(c))
+#define lisxdigit(c) (isxdigit(c))
+
+#define ltolower(c) (tolower(c))
+
+#endif /* } */
+
+#endif
+
diff --git a/src/libs/3rdparty/lua/src/ldblib.c b/src/libs/3rdparty/lua/src/ldblib.c
new file mode 100644
index 0000000000..6dcbaa9824
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ldblib.c
@@ -0,0 +1,483 @@
+/*
+** $Id: ldblib.c $
+** Interface from Lua to its debug API
+** See Copyright Notice in lua.h
+*/
+
+#define ldblib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+/*
+** The hook table at registry[HOOKKEY] maps threads to their current
+** hook function.
+*/
+static const char *const HOOKKEY = "_HOOKKEY";
+
+
+/*
+** If L1 != L, L1 can be in any state, and therefore there are no
+** guarantees about its stack space; any push in L1 must be
+** checked.
+*/
+static void checkstack (lua_State *L, lua_State *L1, int n) {
+ if (l_unlikely(L != L1 && !lua_checkstack(L1, n)))
+ luaL_error(L, "stack overflow");
+}
+
+
+static int db_getregistry (lua_State *L) {
+ lua_pushvalue(L, LUA_REGISTRYINDEX);
+ return 1;
+}
+
+
+static int db_getmetatable (lua_State *L) {
+ luaL_checkany(L, 1);
+ if (!lua_getmetatable(L, 1)) {
+ lua_pushnil(L); /* no metatable */
+ }
+ return 1;
+}
+
+
+static int db_setmetatable (lua_State *L) {
+ int t = lua_type(L, 2);
+ luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table");
+ lua_settop(L, 2);
+ lua_setmetatable(L, 1);
+ return 1; /* return 1st argument */
+}
+
+
+static int db_getuservalue (lua_State *L) {
+ int n = (int)luaL_optinteger(L, 2, 1);
+ if (lua_type(L, 1) != LUA_TUSERDATA)
+ luaL_pushfail(L);
+ else if (lua_getiuservalue(L, 1, n) != LUA_TNONE) {
+ lua_pushboolean(L, 1);
+ return 2;
+ }
+ return 1;
+}
+
+
+static int db_setuservalue (lua_State *L) {
+ int n = (int)luaL_optinteger(L, 3, 1);
+ luaL_checktype(L, 1, LUA_TUSERDATA);
+ luaL_checkany(L, 2);
+ lua_settop(L, 2);
+ if (!lua_setiuservalue(L, 1, n))
+ luaL_pushfail(L);
+ return 1;
+}
+
+
+/*
+** Auxiliary function used by several library functions: check for
+** an optional thread as function's first argument and set 'arg' with
+** 1 if this argument is present (so that functions can skip it to
+** access their other arguments)
+*/
+static lua_State *getthread (lua_State *L, int *arg) {
+ if (lua_isthread(L, 1)) {
+ *arg = 1;
+ return lua_tothread(L, 1);
+ }
+ else {
+ *arg = 0;
+ return L; /* function will operate over current thread */
+ }
+}
+
+
+/*
+** Variations of 'lua_settable', used by 'db_getinfo' to put results
+** from 'lua_getinfo' into result table. Key is always a string;
+** value can be a string, an int, or a boolean.
+*/
+static void settabss (lua_State *L, const char *k, const char *v) {
+ lua_pushstring(L, v);
+ lua_setfield(L, -2, k);
+}
+
+static void settabsi (lua_State *L, const char *k, int v) {
+ lua_pushinteger(L, v);
+ lua_setfield(L, -2, k);
+}
+
+static void settabsb (lua_State *L, const char *k, int v) {
+ lua_pushboolean(L, v);
+ lua_setfield(L, -2, k);
+}
+
+
+/*
+** In function 'db_getinfo', the call to 'lua_getinfo' may push
+** results on the stack; later it creates the result table to put
+** these objects. Function 'treatstackoption' puts the result from
+** 'lua_getinfo' on top of the result table so that it can call
+** 'lua_setfield'.
+*/
+static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) {
+ if (L == L1)
+ lua_rotate(L, -2, 1); /* exchange object and table */
+ else
+ lua_xmove(L1, L, 1); /* move object to the "main" stack */
+ lua_setfield(L, -2, fname); /* put object into table */
+}
+
+
+/*
+** Calls 'lua_getinfo' and collects all results in a new table.
+** L1 needs stack space for an optional input (function) plus
+** two optional outputs (function and line table) from function
+** 'lua_getinfo'.
+*/
+static int db_getinfo (lua_State *L) {
+ lua_Debug ar;
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ const char *options = luaL_optstring(L, arg+2, "flnSrtu");
+ checkstack(L, L1, 3);
+ luaL_argcheck(L, options[0] != '>', arg + 2, "invalid option '>'");
+ if (lua_isfunction(L, arg + 1)) { /* info about a function? */
+ options = lua_pushfstring(L, ">%s", options); /* add '>' to 'options' */
+ lua_pushvalue(L, arg + 1); /* move function to 'L1' stack */
+ lua_xmove(L, L1, 1);
+ }
+ else { /* stack level */
+ if (!lua_getstack(L1, (int)luaL_checkinteger(L, arg + 1), &ar)) {
+ luaL_pushfail(L); /* level out of range */
+ return 1;
+ }
+ }
+ if (!lua_getinfo(L1, options, &ar))
+ return luaL_argerror(L, arg+2, "invalid option");
+ lua_newtable(L); /* table to collect results */
+ if (strchr(options, 'S')) {
+ lua_pushlstring(L, ar.source, ar.srclen);
+ lua_setfield(L, -2, "source");
+ settabss(L, "short_src", ar.short_src);
+ settabsi(L, "linedefined", ar.linedefined);
+ settabsi(L, "lastlinedefined", ar.lastlinedefined);
+ settabss(L, "what", ar.what);
+ }
+ if (strchr(options, 'l'))
+ settabsi(L, "currentline", ar.currentline);
+ if (strchr(options, 'u')) {
+ settabsi(L, "nups", ar.nups);
+ settabsi(L, "nparams", ar.nparams);
+ settabsb(L, "isvararg", ar.isvararg);
+ }
+ if (strchr(options, 'n')) {
+ settabss(L, "name", ar.name);
+ settabss(L, "namewhat", ar.namewhat);
+ }
+ if (strchr(options, 'r')) {
+ settabsi(L, "ftransfer", ar.ftransfer);
+ settabsi(L, "ntransfer", ar.ntransfer);
+ }
+ if (strchr(options, 't'))
+ settabsb(L, "istailcall", ar.istailcall);
+ if (strchr(options, 'L'))
+ treatstackoption(L, L1, "activelines");
+ if (strchr(options, 'f'))
+ treatstackoption(L, L1, "func");
+ return 1; /* return table */
+}
+
+
+static int db_getlocal (lua_State *L) {
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ int nvar = (int)luaL_checkinteger(L, arg + 2); /* local-variable index */
+ if (lua_isfunction(L, arg + 1)) { /* function argument? */
+ lua_pushvalue(L, arg + 1); /* push function */
+ lua_pushstring(L, lua_getlocal(L, NULL, nvar)); /* push local name */
+ return 1; /* return only name (there is no value) */
+ }
+ else { /* stack-level argument */
+ lua_Debug ar;
+ const char *name;
+ int level = (int)luaL_checkinteger(L, arg + 1);
+ if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */
+ return luaL_argerror(L, arg+1, "level out of range");
+ checkstack(L, L1, 1);
+ name = lua_getlocal(L1, &ar, nvar);
+ if (name) {
+ lua_xmove(L1, L, 1); /* move local value */
+ lua_pushstring(L, name); /* push name */
+ lua_rotate(L, -2, 1); /* re-order */
+ return 2;
+ }
+ else {
+ luaL_pushfail(L); /* no name (nor value) */
+ return 1;
+ }
+ }
+}
+
+
+static int db_setlocal (lua_State *L) {
+ int arg;
+ const char *name;
+ lua_State *L1 = getthread(L, &arg);
+ lua_Debug ar;
+ int level = (int)luaL_checkinteger(L, arg + 1);
+ int nvar = (int)luaL_checkinteger(L, arg + 2);
+ if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */
+ return luaL_argerror(L, arg+1, "level out of range");
+ luaL_checkany(L, arg+3);
+ lua_settop(L, arg+3);
+ checkstack(L, L1, 1);
+ lua_xmove(L, L1, 1);
+ name = lua_setlocal(L1, &ar, nvar);
+ if (name == NULL)
+ lua_pop(L1, 1); /* pop value (if not popped by 'lua_setlocal') */
+ lua_pushstring(L, name);
+ return 1;
+}
+
+
+/*
+** get (if 'get' is true) or set an upvalue from a closure
+*/
+static int auxupvalue (lua_State *L, int get) {
+ const char *name;
+ int n = (int)luaL_checkinteger(L, 2); /* upvalue index */
+ luaL_checktype(L, 1, LUA_TFUNCTION); /* closure */
+ name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
+ if (name == NULL) return 0;
+ lua_pushstring(L, name);
+ lua_insert(L, -(get+1)); /* no-op if get is false */
+ return get + 1;
+}
+
+
+static int db_getupvalue (lua_State *L) {
+ return auxupvalue(L, 1);
+}
+
+
+static int db_setupvalue (lua_State *L) {
+ luaL_checkany(L, 3);
+ return auxupvalue(L, 0);
+}
+
+
+/*
+** Check whether a given upvalue from a given closure exists and
+** returns its index
+*/
+static void *checkupval (lua_State *L, int argf, int argnup, int *pnup) {
+ void *id;
+ int nup = (int)luaL_checkinteger(L, argnup); /* upvalue index */
+ luaL_checktype(L, argf, LUA_TFUNCTION); /* closure */
+ id = lua_upvalueid(L, argf, nup);
+ if (pnup) {
+ luaL_argcheck(L, id != NULL, argnup, "invalid upvalue index");
+ *pnup = nup;
+ }
+ return id;
+}
+
+
+static int db_upvalueid (lua_State *L) {
+ void *id = checkupval(L, 1, 2, NULL);
+ if (id != NULL)
+ lua_pushlightuserdata(L, id);
+ else
+ luaL_pushfail(L);
+ return 1;
+}
+
+
+static int db_upvaluejoin (lua_State *L) {
+ int n1, n2;
+ checkupval(L, 1, 2, &n1);
+ checkupval(L, 3, 4, &n2);
+ luaL_argcheck(L, !lua_iscfunction(L, 1), 1, "Lua function expected");
+ luaL_argcheck(L, !lua_iscfunction(L, 3), 3, "Lua function expected");
+ lua_upvaluejoin(L, 1, n1, 3, n2);
+ return 0;
+}
+
+
+/*
+** Call hook function registered at hook table for the current
+** thread (if there is one)
+*/
+static void hookf (lua_State *L, lua_Debug *ar) {
+ static const char *const hooknames[] =
+ {"call", "return", "line", "count", "tail call"};
+ lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY);
+ lua_pushthread(L);
+ if (lua_rawget(L, -2) == LUA_TFUNCTION) { /* is there a hook function? */
+ lua_pushstring(L, hooknames[(int)ar->event]); /* push event name */
+ if (ar->currentline >= 0)
+ lua_pushinteger(L, ar->currentline); /* push current line */
+ else lua_pushnil(L);
+ lua_assert(lua_getinfo(L, "lS", ar));
+ lua_call(L, 2, 0); /* call hook function */
+ }
+}
+
+
+/*
+** Convert a string mask (for 'sethook') into a bit mask
+*/
+static int makemask (const char *smask, int count) {
+ int mask = 0;
+ if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
+ if (strchr(smask, 'r')) mask |= LUA_MASKRET;
+ if (strchr(smask, 'l')) mask |= LUA_MASKLINE;
+ if (count > 0) mask |= LUA_MASKCOUNT;
+ return mask;
+}
+
+
+/*
+** Convert a bit mask (for 'gethook') into a string mask
+*/
+static char *unmakemask (int mask, char *smask) {
+ int i = 0;
+ if (mask & LUA_MASKCALL) smask[i++] = 'c';
+ if (mask & LUA_MASKRET) smask[i++] = 'r';
+ if (mask & LUA_MASKLINE) smask[i++] = 'l';
+ smask[i] = '\0';
+ return smask;
+}
+
+
+static int db_sethook (lua_State *L) {
+ int arg, mask, count;
+ lua_Hook func;
+ lua_State *L1 = getthread(L, &arg);
+ if (lua_isnoneornil(L, arg+1)) { /* no hook? */
+ lua_settop(L, arg+1);
+ func = NULL; mask = 0; count = 0; /* turn off hooks */
+ }
+ else {
+ const char *smask = luaL_checkstring(L, arg+2);
+ luaL_checktype(L, arg+1, LUA_TFUNCTION);
+ count = (int)luaL_optinteger(L, arg + 3, 0);
+ func = hookf; mask = makemask(smask, count);
+ }
+ if (!luaL_getsubtable(L, LUA_REGISTRYINDEX, HOOKKEY)) {
+ /* table just created; initialize it */
+ lua_pushliteral(L, "k");
+ lua_setfield(L, -2, "__mode"); /** hooktable.__mode = "k" */
+ lua_pushvalue(L, -1);
+ lua_setmetatable(L, -2); /* metatable(hooktable) = hooktable */
+ }
+ checkstack(L, L1, 1);
+ lua_pushthread(L1); lua_xmove(L1, L, 1); /* key (thread) */
+ lua_pushvalue(L, arg + 1); /* value (hook function) */
+ lua_rawset(L, -3); /* hooktable[L1] = new Lua hook */
+ lua_sethook(L1, func, mask, count);
+ return 0;
+}
+
+
+static int db_gethook (lua_State *L) {
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ char buff[5];
+ int mask = lua_gethookmask(L1);
+ lua_Hook hook = lua_gethook(L1);
+ if (hook == NULL) { /* no hook? */
+ luaL_pushfail(L);
+ return 1;
+ }
+ else if (hook != hookf) /* external hook? */
+ lua_pushliteral(L, "external hook");
+ else { /* hook table must exist */
+ lua_getfield(L, LUA_REGISTRYINDEX, HOOKKEY);
+ checkstack(L, L1, 1);
+ lua_pushthread(L1); lua_xmove(L1, L, 1);
+ lua_rawget(L, -2); /* 1st result = hooktable[L1] */
+ lua_remove(L, -2); /* remove hook table */
+ }
+ lua_pushstring(L, unmakemask(mask, buff)); /* 2nd result = mask */
+ lua_pushinteger(L, lua_gethookcount(L1)); /* 3rd result = count */
+ return 3;
+}
+
+
+static int db_debug (lua_State *L) {
+ for (;;) {
+ char buffer[250];
+ lua_writestringerror("%s", "lua_debug> ");
+ if (fgets(buffer, sizeof(buffer), stdin) == NULL ||
+ strcmp(buffer, "cont\n") == 0)
+ return 0;
+ if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
+ lua_pcall(L, 0, 0, 0))
+ lua_writestringerror("%s\n", luaL_tolstring(L, -1, NULL));
+ lua_settop(L, 0); /* remove eventual returns */
+ }
+}
+
+
+static int db_traceback (lua_State *L) {
+ int arg;
+ lua_State *L1 = getthread(L, &arg);
+ const char *msg = lua_tostring(L, arg + 1);
+ if (msg == NULL && !lua_isnoneornil(L, arg + 1)) /* non-string 'msg'? */
+ lua_pushvalue(L, arg + 1); /* return it untouched */
+ else {
+ int level = (int)luaL_optinteger(L, arg + 2, (L == L1) ? 1 : 0);
+ luaL_traceback(L, L1, msg, level);
+ }
+ return 1;
+}
+
+
+static int db_setcstacklimit (lua_State *L) {
+ int limit = (int)luaL_checkinteger(L, 1);
+ int res = lua_setcstacklimit(L, limit);
+ lua_pushinteger(L, res);
+ return 1;
+}
+
+
+static const luaL_Reg dblib[] = {
+ {"debug", db_debug},
+ {"getuservalue", db_getuservalue},
+ {"gethook", db_gethook},
+ {"getinfo", db_getinfo},
+ {"getlocal", db_getlocal},
+ {"getregistry", db_getregistry},
+ {"getmetatable", db_getmetatable},
+ {"getupvalue", db_getupvalue},
+ {"upvaluejoin", db_upvaluejoin},
+ {"upvalueid", db_upvalueid},
+ {"setuservalue", db_setuservalue},
+ {"sethook", db_sethook},
+ {"setlocal", db_setlocal},
+ {"setmetatable", db_setmetatable},
+ {"setupvalue", db_setupvalue},
+ {"traceback", db_traceback},
+ {"setcstacklimit", db_setcstacklimit},
+ {NULL, NULL}
+};
+
+
+LUAMOD_API int luaopen_debug (lua_State *L) {
+ luaL_newlib(L, dblib);
+ return 1;
+}
+
diff --git a/src/libs/3rdparty/lua/src/ldebug.c b/src/libs/3rdparty/lua/src/ldebug.c
new file mode 100644
index 0000000000..28b1caabf7
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ldebug.c
@@ -0,0 +1,924 @@
+/*
+** $Id: ldebug.c $
+** Debug Interface
+** See Copyright Notice in lua.h
+*/
+
+#define ldebug_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <stdarg.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "lcode.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lvm.h"
+
+
+
+#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL)
+
+
+static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
+ const char **name);
+
+
+static int currentpc (CallInfo *ci) {
+ lua_assert(isLua(ci));
+ return pcRel(ci->u.l.savedpc, ci_func(ci)->p);
+}
+
+
+/*
+** Get a "base line" to find the line corresponding to an instruction.
+** Base lines are regularly placed at MAXIWTHABS intervals, so usually
+** an integer division gets the right place. When the source file has
+** large sequences of empty/comment lines, it may need extra entries,
+** so the original estimate needs a correction.
+** If the original estimate is -1, the initial 'if' ensures that the
+** 'while' will run at least once.
+** The assertion that the estimate is a lower bound for the correct base
+** is valid as long as the debug info has been generated with the same
+** value for MAXIWTHABS or smaller. (Previous releases use a little
+** smaller value.)
+*/
+static int getbaseline (const Proto *f, int pc, int *basepc) {
+ if (f->sizeabslineinfo == 0 || pc < f->abslineinfo[0].pc) {
+ *basepc = -1; /* start from the beginning */
+ return f->linedefined;
+ }
+ else {
+ int i = cast_uint(pc) / MAXIWTHABS - 1; /* get an estimate */
+ /* estimate must be a lower bound of the correct base */
+ lua_assert(i < 0 ||
+ (i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc));
+ while (i + 1 < f->sizeabslineinfo && pc >= f->abslineinfo[i + 1].pc)
+ i++; /* low estimate; adjust it */
+ *basepc = f->abslineinfo[i].pc;
+ return f->abslineinfo[i].line;
+ }
+}
+
+
+/*
+** Get the line corresponding to instruction 'pc' in function 'f';
+** first gets a base line and from there does the increments until
+** the desired instruction.
+*/
+int luaG_getfuncline (const Proto *f, int pc) {
+ if (f->lineinfo == NULL) /* no debug information? */
+ return -1;
+ else {
+ int basepc;
+ int baseline = getbaseline(f, pc, &basepc);
+ while (basepc++ < pc) { /* walk until given instruction */
+ lua_assert(f->lineinfo[basepc] != ABSLINEINFO);
+ baseline += f->lineinfo[basepc]; /* correct line */
+ }
+ return baseline;
+ }
+}
+
+
+static int getcurrentline (CallInfo *ci) {
+ return luaG_getfuncline(ci_func(ci)->p, currentpc(ci));
+}
+
+
+/*
+** Set 'trap' for all active Lua frames.
+** This function can be called during a signal, under "reasonable"
+** assumptions. A new 'ci' is completely linked in the list before it
+** becomes part of the "active" list, and we assume that pointers are
+** atomic; see comment in next function.
+** (A compiler doing interprocedural optimizations could, theoretically,
+** reorder memory writes in such a way that the list could be
+** temporarily broken while inserting a new element. We simply assume it
+** has no good reasons to do that.)
+*/
+static void settraps (CallInfo *ci) {
+ for (; ci != NULL; ci = ci->previous)
+ if (isLua(ci))
+ ci->u.l.trap = 1;
+}
+
+
+/*
+** This function can be called during a signal, under "reasonable"
+** assumptions.
+** Fields 'basehookcount' and 'hookcount' (set by 'resethookcount')
+** are for debug only, and it is no problem if they get arbitrary
+** values (causes at most one wrong hook call). 'hookmask' is an atomic
+** value. We assume that pointers are atomic too (e.g., gcc ensures that
+** for all platforms where it runs). Moreover, 'hook' is always checked
+** before being called (see 'luaD_hook').
+*/
+LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
+ if (func == NULL || mask == 0) { /* turn off hooks? */
+ mask = 0;
+ func = NULL;
+ }
+ L->hook = func;
+ L->basehookcount = count;
+ resethookcount(L);
+ L->hookmask = cast_byte(mask);
+ if (mask)
+ settraps(L->ci); /* to trace inside 'luaV_execute' */
+}
+
+
+LUA_API lua_Hook lua_gethook (lua_State *L) {
+ return L->hook;
+}
+
+
+LUA_API int lua_gethookmask (lua_State *L) {
+ return L->hookmask;
+}
+
+
+LUA_API int lua_gethookcount (lua_State *L) {
+ return L->basehookcount;
+}
+
+
+LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) {
+ int status;
+ CallInfo *ci;
+ if (level < 0) return 0; /* invalid (negative) level */
+ lua_lock(L);
+ for (ci = L->ci; level > 0 && ci != &L->base_ci; ci = ci->previous)
+ level--;
+ if (level == 0 && ci != &L->base_ci) { /* level found? */
+ status = 1;
+ ar->i_ci = ci;
+ }
+ else status = 0; /* no such level */
+ lua_unlock(L);
+ return status;
+}
+
+
+static const char *upvalname (const Proto *p, int uv) {
+ TString *s = check_exp(uv < p->sizeupvalues, p->upvalues[uv].name);
+ if (s == NULL) return "?";
+ else return getstr(s);
+}
+
+
+static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
+ if (clLvalue(s2v(ci->func.p))->p->is_vararg) {
+ int nextra = ci->u.l.nextraargs;
+ if (n >= -nextra) { /* 'n' is negative */
+ *pos = ci->func.p - nextra - (n + 1);
+ return "(vararg)"; /* generic name for any vararg */
+ }
+ }
+ return NULL; /* no such vararg */
+}
+
+
+const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n, StkId *pos) {
+ StkId base = ci->func.p + 1;
+ const char *name = NULL;
+ if (isLua(ci)) {
+ if (n < 0) /* access to vararg values? */
+ return findvararg(ci, n, pos);
+ else
+ name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
+ }
+ if (name == NULL) { /* no 'standard' name? */
+ StkId limit = (ci == L->ci) ? L->top.p : ci->next->func.p;
+ if (limit - base >= n && n > 0) { /* is 'n' inside 'ci' stack? */
+ /* generic name for any valid slot */
+ name = isLua(ci) ? "(temporary)" : "(C temporary)";
+ }
+ else
+ return NULL; /* no name */
+ }
+ if (pos)
+ *pos = base + (n - 1);
+ return name;
+}
+
+
+LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
+ const char *name;
+ lua_lock(L);
+ if (ar == NULL) { /* information about non-active function? */
+ if (!isLfunction(s2v(L->top.p - 1))) /* not a Lua function? */
+ name = NULL;
+ else /* consider live variables at function start (parameters) */
+ name = luaF_getlocalname(clLvalue(s2v(L->top.p - 1))->p, n, 0);
+ }
+ else { /* active function; get information through 'ar' */
+ StkId pos = NULL; /* to avoid warnings */
+ name = luaG_findlocal(L, ar->i_ci, n, &pos);
+ if (name) {
+ setobjs2s(L, L->top.p, pos);
+ api_incr_top(L);
+ }
+ }
+ lua_unlock(L);
+ return name;
+}
+
+
+LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
+ StkId pos = NULL; /* to avoid warnings */
+ const char *name;
+ lua_lock(L);
+ name = luaG_findlocal(L, ar->i_ci, n, &pos);
+ if (name) {
+ setobjs2s(L, pos, L->top.p - 1);
+ L->top.p--; /* pop value */
+ }
+ lua_unlock(L);
+ return name;
+}
+
+
+static void funcinfo (lua_Debug *ar, Closure *cl) {
+ if (noLuaClosure(cl)) {
+ ar->source = "=[C]";
+ ar->srclen = LL("=[C]");
+ ar->linedefined = -1;
+ ar->lastlinedefined = -1;
+ ar->what = "C";
+ }
+ else {
+ const Proto *p = cl->l.p;
+ if (p->source) {
+ ar->source = getstr(p->source);
+ ar->srclen = tsslen(p->source);
+ }
+ else {
+ ar->source = "=?";
+ ar->srclen = LL("=?");
+ }
+ ar->linedefined = p->linedefined;
+ ar->lastlinedefined = p->lastlinedefined;
+ ar->what = (ar->linedefined == 0) ? "main" : "Lua";
+ }
+ luaO_chunkid(ar->short_src, ar->source, ar->srclen);
+}
+
+
+static int nextline (const Proto *p, int currentline, int pc) {
+ if (p->lineinfo[pc] != ABSLINEINFO)
+ return currentline + p->lineinfo[pc];
+ else
+ return luaG_getfuncline(p, pc);
+}
+
+
+static void collectvalidlines (lua_State *L, Closure *f) {
+ if (noLuaClosure(f)) {
+ setnilvalue(s2v(L->top.p));
+ api_incr_top(L);
+ }
+ else {
+ int i;
+ TValue v;
+ const Proto *p = f->l.p;
+ int currentline = p->linedefined;
+ Table *t = luaH_new(L); /* new table to store active lines */
+ sethvalue2s(L, L->top.p, t); /* push it on stack */
+ api_incr_top(L);
+ setbtvalue(&v); /* boolean 'true' to be the value of all indices */
+ if (!p->is_vararg) /* regular function? */
+ i = 0; /* consider all instructions */
+ else { /* vararg function */
+ lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP);
+ currentline = nextline(p, currentline, 0);
+ i = 1; /* skip first instruction (OP_VARARGPREP) */
+ }
+ for (; i < p->sizelineinfo; i++) { /* for each instruction */
+ currentline = nextline(p, currentline, i); /* get its line */
+ luaH_setint(L, t, currentline, &v); /* table[line] = true */
+ }
+ }
+}
+
+
+static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {
+ /* calling function is a known function? */
+ if (ci != NULL && !(ci->callstatus & CIST_TAIL))
+ return funcnamefromcall(L, ci->previous, name);
+ else return NULL; /* no way to find a name */
+}
+
+
+static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
+ Closure *f, CallInfo *ci) {
+ int status = 1;
+ for (; *what; what++) {
+ switch (*what) {
+ case 'S': {
+ funcinfo(ar, f);
+ break;
+ }
+ case 'l': {
+ ar->currentline = (ci && isLua(ci)) ? getcurrentline(ci) : -1;
+ break;
+ }
+ case 'u': {
+ ar->nups = (f == NULL) ? 0 : f->c.nupvalues;
+ if (noLuaClosure(f)) {
+ ar->isvararg = 1;
+ ar->nparams = 0;
+ }
+ else {
+ ar->isvararg = f->l.p->is_vararg;
+ ar->nparams = f->l.p->numparams;
+ }
+ break;
+ }
+ case 't': {
+ ar->istailcall = (ci) ? ci->callstatus & CIST_TAIL : 0;
+ break;
+ }
+ case 'n': {
+ ar->namewhat = getfuncname(L, ci, &ar->name);
+ if (ar->namewhat == NULL) {
+ ar->namewhat = ""; /* not found */
+ ar->name = NULL;
+ }
+ break;
+ }
+ case 'r': {
+ if (ci == NULL || !(ci->callstatus & CIST_TRAN))
+ ar->ftransfer = ar->ntransfer = 0;
+ else {
+ ar->ftransfer = ci->u2.transferinfo.ftransfer;
+ ar->ntransfer = ci->u2.transferinfo.ntransfer;
+ }
+ break;
+ }
+ case 'L':
+ case 'f': /* handled by lua_getinfo */
+ break;
+ default: status = 0; /* invalid option */
+ }
+ }
+ return status;
+}
+
+
+LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
+ int status;
+ Closure *cl;
+ CallInfo *ci;
+ TValue *func;
+ lua_lock(L);
+ if (*what == '>') {
+ ci = NULL;
+ func = s2v(L->top.p - 1);
+ api_check(L, ttisfunction(func), "function expected");
+ what++; /* skip the '>' */
+ L->top.p--; /* pop function */
+ }
+ else {
+ ci = ar->i_ci;
+ func = s2v(ci->func.p);
+ lua_assert(ttisfunction(func));
+ }
+ cl = ttisclosure(func) ? clvalue(func) : NULL;
+ status = auxgetinfo(L, what, ar, cl, ci);
+ if (strchr(what, 'f')) {
+ setobj2s(L, L->top.p, func);
+ api_incr_top(L);
+ }
+ if (strchr(what, 'L'))
+ collectvalidlines(L, cl);
+ lua_unlock(L);
+ return status;
+}
+
+
+/*
+** {======================================================
+** Symbolic Execution
+** =======================================================
+*/
+
+static const char *getobjname (const Proto *p, int lastpc, int reg,
+ const char **name);
+
+
+/*
+** Find a "name" for the constant 'c'.
+*/
+static void kname (const Proto *p, int c, const char **name) {
+ TValue *kvalue = &p->k[c];
+ *name = (ttisstring(kvalue)) ? svalue(kvalue) : "?";
+}
+
+
+/*
+** Find a "name" for the register 'c'.
+*/
+static void rname (const Proto *p, int pc, int c, const char **name) {
+ const char *what = getobjname(p, pc, c, name); /* search for 'c' */
+ if (!(what && *what == 'c')) /* did not find a constant name? */
+ *name = "?";
+}
+
+
+/*
+** Find a "name" for a 'C' value in an RK instruction.
+*/
+static void rkname (const Proto *p, int pc, Instruction i, const char **name) {
+ int c = GETARG_C(i); /* key index */
+ if (GETARG_k(i)) /* is 'c' a constant? */
+ kname(p, c, name);
+ else /* 'c' is a register */
+ rname(p, pc, c, name);
+}
+
+
+static int filterpc (int pc, int jmptarget) {
+ if (pc < jmptarget) /* is code conditional (inside a jump)? */
+ return -1; /* cannot know who sets that register */
+ else return pc; /* current position sets that register */
+}
+
+
+/*
+** Try to find last instruction before 'lastpc' that modified register 'reg'.
+*/
+static int findsetreg (const Proto *p, int lastpc, int reg) {
+ int pc;
+ int setreg = -1; /* keep last instruction that changed 'reg' */
+ int jmptarget = 0; /* any code before this address is conditional */
+ if (testMMMode(GET_OPCODE(p->code[lastpc])))
+ lastpc--; /* previous instruction was not actually executed */
+ for (pc = 0; pc < lastpc; pc++) {
+ Instruction i = p->code[pc];
+ OpCode op = GET_OPCODE(i);
+ int a = GETARG_A(i);
+ int change; /* true if current instruction changed 'reg' */
+ switch (op) {
+ case OP_LOADNIL: { /* set registers from 'a' to 'a+b' */
+ int b = GETARG_B(i);
+ change = (a <= reg && reg <= a + b);
+ break;
+ }
+ case OP_TFORCALL: { /* affect all regs above its base */
+ change = (reg >= a + 2);
+ break;
+ }
+ case OP_CALL:
+ case OP_TAILCALL: { /* affect all registers above base */
+ change = (reg >= a);
+ break;
+ }
+ case OP_JMP: { /* doesn't change registers, but changes 'jmptarget' */
+ int b = GETARG_sJ(i);
+ int dest = pc + 1 + b;
+ /* jump does not skip 'lastpc' and is larger than current one? */
+ if (dest <= lastpc && dest > jmptarget)
+ jmptarget = dest; /* update 'jmptarget' */
+ change = 0;
+ break;
+ }
+ default: /* any instruction that sets A */
+ change = (testAMode(op) && reg == a);
+ break;
+ }
+ if (change)
+ setreg = filterpc(pc, jmptarget);
+ }
+ return setreg;
+}
+
+
+/*
+** Check whether table being indexed by instruction 'i' is the
+** environment '_ENV'
+*/
+static const char *gxf (const Proto *p, int pc, Instruction i, int isup) {
+ int t = GETARG_B(i); /* table index */
+ const char *name; /* name of indexed variable */
+ if (isup) /* is an upvalue? */
+ name = upvalname(p, t);
+ else
+ getobjname(p, pc, t, &name);
+ return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field";
+}
+
+
+static const char *getobjname (const Proto *p, int lastpc, int reg,
+ const char **name) {
+ int pc;
+ *name = luaF_getlocalname(p, reg + 1, lastpc);
+ if (*name) /* is a local? */
+ return "local";
+ /* else try symbolic execution */
+ pc = findsetreg(p, lastpc, reg);
+ if (pc != -1) { /* could find instruction? */
+ Instruction i = p->code[pc];
+ OpCode op = GET_OPCODE(i);
+ switch (op) {
+ case OP_MOVE: {
+ int b = GETARG_B(i); /* move from 'b' to 'a' */
+ if (b < GETARG_A(i))
+ return getobjname(p, pc, b, name); /* get name for 'b' */
+ break;
+ }
+ case OP_GETTABUP: {
+ int k = GETARG_C(i); /* key index */
+ kname(p, k, name);
+ return gxf(p, pc, i, 1);
+ }
+ case OP_GETTABLE: {
+ int k = GETARG_C(i); /* key index */
+ rname(p, pc, k, name);
+ return gxf(p, pc, i, 0);
+ }
+ case OP_GETI: {
+ *name = "integer index";
+ return "field";
+ }
+ case OP_GETFIELD: {
+ int k = GETARG_C(i); /* key index */
+ kname(p, k, name);
+ return gxf(p, pc, i, 0);
+ }
+ case OP_GETUPVAL: {
+ *name = upvalname(p, GETARG_B(i));
+ return "upvalue";
+ }
+ case OP_LOADK:
+ case OP_LOADKX: {
+ int b = (op == OP_LOADK) ? GETARG_Bx(i)
+ : GETARG_Ax(p->code[pc + 1]);
+ if (ttisstring(&p->k[b])) {
+ *name = svalue(&p->k[b]);
+ return "constant";
+ }
+ break;
+ }
+ case OP_SELF: {
+ rkname(p, pc, i, name);
+ return "method";
+ }
+ default: break; /* go through to return NULL */
+ }
+ }
+ return NULL; /* could not find reasonable name */
+}
+
+
+/*
+** Try to find a name for a function based on the code that called it.
+** (Only works when function was called by a Lua function.)
+** Returns what the name is (e.g., "for iterator", "method",
+** "metamethod") and sets '*name' to point to the name.
+*/
+static const char *funcnamefromcode (lua_State *L, const Proto *p,
+ int pc, const char **name) {
+ TMS tm = (TMS)0; /* (initial value avoids warnings) */
+ Instruction i = p->code[pc]; /* calling instruction */
+ switch (GET_OPCODE(i)) {
+ case OP_CALL:
+ case OP_TAILCALL:
+ return getobjname(p, pc, GETARG_A(i), name); /* get function name */
+ case OP_TFORCALL: { /* for iterator */
+ *name = "for iterator";
+ return "for iterator";
+ }
+ /* other instructions can do calls through metamethods */
+ case OP_SELF: case OP_GETTABUP: case OP_GETTABLE:
+ case OP_GETI: case OP_GETFIELD:
+ tm = TM_INDEX;
+ break;
+ case OP_SETTABUP: case OP_SETTABLE: case OP_SETI: case OP_SETFIELD:
+ tm = TM_NEWINDEX;
+ break;
+ case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: {
+ tm = cast(TMS, GETARG_C(i));
+ break;
+ }
+ case OP_UNM: tm = TM_UNM; break;
+ case OP_BNOT: tm = TM_BNOT; break;
+ case OP_LEN: tm = TM_LEN; break;
+ case OP_CONCAT: tm = TM_CONCAT; break;
+ case OP_EQ: tm = TM_EQ; break;
+ /* no cases for OP_EQI and OP_EQK, as they don't call metamethods */
+ case OP_LT: case OP_LTI: case OP_GTI: tm = TM_LT; break;
+ case OP_LE: case OP_LEI: case OP_GEI: tm = TM_LE; break;
+ case OP_CLOSE: case OP_RETURN: tm = TM_CLOSE; break;
+ default:
+ return NULL; /* cannot find a reasonable name */
+ }
+ *name = getstr(G(L)->tmname[tm]) + 2;
+ return "metamethod";
+}
+
+
+/*
+** Try to find a name for a function based on how it was called.
+*/
+static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
+ const char **name) {
+ if (ci->callstatus & CIST_HOOKED) { /* was it called inside a hook? */
+ *name = "?";
+ return "hook";
+ }
+ else if (ci->callstatus & CIST_FIN) { /* was it called as a finalizer? */
+ *name = "__gc";
+ return "metamethod"; /* report it as such */
+ }
+ else if (isLua(ci))
+ return funcnamefromcode(L, ci_func(ci)->p, currentpc(ci), name);
+ else
+ return NULL;
+}
+
+/* }====================================================== */
+
+
+
+/*
+** Check whether pointer 'o' points to some value in the stack frame of
+** the current function and, if so, returns its index. Because 'o' may
+** not point to a value in this stack, we cannot compare it with the
+** region boundaries (undefined behavior in ISO C).
+*/
+static int instack (CallInfo *ci, const TValue *o) {
+ int pos;
+ StkId base = ci->func.p + 1;
+ for (pos = 0; base + pos < ci->top.p; pos++) {
+ if (o == s2v(base + pos))
+ return pos;
+ }
+ return -1; /* not found */
+}
+
+
+/*
+** Checks whether value 'o' came from an upvalue. (That can only happen
+** with instructions OP_GETTABUP/OP_SETTABUP, which operate directly on
+** upvalues.)
+*/
+static const char *getupvalname (CallInfo *ci, const TValue *o,
+ const char **name) {
+ LClosure *c = ci_func(ci);
+ int i;
+ for (i = 0; i < c->nupvalues; i++) {
+ if (c->upvals[i]->v.p == o) {
+ *name = upvalname(c->p, i);
+ return "upvalue";
+ }
+ }
+ return NULL;
+}
+
+
+static const char *formatvarinfo (lua_State *L, const char *kind,
+ const char *name) {
+ if (kind == NULL)
+ return ""; /* no information */
+ else
+ return luaO_pushfstring(L, " (%s '%s')", kind, name);
+}
+
+/*
+** Build a string with a "description" for the value 'o', such as
+** "variable 'x'" or "upvalue 'y'".
+*/
+static const char *varinfo (lua_State *L, const TValue *o) {
+ CallInfo *ci = L->ci;
+ const char *name = NULL; /* to avoid warnings */
+ const char *kind = NULL;
+ if (isLua(ci)) {
+ kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */
+ if (!kind) { /* not an upvalue? */
+ int reg = instack(ci, o); /* try a register */
+ if (reg >= 0) /* is 'o' a register? */
+ kind = getobjname(ci_func(ci)->p, currentpc(ci), reg, &name);
+ }
+ }
+ return formatvarinfo(L, kind, name);
+}
+
+
+/*
+** Raise a type error
+*/
+static l_noret typeerror (lua_State *L, const TValue *o, const char *op,
+ const char *extra) {
+ const char *t = luaT_objtypename(L, o);
+ luaG_runerror(L, "attempt to %s a %s value%s", op, t, extra);
+}
+
+
+/*
+** Raise a type error with "standard" information about the faulty
+** object 'o' (using 'varinfo').
+*/
+l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
+ typeerror(L, o, op, varinfo(L, o));
+}
+
+
+/*
+** Raise an error for calling a non-callable object. Try to find a name
+** for the object based on how it was called ('funcnamefromcall'); if it
+** cannot get a name there, try 'varinfo'.
+*/
+l_noret luaG_callerror (lua_State *L, const TValue *o) {
+ CallInfo *ci = L->ci;
+ const char *name = NULL; /* to avoid warnings */
+ const char *kind = funcnamefromcall(L, ci, &name);
+ const char *extra = kind ? formatvarinfo(L, kind, name) : varinfo(L, o);
+ typeerror(L, o, "call", extra);
+}
+
+
+l_noret luaG_forerror (lua_State *L, const TValue *o, const char *what) {
+ luaG_runerror(L, "bad 'for' %s (number expected, got %s)",
+ what, luaT_objtypename(L, o));
+}
+
+
+l_noret luaG_concaterror (lua_State *L, const TValue *p1, const TValue *p2) {
+ if (ttisstring(p1) || cvt2str(p1)) p1 = p2;
+ luaG_typeerror(L, p1, "concatenate");
+}
+
+
+l_noret luaG_opinterror (lua_State *L, const TValue *p1,
+ const TValue *p2, const char *msg) {
+ if (!ttisnumber(p1)) /* first operand is wrong? */
+ p2 = p1; /* now second is wrong */
+ luaG_typeerror(L, p2, msg);
+}
+
+
+/*
+** Error when both values are convertible to numbers, but not to integers
+*/
+l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) {
+ lua_Integer temp;
+ if (!luaV_tointegerns(p1, &temp, LUA_FLOORN2I))
+ p2 = p1;
+ luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2));
+}
+
+
+l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) {
+ const char *t1 = luaT_objtypename(L, p1);
+ const char *t2 = luaT_objtypename(L, p2);
+ if (strcmp(t1, t2) == 0)
+ luaG_runerror(L, "attempt to compare two %s values", t1);
+ else
+ luaG_runerror(L, "attempt to compare %s with %s", t1, t2);
+}
+
+
+/* add src:line information to 'msg' */
+const char *luaG_addinfo (lua_State *L, const char *msg, TString *src,
+ int line) {
+ char buff[LUA_IDSIZE];
+ if (src)
+ luaO_chunkid(buff, getstr(src), tsslen(src));
+ else { /* no source available; use "?" instead */
+ buff[0] = '?'; buff[1] = '\0';
+ }
+ return luaO_pushfstring(L, "%s:%d: %s", buff, line, msg);
+}
+
+
+l_noret luaG_errormsg (lua_State *L) {
+ if (L->errfunc != 0) { /* is there an error handling function? */
+ StkId errfunc = restorestack(L, L->errfunc);
+ lua_assert(ttisfunction(s2v(errfunc)));
+ setobjs2s(L, L->top.p, L->top.p - 1); /* move argument */
+ setobjs2s(L, L->top.p - 1, errfunc); /* push function */
+ L->top.p++; /* assume EXTRA_STACK */
+ luaD_callnoyield(L, L->top.p - 2, 1); /* call it */
+ }
+ luaD_throw(L, LUA_ERRRUN);
+}
+
+
+l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
+ CallInfo *ci = L->ci;
+ const char *msg;
+ va_list argp;
+ luaC_checkGC(L); /* error message uses memory */
+ va_start(argp, fmt);
+ msg = luaO_pushvfstring(L, fmt, argp); /* format message */
+ va_end(argp);
+ if (isLua(ci)) { /* if Lua function, add source:line information */
+ luaG_addinfo(L, msg, ci_func(ci)->p->source, getcurrentline(ci));
+ setobjs2s(L, L->top.p - 2, L->top.p - 1); /* remove 'msg' */
+ L->top.p--;
+ }
+ luaG_errormsg(L);
+}
+
+
+/*
+** Check whether new instruction 'newpc' is in a different line from
+** previous instruction 'oldpc'. More often than not, 'newpc' is only
+** one or a few instructions after 'oldpc' (it must be after, see
+** caller), so try to avoid calling 'luaG_getfuncline'. If they are
+** too far apart, there is a good chance of a ABSLINEINFO in the way,
+** so it goes directly to 'luaG_getfuncline'.
+*/
+static int changedline (const Proto *p, int oldpc, int newpc) {
+ if (p->lineinfo == NULL) /* no debug information? */
+ return 0;
+ if (newpc - oldpc < MAXIWTHABS / 2) { /* not too far apart? */
+ int delta = 0; /* line difference */
+ int pc = oldpc;
+ for (;;) {
+ int lineinfo = p->lineinfo[++pc];
+ if (lineinfo == ABSLINEINFO)
+ break; /* cannot compute delta; fall through */
+ delta += lineinfo;
+ if (pc == newpc)
+ return (delta != 0); /* delta computed successfully */
+ }
+ }
+ /* either instructions are too far apart or there is an absolute line
+ info in the way; compute line difference explicitly */
+ return (luaG_getfuncline(p, oldpc) != luaG_getfuncline(p, newpc));
+}
+
+
+/*
+** Traces the execution of a Lua function. Called before the execution
+** of each opcode, when debug is on. 'L->oldpc' stores the last
+** instruction traced, to detect line changes. When entering a new
+** function, 'npci' will be zero and will test as a new line whatever
+** the value of 'oldpc'. Some exceptional conditions may return to
+** a function without setting 'oldpc'. In that case, 'oldpc' may be
+** invalid; if so, use zero as a valid value. (A wrong but valid 'oldpc'
+** at most causes an extra call to a line hook.)
+** This function is not "Protected" when called, so it should correct
+** 'L->top.p' before calling anything that can run the GC.
+*/
+int luaG_traceexec (lua_State *L, const Instruction *pc) {
+ CallInfo *ci = L->ci;
+ lu_byte mask = L->hookmask;
+ const Proto *p = ci_func(ci)->p;
+ int counthook;
+ if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */
+ ci->u.l.trap = 0; /* don't need to stop again */
+ return 0; /* turn off 'trap' */
+ }
+ pc++; /* reference is always next instruction */
+ ci->u.l.savedpc = pc; /* save 'pc' */
+ counthook = (--L->hookcount == 0 && (mask & LUA_MASKCOUNT));
+ if (counthook)
+ resethookcount(L); /* reset count */
+ else if (!(mask & LUA_MASKLINE))
+ return 1; /* no line hook and count != 0; nothing to be done now */
+ if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */
+ ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
+ return 1; /* do not call hook again (VM yielded, so it did not move) */
+ }
+ if (!isIT(*(ci->u.l.savedpc - 1))) /* top not being used? */
+ L->top.p = ci->top.p; /* correct top */
+ if (counthook)
+ luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
+ if (mask & LUA_MASKLINE) {
+ /* 'L->oldpc' may be invalid; use zero in this case */
+ int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0;
+ int npci = pcRel(pc, p);
+ if (npci <= oldpc || /* call hook when jump back (loop), */
+ changedline(p, oldpc, npci)) { /* or when enter new line */
+ int newline = luaG_getfuncline(p, npci);
+ luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */
+ }
+ L->oldpc = npci; /* 'pc' of last call to line hook */
+ }
+ if (L->status == LUA_YIELD) { /* did hook yield? */
+ if (counthook)
+ L->hookcount = 1; /* undo decrement to zero */
+ ci->u.l.savedpc--; /* undo increment (resume will increment it again) */
+ ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */
+ luaD_throw(L, LUA_YIELD);
+ }
+ return 1; /* keep 'trap' on */
+}
+
diff --git a/src/libs/3rdparty/lua/src/ldebug.h b/src/libs/3rdparty/lua/src/ldebug.h
new file mode 100644
index 0000000000..2c3074c61b
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ldebug.h
@@ -0,0 +1,63 @@
+/*
+** $Id: ldebug.h $
+** Auxiliary functions from Debug Interface module
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ldebug_h
+#define ldebug_h
+
+
+#include "lstate.h"
+
+
+#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1)
+
+
+/* Active Lua function (given call info) */
+#define ci_func(ci) (clLvalue(s2v((ci)->func.p)))
+
+
+#define resethookcount(L) (L->hookcount = L->basehookcount)
+
+/*
+** mark for entries in 'lineinfo' array that has absolute information in
+** 'abslineinfo' array
+*/
+#define ABSLINEINFO (-0x80)
+
+
+/*
+** MAXimum number of successive Instructions WiTHout ABSolute line
+** information. (A power of two allows fast divisions.)
+*/
+#if !defined(MAXIWTHABS)
+#define MAXIWTHABS 128
+#endif
+
+
+LUAI_FUNC int luaG_getfuncline (const Proto *f, int pc);
+LUAI_FUNC const char *luaG_findlocal (lua_State *L, CallInfo *ci, int n,
+ StkId *pos);
+LUAI_FUNC l_noret luaG_typeerror (lua_State *L, const TValue *o,
+ const char *opname);
+LUAI_FUNC l_noret luaG_callerror (lua_State *L, const TValue *o);
+LUAI_FUNC l_noret luaG_forerror (lua_State *L, const TValue *o,
+ const char *what);
+LUAI_FUNC l_noret luaG_concaterror (lua_State *L, const TValue *p1,
+ const TValue *p2);
+LUAI_FUNC l_noret luaG_opinterror (lua_State *L, const TValue *p1,
+ const TValue *p2,
+ const char *msg);
+LUAI_FUNC l_noret luaG_tointerror (lua_State *L, const TValue *p1,
+ const TValue *p2);
+LUAI_FUNC l_noret luaG_ordererror (lua_State *L, const TValue *p1,
+ const TValue *p2);
+LUAI_FUNC l_noret luaG_runerror (lua_State *L, const char *fmt, ...);
+LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg,
+ TString *src, int line);
+LUAI_FUNC l_noret luaG_errormsg (lua_State *L);
+LUAI_FUNC int luaG_traceexec (lua_State *L, const Instruction *pc);
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/ldo.c b/src/libs/3rdparty/lua/src/ldo.c
new file mode 100644
index 0000000000..2a0017ca62
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ldo.c
@@ -0,0 +1,1024 @@
+/*
+** $Id: ldo.c $
+** Stack and Call structure of Lua
+** See Copyright Notice in lua.h
+*/
+
+#define ldo_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <setjmp.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lundump.h"
+#include "lvm.h"
+#include "lzio.h"
+
+
+
+#define errorstatus(s) ((s) > LUA_YIELD)
+
+
+/*
+** {======================================================
+** Error-recovery functions
+** =======================================================
+*/
+
+/*
+** LUAI_THROW/LUAI_TRY define how Lua does exception handling. By
+** default, Lua handles errors with exceptions when compiling as
+** C++ code, with _longjmp/_setjmp when asked to use them, and with
+** longjmp/setjmp otherwise.
+*/
+#if !defined(LUAI_THROW) /* { */
+
+#if defined(__cplusplus) && !defined(LUA_USE_LONGJMP) /* { */
+
+/* C++ exceptions */
+#define LUAI_THROW(L,c) throw(c)
+#define LUAI_TRY(L,c,a) \
+ try { a } catch(...) { if ((c)->status == 0) (c)->status = -1; }
+#define luai_jmpbuf int /* dummy variable */
+
+#elif defined(LUA_USE_POSIX) /* }{ */
+
+/* in POSIX, try _longjmp/_setjmp (more efficient) */
+#define LUAI_THROW(L,c) _longjmp((c)->b, 1)
+#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a }
+#define luai_jmpbuf jmp_buf
+
+#else /* }{ */
+
+/* ISO C handling with long jumps */
+#define LUAI_THROW(L,c) longjmp((c)->b, 1)
+#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a }
+#define luai_jmpbuf jmp_buf
+
+#endif /* } */
+
+#endif /* } */
+
+
+
+/* chain list of long jump buffers */
+struct lua_longjmp {
+ struct lua_longjmp *previous;
+ luai_jmpbuf b;
+ volatile int status; /* error code */
+};
+
+
+void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
+ switch (errcode) {
+ case LUA_ERRMEM: { /* memory error? */
+ setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */
+ break;
+ }
+ case LUA_ERRERR: {
+ setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
+ break;
+ }
+ case LUA_OK: { /* special case only for closing upvalues */
+ setnilvalue(s2v(oldtop)); /* no error message */
+ break;
+ }
+ default: {
+ lua_assert(errorstatus(errcode)); /* real error */
+ setobjs2s(L, oldtop, L->top.p - 1); /* error message on current top */
+ break;
+ }
+ }
+ L->top.p = oldtop + 1;
+}
+
+
+l_noret luaD_throw (lua_State *L, int errcode) {
+ if (L->errorJmp) { /* thread has an error handler? */
+ L->errorJmp->status = errcode; /* set status */
+ LUAI_THROW(L, L->errorJmp); /* jump to it */
+ }
+ else { /* thread has no error handler */
+ global_State *g = G(L);
+ errcode = luaE_resetthread(L, errcode); /* close all upvalues */
+ if (g->mainthread->errorJmp) { /* main thread has a handler? */
+ setobjs2s(L, g->mainthread->top.p++, L->top.p - 1); /* copy error obj. */
+ luaD_throw(g->mainthread, errcode); /* re-throw in main thread */
+ }
+ else { /* no handler at all; abort */
+ if (g->panic) { /* panic function? */
+ lua_unlock(L);
+ g->panic(L); /* call panic function (last chance to jump out) */
+ }
+ abort();
+ }
+ }
+}
+
+
+int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
+ l_uint32 oldnCcalls = L->nCcalls;
+ struct lua_longjmp lj;
+ lj.status = LUA_OK;
+ lj.previous = L->errorJmp; /* chain new error handler */
+ L->errorJmp = &lj;
+ LUAI_TRY(L, &lj,
+ (*f)(L, ud);
+ );
+ L->errorJmp = lj.previous; /* restore old error handler */
+ L->nCcalls = oldnCcalls;
+ return lj.status;
+}
+
+/* }====================================================== */
+
+
+/*
+** {==================================================================
+** Stack reallocation
+** ===================================================================
+*/
+
+
+/*
+** Change all pointers to the stack into offsets.
+*/
+static void relstack (lua_State *L) {
+ CallInfo *ci;
+ UpVal *up;
+ L->top.offset = savestack(L, L->top.p);
+ L->tbclist.offset = savestack(L, L->tbclist.p);
+ for (up = L->openupval; up != NULL; up = up->u.open.next)
+ up->v.offset = savestack(L, uplevel(up));
+ for (ci = L->ci; ci != NULL; ci = ci->previous) {
+ ci->top.offset = savestack(L, ci->top.p);
+ ci->func.offset = savestack(L, ci->func.p);
+ }
+}
+
+
+/*
+** Change back all offsets into pointers.
+*/
+static void correctstack (lua_State *L) {
+ CallInfo *ci;
+ UpVal *up;
+ L->top.p = restorestack(L, L->top.offset);
+ L->tbclist.p = restorestack(L, L->tbclist.offset);
+ for (up = L->openupval; up != NULL; up = up->u.open.next)
+ up->v.p = s2v(restorestack(L, up->v.offset));
+ for (ci = L->ci; ci != NULL; ci = ci->previous) {
+ ci->top.p = restorestack(L, ci->top.offset);
+ ci->func.p = restorestack(L, ci->func.offset);
+ if (isLua(ci))
+ ci->u.l.trap = 1; /* signal to update 'trap' in 'luaV_execute' */
+ }
+}
+
+
+/* some space for error handling */
+#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200)
+
+/*
+** Reallocate the stack to a new size, correcting all pointers into it.
+** In ISO C, any pointer use after the pointer has been deallocated is
+** undefined behavior. So, before the reallocation, all pointers are
+** changed to offsets, and after the reallocation they are changed back
+** to pointers. As during the reallocation the pointers are invalid, the
+** reallocation cannot run emergency collections.
+**
+** In case of allocation error, raise an error or return false according
+** to 'raiseerror'.
+*/
+int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) {
+ int oldsize = stacksize(L);
+ int i;
+ StkId newstack;
+ int oldgcstop = G(L)->gcstopem;
+ lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE);
+ relstack(L); /* change pointers to offsets */
+ G(L)->gcstopem = 1; /* stop emergency collection */
+ newstack = luaM_reallocvector(L, L->stack.p, oldsize + EXTRA_STACK,
+ newsize + EXTRA_STACK, StackValue);
+ G(L)->gcstopem = oldgcstop; /* restore emergency collection */
+ if (l_unlikely(newstack == NULL)) { /* reallocation failed? */
+ correctstack(L); /* change offsets back to pointers */
+ if (raiseerror)
+ luaM_error(L);
+ else return 0; /* do not raise an error */
+ }
+ L->stack.p = newstack;
+ correctstack(L); /* change offsets back to pointers */
+ L->stack_last.p = L->stack.p + newsize;
+ for (i = oldsize + EXTRA_STACK; i < newsize + EXTRA_STACK; i++)
+ setnilvalue(s2v(newstack + i)); /* erase new segment */
+ return 1;
+}
+
+
+/*
+** Try to grow the stack by at least 'n' elements. When 'raiseerror'
+** is true, raises any error; otherwise, return 0 in case of errors.
+*/
+int luaD_growstack (lua_State *L, int n, int raiseerror) {
+ int size = stacksize(L);
+ if (l_unlikely(size > LUAI_MAXSTACK)) {
+ /* if stack is larger than maximum, thread is already using the
+ extra space reserved for errors, that is, thread is handling
+ a stack error; cannot grow further than that. */
+ lua_assert(stacksize(L) == ERRORSTACKSIZE);
+ if (raiseerror)
+ luaD_throw(L, LUA_ERRERR); /* error inside message handler */
+ return 0; /* if not 'raiseerror', just signal it */
+ }
+ else if (n < LUAI_MAXSTACK) { /* avoids arithmetic overflows */
+ int newsize = 2 * size; /* tentative new size */
+ int needed = cast_int(L->top.p - L->stack.p) + n;
+ if (newsize > LUAI_MAXSTACK) /* cannot cross the limit */
+ newsize = LUAI_MAXSTACK;
+ if (newsize < needed) /* but must respect what was asked for */
+ newsize = needed;
+ if (l_likely(newsize <= LUAI_MAXSTACK))
+ return luaD_reallocstack(L, newsize, raiseerror);
+ }
+ /* else stack overflow */
+ /* add extra size to be able to handle the error message */
+ luaD_reallocstack(L, ERRORSTACKSIZE, raiseerror);
+ if (raiseerror)
+ luaG_runerror(L, "stack overflow");
+ return 0;
+}
+
+
+/*
+** Compute how much of the stack is being used, by computing the
+** maximum top of all call frames in the stack and the current top.
+*/
+static int stackinuse (lua_State *L) {
+ CallInfo *ci;
+ int res;
+ StkId lim = L->top.p;
+ for (ci = L->ci; ci != NULL; ci = ci->previous) {
+ if (lim < ci->top.p) lim = ci->top.p;
+ }
+ lua_assert(lim <= L->stack_last.p + EXTRA_STACK);
+ res = cast_int(lim - L->stack.p) + 1; /* part of stack in use */
+ if (res < LUA_MINSTACK)
+ res = LUA_MINSTACK; /* ensure a minimum size */
+ return res;
+}
+
+
+/*
+** If stack size is more than 3 times the current use, reduce that size
+** to twice the current use. (So, the final stack size is at most 2/3 the
+** previous size, and half of its entries are empty.)
+** As a particular case, if stack was handling a stack overflow and now
+** it is not, 'max' (limited by LUAI_MAXSTACK) will be smaller than
+** stacksize (equal to ERRORSTACKSIZE in this case), and so the stack
+** will be reduced to a "regular" size.
+*/
+void luaD_shrinkstack (lua_State *L) {
+ int inuse = stackinuse(L);
+ int max = (inuse > LUAI_MAXSTACK / 3) ? LUAI_MAXSTACK : inuse * 3;
+ /* if thread is currently not handling a stack overflow and its
+ size is larger than maximum "reasonable" size, shrink it */
+ if (inuse <= LUAI_MAXSTACK && stacksize(L) > max) {
+ int nsize = (inuse > LUAI_MAXSTACK / 2) ? LUAI_MAXSTACK : inuse * 2;
+ luaD_reallocstack(L, nsize, 0); /* ok if that fails */
+ }
+ else /* don't change stack */
+ condmovestack(L,{},{}); /* (change only for debugging) */
+ luaE_shrinkCI(L); /* shrink CI list */
+}
+
+
+void luaD_inctop (lua_State *L) {
+ luaD_checkstack(L, 1);
+ L->top.p++;
+}
+
+/* }================================================================== */
+
+
+/*
+** Call a hook for the given event. Make sure there is a hook to be
+** called. (Both 'L->hook' and 'L->hookmask', which trigger this
+** function, can be changed asynchronously by signals.)
+*/
+void luaD_hook (lua_State *L, int event, int line,
+ int ftransfer, int ntransfer) {
+ lua_Hook hook = L->hook;
+ if (hook && L->allowhook) { /* make sure there is a hook */
+ int mask = CIST_HOOKED;
+ CallInfo *ci = L->ci;
+ ptrdiff_t top = savestack(L, L->top.p); /* preserve original 'top' */
+ ptrdiff_t ci_top = savestack(L, ci->top.p); /* idem for 'ci->top' */
+ lua_Debug ar;
+ ar.event = event;
+ ar.currentline = line;
+ ar.i_ci = ci;
+ if (ntransfer != 0) {
+ mask |= CIST_TRAN; /* 'ci' has transfer information */
+ ci->u2.transferinfo.ftransfer = ftransfer;
+ ci->u2.transferinfo.ntransfer = ntransfer;
+ }
+ if (isLua(ci) && L->top.p < ci->top.p)
+ L->top.p = ci->top.p; /* protect entire activation register */
+ luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
+ if (ci->top.p < L->top.p + LUA_MINSTACK)
+ ci->top.p = L->top.p + LUA_MINSTACK;
+ L->allowhook = 0; /* cannot call hooks inside a hook */
+ ci->callstatus |= mask;
+ lua_unlock(L);
+ (*hook)(L, &ar);
+ lua_lock(L);
+ lua_assert(!L->allowhook);
+ L->allowhook = 1;
+ ci->top.p = restorestack(L, ci_top);
+ L->top.p = restorestack(L, top);
+ ci->callstatus &= ~mask;
+ }
+}
+
+
+/*
+** Executes a call hook for Lua functions. This function is called
+** whenever 'hookmask' is not zero, so it checks whether call hooks are
+** active.
+*/
+void luaD_hookcall (lua_State *L, CallInfo *ci) {
+ L->oldpc = 0; /* set 'oldpc' for new function */
+ if (L->hookmask & LUA_MASKCALL) { /* is call hook on? */
+ int event = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL
+ : LUA_HOOKCALL;
+ Proto *p = ci_func(ci)->p;
+ ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */
+ luaD_hook(L, event, -1, 1, p->numparams);
+ ci->u.l.savedpc--; /* correct 'pc' */
+ }
+}
+
+
+/*
+** Executes a return hook for Lua and C functions and sets/corrects
+** 'oldpc'. (Note that this correction is needed by the line hook, so it
+** is done even when return hooks are off.)
+*/
+static void rethook (lua_State *L, CallInfo *ci, int nres) {
+ if (L->hookmask & LUA_MASKRET) { /* is return hook on? */
+ StkId firstres = L->top.p - nres; /* index of first result */
+ int delta = 0; /* correction for vararg functions */
+ int ftransfer;
+ if (isLua(ci)) {
+ Proto *p = ci_func(ci)->p;
+ if (p->is_vararg)
+ delta = ci->u.l.nextraargs + p->numparams + 1;
+ }
+ ci->func.p += delta; /* if vararg, back to virtual 'func' */
+ ftransfer = cast(unsigned short, firstres - ci->func.p);
+ luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */
+ ci->func.p -= delta;
+ }
+ if (isLua(ci = ci->previous))
+ L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* set 'oldpc' */
+}
+
+
+/*
+** Check whether 'func' has a '__call' metafield. If so, put it in the
+** stack, below original 'func', so that 'luaD_precall' can call it. Raise
+** an error if there is no '__call' metafield.
+*/
+StkId luaD_tryfuncTM (lua_State *L, StkId func) {
+ const TValue *tm;
+ StkId p;
+ checkstackGCp(L, 1, func); /* space for metamethod */
+ tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); /* (after previous GC) */
+ if (l_unlikely(ttisnil(tm)))
+ luaG_callerror(L, s2v(func)); /* nothing to call */
+ for (p = L->top.p; p > func; p--) /* open space for metamethod */
+ setobjs2s(L, p, p-1);
+ L->top.p++; /* stack space pre-allocated by the caller */
+ setobj2s(L, func, tm); /* metamethod is the new function to be called */
+ return func;
+}
+
+
+/*
+** Given 'nres' results at 'firstResult', move 'wanted' of them to 'res'.
+** Handle most typical cases (zero results for commands, one result for
+** expressions, multiple results for tail calls/single parameters)
+** separated.
+*/
+l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) {
+ StkId firstresult;
+ int i;
+ switch (wanted) { /* handle typical cases separately */
+ case 0: /* no values needed */
+ L->top.p = res;
+ return;
+ case 1: /* one value needed */
+ if (nres == 0) /* no results? */
+ setnilvalue(s2v(res)); /* adjust with nil */
+ else /* at least one result */
+ setobjs2s(L, res, L->top.p - nres); /* move it to proper place */
+ L->top.p = res + 1;
+ return;
+ case LUA_MULTRET:
+ wanted = nres; /* we want all results */
+ break;
+ default: /* two/more results and/or to-be-closed variables */
+ if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */
+ L->ci->callstatus |= CIST_CLSRET; /* in case of yields */
+ L->ci->u2.nres = nres;
+ res = luaF_close(L, res, CLOSEKTOP, 1);
+ L->ci->callstatus &= ~CIST_CLSRET;
+ if (L->hookmask) { /* if needed, call hook after '__close's */
+ ptrdiff_t savedres = savestack(L, res);
+ rethook(L, L->ci, nres);
+ res = restorestack(L, savedres); /* hook can move stack */
+ }
+ wanted = decodeNresults(wanted);
+ if (wanted == LUA_MULTRET)
+ wanted = nres; /* we want all results */
+ }
+ break;
+ }
+ /* generic case */
+ firstresult = L->top.p - nres; /* index of first result */
+ if (nres > wanted) /* extra results? */
+ nres = wanted; /* don't need them */
+ for (i = 0; i < nres; i++) /* move all results to correct place */
+ setobjs2s(L, res + i, firstresult + i);
+ for (; i < wanted; i++) /* complete wanted number of results */
+ setnilvalue(s2v(res + i));
+ L->top.p = res + wanted; /* top points after the last result */
+}
+
+
+/*
+** Finishes a function call: calls hook if necessary, moves current
+** number of results to proper place, and returns to previous call
+** info. If function has to close variables, hook must be called after
+** that.
+*/
+void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
+ int wanted = ci->nresults;
+ if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted)))
+ rethook(L, ci, nres);
+ /* move results to proper place */
+ moveresults(L, ci->func.p, nres, wanted);
+ /* function cannot be in any of these cases when returning */
+ lua_assert(!(ci->callstatus &
+ (CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET)));
+ L->ci = ci->previous; /* back to caller (after closing variables) */
+}
+
+
+
+#define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L))
+
+
+l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret,
+ int mask, StkId top) {
+ CallInfo *ci = L->ci = next_ci(L); /* new frame */
+ ci->func.p = func;
+ ci->nresults = nret;
+ ci->callstatus = mask;
+ ci->top.p = top;
+ return ci;
+}
+
+
+/*
+** precall for C functions
+*/
+l_sinline int precallC (lua_State *L, StkId func, int nresults,
+ lua_CFunction f) {
+ int n; /* number of returns */
+ CallInfo *ci;
+ checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
+ L->ci = ci = prepCallInfo(L, func, nresults, CIST_C,
+ L->top.p + LUA_MINSTACK);
+ lua_assert(ci->top.p <= L->stack_last.p);
+ if (l_unlikely(L->hookmask & LUA_MASKCALL)) {
+ int narg = cast_int(L->top.p - func) - 1;
+ luaD_hook(L, LUA_HOOKCALL, -1, 1, narg);
+ }
+ lua_unlock(L);
+ n = (*f)(L); /* do the actual call */
+ lua_lock(L);
+ api_checknelems(L, n);
+ luaD_poscall(L, ci, n);
+ return n;
+}
+
+
+/*
+** Prepare a function for a tail call, building its call info on top
+** of the current call info. 'narg1' is the number of arguments plus 1
+** (so that it includes the function itself). Return the number of
+** results, if it was a C function, or -1 for a Lua function.
+*/
+int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
+ int narg1, int delta) {
+ retry:
+ switch (ttypetag(s2v(func))) {
+ case LUA_VCCL: /* C closure */
+ return precallC(L, func, LUA_MULTRET, clCvalue(s2v(func))->f);
+ case LUA_VLCF: /* light C function */
+ return precallC(L, func, LUA_MULTRET, fvalue(s2v(func)));
+ case LUA_VLCL: { /* Lua function */
+ Proto *p = clLvalue(s2v(func))->p;
+ int fsize = p->maxstacksize; /* frame size */
+ int nfixparams = p->numparams;
+ int i;
+ checkstackGCp(L, fsize - delta, func);
+ ci->func.p -= delta; /* restore 'func' (if vararg) */
+ for (i = 0; i < narg1; i++) /* move down function and arguments */
+ setobjs2s(L, ci->func.p + i, func + i);
+ func = ci->func.p; /* moved-down function */
+ for (; narg1 <= nfixparams; narg1++)
+ setnilvalue(s2v(func + narg1)); /* complete missing arguments */
+ ci->top.p = func + 1 + fsize; /* top for new function */
+ lua_assert(ci->top.p <= L->stack_last.p);
+ ci->u.l.savedpc = p->code; /* starting point */
+ ci->callstatus |= CIST_TAIL;
+ L->top.p = func + narg1; /* set top */
+ return -1;
+ }
+ default: { /* not a function */
+ func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
+ /* return luaD_pretailcall(L, ci, func, narg1 + 1, delta); */
+ narg1++;
+ goto retry; /* try again */
+ }
+ }
+}
+
+
+/*
+** Prepares the call to a function (C or Lua). For C functions, also do
+** the call. The function to be called is at '*func'. The arguments
+** are on the stack, right after the function. Returns the CallInfo
+** to be executed, if it was a Lua function. Otherwise (a C function)
+** returns NULL, with all the results on the stack, starting at the
+** original function position.
+*/
+CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
+ retry:
+ switch (ttypetag(s2v(func))) {
+ case LUA_VCCL: /* C closure */
+ precallC(L, func, nresults, clCvalue(s2v(func))->f);
+ return NULL;
+ case LUA_VLCF: /* light C function */
+ precallC(L, func, nresults, fvalue(s2v(func)));
+ return NULL;
+ case LUA_VLCL: { /* Lua function */
+ CallInfo *ci;
+ Proto *p = clLvalue(s2v(func))->p;
+ int narg = cast_int(L->top.p - func) - 1; /* number of real arguments */
+ int nfixparams = p->numparams;
+ int fsize = p->maxstacksize; /* frame size */
+ checkstackGCp(L, fsize, func);
+ L->ci = ci = prepCallInfo(L, func, nresults, 0, func + 1 + fsize);
+ ci->u.l.savedpc = p->code; /* starting point */
+ for (; narg < nfixparams; narg++)
+ setnilvalue(s2v(L->top.p++)); /* complete missing arguments */
+ lua_assert(ci->top.p <= L->stack_last.p);
+ return ci;
+ }
+ default: { /* not a function */
+ func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
+ /* return luaD_precall(L, func, nresults); */
+ goto retry; /* try again with metamethod */
+ }
+ }
+}
+
+
+/*
+** Call a function (C or Lua) through C. 'inc' can be 1 (increment
+** number of recursive invocations in the C stack) or nyci (the same
+** plus increment number of non-yieldable calls).
+** This function can be called with some use of EXTRA_STACK, so it should
+** check the stack before doing anything else. 'luaD_precall' already
+** does that.
+*/
+l_sinline void ccall (lua_State *L, StkId func, int nResults, l_uint32 inc) {
+ CallInfo *ci;
+ L->nCcalls += inc;
+ if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) {
+ checkstackp(L, 0, func); /* free any use of EXTRA_STACK */
+ luaE_checkcstack(L);
+ }
+ if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */
+ ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */
+ luaV_execute(L, ci); /* call it */
+ }
+ L->nCcalls -= inc;
+}
+
+
+/*
+** External interface for 'ccall'
+*/
+void luaD_call (lua_State *L, StkId func, int nResults) {
+ ccall(L, func, nResults, 1);
+}
+
+
+/*
+** Similar to 'luaD_call', but does not allow yields during the call.
+*/
+void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
+ ccall(L, func, nResults, nyci);
+}
+
+
+/*
+** Finish the job of 'lua_pcallk' after it was interrupted by an yield.
+** (The caller, 'finishCcall', does the final call to 'adjustresults'.)
+** The main job is to complete the 'luaD_pcall' called by 'lua_pcallk'.
+** If a '__close' method yields here, eventually control will be back
+** to 'finishCcall' (when that '__close' method finally returns) and
+** 'finishpcallk' will run again and close any still pending '__close'
+** methods. Similarly, if a '__close' method errs, 'precover' calls
+** 'unroll' which calls ''finishCcall' and we are back here again, to
+** close any pending '__close' methods.
+** Note that, up to the call to 'luaF_close', the corresponding
+** 'CallInfo' is not modified, so that this repeated run works like the
+** first one (except that it has at least one less '__close' to do). In
+** particular, field CIST_RECST preserves the error status across these
+** multiple runs, changing only if there is a new error.
+*/
+static int finishpcallk (lua_State *L, CallInfo *ci) {
+ int status = getcistrecst(ci); /* get original status */
+ if (l_likely(status == LUA_OK)) /* no error? */
+ status = LUA_YIELD; /* was interrupted by an yield */
+ else { /* error */
+ StkId func = restorestack(L, ci->u2.funcidx);
+ L->allowhook = getoah(ci->callstatus); /* restore 'allowhook' */
+ func = luaF_close(L, func, status, 1); /* can yield or raise an error */
+ luaD_seterrorobj(L, status, func);
+ luaD_shrinkstack(L); /* restore stack size in case of overflow */
+ setcistrecst(ci, LUA_OK); /* clear original status */
+ }
+ ci->callstatus &= ~CIST_YPCALL;
+ L->errfunc = ci->u.c.old_errfunc;
+ /* if it is here, there were errors or yields; unlike 'lua_pcallk',
+ do not change status */
+ return status;
+}
+
+
+/*
+** Completes the execution of a C function interrupted by an yield.
+** The interruption must have happened while the function was either
+** closing its tbc variables in 'moveresults' or executing
+** 'lua_callk'/'lua_pcallk'. In the first case, it just redoes
+** 'luaD_poscall'. In the second case, the call to 'finishpcallk'
+** finishes the interrupted execution of 'lua_pcallk'. After that, it
+** calls the continuation of the interrupted function and finally it
+** completes the job of the 'luaD_call' that called the function. In
+** the call to 'adjustresults', we do not know the number of results
+** of the function called by 'lua_callk'/'lua_pcallk', so we are
+** conservative and use LUA_MULTRET (always adjust).
+*/
+static void finishCcall (lua_State *L, CallInfo *ci) {
+ int n; /* actual number of results from C function */
+ if (ci->callstatus & CIST_CLSRET) { /* was returning? */
+ lua_assert(hastocloseCfunc(ci->nresults));
+ n = ci->u2.nres; /* just redo 'luaD_poscall' */
+ /* don't need to reset CIST_CLSRET, as it will be set again anyway */
+ }
+ else {
+ int status = LUA_YIELD; /* default if there were no errors */
+ /* must have a continuation and must be able to call it */
+ lua_assert(ci->u.c.k != NULL && yieldable(L));
+ if (ci->callstatus & CIST_YPCALL) /* was inside a 'lua_pcallk'? */
+ status = finishpcallk(L, ci); /* finish it */
+ adjustresults(L, LUA_MULTRET); /* finish 'lua_callk' */
+ lua_unlock(L);
+ n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation */
+ lua_lock(L);
+ api_checknelems(L, n);
+ }
+ luaD_poscall(L, ci, n); /* finish 'luaD_call' */
+}
+
+
+/*
+** Executes "full continuation" (everything in the stack) of a
+** previously interrupted coroutine until the stack is empty (or another
+** interruption long-jumps out of the loop).
+*/
+static void unroll (lua_State *L, void *ud) {
+ CallInfo *ci;
+ UNUSED(ud);
+ while ((ci = L->ci) != &L->base_ci) { /* something in the stack */
+ if (!isLua(ci)) /* C function? */
+ finishCcall(L, ci); /* complete its execution */
+ else { /* Lua function */
+ luaV_finishOp(L); /* finish interrupted instruction */
+ luaV_execute(L, ci); /* execute down to higher C 'boundary' */
+ }
+ }
+}
+
+
+/*
+** Try to find a suspended protected call (a "recover point") for the
+** given thread.
+*/
+static CallInfo *findpcall (lua_State *L) {
+ CallInfo *ci;
+ for (ci = L->ci; ci != NULL; ci = ci->previous) { /* search for a pcall */
+ if (ci->callstatus & CIST_YPCALL)
+ return ci;
+ }
+ return NULL; /* no pending pcall */
+}
+
+
+/*
+** Signal an error in the call to 'lua_resume', not in the execution
+** of the coroutine itself. (Such errors should not be handled by any
+** coroutine error handler and should not kill the coroutine.)
+*/
+static int resume_error (lua_State *L, const char *msg, int narg) {
+ L->top.p -= narg; /* remove args from the stack */
+ setsvalue2s(L, L->top.p, luaS_new(L, msg)); /* push error message */
+ api_incr_top(L);
+ lua_unlock(L);
+ return LUA_ERRRUN;
+}
+
+
+/*
+** Do the work for 'lua_resume' in protected mode. Most of the work
+** depends on the status of the coroutine: initial state, suspended
+** inside a hook, or regularly suspended (optionally with a continuation
+** function), plus erroneous cases: non-suspended coroutine or dead
+** coroutine.
+*/
+static void resume (lua_State *L, void *ud) {
+ int n = *(cast(int*, ud)); /* number of arguments */
+ StkId firstArg = L->top.p - n; /* first argument */
+ CallInfo *ci = L->ci;
+ if (L->status == LUA_OK) /* starting a coroutine? */
+ ccall(L, firstArg - 1, LUA_MULTRET, 0); /* just call its body */
+ else { /* resuming from previous yield */
+ lua_assert(L->status == LUA_YIELD);
+ L->status = LUA_OK; /* mark that it is running (again) */
+ if (isLua(ci)) { /* yielded inside a hook? */
+ L->top.p = firstArg; /* discard arguments */
+ luaV_execute(L, ci); /* just continue running Lua code */
+ }
+ else { /* 'common' yield */
+ if (ci->u.c.k != NULL) { /* does it have a continuation function? */
+ lua_unlock(L);
+ n = (*ci->u.c.k)(L, LUA_YIELD, ci->u.c.ctx); /* call continuation */
+ lua_lock(L);
+ api_checknelems(L, n);
+ }
+ luaD_poscall(L, ci, n); /* finish 'luaD_call' */
+ }
+ unroll(L, NULL); /* run continuation */
+ }
+}
+
+
+/*
+** Unrolls a coroutine in protected mode while there are recoverable
+** errors, that is, errors inside a protected call. (Any error
+** interrupts 'unroll', and this loop protects it again so it can
+** continue.) Stops with a normal end (status == LUA_OK), an yield
+** (status == LUA_YIELD), or an unprotected error ('findpcall' doesn't
+** find a recover point).
+*/
+static int precover (lua_State *L, int status) {
+ CallInfo *ci;
+ while (errorstatus(status) && (ci = findpcall(L)) != NULL) {
+ L->ci = ci; /* go down to recovery functions */
+ setcistrecst(ci, status); /* status to finish 'pcall' */
+ status = luaD_rawrunprotected(L, unroll, NULL);
+ }
+ return status;
+}
+
+
+LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
+ int *nresults) {
+ int status;
+ lua_lock(L);
+ if (L->status == LUA_OK) { /* may be starting a coroutine */
+ if (L->ci != &L->base_ci) /* not in base level? */
+ return resume_error(L, "cannot resume non-suspended coroutine", nargs);
+ else if (L->top.p - (L->ci->func.p + 1) == nargs) /* no function? */
+ return resume_error(L, "cannot resume dead coroutine", nargs);
+ }
+ else if (L->status != LUA_YIELD) /* ended with errors? */
+ return resume_error(L, "cannot resume dead coroutine", nargs);
+ L->nCcalls = (from) ? getCcalls(from) : 0;
+ if (getCcalls(L) >= LUAI_MAXCCALLS)
+ return resume_error(L, "C stack overflow", nargs);
+ L->nCcalls++;
+ luai_userstateresume(L, nargs);
+ api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
+ status = luaD_rawrunprotected(L, resume, &nargs);
+ /* continue running after recoverable errors */
+ status = precover(L, status);
+ if (l_likely(!errorstatus(status)))
+ lua_assert(status == L->status); /* normal end or yield */
+ else { /* unrecoverable error */
+ L->status = cast_byte(status); /* mark thread as 'dead' */
+ luaD_seterrorobj(L, status, L->top.p); /* push error message */
+ L->ci->top.p = L->top.p;
+ }
+ *nresults = (status == LUA_YIELD) ? L->ci->u2.nyield
+ : cast_int(L->top.p - (L->ci->func.p + 1));
+ lua_unlock(L);
+ return status;
+}
+
+
+LUA_API int lua_isyieldable (lua_State *L) {
+ return yieldable(L);
+}
+
+
+LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
+ lua_KFunction k) {
+ CallInfo *ci;
+ luai_userstateyield(L, nresults);
+ lua_lock(L);
+ ci = L->ci;
+ api_checknelems(L, nresults);
+ if (l_unlikely(!yieldable(L))) {
+ if (L != G(L)->mainthread)
+ luaG_runerror(L, "attempt to yield across a C-call boundary");
+ else
+ luaG_runerror(L, "attempt to yield from outside a coroutine");
+ }
+ L->status = LUA_YIELD;
+ ci->u2.nyield = nresults; /* save number of results */
+ if (isLua(ci)) { /* inside a hook? */
+ lua_assert(!isLuacode(ci));
+ api_check(L, nresults == 0, "hooks cannot yield values");
+ api_check(L, k == NULL, "hooks cannot continue after yielding");
+ }
+ else {
+ if ((ci->u.c.k = k) != NULL) /* is there a continuation? */
+ ci->u.c.ctx = ctx; /* save context */
+ luaD_throw(L, LUA_YIELD);
+ }
+ lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */
+ lua_unlock(L);
+ return 0; /* return to 'luaD_hook' */
+}
+
+
+/*
+** Auxiliary structure to call 'luaF_close' in protected mode.
+*/
+struct CloseP {
+ StkId level;
+ int status;
+};
+
+
+/*
+** Auxiliary function to call 'luaF_close' in protected mode.
+*/
+static void closepaux (lua_State *L, void *ud) {
+ struct CloseP *pcl = cast(struct CloseP *, ud);
+ luaF_close(L, pcl->level, pcl->status, 0);
+}
+
+
+/*
+** Calls 'luaF_close' in protected mode. Return the original status
+** or, in case of errors, the new status.
+*/
+int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status) {
+ CallInfo *old_ci = L->ci;
+ lu_byte old_allowhooks = L->allowhook;
+ for (;;) { /* keep closing upvalues until no more errors */
+ struct CloseP pcl;
+ pcl.level = restorestack(L, level); pcl.status = status;
+ status = luaD_rawrunprotected(L, &closepaux, &pcl);
+ if (l_likely(status == LUA_OK)) /* no more errors? */
+ return pcl.status;
+ else { /* an error occurred; restore saved state and repeat */
+ L->ci = old_ci;
+ L->allowhook = old_allowhooks;
+ }
+ }
+}
+
+
+/*
+** Call the C function 'func' in protected mode, restoring basic
+** thread information ('allowhook', etc.) and in particular
+** its stack level in case of errors.
+*/
+int luaD_pcall (lua_State *L, Pfunc func, void *u,
+ ptrdiff_t old_top, ptrdiff_t ef) {
+ int status;
+ CallInfo *old_ci = L->ci;
+ lu_byte old_allowhooks = L->allowhook;
+ ptrdiff_t old_errfunc = L->errfunc;
+ L->errfunc = ef;
+ status = luaD_rawrunprotected(L, func, u);
+ if (l_unlikely(status != LUA_OK)) { /* an error occurred? */
+ L->ci = old_ci;
+ L->allowhook = old_allowhooks;
+ status = luaD_closeprotected(L, old_top, status);
+ luaD_seterrorobj(L, status, restorestack(L, old_top));
+ luaD_shrinkstack(L); /* restore stack size in case of overflow */
+ }
+ L->errfunc = old_errfunc;
+ return status;
+}
+
+
+
+/*
+** Execute a protected parser.
+*/
+struct SParser { /* data to 'f_parser' */
+ ZIO *z;
+ Mbuffer buff; /* dynamic structure used by the scanner */
+ Dyndata dyd; /* dynamic structures used by the parser */
+ const char *mode;
+ const char *name;
+};
+
+
+static void checkmode (lua_State *L, const char *mode, const char *x) {
+ if (mode && strchr(mode, x[0]) == NULL) {
+ luaO_pushfstring(L,
+ "attempt to load a %s chunk (mode is '%s')", x, mode);
+ luaD_throw(L, LUA_ERRSYNTAX);
+ }
+}
+
+
+static void f_parser (lua_State *L, void *ud) {
+ LClosure *cl;
+ struct SParser *p = cast(struct SParser *, ud);
+ int c = zgetc(p->z); /* read first character */
+ if (c == LUA_SIGNATURE[0]) {
+ checkmode(L, p->mode, "binary");
+ cl = luaU_undump(L, p->z, p->name);
+ }
+ else {
+ checkmode(L, p->mode, "text");
+ cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c);
+ }
+ lua_assert(cl->nupvalues == cl->p->sizeupvalues);
+ luaF_initupvals(L, cl);
+}
+
+
+int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
+ const char *mode) {
+ struct SParser p;
+ int status;
+ incnny(L); /* cannot yield during parsing */
+ p.z = z; p.name = name; p.mode = mode;
+ p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0;
+ p.dyd.gt.arr = NULL; p.dyd.gt.size = 0;
+ p.dyd.label.arr = NULL; p.dyd.label.size = 0;
+ luaZ_initbuffer(L, &p.buff);
+ status = luaD_pcall(L, f_parser, &p, savestack(L, L->top.p), L->errfunc);
+ luaZ_freebuffer(L, &p.buff);
+ luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size);
+ luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size);
+ luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size);
+ decnny(L);
+ return status;
+}
+
+
diff --git a/src/libs/3rdparty/lua/src/ldo.h b/src/libs/3rdparty/lua/src/ldo.h
new file mode 100644
index 0000000000..1aa446ad09
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ldo.h
@@ -0,0 +1,88 @@
+/*
+** $Id: ldo.h $
+** Stack and Call structure of Lua
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ldo_h
+#define ldo_h
+
+
+#include "llimits.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lzio.h"
+
+
+/*
+** Macro to check stack size and grow stack if needed. Parameters
+** 'pre'/'pos' allow the macro to preserve a pointer into the
+** stack across reallocations, doing the work only when needed.
+** It also allows the running of one GC step when the stack is
+** reallocated.
+** 'condmovestack' is used in heavy tests to force a stack reallocation
+** at every check.
+*/
+#define luaD_checkstackaux(L,n,pre,pos) \
+ if (l_unlikely(L->stack_last.p - L->top.p <= (n))) \
+ { pre; luaD_growstack(L, n, 1); pos; } \
+ else { condmovestack(L,pre,pos); }
+
+/* In general, 'pre'/'pos' are empty (nothing to save) */
+#define luaD_checkstack(L,n) luaD_checkstackaux(L,n,(void)0,(void)0)
+
+
+
+#define savestack(L,pt) (cast_charp(pt) - cast_charp(L->stack.p))
+#define restorestack(L,n) cast(StkId, cast_charp(L->stack.p) + (n))
+
+
+/* macro to check stack size, preserving 'p' */
+#define checkstackp(L,n,p) \
+ luaD_checkstackaux(L, n, \
+ ptrdiff_t t__ = savestack(L, p), /* save 'p' */ \
+ p = restorestack(L, t__)) /* 'pos' part: restore 'p' */
+
+
+/* macro to check stack size and GC, preserving 'p' */
+#define checkstackGCp(L,n,p) \
+ luaD_checkstackaux(L, n, \
+ ptrdiff_t t__ = savestack(L, p); /* save 'p' */ \
+ luaC_checkGC(L), /* stack grow uses memory */ \
+ p = restorestack(L, t__)) /* 'pos' part: restore 'p' */
+
+
+/* macro to check stack size and GC */
+#define checkstackGC(L,fsize) \
+ luaD_checkstackaux(L, (fsize), luaC_checkGC(L), (void)0)
+
+
+/* type of protected functions, to be ran by 'runprotected' */
+typedef void (*Pfunc) (lua_State *L, void *ud);
+
+LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop);
+LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
+ const char *mode);
+LUAI_FUNC void luaD_hook (lua_State *L, int event, int line,
+ int fTransfer, int nTransfer);
+LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci);
+LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
+ int narg1, int delta);
+LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults);
+LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
+LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
+LUAI_FUNC StkId luaD_tryfuncTM (lua_State *L, StkId func);
+LUAI_FUNC int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status);
+LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
+ ptrdiff_t oldtop, ptrdiff_t ef);
+LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres);
+LUAI_FUNC int luaD_reallocstack (lua_State *L, int newsize, int raiseerror);
+LUAI_FUNC int luaD_growstack (lua_State *L, int n, int raiseerror);
+LUAI_FUNC void luaD_shrinkstack (lua_State *L);
+LUAI_FUNC void luaD_inctop (lua_State *L);
+
+LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode);
+LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);
+
+#endif
+
diff --git a/src/libs/3rdparty/lua/src/ldump.c b/src/libs/3rdparty/lua/src/ldump.c
new file mode 100644
index 0000000000..f231691b77
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ldump.c
@@ -0,0 +1,230 @@
+/*
+** $Id: ldump.c $
+** save precompiled Lua chunks
+** See Copyright Notice in lua.h
+*/
+
+#define ldump_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <limits.h>
+#include <stddef.h>
+
+#include "lua.h"
+
+#include "lobject.h"
+#include "lstate.h"
+#include "lundump.h"
+
+
+typedef struct {
+ lua_State *L;
+ lua_Writer writer;
+ void *data;
+ int strip;
+ int status;
+} DumpState;
+
+
+/*
+** All high-level dumps go through dumpVector; you can change it to
+** change the endianness of the result
+*/
+#define dumpVector(D,v,n) dumpBlock(D,v,(n)*sizeof((v)[0]))
+
+#define dumpLiteral(D, s) dumpBlock(D,s,sizeof(s) - sizeof(char))
+
+
+static void dumpBlock (DumpState *D, const void *b, size_t size) {
+ if (D->status == 0 && size > 0) {
+ lua_unlock(D->L);
+ D->status = (*D->writer)(D->L, b, size, D->data);
+ lua_lock(D->L);
+ }
+}
+
+
+#define dumpVar(D,x) dumpVector(D,&x,1)
+
+
+static void dumpByte (DumpState *D, int y) {
+ lu_byte x = (lu_byte)y;
+ dumpVar(D, x);
+}
+
+
+/*
+** 'dumpSize' buffer size: each byte can store up to 7 bits. (The "+6"
+** rounds up the division.)
+*/
+#define DIBS ((sizeof(size_t) * CHAR_BIT + 6) / 7)
+
+static void dumpSize (DumpState *D, size_t x) {
+ lu_byte buff[DIBS];
+ int n = 0;
+ do {
+ buff[DIBS - (++n)] = x & 0x7f; /* fill buffer in reverse order */
+ x >>= 7;
+ } while (x != 0);
+ buff[DIBS - 1] |= 0x80; /* mark last byte */
+ dumpVector(D, buff + DIBS - n, n);
+}
+
+
+static void dumpInt (DumpState *D, int x) {
+ dumpSize(D, x);
+}
+
+
+static void dumpNumber (DumpState *D, lua_Number x) {
+ dumpVar(D, x);
+}
+
+
+static void dumpInteger (DumpState *D, lua_Integer x) {
+ dumpVar(D, x);
+}
+
+
+static void dumpString (DumpState *D, const TString *s) {
+ if (s == NULL)
+ dumpSize(D, 0);
+ else {
+ size_t size = tsslen(s);
+ const char *str = getstr(s);
+ dumpSize(D, size + 1);
+ dumpVector(D, str, size);
+ }
+}
+
+
+static void dumpCode (DumpState *D, const Proto *f) {
+ dumpInt(D, f->sizecode);
+ dumpVector(D, f->code, f->sizecode);
+}
+
+
+static void dumpFunction(DumpState *D, const Proto *f, TString *psource);
+
+static void dumpConstants (DumpState *D, const Proto *f) {
+ int i;
+ int n = f->sizek;
+ dumpInt(D, n);
+ for (i = 0; i < n; i++) {
+ const TValue *o = &f->k[i];
+ int tt = ttypetag(o);
+ dumpByte(D, tt);
+ switch (tt) {
+ case LUA_VNUMFLT:
+ dumpNumber(D, fltvalue(o));
+ break;
+ case LUA_VNUMINT:
+ dumpInteger(D, ivalue(o));
+ break;
+ case LUA_VSHRSTR:
+ case LUA_VLNGSTR:
+ dumpString(D, tsvalue(o));
+ break;
+ default:
+ lua_assert(tt == LUA_VNIL || tt == LUA_VFALSE || tt == LUA_VTRUE);
+ }
+ }
+}
+
+
+static void dumpProtos (DumpState *D, const Proto *f) {
+ int i;
+ int n = f->sizep;
+ dumpInt(D, n);
+ for (i = 0; i < n; i++)
+ dumpFunction(D, f->p[i], f->source);
+}
+
+
+static void dumpUpvalues (DumpState *D, const Proto *f) {
+ int i, n = f->sizeupvalues;
+ dumpInt(D, n);
+ for (i = 0; i < n; i++) {
+ dumpByte(D, f->upvalues[i].instack);
+ dumpByte(D, f->upvalues[i].idx);
+ dumpByte(D, f->upvalues[i].kind);
+ }
+}
+
+
+static void dumpDebug (DumpState *D, const Proto *f) {
+ int i, n;
+ n = (D->strip) ? 0 : f->sizelineinfo;
+ dumpInt(D, n);
+ dumpVector(D, f->lineinfo, n);
+ n = (D->strip) ? 0 : f->sizeabslineinfo;
+ dumpInt(D, n);
+ for (i = 0; i < n; i++) {
+ dumpInt(D, f->abslineinfo[i].pc);
+ dumpInt(D, f->abslineinfo[i].line);
+ }
+ n = (D->strip) ? 0 : f->sizelocvars;
+ dumpInt(D, n);
+ for (i = 0; i < n; i++) {
+ dumpString(D, f->locvars[i].varname);
+ dumpInt(D, f->locvars[i].startpc);
+ dumpInt(D, f->locvars[i].endpc);
+ }
+ n = (D->strip) ? 0 : f->sizeupvalues;
+ dumpInt(D, n);
+ for (i = 0; i < n; i++)
+ dumpString(D, f->upvalues[i].name);
+}
+
+
+static void dumpFunction (DumpState *D, const Proto *f, TString *psource) {
+ if (D->strip || f->source == psource)
+ dumpString(D, NULL); /* no debug info or same source as its parent */
+ else
+ dumpString(D, f->source);
+ dumpInt(D, f->linedefined);
+ dumpInt(D, f->lastlinedefined);
+ dumpByte(D, f->numparams);
+ dumpByte(D, f->is_vararg);
+ dumpByte(D, f->maxstacksize);
+ dumpCode(D, f);
+ dumpConstants(D, f);
+ dumpUpvalues(D, f);
+ dumpProtos(D, f);
+ dumpDebug(D, f);
+}
+
+
+static void dumpHeader (DumpState *D) {
+ dumpLiteral(D, LUA_SIGNATURE);
+ dumpByte(D, LUAC_VERSION);
+ dumpByte(D, LUAC_FORMAT);
+ dumpLiteral(D, LUAC_DATA);
+ dumpByte(D, sizeof(Instruction));
+ dumpByte(D, sizeof(lua_Integer));
+ dumpByte(D, sizeof(lua_Number));
+ dumpInteger(D, LUAC_INT);
+ dumpNumber(D, LUAC_NUM);
+}
+
+
+/*
+** dump Lua function as precompiled chunk
+*/
+int luaU_dump(lua_State *L, const Proto *f, lua_Writer w, void *data,
+ int strip) {
+ DumpState D;
+ D.L = L;
+ D.writer = w;
+ D.data = data;
+ D.strip = strip;
+ D.status = 0;
+ dumpHeader(&D);
+ dumpByte(&D, f->sizeupvalues);
+ dumpFunction(&D, f, NULL);
+ return D.status;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lfunc.c b/src/libs/3rdparty/lua/src/lfunc.c
new file mode 100644
index 0000000000..0945f241de
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lfunc.c
@@ -0,0 +1,294 @@
+/*
+** $Id: lfunc.c $
+** Auxiliary functions to manipulate prototypes and closures
+** See Copyright Notice in lua.h
+*/
+
+#define lfunc_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <stddef.h>
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+
+CClosure *luaF_newCclosure (lua_State *L, int nupvals) {
+ GCObject *o = luaC_newobj(L, LUA_VCCL, sizeCclosure(nupvals));
+ CClosure *c = gco2ccl(o);
+ c->nupvalues = cast_byte(nupvals);
+ return c;
+}
+
+
+LClosure *luaF_newLclosure (lua_State *L, int nupvals) {
+ GCObject *o = luaC_newobj(L, LUA_VLCL, sizeLclosure(nupvals));
+ LClosure *c = gco2lcl(o);
+ c->p = NULL;
+ c->nupvalues = cast_byte(nupvals);
+ while (nupvals--) c->upvals[nupvals] = NULL;
+ return c;
+}
+
+
+/*
+** fill a closure with new closed upvalues
+*/
+void luaF_initupvals (lua_State *L, LClosure *cl) {
+ int i;
+ for (i = 0; i < cl->nupvalues; i++) {
+ GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal));
+ UpVal *uv = gco2upv(o);
+ uv->v.p = &uv->u.value; /* make it closed */
+ setnilvalue(uv->v.p);
+ cl->upvals[i] = uv;
+ luaC_objbarrier(L, cl, uv);
+ }
+}
+
+
+/*
+** Create a new upvalue at the given level, and link it to the list of
+** open upvalues of 'L' after entry 'prev'.
+**/
+static UpVal *newupval (lua_State *L, StkId level, UpVal **prev) {
+ GCObject *o = luaC_newobj(L, LUA_VUPVAL, sizeof(UpVal));
+ UpVal *uv = gco2upv(o);
+ UpVal *next = *prev;
+ uv->v.p = s2v(level); /* current value lives in the stack */
+ uv->u.open.next = next; /* link it to list of open upvalues */
+ uv->u.open.previous = prev;
+ if (next)
+ next->u.open.previous = &uv->u.open.next;
+ *prev = uv;
+ if (!isintwups(L)) { /* thread not in list of threads with upvalues? */
+ L->twups = G(L)->twups; /* link it to the list */
+ G(L)->twups = L;
+ }
+ return uv;
+}
+
+
+/*
+** Find and reuse, or create if it does not exist, an upvalue
+** at the given level.
+*/
+UpVal *luaF_findupval (lua_State *L, StkId level) {
+ UpVal **pp = &L->openupval;
+ UpVal *p;
+ lua_assert(isintwups(L) || L->openupval == NULL);
+ while ((p = *pp) != NULL && uplevel(p) >= level) { /* search for it */
+ lua_assert(!isdead(G(L), p));
+ if (uplevel(p) == level) /* corresponding upvalue? */
+ return p; /* return it */
+ pp = &p->u.open.next;
+ }
+ /* not found: create a new upvalue after 'pp' */
+ return newupval(L, level, pp);
+}
+
+
+/*
+** Call closing method for object 'obj' with error message 'err'. The
+** boolean 'yy' controls whether the call is yieldable.
+** (This function assumes EXTRA_STACK.)
+*/
+static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) {
+ StkId top = L->top.p;
+ const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
+ setobj2s(L, top, tm); /* will call metamethod... */
+ setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */
+ setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */
+ L->top.p = top + 3; /* add function and arguments */
+ if (yy)
+ luaD_call(L, top, 0);
+ else
+ luaD_callnoyield(L, top, 0);
+}
+
+
+/*
+** Check whether object at given level has a close metamethod and raise
+** an error if not.
+*/
+static void checkclosemth (lua_State *L, StkId level) {
+ const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE);
+ if (ttisnil(tm)) { /* no metamethod? */
+ int idx = cast_int(level - L->ci->func.p); /* variable index */
+ const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
+ if (vname == NULL) vname = "?";
+ luaG_runerror(L, "variable '%s' got a non-closable value", vname);
+ }
+}
+
+
+/*
+** Prepare and call a closing method.
+** If status is CLOSEKTOP, the call to the closing method will be pushed
+** at the top of the stack. Otherwise, values can be pushed right after
+** the 'level' of the upvalue being closed, as everything after that
+** won't be used again.
+*/
+static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) {
+ TValue *uv = s2v(level); /* value being closed */
+ TValue *errobj;
+ if (status == CLOSEKTOP)
+ errobj = &G(L)->nilvalue; /* error object is nil */
+ else { /* 'luaD_seterrorobj' will set top to level + 2 */
+ errobj = s2v(level + 1); /* error object goes after 'uv' */
+ luaD_seterrorobj(L, status, level + 1); /* set error object */
+ }
+ callclosemethod(L, uv, errobj, yy);
+}
+
+
+/*
+** Maximum value for deltas in 'tbclist', dependent on the type
+** of delta. (This macro assumes that an 'L' is in scope where it
+** is used.)
+*/
+#define MAXDELTA \
+ ((256ul << ((sizeof(L->stack.p->tbclist.delta) - 1) * 8)) - 1)
+
+
+/*
+** Insert a variable in the list of to-be-closed variables.
+*/
+void luaF_newtbcupval (lua_State *L, StkId level) {
+ lua_assert(level > L->tbclist.p);
+ if (l_isfalse(s2v(level)))
+ return; /* false doesn't need to be closed */
+ checkclosemth(L, level); /* value must have a close method */
+ while (cast_uint(level - L->tbclist.p) > MAXDELTA) {
+ L->tbclist.p += MAXDELTA; /* create a dummy node at maximum delta */
+ L->tbclist.p->tbclist.delta = 0;
+ }
+ level->tbclist.delta = cast(unsigned short, level - L->tbclist.p);
+ L->tbclist.p = level;
+}
+
+
+void luaF_unlinkupval (UpVal *uv) {
+ lua_assert(upisopen(uv));
+ *uv->u.open.previous = uv->u.open.next;
+ if (uv->u.open.next)
+ uv->u.open.next->u.open.previous = uv->u.open.previous;
+}
+
+
+/*
+** Close all upvalues up to the given stack level.
+*/
+void luaF_closeupval (lua_State *L, StkId level) {
+ UpVal *uv;
+ StkId upl; /* stack index pointed by 'uv' */
+ while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
+ TValue *slot = &uv->u.value; /* new position for value */
+ lua_assert(uplevel(uv) < L->top.p);
+ luaF_unlinkupval(uv); /* remove upvalue from 'openupval' list */
+ setobj(L, slot, uv->v.p); /* move value to upvalue slot */
+ uv->v.p = slot; /* now current value lives here */
+ if (!iswhite(uv)) { /* neither white nor dead? */
+ nw2black(uv); /* closed upvalues cannot be gray */
+ luaC_barrier(L, uv, slot);
+ }
+ }
+}
+
+
+/*
+** Remove first element from the tbclist plus its dummy nodes.
+*/
+static void poptbclist (lua_State *L) {
+ StkId tbc = L->tbclist.p;
+ lua_assert(tbc->tbclist.delta > 0); /* first element cannot be dummy */
+ tbc -= tbc->tbclist.delta;
+ while (tbc > L->stack.p && tbc->tbclist.delta == 0)
+ tbc -= MAXDELTA; /* remove dummy nodes */
+ L->tbclist.p = tbc;
+}
+
+
+/*
+** Close all upvalues and to-be-closed variables up to the given stack
+** level. Return restored 'level'.
+*/
+StkId luaF_close (lua_State *L, StkId level, int status, int yy) {
+ ptrdiff_t levelrel = savestack(L, level);
+ luaF_closeupval(L, level); /* first, close the upvalues */
+ while (L->tbclist.p >= level) { /* traverse tbc's down to that level */
+ StkId tbc = L->tbclist.p; /* get variable index */
+ poptbclist(L); /* remove it from list */
+ prepcallclosemth(L, tbc, status, yy); /* close variable */
+ level = restorestack(L, levelrel);
+ }
+ return level;
+}
+
+
+Proto *luaF_newproto (lua_State *L) {
+ GCObject *o = luaC_newobj(L, LUA_VPROTO, sizeof(Proto));
+ Proto *f = gco2p(o);
+ f->k = NULL;
+ f->sizek = 0;
+ f->p = NULL;
+ f->sizep = 0;
+ f->code = NULL;
+ f->sizecode = 0;
+ f->lineinfo = NULL;
+ f->sizelineinfo = 0;
+ f->abslineinfo = NULL;
+ f->sizeabslineinfo = 0;
+ f->upvalues = NULL;
+ f->sizeupvalues = 0;
+ f->numparams = 0;
+ f->is_vararg = 0;
+ f->maxstacksize = 0;
+ f->locvars = NULL;
+ f->sizelocvars = 0;
+ f->linedefined = 0;
+ f->lastlinedefined = 0;
+ f->source = NULL;
+ return f;
+}
+
+
+void luaF_freeproto (lua_State *L, Proto *f) {
+ luaM_freearray(L, f->code, f->sizecode);
+ luaM_freearray(L, f->p, f->sizep);
+ luaM_freearray(L, f->k, f->sizek);
+ luaM_freearray(L, f->lineinfo, f->sizelineinfo);
+ luaM_freearray(L, f->abslineinfo, f->sizeabslineinfo);
+ luaM_freearray(L, f->locvars, f->sizelocvars);
+ luaM_freearray(L, f->upvalues, f->sizeupvalues);
+ luaM_free(L, f);
+}
+
+
+/*
+** Look for n-th local variable at line 'line' in function 'func'.
+** Returns NULL if not found.
+*/
+const char *luaF_getlocalname (const Proto *f, int local_number, int pc) {
+ int i;
+ for (i = 0; i<f->sizelocvars && f->locvars[i].startpc <= pc; i++) {
+ if (pc < f->locvars[i].endpc) { /* is variable active? */
+ local_number--;
+ if (local_number == 0)
+ return getstr(f->locvars[i].varname);
+ }
+ }
+ return NULL; /* not found */
+}
+
diff --git a/src/libs/3rdparty/lua/src/lfunc.h b/src/libs/3rdparty/lua/src/lfunc.h
new file mode 100644
index 0000000000..3be265efb5
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lfunc.h
@@ -0,0 +1,64 @@
+/*
+** $Id: lfunc.h $
+** Auxiliary functions to manipulate prototypes and closures
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lfunc_h
+#define lfunc_h
+
+
+#include "lobject.h"
+
+
+#define sizeCclosure(n) (cast_int(offsetof(CClosure, upvalue)) + \
+ cast_int(sizeof(TValue)) * (n))
+
+#define sizeLclosure(n) (cast_int(offsetof(LClosure, upvals)) + \
+ cast_int(sizeof(TValue *)) * (n))
+
+
+/* test whether thread is in 'twups' list */
+#define isintwups(L) (L->twups != L)
+
+
+/*
+** maximum number of upvalues in a closure (both C and Lua). (Value
+** must fit in a VM register.)
+*/
+#define MAXUPVAL 255
+
+
+#define upisopen(up) ((up)->v.p != &(up)->u.value)
+
+
+#define uplevel(up) check_exp(upisopen(up), cast(StkId, (up)->v.p))
+
+
+/*
+** maximum number of misses before giving up the cache of closures
+** in prototypes
+*/
+#define MAXMISS 10
+
+
+
+/* special status to close upvalues preserving the top of the stack */
+#define CLOSEKTOP (-1)
+
+
+LUAI_FUNC Proto *luaF_newproto (lua_State *L);
+LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nupvals);
+LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals);
+LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl);
+LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
+LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level);
+LUAI_FUNC void luaF_closeupval (lua_State *L, StkId level);
+LUAI_FUNC StkId luaF_close (lua_State *L, StkId level, int status, int yy);
+LUAI_FUNC void luaF_unlinkupval (UpVal *uv);
+LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
+LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
+ int pc);
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lgc.c b/src/libs/3rdparty/lua/src/lgc.c
new file mode 100644
index 0000000000..a3094ff571
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lgc.c
@@ -0,0 +1,1739 @@
+/*
+** $Id: lgc.c $
+** Garbage Collector
+** See Copyright Notice in lua.h
+*/
+
+#define lgc_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+#include <stdio.h>
+#include <string.h>
+
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+
+
+/*
+** Maximum number of elements to sweep in each single step.
+** (Large enough to dissipate fixed overheads but small enough
+** to allow small steps for the collector.)
+*/
+#define GCSWEEPMAX 100
+
+/*
+** Maximum number of finalizers to call in each single step.
+*/
+#define GCFINMAX 10
+
+
+/*
+** Cost of calling one finalizer.
+*/
+#define GCFINALIZECOST 50
+
+
+/*
+** The equivalent, in bytes, of one unit of "work" (visiting a slot,
+** sweeping an object, etc.)
+*/
+#define WORK2MEM sizeof(TValue)
+
+
+/*
+** macro to adjust 'pause': 'pause' is actually used like
+** 'pause / PAUSEADJ' (value chosen by tests)
+*/
+#define PAUSEADJ 100
+
+
+/* mask with all color bits */
+#define maskcolors (bitmask(BLACKBIT) | WHITEBITS)
+
+/* mask with all GC bits */
+#define maskgcbits (maskcolors | AGEBITS)
+
+
+/* macro to erase all color bits then set only the current white bit */
+#define makewhite(g,x) \
+ (x->marked = cast_byte((x->marked & ~maskcolors) | luaC_white(g)))
+
+/* make an object gray (neither white nor black) */
+#define set2gray(x) resetbits(x->marked, maskcolors)
+
+
+/* make an object black (coming from any color) */
+#define set2black(x) \
+ (x->marked = cast_byte((x->marked & ~WHITEBITS) | bitmask(BLACKBIT)))
+
+
+#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x)))
+
+#define keyiswhite(n) (keyiscollectable(n) && iswhite(gckey(n)))
+
+
+/*
+** Protected access to objects in values
+*/
+#define gcvalueN(o) (iscollectable(o) ? gcvalue(o) : NULL)
+
+
+#define markvalue(g,o) { checkliveness(g->mainthread,o); \
+ if (valiswhite(o)) reallymarkobject(g,gcvalue(o)); }
+
+#define markkey(g, n) { if keyiswhite(n) reallymarkobject(g,gckey(n)); }
+
+#define markobject(g,t) { if (iswhite(t)) reallymarkobject(g, obj2gco(t)); }
+
+/*
+** mark an object that can be NULL (either because it is really optional,
+** or it was stripped as debug info, or inside an uncompleted structure)
+*/
+#define markobjectN(g,t) { if (t) markobject(g,t); }
+
+static void reallymarkobject (global_State *g, GCObject *o);
+static lu_mem atomic (lua_State *L);
+static void entersweep (lua_State *L);
+
+
+/*
+** {======================================================
+** Generic functions
+** =======================================================
+*/
+
+
+/*
+** one after last element in a hash array
+*/
+#define gnodelast(h) gnode(h, cast_sizet(sizenode(h)))
+
+
+static GCObject **getgclist (GCObject *o) {
+ switch (o->tt) {
+ case LUA_VTABLE: return &gco2t(o)->gclist;
+ case LUA_VLCL: return &gco2lcl(o)->gclist;
+ case LUA_VCCL: return &gco2ccl(o)->gclist;
+ case LUA_VTHREAD: return &gco2th(o)->gclist;
+ case LUA_VPROTO: return &gco2p(o)->gclist;
+ case LUA_VUSERDATA: {
+ Udata *u = gco2u(o);
+ lua_assert(u->nuvalue > 0);
+ return &u->gclist;
+ }
+ default: lua_assert(0); return 0;
+ }
+}
+
+
+/*
+** Link a collectable object 'o' with a known type into the list 'p'.
+** (Must be a macro to access the 'gclist' field in different types.)
+*/
+#define linkgclist(o,p) linkgclist_(obj2gco(o), &(o)->gclist, &(p))
+
+static void linkgclist_ (GCObject *o, GCObject **pnext, GCObject **list) {
+ lua_assert(!isgray(o)); /* cannot be in a gray list */
+ *pnext = *list;
+ *list = o;
+ set2gray(o); /* now it is */
+}
+
+
+/*
+** Link a generic collectable object 'o' into the list 'p'.
+*/
+#define linkobjgclist(o,p) linkgclist_(obj2gco(o), getgclist(o), &(p))
+
+
+
+/*
+** Clear keys for empty entries in tables. If entry is empty, mark its
+** entry as dead. This allows the collection of the key, but keeps its
+** entry in the table: its removal could break a chain and could break
+** a table traversal. Other places never manipulate dead keys, because
+** its associated empty value is enough to signal that the entry is
+** logically empty.
+*/
+static void clearkey (Node *n) {
+ lua_assert(isempty(gval(n)));
+ if (keyiscollectable(n))
+ setdeadkey(n); /* unused key; remove it */
+}
+
+
+/*
+** tells whether a key or value can be cleared from a weak
+** table. Non-collectable objects are never removed from weak
+** tables. Strings behave as 'values', so are never removed too. for
+** other objects: if really collected, cannot keep them; for objects
+** being finalized, keep them in keys, but not in values
+*/
+static int iscleared (global_State *g, const GCObject *o) {
+ if (o == NULL) return 0; /* non-collectable value */
+ else if (novariant(o->tt) == LUA_TSTRING) {
+ markobject(g, o); /* strings are 'values', so are never weak */
+ return 0;
+ }
+ else return iswhite(o);
+}
+
+
+/*
+** Barrier that moves collector forward, that is, marks the white object
+** 'v' being pointed by the black object 'o'. In the generational
+** mode, 'v' must also become old, if 'o' is old; however, it cannot
+** be changed directly to OLD, because it may still point to non-old
+** objects. So, it is marked as OLD0. In the next cycle it will become
+** OLD1, and in the next it will finally become OLD (regular old). By
+** then, any object it points to will also be old. If called in the
+** incremental sweep phase, it clears the black object to white (sweep
+** it) to avoid other barrier calls for this same object. (That cannot
+** be done is generational mode, as its sweep does not distinguish
+** whites from deads.)
+*/
+void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
+ global_State *g = G(L);
+ lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
+ if (keepinvariant(g)) { /* must keep invariant? */
+ reallymarkobject(g, v); /* restore invariant */
+ if (isold(o)) {
+ lua_assert(!isold(v)); /* white object could not be old */
+ setage(v, G_OLD0); /* restore generational invariant */
+ }
+ }
+ else { /* sweep phase */
+ lua_assert(issweepphase(g));
+ if (g->gckind == KGC_INC) /* incremental mode? */
+ makewhite(g, o); /* mark 'o' as white to avoid other barriers */
+ }
+}
+
+
+/*
+** barrier that moves collector backward, that is, mark the black object
+** pointing to a white object as gray again.
+*/
+void luaC_barrierback_ (lua_State *L, GCObject *o) {
+ global_State *g = G(L);
+ lua_assert(isblack(o) && !isdead(g, o));
+ lua_assert((g->gckind == KGC_GEN) == (isold(o) && getage(o) != G_TOUCHED1));
+ if (getage(o) == G_TOUCHED2) /* already in gray list? */
+ set2gray(o); /* make it gray to become touched1 */
+ else /* link it in 'grayagain' and paint it gray */
+ linkobjgclist(o, g->grayagain);
+ if (isold(o)) /* generational mode? */
+ setage(o, G_TOUCHED1); /* touched in current cycle */
+}
+
+
+void luaC_fix (lua_State *L, GCObject *o) {
+ global_State *g = G(L);
+ lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */
+ set2gray(o); /* they will be gray forever */
+ setage(o, G_OLD); /* and old forever */
+ g->allgc = o->next; /* remove object from 'allgc' list */
+ o->next = g->fixedgc; /* link it to 'fixedgc' list */
+ g->fixedgc = o;
+}
+
+
+/*
+** create a new collectable object (with given type, size, and offset)
+** and link it to 'allgc' list.
+*/
+GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz, size_t offset) {
+ global_State *g = G(L);
+ char *p = cast_charp(luaM_newobject(L, novariant(tt), sz));
+ GCObject *o = cast(GCObject *, p + offset);
+ o->marked = luaC_white(g);
+ o->tt = tt;
+ o->next = g->allgc;
+ g->allgc = o;
+ return o;
+}
+
+
+GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
+ return luaC_newobjdt(L, tt, sz, 0);
+}
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** Mark functions
+** =======================================================
+*/
+
+
+/*
+** Mark an object. Userdata with no user values, strings, and closed
+** upvalues are visited and turned black here. Open upvalues are
+** already indirectly linked through their respective threads in the
+** 'twups' list, so they don't go to the gray list; nevertheless, they
+** are kept gray to avoid barriers, as their values will be revisited
+** by the thread or by 'remarkupvals'. Other objects are added to the
+** gray list to be visited (and turned black) later. Both userdata and
+** upvalues can call this function recursively, but this recursion goes
+** for at most two levels: An upvalue cannot refer to another upvalue
+** (only closures can), and a userdata's metatable must be a table.
+*/
+static void reallymarkobject (global_State *g, GCObject *o) {
+ switch (o->tt) {
+ case LUA_VSHRSTR:
+ case LUA_VLNGSTR: {
+ set2black(o); /* nothing to visit */
+ break;
+ }
+ case LUA_VUPVAL: {
+ UpVal *uv = gco2upv(o);
+ if (upisopen(uv))
+ set2gray(uv); /* open upvalues are kept gray */
+ else
+ set2black(uv); /* closed upvalues are visited here */
+ markvalue(g, uv->v.p); /* mark its content */
+ break;
+ }
+ case LUA_VUSERDATA: {
+ Udata *u = gco2u(o);
+ if (u->nuvalue == 0) { /* no user values? */
+ markobjectN(g, u->metatable); /* mark its metatable */
+ set2black(u); /* nothing else to mark */
+ break;
+ }
+ /* else... */
+ } /* FALLTHROUGH */
+ case LUA_VLCL: case LUA_VCCL: case LUA_VTABLE:
+ case LUA_VTHREAD: case LUA_VPROTO: {
+ linkobjgclist(o, g->gray); /* to be visited later */
+ break;
+ }
+ default: lua_assert(0); break;
+ }
+}
+
+
+/*
+** mark metamethods for basic types
+*/
+static void markmt (global_State *g) {
+ int i;
+ for (i=0; i < LUA_NUMTAGS; i++)
+ markobjectN(g, g->mt[i]);
+}
+
+
+/*
+** mark all objects in list of being-finalized
+*/
+static lu_mem markbeingfnz (global_State *g) {
+ GCObject *o;
+ lu_mem count = 0;
+ for (o = g->tobefnz; o != NULL; o = o->next) {
+ count++;
+ markobject(g, o);
+ }
+ return count;
+}
+
+
+/*
+** For each non-marked thread, simulates a barrier between each open
+** upvalue and its value. (If the thread is collected, the value will be
+** assigned to the upvalue, but then it can be too late for the barrier
+** to act. The "barrier" does not need to check colors: A non-marked
+** thread must be young; upvalues cannot be older than their threads; so
+** any visited upvalue must be young too.) Also removes the thread from
+** the list, as it was already visited. Removes also threads with no
+** upvalues, as they have nothing to be checked. (If the thread gets an
+** upvalue later, it will be linked in the list again.)
+*/
+static int remarkupvals (global_State *g) {
+ lua_State *thread;
+ lua_State **p = &g->twups;
+ int work = 0; /* estimate of how much work was done here */
+ while ((thread = *p) != NULL) {
+ work++;
+ if (!iswhite(thread) && thread->openupval != NULL)
+ p = &thread->twups; /* keep marked thread with upvalues in the list */
+ else { /* thread is not marked or without upvalues */
+ UpVal *uv;
+ lua_assert(!isold(thread) || thread->openupval == NULL);
+ *p = thread->twups; /* remove thread from the list */
+ thread->twups = thread; /* mark that it is out of list */
+ for (uv = thread->openupval; uv != NULL; uv = uv->u.open.next) {
+ lua_assert(getage(uv) <= getage(thread));
+ work++;
+ if (!iswhite(uv)) { /* upvalue already visited? */
+ lua_assert(upisopen(uv) && isgray(uv));
+ markvalue(g, uv->v.p); /* mark its value */
+ }
+ }
+ }
+ }
+ return work;
+}
+
+
+static void cleargraylists (global_State *g) {
+ g->gray = g->grayagain = NULL;
+ g->weak = g->allweak = g->ephemeron = NULL;
+}
+
+
+/*
+** mark root set and reset all gray lists, to start a new collection
+*/
+static void restartcollection (global_State *g) {
+ cleargraylists(g);
+ markobject(g, g->mainthread);
+ markvalue(g, &g->l_registry);
+ markmt(g);
+ markbeingfnz(g); /* mark any finalizing object left from previous cycle */
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Traverse functions
+** =======================================================
+*/
+
+
+/*
+** Check whether object 'o' should be kept in the 'grayagain' list for
+** post-processing by 'correctgraylist'. (It could put all old objects
+** in the list and leave all the work to 'correctgraylist', but it is
+** more efficient to avoid adding elements that will be removed.) Only
+** TOUCHED1 objects need to be in the list. TOUCHED2 doesn't need to go
+** back to a gray list, but then it must become OLD. (That is what
+** 'correctgraylist' does when it finds a TOUCHED2 object.)
+*/
+static void genlink (global_State *g, GCObject *o) {
+ lua_assert(isblack(o));
+ if (getage(o) == G_TOUCHED1) { /* touched in this cycle? */
+ linkobjgclist(o, g->grayagain); /* link it back in 'grayagain' */
+ } /* everything else do not need to be linked back */
+ else if (getage(o) == G_TOUCHED2)
+ changeage(o, G_TOUCHED2, G_OLD); /* advance age */
+}
+
+
+/*
+** Traverse a table with weak values and link it to proper list. During
+** propagate phase, keep it in 'grayagain' list, to be revisited in the
+** atomic phase. In the atomic phase, if table has any white value,
+** put it in 'weak' list, to be cleared.
+*/
+static void traverseweakvalue (global_State *g, Table *h) {
+ Node *n, *limit = gnodelast(h);
+ /* if there is array part, assume it may have white values (it is not
+ worth traversing it now just to check) */
+ int hasclears = (h->alimit > 0);
+ for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */
+ if (isempty(gval(n))) /* entry is empty? */
+ clearkey(n); /* clear its key */
+ else {
+ lua_assert(!keyisnil(n));
+ markkey(g, n);
+ if (!hasclears && iscleared(g, gcvalueN(gval(n)))) /* a white value? */
+ hasclears = 1; /* table will have to be cleared */
+ }
+ }
+ if (g->gcstate == GCSatomic && hasclears)
+ linkgclist(h, g->weak); /* has to be cleared later */
+ else
+ linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */
+}
+
+
+/*
+** Traverse an ephemeron table and link it to proper list. Returns true
+** iff any object was marked during this traversal (which implies that
+** convergence has to continue). During propagation phase, keep table
+** in 'grayagain' list, to be visited again in the atomic phase. In
+** the atomic phase, if table has any white->white entry, it has to
+** be revisited during ephemeron convergence (as that key may turn
+** black). Otherwise, if it has any white key, table has to be cleared
+** (in the atomic phase). In generational mode, some tables
+** must be kept in some gray list for post-processing; this is done
+** by 'genlink'.
+*/
+static int traverseephemeron (global_State *g, Table *h, int inv) {
+ int marked = 0; /* true if an object is marked in this traversal */
+ int hasclears = 0; /* true if table has white keys */
+ int hasww = 0; /* true if table has entry "white-key -> white-value" */
+ unsigned int i;
+ unsigned int asize = luaH_realasize(h);
+ unsigned int nsize = sizenode(h);
+ /* traverse array part */
+ for (i = 0; i < asize; i++) {
+ if (valiswhite(&h->array[i])) {
+ marked = 1;
+ reallymarkobject(g, gcvalue(&h->array[i]));
+ }
+ }
+ /* traverse hash part; if 'inv', traverse descending
+ (see 'convergeephemerons') */
+ for (i = 0; i < nsize; i++) {
+ Node *n = inv ? gnode(h, nsize - 1 - i) : gnode(h, i);
+ if (isempty(gval(n))) /* entry is empty? */
+ clearkey(n); /* clear its key */
+ else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */
+ hasclears = 1; /* table must be cleared */
+ if (valiswhite(gval(n))) /* value not marked yet? */
+ hasww = 1; /* white-white entry */
+ }
+ else if (valiswhite(gval(n))) { /* value not marked yet? */
+ marked = 1;
+ reallymarkobject(g, gcvalue(gval(n))); /* mark it now */
+ }
+ }
+ /* link table into proper list */
+ if (g->gcstate == GCSpropagate)
+ linkgclist(h, g->grayagain); /* must retraverse it in atomic phase */
+ else if (hasww) /* table has white->white entries? */
+ linkgclist(h, g->ephemeron); /* have to propagate again */
+ else if (hasclears) /* table has white keys? */
+ linkgclist(h, g->allweak); /* may have to clean white keys */
+ else
+ genlink(g, obj2gco(h)); /* check whether collector still needs to see it */
+ return marked;
+}
+
+
+static void traversestrongtable (global_State *g, Table *h) {
+ Node *n, *limit = gnodelast(h);
+ unsigned int i;
+ unsigned int asize = luaH_realasize(h);
+ for (i = 0; i < asize; i++) /* traverse array part */
+ markvalue(g, &h->array[i]);
+ for (n = gnode(h, 0); n < limit; n++) { /* traverse hash part */
+ if (isempty(gval(n))) /* entry is empty? */
+ clearkey(n); /* clear its key */
+ else {
+ lua_assert(!keyisnil(n));
+ markkey(g, n);
+ markvalue(g, gval(n));
+ }
+ }
+ genlink(g, obj2gco(h));
+}
+
+
+static lu_mem traversetable (global_State *g, Table *h) {
+ const char *weakkey, *weakvalue;
+ const TValue *mode = gfasttm(g, h->metatable, TM_MODE);
+ markobjectN(g, h->metatable);
+ if (mode && ttisstring(mode) && /* is there a weak mode? */
+ (cast_void(weakkey = strchr(svalue(mode), 'k')),
+ cast_void(weakvalue = strchr(svalue(mode), 'v')),
+ (weakkey || weakvalue))) { /* is really weak? */
+ if (!weakkey) /* strong keys? */
+ traverseweakvalue(g, h);
+ else if (!weakvalue) /* strong values? */
+ traverseephemeron(g, h, 0);
+ else /* all weak */
+ linkgclist(h, g->allweak); /* nothing to traverse now */
+ }
+ else /* not weak */
+ traversestrongtable(g, h);
+ return 1 + h->alimit + 2 * allocsizenode(h);
+}
+
+
+static int traverseudata (global_State *g, Udata *u) {
+ int i;
+ markobjectN(g, u->metatable); /* mark its metatable */
+ for (i = 0; i < u->nuvalue; i++)
+ markvalue(g, &u->uv[i].uv);
+ genlink(g, obj2gco(u));
+ return 1 + u->nuvalue;
+}
+
+
+/*
+** Traverse a prototype. (While a prototype is being build, its
+** arrays can be larger than needed; the extra slots are filled with
+** NULL, so the use of 'markobjectN')
+*/
+static int traverseproto (global_State *g, Proto *f) {
+ int i;
+ markobjectN(g, f->source);
+ for (i = 0; i < f->sizek; i++) /* mark literals */
+ markvalue(g, &f->k[i]);
+ for (i = 0; i < f->sizeupvalues; i++) /* mark upvalue names */
+ markobjectN(g, f->upvalues[i].name);
+ for (i = 0; i < f->sizep; i++) /* mark nested protos */
+ markobjectN(g, f->p[i]);
+ for (i = 0; i < f->sizelocvars; i++) /* mark local-variable names */
+ markobjectN(g, f->locvars[i].varname);
+ return 1 + f->sizek + f->sizeupvalues + f->sizep + f->sizelocvars;
+}
+
+
+static int traverseCclosure (global_State *g, CClosure *cl) {
+ int i;
+ for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */
+ markvalue(g, &cl->upvalue[i]);
+ return 1 + cl->nupvalues;
+}
+
+/*
+** Traverse a Lua closure, marking its prototype and its upvalues.
+** (Both can be NULL while closure is being created.)
+*/
+static int traverseLclosure (global_State *g, LClosure *cl) {
+ int i;
+ markobjectN(g, cl->p); /* mark its prototype */
+ for (i = 0; i < cl->nupvalues; i++) { /* visit its upvalues */
+ UpVal *uv = cl->upvals[i];
+ markobjectN(g, uv); /* mark upvalue */
+ }
+ return 1 + cl->nupvalues;
+}
+
+
+/*
+** Traverse a thread, marking the elements in the stack up to its top
+** and cleaning the rest of the stack in the final traversal. That
+** ensures that the entire stack have valid (non-dead) objects.
+** Threads have no barriers. In gen. mode, old threads must be visited
+** at every cycle, because they might point to young objects. In inc.
+** mode, the thread can still be modified before the end of the cycle,
+** and therefore it must be visited again in the atomic phase. To ensure
+** these visits, threads must return to a gray list if they are not new
+** (which can only happen in generational mode) or if the traverse is in
+** the propagate phase (which can only happen in incremental mode).
+*/
+static int traversethread (global_State *g, lua_State *th) {
+ UpVal *uv;
+ StkId o = th->stack.p;
+ if (isold(th) || g->gcstate == GCSpropagate)
+ linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
+ if (o == NULL)
+ return 1; /* stack not completely built yet */
+ lua_assert(g->gcstate == GCSatomic ||
+ th->openupval == NULL || isintwups(th));
+ for (; o < th->top.p; o++) /* mark live elements in the stack */
+ markvalue(g, s2v(o));
+ for (uv = th->openupval; uv != NULL; uv = uv->u.open.next)
+ markobject(g, uv); /* open upvalues cannot be collected */
+ if (g->gcstate == GCSatomic) { /* final traversal? */
+ for (; o < th->stack_last.p + EXTRA_STACK; o++)
+ setnilvalue(s2v(o)); /* clear dead stack slice */
+ /* 'remarkupvals' may have removed thread from 'twups' list */
+ if (!isintwups(th) && th->openupval != NULL) {
+ th->twups = g->twups; /* link it back to the list */
+ g->twups = th;
+ }
+ }
+ else if (!g->gcemergency)
+ luaD_shrinkstack(th); /* do not change stack in emergency cycle */
+ return 1 + stacksize(th);
+}
+
+
+/*
+** traverse one gray object, turning it to black.
+*/
+static lu_mem propagatemark (global_State *g) {
+ GCObject *o = g->gray;
+ nw2black(o);
+ g->gray = *getgclist(o); /* remove from 'gray' list */
+ switch (o->tt) {
+ case LUA_VTABLE: return traversetable(g, gco2t(o));
+ case LUA_VUSERDATA: return traverseudata(g, gco2u(o));
+ case LUA_VLCL: return traverseLclosure(g, gco2lcl(o));
+ case LUA_VCCL: return traverseCclosure(g, gco2ccl(o));
+ case LUA_VPROTO: return traverseproto(g, gco2p(o));
+ case LUA_VTHREAD: return traversethread(g, gco2th(o));
+ default: lua_assert(0); return 0;
+ }
+}
+
+
+static lu_mem propagateall (global_State *g) {
+ lu_mem tot = 0;
+ while (g->gray)
+ tot += propagatemark(g);
+ return tot;
+}
+
+
+/*
+** Traverse all ephemeron tables propagating marks from keys to values.
+** Repeat until it converges, that is, nothing new is marked. 'dir'
+** inverts the direction of the traversals, trying to speed up
+** convergence on chains in the same table.
+**
+*/
+static void convergeephemerons (global_State *g) {
+ int changed;
+ int dir = 0;
+ do {
+ GCObject *w;
+ GCObject *next = g->ephemeron; /* get ephemeron list */
+ g->ephemeron = NULL; /* tables may return to this list when traversed */
+ changed = 0;
+ while ((w = next) != NULL) { /* for each ephemeron table */
+ Table *h = gco2t(w);
+ next = h->gclist; /* list is rebuilt during loop */
+ nw2black(h); /* out of the list (for now) */
+ if (traverseephemeron(g, h, dir)) { /* marked some value? */
+ propagateall(g); /* propagate changes */
+ changed = 1; /* will have to revisit all ephemeron tables */
+ }
+ }
+ dir = !dir; /* invert direction next time */
+ } while (changed); /* repeat until no more changes */
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Sweep Functions
+** =======================================================
+*/
+
+
+/*
+** clear entries with unmarked keys from all weaktables in list 'l'
+*/
+static void clearbykeys (global_State *g, GCObject *l) {
+ for (; l; l = gco2t(l)->gclist) {
+ Table *h = gco2t(l);
+ Node *limit = gnodelast(h);
+ Node *n;
+ for (n = gnode(h, 0); n < limit; n++) {
+ if (iscleared(g, gckeyN(n))) /* unmarked key? */
+ setempty(gval(n)); /* remove entry */
+ if (isempty(gval(n))) /* is entry empty? */
+ clearkey(n); /* clear its key */
+ }
+ }
+}
+
+
+/*
+** clear entries with unmarked values from all weaktables in list 'l' up
+** to element 'f'
+*/
+static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) {
+ for (; l != f; l = gco2t(l)->gclist) {
+ Table *h = gco2t(l);
+ Node *n, *limit = gnodelast(h);
+ unsigned int i;
+ unsigned int asize = luaH_realasize(h);
+ for (i = 0; i < asize; i++) {
+ TValue *o = &h->array[i];
+ if (iscleared(g, gcvalueN(o))) /* value was collected? */
+ setempty(o); /* remove entry */
+ }
+ for (n = gnode(h, 0); n < limit; n++) {
+ if (iscleared(g, gcvalueN(gval(n)))) /* unmarked value? */
+ setempty(gval(n)); /* remove entry */
+ if (isempty(gval(n))) /* is entry empty? */
+ clearkey(n); /* clear its key */
+ }
+ }
+}
+
+
+static void freeupval (lua_State *L, UpVal *uv) {
+ if (upisopen(uv))
+ luaF_unlinkupval(uv);
+ luaM_free(L, uv);
+}
+
+
+static void freeobj (lua_State *L, GCObject *o) {
+ switch (o->tt) {
+ case LUA_VPROTO:
+ luaF_freeproto(L, gco2p(o));
+ break;
+ case LUA_VUPVAL:
+ freeupval(L, gco2upv(o));
+ break;
+ case LUA_VLCL: {
+ LClosure *cl = gco2lcl(o);
+ luaM_freemem(L, cl, sizeLclosure(cl->nupvalues));
+ break;
+ }
+ case LUA_VCCL: {
+ CClosure *cl = gco2ccl(o);
+ luaM_freemem(L, cl, sizeCclosure(cl->nupvalues));
+ break;
+ }
+ case LUA_VTABLE:
+ luaH_free(L, gco2t(o));
+ break;
+ case LUA_VTHREAD:
+ luaE_freethread(L, gco2th(o));
+ break;
+ case LUA_VUSERDATA: {
+ Udata *u = gco2u(o);
+ luaM_freemem(L, o, sizeudata(u->nuvalue, u->len));
+ break;
+ }
+ case LUA_VSHRSTR: {
+ TString *ts = gco2ts(o);
+ luaS_remove(L, ts); /* remove it from hash table */
+ luaM_freemem(L, ts, sizelstring(ts->shrlen));
+ break;
+ }
+ case LUA_VLNGSTR: {
+ TString *ts = gco2ts(o);
+ luaM_freemem(L, ts, sizelstring(ts->u.lnglen));
+ break;
+ }
+ default: lua_assert(0);
+ }
+}
+
+
+/*
+** sweep at most 'countin' elements from a list of GCObjects erasing dead
+** objects, where a dead object is one marked with the old (non current)
+** white; change all non-dead objects back to white, preparing for next
+** collection cycle. Return where to continue the traversal or NULL if
+** list is finished. ('*countout' gets the number of elements traversed.)
+*/
+static GCObject **sweeplist (lua_State *L, GCObject **p, int countin,
+ int *countout) {
+ global_State *g = G(L);
+ int ow = otherwhite(g);
+ int i;
+ int white = luaC_white(g); /* current white */
+ for (i = 0; *p != NULL && i < countin; i++) {
+ GCObject *curr = *p;
+ int marked = curr->marked;
+ if (isdeadm(ow, marked)) { /* is 'curr' dead? */
+ *p = curr->next; /* remove 'curr' from list */
+ freeobj(L, curr); /* erase 'curr' */
+ }
+ else { /* change mark to 'white' */
+ curr->marked = cast_byte((marked & ~maskgcbits) | white);
+ p = &curr->next; /* go to next element */
+ }
+ }
+ if (countout)
+ *countout = i; /* number of elements traversed */
+ return (*p == NULL) ? NULL : p;
+}
+
+
+/*
+** sweep a list until a live object (or end of list)
+*/
+static GCObject **sweeptolive (lua_State *L, GCObject **p) {
+ GCObject **old = p;
+ do {
+ p = sweeplist(L, p, 1, NULL);
+ } while (p == old);
+ return p;
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Finalization
+** =======================================================
+*/
+
+/*
+** If possible, shrink string table.
+*/
+static void checkSizes (lua_State *L, global_State *g) {
+ if (!g->gcemergency) {
+ if (g->strt.nuse < g->strt.size / 4) { /* string table too big? */
+ l_mem olddebt = g->GCdebt;
+ luaS_resize(L, g->strt.size / 2);
+ g->GCestimate += g->GCdebt - olddebt; /* correct estimate */
+ }
+ }
+}
+
+
+/*
+** Get the next udata to be finalized from the 'tobefnz' list, and
+** link it back into the 'allgc' list.
+*/
+static GCObject *udata2finalize (global_State *g) {
+ GCObject *o = g->tobefnz; /* get first element */
+ lua_assert(tofinalize(o));
+ g->tobefnz = o->next; /* remove it from 'tobefnz' list */
+ o->next = g->allgc; /* return it to 'allgc' list */
+ g->allgc = o;
+ resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */
+ if (issweepphase(g))
+ makewhite(g, o); /* "sweep" object */
+ else if (getage(o) == G_OLD1)
+ g->firstold1 = o; /* it is the first OLD1 object in the list */
+ return o;
+}
+
+
+static void dothecall (lua_State *L, void *ud) {
+ UNUSED(ud);
+ luaD_callnoyield(L, L->top.p - 2, 0);
+}
+
+
+static void GCTM (lua_State *L) {
+ global_State *g = G(L);
+ const TValue *tm;
+ TValue v;
+ lua_assert(!g->gcemergency);
+ setgcovalue(L, &v, udata2finalize(g));
+ tm = luaT_gettmbyobj(L, &v, TM_GC);
+ if (!notm(tm)) { /* is there a finalizer? */
+ int status;
+ lu_byte oldah = L->allowhook;
+ int oldgcstp = g->gcstp;
+ g->gcstp |= GCSTPGC; /* avoid GC steps */
+ L->allowhook = 0; /* stop debug hooks during GC metamethod */
+ setobj2s(L, L->top.p++, tm); /* push finalizer... */
+ setobj2s(L, L->top.p++, &v); /* ... and its argument */
+ L->ci->callstatus |= CIST_FIN; /* will run a finalizer */
+ status = luaD_pcall(L, dothecall, NULL, savestack(L, L->top.p - 2), 0);
+ L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */
+ L->allowhook = oldah; /* restore hooks */
+ g->gcstp = oldgcstp; /* restore state */
+ if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */
+ luaE_warnerror(L, "__gc");
+ L->top.p--; /* pops error object */
+ }
+ }
+}
+
+
+/*
+** Call a few finalizers
+*/
+static int runafewfinalizers (lua_State *L, int n) {
+ global_State *g = G(L);
+ int i;
+ for (i = 0; i < n && g->tobefnz; i++)
+ GCTM(L); /* call one finalizer */
+ return i;
+}
+
+
+/*
+** call all pending finalizers
+*/
+static void callallpendingfinalizers (lua_State *L) {
+ global_State *g = G(L);
+ while (g->tobefnz)
+ GCTM(L);
+}
+
+
+/*
+** find last 'next' field in list 'p' list (to add elements in its end)
+*/
+static GCObject **findlast (GCObject **p) {
+ while (*p != NULL)
+ p = &(*p)->next;
+ return p;
+}
+
+
+/*
+** Move all unreachable objects (or 'all' objects) that need
+** finalization from list 'finobj' to list 'tobefnz' (to be finalized).
+** (Note that objects after 'finobjold1' cannot be white, so they
+** don't need to be traversed. In incremental mode, 'finobjold1' is NULL,
+** so the whole list is traversed.)
+*/
+static void separatetobefnz (global_State *g, int all) {
+ GCObject *curr;
+ GCObject **p = &g->finobj;
+ GCObject **lastnext = findlast(&g->tobefnz);
+ while ((curr = *p) != g->finobjold1) { /* traverse all finalizable objects */
+ lua_assert(tofinalize(curr));
+ if (!(iswhite(curr) || all)) /* not being collected? */
+ p = &curr->next; /* don't bother with it */
+ else {
+ if (curr == g->finobjsur) /* removing 'finobjsur'? */
+ g->finobjsur = curr->next; /* correct it */
+ *p = curr->next; /* remove 'curr' from 'finobj' list */
+ curr->next = *lastnext; /* link at the end of 'tobefnz' list */
+ *lastnext = curr;
+ lastnext = &curr->next;
+ }
+ }
+}
+
+
+/*
+** If pointer 'p' points to 'o', move it to the next element.
+*/
+static void checkpointer (GCObject **p, GCObject *o) {
+ if (o == *p)
+ *p = o->next;
+}
+
+
+/*
+** Correct pointers to objects inside 'allgc' list when
+** object 'o' is being removed from the list.
+*/
+static void correctpointers (global_State *g, GCObject *o) {
+ checkpointer(&g->survival, o);
+ checkpointer(&g->old1, o);
+ checkpointer(&g->reallyold, o);
+ checkpointer(&g->firstold1, o);
+}
+
+
+/*
+** if object 'o' has a finalizer, remove it from 'allgc' list (must
+** search the list to find it) and link it in 'finobj' list.
+*/
+void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
+ global_State *g = G(L);
+ if (tofinalize(o) || /* obj. is already marked... */
+ gfasttm(g, mt, TM_GC) == NULL || /* or has no finalizer... */
+ (g->gcstp & GCSTPCLS)) /* or closing state? */
+ return; /* nothing to be done */
+ else { /* move 'o' to 'finobj' list */
+ GCObject **p;
+ if (issweepphase(g)) {
+ makewhite(g, o); /* "sweep" object 'o' */
+ if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */
+ g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */
+ }
+ else
+ correctpointers(g, o);
+ /* search for pointer pointing to 'o' */
+ for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ }
+ *p = o->next; /* remove 'o' from 'allgc' list */
+ o->next = g->finobj; /* link it in 'finobj' list */
+ g->finobj = o;
+ l_setbit(o->marked, FINALIZEDBIT); /* mark it as such */
+ }
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** Generational Collector
+** =======================================================
+*/
+
+
+/*
+** Set the "time" to wait before starting a new GC cycle; cycle will
+** start when memory use hits the threshold of ('estimate' * pause /
+** PAUSEADJ). (Division by 'estimate' should be OK: it cannot be zero,
+** because Lua cannot even start with less than PAUSEADJ bytes).
+*/
+static void setpause (global_State *g) {
+ l_mem threshold, debt;
+ int pause = getgcparam(g->gcpause);
+ l_mem estimate = g->GCestimate / PAUSEADJ; /* adjust 'estimate' */
+ lua_assert(estimate > 0);
+ threshold = (pause < MAX_LMEM / estimate) /* overflow? */
+ ? estimate * pause /* no overflow */
+ : MAX_LMEM; /* overflow; truncate to maximum */
+ debt = gettotalbytes(g) - threshold;
+ if (debt > 0) debt = 0;
+ luaE_setdebt(g, debt);
+}
+
+
+/*
+** Sweep a list of objects to enter generational mode. Deletes dead
+** objects and turns the non dead to old. All non-dead threads---which
+** are now old---must be in a gray list. Everything else is not in a
+** gray list. Open upvalues are also kept gray.
+*/
+static void sweep2old (lua_State *L, GCObject **p) {
+ GCObject *curr;
+ global_State *g = G(L);
+ while ((curr = *p) != NULL) {
+ if (iswhite(curr)) { /* is 'curr' dead? */
+ lua_assert(isdead(g, curr));
+ *p = curr->next; /* remove 'curr' from list */
+ freeobj(L, curr); /* erase 'curr' */
+ }
+ else { /* all surviving objects become old */
+ setage(curr, G_OLD);
+ if (curr->tt == LUA_VTHREAD) { /* threads must be watched */
+ lua_State *th = gco2th(curr);
+ linkgclist(th, g->grayagain); /* insert into 'grayagain' list */
+ }
+ else if (curr->tt == LUA_VUPVAL && upisopen(gco2upv(curr)))
+ set2gray(curr); /* open upvalues are always gray */
+ else /* everything else is black */
+ nw2black(curr);
+ p = &curr->next; /* go to next element */
+ }
+ }
+}
+
+
+/*
+** Sweep for generational mode. Delete dead objects. (Because the
+** collection is not incremental, there are no "new white" objects
+** during the sweep. So, any white object must be dead.) For
+** non-dead objects, advance their ages and clear the color of
+** new objects. (Old objects keep their colors.)
+** The ages of G_TOUCHED1 and G_TOUCHED2 objects cannot be advanced
+** here, because these old-generation objects are usually not swept
+** here. They will all be advanced in 'correctgraylist'. That function
+** will also remove objects turned white here from any gray list.
+*/
+static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
+ GCObject *limit, GCObject **pfirstold1) {
+ static const lu_byte nextage[] = {
+ G_SURVIVAL, /* from G_NEW */
+ G_OLD1, /* from G_SURVIVAL */
+ G_OLD1, /* from G_OLD0 */
+ G_OLD, /* from G_OLD1 */
+ G_OLD, /* from G_OLD (do not change) */
+ G_TOUCHED1, /* from G_TOUCHED1 (do not change) */
+ G_TOUCHED2 /* from G_TOUCHED2 (do not change) */
+ };
+ int white = luaC_white(g);
+ GCObject *curr;
+ while ((curr = *p) != limit) {
+ if (iswhite(curr)) { /* is 'curr' dead? */
+ lua_assert(!isold(curr) && isdead(g, curr));
+ *p = curr->next; /* remove 'curr' from list */
+ freeobj(L, curr); /* erase 'curr' */
+ }
+ else { /* correct mark and age */
+ if (getage(curr) == G_NEW) { /* new objects go back to white */
+ int marked = curr->marked & ~maskgcbits; /* erase GC bits */
+ curr->marked = cast_byte(marked | G_SURVIVAL | white);
+ }
+ else { /* all other objects will be old, and so keep their color */
+ setage(curr, nextage[getage(curr)]);
+ if (getage(curr) == G_OLD1 && *pfirstold1 == NULL)
+ *pfirstold1 = curr; /* first OLD1 object in the list */
+ }
+ p = &curr->next; /* go to next element */
+ }
+ }
+ return p;
+}
+
+
+/*
+** Traverse a list making all its elements white and clearing their
+** age. In incremental mode, all objects are 'new' all the time,
+** except for fixed strings (which are always old).
+*/
+static void whitelist (global_State *g, GCObject *p) {
+ int white = luaC_white(g);
+ for (; p != NULL; p = p->next)
+ p->marked = cast_byte((p->marked & ~maskgcbits) | white);
+}
+
+
+/*
+** Correct a list of gray objects. Return pointer to where rest of the
+** list should be linked.
+** Because this correction is done after sweeping, young objects might
+** be turned white and still be in the list. They are only removed.
+** 'TOUCHED1' objects are advanced to 'TOUCHED2' and remain on the list;
+** Non-white threads also remain on the list; 'TOUCHED2' objects become
+** regular old; they and anything else are removed from the list.
+*/
+static GCObject **correctgraylist (GCObject **p) {
+ GCObject *curr;
+ while ((curr = *p) != NULL) {
+ GCObject **next = getgclist(curr);
+ if (iswhite(curr))
+ goto remove; /* remove all white objects */
+ else if (getage(curr) == G_TOUCHED1) { /* touched in this cycle? */
+ lua_assert(isgray(curr));
+ nw2black(curr); /* make it black, for next barrier */
+ changeage(curr, G_TOUCHED1, G_TOUCHED2);
+ goto remain; /* keep it in the list and go to next element */
+ }
+ else if (curr->tt == LUA_VTHREAD) {
+ lua_assert(isgray(curr));
+ goto remain; /* keep non-white threads on the list */
+ }
+ else { /* everything else is removed */
+ lua_assert(isold(curr)); /* young objects should be white here */
+ if (getage(curr) == G_TOUCHED2) /* advance from TOUCHED2... */
+ changeage(curr, G_TOUCHED2, G_OLD); /* ... to OLD */
+ nw2black(curr); /* make object black (to be removed) */
+ goto remove;
+ }
+ remove: *p = *next; continue;
+ remain: p = next; continue;
+ }
+ return p;
+}
+
+
+/*
+** Correct all gray lists, coalescing them into 'grayagain'.
+*/
+static void correctgraylists (global_State *g) {
+ GCObject **list = correctgraylist(&g->grayagain);
+ *list = g->weak; g->weak = NULL;
+ list = correctgraylist(list);
+ *list = g->allweak; g->allweak = NULL;
+ list = correctgraylist(list);
+ *list = g->ephemeron; g->ephemeron = NULL;
+ correctgraylist(list);
+}
+
+
+/*
+** Mark black 'OLD1' objects when starting a new young collection.
+** Gray objects are already in some gray list, and so will be visited
+** in the atomic step.
+*/
+static void markold (global_State *g, GCObject *from, GCObject *to) {
+ GCObject *p;
+ for (p = from; p != to; p = p->next) {
+ if (getage(p) == G_OLD1) {
+ lua_assert(!iswhite(p));
+ changeage(p, G_OLD1, G_OLD); /* now they are old */
+ if (isblack(p))
+ reallymarkobject(g, p);
+ }
+ }
+}
+
+
+/*
+** Finish a young-generation collection.
+*/
+static void finishgencycle (lua_State *L, global_State *g) {
+ correctgraylists(g);
+ checkSizes(L, g);
+ g->gcstate = GCSpropagate; /* skip restart */
+ if (!g->gcemergency)
+ callallpendingfinalizers(L);
+}
+
+
+/*
+** Does a young collection. First, mark 'OLD1' objects. Then does the
+** atomic step. Then, sweep all lists and advance pointers. Finally,
+** finish the collection.
+*/
+static void youngcollection (lua_State *L, global_State *g) {
+ GCObject **psurvival; /* to point to first non-dead survival object */
+ GCObject *dummy; /* dummy out parameter to 'sweepgen' */
+ lua_assert(g->gcstate == GCSpropagate);
+ if (g->firstold1) { /* are there regular OLD1 objects? */
+ markold(g, g->firstold1, g->reallyold); /* mark them */
+ g->firstold1 = NULL; /* no more OLD1 objects (for now) */
+ }
+ markold(g, g->finobj, g->finobjrold);
+ markold(g, g->tobefnz, NULL);
+ atomic(L);
+
+ /* sweep nursery and get a pointer to its last live element */
+ g->gcstate = GCSswpallgc;
+ psurvival = sweepgen(L, g, &g->allgc, g->survival, &g->firstold1);
+ /* sweep 'survival' */
+ sweepgen(L, g, psurvival, g->old1, &g->firstold1);
+ g->reallyold = g->old1;
+ g->old1 = *psurvival; /* 'survival' survivals are old now */
+ g->survival = g->allgc; /* all news are survivals */
+
+ /* repeat for 'finobj' lists */
+ dummy = NULL; /* no 'firstold1' optimization for 'finobj' lists */
+ psurvival = sweepgen(L, g, &g->finobj, g->finobjsur, &dummy);
+ /* sweep 'survival' */
+ sweepgen(L, g, psurvival, g->finobjold1, &dummy);
+ g->finobjrold = g->finobjold1;
+ g->finobjold1 = *psurvival; /* 'survival' survivals are old now */
+ g->finobjsur = g->finobj; /* all news are survivals */
+
+ sweepgen(L, g, &g->tobefnz, NULL, &dummy);
+ finishgencycle(L, g);
+}
+
+
+/*
+** Clears all gray lists, sweeps objects, and prepare sublists to enter
+** generational mode. The sweeps remove dead objects and turn all
+** surviving objects to old. Threads go back to 'grayagain'; everything
+** else is turned black (not in any gray list).
+*/
+static void atomic2gen (lua_State *L, global_State *g) {
+ cleargraylists(g);
+ /* sweep all elements making them old */
+ g->gcstate = GCSswpallgc;
+ sweep2old(L, &g->allgc);
+ /* everything alive now is old */
+ g->reallyold = g->old1 = g->survival = g->allgc;
+ g->firstold1 = NULL; /* there are no OLD1 objects anywhere */
+
+ /* repeat for 'finobj' lists */
+ sweep2old(L, &g->finobj);
+ g->finobjrold = g->finobjold1 = g->finobjsur = g->finobj;
+
+ sweep2old(L, &g->tobefnz);
+
+ g->gckind = KGC_GEN;
+ g->lastatomic = 0;
+ g->GCestimate = gettotalbytes(g); /* base for memory control */
+ finishgencycle(L, g);
+}
+
+
+/*
+** Set debt for the next minor collection, which will happen when
+** memory grows 'genminormul'%.
+*/
+static void setminordebt (global_State *g) {
+ luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul));
+}
+
+
+/*
+** Enter generational mode. Must go until the end of an atomic cycle
+** to ensure that all objects are correctly marked and weak tables
+** are cleared. Then, turn all objects into old and finishes the
+** collection.
+*/
+static lu_mem entergen (lua_State *L, global_State *g) {
+ lu_mem numobjs;
+ luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */
+ luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
+ numobjs = atomic(L); /* propagates all and then do the atomic stuff */
+ atomic2gen(L, g);
+ setminordebt(g); /* set debt assuming next cycle will be minor */
+ return numobjs;
+}
+
+
+/*
+** Enter incremental mode. Turn all objects white, make all
+** intermediate lists point to NULL (to avoid invalid pointers),
+** and go to the pause state.
+*/
+static void enterinc (global_State *g) {
+ whitelist(g, g->allgc);
+ g->reallyold = g->old1 = g->survival = NULL;
+ whitelist(g, g->finobj);
+ whitelist(g, g->tobefnz);
+ g->finobjrold = g->finobjold1 = g->finobjsur = NULL;
+ g->gcstate = GCSpause;
+ g->gckind = KGC_INC;
+ g->lastatomic = 0;
+}
+
+
+/*
+** Change collector mode to 'newmode'.
+*/
+void luaC_changemode (lua_State *L, int newmode) {
+ global_State *g = G(L);
+ if (newmode != g->gckind) {
+ if (newmode == KGC_GEN) /* entering generational mode? */
+ entergen(L, g);
+ else
+ enterinc(g); /* entering incremental mode */
+ }
+ g->lastatomic = 0;
+}
+
+
+/*
+** Does a full collection in generational mode.
+*/
+static lu_mem fullgen (lua_State *L, global_State *g) {
+ enterinc(g);
+ return entergen(L, g);
+}
+
+
+/*
+** Does a major collection after last collection was a "bad collection".
+**
+** When the program is building a big structure, it allocates lots of
+** memory but generates very little garbage. In those scenarios,
+** the generational mode just wastes time doing small collections, and
+** major collections are frequently what we call a "bad collection", a
+** collection that frees too few objects. To avoid the cost of switching
+** between generational mode and the incremental mode needed for full
+** (major) collections, the collector tries to stay in incremental mode
+** after a bad collection, and to switch back to generational mode only
+** after a "good" collection (one that traverses less than 9/8 objects
+** of the previous one).
+** The collector must choose whether to stay in incremental mode or to
+** switch back to generational mode before sweeping. At this point, it
+** does not know the real memory in use, so it cannot use memory to
+** decide whether to return to generational mode. Instead, it uses the
+** number of objects traversed (returned by 'atomic') as a proxy. The
+** field 'g->lastatomic' keeps this count from the last collection.
+** ('g->lastatomic != 0' also means that the last collection was bad.)
+*/
+static void stepgenfull (lua_State *L, global_State *g) {
+ lu_mem newatomic; /* count of traversed objects */
+ lu_mem lastatomic = g->lastatomic; /* count from last collection */
+ if (g->gckind == KGC_GEN) /* still in generational mode? */
+ enterinc(g); /* enter incremental mode */
+ luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
+ newatomic = atomic(L); /* mark everybody */
+ if (newatomic < lastatomic + (lastatomic >> 3)) { /* good collection? */
+ atomic2gen(L, g); /* return to generational mode */
+ setminordebt(g);
+ }
+ else { /* another bad collection; stay in incremental mode */
+ g->GCestimate = gettotalbytes(g); /* first estimate */;
+ entersweep(L);
+ luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */
+ setpause(g);
+ g->lastatomic = newatomic;
+ }
+}
+
+
+/*
+** Does a generational "step".
+** Usually, this means doing a minor collection and setting the debt to
+** make another collection when memory grows 'genminormul'% larger.
+**
+** However, there are exceptions. If memory grows 'genmajormul'%
+** larger than it was at the end of the last major collection (kept
+** in 'g->GCestimate'), the function does a major collection. At the
+** end, it checks whether the major collection was able to free a
+** decent amount of memory (at least half the growth in memory since
+** previous major collection). If so, the collector keeps its state,
+** and the next collection will probably be minor again. Otherwise,
+** we have what we call a "bad collection". In that case, set the field
+** 'g->lastatomic' to signal that fact, so that the next collection will
+** go to 'stepgenfull'.
+**
+** 'GCdebt <= 0' means an explicit call to GC step with "size" zero;
+** in that case, do a minor collection.
+*/
+static void genstep (lua_State *L, global_State *g) {
+ if (g->lastatomic != 0) /* last collection was a bad one? */
+ stepgenfull(L, g); /* do a full step */
+ else {
+ lu_mem majorbase = g->GCestimate; /* memory after last major collection */
+ lu_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul);
+ if (g->GCdebt > 0 && gettotalbytes(g) > majorbase + majorinc) {
+ lu_mem numobjs = fullgen(L, g); /* do a major collection */
+ if (gettotalbytes(g) < majorbase + (majorinc / 2)) {
+ /* collected at least half of memory growth since last major
+ collection; keep doing minor collections. */
+ lua_assert(g->lastatomic == 0);
+ }
+ else { /* bad collection */
+ g->lastatomic = numobjs; /* signal that last collection was bad */
+ setpause(g); /* do a long wait for next (major) collection */
+ }
+ }
+ else { /* regular case; do a minor collection */
+ youngcollection(L, g);
+ setminordebt(g);
+ g->GCestimate = majorbase; /* preserve base value */
+ }
+ }
+ lua_assert(isdecGCmodegen(g));
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** GC control
+** =======================================================
+*/
+
+
+/*
+** Enter first sweep phase.
+** The call to 'sweeptolive' makes the pointer point to an object
+** inside the list (instead of to the header), so that the real sweep do
+** not need to skip objects created between "now" and the start of the
+** real sweep.
+*/
+static void entersweep (lua_State *L) {
+ global_State *g = G(L);
+ g->gcstate = GCSswpallgc;
+ lua_assert(g->sweepgc == NULL);
+ g->sweepgc = sweeptolive(L, &g->allgc);
+}
+
+
+/*
+** Delete all objects in list 'p' until (but not including) object
+** 'limit'.
+*/
+static void deletelist (lua_State *L, GCObject *p, GCObject *limit) {
+ while (p != limit) {
+ GCObject *next = p->next;
+ freeobj(L, p);
+ p = next;
+ }
+}
+
+
+/*
+** Call all finalizers of the objects in the given Lua state, and
+** then free all objects, except for the main thread.
+*/
+void luaC_freeallobjects (lua_State *L) {
+ global_State *g = G(L);
+ g->gcstp = GCSTPCLS; /* no extra finalizers after here */
+ luaC_changemode(L, KGC_INC);
+ separatetobefnz(g, 1); /* separate all objects with finalizers */
+ lua_assert(g->finobj == NULL);
+ callallpendingfinalizers(L);
+ deletelist(L, g->allgc, obj2gco(g->mainthread));
+ lua_assert(g->finobj == NULL); /* no new finalizers */
+ deletelist(L, g->fixedgc, NULL); /* collect fixed objects */
+ lua_assert(g->strt.nuse == 0);
+}
+
+
+static lu_mem atomic (lua_State *L) {
+ global_State *g = G(L);
+ lu_mem work = 0;
+ GCObject *origweak, *origall;
+ GCObject *grayagain = g->grayagain; /* save original list */
+ g->grayagain = NULL;
+ lua_assert(g->ephemeron == NULL && g->weak == NULL);
+ lua_assert(!iswhite(g->mainthread));
+ g->gcstate = GCSatomic;
+ markobject(g, L); /* mark running thread */
+ /* registry and global metatables may be changed by API */
+ markvalue(g, &g->l_registry);
+ markmt(g); /* mark global metatables */
+ work += propagateall(g); /* empties 'gray' list */
+ /* remark occasional upvalues of (maybe) dead threads */
+ work += remarkupvals(g);
+ work += propagateall(g); /* propagate changes */
+ g->gray = grayagain;
+ work += propagateall(g); /* traverse 'grayagain' list */
+ convergeephemerons(g);
+ /* at this point, all strongly accessible objects are marked. */
+ /* Clear values from weak tables, before checking finalizers */
+ clearbyvalues(g, g->weak, NULL);
+ clearbyvalues(g, g->allweak, NULL);
+ origweak = g->weak; origall = g->allweak;
+ separatetobefnz(g, 0); /* separate objects to be finalized */
+ work += markbeingfnz(g); /* mark objects that will be finalized */
+ work += propagateall(g); /* remark, to propagate 'resurrection' */
+ convergeephemerons(g);
+ /* at this point, all resurrected objects are marked. */
+ /* remove dead objects from weak tables */
+ clearbykeys(g, g->ephemeron); /* clear keys from all ephemeron tables */
+ clearbykeys(g, g->allweak); /* clear keys from all 'allweak' tables */
+ /* clear values from resurrected weak tables */
+ clearbyvalues(g, g->weak, origweak);
+ clearbyvalues(g, g->allweak, origall);
+ luaS_clearcache(g);
+ g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */
+ lua_assert(g->gray == NULL);
+ return work; /* estimate of slots marked by 'atomic' */
+}
+
+
+static int sweepstep (lua_State *L, global_State *g,
+ int nextstate, GCObject **nextlist) {
+ if (g->sweepgc) {
+ l_mem olddebt = g->GCdebt;
+ int count;
+ g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX, &count);
+ g->GCestimate += g->GCdebt - olddebt; /* update estimate */
+ return count;
+ }
+ else { /* enter next state */
+ g->gcstate = nextstate;
+ g->sweepgc = nextlist;
+ return 0; /* no work done */
+ }
+}
+
+
+static lu_mem singlestep (lua_State *L) {
+ global_State *g = G(L);
+ lu_mem work;
+ lua_assert(!g->gcstopem); /* collector is not reentrant */
+ g->gcstopem = 1; /* no emergency collections while collecting */
+ switch (g->gcstate) {
+ case GCSpause: {
+ restartcollection(g);
+ g->gcstate = GCSpropagate;
+ work = 1;
+ break;
+ }
+ case GCSpropagate: {
+ if (g->gray == NULL) { /* no more gray objects? */
+ g->gcstate = GCSenteratomic; /* finish propagate phase */
+ work = 0;
+ }
+ else
+ work = propagatemark(g); /* traverse one gray object */
+ break;
+ }
+ case GCSenteratomic: {
+ work = atomic(L); /* work is what was traversed by 'atomic' */
+ entersweep(L);
+ g->GCestimate = gettotalbytes(g); /* first estimate */;
+ break;
+ }
+ case GCSswpallgc: { /* sweep "regular" objects */
+ work = sweepstep(L, g, GCSswpfinobj, &g->finobj);
+ break;
+ }
+ case GCSswpfinobj: { /* sweep objects with finalizers */
+ work = sweepstep(L, g, GCSswptobefnz, &g->tobefnz);
+ break;
+ }
+ case GCSswptobefnz: { /* sweep objects to be finalized */
+ work = sweepstep(L, g, GCSswpend, NULL);
+ break;
+ }
+ case GCSswpend: { /* finish sweeps */
+ checkSizes(L, g);
+ g->gcstate = GCScallfin;
+ work = 0;
+ break;
+ }
+ case GCScallfin: { /* call remaining finalizers */
+ if (g->tobefnz && !g->gcemergency) {
+ g->gcstopem = 0; /* ok collections during finalizers */
+ work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST;
+ }
+ else { /* emergency mode or no more finalizers */
+ g->gcstate = GCSpause; /* finish collection */
+ work = 0;
+ }
+ break;
+ }
+ default: lua_assert(0); return 0;
+ }
+ g->gcstopem = 0;
+ return work;
+}
+
+
+/*
+** advances the garbage collector until it reaches a state allowed
+** by 'statemask'
+*/
+void luaC_runtilstate (lua_State *L, int statesmask) {
+ global_State *g = G(L);
+ while (!testbit(statesmask, g->gcstate))
+ singlestep(L);
+}
+
+
+
+/*
+** Performs a basic incremental step. The debt and step size are
+** converted from bytes to "units of work"; then the function loops
+** running single steps until adding that many units of work or
+** finishing a cycle (pause state). Finally, it sets the debt that
+** controls when next step will be performed.
+*/
+static void incstep (lua_State *L, global_State *g) {
+ int stepmul = (getgcparam(g->gcstepmul) | 1); /* avoid division by 0 */
+ l_mem debt = (g->GCdebt / WORK2MEM) * stepmul;
+ l_mem stepsize = (g->gcstepsize <= log2maxs(l_mem))
+ ? ((cast(l_mem, 1) << g->gcstepsize) / WORK2MEM) * stepmul
+ : MAX_LMEM; /* overflow; keep maximum value */
+ do { /* repeat until pause or enough "credit" (negative debt) */
+ lu_mem work = singlestep(L); /* perform one single step */
+ debt -= work;
+ } while (debt > -stepsize && g->gcstate != GCSpause);
+ if (g->gcstate == GCSpause)
+ setpause(g); /* pause until next cycle */
+ else {
+ debt = (debt / stepmul) * WORK2MEM; /* convert 'work units' to bytes */
+ luaE_setdebt(g, debt);
+ }
+}
+
+/*
+** Performs a basic GC step if collector is running. (If collector is
+** not running, set a reasonable debt to avoid it being called at
+** every single check.)
+*/
+void luaC_step (lua_State *L) {
+ global_State *g = G(L);
+ if (!gcrunning(g)) /* not running? */
+ luaE_setdebt(g, -2000);
+ else {
+ if(isdecGCmodegen(g))
+ genstep(L, g);
+ else
+ incstep(L, g);
+ }
+}
+
+
+/*
+** Perform a full collection in incremental mode.
+** Before running the collection, check 'keepinvariant'; if it is true,
+** there may be some objects marked as black, so the collector has
+** to sweep all objects to turn them back to white (as white has not
+** changed, nothing will be collected).
+*/
+static void fullinc (lua_State *L, global_State *g) {
+ if (keepinvariant(g)) /* black objects? */
+ entersweep(L); /* sweep everything to turn them back to white */
+ /* finish any pending sweep phase to start a new cycle */
+ luaC_runtilstate(L, bitmask(GCSpause));
+ luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */
+ /* estimate must be correct after a full GC cycle */
+ lua_assert(g->GCestimate == gettotalbytes(g));
+ luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */
+ setpause(g);
+}
+
+
+/*
+** Performs a full GC cycle; if 'isemergency', set a flag to avoid
+** some operations which could change the interpreter state in some
+** unexpected ways (running finalizers and shrinking some structures).
+*/
+void luaC_fullgc (lua_State *L, int isemergency) {
+ global_State *g = G(L);
+ lua_assert(!g->gcemergency);
+ g->gcemergency = isemergency; /* set flag */
+ if (g->gckind == KGC_INC)
+ fullinc(L, g);
+ else
+ fullgen(L, g);
+ g->gcemergency = 0;
+}
+
+/* }====================================================== */
+
+
diff --git a/src/libs/3rdparty/lua/src/lgc.h b/src/libs/3rdparty/lua/src/lgc.h
new file mode 100644
index 0000000000..538f6edccc
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lgc.h
@@ -0,0 +1,202 @@
+/*
+** $Id: lgc.h $
+** Garbage Collector
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lgc_h
+#define lgc_h
+
+
+#include "lobject.h"
+#include "lstate.h"
+
+/*
+** Collectable objects may have one of three colors: white, which means
+** the object is not marked; gray, which means the object is marked, but
+** its references may be not marked; and black, which means that the
+** object and all its references are marked. The main invariant of the
+** garbage collector, while marking objects, is that a black object can
+** never point to a white one. Moreover, any gray object must be in a
+** "gray list" (gray, grayagain, weak, allweak, ephemeron) so that it
+** can be visited again before finishing the collection cycle. (Open
+** upvalues are an exception to this rule.) These lists have no meaning
+** when the invariant is not being enforced (e.g., sweep phase).
+*/
+
+
+/*
+** Possible states of the Garbage Collector
+*/
+#define GCSpropagate 0
+#define GCSenteratomic 1
+#define GCSatomic 2
+#define GCSswpallgc 3
+#define GCSswpfinobj 4
+#define GCSswptobefnz 5
+#define GCSswpend 6
+#define GCScallfin 7
+#define GCSpause 8
+
+
+#define issweepphase(g) \
+ (GCSswpallgc <= (g)->gcstate && (g)->gcstate <= GCSswpend)
+
+
+/*
+** macro to tell when main invariant (white objects cannot point to black
+** ones) must be kept. During a collection, the sweep
+** phase may break the invariant, as objects turned white may point to
+** still-black objects. The invariant is restored when sweep ends and
+** all objects are white again.
+*/
+
+#define keepinvariant(g) ((g)->gcstate <= GCSatomic)
+
+
+/*
+** some useful bit tricks
+*/
+#define resetbits(x,m) ((x) &= cast_byte(~(m)))
+#define setbits(x,m) ((x) |= (m))
+#define testbits(x,m) ((x) & (m))
+#define bitmask(b) (1<<(b))
+#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2))
+#define l_setbit(x,b) setbits(x, bitmask(b))
+#define resetbit(x,b) resetbits(x, bitmask(b))
+#define testbit(x,b) testbits(x, bitmask(b))
+
+
+/*
+** Layout for bit use in 'marked' field. First three bits are
+** used for object "age" in generational mode. Last bit is used
+** by tests.
+*/
+#define WHITE0BIT 3 /* object is white (type 0) */
+#define WHITE1BIT 4 /* object is white (type 1) */
+#define BLACKBIT 5 /* object is black */
+#define FINALIZEDBIT 6 /* object has been marked for finalization */
+
+#define TESTBIT 7
+
+
+
+#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT)
+
+
+#define iswhite(x) testbits((x)->marked, WHITEBITS)
+#define isblack(x) testbit((x)->marked, BLACKBIT)
+#define isgray(x) /* neither white nor black */ \
+ (!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT)))
+
+#define tofinalize(x) testbit((x)->marked, FINALIZEDBIT)
+
+#define otherwhite(g) ((g)->currentwhite ^ WHITEBITS)
+#define isdeadm(ow,m) ((m) & (ow))
+#define isdead(g,v) isdeadm(otherwhite(g), (v)->marked)
+
+#define changewhite(x) ((x)->marked ^= WHITEBITS)
+#define nw2black(x) \
+ check_exp(!iswhite(x), l_setbit((x)->marked, BLACKBIT))
+
+#define luaC_white(g) cast_byte((g)->currentwhite & WHITEBITS)
+
+
+/* object age in generational mode */
+#define G_NEW 0 /* created in current cycle */
+#define G_SURVIVAL 1 /* created in previous cycle */
+#define G_OLD0 2 /* marked old by frw. barrier in this cycle */
+#define G_OLD1 3 /* first full cycle as old */
+#define G_OLD 4 /* really old object (not to be visited) */
+#define G_TOUCHED1 5 /* old object touched this cycle */
+#define G_TOUCHED2 6 /* old object touched in previous cycle */
+
+#define AGEBITS 7 /* all age bits (111) */
+
+#define getage(o) ((o)->marked & AGEBITS)
+#define setage(o,a) ((o)->marked = cast_byte(((o)->marked & (~AGEBITS)) | a))
+#define isold(o) (getage(o) > G_SURVIVAL)
+
+#define changeage(o,f,t) \
+ check_exp(getage(o) == (f), (o)->marked ^= ((f)^(t)))
+
+
+/* Default Values for GC parameters */
+#define LUAI_GENMAJORMUL 100
+#define LUAI_GENMINORMUL 20
+
+/* wait memory to double before starting new cycle */
+#define LUAI_GCPAUSE 200
+
+/*
+** some gc parameters are stored divided by 4 to allow a maximum value
+** up to 1023 in a 'lu_byte'.
+*/
+#define getgcparam(p) ((p) * 4)
+#define setgcparam(p,v) ((p) = (v) / 4)
+
+#define LUAI_GCMUL 100
+
+/* how much to allocate before next GC step (log2) */
+#define LUAI_GCSTEPSIZE 13 /* 8 KB */
+
+
+/*
+** Check whether the declared GC mode is generational. While in
+** generational mode, the collector can go temporarily to incremental
+** mode to improve performance. This is signaled by 'g->lastatomic != 0'.
+*/
+#define isdecGCmodegen(g) (g->gckind == KGC_GEN || g->lastatomic != 0)
+
+
+/*
+** Control when GC is running:
+*/
+#define GCSTPUSR 1 /* bit true when GC stopped by user */
+#define GCSTPGC 2 /* bit true when GC stopped by itself */
+#define GCSTPCLS 4 /* bit true when closing Lua state */
+#define gcrunning(g) ((g)->gcstp == 0)
+
+
+/*
+** Does one step of collection when debt becomes positive. 'pre'/'pos'
+** allows some adjustments to be done only when needed. macro
+** 'condchangemem' is used only for heavy tests (forcing a full
+** GC cycle on every opportunity)
+*/
+#define luaC_condGC(L,pre,pos) \
+ { if (G(L)->GCdebt > 0) { pre; luaC_step(L); pos;}; \
+ condchangemem(L,pre,pos); }
+
+/* more often than not, 'pre'/'pos' are empty */
+#define luaC_checkGC(L) luaC_condGC(L,(void)0,(void)0)
+
+
+#define luaC_objbarrier(L,p,o) ( \
+ (isblack(p) && iswhite(o)) ? \
+ luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0))
+
+#define luaC_barrier(L,p,v) ( \
+ iscollectable(v) ? luaC_objbarrier(L,p,gcvalue(v)) : cast_void(0))
+
+#define luaC_objbarrierback(L,p,o) ( \
+ (isblack(p) && iswhite(o)) ? luaC_barrierback_(L,p) : cast_void(0))
+
+#define luaC_barrierback(L,p,v) ( \
+ iscollectable(v) ? luaC_objbarrierback(L, p, gcvalue(v)) : cast_void(0))
+
+LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o);
+LUAI_FUNC void luaC_freeallobjects (lua_State *L);
+LUAI_FUNC void luaC_step (lua_State *L);
+LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask);
+LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency);
+LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz);
+LUAI_FUNC GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz,
+ size_t offset);
+LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v);
+LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o);
+LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt);
+LUAI_FUNC void luaC_changemode (lua_State *L, int newmode);
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/linit.c b/src/libs/3rdparty/lua/src/linit.c
new file mode 100644
index 0000000000..69808f84f4
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/linit.c
@@ -0,0 +1,65 @@
+/*
+** $Id: linit.c $
+** Initialization of libraries for lua.c and other clients
+** See Copyright Notice in lua.h
+*/
+
+
+#define linit_c
+#define LUA_LIB
+
+/*
+** If you embed Lua in your program and need to open the standard
+** libraries, call luaL_openlibs in your program. If you need a
+** different set of libraries, copy this file to your project and edit
+** it to suit your needs.
+**
+** You can also *preload* libraries, so that a later 'require' can
+** open the library, which is already linked to the application.
+** For that, do the following code:
+**
+** luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
+** lua_pushcfunction(L, luaopen_modname);
+** lua_setfield(L, -2, modname);
+** lua_pop(L, 1); // remove PRELOAD table
+*/
+
+#include "lprefix.h"
+
+
+#include <stddef.h>
+
+#include "lua.h"
+
+#include "lualib.h"
+#include "lauxlib.h"
+
+
+/*
+** these libs are loaded by lua.c and are readily available to any Lua
+** program
+*/
+static const luaL_Reg loadedlibs[] = {
+ {LUA_GNAME, luaopen_base},
+ {LUA_LOADLIBNAME, luaopen_package},
+ {LUA_COLIBNAME, luaopen_coroutine},
+ {LUA_TABLIBNAME, luaopen_table},
+ {LUA_IOLIBNAME, luaopen_io},
+ {LUA_OSLIBNAME, luaopen_os},
+ {LUA_STRLIBNAME, luaopen_string},
+ {LUA_MATHLIBNAME, luaopen_math},
+ {LUA_UTF8LIBNAME, luaopen_utf8},
+ {LUA_DBLIBNAME, luaopen_debug},
+ {NULL, NULL}
+};
+
+
+LUALIB_API void luaL_openlibs (lua_State *L) {
+ const luaL_Reg *lib;
+ /* "require" functions from 'loadedlibs' and set results to global table */
+ for (lib = loadedlibs; lib->func; lib++) {
+ luaL_requiref(L, lib->name, lib->func, 1);
+ lua_pop(L, 1); /* remove lib */
+ }
+}
+
diff --git a/src/libs/3rdparty/lua/src/liolib.c b/src/libs/3rdparty/lua/src/liolib.c
new file mode 100644
index 0000000000..b08397da45
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/liolib.c
@@ -0,0 +1,828 @@
+/*
+** $Id: liolib.c $
+** Standard I/O (and system) library
+** See Copyright Notice in lua.h
+*/
+
+#define liolib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <ctype.h>
+#include <errno.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+
+
+/*
+** Change this macro to accept other modes for 'fopen' besides
+** the standard ones.
+*/
+#if !defined(l_checkmode)
+
+/* accepted extensions to 'mode' in 'fopen' */
+#if !defined(L_MODEEXT)
+#define L_MODEEXT "b"
+#endif
+
+/* Check whether 'mode' matches '[rwa]%+?[L_MODEEXT]*' */
+static int l_checkmode (const char *mode) {
+ return (*mode != '\0' && strchr("rwa", *(mode++)) != NULL &&
+ (*mode != '+' || ((void)(++mode), 1)) && /* skip if char is '+' */
+ (strspn(mode, L_MODEEXT) == strlen(mode))); /* check extensions */
+}
+
+#endif
+
+/*
+** {======================================================
+** l_popen spawns a new process connected to the current
+** one through the file streams.
+** =======================================================
+*/
+
+#if !defined(l_popen) /* { */
+
+#if defined(LUA_USE_POSIX) /* { */
+
+#define l_popen(L,c,m) (fflush(NULL), popen(c,m))
+#define l_pclose(L,file) (pclose(file))
+
+#elif defined(LUA_USE_WINDOWS) /* }{ */
+
+#define l_popen(L,c,m) (_popen(c,m))
+#define l_pclose(L,file) (_pclose(file))
+
+#if !defined(l_checkmodep)
+/* Windows accepts "[rw][bt]?" as valid modes */
+#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && \
+ (m[1] == '\0' || ((m[1] == 'b' || m[1] == 't') && m[2] == '\0')))
+#endif
+
+#else /* }{ */
+
+/* ISO C definitions */
+#define l_popen(L,c,m) \
+ ((void)c, (void)m, \
+ luaL_error(L, "'popen' not supported"), \
+ (FILE*)0)
+#define l_pclose(L,file) ((void)L, (void)file, -1)
+
+#endif /* } */
+
+#endif /* } */
+
+
+#if !defined(l_checkmodep)
+/* By default, Lua accepts only "r" or "w" as valid modes */
+#define l_checkmodep(m) ((m[0] == 'r' || m[0] == 'w') && m[1] == '\0')
+#endif
+
+/* }====================================================== */
+
+
+#if !defined(l_getc) /* { */
+
+#if defined(LUA_USE_POSIX)
+#define l_getc(f) getc_unlocked(f)
+#define l_lockfile(f) flockfile(f)
+#define l_unlockfile(f) funlockfile(f)
+#else
+#define l_getc(f) getc(f)
+#define l_lockfile(f) ((void)0)
+#define l_unlockfile(f) ((void)0)
+#endif
+
+#endif /* } */
+
+
+/*
+** {======================================================
+** l_fseek: configuration for longer offsets
+** =======================================================
+*/
+
+#if !defined(l_fseek) /* { */
+
+#if defined(LUA_USE_POSIX) /* { */
+
+#include <sys/types.h>
+
+#define l_fseek(f,o,w) fseeko(f,o,w)
+#define l_ftell(f) ftello(f)
+#define l_seeknum off_t
+
+#elif defined(LUA_USE_WINDOWS) && !defined(_CRTIMP_TYPEINFO) \
+ && defined(_MSC_VER) && (_MSC_VER >= 1400) /* }{ */
+
+/* Windows (but not DDK) and Visual C++ 2005 or higher */
+#define l_fseek(f,o,w) _fseeki64(f,o,w)
+#define l_ftell(f) _ftelli64(f)
+#define l_seeknum __int64
+
+#else /* }{ */
+
+/* ISO C definitions */
+#define l_fseek(f,o,w) fseek(f,o,w)
+#define l_ftell(f) ftell(f)
+#define l_seeknum long
+
+#endif /* } */
+
+#endif /* } */
+
+/* }====================================================== */
+
+
+
+#define IO_PREFIX "_IO_"
+#define IOPREF_LEN (sizeof(IO_PREFIX)/sizeof(char) - 1)
+#define IO_INPUT (IO_PREFIX "input")
+#define IO_OUTPUT (IO_PREFIX "output")
+
+
+typedef luaL_Stream LStream;
+
+
+#define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE))
+
+#define isclosed(p) ((p)->closef == NULL)
+
+
+static int io_type (lua_State *L) {
+ LStream *p;
+ luaL_checkany(L, 1);
+ p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE);
+ if (p == NULL)
+ luaL_pushfail(L); /* not a file */
+ else if (isclosed(p))
+ lua_pushliteral(L, "closed file");
+ else
+ lua_pushliteral(L, "file");
+ return 1;
+}
+
+
+static int f_tostring (lua_State *L) {
+ LStream *p = tolstream(L);
+ if (isclosed(p))
+ lua_pushliteral(L, "file (closed)");
+ else
+ lua_pushfstring(L, "file (%p)", p->f);
+ return 1;
+}
+
+
+static FILE *tofile (lua_State *L) {
+ LStream *p = tolstream(L);
+ if (l_unlikely(isclosed(p)))
+ luaL_error(L, "attempt to use a closed file");
+ lua_assert(p->f);
+ return p->f;
+}
+
+
+/*
+** When creating file handles, always creates a 'closed' file handle
+** before opening the actual file; so, if there is a memory error, the
+** handle is in a consistent state.
+*/
+static LStream *newprefile (lua_State *L) {
+ LStream *p = (LStream *)lua_newuserdatauv(L, sizeof(LStream), 0);
+ p->closef = NULL; /* mark file handle as 'closed' */
+ luaL_setmetatable(L, LUA_FILEHANDLE);
+ return p;
+}
+
+
+/*
+** Calls the 'close' function from a file handle. The 'volatile' avoids
+** a bug in some versions of the Clang compiler (e.g., clang 3.0 for
+** 32 bits).
+*/
+static int aux_close (lua_State *L) {
+ LStream *p = tolstream(L);
+ volatile lua_CFunction cf = p->closef;
+ p->closef = NULL; /* mark stream as closed */
+ return (*cf)(L); /* close it */
+}
+
+
+static int f_close (lua_State *L) {
+ tofile(L); /* make sure argument is an open stream */
+ return aux_close(L);
+}
+
+
+static int io_close (lua_State *L) {
+ if (lua_isnone(L, 1)) /* no argument? */
+ lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use default output */
+ return f_close(L);
+}
+
+
+static int f_gc (lua_State *L) {
+ LStream *p = tolstream(L);
+ if (!isclosed(p) && p->f != NULL)
+ aux_close(L); /* ignore closed and incompletely open files */
+ return 0;
+}
+
+
+/*
+** function to close regular files
+*/
+static int io_fclose (lua_State *L) {
+ LStream *p = tolstream(L);
+ int res = fclose(p->f);
+ return luaL_fileresult(L, (res == 0), NULL);
+}
+
+
+static LStream *newfile (lua_State *L) {
+ LStream *p = newprefile(L);
+ p->f = NULL;
+ p->closef = &io_fclose;
+ return p;
+}
+
+
+static void opencheck (lua_State *L, const char *fname, const char *mode) {
+ LStream *p = newfile(L);
+ p->f = fopen(fname, mode);
+ if (l_unlikely(p->f == NULL))
+ luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno));
+}
+
+
+static int io_open (lua_State *L) {
+ const char *filename = luaL_checkstring(L, 1);
+ const char *mode = luaL_optstring(L, 2, "r");
+ LStream *p = newfile(L);
+ const char *md = mode; /* to traverse/check mode */
+ luaL_argcheck(L, l_checkmode(md), 2, "invalid mode");
+ p->f = fopen(filename, mode);
+ return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
+}
+
+
+/*
+** function to close 'popen' files
+*/
+static int io_pclose (lua_State *L) {
+ LStream *p = tolstream(L);
+ errno = 0;
+ return luaL_execresult(L, l_pclose(L, p->f));
+}
+
+
+static int io_popen (lua_State *L) {
+ const char *filename = luaL_checkstring(L, 1);
+ const char *mode = luaL_optstring(L, 2, "r");
+ LStream *p = newprefile(L);
+ luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode");
+ p->f = l_popen(L, filename, mode);
+ p->closef = &io_pclose;
+ return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
+}
+
+
+static int io_tmpfile (lua_State *L) {
+ LStream *p = newfile(L);
+ p->f = tmpfile();
+ return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1;
+}
+
+
+static FILE *getiofile (lua_State *L, const char *findex) {
+ LStream *p;
+ lua_getfield(L, LUA_REGISTRYINDEX, findex);
+ p = (LStream *)lua_touserdata(L, -1);
+ if (l_unlikely(isclosed(p)))
+ luaL_error(L, "default %s file is closed", findex + IOPREF_LEN);
+ return p->f;
+}
+
+
+static int g_iofile (lua_State *L, const char *f, const char *mode) {
+ if (!lua_isnoneornil(L, 1)) {
+ const char *filename = lua_tostring(L, 1);
+ if (filename)
+ opencheck(L, filename, mode);
+ else {
+ tofile(L); /* check that it's a valid file handle */
+ lua_pushvalue(L, 1);
+ }
+ lua_setfield(L, LUA_REGISTRYINDEX, f);
+ }
+ /* return current value */
+ lua_getfield(L, LUA_REGISTRYINDEX, f);
+ return 1;
+}
+
+
+static int io_input (lua_State *L) {
+ return g_iofile(L, IO_INPUT, "r");
+}
+
+
+static int io_output (lua_State *L) {
+ return g_iofile(L, IO_OUTPUT, "w");
+}
+
+
+static int io_readline (lua_State *L);
+
+
+/*
+** maximum number of arguments to 'f:lines'/'io.lines' (it + 3 must fit
+** in the limit for upvalues of a closure)
+*/
+#define MAXARGLINE 250
+
+/*
+** Auxiliary function to create the iteration function for 'lines'.
+** The iteration function is a closure over 'io_readline', with
+** the following upvalues:
+** 1) The file being read (first value in the stack)
+** 2) the number of arguments to read
+** 3) a boolean, true iff file has to be closed when finished ('toclose')
+** *) a variable number of format arguments (rest of the stack)
+*/
+static void aux_lines (lua_State *L, int toclose) {
+ int n = lua_gettop(L) - 1; /* number of arguments to read */
+ luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments");
+ lua_pushvalue(L, 1); /* file */
+ lua_pushinteger(L, n); /* number of arguments to read */
+ lua_pushboolean(L, toclose); /* close/not close file when finished */
+ lua_rotate(L, 2, 3); /* move the three values to their positions */
+ lua_pushcclosure(L, io_readline, 3 + n);
+}
+
+
+static int f_lines (lua_State *L) {
+ tofile(L); /* check that it's a valid file handle */
+ aux_lines(L, 0);
+ return 1;
+}
+
+
+/*
+** Return an iteration function for 'io.lines'. If file has to be
+** closed, also returns the file itself as a second result (to be
+** closed as the state at the exit of a generic for).
+*/
+static int io_lines (lua_State *L) {
+ int toclose;
+ if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */
+ if (lua_isnil(L, 1)) { /* no file name? */
+ lua_getfield(L, LUA_REGISTRYINDEX, IO_INPUT); /* get default input */
+ lua_replace(L, 1); /* put it at index 1 */
+ tofile(L); /* check that it's a valid file handle */
+ toclose = 0; /* do not close it after iteration */
+ }
+ else { /* open a new file */
+ const char *filename = luaL_checkstring(L, 1);
+ opencheck(L, filename, "r");
+ lua_replace(L, 1); /* put file at index 1 */
+ toclose = 1; /* close it after iteration */
+ }
+ aux_lines(L, toclose); /* push iteration function */
+ if (toclose) {
+ lua_pushnil(L); /* state */
+ lua_pushnil(L); /* control */
+ lua_pushvalue(L, 1); /* file is the to-be-closed variable (4th result) */
+ return 4;
+ }
+ else
+ return 1;
+}
+
+
+/*
+** {======================================================
+** READ
+** =======================================================
+*/
+
+
+/* maximum length of a numeral */
+#if !defined (L_MAXLENNUM)
+#define L_MAXLENNUM 200
+#endif
+
+
+/* auxiliary structure used by 'read_number' */
+typedef struct {
+ FILE *f; /* file being read */
+ int c; /* current character (look ahead) */
+ int n; /* number of elements in buffer 'buff' */
+ char buff[L_MAXLENNUM + 1]; /* +1 for ending '\0' */
+} RN;
+
+
+/*
+** Add current char to buffer (if not out of space) and read next one
+*/
+static int nextc (RN *rn) {
+ if (l_unlikely(rn->n >= L_MAXLENNUM)) { /* buffer overflow? */
+ rn->buff[0] = '\0'; /* invalidate result */
+ return 0; /* fail */
+ }
+ else {
+ rn->buff[rn->n++] = rn->c; /* save current char */
+ rn->c = l_getc(rn->f); /* read next one */
+ return 1;
+ }
+}
+
+
+/*
+** Accept current char if it is in 'set' (of size 2)
+*/
+static int test2 (RN *rn, const char *set) {
+ if (rn->c == set[0] || rn->c == set[1])
+ return nextc(rn);
+ else return 0;
+}
+
+
+/*
+** Read a sequence of (hex)digits
+*/
+static int readdigits (RN *rn, int hex) {
+ int count = 0;
+ while ((hex ? isxdigit(rn->c) : isdigit(rn->c)) && nextc(rn))
+ count++;
+ return count;
+}
+
+
+/*
+** Read a number: first reads a valid prefix of a numeral into a buffer.
+** Then it calls 'lua_stringtonumber' to check whether the format is
+** correct and to convert it to a Lua number.
+*/
+static int read_number (lua_State *L, FILE *f) {
+ RN rn;
+ int count = 0;
+ int hex = 0;
+ char decp[2];
+ rn.f = f; rn.n = 0;
+ decp[0] = lua_getlocaledecpoint(); /* get decimal point from locale */
+ decp[1] = '.'; /* always accept a dot */
+ l_lockfile(rn.f);
+ do { rn.c = l_getc(rn.f); } while (isspace(rn.c)); /* skip spaces */
+ test2(&rn, "-+"); /* optional sign */
+ if (test2(&rn, "00")) {
+ if (test2(&rn, "xX")) hex = 1; /* numeral is hexadecimal */
+ else count = 1; /* count initial '0' as a valid digit */
+ }
+ count += readdigits(&rn, hex); /* integral part */
+ if (test2(&rn, decp)) /* decimal point? */
+ count += readdigits(&rn, hex); /* fractional part */
+ if (count > 0 && test2(&rn, (hex ? "pP" : "eE"))) { /* exponent mark? */
+ test2(&rn, "-+"); /* exponent sign */
+ readdigits(&rn, 0); /* exponent digits */
+ }
+ ungetc(rn.c, rn.f); /* unread look-ahead char */
+ l_unlockfile(rn.f);
+ rn.buff[rn.n] = '\0'; /* finish string */
+ if (l_likely(lua_stringtonumber(L, rn.buff)))
+ return 1; /* ok, it is a valid number */
+ else { /* invalid format */
+ lua_pushnil(L); /* "result" to be removed */
+ return 0; /* read fails */
+ }
+}
+
+
+static int test_eof (lua_State *L, FILE *f) {
+ int c = getc(f);
+ ungetc(c, f); /* no-op when c == EOF */
+ lua_pushliteral(L, "");
+ return (c != EOF);
+}
+
+
+static int read_line (lua_State *L, FILE *f, int chop) {
+ luaL_Buffer b;
+ int c;
+ luaL_buffinit(L, &b);
+ do { /* may need to read several chunks to get whole line */
+ char *buff = luaL_prepbuffer(&b); /* preallocate buffer space */
+ int i = 0;
+ l_lockfile(f); /* no memory errors can happen inside the lock */
+ while (i < LUAL_BUFFERSIZE && (c = l_getc(f)) != EOF && c != '\n')
+ buff[i++] = c; /* read up to end of line or buffer limit */
+ l_unlockfile(f);
+ luaL_addsize(&b, i);
+ } while (c != EOF && c != '\n'); /* repeat until end of line */
+ if (!chop && c == '\n') /* want a newline and have one? */
+ luaL_addchar(&b, c); /* add ending newline to result */
+ luaL_pushresult(&b); /* close buffer */
+ /* return ok if read something (either a newline or something else) */
+ return (c == '\n' || lua_rawlen(L, -1) > 0);
+}
+
+
+static void read_all (lua_State *L, FILE *f) {
+ size_t nr;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ do { /* read file in chunks of LUAL_BUFFERSIZE bytes */
+ char *p = luaL_prepbuffer(&b);
+ nr = fread(p, sizeof(char), LUAL_BUFFERSIZE, f);
+ luaL_addsize(&b, nr);
+ } while (nr == LUAL_BUFFERSIZE);
+ luaL_pushresult(&b); /* close buffer */
+}
+
+
+static int read_chars (lua_State *L, FILE *f, size_t n) {
+ size_t nr; /* number of chars actually read */
+ char *p;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ p = luaL_prepbuffsize(&b, n); /* prepare buffer to read whole block */
+ nr = fread(p, sizeof(char), n, f); /* try to read 'n' chars */
+ luaL_addsize(&b, nr);
+ luaL_pushresult(&b); /* close buffer */
+ return (nr > 0); /* true iff read something */
+}
+
+
+static int g_read (lua_State *L, FILE *f, int first) {
+ int nargs = lua_gettop(L) - 1;
+ int n, success;
+ clearerr(f);
+ if (nargs == 0) { /* no arguments? */
+ success = read_line(L, f, 1);
+ n = first + 1; /* to return 1 result */
+ }
+ else {
+ /* ensure stack space for all results and for auxlib's buffer */
+ luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
+ success = 1;
+ for (n = first; nargs-- && success; n++) {
+ if (lua_type(L, n) == LUA_TNUMBER) {
+ size_t l = (size_t)luaL_checkinteger(L, n);
+ success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
+ }
+ else {
+ const char *p = luaL_checkstring(L, n);
+ if (*p == '*') p++; /* skip optional '*' (for compatibility) */
+ switch (*p) {
+ case 'n': /* number */
+ success = read_number(L, f);
+ break;
+ case 'l': /* line */
+ success = read_line(L, f, 1);
+ break;
+ case 'L': /* line with end-of-line */
+ success = read_line(L, f, 0);
+ break;
+ case 'a': /* file */
+ read_all(L, f); /* read entire file */
+ success = 1; /* always success */
+ break;
+ default:
+ return luaL_argerror(L, n, "invalid format");
+ }
+ }
+ }
+ }
+ if (ferror(f))
+ return luaL_fileresult(L, 0, NULL);
+ if (!success) {
+ lua_pop(L, 1); /* remove last result */
+ luaL_pushfail(L); /* push nil instead */
+ }
+ return n - first;
+}
+
+
+static int io_read (lua_State *L) {
+ return g_read(L, getiofile(L, IO_INPUT), 1);
+}
+
+
+static int f_read (lua_State *L) {
+ return g_read(L, tofile(L), 2);
+}
+
+
+/*
+** Iteration function for 'lines'.
+*/
+static int io_readline (lua_State *L) {
+ LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1));
+ int i;
+ int n = (int)lua_tointeger(L, lua_upvalueindex(2));
+ if (isclosed(p)) /* file is already closed? */
+ return luaL_error(L, "file is already closed");
+ lua_settop(L , 1);
+ luaL_checkstack(L, n, "too many arguments");
+ for (i = 1; i <= n; i++) /* push arguments to 'g_read' */
+ lua_pushvalue(L, lua_upvalueindex(3 + i));
+ n = g_read(L, p->f, 2); /* 'n' is number of results */
+ lua_assert(n > 0); /* should return at least a nil */
+ if (lua_toboolean(L, -n)) /* read at least one value? */
+ return n; /* return them */
+ else { /* first result is false: EOF or error */
+ if (n > 1) { /* is there error information? */
+ /* 2nd result is error message */
+ return luaL_error(L, "%s", lua_tostring(L, -n + 1));
+ }
+ if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */
+ lua_settop(L, 0); /* clear stack */
+ lua_pushvalue(L, lua_upvalueindex(1)); /* push file at index 1 */
+ aux_close(L); /* close it */
+ }
+ return 0;
+ }
+}
+
+/* }====================================================== */
+
+
+static int g_write (lua_State *L, FILE *f, int arg) {
+ int nargs = lua_gettop(L) - arg;
+ int status = 1;
+ for (; nargs--; arg++) {
+ if (lua_type(L, arg) == LUA_TNUMBER) {
+ /* optimization: could be done exactly as for strings */
+ int len = lua_isinteger(L, arg)
+ ? fprintf(f, LUA_INTEGER_FMT,
+ (LUAI_UACINT)lua_tointeger(L, arg))
+ : fprintf(f, LUA_NUMBER_FMT,
+ (LUAI_UACNUMBER)lua_tonumber(L, arg));
+ status = status && (len > 0);
+ }
+ else {
+ size_t l;
+ const char *s = luaL_checklstring(L, arg, &l);
+ status = status && (fwrite(s, sizeof(char), l, f) == l);
+ }
+ }
+ if (l_likely(status))
+ return 1; /* file handle already on stack top */
+ else return luaL_fileresult(L, status, NULL);
+}
+
+
+static int io_write (lua_State *L) {
+ return g_write(L, getiofile(L, IO_OUTPUT), 1);
+}
+
+
+static int f_write (lua_State *L) {
+ FILE *f = tofile(L);
+ lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */
+ return g_write(L, f, 2);
+}
+
+
+static int f_seek (lua_State *L) {
+ static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
+ static const char *const modenames[] = {"set", "cur", "end", NULL};
+ FILE *f = tofile(L);
+ int op = luaL_checkoption(L, 2, "cur", modenames);
+ lua_Integer p3 = luaL_optinteger(L, 3, 0);
+ l_seeknum offset = (l_seeknum)p3;
+ luaL_argcheck(L, (lua_Integer)offset == p3, 3,
+ "not an integer in proper range");
+ op = l_fseek(f, offset, mode[op]);
+ if (l_unlikely(op))
+ return luaL_fileresult(L, 0, NULL); /* error */
+ else {
+ lua_pushinteger(L, (lua_Integer)l_ftell(f));
+ return 1;
+ }
+}
+
+
+static int f_setvbuf (lua_State *L) {
+ static const int mode[] = {_IONBF, _IOFBF, _IOLBF};
+ static const char *const modenames[] = {"no", "full", "line", NULL};
+ FILE *f = tofile(L);
+ int op = luaL_checkoption(L, 2, NULL, modenames);
+ lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
+ int res = setvbuf(f, NULL, mode[op], (size_t)sz);
+ return luaL_fileresult(L, res == 0, NULL);
+}
+
+
+
+static int io_flush (lua_State *L) {
+ return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);
+}
+
+
+static int f_flush (lua_State *L) {
+ return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL);
+}
+
+
+/*
+** functions for 'io' library
+*/
+static const luaL_Reg iolib[] = {
+ {"close", io_close},
+ {"flush", io_flush},
+ {"input", io_input},
+ {"lines", io_lines},
+ {"open", io_open},
+ {"output", io_output},
+ {"popen", io_popen},
+ {"read", io_read},
+ {"tmpfile", io_tmpfile},
+ {"type", io_type},
+ {"write", io_write},
+ {NULL, NULL}
+};
+
+
+/*
+** methods for file handles
+*/
+static const luaL_Reg meth[] = {
+ {"read", f_read},
+ {"write", f_write},
+ {"lines", f_lines},
+ {"flush", f_flush},
+ {"seek", f_seek},
+ {"close", f_close},
+ {"setvbuf", f_setvbuf},
+ {NULL, NULL}
+};
+
+
+/*
+** metamethods for file handles
+*/
+static const luaL_Reg metameth[] = {
+ {"__index", NULL}, /* place holder */
+ {"__gc", f_gc},
+ {"__close", f_gc},
+ {"__tostring", f_tostring},
+ {NULL, NULL}
+};
+
+
+static void createmeta (lua_State *L) {
+ luaL_newmetatable(L, LUA_FILEHANDLE); /* metatable for file handles */
+ luaL_setfuncs(L, metameth, 0); /* add metamethods to new metatable */
+ luaL_newlibtable(L, meth); /* create method table */
+ luaL_setfuncs(L, meth, 0); /* add file methods to method table */
+ lua_setfield(L, -2, "__index"); /* metatable.__index = method table */
+ lua_pop(L, 1); /* pop metatable */
+}
+
+
+/*
+** function to (not) close the standard files stdin, stdout, and stderr
+*/
+static int io_noclose (lua_State *L) {
+ LStream *p = tolstream(L);
+ p->closef = &io_noclose; /* keep file opened */
+ luaL_pushfail(L);
+ lua_pushliteral(L, "cannot close standard file");
+ return 2;
+}
+
+
+static void createstdfile (lua_State *L, FILE *f, const char *k,
+ const char *fname) {
+ LStream *p = newprefile(L);
+ p->f = f;
+ p->closef = &io_noclose;
+ if (k != NULL) {
+ lua_pushvalue(L, -1);
+ lua_setfield(L, LUA_REGISTRYINDEX, k); /* add file to registry */
+ }
+ lua_setfield(L, -2, fname); /* add file to module */
+}
+
+
+LUAMOD_API int luaopen_io (lua_State *L) {
+ luaL_newlib(L, iolib); /* new module */
+ createmeta(L);
+ /* create (and set) default files */
+ createstdfile(L, stdin, IO_INPUT, "stdin");
+ createstdfile(L, stdout, IO_OUTPUT, "stdout");
+ createstdfile(L, stderr, NULL, "stderr");
+ return 1;
+}
+
diff --git a/src/libs/3rdparty/lua/src/ljumptab.h b/src/libs/3rdparty/lua/src/ljumptab.h
new file mode 100644
index 0000000000..8306f250cc
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ljumptab.h
@@ -0,0 +1,112 @@
+/*
+** $Id: ljumptab.h $
+** Jump Table for the Lua interpreter
+** See Copyright Notice in lua.h
+*/
+
+
+#undef vmdispatch
+#undef vmcase
+#undef vmbreak
+
+#define vmdispatch(x) goto *disptab[x];
+
+#define vmcase(l) L_##l:
+
+#define vmbreak vmfetch(); vmdispatch(GET_OPCODE(i));
+
+
+static const void *const disptab[NUM_OPCODES] = {
+
+#if 0
+** you can update the following list with this command:
+**
+** sed -n '/^OP_/\!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h
+**
+#endif
+
+&&L_OP_MOVE,
+&&L_OP_LOADI,
+&&L_OP_LOADF,
+&&L_OP_LOADK,
+&&L_OP_LOADKX,
+&&L_OP_LOADFALSE,
+&&L_OP_LFALSESKIP,
+&&L_OP_LOADTRUE,
+&&L_OP_LOADNIL,
+&&L_OP_GETUPVAL,
+&&L_OP_SETUPVAL,
+&&L_OP_GETTABUP,
+&&L_OP_GETTABLE,
+&&L_OP_GETI,
+&&L_OP_GETFIELD,
+&&L_OP_SETTABUP,
+&&L_OP_SETTABLE,
+&&L_OP_SETI,
+&&L_OP_SETFIELD,
+&&L_OP_NEWTABLE,
+&&L_OP_SELF,
+&&L_OP_ADDI,
+&&L_OP_ADDK,
+&&L_OP_SUBK,
+&&L_OP_MULK,
+&&L_OP_MODK,
+&&L_OP_POWK,
+&&L_OP_DIVK,
+&&L_OP_IDIVK,
+&&L_OP_BANDK,
+&&L_OP_BORK,
+&&L_OP_BXORK,
+&&L_OP_SHRI,
+&&L_OP_SHLI,
+&&L_OP_ADD,
+&&L_OP_SUB,
+&&L_OP_MUL,
+&&L_OP_MOD,
+&&L_OP_POW,
+&&L_OP_DIV,
+&&L_OP_IDIV,
+&&L_OP_BAND,
+&&L_OP_BOR,
+&&L_OP_BXOR,
+&&L_OP_SHL,
+&&L_OP_SHR,
+&&L_OP_MMBIN,
+&&L_OP_MMBINI,
+&&L_OP_MMBINK,
+&&L_OP_UNM,
+&&L_OP_BNOT,
+&&L_OP_NOT,
+&&L_OP_LEN,
+&&L_OP_CONCAT,
+&&L_OP_CLOSE,
+&&L_OP_TBC,
+&&L_OP_JMP,
+&&L_OP_EQ,
+&&L_OP_LT,
+&&L_OP_LE,
+&&L_OP_EQK,
+&&L_OP_EQI,
+&&L_OP_LTI,
+&&L_OP_LEI,
+&&L_OP_GTI,
+&&L_OP_GEI,
+&&L_OP_TEST,
+&&L_OP_TESTSET,
+&&L_OP_CALL,
+&&L_OP_TAILCALL,
+&&L_OP_RETURN,
+&&L_OP_RETURN0,
+&&L_OP_RETURN1,
+&&L_OP_FORLOOP,
+&&L_OP_FORPREP,
+&&L_OP_TFORPREP,
+&&L_OP_TFORCALL,
+&&L_OP_TFORLOOP,
+&&L_OP_SETLIST,
+&&L_OP_CLOSURE,
+&&L_OP_VARARG,
+&&L_OP_VARARGPREP,
+&&L_OP_EXTRAARG
+
+};
diff --git a/src/libs/3rdparty/lua/src/llex.c b/src/libs/3rdparty/lua/src/llex.c
new file mode 100644
index 0000000000..5fc39a5cde
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/llex.c
@@ -0,0 +1,581 @@
+/*
+** $Id: llex.c $
+** Lexical Analyzer
+** See Copyright Notice in lua.h
+*/
+
+#define llex_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <locale.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lctype.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lgc.h"
+#include "llex.h"
+#include "lobject.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "lzio.h"
+
+
+
+#define next(ls) (ls->current = zgetc(ls->z))
+
+
+
+#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r')
+
+
+/* ORDER RESERVED */
+static const char *const luaX_tokens [] = {
+ "and", "break", "do", "else", "elseif",
+ "end", "false", "for", "function", "goto", "if",
+ "in", "local", "nil", "not", "or", "repeat",
+ "return", "then", "true", "until", "while",
+ "//", "..", "...", "==", ">=", "<=", "~=",
+ "<<", ">>", "::", "<eof>",
+ "<number>", "<integer>", "<name>", "<string>"
+};
+
+
+#define save_and_next(ls) (save(ls, ls->current), next(ls))
+
+
+static l_noret lexerror (LexState *ls, const char *msg, int token);
+
+
+static void save (LexState *ls, int c) {
+ Mbuffer *b = ls->buff;
+ if (luaZ_bufflen(b) + 1 > luaZ_sizebuffer(b)) {
+ size_t newsize;
+ if (luaZ_sizebuffer(b) >= MAX_SIZE/2)
+ lexerror(ls, "lexical element too long", 0);
+ newsize = luaZ_sizebuffer(b) * 2;
+ luaZ_resizebuffer(ls->L, b, newsize);
+ }
+ b->buffer[luaZ_bufflen(b)++] = cast_char(c);
+}
+
+
+void luaX_init (lua_State *L) {
+ int i;
+ TString *e = luaS_newliteral(L, LUA_ENV); /* create env name */
+ luaC_fix(L, obj2gco(e)); /* never collect this name */
+ for (i=0; i<NUM_RESERVED; i++) {
+ TString *ts = luaS_new(L, luaX_tokens[i]);
+ luaC_fix(L, obj2gco(ts)); /* reserved words are never collected */
+ ts->extra = cast_byte(i+1); /* reserved word */
+ }
+}
+
+
+const char *luaX_token2str (LexState *ls, int token) {
+ if (token < FIRST_RESERVED) { /* single-byte symbols? */
+ if (lisprint(token))
+ return luaO_pushfstring(ls->L, "'%c'", token);
+ else /* control character */
+ return luaO_pushfstring(ls->L, "'<\\%d>'", token);
+ }
+ else {
+ const char *s = luaX_tokens[token - FIRST_RESERVED];
+ if (token < TK_EOS) /* fixed format (symbols and reserved words)? */
+ return luaO_pushfstring(ls->L, "'%s'", s);
+ else /* names, strings, and numerals */
+ return s;
+ }
+}
+
+
+static const char *txtToken (LexState *ls, int token) {
+ switch (token) {
+ case TK_NAME: case TK_STRING:
+ case TK_FLT: case TK_INT:
+ save(ls, '\0');
+ return luaO_pushfstring(ls->L, "'%s'", luaZ_buffer(ls->buff));
+ default:
+ return luaX_token2str(ls, token);
+ }
+}
+
+
+static l_noret lexerror (LexState *ls, const char *msg, int token) {
+ msg = luaG_addinfo(ls->L, msg, ls->source, ls->linenumber);
+ if (token)
+ luaO_pushfstring(ls->L, "%s near %s", msg, txtToken(ls, token));
+ luaD_throw(ls->L, LUA_ERRSYNTAX);
+}
+
+
+l_noret luaX_syntaxerror (LexState *ls, const char *msg) {
+ lexerror(ls, msg, ls->t.token);
+}
+
+
+/*
+** Creates a new string and anchors it in scanner's table so that it
+** will not be collected until the end of the compilation; by that time
+** it should be anchored somewhere. It also internalizes long strings,
+** ensuring there is only one copy of each unique string. The table
+** here is used as a set: the string enters as the key, while its value
+** is irrelevant. We use the string itself as the value only because it
+** is a TValue readily available. Later, the code generation can change
+** this value.
+*/
+TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
+ lua_State *L = ls->L;
+ TString *ts = luaS_newlstr(L, str, l); /* create new string */
+ const TValue *o = luaH_getstr(ls->h, ts);
+ if (!ttisnil(o)) /* string already present? */
+ ts = keystrval(nodefromval(o)); /* get saved copy */
+ else { /* not in use yet */
+ TValue *stv = s2v(L->top.p++); /* reserve stack space for string */
+ setsvalue(L, stv, ts); /* temporarily anchor the string */
+ luaH_finishset(L, ls->h, stv, o, stv); /* t[string] = string */
+ /* table is not a metatable, so it does not need to invalidate cache */
+ luaC_checkGC(L);
+ L->top.p--; /* remove string from stack */
+ }
+ return ts;
+}
+
+
+/*
+** increment line number and skips newline sequence (any of
+** \n, \r, \n\r, or \r\n)
+*/
+static void inclinenumber (LexState *ls) {
+ int old = ls->current;
+ lua_assert(currIsNewline(ls));
+ next(ls); /* skip '\n' or '\r' */
+ if (currIsNewline(ls) && ls->current != old)
+ next(ls); /* skip '\n\r' or '\r\n' */
+ if (++ls->linenumber >= MAX_INT)
+ lexerror(ls, "chunk has too many lines", 0);
+}
+
+
+void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source,
+ int firstchar) {
+ ls->t.token = 0;
+ ls->L = L;
+ ls->current = firstchar;
+ ls->lookahead.token = TK_EOS; /* no look-ahead token */
+ ls->z = z;
+ ls->fs = NULL;
+ ls->linenumber = 1;
+ ls->lastline = 1;
+ ls->source = source;
+ ls->envn = luaS_newliteral(L, LUA_ENV); /* get env name */
+ luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */
+}
+
+
+
+/*
+** =======================================================
+** LEXICAL ANALYZER
+** =======================================================
+*/
+
+
+static int check_next1 (LexState *ls, int c) {
+ if (ls->current == c) {
+ next(ls);
+ return 1;
+ }
+ else return 0;
+}
+
+
+/*
+** Check whether current char is in set 'set' (with two chars) and
+** saves it
+*/
+static int check_next2 (LexState *ls, const char *set) {
+ lua_assert(set[2] == '\0');
+ if (ls->current == set[0] || ls->current == set[1]) {
+ save_and_next(ls);
+ return 1;
+ }
+ else return 0;
+}
+
+
+/* LUA_NUMBER */
+/*
+** This function is quite liberal in what it accepts, as 'luaO_str2num'
+** will reject ill-formed numerals. Roughly, it accepts the following
+** pattern:
+**
+** %d(%x|%.|([Ee][+-]?))* | 0[Xx](%x|%.|([Pp][+-]?))*
+**
+** The only tricky part is to accept [+-] only after a valid exponent
+** mark, to avoid reading '3-4' or '0xe+1' as a single number.
+**
+** The caller might have already read an initial dot.
+*/
+static int read_numeral (LexState *ls, SemInfo *seminfo) {
+ TValue obj;
+ const char *expo = "Ee";
+ int first = ls->current;
+ lua_assert(lisdigit(ls->current));
+ save_and_next(ls);
+ if (first == '0' && check_next2(ls, "xX")) /* hexadecimal? */
+ expo = "Pp";
+ for (;;) {
+ if (check_next2(ls, expo)) /* exponent mark? */
+ check_next2(ls, "-+"); /* optional exponent sign */
+ else if (lisxdigit(ls->current) || ls->current == '.') /* '%x|%.' */
+ save_and_next(ls);
+ else break;
+ }
+ if (lislalpha(ls->current)) /* is numeral touching a letter? */
+ save_and_next(ls); /* force an error */
+ save(ls, '\0');
+ if (luaO_str2num(luaZ_buffer(ls->buff), &obj) == 0) /* format error? */
+ lexerror(ls, "malformed number", TK_FLT);
+ if (ttisinteger(&obj)) {
+ seminfo->i = ivalue(&obj);
+ return TK_INT;
+ }
+ else {
+ lua_assert(ttisfloat(&obj));
+ seminfo->r = fltvalue(&obj);
+ return TK_FLT;
+ }
+}
+
+
+/*
+** read a sequence '[=*[' or ']=*]', leaving the last bracket. If
+** sequence is well formed, return its number of '='s + 2; otherwise,
+** return 1 if it is a single bracket (no '='s and no 2nd bracket);
+** otherwise (an unfinished '[==...') return 0.
+*/
+static size_t skip_sep (LexState *ls) {
+ size_t count = 0;
+ int s = ls->current;
+ lua_assert(s == '[' || s == ']');
+ save_and_next(ls);
+ while (ls->current == '=') {
+ save_and_next(ls);
+ count++;
+ }
+ return (ls->current == s) ? count + 2
+ : (count == 0) ? 1
+ : 0;
+}
+
+
+static void read_long_string (LexState *ls, SemInfo *seminfo, size_t sep) {
+ int line = ls->linenumber; /* initial line (for error message) */
+ save_and_next(ls); /* skip 2nd '[' */
+ if (currIsNewline(ls)) /* string starts with a newline? */
+ inclinenumber(ls); /* skip it */
+ for (;;) {
+ switch (ls->current) {
+ case EOZ: { /* error */
+ const char *what = (seminfo ? "string" : "comment");
+ const char *msg = luaO_pushfstring(ls->L,
+ "unfinished long %s (starting at line %d)", what, line);
+ lexerror(ls, msg, TK_EOS);
+ break; /* to avoid warnings */
+ }
+ case ']': {
+ if (skip_sep(ls) == sep) {
+ save_and_next(ls); /* skip 2nd ']' */
+ goto endloop;
+ }
+ break;
+ }
+ case '\n': case '\r': {
+ save(ls, '\n');
+ inclinenumber(ls);
+ if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */
+ break;
+ }
+ default: {
+ if (seminfo) save_and_next(ls);
+ else next(ls);
+ }
+ }
+ } endloop:
+ if (seminfo)
+ seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + sep,
+ luaZ_bufflen(ls->buff) - 2 * sep);
+}
+
+
+static void esccheck (LexState *ls, int c, const char *msg) {
+ if (!c) {
+ if (ls->current != EOZ)
+ save_and_next(ls); /* add current to buffer for error message */
+ lexerror(ls, msg, TK_STRING);
+ }
+}
+
+
+static int gethexa (LexState *ls) {
+ save_and_next(ls);
+ esccheck (ls, lisxdigit(ls->current), "hexadecimal digit expected");
+ return luaO_hexavalue(ls->current);
+}
+
+
+static int readhexaesc (LexState *ls) {
+ int r = gethexa(ls);
+ r = (r << 4) + gethexa(ls);
+ luaZ_buffremove(ls->buff, 2); /* remove saved chars from buffer */
+ return r;
+}
+
+
+static unsigned long readutf8esc (LexState *ls) {
+ unsigned long r;
+ int i = 4; /* chars to be removed: '\', 'u', '{', and first digit */
+ save_and_next(ls); /* skip 'u' */
+ esccheck(ls, ls->current == '{', "missing '{'");
+ r = gethexa(ls); /* must have at least one digit */
+ while (cast_void(save_and_next(ls)), lisxdigit(ls->current)) {
+ i++;
+ esccheck(ls, r <= (0x7FFFFFFFu >> 4), "UTF-8 value too large");
+ r = (r << 4) + luaO_hexavalue(ls->current);
+ }
+ esccheck(ls, ls->current == '}', "missing '}'");
+ next(ls); /* skip '}' */
+ luaZ_buffremove(ls->buff, i); /* remove saved chars from buffer */
+ return r;
+}
+
+
+static void utf8esc (LexState *ls) {
+ char buff[UTF8BUFFSZ];
+ int n = luaO_utf8esc(buff, readutf8esc(ls));
+ for (; n > 0; n--) /* add 'buff' to string */
+ save(ls, buff[UTF8BUFFSZ - n]);
+}
+
+
+static int readdecesc (LexState *ls) {
+ int i;
+ int r = 0; /* result accumulator */
+ for (i = 0; i < 3 && lisdigit(ls->current); i++) { /* read up to 3 digits */
+ r = 10*r + ls->current - '0';
+ save_and_next(ls);
+ }
+ esccheck(ls, r <= UCHAR_MAX, "decimal escape too large");
+ luaZ_buffremove(ls->buff, i); /* remove read digits from buffer */
+ return r;
+}
+
+
+static void read_string (LexState *ls, int del, SemInfo *seminfo) {
+ save_and_next(ls); /* keep delimiter (for error messages) */
+ while (ls->current != del) {
+ switch (ls->current) {
+ case EOZ:
+ lexerror(ls, "unfinished string", TK_EOS);
+ break; /* to avoid warnings */
+ case '\n':
+ case '\r':
+ lexerror(ls, "unfinished string", TK_STRING);
+ break; /* to avoid warnings */
+ case '\\': { /* escape sequences */
+ int c; /* final character to be saved */
+ save_and_next(ls); /* keep '\\' for error messages */
+ switch (ls->current) {
+ case 'a': c = '\a'; goto read_save;
+ case 'b': c = '\b'; goto read_save;
+ case 'f': c = '\f'; goto read_save;
+ case 'n': c = '\n'; goto read_save;
+ case 'r': c = '\r'; goto read_save;
+ case 't': c = '\t'; goto read_save;
+ case 'v': c = '\v'; goto read_save;
+ case 'x': c = readhexaesc(ls); goto read_save;
+ case 'u': utf8esc(ls); goto no_save;
+ case '\n': case '\r':
+ inclinenumber(ls); c = '\n'; goto only_save;
+ case '\\': case '\"': case '\'':
+ c = ls->current; goto read_save;
+ case EOZ: goto no_save; /* will raise an error next loop */
+ case 'z': { /* zap following span of spaces */
+ luaZ_buffremove(ls->buff, 1); /* remove '\\' */
+ next(ls); /* skip the 'z' */
+ while (lisspace(ls->current)) {
+ if (currIsNewline(ls)) inclinenumber(ls);
+ else next(ls);
+ }
+ goto no_save;
+ }
+ default: {
+ esccheck(ls, lisdigit(ls->current), "invalid escape sequence");
+ c = readdecesc(ls); /* digital escape '\ddd' */
+ goto only_save;
+ }
+ }
+ read_save:
+ next(ls);
+ /* go through */
+ only_save:
+ luaZ_buffremove(ls->buff, 1); /* remove '\\' */
+ save(ls, c);
+ /* go through */
+ no_save: break;
+ }
+ default:
+ save_and_next(ls);
+ }
+ }
+ save_and_next(ls); /* skip delimiter */
+ seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1,
+ luaZ_bufflen(ls->buff) - 2);
+}
+
+
+static int llex (LexState *ls, SemInfo *seminfo) {
+ luaZ_resetbuffer(ls->buff);
+ for (;;) {
+ switch (ls->current) {
+ case '\n': case '\r': { /* line breaks */
+ inclinenumber(ls);
+ break;
+ }
+ case ' ': case '\f': case '\t': case '\v': { /* spaces */
+ next(ls);
+ break;
+ }
+ case '-': { /* '-' or '--' (comment) */
+ next(ls);
+ if (ls->current != '-') return '-';
+ /* else is a comment */
+ next(ls);
+ if (ls->current == '[') { /* long comment? */
+ size_t sep = skip_sep(ls);
+ luaZ_resetbuffer(ls->buff); /* 'skip_sep' may dirty the buffer */
+ if (sep >= 2) {
+ read_long_string(ls, NULL, sep); /* skip long comment */
+ luaZ_resetbuffer(ls->buff); /* previous call may dirty the buff. */
+ break;
+ }
+ }
+ /* else short comment */
+ while (!currIsNewline(ls) && ls->current != EOZ)
+ next(ls); /* skip until end of line (or end of file) */
+ break;
+ }
+ case '[': { /* long string or simply '[' */
+ size_t sep = skip_sep(ls);
+ if (sep >= 2) {
+ read_long_string(ls, seminfo, sep);
+ return TK_STRING;
+ }
+ else if (sep == 0) /* '[=...' missing second bracket? */
+ lexerror(ls, "invalid long string delimiter", TK_STRING);
+ return '[';
+ }
+ case '=': {
+ next(ls);
+ if (check_next1(ls, '=')) return TK_EQ; /* '==' */
+ else return '=';
+ }
+ case '<': {
+ next(ls);
+ if (check_next1(ls, '=')) return TK_LE; /* '<=' */
+ else if (check_next1(ls, '<')) return TK_SHL; /* '<<' */
+ else return '<';
+ }
+ case '>': {
+ next(ls);
+ if (check_next1(ls, '=')) return TK_GE; /* '>=' */
+ else if (check_next1(ls, '>')) return TK_SHR; /* '>>' */
+ else return '>';
+ }
+ case '/': {
+ next(ls);
+ if (check_next1(ls, '/')) return TK_IDIV; /* '//' */
+ else return '/';
+ }
+ case '~': {
+ next(ls);
+ if (check_next1(ls, '=')) return TK_NE; /* '~=' */
+ else return '~';
+ }
+ case ':': {
+ next(ls);
+ if (check_next1(ls, ':')) return TK_DBCOLON; /* '::' */
+ else return ':';
+ }
+ case '"': case '\'': { /* short literal strings */
+ read_string(ls, ls->current, seminfo);
+ return TK_STRING;
+ }
+ case '.': { /* '.', '..', '...', or number */
+ save_and_next(ls);
+ if (check_next1(ls, '.')) {
+ if (check_next1(ls, '.'))
+ return TK_DOTS; /* '...' */
+ else return TK_CONCAT; /* '..' */
+ }
+ else if (!lisdigit(ls->current)) return '.';
+ else return read_numeral(ls, seminfo);
+ }
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9': {
+ return read_numeral(ls, seminfo);
+ }
+ case EOZ: {
+ return TK_EOS;
+ }
+ default: {
+ if (lislalpha(ls->current)) { /* identifier or reserved word? */
+ TString *ts;
+ do {
+ save_and_next(ls);
+ } while (lislalnum(ls->current));
+ ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
+ luaZ_bufflen(ls->buff));
+ seminfo->ts = ts;
+ if (isreserved(ts)) /* reserved word? */
+ return ts->extra - 1 + FIRST_RESERVED;
+ else {
+ return TK_NAME;
+ }
+ }
+ else { /* single-char tokens ('+', '*', '%', '{', '}', ...) */
+ int c = ls->current;
+ next(ls);
+ return c;
+ }
+ }
+ }
+ }
+}
+
+
+void luaX_next (LexState *ls) {
+ ls->lastline = ls->linenumber;
+ if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */
+ ls->t = ls->lookahead; /* use this one */
+ ls->lookahead.token = TK_EOS; /* and discharge it */
+ }
+ else
+ ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */
+}
+
+
+int luaX_lookahead (LexState *ls) {
+ lua_assert(ls->lookahead.token == TK_EOS);
+ ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);
+ return ls->lookahead.token;
+}
+
diff --git a/src/libs/3rdparty/lua/src/llex.h b/src/libs/3rdparty/lua/src/llex.h
new file mode 100644
index 0000000000..389d2f8635
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/llex.h
@@ -0,0 +1,91 @@
+/*
+** $Id: llex.h $
+** Lexical Analyzer
+** See Copyright Notice in lua.h
+*/
+
+#ifndef llex_h
+#define llex_h
+
+#include <limits.h>
+
+#include "lobject.h"
+#include "lzio.h"
+
+
+/*
+** Single-char tokens (terminal symbols) are represented by their own
+** numeric code. Other tokens start at the following value.
+*/
+#define FIRST_RESERVED (UCHAR_MAX + 1)
+
+
+#if !defined(LUA_ENV)
+#define LUA_ENV "_ENV"
+#endif
+
+
+/*
+* WARNING: if you change the order of this enumeration,
+* grep "ORDER RESERVED"
+*/
+enum RESERVED {
+ /* terminal symbols denoted by reserved words */
+ TK_AND = FIRST_RESERVED, TK_BREAK,
+ TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
+ TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
+ TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
+ /* other terminal symbols */
+ TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE,
+ TK_SHL, TK_SHR,
+ TK_DBCOLON, TK_EOS,
+ TK_FLT, TK_INT, TK_NAME, TK_STRING
+};
+
+/* number of reserved words */
+#define NUM_RESERVED (cast_int(TK_WHILE-FIRST_RESERVED + 1))
+
+
+typedef union {
+ lua_Number r;
+ lua_Integer i;
+ TString *ts;
+} SemInfo; /* semantics information */
+
+
+typedef struct Token {
+ int token;
+ SemInfo seminfo;
+} Token;
+
+
+/* state of the lexer plus state of the parser when shared by all
+ functions */
+typedef struct LexState {
+ int current; /* current character (charint) */
+ int linenumber; /* input line counter */
+ int lastline; /* line of last token 'consumed' */
+ Token t; /* current token */
+ Token lookahead; /* look ahead token */
+ struct FuncState *fs; /* current function (parser) */
+ struct lua_State *L;
+ ZIO *z; /* input stream */
+ Mbuffer *buff; /* buffer for tokens */
+ Table *h; /* to avoid collection/reuse strings */
+ struct Dyndata *dyd; /* dynamic structures used by the parser */
+ TString *source; /* current source name */
+ TString *envn; /* environment variable name */
+} LexState;
+
+
+LUAI_FUNC void luaX_init (lua_State *L);
+LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z,
+ TString *source, int firstchar);
+LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l);
+LUAI_FUNC void luaX_next (LexState *ls);
+LUAI_FUNC int luaX_lookahead (LexState *ls);
+LUAI_FUNC l_noret luaX_syntaxerror (LexState *ls, const char *s);
+LUAI_FUNC const char *luaX_token2str (LexState *ls, int token);
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/llimits.h b/src/libs/3rdparty/lua/src/llimits.h
new file mode 100644
index 0000000000..1c826f7be2
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/llimits.h
@@ -0,0 +1,380 @@
+/*
+** $Id: llimits.h $
+** Limits, basic types, and some other 'installation-dependent' definitions
+** See Copyright Notice in lua.h
+*/
+
+#ifndef llimits_h
+#define llimits_h
+
+
+#include <limits.h>
+#include <stddef.h>
+
+
+#include "lua.h"
+
+
+/*
+** 'lu_mem' and 'l_mem' are unsigned/signed integers big enough to count
+** the total memory used by Lua (in bytes). Usually, 'size_t' and
+** 'ptrdiff_t' should work, but we use 'long' for 16-bit machines.
+*/
+#if defined(LUAI_MEM) /* { external definitions? */
+typedef LUAI_UMEM lu_mem;
+typedef LUAI_MEM l_mem;
+#elif LUAI_IS32INT /* }{ */
+typedef size_t lu_mem;
+typedef ptrdiff_t l_mem;
+#else /* 16-bit ints */ /* }{ */
+typedef unsigned long lu_mem;
+typedef long l_mem;
+#endif /* } */
+
+
+/* chars used as small naturals (so that 'char' is reserved for characters) */
+typedef unsigned char lu_byte;
+typedef signed char ls_byte;
+
+
+/* maximum value for size_t */
+#define MAX_SIZET ((size_t)(~(size_t)0))
+
+/* maximum size visible for Lua (must be representable in a lua_Integer) */
+#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \
+ : (size_t)(LUA_MAXINTEGER))
+
+
+#define MAX_LUMEM ((lu_mem)(~(lu_mem)0))
+
+#define MAX_LMEM ((l_mem)(MAX_LUMEM >> 1))
+
+
+#define MAX_INT INT_MAX /* maximum value of an int */
+
+
+/*
+** floor of the log2 of the maximum signed value for integral type 't'.
+** (That is, maximum 'n' such that '2^n' fits in the given signed type.)
+*/
+#define log2maxs(t) (sizeof(t) * 8 - 2)
+
+
+/*
+** test whether an unsigned value is a power of 2 (or zero)
+*/
+#define ispow2(x) (((x) & ((x) - 1)) == 0)
+
+
+/* number of chars of a literal string without the ending \0 */
+#define LL(x) (sizeof(x)/sizeof(char) - 1)
+
+
+/*
+** conversion of pointer to unsigned integer: this is for hashing only;
+** there is no problem if the integer cannot hold the whole pointer
+** value. (In strict ISO C this may cause undefined behavior, but no
+** actual machine seems to bother.)
+*/
+#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \
+ __STDC_VERSION__ >= 199901L
+#include <stdint.h>
+#if defined(UINTPTR_MAX) /* even in C99 this type is optional */
+#define L_P2I uintptr_t
+#else /* no 'intptr'? */
+#define L_P2I uintmax_t /* use the largest available integer */
+#endif
+#else /* C89 option */
+#define L_P2I size_t
+#endif
+
+#define point2uint(p) ((unsigned int)((L_P2I)(p) & UINT_MAX))
+
+
+
+/* types of 'usual argument conversions' for lua_Number and lua_Integer */
+typedef LUAI_UACNUMBER l_uacNumber;
+typedef LUAI_UACINT l_uacInt;
+
+
+/*
+** Internal assertions for in-house debugging
+*/
+#if defined LUAI_ASSERT
+#undef NDEBUG
+#include <assert.h>
+#define lua_assert(c) assert(c)
+#endif
+
+#if defined(lua_assert)
+#define check_exp(c,e) (lua_assert(c), (e))
+/* to avoid problems with conditions too long */
+#define lua_longassert(c) ((c) ? (void)0 : lua_assert(0))
+#else
+#define lua_assert(c) ((void)0)
+#define check_exp(c,e) (e)
+#define lua_longassert(c) ((void)0)
+#endif
+
+/*
+** assertion for checking API calls
+*/
+#if !defined(luai_apicheck)
+#define luai_apicheck(l,e) ((void)l, lua_assert(e))
+#endif
+
+#define api_check(l,e,msg) luai_apicheck(l,(e) && msg)
+
+
+/* macro to avoid warnings about unused variables */
+#if !defined(UNUSED)
+#define UNUSED(x) ((void)(x))
+#endif
+
+
+/* type casts (a macro highlights casts in the code) */
+#define cast(t, exp) ((t)(exp))
+
+#define cast_void(i) cast(void, (i))
+#define cast_voidp(i) cast(void *, (i))
+#define cast_num(i) cast(lua_Number, (i))
+#define cast_int(i) cast(int, (i))
+#define cast_uint(i) cast(unsigned int, (i))
+#define cast_byte(i) cast(lu_byte, (i))
+#define cast_uchar(i) cast(unsigned char, (i))
+#define cast_char(i) cast(char, (i))
+#define cast_charp(i) cast(char *, (i))
+#define cast_sizet(i) cast(size_t, (i))
+
+
+/* cast a signed lua_Integer to lua_Unsigned */
+#if !defined(l_castS2U)
+#define l_castS2U(i) ((lua_Unsigned)(i))
+#endif
+
+/*
+** cast a lua_Unsigned to a signed lua_Integer; this cast is
+** not strict ISO C, but two-complement architectures should
+** work fine.
+*/
+#if !defined(l_castU2S)
+#define l_castU2S(i) ((lua_Integer)(i))
+#endif
+
+
+/*
+** non-return type
+*/
+#if !defined(l_noret)
+
+#if defined(__GNUC__)
+#define l_noret void __attribute__((noreturn))
+#elif defined(_MSC_VER) && _MSC_VER >= 1200
+#define l_noret void __declspec(noreturn)
+#else
+#define l_noret void
+#endif
+
+#endif
+
+
+/*
+** Inline functions
+*/
+#if !defined(LUA_USE_C89)
+#define l_inline inline
+#elif defined(__GNUC__)
+#define l_inline __inline__
+#else
+#define l_inline /* empty */
+#endif
+
+#define l_sinline static l_inline
+
+
+/*
+** type for virtual-machine instructions;
+** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
+*/
+#if LUAI_IS32INT
+typedef unsigned int l_uint32;
+#else
+typedef unsigned long l_uint32;
+#endif
+
+typedef l_uint32 Instruction;
+
+
+
+/*
+** Maximum length for short strings, that is, strings that are
+** internalized. (Cannot be smaller than reserved words or tags for
+** metamethods, as these strings must be internalized;
+** #("function") = 8, #("__newindex") = 10.)
+*/
+#if !defined(LUAI_MAXSHORTLEN)
+#define LUAI_MAXSHORTLEN 40
+#endif
+
+
+/*
+** Initial size for the string table (must be power of 2).
+** The Lua core alone registers ~50 strings (reserved words +
+** metaevent keys + a few others). Libraries would typically add
+** a few dozens more.
+*/
+#if !defined(MINSTRTABSIZE)
+#define MINSTRTABSIZE 128
+#endif
+
+
+/*
+** Size of cache for strings in the API. 'N' is the number of
+** sets (better be a prime) and "M" is the size of each set (M == 1
+** makes a direct cache.)
+*/
+#if !defined(STRCACHE_N)
+#define STRCACHE_N 53
+#define STRCACHE_M 2
+#endif
+
+
+/* minimum size for string buffer */
+#if !defined(LUA_MINBUFFER)
+#define LUA_MINBUFFER 32
+#endif
+
+
+/*
+** Maximum depth for nested C calls, syntactical nested non-terminals,
+** and other features implemented through recursion in C. (Value must
+** fit in a 16-bit unsigned integer. It must also be compatible with
+** the size of the C stack.)
+*/
+#if !defined(LUAI_MAXCCALLS)
+#define LUAI_MAXCCALLS 200
+#endif
+
+
+/*
+** macros that are executed whenever program enters the Lua core
+** ('lua_lock') and leaves the core ('lua_unlock')
+*/
+#if !defined(lua_lock)
+#define lua_lock(L) ((void) 0)
+#define lua_unlock(L) ((void) 0)
+#endif
+
+/*
+** macro executed during Lua functions at points where the
+** function can yield.
+*/
+#if !defined(luai_threadyield)
+#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);}
+#endif
+
+
+/*
+** these macros allow user-specific actions when a thread is
+** created/deleted/resumed/yielded.
+*/
+#if !defined(luai_userstateopen)
+#define luai_userstateopen(L) ((void)L)
+#endif
+
+#if !defined(luai_userstateclose)
+#define luai_userstateclose(L) ((void)L)
+#endif
+
+#if !defined(luai_userstatethread)
+#define luai_userstatethread(L,L1) ((void)L)
+#endif
+
+#if !defined(luai_userstatefree)
+#define luai_userstatefree(L,L1) ((void)L)
+#endif
+
+#if !defined(luai_userstateresume)
+#define luai_userstateresume(L,n) ((void)L)
+#endif
+
+#if !defined(luai_userstateyield)
+#define luai_userstateyield(L,n) ((void)L)
+#endif
+
+
+
+/*
+** The luai_num* macros define the primitive operations over numbers.
+*/
+
+/* floor division (defined as 'floor(a/b)') */
+#if !defined(luai_numidiv)
+#define luai_numidiv(L,a,b) ((void)L, l_floor(luai_numdiv(L,a,b)))
+#endif
+
+/* float division */
+#if !defined(luai_numdiv)
+#define luai_numdiv(L,a,b) ((a)/(b))
+#endif
+
+/*
+** modulo: defined as 'a - floor(a/b)*b'; the direct computation
+** using this definition has several problems with rounding errors,
+** so it is better to use 'fmod'. 'fmod' gives the result of
+** 'a - trunc(a/b)*b', and therefore must be corrected when
+** 'trunc(a/b) ~= floor(a/b)'. That happens when the division has a
+** non-integer negative result: non-integer result is equivalent to
+** a non-zero remainder 'm'; negative result is equivalent to 'a' and
+** 'b' with different signs, or 'm' and 'b' with different signs
+** (as the result 'm' of 'fmod' has the same sign of 'a').
+*/
+#if !defined(luai_nummod)
+#define luai_nummod(L,a,b,m) \
+ { (void)L; (m) = l_mathop(fmod)(a,b); \
+ if (((m) > 0) ? (b) < 0 : ((m) < 0 && (b) > 0)) (m) += (b); }
+#endif
+
+/* exponentiation */
+#if !defined(luai_numpow)
+#define luai_numpow(L,a,b) \
+ ((void)L, (b == 2) ? (a)*(a) : l_mathop(pow)(a,b))
+#endif
+
+/* the others are quite standard operations */
+#if !defined(luai_numadd)
+#define luai_numadd(L,a,b) ((a)+(b))
+#define luai_numsub(L,a,b) ((a)-(b))
+#define luai_nummul(L,a,b) ((a)*(b))
+#define luai_numunm(L,a) (-(a))
+#define luai_numeq(a,b) ((a)==(b))
+#define luai_numlt(a,b) ((a)<(b))
+#define luai_numle(a,b) ((a)<=(b))
+#define luai_numgt(a,b) ((a)>(b))
+#define luai_numge(a,b) ((a)>=(b))
+#define luai_numisnan(a) (!luai_numeq((a), (a)))
+#endif
+
+
+
+
+
+/*
+** macro to control inclusion of some hard tests on stack reallocation
+*/
+#if !defined(HARDSTACKTESTS)
+#define condmovestack(L,pre,pos) ((void)0)
+#else
+/* realloc stack keeping its size */
+#define condmovestack(L,pre,pos) \
+ { int sz_ = stacksize(L); pre; luaD_reallocstack((L), sz_, 0); pos; }
+#endif
+
+#if !defined(HARDMEMTESTS)
+#define condchangemem(L,pre,pos) ((void)0)
+#else
+#define condchangemem(L,pre,pos) \
+ { if (gcrunning(G(L))) { pre; luaC_fullgc(L, 0); pos; } }
+#endif
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lmathlib.c b/src/libs/3rdparty/lua/src/lmathlib.c
new file mode 100644
index 0000000000..d0b1e1e5d6
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lmathlib.c
@@ -0,0 +1,764 @@
+/*
+** $Id: lmathlib.c $
+** Standard mathematical library
+** See Copyright Notice in lua.h
+*/
+
+#define lmathlib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+#undef PI
+#define PI (l_mathop(3.141592653589793238462643383279502884))
+
+
+static int math_abs (lua_State *L) {
+ if (lua_isinteger(L, 1)) {
+ lua_Integer n = lua_tointeger(L, 1);
+ if (n < 0) n = (lua_Integer)(0u - (lua_Unsigned)n);
+ lua_pushinteger(L, n);
+ }
+ else
+ lua_pushnumber(L, l_mathop(fabs)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_sin (lua_State *L) {
+ lua_pushnumber(L, l_mathop(sin)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_cos (lua_State *L) {
+ lua_pushnumber(L, l_mathop(cos)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_tan (lua_State *L) {
+ lua_pushnumber(L, l_mathop(tan)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_asin (lua_State *L) {
+ lua_pushnumber(L, l_mathop(asin)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_acos (lua_State *L) {
+ lua_pushnumber(L, l_mathop(acos)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_atan (lua_State *L) {
+ lua_Number y = luaL_checknumber(L, 1);
+ lua_Number x = luaL_optnumber(L, 2, 1);
+ lua_pushnumber(L, l_mathop(atan2)(y, x));
+ return 1;
+}
+
+
+static int math_toint (lua_State *L) {
+ int valid;
+ lua_Integer n = lua_tointegerx(L, 1, &valid);
+ if (l_likely(valid))
+ lua_pushinteger(L, n);
+ else {
+ luaL_checkany(L, 1);
+ luaL_pushfail(L); /* value is not convertible to integer */
+ }
+ return 1;
+}
+
+
+static void pushnumint (lua_State *L, lua_Number d) {
+ lua_Integer n;
+ if (lua_numbertointeger(d, &n)) /* does 'd' fit in an integer? */
+ lua_pushinteger(L, n); /* result is integer */
+ else
+ lua_pushnumber(L, d); /* result is float */
+}
+
+
+static int math_floor (lua_State *L) {
+ if (lua_isinteger(L, 1))
+ lua_settop(L, 1); /* integer is its own floor */
+ else {
+ lua_Number d = l_mathop(floor)(luaL_checknumber(L, 1));
+ pushnumint(L, d);
+ }
+ return 1;
+}
+
+
+static int math_ceil (lua_State *L) {
+ if (lua_isinteger(L, 1))
+ lua_settop(L, 1); /* integer is its own ceil */
+ else {
+ lua_Number d = l_mathop(ceil)(luaL_checknumber(L, 1));
+ pushnumint(L, d);
+ }
+ return 1;
+}
+
+
+static int math_fmod (lua_State *L) {
+ if (lua_isinteger(L, 1) && lua_isinteger(L, 2)) {
+ lua_Integer d = lua_tointeger(L, 2);
+ if ((lua_Unsigned)d + 1u <= 1u) { /* special cases: -1 or 0 */
+ luaL_argcheck(L, d != 0, 2, "zero");
+ lua_pushinteger(L, 0); /* avoid overflow with 0x80000... / -1 */
+ }
+ else
+ lua_pushinteger(L, lua_tointeger(L, 1) % d);
+ }
+ else
+ lua_pushnumber(L, l_mathop(fmod)(luaL_checknumber(L, 1),
+ luaL_checknumber(L, 2)));
+ return 1;
+}
+
+
+/*
+** next function does not use 'modf', avoiding problems with 'double*'
+** (which is not compatible with 'float*') when lua_Number is not
+** 'double'.
+*/
+static int math_modf (lua_State *L) {
+ if (lua_isinteger(L ,1)) {
+ lua_settop(L, 1); /* number is its own integer part */
+ lua_pushnumber(L, 0); /* no fractional part */
+ }
+ else {
+ lua_Number n = luaL_checknumber(L, 1);
+ /* integer part (rounds toward zero) */
+ lua_Number ip = (n < 0) ? l_mathop(ceil)(n) : l_mathop(floor)(n);
+ pushnumint(L, ip);
+ /* fractional part (test needed for inf/-inf) */
+ lua_pushnumber(L, (n == ip) ? l_mathop(0.0) : (n - ip));
+ }
+ return 2;
+}
+
+
+static int math_sqrt (lua_State *L) {
+ lua_pushnumber(L, l_mathop(sqrt)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+
+static int math_ult (lua_State *L) {
+ lua_Integer a = luaL_checkinteger(L, 1);
+ lua_Integer b = luaL_checkinteger(L, 2);
+ lua_pushboolean(L, (lua_Unsigned)a < (lua_Unsigned)b);
+ return 1;
+}
+
+static int math_log (lua_State *L) {
+ lua_Number x = luaL_checknumber(L, 1);
+ lua_Number res;
+ if (lua_isnoneornil(L, 2))
+ res = l_mathop(log)(x);
+ else {
+ lua_Number base = luaL_checknumber(L, 2);
+#if !defined(LUA_USE_C89)
+ if (base == l_mathop(2.0))
+ res = l_mathop(log2)(x);
+ else
+#endif
+ if (base == l_mathop(10.0))
+ res = l_mathop(log10)(x);
+ else
+ res = l_mathop(log)(x)/l_mathop(log)(base);
+ }
+ lua_pushnumber(L, res);
+ return 1;
+}
+
+static int math_exp (lua_State *L) {
+ lua_pushnumber(L, l_mathop(exp)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_deg (lua_State *L) {
+ lua_pushnumber(L, luaL_checknumber(L, 1) * (l_mathop(180.0) / PI));
+ return 1;
+}
+
+static int math_rad (lua_State *L) {
+ lua_pushnumber(L, luaL_checknumber(L, 1) * (PI / l_mathop(180.0)));
+ return 1;
+}
+
+
+static int math_min (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int imin = 1; /* index of current minimum value */
+ int i;
+ luaL_argcheck(L, n >= 1, 1, "value expected");
+ for (i = 2; i <= n; i++) {
+ if (lua_compare(L, i, imin, LUA_OPLT))
+ imin = i;
+ }
+ lua_pushvalue(L, imin);
+ return 1;
+}
+
+
+static int math_max (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int imax = 1; /* index of current maximum value */
+ int i;
+ luaL_argcheck(L, n >= 1, 1, "value expected");
+ for (i = 2; i <= n; i++) {
+ if (lua_compare(L, imax, i, LUA_OPLT))
+ imax = i;
+ }
+ lua_pushvalue(L, imax);
+ return 1;
+}
+
+
+static int math_type (lua_State *L) {
+ if (lua_type(L, 1) == LUA_TNUMBER)
+ lua_pushstring(L, (lua_isinteger(L, 1)) ? "integer" : "float");
+ else {
+ luaL_checkany(L, 1);
+ luaL_pushfail(L);
+ }
+ return 1;
+}
+
+
+
+/*
+** {==================================================================
+** Pseudo-Random Number Generator based on 'xoshiro256**'.
+** ===================================================================
+*/
+
+/* number of binary digits in the mantissa of a float */
+#define FIGS l_floatatt(MANT_DIG)
+
+#if FIGS > 64
+/* there are only 64 random bits; use them all */
+#undef FIGS
+#define FIGS 64
+#endif
+
+
+/*
+** LUA_RAND32 forces the use of 32-bit integers in the implementation
+** of the PRN generator (mainly for testing).
+*/
+#if !defined(LUA_RAND32) && !defined(Rand64)
+
+/* try to find an integer type with at least 64 bits */
+
+#if ((ULONG_MAX >> 31) >> 31) >= 3
+
+/* 'long' has at least 64 bits */
+#define Rand64 unsigned long
+
+#elif !defined(LUA_USE_C89) && defined(LLONG_MAX)
+
+/* there is a 'long long' type (which must have at least 64 bits) */
+#define Rand64 unsigned long long
+
+#elif ((LUA_MAXUNSIGNED >> 31) >> 31) >= 3
+
+/* 'lua_Unsigned' has at least 64 bits */
+#define Rand64 lua_Unsigned
+
+#endif
+
+#endif
+
+
+#if defined(Rand64) /* { */
+
+/*
+** Standard implementation, using 64-bit integers.
+** If 'Rand64' has more than 64 bits, the extra bits do not interfere
+** with the 64 initial bits, except in a right shift. Moreover, the
+** final result has to discard the extra bits.
+*/
+
+/* avoid using extra bits when needed */
+#define trim64(x) ((x) & 0xffffffffffffffffu)
+
+
+/* rotate left 'x' by 'n' bits */
+static Rand64 rotl (Rand64 x, int n) {
+ return (x << n) | (trim64(x) >> (64 - n));
+}
+
+static Rand64 nextrand (Rand64 *state) {
+ Rand64 state0 = state[0];
+ Rand64 state1 = state[1];
+ Rand64 state2 = state[2] ^ state0;
+ Rand64 state3 = state[3] ^ state1;
+ Rand64 res = rotl(state1 * 5, 7) * 9;
+ state[0] = state0 ^ state3;
+ state[1] = state1 ^ state2;
+ state[2] = state2 ^ (state1 << 17);
+ state[3] = rotl(state3, 45);
+ return res;
+}
+
+
+/* must take care to not shift stuff by more than 63 slots */
+
+
+/*
+** Convert bits from a random integer into a float in the
+** interval [0,1), getting the higher FIG bits from the
+** random unsigned integer and converting that to a float.
+*/
+
+/* must throw out the extra (64 - FIGS) bits */
+#define shift64_FIG (64 - FIGS)
+
+/* to scale to [0, 1), multiply by scaleFIG = 2^(-FIGS) */
+#define scaleFIG (l_mathop(0.5) / ((Rand64)1 << (FIGS - 1)))
+
+static lua_Number I2d (Rand64 x) {
+ return (lua_Number)(trim64(x) >> shift64_FIG) * scaleFIG;
+}
+
+/* convert a 'Rand64' to a 'lua_Unsigned' */
+#define I2UInt(x) ((lua_Unsigned)trim64(x))
+
+/* convert a 'lua_Unsigned' to a 'Rand64' */
+#define Int2I(x) ((Rand64)(x))
+
+
+#else /* no 'Rand64' }{ */
+
+/* get an integer with at least 32 bits */
+#if LUAI_IS32INT
+typedef unsigned int lu_int32;
+#else
+typedef unsigned long lu_int32;
+#endif
+
+
+/*
+** Use two 32-bit integers to represent a 64-bit quantity.
+*/
+typedef struct Rand64 {
+ lu_int32 h; /* higher half */
+ lu_int32 l; /* lower half */
+} Rand64;
+
+
+/*
+** If 'lu_int32' has more than 32 bits, the extra bits do not interfere
+** with the 32 initial bits, except in a right shift and comparisons.
+** Moreover, the final result has to discard the extra bits.
+*/
+
+/* avoid using extra bits when needed */
+#define trim32(x) ((x) & 0xffffffffu)
+
+
+/*
+** basic operations on 'Rand64' values
+*/
+
+/* build a new Rand64 value */
+static Rand64 packI (lu_int32 h, lu_int32 l) {
+ Rand64 result;
+ result.h = h;
+ result.l = l;
+ return result;
+}
+
+/* return i << n */
+static Rand64 Ishl (Rand64 i, int n) {
+ lua_assert(n > 0 && n < 32);
+ return packI((i.h << n) | (trim32(i.l) >> (32 - n)), i.l << n);
+}
+
+/* i1 ^= i2 */
+static void Ixor (Rand64 *i1, Rand64 i2) {
+ i1->h ^= i2.h;
+ i1->l ^= i2.l;
+}
+
+/* return i1 + i2 */
+static Rand64 Iadd (Rand64 i1, Rand64 i2) {
+ Rand64 result = packI(i1.h + i2.h, i1.l + i2.l);
+ if (trim32(result.l) < trim32(i1.l)) /* carry? */
+ result.h++;
+ return result;
+}
+
+/* return i * 5 */
+static Rand64 times5 (Rand64 i) {
+ return Iadd(Ishl(i, 2), i); /* i * 5 == (i << 2) + i */
+}
+
+/* return i * 9 */
+static Rand64 times9 (Rand64 i) {
+ return Iadd(Ishl(i, 3), i); /* i * 9 == (i << 3) + i */
+}
+
+/* return 'i' rotated left 'n' bits */
+static Rand64 rotl (Rand64 i, int n) {
+ lua_assert(n > 0 && n < 32);
+ return packI((i.h << n) | (trim32(i.l) >> (32 - n)),
+ (trim32(i.h) >> (32 - n)) | (i.l << n));
+}
+
+/* for offsets larger than 32, rotate right by 64 - offset */
+static Rand64 rotl1 (Rand64 i, int n) {
+ lua_assert(n > 32 && n < 64);
+ n = 64 - n;
+ return packI((trim32(i.h) >> n) | (i.l << (32 - n)),
+ (i.h << (32 - n)) | (trim32(i.l) >> n));
+}
+
+/*
+** implementation of 'xoshiro256**' algorithm on 'Rand64' values
+*/
+static Rand64 nextrand (Rand64 *state) {
+ Rand64 res = times9(rotl(times5(state[1]), 7));
+ Rand64 t = Ishl(state[1], 17);
+ Ixor(&state[2], state[0]);
+ Ixor(&state[3], state[1]);
+ Ixor(&state[1], state[2]);
+ Ixor(&state[0], state[3]);
+ Ixor(&state[2], t);
+ state[3] = rotl1(state[3], 45);
+ return res;
+}
+
+
+/*
+** Converts a 'Rand64' into a float.
+*/
+
+/* an unsigned 1 with proper type */
+#define UONE ((lu_int32)1)
+
+
+#if FIGS <= 32
+
+/* 2^(-FIGS) */
+#define scaleFIG (l_mathop(0.5) / (UONE << (FIGS - 1)))
+
+/*
+** get up to 32 bits from higher half, shifting right to
+** throw out the extra bits.
+*/
+static lua_Number I2d (Rand64 x) {
+ lua_Number h = (lua_Number)(trim32(x.h) >> (32 - FIGS));
+ return h * scaleFIG;
+}
+
+#else /* 32 < FIGS <= 64 */
+
+/* must take care to not shift stuff by more than 31 slots */
+
+/* 2^(-FIGS) = 1.0 / 2^30 / 2^3 / 2^(FIGS-33) */
+#define scaleFIG \
+ (l_mathop(1.0) / (UONE << 30) / l_mathop(8.0) / (UONE << (FIGS - 33)))
+
+/*
+** use FIGS - 32 bits from lower half, throwing out the other
+** (32 - (FIGS - 32)) = (64 - FIGS) bits
+*/
+#define shiftLOW (64 - FIGS)
+
+/*
+** higher 32 bits go after those (FIGS - 32) bits: shiftHI = 2^(FIGS - 32)
+*/
+#define shiftHI ((lua_Number)(UONE << (FIGS - 33)) * l_mathop(2.0))
+
+
+static lua_Number I2d (Rand64 x) {
+ lua_Number h = (lua_Number)trim32(x.h) * shiftHI;
+ lua_Number l = (lua_Number)(trim32(x.l) >> shiftLOW);
+ return (h + l) * scaleFIG;
+}
+
+#endif
+
+
+/* convert a 'Rand64' to a 'lua_Unsigned' */
+static lua_Unsigned I2UInt (Rand64 x) {
+ return (((lua_Unsigned)trim32(x.h) << 31) << 1) | (lua_Unsigned)trim32(x.l);
+}
+
+/* convert a 'lua_Unsigned' to a 'Rand64' */
+static Rand64 Int2I (lua_Unsigned n) {
+ return packI((lu_int32)((n >> 31) >> 1), (lu_int32)n);
+}
+
+#endif /* } */
+
+
+/*
+** A state uses four 'Rand64' values.
+*/
+typedef struct {
+ Rand64 s[4];
+} RanState;
+
+
+/*
+** Project the random integer 'ran' into the interval [0, n].
+** Because 'ran' has 2^B possible values, the projection can only be
+** uniform when the size of the interval is a power of 2 (exact
+** division). Otherwise, to get a uniform projection into [0, n], we
+** first compute 'lim', the smallest Mersenne number not smaller than
+** 'n'. We then project 'ran' into the interval [0, lim]. If the result
+** is inside [0, n], we are done. Otherwise, we try with another 'ran',
+** until we have a result inside the interval.
+*/
+static lua_Unsigned project (lua_Unsigned ran, lua_Unsigned n,
+ RanState *state) {
+ if ((n & (n + 1)) == 0) /* is 'n + 1' a power of 2? */
+ return ran & n; /* no bias */
+ else {
+ lua_Unsigned lim = n;
+ /* compute the smallest (2^b - 1) not smaller than 'n' */
+ lim |= (lim >> 1);
+ lim |= (lim >> 2);
+ lim |= (lim >> 4);
+ lim |= (lim >> 8);
+ lim |= (lim >> 16);
+#if (LUA_MAXUNSIGNED >> 31) >= 3
+ lim |= (lim >> 32); /* integer type has more than 32 bits */
+#endif
+ lua_assert((lim & (lim + 1)) == 0 /* 'lim + 1' is a power of 2, */
+ && lim >= n /* not smaller than 'n', */
+ && (lim >> 1) < n); /* and it is the smallest one */
+ while ((ran &= lim) > n) /* project 'ran' into [0..lim] */
+ ran = I2UInt(nextrand(state->s)); /* not inside [0..n]? try again */
+ return ran;
+ }
+}
+
+
+static int math_random (lua_State *L) {
+ lua_Integer low, up;
+ lua_Unsigned p;
+ RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1));
+ Rand64 rv = nextrand(state->s); /* next pseudo-random value */
+ switch (lua_gettop(L)) { /* check number of arguments */
+ case 0: { /* no arguments */
+ lua_pushnumber(L, I2d(rv)); /* float between 0 and 1 */
+ return 1;
+ }
+ case 1: { /* only upper limit */
+ low = 1;
+ up = luaL_checkinteger(L, 1);
+ if (up == 0) { /* single 0 as argument? */
+ lua_pushinteger(L, I2UInt(rv)); /* full random integer */
+ return 1;
+ }
+ break;
+ }
+ case 2: { /* lower and upper limits */
+ low = luaL_checkinteger(L, 1);
+ up = luaL_checkinteger(L, 2);
+ break;
+ }
+ default: return luaL_error(L, "wrong number of arguments");
+ }
+ /* random integer in the interval [low, up] */
+ luaL_argcheck(L, low <= up, 1, "interval is empty");
+ /* project random integer into the interval [0, up - low] */
+ p = project(I2UInt(rv), (lua_Unsigned)up - (lua_Unsigned)low, state);
+ lua_pushinteger(L, p + (lua_Unsigned)low);
+ return 1;
+}
+
+
+static void setseed (lua_State *L, Rand64 *state,
+ lua_Unsigned n1, lua_Unsigned n2) {
+ int i;
+ state[0] = Int2I(n1);
+ state[1] = Int2I(0xff); /* avoid a zero state */
+ state[2] = Int2I(n2);
+ state[3] = Int2I(0);
+ for (i = 0; i < 16; i++)
+ nextrand(state); /* discard initial values to "spread" seed */
+ lua_pushinteger(L, n1);
+ lua_pushinteger(L, n2);
+}
+
+
+/*
+** Set a "random" seed. To get some randomness, use the current time
+** and the address of 'L' (in case the machine does address space layout
+** randomization).
+*/
+static void randseed (lua_State *L, RanState *state) {
+ lua_Unsigned seed1 = (lua_Unsigned)time(NULL);
+ lua_Unsigned seed2 = (lua_Unsigned)(size_t)L;
+ setseed(L, state->s, seed1, seed2);
+}
+
+
+static int math_randomseed (lua_State *L) {
+ RanState *state = (RanState *)lua_touserdata(L, lua_upvalueindex(1));
+ if (lua_isnone(L, 1)) {
+ randseed(L, state);
+ }
+ else {
+ lua_Integer n1 = luaL_checkinteger(L, 1);
+ lua_Integer n2 = luaL_optinteger(L, 2, 0);
+ setseed(L, state->s, n1, n2);
+ }
+ return 2; /* return seeds */
+}
+
+
+static const luaL_Reg randfuncs[] = {
+ {"random", math_random},
+ {"randomseed", math_randomseed},
+ {NULL, NULL}
+};
+
+
+/*
+** Register the random functions and initialize their state.
+*/
+static void setrandfunc (lua_State *L) {
+ RanState *state = (RanState *)lua_newuserdatauv(L, sizeof(RanState), 0);
+ randseed(L, state); /* initialize with a "random" seed */
+ lua_pop(L, 2); /* remove pushed seeds */
+ luaL_setfuncs(L, randfuncs, 1);
+}
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Deprecated functions (for compatibility only)
+** ===================================================================
+*/
+#if defined(LUA_COMPAT_MATHLIB)
+
+static int math_cosh (lua_State *L) {
+ lua_pushnumber(L, l_mathop(cosh)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_sinh (lua_State *L) {
+ lua_pushnumber(L, l_mathop(sinh)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_tanh (lua_State *L) {
+ lua_pushnumber(L, l_mathop(tanh)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+static int math_pow (lua_State *L) {
+ lua_Number x = luaL_checknumber(L, 1);
+ lua_Number y = luaL_checknumber(L, 2);
+ lua_pushnumber(L, l_mathop(pow)(x, y));
+ return 1;
+}
+
+static int math_frexp (lua_State *L) {
+ int e;
+ lua_pushnumber(L, l_mathop(frexp)(luaL_checknumber(L, 1), &e));
+ lua_pushinteger(L, e);
+ return 2;
+}
+
+static int math_ldexp (lua_State *L) {
+ lua_Number x = luaL_checknumber(L, 1);
+ int ep = (int)luaL_checkinteger(L, 2);
+ lua_pushnumber(L, l_mathop(ldexp)(x, ep));
+ return 1;
+}
+
+static int math_log10 (lua_State *L) {
+ lua_pushnumber(L, l_mathop(log10)(luaL_checknumber(L, 1)));
+ return 1;
+}
+
+#endif
+/* }================================================================== */
+
+
+
+static const luaL_Reg mathlib[] = {
+ {"abs", math_abs},
+ {"acos", math_acos},
+ {"asin", math_asin},
+ {"atan", math_atan},
+ {"ceil", math_ceil},
+ {"cos", math_cos},
+ {"deg", math_deg},
+ {"exp", math_exp},
+ {"tointeger", math_toint},
+ {"floor", math_floor},
+ {"fmod", math_fmod},
+ {"ult", math_ult},
+ {"log", math_log},
+ {"max", math_max},
+ {"min", math_min},
+ {"modf", math_modf},
+ {"rad", math_rad},
+ {"sin", math_sin},
+ {"sqrt", math_sqrt},
+ {"tan", math_tan},
+ {"type", math_type},
+#if defined(LUA_COMPAT_MATHLIB)
+ {"atan2", math_atan},
+ {"cosh", math_cosh},
+ {"sinh", math_sinh},
+ {"tanh", math_tanh},
+ {"pow", math_pow},
+ {"frexp", math_frexp},
+ {"ldexp", math_ldexp},
+ {"log10", math_log10},
+#endif
+ /* placeholders */
+ {"random", NULL},
+ {"randomseed", NULL},
+ {"pi", NULL},
+ {"huge", NULL},
+ {"maxinteger", NULL},
+ {"mininteger", NULL},
+ {NULL, NULL}
+};
+
+
+/*
+** Open math library
+*/
+LUAMOD_API int luaopen_math (lua_State *L) {
+ luaL_newlib(L, mathlib);
+ lua_pushnumber(L, PI);
+ lua_setfield(L, -2, "pi");
+ lua_pushnumber(L, (lua_Number)HUGE_VAL);
+ lua_setfield(L, -2, "huge");
+ lua_pushinteger(L, LUA_MAXINTEGER);
+ lua_setfield(L, -2, "maxinteger");
+ lua_pushinteger(L, LUA_MININTEGER);
+ lua_setfield(L, -2, "mininteger");
+ setrandfunc(L);
+ return 1;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lmem.c b/src/libs/3rdparty/lua/src/lmem.c
new file mode 100644
index 0000000000..9800a86fc0
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lmem.c
@@ -0,0 +1,215 @@
+/*
+** $Id: lmem.c $
+** Interface to Memory Manager
+** See Copyright Notice in lua.h
+*/
+
+#define lmem_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <stddef.h>
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+
+/*
+** About the realloc function:
+** void *frealloc (void *ud, void *ptr, size_t osize, size_t nsize);
+** ('osize' is the old size, 'nsize' is the new size)
+**
+** - frealloc(ud, p, x, 0) frees the block 'p' and returns NULL.
+** Particularly, frealloc(ud, NULL, 0, 0) does nothing,
+** which is equivalent to free(NULL) in ISO C.
+**
+** - frealloc(ud, NULL, x, s) creates a new block of size 's'
+** (no matter 'x'). Returns NULL if it cannot create the new block.
+**
+** - otherwise, frealloc(ud, b, x, y) reallocates the block 'b' from
+** size 'x' to size 'y'. Returns NULL if it cannot reallocate the
+** block to the new size.
+*/
+
+
+/*
+** Macro to call the allocation function.
+*/
+#define callfrealloc(g,block,os,ns) ((*g->frealloc)(g->ud, block, os, ns))
+
+
+/*
+** When an allocation fails, it will try again after an emergency
+** collection, except when it cannot run a collection. The GC should
+** not be called while the state is not fully built, as the collector
+** is not yet fully initialized. Also, it should not be called when
+** 'gcstopem' is true, because then the interpreter is in the middle of
+** a collection step.
+*/
+#define cantryagain(g) (completestate(g) && !g->gcstopem)
+
+
+
+
+#if defined(EMERGENCYGCTESTS)
+/*
+** First allocation will fail except when freeing a block (frees never
+** fail) and when it cannot try again; this fail will trigger 'tryagain'
+** and a full GC cycle at every allocation.
+*/
+static void *firsttry (global_State *g, void *block, size_t os, size_t ns) {
+ if (ns > 0 && cantryagain(g))
+ return NULL; /* fail */
+ else /* normal allocation */
+ return callfrealloc(g, block, os, ns);
+}
+#else
+#define firsttry(g,block,os,ns) callfrealloc(g, block, os, ns)
+#endif
+
+
+
+
+
+/*
+** {==================================================================
+** Functions to allocate/deallocate arrays for the Parser
+** ===================================================================
+*/
+
+/*
+** Minimum size for arrays during parsing, to avoid overhead of
+** reallocating to size 1, then 2, and then 4. All these arrays
+** will be reallocated to exact sizes or erased when parsing ends.
+*/
+#define MINSIZEARRAY 4
+
+
+void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize,
+ int size_elems, int limit, const char *what) {
+ void *newblock;
+ int size = *psize;
+ if (nelems + 1 <= size) /* does one extra element still fit? */
+ return block; /* nothing to be done */
+ if (size >= limit / 2) { /* cannot double it? */
+ if (l_unlikely(size >= limit)) /* cannot grow even a little? */
+ luaG_runerror(L, "too many %s (limit is %d)", what, limit);
+ size = limit; /* still have at least one free place */
+ }
+ else {
+ size *= 2;
+ if (size < MINSIZEARRAY)
+ size = MINSIZEARRAY; /* minimum size */
+ }
+ lua_assert(nelems + 1 <= size && size <= limit);
+ /* 'limit' ensures that multiplication will not overflow */
+ newblock = luaM_saferealloc_(L, block, cast_sizet(*psize) * size_elems,
+ cast_sizet(size) * size_elems);
+ *psize = size; /* update only when everything else is OK */
+ return newblock;
+}
+
+
+/*
+** In prototypes, the size of the array is also its number of
+** elements (to save memory). So, if it cannot shrink an array
+** to its number of elements, the only option is to raise an
+** error.
+*/
+void *luaM_shrinkvector_ (lua_State *L, void *block, int *size,
+ int final_n, int size_elem) {
+ void *newblock;
+ size_t oldsize = cast_sizet((*size) * size_elem);
+ size_t newsize = cast_sizet(final_n * size_elem);
+ lua_assert(newsize <= oldsize);
+ newblock = luaM_saferealloc_(L, block, oldsize, newsize);
+ *size = final_n;
+ return newblock;
+}
+
+/* }================================================================== */
+
+
+l_noret luaM_toobig (lua_State *L) {
+ luaG_runerror(L, "memory allocation error: block too big");
+}
+
+
+/*
+** Free memory
+*/
+void luaM_free_ (lua_State *L, void *block, size_t osize) {
+ global_State *g = G(L);
+ lua_assert((osize == 0) == (block == NULL));
+ callfrealloc(g, block, osize, 0);
+ g->GCdebt -= osize;
+}
+
+
+/*
+** In case of allocation fail, this function will do an emergency
+** collection to free some memory and then try the allocation again.
+*/
+static void *tryagain (lua_State *L, void *block,
+ size_t osize, size_t nsize) {
+ global_State *g = G(L);
+ if (cantryagain(g)) {
+ luaC_fullgc(L, 1); /* try to free some memory... */
+ return callfrealloc(g, block, osize, nsize); /* try again */
+ }
+ else return NULL; /* cannot run an emergency collection */
+}
+
+
+/*
+** Generic allocation routine.
+*/
+void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
+ void *newblock;
+ global_State *g = G(L);
+ lua_assert((osize == 0) == (block == NULL));
+ newblock = firsttry(g, block, osize, nsize);
+ if (l_unlikely(newblock == NULL && nsize > 0)) {
+ newblock = tryagain(L, block, osize, nsize);
+ if (newblock == NULL) /* still no memory? */
+ return NULL; /* do not update 'GCdebt' */
+ }
+ lua_assert((nsize == 0) == (newblock == NULL));
+ g->GCdebt = (g->GCdebt + nsize) - osize;
+ return newblock;
+}
+
+
+void *luaM_saferealloc_ (lua_State *L, void *block, size_t osize,
+ size_t nsize) {
+ void *newblock = luaM_realloc_(L, block, osize, nsize);
+ if (l_unlikely(newblock == NULL && nsize > 0)) /* allocation failed? */
+ luaM_error(L);
+ return newblock;
+}
+
+
+void *luaM_malloc_ (lua_State *L, size_t size, int tag) {
+ if (size == 0)
+ return NULL; /* that's all */
+ else {
+ global_State *g = G(L);
+ void *newblock = firsttry(g, NULL, tag, size);
+ if (l_unlikely(newblock == NULL)) {
+ newblock = tryagain(L, NULL, tag, size);
+ if (newblock == NULL)
+ luaM_error(L);
+ }
+ g->GCdebt += size;
+ return newblock;
+ }
+}
diff --git a/src/libs/3rdparty/lua/src/lmem.h b/src/libs/3rdparty/lua/src/lmem.h
new file mode 100644
index 0000000000..8c75a44beb
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lmem.h
@@ -0,0 +1,93 @@
+/*
+** $Id: lmem.h $
+** Interface to Memory Manager
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lmem_h
+#define lmem_h
+
+
+#include <stddef.h>
+
+#include "llimits.h"
+#include "lua.h"
+
+
+#define luaM_error(L) luaD_throw(L, LUA_ERRMEM)
+
+
+/*
+** This macro tests whether it is safe to multiply 'n' by the size of
+** type 't' without overflows. Because 'e' is always constant, it avoids
+** the runtime division MAX_SIZET/(e).
+** (The macro is somewhat complex to avoid warnings: The 'sizeof'
+** comparison avoids a runtime comparison when overflow cannot occur.
+** The compiler should be able to optimize the real test by itself, but
+** when it does it, it may give a warning about "comparison is always
+** false due to limited range of data type"; the +1 tricks the compiler,
+** avoiding this warning but also this optimization.)
+*/
+#define luaM_testsize(n,e) \
+ (sizeof(n) >= sizeof(size_t) && cast_sizet((n)) + 1 > MAX_SIZET/(e))
+
+#define luaM_checksize(L,n,e) \
+ (luaM_testsize(n,e) ? luaM_toobig(L) : cast_void(0))
+
+
+/*
+** Computes the minimum between 'n' and 'MAX_SIZET/sizeof(t)', so that
+** the result is not larger than 'n' and cannot overflow a 'size_t'
+** when multiplied by the size of type 't'. (Assumes that 'n' is an
+** 'int' or 'unsigned int' and that 'int' is not larger than 'size_t'.)
+*/
+#define luaM_limitN(n,t) \
+ ((cast_sizet(n) <= MAX_SIZET/sizeof(t)) ? (n) : \
+ cast_uint((MAX_SIZET/sizeof(t))))
+
+
+/*
+** Arrays of chars do not need any test
+*/
+#define luaM_reallocvchar(L,b,on,n) \
+ cast_charp(luaM_saferealloc_(L, (b), (on)*sizeof(char), (n)*sizeof(char)))
+
+#define luaM_freemem(L, b, s) luaM_free_(L, (b), (s))
+#define luaM_free(L, b) luaM_free_(L, (b), sizeof(*(b)))
+#define luaM_freearray(L, b, n) luaM_free_(L, (b), (n)*sizeof(*(b)))
+
+#define luaM_new(L,t) cast(t*, luaM_malloc_(L, sizeof(t), 0))
+#define luaM_newvector(L,n,t) cast(t*, luaM_malloc_(L, (n)*sizeof(t), 0))
+#define luaM_newvectorchecked(L,n,t) \
+ (luaM_checksize(L,n,sizeof(t)), luaM_newvector(L,n,t))
+
+#define luaM_newobject(L,tag,s) luaM_malloc_(L, (s), tag)
+
+#define luaM_growvector(L,v,nelems,size,t,limit,e) \
+ ((v)=cast(t *, luaM_growaux_(L,v,nelems,&(size),sizeof(t), \
+ luaM_limitN(limit,t),e)))
+
+#define luaM_reallocvector(L, v,oldn,n,t) \
+ (cast(t *, luaM_realloc_(L, v, cast_sizet(oldn) * sizeof(t), \
+ cast_sizet(n) * sizeof(t))))
+
+#define luaM_shrinkvector(L,v,size,fs,t) \
+ ((v)=cast(t *, luaM_shrinkvector_(L, v, &(size), fs, sizeof(t))))
+
+LUAI_FUNC l_noret luaM_toobig (lua_State *L);
+
+/* not to be called directly */
+LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize,
+ size_t size);
+LUAI_FUNC void *luaM_saferealloc_ (lua_State *L, void *block, size_t oldsize,
+ size_t size);
+LUAI_FUNC void luaM_free_ (lua_State *L, void *block, size_t osize);
+LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int nelems,
+ int *size, int size_elem, int limit,
+ const char *what);
+LUAI_FUNC void *luaM_shrinkvector_ (lua_State *L, void *block, int *nelem,
+ int final_n, int size_elem);
+LUAI_FUNC void *luaM_malloc_ (lua_State *L, size_t size, int tag);
+
+#endif
+
diff --git a/src/libs/3rdparty/lua/src/loadlib.c b/src/libs/3rdparty/lua/src/loadlib.c
new file mode 100644
index 0000000000..d792dffaa0
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/loadlib.c
@@ -0,0 +1,767 @@
+/*
+** $Id: loadlib.c $
+** Dynamic library loader for Lua
+** See Copyright Notice in lua.h
+**
+** This module contains an implementation of loadlib for Unix systems
+** that have dlfcn, an implementation for Windows, and a stub for other
+** systems.
+*/
+
+#define loadlib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+/*
+** LUA_IGMARK is a mark to ignore all before it when building the
+** luaopen_ function name.
+*/
+#if !defined (LUA_IGMARK)
+#define LUA_IGMARK "-"
+#endif
+
+
+/*
+** LUA_CSUBSEP is the character that replaces dots in submodule names
+** when searching for a C loader.
+** LUA_LSUBSEP is the character that replaces dots in submodule names
+** when searching for a Lua loader.
+*/
+#if !defined(LUA_CSUBSEP)
+#define LUA_CSUBSEP LUA_DIRSEP
+#endif
+
+#if !defined(LUA_LSUBSEP)
+#define LUA_LSUBSEP LUA_DIRSEP
+#endif
+
+
+/* prefix for open functions in C libraries */
+#define LUA_POF "luaopen_"
+
+/* separator for open functions in C libraries */
+#define LUA_OFSEP "_"
+
+
+/*
+** key for table in the registry that keeps handles
+** for all loaded C libraries
+*/
+static const char *const CLIBS = "_CLIBS";
+
+#define LIB_FAIL "open"
+
+
+#define setprogdir(L) ((void)0)
+
+
+/*
+** Special type equivalent to '(void*)' for functions in gcc
+** (to suppress warnings when converting function pointers)
+*/
+typedef void (*voidf)(void);
+
+
+/*
+** system-dependent functions
+*/
+
+/*
+** unload library 'lib'
+*/
+static void lsys_unloadlib (void *lib);
+
+/*
+** load C library in file 'path'. If 'seeglb', load with all names in
+** the library global.
+** Returns the library; in case of error, returns NULL plus an
+** error string in the stack.
+*/
+static void *lsys_load (lua_State *L, const char *path, int seeglb);
+
+/*
+** Try to find a function named 'sym' in library 'lib'.
+** Returns the function; in case of error, returns NULL plus an
+** error string in the stack.
+*/
+static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym);
+
+
+
+
+#if defined(LUA_USE_DLOPEN) /* { */
+/*
+** {========================================================================
+** This is an implementation of loadlib based on the dlfcn interface.
+** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD,
+** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least
+** as an emulation layer on top of native functions.
+** =========================================================================
+*/
+
+#include <dlfcn.h>
+
+/*
+** Macro to convert pointer-to-void* to pointer-to-function. This cast
+** is undefined according to ISO C, but POSIX assumes that it works.
+** (The '__extension__' in gnu compilers is only to avoid warnings.)
+*/
+#if defined(__GNUC__)
+#define cast_func(p) (__extension__ (lua_CFunction)(p))
+#else
+#define cast_func(p) ((lua_CFunction)(p))
+#endif
+
+
+static void lsys_unloadlib (void *lib) {
+ dlclose(lib);
+}
+
+
+static void *lsys_load (lua_State *L, const char *path, int seeglb) {
+ void *lib = dlopen(path, RTLD_NOW | (seeglb ? RTLD_GLOBAL : RTLD_LOCAL));
+ if (l_unlikely(lib == NULL))
+ lua_pushstring(L, dlerror());
+ return lib;
+}
+
+
+static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) {
+ lua_CFunction f = cast_func(dlsym(lib, sym));
+ if (l_unlikely(f == NULL))
+ lua_pushstring(L, dlerror());
+ return f;
+}
+
+/* }====================================================== */
+
+
+
+#elif defined(LUA_DL_DLL) /* }{ */
+/*
+** {======================================================================
+** This is an implementation of loadlib for Windows using native functions.
+** =======================================================================
+*/
+
+#include <windows.h>
+
+
+/*
+** optional flags for LoadLibraryEx
+*/
+#if !defined(LUA_LLE_FLAGS)
+#define LUA_LLE_FLAGS 0
+#endif
+
+
+#undef setprogdir
+
+
+/*
+** Replace in the path (on the top of the stack) any occurrence
+** of LUA_EXEC_DIR with the executable's path.
+*/
+static void setprogdir (lua_State *L) {
+ char buff[MAX_PATH + 1];
+ char *lb;
+ DWORD nsize = sizeof(buff)/sizeof(char);
+ DWORD n = GetModuleFileNameA(NULL, buff, nsize); /* get exec. name */
+ if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL)
+ luaL_error(L, "unable to get ModuleFileName");
+ else {
+ *lb = '\0'; /* cut name on the last '\\' to get the path */
+ luaL_gsub(L, lua_tostring(L, -1), LUA_EXEC_DIR, buff);
+ lua_remove(L, -2); /* remove original string */
+ }
+}
+
+
+
+
+static void pusherror (lua_State *L) {
+ int error = GetLastError();
+ char buffer[128];
+ if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, error, 0, buffer, sizeof(buffer)/sizeof(char), NULL))
+ lua_pushstring(L, buffer);
+ else
+ lua_pushfstring(L, "system error %d\n", error);
+}
+
+static void lsys_unloadlib (void *lib) {
+ FreeLibrary((HMODULE)lib);
+}
+
+
+static void *lsys_load (lua_State *L, const char *path, int seeglb) {
+ HMODULE lib = LoadLibraryExA(path, NULL, LUA_LLE_FLAGS);
+ (void)(seeglb); /* not used: symbols are 'global' by default */
+ if (lib == NULL) pusherror(L);
+ return lib;
+}
+
+
+static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) {
+ lua_CFunction f = (lua_CFunction)(voidf)GetProcAddress((HMODULE)lib, sym);
+ if (f == NULL) pusherror(L);
+ return f;
+}
+
+/* }====================================================== */
+
+
+#else /* }{ */
+/*
+** {======================================================
+** Fallback for other systems
+** =======================================================
+*/
+
+#undef LIB_FAIL
+#define LIB_FAIL "absent"
+
+
+#define DLMSG "dynamic libraries not enabled; check your Lua installation"
+
+
+static void lsys_unloadlib (void *lib) {
+ (void)(lib); /* not used */
+}
+
+
+static void *lsys_load (lua_State *L, const char *path, int seeglb) {
+ (void)(path); (void)(seeglb); /* not used */
+ lua_pushliteral(L, DLMSG);
+ return NULL;
+}
+
+
+static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) {
+ (void)(lib); (void)(sym); /* not used */
+ lua_pushliteral(L, DLMSG);
+ return NULL;
+}
+
+/* }====================================================== */
+#endif /* } */
+
+
+/*
+** {==================================================================
+** Set Paths
+** ===================================================================
+*/
+
+/*
+** LUA_PATH_VAR and LUA_CPATH_VAR are the names of the environment
+** variables that Lua check to set its paths.
+*/
+#if !defined(LUA_PATH_VAR)
+#define LUA_PATH_VAR "LUA_PATH"
+#endif
+
+#if !defined(LUA_CPATH_VAR)
+#define LUA_CPATH_VAR "LUA_CPATH"
+#endif
+
+
+
+/*
+** return registry.LUA_NOENV as a boolean
+*/
+static int noenv (lua_State *L) {
+ int b;
+ lua_getfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
+ b = lua_toboolean(L, -1);
+ lua_pop(L, 1); /* remove value */
+ return b;
+}
+
+
+/*
+** Set a path
+*/
+static void setpath (lua_State *L, const char *fieldname,
+ const char *envname,
+ const char *dft) {
+ const char *dftmark;
+ const char *nver = lua_pushfstring(L, "%s%s", envname, LUA_VERSUFFIX);
+ const char *path = getenv(nver); /* try versioned name */
+ if (path == NULL) /* no versioned environment variable? */
+ path = getenv(envname); /* try unversioned name */
+ if (path == NULL || noenv(L)) /* no environment variable? */
+ lua_pushstring(L, dft); /* use default */
+ else if ((dftmark = strstr(path, LUA_PATH_SEP LUA_PATH_SEP)) == NULL)
+ lua_pushstring(L, path); /* nothing to change */
+ else { /* path contains a ";;": insert default path in its place */
+ size_t len = strlen(path);
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ if (path < dftmark) { /* is there a prefix before ';;'? */
+ luaL_addlstring(&b, path, dftmark - path); /* add it */
+ luaL_addchar(&b, *LUA_PATH_SEP);
+ }
+ luaL_addstring(&b, dft); /* add default */
+ if (dftmark < path + len - 2) { /* is there a suffix after ';;'? */
+ luaL_addchar(&b, *LUA_PATH_SEP);
+ luaL_addlstring(&b, dftmark + 2, (path + len - 2) - dftmark);
+ }
+ luaL_pushresult(&b);
+ }
+ setprogdir(L);
+ lua_setfield(L, -3, fieldname); /* package[fieldname] = path value */
+ lua_pop(L, 1); /* pop versioned variable name ('nver') */
+}
+
+/* }================================================================== */
+
+
+/*
+** return registry.CLIBS[path]
+*/
+static void *checkclib (lua_State *L, const char *path) {
+ void *plib;
+ lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
+ lua_getfield(L, -1, path);
+ plib = lua_touserdata(L, -1); /* plib = CLIBS[path] */
+ lua_pop(L, 2); /* pop CLIBS table and 'plib' */
+ return plib;
+}
+
+
+/*
+** registry.CLIBS[path] = plib -- for queries
+** registry.CLIBS[#CLIBS + 1] = plib -- also keep a list of all libraries
+*/
+static void addtoclib (lua_State *L, const char *path, void *plib) {
+ lua_getfield(L, LUA_REGISTRYINDEX, CLIBS);
+ lua_pushlightuserdata(L, plib);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -3, path); /* CLIBS[path] = plib */
+ lua_rawseti(L, -2, luaL_len(L, -2) + 1); /* CLIBS[#CLIBS + 1] = plib */
+ lua_pop(L, 1); /* pop CLIBS table */
+}
+
+
+/*
+** __gc tag method for CLIBS table: calls 'lsys_unloadlib' for all lib
+** handles in list CLIBS
+*/
+static int gctm (lua_State *L) {
+ lua_Integer n = luaL_len(L, 1);
+ for (; n >= 1; n--) { /* for each handle, in reverse order */
+ lua_rawgeti(L, 1, n); /* get handle CLIBS[n] */
+ lsys_unloadlib(lua_touserdata(L, -1));
+ lua_pop(L, 1); /* pop handle */
+ }
+ return 0;
+}
+
+
+
+/* error codes for 'lookforfunc' */
+#define ERRLIB 1
+#define ERRFUNC 2
+
+/*
+** Look for a C function named 'sym' in a dynamically loaded library
+** 'path'.
+** First, check whether the library is already loaded; if not, try
+** to load it.
+** Then, if 'sym' is '*', return true (as library has been loaded).
+** Otherwise, look for symbol 'sym' in the library and push a
+** C function with that symbol.
+** Return 0 and 'true' or a function in the stack; in case of
+** errors, return an error code and an error message in the stack.
+*/
+static int lookforfunc (lua_State *L, const char *path, const char *sym) {
+ void *reg = checkclib(L, path); /* check loaded C libraries */
+ if (reg == NULL) { /* must load library? */
+ reg = lsys_load(L, path, *sym == '*'); /* global symbols if 'sym'=='*' */
+ if (reg == NULL) return ERRLIB; /* unable to load library */
+ addtoclib(L, path, reg);
+ }
+ if (*sym == '*') { /* loading only library (no function)? */
+ lua_pushboolean(L, 1); /* return 'true' */
+ return 0; /* no errors */
+ }
+ else {
+ lua_CFunction f = lsys_sym(L, reg, sym);
+ if (f == NULL)
+ return ERRFUNC; /* unable to find function */
+ lua_pushcfunction(L, f); /* else create new function */
+ return 0; /* no errors */
+ }
+}
+
+
+static int ll_loadlib (lua_State *L) {
+ const char *path = luaL_checkstring(L, 1);
+ const char *init = luaL_checkstring(L, 2);
+ int stat = lookforfunc(L, path, init);
+ if (l_likely(stat == 0)) /* no errors? */
+ return 1; /* return the loaded function */
+ else { /* error; error message is on stack top */
+ luaL_pushfail(L);
+ lua_insert(L, -2);
+ lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init");
+ return 3; /* return fail, error message, and where */
+ }
+}
+
+
+
+/*
+** {======================================================
+** 'require' function
+** =======================================================
+*/
+
+
+static int readable (const char *filename) {
+ FILE *f = fopen(filename, "r"); /* try to open file */
+ if (f == NULL) return 0; /* open failed */
+ fclose(f);
+ return 1;
+}
+
+
+/*
+** Get the next name in '*path' = 'name1;name2;name3;...', changing
+** the ending ';' to '\0' to create a zero-terminated string. Return
+** NULL when list ends.
+*/
+static const char *getnextfilename (char **path, char *end) {
+ char *sep;
+ char *name = *path;
+ if (name == end)
+ return NULL; /* no more names */
+ else if (*name == '\0') { /* from previous iteration? */
+ *name = *LUA_PATH_SEP; /* restore separator */
+ name++; /* skip it */
+ }
+ sep = strchr(name, *LUA_PATH_SEP); /* find next separator */
+ if (sep == NULL) /* separator not found? */
+ sep = end; /* name goes until the end */
+ *sep = '\0'; /* finish file name */
+ *path = sep; /* will start next search from here */
+ return name;
+}
+
+
+/*
+** Given a path such as ";blabla.so;blublu.so", pushes the string
+**
+** no file 'blabla.so'
+** no file 'blublu.so'
+*/
+static void pusherrornotfound (lua_State *L, const char *path) {
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ luaL_addstring(&b, "no file '");
+ luaL_addgsub(&b, path, LUA_PATH_SEP, "'\n\tno file '");
+ luaL_addstring(&b, "'");
+ luaL_pushresult(&b);
+}
+
+
+static const char *searchpath (lua_State *L, const char *name,
+ const char *path,
+ const char *sep,
+ const char *dirsep) {
+ luaL_Buffer buff;
+ char *pathname; /* path with name inserted */
+ char *endpathname; /* its end */
+ const char *filename;
+ /* separator is non-empty and appears in 'name'? */
+ if (*sep != '\0' && strchr(name, *sep) != NULL)
+ name = luaL_gsub(L, name, sep, dirsep); /* replace it by 'dirsep' */
+ luaL_buffinit(L, &buff);
+ /* add path to the buffer, replacing marks ('?') with the file name */
+ luaL_addgsub(&buff, path, LUA_PATH_MARK, name);
+ luaL_addchar(&buff, '\0');
+ pathname = luaL_buffaddr(&buff); /* writable list of file names */
+ endpathname = pathname + luaL_bufflen(&buff) - 1;
+ while ((filename = getnextfilename(&pathname, endpathname)) != NULL) {
+ if (readable(filename)) /* does file exist and is readable? */
+ return lua_pushstring(L, filename); /* save and return name */
+ }
+ luaL_pushresult(&buff); /* push path to create error message */
+ pusherrornotfound(L, lua_tostring(L, -1)); /* create error message */
+ return NULL; /* not found */
+}
+
+
+static int ll_searchpath (lua_State *L) {
+ const char *f = searchpath(L, luaL_checkstring(L, 1),
+ luaL_checkstring(L, 2),
+ luaL_optstring(L, 3, "."),
+ luaL_optstring(L, 4, LUA_DIRSEP));
+ if (f != NULL) return 1;
+ else { /* error message is on top of the stack */
+ luaL_pushfail(L);
+ lua_insert(L, -2);
+ return 2; /* return fail + error message */
+ }
+}
+
+
+static const char *findfile (lua_State *L, const char *name,
+ const char *pname,
+ const char *dirsep) {
+ const char *path;
+ lua_getfield(L, lua_upvalueindex(1), pname);
+ path = lua_tostring(L, -1);
+ if (l_unlikely(path == NULL))
+ luaL_error(L, "'package.%s' must be a string", pname);
+ return searchpath(L, name, path, ".", dirsep);
+}
+
+
+static int checkload (lua_State *L, int stat, const char *filename) {
+ if (l_likely(stat)) { /* module loaded successfully? */
+ lua_pushstring(L, filename); /* will be 2nd argument to module */
+ return 2; /* return open function and file name */
+ }
+ else
+ return luaL_error(L, "error loading module '%s' from file '%s':\n\t%s",
+ lua_tostring(L, 1), filename, lua_tostring(L, -1));
+}
+
+
+static int searcher_Lua (lua_State *L) {
+ const char *filename;
+ const char *name = luaL_checkstring(L, 1);
+ filename = findfile(L, name, "path", LUA_LSUBSEP);
+ if (filename == NULL) return 1; /* module not found in this path */
+ return checkload(L, (luaL_loadfile(L, filename) == LUA_OK), filename);
+}
+
+
+/*
+** Try to find a load function for module 'modname' at file 'filename'.
+** First, change '.' to '_' in 'modname'; then, if 'modname' has
+** the form X-Y (that is, it has an "ignore mark"), build a function
+** name "luaopen_X" and look for it. (For compatibility, if that
+** fails, it also tries "luaopen_Y".) If there is no ignore mark,
+** look for a function named "luaopen_modname".
+*/
+static int loadfunc (lua_State *L, const char *filename, const char *modname) {
+ const char *openfunc;
+ const char *mark;
+ modname = luaL_gsub(L, modname, ".", LUA_OFSEP);
+ mark = strchr(modname, *LUA_IGMARK);
+ if (mark) {
+ int stat;
+ openfunc = lua_pushlstring(L, modname, mark - modname);
+ openfunc = lua_pushfstring(L, LUA_POF"%s", openfunc);
+ stat = lookforfunc(L, filename, openfunc);
+ if (stat != ERRFUNC) return stat;
+ modname = mark + 1; /* else go ahead and try old-style name */
+ }
+ openfunc = lua_pushfstring(L, LUA_POF"%s", modname);
+ return lookforfunc(L, filename, openfunc);
+}
+
+
+static int searcher_C (lua_State *L) {
+ const char *name = luaL_checkstring(L, 1);
+ const char *filename = findfile(L, name, "cpath", LUA_CSUBSEP);
+ if (filename == NULL) return 1; /* module not found in this path */
+ return checkload(L, (loadfunc(L, filename, name) == 0), filename);
+}
+
+
+static int searcher_Croot (lua_State *L) {
+ const char *filename;
+ const char *name = luaL_checkstring(L, 1);
+ const char *p = strchr(name, '.');
+ int stat;
+ if (p == NULL) return 0; /* is root */
+ lua_pushlstring(L, name, p - name);
+ filename = findfile(L, lua_tostring(L, -1), "cpath", LUA_CSUBSEP);
+ if (filename == NULL) return 1; /* root not found */
+ if ((stat = loadfunc(L, filename, name)) != 0) {
+ if (stat != ERRFUNC)
+ return checkload(L, 0, filename); /* real error */
+ else { /* open function not found */
+ lua_pushfstring(L, "no module '%s' in file '%s'", name, filename);
+ return 1;
+ }
+ }
+ lua_pushstring(L, filename); /* will be 2nd argument to module */
+ return 2;
+}
+
+
+static int searcher_preload (lua_State *L) {
+ const char *name = luaL_checkstring(L, 1);
+ lua_getfield(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
+ if (lua_getfield(L, -1, name) == LUA_TNIL) { /* not found? */
+ lua_pushfstring(L, "no field package.preload['%s']", name);
+ return 1;
+ }
+ else {
+ lua_pushliteral(L, ":preload:");
+ return 2;
+ }
+}
+
+
+static void findloader (lua_State *L, const char *name) {
+ int i;
+ luaL_Buffer msg; /* to build error message */
+ /* push 'package.searchers' to index 3 in the stack */
+ if (l_unlikely(lua_getfield(L, lua_upvalueindex(1), "searchers")
+ != LUA_TTABLE))
+ luaL_error(L, "'package.searchers' must be a table");
+ luaL_buffinit(L, &msg);
+ /* iterate over available searchers to find a loader */
+ for (i = 1; ; i++) {
+ luaL_addstring(&msg, "\n\t"); /* error-message prefix */
+ if (l_unlikely(lua_rawgeti(L, 3, i) == LUA_TNIL)) { /* no more searchers? */
+ lua_pop(L, 1); /* remove nil */
+ luaL_buffsub(&msg, 2); /* remove prefix */
+ luaL_pushresult(&msg); /* create error message */
+ luaL_error(L, "module '%s' not found:%s", name, lua_tostring(L, -1));
+ }
+ lua_pushstring(L, name);
+ lua_call(L, 1, 2); /* call it */
+ if (lua_isfunction(L, -2)) /* did it find a loader? */
+ return; /* module loader found */
+ else if (lua_isstring(L, -2)) { /* searcher returned error message? */
+ lua_pop(L, 1); /* remove extra return */
+ luaL_addvalue(&msg); /* concatenate error message */
+ }
+ else { /* no error message */
+ lua_pop(L, 2); /* remove both returns */
+ luaL_buffsub(&msg, 2); /* remove prefix */
+ }
+ }
+}
+
+
+static int ll_require (lua_State *L) {
+ const char *name = luaL_checkstring(L, 1);
+ lua_settop(L, 1); /* LOADED table will be at index 2 */
+ lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
+ lua_getfield(L, 2, name); /* LOADED[name] */
+ if (lua_toboolean(L, -1)) /* is it there? */
+ return 1; /* package is already loaded */
+ /* else must load package */
+ lua_pop(L, 1); /* remove 'getfield' result */
+ findloader(L, name);
+ lua_rotate(L, -2, 1); /* function <-> loader data */
+ lua_pushvalue(L, 1); /* name is 1st argument to module loader */
+ lua_pushvalue(L, -3); /* loader data is 2nd argument */
+ /* stack: ...; loader data; loader function; mod. name; loader data */
+ lua_call(L, 2, 1); /* run loader to load module */
+ /* stack: ...; loader data; result from loader */
+ if (!lua_isnil(L, -1)) /* non-nil return? */
+ lua_setfield(L, 2, name); /* LOADED[name] = returned value */
+ else
+ lua_pop(L, 1); /* pop nil */
+ if (lua_getfield(L, 2, name) == LUA_TNIL) { /* module set no value? */
+ lua_pushboolean(L, 1); /* use true as result */
+ lua_copy(L, -1, -2); /* replace loader result */
+ lua_setfield(L, 2, name); /* LOADED[name] = true */
+ }
+ lua_rotate(L, -2, 1); /* loader data <-> module result */
+ return 2; /* return module result and loader data */
+}
+
+/* }====================================================== */
+
+
+
+
+static const luaL_Reg pk_funcs[] = {
+ {"loadlib", ll_loadlib},
+ {"searchpath", ll_searchpath},
+ /* placeholders */
+ {"preload", NULL},
+ {"cpath", NULL},
+ {"path", NULL},
+ {"searchers", NULL},
+ {"loaded", NULL},
+ {NULL, NULL}
+};
+
+
+static const luaL_Reg ll_funcs[] = {
+ {"require", ll_require},
+ {NULL, NULL}
+};
+
+
+static void createsearcherstable (lua_State *L) {
+ static const lua_CFunction searchers[] = {
+ searcher_preload,
+ searcher_Lua,
+ searcher_C,
+ searcher_Croot,
+ NULL
+ };
+ int i;
+ /* create 'searchers' table */
+ lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0);
+ /* fill it with predefined searchers */
+ for (i=0; searchers[i] != NULL; i++) {
+ lua_pushvalue(L, -2); /* set 'package' as upvalue for all searchers */
+ lua_pushcclosure(L, searchers[i], 1);
+ lua_rawseti(L, -2, i+1);
+ }
+ lua_setfield(L, -2, "searchers"); /* put it in field 'searchers' */
+}
+
+
+/*
+** create table CLIBS to keep track of loaded C libraries,
+** setting a finalizer to close all libraries when closing state.
+*/
+static void createclibstable (lua_State *L) {
+ luaL_getsubtable(L, LUA_REGISTRYINDEX, CLIBS); /* create CLIBS table */
+ lua_createtable(L, 0, 1); /* create metatable for CLIBS */
+ lua_pushcfunction(L, gctm);
+ lua_setfield(L, -2, "__gc"); /* set finalizer for CLIBS table */
+ lua_setmetatable(L, -2);
+}
+
+
+LUAMOD_API int luaopen_package (lua_State *L) {
+ createclibstable(L);
+ luaL_newlib(L, pk_funcs); /* create 'package' table */
+ createsearcherstable(L);
+ /* set paths */
+ setpath(L, "path", LUA_PATH_VAR, LUA_PATH_DEFAULT);
+ setpath(L, "cpath", LUA_CPATH_VAR, LUA_CPATH_DEFAULT);
+ /* store config information */
+ lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATH_SEP "\n" LUA_PATH_MARK "\n"
+ LUA_EXEC_DIR "\n" LUA_IGMARK "\n");
+ lua_setfield(L, -2, "config");
+ /* set field 'loaded' */
+ luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
+ lua_setfield(L, -2, "loaded");
+ /* set field 'preload' */
+ luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
+ lua_setfield(L, -2, "preload");
+ lua_pushglobaltable(L);
+ lua_pushvalue(L, -2); /* set 'package' as upvalue for next lib */
+ luaL_setfuncs(L, ll_funcs, 1); /* open lib into global table */
+ lua_pop(L, 1); /* pop global table */
+ return 1; /* return 'package' table */
+}
+
diff --git a/src/libs/3rdparty/lua/src/lobject.c b/src/libs/3rdparty/lua/src/lobject.c
new file mode 100644
index 0000000000..f73ffc6d92
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lobject.c
@@ -0,0 +1,602 @@
+/*
+** $Id: lobject.c $
+** Some generic functions over Lua objects
+** See Copyright Notice in lua.h
+*/
+
+#define lobject_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <locale.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lctype.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "lvm.h"
+
+
+/*
+** Computes ceil(log2(x))
+*/
+int luaO_ceillog2 (unsigned int x) {
+ static const lu_byte log_2[256] = { /* log_2[i] = ceil(log2(i - 1)) */
+ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
+ };
+ int l = 0;
+ x--;
+ while (x >= 256) { l += 8; x >>= 8; }
+ return l + log_2[x];
+}
+
+
+static lua_Integer intarith (lua_State *L, int op, lua_Integer v1,
+ lua_Integer v2) {
+ switch (op) {
+ case LUA_OPADD: return intop(+, v1, v2);
+ case LUA_OPSUB:return intop(-, v1, v2);
+ case LUA_OPMUL:return intop(*, v1, v2);
+ case LUA_OPMOD: return luaV_mod(L, v1, v2);
+ case LUA_OPIDIV: return luaV_idiv(L, v1, v2);
+ case LUA_OPBAND: return intop(&, v1, v2);
+ case LUA_OPBOR: return intop(|, v1, v2);
+ case LUA_OPBXOR: return intop(^, v1, v2);
+ case LUA_OPSHL: return luaV_shiftl(v1, v2);
+ case LUA_OPSHR: return luaV_shiftr(v1, v2);
+ case LUA_OPUNM: return intop(-, 0, v1);
+ case LUA_OPBNOT: return intop(^, ~l_castS2U(0), v1);
+ default: lua_assert(0); return 0;
+ }
+}
+
+
+static lua_Number numarith (lua_State *L, int op, lua_Number v1,
+ lua_Number v2) {
+ switch (op) {
+ case LUA_OPADD: return luai_numadd(L, v1, v2);
+ case LUA_OPSUB: return luai_numsub(L, v1, v2);
+ case LUA_OPMUL: return luai_nummul(L, v1, v2);
+ case LUA_OPDIV: return luai_numdiv(L, v1, v2);
+ case LUA_OPPOW: return luai_numpow(L, v1, v2);
+ case LUA_OPIDIV: return luai_numidiv(L, v1, v2);
+ case LUA_OPUNM: return luai_numunm(L, v1);
+ case LUA_OPMOD: return luaV_modf(L, v1, v2);
+ default: lua_assert(0); return 0;
+ }
+}
+
+
+int luaO_rawarith (lua_State *L, int op, const TValue *p1, const TValue *p2,
+ TValue *res) {
+ switch (op) {
+ case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR:
+ case LUA_OPSHL: case LUA_OPSHR:
+ case LUA_OPBNOT: { /* operate only on integers */
+ lua_Integer i1; lua_Integer i2;
+ if (tointegerns(p1, &i1) && tointegerns(p2, &i2)) {
+ setivalue(res, intarith(L, op, i1, i2));
+ return 1;
+ }
+ else return 0; /* fail */
+ }
+ case LUA_OPDIV: case LUA_OPPOW: { /* operate only on floats */
+ lua_Number n1; lua_Number n2;
+ if (tonumberns(p1, n1) && tonumberns(p2, n2)) {
+ setfltvalue(res, numarith(L, op, n1, n2));
+ return 1;
+ }
+ else return 0; /* fail */
+ }
+ default: { /* other operations */
+ lua_Number n1; lua_Number n2;
+ if (ttisinteger(p1) && ttisinteger(p2)) {
+ setivalue(res, intarith(L, op, ivalue(p1), ivalue(p2)));
+ return 1;
+ }
+ else if (tonumberns(p1, n1) && tonumberns(p2, n2)) {
+ setfltvalue(res, numarith(L, op, n1, n2));
+ return 1;
+ }
+ else return 0; /* fail */
+ }
+ }
+}
+
+
+void luaO_arith (lua_State *L, int op, const TValue *p1, const TValue *p2,
+ StkId res) {
+ if (!luaO_rawarith(L, op, p1, p2, s2v(res))) {
+ /* could not perform raw operation; try metamethod */
+ luaT_trybinTM(L, p1, p2, res, cast(TMS, (op - LUA_OPADD) + TM_ADD));
+ }
+}
+
+
+int luaO_hexavalue (int c) {
+ if (lisdigit(c)) return c - '0';
+ else return (ltolower(c) - 'a') + 10;
+}
+
+
+static int isneg (const char **s) {
+ if (**s == '-') { (*s)++; return 1; }
+ else if (**s == '+') (*s)++;
+ return 0;
+}
+
+
+
+/*
+** {==================================================================
+** Lua's implementation for 'lua_strx2number'
+** ===================================================================
+*/
+
+#if !defined(lua_strx2number)
+
+/* maximum number of significant digits to read (to avoid overflows
+ even with single floats) */
+#define MAXSIGDIG 30
+
+/*
+** convert a hexadecimal numeric string to a number, following
+** C99 specification for 'strtod'
+*/
+static lua_Number lua_strx2number (const char *s, char **endptr) {
+ int dot = lua_getlocaledecpoint();
+ lua_Number r = l_mathop(0.0); /* result (accumulator) */
+ int sigdig = 0; /* number of significant digits */
+ int nosigdig = 0; /* number of non-significant digits */
+ int e = 0; /* exponent correction */
+ int neg; /* 1 if number is negative */
+ int hasdot = 0; /* true after seen a dot */
+ *endptr = cast_charp(s); /* nothing is valid yet */
+ while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */
+ neg = isneg(&s); /* check sign */
+ if (!(*s == '0' && (*(s + 1) == 'x' || *(s + 1) == 'X'))) /* check '0x' */
+ return l_mathop(0.0); /* invalid format (no '0x') */
+ for (s += 2; ; s++) { /* skip '0x' and read numeral */
+ if (*s == dot) {
+ if (hasdot) break; /* second dot? stop loop */
+ else hasdot = 1;
+ }
+ else if (lisxdigit(cast_uchar(*s))) {
+ if (sigdig == 0 && *s == '0') /* non-significant digit (zero)? */
+ nosigdig++;
+ else if (++sigdig <= MAXSIGDIG) /* can read it without overflow? */
+ r = (r * l_mathop(16.0)) + luaO_hexavalue(*s);
+ else e++; /* too many digits; ignore, but still count for exponent */
+ if (hasdot) e--; /* decimal digit? correct exponent */
+ }
+ else break; /* neither a dot nor a digit */
+ }
+ if (nosigdig + sigdig == 0) /* no digits? */
+ return l_mathop(0.0); /* invalid format */
+ *endptr = cast_charp(s); /* valid up to here */
+ e *= 4; /* each digit multiplies/divides value by 2^4 */
+ if (*s == 'p' || *s == 'P') { /* exponent part? */
+ int exp1 = 0; /* exponent value */
+ int neg1; /* exponent sign */
+ s++; /* skip 'p' */
+ neg1 = isneg(&s); /* sign */
+ if (!lisdigit(cast_uchar(*s)))
+ return l_mathop(0.0); /* invalid; must have at least one digit */
+ while (lisdigit(cast_uchar(*s))) /* read exponent */
+ exp1 = exp1 * 10 + *(s++) - '0';
+ if (neg1) exp1 = -exp1;
+ e += exp1;
+ *endptr = cast_charp(s); /* valid up to here */
+ }
+ if (neg) r = -r;
+ return l_mathop(ldexp)(r, e);
+}
+
+#endif
+/* }====================================================== */
+
+
+/* maximum length of a numeral to be converted to a number */
+#if !defined (L_MAXLENNUM)
+#define L_MAXLENNUM 200
+#endif
+
+/*
+** Convert string 's' to a Lua number (put in 'result'). Return NULL on
+** fail or the address of the ending '\0' on success. ('mode' == 'x')
+** means a hexadecimal numeral.
+*/
+static const char *l_str2dloc (const char *s, lua_Number *result, int mode) {
+ char *endptr;
+ *result = (mode == 'x') ? lua_strx2number(s, &endptr) /* try to convert */
+ : lua_str2number(s, &endptr);
+ if (endptr == s) return NULL; /* nothing recognized? */
+ while (lisspace(cast_uchar(*endptr))) endptr++; /* skip trailing spaces */
+ return (*endptr == '\0') ? endptr : NULL; /* OK iff no trailing chars */
+}
+
+
+/*
+** Convert string 's' to a Lua number (put in 'result') handling the
+** current locale.
+** This function accepts both the current locale or a dot as the radix
+** mark. If the conversion fails, it may mean number has a dot but
+** locale accepts something else. In that case, the code copies 's'
+** to a buffer (because 's' is read-only), changes the dot to the
+** current locale radix mark, and tries to convert again.
+** The variable 'mode' checks for special characters in the string:
+** - 'n' means 'inf' or 'nan' (which should be rejected)
+** - 'x' means a hexadecimal numeral
+** - '.' just optimizes the search for the common case (no special chars)
+*/
+static const char *l_str2d (const char *s, lua_Number *result) {
+ const char *endptr;
+ const char *pmode = strpbrk(s, ".xXnN"); /* look for special chars */
+ int mode = pmode ? ltolower(cast_uchar(*pmode)) : 0;
+ if (mode == 'n') /* reject 'inf' and 'nan' */
+ return NULL;
+ endptr = l_str2dloc(s, result, mode); /* try to convert */
+ if (endptr == NULL) { /* failed? may be a different locale */
+ char buff[L_MAXLENNUM + 1];
+ const char *pdot = strchr(s, '.');
+ if (pdot == NULL || strlen(s) > L_MAXLENNUM)
+ return NULL; /* string too long or no dot; fail */
+ strcpy(buff, s); /* copy string to buffer */
+ buff[pdot - s] = lua_getlocaledecpoint(); /* correct decimal point */
+ endptr = l_str2dloc(buff, result, mode); /* try again */
+ if (endptr != NULL)
+ endptr = s + (endptr - buff); /* make relative to 's' */
+ }
+ return endptr;
+}
+
+
+#define MAXBY10 cast(lua_Unsigned, LUA_MAXINTEGER / 10)
+#define MAXLASTD cast_int(LUA_MAXINTEGER % 10)
+
+static const char *l_str2int (const char *s, lua_Integer *result) {
+ lua_Unsigned a = 0;
+ int empty = 1;
+ int neg;
+ while (lisspace(cast_uchar(*s))) s++; /* skip initial spaces */
+ neg = isneg(&s);
+ if (s[0] == '0' &&
+ (s[1] == 'x' || s[1] == 'X')) { /* hex? */
+ s += 2; /* skip '0x' */
+ for (; lisxdigit(cast_uchar(*s)); s++) {
+ a = a * 16 + luaO_hexavalue(*s);
+ empty = 0;
+ }
+ }
+ else { /* decimal */
+ for (; lisdigit(cast_uchar(*s)); s++) {
+ int d = *s - '0';
+ if (a >= MAXBY10 && (a > MAXBY10 || d > MAXLASTD + neg)) /* overflow? */
+ return NULL; /* do not accept it (as integer) */
+ a = a * 10 + d;
+ empty = 0;
+ }
+ }
+ while (lisspace(cast_uchar(*s))) s++; /* skip trailing spaces */
+ if (empty || *s != '\0') return NULL; /* something wrong in the numeral */
+ else {
+ *result = l_castU2S((neg) ? 0u - a : a);
+ return s;
+ }
+}
+
+
+size_t luaO_str2num (const char *s, TValue *o) {
+ lua_Integer i; lua_Number n;
+ const char *e;
+ if ((e = l_str2int(s, &i)) != NULL) { /* try as an integer */
+ setivalue(o, i);
+ }
+ else if ((e = l_str2d(s, &n)) != NULL) { /* else try as a float */
+ setfltvalue(o, n);
+ }
+ else
+ return 0; /* conversion failed */
+ return (e - s) + 1; /* success; return string size */
+}
+
+
+int luaO_utf8esc (char *buff, unsigned long x) {
+ int n = 1; /* number of bytes put in buffer (backwards) */
+ lua_assert(x <= 0x7FFFFFFFu);
+ if (x < 0x80) /* ascii? */
+ buff[UTF8BUFFSZ - 1] = cast_char(x);
+ else { /* need continuation bytes */
+ unsigned int mfb = 0x3f; /* maximum that fits in first byte */
+ do { /* add continuation bytes */
+ buff[UTF8BUFFSZ - (n++)] = cast_char(0x80 | (x & 0x3f));
+ x >>= 6; /* remove added bits */
+ mfb >>= 1; /* now there is one less bit available in first byte */
+ } while (x > mfb); /* still needs continuation byte? */
+ buff[UTF8BUFFSZ - n] = cast_char((~mfb << 1) | x); /* add first byte */
+ }
+ return n;
+}
+
+
+/*
+** Maximum length of the conversion of a number to a string. Must be
+** enough to accommodate both LUA_INTEGER_FMT and LUA_NUMBER_FMT.
+** (For a long long int, this is 19 digits plus a sign and a final '\0',
+** adding to 21. For a long double, it can go to a sign, 33 digits,
+** the dot, an exponent letter, an exponent sign, 5 exponent digits,
+** and a final '\0', adding to 43.)
+*/
+#define MAXNUMBER2STR 44
+
+
+/*
+** Convert a number object to a string, adding it to a buffer
+*/
+static int tostringbuff (TValue *obj, char *buff) {
+ int len;
+ lua_assert(ttisnumber(obj));
+ if (ttisinteger(obj))
+ len = lua_integer2str(buff, MAXNUMBER2STR, ivalue(obj));
+ else {
+ len = lua_number2str(buff, MAXNUMBER2STR, fltvalue(obj));
+ if (buff[strspn(buff, "-0123456789")] == '\0') { /* looks like an int? */
+ buff[len++] = lua_getlocaledecpoint();
+ buff[len++] = '0'; /* adds '.0' to result */
+ }
+ }
+ return len;
+}
+
+
+/*
+** Convert a number object to a Lua string, replacing the value at 'obj'
+*/
+void luaO_tostring (lua_State *L, TValue *obj) {
+ char buff[MAXNUMBER2STR];
+ int len = tostringbuff(obj, buff);
+ setsvalue(L, obj, luaS_newlstr(L, buff, len));
+}
+
+
+
+
+/*
+** {==================================================================
+** 'luaO_pushvfstring'
+** ===================================================================
+*/
+
+/*
+** Size for buffer space used by 'luaO_pushvfstring'. It should be
+** (LUA_IDSIZE + MAXNUMBER2STR) + a minimal space for basic messages,
+** so that 'luaG_addinfo' can work directly on the buffer.
+*/
+#define BUFVFS (LUA_IDSIZE + MAXNUMBER2STR + 95)
+
+/* buffer used by 'luaO_pushvfstring' */
+typedef struct BuffFS {
+ lua_State *L;
+ int pushed; /* true if there is a part of the result on the stack */
+ int blen; /* length of partial string in 'space' */
+ char space[BUFVFS]; /* holds last part of the result */
+} BuffFS;
+
+
+/*
+** Push given string to the stack, as part of the result, and
+** join it to previous partial result if there is one.
+** It may call 'luaV_concat' while using one slot from EXTRA_STACK.
+** This call cannot invoke metamethods, as both operands must be
+** strings. It can, however, raise an error if the result is too
+** long. In that case, 'luaV_concat' frees the extra slot before
+** raising the error.
+*/
+static void pushstr (BuffFS *buff, const char *str, size_t lstr) {
+ lua_State *L = buff->L;
+ setsvalue2s(L, L->top.p, luaS_newlstr(L, str, lstr));
+ L->top.p++; /* may use one slot from EXTRA_STACK */
+ if (!buff->pushed) /* no previous string on the stack? */
+ buff->pushed = 1; /* now there is one */
+ else /* join previous string with new one */
+ luaV_concat(L, 2);
+}
+
+
+/*
+** empty the buffer space into the stack
+*/
+static void clearbuff (BuffFS *buff) {
+ pushstr(buff, buff->space, buff->blen); /* push buffer contents */
+ buff->blen = 0; /* space now is empty */
+}
+
+
+/*
+** Get a space of size 'sz' in the buffer. If buffer has not enough
+** space, empty it. 'sz' must fit in an empty buffer.
+*/
+static char *getbuff (BuffFS *buff, int sz) {
+ lua_assert(buff->blen <= BUFVFS); lua_assert(sz <= BUFVFS);
+ if (sz > BUFVFS - buff->blen) /* not enough space? */
+ clearbuff(buff);
+ return buff->space + buff->blen;
+}
+
+
+#define addsize(b,sz) ((b)->blen += (sz))
+
+
+/*
+** Add 'str' to the buffer. If string is larger than the buffer space,
+** push the string directly to the stack.
+*/
+static void addstr2buff (BuffFS *buff, const char *str, size_t slen) {
+ if (slen <= BUFVFS) { /* does string fit into buffer? */
+ char *bf = getbuff(buff, cast_int(slen));
+ memcpy(bf, str, slen); /* add string to buffer */
+ addsize(buff, cast_int(slen));
+ }
+ else { /* string larger than buffer */
+ clearbuff(buff); /* string comes after buffer's content */
+ pushstr(buff, str, slen); /* push string */
+ }
+}
+
+
+/*
+** Add a numeral to the buffer.
+*/
+static void addnum2buff (BuffFS *buff, TValue *num) {
+ char *numbuff = getbuff(buff, MAXNUMBER2STR);
+ int len = tostringbuff(num, numbuff); /* format number into 'numbuff' */
+ addsize(buff, len);
+}
+
+
+/*
+** this function handles only '%d', '%c', '%f', '%p', '%s', and '%%'
+ conventional formats, plus Lua-specific '%I' and '%U'
+*/
+const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
+ BuffFS buff; /* holds last part of the result */
+ const char *e; /* points to next '%' */
+ buff.pushed = buff.blen = 0;
+ buff.L = L;
+ while ((e = strchr(fmt, '%')) != NULL) {
+ addstr2buff(&buff, fmt, e - fmt); /* add 'fmt' up to '%' */
+ switch (*(e + 1)) { /* conversion specifier */
+ case 's': { /* zero-terminated string */
+ const char *s = va_arg(argp, char *);
+ if (s == NULL) s = "(null)";
+ addstr2buff(&buff, s, strlen(s));
+ break;
+ }
+ case 'c': { /* an 'int' as a character */
+ char c = cast_uchar(va_arg(argp, int));
+ addstr2buff(&buff, &c, sizeof(char));
+ break;
+ }
+ case 'd': { /* an 'int' */
+ TValue num;
+ setivalue(&num, va_arg(argp, int));
+ addnum2buff(&buff, &num);
+ break;
+ }
+ case 'I': { /* a 'lua_Integer' */
+ TValue num;
+ setivalue(&num, cast(lua_Integer, va_arg(argp, l_uacInt)));
+ addnum2buff(&buff, &num);
+ break;
+ }
+ case 'f': { /* a 'lua_Number' */
+ TValue num;
+ setfltvalue(&num, cast_num(va_arg(argp, l_uacNumber)));
+ addnum2buff(&buff, &num);
+ break;
+ }
+ case 'p': { /* a pointer */
+ const int sz = 3 * sizeof(void*) + 8; /* enough space for '%p' */
+ char *bf = getbuff(&buff, sz);
+ void *p = va_arg(argp, void *);
+ int len = lua_pointer2str(bf, sz, p);
+ addsize(&buff, len);
+ break;
+ }
+ case 'U': { /* a 'long' as a UTF-8 sequence */
+ char bf[UTF8BUFFSZ];
+ int len = luaO_utf8esc(bf, va_arg(argp, long));
+ addstr2buff(&buff, bf + UTF8BUFFSZ - len, len);
+ break;
+ }
+ case '%': {
+ addstr2buff(&buff, "%", 1);
+ break;
+ }
+ default: {
+ luaG_runerror(L, "invalid option '%%%c' to 'lua_pushfstring'",
+ *(e + 1));
+ }
+ }
+ fmt = e + 2; /* skip '%' and the specifier */
+ }
+ addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */
+ clearbuff(&buff); /* empty buffer into the stack */
+ lua_assert(buff.pushed == 1);
+ return svalue(s2v(L->top.p - 1));
+}
+
+
+const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) {
+ const char *msg;
+ va_list argp;
+ va_start(argp, fmt);
+ msg = luaO_pushvfstring(L, fmt, argp);
+ va_end(argp);
+ return msg;
+}
+
+/* }================================================================== */
+
+
+#define RETS "..."
+#define PRE "[string \""
+#define POS "\"]"
+
+#define addstr(a,b,l) ( memcpy(a,b,(l) * sizeof(char)), a += (l) )
+
+void luaO_chunkid (char *out, const char *source, size_t srclen) {
+ size_t bufflen = LUA_IDSIZE; /* free space in buffer */
+ if (*source == '=') { /* 'literal' source */
+ if (srclen <= bufflen) /* small enough? */
+ memcpy(out, source + 1, srclen * sizeof(char));
+ else { /* truncate it */
+ addstr(out, source + 1, bufflen - 1);
+ *out = '\0';
+ }
+ }
+ else if (*source == '@') { /* file name */
+ if (srclen <= bufflen) /* small enough? */
+ memcpy(out, source + 1, srclen * sizeof(char));
+ else { /* add '...' before rest of name */
+ addstr(out, RETS, LL(RETS));
+ bufflen -= LL(RETS);
+ memcpy(out, source + 1 + srclen - bufflen, bufflen * sizeof(char));
+ }
+ }
+ else { /* string; format as [string "source"] */
+ const char *nl = strchr(source, '\n'); /* find first new line (if any) */
+ addstr(out, PRE, LL(PRE)); /* add prefix */
+ bufflen -= LL(PRE RETS POS) + 1; /* save space for prefix+suffix+'\0' */
+ if (srclen < bufflen && nl == NULL) { /* small one-line source? */
+ addstr(out, source, srclen); /* keep it */
+ }
+ else {
+ if (nl != NULL) srclen = nl - source; /* stop at first newline */
+ if (srclen > bufflen) srclen = bufflen;
+ addstr(out, source, srclen);
+ addstr(out, RETS, LL(RETS));
+ }
+ memcpy(out, POS, (LL(POS) + 1) * sizeof(char));
+ }
+}
+
diff --git a/src/libs/3rdparty/lua/src/lobject.h b/src/libs/3rdparty/lua/src/lobject.h
new file mode 100644
index 0000000000..556608e4aa
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lobject.h
@@ -0,0 +1,815 @@
+/*
+** $Id: lobject.h $
+** Type definitions for Lua objects
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lobject_h
+#define lobject_h
+
+
+#include <stdarg.h>
+
+
+#include "llimits.h"
+#include "lua.h"
+
+
+/*
+** Extra types for collectable non-values
+*/
+#define LUA_TUPVAL LUA_NUMTYPES /* upvalues */
+#define LUA_TPROTO (LUA_NUMTYPES+1) /* function prototypes */
+#define LUA_TDEADKEY (LUA_NUMTYPES+2) /* removed keys in tables */
+
+
+
+/*
+** number of all possible types (including LUA_TNONE but excluding DEADKEY)
+*/
+#define LUA_TOTALTYPES (LUA_TPROTO + 2)
+
+
+/*
+** tags for Tagged Values have the following use of bits:
+** bits 0-3: actual tag (a LUA_T* constant)
+** bits 4-5: variant bits
+** bit 6: whether value is collectable
+*/
+
+/* add variant bits to a type */
+#define makevariant(t,v) ((t) | ((v) << 4))
+
+
+
+/*
+** Union of all Lua values
+*/
+typedef union Value {
+ struct GCObject *gc; /* collectable objects */
+ void *p; /* light userdata */
+ lua_CFunction f; /* light C functions */
+ lua_Integer i; /* integer numbers */
+ lua_Number n; /* float numbers */
+ /* not used, but may avoid warnings for uninitialized value */
+ lu_byte ub;
+} Value;
+
+
+/*
+** Tagged Values. This is the basic representation of values in Lua:
+** an actual value plus a tag with its type.
+*/
+
+#define TValuefields Value value_; lu_byte tt_
+
+typedef struct TValue {
+ TValuefields;
+} TValue;
+
+
+#define val_(o) ((o)->value_)
+#define valraw(o) (val_(o))
+
+
+/* raw type tag of a TValue */
+#define rawtt(o) ((o)->tt_)
+
+/* tag with no variants (bits 0-3) */
+#define novariant(t) ((t) & 0x0F)
+
+/* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */
+#define withvariant(t) ((t) & 0x3F)
+#define ttypetag(o) withvariant(rawtt(o))
+
+/* type of a TValue */
+#define ttype(o) (novariant(rawtt(o)))
+
+
+/* Macros to test type */
+#define checktag(o,t) (rawtt(o) == (t))
+#define checktype(o,t) (ttype(o) == (t))
+
+
+/* Macros for internal tests */
+
+/* collectable object has the same tag as the original value */
+#define righttt(obj) (ttypetag(obj) == gcvalue(obj)->tt)
+
+/*
+** Any value being manipulated by the program either is non
+** collectable, or the collectable object has the right tag
+** and it is not dead. The option 'L == NULL' allows other
+** macros using this one to be used where L is not available.
+*/
+#define checkliveness(L,obj) \
+ ((void)L, lua_longassert(!iscollectable(obj) || \
+ (righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj))))))
+
+
+/* Macros to set values */
+
+/* set a value's tag */
+#define settt_(o,t) ((o)->tt_=(t))
+
+
+/* main macro to copy values (from 'obj2' to 'obj1') */
+#define setobj(L,obj1,obj2) \
+ { TValue *io1=(obj1); const TValue *io2=(obj2); \
+ io1->value_ = io2->value_; settt_(io1, io2->tt_); \
+ checkliveness(L,io1); lua_assert(!isnonstrictnil(io1)); }
+
+/*
+** Different types of assignments, according to source and destination.
+** (They are mostly equal now, but may be different in the future.)
+*/
+
+/* from stack to stack */
+#define setobjs2s(L,o1,o2) setobj(L,s2v(o1),s2v(o2))
+/* to stack (not from same stack) */
+#define setobj2s(L,o1,o2) setobj(L,s2v(o1),o2)
+/* from table to same table */
+#define setobjt2t setobj
+/* to new object */
+#define setobj2n setobj
+/* to table */
+#define setobj2t setobj
+
+
+/*
+** Entries in a Lua stack. Field 'tbclist' forms a list of all
+** to-be-closed variables active in this stack. Dummy entries are
+** used when the distance between two tbc variables does not fit
+** in an unsigned short. They are represented by delta==0, and
+** their real delta is always the maximum value that fits in
+** that field.
+*/
+typedef union StackValue {
+ TValue val;
+ struct {
+ TValuefields;
+ unsigned short delta;
+ } tbclist;
+} StackValue;
+
+
+/* index to stack elements */
+typedef StackValue *StkId;
+
+
+/*
+** When reallocating the stack, change all pointers to the stack into
+** proper offsets.
+*/
+typedef union {
+ StkId p; /* actual pointer */
+ ptrdiff_t offset; /* used while the stack is being reallocated */
+} StkIdRel;
+
+
+/* convert a 'StackValue' to a 'TValue' */
+#define s2v(o) (&(o)->val)
+
+
+
+/*
+** {==================================================================
+** Nil
+** ===================================================================
+*/
+
+/* Standard nil */
+#define LUA_VNIL makevariant(LUA_TNIL, 0)
+
+/* Empty slot (which might be different from a slot containing nil) */
+#define LUA_VEMPTY makevariant(LUA_TNIL, 1)
+
+/* Value returned for a key not found in a table (absent key) */
+#define LUA_VABSTKEY makevariant(LUA_TNIL, 2)
+
+
+/* macro to test for (any kind of) nil */
+#define ttisnil(v) checktype((v), LUA_TNIL)
+
+
+/* macro to test for a standard nil */
+#define ttisstrictnil(o) checktag((o), LUA_VNIL)
+
+
+#define setnilvalue(obj) settt_(obj, LUA_VNIL)
+
+
+#define isabstkey(v) checktag((v), LUA_VABSTKEY)
+
+
+/*
+** macro to detect non-standard nils (used only in assertions)
+*/
+#define isnonstrictnil(v) (ttisnil(v) && !ttisstrictnil(v))
+
+
+/*
+** By default, entries with any kind of nil are considered empty.
+** (In any definition, values associated with absent keys must also
+** be accepted as empty.)
+*/
+#define isempty(v) ttisnil(v)
+
+
+/* macro defining a value corresponding to an absent key */
+#define ABSTKEYCONSTANT {NULL}, LUA_VABSTKEY
+
+
+/* mark an entry as empty */
+#define setempty(v) settt_(v, LUA_VEMPTY)
+
+
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Booleans
+** ===================================================================
+*/
+
+
+#define LUA_VFALSE makevariant(LUA_TBOOLEAN, 0)
+#define LUA_VTRUE makevariant(LUA_TBOOLEAN, 1)
+
+#define ttisboolean(o) checktype((o), LUA_TBOOLEAN)
+#define ttisfalse(o) checktag((o), LUA_VFALSE)
+#define ttistrue(o) checktag((o), LUA_VTRUE)
+
+
+#define l_isfalse(o) (ttisfalse(o) || ttisnil(o))
+
+
+#define setbfvalue(obj) settt_(obj, LUA_VFALSE)
+#define setbtvalue(obj) settt_(obj, LUA_VTRUE)
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Threads
+** ===================================================================
+*/
+
+#define LUA_VTHREAD makevariant(LUA_TTHREAD, 0)
+
+#define ttisthread(o) checktag((o), ctb(LUA_VTHREAD))
+
+#define thvalue(o) check_exp(ttisthread(o), gco2th(val_(o).gc))
+
+#define setthvalue(L,obj,x) \
+ { TValue *io = (obj); lua_State *x_ = (x); \
+ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VTHREAD)); \
+ checkliveness(L,io); }
+
+#define setthvalue2s(L,o,t) setthvalue(L,s2v(o),t)
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Collectable Objects
+** ===================================================================
+*/
+
+/*
+** Common Header for all collectable objects (in macro form, to be
+** included in other objects)
+*/
+#define CommonHeader struct GCObject *next; lu_byte tt; lu_byte marked
+
+
+/* Common type for all collectable objects */
+typedef struct GCObject {
+ CommonHeader;
+} GCObject;
+
+
+/* Bit mark for collectable types */
+#define BIT_ISCOLLECTABLE (1 << 6)
+
+#define iscollectable(o) (rawtt(o) & BIT_ISCOLLECTABLE)
+
+/* mark a tag as collectable */
+#define ctb(t) ((t) | BIT_ISCOLLECTABLE)
+
+#define gcvalue(o) check_exp(iscollectable(o), val_(o).gc)
+
+#define gcvalueraw(v) ((v).gc)
+
+#define setgcovalue(L,obj,x) \
+ { TValue *io = (obj); GCObject *i_g=(x); \
+ val_(io).gc = i_g; settt_(io, ctb(i_g->tt)); }
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Numbers
+** ===================================================================
+*/
+
+/* Variant tags for numbers */
+#define LUA_VNUMINT makevariant(LUA_TNUMBER, 0) /* integer numbers */
+#define LUA_VNUMFLT makevariant(LUA_TNUMBER, 1) /* float numbers */
+
+#define ttisnumber(o) checktype((o), LUA_TNUMBER)
+#define ttisfloat(o) checktag((o), LUA_VNUMFLT)
+#define ttisinteger(o) checktag((o), LUA_VNUMINT)
+
+#define nvalue(o) check_exp(ttisnumber(o), \
+ (ttisinteger(o) ? cast_num(ivalue(o)) : fltvalue(o)))
+#define fltvalue(o) check_exp(ttisfloat(o), val_(o).n)
+#define ivalue(o) check_exp(ttisinteger(o), val_(o).i)
+
+#define fltvalueraw(v) ((v).n)
+#define ivalueraw(v) ((v).i)
+
+#define setfltvalue(obj,x) \
+ { TValue *io=(obj); val_(io).n=(x); settt_(io, LUA_VNUMFLT); }
+
+#define chgfltvalue(obj,x) \
+ { TValue *io=(obj); lua_assert(ttisfloat(io)); val_(io).n=(x); }
+
+#define setivalue(obj,x) \
+ { TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_VNUMINT); }
+
+#define chgivalue(obj,x) \
+ { TValue *io=(obj); lua_assert(ttisinteger(io)); val_(io).i=(x); }
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Strings
+** ===================================================================
+*/
+
+/* Variant tags for strings */
+#define LUA_VSHRSTR makevariant(LUA_TSTRING, 0) /* short strings */
+#define LUA_VLNGSTR makevariant(LUA_TSTRING, 1) /* long strings */
+
+#define ttisstring(o) checktype((o), LUA_TSTRING)
+#define ttisshrstring(o) checktag((o), ctb(LUA_VSHRSTR))
+#define ttislngstring(o) checktag((o), ctb(LUA_VLNGSTR))
+
+#define tsvalueraw(v) (gco2ts((v).gc))
+
+#define tsvalue(o) check_exp(ttisstring(o), gco2ts(val_(o).gc))
+
+#define setsvalue(L,obj,x) \
+ { TValue *io = (obj); TString *x_ = (x); \
+ val_(io).gc = obj2gco(x_); settt_(io, ctb(x_->tt)); \
+ checkliveness(L,io); }
+
+/* set a string to the stack */
+#define setsvalue2s(L,o,s) setsvalue(L,s2v(o),s)
+
+/* set a string to a new object */
+#define setsvalue2n setsvalue
+
+
+/*
+** Header for a string value.
+*/
+typedef struct TString {
+ CommonHeader;
+ lu_byte extra; /* reserved words for short strings; "has hash" for longs */
+ lu_byte shrlen; /* length for short strings */
+ unsigned int hash;
+ union {
+ size_t lnglen; /* length for long strings */
+ struct TString *hnext; /* linked list for hash table */
+ } u;
+ char contents[1];
+} TString;
+
+
+
+/*
+** Get the actual string (array of bytes) from a 'TString'.
+*/
+#define getstr(ts) ((ts)->contents)
+
+
+/* get the actual string (array of bytes) from a Lua value */
+#define svalue(o) getstr(tsvalue(o))
+
+/* get string length from 'TString *s' */
+#define tsslen(s) ((s)->tt == LUA_VSHRSTR ? (s)->shrlen : (s)->u.lnglen)
+
+/* get string length from 'TValue *o' */
+#define vslen(o) tsslen(tsvalue(o))
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Userdata
+** ===================================================================
+*/
+
+
+/*
+** Light userdata should be a variant of userdata, but for compatibility
+** reasons they are also different types.
+*/
+#define LUA_VLIGHTUSERDATA makevariant(LUA_TLIGHTUSERDATA, 0)
+
+#define LUA_VUSERDATA makevariant(LUA_TUSERDATA, 0)
+
+#define ttislightuserdata(o) checktag((o), LUA_VLIGHTUSERDATA)
+#define ttisfulluserdata(o) checktag((o), ctb(LUA_VUSERDATA))
+
+#define pvalue(o) check_exp(ttislightuserdata(o), val_(o).p)
+#define uvalue(o) check_exp(ttisfulluserdata(o), gco2u(val_(o).gc))
+
+#define pvalueraw(v) ((v).p)
+
+#define setpvalue(obj,x) \
+ { TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_VLIGHTUSERDATA); }
+
+#define setuvalue(L,obj,x) \
+ { TValue *io = (obj); Udata *x_ = (x); \
+ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VUSERDATA)); \
+ checkliveness(L,io); }
+
+
+/* Ensures that addresses after this type are always fully aligned. */
+typedef union UValue {
+ TValue uv;
+ LUAI_MAXALIGN; /* ensures maximum alignment for udata bytes */
+} UValue;
+
+
+/*
+** Header for userdata with user values;
+** memory area follows the end of this structure.
+*/
+typedef struct Udata {
+ CommonHeader;
+ unsigned short nuvalue; /* number of user values */
+ size_t len; /* number of bytes */
+ struct Table *metatable;
+ GCObject *gclist;
+ UValue uv[1]; /* user values */
+} Udata;
+
+
+/*
+** Header for userdata with no user values. These userdata do not need
+** to be gray during GC, and therefore do not need a 'gclist' field.
+** To simplify, the code always use 'Udata' for both kinds of userdata,
+** making sure it never accesses 'gclist' on userdata with no user values.
+** This structure here is used only to compute the correct size for
+** this representation. (The 'bindata' field in its end ensures correct
+** alignment for binary data following this header.)
+*/
+typedef struct Udata0 {
+ CommonHeader;
+ unsigned short nuvalue; /* number of user values */
+ size_t len; /* number of bytes */
+ struct Table *metatable;
+ union {LUAI_MAXALIGN;} bindata;
+} Udata0;
+
+
+/* compute the offset of the memory area of a userdata */
+#define udatamemoffset(nuv) \
+ ((nuv) == 0 ? offsetof(Udata0, bindata) \
+ : offsetof(Udata, uv) + (sizeof(UValue) * (nuv)))
+
+/* get the address of the memory block inside 'Udata' */
+#define getudatamem(u) (cast_charp(u) + udatamemoffset((u)->nuvalue))
+
+/* compute the size of a userdata */
+#define sizeudata(nuv,nb) (udatamemoffset(nuv) + (nb))
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Prototypes
+** ===================================================================
+*/
+
+#define LUA_VPROTO makevariant(LUA_TPROTO, 0)
+
+
+/*
+** Description of an upvalue for function prototypes
+*/
+typedef struct Upvaldesc {
+ TString *name; /* upvalue name (for debug information) */
+ lu_byte instack; /* whether it is in stack (register) */
+ lu_byte idx; /* index of upvalue (in stack or in outer function's list) */
+ lu_byte kind; /* kind of corresponding variable */
+} Upvaldesc;
+
+
+/*
+** Description of a local variable for function prototypes
+** (used for debug information)
+*/
+typedef struct LocVar {
+ TString *varname;
+ int startpc; /* first point where variable is active */
+ int endpc; /* first point where variable is dead */
+} LocVar;
+
+
+/*
+** Associates the absolute line source for a given instruction ('pc').
+** The array 'lineinfo' gives, for each instruction, the difference in
+** lines from the previous instruction. When that difference does not
+** fit into a byte, Lua saves the absolute line for that instruction.
+** (Lua also saves the absolute line periodically, to speed up the
+** computation of a line number: we can use binary search in the
+** absolute-line array, but we must traverse the 'lineinfo' array
+** linearly to compute a line.)
+*/
+typedef struct AbsLineInfo {
+ int pc;
+ int line;
+} AbsLineInfo;
+
+/*
+** Function Prototypes
+*/
+typedef struct Proto {
+ CommonHeader;
+ lu_byte numparams; /* number of fixed (named) parameters */
+ lu_byte is_vararg;
+ lu_byte maxstacksize; /* number of registers needed by this function */
+ int sizeupvalues; /* size of 'upvalues' */
+ int sizek; /* size of 'k' */
+ int sizecode;
+ int sizelineinfo;
+ int sizep; /* size of 'p' */
+ int sizelocvars;
+ int sizeabslineinfo; /* size of 'abslineinfo' */
+ int linedefined; /* debug information */
+ int lastlinedefined; /* debug information */
+ TValue *k; /* constants used by the function */
+ Instruction *code; /* opcodes */
+ struct Proto **p; /* functions defined inside the function */
+ Upvaldesc *upvalues; /* upvalue information */
+ ls_byte *lineinfo; /* information about source lines (debug information) */
+ AbsLineInfo *abslineinfo; /* idem */
+ LocVar *locvars; /* information about local variables (debug information) */
+ TString *source; /* used for debug information */
+ GCObject *gclist;
+} Proto;
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Functions
+** ===================================================================
+*/
+
+#define LUA_VUPVAL makevariant(LUA_TUPVAL, 0)
+
+
+/* Variant tags for functions */
+#define LUA_VLCL makevariant(LUA_TFUNCTION, 0) /* Lua closure */
+#define LUA_VLCF makevariant(LUA_TFUNCTION, 1) /* light C function */
+#define LUA_VCCL makevariant(LUA_TFUNCTION, 2) /* C closure */
+
+#define ttisfunction(o) checktype(o, LUA_TFUNCTION)
+#define ttisLclosure(o) checktag((o), ctb(LUA_VLCL))
+#define ttislcf(o) checktag((o), LUA_VLCF)
+#define ttisCclosure(o) checktag((o), ctb(LUA_VCCL))
+#define ttisclosure(o) (ttisLclosure(o) || ttisCclosure(o))
+
+
+#define isLfunction(o) ttisLclosure(o)
+
+#define clvalue(o) check_exp(ttisclosure(o), gco2cl(val_(o).gc))
+#define clLvalue(o) check_exp(ttisLclosure(o), gco2lcl(val_(o).gc))
+#define fvalue(o) check_exp(ttislcf(o), val_(o).f)
+#define clCvalue(o) check_exp(ttisCclosure(o), gco2ccl(val_(o).gc))
+
+#define fvalueraw(v) ((v).f)
+
+#define setclLvalue(L,obj,x) \
+ { TValue *io = (obj); LClosure *x_ = (x); \
+ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VLCL)); \
+ checkliveness(L,io); }
+
+#define setclLvalue2s(L,o,cl) setclLvalue(L,s2v(o),cl)
+
+#define setfvalue(obj,x) \
+ { TValue *io=(obj); val_(io).f=(x); settt_(io, LUA_VLCF); }
+
+#define setclCvalue(L,obj,x) \
+ { TValue *io = (obj); CClosure *x_ = (x); \
+ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VCCL)); \
+ checkliveness(L,io); }
+
+
+/*
+** Upvalues for Lua closures
+*/
+typedef struct UpVal {
+ CommonHeader;
+ union {
+ TValue *p; /* points to stack or to its own value */
+ ptrdiff_t offset; /* used while the stack is being reallocated */
+ } v;
+ union {
+ struct { /* (when open) */
+ struct UpVal *next; /* linked list */
+ struct UpVal **previous;
+ } open;
+ TValue value; /* the value (when closed) */
+ } u;
+} UpVal;
+
+
+
+#define ClosureHeader \
+ CommonHeader; lu_byte nupvalues; GCObject *gclist
+
+typedef struct CClosure {
+ ClosureHeader;
+ lua_CFunction f;
+ TValue upvalue[1]; /* list of upvalues */
+} CClosure;
+
+
+typedef struct LClosure {
+ ClosureHeader;
+ struct Proto *p;
+ UpVal *upvals[1]; /* list of upvalues */
+} LClosure;
+
+
+typedef union Closure {
+ CClosure c;
+ LClosure l;
+} Closure;
+
+
+#define getproto(o) (clLvalue(o)->p)
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Tables
+** ===================================================================
+*/
+
+#define LUA_VTABLE makevariant(LUA_TTABLE, 0)
+
+#define ttistable(o) checktag((o), ctb(LUA_VTABLE))
+
+#define hvalue(o) check_exp(ttistable(o), gco2t(val_(o).gc))
+
+#define sethvalue(L,obj,x) \
+ { TValue *io = (obj); Table *x_ = (x); \
+ val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_VTABLE)); \
+ checkliveness(L,io); }
+
+#define sethvalue2s(L,o,h) sethvalue(L,s2v(o),h)
+
+
+/*
+** Nodes for Hash tables: A pack of two TValue's (key-value pairs)
+** plus a 'next' field to link colliding entries. The distribution
+** of the key's fields ('key_tt' and 'key_val') not forming a proper
+** 'TValue' allows for a smaller size for 'Node' both in 4-byte
+** and 8-byte alignments.
+*/
+typedef union Node {
+ struct NodeKey {
+ TValuefields; /* fields for value */
+ lu_byte key_tt; /* key type */
+ int next; /* for chaining */
+ Value key_val; /* key value */
+ } u;
+ TValue i_val; /* direct access to node's value as a proper 'TValue' */
+} Node;
+
+
+/* copy a value into a key */
+#define setnodekey(L,node,obj) \
+ { Node *n_=(node); const TValue *io_=(obj); \
+ n_->u.key_val = io_->value_; n_->u.key_tt = io_->tt_; \
+ checkliveness(L,io_); }
+
+
+/* copy a value from a key */
+#define getnodekey(L,obj,node) \
+ { TValue *io_=(obj); const Node *n_=(node); \
+ io_->value_ = n_->u.key_val; io_->tt_ = n_->u.key_tt; \
+ checkliveness(L,io_); }
+
+
+/*
+** About 'alimit': if 'isrealasize(t)' is true, then 'alimit' is the
+** real size of 'array'. Otherwise, the real size of 'array' is the
+** smallest power of two not smaller than 'alimit' (or zero iff 'alimit'
+** is zero); 'alimit' is then used as a hint for #t.
+*/
+
+#define BITRAS (1 << 7)
+#define isrealasize(t) (!((t)->flags & BITRAS))
+#define setrealasize(t) ((t)->flags &= cast_byte(~BITRAS))
+#define setnorealasize(t) ((t)->flags |= BITRAS)
+
+
+typedef struct Table {
+ CommonHeader;
+ lu_byte flags; /* 1<<p means tagmethod(p) is not present */
+ lu_byte lsizenode; /* log2 of size of 'node' array */
+ unsigned int alimit; /* "limit" of 'array' array */
+ TValue *array; /* array part */
+ Node *node;
+ Node *lastfree; /* any free position is before this position */
+ struct Table *metatable;
+ GCObject *gclist;
+} Table;
+
+
+/*
+** Macros to manipulate keys inserted in nodes
+*/
+#define keytt(node) ((node)->u.key_tt)
+#define keyval(node) ((node)->u.key_val)
+
+#define keyisnil(node) (keytt(node) == LUA_TNIL)
+#define keyisinteger(node) (keytt(node) == LUA_VNUMINT)
+#define keyival(node) (keyval(node).i)
+#define keyisshrstr(node) (keytt(node) == ctb(LUA_VSHRSTR))
+#define keystrval(node) (gco2ts(keyval(node).gc))
+
+#define setnilkey(node) (keytt(node) = LUA_TNIL)
+
+#define keyiscollectable(n) (keytt(n) & BIT_ISCOLLECTABLE)
+
+#define gckey(n) (keyval(n).gc)
+#define gckeyN(n) (keyiscollectable(n) ? gckey(n) : NULL)
+
+
+/*
+** Dead keys in tables have the tag DEADKEY but keep their original
+** gcvalue. This distinguishes them from regular keys but allows them to
+** be found when searched in a special way. ('next' needs that to find
+** keys removed from a table during a traversal.)
+*/
+#define setdeadkey(node) (keytt(node) = LUA_TDEADKEY)
+#define keyisdead(node) (keytt(node) == LUA_TDEADKEY)
+
+/* }================================================================== */
+
+
+
+/*
+** 'module' operation for hashing (size is always a power of 2)
+*/
+#define lmod(s,size) \
+ (check_exp((size&(size-1))==0, (cast_int((s) & ((size)-1)))))
+
+
+#define twoto(x) (1<<(x))
+#define sizenode(t) (twoto((t)->lsizenode))
+
+
+/* size of buffer for 'luaO_utf8esc' function */
+#define UTF8BUFFSZ 8
+
+LUAI_FUNC int luaO_utf8esc (char *buff, unsigned long x);
+LUAI_FUNC int luaO_ceillog2 (unsigned int x);
+LUAI_FUNC int luaO_rawarith (lua_State *L, int op, const TValue *p1,
+ const TValue *p2, TValue *res);
+LUAI_FUNC void luaO_arith (lua_State *L, int op, const TValue *p1,
+ const TValue *p2, StkId res);
+LUAI_FUNC size_t luaO_str2num (const char *s, TValue *o);
+LUAI_FUNC int luaO_hexavalue (int c);
+LUAI_FUNC void luaO_tostring (lua_State *L, TValue *obj);
+LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt,
+ va_list argp);
+LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...);
+LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t srclen);
+
+
+#endif
+
diff --git a/src/libs/3rdparty/lua/src/lopcodes.c b/src/libs/3rdparty/lua/src/lopcodes.c
new file mode 100644
index 0000000000..c67aa227c5
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lopcodes.c
@@ -0,0 +1,104 @@
+/*
+** $Id: lopcodes.c $
+** Opcodes for Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#define lopcodes_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include "lopcodes.h"
+
+
+/* ORDER OP */
+
+LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
+/* MM OT IT T A mode opcode */
+ opmode(0, 0, 0, 0, 1, iABC) /* OP_MOVE */
+ ,opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADI */
+ ,opmode(0, 0, 0, 0, 1, iAsBx) /* OP_LOADF */
+ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADK */
+ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_LOADKX */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADFALSE */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LFALSESKIP */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADTRUE */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LOADNIL */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETUPVAL */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETUPVAL */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABUP */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETTABLE */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETI */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETFIELD */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABUP */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETTABLE */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETI */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_SETFIELD */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_NEWTABLE */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SELF */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDI */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADDK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUBK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MULK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MODK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_POWK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_DIVK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIVK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BANDK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BORK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXORK */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHRI */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHLI */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_ADD */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SUB */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MUL */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_MOD */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_POW */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_DIV */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_IDIV */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BAND */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BOR */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BXOR */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHL */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_SHR */
+ ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBIN */
+ ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINI*/
+ ,opmode(1, 0, 0, 0, 0, iABC) /* OP_MMBINK*/
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_UNM */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_BNOT */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_NOT */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_LEN */
+ ,opmode(0, 0, 0, 0, 1, iABC) /* OP_CONCAT */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_CLOSE */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_TBC */
+ ,opmode(0, 0, 0, 0, 0, isJ) /* OP_JMP */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQ */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LT */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LE */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQK */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_EQI */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LTI */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_LEI */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_GTI */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_GEI */
+ ,opmode(0, 0, 0, 1, 0, iABC) /* OP_TEST */
+ ,opmode(0, 0, 0, 1, 1, iABC) /* OP_TESTSET */
+ ,opmode(0, 1, 1, 0, 1, iABC) /* OP_CALL */
+ ,opmode(0, 1, 1, 0, 1, iABC) /* OP_TAILCALL */
+ ,opmode(0, 0, 1, 0, 0, iABC) /* OP_RETURN */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN0 */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_RETURN1 */
+ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_FORLOOP */
+ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_FORPREP */
+ ,opmode(0, 0, 0, 0, 0, iABx) /* OP_TFORPREP */
+ ,opmode(0, 0, 0, 0, 0, iABC) /* OP_TFORCALL */
+ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_TFORLOOP */
+ ,opmode(0, 0, 1, 0, 0, iABC) /* OP_SETLIST */
+ ,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */
+ ,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */
+ ,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */
+ ,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */
+};
+
diff --git a/src/libs/3rdparty/lua/src/lopcodes.h b/src/libs/3rdparty/lua/src/lopcodes.h
new file mode 100644
index 0000000000..4c55145399
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lopcodes.h
@@ -0,0 +1,405 @@
+/*
+** $Id: lopcodes.h $
+** Opcodes for Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lopcodes_h
+#define lopcodes_h
+
+#include "llimits.h"
+
+
+/*===========================================================================
+ We assume that instructions are unsigned 32-bit integers.
+ All instructions have an opcode in the first 7 bits.
+ Instructions can have the following formats:
+
+ 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
+ 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+iABC C(8) | B(8) |k| A(8) | Op(7) |
+iABx Bx(17) | A(8) | Op(7) |
+iAsBx sBx (signed)(17) | A(8) | Op(7) |
+iAx Ax(25) | Op(7) |
+isJ sJ (signed)(25) | Op(7) |
+
+ A signed argument is represented in excess K: the represented value is
+ the written unsigned value minus K, where K is half the maximum for the
+ corresponding unsigned argument.
+===========================================================================*/
+
+
+enum OpMode {iABC, iABx, iAsBx, iAx, isJ}; /* basic instruction formats */
+
+
+/*
+** size and position of opcode arguments.
+*/
+#define SIZE_C 8
+#define SIZE_B 8
+#define SIZE_Bx (SIZE_C + SIZE_B + 1)
+#define SIZE_A 8
+#define SIZE_Ax (SIZE_Bx + SIZE_A)
+#define SIZE_sJ (SIZE_Bx + SIZE_A)
+
+#define SIZE_OP 7
+
+#define POS_OP 0
+
+#define POS_A (POS_OP + SIZE_OP)
+#define POS_k (POS_A + SIZE_A)
+#define POS_B (POS_k + 1)
+#define POS_C (POS_B + SIZE_B)
+
+#define POS_Bx POS_k
+
+#define POS_Ax POS_A
+
+#define POS_sJ POS_A
+
+
+/*
+** limits for opcode arguments.
+** we use (signed) 'int' to manipulate most arguments,
+** so they must fit in ints.
+*/
+
+/* Check whether type 'int' has at least 'b' bits ('b' < 32) */
+#define L_INTHASBITS(b) ((UINT_MAX >> ((b) - 1)) >= 1)
+
+
+#if L_INTHASBITS(SIZE_Bx)
+#define MAXARG_Bx ((1<<SIZE_Bx)-1)
+#else
+#define MAXARG_Bx MAX_INT
+#endif
+
+#define OFFSET_sBx (MAXARG_Bx>>1) /* 'sBx' is signed */
+
+
+#if L_INTHASBITS(SIZE_Ax)
+#define MAXARG_Ax ((1<<SIZE_Ax)-1)
+#else
+#define MAXARG_Ax MAX_INT
+#endif
+
+#if L_INTHASBITS(SIZE_sJ)
+#define MAXARG_sJ ((1 << SIZE_sJ) - 1)
+#else
+#define MAXARG_sJ MAX_INT
+#endif
+
+#define OFFSET_sJ (MAXARG_sJ >> 1)
+
+
+#define MAXARG_A ((1<<SIZE_A)-1)
+#define MAXARG_B ((1<<SIZE_B)-1)
+#define MAXARG_C ((1<<SIZE_C)-1)
+#define OFFSET_sC (MAXARG_C >> 1)
+
+#define int2sC(i) ((i) + OFFSET_sC)
+#define sC2int(i) ((i) - OFFSET_sC)
+
+
+/* creates a mask with 'n' 1 bits at position 'p' */
+#define MASK1(n,p) ((~((~(Instruction)0)<<(n)))<<(p))
+
+/* creates a mask with 'n' 0 bits at position 'p' */
+#define MASK0(n,p) (~MASK1(n,p))
+
+/*
+** the following macros help to manipulate instructions
+*/
+
+#define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0)))
+#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \
+ ((cast(Instruction, o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))
+
+#define checkopm(i,m) (getOpMode(GET_OPCODE(i)) == m)
+
+
+#define getarg(i,pos,size) (cast_int(((i)>>(pos)) & MASK1(size,0)))
+#define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \
+ ((cast(Instruction, v)<<pos)&MASK1(size,pos))))
+
+#define GETARG_A(i) getarg(i, POS_A, SIZE_A)
+#define SETARG_A(i,v) setarg(i, v, POS_A, SIZE_A)
+
+#define GETARG_B(i) check_exp(checkopm(i, iABC), getarg(i, POS_B, SIZE_B))
+#define GETARG_sB(i) sC2int(GETARG_B(i))
+#define SETARG_B(i,v) setarg(i, v, POS_B, SIZE_B)
+
+#define GETARG_C(i) check_exp(checkopm(i, iABC), getarg(i, POS_C, SIZE_C))
+#define GETARG_sC(i) sC2int(GETARG_C(i))
+#define SETARG_C(i,v) setarg(i, v, POS_C, SIZE_C)
+
+#define TESTARG_k(i) check_exp(checkopm(i, iABC), (cast_int(((i) & (1u << POS_k)))))
+#define GETARG_k(i) check_exp(checkopm(i, iABC), getarg(i, POS_k, 1))
+#define SETARG_k(i,v) setarg(i, v, POS_k, 1)
+
+#define GETARG_Bx(i) check_exp(checkopm(i, iABx), getarg(i, POS_Bx, SIZE_Bx))
+#define SETARG_Bx(i,v) setarg(i, v, POS_Bx, SIZE_Bx)
+
+#define GETARG_Ax(i) check_exp(checkopm(i, iAx), getarg(i, POS_Ax, SIZE_Ax))
+#define SETARG_Ax(i,v) setarg(i, v, POS_Ax, SIZE_Ax)
+
+#define GETARG_sBx(i) \
+ check_exp(checkopm(i, iAsBx), getarg(i, POS_Bx, SIZE_Bx) - OFFSET_sBx)
+#define SETARG_sBx(i,b) SETARG_Bx((i),cast_uint((b)+OFFSET_sBx))
+
+#define GETARG_sJ(i) \
+ check_exp(checkopm(i, isJ), getarg(i, POS_sJ, SIZE_sJ) - OFFSET_sJ)
+#define SETARG_sJ(i,j) \
+ setarg(i, cast_uint((j)+OFFSET_sJ), POS_sJ, SIZE_sJ)
+
+
+#define CREATE_ABCk(o,a,b,c,k) ((cast(Instruction, o)<<POS_OP) \
+ | (cast(Instruction, a)<<POS_A) \
+ | (cast(Instruction, b)<<POS_B) \
+ | (cast(Instruction, c)<<POS_C) \
+ | (cast(Instruction, k)<<POS_k))
+
+#define CREATE_ABx(o,a,bc) ((cast(Instruction, o)<<POS_OP) \
+ | (cast(Instruction, a)<<POS_A) \
+ | (cast(Instruction, bc)<<POS_Bx))
+
+#define CREATE_Ax(o,a) ((cast(Instruction, o)<<POS_OP) \
+ | (cast(Instruction, a)<<POS_Ax))
+
+#define CREATE_sJ(o,j,k) ((cast(Instruction, o) << POS_OP) \
+ | (cast(Instruction, j) << POS_sJ) \
+ | (cast(Instruction, k) << POS_k))
+
+
+#if !defined(MAXINDEXRK) /* (for debugging only) */
+#define MAXINDEXRK MAXARG_B
+#endif
+
+
+/*
+** invalid register that fits in 8 bits
+*/
+#define NO_REG MAXARG_A
+
+
+/*
+** R[x] - register
+** K[x] - constant (in constant table)
+** RK(x) == if k(i) then K[x] else R[x]
+*/
+
+
+/*
+** Grep "ORDER OP" if you change these enums. Opcodes marked with a (*)
+** has extra descriptions in the notes after the enumeration.
+*/
+
+typedef enum {
+/*----------------------------------------------------------------------
+ name args description
+------------------------------------------------------------------------*/
+OP_MOVE,/* A B R[A] := R[B] */
+OP_LOADI,/* A sBx R[A] := sBx */
+OP_LOADF,/* A sBx R[A] := (lua_Number)sBx */
+OP_LOADK,/* A Bx R[A] := K[Bx] */
+OP_LOADKX,/* A R[A] := K[extra arg] */
+OP_LOADFALSE,/* A R[A] := false */
+OP_LFALSESKIP,/*A R[A] := false; pc++ (*) */
+OP_LOADTRUE,/* A R[A] := true */
+OP_LOADNIL,/* A B R[A], R[A+1], ..., R[A+B] := nil */
+OP_GETUPVAL,/* A B R[A] := UpValue[B] */
+OP_SETUPVAL,/* A B UpValue[B] := R[A] */
+
+OP_GETTABUP,/* A B C R[A] := UpValue[B][K[C]:string] */
+OP_GETTABLE,/* A B C R[A] := R[B][R[C]] */
+OP_GETI,/* A B C R[A] := R[B][C] */
+OP_GETFIELD,/* A B C R[A] := R[B][K[C]:string] */
+
+OP_SETTABUP,/* A B C UpValue[A][K[B]:string] := RK(C) */
+OP_SETTABLE,/* A B C R[A][R[B]] := RK(C) */
+OP_SETI,/* A B C R[A][B] := RK(C) */
+OP_SETFIELD,/* A B C R[A][K[B]:string] := RK(C) */
+
+OP_NEWTABLE,/* A B C k R[A] := {} */
+
+OP_SELF,/* A B C R[A+1] := R[B]; R[A] := R[B][RK(C):string] */
+
+OP_ADDI,/* A B sC R[A] := R[B] + sC */
+
+OP_ADDK,/* A B C R[A] := R[B] + K[C]:number */
+OP_SUBK,/* A B C R[A] := R[B] - K[C]:number */
+OP_MULK,/* A B C R[A] := R[B] * K[C]:number */
+OP_MODK,/* A B C R[A] := R[B] % K[C]:number */
+OP_POWK,/* A B C R[A] := R[B] ^ K[C]:number */
+OP_DIVK,/* A B C R[A] := R[B] / K[C]:number */
+OP_IDIVK,/* A B C R[A] := R[B] // K[C]:number */
+
+OP_BANDK,/* A B C R[A] := R[B] & K[C]:integer */
+OP_BORK,/* A B C R[A] := R[B] | K[C]:integer */
+OP_BXORK,/* A B C R[A] := R[B] ~ K[C]:integer */
+
+OP_SHRI,/* A B sC R[A] := R[B] >> sC */
+OP_SHLI,/* A B sC R[A] := sC << R[B] */
+
+OP_ADD,/* A B C R[A] := R[B] + R[C] */
+OP_SUB,/* A B C R[A] := R[B] - R[C] */
+OP_MUL,/* A B C R[A] := R[B] * R[C] */
+OP_MOD,/* A B C R[A] := R[B] % R[C] */
+OP_POW,/* A B C R[A] := R[B] ^ R[C] */
+OP_DIV,/* A B C R[A] := R[B] / R[C] */
+OP_IDIV,/* A B C R[A] := R[B] // R[C] */
+
+OP_BAND,/* A B C R[A] := R[B] & R[C] */
+OP_BOR,/* A B C R[A] := R[B] | R[C] */
+OP_BXOR,/* A B C R[A] := R[B] ~ R[C] */
+OP_SHL,/* A B C R[A] := R[B] << R[C] */
+OP_SHR,/* A B C R[A] := R[B] >> R[C] */
+
+OP_MMBIN,/* A B C call C metamethod over R[A] and R[B] (*) */
+OP_MMBINI,/* A sB C k call C metamethod over R[A] and sB */
+OP_MMBINK,/* A B C k call C metamethod over R[A] and K[B] */
+
+OP_UNM,/* A B R[A] := -R[B] */
+OP_BNOT,/* A B R[A] := ~R[B] */
+OP_NOT,/* A B R[A] := not R[B] */
+OP_LEN,/* A B R[A] := #R[B] (length operator) */
+
+OP_CONCAT,/* A B R[A] := R[A].. ... ..R[A + B - 1] */
+
+OP_CLOSE,/* A close all upvalues >= R[A] */
+OP_TBC,/* A mark variable A "to be closed" */
+OP_JMP,/* sJ pc += sJ */
+OP_EQ,/* A B k if ((R[A] == R[B]) ~= k) then pc++ */
+OP_LT,/* A B k if ((R[A] < R[B]) ~= k) then pc++ */
+OP_LE,/* A B k if ((R[A] <= R[B]) ~= k) then pc++ */
+
+OP_EQK,/* A B k if ((R[A] == K[B]) ~= k) then pc++ */
+OP_EQI,/* A sB k if ((R[A] == sB) ~= k) then pc++ */
+OP_LTI,/* A sB k if ((R[A] < sB) ~= k) then pc++ */
+OP_LEI,/* A sB k if ((R[A] <= sB) ~= k) then pc++ */
+OP_GTI,/* A sB k if ((R[A] > sB) ~= k) then pc++ */
+OP_GEI,/* A sB k if ((R[A] >= sB) ~= k) then pc++ */
+
+OP_TEST,/* A k if (not R[A] == k) then pc++ */
+OP_TESTSET,/* A B k if (not R[B] == k) then pc++ else R[A] := R[B] (*) */
+
+OP_CALL,/* A B C R[A], ... ,R[A+C-2] := R[A](R[A+1], ... ,R[A+B-1]) */
+OP_TAILCALL,/* A B C k return R[A](R[A+1], ... ,R[A+B-1]) */
+
+OP_RETURN,/* A B C k return R[A], ... ,R[A+B-2] (see note) */
+OP_RETURN0,/* return */
+OP_RETURN1,/* A return R[A] */
+
+OP_FORLOOP,/* A Bx update counters; if loop continues then pc-=Bx; */
+OP_FORPREP,/* A Bx <check values and prepare counters>;
+ if not to run then pc+=Bx+1; */
+
+OP_TFORPREP,/* A Bx create upvalue for R[A + 3]; pc+=Bx */
+OP_TFORCALL,/* A C R[A+4], ... ,R[A+3+C] := R[A](R[A+1], R[A+2]); */
+OP_TFORLOOP,/* A Bx if R[A+2] ~= nil then { R[A]=R[A+2]; pc -= Bx } */
+
+OP_SETLIST,/* A B C k R[A][C+i] := R[A+i], 1 <= i <= B */
+
+OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */
+
+OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */
+
+OP_VARARGPREP,/*A (adjust vararg parameters) */
+
+OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
+} OpCode;
+
+
+#define NUM_OPCODES ((int)(OP_EXTRAARG) + 1)
+
+
+
+/*===========================================================================
+ Notes:
+
+ (*) Opcode OP_LFALSESKIP is used to convert a condition to a boolean
+ value, in a code equivalent to (not cond ? false : true). (It
+ produces false and skips the next instruction producing true.)
+
+ (*) Opcodes OP_MMBIN and variants follow each arithmetic and
+ bitwise opcode. If the operation succeeds, it skips this next
+ opcode. Otherwise, this opcode calls the corresponding metamethod.
+
+ (*) Opcode OP_TESTSET is used in short-circuit expressions that need
+ both to jump and to produce a value, such as (a = b or c).
+
+ (*) In OP_CALL, if (B == 0) then B = top - A. If (C == 0), then
+ 'top' is set to last_result+1, so next open instruction (OP_CALL,
+ OP_RETURN*, OP_SETLIST) may use 'top'.
+
+ (*) In OP_VARARG, if (C == 0) then use actual number of varargs and
+ set top (like in OP_CALL with C == 0).
+
+ (*) In OP_RETURN, if (B == 0) then return up to 'top'.
+
+ (*) In OP_LOADKX and OP_NEWTABLE, the next instruction is always
+ OP_EXTRAARG.
+
+ (*) In OP_SETLIST, if (B == 0) then real B = 'top'; if k, then
+ real C = EXTRAARG _ C (the bits of EXTRAARG concatenated with the
+ bits of C).
+
+ (*) In OP_NEWTABLE, B is log2 of the hash size (which is always a
+ power of 2) plus 1, or zero for size zero. If not k, the array size
+ is C. Otherwise, the array size is EXTRAARG _ C.
+
+ (*) For comparisons, k specifies what condition the test should accept
+ (true or false).
+
+ (*) In OP_MMBINI/OP_MMBINK, k means the arguments were flipped
+ (the constant is the first operand).
+
+ (*) All 'skips' (pc++) assume that next instruction is a jump.
+
+ (*) In instructions OP_RETURN/OP_TAILCALL, 'k' specifies that the
+ function builds upvalues, which may need to be closed. C > 0 means
+ the function is vararg, so that its 'func' must be corrected before
+ returning; in this case, (C - 1) is its number of fixed parameters.
+
+ (*) In comparisons with an immediate operand, C signals whether the
+ original operand was a float. (It must be corrected in case of
+ metamethods.)
+
+===========================================================================*/
+
+
+/*
+** masks for instruction properties. The format is:
+** bits 0-2: op mode
+** bit 3: instruction set register A
+** bit 4: operator is a test (next instruction must be a jump)
+** bit 5: instruction uses 'L->top' set by previous instruction (when B == 0)
+** bit 6: instruction sets 'L->top' for next instruction (when C == 0)
+** bit 7: instruction is an MM instruction (call a metamethod)
+*/
+
+LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];)
+
+#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 7))
+#define testAMode(m) (luaP_opmodes[m] & (1 << 3))
+#define testTMode(m) (luaP_opmodes[m] & (1 << 4))
+#define testITMode(m) (luaP_opmodes[m] & (1 << 5))
+#define testOTMode(m) (luaP_opmodes[m] & (1 << 6))
+#define testMMMode(m) (luaP_opmodes[m] & (1 << 7))
+
+/* "out top" (set top for next instruction) */
+#define isOT(i) \
+ ((testOTMode(GET_OPCODE(i)) && GETARG_C(i) == 0) || \
+ GET_OPCODE(i) == OP_TAILCALL)
+
+/* "in top" (uses top from previous instruction) */
+#define isIT(i) (testITMode(GET_OPCODE(i)) && GETARG_B(i) == 0)
+
+#define opmode(mm,ot,it,t,a,m) \
+ (((mm) << 7) | ((ot) << 6) | ((it) << 5) | ((t) << 4) | ((a) << 3) | (m))
+
+
+/* number of list items to accumulate before a SETLIST instruction */
+#define LFIELDS_PER_FLUSH 50
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lopnames.h b/src/libs/3rdparty/lua/src/lopnames.h
new file mode 100644
index 0000000000..965cec9bf2
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lopnames.h
@@ -0,0 +1,103 @@
+/*
+** $Id: lopnames.h $
+** Opcode names
+** See Copyright Notice in lua.h
+*/
+
+#if !defined(lopnames_h)
+#define lopnames_h
+
+#include <stddef.h>
+
+
+/* ORDER OP */
+
+static const char *const opnames[] = {
+ "MOVE",
+ "LOADI",
+ "LOADF",
+ "LOADK",
+ "LOADKX",
+ "LOADFALSE",
+ "LFALSESKIP",
+ "LOADTRUE",
+ "LOADNIL",
+ "GETUPVAL",
+ "SETUPVAL",
+ "GETTABUP",
+ "GETTABLE",
+ "GETI",
+ "GETFIELD",
+ "SETTABUP",
+ "SETTABLE",
+ "SETI",
+ "SETFIELD",
+ "NEWTABLE",
+ "SELF",
+ "ADDI",
+ "ADDK",
+ "SUBK",
+ "MULK",
+ "MODK",
+ "POWK",
+ "DIVK",
+ "IDIVK",
+ "BANDK",
+ "BORK",
+ "BXORK",
+ "SHRI",
+ "SHLI",
+ "ADD",
+ "SUB",
+ "MUL",
+ "MOD",
+ "POW",
+ "DIV",
+ "IDIV",
+ "BAND",
+ "BOR",
+ "BXOR",
+ "SHL",
+ "SHR",
+ "MMBIN",
+ "MMBINI",
+ "MMBINK",
+ "UNM",
+ "BNOT",
+ "NOT",
+ "LEN",
+ "CONCAT",
+ "CLOSE",
+ "TBC",
+ "JMP",
+ "EQ",
+ "LT",
+ "LE",
+ "EQK",
+ "EQI",
+ "LTI",
+ "LEI",
+ "GTI",
+ "GEI",
+ "TEST",
+ "TESTSET",
+ "CALL",
+ "TAILCALL",
+ "RETURN",
+ "RETURN0",
+ "RETURN1",
+ "FORLOOP",
+ "FORPREP",
+ "TFORPREP",
+ "TFORCALL",
+ "TFORLOOP",
+ "SETLIST",
+ "CLOSURE",
+ "VARARG",
+ "VARARGPREP",
+ "EXTRAARG",
+ NULL
+};
+
+#endif
+
diff --git a/src/libs/3rdparty/lua/src/loslib.c b/src/libs/3rdparty/lua/src/loslib.c
new file mode 100644
index 0000000000..ad5a927688
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/loslib.c
@@ -0,0 +1,428 @@
+/*
+** $Id: loslib.c $
+** Standard Operating System library
+** See Copyright Notice in lua.h
+*/
+
+#define loslib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <errno.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+/*
+** {==================================================================
+** List of valid conversion specifiers for the 'strftime' function;
+** options are grouped by length; group of length 2 start with '||'.
+** ===================================================================
+*/
+#if !defined(LUA_STRFTIMEOPTIONS) /* { */
+
+#if defined(LUA_USE_WINDOWS)
+#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYzZ%" \
+ "||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */
+#elif defined(LUA_USE_C89) /* ANSI C 89 (only 1-char options) */
+#define LUA_STRFTIMEOPTIONS "aAbBcdHIjmMpSUwWxXyYZ%"
+#else /* C99 specification */
+#define LUA_STRFTIMEOPTIONS "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \
+ "||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */
+#endif
+
+#endif /* } */
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Configuration for time-related stuff
+** ===================================================================
+*/
+
+/*
+** type to represent time_t in Lua
+*/
+#if !defined(LUA_NUMTIME) /* { */
+
+#define l_timet lua_Integer
+#define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t))
+#define l_gettime(L,arg) luaL_checkinteger(L, arg)
+
+#else /* }{ */
+
+#define l_timet lua_Number
+#define l_pushtime(L,t) lua_pushnumber(L,(lua_Number)(t))
+#define l_gettime(L,arg) luaL_checknumber(L, arg)
+
+#endif /* } */
+
+
+#if !defined(l_gmtime) /* { */
+/*
+** By default, Lua uses gmtime/localtime, except when POSIX is available,
+** where it uses gmtime_r/localtime_r
+*/
+
+#if defined(LUA_USE_POSIX) /* { */
+
+#define l_gmtime(t,r) gmtime_r(t,r)
+#define l_localtime(t,r) localtime_r(t,r)
+
+#else /* }{ */
+
+/* ISO C definitions */
+#define l_gmtime(t,r) ((void)(r)->tm_sec, gmtime(t))
+#define l_localtime(t,r) ((void)(r)->tm_sec, localtime(t))
+
+#endif /* } */
+
+#endif /* } */
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Configuration for 'tmpnam':
+** By default, Lua uses tmpnam except when POSIX is available, where
+** it uses mkstemp.
+** ===================================================================
+*/
+#if !defined(lua_tmpnam) /* { */
+
+#if defined(LUA_USE_POSIX) /* { */
+
+#include <unistd.h>
+
+#define LUA_TMPNAMBUFSIZE 32
+
+#if !defined(LUA_TMPNAMTEMPLATE)
+#define LUA_TMPNAMTEMPLATE "/tmp/lua_XXXXXX"
+#endif
+
+#define lua_tmpnam(b,e) { \
+ strcpy(b, LUA_TMPNAMTEMPLATE); \
+ e = mkstemp(b); \
+ if (e != -1) close(e); \
+ e = (e == -1); }
+
+#else /* }{ */
+
+/* ISO C definitions */
+#define LUA_TMPNAMBUFSIZE L_tmpnam
+#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); }
+
+#endif /* } */
+
+#endif /* } */
+/* }================================================================== */
+
+
+#if !defined(l_system)
+#if defined(LUA_USE_IOS)
+/* Despite claiming to be ISO C, iOS does not implement 'system'. */
+#define l_system(cmd) ((cmd) == NULL ? 0 : -1)
+#else
+#define l_system(cmd) system(cmd) /* default definition */
+#endif
+#endif
+
+
+static int os_execute (lua_State *L) {
+ const char *cmd = luaL_optstring(L, 1, NULL);
+ int stat;
+ errno = 0;
+ stat = l_system(cmd);
+ if (cmd != NULL)
+ return luaL_execresult(L, stat);
+ else {
+ lua_pushboolean(L, stat); /* true if there is a shell */
+ return 1;
+ }
+}
+
+
+static int os_remove (lua_State *L) {
+ const char *filename = luaL_checkstring(L, 1);
+ return luaL_fileresult(L, remove(filename) == 0, filename);
+}
+
+
+static int os_rename (lua_State *L) {
+ const char *fromname = luaL_checkstring(L, 1);
+ const char *toname = luaL_checkstring(L, 2);
+ return luaL_fileresult(L, rename(fromname, toname) == 0, NULL);
+}
+
+
+static int os_tmpname (lua_State *L) {
+ char buff[LUA_TMPNAMBUFSIZE];
+ int err;
+ lua_tmpnam(buff, err);
+ if (l_unlikely(err))
+ return luaL_error(L, "unable to generate a unique filename");
+ lua_pushstring(L, buff);
+ return 1;
+}
+
+
+static int os_getenv (lua_State *L) {
+ lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */
+ return 1;
+}
+
+
+static int os_clock (lua_State *L) {
+ lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC);
+ return 1;
+}
+
+
+/*
+** {======================================================
+** Time/Date operations
+** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S,
+** wday=%w+1, yday=%j, isdst=? }
+** =======================================================
+*/
+
+/*
+** About the overflow check: an overflow cannot occur when time
+** is represented by a lua_Integer, because either lua_Integer is
+** large enough to represent all int fields or it is not large enough
+** to represent a time that cause a field to overflow. However, if
+** times are represented as doubles and lua_Integer is int, then the
+** time 0x1.e1853b0d184f6p+55 would cause an overflow when adding 1900
+** to compute the year.
+*/
+static void setfield (lua_State *L, const char *key, int value, int delta) {
+ #if (defined(LUA_NUMTIME) && LUA_MAXINTEGER <= INT_MAX)
+ if (l_unlikely(value > LUA_MAXINTEGER - delta))
+ luaL_error(L, "field '%s' is out-of-bound", key);
+ #endif
+ lua_pushinteger(L, (lua_Integer)value + delta);
+ lua_setfield(L, -2, key);
+}
+
+
+static void setboolfield (lua_State *L, const char *key, int value) {
+ if (value < 0) /* undefined? */
+ return; /* does not set field */
+ lua_pushboolean(L, value);
+ lua_setfield(L, -2, key);
+}
+
+
+/*
+** Set all fields from structure 'tm' in the table on top of the stack
+*/
+static void setallfields (lua_State *L, struct tm *stm) {
+ setfield(L, "year", stm->tm_year, 1900);
+ setfield(L, "month", stm->tm_mon, 1);
+ setfield(L, "day", stm->tm_mday, 0);
+ setfield(L, "hour", stm->tm_hour, 0);
+ setfield(L, "min", stm->tm_min, 0);
+ setfield(L, "sec", stm->tm_sec, 0);
+ setfield(L, "yday", stm->tm_yday, 1);
+ setfield(L, "wday", stm->tm_wday, 1);
+ setboolfield(L, "isdst", stm->tm_isdst);
+}
+
+
+static int getboolfield (lua_State *L, const char *key) {
+ int res;
+ res = (lua_getfield(L, -1, key) == LUA_TNIL) ? -1 : lua_toboolean(L, -1);
+ lua_pop(L, 1);
+ return res;
+}
+
+
+static int getfield (lua_State *L, const char *key, int d, int delta) {
+ int isnum;
+ int t = lua_getfield(L, -1, key); /* get field and its type */
+ lua_Integer res = lua_tointegerx(L, -1, &isnum);
+ if (!isnum) { /* field is not an integer? */
+ if (l_unlikely(t != LUA_TNIL)) /* some other value? */
+ return luaL_error(L, "field '%s' is not an integer", key);
+ else if (l_unlikely(d < 0)) /* absent field; no default? */
+ return luaL_error(L, "field '%s' missing in date table", key);
+ res = d;
+ }
+ else {
+ if (!(res >= 0 ? res - delta <= INT_MAX : INT_MIN + delta <= res))
+ return luaL_error(L, "field '%s' is out-of-bound", key);
+ res -= delta;
+ }
+ lua_pop(L, 1);
+ return (int)res;
+}
+
+
+static const char *checkoption (lua_State *L, const char *conv,
+ ptrdiff_t convlen, char *buff) {
+ const char *option = LUA_STRFTIMEOPTIONS;
+ int oplen = 1; /* length of options being checked */
+ for (; *option != '\0' && oplen <= convlen; option += oplen) {
+ if (*option == '|') /* next block? */
+ oplen++; /* will check options with next length (+1) */
+ else if (memcmp(conv, option, oplen) == 0) { /* match? */
+ memcpy(buff, conv, oplen); /* copy valid option to buffer */
+ buff[oplen] = '\0';
+ return conv + oplen; /* return next item */
+ }
+ }
+ luaL_argerror(L, 1,
+ lua_pushfstring(L, "invalid conversion specifier '%%%s'", conv));
+ return conv; /* to avoid warnings */
+}
+
+
+static time_t l_checktime (lua_State *L, int arg) {
+ l_timet t = l_gettime(L, arg);
+ luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds");
+ return (time_t)t;
+}
+
+
+/* maximum size for an individual 'strftime' item */
+#define SIZETIMEFMT 250
+
+
+static int os_date (lua_State *L) {
+ size_t slen;
+ const char *s = luaL_optlstring(L, 1, "%c", &slen);
+ time_t t = luaL_opt(L, l_checktime, 2, time(NULL));
+ const char *se = s + slen; /* 's' end */
+ struct tm tmr, *stm;
+ if (*s == '!') { /* UTC? */
+ stm = l_gmtime(&t, &tmr);
+ s++; /* skip '!' */
+ }
+ else
+ stm = l_localtime(&t, &tmr);
+ if (stm == NULL) /* invalid date? */
+ return luaL_error(L,
+ "date result cannot be represented in this installation");
+ if (strcmp(s, "*t") == 0) {
+ lua_createtable(L, 0, 9); /* 9 = number of fields */
+ setallfields(L, stm);
+ }
+ else {
+ char cc[4]; /* buffer for individual conversion specifiers */
+ luaL_Buffer b;
+ cc[0] = '%';
+ luaL_buffinit(L, &b);
+ while (s < se) {
+ if (*s != '%') /* not a conversion specifier? */
+ luaL_addchar(&b, *s++);
+ else {
+ size_t reslen;
+ char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT);
+ s++; /* skip '%' */
+ s = checkoption(L, s, se - s, cc + 1); /* copy specifier to 'cc' */
+ reslen = strftime(buff, SIZETIMEFMT, cc, stm);
+ luaL_addsize(&b, reslen);
+ }
+ }
+ luaL_pushresult(&b);
+ }
+ return 1;
+}
+
+
+static int os_time (lua_State *L) {
+ time_t t;
+ if (lua_isnoneornil(L, 1)) /* called without args? */
+ t = time(NULL); /* get current time */
+ else {
+ struct tm ts;
+ luaL_checktype(L, 1, LUA_TTABLE);
+ lua_settop(L, 1); /* make sure table is at the top */
+ ts.tm_year = getfield(L, "year", -1, 1900);
+ ts.tm_mon = getfield(L, "month", -1, 1);
+ ts.tm_mday = getfield(L, "day", -1, 0);
+ ts.tm_hour = getfield(L, "hour", 12, 0);
+ ts.tm_min = getfield(L, "min", 0, 0);
+ ts.tm_sec = getfield(L, "sec", 0, 0);
+ ts.tm_isdst = getboolfield(L, "isdst");
+ t = mktime(&ts);
+ setallfields(L, &ts); /* update fields with normalized values */
+ }
+ if (t != (time_t)(l_timet)t || t == (time_t)(-1))
+ return luaL_error(L,
+ "time result cannot be represented in this installation");
+ l_pushtime(L, t);
+ return 1;
+}
+
+
+static int os_difftime (lua_State *L) {
+ time_t t1 = l_checktime(L, 1);
+ time_t t2 = l_checktime(L, 2);
+ lua_pushnumber(L, (lua_Number)difftime(t1, t2));
+ return 1;
+}
+
+/* }====================================================== */
+
+
+static int os_setlocale (lua_State *L) {
+ static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY,
+ LC_NUMERIC, LC_TIME};
+ static const char *const catnames[] = {"all", "collate", "ctype", "monetary",
+ "numeric", "time", NULL};
+ const char *l = luaL_optstring(L, 1, NULL);
+ int op = luaL_checkoption(L, 2, "all", catnames);
+ lua_pushstring(L, setlocale(cat[op], l));
+ return 1;
+}
+
+
+static int os_exit (lua_State *L) {
+ int status;
+ if (lua_isboolean(L, 1))
+ status = (lua_toboolean(L, 1) ? EXIT_SUCCESS : EXIT_FAILURE);
+ else
+ status = (int)luaL_optinteger(L, 1, EXIT_SUCCESS);
+ if (lua_toboolean(L, 2))
+ lua_close(L);
+ if (L) exit(status); /* 'if' to avoid warnings for unreachable 'return' */
+ return 0;
+}
+
+
+static const luaL_Reg syslib[] = {
+ {"clock", os_clock},
+ {"date", os_date},
+ {"difftime", os_difftime},
+ {"execute", os_execute},
+ {"exit", os_exit},
+ {"getenv", os_getenv},
+ {"remove", os_remove},
+ {"rename", os_rename},
+ {"setlocale", os_setlocale},
+ {"time", os_time},
+ {"tmpname", os_tmpname},
+ {NULL, NULL}
+};
+
+/* }====================================================== */
+
+
+
+LUAMOD_API int luaopen_os (lua_State *L) {
+ luaL_newlib(L, syslib);
+ return 1;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lparser.c b/src/libs/3rdparty/lua/src/lparser.c
new file mode 100644
index 0000000000..b745f236f0
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lparser.c
@@ -0,0 +1,1967 @@
+/*
+** $Id: lparser.c $
+** Lua Parser
+** See Copyright Notice in lua.h
+*/
+
+#define lparser_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <limits.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lcode.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lparser.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+
+
+
+/* maximum number of local variables per function (must be smaller
+ than 250, due to the bytecode format) */
+#define MAXVARS 200
+
+
+#define hasmultret(k) ((k) == VCALL || (k) == VVARARG)
+
+
+/* because all strings are unified by the scanner, the parser
+ can use pointer equality for string equality */
+#define eqstr(a,b) ((a) == (b))
+
+
+/*
+** nodes for block list (list of active blocks)
+*/
+typedef struct BlockCnt {
+ struct BlockCnt *previous; /* chain */
+ int firstlabel; /* index of first label in this block */
+ int firstgoto; /* index of first pending goto in this block */
+ lu_byte nactvar; /* # active locals outside the block */
+ lu_byte upval; /* true if some variable in the block is an upvalue */
+ lu_byte isloop; /* true if 'block' is a loop */
+ lu_byte insidetbc; /* true if inside the scope of a to-be-closed var. */
+} BlockCnt;
+
+
+
+/*
+** prototypes for recursive non-terminal functions
+*/
+static void statement (LexState *ls);
+static void expr (LexState *ls, expdesc *v);
+
+
+static l_noret error_expected (LexState *ls, int token) {
+ luaX_syntaxerror(ls,
+ luaO_pushfstring(ls->L, "%s expected", luaX_token2str(ls, token)));
+}
+
+
+static l_noret errorlimit (FuncState *fs, int limit, const char *what) {
+ lua_State *L = fs->ls->L;
+ const char *msg;
+ int line = fs->f->linedefined;
+ const char *where = (line == 0)
+ ? "main function"
+ : luaO_pushfstring(L, "function at line %d", line);
+ msg = luaO_pushfstring(L, "too many %s (limit is %d) in %s",
+ what, limit, where);
+ luaX_syntaxerror(fs->ls, msg);
+}
+
+
+static void checklimit (FuncState *fs, int v, int l, const char *what) {
+ if (v > l) errorlimit(fs, l, what);
+}
+
+
+/*
+** Test whether next token is 'c'; if so, skip it.
+*/
+static int testnext (LexState *ls, int c) {
+ if (ls->t.token == c) {
+ luaX_next(ls);
+ return 1;
+ }
+ else return 0;
+}
+
+
+/*
+** Check that next token is 'c'.
+*/
+static void check (LexState *ls, int c) {
+ if (ls->t.token != c)
+ error_expected(ls, c);
+}
+
+
+/*
+** Check that next token is 'c' and skip it.
+*/
+static void checknext (LexState *ls, int c) {
+ check(ls, c);
+ luaX_next(ls);
+}
+
+
+#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); }
+
+
+/*
+** Check that next token is 'what' and skip it. In case of error,
+** raise an error that the expected 'what' should match a 'who'
+** in line 'where' (if that is not the current line).
+*/
+static void check_match (LexState *ls, int what, int who, int where) {
+ if (l_unlikely(!testnext(ls, what))) {
+ if (where == ls->linenumber) /* all in the same line? */
+ error_expected(ls, what); /* do not need a complex message */
+ else {
+ luaX_syntaxerror(ls, luaO_pushfstring(ls->L,
+ "%s expected (to close %s at line %d)",
+ luaX_token2str(ls, what), luaX_token2str(ls, who), where));
+ }
+ }
+}
+
+
+static TString *str_checkname (LexState *ls) {
+ TString *ts;
+ check(ls, TK_NAME);
+ ts = ls->t.seminfo.ts;
+ luaX_next(ls);
+ return ts;
+}
+
+
+static void init_exp (expdesc *e, expkind k, int i) {
+ e->f = e->t = NO_JUMP;
+ e->k = k;
+ e->u.info = i;
+}
+
+
+static void codestring (expdesc *e, TString *s) {
+ e->f = e->t = NO_JUMP;
+ e->k = VKSTR;
+ e->u.strval = s;
+}
+
+
+static void codename (LexState *ls, expdesc *e) {
+ codestring(e, str_checkname(ls));
+}
+
+
+/*
+** Register a new local variable in the active 'Proto' (for debug
+** information).
+*/
+static int registerlocalvar (LexState *ls, FuncState *fs, TString *varname) {
+ Proto *f = fs->f;
+ int oldsize = f->sizelocvars;
+ luaM_growvector(ls->L, f->locvars, fs->ndebugvars, f->sizelocvars,
+ LocVar, SHRT_MAX, "local variables");
+ while (oldsize < f->sizelocvars)
+ f->locvars[oldsize++].varname = NULL;
+ f->locvars[fs->ndebugvars].varname = varname;
+ f->locvars[fs->ndebugvars].startpc = fs->pc;
+ luaC_objbarrier(ls->L, f, varname);
+ return fs->ndebugvars++;
+}
+
+
+/*
+** Create a new local variable with the given 'name'. Return its index
+** in the function.
+*/
+static int new_localvar (LexState *ls, TString *name) {
+ lua_State *L = ls->L;
+ FuncState *fs = ls->fs;
+ Dyndata *dyd = ls->dyd;
+ Vardesc *var;
+ checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal,
+ MAXVARS, "local variables");
+ luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
+ dyd->actvar.size, Vardesc, USHRT_MAX, "local variables");
+ var = &dyd->actvar.arr[dyd->actvar.n++];
+ var->vd.kind = VDKREG; /* default */
+ var->vd.name = name;
+ return dyd->actvar.n - 1 - fs->firstlocal;
+}
+
+#define new_localvarliteral(ls,v) \
+ new_localvar(ls, \
+ luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char)) - 1));
+
+
+
+/*
+** Return the "variable description" (Vardesc) of a given variable.
+** (Unless noted otherwise, all variables are referred to by their
+** compiler indices.)
+*/
+static Vardesc *getlocalvardesc (FuncState *fs, int vidx) {
+ return &fs->ls->dyd->actvar.arr[fs->firstlocal + vidx];
+}
+
+
+/*
+** Convert 'nvar', a compiler index level, to its corresponding
+** register. For that, search for the highest variable below that level
+** that is in a register and uses its register index ('ridx') plus one.
+*/
+static int reglevel (FuncState *fs, int nvar) {
+ while (nvar-- > 0) {
+ Vardesc *vd = getlocalvardesc(fs, nvar); /* get previous variable */
+ if (vd->vd.kind != RDKCTC) /* is in a register? */
+ return vd->vd.ridx + 1;
+ }
+ return 0; /* no variables in registers */
+}
+
+
+/*
+** Return the number of variables in the register stack for the given
+** function.
+*/
+int luaY_nvarstack (FuncState *fs) {
+ return reglevel(fs, fs->nactvar);
+}
+
+
+/*
+** Get the debug-information entry for current variable 'vidx'.
+*/
+static LocVar *localdebuginfo (FuncState *fs, int vidx) {
+ Vardesc *vd = getlocalvardesc(fs, vidx);
+ if (vd->vd.kind == RDKCTC)
+ return NULL; /* no debug info. for constants */
+ else {
+ int idx = vd->vd.pidx;
+ lua_assert(idx < fs->ndebugvars);
+ return &fs->f->locvars[idx];
+ }
+}
+
+
+/*
+** Create an expression representing variable 'vidx'
+*/
+static void init_var (FuncState *fs, expdesc *e, int vidx) {
+ e->f = e->t = NO_JUMP;
+ e->k = VLOCAL;
+ e->u.var.vidx = vidx;
+ e->u.var.ridx = getlocalvardesc(fs, vidx)->vd.ridx;
+}
+
+
+/*
+** Raises an error if variable described by 'e' is read only
+*/
+static void check_readonly (LexState *ls, expdesc *e) {
+ FuncState *fs = ls->fs;
+ TString *varname = NULL; /* to be set if variable is const */
+ switch (e->k) {
+ case VCONST: {
+ varname = ls->dyd->actvar.arr[e->u.info].vd.name;
+ break;
+ }
+ case VLOCAL: {
+ Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx);
+ if (vardesc->vd.kind != VDKREG) /* not a regular variable? */
+ varname = vardesc->vd.name;
+ break;
+ }
+ case VUPVAL: {
+ Upvaldesc *up = &fs->f->upvalues[e->u.info];
+ if (up->kind != VDKREG)
+ varname = up->name;
+ break;
+ }
+ default:
+ return; /* other cases cannot be read-only */
+ }
+ if (varname) {
+ const char *msg = luaO_pushfstring(ls->L,
+ "attempt to assign to const variable '%s'", getstr(varname));
+ luaK_semerror(ls, msg); /* error */
+ }
+}
+
+
+/*
+** Start the scope for the last 'nvars' created variables.
+*/
+static void adjustlocalvars (LexState *ls, int nvars) {
+ FuncState *fs = ls->fs;
+ int reglevel = luaY_nvarstack(fs);
+ int i;
+ for (i = 0; i < nvars; i++) {
+ int vidx = fs->nactvar++;
+ Vardesc *var = getlocalvardesc(fs, vidx);
+ var->vd.ridx = reglevel++;
+ var->vd.pidx = registerlocalvar(ls, fs, var->vd.name);
+ }
+}
+
+
+/*
+** Close the scope for all variables up to level 'tolevel'.
+** (debug info.)
+*/
+static void removevars (FuncState *fs, int tolevel) {
+ fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel);
+ while (fs->nactvar > tolevel) {
+ LocVar *var = localdebuginfo(fs, --fs->nactvar);
+ if (var) /* does it have debug information? */
+ var->endpc = fs->pc;
+ }
+}
+
+
+/*
+** Search the upvalues of the function 'fs' for one
+** with the given 'name'.
+*/
+static int searchupvalue (FuncState *fs, TString *name) {
+ int i;
+ Upvaldesc *up = fs->f->upvalues;
+ for (i = 0; i < fs->nups; i++) {
+ if (eqstr(up[i].name, name)) return i;
+ }
+ return -1; /* not found */
+}
+
+
+static Upvaldesc *allocupvalue (FuncState *fs) {
+ Proto *f = fs->f;
+ int oldsize = f->sizeupvalues;
+ checklimit(fs, fs->nups + 1, MAXUPVAL, "upvalues");
+ luaM_growvector(fs->ls->L, f->upvalues, fs->nups, f->sizeupvalues,
+ Upvaldesc, MAXUPVAL, "upvalues");
+ while (oldsize < f->sizeupvalues)
+ f->upvalues[oldsize++].name = NULL;
+ return &f->upvalues[fs->nups++];
+}
+
+
+static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
+ Upvaldesc *up = allocupvalue(fs);
+ FuncState *prev = fs->prev;
+ if (v->k == VLOCAL) {
+ up->instack = 1;
+ up->idx = v->u.var.ridx;
+ up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind;
+ lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name));
+ }
+ else {
+ up->instack = 0;
+ up->idx = cast_byte(v->u.info);
+ up->kind = prev->f->upvalues[v->u.info].kind;
+ lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name));
+ }
+ up->name = name;
+ luaC_objbarrier(fs->ls->L, fs->f, name);
+ return fs->nups - 1;
+}
+
+
+/*
+** Look for an active local variable with the name 'n' in the
+** function 'fs'. If found, initialize 'var' with it and return
+** its expression kind; otherwise return -1.
+*/
+static int searchvar (FuncState *fs, TString *n, expdesc *var) {
+ int i;
+ for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) {
+ Vardesc *vd = getlocalvardesc(fs, i);
+ if (eqstr(n, vd->vd.name)) { /* found? */
+ if (vd->vd.kind == RDKCTC) /* compile-time constant? */
+ init_exp(var, VCONST, fs->firstlocal + i);
+ else /* real variable */
+ init_var(fs, var, i);
+ return var->k;
+ }
+ }
+ return -1; /* not found */
+}
+
+
+/*
+** Mark block where variable at given level was defined
+** (to emit close instructions later).
+*/
+static void markupval (FuncState *fs, int level) {
+ BlockCnt *bl = fs->bl;
+ while (bl->nactvar > level)
+ bl = bl->previous;
+ bl->upval = 1;
+ fs->needclose = 1;
+}
+
+
+/*
+** Mark that current block has a to-be-closed variable.
+*/
+static void marktobeclosed (FuncState *fs) {
+ BlockCnt *bl = fs->bl;
+ bl->upval = 1;
+ bl->insidetbc = 1;
+ fs->needclose = 1;
+}
+
+
+/*
+** Find a variable with the given name 'n'. If it is an upvalue, add
+** this upvalue into all intermediate functions. If it is a global, set
+** 'var' as 'void' as a flag.
+*/
+static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
+ if (fs == NULL) /* no more levels? */
+ init_exp(var, VVOID, 0); /* default is global */
+ else {
+ int v = searchvar(fs, n, var); /* look up locals at current level */
+ if (v >= 0) { /* found? */
+ if (v == VLOCAL && !base)
+ markupval(fs, var->u.var.vidx); /* local will be used as an upval */
+ }
+ else { /* not found as local at current level; try upvalues */
+ int idx = searchupvalue(fs, n); /* try existing upvalues */
+ if (idx < 0) { /* not found? */
+ singlevaraux(fs->prev, n, var, 0); /* try upper levels */
+ if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */
+ idx = newupvalue(fs, n, var); /* will be a new upvalue */
+ else /* it is a global or a constant */
+ return; /* don't need to do anything at this level */
+ }
+ init_exp(var, VUPVAL, idx); /* new or old upvalue */
+ }
+ }
+}
+
+
+/*
+** Find a variable with the given name 'n', handling global variables
+** too.
+*/
+static void singlevar (LexState *ls, expdesc *var) {
+ TString *varname = str_checkname(ls);
+ FuncState *fs = ls->fs;
+ singlevaraux(fs, varname, var, 1);
+ if (var->k == VVOID) { /* global name? */
+ expdesc key;
+ singlevaraux(fs, ls->envn, var, 1); /* get environment variable */
+ lua_assert(var->k != VVOID); /* this one must exist */
+ luaK_exp2anyregup(fs, var); /* but could be a constant */
+ codestring(&key, varname); /* key is variable name */
+ luaK_indexed(fs, var, &key); /* env[varname] */
+ }
+}
+
+
+/*
+** Adjust the number of results from an expression list 'e' with 'nexps'
+** expressions to 'nvars' values.
+*/
+static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
+ FuncState *fs = ls->fs;
+ int needed = nvars - nexps; /* extra values needed */
+ if (hasmultret(e->k)) { /* last expression has multiple returns? */
+ int extra = needed + 1; /* discount last expression itself */
+ if (extra < 0)
+ extra = 0;
+ luaK_setreturns(fs, e, extra); /* last exp. provides the difference */
+ }
+ else {
+ if (e->k != VVOID) /* at least one expression? */
+ luaK_exp2nextreg(fs, e); /* close last expression */
+ if (needed > 0) /* missing values? */
+ luaK_nil(fs, fs->freereg, needed); /* complete with nils */
+ }
+ if (needed > 0)
+ luaK_reserveregs(fs, needed); /* registers for extra values */
+ else /* adding 'needed' is actually a subtraction */
+ fs->freereg += needed; /* remove extra values */
+}
+
+
+#define enterlevel(ls) luaE_incCstack(ls->L)
+
+
+#define leavelevel(ls) ((ls)->L->nCcalls--)
+
+
+/*
+** Generates an error that a goto jumps into the scope of some
+** local variable.
+*/
+static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) {
+ const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->vd.name);
+ const char *msg = "<goto %s> at line %d jumps into the scope of local '%s'";
+ msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname);
+ luaK_semerror(ls, msg); /* raise the error */
+}
+
+
+/*
+** Solves the goto at index 'g' to given 'label' and removes it
+** from the list of pending gotos.
+** If it jumps into the scope of some variable, raises an error.
+*/
+static void solvegoto (LexState *ls, int g, Labeldesc *label) {
+ int i;
+ Labellist *gl = &ls->dyd->gt; /* list of gotos */
+ Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */
+ lua_assert(eqstr(gt->name, label->name));
+ if (l_unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */
+ jumpscopeerror(ls, gt);
+ luaK_patchlist(ls->fs, gt->pc, label->pc);
+ for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */
+ gl->arr[i] = gl->arr[i + 1];
+ gl->n--;
+}
+
+
+/*
+** Search for an active label with the given name.
+*/
+static Labeldesc *findlabel (LexState *ls, TString *name) {
+ int i;
+ Dyndata *dyd = ls->dyd;
+ /* check labels in current function for a match */
+ for (i = ls->fs->firstlabel; i < dyd->label.n; i++) {
+ Labeldesc *lb = &dyd->label.arr[i];
+ if (eqstr(lb->name, name)) /* correct label? */
+ return lb;
+ }
+ return NULL; /* label not found */
+}
+
+
+/*
+** Adds a new label/goto in the corresponding list.
+*/
+static int newlabelentry (LexState *ls, Labellist *l, TString *name,
+ int line, int pc) {
+ int n = l->n;
+ luaM_growvector(ls->L, l->arr, n, l->size,
+ Labeldesc, SHRT_MAX, "labels/gotos");
+ l->arr[n].name = name;
+ l->arr[n].line = line;
+ l->arr[n].nactvar = ls->fs->nactvar;
+ l->arr[n].close = 0;
+ l->arr[n].pc = pc;
+ l->n = n + 1;
+ return n;
+}
+
+
+static int newgotoentry (LexState *ls, TString *name, int line, int pc) {
+ return newlabelentry(ls, &ls->dyd->gt, name, line, pc);
+}
+
+
+/*
+** Solves forward jumps. Check whether new label 'lb' matches any
+** pending gotos in current block and solves them. Return true
+** if any of the gotos need to close upvalues.
+*/
+static int solvegotos (LexState *ls, Labeldesc *lb) {
+ Labellist *gl = &ls->dyd->gt;
+ int i = ls->fs->bl->firstgoto;
+ int needsclose = 0;
+ while (i < gl->n) {
+ if (eqstr(gl->arr[i].name, lb->name)) {
+ needsclose |= gl->arr[i].close;
+ solvegoto(ls, i, lb); /* will remove 'i' from the list */
+ }
+ else
+ i++;
+ }
+ return needsclose;
+}
+
+
+/*
+** Create a new label with the given 'name' at the given 'line'.
+** 'last' tells whether label is the last non-op statement in its
+** block. Solves all pending gotos to this new label and adds
+** a close instruction if necessary.
+** Returns true iff it added a close instruction.
+*/
+static int createlabel (LexState *ls, TString *name, int line,
+ int last) {
+ FuncState *fs = ls->fs;
+ Labellist *ll = &ls->dyd->label;
+ int l = newlabelentry(ls, ll, name, line, luaK_getlabel(fs));
+ if (last) { /* label is last no-op statement in the block? */
+ /* assume that locals are already out of scope */
+ ll->arr[l].nactvar = fs->bl->nactvar;
+ }
+ if (solvegotos(ls, &ll->arr[l])) { /* need close? */
+ luaK_codeABC(fs, OP_CLOSE, luaY_nvarstack(fs), 0, 0);
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+** Adjust pending gotos to outer level of a block.
+*/
+static void movegotosout (FuncState *fs, BlockCnt *bl) {
+ int i;
+ Labellist *gl = &fs->ls->dyd->gt;
+ /* correct pending gotos to current block */
+ for (i = bl->firstgoto; i < gl->n; i++) { /* for each pending goto */
+ Labeldesc *gt = &gl->arr[i];
+ /* leaving a variable scope? */
+ if (reglevel(fs, gt->nactvar) > reglevel(fs, bl->nactvar))
+ gt->close |= bl->upval; /* jump may need a close */
+ gt->nactvar = bl->nactvar; /* update goto level */
+ }
+}
+
+
+static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) {
+ bl->isloop = isloop;
+ bl->nactvar = fs->nactvar;
+ bl->firstlabel = fs->ls->dyd->label.n;
+ bl->firstgoto = fs->ls->dyd->gt.n;
+ bl->upval = 0;
+ bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc);
+ bl->previous = fs->bl;
+ fs->bl = bl;
+ lua_assert(fs->freereg == luaY_nvarstack(fs));
+}
+
+
+/*
+** generates an error for an undefined 'goto'.
+*/
+static l_noret undefgoto (LexState *ls, Labeldesc *gt) {
+ const char *msg;
+ if (eqstr(gt->name, luaS_newliteral(ls->L, "break"))) {
+ msg = "break outside loop at line %d";
+ msg = luaO_pushfstring(ls->L, msg, gt->line);
+ }
+ else {
+ msg = "no visible label '%s' for <goto> at line %d";
+ msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line);
+ }
+ luaK_semerror(ls, msg);
+}
+
+
+static void leaveblock (FuncState *fs) {
+ BlockCnt *bl = fs->bl;
+ LexState *ls = fs->ls;
+ int hasclose = 0;
+ int stklevel = reglevel(fs, bl->nactvar); /* level outside the block */
+ removevars(fs, bl->nactvar); /* remove block locals */
+ lua_assert(bl->nactvar == fs->nactvar); /* back to level on entry */
+ if (bl->isloop) /* has to fix pending breaks? */
+ hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0);
+ if (!hasclose && bl->previous && bl->upval) /* still need a 'close'? */
+ luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0);
+ fs->freereg = stklevel; /* free registers */
+ ls->dyd->label.n = bl->firstlabel; /* remove local labels */
+ fs->bl = bl->previous; /* current block now is previous one */
+ if (bl->previous) /* was it a nested block? */
+ movegotosout(fs, bl); /* update pending gotos to enclosing block */
+ else {
+ if (bl->firstgoto < ls->dyd->gt.n) /* still pending gotos? */
+ undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */
+ }
+}
+
+
+/*
+** adds a new prototype into list of prototypes
+*/
+static Proto *addprototype (LexState *ls) {
+ Proto *clp;
+ lua_State *L = ls->L;
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f; /* prototype of current function */
+ if (fs->np >= f->sizep) {
+ int oldsize = f->sizep;
+ luaM_growvector(L, f->p, fs->np, f->sizep, Proto *, MAXARG_Bx, "functions");
+ while (oldsize < f->sizep)
+ f->p[oldsize++] = NULL;
+ }
+ f->p[fs->np++] = clp = luaF_newproto(L);
+ luaC_objbarrier(L, f, clp);
+ return clp;
+}
+
+
+/*
+** codes instruction to create new closure in parent function.
+** The OP_CLOSURE instruction uses the last available register,
+** so that, if it invokes the GC, the GC knows which registers
+** are in use at that time.
+
+*/
+static void codeclosure (LexState *ls, expdesc *v) {
+ FuncState *fs = ls->fs->prev;
+ init_exp(v, VRELOC, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np - 1));
+ luaK_exp2nextreg(fs, v); /* fix it at the last register */
+}
+
+
+static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) {
+ Proto *f = fs->f;
+ fs->prev = ls->fs; /* linked list of funcstates */
+ fs->ls = ls;
+ ls->fs = fs;
+ fs->pc = 0;
+ fs->previousline = f->linedefined;
+ fs->iwthabs = 0;
+ fs->lasttarget = 0;
+ fs->freereg = 0;
+ fs->nk = 0;
+ fs->nabslineinfo = 0;
+ fs->np = 0;
+ fs->nups = 0;
+ fs->ndebugvars = 0;
+ fs->nactvar = 0;
+ fs->needclose = 0;
+ fs->firstlocal = ls->dyd->actvar.n;
+ fs->firstlabel = ls->dyd->label.n;
+ fs->bl = NULL;
+ f->source = ls->source;
+ luaC_objbarrier(ls->L, f, f->source);
+ f->maxstacksize = 2; /* registers 0/1 are always valid */
+ enterblock(fs, bl, 0);
+}
+
+
+static void close_func (LexState *ls) {
+ lua_State *L = ls->L;
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ luaK_ret(fs, luaY_nvarstack(fs), 0); /* final return */
+ leaveblock(fs);
+ lua_assert(fs->bl == NULL);
+ luaK_finish(fs);
+ luaM_shrinkvector(L, f->code, f->sizecode, fs->pc, Instruction);
+ luaM_shrinkvector(L, f->lineinfo, f->sizelineinfo, fs->pc, ls_byte);
+ luaM_shrinkvector(L, f->abslineinfo, f->sizeabslineinfo,
+ fs->nabslineinfo, AbsLineInfo);
+ luaM_shrinkvector(L, f->k, f->sizek, fs->nk, TValue);
+ luaM_shrinkvector(L, f->p, f->sizep, fs->np, Proto *);
+ luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->ndebugvars, LocVar);
+ luaM_shrinkvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc);
+ ls->fs = fs->prev;
+ luaC_checkGC(L);
+}
+
+
+
+/*============================================================*/
+/* GRAMMAR RULES */
+/*============================================================*/
+
+
+/*
+** check whether current token is in the follow set of a block.
+** 'until' closes syntactical blocks, but do not close scope,
+** so it is handled in separate.
+*/
+static int block_follow (LexState *ls, int withuntil) {
+ switch (ls->t.token) {
+ case TK_ELSE: case TK_ELSEIF:
+ case TK_END: case TK_EOS:
+ return 1;
+ case TK_UNTIL: return withuntil;
+ default: return 0;
+ }
+}
+
+
+static void statlist (LexState *ls) {
+ /* statlist -> { stat [';'] } */
+ while (!block_follow(ls, 1)) {
+ if (ls->t.token == TK_RETURN) {
+ statement(ls);
+ return; /* 'return' must be last statement */
+ }
+ statement(ls);
+ }
+}
+
+
+static void fieldsel (LexState *ls, expdesc *v) {
+ /* fieldsel -> ['.' | ':'] NAME */
+ FuncState *fs = ls->fs;
+ expdesc key;
+ luaK_exp2anyregup(fs, v);
+ luaX_next(ls); /* skip the dot or colon */
+ codename(ls, &key);
+ luaK_indexed(fs, v, &key);
+}
+
+
+static void yindex (LexState *ls, expdesc *v) {
+ /* index -> '[' expr ']' */
+ luaX_next(ls); /* skip the '[' */
+ expr(ls, v);
+ luaK_exp2val(ls->fs, v);
+ checknext(ls, ']');
+}
+
+
+/*
+** {======================================================================
+** Rules for Constructors
+** =======================================================================
+*/
+
+
+typedef struct ConsControl {
+ expdesc v; /* last list item read */
+ expdesc *t; /* table descriptor */
+ int nh; /* total number of 'record' elements */
+ int na; /* number of array elements already stored */
+ int tostore; /* number of array elements pending to be stored */
+} ConsControl;
+
+
+static void recfield (LexState *ls, ConsControl *cc) {
+ /* recfield -> (NAME | '['exp']') = exp */
+ FuncState *fs = ls->fs;
+ int reg = ls->fs->freereg;
+ expdesc tab, key, val;
+ if (ls->t.token == TK_NAME) {
+ checklimit(fs, cc->nh, MAX_INT, "items in a constructor");
+ codename(ls, &key);
+ }
+ else /* ls->t.token == '[' */
+ yindex(ls, &key);
+ cc->nh++;
+ checknext(ls, '=');
+ tab = *cc->t;
+ luaK_indexed(fs, &tab, &key);
+ expr(ls, &val);
+ luaK_storevar(fs, &tab, &val);
+ fs->freereg = reg; /* free registers */
+}
+
+
+static void closelistfield (FuncState *fs, ConsControl *cc) {
+ if (cc->v.k == VVOID) return; /* there is no list item */
+ luaK_exp2nextreg(fs, &cc->v);
+ cc->v.k = VVOID;
+ if (cc->tostore == LFIELDS_PER_FLUSH) {
+ luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); /* flush */
+ cc->na += cc->tostore;
+ cc->tostore = 0; /* no more items pending */
+ }
+}
+
+
+static void lastlistfield (FuncState *fs, ConsControl *cc) {
+ if (cc->tostore == 0) return;
+ if (hasmultret(cc->v.k)) {
+ luaK_setmultret(fs, &cc->v);
+ luaK_setlist(fs, cc->t->u.info, cc->na, LUA_MULTRET);
+ cc->na--; /* do not count last expression (unknown number of elements) */
+ }
+ else {
+ if (cc->v.k != VVOID)
+ luaK_exp2nextreg(fs, &cc->v);
+ luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore);
+ }
+ cc->na += cc->tostore;
+}
+
+
+static void listfield (LexState *ls, ConsControl *cc) {
+ /* listfield -> exp */
+ expr(ls, &cc->v);
+ cc->tostore++;
+}
+
+
+static void field (LexState *ls, ConsControl *cc) {
+ /* field -> listfield | recfield */
+ switch(ls->t.token) {
+ case TK_NAME: { /* may be 'listfield' or 'recfield' */
+ if (luaX_lookahead(ls) != '=') /* expression? */
+ listfield(ls, cc);
+ else
+ recfield(ls, cc);
+ break;
+ }
+ case '[': {
+ recfield(ls, cc);
+ break;
+ }
+ default: {
+ listfield(ls, cc);
+ break;
+ }
+ }
+}
+
+
+static void constructor (LexState *ls, expdesc *t) {
+ /* constructor -> '{' [ field { sep field } [sep] ] '}'
+ sep -> ',' | ';' */
+ FuncState *fs = ls->fs;
+ int line = ls->linenumber;
+ int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0);
+ ConsControl cc;
+ luaK_code(fs, 0); /* space for extra arg. */
+ cc.na = cc.nh = cc.tostore = 0;
+ cc.t = t;
+ init_exp(t, VNONRELOC, fs->freereg); /* table will be at stack top */
+ luaK_reserveregs(fs, 1);
+ init_exp(&cc.v, VVOID, 0); /* no value (yet) */
+ checknext(ls, '{');
+ do {
+ lua_assert(cc.v.k == VVOID || cc.tostore > 0);
+ if (ls->t.token == '}') break;
+ closelistfield(fs, &cc);
+ field(ls, &cc);
+ } while (testnext(ls, ',') || testnext(ls, ';'));
+ check_match(ls, '}', '{', line);
+ lastlistfield(fs, &cc);
+ luaK_settablesize(fs, pc, t->u.info, cc.na, cc.nh);
+}
+
+/* }====================================================================== */
+
+
+static void setvararg (FuncState *fs, int nparams) {
+ fs->f->is_vararg = 1;
+ luaK_codeABC(fs, OP_VARARGPREP, nparams, 0, 0);
+}
+
+
+static void parlist (LexState *ls) {
+ /* parlist -> [ {NAME ','} (NAME | '...') ] */
+ FuncState *fs = ls->fs;
+ Proto *f = fs->f;
+ int nparams = 0;
+ int isvararg = 0;
+ if (ls->t.token != ')') { /* is 'parlist' not empty? */
+ do {
+ switch (ls->t.token) {
+ case TK_NAME: {
+ new_localvar(ls, str_checkname(ls));
+ nparams++;
+ break;
+ }
+ case TK_DOTS: {
+ luaX_next(ls);
+ isvararg = 1;
+ break;
+ }
+ default: luaX_syntaxerror(ls, "<name> or '...' expected");
+ }
+ } while (!isvararg && testnext(ls, ','));
+ }
+ adjustlocalvars(ls, nparams);
+ f->numparams = cast_byte(fs->nactvar);
+ if (isvararg)
+ setvararg(fs, f->numparams); /* declared vararg */
+ luaK_reserveregs(fs, fs->nactvar); /* reserve registers for parameters */
+}
+
+
+static void body (LexState *ls, expdesc *e, int ismethod, int line) {
+ /* body -> '(' parlist ')' block END */
+ FuncState new_fs;
+ BlockCnt bl;
+ new_fs.f = addprototype(ls);
+ new_fs.f->linedefined = line;
+ open_func(ls, &new_fs, &bl);
+ checknext(ls, '(');
+ if (ismethod) {
+ new_localvarliteral(ls, "self"); /* create 'self' parameter */
+ adjustlocalvars(ls, 1);
+ }
+ parlist(ls);
+ checknext(ls, ')');
+ statlist(ls);
+ new_fs.f->lastlinedefined = ls->linenumber;
+ check_match(ls, TK_END, TK_FUNCTION, line);
+ codeclosure(ls, e);
+ close_func(ls);
+}
+
+
+static int explist (LexState *ls, expdesc *v) {
+ /* explist -> expr { ',' expr } */
+ int n = 1; /* at least one expression */
+ expr(ls, v);
+ while (testnext(ls, ',')) {
+ luaK_exp2nextreg(ls->fs, v);
+ expr(ls, v);
+ n++;
+ }
+ return n;
+}
+
+
+static void funcargs (LexState *ls, expdesc *f, int line) {
+ FuncState *fs = ls->fs;
+ expdesc args;
+ int base, nparams;
+ switch (ls->t.token) {
+ case '(': { /* funcargs -> '(' [ explist ] ')' */
+ luaX_next(ls);
+ if (ls->t.token == ')') /* arg list is empty? */
+ args.k = VVOID;
+ else {
+ explist(ls, &args);
+ if (hasmultret(args.k))
+ luaK_setmultret(fs, &args);
+ }
+ check_match(ls, ')', '(', line);
+ break;
+ }
+ case '{': { /* funcargs -> constructor */
+ constructor(ls, &args);
+ break;
+ }
+ case TK_STRING: { /* funcargs -> STRING */
+ codestring(&args, ls->t.seminfo.ts);
+ luaX_next(ls); /* must use 'seminfo' before 'next' */
+ break;
+ }
+ default: {
+ luaX_syntaxerror(ls, "function arguments expected");
+ }
+ }
+ lua_assert(f->k == VNONRELOC);
+ base = f->u.info; /* base register for call */
+ if (hasmultret(args.k))
+ nparams = LUA_MULTRET; /* open call */
+ else {
+ if (args.k != VVOID)
+ luaK_exp2nextreg(fs, &args); /* close last argument */
+ nparams = fs->freereg - (base+1);
+ }
+ init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2));
+ luaK_fixline(fs, line);
+ fs->freereg = base+1; /* call remove function and arguments and leaves
+ (unless changed) one result */
+}
+
+
+
+
+/*
+** {======================================================================
+** Expression parsing
+** =======================================================================
+*/
+
+
+static void primaryexp (LexState *ls, expdesc *v) {
+ /* primaryexp -> NAME | '(' expr ')' */
+ switch (ls->t.token) {
+ case '(': {
+ int line = ls->linenumber;
+ luaX_next(ls);
+ expr(ls, v);
+ check_match(ls, ')', '(', line);
+ luaK_dischargevars(ls->fs, v);
+ return;
+ }
+ case TK_NAME: {
+ singlevar(ls, v);
+ return;
+ }
+ default: {
+ luaX_syntaxerror(ls, "unexpected symbol");
+ }
+ }
+}
+
+
+static void suffixedexp (LexState *ls, expdesc *v) {
+ /* suffixedexp ->
+ primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */
+ FuncState *fs = ls->fs;
+ int line = ls->linenumber;
+ primaryexp(ls, v);
+ for (;;) {
+ switch (ls->t.token) {
+ case '.': { /* fieldsel */
+ fieldsel(ls, v);
+ break;
+ }
+ case '[': { /* '[' exp ']' */
+ expdesc key;
+ luaK_exp2anyregup(fs, v);
+ yindex(ls, &key);
+ luaK_indexed(fs, v, &key);
+ break;
+ }
+ case ':': { /* ':' NAME funcargs */
+ expdesc key;
+ luaX_next(ls);
+ codename(ls, &key);
+ luaK_self(fs, v, &key);
+ funcargs(ls, v, line);
+ break;
+ }
+ case '(': case TK_STRING: case '{': { /* funcargs */
+ luaK_exp2nextreg(fs, v);
+ funcargs(ls, v, line);
+ break;
+ }
+ default: return;
+ }
+ }
+}
+
+
+static void simpleexp (LexState *ls, expdesc *v) {
+ /* simpleexp -> FLT | INT | STRING | NIL | TRUE | FALSE | ... |
+ constructor | FUNCTION body | suffixedexp */
+ switch (ls->t.token) {
+ case TK_FLT: {
+ init_exp(v, VKFLT, 0);
+ v->u.nval = ls->t.seminfo.r;
+ break;
+ }
+ case TK_INT: {
+ init_exp(v, VKINT, 0);
+ v->u.ival = ls->t.seminfo.i;
+ break;
+ }
+ case TK_STRING: {
+ codestring(v, ls->t.seminfo.ts);
+ break;
+ }
+ case TK_NIL: {
+ init_exp(v, VNIL, 0);
+ break;
+ }
+ case TK_TRUE: {
+ init_exp(v, VTRUE, 0);
+ break;
+ }
+ case TK_FALSE: {
+ init_exp(v, VFALSE, 0);
+ break;
+ }
+ case TK_DOTS: { /* vararg */
+ FuncState *fs = ls->fs;
+ check_condition(ls, fs->f->is_vararg,
+ "cannot use '...' outside a vararg function");
+ init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 0, 1));
+ break;
+ }
+ case '{': { /* constructor */
+ constructor(ls, v);
+ return;
+ }
+ case TK_FUNCTION: {
+ luaX_next(ls);
+ body(ls, v, 0, ls->linenumber);
+ return;
+ }
+ default: {
+ suffixedexp(ls, v);
+ return;
+ }
+ }
+ luaX_next(ls);
+}
+
+
+static UnOpr getunopr (int op) {
+ switch (op) {
+ case TK_NOT: return OPR_NOT;
+ case '-': return OPR_MINUS;
+ case '~': return OPR_BNOT;
+ case '#': return OPR_LEN;
+ default: return OPR_NOUNOPR;
+ }
+}
+
+
+static BinOpr getbinopr (int op) {
+ switch (op) {
+ case '+': return OPR_ADD;
+ case '-': return OPR_SUB;
+ case '*': return OPR_MUL;
+ case '%': return OPR_MOD;
+ case '^': return OPR_POW;
+ case '/': return OPR_DIV;
+ case TK_IDIV: return OPR_IDIV;
+ case '&': return OPR_BAND;
+ case '|': return OPR_BOR;
+ case '~': return OPR_BXOR;
+ case TK_SHL: return OPR_SHL;
+ case TK_SHR: return OPR_SHR;
+ case TK_CONCAT: return OPR_CONCAT;
+ case TK_NE: return OPR_NE;
+ case TK_EQ: return OPR_EQ;
+ case '<': return OPR_LT;
+ case TK_LE: return OPR_LE;
+ case '>': return OPR_GT;
+ case TK_GE: return OPR_GE;
+ case TK_AND: return OPR_AND;
+ case TK_OR: return OPR_OR;
+ default: return OPR_NOBINOPR;
+ }
+}
+
+
+/*
+** Priority table for binary operators.
+*/
+static const struct {
+ lu_byte left; /* left priority for each binary operator */
+ lu_byte right; /* right priority */
+} priority[] = { /* ORDER OPR */
+ {10, 10}, {10, 10}, /* '+' '-' */
+ {11, 11}, {11, 11}, /* '*' '%' */
+ {14, 13}, /* '^' (right associative) */
+ {11, 11}, {11, 11}, /* '/' '//' */
+ {6, 6}, {4, 4}, {5, 5}, /* '&' '|' '~' */
+ {7, 7}, {7, 7}, /* '<<' '>>' */
+ {9, 8}, /* '..' (right associative) */
+ {3, 3}, {3, 3}, {3, 3}, /* ==, <, <= */
+ {3, 3}, {3, 3}, {3, 3}, /* ~=, >, >= */
+ {2, 2}, {1, 1} /* and, or */
+};
+
+#define UNARY_PRIORITY 12 /* priority for unary operators */
+
+
+/*
+** subexpr -> (simpleexp | unop subexpr) { binop subexpr }
+** where 'binop' is any binary operator with a priority higher than 'limit'
+*/
+static BinOpr subexpr (LexState *ls, expdesc *v, int limit) {
+ BinOpr op;
+ UnOpr uop;
+ enterlevel(ls);
+ uop = getunopr(ls->t.token);
+ if (uop != OPR_NOUNOPR) { /* prefix (unary) operator? */
+ int line = ls->linenumber;
+ luaX_next(ls); /* skip operator */
+ subexpr(ls, v, UNARY_PRIORITY);
+ luaK_prefix(ls->fs, uop, v, line);
+ }
+ else simpleexp(ls, v);
+ /* expand while operators have priorities higher than 'limit' */
+ op = getbinopr(ls->t.token);
+ while (op != OPR_NOBINOPR && priority[op].left > limit) {
+ expdesc v2;
+ BinOpr nextop;
+ int line = ls->linenumber;
+ luaX_next(ls); /* skip operator */
+ luaK_infix(ls->fs, op, v);
+ /* read sub-expression with higher priority */
+ nextop = subexpr(ls, &v2, priority[op].right);
+ luaK_posfix(ls->fs, op, v, &v2, line);
+ op = nextop;
+ }
+ leavelevel(ls);
+ return op; /* return first untreated operator */
+}
+
+
+static void expr (LexState *ls, expdesc *v) {
+ subexpr(ls, v, 0);
+}
+
+/* }==================================================================== */
+
+
+
+/*
+** {======================================================================
+** Rules for Statements
+** =======================================================================
+*/
+
+
+static void block (LexState *ls) {
+ /* block -> statlist */
+ FuncState *fs = ls->fs;
+ BlockCnt bl;
+ enterblock(fs, &bl, 0);
+ statlist(ls);
+ leaveblock(fs);
+}
+
+
+/*
+** structure to chain all variables in the left-hand side of an
+** assignment
+*/
+struct LHS_assign {
+ struct LHS_assign *prev;
+ expdesc v; /* variable (global, local, upvalue, or indexed) */
+};
+
+
+/*
+** check whether, in an assignment to an upvalue/local variable, the
+** upvalue/local variable is begin used in a previous assignment to a
+** table. If so, save original upvalue/local value in a safe place and
+** use this safe copy in the previous assignment.
+*/
+static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {
+ FuncState *fs = ls->fs;
+ int extra = fs->freereg; /* eventual position to save local variable */
+ int conflict = 0;
+ for (; lh; lh = lh->prev) { /* check all previous assignments */
+ if (vkisindexed(lh->v.k)) { /* assignment to table field? */
+ if (lh->v.k == VINDEXUP) { /* is table an upvalue? */
+ if (v->k == VUPVAL && lh->v.u.ind.t == v->u.info) {
+ conflict = 1; /* table is the upvalue being assigned now */
+ lh->v.k = VINDEXSTR;
+ lh->v.u.ind.t = extra; /* assignment will use safe copy */
+ }
+ }
+ else { /* table is a register */
+ if (v->k == VLOCAL && lh->v.u.ind.t == v->u.var.ridx) {
+ conflict = 1; /* table is the local being assigned now */
+ lh->v.u.ind.t = extra; /* assignment will use safe copy */
+ }
+ /* is index the local being assigned? */
+ if (lh->v.k == VINDEXED && v->k == VLOCAL &&
+ lh->v.u.ind.idx == v->u.var.ridx) {
+ conflict = 1;
+ lh->v.u.ind.idx = extra; /* previous assignment will use safe copy */
+ }
+ }
+ }
+ }
+ if (conflict) {
+ /* copy upvalue/local value to a temporary (in position 'extra') */
+ if (v->k == VLOCAL)
+ luaK_codeABC(fs, OP_MOVE, extra, v->u.var.ridx, 0);
+ else
+ luaK_codeABC(fs, OP_GETUPVAL, extra, v->u.info, 0);
+ luaK_reserveregs(fs, 1);
+ }
+}
+
+/*
+** Parse and compile a multiple assignment. The first "variable"
+** (a 'suffixedexp') was already read by the caller.
+**
+** assignment -> suffixedexp restassign
+** restassign -> ',' suffixedexp restassign | '=' explist
+*/
+static void restassign (LexState *ls, struct LHS_assign *lh, int nvars) {
+ expdesc e;
+ check_condition(ls, vkisvar(lh->v.k), "syntax error");
+ check_readonly(ls, &lh->v);
+ if (testnext(ls, ',')) { /* restassign -> ',' suffixedexp restassign */
+ struct LHS_assign nv;
+ nv.prev = lh;
+ suffixedexp(ls, &nv.v);
+ if (!vkisindexed(nv.v.k))
+ check_conflict(ls, lh, &nv.v);
+ enterlevel(ls); /* control recursion depth */
+ restassign(ls, &nv, nvars+1);
+ leavelevel(ls);
+ }
+ else { /* restassign -> '=' explist */
+ int nexps;
+ checknext(ls, '=');
+ nexps = explist(ls, &e);
+ if (nexps != nvars)
+ adjust_assign(ls, nvars, nexps, &e);
+ else {
+ luaK_setoneret(ls->fs, &e); /* close last expression */
+ luaK_storevar(ls->fs, &lh->v, &e);
+ return; /* avoid default */
+ }
+ }
+ init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */
+ luaK_storevar(ls->fs, &lh->v, &e);
+}
+
+
+static int cond (LexState *ls) {
+ /* cond -> exp */
+ expdesc v;
+ expr(ls, &v); /* read condition */
+ if (v.k == VNIL) v.k = VFALSE; /* 'falses' are all equal here */
+ luaK_goiftrue(ls->fs, &v);
+ return v.f;
+}
+
+
+static void gotostat (LexState *ls) {
+ FuncState *fs = ls->fs;
+ int line = ls->linenumber;
+ TString *name = str_checkname(ls); /* label's name */
+ Labeldesc *lb = findlabel(ls, name);
+ if (lb == NULL) /* no label? */
+ /* forward jump; will be resolved when the label is declared */
+ newgotoentry(ls, name, line, luaK_jump(fs));
+ else { /* found a label */
+ /* backward jump; will be resolved here */
+ int lblevel = reglevel(fs, lb->nactvar); /* label level */
+ if (luaY_nvarstack(fs) > lblevel) /* leaving the scope of a variable? */
+ luaK_codeABC(fs, OP_CLOSE, lblevel, 0, 0);
+ /* create jump and link it to the label */
+ luaK_patchlist(fs, luaK_jump(fs), lb->pc);
+ }
+}
+
+
+/*
+** Break statement. Semantically equivalent to "goto break".
+*/
+static void breakstat (LexState *ls) {
+ int line = ls->linenumber;
+ luaX_next(ls); /* skip break */
+ newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, luaK_jump(ls->fs));
+}
+
+
+/*
+** Check whether there is already a label with the given 'name'.
+*/
+static void checkrepeated (LexState *ls, TString *name) {
+ Labeldesc *lb = findlabel(ls, name);
+ if (l_unlikely(lb != NULL)) { /* already defined? */
+ const char *msg = "label '%s' already defined on line %d";
+ msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line);
+ luaK_semerror(ls, msg); /* error */
+ }
+}
+
+
+static void labelstat (LexState *ls, TString *name, int line) {
+ /* label -> '::' NAME '::' */
+ checknext(ls, TK_DBCOLON); /* skip double colon */
+ while (ls->t.token == ';' || ls->t.token == TK_DBCOLON)
+ statement(ls); /* skip other no-op statements */
+ checkrepeated(ls, name); /* check for repeated labels */
+ createlabel(ls, name, line, block_follow(ls, 0));
+}
+
+
+static void whilestat (LexState *ls, int line) {
+ /* whilestat -> WHILE cond DO block END */
+ FuncState *fs = ls->fs;
+ int whileinit;
+ int condexit;
+ BlockCnt bl;
+ luaX_next(ls); /* skip WHILE */
+ whileinit = luaK_getlabel(fs);
+ condexit = cond(ls);
+ enterblock(fs, &bl, 1);
+ checknext(ls, TK_DO);
+ block(ls);
+ luaK_jumpto(fs, whileinit);
+ check_match(ls, TK_END, TK_WHILE, line);
+ leaveblock(fs);
+ luaK_patchtohere(fs, condexit); /* false conditions finish the loop */
+}
+
+
+static void repeatstat (LexState *ls, int line) {
+ /* repeatstat -> REPEAT block UNTIL cond */
+ int condexit;
+ FuncState *fs = ls->fs;
+ int repeat_init = luaK_getlabel(fs);
+ BlockCnt bl1, bl2;
+ enterblock(fs, &bl1, 1); /* loop block */
+ enterblock(fs, &bl2, 0); /* scope block */
+ luaX_next(ls); /* skip REPEAT */
+ statlist(ls);
+ check_match(ls, TK_UNTIL, TK_REPEAT, line);
+ condexit = cond(ls); /* read condition (inside scope block) */
+ leaveblock(fs); /* finish scope */
+ if (bl2.upval) { /* upvalues? */
+ int exit = luaK_jump(fs); /* normal exit must jump over fix */
+ luaK_patchtohere(fs, condexit); /* repetition must close upvalues */
+ luaK_codeABC(fs, OP_CLOSE, reglevel(fs, bl2.nactvar), 0, 0);
+ condexit = luaK_jump(fs); /* repeat after closing upvalues */
+ luaK_patchtohere(fs, exit); /* normal exit comes to here */
+ }
+ luaK_patchlist(fs, condexit, repeat_init); /* close the loop */
+ leaveblock(fs); /* finish loop */
+}
+
+
+/*
+** Read an expression and generate code to put its results in next
+** stack slot.
+**
+*/
+static void exp1 (LexState *ls) {
+ expdesc e;
+ expr(ls, &e);
+ luaK_exp2nextreg(ls->fs, &e);
+ lua_assert(e.k == VNONRELOC);
+}
+
+
+/*
+** Fix for instruction at position 'pc' to jump to 'dest'.
+** (Jump addresses are relative in Lua). 'back' true means
+** a back jump.
+*/
+static void fixforjump (FuncState *fs, int pc, int dest, int back) {
+ Instruction *jmp = &fs->f->code[pc];
+ int offset = dest - (pc + 1);
+ if (back)
+ offset = -offset;
+ if (l_unlikely(offset > MAXARG_Bx))
+ luaX_syntaxerror(fs->ls, "control structure too long");
+ SETARG_Bx(*jmp, offset);
+}
+
+
+/*
+** Generate code for a 'for' loop.
+*/
+static void forbody (LexState *ls, int base, int line, int nvars, int isgen) {
+ /* forbody -> DO block */
+ static const OpCode forprep[2] = {OP_FORPREP, OP_TFORPREP};
+ static const OpCode forloop[2] = {OP_FORLOOP, OP_TFORLOOP};
+ BlockCnt bl;
+ FuncState *fs = ls->fs;
+ int prep, endfor;
+ checknext(ls, TK_DO);
+ prep = luaK_codeABx(fs, forprep[isgen], base, 0);
+ enterblock(fs, &bl, 0); /* scope for declared variables */
+ adjustlocalvars(ls, nvars);
+ luaK_reserveregs(fs, nvars);
+ block(ls);
+ leaveblock(fs); /* end of scope for declared variables */
+ fixforjump(fs, prep, luaK_getlabel(fs), 0);
+ if (isgen) { /* generic for? */
+ luaK_codeABC(fs, OP_TFORCALL, base, 0, nvars);
+ luaK_fixline(fs, line);
+ }
+ endfor = luaK_codeABx(fs, forloop[isgen], base, 0);
+ fixforjump(fs, endfor, prep + 1, 1);
+ luaK_fixline(fs, line);
+}
+
+
+static void fornum (LexState *ls, TString *varname, int line) {
+ /* fornum -> NAME = exp,exp[,exp] forbody */
+ FuncState *fs = ls->fs;
+ int base = fs->freereg;
+ new_localvarliteral(ls, "(for state)");
+ new_localvarliteral(ls, "(for state)");
+ new_localvarliteral(ls, "(for state)");
+ new_localvar(ls, varname);
+ checknext(ls, '=');
+ exp1(ls); /* initial value */
+ checknext(ls, ',');
+ exp1(ls); /* limit */
+ if (testnext(ls, ','))
+ exp1(ls); /* optional step */
+ else { /* default step = 1 */
+ luaK_int(fs, fs->freereg, 1);
+ luaK_reserveregs(fs, 1);
+ }
+ adjustlocalvars(ls, 3); /* control variables */
+ forbody(ls, base, line, 1, 0);
+}
+
+
+static void forlist (LexState *ls, TString *indexname) {
+ /* forlist -> NAME {,NAME} IN explist forbody */
+ FuncState *fs = ls->fs;
+ expdesc e;
+ int nvars = 5; /* gen, state, control, toclose, 'indexname' */
+ int line;
+ int base = fs->freereg;
+ /* create control variables */
+ new_localvarliteral(ls, "(for state)");
+ new_localvarliteral(ls, "(for state)");
+ new_localvarliteral(ls, "(for state)");
+ new_localvarliteral(ls, "(for state)");
+ /* create declared variables */
+ new_localvar(ls, indexname);
+ while (testnext(ls, ',')) {
+ new_localvar(ls, str_checkname(ls));
+ nvars++;
+ }
+ checknext(ls, TK_IN);
+ line = ls->linenumber;
+ adjust_assign(ls, 4, explist(ls, &e), &e);
+ adjustlocalvars(ls, 4); /* control variables */
+ marktobeclosed(fs); /* last control var. must be closed */
+ luaK_checkstack(fs, 3); /* extra space to call generator */
+ forbody(ls, base, line, nvars - 4, 1);
+}
+
+
+static void forstat (LexState *ls, int line) {
+ /* forstat -> FOR (fornum | forlist) END */
+ FuncState *fs = ls->fs;
+ TString *varname;
+ BlockCnt bl;
+ enterblock(fs, &bl, 1); /* scope for loop and control variables */
+ luaX_next(ls); /* skip 'for' */
+ varname = str_checkname(ls); /* first variable name */
+ switch (ls->t.token) {
+ case '=': fornum(ls, varname, line); break;
+ case ',': case TK_IN: forlist(ls, varname); break;
+ default: luaX_syntaxerror(ls, "'=' or 'in' expected");
+ }
+ check_match(ls, TK_END, TK_FOR, line);
+ leaveblock(fs); /* loop scope ('break' jumps to this point) */
+}
+
+
+static void test_then_block (LexState *ls, int *escapelist) {
+ /* test_then_block -> [IF | ELSEIF] cond THEN block */
+ BlockCnt bl;
+ FuncState *fs = ls->fs;
+ expdesc v;
+ int jf; /* instruction to skip 'then' code (if condition is false) */
+ luaX_next(ls); /* skip IF or ELSEIF */
+ expr(ls, &v); /* read condition */
+ checknext(ls, TK_THEN);
+ if (ls->t.token == TK_BREAK) { /* 'if x then break' ? */
+ int line = ls->linenumber;
+ luaK_goiffalse(ls->fs, &v); /* will jump if condition is true */
+ luaX_next(ls); /* skip 'break' */
+ enterblock(fs, &bl, 0); /* must enter block before 'goto' */
+ newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, v.t);
+ while (testnext(ls, ';')) {} /* skip semicolons */
+ if (block_follow(ls, 0)) { /* jump is the entire block? */
+ leaveblock(fs);
+ return; /* and that is it */
+ }
+ else /* must skip over 'then' part if condition is false */
+ jf = luaK_jump(fs);
+ }
+ else { /* regular case (not a break) */
+ luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */
+ enterblock(fs, &bl, 0);
+ jf = v.f;
+ }
+ statlist(ls); /* 'then' part */
+ leaveblock(fs);
+ if (ls->t.token == TK_ELSE ||
+ ls->t.token == TK_ELSEIF) /* followed by 'else'/'elseif'? */
+ luaK_concat(fs, escapelist, luaK_jump(fs)); /* must jump over it */
+ luaK_patchtohere(fs, jf);
+}
+
+
+static void ifstat (LexState *ls, int line) {
+ /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */
+ FuncState *fs = ls->fs;
+ int escapelist = NO_JUMP; /* exit list for finished parts */
+ test_then_block(ls, &escapelist); /* IF cond THEN block */
+ while (ls->t.token == TK_ELSEIF)
+ test_then_block(ls, &escapelist); /* ELSEIF cond THEN block */
+ if (testnext(ls, TK_ELSE))
+ block(ls); /* 'else' part */
+ check_match(ls, TK_END, TK_IF, line);
+ luaK_patchtohere(fs, escapelist); /* patch escape list to 'if' end */
+}
+
+
+static void localfunc (LexState *ls) {
+ expdesc b;
+ FuncState *fs = ls->fs;
+ int fvar = fs->nactvar; /* function's variable index */
+ new_localvar(ls, str_checkname(ls)); /* new local variable */
+ adjustlocalvars(ls, 1); /* enter its scope */
+ body(ls, &b, 0, ls->linenumber); /* function created in next register */
+ /* debug information will only see the variable after this point! */
+ localdebuginfo(fs, fvar)->startpc = fs->pc;
+}
+
+
+static int getlocalattribute (LexState *ls) {
+ /* ATTRIB -> ['<' Name '>'] */
+ if (testnext(ls, '<')) {
+ const char *attr = getstr(str_checkname(ls));
+ checknext(ls, '>');
+ if (strcmp(attr, "const") == 0)
+ return RDKCONST; /* read-only variable */
+ else if (strcmp(attr, "close") == 0)
+ return RDKTOCLOSE; /* to-be-closed variable */
+ else
+ luaK_semerror(ls,
+ luaO_pushfstring(ls->L, "unknown attribute '%s'", attr));
+ }
+ return VDKREG; /* regular variable */
+}
+
+
+static void checktoclose (FuncState *fs, int level) {
+ if (level != -1) { /* is there a to-be-closed variable? */
+ marktobeclosed(fs);
+ luaK_codeABC(fs, OP_TBC, reglevel(fs, level), 0, 0);
+ }
+}
+
+
+static void localstat (LexState *ls) {
+ /* stat -> LOCAL NAME ATTRIB { ',' NAME ATTRIB } ['=' explist] */
+ FuncState *fs = ls->fs;
+ int toclose = -1; /* index of to-be-closed variable (if any) */
+ Vardesc *var; /* last variable */
+ int vidx, kind; /* index and kind of last variable */
+ int nvars = 0;
+ int nexps;
+ expdesc e;
+ do {
+ vidx = new_localvar(ls, str_checkname(ls));
+ kind = getlocalattribute(ls);
+ getlocalvardesc(fs, vidx)->vd.kind = kind;
+ if (kind == RDKTOCLOSE) { /* to-be-closed? */
+ if (toclose != -1) /* one already present? */
+ luaK_semerror(ls, "multiple to-be-closed variables in local list");
+ toclose = fs->nactvar + nvars;
+ }
+ nvars++;
+ } while (testnext(ls, ','));
+ if (testnext(ls, '='))
+ nexps = explist(ls, &e);
+ else {
+ e.k = VVOID;
+ nexps = 0;
+ }
+ var = getlocalvardesc(fs, vidx); /* get last variable */
+ if (nvars == nexps && /* no adjustments? */
+ var->vd.kind == RDKCONST && /* last variable is const? */
+ luaK_exp2const(fs, &e, &var->k)) { /* compile-time constant? */
+ var->vd.kind = RDKCTC; /* variable is a compile-time constant */
+ adjustlocalvars(ls, nvars - 1); /* exclude last variable */
+ fs->nactvar++; /* but count it */
+ }
+ else {
+ adjust_assign(ls, nvars, nexps, &e);
+ adjustlocalvars(ls, nvars);
+ }
+ checktoclose(fs, toclose);
+}
+
+
+static int funcname (LexState *ls, expdesc *v) {
+ /* funcname -> NAME {fieldsel} [':' NAME] */
+ int ismethod = 0;
+ singlevar(ls, v);
+ while (ls->t.token == '.')
+ fieldsel(ls, v);
+ if (ls->t.token == ':') {
+ ismethod = 1;
+ fieldsel(ls, v);
+ }
+ return ismethod;
+}
+
+
+static void funcstat (LexState *ls, int line) {
+ /* funcstat -> FUNCTION funcname body */
+ int ismethod;
+ expdesc v, b;
+ luaX_next(ls); /* skip FUNCTION */
+ ismethod = funcname(ls, &v);
+ body(ls, &b, ismethod, line);
+ check_readonly(ls, &v);
+ luaK_storevar(ls->fs, &v, &b);
+ luaK_fixline(ls->fs, line); /* definition "happens" in the first line */
+}
+
+
+static void exprstat (LexState *ls) {
+ /* stat -> func | assignment */
+ FuncState *fs = ls->fs;
+ struct LHS_assign v;
+ suffixedexp(ls, &v.v);
+ if (ls->t.token == '=' || ls->t.token == ',') { /* stat -> assignment ? */
+ v.prev = NULL;
+ restassign(ls, &v, 1);
+ }
+ else { /* stat -> func */
+ Instruction *inst;
+ check_condition(ls, v.v.k == VCALL, "syntax error");
+ inst = &getinstruction(fs, &v.v);
+ SETARG_C(*inst, 1); /* call statement uses no results */
+ }
+}
+
+
+static void retstat (LexState *ls) {
+ /* stat -> RETURN [explist] [';'] */
+ FuncState *fs = ls->fs;
+ expdesc e;
+ int nret; /* number of values being returned */
+ int first = luaY_nvarstack(fs); /* first slot to be returned */
+ if (block_follow(ls, 1) || ls->t.token == ';')
+ nret = 0; /* return no values */
+ else {
+ nret = explist(ls, &e); /* optional return values */
+ if (hasmultret(e.k)) {
+ luaK_setmultret(fs, &e);
+ if (e.k == VCALL && nret == 1 && !fs->bl->insidetbc) { /* tail call? */
+ SET_OPCODE(getinstruction(fs,&e), OP_TAILCALL);
+ lua_assert(GETARG_A(getinstruction(fs,&e)) == luaY_nvarstack(fs));
+ }
+ nret = LUA_MULTRET; /* return all values */
+ }
+ else {
+ if (nret == 1) /* only one single value? */
+ first = luaK_exp2anyreg(fs, &e); /* can use original slot */
+ else { /* values must go to the top of the stack */
+ luaK_exp2nextreg(fs, &e);
+ lua_assert(nret == fs->freereg - first);
+ }
+ }
+ }
+ luaK_ret(fs, first, nret);
+ testnext(ls, ';'); /* skip optional semicolon */
+}
+
+
+static void statement (LexState *ls) {
+ int line = ls->linenumber; /* may be needed for error messages */
+ enterlevel(ls);
+ switch (ls->t.token) {
+ case ';': { /* stat -> ';' (empty statement) */
+ luaX_next(ls); /* skip ';' */
+ break;
+ }
+ case TK_IF: { /* stat -> ifstat */
+ ifstat(ls, line);
+ break;
+ }
+ case TK_WHILE: { /* stat -> whilestat */
+ whilestat(ls, line);
+ break;
+ }
+ case TK_DO: { /* stat -> DO block END */
+ luaX_next(ls); /* skip DO */
+ block(ls);
+ check_match(ls, TK_END, TK_DO, line);
+ break;
+ }
+ case TK_FOR: { /* stat -> forstat */
+ forstat(ls, line);
+ break;
+ }
+ case TK_REPEAT: { /* stat -> repeatstat */
+ repeatstat(ls, line);
+ break;
+ }
+ case TK_FUNCTION: { /* stat -> funcstat */
+ funcstat(ls, line);
+ break;
+ }
+ case TK_LOCAL: { /* stat -> localstat */
+ luaX_next(ls); /* skip LOCAL */
+ if (testnext(ls, TK_FUNCTION)) /* local function? */
+ localfunc(ls);
+ else
+ localstat(ls);
+ break;
+ }
+ case TK_DBCOLON: { /* stat -> label */
+ luaX_next(ls); /* skip double colon */
+ labelstat(ls, str_checkname(ls), line);
+ break;
+ }
+ case TK_RETURN: { /* stat -> retstat */
+ luaX_next(ls); /* skip RETURN */
+ retstat(ls);
+ break;
+ }
+ case TK_BREAK: { /* stat -> breakstat */
+ breakstat(ls);
+ break;
+ }
+ case TK_GOTO: { /* stat -> 'goto' NAME */
+ luaX_next(ls); /* skip 'goto' */
+ gotostat(ls);
+ break;
+ }
+ default: { /* stat -> func | assignment */
+ exprstat(ls);
+ break;
+ }
+ }
+ lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg &&
+ ls->fs->freereg >= luaY_nvarstack(ls->fs));
+ ls->fs->freereg = luaY_nvarstack(ls->fs); /* free registers */
+ leavelevel(ls);
+}
+
+/* }====================================================================== */
+
+
+/*
+** compiles the main function, which is a regular vararg function with an
+** upvalue named LUA_ENV
+*/
+static void mainfunc (LexState *ls, FuncState *fs) {
+ BlockCnt bl;
+ Upvaldesc *env;
+ open_func(ls, fs, &bl);
+ setvararg(fs, 0); /* main function is always declared vararg */
+ env = allocupvalue(fs); /* ...set environment upvalue */
+ env->instack = 1;
+ env->idx = 0;
+ env->kind = VDKREG;
+ env->name = ls->envn;
+ luaC_objbarrier(ls->L, fs->f, env->name);
+ luaX_next(ls); /* read first token */
+ statlist(ls); /* parse main body */
+ check(ls, TK_EOS);
+ close_func(ls);
+}
+
+
+LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
+ Dyndata *dyd, const char *name, int firstchar) {
+ LexState lexstate;
+ FuncState funcstate;
+ LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */
+ setclLvalue2s(L, L->top.p, cl); /* anchor it (to avoid being collected) */
+ luaD_inctop(L);
+ lexstate.h = luaH_new(L); /* create table for scanner */
+ sethvalue2s(L, L->top.p, lexstate.h); /* anchor it */
+ luaD_inctop(L);
+ funcstate.f = cl->p = luaF_newproto(L);
+ luaC_objbarrier(L, cl, cl->p);
+ funcstate.f->source = luaS_new(L, name); /* create and anchor TString */
+ luaC_objbarrier(L, funcstate.f, funcstate.f->source);
+ lexstate.buff = buff;
+ lexstate.dyd = dyd;
+ dyd->actvar.n = dyd->gt.n = dyd->label.n = 0;
+ luaX_setinput(L, &lexstate, z, funcstate.f->source, firstchar);
+ mainfunc(&lexstate, &funcstate);
+ lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs);
+ /* all scopes should be correctly finished */
+ lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0);
+ L->top.p--; /* remove scanner's table */
+ return cl; /* closure is on the stack, too */
+}
+
diff --git a/src/libs/3rdparty/lua/src/lparser.h b/src/libs/3rdparty/lua/src/lparser.h
new file mode 100644
index 0000000000..5e4500f181
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lparser.h
@@ -0,0 +1,171 @@
+/*
+** $Id: lparser.h $
+** Lua Parser
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lparser_h
+#define lparser_h
+
+#include "llimits.h"
+#include "lobject.h"
+#include "lzio.h"
+
+
+/*
+** Expression and variable descriptor.
+** Code generation for variables and expressions can be delayed to allow
+** optimizations; An 'expdesc' structure describes a potentially-delayed
+** variable/expression. It has a description of its "main" value plus a
+** list of conditional jumps that can also produce its value (generated
+** by short-circuit operators 'and'/'or').
+*/
+
+/* kinds of variables/expressions */
+typedef enum {
+ VVOID, /* when 'expdesc' describes the last expression of a list,
+ this kind means an empty list (so, no expression) */
+ VNIL, /* constant nil */
+ VTRUE, /* constant true */
+ VFALSE, /* constant false */
+ VK, /* constant in 'k'; info = index of constant in 'k' */
+ VKFLT, /* floating constant; nval = numerical float value */
+ VKINT, /* integer constant; ival = numerical integer value */
+ VKSTR, /* string constant; strval = TString address;
+ (string is fixed by the lexer) */
+ VNONRELOC, /* expression has its value in a fixed register;
+ info = result register */
+ VLOCAL, /* local variable; var.ridx = register index;
+ var.vidx = relative index in 'actvar.arr' */
+ VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */
+ VCONST, /* compile-time <const> variable;
+ info = absolute index in 'actvar.arr' */
+ VINDEXED, /* indexed variable;
+ ind.t = table register;
+ ind.idx = key's R index */
+ VINDEXUP, /* indexed upvalue;
+ ind.t = table upvalue;
+ ind.idx = key's K index */
+ VINDEXI, /* indexed variable with constant integer;
+ ind.t = table register;
+ ind.idx = key's value */
+ VINDEXSTR, /* indexed variable with literal string;
+ ind.t = table register;
+ ind.idx = key's K index */
+ VJMP, /* expression is a test/comparison;
+ info = pc of corresponding jump instruction */
+ VRELOC, /* expression can put result in any register;
+ info = instruction pc */
+ VCALL, /* expression is a function call; info = instruction pc */
+ VVARARG /* vararg expression; info = instruction pc */
+} expkind;
+
+
+#define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXSTR)
+#define vkisindexed(k) (VINDEXED <= (k) && (k) <= VINDEXSTR)
+
+
+typedef struct expdesc {
+ expkind k;
+ union {
+ lua_Integer ival; /* for VKINT */
+ lua_Number nval; /* for VKFLT */
+ TString *strval; /* for VKSTR */
+ int info; /* for generic use */
+ struct { /* for indexed variables */
+ short idx; /* index (R or "long" K) */
+ lu_byte t; /* table (register or upvalue) */
+ } ind;
+ struct { /* for local variables */
+ lu_byte ridx; /* register holding the variable */
+ unsigned short vidx; /* compiler index (in 'actvar.arr') */
+ } var;
+ } u;
+ int t; /* patch list of 'exit when true' */
+ int f; /* patch list of 'exit when false' */
+} expdesc;
+
+
+/* kinds of variables */
+#define VDKREG 0 /* regular */
+#define RDKCONST 1 /* constant */
+#define RDKTOCLOSE 2 /* to-be-closed */
+#define RDKCTC 3 /* compile-time constant */
+
+/* description of an active local variable */
+typedef union Vardesc {
+ struct {
+ TValuefields; /* constant value (if it is a compile-time constant) */
+ lu_byte kind;
+ lu_byte ridx; /* register holding the variable */
+ short pidx; /* index of the variable in the Proto's 'locvars' array */
+ TString *name; /* variable name */
+ } vd;
+ TValue k; /* constant value (if any) */
+} Vardesc;
+
+
+
+/* description of pending goto statements and label statements */
+typedef struct Labeldesc {
+ TString *name; /* label identifier */
+ int pc; /* position in code */
+ int line; /* line where it appeared */
+ lu_byte nactvar; /* number of active variables in that position */
+ lu_byte close; /* goto that escapes upvalues */
+} Labeldesc;
+
+
+/* list of labels or gotos */
+typedef struct Labellist {
+ Labeldesc *arr; /* array */
+ int n; /* number of entries in use */
+ int size; /* array size */
+} Labellist;
+
+
+/* dynamic structures used by the parser */
+typedef struct Dyndata {
+ struct { /* list of all active local variables */
+ Vardesc *arr;
+ int n;
+ int size;
+ } actvar;
+ Labellist gt; /* list of pending gotos */
+ Labellist label; /* list of active labels */
+} Dyndata;
+
+
+/* control of blocks */
+struct BlockCnt; /* defined in lparser.c */
+
+
+/* state needed to generate code for a given function */
+typedef struct FuncState {
+ Proto *f; /* current function header */
+ struct FuncState *prev; /* enclosing function */
+ struct LexState *ls; /* lexical state */
+ struct BlockCnt *bl; /* chain of current blocks */
+ int pc; /* next position to code (equivalent to 'ncode') */
+ int lasttarget; /* 'label' of last 'jump label' */
+ int previousline; /* last line that was saved in 'lineinfo' */
+ int nk; /* number of elements in 'k' */
+ int np; /* number of elements in 'p' */
+ int nabslineinfo; /* number of elements in 'abslineinfo' */
+ int firstlocal; /* index of first local var (in Dyndata array) */
+ int firstlabel; /* index of first label (in 'dyd->label->arr') */
+ short ndebugvars; /* number of elements in 'f->locvars' */
+ lu_byte nactvar; /* number of active local variables */
+ lu_byte nups; /* number of upvalues */
+ lu_byte freereg; /* first free register */
+ lu_byte iwthabs; /* instructions issued since last absolute line info */
+ lu_byte needclose; /* function needs to close upvalues when returning */
+} FuncState;
+
+
+LUAI_FUNC int luaY_nvarstack (FuncState *fs);
+LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
+ Dyndata *dyd, const char *name, int firstchar);
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lprefix.h b/src/libs/3rdparty/lua/src/lprefix.h
new file mode 100644
index 0000000000..484f2ad6fb
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lprefix.h
@@ -0,0 +1,45 @@
+/*
+** $Id: lprefix.h $
+** Definitions for Lua code that must come before any other header file
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lprefix_h
+#define lprefix_h
+
+
+/*
+** Allows POSIX/XSI stuff
+*/
+#if !defined(LUA_USE_C89) /* { */
+
+#if !defined(_XOPEN_SOURCE)
+#define _XOPEN_SOURCE 600
+#elif _XOPEN_SOURCE == 0
+#undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */
+#endif
+
+/*
+** Allows manipulation of large files in gcc and some other compilers
+*/
+#if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS)
+#define _LARGEFILE_SOURCE 1
+#define _FILE_OFFSET_BITS 64
+#endif
+
+#endif /* } */
+
+
+/*
+** Windows stuff
+*/
+#if defined(_WIN32) /* { */
+
+#if !defined(_CRT_SECURE_NO_WARNINGS)
+#define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */
+#endif
+
+#endif /* } */
+
+#endif
+
diff --git a/src/libs/3rdparty/lua/src/lstate.c b/src/libs/3rdparty/lua/src/lstate.c
new file mode 100644
index 0000000000..1e925e5ad4
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lstate.c
@@ -0,0 +1,445 @@
+/*
+** $Id: lstate.c $
+** Global State
+** See Copyright Notice in lua.h
+*/
+
+#define lstate_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <stddef.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lapi.h"
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "llex.h"
+#include "lmem.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+
+
+
+/*
+** thread state + extra space
+*/
+typedef struct LX {
+ lu_byte extra_[LUA_EXTRASPACE];
+ lua_State l;
+} LX;
+
+
+/*
+** Main thread combines a thread state and the global state
+*/
+typedef struct LG {
+ LX l;
+ global_State g;
+} LG;
+
+
+
+#define fromstate(L) (cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l)))
+
+
+/*
+** A macro to create a "random" seed when a state is created;
+** the seed is used to randomize string hashes.
+*/
+#if !defined(luai_makeseed)
+
+#include <time.h>
+
+/*
+** Compute an initial seed with some level of randomness.
+** Rely on Address Space Layout Randomization (if present) and
+** current time.
+*/
+#define addbuff(b,p,e) \
+ { size_t t = cast_sizet(e); \
+ memcpy(b + p, &t, sizeof(t)); p += sizeof(t); }
+
+static unsigned int luai_makeseed (lua_State *L) {
+ char buff[3 * sizeof(size_t)];
+ unsigned int h = cast_uint(time(NULL));
+ int p = 0;
+ addbuff(buff, p, L); /* heap variable */
+ addbuff(buff, p, &h); /* local variable */
+ addbuff(buff, p, &lua_newstate); /* public function */
+ lua_assert(p == sizeof(buff));
+ return luaS_hash(buff, p, h);
+}
+
+#endif
+
+
+/*
+** set GCdebt to a new value keeping the value (totalbytes + GCdebt)
+** invariant (and avoiding underflows in 'totalbytes')
+*/
+void luaE_setdebt (global_State *g, l_mem debt) {
+ l_mem tb = gettotalbytes(g);
+ lua_assert(tb > 0);
+ if (debt < tb - MAX_LMEM)
+ debt = tb - MAX_LMEM; /* will make 'totalbytes == MAX_LMEM' */
+ g->totalbytes = tb - debt;
+ g->GCdebt = debt;
+}
+
+
+LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) {
+ UNUSED(L); UNUSED(limit);
+ return LUAI_MAXCCALLS; /* warning?? */
+}
+
+
+CallInfo *luaE_extendCI (lua_State *L) {
+ CallInfo *ci;
+ lua_assert(L->ci->next == NULL);
+ ci = luaM_new(L, CallInfo);
+ lua_assert(L->ci->next == NULL);
+ L->ci->next = ci;
+ ci->previous = L->ci;
+ ci->next = NULL;
+ ci->u.l.trap = 0;
+ L->nci++;
+ return ci;
+}
+
+
+/*
+** free all CallInfo structures not in use by a thread
+*/
+void luaE_freeCI (lua_State *L) {
+ CallInfo *ci = L->ci;
+ CallInfo *next = ci->next;
+ ci->next = NULL;
+ while ((ci = next) != NULL) {
+ next = ci->next;
+ luaM_free(L, ci);
+ L->nci--;
+ }
+}
+
+
+/*
+** free half of the CallInfo structures not in use by a thread,
+** keeping the first one.
+*/
+void luaE_shrinkCI (lua_State *L) {
+ CallInfo *ci = L->ci->next; /* first free CallInfo */
+ CallInfo *next;
+ if (ci == NULL)
+ return; /* no extra elements */
+ while ((next = ci->next) != NULL) { /* two extra elements? */
+ CallInfo *next2 = next->next; /* next's next */
+ ci->next = next2; /* remove next from the list */
+ L->nci--;
+ luaM_free(L, next); /* free next */
+ if (next2 == NULL)
+ break; /* no more elements */
+ else {
+ next2->previous = ci;
+ ci = next2; /* continue */
+ }
+ }
+}
+
+
+/*
+** Called when 'getCcalls(L)' larger or equal to LUAI_MAXCCALLS.
+** If equal, raises an overflow error. If value is larger than
+** LUAI_MAXCCALLS (which means it is handling an overflow) but
+** not much larger, does not report an error (to allow overflow
+** handling to work).
+*/
+void luaE_checkcstack (lua_State *L) {
+ if (getCcalls(L) == LUAI_MAXCCALLS)
+ luaG_runerror(L, "C stack overflow");
+ else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11))
+ luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
+}
+
+
+LUAI_FUNC void luaE_incCstack (lua_State *L) {
+ L->nCcalls++;
+ if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS))
+ luaE_checkcstack(L);
+}
+
+
+static void stack_init (lua_State *L1, lua_State *L) {
+ int i; CallInfo *ci;
+ /* initialize stack array */
+ L1->stack.p = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, StackValue);
+ L1->tbclist.p = L1->stack.p;
+ for (i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
+ setnilvalue(s2v(L1->stack.p + i)); /* erase new stack */
+ L1->top.p = L1->stack.p;
+ L1->stack_last.p = L1->stack.p + BASIC_STACK_SIZE;
+ /* initialize first ci */
+ ci = &L1->base_ci;
+ ci->next = ci->previous = NULL;
+ ci->callstatus = CIST_C;
+ ci->func.p = L1->top.p;
+ ci->u.c.k = NULL;
+ ci->nresults = 0;
+ setnilvalue(s2v(L1->top.p)); /* 'function' entry for this 'ci' */
+ L1->top.p++;
+ ci->top.p = L1->top.p + LUA_MINSTACK;
+ L1->ci = ci;
+}
+
+
+static void freestack (lua_State *L) {
+ if (L->stack.p == NULL)
+ return; /* stack not completely built yet */
+ L->ci = &L->base_ci; /* free the entire 'ci' list */
+ luaE_freeCI(L);
+ lua_assert(L->nci == 0);
+ luaM_freearray(L, L->stack.p, stacksize(L) + EXTRA_STACK); /* free stack */
+}
+
+
+/*
+** Create registry table and its predefined values
+*/
+static void init_registry (lua_State *L, global_State *g) {
+ /* create registry */
+ Table *registry = luaH_new(L);
+ sethvalue(L, &g->l_registry, registry);
+ luaH_resize(L, registry, LUA_RIDX_LAST, 0);
+ /* registry[LUA_RIDX_MAINTHREAD] = L */
+ setthvalue(L, &registry->array[LUA_RIDX_MAINTHREAD - 1], L);
+ /* registry[LUA_RIDX_GLOBALS] = new table (table of globals) */
+ sethvalue(L, &registry->array[LUA_RIDX_GLOBALS - 1], luaH_new(L));
+}
+
+
+/*
+** open parts of the state that may cause memory-allocation errors.
+*/
+static void f_luaopen (lua_State *L, void *ud) {
+ global_State *g = G(L);
+ UNUSED(ud);
+ stack_init(L, L); /* init stack */
+ init_registry(L, g);
+ luaS_init(L);
+ luaT_init(L);
+ luaX_init(L);
+ g->gcstp = 0; /* allow gc */
+ setnilvalue(&g->nilvalue); /* now state is complete */
+ luai_userstateopen(L);
+}
+
+
+/*
+** preinitialize a thread with consistent values without allocating
+** any memory (to avoid errors)
+*/
+static void preinit_thread (lua_State *L, global_State *g) {
+ G(L) = g;
+ L->stack.p = NULL;
+ L->ci = NULL;
+ L->nci = 0;
+ L->twups = L; /* thread has no upvalues */
+ L->nCcalls = 0;
+ L->errorJmp = NULL;
+ L->hook = NULL;
+ L->hookmask = 0;
+ L->basehookcount = 0;
+ L->allowhook = 1;
+ resethookcount(L);
+ L->openupval = NULL;
+ L->status = LUA_OK;
+ L->errfunc = 0;
+ L->oldpc = 0;
+}
+
+
+static void close_state (lua_State *L) {
+ global_State *g = G(L);
+ if (!completestate(g)) /* closing a partially built state? */
+ luaC_freeallobjects(L); /* just collect its objects */
+ else { /* closing a fully built state */
+ L->ci = &L->base_ci; /* unwind CallInfo list */
+ luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */
+ luaC_freeallobjects(L); /* collect all objects */
+ luai_userstateclose(L);
+ }
+ luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size);
+ freestack(L);
+ lua_assert(gettotalbytes(g) == sizeof(LG));
+ (*g->frealloc)(g->ud, fromstate(L), sizeof(LG), 0); /* free main block */
+}
+
+
+LUA_API lua_State *lua_newthread (lua_State *L) {
+ global_State *g = G(L);
+ GCObject *o;
+ lua_State *L1;
+ lua_lock(L);
+ luaC_checkGC(L);
+ /* create new thread */
+ o = luaC_newobjdt(L, LUA_TTHREAD, sizeof(LX), offsetof(LX, l));
+ L1 = gco2th(o);
+ /* anchor it on L stack */
+ setthvalue2s(L, L->top.p, L1);
+ api_incr_top(L);
+ preinit_thread(L1, g);
+ L1->hookmask = L->hookmask;
+ L1->basehookcount = L->basehookcount;
+ L1->hook = L->hook;
+ resethookcount(L1);
+ /* initialize L1 extra space */
+ memcpy(lua_getextraspace(L1), lua_getextraspace(g->mainthread),
+ LUA_EXTRASPACE);
+ luai_userstatethread(L, L1);
+ stack_init(L1, L); /* init stack */
+ lua_unlock(L);
+ return L1;
+}
+
+
+void luaE_freethread (lua_State *L, lua_State *L1) {
+ LX *l = fromstate(L1);
+ luaF_closeupval(L1, L1->stack.p); /* close all upvalues */
+ lua_assert(L1->openupval == NULL);
+ luai_userstatefree(L, L1);
+ freestack(L1);
+ luaM_free(L, l);
+}
+
+
+int luaE_resetthread (lua_State *L, int status) {
+ CallInfo *ci = L->ci = &L->base_ci; /* unwind CallInfo list */
+ setnilvalue(s2v(L->stack.p)); /* 'function' entry for basic 'ci' */
+ ci->func.p = L->stack.p;
+ ci->callstatus = CIST_C;
+ if (status == LUA_YIELD)
+ status = LUA_OK;
+ L->status = LUA_OK; /* so it can run __close metamethods */
+ status = luaD_closeprotected(L, 1, status);
+ if (status != LUA_OK) /* errors? */
+ luaD_seterrorobj(L, status, L->stack.p + 1);
+ else
+ L->top.p = L->stack.p + 1;
+ ci->top.p = L->top.p + LUA_MINSTACK;
+ luaD_reallocstack(L, cast_int(ci->top.p - L->stack.p), 0);
+ return status;
+}
+
+
+LUA_API int lua_closethread (lua_State *L, lua_State *from) {
+ int status;
+ lua_lock(L);
+ L->nCcalls = (from) ? getCcalls(from) : 0;
+ status = luaE_resetthread(L, L->status);
+ lua_unlock(L);
+ return status;
+}
+
+
+/*
+** Deprecated! Use 'lua_closethread' instead.
+*/
+LUA_API int lua_resetthread (lua_State *L) {
+ return lua_closethread(L, NULL);
+}
+
+
+LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
+ int i;
+ lua_State *L;
+ global_State *g;
+ LG *l = cast(LG *, (*f)(ud, NULL, LUA_TTHREAD, sizeof(LG)));
+ if (l == NULL) return NULL;
+ L = &l->l.l;
+ g = &l->g;
+ L->tt = LUA_VTHREAD;
+ g->currentwhite = bitmask(WHITE0BIT);
+ L->marked = luaC_white(g);
+ preinit_thread(L, g);
+ g->allgc = obj2gco(L); /* by now, only object is the main thread */
+ L->next = NULL;
+ incnny(L); /* main thread is always non yieldable */
+ g->frealloc = f;
+ g->ud = ud;
+ g->warnf = NULL;
+ g->ud_warn = NULL;
+ g->mainthread = L;
+ g->seed = luai_makeseed(L);
+ g->gcstp = GCSTPGC; /* no GC while building state */
+ g->strt.size = g->strt.nuse = 0;
+ g->strt.hash = NULL;
+ setnilvalue(&g->l_registry);
+ g->panic = NULL;
+ g->gcstate = GCSpause;
+ g->gckind = KGC_INC;
+ g->gcstopem = 0;
+ g->gcemergency = 0;
+ g->finobj = g->tobefnz = g->fixedgc = NULL;
+ g->firstold1 = g->survival = g->old1 = g->reallyold = NULL;
+ g->finobjsur = g->finobjold1 = g->finobjrold = NULL;
+ g->sweepgc = NULL;
+ g->gray = g->grayagain = NULL;
+ g->weak = g->ephemeron = g->allweak = NULL;
+ g->twups = NULL;
+ g->totalbytes = sizeof(LG);
+ g->GCdebt = 0;
+ g->lastatomic = 0;
+ setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */
+ setgcparam(g->gcpause, LUAI_GCPAUSE);
+ setgcparam(g->gcstepmul, LUAI_GCMUL);
+ g->gcstepsize = LUAI_GCSTEPSIZE;
+ setgcparam(g->genmajormul, LUAI_GENMAJORMUL);
+ g->genminormul = LUAI_GENMINORMUL;
+ for (i=0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL;
+ if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) {
+ /* memory allocation error: free partial state */
+ close_state(L);
+ L = NULL;
+ }
+ return L;
+}
+
+
+LUA_API void lua_close (lua_State *L) {
+ lua_lock(L);
+ L = G(L)->mainthread; /* only the main thread can be closed */
+ close_state(L);
+}
+
+
+void luaE_warning (lua_State *L, const char *msg, int tocont) {
+ lua_WarnFunction wf = G(L)->warnf;
+ if (wf != NULL)
+ wf(G(L)->ud_warn, msg, tocont);
+}
+
+
+/*
+** Generate a warning from an error message
+*/
+void luaE_warnerror (lua_State *L, const char *where) {
+ TValue *errobj = s2v(L->top.p - 1); /* error object */
+ const char *msg = (ttisstring(errobj))
+ ? svalue(errobj)
+ : "error object is not a string";
+ /* produce warning "error in %s (%s)" (where, msg) */
+ luaE_warning(L, "error in ", 1);
+ luaE_warning(L, where, 1);
+ luaE_warning(L, " (", 1);
+ luaE_warning(L, msg, 1);
+ luaE_warning(L, ")", 0);
+}
+
diff --git a/src/libs/3rdparty/lua/src/lstate.h b/src/libs/3rdparty/lua/src/lstate.h
new file mode 100644
index 0000000000..8bf6600e34
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lstate.h
@@ -0,0 +1,409 @@
+/*
+** $Id: lstate.h $
+** Global State
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lstate_h
+#define lstate_h
+
+#include "lua.h"
+
+
+/* Some header files included here need this definition */
+typedef struct CallInfo CallInfo;
+
+
+#include "lobject.h"
+#include "ltm.h"
+#include "lzio.h"
+
+
+/*
+** Some notes about garbage-collected objects: All objects in Lua must
+** be kept somehow accessible until being freed, so all objects always
+** belong to one (and only one) of these lists, using field 'next' of
+** the 'CommonHeader' for the link:
+**
+** 'allgc': all objects not marked for finalization;
+** 'finobj': all objects marked for finalization;
+** 'tobefnz': all objects ready to be finalized;
+** 'fixedgc': all objects that are not to be collected (currently
+** only small strings, such as reserved words).
+**
+** For the generational collector, some of these lists have marks for
+** generations. Each mark points to the first element in the list for
+** that particular generation; that generation goes until the next mark.
+**
+** 'allgc' -> 'survival': new objects;
+** 'survival' -> 'old': objects that survived one collection;
+** 'old1' -> 'reallyold': objects that became old in last collection;
+** 'reallyold' -> NULL: objects old for more than one cycle.
+**
+** 'finobj' -> 'finobjsur': new objects marked for finalization;
+** 'finobjsur' -> 'finobjold1': survived """";
+** 'finobjold1' -> 'finobjrold': just old """";
+** 'finobjrold' -> NULL: really old """".
+**
+** All lists can contain elements older than their main ages, due
+** to 'luaC_checkfinalizer' and 'udata2finalize', which move
+** objects between the normal lists and the "marked for finalization"
+** lists. Moreover, barriers can age young objects in young lists as
+** OLD0, which then become OLD1. However, a list never contains
+** elements younger than their main ages.
+**
+** The generational collector also uses a pointer 'firstold1', which
+** points to the first OLD1 object in the list. It is used to optimize
+** 'markold'. (Potentially OLD1 objects can be anywhere between 'allgc'
+** and 'reallyold', but often the list has no OLD1 objects or they are
+** after 'old1'.) Note the difference between it and 'old1':
+** 'firstold1': no OLD1 objects before this point; there can be all
+** ages after it.
+** 'old1': no objects younger than OLD1 after this point.
+*/
+
+/*
+** Moreover, there is another set of lists that control gray objects.
+** These lists are linked by fields 'gclist'. (All objects that
+** can become gray have such a field. The field is not the same
+** in all objects, but it always has this name.) Any gray object
+** must belong to one of these lists, and all objects in these lists
+** must be gray (with two exceptions explained below):
+**
+** 'gray': regular gray objects, still waiting to be visited.
+** 'grayagain': objects that must be revisited at the atomic phase.
+** That includes
+** - black objects got in a write barrier;
+** - all kinds of weak tables during propagation phase;
+** - all threads.
+** 'weak': tables with weak values to be cleared;
+** 'ephemeron': ephemeron tables with white->white entries;
+** 'allweak': tables with weak keys and/or weak values to be cleared.
+**
+** The exceptions to that "gray rule" are:
+** - TOUCHED2 objects in generational mode stay in a gray list (because
+** they must be visited again at the end of the cycle), but they are
+** marked black because assignments to them must activate barriers (to
+** move them back to TOUCHED1).
+** - Open upvales are kept gray to avoid barriers, but they stay out
+** of gray lists. (They don't even have a 'gclist' field.)
+*/
+
+
+
+/*
+** About 'nCcalls': This count has two parts: the lower 16 bits counts
+** the number of recursive invocations in the C stack; the higher
+** 16 bits counts the number of non-yieldable calls in the stack.
+** (They are together so that we can change and save both with one
+** instruction.)
+*/
+
+
+/* true if this thread does not have non-yieldable calls in the stack */
+#define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0)
+
+/* real number of C calls */
+#define getCcalls(L) ((L)->nCcalls & 0xffff)
+
+
+/* Increment the number of non-yieldable calls */
+#define incnny(L) ((L)->nCcalls += 0x10000)
+
+/* Decrement the number of non-yieldable calls */
+#define decnny(L) ((L)->nCcalls -= 0x10000)
+
+/* Non-yieldable call increment */
+#define nyci (0x10000 | 1)
+
+
+
+
+struct lua_longjmp; /* defined in ldo.c */
+
+
+/*
+** Atomic type (relative to signals) to better ensure that 'lua_sethook'
+** is thread safe
+*/
+#if !defined(l_signalT)
+#include <signal.h>
+#define l_signalT sig_atomic_t
+#endif
+
+
+/*
+** Extra stack space to handle TM calls and some other extras. This
+** space is not included in 'stack_last'. It is used only to avoid stack
+** checks, either because the element will be promptly popped or because
+** there will be a stack check soon after the push. Function frames
+** never use this extra space, so it does not need to be kept clean.
+*/
+#define EXTRA_STACK 5
+
+
+#define BASIC_STACK_SIZE (2*LUA_MINSTACK)
+
+#define stacksize(th) cast_int((th)->stack_last.p - (th)->stack.p)
+
+
+/* kinds of Garbage Collection */
+#define KGC_INC 0 /* incremental gc */
+#define KGC_GEN 1 /* generational gc */
+
+
+typedef struct stringtable {
+ TString **hash;
+ int nuse; /* number of elements */
+ int size;
+} stringtable;
+
+
+/*
+** Information about a call.
+** About union 'u':
+** - field 'l' is used only for Lua functions;
+** - field 'c' is used only for C functions.
+** About union 'u2':
+** - field 'funcidx' is used only by C functions while doing a
+** protected call;
+** - field 'nyield' is used only while a function is "doing" an
+** yield (from the yield until the next resume);
+** - field 'nres' is used only while closing tbc variables when
+** returning from a function;
+** - field 'transferinfo' is used only during call/returnhooks,
+** before the function starts or after it ends.
+*/
+struct CallInfo {
+ StkIdRel func; /* function index in the stack */
+ StkIdRel top; /* top for this function */
+ struct CallInfo *previous, *next; /* dynamic call link */
+ union {
+ struct { /* only for Lua functions */
+ const Instruction *savedpc;
+ volatile l_signalT trap;
+ int nextraargs; /* # of extra arguments in vararg functions */
+ } l;
+ struct { /* only for C functions */
+ lua_KFunction k; /* continuation in case of yields */
+ ptrdiff_t old_errfunc;
+ lua_KContext ctx; /* context info. in case of yields */
+ } c;
+ } u;
+ union {
+ int funcidx; /* called-function index */
+ int nyield; /* number of values yielded */
+ int nres; /* number of values returned */
+ struct { /* info about transferred values (for call/return hooks) */
+ unsigned short ftransfer; /* offset of first value transferred */
+ unsigned short ntransfer; /* number of values transferred */
+ } transferinfo;
+ } u2;
+ short nresults; /* expected number of results from this function */
+ unsigned short callstatus;
+};
+
+
+/*
+** Bits in CallInfo status
+*/
+#define CIST_OAH (1<<0) /* original value of 'allowhook' */
+#define CIST_C (1<<1) /* call is running a C function */
+#define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */
+#define CIST_HOOKED (1<<3) /* call is running a debug hook */
+#define CIST_YPCALL (1<<4) /* doing a yieldable protected call */
+#define CIST_TAIL (1<<5) /* call was tail called */
+#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */
+#define CIST_FIN (1<<7) /* function "called" a finalizer */
+#define CIST_TRAN (1<<8) /* 'ci' has transfer information */
+#define CIST_CLSRET (1<<9) /* function is closing tbc variables */
+/* Bits 10-12 are used for CIST_RECST (see below) */
+#define CIST_RECST 10
+#if defined(LUA_COMPAT_LT_LE)
+#define CIST_LEQ (1<<13) /* using __lt for __le */
+#endif
+
+
+/*
+** Field CIST_RECST stores the "recover status", used to keep the error
+** status while closing to-be-closed variables in coroutines, so that
+** Lua can correctly resume after an yield from a __close method called
+** because of an error. (Three bits are enough for error status.)
+*/
+#define getcistrecst(ci) (((ci)->callstatus >> CIST_RECST) & 7)
+#define setcistrecst(ci,st) \
+ check_exp(((st) & 7) == (st), /* status must fit in three bits */ \
+ ((ci)->callstatus = ((ci)->callstatus & ~(7 << CIST_RECST)) \
+ | ((st) << CIST_RECST)))
+
+
+/* active function is a Lua function */
+#define isLua(ci) (!((ci)->callstatus & CIST_C))
+
+/* call is running Lua code (not a hook) */
+#define isLuacode(ci) (!((ci)->callstatus & (CIST_C | CIST_HOOKED)))
+
+/* assume that CIST_OAH has offset 0 and that 'v' is strictly 0/1 */
+#define setoah(st,v) ((st) = ((st) & ~CIST_OAH) | (v))
+#define getoah(st) ((st) & CIST_OAH)
+
+
+/*
+** 'global state', shared by all threads of this state
+*/
+typedef struct global_State {
+ lua_Alloc frealloc; /* function to reallocate memory */
+ void *ud; /* auxiliary data to 'frealloc' */
+ l_mem totalbytes; /* number of bytes currently allocated - GCdebt */
+ l_mem GCdebt; /* bytes allocated not yet compensated by the collector */
+ lu_mem GCestimate; /* an estimate of the non-garbage memory in use */
+ lu_mem lastatomic; /* see function 'genstep' in file 'lgc.c' */
+ stringtable strt; /* hash table for strings */
+ TValue l_registry;
+ TValue nilvalue; /* a nil value */
+ unsigned int seed; /* randomized seed for hashes */
+ lu_byte currentwhite;
+ lu_byte gcstate; /* state of garbage collector */
+ lu_byte gckind; /* kind of GC running */
+ lu_byte gcstopem; /* stops emergency collections */
+ lu_byte genminormul; /* control for minor generational collections */
+ lu_byte genmajormul; /* control for major generational collections */
+ lu_byte gcstp; /* control whether GC is running */
+ lu_byte gcemergency; /* true if this is an emergency collection */
+ lu_byte gcpause; /* size of pause between successive GCs */
+ lu_byte gcstepmul; /* GC "speed" */
+ lu_byte gcstepsize; /* (log2 of) GC granularity */
+ GCObject *allgc; /* list of all collectable objects */
+ GCObject **sweepgc; /* current position of sweep in list */
+ GCObject *finobj; /* list of collectable objects with finalizers */
+ GCObject *gray; /* list of gray objects */
+ GCObject *grayagain; /* list of objects to be traversed atomically */
+ GCObject *weak; /* list of tables with weak values */
+ GCObject *ephemeron; /* list of ephemeron tables (weak keys) */
+ GCObject *allweak; /* list of all-weak tables */
+ GCObject *tobefnz; /* list of userdata to be GC */
+ GCObject *fixedgc; /* list of objects not to be collected */
+ /* fields for generational collector */
+ GCObject *survival; /* start of objects that survived one GC cycle */
+ GCObject *old1; /* start of old1 objects */
+ GCObject *reallyold; /* objects more than one cycle old ("really old") */
+ GCObject *firstold1; /* first OLD1 object in the list (if any) */
+ GCObject *finobjsur; /* list of survival objects with finalizers */
+ GCObject *finobjold1; /* list of old1 objects with finalizers */
+ GCObject *finobjrold; /* list of really old objects with finalizers */
+ struct lua_State *twups; /* list of threads with open upvalues */
+ lua_CFunction panic; /* to be called in unprotected errors */
+ struct lua_State *mainthread;
+ TString *memerrmsg; /* message for memory-allocation errors */
+ TString *tmname[TM_N]; /* array with tag-method names */
+ struct Table *mt[LUA_NUMTYPES]; /* metatables for basic types */
+ TString *strcache[STRCACHE_N][STRCACHE_M]; /* cache for strings in API */
+ lua_WarnFunction warnf; /* warning function */
+ void *ud_warn; /* auxiliary data to 'warnf' */
+} global_State;
+
+
+/*
+** 'per thread' state
+*/
+struct lua_State {
+ CommonHeader;
+ lu_byte status;
+ lu_byte allowhook;
+ unsigned short nci; /* number of items in 'ci' list */
+ StkIdRel top; /* first free slot in the stack */
+ global_State *l_G;
+ CallInfo *ci; /* call info for current function */
+ StkIdRel stack_last; /* end of stack (last element + 1) */
+ StkIdRel stack; /* stack base */
+ UpVal *openupval; /* list of open upvalues in this stack */
+ StkIdRel tbclist; /* list of to-be-closed variables */
+ GCObject *gclist;
+ struct lua_State *twups; /* list of threads with open upvalues */
+ struct lua_longjmp *errorJmp; /* current error recover point */
+ CallInfo base_ci; /* CallInfo for first level (C calling Lua) */
+ volatile lua_Hook hook;
+ ptrdiff_t errfunc; /* current error handling function (stack index) */
+ l_uint32 nCcalls; /* number of nested (non-yieldable | C) calls */
+ int oldpc; /* last pc traced */
+ int basehookcount;
+ int hookcount;
+ volatile l_signalT hookmask;
+};
+
+
+#define G(L) (L->l_G)
+
+/*
+** 'g->nilvalue' being a nil value flags that the state was completely
+** build.
+*/
+#define completestate(g) ttisnil(&g->nilvalue)
+
+
+/*
+** Union of all collectable objects (only for conversions)
+** ISO C99, 6.5.2.3 p.5:
+** "if a union contains several structures that share a common initial
+** sequence [...], and if the union object currently contains one
+** of these structures, it is permitted to inspect the common initial
+** part of any of them anywhere that a declaration of the complete type
+** of the union is visible."
+*/
+union GCUnion {
+ GCObject gc; /* common header */
+ struct TString ts;
+ struct Udata u;
+ union Closure cl;
+ struct Table h;
+ struct Proto p;
+ struct lua_State th; /* thread */
+ struct UpVal upv;
+};
+
+
+/*
+** ISO C99, 6.7.2.1 p.14:
+** "A pointer to a union object, suitably converted, points to each of
+** its members [...], and vice versa."
+*/
+#define cast_u(o) cast(union GCUnion *, (o))
+
+/* macros to convert a GCObject into a specific value */
+#define gco2ts(o) \
+ check_exp(novariant((o)->tt) == LUA_TSTRING, &((cast_u(o))->ts))
+#define gco2u(o) check_exp((o)->tt == LUA_VUSERDATA, &((cast_u(o))->u))
+#define gco2lcl(o) check_exp((o)->tt == LUA_VLCL, &((cast_u(o))->cl.l))
+#define gco2ccl(o) check_exp((o)->tt == LUA_VCCL, &((cast_u(o))->cl.c))
+#define gco2cl(o) \
+ check_exp(novariant((o)->tt) == LUA_TFUNCTION, &((cast_u(o))->cl))
+#define gco2t(o) check_exp((o)->tt == LUA_VTABLE, &((cast_u(o))->h))
+#define gco2p(o) check_exp((o)->tt == LUA_VPROTO, &((cast_u(o))->p))
+#define gco2th(o) check_exp((o)->tt == LUA_VTHREAD, &((cast_u(o))->th))
+#define gco2upv(o) check_exp((o)->tt == LUA_VUPVAL, &((cast_u(o))->upv))
+
+
+/*
+** macro to convert a Lua object into a GCObject
+** (The access to 'tt' tries to ensure that 'v' is actually a Lua object.)
+*/
+#define obj2gco(v) check_exp((v)->tt >= LUA_TSTRING, &(cast_u(v)->gc))
+
+
+/* actual number of total bytes allocated */
+#define gettotalbytes(g) cast(lu_mem, (g)->totalbytes + (g)->GCdebt)
+
+LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt);
+LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
+LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
+LUAI_FUNC void luaE_freeCI (lua_State *L);
+LUAI_FUNC void luaE_shrinkCI (lua_State *L);
+LUAI_FUNC void luaE_checkcstack (lua_State *L);
+LUAI_FUNC void luaE_incCstack (lua_State *L);
+LUAI_FUNC void luaE_warning (lua_State *L, const char *msg, int tocont);
+LUAI_FUNC void luaE_warnerror (lua_State *L, const char *where);
+LUAI_FUNC int luaE_resetthread (lua_State *L, int status);
+
+
+#endif
+
diff --git a/src/libs/3rdparty/lua/src/lstring.c b/src/libs/3rdparty/lua/src/lstring.c
new file mode 100644
index 0000000000..13dcaf4259
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lstring.c
@@ -0,0 +1,273 @@
+/*
+** $Id: lstring.c $
+** String table (keeps all strings handled by Lua)
+** See Copyright Notice in lua.h
+*/
+
+#define lstring_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <string.h>
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+
+
+/*
+** Maximum size for string table.
+*/
+#define MAXSTRTB cast_int(luaM_limitN(MAX_INT, TString*))
+
+
+/*
+** equality for long strings
+*/
+int luaS_eqlngstr (TString *a, TString *b) {
+ size_t len = a->u.lnglen;
+ lua_assert(a->tt == LUA_VLNGSTR && b->tt == LUA_VLNGSTR);
+ return (a == b) || /* same instance or... */
+ ((len == b->u.lnglen) && /* equal length and ... */
+ (memcmp(getstr(a), getstr(b), len) == 0)); /* equal contents */
+}
+
+
+unsigned int luaS_hash (const char *str, size_t l, unsigned int seed) {
+ unsigned int h = seed ^ cast_uint(l);
+ for (; l > 0; l--)
+ h ^= ((h<<5) + (h>>2) + cast_byte(str[l - 1]));
+ return h;
+}
+
+
+unsigned int luaS_hashlongstr (TString *ts) {
+ lua_assert(ts->tt == LUA_VLNGSTR);
+ if (ts->extra == 0) { /* no hash? */
+ size_t len = ts->u.lnglen;
+ ts->hash = luaS_hash(getstr(ts), len, ts->hash);
+ ts->extra = 1; /* now it has its hash */
+ }
+ return ts->hash;
+}
+
+
+static void tablerehash (TString **vect, int osize, int nsize) {
+ int i;
+ for (i = osize; i < nsize; i++) /* clear new elements */
+ vect[i] = NULL;
+ for (i = 0; i < osize; i++) { /* rehash old part of the array */
+ TString *p = vect[i];
+ vect[i] = NULL;
+ while (p) { /* for each string in the list */
+ TString *hnext = p->u.hnext; /* save next */
+ unsigned int h = lmod(p->hash, nsize); /* new position */
+ p->u.hnext = vect[h]; /* chain it into array */
+ vect[h] = p;
+ p = hnext;
+ }
+ }
+}
+
+
+/*
+** Resize the string table. If allocation fails, keep the current size.
+** (This can degrade performance, but any non-zero size should work
+** correctly.)
+*/
+void luaS_resize (lua_State *L, int nsize) {
+ stringtable *tb = &G(L)->strt;
+ int osize = tb->size;
+ TString **newvect;
+ if (nsize < osize) /* shrinking table? */
+ tablerehash(tb->hash, osize, nsize); /* depopulate shrinking part */
+ newvect = luaM_reallocvector(L, tb->hash, osize, nsize, TString*);
+ if (l_unlikely(newvect == NULL)) { /* reallocation failed? */
+ if (nsize < osize) /* was it shrinking table? */
+ tablerehash(tb->hash, nsize, osize); /* restore to original size */
+ /* leave table as it was */
+ }
+ else { /* allocation succeeded */
+ tb->hash = newvect;
+ tb->size = nsize;
+ if (nsize > osize)
+ tablerehash(newvect, osize, nsize); /* rehash for new size */
+ }
+}
+
+
+/*
+** Clear API string cache. (Entries cannot be empty, so fill them with
+** a non-collectable string.)
+*/
+void luaS_clearcache (global_State *g) {
+ int i, j;
+ for (i = 0; i < STRCACHE_N; i++)
+ for (j = 0; j < STRCACHE_M; j++) {
+ if (iswhite(g->strcache[i][j])) /* will entry be collected? */
+ g->strcache[i][j] = g->memerrmsg; /* replace it with something fixed */
+ }
+}
+
+
+/*
+** Initialize the string table and the string cache
+*/
+void luaS_init (lua_State *L) {
+ global_State *g = G(L);
+ int i, j;
+ stringtable *tb = &G(L)->strt;
+ tb->hash = luaM_newvector(L, MINSTRTABSIZE, TString*);
+ tablerehash(tb->hash, 0, MINSTRTABSIZE); /* clear array */
+ tb->size = MINSTRTABSIZE;
+ /* pre-create memory-error message */
+ g->memerrmsg = luaS_newliteral(L, MEMERRMSG);
+ luaC_fix(L, obj2gco(g->memerrmsg)); /* it should never be collected */
+ for (i = 0; i < STRCACHE_N; i++) /* fill cache with valid strings */
+ for (j = 0; j < STRCACHE_M; j++)
+ g->strcache[i][j] = g->memerrmsg;
+}
+
+
+
+/*
+** creates a new string object
+*/
+static TString *createstrobj (lua_State *L, size_t l, int tag, unsigned int h) {
+ TString *ts;
+ GCObject *o;
+ size_t totalsize; /* total size of TString object */
+ totalsize = sizelstring(l);
+ o = luaC_newobj(L, tag, totalsize);
+ ts = gco2ts(o);
+ ts->hash = h;
+ ts->extra = 0;
+ getstr(ts)[l] = '\0'; /* ending 0 */
+ return ts;
+}
+
+
+TString *luaS_createlngstrobj (lua_State *L, size_t l) {
+ TString *ts = createstrobj(L, l, LUA_VLNGSTR, G(L)->seed);
+ ts->u.lnglen = l;
+ return ts;
+}
+
+
+void luaS_remove (lua_State *L, TString *ts) {
+ stringtable *tb = &G(L)->strt;
+ TString **p = &tb->hash[lmod(ts->hash, tb->size)];
+ while (*p != ts) /* find previous element */
+ p = &(*p)->u.hnext;
+ *p = (*p)->u.hnext; /* remove element from its list */
+ tb->nuse--;
+}
+
+
+static void growstrtab (lua_State *L, stringtable *tb) {
+ if (l_unlikely(tb->nuse == MAX_INT)) { /* too many strings? */
+ luaC_fullgc(L, 1); /* try to free some... */
+ if (tb->nuse == MAX_INT) /* still too many? */
+ luaM_error(L); /* cannot even create a message... */
+ }
+ if (tb->size <= MAXSTRTB / 2) /* can grow string table? */
+ luaS_resize(L, tb->size * 2);
+}
+
+
+/*
+** Checks whether short string exists and reuses it or creates a new one.
+*/
+static TString *internshrstr (lua_State *L, const char *str, size_t l) {
+ TString *ts;
+ global_State *g = G(L);
+ stringtable *tb = &g->strt;
+ unsigned int h = luaS_hash(str, l, g->seed);
+ TString **list = &tb->hash[lmod(h, tb->size)];
+ lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */
+ for (ts = *list; ts != NULL; ts = ts->u.hnext) {
+ if (l == ts->shrlen && (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) {
+ /* found! */
+ if (isdead(g, ts)) /* dead (but not collected yet)? */
+ changewhite(ts); /* resurrect it */
+ return ts;
+ }
+ }
+ /* else must create a new string */
+ if (tb->nuse >= tb->size) { /* need to grow string table? */
+ growstrtab(L, tb);
+ list = &tb->hash[lmod(h, tb->size)]; /* rehash with new size */
+ }
+ ts = createstrobj(L, l, LUA_VSHRSTR, h);
+ memcpy(getstr(ts), str, l * sizeof(char));
+ ts->shrlen = cast_byte(l);
+ ts->u.hnext = *list;
+ *list = ts;
+ tb->nuse++;
+ return ts;
+}
+
+
+/*
+** new string (with explicit length)
+*/
+TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
+ if (l <= LUAI_MAXSHORTLEN) /* short string? */
+ return internshrstr(L, str, l);
+ else {
+ TString *ts;
+ if (l_unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char)))
+ luaM_toobig(L);
+ ts = luaS_createlngstrobj(L, l);
+ memcpy(getstr(ts), str, l * sizeof(char));
+ return ts;
+ }
+}
+
+
+/*
+** Create or reuse a zero-terminated string, first checking in the
+** cache (using the string address as a key). The cache can contain
+** only zero-terminated strings, so it is safe to use 'strcmp' to
+** check hits.
+*/
+TString *luaS_new (lua_State *L, const char *str) {
+ unsigned int i = point2uint(str) % STRCACHE_N; /* hash */
+ int j;
+ TString **p = G(L)->strcache[i];
+ for (j = 0; j < STRCACHE_M; j++) {
+ if (strcmp(str, getstr(p[j])) == 0) /* hit? */
+ return p[j]; /* that is it */
+ }
+ /* normal route */
+ for (j = STRCACHE_M - 1; j > 0; j--)
+ p[j] = p[j - 1]; /* move out last element */
+ /* new element is first in the list */
+ p[0] = luaS_newlstr(L, str, strlen(str));
+ return p[0];
+}
+
+
+Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue) {
+ Udata *u;
+ int i;
+ GCObject *o;
+ if (l_unlikely(s > MAX_SIZE - udatamemoffset(nuvalue)))
+ luaM_toobig(L);
+ o = luaC_newobj(L, LUA_VUSERDATA, sizeudata(nuvalue, s));
+ u = gco2u(o);
+ u->len = s;
+ u->nuvalue = nuvalue;
+ u->metatable = NULL;
+ for (i = 0; i < nuvalue; i++)
+ setnilvalue(&u->uv[i].uv);
+ return u;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lstring.h b/src/libs/3rdparty/lua/src/lstring.h
new file mode 100644
index 0000000000..450c2390d1
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lstring.h
@@ -0,0 +1,57 @@
+/*
+** $Id: lstring.h $
+** String table (keep all strings handled by Lua)
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lstring_h
+#define lstring_h
+
+#include "lgc.h"
+#include "lobject.h"
+#include "lstate.h"
+
+
+/*
+** Memory-allocation error message must be preallocated (it cannot
+** be created after memory is exhausted)
+*/
+#define MEMERRMSG "not enough memory"
+
+
+/*
+** Size of a TString: Size of the header plus space for the string
+** itself (including final '\0').
+*/
+#define sizelstring(l) (offsetof(TString, contents) + ((l) + 1) * sizeof(char))
+
+#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \
+ (sizeof(s)/sizeof(char))-1))
+
+
+/*
+** test whether a string is a reserved word
+*/
+#define isreserved(s) ((s)->tt == LUA_VSHRSTR && (s)->extra > 0)
+
+
+/*
+** equality for short strings, which are always internalized
+*/
+#define eqshrstr(a,b) check_exp((a)->tt == LUA_VSHRSTR, (a) == (b))
+
+
+LUAI_FUNC unsigned int luaS_hash (const char *str, size_t l, unsigned int seed);
+LUAI_FUNC unsigned int luaS_hashlongstr (TString *ts);
+LUAI_FUNC int luaS_eqlngstr (TString *a, TString *b);
+LUAI_FUNC void luaS_resize (lua_State *L, int newsize);
+LUAI_FUNC void luaS_clearcache (global_State *g);
+LUAI_FUNC void luaS_init (lua_State *L);
+LUAI_FUNC void luaS_remove (lua_State *L, TString *ts);
+LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue);
+LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l);
+LUAI_FUNC TString *luaS_new (lua_State *L, const char *str);
+LUAI_FUNC TString *luaS_createlngstrobj (lua_State *L, size_t l);
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lstrlib.c b/src/libs/3rdparty/lua/src/lstrlib.c
new file mode 100644
index 0000000000..03167161df
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lstrlib.c
@@ -0,0 +1,1874 @@
+/*
+** $Id: lstrlib.c $
+** Standard library for string operations and pattern-matching
+** See Copyright Notice in lua.h
+*/
+
+#define lstrlib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <ctype.h>
+#include <float.h>
+#include <limits.h>
+#include <locale.h>
+#include <math.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+/*
+** maximum number of captures that a pattern can do during
+** pattern-matching. This limit is arbitrary, but must fit in
+** an unsigned char.
+*/
+#if !defined(LUA_MAXCAPTURES)
+#define LUA_MAXCAPTURES 32
+#endif
+
+
+/* macro to 'unsign' a character */
+#define uchar(c) ((unsigned char)(c))
+
+
+/*
+** Some sizes are better limited to fit in 'int', but must also fit in
+** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.)
+*/
+#define MAX_SIZET ((size_t)(~(size_t)0))
+
+#define MAXSIZE \
+ (sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX))
+
+
+
+
+static int str_len (lua_State *L) {
+ size_t l;
+ luaL_checklstring(L, 1, &l);
+ lua_pushinteger(L, (lua_Integer)l);
+ return 1;
+}
+
+
+/*
+** translate a relative initial string position
+** (negative means back from end): clip result to [1, inf).
+** The length of any string in Lua must fit in a lua_Integer,
+** so there are no overflows in the casts.
+** The inverted comparison avoids a possible overflow
+** computing '-pos'.
+*/
+static size_t posrelatI (lua_Integer pos, size_t len) {
+ if (pos > 0)
+ return (size_t)pos;
+ else if (pos == 0)
+ return 1;
+ else if (pos < -(lua_Integer)len) /* inverted comparison */
+ return 1; /* clip to 1 */
+ else return len + (size_t)pos + 1;
+}
+
+
+/*
+** Gets an optional ending string position from argument 'arg',
+** with default value 'def'.
+** Negative means back from end: clip result to [0, len]
+*/
+static size_t getendpos (lua_State *L, int arg, lua_Integer def,
+ size_t len) {
+ lua_Integer pos = luaL_optinteger(L, arg, def);
+ if (pos > (lua_Integer)len)
+ return len;
+ else if (pos >= 0)
+ return (size_t)pos;
+ else if (pos < -(lua_Integer)len)
+ return 0;
+ else return len + (size_t)pos + 1;
+}
+
+
+static int str_sub (lua_State *L) {
+ size_t l;
+ const char *s = luaL_checklstring(L, 1, &l);
+ size_t start = posrelatI(luaL_checkinteger(L, 2), l);
+ size_t end = getendpos(L, 3, -1, l);
+ if (start <= end)
+ lua_pushlstring(L, s + start - 1, (end - start) + 1);
+ else lua_pushliteral(L, "");
+ return 1;
+}
+
+
+static int str_reverse (lua_State *L) {
+ size_t l, i;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ char *p = luaL_buffinitsize(L, &b, l);
+ for (i = 0; i < l; i++)
+ p[i] = s[l - i - 1];
+ luaL_pushresultsize(&b, l);
+ return 1;
+}
+
+
+static int str_lower (lua_State *L) {
+ size_t l;
+ size_t i;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ char *p = luaL_buffinitsize(L, &b, l);
+ for (i=0; i<l; i++)
+ p[i] = tolower(uchar(s[i]));
+ luaL_pushresultsize(&b, l);
+ return 1;
+}
+
+
+static int str_upper (lua_State *L) {
+ size_t l;
+ size_t i;
+ luaL_Buffer b;
+ const char *s = luaL_checklstring(L, 1, &l);
+ char *p = luaL_buffinitsize(L, &b, l);
+ for (i=0; i<l; i++)
+ p[i] = toupper(uchar(s[i]));
+ luaL_pushresultsize(&b, l);
+ return 1;
+}
+
+
+static int str_rep (lua_State *L) {
+ size_t l, lsep;
+ const char *s = luaL_checklstring(L, 1, &l);
+ lua_Integer n = luaL_checkinteger(L, 2);
+ const char *sep = luaL_optlstring(L, 3, "", &lsep);
+ if (n <= 0)
+ lua_pushliteral(L, "");
+ else if (l_unlikely(l + lsep < l || l + lsep > MAXSIZE / n))
+ return luaL_error(L, "resulting string too large");
+ else {
+ size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep;
+ luaL_Buffer b;
+ char *p = luaL_buffinitsize(L, &b, totallen);
+ while (n-- > 1) { /* first n-1 copies (followed by separator) */
+ memcpy(p, s, l * sizeof(char)); p += l;
+ if (lsep > 0) { /* empty 'memcpy' is not that cheap */
+ memcpy(p, sep, lsep * sizeof(char));
+ p += lsep;
+ }
+ }
+ memcpy(p, s, l * sizeof(char)); /* last copy (not followed by separator) */
+ luaL_pushresultsize(&b, totallen);
+ }
+ return 1;
+}
+
+
+static int str_byte (lua_State *L) {
+ size_t l;
+ const char *s = luaL_checklstring(L, 1, &l);
+ lua_Integer pi = luaL_optinteger(L, 2, 1);
+ size_t posi = posrelatI(pi, l);
+ size_t pose = getendpos(L, 3, pi, l);
+ int n, i;
+ if (posi > pose) return 0; /* empty interval; return no values */
+ if (l_unlikely(pose - posi >= (size_t)INT_MAX)) /* arithmetic overflow? */
+ return luaL_error(L, "string slice too long");
+ n = (int)(pose - posi) + 1;
+ luaL_checkstack(L, n, "string slice too long");
+ for (i=0; i<n; i++)
+ lua_pushinteger(L, uchar(s[posi+i-1]));
+ return n;
+}
+
+
+static int str_char (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ int i;
+ luaL_Buffer b;
+ char *p = luaL_buffinitsize(L, &b, n);
+ for (i=1; i<=n; i++) {
+ lua_Unsigned c = (lua_Unsigned)luaL_checkinteger(L, i);
+ luaL_argcheck(L, c <= (lua_Unsigned)UCHAR_MAX, i, "value out of range");
+ p[i - 1] = uchar(c);
+ }
+ luaL_pushresultsize(&b, n);
+ return 1;
+}
+
+
+/*
+** Buffer to store the result of 'string.dump'. It must be initialized
+** after the call to 'lua_dump', to ensure that the function is on the
+** top of the stack when 'lua_dump' is called. ('luaL_buffinit' might
+** push stuff.)
+*/
+struct str_Writer {
+ int init; /* true iff buffer has been initialized */
+ luaL_Buffer B;
+};
+
+
+static int writer (lua_State *L, const void *b, size_t size, void *ud) {
+ struct str_Writer *state = (struct str_Writer *)ud;
+ if (!state->init) {
+ state->init = 1;
+ luaL_buffinit(L, &state->B);
+ }
+ luaL_addlstring(&state->B, (const char *)b, size);
+ return 0;
+}
+
+
+static int str_dump (lua_State *L) {
+ struct str_Writer state;
+ int strip = lua_toboolean(L, 2);
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+ lua_settop(L, 1); /* ensure function is on the top of the stack */
+ state.init = 0;
+ if (l_unlikely(lua_dump(L, writer, &state, strip) != 0))
+ return luaL_error(L, "unable to dump given function");
+ luaL_pushresult(&state.B);
+ return 1;
+}
+
+
+
+/*
+** {======================================================
+** METAMETHODS
+** =======================================================
+*/
+
+#if defined(LUA_NOCVTS2N) /* { */
+
+/* no coercion from strings to numbers */
+
+static const luaL_Reg stringmetamethods[] = {
+ {"__index", NULL}, /* placeholder */
+ {NULL, NULL}
+};
+
+#else /* }{ */
+
+static int tonum (lua_State *L, int arg) {
+ if (lua_type(L, arg) == LUA_TNUMBER) { /* already a number? */
+ lua_pushvalue(L, arg);
+ return 1;
+ }
+ else { /* check whether it is a numerical string */
+ size_t len;
+ const char *s = lua_tolstring(L, arg, &len);
+ return (s != NULL && lua_stringtonumber(L, s) == len + 1);
+ }
+}
+
+
+static void trymt (lua_State *L, const char *mtname) {
+ lua_settop(L, 2); /* back to the original arguments */
+ if (l_unlikely(lua_type(L, 2) == LUA_TSTRING ||
+ !luaL_getmetafield(L, 2, mtname)))
+ luaL_error(L, "attempt to %s a '%s' with a '%s'", mtname + 2,
+ luaL_typename(L, -2), luaL_typename(L, -1));
+ lua_insert(L, -3); /* put metamethod before arguments */
+ lua_call(L, 2, 1); /* call metamethod */
+}
+
+
+static int arith (lua_State *L, int op, const char *mtname) {
+ if (tonum(L, 1) && tonum(L, 2))
+ lua_arith(L, op); /* result will be on the top */
+ else
+ trymt(L, mtname);
+ return 1;
+}
+
+
+static int arith_add (lua_State *L) {
+ return arith(L, LUA_OPADD, "__add");
+}
+
+static int arith_sub (lua_State *L) {
+ return arith(L, LUA_OPSUB, "__sub");
+}
+
+static int arith_mul (lua_State *L) {
+ return arith(L, LUA_OPMUL, "__mul");
+}
+
+static int arith_mod (lua_State *L) {
+ return arith(L, LUA_OPMOD, "__mod");
+}
+
+static int arith_pow (lua_State *L) {
+ return arith(L, LUA_OPPOW, "__pow");
+}
+
+static int arith_div (lua_State *L) {
+ return arith(L, LUA_OPDIV, "__div");
+}
+
+static int arith_idiv (lua_State *L) {
+ return arith(L, LUA_OPIDIV, "__idiv");
+}
+
+static int arith_unm (lua_State *L) {
+ return arith(L, LUA_OPUNM, "__unm");
+}
+
+
+static const luaL_Reg stringmetamethods[] = {
+ {"__add", arith_add},
+ {"__sub", arith_sub},
+ {"__mul", arith_mul},
+ {"__mod", arith_mod},
+ {"__pow", arith_pow},
+ {"__div", arith_div},
+ {"__idiv", arith_idiv},
+ {"__unm", arith_unm},
+ {"__index", NULL}, /* placeholder */
+ {NULL, NULL}
+};
+
+#endif /* } */
+
+/* }====================================================== */
+
+/*
+** {======================================================
+** PATTERN MATCHING
+** =======================================================
+*/
+
+
+#define CAP_UNFINISHED (-1)
+#define CAP_POSITION (-2)
+
+
+typedef struct MatchState {
+ const char *src_init; /* init of source string */
+ const char *src_end; /* end ('\0') of source string */
+ const char *p_end; /* end ('\0') of pattern */
+ lua_State *L;
+ int matchdepth; /* control for recursive depth (to avoid C stack overflow) */
+ unsigned char level; /* total number of captures (finished or unfinished) */
+ struct {
+ const char *init;
+ ptrdiff_t len;
+ } capture[LUA_MAXCAPTURES];
+} MatchState;
+
+
+/* recursive function */
+static const char *match (MatchState *ms, const char *s, const char *p);
+
+
+/* maximum recursion depth for 'match' */
+#if !defined(MAXCCALLS)
+#define MAXCCALLS 200
+#endif
+
+
+#define L_ESC '%'
+#define SPECIALS "^$*+?.([%-"
+
+
+static int check_capture (MatchState *ms, int l) {
+ l -= '1';
+ if (l_unlikely(l < 0 || l >= ms->level ||
+ ms->capture[l].len == CAP_UNFINISHED))
+ return luaL_error(ms->L, "invalid capture index %%%d", l + 1);
+ return l;
+}
+
+
+static int capture_to_close (MatchState *ms) {
+ int level = ms->level;
+ for (level--; level>=0; level--)
+ if (ms->capture[level].len == CAP_UNFINISHED) return level;
+ return luaL_error(ms->L, "invalid pattern capture");
+}
+
+
+static const char *classend (MatchState *ms, const char *p) {
+ switch (*p++) {
+ case L_ESC: {
+ if (l_unlikely(p == ms->p_end))
+ luaL_error(ms->L, "malformed pattern (ends with '%%')");
+ return p+1;
+ }
+ case '[': {
+ if (*p == '^') p++;
+ do { /* look for a ']' */
+ if (l_unlikely(p == ms->p_end))
+ luaL_error(ms->L, "malformed pattern (missing ']')");
+ if (*(p++) == L_ESC && p < ms->p_end)
+ p++; /* skip escapes (e.g. '%]') */
+ } while (*p != ']');
+ return p+1;
+ }
+ default: {
+ return p;
+ }
+ }
+}
+
+
+static int match_class (int c, int cl) {
+ int res;
+ switch (tolower(cl)) {
+ case 'a' : res = isalpha(c); break;
+ case 'c' : res = iscntrl(c); break;
+ case 'd' : res = isdigit(c); break;
+ case 'g' : res = isgraph(c); break;
+ case 'l' : res = islower(c); break;
+ case 'p' : res = ispunct(c); break;
+ case 's' : res = isspace(c); break;
+ case 'u' : res = isupper(c); break;
+ case 'w' : res = isalnum(c); break;
+ case 'x' : res = isxdigit(c); break;
+ case 'z' : res = (c == 0); break; /* deprecated option */
+ default: return (cl == c);
+ }
+ return (islower(cl) ? res : !res);
+}
+
+
+static int matchbracketclass (int c, const char *p, const char *ec) {
+ int sig = 1;
+ if (*(p+1) == '^') {
+ sig = 0;
+ p++; /* skip the '^' */
+ }
+ while (++p < ec) {
+ if (*p == L_ESC) {
+ p++;
+ if (match_class(c, uchar(*p)))
+ return sig;
+ }
+ else if ((*(p+1) == '-') && (p+2 < ec)) {
+ p+=2;
+ if (uchar(*(p-2)) <= c && c <= uchar(*p))
+ return sig;
+ }
+ else if (uchar(*p) == c) return sig;
+ }
+ return !sig;
+}
+
+
+static int singlematch (MatchState *ms, const char *s, const char *p,
+ const char *ep) {
+ if (s >= ms->src_end)
+ return 0;
+ else {
+ int c = uchar(*s);
+ switch (*p) {
+ case '.': return 1; /* matches any char */
+ case L_ESC: return match_class(c, uchar(*(p+1)));
+ case '[': return matchbracketclass(c, p, ep-1);
+ default: return (uchar(*p) == c);
+ }
+ }
+}
+
+
+static const char *matchbalance (MatchState *ms, const char *s,
+ const char *p) {
+ if (l_unlikely(p >= ms->p_end - 1))
+ luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')");
+ if (*s != *p) return NULL;
+ else {
+ int b = *p;
+ int e = *(p+1);
+ int cont = 1;
+ while (++s < ms->src_end) {
+ if (*s == e) {
+ if (--cont == 0) return s+1;
+ }
+ else if (*s == b) cont++;
+ }
+ }
+ return NULL; /* string ends out of balance */
+}
+
+
+static const char *max_expand (MatchState *ms, const char *s,
+ const char *p, const char *ep) {
+ ptrdiff_t i = 0; /* counts maximum expand for item */
+ while (singlematch(ms, s + i, p, ep))
+ i++;
+ /* keeps trying to match with the maximum repetitions */
+ while (i>=0) {
+ const char *res = match(ms, (s+i), ep+1);
+ if (res) return res;
+ i--; /* else didn't match; reduce 1 repetition to try again */
+ }
+ return NULL;
+}
+
+
+static const char *min_expand (MatchState *ms, const char *s,
+ const char *p, const char *ep) {
+ for (;;) {
+ const char *res = match(ms, s, ep+1);
+ if (res != NULL)
+ return res;
+ else if (singlematch(ms, s, p, ep))
+ s++; /* try with one more repetition */
+ else return NULL;
+ }
+}
+
+
+static const char *start_capture (MatchState *ms, const char *s,
+ const char *p, int what) {
+ const char *res;
+ int level = ms->level;
+ if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures");
+ ms->capture[level].init = s;
+ ms->capture[level].len = what;
+ ms->level = level+1;
+ if ((res=match(ms, s, p)) == NULL) /* match failed? */
+ ms->level--; /* undo capture */
+ return res;
+}
+
+
+static const char *end_capture (MatchState *ms, const char *s,
+ const char *p) {
+ int l = capture_to_close(ms);
+ const char *res;
+ ms->capture[l].len = s - ms->capture[l].init; /* close capture */
+ if ((res = match(ms, s, p)) == NULL) /* match failed? */
+ ms->capture[l].len = CAP_UNFINISHED; /* undo capture */
+ return res;
+}
+
+
+static const char *match_capture (MatchState *ms, const char *s, int l) {
+ size_t len;
+ l = check_capture(ms, l);
+ len = ms->capture[l].len;
+ if ((size_t)(ms->src_end-s) >= len &&
+ memcmp(ms->capture[l].init, s, len) == 0)
+ return s+len;
+ else return NULL;
+}
+
+
+static const char *match (MatchState *ms, const char *s, const char *p) {
+ if (l_unlikely(ms->matchdepth-- == 0))
+ luaL_error(ms->L, "pattern too complex");
+ init: /* using goto to optimize tail recursion */
+ if (p != ms->p_end) { /* end of pattern? */
+ switch (*p) {
+ case '(': { /* start capture */
+ if (*(p + 1) == ')') /* position capture? */
+ s = start_capture(ms, s, p + 2, CAP_POSITION);
+ else
+ s = start_capture(ms, s, p + 1, CAP_UNFINISHED);
+ break;
+ }
+ case ')': { /* end capture */
+ s = end_capture(ms, s, p + 1);
+ break;
+ }
+ case '$': {
+ if ((p + 1) != ms->p_end) /* is the '$' the last char in pattern? */
+ goto dflt; /* no; go to default */
+ s = (s == ms->src_end) ? s : NULL; /* check end of string */
+ break;
+ }
+ case L_ESC: { /* escaped sequences not in the format class[*+?-]? */
+ switch (*(p + 1)) {
+ case 'b': { /* balanced string? */
+ s = matchbalance(ms, s, p + 2);
+ if (s != NULL) {
+ p += 4; goto init; /* return match(ms, s, p + 4); */
+ } /* else fail (s == NULL) */
+ break;
+ }
+ case 'f': { /* frontier? */
+ const char *ep; char previous;
+ p += 2;
+ if (l_unlikely(*p != '['))
+ luaL_error(ms->L, "missing '[' after '%%f' in pattern");
+ ep = classend(ms, p); /* points to what is next */
+ previous = (s == ms->src_init) ? '\0' : *(s - 1);
+ if (!matchbracketclass(uchar(previous), p, ep - 1) &&
+ matchbracketclass(uchar(*s), p, ep - 1)) {
+ p = ep; goto init; /* return match(ms, s, ep); */
+ }
+ s = NULL; /* match failed */
+ break;
+ }
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case '8': case '9': { /* capture results (%0-%9)? */
+ s = match_capture(ms, s, uchar(*(p + 1)));
+ if (s != NULL) {
+ p += 2; goto init; /* return match(ms, s, p + 2) */
+ }
+ break;
+ }
+ default: goto dflt;
+ }
+ break;
+ }
+ default: dflt: { /* pattern class plus optional suffix */
+ const char *ep = classend(ms, p); /* points to optional suffix */
+ /* does not match at least once? */
+ if (!singlematch(ms, s, p, ep)) {
+ if (*ep == '*' || *ep == '?' || *ep == '-') { /* accept empty? */
+ p = ep + 1; goto init; /* return match(ms, s, ep + 1); */
+ }
+ else /* '+' or no suffix */
+ s = NULL; /* fail */
+ }
+ else { /* matched once */
+ switch (*ep) { /* handle optional suffix */
+ case '?': { /* optional */
+ const char *res;
+ if ((res = match(ms, s + 1, ep + 1)) != NULL)
+ s = res;
+ else {
+ p = ep + 1; goto init; /* else return match(ms, s, ep + 1); */
+ }
+ break;
+ }
+ case '+': /* 1 or more repetitions */
+ s++; /* 1 match already done */
+ /* FALLTHROUGH */
+ case '*': /* 0 or more repetitions */
+ s = max_expand(ms, s, p, ep);
+ break;
+ case '-': /* 0 or more repetitions (minimum) */
+ s = min_expand(ms, s, p, ep);
+ break;
+ default: /* no suffix */
+ s++; p = ep; goto init; /* return match(ms, s + 1, ep); */
+ }
+ }
+ break;
+ }
+ }
+ }
+ ms->matchdepth++;
+ return s;
+}
+
+
+
+static const char *lmemfind (const char *s1, size_t l1,
+ const char *s2, size_t l2) {
+ if (l2 == 0) return s1; /* empty strings are everywhere */
+ else if (l2 > l1) return NULL; /* avoids a negative 'l1' */
+ else {
+ const char *init; /* to search for a '*s2' inside 's1' */
+ l2--; /* 1st char will be checked by 'memchr' */
+ l1 = l1-l2; /* 's2' cannot be found after that */
+ while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) {
+ init++; /* 1st char is already checked */
+ if (memcmp(init, s2+1, l2) == 0)
+ return init-1;
+ else { /* correct 'l1' and 's1' to try again */
+ l1 -= init-s1;
+ s1 = init;
+ }
+ }
+ return NULL; /* not found */
+ }
+}
+
+
+/*
+** get information about the i-th capture. If there are no captures
+** and 'i==0', return information about the whole match, which
+** is the range 's'..'e'. If the capture is a string, return
+** its length and put its address in '*cap'. If it is an integer
+** (a position), push it on the stack and return CAP_POSITION.
+*/
+static size_t get_onecapture (MatchState *ms, int i, const char *s,
+ const char *e, const char **cap) {
+ if (i >= ms->level) {
+ if (l_unlikely(i != 0))
+ luaL_error(ms->L, "invalid capture index %%%d", i + 1);
+ *cap = s;
+ return e - s;
+ }
+ else {
+ ptrdiff_t capl = ms->capture[i].len;
+ *cap = ms->capture[i].init;
+ if (l_unlikely(capl == CAP_UNFINISHED))
+ luaL_error(ms->L, "unfinished capture");
+ else if (capl == CAP_POSITION)
+ lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1);
+ return capl;
+ }
+}
+
+
+/*
+** Push the i-th capture on the stack.
+*/
+static void push_onecapture (MatchState *ms, int i, const char *s,
+ const char *e) {
+ const char *cap;
+ ptrdiff_t l = get_onecapture(ms, i, s, e, &cap);
+ if (l != CAP_POSITION)
+ lua_pushlstring(ms->L, cap, l);
+ /* else position was already pushed */
+}
+
+
+static int push_captures (MatchState *ms, const char *s, const char *e) {
+ int i;
+ int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
+ luaL_checkstack(ms->L, nlevels, "too many captures");
+ for (i = 0; i < nlevels; i++)
+ push_onecapture(ms, i, s, e);
+ return nlevels; /* number of strings pushed */
+}
+
+
+/* check whether pattern has no special characters */
+static int nospecials (const char *p, size_t l) {
+ size_t upto = 0;
+ do {
+ if (strpbrk(p + upto, SPECIALS))
+ return 0; /* pattern has a special character */
+ upto += strlen(p + upto) + 1; /* may have more after \0 */
+ } while (upto <= l);
+ return 1; /* no special chars found */
+}
+
+
+static void prepstate (MatchState *ms, lua_State *L,
+ const char *s, size_t ls, const char *p, size_t lp) {
+ ms->L = L;
+ ms->matchdepth = MAXCCALLS;
+ ms->src_init = s;
+ ms->src_end = s + ls;
+ ms->p_end = p + lp;
+}
+
+
+static void reprepstate (MatchState *ms) {
+ ms->level = 0;
+ lua_assert(ms->matchdepth == MAXCCALLS);
+}
+
+
+static int str_find_aux (lua_State *L, int find) {
+ size_t ls, lp;
+ const char *s = luaL_checklstring(L, 1, &ls);
+ const char *p = luaL_checklstring(L, 2, &lp);
+ size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1;
+ if (init > ls) { /* start after string's end? */
+ luaL_pushfail(L); /* cannot find anything */
+ return 1;
+ }
+ /* explicit request or no special characters? */
+ if (find && (lua_toboolean(L, 4) || nospecials(p, lp))) {
+ /* do a plain search */
+ const char *s2 = lmemfind(s + init, ls - init, p, lp);
+ if (s2) {
+ lua_pushinteger(L, (s2 - s) + 1);
+ lua_pushinteger(L, (s2 - s) + lp);
+ return 2;
+ }
+ }
+ else {
+ MatchState ms;
+ const char *s1 = s + init;
+ int anchor = (*p == '^');
+ if (anchor) {
+ p++; lp--; /* skip anchor character */
+ }
+ prepstate(&ms, L, s, ls, p, lp);
+ do {
+ const char *res;
+ reprepstate(&ms);
+ if ((res=match(&ms, s1, p)) != NULL) {
+ if (find) {
+ lua_pushinteger(L, (s1 - s) + 1); /* start */
+ lua_pushinteger(L, res - s); /* end */
+ return push_captures(&ms, NULL, 0) + 2;
+ }
+ else
+ return push_captures(&ms, s1, res);
+ }
+ } while (s1++ < ms.src_end && !anchor);
+ }
+ luaL_pushfail(L); /* not found */
+ return 1;
+}
+
+
+static int str_find (lua_State *L) {
+ return str_find_aux(L, 1);
+}
+
+
+static int str_match (lua_State *L) {
+ return str_find_aux(L, 0);
+}
+
+
+/* state for 'gmatch' */
+typedef struct GMatchState {
+ const char *src; /* current position */
+ const char *p; /* pattern */
+ const char *lastmatch; /* end of last match */
+ MatchState ms; /* match state */
+} GMatchState;
+
+
+static int gmatch_aux (lua_State *L) {
+ GMatchState *gm = (GMatchState *)lua_touserdata(L, lua_upvalueindex(3));
+ const char *src;
+ gm->ms.L = L;
+ for (src = gm->src; src <= gm->ms.src_end; src++) {
+ const char *e;
+ reprepstate(&gm->ms);
+ if ((e = match(&gm->ms, src, gm->p)) != NULL && e != gm->lastmatch) {
+ gm->src = gm->lastmatch = e;
+ return push_captures(&gm->ms, src, e);
+ }
+ }
+ return 0; /* not found */
+}
+
+
+static int gmatch (lua_State *L) {
+ size_t ls, lp;
+ const char *s = luaL_checklstring(L, 1, &ls);
+ const char *p = luaL_checklstring(L, 2, &lp);
+ size_t init = posrelatI(luaL_optinteger(L, 3, 1), ls) - 1;
+ GMatchState *gm;
+ lua_settop(L, 2); /* keep strings on closure to avoid being collected */
+ gm = (GMatchState *)lua_newuserdatauv(L, sizeof(GMatchState), 0);
+ if (init > ls) /* start after string's end? */
+ init = ls + 1; /* avoid overflows in 's + init' */
+ prepstate(&gm->ms, L, s, ls, p, lp);
+ gm->src = s + init; gm->p = p; gm->lastmatch = NULL;
+ lua_pushcclosure(L, gmatch_aux, 3);
+ return 1;
+}
+
+
+static void add_s (MatchState *ms, luaL_Buffer *b, const char *s,
+ const char *e) {
+ size_t l;
+ lua_State *L = ms->L;
+ const char *news = lua_tolstring(L, 3, &l);
+ const char *p;
+ while ((p = (char *)memchr(news, L_ESC, l)) != NULL) {
+ luaL_addlstring(b, news, p - news);
+ p++; /* skip ESC */
+ if (*p == L_ESC) /* '%%' */
+ luaL_addchar(b, *p);
+ else if (*p == '0') /* '%0' */
+ luaL_addlstring(b, s, e - s);
+ else if (isdigit(uchar(*p))) { /* '%n' */
+ const char *cap;
+ ptrdiff_t resl = get_onecapture(ms, *p - '1', s, e, &cap);
+ if (resl == CAP_POSITION)
+ luaL_addvalue(b); /* add position to accumulated result */
+ else
+ luaL_addlstring(b, cap, resl);
+ }
+ else
+ luaL_error(L, "invalid use of '%c' in replacement string", L_ESC);
+ l -= p + 1 - news;
+ news = p + 1;
+ }
+ luaL_addlstring(b, news, l);
+}
+
+
+/*
+** Add the replacement value to the string buffer 'b'.
+** Return true if the original string was changed. (Function calls and
+** table indexing resulting in nil or false do not change the subject.)
+*/
+static int add_value (MatchState *ms, luaL_Buffer *b, const char *s,
+ const char *e, int tr) {
+ lua_State *L = ms->L;
+ switch (tr) {
+ case LUA_TFUNCTION: { /* call the function */
+ int n;
+ lua_pushvalue(L, 3); /* push the function */
+ n = push_captures(ms, s, e); /* all captures as arguments */
+ lua_call(L, n, 1); /* call it */
+ break;
+ }
+ case LUA_TTABLE: { /* index the table */
+ push_onecapture(ms, 0, s, e); /* first capture is the index */
+ lua_gettable(L, 3);
+ break;
+ }
+ default: { /* LUA_TNUMBER or LUA_TSTRING */
+ add_s(ms, b, s, e); /* add value to the buffer */
+ return 1; /* something changed */
+ }
+ }
+ if (!lua_toboolean(L, -1)) { /* nil or false? */
+ lua_pop(L, 1); /* remove value */
+ luaL_addlstring(b, s, e - s); /* keep original text */
+ return 0; /* no changes */
+ }
+ else if (l_unlikely(!lua_isstring(L, -1)))
+ return luaL_error(L, "invalid replacement value (a %s)",
+ luaL_typename(L, -1));
+ else {
+ luaL_addvalue(b); /* add result to accumulator */
+ return 1; /* something changed */
+ }
+}
+
+
+static int str_gsub (lua_State *L) {
+ size_t srcl, lp;
+ const char *src = luaL_checklstring(L, 1, &srcl); /* subject */
+ const char *p = luaL_checklstring(L, 2, &lp); /* pattern */
+ const char *lastmatch = NULL; /* end of last match */
+ int tr = lua_type(L, 3); /* replacement type */
+ lua_Integer max_s = luaL_optinteger(L, 4, srcl + 1); /* max replacements */
+ int anchor = (*p == '^');
+ lua_Integer n = 0; /* replacement count */
+ int changed = 0; /* change flag */
+ MatchState ms;
+ luaL_Buffer b;
+ luaL_argexpected(L, tr == LUA_TNUMBER || tr == LUA_TSTRING ||
+ tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3,
+ "string/function/table");
+ luaL_buffinit(L, &b);
+ if (anchor) {
+ p++; lp--; /* skip anchor character */
+ }
+ prepstate(&ms, L, src, srcl, p, lp);
+ while (n < max_s) {
+ const char *e;
+ reprepstate(&ms); /* (re)prepare state for new match */
+ if ((e = match(&ms, src, p)) != NULL && e != lastmatch) { /* match? */
+ n++;
+ changed = add_value(&ms, &b, src, e, tr) | changed;
+ src = lastmatch = e;
+ }
+ else if (src < ms.src_end) /* otherwise, skip one character */
+ luaL_addchar(&b, *src++);
+ else break; /* end of subject */
+ if (anchor) break;
+ }
+ if (!changed) /* no changes? */
+ lua_pushvalue(L, 1); /* return original string */
+ else { /* something changed */
+ luaL_addlstring(&b, src, ms.src_end-src);
+ luaL_pushresult(&b); /* create and return new string */
+ }
+ lua_pushinteger(L, n); /* number of substitutions */
+ return 2;
+}
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** STRING FORMAT
+** =======================================================
+*/
+
+#if !defined(lua_number2strx) /* { */
+
+/*
+** Hexadecimal floating-point formatter
+*/
+
+#define SIZELENMOD (sizeof(LUA_NUMBER_FRMLEN)/sizeof(char))
+
+
+/*
+** Number of bits that goes into the first digit. It can be any value
+** between 1 and 4; the following definition tries to align the number
+** to nibble boundaries by making what is left after that first digit a
+** multiple of 4.
+*/
+#define L_NBFD ((l_floatatt(MANT_DIG) - 1)%4 + 1)
+
+
+/*
+** Add integer part of 'x' to buffer and return new 'x'
+*/
+static lua_Number adddigit (char *buff, int n, lua_Number x) {
+ lua_Number dd = l_mathop(floor)(x); /* get integer part from 'x' */
+ int d = (int)dd;
+ buff[n] = (d < 10 ? d + '0' : d - 10 + 'a'); /* add to buffer */
+ return x - dd; /* return what is left */
+}
+
+
+static int num2straux (char *buff, int sz, lua_Number x) {
+ /* if 'inf' or 'NaN', format it like '%g' */
+ if (x != x || x == (lua_Number)HUGE_VAL || x == -(lua_Number)HUGE_VAL)
+ return l_sprintf(buff, sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)x);
+ else if (x == 0) { /* can be -0... */
+ /* create "0" or "-0" followed by exponent */
+ return l_sprintf(buff, sz, LUA_NUMBER_FMT "x0p+0", (LUAI_UACNUMBER)x);
+ }
+ else {
+ int e;
+ lua_Number m = l_mathop(frexp)(x, &e); /* 'x' fraction and exponent */
+ int n = 0; /* character count */
+ if (m < 0) { /* is number negative? */
+ buff[n++] = '-'; /* add sign */
+ m = -m; /* make it positive */
+ }
+ buff[n++] = '0'; buff[n++] = 'x'; /* add "0x" */
+ m = adddigit(buff, n++, m * (1 << L_NBFD)); /* add first digit */
+ e -= L_NBFD; /* this digit goes before the radix point */
+ if (m > 0) { /* more digits? */
+ buff[n++] = lua_getlocaledecpoint(); /* add radix point */
+ do { /* add as many digits as needed */
+ m = adddigit(buff, n++, m * 16);
+ } while (m > 0);
+ }
+ n += l_sprintf(buff + n, sz - n, "p%+d", e); /* add exponent */
+ lua_assert(n < sz);
+ return n;
+ }
+}
+
+
+static int lua_number2strx (lua_State *L, char *buff, int sz,
+ const char *fmt, lua_Number x) {
+ int n = num2straux(buff, sz, x);
+ if (fmt[SIZELENMOD] == 'A') {
+ int i;
+ for (i = 0; i < n; i++)
+ buff[i] = toupper(uchar(buff[i]));
+ }
+ else if (l_unlikely(fmt[SIZELENMOD] != 'a'))
+ return luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented");
+ return n;
+}
+
+#endif /* } */
+
+
+/*
+** Maximum size for items formatted with '%f'. This size is produced
+** by format('%.99f', -maxfloat), and is equal to 99 + 3 ('-', '.',
+** and '\0') + number of decimal digits to represent maxfloat (which
+** is maximum exponent + 1). (99+3+1, adding some extra, 110)
+*/
+#define MAX_ITEMF (110 + l_floatatt(MAX_10_EXP))
+
+
+/*
+** All formats except '%f' do not need that large limit. The other
+** float formats use exponents, so that they fit in the 99 limit for
+** significant digits; 's' for large strings and 'q' add items directly
+** to the buffer; all integer formats also fit in the 99 limit. The
+** worst case are floats: they may need 99 significant digits, plus
+** '0x', '-', '.', 'e+XXXX', and '\0'. Adding some extra, 120.
+*/
+#define MAX_ITEM 120
+
+
+/* valid flags in a format specification */
+#if !defined(L_FMTFLAGSF)
+
+/* valid flags for a, A, e, E, f, F, g, and G conversions */
+#define L_FMTFLAGSF "-+#0 "
+
+/* valid flags for o, x, and X conversions */
+#define L_FMTFLAGSX "-#0"
+
+/* valid flags for d and i conversions */
+#define L_FMTFLAGSI "-+0 "
+
+/* valid flags for u conversions */
+#define L_FMTFLAGSU "-0"
+
+/* valid flags for c, p, and s conversions */
+#define L_FMTFLAGSC "-"
+
+#endif
+
+
+/*
+** Maximum size of each format specification (such as "%-099.99d"):
+** Initial '%', flags (up to 5), width (2), period, precision (2),
+** length modifier (8), conversion specifier, and final '\0', plus some
+** extra.
+*/
+#define MAX_FORMAT 32
+
+
+static void addquoted (luaL_Buffer *b, const char *s, size_t len) {
+ luaL_addchar(b, '"');
+ while (len--) {
+ if (*s == '"' || *s == '\\' || *s == '\n') {
+ luaL_addchar(b, '\\');
+ luaL_addchar(b, *s);
+ }
+ else if (iscntrl(uchar(*s))) {
+ char buff[10];
+ if (!isdigit(uchar(*(s+1))))
+ l_sprintf(buff, sizeof(buff), "\\%d", (int)uchar(*s));
+ else
+ l_sprintf(buff, sizeof(buff), "\\%03d", (int)uchar(*s));
+ luaL_addstring(b, buff);
+ }
+ else
+ luaL_addchar(b, *s);
+ s++;
+ }
+ luaL_addchar(b, '"');
+}
+
+
+/*
+** Serialize a floating-point number in such a way that it can be
+** scanned back by Lua. Use hexadecimal format for "common" numbers
+** (to preserve precision); inf, -inf, and NaN are handled separately.
+** (NaN cannot be expressed as a numeral, so we write '(0/0)' for it.)
+*/
+static int quotefloat (lua_State *L, char *buff, lua_Number n) {
+ const char *s; /* for the fixed representations */
+ if (n == (lua_Number)HUGE_VAL) /* inf? */
+ s = "1e9999";
+ else if (n == -(lua_Number)HUGE_VAL) /* -inf? */
+ s = "-1e9999";
+ else if (n != n) /* NaN? */
+ s = "(0/0)";
+ else { /* format number as hexadecimal */
+ int nb = lua_number2strx(L, buff, MAX_ITEM,
+ "%" LUA_NUMBER_FRMLEN "a", n);
+ /* ensures that 'buff' string uses a dot as the radix character */
+ if (memchr(buff, '.', nb) == NULL) { /* no dot? */
+ char point = lua_getlocaledecpoint(); /* try locale point */
+ char *ppoint = (char *)memchr(buff, point, nb);
+ if (ppoint) *ppoint = '.'; /* change it to a dot */
+ }
+ return nb;
+ }
+ /* for the fixed representations */
+ return l_sprintf(buff, MAX_ITEM, "%s", s);
+}
+
+
+static void addliteral (lua_State *L, luaL_Buffer *b, int arg) {
+ switch (lua_type(L, arg)) {
+ case LUA_TSTRING: {
+ size_t len;
+ const char *s = lua_tolstring(L, arg, &len);
+ addquoted(b, s, len);
+ break;
+ }
+ case LUA_TNUMBER: {
+ char *buff = luaL_prepbuffsize(b, MAX_ITEM);
+ int nb;
+ if (!lua_isinteger(L, arg)) /* float? */
+ nb = quotefloat(L, buff, lua_tonumber(L, arg));
+ else { /* integers */
+ lua_Integer n = lua_tointeger(L, arg);
+ const char *format = (n == LUA_MININTEGER) /* corner case? */
+ ? "0x%" LUA_INTEGER_FRMLEN "x" /* use hex */
+ : LUA_INTEGER_FMT; /* else use default format */
+ nb = l_sprintf(buff, MAX_ITEM, format, (LUAI_UACINT)n);
+ }
+ luaL_addsize(b, nb);
+ break;
+ }
+ case LUA_TNIL: case LUA_TBOOLEAN: {
+ luaL_tolstring(L, arg, NULL);
+ luaL_addvalue(b);
+ break;
+ }
+ default: {
+ luaL_argerror(L, arg, "value has no literal form");
+ }
+ }
+}
+
+
+static const char *get2digits (const char *s) {
+ if (isdigit(uchar(*s))) {
+ s++;
+ if (isdigit(uchar(*s))) s++; /* (2 digits at most) */
+ }
+ return s;
+}
+
+
+/*
+** Check whether a conversion specification is valid. When called,
+** first character in 'form' must be '%' and last character must
+** be a valid conversion specifier. 'flags' are the accepted flags;
+** 'precision' signals whether to accept a precision.
+*/
+static void checkformat (lua_State *L, const char *form, const char *flags,
+ int precision) {
+ const char *spec = form + 1; /* skip '%' */
+ spec += strspn(spec, flags); /* skip flags */
+ if (*spec != '0') { /* a width cannot start with '0' */
+ spec = get2digits(spec); /* skip width */
+ if (*spec == '.' && precision) {
+ spec++;
+ spec = get2digits(spec); /* skip precision */
+ }
+ }
+ if (!isalpha(uchar(*spec))) /* did not go to the end? */
+ luaL_error(L, "invalid conversion specification: '%s'", form);
+}
+
+
+/*
+** Get a conversion specification and copy it to 'form'.
+** Return the address of its last character.
+*/
+static const char *getformat (lua_State *L, const char *strfrmt,
+ char *form) {
+ /* spans flags, width, and precision ('0' is included as a flag) */
+ size_t len = strspn(strfrmt, L_FMTFLAGSF "123456789.");
+ len++; /* adds following character (should be the specifier) */
+ /* still needs space for '%', '\0', plus a length modifier */
+ if (len >= MAX_FORMAT - 10)
+ luaL_error(L, "invalid format (too long)");
+ *(form++) = '%';
+ memcpy(form, strfrmt, len * sizeof(char));
+ *(form + len) = '\0';
+ return strfrmt + len - 1;
+}
+
+
+/*
+** add length modifier into formats
+*/
+static void addlenmod (char *form, const char *lenmod) {
+ size_t l = strlen(form);
+ size_t lm = strlen(lenmod);
+ char spec = form[l - 1];
+ strcpy(form + l - 1, lenmod);
+ form[l + lm - 1] = spec;
+ form[l + lm] = '\0';
+}
+
+
+static int str_format (lua_State *L) {
+ int top = lua_gettop(L);
+ int arg = 1;
+ size_t sfl;
+ const char *strfrmt = luaL_checklstring(L, arg, &sfl);
+ const char *strfrmt_end = strfrmt+sfl;
+ const char *flags;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ while (strfrmt < strfrmt_end) {
+ if (*strfrmt != L_ESC)
+ luaL_addchar(&b, *strfrmt++);
+ else if (*++strfrmt == L_ESC)
+ luaL_addchar(&b, *strfrmt++); /* %% */
+ else { /* format item */
+ char form[MAX_FORMAT]; /* to store the format ('%...') */
+ int maxitem = MAX_ITEM; /* maximum length for the result */
+ char *buff = luaL_prepbuffsize(&b, maxitem); /* to put result */
+ int nb = 0; /* number of bytes in result */
+ if (++arg > top)
+ return luaL_argerror(L, arg, "no value");
+ strfrmt = getformat(L, strfrmt, form);
+ switch (*strfrmt++) {
+ case 'c': {
+ checkformat(L, form, L_FMTFLAGSC, 0);
+ nb = l_sprintf(buff, maxitem, form, (int)luaL_checkinteger(L, arg));
+ break;
+ }
+ case 'd': case 'i':
+ flags = L_FMTFLAGSI;
+ goto intcase;
+ case 'u':
+ flags = L_FMTFLAGSU;
+ goto intcase;
+ case 'o': case 'x': case 'X':
+ flags = L_FMTFLAGSX;
+ intcase: {
+ lua_Integer n = luaL_checkinteger(L, arg);
+ checkformat(L, form, flags, 1);
+ addlenmod(form, LUA_INTEGER_FRMLEN);
+ nb = l_sprintf(buff, maxitem, form, (LUAI_UACINT)n);
+ break;
+ }
+ case 'a': case 'A':
+ checkformat(L, form, L_FMTFLAGSF, 1);
+ addlenmod(form, LUA_NUMBER_FRMLEN);
+ nb = lua_number2strx(L, buff, maxitem, form,
+ luaL_checknumber(L, arg));
+ break;
+ case 'f':
+ maxitem = MAX_ITEMF; /* extra space for '%f' */
+ buff = luaL_prepbuffsize(&b, maxitem);
+ /* FALLTHROUGH */
+ case 'e': case 'E': case 'g': case 'G': {
+ lua_Number n = luaL_checknumber(L, arg);
+ checkformat(L, form, L_FMTFLAGSF, 1);
+ addlenmod(form, LUA_NUMBER_FRMLEN);
+ nb = l_sprintf(buff, maxitem, form, (LUAI_UACNUMBER)n);
+ break;
+ }
+ case 'p': {
+ const void *p = lua_topointer(L, arg);
+ checkformat(L, form, L_FMTFLAGSC, 0);
+ if (p == NULL) { /* avoid calling 'printf' with argument NULL */
+ p = "(null)"; /* result */
+ form[strlen(form) - 1] = 's'; /* format it as a string */
+ }
+ nb = l_sprintf(buff, maxitem, form, p);
+ break;
+ }
+ case 'q': {
+ if (form[2] != '\0') /* modifiers? */
+ return luaL_error(L, "specifier '%%q' cannot have modifiers");
+ addliteral(L, &b, arg);
+ break;
+ }
+ case 's': {
+ size_t l;
+ const char *s = luaL_tolstring(L, arg, &l);
+ if (form[2] == '\0') /* no modifiers? */
+ luaL_addvalue(&b); /* keep entire string */
+ else {
+ luaL_argcheck(L, l == strlen(s), arg, "string contains zeros");
+ checkformat(L, form, L_FMTFLAGSC, 1);
+ if (strchr(form, '.') == NULL && l >= 100) {
+ /* no precision and string is too long to be formatted */
+ luaL_addvalue(&b); /* keep entire string */
+ }
+ else { /* format the string into 'buff' */
+ nb = l_sprintf(buff, maxitem, form, s);
+ lua_pop(L, 1); /* remove result from 'luaL_tolstring' */
+ }
+ }
+ break;
+ }
+ default: { /* also treat cases 'pnLlh' */
+ return luaL_error(L, "invalid conversion '%s' to 'format'", form);
+ }
+ }
+ lua_assert(nb < maxitem);
+ luaL_addsize(&b, nb);
+ }
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+/* }====================================================== */
+
+
+/*
+** {======================================================
+** PACK/UNPACK
+** =======================================================
+*/
+
+
+/* value used for padding */
+#if !defined(LUAL_PACKPADBYTE)
+#define LUAL_PACKPADBYTE 0x00
+#endif
+
+/* maximum size for the binary representation of an integer */
+#define MAXINTSIZE 16
+
+/* number of bits in a character */
+#define NB CHAR_BIT
+
+/* mask for one character (NB 1's) */
+#define MC ((1 << NB) - 1)
+
+/* size of a lua_Integer */
+#define SZINT ((int)sizeof(lua_Integer))
+
+
+/* dummy union to get native endianness */
+static const union {
+ int dummy;
+ char little; /* true iff machine is little endian */
+} nativeendian = {1};
+
+
+/*
+** information to pack/unpack stuff
+*/
+typedef struct Header {
+ lua_State *L;
+ int islittle;
+ int maxalign;
+} Header;
+
+
+/*
+** options for pack/unpack
+*/
+typedef enum KOption {
+ Kint, /* signed integers */
+ Kuint, /* unsigned integers */
+ Kfloat, /* single-precision floating-point numbers */
+ Knumber, /* Lua "native" floating-point numbers */
+ Kdouble, /* double-precision floating-point numbers */
+ Kchar, /* fixed-length strings */
+ Kstring, /* strings with prefixed length */
+ Kzstr, /* zero-terminated strings */
+ Kpadding, /* padding */
+ Kpaddalign, /* padding for alignment */
+ Knop /* no-op (configuration or spaces) */
+} KOption;
+
+
+/*
+** Read an integer numeral from string 'fmt' or return 'df' if
+** there is no numeral
+*/
+static int digit (int c) { return '0' <= c && c <= '9'; }
+
+static int getnum (const char **fmt, int df) {
+ if (!digit(**fmt)) /* no number? */
+ return df; /* return default value */
+ else {
+ int a = 0;
+ do {
+ a = a*10 + (*((*fmt)++) - '0');
+ } while (digit(**fmt) && a <= ((int)MAXSIZE - 9)/10);
+ return a;
+ }
+}
+
+
+/*
+** Read an integer numeral and raises an error if it is larger
+** than the maximum size for integers.
+*/
+static int getnumlimit (Header *h, const char **fmt, int df) {
+ int sz = getnum(fmt, df);
+ if (l_unlikely(sz > MAXINTSIZE || sz <= 0))
+ return luaL_error(h->L, "integral size (%d) out of limits [1,%d]",
+ sz, MAXINTSIZE);
+ return sz;
+}
+
+
+/*
+** Initialize Header
+*/
+static void initheader (lua_State *L, Header *h) {
+ h->L = L;
+ h->islittle = nativeendian.little;
+ h->maxalign = 1;
+}
+
+
+/*
+** Read and classify next option. 'size' is filled with option's size.
+*/
+static KOption getoption (Header *h, const char **fmt, int *size) {
+ /* dummy structure to get native alignment requirements */
+ struct cD { char c; union { LUAI_MAXALIGN; } u; };
+ int opt = *((*fmt)++);
+ *size = 0; /* default */
+ switch (opt) {
+ case 'b': *size = sizeof(char); return Kint;
+ case 'B': *size = sizeof(char); return Kuint;
+ case 'h': *size = sizeof(short); return Kint;
+ case 'H': *size = sizeof(short); return Kuint;
+ case 'l': *size = sizeof(long); return Kint;
+ case 'L': *size = sizeof(long); return Kuint;
+ case 'j': *size = sizeof(lua_Integer); return Kint;
+ case 'J': *size = sizeof(lua_Integer); return Kuint;
+ case 'T': *size = sizeof(size_t); return Kuint;
+ case 'f': *size = sizeof(float); return Kfloat;
+ case 'n': *size = sizeof(lua_Number); return Knumber;
+ case 'd': *size = sizeof(double); return Kdouble;
+ case 'i': *size = getnumlimit(h, fmt, sizeof(int)); return Kint;
+ case 'I': *size = getnumlimit(h, fmt, sizeof(int)); return Kuint;
+ case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring;
+ case 'c':
+ *size = getnum(fmt, -1);
+ if (l_unlikely(*size == -1))
+ luaL_error(h->L, "missing size for format option 'c'");
+ return Kchar;
+ case 'z': return Kzstr;
+ case 'x': *size = 1; return Kpadding;
+ case 'X': return Kpaddalign;
+ case ' ': break;
+ case '<': h->islittle = 1; break;
+ case '>': h->islittle = 0; break;
+ case '=': h->islittle = nativeendian.little; break;
+ case '!': {
+ const int maxalign = offsetof(struct cD, u);
+ h->maxalign = getnumlimit(h, fmt, maxalign);
+ break;
+ }
+ default: luaL_error(h->L, "invalid format option '%c'", opt);
+ }
+ return Knop;
+}
+
+
+/*
+** Read, classify, and fill other details about the next option.
+** 'psize' is filled with option's size, 'notoalign' with its
+** alignment requirements.
+** Local variable 'size' gets the size to be aligned. (Kpadal option
+** always gets its full alignment, other options are limited by
+** the maximum alignment ('maxalign'). Kchar option needs no alignment
+** despite its size.
+*/
+static KOption getdetails (Header *h, size_t totalsize,
+ const char **fmt, int *psize, int *ntoalign) {
+ KOption opt = getoption(h, fmt, psize);
+ int align = *psize; /* usually, alignment follows size */
+ if (opt == Kpaddalign) { /* 'X' gets alignment from following option */
+ if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0)
+ luaL_argerror(h->L, 1, "invalid next option for option 'X'");
+ }
+ if (align <= 1 || opt == Kchar) /* need no alignment? */
+ *ntoalign = 0;
+ else {
+ if (align > h->maxalign) /* enforce maximum alignment */
+ align = h->maxalign;
+ if (l_unlikely((align & (align - 1)) != 0)) /* not a power of 2? */
+ luaL_argerror(h->L, 1, "format asks for alignment not power of 2");
+ *ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1);
+ }
+ return opt;
+}
+
+
+/*
+** Pack integer 'n' with 'size' bytes and 'islittle' endianness.
+** The final 'if' handles the case when 'size' is larger than
+** the size of a Lua integer, correcting the extra sign-extension
+** bytes if necessary (by default they would be zeros).
+*/
+static void packint (luaL_Buffer *b, lua_Unsigned n,
+ int islittle, int size, int neg) {
+ char *buff = luaL_prepbuffsize(b, size);
+ int i;
+ buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */
+ for (i = 1; i < size; i++) {
+ n >>= NB;
+ buff[islittle ? i : size - 1 - i] = (char)(n & MC);
+ }
+ if (neg && size > SZINT) { /* negative number need sign extension? */
+ for (i = SZINT; i < size; i++) /* correct extra bytes */
+ buff[islittle ? i : size - 1 - i] = (char)MC;
+ }
+ luaL_addsize(b, size); /* add result to buffer */
+}
+
+
+/*
+** Copy 'size' bytes from 'src' to 'dest', correcting endianness if
+** given 'islittle' is different from native endianness.
+*/
+static void copywithendian (char *dest, const char *src,
+ int size, int islittle) {
+ if (islittle == nativeendian.little)
+ memcpy(dest, src, size);
+ else {
+ dest += size - 1;
+ while (size-- != 0)
+ *(dest--) = *(src++);
+ }
+}
+
+
+static int str_pack (lua_State *L) {
+ luaL_Buffer b;
+ Header h;
+ const char *fmt = luaL_checkstring(L, 1); /* format string */
+ int arg = 1; /* current argument to pack */
+ size_t totalsize = 0; /* accumulate total size of result */
+ initheader(L, &h);
+ lua_pushnil(L); /* mark to separate arguments from string buffer */
+ luaL_buffinit(L, &b);
+ while (*fmt != '\0') {
+ int size, ntoalign;
+ KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign);
+ totalsize += ntoalign + size;
+ while (ntoalign-- > 0)
+ luaL_addchar(&b, LUAL_PACKPADBYTE); /* fill alignment */
+ arg++;
+ switch (opt) {
+ case Kint: { /* signed integers */
+ lua_Integer n = luaL_checkinteger(L, arg);
+ if (size < SZINT) { /* need overflow check? */
+ lua_Integer lim = (lua_Integer)1 << ((size * NB) - 1);
+ luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow");
+ }
+ packint(&b, (lua_Unsigned)n, h.islittle, size, (n < 0));
+ break;
+ }
+ case Kuint: { /* unsigned integers */
+ lua_Integer n = luaL_checkinteger(L, arg);
+ if (size < SZINT) /* need overflow check? */
+ luaL_argcheck(L, (lua_Unsigned)n < ((lua_Unsigned)1 << (size * NB)),
+ arg, "unsigned overflow");
+ packint(&b, (lua_Unsigned)n, h.islittle, size, 0);
+ break;
+ }
+ case Kfloat: { /* C float */
+ float f = (float)luaL_checknumber(L, arg); /* get argument */
+ char *buff = luaL_prepbuffsize(&b, sizeof(f));
+ /* move 'f' to final result, correcting endianness if needed */
+ copywithendian(buff, (char *)&f, sizeof(f), h.islittle);
+ luaL_addsize(&b, size);
+ break;
+ }
+ case Knumber: { /* Lua float */
+ lua_Number f = luaL_checknumber(L, arg); /* get argument */
+ char *buff = luaL_prepbuffsize(&b, sizeof(f));
+ /* move 'f' to final result, correcting endianness if needed */
+ copywithendian(buff, (char *)&f, sizeof(f), h.islittle);
+ luaL_addsize(&b, size);
+ break;
+ }
+ case Kdouble: { /* C double */
+ double f = (double)luaL_checknumber(L, arg); /* get argument */
+ char *buff = luaL_prepbuffsize(&b, sizeof(f));
+ /* move 'f' to final result, correcting endianness if needed */
+ copywithendian(buff, (char *)&f, sizeof(f), h.islittle);
+ luaL_addsize(&b, size);
+ break;
+ }
+ case Kchar: { /* fixed-size string */
+ size_t len;
+ const char *s = luaL_checklstring(L, arg, &len);
+ luaL_argcheck(L, len <= (size_t)size, arg,
+ "string longer than given size");
+ luaL_addlstring(&b, s, len); /* add string */
+ while (len++ < (size_t)size) /* pad extra space */
+ luaL_addchar(&b, LUAL_PACKPADBYTE);
+ break;
+ }
+ case Kstring: { /* strings with length count */
+ size_t len;
+ const char *s = luaL_checklstring(L, arg, &len);
+ luaL_argcheck(L, size >= (int)sizeof(size_t) ||
+ len < ((size_t)1 << (size * NB)),
+ arg, "string length does not fit in given size");
+ packint(&b, (lua_Unsigned)len, h.islittle, size, 0); /* pack length */
+ luaL_addlstring(&b, s, len);
+ totalsize += len;
+ break;
+ }
+ case Kzstr: { /* zero-terminated string */
+ size_t len;
+ const char *s = luaL_checklstring(L, arg, &len);
+ luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros");
+ luaL_addlstring(&b, s, len);
+ luaL_addchar(&b, '\0'); /* add zero at the end */
+ totalsize += len + 1;
+ break;
+ }
+ case Kpadding: luaL_addchar(&b, LUAL_PACKPADBYTE); /* FALLTHROUGH */
+ case Kpaddalign: case Knop:
+ arg--; /* undo increment */
+ break;
+ }
+ }
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+static int str_packsize (lua_State *L) {
+ Header h;
+ const char *fmt = luaL_checkstring(L, 1); /* format string */
+ size_t totalsize = 0; /* accumulate total size of result */
+ initheader(L, &h);
+ while (*fmt != '\0') {
+ int size, ntoalign;
+ KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign);
+ luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1,
+ "variable-length format");
+ size += ntoalign; /* total space used by option */
+ luaL_argcheck(L, totalsize <= MAXSIZE - size, 1,
+ "format result too large");
+ totalsize += size;
+ }
+ lua_pushinteger(L, (lua_Integer)totalsize);
+ return 1;
+}
+
+
+/*
+** Unpack an integer with 'size' bytes and 'islittle' endianness.
+** If size is smaller than the size of a Lua integer and integer
+** is signed, must do sign extension (propagating the sign to the
+** higher bits); if size is larger than the size of a Lua integer,
+** it must check the unread bytes to see whether they do not cause an
+** overflow.
+*/
+static lua_Integer unpackint (lua_State *L, const char *str,
+ int islittle, int size, int issigned) {
+ lua_Unsigned res = 0;
+ int i;
+ int limit = (size <= SZINT) ? size : SZINT;
+ for (i = limit - 1; i >= 0; i--) {
+ res <<= NB;
+ res |= (lua_Unsigned)(unsigned char)str[islittle ? i : size - 1 - i];
+ }
+ if (size < SZINT) { /* real size smaller than lua_Integer? */
+ if (issigned) { /* needs sign extension? */
+ lua_Unsigned mask = (lua_Unsigned)1 << (size*NB - 1);
+ res = ((res ^ mask) - mask); /* do sign extension */
+ }
+ }
+ else if (size > SZINT) { /* must check unread bytes */
+ int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC;
+ for (i = limit; i < size; i++) {
+ if (l_unlikely((unsigned char)str[islittle ? i : size - 1 - i] != mask))
+ luaL_error(L, "%d-byte integer does not fit into Lua Integer", size);
+ }
+ }
+ return (lua_Integer)res;
+}
+
+
+static int str_unpack (lua_State *L) {
+ Header h;
+ const char *fmt = luaL_checkstring(L, 1);
+ size_t ld;
+ const char *data = luaL_checklstring(L, 2, &ld);
+ size_t pos = posrelatI(luaL_optinteger(L, 3, 1), ld) - 1;
+ int n = 0; /* number of results */
+ luaL_argcheck(L, pos <= ld, 3, "initial position out of string");
+ initheader(L, &h);
+ while (*fmt != '\0') {
+ int size, ntoalign;
+ KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign);
+ luaL_argcheck(L, (size_t)ntoalign + size <= ld - pos, 2,
+ "data string too short");
+ pos += ntoalign; /* skip alignment */
+ /* stack space for item + next position */
+ luaL_checkstack(L, 2, "too many results");
+ n++;
+ switch (opt) {
+ case Kint:
+ case Kuint: {
+ lua_Integer res = unpackint(L, data + pos, h.islittle, size,
+ (opt == Kint));
+ lua_pushinteger(L, res);
+ break;
+ }
+ case Kfloat: {
+ float f;
+ copywithendian((char *)&f, data + pos, sizeof(f), h.islittle);
+ lua_pushnumber(L, (lua_Number)f);
+ break;
+ }
+ case Knumber: {
+ lua_Number f;
+ copywithendian((char *)&f, data + pos, sizeof(f), h.islittle);
+ lua_pushnumber(L, f);
+ break;
+ }
+ case Kdouble: {
+ double f;
+ copywithendian((char *)&f, data + pos, sizeof(f), h.islittle);
+ lua_pushnumber(L, (lua_Number)f);
+ break;
+ }
+ case Kchar: {
+ lua_pushlstring(L, data + pos, size);
+ break;
+ }
+ case Kstring: {
+ size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0);
+ luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short");
+ lua_pushlstring(L, data + pos + size, len);
+ pos += len; /* skip string */
+ break;
+ }
+ case Kzstr: {
+ size_t len = strlen(data + pos);
+ luaL_argcheck(L, pos + len < ld, 2,
+ "unfinished string for format 'z'");
+ lua_pushlstring(L, data + pos, len);
+ pos += len + 1; /* skip string plus final '\0' */
+ break;
+ }
+ case Kpaddalign: case Kpadding: case Knop:
+ n--; /* undo increment */
+ break;
+ }
+ pos += size;
+ }
+ lua_pushinteger(L, pos + 1); /* next position */
+ return n + 1;
+}
+
+/* }====================================================== */
+
+
+static const luaL_Reg strlib[] = {
+ {"byte", str_byte},
+ {"char", str_char},
+ {"dump", str_dump},
+ {"find", str_find},
+ {"format", str_format},
+ {"gmatch", gmatch},
+ {"gsub", str_gsub},
+ {"len", str_len},
+ {"lower", str_lower},
+ {"match", str_match},
+ {"rep", str_rep},
+ {"reverse", str_reverse},
+ {"sub", str_sub},
+ {"upper", str_upper},
+ {"pack", str_pack},
+ {"packsize", str_packsize},
+ {"unpack", str_unpack},
+ {NULL, NULL}
+};
+
+
+static void createmetatable (lua_State *L) {
+ /* table to be metatable for strings */
+ luaL_newlibtable(L, stringmetamethods);
+ luaL_setfuncs(L, stringmetamethods, 0);
+ lua_pushliteral(L, ""); /* dummy string */
+ lua_pushvalue(L, -2); /* copy table */
+ lua_setmetatable(L, -2); /* set table as metatable for strings */
+ lua_pop(L, 1); /* pop dummy string */
+ lua_pushvalue(L, -2); /* get string library */
+ lua_setfield(L, -2, "__index"); /* metatable.__index = string */
+ lua_pop(L, 1); /* pop metatable */
+}
+
+
+/*
+** Open string library
+*/
+LUAMOD_API int luaopen_string (lua_State *L) {
+ luaL_newlib(L, strlib);
+ createmetatable(L);
+ return 1;
+}
+
diff --git a/src/libs/3rdparty/lua/src/ltable.c b/src/libs/3rdparty/lua/src/ltable.c
new file mode 100644
index 0000000000..3c690c5f17
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ltable.c
@@ -0,0 +1,980 @@
+/*
+** $Id: ltable.c $
+** Lua tables (hash)
+** See Copyright Notice in lua.h
+*/
+
+#define ltable_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+/*
+** Implementation of tables (aka arrays, objects, or hash tables).
+** Tables keep its elements in two parts: an array part and a hash part.
+** Non-negative integer keys are all candidates to be kept in the array
+** part. The actual size of the array is the largest 'n' such that
+** more than half the slots between 1 and n are in use.
+** Hash uses a mix of chained scatter table with Brent's variation.
+** A main invariant of these tables is that, if an element is not
+** in its main position (i.e. the 'original' position that its hash gives
+** to it), then the colliding element is in its own main position.
+** Hence even when the load factor reaches 100%, performance remains good.
+*/
+
+#include <math.h>
+#include <limits.h>
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lgc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "lvm.h"
+
+
+/*
+** MAXABITS is the largest integer such that MAXASIZE fits in an
+** unsigned int.
+*/
+#define MAXABITS cast_int(sizeof(int) * CHAR_BIT - 1)
+
+
+/*
+** MAXASIZE is the maximum size of the array part. It is the minimum
+** between 2^MAXABITS and the maximum size that, measured in bytes,
+** fits in a 'size_t'.
+*/
+#define MAXASIZE luaM_limitN(1u << MAXABITS, TValue)
+
+/*
+** MAXHBITS is the largest integer such that 2^MAXHBITS fits in a
+** signed int.
+*/
+#define MAXHBITS (MAXABITS - 1)
+
+
+/*
+** MAXHSIZE is the maximum size of the hash part. It is the minimum
+** between 2^MAXHBITS and the maximum size such that, measured in bytes,
+** it fits in a 'size_t'.
+*/
+#define MAXHSIZE luaM_limitN(1u << MAXHBITS, Node)
+
+
+/*
+** When the original hash value is good, hashing by a power of 2
+** avoids the cost of '%'.
+*/
+#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t))))
+
+/*
+** for other types, it is better to avoid modulo by power of 2, as
+** they can have many 2 factors.
+*/
+#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1))))
+
+
+#define hashstr(t,str) hashpow2(t, (str)->hash)
+#define hashboolean(t,p) hashpow2(t, p)
+
+
+#define hashpointer(t,p) hashmod(t, point2uint(p))
+
+
+#define dummynode (&dummynode_)
+
+static const Node dummynode_ = {
+ {{NULL}, LUA_VEMPTY, /* value's value and type */
+ LUA_VNIL, 0, {NULL}} /* key type, next, and key value */
+};
+
+
+static const TValue absentkey = {ABSTKEYCONSTANT};
+
+
+/*
+** Hash for integers. To allow a good hash, use the remainder operator
+** ('%'). If integer fits as a non-negative int, compute an int
+** remainder, which is faster. Otherwise, use an unsigned-integer
+** remainder, which uses all bits and ensures a non-negative result.
+*/
+static Node *hashint (const Table *t, lua_Integer i) {
+ lua_Unsigned ui = l_castS2U(i);
+ if (ui <= cast_uint(INT_MAX))
+ return hashmod(t, cast_int(ui));
+ else
+ return hashmod(t, ui);
+}
+
+
+/*
+** Hash for floating-point numbers.
+** The main computation should be just
+** n = frexp(n, &i); return (n * INT_MAX) + i
+** but there are some numerical subtleties.
+** In a two-complement representation, INT_MAX does not has an exact
+** representation as a float, but INT_MIN does; because the absolute
+** value of 'frexp' is smaller than 1 (unless 'n' is inf/NaN), the
+** absolute value of the product 'frexp * -INT_MIN' is smaller or equal
+** to INT_MAX. Next, the use of 'unsigned int' avoids overflows when
+** adding 'i'; the use of '~u' (instead of '-u') avoids problems with
+** INT_MIN.
+*/
+#if !defined(l_hashfloat)
+static int l_hashfloat (lua_Number n) {
+ int i;
+ lua_Integer ni;
+ n = l_mathop(frexp)(n, &i) * -cast_num(INT_MIN);
+ if (!lua_numbertointeger(n, &ni)) { /* is 'n' inf/-inf/NaN? */
+ lua_assert(luai_numisnan(n) || l_mathop(fabs)(n) == cast_num(HUGE_VAL));
+ return 0;
+ }
+ else { /* normal case */
+ unsigned int u = cast_uint(i) + cast_uint(ni);
+ return cast_int(u <= cast_uint(INT_MAX) ? u : ~u);
+ }
+}
+#endif
+
+
+/*
+** returns the 'main' position of an element in a table (that is,
+** the index of its hash value).
+*/
+static Node *mainpositionTV (const Table *t, const TValue *key) {
+ switch (ttypetag(key)) {
+ case LUA_VNUMINT: {
+ lua_Integer i = ivalue(key);
+ return hashint(t, i);
+ }
+ case LUA_VNUMFLT: {
+ lua_Number n = fltvalue(key);
+ return hashmod(t, l_hashfloat(n));
+ }
+ case LUA_VSHRSTR: {
+ TString *ts = tsvalue(key);
+ return hashstr(t, ts);
+ }
+ case LUA_VLNGSTR: {
+ TString *ts = tsvalue(key);
+ return hashpow2(t, luaS_hashlongstr(ts));
+ }
+ case LUA_VFALSE:
+ return hashboolean(t, 0);
+ case LUA_VTRUE:
+ return hashboolean(t, 1);
+ case LUA_VLIGHTUSERDATA: {
+ void *p = pvalue(key);
+ return hashpointer(t, p);
+ }
+ case LUA_VLCF: {
+ lua_CFunction f = fvalue(key);
+ return hashpointer(t, f);
+ }
+ default: {
+ GCObject *o = gcvalue(key);
+ return hashpointer(t, o);
+ }
+ }
+}
+
+
+l_sinline Node *mainpositionfromnode (const Table *t, Node *nd) {
+ TValue key;
+ getnodekey(cast(lua_State *, NULL), &key, nd);
+ return mainpositionTV(t, &key);
+}
+
+
+/*
+** Check whether key 'k1' is equal to the key in node 'n2'. This
+** equality is raw, so there are no metamethods. Floats with integer
+** values have been normalized, so integers cannot be equal to
+** floats. It is assumed that 'eqshrstr' is simply pointer equality, so
+** that short strings are handled in the default case.
+** A true 'deadok' means to accept dead keys as equal to their original
+** values. All dead keys are compared in the default case, by pointer
+** identity. (Only collectable objects can produce dead keys.) Note that
+** dead long strings are also compared by identity.
+** Once a key is dead, its corresponding value may be collected, and
+** then another value can be created with the same address. If this
+** other value is given to 'next', 'equalkey' will signal a false
+** positive. In a regular traversal, this situation should never happen,
+** as all keys given to 'next' came from the table itself, and therefore
+** could not have been collected. Outside a regular traversal, we
+** have garbage in, garbage out. What is relevant is that this false
+** positive does not break anything. (In particular, 'next' will return
+** some other valid item on the table or nil.)
+*/
+static int equalkey (const TValue *k1, const Node *n2, int deadok) {
+ if ((rawtt(k1) != keytt(n2)) && /* not the same variants? */
+ !(deadok && keyisdead(n2) && iscollectable(k1)))
+ return 0; /* cannot be same key */
+ switch (keytt(n2)) {
+ case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE:
+ return 1;
+ case LUA_VNUMINT:
+ return (ivalue(k1) == keyival(n2));
+ case LUA_VNUMFLT:
+ return luai_numeq(fltvalue(k1), fltvalueraw(keyval(n2)));
+ case LUA_VLIGHTUSERDATA:
+ return pvalue(k1) == pvalueraw(keyval(n2));
+ case LUA_VLCF:
+ return fvalue(k1) == fvalueraw(keyval(n2));
+ case ctb(LUA_VLNGSTR):
+ return luaS_eqlngstr(tsvalue(k1), keystrval(n2));
+ default:
+ return gcvalue(k1) == gcvalueraw(keyval(n2));
+ }
+}
+
+
+/*
+** True if value of 'alimit' is equal to the real size of the array
+** part of table 't'. (Otherwise, the array part must be larger than
+** 'alimit'.)
+*/
+#define limitequalsasize(t) (isrealasize(t) || ispow2((t)->alimit))
+
+
+/*
+** Returns the real size of the 'array' array
+*/
+LUAI_FUNC unsigned int luaH_realasize (const Table *t) {
+ if (limitequalsasize(t))
+ return t->alimit; /* this is the size */
+ else {
+ unsigned int size = t->alimit;
+ /* compute the smallest power of 2 not smaller than 'n' */
+ size |= (size >> 1);
+ size |= (size >> 2);
+ size |= (size >> 4);
+ size |= (size >> 8);
+#if (UINT_MAX >> 14) > 3 /* unsigned int has more than 16 bits */
+ size |= (size >> 16);
+#if (UINT_MAX >> 30) > 3
+ size |= (size >> 32); /* unsigned int has more than 32 bits */
+#endif
+#endif
+ size++;
+ lua_assert(ispow2(size) && size/2 < t->alimit && t->alimit < size);
+ return size;
+ }
+}
+
+
+/*
+** Check whether real size of the array is a power of 2.
+** (If it is not, 'alimit' cannot be changed to any other value
+** without changing the real size.)
+*/
+static int ispow2realasize (const Table *t) {
+ return (!isrealasize(t) || ispow2(t->alimit));
+}
+
+
+static unsigned int setlimittosize (Table *t) {
+ t->alimit = luaH_realasize(t);
+ setrealasize(t);
+ return t->alimit;
+}
+
+
+#define limitasasize(t) check_exp(isrealasize(t), t->alimit)
+
+
+
+/*
+** "Generic" get version. (Not that generic: not valid for integers,
+** which may be in array part, nor for floats with integral values.)
+** See explanation about 'deadok' in function 'equalkey'.
+*/
+static const TValue *getgeneric (Table *t, const TValue *key, int deadok) {
+ Node *n = mainpositionTV(t, key);
+ for (;;) { /* check whether 'key' is somewhere in the chain */
+ if (equalkey(key, n, deadok))
+ return gval(n); /* that's it */
+ else {
+ int nx = gnext(n);
+ if (nx == 0)
+ return &absentkey; /* not found */
+ n += nx;
+ }
+ }
+}
+
+
+/*
+** returns the index for 'k' if 'k' is an appropriate key to live in
+** the array part of a table, 0 otherwise.
+*/
+static unsigned int arrayindex (lua_Integer k) {
+ if (l_castS2U(k) - 1u < MAXASIZE) /* 'k' in [1, MAXASIZE]? */
+ return cast_uint(k); /* 'key' is an appropriate array index */
+ else
+ return 0;
+}
+
+
+/*
+** returns the index of a 'key' for table traversals. First goes all
+** elements in the array part, then elements in the hash part. The
+** beginning of a traversal is signaled by 0.
+*/
+static unsigned int findindex (lua_State *L, Table *t, TValue *key,
+ unsigned int asize) {
+ unsigned int i;
+ if (ttisnil(key)) return 0; /* first iteration */
+ i = ttisinteger(key) ? arrayindex(ivalue(key)) : 0;
+ if (i - 1u < asize) /* is 'key' inside array part? */
+ return i; /* yes; that's the index */
+ else {
+ const TValue *n = getgeneric(t, key, 1);
+ if (l_unlikely(isabstkey(n)))
+ luaG_runerror(L, "invalid key to 'next'"); /* key not found */
+ i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */
+ /* hash elements are numbered after array ones */
+ return (i + 1) + asize;
+ }
+}
+
+
+int luaH_next (lua_State *L, Table *t, StkId key) {
+ unsigned int asize = luaH_realasize(t);
+ unsigned int i = findindex(L, t, s2v(key), asize); /* find original key */
+ for (; i < asize; i++) { /* try first array part */
+ if (!isempty(&t->array[i])) { /* a non-empty entry? */
+ setivalue(s2v(key), i + 1);
+ setobj2s(L, key + 1, &t->array[i]);
+ return 1;
+ }
+ }
+ for (i -= asize; cast_int(i) < sizenode(t); i++) { /* hash part */
+ if (!isempty(gval(gnode(t, i)))) { /* a non-empty entry? */
+ Node *n = gnode(t, i);
+ getnodekey(L, s2v(key), n);
+ setobj2s(L, key + 1, gval(n));
+ return 1;
+ }
+ }
+ return 0; /* no more elements */
+}
+
+
+static void freehash (lua_State *L, Table *t) {
+ if (!isdummy(t))
+ luaM_freearray(L, t->node, cast_sizet(sizenode(t)));
+}
+
+
+/*
+** {=============================================================
+** Rehash
+** ==============================================================
+*/
+
+/*
+** Compute the optimal size for the array part of table 't'. 'nums' is a
+** "count array" where 'nums[i]' is the number of integers in the table
+** between 2^(i - 1) + 1 and 2^i. 'pna' enters with the total number of
+** integer keys in the table and leaves with the number of keys that
+** will go to the array part; return the optimal size. (The condition
+** 'twotoi > 0' in the for loop stops the loop if 'twotoi' overflows.)
+*/
+static unsigned int computesizes (unsigned int nums[], unsigned int *pna) {
+ int i;
+ unsigned int twotoi; /* 2^i (candidate for optimal size) */
+ unsigned int a = 0; /* number of elements smaller than 2^i */
+ unsigned int na = 0; /* number of elements to go to array part */
+ unsigned int optimal = 0; /* optimal size for array part */
+ /* loop while keys can fill more than half of total size */
+ for (i = 0, twotoi = 1;
+ twotoi > 0 && *pna > twotoi / 2;
+ i++, twotoi *= 2) {
+ a += nums[i];
+ if (a > twotoi/2) { /* more than half elements present? */
+ optimal = twotoi; /* optimal size (till now) */
+ na = a; /* all elements up to 'optimal' will go to array part */
+ }
+ }
+ lua_assert((optimal == 0 || optimal / 2 < na) && na <= optimal);
+ *pna = na;
+ return optimal;
+}
+
+
+static int countint (lua_Integer key, unsigned int *nums) {
+ unsigned int k = arrayindex(key);
+ if (k != 0) { /* is 'key' an appropriate array index? */
+ nums[luaO_ceillog2(k)]++; /* count as such */
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+/*
+** Count keys in array part of table 't': Fill 'nums[i]' with
+** number of keys that will go into corresponding slice and return
+** total number of non-nil keys.
+*/
+static unsigned int numusearray (const Table *t, unsigned int *nums) {
+ int lg;
+ unsigned int ttlg; /* 2^lg */
+ unsigned int ause = 0; /* summation of 'nums' */
+ unsigned int i = 1; /* count to traverse all array keys */
+ unsigned int asize = limitasasize(t); /* real array size */
+ /* traverse each slice */
+ for (lg = 0, ttlg = 1; lg <= MAXABITS; lg++, ttlg *= 2) {
+ unsigned int lc = 0; /* counter */
+ unsigned int lim = ttlg;
+ if (lim > asize) {
+ lim = asize; /* adjust upper limit */
+ if (i > lim)
+ break; /* no more elements to count */
+ }
+ /* count elements in range (2^(lg - 1), 2^lg] */
+ for (; i <= lim; i++) {
+ if (!isempty(&t->array[i-1]))
+ lc++;
+ }
+ nums[lg] += lc;
+ ause += lc;
+ }
+ return ause;
+}
+
+
+static int numusehash (const Table *t, unsigned int *nums, unsigned int *pna) {
+ int totaluse = 0; /* total number of elements */
+ int ause = 0; /* elements added to 'nums' (can go to array part) */
+ int i = sizenode(t);
+ while (i--) {
+ Node *n = &t->node[i];
+ if (!isempty(gval(n))) {
+ if (keyisinteger(n))
+ ause += countint(keyival(n), nums);
+ totaluse++;
+ }
+ }
+ *pna += ause;
+ return totaluse;
+}
+
+
+/*
+** Creates an array for the hash part of a table with the given
+** size, or reuses the dummy node if size is zero.
+** The computation for size overflow is in two steps: the first
+** comparison ensures that the shift in the second one does not
+** overflow.
+*/
+static void setnodevector (lua_State *L, Table *t, unsigned int size) {
+ if (size == 0) { /* no elements to hash part? */
+ t->node = cast(Node *, dummynode); /* use common 'dummynode' */
+ t->lsizenode = 0;
+ t->lastfree = NULL; /* signal that it is using dummy node */
+ }
+ else {
+ int i;
+ int lsize = luaO_ceillog2(size);
+ if (lsize > MAXHBITS || (1u << lsize) > MAXHSIZE)
+ luaG_runerror(L, "table overflow");
+ size = twoto(lsize);
+ t->node = luaM_newvector(L, size, Node);
+ for (i = 0; i < cast_int(size); i++) {
+ Node *n = gnode(t, i);
+ gnext(n) = 0;
+ setnilkey(n);
+ setempty(gval(n));
+ }
+ t->lsizenode = cast_byte(lsize);
+ t->lastfree = gnode(t, size); /* all positions are free */
+ }
+}
+
+
+/*
+** (Re)insert all elements from the hash part of 'ot' into table 't'.
+*/
+static void reinsert (lua_State *L, Table *ot, Table *t) {
+ int j;
+ int size = sizenode(ot);
+ for (j = 0; j < size; j++) {
+ Node *old = gnode(ot, j);
+ if (!isempty(gval(old))) {
+ /* doesn't need barrier/invalidate cache, as entry was
+ already present in the table */
+ TValue k;
+ getnodekey(L, &k, old);
+ luaH_set(L, t, &k, gval(old));
+ }
+ }
+}
+
+
+/*
+** Exchange the hash part of 't1' and 't2'.
+*/
+static void exchangehashpart (Table *t1, Table *t2) {
+ lu_byte lsizenode = t1->lsizenode;
+ Node *node = t1->node;
+ Node *lastfree = t1->lastfree;
+ t1->lsizenode = t2->lsizenode;
+ t1->node = t2->node;
+ t1->lastfree = t2->lastfree;
+ t2->lsizenode = lsizenode;
+ t2->node = node;
+ t2->lastfree = lastfree;
+}
+
+
+/*
+** Resize table 't' for the new given sizes. Both allocations (for
+** the hash part and for the array part) can fail, which creates some
+** subtleties. If the first allocation, for the hash part, fails, an
+** error is raised and that is it. Otherwise, it copies the elements from
+** the shrinking part of the array (if it is shrinking) into the new
+** hash. Then it reallocates the array part. If that fails, the table
+** is in its original state; the function frees the new hash part and then
+** raises the allocation error. Otherwise, it sets the new hash part
+** into the table, initializes the new part of the array (if any) with
+** nils and reinserts the elements of the old hash back into the new
+** parts of the table.
+*/
+void luaH_resize (lua_State *L, Table *t, unsigned int newasize,
+ unsigned int nhsize) {
+ unsigned int i;
+ Table newt; /* to keep the new hash part */
+ unsigned int oldasize = setlimittosize(t);
+ TValue *newarray;
+ /* create new hash part with appropriate size into 'newt' */
+ setnodevector(L, &newt, nhsize);
+ if (newasize < oldasize) { /* will array shrink? */
+ t->alimit = newasize; /* pretend array has new size... */
+ exchangehashpart(t, &newt); /* and new hash */
+ /* re-insert into the new hash the elements from vanishing slice */
+ for (i = newasize; i < oldasize; i++) {
+ if (!isempty(&t->array[i]))
+ luaH_setint(L, t, i + 1, &t->array[i]);
+ }
+ t->alimit = oldasize; /* restore current size... */
+ exchangehashpart(t, &newt); /* and hash (in case of errors) */
+ }
+ /* allocate new array */
+ newarray = luaM_reallocvector(L, t->array, oldasize, newasize, TValue);
+ if (l_unlikely(newarray == NULL && newasize > 0)) { /* allocation failed? */
+ freehash(L, &newt); /* release new hash part */
+ luaM_error(L); /* raise error (with array unchanged) */
+ }
+ /* allocation ok; initialize new part of the array */
+ exchangehashpart(t, &newt); /* 't' has the new hash ('newt' has the old) */
+ t->array = newarray; /* set new array part */
+ t->alimit = newasize;
+ for (i = oldasize; i < newasize; i++) /* clear new slice of the array */
+ setempty(&t->array[i]);
+ /* re-insert elements from old hash part into new parts */
+ reinsert(L, &newt, t); /* 'newt' now has the old hash */
+ freehash(L, &newt); /* free old hash part */
+}
+
+
+void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize) {
+ int nsize = allocsizenode(t);
+ luaH_resize(L, t, nasize, nsize);
+}
+
+/*
+** nums[i] = number of keys 'k' where 2^(i - 1) < k <= 2^i
+*/
+static void rehash (lua_State *L, Table *t, const TValue *ek) {
+ unsigned int asize; /* optimal size for array part */
+ unsigned int na; /* number of keys in the array part */
+ unsigned int nums[MAXABITS + 1];
+ int i;
+ int totaluse;
+ for (i = 0; i <= MAXABITS; i++) nums[i] = 0; /* reset counts */
+ setlimittosize(t);
+ na = numusearray(t, nums); /* count keys in array part */
+ totaluse = na; /* all those keys are integer keys */
+ totaluse += numusehash(t, nums, &na); /* count keys in hash part */
+ /* count extra key */
+ if (ttisinteger(ek))
+ na += countint(ivalue(ek), nums);
+ totaluse++;
+ /* compute new size for array part */
+ asize = computesizes(nums, &na);
+ /* resize the table to new computed sizes */
+ luaH_resize(L, t, asize, totaluse - na);
+}
+
+
+
+/*
+** }=============================================================
+*/
+
+
+Table *luaH_new (lua_State *L) {
+ GCObject *o = luaC_newobj(L, LUA_VTABLE, sizeof(Table));
+ Table *t = gco2t(o);
+ t->metatable = NULL;
+ t->flags = cast_byte(maskflags); /* table has no metamethod fields */
+ t->array = NULL;
+ t->alimit = 0;
+ setnodevector(L, t, 0);
+ return t;
+}
+
+
+void luaH_free (lua_State *L, Table *t) {
+ freehash(L, t);
+ luaM_freearray(L, t->array, luaH_realasize(t));
+ luaM_free(L, t);
+}
+
+
+static Node *getfreepos (Table *t) {
+ if (!isdummy(t)) {
+ while (t->lastfree > t->node) {
+ t->lastfree--;
+ if (keyisnil(t->lastfree))
+ return t->lastfree;
+ }
+ }
+ return NULL; /* could not find a free place */
+}
+
+
+
+/*
+** inserts a new key into a hash table; first, check whether key's main
+** position is free. If not, check whether colliding node is in its main
+** position or not: if it is not, move colliding node to an empty place and
+** put new key in its main position; otherwise (colliding node is in its main
+** position), new key goes to an empty position.
+*/
+void luaH_newkey (lua_State *L, Table *t, const TValue *key, TValue *value) {
+ Node *mp;
+ TValue aux;
+ if (l_unlikely(ttisnil(key)))
+ luaG_runerror(L, "table index is nil");
+ else if (ttisfloat(key)) {
+ lua_Number f = fltvalue(key);
+ lua_Integer k;
+ if (luaV_flttointeger(f, &k, F2Ieq)) { /* does key fit in an integer? */
+ setivalue(&aux, k);
+ key = &aux; /* insert it as an integer */
+ }
+ else if (l_unlikely(luai_numisnan(f)))
+ luaG_runerror(L, "table index is NaN");
+ }
+ if (ttisnil(value))
+ return; /* do not insert nil values */
+ mp = mainpositionTV(t, key);
+ if (!isempty(gval(mp)) || isdummy(t)) { /* main position is taken? */
+ Node *othern;
+ Node *f = getfreepos(t); /* get a free place */
+ if (f == NULL) { /* cannot find a free place? */
+ rehash(L, t, key); /* grow table */
+ /* whatever called 'newkey' takes care of TM cache */
+ luaH_set(L, t, key, value); /* insert key into grown table */
+ return;
+ }
+ lua_assert(!isdummy(t));
+ othern = mainpositionfromnode(t, mp);
+ if (othern != mp) { /* is colliding node out of its main position? */
+ /* yes; move colliding node into free position */
+ while (othern + gnext(othern) != mp) /* find previous */
+ othern += gnext(othern);
+ gnext(othern) = cast_int(f - othern); /* rechain to point to 'f' */
+ *f = *mp; /* copy colliding node into free pos. (mp->next also goes) */
+ if (gnext(mp) != 0) {
+ gnext(f) += cast_int(mp - f); /* correct 'next' */
+ gnext(mp) = 0; /* now 'mp' is free */
+ }
+ setempty(gval(mp));
+ }
+ else { /* colliding node is in its own main position */
+ /* new node will go into free position */
+ if (gnext(mp) != 0)
+ gnext(f) = cast_int((mp + gnext(mp)) - f); /* chain new position */
+ else lua_assert(gnext(f) == 0);
+ gnext(mp) = cast_int(f - mp);
+ mp = f;
+ }
+ }
+ setnodekey(L, mp, key);
+ luaC_barrierback(L, obj2gco(t), key);
+ lua_assert(isempty(gval(mp)));
+ setobj2t(L, gval(mp), value);
+}
+
+
+/*
+** Search function for integers. If integer is inside 'alimit', get it
+** directly from the array part. Otherwise, if 'alimit' is not equal to
+** the real size of the array, key still can be in the array part. In
+** this case, try to avoid a call to 'luaH_realasize' when key is just
+** one more than the limit (so that it can be incremented without
+** changing the real size of the array).
+*/
+const TValue *luaH_getint (Table *t, lua_Integer key) {
+ if (l_castS2U(key) - 1u < t->alimit) /* 'key' in [1, t->alimit]? */
+ return &t->array[key - 1];
+ else if (!limitequalsasize(t) && /* key still may be in the array part? */
+ (l_castS2U(key) == t->alimit + 1 ||
+ l_castS2U(key) - 1u < luaH_realasize(t))) {
+ t->alimit = cast_uint(key); /* probably '#t' is here now */
+ return &t->array[key - 1];
+ }
+ else {
+ Node *n = hashint(t, key);
+ for (;;) { /* check whether 'key' is somewhere in the chain */
+ if (keyisinteger(n) && keyival(n) == key)
+ return gval(n); /* that's it */
+ else {
+ int nx = gnext(n);
+ if (nx == 0) break;
+ n += nx;
+ }
+ }
+ return &absentkey;
+ }
+}
+
+
+/*
+** search function for short strings
+*/
+const TValue *luaH_getshortstr (Table *t, TString *key) {
+ Node *n = hashstr(t, key);
+ lua_assert(key->tt == LUA_VSHRSTR);
+ for (;;) { /* check whether 'key' is somewhere in the chain */
+ if (keyisshrstr(n) && eqshrstr(keystrval(n), key))
+ return gval(n); /* that's it */
+ else {
+ int nx = gnext(n);
+ if (nx == 0)
+ return &absentkey; /* not found */
+ n += nx;
+ }
+ }
+}
+
+
+const TValue *luaH_getstr (Table *t, TString *key) {
+ if (key->tt == LUA_VSHRSTR)
+ return luaH_getshortstr(t, key);
+ else { /* for long strings, use generic case */
+ TValue ko;
+ setsvalue(cast(lua_State *, NULL), &ko, key);
+ return getgeneric(t, &ko, 0);
+ }
+}
+
+
+/*
+** main search function
+*/
+const TValue *luaH_get (Table *t, const TValue *key) {
+ switch (ttypetag(key)) {
+ case LUA_VSHRSTR: return luaH_getshortstr(t, tsvalue(key));
+ case LUA_VNUMINT: return luaH_getint(t, ivalue(key));
+ case LUA_VNIL: return &absentkey;
+ case LUA_VNUMFLT: {
+ lua_Integer k;
+ if (luaV_flttointeger(fltvalue(key), &k, F2Ieq)) /* integral index? */
+ return luaH_getint(t, k); /* use specialized version */
+ /* else... */
+ } /* FALLTHROUGH */
+ default:
+ return getgeneric(t, key, 0);
+ }
+}
+
+
+/*
+** Finish a raw "set table" operation, where 'slot' is where the value
+** should have been (the result of a previous "get table").
+** Beware: when using this function you probably need to check a GC
+** barrier and invalidate the TM cache.
+*/
+void luaH_finishset (lua_State *L, Table *t, const TValue *key,
+ const TValue *slot, TValue *value) {
+ if (isabstkey(slot))
+ luaH_newkey(L, t, key, value);
+ else
+ setobj2t(L, cast(TValue *, slot), value);
+}
+
+
+/*
+** beware: when using this function you probably need to check a GC
+** barrier and invalidate the TM cache.
+*/
+void luaH_set (lua_State *L, Table *t, const TValue *key, TValue *value) {
+ const TValue *slot = luaH_get(t, key);
+ luaH_finishset(L, t, key, slot, value);
+}
+
+
+void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) {
+ const TValue *p = luaH_getint(t, key);
+ if (isabstkey(p)) {
+ TValue k;
+ setivalue(&k, key);
+ luaH_newkey(L, t, &k, value);
+ }
+ else
+ setobj2t(L, cast(TValue *, p), value);
+}
+
+
+/*
+** Try to find a boundary in the hash part of table 't'. From the
+** caller, we know that 'j' is zero or present and that 'j + 1' is
+** present. We want to find a larger key that is absent from the
+** table, so that we can do a binary search between the two keys to
+** find a boundary. We keep doubling 'j' until we get an absent index.
+** If the doubling would overflow, we try LUA_MAXINTEGER. If it is
+** absent, we are ready for the binary search. ('j', being max integer,
+** is larger or equal to 'i', but it cannot be equal because it is
+** absent while 'i' is present; so 'j > i'.) Otherwise, 'j' is a
+** boundary. ('j + 1' cannot be a present integer key because it is
+** not a valid integer in Lua.)
+*/
+static lua_Unsigned hash_search (Table *t, lua_Unsigned j) {
+ lua_Unsigned i;
+ if (j == 0) j++; /* the caller ensures 'j + 1' is present */
+ do {
+ i = j; /* 'i' is a present index */
+ if (j <= l_castS2U(LUA_MAXINTEGER) / 2)
+ j *= 2;
+ else {
+ j = LUA_MAXINTEGER;
+ if (isempty(luaH_getint(t, j))) /* t[j] not present? */
+ break; /* 'j' now is an absent index */
+ else /* weird case */
+ return j; /* well, max integer is a boundary... */
+ }
+ } while (!isempty(luaH_getint(t, j))); /* repeat until an absent t[j] */
+ /* i < j && t[i] present && t[j] absent */
+ while (j - i > 1u) { /* do a binary search between them */
+ lua_Unsigned m = (i + j) / 2;
+ if (isempty(luaH_getint(t, m))) j = m;
+ else i = m;
+ }
+ return i;
+}
+
+
+static unsigned int binsearch (const TValue *array, unsigned int i,
+ unsigned int j) {
+ while (j - i > 1u) { /* binary search */
+ unsigned int m = (i + j) / 2;
+ if (isempty(&array[m - 1])) j = m;
+ else i = m;
+ }
+ return i;
+}
+
+
+/*
+** Try to find a boundary in table 't'. (A 'boundary' is an integer index
+** such that t[i] is present and t[i+1] is absent, or 0 if t[1] is absent
+** and 'maxinteger' if t[maxinteger] is present.)
+** (In the next explanation, we use Lua indices, that is, with base 1.
+** The code itself uses base 0 when indexing the array part of the table.)
+** The code starts with 'limit = t->alimit', a position in the array
+** part that may be a boundary.
+**
+** (1) If 't[limit]' is empty, there must be a boundary before it.
+** As a common case (e.g., after 't[#t]=nil'), check whether 'limit-1'
+** is present. If so, it is a boundary. Otherwise, do a binary search
+** between 0 and limit to find a boundary. In both cases, try to
+** use this boundary as the new 'alimit', as a hint for the next call.
+**
+** (2) If 't[limit]' is not empty and the array has more elements
+** after 'limit', try to find a boundary there. Again, try first
+** the special case (which should be quite frequent) where 'limit+1'
+** is empty, so that 'limit' is a boundary. Otherwise, check the
+** last element of the array part. If it is empty, there must be a
+** boundary between the old limit (present) and the last element
+** (absent), which is found with a binary search. (This boundary always
+** can be a new limit.)
+**
+** (3) The last case is when there are no elements in the array part
+** (limit == 0) or its last element (the new limit) is present.
+** In this case, must check the hash part. If there is no hash part
+** or 'limit+1' is absent, 'limit' is a boundary. Otherwise, call
+** 'hash_search' to find a boundary in the hash part of the table.
+** (In those cases, the boundary is not inside the array part, and
+** therefore cannot be used as a new limit.)
+*/
+lua_Unsigned luaH_getn (Table *t) {
+ unsigned int limit = t->alimit;
+ if (limit > 0 && isempty(&t->array[limit - 1])) { /* (1)? */
+ /* there must be a boundary before 'limit' */
+ if (limit >= 2 && !isempty(&t->array[limit - 2])) {
+ /* 'limit - 1' is a boundary; can it be a new limit? */
+ if (ispow2realasize(t) && !ispow2(limit - 1)) {
+ t->alimit = limit - 1;
+ setnorealasize(t); /* now 'alimit' is not the real size */
+ }
+ return limit - 1;
+ }
+ else { /* must search for a boundary in [0, limit] */
+ unsigned int boundary = binsearch(t->array, 0, limit);
+ /* can this boundary represent the real size of the array? */
+ if (ispow2realasize(t) && boundary > luaH_realasize(t) / 2) {
+ t->alimit = boundary; /* use it as the new limit */
+ setnorealasize(t);
+ }
+ return boundary;
+ }
+ }
+ /* 'limit' is zero or present in table */
+ if (!limitequalsasize(t)) { /* (2)? */
+ /* 'limit' > 0 and array has more elements after 'limit' */
+ if (isempty(&t->array[limit])) /* 'limit + 1' is empty? */
+ return limit; /* this is the boundary */
+ /* else, try last element in the array */
+ limit = luaH_realasize(t);
+ if (isempty(&t->array[limit - 1])) { /* empty? */
+ /* there must be a boundary in the array after old limit,
+ and it must be a valid new limit */
+ unsigned int boundary = binsearch(t->array, t->alimit, limit);
+ t->alimit = boundary;
+ return boundary;
+ }
+ /* else, new limit is present in the table; check the hash part */
+ }
+ /* (3) 'limit' is the last element and either is zero or present in table */
+ lua_assert(limit == luaH_realasize(t) &&
+ (limit == 0 || !isempty(&t->array[limit - 1])));
+ if (isdummy(t) || isempty(luaH_getint(t, cast(lua_Integer, limit + 1))))
+ return limit; /* 'limit + 1' is absent */
+ else /* 'limit + 1' is also present */
+ return hash_search(t, limit);
+}
+
+
+
+#if defined(LUA_DEBUG)
+
+/* export these functions for the test library */
+
+Node *luaH_mainposition (const Table *t, const TValue *key) {
+ return mainpositionTV(t, key);
+}
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/ltable.h b/src/libs/3rdparty/lua/src/ltable.h
new file mode 100644
index 0000000000..75dd9e26e0
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ltable.h
@@ -0,0 +1,65 @@
+/*
+** $Id: ltable.h $
+** Lua tables (hash)
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ltable_h
+#define ltable_h
+
+#include "lobject.h"
+
+
+#define gnode(t,i) (&(t)->node[i])
+#define gval(n) (&(n)->i_val)
+#define gnext(n) ((n)->u.next)
+
+
+/*
+** Clear all bits of fast-access metamethods, which means that the table
+** may have any of these metamethods. (First access that fails after the
+** clearing will set the bit again.)
+*/
+#define invalidateTMcache(t) ((t)->flags &= ~maskflags)
+
+
+/* true when 't' is using 'dummynode' as its hash part */
+#define isdummy(t) ((t)->lastfree == NULL)
+
+
+/* allocated size for hash nodes */
+#define allocsizenode(t) (isdummy(t) ? 0 : sizenode(t))
+
+
+/* returns the Node, given the value of a table entry */
+#define nodefromval(v) cast(Node *, (v))
+
+
+LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key);
+LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key,
+ TValue *value);
+LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key);
+LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key);
+LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key);
+LUAI_FUNC void luaH_newkey (lua_State *L, Table *t, const TValue *key,
+ TValue *value);
+LUAI_FUNC void luaH_set (lua_State *L, Table *t, const TValue *key,
+ TValue *value);
+LUAI_FUNC void luaH_finishset (lua_State *L, Table *t, const TValue *key,
+ const TValue *slot, TValue *value);
+LUAI_FUNC Table *luaH_new (lua_State *L);
+LUAI_FUNC void luaH_resize (lua_State *L, Table *t, unsigned int nasize,
+ unsigned int nhsize);
+LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize);
+LUAI_FUNC void luaH_free (lua_State *L, Table *t);
+LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key);
+LUAI_FUNC lua_Unsigned luaH_getn (Table *t);
+LUAI_FUNC unsigned int luaH_realasize (const Table *t);
+
+
+#if defined(LUA_DEBUG)
+LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key);
+#endif
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/ltablib.c b/src/libs/3rdparty/lua/src/ltablib.c
new file mode 100644
index 0000000000..e6bc4d04af
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ltablib.c
@@ -0,0 +1,430 @@
+/*
+** $Id: ltablib.c $
+** Library for Table Manipulation
+** See Copyright Notice in lua.h
+*/
+
+#define ltablib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+/*
+** Operations that an object must define to mimic a table
+** (some functions only need some of them)
+*/
+#define TAB_R 1 /* read */
+#define TAB_W 2 /* write */
+#define TAB_L 4 /* length */
+#define TAB_RW (TAB_R | TAB_W) /* read/write */
+
+
+#define aux_getn(L,n,w) (checktab(L, n, (w) | TAB_L), luaL_len(L, n))
+
+
+static int checkfield (lua_State *L, const char *key, int n) {
+ lua_pushstring(L, key);
+ return (lua_rawget(L, -n) != LUA_TNIL);
+}
+
+
+/*
+** Check that 'arg' either is a table or can behave like one (that is,
+** has a metatable with the required metamethods)
+*/
+static void checktab (lua_State *L, int arg, int what) {
+ if (lua_type(L, arg) != LUA_TTABLE) { /* is it not a table? */
+ int n = 1; /* number of elements to pop */
+ if (lua_getmetatable(L, arg) && /* must have metatable */
+ (!(what & TAB_R) || checkfield(L, "__index", ++n)) &&
+ (!(what & TAB_W) || checkfield(L, "__newindex", ++n)) &&
+ (!(what & TAB_L) || checkfield(L, "__len", ++n))) {
+ lua_pop(L, n); /* pop metatable and tested metamethods */
+ }
+ else
+ luaL_checktype(L, arg, LUA_TTABLE); /* force an error */
+ }
+}
+
+
+static int tinsert (lua_State *L) {
+ lua_Integer pos; /* where to insert new element */
+ lua_Integer e = aux_getn(L, 1, TAB_RW);
+ e = luaL_intop(+, e, 1); /* first empty element */
+ switch (lua_gettop(L)) {
+ case 2: { /* called with only 2 arguments */
+ pos = e; /* insert new element at the end */
+ break;
+ }
+ case 3: {
+ lua_Integer i;
+ pos = luaL_checkinteger(L, 2); /* 2nd argument is the position */
+ /* check whether 'pos' is in [1, e] */
+ luaL_argcheck(L, (lua_Unsigned)pos - 1u < (lua_Unsigned)e, 2,
+ "position out of bounds");
+ for (i = e; i > pos; i--) { /* move up elements */
+ lua_geti(L, 1, i - 1);
+ lua_seti(L, 1, i); /* t[i] = t[i - 1] */
+ }
+ break;
+ }
+ default: {
+ return luaL_error(L, "wrong number of arguments to 'insert'");
+ }
+ }
+ lua_seti(L, 1, pos); /* t[pos] = v */
+ return 0;
+}
+
+
+static int tremove (lua_State *L) {
+ lua_Integer size = aux_getn(L, 1, TAB_RW);
+ lua_Integer pos = luaL_optinteger(L, 2, size);
+ if (pos != size) /* validate 'pos' if given */
+ /* check whether 'pos' is in [1, size + 1] */
+ luaL_argcheck(L, (lua_Unsigned)pos - 1u <= (lua_Unsigned)size, 2,
+ "position out of bounds");
+ lua_geti(L, 1, pos); /* result = t[pos] */
+ for ( ; pos < size; pos++) {
+ lua_geti(L, 1, pos + 1);
+ lua_seti(L, 1, pos); /* t[pos] = t[pos + 1] */
+ }
+ lua_pushnil(L);
+ lua_seti(L, 1, pos); /* remove entry t[pos] */
+ return 1;
+}
+
+
+/*
+** Copy elements (1[f], ..., 1[e]) into (tt[t], tt[t+1], ...). Whenever
+** possible, copy in increasing order, which is better for rehashing.
+** "possible" means destination after original range, or smaller
+** than origin, or copying to another table.
+*/
+static int tmove (lua_State *L) {
+ lua_Integer f = luaL_checkinteger(L, 2);
+ lua_Integer e = luaL_checkinteger(L, 3);
+ lua_Integer t = luaL_checkinteger(L, 4);
+ int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */
+ checktab(L, 1, TAB_R);
+ checktab(L, tt, TAB_W);
+ if (e >= f) { /* otherwise, nothing to move */
+ lua_Integer n, i;
+ luaL_argcheck(L, f > 0 || e < LUA_MAXINTEGER + f, 3,
+ "too many elements to move");
+ n = e - f + 1; /* number of elements to move */
+ luaL_argcheck(L, t <= LUA_MAXINTEGER - n + 1, 4,
+ "destination wrap around");
+ if (t > e || t <= f || (tt != 1 && !lua_compare(L, 1, tt, LUA_OPEQ))) {
+ for (i = 0; i < n; i++) {
+ lua_geti(L, 1, f + i);
+ lua_seti(L, tt, t + i);
+ }
+ }
+ else {
+ for (i = n - 1; i >= 0; i--) {
+ lua_geti(L, 1, f + i);
+ lua_seti(L, tt, t + i);
+ }
+ }
+ }
+ lua_pushvalue(L, tt); /* return destination table */
+ return 1;
+}
+
+
+static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) {
+ lua_geti(L, 1, i);
+ if (l_unlikely(!lua_isstring(L, -1)))
+ luaL_error(L, "invalid value (%s) at index %I in table for 'concat'",
+ luaL_typename(L, -1), (LUAI_UACINT)i);
+ luaL_addvalue(b);
+}
+
+
+static int tconcat (lua_State *L) {
+ luaL_Buffer b;
+ lua_Integer last = aux_getn(L, 1, TAB_R);
+ size_t lsep;
+ const char *sep = luaL_optlstring(L, 2, "", &lsep);
+ lua_Integer i = luaL_optinteger(L, 3, 1);
+ last = luaL_optinteger(L, 4, last);
+ luaL_buffinit(L, &b);
+ for (; i < last; i++) {
+ addfield(L, &b, i);
+ luaL_addlstring(&b, sep, lsep);
+ }
+ if (i == last) /* add last value (if interval was not empty) */
+ addfield(L, &b, i);
+ luaL_pushresult(&b);
+ return 1;
+}
+
+
+/*
+** {======================================================
+** Pack/unpack
+** =======================================================
+*/
+
+static int tpack (lua_State *L) {
+ int i;
+ int n = lua_gettop(L); /* number of elements to pack */
+ lua_createtable(L, n, 1); /* create result table */
+ lua_insert(L, 1); /* put it at index 1 */
+ for (i = n; i >= 1; i--) /* assign elements */
+ lua_seti(L, 1, i);
+ lua_pushinteger(L, n);
+ lua_setfield(L, 1, "n"); /* t.n = number of elements */
+ return 1; /* return table */
+}
+
+
+static int tunpack (lua_State *L) {
+ lua_Unsigned n;
+ lua_Integer i = luaL_optinteger(L, 2, 1);
+ lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1));
+ if (i > e) return 0; /* empty range */
+ n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */
+ if (l_unlikely(n >= (unsigned int)INT_MAX ||
+ !lua_checkstack(L, (int)(++n))))
+ return luaL_error(L, "too many results to unpack");
+ for (; i < e; i++) { /* push arg[i..e - 1] (to avoid overflows) */
+ lua_geti(L, 1, i);
+ }
+ lua_geti(L, 1, e); /* push last element */
+ return (int)n;
+}
+
+/* }====================================================== */
+
+
+
+/*
+** {======================================================
+** Quicksort
+** (based on 'Algorithms in MODULA-3', Robert Sedgewick;
+** Addison-Wesley, 1993.)
+** =======================================================
+*/
+
+
+/* type for array indices */
+typedef unsigned int IdxT;
+
+
+/*
+** Produce a "random" 'unsigned int' to randomize pivot choice. This
+** macro is used only when 'sort' detects a big imbalance in the result
+** of a partition. (If you don't want/need this "randomness", ~0 is a
+** good choice.)
+*/
+#if !defined(l_randomizePivot) /* { */
+
+#include <time.h>
+
+/* size of 'e' measured in number of 'unsigned int's */
+#define sof(e) (sizeof(e) / sizeof(unsigned int))
+
+/*
+** Use 'time' and 'clock' as sources of "randomness". Because we don't
+** know the types 'clock_t' and 'time_t', we cannot cast them to
+** anything without risking overflows. A safe way to use their values
+** is to copy them to an array of a known type and use the array values.
+*/
+static unsigned int l_randomizePivot (void) {
+ clock_t c = clock();
+ time_t t = time(NULL);
+ unsigned int buff[sof(c) + sof(t)];
+ unsigned int i, rnd = 0;
+ memcpy(buff, &c, sof(c) * sizeof(unsigned int));
+ memcpy(buff + sof(c), &t, sof(t) * sizeof(unsigned int));
+ for (i = 0; i < sof(buff); i++)
+ rnd += buff[i];
+ return rnd;
+}
+
+#endif /* } */
+
+
+/* arrays larger than 'RANLIMIT' may use randomized pivots */
+#define RANLIMIT 100u
+
+
+static void set2 (lua_State *L, IdxT i, IdxT j) {
+ lua_seti(L, 1, i);
+ lua_seti(L, 1, j);
+}
+
+
+/*
+** Return true iff value at stack index 'a' is less than the value at
+** index 'b' (according to the order of the sort).
+*/
+static int sort_comp (lua_State *L, int a, int b) {
+ if (lua_isnil(L, 2)) /* no function? */
+ return lua_compare(L, a, b, LUA_OPLT); /* a < b */
+ else { /* function */
+ int res;
+ lua_pushvalue(L, 2); /* push function */
+ lua_pushvalue(L, a-1); /* -1 to compensate function */
+ lua_pushvalue(L, b-2); /* -2 to compensate function and 'a' */
+ lua_call(L, 2, 1); /* call function */
+ res = lua_toboolean(L, -1); /* get result */
+ lua_pop(L, 1); /* pop result */
+ return res;
+ }
+}
+
+
+/*
+** Does the partition: Pivot P is at the top of the stack.
+** precondition: a[lo] <= P == a[up-1] <= a[up],
+** so it only needs to do the partition from lo + 1 to up - 2.
+** Pos-condition: a[lo .. i - 1] <= a[i] == P <= a[i + 1 .. up]
+** returns 'i'.
+*/
+static IdxT partition (lua_State *L, IdxT lo, IdxT up) {
+ IdxT i = lo; /* will be incremented before first use */
+ IdxT j = up - 1; /* will be decremented before first use */
+ /* loop invariant: a[lo .. i] <= P <= a[j .. up] */
+ for (;;) {
+ /* next loop: repeat ++i while a[i] < P */
+ while ((void)lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) {
+ if (l_unlikely(i == up - 1)) /* a[i] < P but a[up - 1] == P ?? */
+ luaL_error(L, "invalid order function for sorting");
+ lua_pop(L, 1); /* remove a[i] */
+ }
+ /* after the loop, a[i] >= P and a[lo .. i - 1] < P */
+ /* next loop: repeat --j while P < a[j] */
+ while ((void)lua_geti(L, 1, --j), sort_comp(L, -3, -1)) {
+ if (l_unlikely(j < i)) /* j < i but a[j] > P ?? */
+ luaL_error(L, "invalid order function for sorting");
+ lua_pop(L, 1); /* remove a[j] */
+ }
+ /* after the loop, a[j] <= P and a[j + 1 .. up] >= P */
+ if (j < i) { /* no elements out of place? */
+ /* a[lo .. i - 1] <= P <= a[j + 1 .. i .. up] */
+ lua_pop(L, 1); /* pop a[j] */
+ /* swap pivot (a[up - 1]) with a[i] to satisfy pos-condition */
+ set2(L, up - 1, i);
+ return i;
+ }
+ /* otherwise, swap a[i] - a[j] to restore invariant and repeat */
+ set2(L, i, j);
+ }
+}
+
+
+/*
+** Choose an element in the middle (2nd-3th quarters) of [lo,up]
+** "randomized" by 'rnd'
+*/
+static IdxT choosePivot (IdxT lo, IdxT up, unsigned int rnd) {
+ IdxT r4 = (up - lo) / 4; /* range/4 */
+ IdxT p = rnd % (r4 * 2) + (lo + r4);
+ lua_assert(lo + r4 <= p && p <= up - r4);
+ return p;
+}
+
+
+/*
+** Quicksort algorithm (recursive function)
+*/
+static void auxsort (lua_State *L, IdxT lo, IdxT up,
+ unsigned int rnd) {
+ while (lo < up) { /* loop for tail recursion */
+ IdxT p; /* Pivot index */
+ IdxT n; /* to be used later */
+ /* sort elements 'lo', 'p', and 'up' */
+ lua_geti(L, 1, lo);
+ lua_geti(L, 1, up);
+ if (sort_comp(L, -1, -2)) /* a[up] < a[lo]? */
+ set2(L, lo, up); /* swap a[lo] - a[up] */
+ else
+ lua_pop(L, 2); /* remove both values */
+ if (up - lo == 1) /* only 2 elements? */
+ return; /* already sorted */
+ if (up - lo < RANLIMIT || rnd == 0) /* small interval or no randomize? */
+ p = (lo + up)/2; /* middle element is a good pivot */
+ else /* for larger intervals, it is worth a random pivot */
+ p = choosePivot(lo, up, rnd);
+ lua_geti(L, 1, p);
+ lua_geti(L, 1, lo);
+ if (sort_comp(L, -2, -1)) /* a[p] < a[lo]? */
+ set2(L, p, lo); /* swap a[p] - a[lo] */
+ else {
+ lua_pop(L, 1); /* remove a[lo] */
+ lua_geti(L, 1, up);
+ if (sort_comp(L, -1, -2)) /* a[up] < a[p]? */
+ set2(L, p, up); /* swap a[up] - a[p] */
+ else
+ lua_pop(L, 2);
+ }
+ if (up - lo == 2) /* only 3 elements? */
+ return; /* already sorted */
+ lua_geti(L, 1, p); /* get middle element (Pivot) */
+ lua_pushvalue(L, -1); /* push Pivot */
+ lua_geti(L, 1, up - 1); /* push a[up - 1] */
+ set2(L, p, up - 1); /* swap Pivot (a[p]) with a[up - 1] */
+ p = partition(L, lo, up);
+ /* a[lo .. p - 1] <= a[p] == P <= a[p + 1 .. up] */
+ if (p - lo < up - p) { /* lower interval is smaller? */
+ auxsort(L, lo, p - 1, rnd); /* call recursively for lower interval */
+ n = p - lo; /* size of smaller interval */
+ lo = p + 1; /* tail call for [p + 1 .. up] (upper interval) */
+ }
+ else {
+ auxsort(L, p + 1, up, rnd); /* call recursively for upper interval */
+ n = up - p; /* size of smaller interval */
+ up = p - 1; /* tail call for [lo .. p - 1] (lower interval) */
+ }
+ if ((up - lo) / 128 > n) /* partition too imbalanced? */
+ rnd = l_randomizePivot(); /* try a new randomization */
+ } /* tail call auxsort(L, lo, up, rnd) */
+}
+
+
+static int sort (lua_State *L) {
+ lua_Integer n = aux_getn(L, 1, TAB_RW);
+ if (n > 1) { /* non-trivial interval? */
+ luaL_argcheck(L, n < INT_MAX, 1, "array too big");
+ if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */
+ luaL_checktype(L, 2, LUA_TFUNCTION); /* must be a function */
+ lua_settop(L, 2); /* make sure there are two arguments */
+ auxsort(L, 1, (IdxT)n, 0);
+ }
+ return 0;
+}
+
+/* }====================================================== */
+
+
+static const luaL_Reg tab_funcs[] = {
+ {"concat", tconcat},
+ {"insert", tinsert},
+ {"pack", tpack},
+ {"unpack", tunpack},
+ {"remove", tremove},
+ {"move", tmove},
+ {"sort", sort},
+ {NULL, NULL}
+};
+
+
+LUAMOD_API int luaopen_table (lua_State *L) {
+ luaL_newlib(L, tab_funcs);
+ return 1;
+}
+
diff --git a/src/libs/3rdparty/lua/src/ltm.c b/src/libs/3rdparty/lua/src/ltm.c
new file mode 100644
index 0000000000..07a060811d
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ltm.c
@@ -0,0 +1,271 @@
+/*
+** $Id: ltm.c $
+** Tag methods
+** See Copyright Notice in lua.h
+*/
+
+#define ltm_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <string.h>
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lgc.h"
+#include "lobject.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lvm.h"
+
+
+static const char udatatypename[] = "userdata";
+
+LUAI_DDEF const char *const luaT_typenames_[LUA_TOTALTYPES] = {
+ "no value",
+ "nil", "boolean", udatatypename, "number",
+ "string", "table", "function", udatatypename, "thread",
+ "upvalue", "proto" /* these last cases are used for tests only */
+};
+
+
+void luaT_init (lua_State *L) {
+ static const char *const luaT_eventname[] = { /* ORDER TM */
+ "__index", "__newindex",
+ "__gc", "__mode", "__len", "__eq",
+ "__add", "__sub", "__mul", "__mod", "__pow",
+ "__div", "__idiv",
+ "__band", "__bor", "__bxor", "__shl", "__shr",
+ "__unm", "__bnot", "__lt", "__le",
+ "__concat", "__call", "__close"
+ };
+ int i;
+ for (i=0; i<TM_N; i++) {
+ G(L)->tmname[i] = luaS_new(L, luaT_eventname[i]);
+ luaC_fix(L, obj2gco(G(L)->tmname[i])); /* never collect these names */
+ }
+}
+
+
+/*
+** function to be used with macro "fasttm": optimized for absence of
+** tag methods
+*/
+const TValue *luaT_gettm (Table *events, TMS event, TString *ename) {
+ const TValue *tm = luaH_getshortstr(events, ename);
+ lua_assert(event <= TM_EQ);
+ if (notm(tm)) { /* no tag method? */
+ events->flags |= cast_byte(1u<<event); /* cache this fact */
+ return NULL;
+ }
+ else return tm;
+}
+
+
+const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) {
+ Table *mt;
+ switch (ttype(o)) {
+ case LUA_TTABLE:
+ mt = hvalue(o)->metatable;
+ break;
+ case LUA_TUSERDATA:
+ mt = uvalue(o)->metatable;
+ break;
+ default:
+ mt = G(L)->mt[ttype(o)];
+ }
+ return (mt ? luaH_getshortstr(mt, G(L)->tmname[event]) : &G(L)->nilvalue);
+}
+
+
+/*
+** Return the name of the type of an object. For tables and userdata
+** with metatable, use their '__name' metafield, if present.
+*/
+const char *luaT_objtypename (lua_State *L, const TValue *o) {
+ Table *mt;
+ if ((ttistable(o) && (mt = hvalue(o)->metatable) != NULL) ||
+ (ttisfulluserdata(o) && (mt = uvalue(o)->metatable) != NULL)) {
+ const TValue *name = luaH_getshortstr(mt, luaS_new(L, "__name"));
+ if (ttisstring(name)) /* is '__name' a string? */
+ return getstr(tsvalue(name)); /* use it as type name */
+ }
+ return ttypename(ttype(o)); /* else use standard type name */
+}
+
+
+void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1,
+ const TValue *p2, const TValue *p3) {
+ StkId func = L->top.p;
+ setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */
+ setobj2s(L, func + 1, p1); /* 1st argument */
+ setobj2s(L, func + 2, p2); /* 2nd argument */
+ setobj2s(L, func + 3, p3); /* 3rd argument */
+ L->top.p = func + 4;
+ /* metamethod may yield only when called from Lua code */
+ if (isLuacode(L->ci))
+ luaD_call(L, func, 0);
+ else
+ luaD_callnoyield(L, func, 0);
+}
+
+
+void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1,
+ const TValue *p2, StkId res) {
+ ptrdiff_t result = savestack(L, res);
+ StkId func = L->top.p;
+ setobj2s(L, func, f); /* push function (assume EXTRA_STACK) */
+ setobj2s(L, func + 1, p1); /* 1st argument */
+ setobj2s(L, func + 2, p2); /* 2nd argument */
+ L->top.p += 3;
+ /* metamethod may yield only when called from Lua code */
+ if (isLuacode(L->ci))
+ luaD_call(L, func, 1);
+ else
+ luaD_callnoyield(L, func, 1);
+ res = restorestack(L, result);
+ setobjs2s(L, res, --L->top.p); /* move result to its place */
+}
+
+
+static int callbinTM (lua_State *L, const TValue *p1, const TValue *p2,
+ StkId res, TMS event) {
+ const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */
+ if (notm(tm))
+ tm = luaT_gettmbyobj(L, p2, event); /* try second operand */
+ if (notm(tm)) return 0;
+ luaT_callTMres(L, tm, p1, p2, res);
+ return 1;
+}
+
+
+void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
+ StkId res, TMS event) {
+ if (l_unlikely(!callbinTM(L, p1, p2, res, event))) {
+ switch (event) {
+ case TM_BAND: case TM_BOR: case TM_BXOR:
+ case TM_SHL: case TM_SHR: case TM_BNOT: {
+ if (ttisnumber(p1) && ttisnumber(p2))
+ luaG_tointerror(L, p1, p2);
+ else
+ luaG_opinterror(L, p1, p2, "perform bitwise operation on");
+ }
+ /* calls never return, but to avoid warnings: *//* FALLTHROUGH */
+ default:
+ luaG_opinterror(L, p1, p2, "perform arithmetic on");
+ }
+ }
+}
+
+
+void luaT_tryconcatTM (lua_State *L) {
+ StkId top = L->top.p;
+ if (l_unlikely(!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2,
+ TM_CONCAT)))
+ luaG_concaterror(L, s2v(top - 2), s2v(top - 1));
+}
+
+
+void luaT_trybinassocTM (lua_State *L, const TValue *p1, const TValue *p2,
+ int flip, StkId res, TMS event) {
+ if (flip)
+ luaT_trybinTM(L, p2, p1, res, event);
+ else
+ luaT_trybinTM(L, p1, p2, res, event);
+}
+
+
+void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
+ int flip, StkId res, TMS event) {
+ TValue aux;
+ setivalue(&aux, i2);
+ luaT_trybinassocTM(L, p1, &aux, flip, res, event);
+}
+
+
+/*
+** Calls an order tag method.
+** For lessequal, LUA_COMPAT_LT_LE keeps compatibility with old
+** behavior: if there is no '__le', try '__lt', based on l <= r iff
+** !(r < l) (assuming a total order). If the metamethod yields during
+** this substitution, the continuation has to know about it (to negate
+** the result of r<l); bit CIST_LEQ in the call status keeps that
+** information.
+*/
+int luaT_callorderTM (lua_State *L, const TValue *p1, const TValue *p2,
+ TMS event) {
+ if (callbinTM(L, p1, p2, L->top.p, event)) /* try original event */
+ return !l_isfalse(s2v(L->top.p));
+#if defined(LUA_COMPAT_LT_LE)
+ else if (event == TM_LE) {
+ /* try '!(p2 < p1)' for '(p1 <= p2)' */
+ L->ci->callstatus |= CIST_LEQ; /* mark it is doing 'lt' for 'le' */
+ if (callbinTM(L, p2, p1, L->top.p, TM_LT)) {
+ L->ci->callstatus ^= CIST_LEQ; /* clear mark */
+ return l_isfalse(s2v(L->top.p));
+ }
+ /* else error will remove this 'ci'; no need to clear mark */
+ }
+#endif
+ luaG_ordererror(L, p1, p2); /* no metamethod found */
+ return 0; /* to avoid warnings */
+}
+
+
+int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
+ int flip, int isfloat, TMS event) {
+ TValue aux; const TValue *p2;
+ if (isfloat) {
+ setfltvalue(&aux, cast_num(v2));
+ }
+ else
+ setivalue(&aux, v2);
+ if (flip) { /* arguments were exchanged? */
+ p2 = p1; p1 = &aux; /* correct them */
+ }
+ else
+ p2 = &aux;
+ return luaT_callorderTM(L, p1, p2, event);
+}
+
+
+void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci,
+ const Proto *p) {
+ int i;
+ int actual = cast_int(L->top.p - ci->func.p) - 1; /* number of arguments */
+ int nextra = actual - nfixparams; /* number of extra arguments */
+ ci->u.l.nextraargs = nextra;
+ luaD_checkstack(L, p->maxstacksize + 1);
+ /* copy function to the top of the stack */
+ setobjs2s(L, L->top.p++, ci->func.p);
+ /* move fixed parameters to the top of the stack */
+ for (i = 1; i <= nfixparams; i++) {
+ setobjs2s(L, L->top.p++, ci->func.p + i);
+ setnilvalue(s2v(ci->func.p + i)); /* erase original parameter (for GC) */
+ }
+ ci->func.p += actual + 1;
+ ci->top.p += actual + 1;
+ lua_assert(L->top.p <= ci->top.p && ci->top.p <= L->stack_last.p);
+}
+
+
+void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) {
+ int i;
+ int nextra = ci->u.l.nextraargs;
+ if (wanted < 0) {
+ wanted = nextra; /* get all extra arguments available */
+ checkstackGCp(L, nextra, where); /* ensure stack space */
+ L->top.p = where + nextra; /* next instruction will need top */
+ }
+ for (i = 0; i < wanted && i < nextra; i++)
+ setobjs2s(L, where + i, ci->func.p - nextra + i);
+ for (; i < wanted; i++) /* complete required results with nil */
+ setnilvalue(s2v(where + i));
+}
+
diff --git a/src/libs/3rdparty/lua/src/ltm.h b/src/libs/3rdparty/lua/src/ltm.h
new file mode 100644
index 0000000000..c309e2ae10
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/ltm.h
@@ -0,0 +1,104 @@
+/*
+** $Id: ltm.h $
+** Tag methods
+** See Copyright Notice in lua.h
+*/
+
+#ifndef ltm_h
+#define ltm_h
+
+
+#include "lobject.h"
+#include "lstate.h"
+
+
+/*
+* WARNING: if you change the order of this enumeration,
+* grep "ORDER TM" and "ORDER OP"
+*/
+typedef enum {
+ TM_INDEX,
+ TM_NEWINDEX,
+ TM_GC,
+ TM_MODE,
+ TM_LEN,
+ TM_EQ, /* last tag method with fast access */
+ TM_ADD,
+ TM_SUB,
+ TM_MUL,
+ TM_MOD,
+ TM_POW,
+ TM_DIV,
+ TM_IDIV,
+ TM_BAND,
+ TM_BOR,
+ TM_BXOR,
+ TM_SHL,
+ TM_SHR,
+ TM_UNM,
+ TM_BNOT,
+ TM_LT,
+ TM_LE,
+ TM_CONCAT,
+ TM_CALL,
+ TM_CLOSE,
+ TM_N /* number of elements in the enum */
+} TMS;
+
+
+/*
+** Mask with 1 in all fast-access methods. A 1 in any of these bits
+** in the flag of a (meta)table means the metatable does not have the
+** corresponding metamethod field. (Bit 7 of the flag is used for
+** 'isrealasize'.)
+*/
+#define maskflags (~(~0u << (TM_EQ + 1)))
+
+
+/*
+** Test whether there is no tagmethod.
+** (Because tagmethods use raw accesses, the result may be an "empty" nil.)
+*/
+#define notm(tm) ttisnil(tm)
+
+
+#define gfasttm(g,et,e) ((et) == NULL ? NULL : \
+ ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))
+
+#define fasttm(l,et,e) gfasttm(G(l), et, e)
+
+#define ttypename(x) luaT_typenames_[(x) + 1]
+
+LUAI_DDEC(const char *const luaT_typenames_[LUA_TOTALTYPES];)
+
+
+LUAI_FUNC const char *luaT_objtypename (lua_State *L, const TValue *o);
+
+LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename);
+LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o,
+ TMS event);
+LUAI_FUNC void luaT_init (lua_State *L);
+
+LUAI_FUNC void luaT_callTM (lua_State *L, const TValue *f, const TValue *p1,
+ const TValue *p2, const TValue *p3);
+LUAI_FUNC void luaT_callTMres (lua_State *L, const TValue *f,
+ const TValue *p1, const TValue *p2, StkId p3);
+LUAI_FUNC void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
+ StkId res, TMS event);
+LUAI_FUNC void luaT_tryconcatTM (lua_State *L);
+LUAI_FUNC void luaT_trybinassocTM (lua_State *L, const TValue *p1,
+ const TValue *p2, int inv, StkId res, TMS event);
+LUAI_FUNC void luaT_trybiniTM (lua_State *L, const TValue *p1, lua_Integer i2,
+ int inv, StkId res, TMS event);
+LUAI_FUNC int luaT_callorderTM (lua_State *L, const TValue *p1,
+ const TValue *p2, TMS event);
+LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
+ int inv, int isfloat, TMS event);
+
+LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams,
+ CallInfo *ci, const Proto *p);
+LUAI_FUNC void luaT_getvarargs (lua_State *L, CallInfo *ci,
+ StkId where, int wanted);
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lua.c b/src/libs/3rdparty/lua/src/lua.c
new file mode 100644
index 0000000000..0ff8845453
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lua.c
@@ -0,0 +1,679 @@
+/*
+** $Id: lua.c $
+** Lua stand-alone interpreter
+** See Copyright Notice in lua.h
+*/
+
+#define lua_c
+
+#include "lprefix.h"
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <signal.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+#if !defined(LUA_PROGNAME)
+#define LUA_PROGNAME "lua"
+#endif
+
+#if !defined(LUA_INIT_VAR)
+#define LUA_INIT_VAR "LUA_INIT"
+#endif
+
+#define LUA_INITVARVERSION LUA_INIT_VAR LUA_VERSUFFIX
+
+
+static lua_State *globalL = NULL;
+
+static const char *progname = LUA_PROGNAME;
+
+
+#if defined(LUA_USE_POSIX) /* { */
+
+/*
+** Use 'sigaction' when available.
+*/
+static void setsignal (int sig, void (*handler)(int)) {
+ struct sigaction sa;
+ sa.sa_handler = handler;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask); /* do not mask any signal */
+ sigaction(sig, &sa, NULL);
+}
+
+#else /* }{ */
+
+#define setsignal signal
+
+#endif /* } */
+
+
+/*
+** Hook set by signal function to stop the interpreter.
+*/
+static void lstop (lua_State *L, lua_Debug *ar) {
+ (void)ar; /* unused arg. */
+ lua_sethook(L, NULL, 0, 0); /* reset hook */
+ luaL_error(L, "interrupted!");
+}
+
+
+/*
+** Function to be called at a C signal. Because a C signal cannot
+** just change a Lua state (as there is no proper synchronization),
+** this function only sets a hook that, when called, will stop the
+** interpreter.
+*/
+static void laction (int i) {
+ int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT;
+ setsignal(i, SIG_DFL); /* if another SIGINT happens, terminate process */
+ lua_sethook(globalL, lstop, flag, 1);
+}
+
+
+static void print_usage (const char *badoption) {
+ lua_writestringerror("%s: ", progname);
+ if (badoption[1] == 'e' || badoption[1] == 'l')
+ lua_writestringerror("'%s' needs argument\n", badoption);
+ else
+ lua_writestringerror("unrecognized option '%s'\n", badoption);
+ lua_writestringerror(
+ "usage: %s [options] [script [args]]\n"
+ "Available options are:\n"
+ " -e stat execute string 'stat'\n"
+ " -i enter interactive mode after executing 'script'\n"
+ " -l mod require library 'mod' into global 'mod'\n"
+ " -l g=mod require library 'mod' into global 'g'\n"
+ " -v show version information\n"
+ " -E ignore environment variables\n"
+ " -W turn warnings on\n"
+ " -- stop handling options\n"
+ " - stop handling options and execute stdin\n"
+ ,
+ progname);
+}
+
+
+/*
+** Prints an error message, adding the program name in front of it
+** (if present)
+*/
+static void l_message (const char *pname, const char *msg) {
+ if (pname) lua_writestringerror("%s: ", pname);
+ lua_writestringerror("%s\n", msg);
+}
+
+
+/*
+** Check whether 'status' is not OK and, if so, prints the error
+** message on the top of the stack. It assumes that the error object
+** is a string, as it was either generated by Lua or by 'msghandler'.
+*/
+static int report (lua_State *L, int status) {
+ if (status != LUA_OK) {
+ const char *msg = lua_tostring(L, -1);
+ l_message(progname, msg);
+ lua_pop(L, 1); /* remove message */
+ }
+ return status;
+}
+
+
+/*
+** Message handler used to run all chunks
+*/
+static int msghandler (lua_State *L) {
+ const char *msg = lua_tostring(L, 1);
+ if (msg == NULL) { /* is error object not a string? */
+ if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */
+ lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */
+ return 1; /* that is the message */
+ else
+ msg = lua_pushfstring(L, "(error object is a %s value)",
+ luaL_typename(L, 1));
+ }
+ luaL_traceback(L, L, msg, 1); /* append a standard traceback */
+ return 1; /* return the traceback */
+}
+
+
+/*
+** Interface to 'lua_pcall', which sets appropriate message function
+** and C-signal handler. Used to run all chunks.
+*/
+static int docall (lua_State *L, int narg, int nres) {
+ int status;
+ int base = lua_gettop(L) - narg; /* function index */
+ lua_pushcfunction(L, msghandler); /* push message handler */
+ lua_insert(L, base); /* put it under function and args */
+ globalL = L; /* to be available to 'laction' */
+ setsignal(SIGINT, laction); /* set C-signal handler */
+ status = lua_pcall(L, narg, nres, base);
+ setsignal(SIGINT, SIG_DFL); /* reset C-signal handler */
+ lua_remove(L, base); /* remove message handler from the stack */
+ return status;
+}
+
+
+static void print_version (void) {
+ lua_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT));
+ lua_writeline();
+}
+
+
+/*
+** Create the 'arg' table, which stores all arguments from the
+** command line ('argv'). It should be aligned so that, at index 0,
+** it has 'argv[script]', which is the script name. The arguments
+** to the script (everything after 'script') go to positive indices;
+** other arguments (before the script name) go to negative indices.
+** If there is no script name, assume interpreter's name as base.
+** (If there is no interpreter's name either, 'script' is -1, so
+** table sizes are zero.)
+*/
+static void createargtable (lua_State *L, char **argv, int argc, int script) {
+ int i, narg;
+ narg = argc - (script + 1); /* number of positive indices */
+ lua_createtable(L, narg, script + 1);
+ for (i = 0; i < argc; i++) {
+ lua_pushstring(L, argv[i]);
+ lua_rawseti(L, -2, i - script);
+ }
+ lua_setglobal(L, "arg");
+}
+
+
+static int dochunk (lua_State *L, int status) {
+ if (status == LUA_OK) status = docall(L, 0, 0);
+ return report(L, status);
+}
+
+
+static int dofile (lua_State *L, const char *name) {
+ return dochunk(L, luaL_loadfile(L, name));
+}
+
+
+static int dostring (lua_State *L, const char *s, const char *name) {
+ return dochunk(L, luaL_loadbuffer(L, s, strlen(s), name));
+}
+
+
+/*
+** Receives 'globname[=modname]' and runs 'globname = require(modname)'.
+*/
+static int dolibrary (lua_State *L, char *globname) {
+ int status;
+ char *modname = strchr(globname, '=');
+ if (modname == NULL) /* no explicit name? */
+ modname = globname; /* module name is equal to global name */
+ else {
+ *modname = '\0'; /* global name ends here */
+ modname++; /* module name starts after the '=' */
+ }
+ lua_getglobal(L, "require");
+ lua_pushstring(L, modname);
+ status = docall(L, 1, 1); /* call 'require(modname)' */
+ if (status == LUA_OK)
+ lua_setglobal(L, globname); /* globname = require(modname) */
+ return report(L, status);
+}
+
+
+/*
+** Push on the stack the contents of table 'arg' from 1 to #arg
+*/
+static int pushargs (lua_State *L) {
+ int i, n;
+ if (lua_getglobal(L, "arg") != LUA_TTABLE)
+ luaL_error(L, "'arg' is not a table");
+ n = (int)luaL_len(L, -1);
+ luaL_checkstack(L, n + 3, "too many arguments to script");
+ for (i = 1; i <= n; i++)
+ lua_rawgeti(L, -i, i);
+ lua_remove(L, -i); /* remove table from the stack */
+ return n;
+}
+
+
+static int handle_script (lua_State *L, char **argv) {
+ int status;
+ const char *fname = argv[0];
+ if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0)
+ fname = NULL; /* stdin */
+ status = luaL_loadfile(L, fname);
+ if (status == LUA_OK) {
+ int n = pushargs(L); /* push arguments to script */
+ status = docall(L, n, LUA_MULTRET);
+ }
+ return report(L, status);
+}
+
+
+/* bits of various argument indicators in 'args' */
+#define has_error 1 /* bad option */
+#define has_i 2 /* -i */
+#define has_v 4 /* -v */
+#define has_e 8 /* -e */
+#define has_E 16 /* -E */
+
+
+/*
+** Traverses all arguments from 'argv', returning a mask with those
+** needed before running any Lua code or an error code if it finds any
+** invalid argument. In case of error, 'first' is the index of the bad
+** argument. Otherwise, 'first' is -1 if there is no program name,
+** 0 if there is no script name, or the index of the script name.
+*/
+static int collectargs (char **argv, int *first) {
+ int args = 0;
+ int i;
+ if (argv[0] != NULL) { /* is there a program name? */
+ if (argv[0][0]) /* not empty? */
+ progname = argv[0]; /* save it */
+ }
+ else { /* no program name */
+ *first = -1;
+ return 0;
+ }
+ for (i = 1; argv[i] != NULL; i++) { /* handle arguments */
+ *first = i;
+ if (argv[i][0] != '-') /* not an option? */
+ return args; /* stop handling options */
+ switch (argv[i][1]) { /* else check option */
+ case '-': /* '--' */
+ if (argv[i][2] != '\0') /* extra characters after '--'? */
+ return has_error; /* invalid option */
+ *first = i + 1;
+ return args;
+ case '\0': /* '-' */
+ return args; /* script "name" is '-' */
+ case 'E':
+ if (argv[i][2] != '\0') /* extra characters? */
+ return has_error; /* invalid option */
+ args |= has_E;
+ break;
+ case 'W':
+ if (argv[i][2] != '\0') /* extra characters? */
+ return has_error; /* invalid option */
+ break;
+ case 'i':
+ args |= has_i; /* (-i implies -v) *//* FALLTHROUGH */
+ case 'v':
+ if (argv[i][2] != '\0') /* extra characters? */
+ return has_error; /* invalid option */
+ args |= has_v;
+ break;
+ case 'e':
+ args |= has_e; /* FALLTHROUGH */
+ case 'l': /* both options need an argument */
+ if (argv[i][2] == '\0') { /* no concatenated argument? */
+ i++; /* try next 'argv' */
+ if (argv[i] == NULL || argv[i][0] == '-')
+ return has_error; /* no next argument or it is another option */
+ }
+ break;
+ default: /* invalid option */
+ return has_error;
+ }
+ }
+ *first = 0; /* no script name */
+ return args;
+}
+
+
+/*
+** Processes options 'e' and 'l', which involve running Lua code, and
+** 'W', which also affects the state.
+** Returns 0 if some code raises an error.
+*/
+static int runargs (lua_State *L, char **argv, int n) {
+ int i;
+ for (i = 1; i < n; i++) {
+ int option = argv[i][1];
+ lua_assert(argv[i][0] == '-'); /* already checked */
+ switch (option) {
+ case 'e': case 'l': {
+ int status;
+ char *extra = argv[i] + 2; /* both options need an argument */
+ if (*extra == '\0') extra = argv[++i];
+ lua_assert(extra != NULL);
+ status = (option == 'e')
+ ? dostring(L, extra, "=(command line)")
+ : dolibrary(L, extra);
+ if (status != LUA_OK) return 0;
+ break;
+ }
+ case 'W':
+ lua_warning(L, "@on", 0); /* warnings on */
+ break;
+ }
+ }
+ return 1;
+}
+
+
+static int handle_luainit (lua_State *L) {
+ const char *name = "=" LUA_INITVARVERSION;
+ const char *init = getenv(name + 1);
+ if (init == NULL) {
+ name = "=" LUA_INIT_VAR;
+ init = getenv(name + 1); /* try alternative name */
+ }
+ if (init == NULL) return LUA_OK;
+ else if (init[0] == '@')
+ return dofile(L, init+1);
+ else
+ return dostring(L, init, name);
+}
+
+
+/*
+** {==================================================================
+** Read-Eval-Print Loop (REPL)
+** ===================================================================
+*/
+
+#if !defined(LUA_PROMPT)
+#define LUA_PROMPT "> "
+#define LUA_PROMPT2 ">> "
+#endif
+
+#if !defined(LUA_MAXINPUT)
+#define LUA_MAXINPUT 512
+#endif
+
+
+/*
+** lua_stdin_is_tty detects whether the standard input is a 'tty' (that
+** is, whether we're running lua interactively).
+*/
+#if !defined(lua_stdin_is_tty) /* { */
+
+#if defined(LUA_USE_POSIX) /* { */
+
+#include <unistd.h>
+#define lua_stdin_is_tty() isatty(0)
+
+#elif defined(LUA_USE_WINDOWS) /* }{ */
+
+#include <io.h>
+#include <windows.h>
+
+#define lua_stdin_is_tty() _isatty(_fileno(stdin))
+
+#else /* }{ */
+
+/* ISO C definition */
+#define lua_stdin_is_tty() 1 /* assume stdin is a tty */
+
+#endif /* } */
+
+#endif /* } */
+
+
+/*
+** lua_readline defines how to show a prompt and then read a line from
+** the standard input.
+** lua_saveline defines how to "save" a read line in a "history".
+** lua_freeline defines how to free a line read by lua_readline.
+*/
+#if !defined(lua_readline) /* { */
+
+#if defined(LUA_USE_READLINE) /* { */
+
+#include <readline/readline.h>
+#include <readline/history.h>
+#define lua_initreadline(L) ((void)L, rl_readline_name="lua")
+#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL)
+#define lua_saveline(L,line) ((void)L, add_history(line))
+#define lua_freeline(L,b) ((void)L, free(b))
+
+#else /* }{ */
+
+#define lua_initreadline(L) ((void)L)
+#define lua_readline(L,b,p) \
+ ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \
+ fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */
+#define lua_saveline(L,line) { (void)L; (void)line; }
+#define lua_freeline(L,b) { (void)L; (void)b; }
+
+#endif /* } */
+
+#endif /* } */
+
+
+/*
+** Return the string to be used as a prompt by the interpreter. Leave
+** the string (or nil, if using the default value) on the stack, to keep
+** it anchored.
+*/
+static const char *get_prompt (lua_State *L, int firstline) {
+ if (lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2") == LUA_TNIL)
+ return (firstline ? LUA_PROMPT : LUA_PROMPT2); /* use the default */
+ else { /* apply 'tostring' over the value */
+ const char *p = luaL_tolstring(L, -1, NULL);
+ lua_remove(L, -2); /* remove original value */
+ return p;
+ }
+}
+
+/* mark in error messages for incomplete statements */
+#define EOFMARK "<eof>"
+#define marklen (sizeof(EOFMARK)/sizeof(char) - 1)
+
+
+/*
+** Check whether 'status' signals a syntax error and the error
+** message at the top of the stack ends with the above mark for
+** incomplete statements.
+*/
+static int incomplete (lua_State *L, int status) {
+ if (status == LUA_ERRSYNTAX) {
+ size_t lmsg;
+ const char *msg = lua_tolstring(L, -1, &lmsg);
+ if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) {
+ lua_pop(L, 1);
+ return 1;
+ }
+ }
+ return 0; /* else... */
+}
+
+
+/*
+** Prompt the user, read a line, and push it into the Lua stack.
+*/
+static int pushline (lua_State *L, int firstline) {
+ char buffer[LUA_MAXINPUT];
+ char *b = buffer;
+ size_t l;
+ const char *prmt = get_prompt(L, firstline);
+ int readstatus = lua_readline(L, b, prmt);
+ if (readstatus == 0)
+ return 0; /* no input (prompt will be popped by caller) */
+ lua_pop(L, 1); /* remove prompt */
+ l = strlen(b);
+ if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
+ b[--l] = '\0'; /* remove it */
+ if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */
+ lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */
+ else
+ lua_pushlstring(L, b, l);
+ lua_freeline(L, b);
+ return 1;
+}
+
+
+/*
+** Try to compile line on the stack as 'return <line>;'; on return, stack
+** has either compiled chunk or original line (if compilation failed).
+*/
+static int addreturn (lua_State *L) {
+ const char *line = lua_tostring(L, -1); /* original line */
+ const char *retline = lua_pushfstring(L, "return %s;", line);
+ int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin");
+ if (status == LUA_OK) {
+ lua_remove(L, -2); /* remove modified line */
+ if (line[0] != '\0') /* non empty? */
+ lua_saveline(L, line); /* keep history */
+ }
+ else
+ lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */
+ return status;
+}
+
+
+/*
+** Read multiple lines until a complete Lua statement
+*/
+static int multiline (lua_State *L) {
+ for (;;) { /* repeat until gets a complete statement */
+ size_t len;
+ const char *line = lua_tolstring(L, 1, &len); /* get what it has */
+ int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */
+ if (!incomplete(L, status) || !pushline(L, 0)) {
+ lua_saveline(L, line); /* keep history */
+ return status; /* cannot or should not try to add continuation line */
+ }
+ lua_pushliteral(L, "\n"); /* add newline... */
+ lua_insert(L, -2); /* ...between the two lines */
+ lua_concat(L, 3); /* join them */
+ }
+}
+
+
+/*
+** Read a line and try to load (compile) it first as an expression (by
+** adding "return " in front of it) and second as a statement. Return
+** the final status of load/call with the resulting function (if any)
+** in the top of the stack.
+*/
+static int loadline (lua_State *L) {
+ int status;
+ lua_settop(L, 0);
+ if (!pushline(L, 1))
+ return -1; /* no input */
+ if ((status = addreturn(L)) != LUA_OK) /* 'return ...' did not work? */
+ status = multiline(L); /* try as command, maybe with continuation lines */
+ lua_remove(L, 1); /* remove line from the stack */
+ lua_assert(lua_gettop(L) == 1);
+ return status;
+}
+
+
+/*
+** Prints (calling the Lua 'print' function) any values on the stack
+*/
+static void l_print (lua_State *L) {
+ int n = lua_gettop(L);
+ if (n > 0) { /* any result to be printed? */
+ luaL_checkstack(L, LUA_MINSTACK, "too many results to print");
+ lua_getglobal(L, "print");
+ lua_insert(L, 1);
+ if (lua_pcall(L, n, 0, 0) != LUA_OK)
+ l_message(progname, lua_pushfstring(L, "error calling 'print' (%s)",
+ lua_tostring(L, -1)));
+ }
+}
+
+
+/*
+** Do the REPL: repeatedly read (load) a line, evaluate (call) it, and
+** print any results.
+*/
+static void doREPL (lua_State *L) {
+ int status;
+ const char *oldprogname = progname;
+ progname = NULL; /* no 'progname' on errors in interactive mode */
+ lua_initreadline(L);
+ while ((status = loadline(L)) != -1) {
+ if (status == LUA_OK)
+ status = docall(L, 0, LUA_MULTRET);
+ if (status == LUA_OK) l_print(L);
+ else report(L, status);
+ }
+ lua_settop(L, 0); /* clear stack */
+ lua_writeline();
+ progname = oldprogname;
+}
+
+/* }================================================================== */
+
+
+/*
+** Main body of stand-alone interpreter (to be called in protected mode).
+** Reads the options and handles them all.
+*/
+static int pmain (lua_State *L) {
+ int argc = (int)lua_tointeger(L, 1);
+ char **argv = (char **)lua_touserdata(L, 2);
+ int script;
+ int args = collectargs(argv, &script);
+ int optlim = (script > 0) ? script : argc; /* first argv not an option */
+ luaL_checkversion(L); /* check that interpreter has correct version */
+ if (args == has_error) { /* bad arg? */
+ print_usage(argv[script]); /* 'script' has index of bad arg. */
+ return 0;
+ }
+ if (args & has_v) /* option '-v'? */
+ print_version();
+ if (args & has_E) { /* option '-E'? */
+ lua_pushboolean(L, 1); /* signal for libraries to ignore env. vars. */
+ lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
+ }
+ luaL_openlibs(L); /* open standard libraries */
+ createargtable(L, argv, argc, script); /* create table 'arg' */
+ lua_gc(L, LUA_GCRESTART); /* start GC... */
+ lua_gc(L, LUA_GCGEN, 0, 0); /* ...in generational mode */
+ if (!(args & has_E)) { /* no option '-E'? */
+ if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */
+ return 0; /* error running LUA_INIT */
+ }
+ if (!runargs(L, argv, optlim)) /* execute arguments -e and -l */
+ return 0; /* something failed */
+ if (script > 0) { /* execute main script (if there is one) */
+ if (handle_script(L, argv + script) != LUA_OK)
+ return 0; /* interrupt in case of error */
+ }
+ if (args & has_i) /* -i option? */
+ doREPL(L); /* do read-eval-print loop */
+ else if (script < 1 && !(args & (has_e | has_v))) { /* no active option? */
+ if (lua_stdin_is_tty()) { /* running in interactive mode? */
+ print_version();
+ doREPL(L); /* do read-eval-print loop */
+ }
+ else dofile(L, NULL); /* executes stdin as a file */
+ }
+ lua_pushboolean(L, 1); /* signal no errors */
+ return 1;
+}
+
+
+int main (int argc, char **argv) {
+ int status, result;
+ lua_State *L = luaL_newstate(); /* create state */
+ if (L == NULL) {
+ l_message(argv[0], "cannot create state: not enough memory");
+ return EXIT_FAILURE;
+ }
+ lua_gc(L, LUA_GCSTOP); /* stop GC while building state */
+ lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */
+ lua_pushinteger(L, argc); /* 1st argument */
+ lua_pushlightuserdata(L, argv); /* 2nd argument */
+ status = lua_pcall(L, 2, 1, 0); /* do the call */
+ result = lua_toboolean(L, -1); /* get result */
+ report(L, status);
+ lua_close(L);
+ return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lua.h b/src/libs/3rdparty/lua/src/lua.h
new file mode 100644
index 0000000000..fd16cf8050
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lua.h
@@ -0,0 +1,523 @@
+/*
+** $Id: lua.h $
+** Lua - A Scripting Language
+** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
+** See Copyright Notice at the end of this file
+*/
+
+
+#ifndef lua_h
+#define lua_h
+
+#include <stdarg.h>
+#include <stddef.h>
+
+
+#include "luaconf.h"
+
+
+#define LUA_VERSION_MAJOR "5"
+#define LUA_VERSION_MINOR "4"
+#define LUA_VERSION_RELEASE "6"
+
+#define LUA_VERSION_NUM 504
+#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 6)
+
+#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
+#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
+#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2023 Lua.org, PUC-Rio"
+#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
+
+
+/* mark for precompiled code ('<esc>Lua') */
+#define LUA_SIGNATURE "\x1bLua"
+
+/* option for multiple returns in 'lua_pcall' and 'lua_call' */
+#define LUA_MULTRET (-1)
+
+
+/*
+** Pseudo-indices
+** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty
+** space after that to help overflow detection)
+*/
+#define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000)
+#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i))
+
+
+/* thread status */
+#define LUA_OK 0
+#define LUA_YIELD 1
+#define LUA_ERRRUN 2
+#define LUA_ERRSYNTAX 3
+#define LUA_ERRMEM 4
+#define LUA_ERRERR 5
+
+
+typedef struct lua_State lua_State;
+
+
+/*
+** basic types
+*/
+#define LUA_TNONE (-1)
+
+#define LUA_TNIL 0
+#define LUA_TBOOLEAN 1
+#define LUA_TLIGHTUSERDATA 2
+#define LUA_TNUMBER 3
+#define LUA_TSTRING 4
+#define LUA_TTABLE 5
+#define LUA_TFUNCTION 6
+#define LUA_TUSERDATA 7
+#define LUA_TTHREAD 8
+
+#define LUA_NUMTYPES 9
+
+
+
+/* minimum Lua stack available to a C function */
+#define LUA_MINSTACK 20
+
+
+/* predefined values in the registry */
+#define LUA_RIDX_MAINTHREAD 1
+#define LUA_RIDX_GLOBALS 2
+#define LUA_RIDX_LAST LUA_RIDX_GLOBALS
+
+
+/* type of numbers in Lua */
+typedef LUA_NUMBER lua_Number;
+
+
+/* type for integer functions */
+typedef LUA_INTEGER lua_Integer;
+
+/* unsigned integer type */
+typedef LUA_UNSIGNED lua_Unsigned;
+
+/* type for continuation-function contexts */
+typedef LUA_KCONTEXT lua_KContext;
+
+
+/*
+** Type for C functions registered with Lua
+*/
+typedef int (*lua_CFunction) (lua_State *L);
+
+/*
+** Type for continuation functions
+*/
+typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx);
+
+
+/*
+** Type for functions that read/write blocks when loading/dumping Lua chunks
+*/
+typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
+
+typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud);
+
+
+/*
+** Type for memory-allocation functions
+*/
+typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
+
+
+/*
+** Type for warning functions
+*/
+typedef void (*lua_WarnFunction) (void *ud, const char *msg, int tocont);
+
+
+/*
+** Type used by the debug API to collect debug information
+*/
+typedef struct lua_Debug lua_Debug;
+
+
+/*
+** Functions to be called by the debugger in specific events
+*/
+typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
+
+
+/*
+** generic extra include file
+*/
+#if defined(LUA_USER_H)
+#include LUA_USER_H
+#endif
+
+
+/*
+** RCS ident string
+*/
+extern const char lua_ident[];
+
+
+/*
+** state manipulation
+*/
+LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
+LUA_API void (lua_close) (lua_State *L);
+LUA_API lua_State *(lua_newthread) (lua_State *L);
+LUA_API int (lua_closethread) (lua_State *L, lua_State *from);
+LUA_API int (lua_resetthread) (lua_State *L); /* Deprecated! */
+
+LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
+
+
+LUA_API lua_Number (lua_version) (lua_State *L);
+
+
+/*
+** basic stack manipulation
+*/
+LUA_API int (lua_absindex) (lua_State *L, int idx);
+LUA_API int (lua_gettop) (lua_State *L);
+LUA_API void (lua_settop) (lua_State *L, int idx);
+LUA_API void (lua_pushvalue) (lua_State *L, int idx);
+LUA_API void (lua_rotate) (lua_State *L, int idx, int n);
+LUA_API void (lua_copy) (lua_State *L, int fromidx, int toidx);
+LUA_API int (lua_checkstack) (lua_State *L, int n);
+
+LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n);
+
+
+/*
+** access functions (stack -> C)
+*/
+
+LUA_API int (lua_isnumber) (lua_State *L, int idx);
+LUA_API int (lua_isstring) (lua_State *L, int idx);
+LUA_API int (lua_iscfunction) (lua_State *L, int idx);
+LUA_API int (lua_isinteger) (lua_State *L, int idx);
+LUA_API int (lua_isuserdata) (lua_State *L, int idx);
+LUA_API int (lua_type) (lua_State *L, int idx);
+LUA_API const char *(lua_typename) (lua_State *L, int tp);
+
+LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum);
+LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum);
+LUA_API int (lua_toboolean) (lua_State *L, int idx);
+LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
+LUA_API lua_Unsigned (lua_rawlen) (lua_State *L, int idx);
+LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
+LUA_API void *(lua_touserdata) (lua_State *L, int idx);
+LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
+LUA_API const void *(lua_topointer) (lua_State *L, int idx);
+
+
+/*
+** Comparison and arithmetic functions
+*/
+
+#define LUA_OPADD 0 /* ORDER TM, ORDER OP */
+#define LUA_OPSUB 1
+#define LUA_OPMUL 2
+#define LUA_OPMOD 3
+#define LUA_OPPOW 4
+#define LUA_OPDIV 5
+#define LUA_OPIDIV 6
+#define LUA_OPBAND 7
+#define LUA_OPBOR 8
+#define LUA_OPBXOR 9
+#define LUA_OPSHL 10
+#define LUA_OPSHR 11
+#define LUA_OPUNM 12
+#define LUA_OPBNOT 13
+
+LUA_API void (lua_arith) (lua_State *L, int op);
+
+#define LUA_OPEQ 0
+#define LUA_OPLT 1
+#define LUA_OPLE 2
+
+LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2);
+LUA_API int (lua_compare) (lua_State *L, int idx1, int idx2, int op);
+
+
+/*
+** push functions (C -> stack)
+*/
+LUA_API void (lua_pushnil) (lua_State *L);
+LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
+LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
+LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len);
+LUA_API const char *(lua_pushstring) (lua_State *L, const char *s);
+LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
+ va_list argp);
+LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
+LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
+LUA_API void (lua_pushboolean) (lua_State *L, int b);
+LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
+LUA_API int (lua_pushthread) (lua_State *L);
+
+
+/*
+** get functions (Lua -> stack)
+*/
+LUA_API int (lua_getglobal) (lua_State *L, const char *name);
+LUA_API int (lua_gettable) (lua_State *L, int idx);
+LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k);
+LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n);
+LUA_API int (lua_rawget) (lua_State *L, int idx);
+LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n);
+LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p);
+
+LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec);
+LUA_API void *(lua_newuserdatauv) (lua_State *L, size_t sz, int nuvalue);
+LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
+LUA_API int (lua_getiuservalue) (lua_State *L, int idx, int n);
+
+
+/*
+** set functions (stack -> Lua)
+*/
+LUA_API void (lua_setglobal) (lua_State *L, const char *name);
+LUA_API void (lua_settable) (lua_State *L, int idx);
+LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k);
+LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n);
+LUA_API void (lua_rawset) (lua_State *L, int idx);
+LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n);
+LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p);
+LUA_API int (lua_setmetatable) (lua_State *L, int objindex);
+LUA_API int (lua_setiuservalue) (lua_State *L, int idx, int n);
+
+
+/*
+** 'load' and 'call' functions (load and run Lua code)
+*/
+LUA_API void (lua_callk) (lua_State *L, int nargs, int nresults,
+ lua_KContext ctx, lua_KFunction k);
+#define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL)
+
+LUA_API int (lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc,
+ lua_KContext ctx, lua_KFunction k);
+#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL)
+
+LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt,
+ const char *chunkname, const char *mode);
+
+LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data, int strip);
+
+
+/*
+** coroutine functions
+*/
+LUA_API int (lua_yieldk) (lua_State *L, int nresults, lua_KContext ctx,
+ lua_KFunction k);
+LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg,
+ int *nres);
+LUA_API int (lua_status) (lua_State *L);
+LUA_API int (lua_isyieldable) (lua_State *L);
+
+#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL)
+
+
+/*
+** Warning-related functions
+*/
+LUA_API void (lua_setwarnf) (lua_State *L, lua_WarnFunction f, void *ud);
+LUA_API void (lua_warning) (lua_State *L, const char *msg, int tocont);
+
+
+/*
+** garbage-collection function and options
+*/
+
+#define LUA_GCSTOP 0
+#define LUA_GCRESTART 1
+#define LUA_GCCOLLECT 2
+#define LUA_GCCOUNT 3
+#define LUA_GCCOUNTB 4
+#define LUA_GCSTEP 5
+#define LUA_GCSETPAUSE 6
+#define LUA_GCSETSTEPMUL 7
+#define LUA_GCISRUNNING 9
+#define LUA_GCGEN 10
+#define LUA_GCINC 11
+
+LUA_API int (lua_gc) (lua_State *L, int what, ...);
+
+
+/*
+** miscellaneous functions
+*/
+
+LUA_API int (lua_error) (lua_State *L);
+
+LUA_API int (lua_next) (lua_State *L, int idx);
+
+LUA_API void (lua_concat) (lua_State *L, int n);
+LUA_API void (lua_len) (lua_State *L, int idx);
+
+LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s);
+
+LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
+LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
+
+LUA_API void (lua_toclose) (lua_State *L, int idx);
+LUA_API void (lua_closeslot) (lua_State *L, int idx);
+
+
+/*
+** {==============================================================
+** some useful macros
+** ===============================================================
+*/
+
+#define lua_getextraspace(L) ((void *)((char *)(L) - LUA_EXTRASPACE))
+
+#define lua_tonumber(L,i) lua_tonumberx(L,(i),NULL)
+#define lua_tointeger(L,i) lua_tointegerx(L,(i),NULL)
+
+#define lua_pop(L,n) lua_settop(L, -(n)-1)
+
+#define lua_newtable(L) lua_createtable(L, 0, 0)
+
+#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))
+
+#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
+
+#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION)
+#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE)
+#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
+#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL)
+#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN)
+#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD)
+#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE)
+#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0)
+
+#define lua_pushliteral(L, s) lua_pushstring(L, "" s)
+
+#define lua_pushglobaltable(L) \
+ ((void)lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS))
+
+#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
+
+
+#define lua_insert(L,idx) lua_rotate(L, (idx), 1)
+
+#define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1))
+
+#define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1))
+
+/* }============================================================== */
+
+
+/*
+** {==============================================================
+** compatibility macros
+** ===============================================================
+*/
+#if defined(LUA_COMPAT_APIINTCASTS)
+
+#define lua_pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n))
+#define lua_tounsignedx(L,i,is) ((lua_Unsigned)lua_tointegerx(L,i,is))
+#define lua_tounsigned(L,i) lua_tounsignedx(L,(i),NULL)
+
+#endif
+
+#define lua_newuserdata(L,s) lua_newuserdatauv(L,s,1)
+#define lua_getuservalue(L,idx) lua_getiuservalue(L,idx,1)
+#define lua_setuservalue(L,idx) lua_setiuservalue(L,idx,1)
+
+#define LUA_NUMTAGS LUA_NUMTYPES
+
+/* }============================================================== */
+
+/*
+** {======================================================================
+** Debug API
+** =======================================================================
+*/
+
+
+/*
+** Event codes
+*/
+#define LUA_HOOKCALL 0
+#define LUA_HOOKRET 1
+#define LUA_HOOKLINE 2
+#define LUA_HOOKCOUNT 3
+#define LUA_HOOKTAILCALL 4
+
+
+/*
+** Event masks
+*/
+#define LUA_MASKCALL (1 << LUA_HOOKCALL)
+#define LUA_MASKRET (1 << LUA_HOOKRET)
+#define LUA_MASKLINE (1 << LUA_HOOKLINE)
+#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
+
+
+LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar);
+LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar);
+LUA_API const char *(lua_getlocal) (lua_State *L, const lua_Debug *ar, int n);
+LUA_API const char *(lua_setlocal) (lua_State *L, const lua_Debug *ar, int n);
+LUA_API const char *(lua_getupvalue) (lua_State *L, int funcindex, int n);
+LUA_API const char *(lua_setupvalue) (lua_State *L, int funcindex, int n);
+
+LUA_API void *(lua_upvalueid) (lua_State *L, int fidx, int n);
+LUA_API void (lua_upvaluejoin) (lua_State *L, int fidx1, int n1,
+ int fidx2, int n2);
+
+LUA_API void (lua_sethook) (lua_State *L, lua_Hook func, int mask, int count);
+LUA_API lua_Hook (lua_gethook) (lua_State *L);
+LUA_API int (lua_gethookmask) (lua_State *L);
+LUA_API int (lua_gethookcount) (lua_State *L);
+
+LUA_API int (lua_setcstacklimit) (lua_State *L, unsigned int limit);
+
+struct lua_Debug {
+ int event;
+ const char *name; /* (n) */
+ const char *namewhat; /* (n) 'global', 'local', 'field', 'method' */
+ const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */
+ const char *source; /* (S) */
+ size_t srclen; /* (S) */
+ int currentline; /* (l) */
+ int linedefined; /* (S) */
+ int lastlinedefined; /* (S) */
+ unsigned char nups; /* (u) number of upvalues */
+ unsigned char nparams;/* (u) number of parameters */
+ char isvararg; /* (u) */
+ char istailcall; /* (t) */
+ unsigned short ftransfer; /* (r) index of first value transferred */
+ unsigned short ntransfer; /* (r) number of transferred values */
+ char short_src[LUA_IDSIZE]; /* (S) */
+ /* private part */
+ struct CallInfo *i_ci; /* active function */
+};
+
+/* }====================================================================== */
+
+
+/******************************************************************************
+* Copyright (C) 1994-2023 Lua.org, PUC-Rio.
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files (the
+* "Software"), to deal in the Software without restriction, including
+* without limitation the rights to use, copy, modify, merge, publish,
+* distribute, sublicense, and/or sell copies of the Software, and to
+* permit persons to whom the Software is furnished to do so, subject to
+* the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+******************************************************************************/
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lua.hpp b/src/libs/3rdparty/lua/src/lua.hpp
new file mode 100644
index 0000000000..ec417f5946
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lua.hpp
@@ -0,0 +1,9 @@
+// lua.hpp
+// Lua header files for C++
+// <<extern "C">> not supplied automatically because Lua also compiles as C++
+
+extern "C" {
+#include "lua.h"
+#include "lualib.h"
+#include "lauxlib.h"
+}
diff --git a/src/libs/3rdparty/lua/src/luac.c b/src/libs/3rdparty/lua/src/luac.c
new file mode 100644
index 0000000000..5f4a141bdd
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/luac.c
@@ -0,0 +1,723 @@
+/*
+** $Id: luac.c $
+** Lua compiler (saves bytecodes to files; also lists bytecodes)
+** See Copyright Notice in lua.h
+*/
+
+#define luac_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+#include "lauxlib.h"
+
+#include "ldebug.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lopnames.h"
+#include "lstate.h"
+#include "lundump.h"
+
+static void PrintFunction(const Proto* f, int full);
+#define luaU_print PrintFunction
+
+#define PROGNAME "luac" /* default program name */
+#define OUTPUT PROGNAME ".out" /* default output file */
+
+static int listing=0; /* list bytecodes? */
+static int dumping=1; /* dump bytecodes? */
+static int stripping=0; /* strip debug information? */
+static char Output[]={ OUTPUT }; /* default output file name */
+static const char* output=Output; /* actual output file name */
+static const char* progname=PROGNAME; /* actual program name */
+static TString **tmname;
+
+static void fatal(const char* message)
+{
+ fprintf(stderr,"%s: %s\n",progname,message);
+ exit(EXIT_FAILURE);
+}
+
+static void cannot(const char* what)
+{
+ fprintf(stderr,"%s: cannot %s %s: %s\n",progname,what,output,strerror(errno));
+ exit(EXIT_FAILURE);
+}
+
+static void usage(const char* message)
+{
+ if (*message=='-')
+ fprintf(stderr,"%s: unrecognized option '%s'\n",progname,message);
+ else
+ fprintf(stderr,"%s: %s\n",progname,message);
+ fprintf(stderr,
+ "usage: %s [options] [filenames]\n"
+ "Available options are:\n"
+ " -l list (use -l -l for full listing)\n"
+ " -o name output to file 'name' (default is \"%s\")\n"
+ " -p parse only\n"
+ " -s strip debug information\n"
+ " -v show version information\n"
+ " -- stop handling options\n"
+ " - stop handling options and process stdin\n"
+ ,progname,Output);
+ exit(EXIT_FAILURE);
+}
+
+#define IS(s) (strcmp(argv[i],s)==0)
+
+static int doargs(int argc, char* argv[])
+{
+ int i;
+ int version=0;
+ if (argv[0]!=NULL && *argv[0]!=0) progname=argv[0];
+ for (i=1; i<argc; i++)
+ {
+ if (*argv[i]!='-') /* end of options; keep it */
+ break;
+ else if (IS("--")) /* end of options; skip it */
+ {
+ ++i;
+ if (version) ++version;
+ break;
+ }
+ else if (IS("-")) /* end of options; use stdin */
+ break;
+ else if (IS("-l")) /* list */
+ ++listing;
+ else if (IS("-o")) /* output file */
+ {
+ output=argv[++i];
+ if (output==NULL || *output==0 || (*output=='-' && output[1]!=0))
+ usage("'-o' needs argument");
+ if (IS("-")) output=NULL;
+ }
+ else if (IS("-p")) /* parse only */
+ dumping=0;
+ else if (IS("-s")) /* strip debug information */
+ stripping=1;
+ else if (IS("-v")) /* show version */
+ ++version;
+ else /* unknown option */
+ usage(argv[i]);
+ }
+ if (i==argc && (listing || !dumping))
+ {
+ dumping=0;
+ argv[--i]=Output;
+ }
+ if (version)
+ {
+ printf("%s\n",LUA_COPYRIGHT);
+ if (version==argc-1) exit(EXIT_SUCCESS);
+ }
+ return i;
+}
+
+#define FUNCTION "(function()end)();\n"
+
+static const char* reader(lua_State* L, void* ud, size_t* size)
+{
+ UNUSED(L);
+ if ((*(int*)ud)--)
+ {
+ *size=sizeof(FUNCTION)-1;
+ return FUNCTION;
+ }
+ else
+ {
+ *size=0;
+ return NULL;
+ }
+}
+
+#define toproto(L,i) getproto(s2v(L->top.p+(i)))
+
+static const Proto* combine(lua_State* L, int n)
+{
+ if (n==1)
+ return toproto(L,-1);
+ else
+ {
+ Proto* f;
+ int i=n;
+ if (lua_load(L,reader,&i,"=(" PROGNAME ")",NULL)!=LUA_OK) fatal(lua_tostring(L,-1));
+ f=toproto(L,-1);
+ for (i=0; i<n; i++)
+ {
+ f->p[i]=toproto(L,i-n-1);
+ if (f->p[i]->sizeupvalues>0) f->p[i]->upvalues[0].instack=0;
+ }
+ return f;
+ }
+}
+
+static int writer(lua_State* L, const void* p, size_t size, void* u)
+{
+ UNUSED(L);
+ return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0);
+}
+
+static int pmain(lua_State* L)
+{
+ int argc=(int)lua_tointeger(L,1);
+ char** argv=(char**)lua_touserdata(L,2);
+ const Proto* f;
+ int i;
+ tmname=G(L)->tmname;
+ if (!lua_checkstack(L,argc)) fatal("too many input files");
+ for (i=0; i<argc; i++)
+ {
+ const char* filename=IS("-") ? NULL : argv[i];
+ if (luaL_loadfile(L,filename)!=LUA_OK) fatal(lua_tostring(L,-1));
+ }
+ f=combine(L,argc);
+ if (listing) luaU_print(f,listing>1);
+ if (dumping)
+ {
+ FILE* D= (output==NULL) ? stdout : fopen(output,"wb");
+ if (D==NULL) cannot("open");
+ lua_lock(L);
+ luaU_dump(L,f,writer,D,stripping);
+ lua_unlock(L);
+ if (ferror(D)) cannot("write");
+ if (fclose(D)) cannot("close");
+ }
+ return 0;
+}
+
+int main(int argc, char* argv[])
+{
+ lua_State* L;
+ int i=doargs(argc,argv);
+ argc-=i; argv+=i;
+ if (argc<=0) usage("no input files given");
+ L=luaL_newstate();
+ if (L==NULL) fatal("cannot create state: not enough memory");
+ lua_pushcfunction(L,&pmain);
+ lua_pushinteger(L,argc);
+ lua_pushlightuserdata(L,argv);
+ if (lua_pcall(L,2,0,0)!=LUA_OK) fatal(lua_tostring(L,-1));
+ lua_close(L);
+ return EXIT_SUCCESS;
+}
+
+/*
+** print bytecodes
+*/
+
+#define UPVALNAME(x) ((f->upvalues[x].name) ? getstr(f->upvalues[x].name) : "-")
+#define VOID(p) ((const void*)(p))
+#define eventname(i) (getstr(tmname[i]))
+
+static void PrintString(const TString* ts)
+{
+ const char* s=getstr(ts);
+ size_t i,n=tsslen(ts);
+ printf("\"");
+ for (i=0; i<n; i++)
+ {
+ int c=(int)(unsigned char)s[i];
+ switch (c)
+ {
+ case '"':
+ printf("\\\"");
+ break;
+ case '\\':
+ printf("\\\\");
+ break;
+ case '\a':
+ printf("\\a");
+ break;
+ case '\b':
+ printf("\\b");
+ break;
+ case '\f':
+ printf("\\f");
+ break;
+ case '\n':
+ printf("\\n");
+ break;
+ case '\r':
+ printf("\\r");
+ break;
+ case '\t':
+ printf("\\t");
+ break;
+ case '\v':
+ printf("\\v");
+ break;
+ default:
+ if (isprint(c)) printf("%c",c); else printf("\\%03d",c);
+ break;
+ }
+ }
+ printf("\"");
+}
+
+static void PrintType(const Proto* f, int i)
+{
+ const TValue* o=&f->k[i];
+ switch (ttypetag(o))
+ {
+ case LUA_VNIL:
+ printf("N");
+ break;
+ case LUA_VFALSE:
+ case LUA_VTRUE:
+ printf("B");
+ break;
+ case LUA_VNUMFLT:
+ printf("F");
+ break;
+ case LUA_VNUMINT:
+ printf("I");
+ break;
+ case LUA_VSHRSTR:
+ case LUA_VLNGSTR:
+ printf("S");
+ break;
+ default: /* cannot happen */
+ printf("?%d",ttypetag(o));
+ break;
+ }
+ printf("\t");
+}
+
+static void PrintConstant(const Proto* f, int i)
+{
+ const TValue* o=&f->k[i];
+ switch (ttypetag(o))
+ {
+ case LUA_VNIL:
+ printf("nil");
+ break;
+ case LUA_VFALSE:
+ printf("false");
+ break;
+ case LUA_VTRUE:
+ printf("true");
+ break;
+ case LUA_VNUMFLT:
+ {
+ char buff[100];
+ sprintf(buff,LUA_NUMBER_FMT,fltvalue(o));
+ printf("%s",buff);
+ if (buff[strspn(buff,"-0123456789")]=='\0') printf(".0");
+ break;
+ }
+ case LUA_VNUMINT:
+ printf(LUA_INTEGER_FMT,ivalue(o));
+ break;
+ case LUA_VSHRSTR:
+ case LUA_VLNGSTR:
+ PrintString(tsvalue(o));
+ break;
+ default: /* cannot happen */
+ printf("?%d",ttypetag(o));
+ break;
+ }
+}
+
+#define COMMENT "\t; "
+#define EXTRAARG GETARG_Ax(code[pc+1])
+#define EXTRAARGC (EXTRAARG*(MAXARG_C+1))
+#define ISK (isk ? "k" : "")
+
+static void PrintCode(const Proto* f)
+{
+ const Instruction* code=f->code;
+ int pc,n=f->sizecode;
+ for (pc=0; pc<n; pc++)
+ {
+ Instruction i=code[pc];
+ OpCode o=GET_OPCODE(i);
+ int a=GETARG_A(i);
+ int b=GETARG_B(i);
+ int c=GETARG_C(i);
+ int ax=GETARG_Ax(i);
+ int bx=GETARG_Bx(i);
+ int sb=GETARG_sB(i);
+ int sc=GETARG_sC(i);
+ int sbx=GETARG_sBx(i);
+ int isk=GETARG_k(i);
+ int line=luaG_getfuncline(f,pc);
+ printf("\t%d\t",pc+1);
+ if (line>0) printf("[%d]\t",line); else printf("[-]\t");
+ printf("%-9s\t",opnames[o]);
+ switch (o)
+ {
+ case OP_MOVE:
+ printf("%d %d",a,b);
+ break;
+ case OP_LOADI:
+ printf("%d %d",a,sbx);
+ break;
+ case OP_LOADF:
+ printf("%d %d",a,sbx);
+ break;
+ case OP_LOADK:
+ printf("%d %d",a,bx);
+ printf(COMMENT); PrintConstant(f,bx);
+ break;
+ case OP_LOADKX:
+ printf("%d",a);
+ printf(COMMENT); PrintConstant(f,EXTRAARG);
+ break;
+ case OP_LOADFALSE:
+ printf("%d",a);
+ break;
+ case OP_LFALSESKIP:
+ printf("%d",a);
+ break;
+ case OP_LOADTRUE:
+ printf("%d",a);
+ break;
+ case OP_LOADNIL:
+ printf("%d %d",a,b);
+ printf(COMMENT "%d out",b+1);
+ break;
+ case OP_GETUPVAL:
+ printf("%d %d",a,b);
+ printf(COMMENT "%s",UPVALNAME(b));
+ break;
+ case OP_SETUPVAL:
+ printf("%d %d",a,b);
+ printf(COMMENT "%s",UPVALNAME(b));
+ break;
+ case OP_GETTABUP:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT "%s",UPVALNAME(b));
+ printf(" "); PrintConstant(f,c);
+ break;
+ case OP_GETTABLE:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_GETI:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_GETFIELD:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_SETTABUP:
+ printf("%d %d %d%s",a,b,c,ISK);
+ printf(COMMENT "%s",UPVALNAME(a));
+ printf(" "); PrintConstant(f,b);
+ if (isk) { printf(" "); PrintConstant(f,c); }
+ break;
+ case OP_SETTABLE:
+ printf("%d %d %d%s",a,b,c,ISK);
+ if (isk) { printf(COMMENT); PrintConstant(f,c); }
+ break;
+ case OP_SETI:
+ printf("%d %d %d%s",a,b,c,ISK);
+ if (isk) { printf(COMMENT); PrintConstant(f,c); }
+ break;
+ case OP_SETFIELD:
+ printf("%d %d %d%s",a,b,c,ISK);
+ printf(COMMENT); PrintConstant(f,b);
+ if (isk) { printf(" "); PrintConstant(f,c); }
+ break;
+ case OP_NEWTABLE:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT "%d",c+EXTRAARGC);
+ break;
+ case OP_SELF:
+ printf("%d %d %d%s",a,b,c,ISK);
+ if (isk) { printf(COMMENT); PrintConstant(f,c); }
+ break;
+ case OP_ADDI:
+ printf("%d %d %d",a,b,sc);
+ break;
+ case OP_ADDK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_SUBK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_MULK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_MODK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_POWK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_DIVK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_IDIVK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_BANDK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_BORK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_BXORK:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT); PrintConstant(f,c);
+ break;
+ case OP_SHRI:
+ printf("%d %d %d",a,b,sc);
+ break;
+ case OP_SHLI:
+ printf("%d %d %d",a,b,sc);
+ break;
+ case OP_ADD:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_SUB:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_MUL:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_MOD:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_POW:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_DIV:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_IDIV:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_BAND:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_BOR:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_BXOR:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_SHL:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_SHR:
+ printf("%d %d %d",a,b,c);
+ break;
+ case OP_MMBIN:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT "%s",eventname(c));
+ break;
+ case OP_MMBINI:
+ printf("%d %d %d %d",a,sb,c,isk);
+ printf(COMMENT "%s",eventname(c));
+ if (isk) printf(" flip");
+ break;
+ case OP_MMBINK:
+ printf("%d %d %d %d",a,b,c,isk);
+ printf(COMMENT "%s ",eventname(c)); PrintConstant(f,b);
+ if (isk) printf(" flip");
+ break;
+ case OP_UNM:
+ printf("%d %d",a,b);
+ break;
+ case OP_BNOT:
+ printf("%d %d",a,b);
+ break;
+ case OP_NOT:
+ printf("%d %d",a,b);
+ break;
+ case OP_LEN:
+ printf("%d %d",a,b);
+ break;
+ case OP_CONCAT:
+ printf("%d %d",a,b);
+ break;
+ case OP_CLOSE:
+ printf("%d",a);
+ break;
+ case OP_TBC:
+ printf("%d",a);
+ break;
+ case OP_JMP:
+ printf("%d",GETARG_sJ(i));
+ printf(COMMENT "to %d",GETARG_sJ(i)+pc+2);
+ break;
+ case OP_EQ:
+ printf("%d %d %d",a,b,isk);
+ break;
+ case OP_LT:
+ printf("%d %d %d",a,b,isk);
+ break;
+ case OP_LE:
+ printf("%d %d %d",a,b,isk);
+ break;
+ case OP_EQK:
+ printf("%d %d %d",a,b,isk);
+ printf(COMMENT); PrintConstant(f,b);
+ break;
+ case OP_EQI:
+ printf("%d %d %d",a,sb,isk);
+ break;
+ case OP_LTI:
+ printf("%d %d %d",a,sb,isk);
+ break;
+ case OP_LEI:
+ printf("%d %d %d",a,sb,isk);
+ break;
+ case OP_GTI:
+ printf("%d %d %d",a,sb,isk);
+ break;
+ case OP_GEI:
+ printf("%d %d %d",a,sb,isk);
+ break;
+ case OP_TEST:
+ printf("%d %d",a,isk);
+ break;
+ case OP_TESTSET:
+ printf("%d %d %d",a,b,isk);
+ break;
+ case OP_CALL:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT);
+ if (b==0) printf("all in "); else printf("%d in ",b-1);
+ if (c==0) printf("all out"); else printf("%d out",c-1);
+ break;
+ case OP_TAILCALL:
+ printf("%d %d %d%s",a,b,c,ISK);
+ printf(COMMENT "%d in",b-1);
+ break;
+ case OP_RETURN:
+ printf("%d %d %d%s",a,b,c,ISK);
+ printf(COMMENT);
+ if (b==0) printf("all out"); else printf("%d out",b-1);
+ break;
+ case OP_RETURN0:
+ break;
+ case OP_RETURN1:
+ printf("%d",a);
+ break;
+ case OP_FORLOOP:
+ printf("%d %d",a,bx);
+ printf(COMMENT "to %d",pc-bx+2);
+ break;
+ case OP_FORPREP:
+ printf("%d %d",a,bx);
+ printf(COMMENT "exit to %d",pc+bx+3);
+ break;
+ case OP_TFORPREP:
+ printf("%d %d",a,bx);
+ printf(COMMENT "to %d",pc+bx+2);
+ break;
+ case OP_TFORCALL:
+ printf("%d %d",a,c);
+ break;
+ case OP_TFORLOOP:
+ printf("%d %d",a,bx);
+ printf(COMMENT "to %d",pc-bx+2);
+ break;
+ case OP_SETLIST:
+ printf("%d %d %d",a,b,c);
+ if (isk) printf(COMMENT "%d",c+EXTRAARGC);
+ break;
+ case OP_CLOSURE:
+ printf("%d %d",a,bx);
+ printf(COMMENT "%p",VOID(f->p[bx]));
+ break;
+ case OP_VARARG:
+ printf("%d %d",a,c);
+ printf(COMMENT);
+ if (c==0) printf("all out"); else printf("%d out",c-1);
+ break;
+ case OP_VARARGPREP:
+ printf("%d",a);
+ break;
+ case OP_EXTRAARG:
+ printf("%d",ax);
+ break;
+#if 0
+ default:
+ printf("%d %d %d",a,b,c);
+ printf(COMMENT "not handled");
+ break;
+#endif
+ }
+ printf("\n");
+ }
+}
+
+
+#define SS(x) ((x==1)?"":"s")
+#define S(x) (int)(x),SS(x)
+
+static void PrintHeader(const Proto* f)
+{
+ const char* s=f->source ? getstr(f->source) : "=?";
+ if (*s=='@' || *s=='=')
+ s++;
+ else if (*s==LUA_SIGNATURE[0])
+ s="(bstring)";
+ else
+ s="(string)";
+ printf("\n%s <%s:%d,%d> (%d instruction%s at %p)\n",
+ (f->linedefined==0)?"main":"function",s,
+ f->linedefined,f->lastlinedefined,
+ S(f->sizecode),VOID(f));
+ printf("%d%s param%s, %d slot%s, %d upvalue%s, ",
+ (int)(f->numparams),f->is_vararg?"+":"",SS(f->numparams),
+ S(f->maxstacksize),S(f->sizeupvalues));
+ printf("%d local%s, %d constant%s, %d function%s\n",
+ S(f->sizelocvars),S(f->sizek),S(f->sizep));
+}
+
+static void PrintDebug(const Proto* f)
+{
+ int i,n;
+ n=f->sizek;
+ printf("constants (%d) for %p:\n",n,VOID(f));
+ for (i=0; i<n; i++)
+ {
+ printf("\t%d\t",i);
+ PrintType(f,i);
+ PrintConstant(f,i);
+ printf("\n");
+ }
+ n=f->sizelocvars;
+ printf("locals (%d) for %p:\n",n,VOID(f));
+ for (i=0; i<n; i++)
+ {
+ printf("\t%d\t%s\t%d\t%d\n",
+ i,getstr(f->locvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1);
+ }
+ n=f->sizeupvalues;
+ printf("upvalues (%d) for %p:\n",n,VOID(f));
+ for (i=0; i<n; i++)
+ {
+ printf("\t%d\t%s\t%d\t%d\n",
+ i,UPVALNAME(i),f->upvalues[i].instack,f->upvalues[i].idx);
+ }
+}
+
+static void PrintFunction(const Proto* f, int full)
+{
+ int i,n=f->sizep;
+ PrintHeader(f);
+ PrintCode(f);
+ if (full) PrintDebug(f);
+ for (i=0; i<n; i++) PrintFunction(f->p[i],full);
+}
diff --git a/src/libs/3rdparty/lua/src/luaconf.h b/src/libs/3rdparty/lua/src/luaconf.h
new file mode 100644
index 0000000000..433df49295
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/luaconf.h
@@ -0,0 +1,793 @@
+/*
+** $Id: luaconf.h $
+** Configuration file for Lua
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef luaconf_h
+#define luaconf_h
+
+#include <limits.h>
+#include <stddef.h>
+
+
+/*
+** ===================================================================
+** General Configuration File for Lua
+**
+** Some definitions here can be changed externally, through the compiler
+** (e.g., with '-D' options): They are commented out or protected
+** by '#if !defined' guards. However, several other definitions
+** should be changed directly here, either because they affect the
+** Lua ABI (by making the changes here, you ensure that all software
+** connected to Lua, such as C libraries, will be compiled with the same
+** configuration); or because they are seldom changed.
+**
+** Search for "@@" to find all configurable definitions.
+** ===================================================================
+*/
+
+
+/*
+** {====================================================================
+** System Configuration: macros to adapt (if needed) Lua to some
+** particular platform, for instance restricting it to C89.
+** =====================================================================
+*/
+
+/*
+@@ LUA_USE_C89 controls the use of non-ISO-C89 features.
+** Define it if you want Lua to avoid the use of a few C99 features
+** or Windows-specific features on Windows.
+*/
+/* #define LUA_USE_C89 */
+
+
+/*
+** By default, Lua on Windows use (some) specific Windows features
+*/
+#if !defined(LUA_USE_C89) && defined(_WIN32) && !defined(_WIN32_WCE) &&!defined(LUA_USE_WINDOWS)
+#define LUA_USE_WINDOWS /* enable goodies for regular Windows */
+#endif
+
+
+#if defined(LUA_USE_WINDOWS)
+#define LUA_DL_DLL /* enable support for DLL */
+#define LUA_USE_C89 /* broadly, Windows is C89 */
+#endif
+
+
+#if defined(LUA_USE_LINUX)
+#define LUA_USE_POSIX
+#define LUA_USE_DLOPEN /* needs an extra library: -ldl */
+#endif
+
+
+#if defined(LUA_USE_MACOSX)
+#define LUA_USE_POSIX
+#define LUA_USE_DLOPEN /* MacOS does not need -ldl */
+#endif
+
+
+#if defined(LUA_USE_IOS)
+#define LUA_USE_POSIX
+#define LUA_USE_DLOPEN
+#endif
+
+
+/*
+@@ LUAI_IS32INT is true iff 'int' has (at least) 32 bits.
+*/
+#define LUAI_IS32INT ((UINT_MAX >> 30) >= 3)
+
+/* }================================================================== */
+
+
+
+/*
+** {==================================================================
+** Configuration for Number types. These options should not be
+** set externally, because any other code connected to Lua must
+** use the same configuration.
+** ===================================================================
+*/
+
+/*
+@@ LUA_INT_TYPE defines the type for Lua integers.
+@@ LUA_FLOAT_TYPE defines the type for Lua floats.
+** Lua should work fine with any mix of these options supported
+** by your C compiler. The usual configurations are 64-bit integers
+** and 'double' (the default), 32-bit integers and 'float' (for
+** restricted platforms), and 'long'/'double' (for C compilers not
+** compliant with C99, which may not have support for 'long long').
+*/
+
+/* predefined options for LUA_INT_TYPE */
+#define LUA_INT_INT 1
+#define LUA_INT_LONG 2
+#define LUA_INT_LONGLONG 3
+
+/* predefined options for LUA_FLOAT_TYPE */
+#define LUA_FLOAT_FLOAT 1
+#define LUA_FLOAT_DOUBLE 2
+#define LUA_FLOAT_LONGDOUBLE 3
+
+
+/* Default configuration ('long long' and 'double', for 64-bit Lua) */
+#define LUA_INT_DEFAULT LUA_INT_LONGLONG
+#define LUA_FLOAT_DEFAULT LUA_FLOAT_DOUBLE
+
+
+/*
+@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats.
+*/
+#define LUA_32BITS 0
+
+
+/*
+@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for
+** C89 ('long' and 'double'); Windows always has '__int64', so it does
+** not need to use this case.
+*/
+#if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS)
+#define LUA_C89_NUMBERS 1
+#else
+#define LUA_C89_NUMBERS 0
+#endif
+
+
+#if LUA_32BITS /* { */
+/*
+** 32-bit integers and 'float'
+*/
+#if LUAI_IS32INT /* use 'int' if big enough */
+#define LUA_INT_TYPE LUA_INT_INT
+#else /* otherwise use 'long' */
+#define LUA_INT_TYPE LUA_INT_LONG
+#endif
+#define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT
+
+#elif LUA_C89_NUMBERS /* }{ */
+/*
+** largest types available for C89 ('long' and 'double')
+*/
+#define LUA_INT_TYPE LUA_INT_LONG
+#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE
+
+#else /* }{ */
+/* use defaults */
+
+#define LUA_INT_TYPE LUA_INT_DEFAULT
+#define LUA_FLOAT_TYPE LUA_FLOAT_DEFAULT
+
+#endif /* } */
+
+
+/* }================================================================== */
+
+
+
+/*
+** {==================================================================
+** Configuration for Paths.
+** ===================================================================
+*/
+
+/*
+** LUA_PATH_SEP is the character that separates templates in a path.
+** LUA_PATH_MARK is the string that marks the substitution points in a
+** template.
+** LUA_EXEC_DIR in a Windows path is replaced by the executable's
+** directory.
+*/
+#define LUA_PATH_SEP ";"
+#define LUA_PATH_MARK "?"
+#define LUA_EXEC_DIR "!"
+
+
+/*
+@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for
+** Lua libraries.
+@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for
+** C libraries.
+** CHANGE them if your machine has a non-conventional directory
+** hierarchy or if you want to install your libraries in
+** non-conventional directories.
+*/
+
+#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
+#if defined(_WIN32) /* { */
+/*
+** In Windows, any exclamation mark ('!') in the path is replaced by the
+** path of the directory of the executable file of the current process.
+*/
+#define LUA_LDIR "!\\lua\\"
+#define LUA_CDIR "!\\"
+#define LUA_SHRDIR "!\\..\\share\\lua\\" LUA_VDIR "\\"
+
+#if !defined(LUA_PATH_DEFAULT)
+#define LUA_PATH_DEFAULT \
+ LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \
+ LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \
+ LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \
+ ".\\?.lua;" ".\\?\\init.lua"
+#endif
+
+#if !defined(LUA_CPATH_DEFAULT)
+#define LUA_CPATH_DEFAULT \
+ LUA_CDIR"?.dll;" \
+ LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \
+ LUA_CDIR"loadall.dll;" ".\\?.dll"
+#endif
+
+#else /* }{ */
+
+#define LUA_ROOT "/usr/local/"
+#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR "/"
+#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR "/"
+
+#if !defined(LUA_PATH_DEFAULT)
+#define LUA_PATH_DEFAULT \
+ LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \
+ LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \
+ "./?.lua;" "./?/init.lua"
+#endif
+
+#if !defined(LUA_CPATH_DEFAULT)
+#define LUA_CPATH_DEFAULT \
+ LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so"
+#endif
+
+#endif /* } */
+
+
+/*
+@@ LUA_DIRSEP is the directory separator (for submodules).
+** CHANGE it if your machine does not use "/" as the directory separator
+** and is not Windows. (On Windows Lua automatically uses "\".)
+*/
+#if !defined(LUA_DIRSEP)
+
+#if defined(_WIN32)
+#define LUA_DIRSEP "\\"
+#else
+#define LUA_DIRSEP "/"
+#endif
+
+#endif
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Marks for exported symbols in the C code
+** ===================================================================
+*/
+
+/*
+@@ LUA_API is a mark for all core API functions.
+@@ LUALIB_API is a mark for all auxiliary library functions.
+@@ LUAMOD_API is a mark for all standard library opening functions.
+** CHANGE them if you need to define those functions in some special way.
+** For instance, if you want to create one Windows DLL with the core and
+** the libraries, you may want to use the following definition (define
+** LUA_BUILD_AS_DLL to get it).
+*/
+#if defined(LUA_BUILD_AS_DLL) /* { */
+
+#if defined(LUA_CORE) || defined(LUA_LIB) /* { */
+#define LUA_API __declspec(dllexport)
+#else /* }{ */
+#define LUA_API __declspec(dllimport)
+#endif /* } */
+
+#else /* }{ */
+
+#define LUA_API extern
+
+#endif /* } */
+
+
+/*
+** More often than not the libs go together with the core.
+*/
+#define LUALIB_API LUA_API
+#define LUAMOD_API LUA_API
+
+
+/*
+@@ LUAI_FUNC is a mark for all extern functions that are not to be
+** exported to outside modules.
+@@ LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables,
+** none of which to be exported to outside modules (LUAI_DDEF for
+** definitions and LUAI_DDEC for declarations).
+** CHANGE them if you need to mark them in some special way. Elf/gcc
+** (versions 3.2 and later) mark them as "hidden" to optimize access
+** when Lua is compiled as a shared library. Not all elf targets support
+** this attribute. Unfortunately, gcc does not offer a way to check
+** whether the target offers that support, and those without support
+** give a warning about it. To avoid these warnings, change to the
+** default definition.
+*/
+#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \
+ defined(__ELF__) /* { */
+#define LUAI_FUNC __attribute__((visibility("internal"))) extern
+#else /* }{ */
+#define LUAI_FUNC extern
+#endif /* } */
+
+#define LUAI_DDEC(dec) LUAI_FUNC dec
+#define LUAI_DDEF /* empty */
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Compatibility with previous versions
+** ===================================================================
+*/
+
+/*
+@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.3.
+** You can define it to get all options, or change specific options
+** to fit your specific needs.
+*/
+#if defined(LUA_COMPAT_5_3) /* { */
+
+/*
+@@ LUA_COMPAT_MATHLIB controls the presence of several deprecated
+** functions in the mathematical library.
+** (These functions were already officially removed in 5.3;
+** nevertheless they are still available here.)
+*/
+#define LUA_COMPAT_MATHLIB
+
+/*
+@@ LUA_COMPAT_APIINTCASTS controls the presence of macros for
+** manipulating other integer types (lua_pushunsigned, lua_tounsigned,
+** luaL_checkint, luaL_checklong, etc.)
+** (These macros were also officially removed in 5.3, but they are still
+** available here.)
+*/
+#define LUA_COMPAT_APIINTCASTS
+
+
+/*
+@@ LUA_COMPAT_LT_LE controls the emulation of the '__le' metamethod
+** using '__lt'.
+*/
+#define LUA_COMPAT_LT_LE
+
+
+/*
+@@ The following macros supply trivial compatibility for some
+** changes in the API. The macros themselves document how to
+** change your code to avoid using them.
+** (Once more, these macros were officially removed in 5.3, but they are
+** still available here.)
+*/
+#define lua_strlen(L,i) lua_rawlen(L, (i))
+
+#define lua_objlen(L,i) lua_rawlen(L, (i))
+
+#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ)
+#define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT)
+
+#endif /* } */
+
+/* }================================================================== */
+
+
+
+/*
+** {==================================================================
+** Configuration for Numbers (low-level part).
+** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_*
+** satisfy your needs.
+** ===================================================================
+*/
+
+/*
+@@ LUAI_UACNUMBER is the result of a 'default argument promotion'
+@@ over a floating number.
+@@ l_floatatt(x) corrects float attribute 'x' to the proper float type
+** by prefixing it with one of FLT/DBL/LDBL.
+@@ LUA_NUMBER_FRMLEN is the length modifier for writing floats.
+@@ LUA_NUMBER_FMT is the format for writing floats.
+@@ lua_number2str converts a float to a string.
+@@ l_mathop allows the addition of an 'l' or 'f' to all math operations.
+@@ l_floor takes the floor of a float.
+@@ lua_str2number converts a decimal numeral to a number.
+*/
+
+
+/* The following definitions are good for most cases here */
+
+#define l_floor(x) (l_mathop(floor)(x))
+
+#define lua_number2str(s,sz,n) \
+ l_sprintf((s), sz, LUA_NUMBER_FMT, (LUAI_UACNUMBER)(n))
+
+/*
+@@ lua_numbertointeger converts a float number with an integral value
+** to an integer, or returns 0 if float is not within the range of
+** a lua_Integer. (The range comparisons are tricky because of
+** rounding. The tests here assume a two-complement representation,
+** where MININTEGER always has an exact representation as a float;
+** MAXINTEGER may not have one, and therefore its conversion to float
+** may have an ill-defined value.)
+*/
+#define lua_numbertointeger(n,p) \
+ ((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \
+ (n) < -(LUA_NUMBER)(LUA_MININTEGER) && \
+ (*(p) = (LUA_INTEGER)(n), 1))
+
+
+/* now the variable definitions */
+
+#if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */
+
+#define LUA_NUMBER float
+
+#define l_floatatt(n) (FLT_##n)
+
+#define LUAI_UACNUMBER double
+
+#define LUA_NUMBER_FRMLEN ""
+#define LUA_NUMBER_FMT "%.7g"
+
+#define l_mathop(op) op##f
+
+#define lua_str2number(s,p) strtof((s), (p))
+
+
+#elif LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE /* }{ long double */
+
+#define LUA_NUMBER long double
+
+#define l_floatatt(n) (LDBL_##n)
+
+#define LUAI_UACNUMBER long double
+
+#define LUA_NUMBER_FRMLEN "L"
+#define LUA_NUMBER_FMT "%.19Lg"
+
+#define l_mathop(op) op##l
+
+#define lua_str2number(s,p) strtold((s), (p))
+
+#elif LUA_FLOAT_TYPE == LUA_FLOAT_DOUBLE /* }{ double */
+
+#define LUA_NUMBER double
+
+#define l_floatatt(n) (DBL_##n)
+
+#define LUAI_UACNUMBER double
+
+#define LUA_NUMBER_FRMLEN ""
+#define LUA_NUMBER_FMT "%.14g"
+
+#define l_mathop(op) op
+
+#define lua_str2number(s,p) strtod((s), (p))
+
+#else /* }{ */
+
+#error "numeric float type not defined"
+
+#endif /* } */
+
+
+
+/*
+@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER.
+@@ LUAI_UACINT is the result of a 'default argument promotion'
+@@ over a LUA_INTEGER.
+@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers.
+@@ LUA_INTEGER_FMT is the format for writing integers.
+@@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER.
+@@ LUA_MININTEGER is the minimum value for a LUA_INTEGER.
+@@ LUA_MAXUNSIGNED is the maximum value for a LUA_UNSIGNED.
+@@ lua_integer2str converts an integer to a string.
+*/
+
+
+/* The following definitions are good for most cases here */
+
+#define LUA_INTEGER_FMT "%" LUA_INTEGER_FRMLEN "d"
+
+#define LUAI_UACINT LUA_INTEGER
+
+#define lua_integer2str(s,sz,n) \
+ l_sprintf((s), sz, LUA_INTEGER_FMT, (LUAI_UACINT)(n))
+
+/*
+** use LUAI_UACINT here to avoid problems with promotions (which
+** can turn a comparison between unsigneds into a signed comparison)
+*/
+#define LUA_UNSIGNED unsigned LUAI_UACINT
+
+
+/* now the variable definitions */
+
+#if LUA_INT_TYPE == LUA_INT_INT /* { int */
+
+#define LUA_INTEGER int
+#define LUA_INTEGER_FRMLEN ""
+
+#define LUA_MAXINTEGER INT_MAX
+#define LUA_MININTEGER INT_MIN
+
+#define LUA_MAXUNSIGNED UINT_MAX
+
+#elif LUA_INT_TYPE == LUA_INT_LONG /* }{ long */
+
+#define LUA_INTEGER long
+#define LUA_INTEGER_FRMLEN "l"
+
+#define LUA_MAXINTEGER LONG_MAX
+#define LUA_MININTEGER LONG_MIN
+
+#define LUA_MAXUNSIGNED ULONG_MAX
+
+#elif LUA_INT_TYPE == LUA_INT_LONGLONG /* }{ long long */
+
+/* use presence of macro LLONG_MAX as proxy for C99 compliance */
+#if defined(LLONG_MAX) /* { */
+/* use ISO C99 stuff */
+
+#define LUA_INTEGER long long
+#define LUA_INTEGER_FRMLEN "ll"
+
+#define LUA_MAXINTEGER LLONG_MAX
+#define LUA_MININTEGER LLONG_MIN
+
+#define LUA_MAXUNSIGNED ULLONG_MAX
+
+#elif defined(LUA_USE_WINDOWS) /* }{ */
+/* in Windows, can use specific Windows types */
+
+#define LUA_INTEGER __int64
+#define LUA_INTEGER_FRMLEN "I64"
+
+#define LUA_MAXINTEGER _I64_MAX
+#define LUA_MININTEGER _I64_MIN
+
+#define LUA_MAXUNSIGNED _UI64_MAX
+
+#else /* }{ */
+
+#error "Compiler does not support 'long long'. Use option '-DLUA_32BITS' \
+ or '-DLUA_C89_NUMBERS' (see file 'luaconf.h' for details)"
+
+#endif /* } */
+
+#else /* }{ */
+
+#error "numeric integer type not defined"
+
+#endif /* } */
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Dependencies with C99 and other C details
+** ===================================================================
+*/
+
+/*
+@@ l_sprintf is equivalent to 'snprintf' or 'sprintf' in C89.
+** (All uses in Lua have only one format item.)
+*/
+#if !defined(LUA_USE_C89)
+#define l_sprintf(s,sz,f,i) snprintf(s,sz,f,i)
+#else
+#define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s,f,i))
+#endif
+
+
+/*
+@@ lua_strx2number converts a hexadecimal numeral to a number.
+** In C99, 'strtod' does that conversion. Otherwise, you can
+** leave 'lua_strx2number' undefined and Lua will provide its own
+** implementation.
+*/
+#if !defined(LUA_USE_C89)
+#define lua_strx2number(s,p) lua_str2number(s,p)
+#endif
+
+
+/*
+@@ lua_pointer2str converts a pointer to a readable string in a
+** non-specified way.
+*/
+#define lua_pointer2str(buff,sz,p) l_sprintf(buff,sz,"%p",p)
+
+
+/*
+@@ lua_number2strx converts a float to a hexadecimal numeral.
+** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that.
+** Otherwise, you can leave 'lua_number2strx' undefined and Lua will
+** provide its own implementation.
+*/
+#if !defined(LUA_USE_C89)
+#define lua_number2strx(L,b,sz,f,n) \
+ ((void)L, l_sprintf(b,sz,f,(LUAI_UACNUMBER)(n)))
+#endif
+
+
+/*
+** 'strtof' and 'opf' variants for math functions are not valid in
+** C89. Otherwise, the macro 'HUGE_VALF' is a good proxy for testing the
+** availability of these variants. ('math.h' is already included in
+** all files that use these macros.)
+*/
+#if defined(LUA_USE_C89) || (defined(HUGE_VAL) && !defined(HUGE_VALF))
+#undef l_mathop /* variants not available */
+#undef lua_str2number
+#define l_mathop(op) (lua_Number)op /* no variant */
+#define lua_str2number(s,p) ((lua_Number)strtod((s), (p)))
+#endif
+
+
+/*
+@@ LUA_KCONTEXT is the type of the context ('ctx') for continuation
+** functions. It must be a numerical type; Lua will use 'intptr_t' if
+** available, otherwise it will use 'ptrdiff_t' (the nearest thing to
+** 'intptr_t' in C89)
+*/
+#define LUA_KCONTEXT ptrdiff_t
+
+#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \
+ __STDC_VERSION__ >= 199901L
+#include <stdint.h>
+#if defined(INTPTR_MAX) /* even in C99 this type is optional */
+#undef LUA_KCONTEXT
+#define LUA_KCONTEXT intptr_t
+#endif
+#endif
+
+
+/*
+@@ lua_getlocaledecpoint gets the locale "radix character" (decimal point).
+** Change that if you do not want to use C locales. (Code using this
+** macro must include the header 'locale.h'.)
+*/
+#if !defined(lua_getlocaledecpoint)
+#define lua_getlocaledecpoint() (localeconv()->decimal_point[0])
+#endif
+
+
+/*
+** macros to improve jump prediction, used mostly for error handling
+** and debug facilities. (Some macros in the Lua API use these macros.
+** Define LUA_NOBUILTIN if you do not want '__builtin_expect' in your
+** code.)
+*/
+#if !defined(luai_likely)
+
+#if defined(__GNUC__) && !defined(LUA_NOBUILTIN)
+#define luai_likely(x) (__builtin_expect(((x) != 0), 1))
+#define luai_unlikely(x) (__builtin_expect(((x) != 0), 0))
+#else
+#define luai_likely(x) (x)
+#define luai_unlikely(x) (x)
+#endif
+
+#endif
+
+
+#if defined(LUA_CORE) || defined(LUA_LIB)
+/* shorter names for Lua's own use */
+#define l_likely(x) luai_likely(x)
+#define l_unlikely(x) luai_unlikely(x)
+#endif
+
+
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Language Variations
+** =====================================================================
+*/
+
+/*
+@@ LUA_NOCVTN2S/LUA_NOCVTS2N control how Lua performs some
+** coercions. Define LUA_NOCVTN2S to turn off automatic coercion from
+** numbers to strings. Define LUA_NOCVTS2N to turn off automatic
+** coercion from strings to numbers.
+*/
+/* #define LUA_NOCVTN2S */
+/* #define LUA_NOCVTS2N */
+
+
+/*
+@@ LUA_USE_APICHECK turns on several consistency checks on the C API.
+** Define it as a help when debugging C code.
+*/
+#if defined(LUA_USE_APICHECK)
+#include <assert.h>
+#define luai_apicheck(l,e) assert(e)
+#endif
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Macros that affect the API and must be stable (that is, must be the
+** same when you compile Lua and when you compile code that links to
+** Lua).
+** =====================================================================
+*/
+
+/*
+@@ LUAI_MAXSTACK limits the size of the Lua stack.
+** CHANGE it if you need a different limit. This limit is arbitrary;
+** its only purpose is to stop Lua from consuming unlimited stack
+** space (and to reserve some numbers for pseudo-indices).
+** (It must fit into max(size_t)/32 and max(int)/2.)
+*/
+#if LUAI_IS32INT
+#define LUAI_MAXSTACK 1000000
+#else
+#define LUAI_MAXSTACK 15000
+#endif
+
+
+/*
+@@ LUA_EXTRASPACE defines the size of a raw memory area associated with
+** a Lua state with very fast access.
+** CHANGE it if you need a different size.
+*/
+#define LUA_EXTRASPACE (sizeof(void *))
+
+
+/*
+@@ LUA_IDSIZE gives the maximum size for the description of the source
+** of a function in debug information.
+** CHANGE it if you want a different size.
+*/
+#define LUA_IDSIZE 60
+
+
+/*
+@@ LUAL_BUFFERSIZE is the initial buffer size used by the lauxlib
+** buffer system.
+*/
+#define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number)))
+
+
+/*
+@@ LUAI_MAXALIGN defines fields that, when used in a union, ensure
+** maximum alignment for the other items in that union.
+*/
+#define LUAI_MAXALIGN lua_Number n; double u; void *s; lua_Integer i; long l
+
+/* }================================================================== */
+
+
+
+
+
+/* =================================================================== */
+
+/*
+** Local configuration. You can use this space to add your redefinitions
+** without modifying the main part of the file.
+*/
+
+
+
+
+
+#endif
+
diff --git a/src/libs/3rdparty/lua/src/lualib.h b/src/libs/3rdparty/lua/src/lualib.h
new file mode 100644
index 0000000000..2625529076
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lualib.h
@@ -0,0 +1,52 @@
+/*
+** $Id: lualib.h $
+** Lua standard libraries
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lualib_h
+#define lualib_h
+
+#include "lua.h"
+
+
+/* version suffix for environment variable names */
+#define LUA_VERSUFFIX "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR
+
+
+LUAMOD_API int (luaopen_base) (lua_State *L);
+
+#define LUA_COLIBNAME "coroutine"
+LUAMOD_API int (luaopen_coroutine) (lua_State *L);
+
+#define LUA_TABLIBNAME "table"
+LUAMOD_API int (luaopen_table) (lua_State *L);
+
+#define LUA_IOLIBNAME "io"
+LUAMOD_API int (luaopen_io) (lua_State *L);
+
+#define LUA_OSLIBNAME "os"
+LUAMOD_API int (luaopen_os) (lua_State *L);
+
+#define LUA_STRLIBNAME "string"
+LUAMOD_API int (luaopen_string) (lua_State *L);
+
+#define LUA_UTF8LIBNAME "utf8"
+LUAMOD_API int (luaopen_utf8) (lua_State *L);
+
+#define LUA_MATHLIBNAME "math"
+LUAMOD_API int (luaopen_math) (lua_State *L);
+
+#define LUA_DBLIBNAME "debug"
+LUAMOD_API int (luaopen_debug) (lua_State *L);
+
+#define LUA_LOADLIBNAME "package"
+LUAMOD_API int (luaopen_package) (lua_State *L);
+
+
+/* open all previous libraries */
+LUALIB_API void (luaL_openlibs) (lua_State *L);
+
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lundump.c b/src/libs/3rdparty/lua/src/lundump.c
new file mode 100644
index 0000000000..02aed64fb6
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lundump.c
@@ -0,0 +1,335 @@
+/*
+** $Id: lundump.c $
+** load precompiled Lua chunks
+** See Copyright Notice in lua.h
+*/
+
+#define lundump_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <limits.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lmem.h"
+#include "lobject.h"
+#include "lstring.h"
+#include "lundump.h"
+#include "lzio.h"
+
+
+#if !defined(luai_verifycode)
+#define luai_verifycode(L,f) /* empty */
+#endif
+
+
+typedef struct {
+ lua_State *L;
+ ZIO *Z;
+ const char *name;
+} LoadState;
+
+
+static l_noret error (LoadState *S, const char *why) {
+ luaO_pushfstring(S->L, "%s: bad binary format (%s)", S->name, why);
+ luaD_throw(S->L, LUA_ERRSYNTAX);
+}
+
+
+/*
+** All high-level loads go through loadVector; you can change it to
+** adapt to the endianness of the input
+*/
+#define loadVector(S,b,n) loadBlock(S,b,(n)*sizeof((b)[0]))
+
+static void loadBlock (LoadState *S, void *b, size_t size) {
+ if (luaZ_read(S->Z, b, size) != 0)
+ error(S, "truncated chunk");
+}
+
+
+#define loadVar(S,x) loadVector(S,&x,1)
+
+
+static lu_byte loadByte (LoadState *S) {
+ int b = zgetc(S->Z);
+ if (b == EOZ)
+ error(S, "truncated chunk");
+ return cast_byte(b);
+}
+
+
+static size_t loadUnsigned (LoadState *S, size_t limit) {
+ size_t x = 0;
+ int b;
+ limit >>= 7;
+ do {
+ b = loadByte(S);
+ if (x >= limit)
+ error(S, "integer overflow");
+ x = (x << 7) | (b & 0x7f);
+ } while ((b & 0x80) == 0);
+ return x;
+}
+
+
+static size_t loadSize (LoadState *S) {
+ return loadUnsigned(S, ~(size_t)0);
+}
+
+
+static int loadInt (LoadState *S) {
+ return cast_int(loadUnsigned(S, INT_MAX));
+}
+
+
+static lua_Number loadNumber (LoadState *S) {
+ lua_Number x;
+ loadVar(S, x);
+ return x;
+}
+
+
+static lua_Integer loadInteger (LoadState *S) {
+ lua_Integer x;
+ loadVar(S, x);
+ return x;
+}
+
+
+/*
+** Load a nullable string into prototype 'p'.
+*/
+static TString *loadStringN (LoadState *S, Proto *p) {
+ lua_State *L = S->L;
+ TString *ts;
+ size_t size = loadSize(S);
+ if (size == 0) /* no string? */
+ return NULL;
+ else if (--size <= LUAI_MAXSHORTLEN) { /* short string? */
+ char buff[LUAI_MAXSHORTLEN];
+ loadVector(S, buff, size); /* load string into buffer */
+ ts = luaS_newlstr(L, buff, size); /* create string */
+ }
+ else { /* long string */
+ ts = luaS_createlngstrobj(L, size); /* create string */
+ setsvalue2s(L, L->top.p, ts); /* anchor it ('loadVector' can GC) */
+ luaD_inctop(L);
+ loadVector(S, getstr(ts), size); /* load directly in final place */
+ L->top.p--; /* pop string */
+ }
+ luaC_objbarrier(L, p, ts);
+ return ts;
+}
+
+
+/*
+** Load a non-nullable string into prototype 'p'.
+*/
+static TString *loadString (LoadState *S, Proto *p) {
+ TString *st = loadStringN(S, p);
+ if (st == NULL)
+ error(S, "bad format for constant string");
+ return st;
+}
+
+
+static void loadCode (LoadState *S, Proto *f) {
+ int n = loadInt(S);
+ f->code = luaM_newvectorchecked(S->L, n, Instruction);
+ f->sizecode = n;
+ loadVector(S, f->code, n);
+}
+
+
+static void loadFunction(LoadState *S, Proto *f, TString *psource);
+
+
+static void loadConstants (LoadState *S, Proto *f) {
+ int i;
+ int n = loadInt(S);
+ f->k = luaM_newvectorchecked(S->L, n, TValue);
+ f->sizek = n;
+ for (i = 0; i < n; i++)
+ setnilvalue(&f->k[i]);
+ for (i = 0; i < n; i++) {
+ TValue *o = &f->k[i];
+ int t = loadByte(S);
+ switch (t) {
+ case LUA_VNIL:
+ setnilvalue(o);
+ break;
+ case LUA_VFALSE:
+ setbfvalue(o);
+ break;
+ case LUA_VTRUE:
+ setbtvalue(o);
+ break;
+ case LUA_VNUMFLT:
+ setfltvalue(o, loadNumber(S));
+ break;
+ case LUA_VNUMINT:
+ setivalue(o, loadInteger(S));
+ break;
+ case LUA_VSHRSTR:
+ case LUA_VLNGSTR:
+ setsvalue2n(S->L, o, loadString(S, f));
+ break;
+ default: lua_assert(0);
+ }
+ }
+}
+
+
+static void loadProtos (LoadState *S, Proto *f) {
+ int i;
+ int n = loadInt(S);
+ f->p = luaM_newvectorchecked(S->L, n, Proto *);
+ f->sizep = n;
+ for (i = 0; i < n; i++)
+ f->p[i] = NULL;
+ for (i = 0; i < n; i++) {
+ f->p[i] = luaF_newproto(S->L);
+ luaC_objbarrier(S->L, f, f->p[i]);
+ loadFunction(S, f->p[i], f->source);
+ }
+}
+
+
+/*
+** Load the upvalues for a function. The names must be filled first,
+** because the filling of the other fields can raise read errors and
+** the creation of the error message can call an emergency collection;
+** in that case all prototypes must be consistent for the GC.
+*/
+static void loadUpvalues (LoadState *S, Proto *f) {
+ int i, n;
+ n = loadInt(S);
+ f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc);
+ f->sizeupvalues = n;
+ for (i = 0; i < n; i++) /* make array valid for GC */
+ f->upvalues[i].name = NULL;
+ for (i = 0; i < n; i++) { /* following calls can raise errors */
+ f->upvalues[i].instack = loadByte(S);
+ f->upvalues[i].idx = loadByte(S);
+ f->upvalues[i].kind = loadByte(S);
+ }
+}
+
+
+static void loadDebug (LoadState *S, Proto *f) {
+ int i, n;
+ n = loadInt(S);
+ f->lineinfo = luaM_newvectorchecked(S->L, n, ls_byte);
+ f->sizelineinfo = n;
+ loadVector(S, f->lineinfo, n);
+ n = loadInt(S);
+ f->abslineinfo = luaM_newvectorchecked(S->L, n, AbsLineInfo);
+ f->sizeabslineinfo = n;
+ for (i = 0; i < n; i++) {
+ f->abslineinfo[i].pc = loadInt(S);
+ f->abslineinfo[i].line = loadInt(S);
+ }
+ n = loadInt(S);
+ f->locvars = luaM_newvectorchecked(S->L, n, LocVar);
+ f->sizelocvars = n;
+ for (i = 0; i < n; i++)
+ f->locvars[i].varname = NULL;
+ for (i = 0; i < n; i++) {
+ f->locvars[i].varname = loadStringN(S, f);
+ f->locvars[i].startpc = loadInt(S);
+ f->locvars[i].endpc = loadInt(S);
+ }
+ n = loadInt(S);
+ if (n != 0) /* does it have debug information? */
+ n = f->sizeupvalues; /* must be this many */
+ for (i = 0; i < n; i++)
+ f->upvalues[i].name = loadStringN(S, f);
+}
+
+
+static void loadFunction (LoadState *S, Proto *f, TString *psource) {
+ f->source = loadStringN(S, f);
+ if (f->source == NULL) /* no source in dump? */
+ f->source = psource; /* reuse parent's source */
+ f->linedefined = loadInt(S);
+ f->lastlinedefined = loadInt(S);
+ f->numparams = loadByte(S);
+ f->is_vararg = loadByte(S);
+ f->maxstacksize = loadByte(S);
+ loadCode(S, f);
+ loadConstants(S, f);
+ loadUpvalues(S, f);
+ loadProtos(S, f);
+ loadDebug(S, f);
+}
+
+
+static void checkliteral (LoadState *S, const char *s, const char *msg) {
+ char buff[sizeof(LUA_SIGNATURE) + sizeof(LUAC_DATA)]; /* larger than both */
+ size_t len = strlen(s);
+ loadVector(S, buff, len);
+ if (memcmp(s, buff, len) != 0)
+ error(S, msg);
+}
+
+
+static void fchecksize (LoadState *S, size_t size, const char *tname) {
+ if (loadByte(S) != size)
+ error(S, luaO_pushfstring(S->L, "%s size mismatch", tname));
+}
+
+
+#define checksize(S,t) fchecksize(S,sizeof(t),#t)
+
+static void checkHeader (LoadState *S) {
+ /* skip 1st char (already read and checked) */
+ checkliteral(S, &LUA_SIGNATURE[1], "not a binary chunk");
+ if (loadByte(S) != LUAC_VERSION)
+ error(S, "version mismatch");
+ if (loadByte(S) != LUAC_FORMAT)
+ error(S, "format mismatch");
+ checkliteral(S, LUAC_DATA, "corrupted chunk");
+ checksize(S, Instruction);
+ checksize(S, lua_Integer);
+ checksize(S, lua_Number);
+ if (loadInteger(S) != LUAC_INT)
+ error(S, "integer format mismatch");
+ if (loadNumber(S) != LUAC_NUM)
+ error(S, "float format mismatch");
+}
+
+
+/*
+** Load precompiled chunk.
+*/
+LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) {
+ LoadState S;
+ LClosure *cl;
+ if (*name == '@' || *name == '=')
+ S.name = name + 1;
+ else if (*name == LUA_SIGNATURE[0])
+ S.name = "binary string";
+ else
+ S.name = name;
+ S.L = L;
+ S.Z = Z;
+ checkHeader(&S);
+ cl = luaF_newLclosure(L, loadByte(&S));
+ setclLvalue2s(L, L->top.p, cl);
+ luaD_inctop(L);
+ cl->p = luaF_newproto(L);
+ luaC_objbarrier(L, cl, cl->p);
+ loadFunction(&S, cl->p, NULL);
+ lua_assert(cl->nupvalues == cl->p->sizeupvalues);
+ luai_verifycode(L, cl->p);
+ return cl;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lundump.h b/src/libs/3rdparty/lua/src/lundump.h
new file mode 100644
index 0000000000..f3748a9980
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lundump.h
@@ -0,0 +1,36 @@
+/*
+** $Id: lundump.h $
+** load precompiled Lua chunks
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lundump_h
+#define lundump_h
+
+#include "llimits.h"
+#include "lobject.h"
+#include "lzio.h"
+
+
+/* data to catch conversion errors */
+#define LUAC_DATA "\x19\x93\r\n\x1a\n"
+
+#define LUAC_INT 0x5678
+#define LUAC_NUM cast_num(370.5)
+
+/*
+** Encode major-minor version in one byte, one nibble for each
+*/
+#define MYINT(s) (s[0]-'0') /* assume one-digit numerals */
+#define LUAC_VERSION (MYINT(LUA_VERSION_MAJOR)*16+MYINT(LUA_VERSION_MINOR))
+
+#define LUAC_FORMAT 0 /* this is the official format */
+
+/* load one chunk; from lundump.c */
+LUAI_FUNC LClosure* luaU_undump (lua_State* L, ZIO* Z, const char* name);
+
+/* dump one chunk; from ldump.c */
+LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w,
+ void* data, int strip);
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lutf8lib.c b/src/libs/3rdparty/lua/src/lutf8lib.c
new file mode 100644
index 0000000000..3a5b9bc38a
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lutf8lib.c
@@ -0,0 +1,291 @@
+/*
+** $Id: lutf8lib.c $
+** Standard library for UTF-8 manipulation
+** See Copyright Notice in lua.h
+*/
+
+#define lutf8lib_c
+#define LUA_LIB
+
+#include "lprefix.h"
+
+
+#include <assert.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "lauxlib.h"
+#include "lualib.h"
+
+
+#define MAXUNICODE 0x10FFFFu
+
+#define MAXUTF 0x7FFFFFFFu
+
+
+#define MSGInvalid "invalid UTF-8 code"
+
+/*
+** Integer type for decoded UTF-8 values; MAXUTF needs 31 bits.
+*/
+#if (UINT_MAX >> 30) >= 1
+typedef unsigned int utfint;
+#else
+typedef unsigned long utfint;
+#endif
+
+
+#define iscont(c) (((c) & 0xC0) == 0x80)
+#define iscontp(p) iscont(*(p))
+
+
+/* from strlib */
+/* translate a relative string position: negative means back from end */
+static lua_Integer u_posrelat (lua_Integer pos, size_t len) {
+ if (pos >= 0) return pos;
+ else if (0u - (size_t)pos > len) return 0;
+ else return (lua_Integer)len + pos + 1;
+}
+
+
+/*
+** Decode one UTF-8 sequence, returning NULL if byte sequence is
+** invalid. The array 'limits' stores the minimum value for each
+** sequence length, to check for overlong representations. Its first
+** entry forces an error for non-ascii bytes with no continuation
+** bytes (count == 0).
+*/
+static const char *utf8_decode (const char *s, utfint *val, int strict) {
+ static const utfint limits[] =
+ {~(utfint)0, 0x80, 0x800, 0x10000u, 0x200000u, 0x4000000u};
+ unsigned int c = (unsigned char)s[0];
+ utfint res = 0; /* final result */
+ if (c < 0x80) /* ascii? */
+ res = c;
+ else {
+ int count = 0; /* to count number of continuation bytes */
+ for (; c & 0x40; c <<= 1) { /* while it needs continuation bytes... */
+ unsigned int cc = (unsigned char)s[++count]; /* read next byte */
+ if (!iscont(cc)) /* not a continuation byte? */
+ return NULL; /* invalid byte sequence */
+ res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
+ }
+ res |= ((utfint)(c & 0x7F) << (count * 5)); /* add first byte */
+ if (count > 5 || res > MAXUTF || res < limits[count])
+ return NULL; /* invalid byte sequence */
+ s += count; /* skip continuation bytes read */
+ }
+ if (strict) {
+ /* check for invalid code points; too large or surrogates */
+ if (res > MAXUNICODE || (0xD800u <= res && res <= 0xDFFFu))
+ return NULL;
+ }
+ if (val) *val = res;
+ return s + 1; /* +1 to include first byte */
+}
+
+
+/*
+** utf8len(s [, i [, j [, lax]]]) --> number of characters that
+** start in the range [i,j], or nil + current position if 's' is not
+** well formed in that interval
+*/
+static int utflen (lua_State *L) {
+ lua_Integer n = 0; /* counter for the number of characters */
+ size_t len; /* string length in bytes */
+ const char *s = luaL_checklstring(L, 1, &len);
+ lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len);
+ lua_Integer posj = u_posrelat(luaL_optinteger(L, 3, -1), len);
+ int lax = lua_toboolean(L, 4);
+ luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 2,
+ "initial position out of bounds");
+ luaL_argcheck(L, --posj < (lua_Integer)len, 3,
+ "final position out of bounds");
+ while (posi <= posj) {
+ const char *s1 = utf8_decode(s + posi, NULL, !lax);
+ if (s1 == NULL) { /* conversion error? */
+ luaL_pushfail(L); /* return fail ... */
+ lua_pushinteger(L, posi + 1); /* ... and current position */
+ return 2;
+ }
+ posi = s1 - s;
+ n++;
+ }
+ lua_pushinteger(L, n);
+ return 1;
+}
+
+
+/*
+** codepoint(s, [i, [j [, lax]]]) -> returns codepoints for all
+** characters that start in the range [i,j]
+*/
+static int codepoint (lua_State *L) {
+ size_t len;
+ const char *s = luaL_checklstring(L, 1, &len);
+ lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len);
+ lua_Integer pose = u_posrelat(luaL_optinteger(L, 3, posi), len);
+ int lax = lua_toboolean(L, 4);
+ int n;
+ const char *se;
+ luaL_argcheck(L, posi >= 1, 2, "out of bounds");
+ luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of bounds");
+ if (posi > pose) return 0; /* empty interval; return no values */
+ if (pose - posi >= INT_MAX) /* (lua_Integer -> int) overflow? */
+ return luaL_error(L, "string slice too long");
+ n = (int)(pose - posi) + 1; /* upper bound for number of returns */
+ luaL_checkstack(L, n, "string slice too long");
+ n = 0; /* count the number of returns */
+ se = s + pose; /* string end */
+ for (s += posi - 1; s < se;) {
+ utfint code;
+ s = utf8_decode(s, &code, !lax);
+ if (s == NULL)
+ return luaL_error(L, MSGInvalid);
+ lua_pushinteger(L, code);
+ n++;
+ }
+ return n;
+}
+
+
+static void pushutfchar (lua_State *L, int arg) {
+ lua_Unsigned code = (lua_Unsigned)luaL_checkinteger(L, arg);
+ luaL_argcheck(L, code <= MAXUTF, arg, "value out of range");
+ lua_pushfstring(L, "%U", (long)code);
+}
+
+
+/*
+** utfchar(n1, n2, ...) -> char(n1)..char(n2)...
+*/
+static int utfchar (lua_State *L) {
+ int n = lua_gettop(L); /* number of arguments */
+ if (n == 1) /* optimize common case of single char */
+ pushutfchar(L, 1);
+ else {
+ int i;
+ luaL_Buffer b;
+ luaL_buffinit(L, &b);
+ for (i = 1; i <= n; i++) {
+ pushutfchar(L, i);
+ luaL_addvalue(&b);
+ }
+ luaL_pushresult(&b);
+ }
+ return 1;
+}
+
+
+/*
+** offset(s, n, [i]) -> index where n-th character counting from
+** position 'i' starts; 0 means character at 'i'.
+*/
+static int byteoffset (lua_State *L) {
+ size_t len;
+ const char *s = luaL_checklstring(L, 1, &len);
+ lua_Integer n = luaL_checkinteger(L, 2);
+ lua_Integer posi = (n >= 0) ? 1 : len + 1;
+ posi = u_posrelat(luaL_optinteger(L, 3, posi), len);
+ luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 3,
+ "position out of bounds");
+ if (n == 0) {
+ /* find beginning of current byte sequence */
+ while (posi > 0 && iscontp(s + posi)) posi--;
+ }
+ else {
+ if (iscontp(s + posi))
+ return luaL_error(L, "initial position is a continuation byte");
+ if (n < 0) {
+ while (n < 0 && posi > 0) { /* move back */
+ do { /* find beginning of previous character */
+ posi--;
+ } while (posi > 0 && iscontp(s + posi));
+ n++;
+ }
+ }
+ else {
+ n--; /* do not move for 1st character */
+ while (n > 0 && posi < (lua_Integer)len) {
+ do { /* find beginning of next character */
+ posi++;
+ } while (iscontp(s + posi)); /* (cannot pass final '\0') */
+ n--;
+ }
+ }
+ }
+ if (n == 0) /* did it find given character? */
+ lua_pushinteger(L, posi + 1);
+ else /* no such character */
+ luaL_pushfail(L);
+ return 1;
+}
+
+
+static int iter_aux (lua_State *L, int strict) {
+ size_t len;
+ const char *s = luaL_checklstring(L, 1, &len);
+ lua_Unsigned n = (lua_Unsigned)lua_tointeger(L, 2);
+ if (n < len) {
+ while (iscontp(s + n)) n++; /* go to next character */
+ }
+ if (n >= len) /* (also handles original 'n' being negative) */
+ return 0; /* no more codepoints */
+ else {
+ utfint code;
+ const char *next = utf8_decode(s + n, &code, strict);
+ if (next == NULL || iscontp(next))
+ return luaL_error(L, MSGInvalid);
+ lua_pushinteger(L, n + 1);
+ lua_pushinteger(L, code);
+ return 2;
+ }
+}
+
+
+static int iter_auxstrict (lua_State *L) {
+ return iter_aux(L, 1);
+}
+
+static int iter_auxlax (lua_State *L) {
+ return iter_aux(L, 0);
+}
+
+
+static int iter_codes (lua_State *L) {
+ int lax = lua_toboolean(L, 2);
+ const char *s = luaL_checkstring(L, 1);
+ luaL_argcheck(L, !iscontp(s), 1, MSGInvalid);
+ lua_pushcfunction(L, lax ? iter_auxlax : iter_auxstrict);
+ lua_pushvalue(L, 1);
+ lua_pushinteger(L, 0);
+ return 3;
+}
+
+
+/* pattern to match a single UTF-8 character */
+#define UTF8PATT "[\0-\x7F\xC2-\xFD][\x80-\xBF]*"
+
+
+static const luaL_Reg funcs[] = {
+ {"offset", byteoffset},
+ {"codepoint", codepoint},
+ {"char", utfchar},
+ {"len", utflen},
+ {"codes", iter_codes},
+ /* placeholders */
+ {"charpattern", NULL},
+ {NULL, NULL}
+};
+
+
+LUAMOD_API int luaopen_utf8 (lua_State *L) {
+ luaL_newlib(L, funcs);
+ lua_pushlstring(L, UTF8PATT, sizeof(UTF8PATT)/sizeof(char) - 1);
+ lua_setfield(L, -2, "charpattern");
+ return 1;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lvm.c b/src/libs/3rdparty/lua/src/lvm.c
new file mode 100644
index 0000000000..8493a770c5
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lvm.c
@@ -0,0 +1,1901 @@
+/*
+** $Id: lvm.c $
+** Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#define lvm_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lua.h"
+
+#include "ldebug.h"
+#include "ldo.h"
+#include "lfunc.h"
+#include "lgc.h"
+#include "lobject.h"
+#include "lopcodes.h"
+#include "lstate.h"
+#include "lstring.h"
+#include "ltable.h"
+#include "ltm.h"
+#include "lvm.h"
+
+
+/*
+** By default, use jump tables in the main interpreter loop on gcc
+** and compatible compilers.
+*/
+#if !defined(LUA_USE_JUMPTABLE)
+#if defined(__GNUC__)
+#define LUA_USE_JUMPTABLE 1
+#else
+#define LUA_USE_JUMPTABLE 0
+#endif
+#endif
+
+
+
+/* limit for table tag-method chains (to avoid infinite loops) */
+#define MAXTAGLOOP 2000
+
+
+/*
+** 'l_intfitsf' checks whether a given integer is in the range that
+** can be converted to a float without rounding. Used in comparisons.
+*/
+
+/* number of bits in the mantissa of a float */
+#define NBM (l_floatatt(MANT_DIG))
+
+/*
+** Check whether some integers may not fit in a float, testing whether
+** (maxinteger >> NBM) > 0. (That implies (1 << NBM) <= maxinteger.)
+** (The shifts are done in parts, to avoid shifting by more than the size
+** of an integer. In a worst case, NBM == 113 for long double and
+** sizeof(long) == 32.)
+*/
+#if ((((LUA_MAXINTEGER >> (NBM / 4)) >> (NBM / 4)) >> (NBM / 4)) \
+ >> (NBM - (3 * (NBM / 4)))) > 0
+
+/* limit for integers that fit in a float */
+#define MAXINTFITSF ((lua_Unsigned)1 << NBM)
+
+/* check whether 'i' is in the interval [-MAXINTFITSF, MAXINTFITSF] */
+#define l_intfitsf(i) ((MAXINTFITSF + l_castS2U(i)) <= (2 * MAXINTFITSF))
+
+#else /* all integers fit in a float precisely */
+
+#define l_intfitsf(i) 1
+
+#endif
+
+
+/*
+** Try to convert a value from string to a number value.
+** If the value is not a string or is a string not representing
+** a valid numeral (or if coercions from strings to numbers
+** are disabled via macro 'cvt2num'), do not modify 'result'
+** and return 0.
+*/
+static int l_strton (const TValue *obj, TValue *result) {
+ lua_assert(obj != result);
+ if (!cvt2num(obj)) /* is object not a string? */
+ return 0;
+ else
+ return (luaO_str2num(svalue(obj), result) == vslen(obj) + 1);
+}
+
+
+/*
+** Try to convert a value to a float. The float case is already handled
+** by the macro 'tonumber'.
+*/
+int luaV_tonumber_ (const TValue *obj, lua_Number *n) {
+ TValue v;
+ if (ttisinteger(obj)) {
+ *n = cast_num(ivalue(obj));
+ return 1;
+ }
+ else if (l_strton(obj, &v)) { /* string coercible to number? */
+ *n = nvalue(&v); /* convert result of 'luaO_str2num' to a float */
+ return 1;
+ }
+ else
+ return 0; /* conversion failed */
+}
+
+
+/*
+** try to convert a float to an integer, rounding according to 'mode'.
+*/
+int luaV_flttointeger (lua_Number n, lua_Integer *p, F2Imod mode) {
+ lua_Number f = l_floor(n);
+ if (n != f) { /* not an integral value? */
+ if (mode == F2Ieq) return 0; /* fails if mode demands integral value */
+ else if (mode == F2Iceil) /* needs ceil? */
+ f += 1; /* convert floor to ceil (remember: n != f) */
+ }
+ return lua_numbertointeger(f, p);
+}
+
+
+/*
+** try to convert a value to an integer, rounding according to 'mode',
+** without string coercion.
+** ("Fast track" handled by macro 'tointegerns'.)
+*/
+int luaV_tointegerns (const TValue *obj, lua_Integer *p, F2Imod mode) {
+ if (ttisfloat(obj))
+ return luaV_flttointeger(fltvalue(obj), p, mode);
+ else if (ttisinteger(obj)) {
+ *p = ivalue(obj);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+/*
+** try to convert a value to an integer.
+*/
+int luaV_tointeger (const TValue *obj, lua_Integer *p, F2Imod mode) {
+ TValue v;
+ if (l_strton(obj, &v)) /* does 'obj' point to a numerical string? */
+ obj = &v; /* change it to point to its corresponding number */
+ return luaV_tointegerns(obj, p, mode);
+}
+
+
+/*
+** Try to convert a 'for' limit to an integer, preserving the semantics
+** of the loop. Return true if the loop must not run; otherwise, '*p'
+** gets the integer limit.
+** (The following explanation assumes a positive step; it is valid for
+** negative steps mutatis mutandis.)
+** If the limit is an integer or can be converted to an integer,
+** rounding down, that is the limit.
+** Otherwise, check whether the limit can be converted to a float. If
+** the float is too large, clip it to LUA_MAXINTEGER. If the float
+** is too negative, the loop should not run, because any initial
+** integer value is greater than such limit; so, the function returns
+** true to signal that. (For this latter case, no integer limit would be
+** correct; even a limit of LUA_MININTEGER would run the loop once for
+** an initial value equal to LUA_MININTEGER.)
+*/
+static int forlimit (lua_State *L, lua_Integer init, const TValue *lim,
+ lua_Integer *p, lua_Integer step) {
+ if (!luaV_tointeger(lim, p, (step < 0 ? F2Iceil : F2Ifloor))) {
+ /* not coercible to in integer */
+ lua_Number flim; /* try to convert to float */
+ if (!tonumber(lim, &flim)) /* cannot convert to float? */
+ luaG_forerror(L, lim, "limit");
+ /* else 'flim' is a float out of integer bounds */
+ if (luai_numlt(0, flim)) { /* if it is positive, it is too large */
+ if (step < 0) return 1; /* initial value must be less than it */
+ *p = LUA_MAXINTEGER; /* truncate */
+ }
+ else { /* it is less than min integer */
+ if (step > 0) return 1; /* initial value must be greater than it */
+ *p = LUA_MININTEGER; /* truncate */
+ }
+ }
+ return (step > 0 ? init > *p : init < *p); /* not to run? */
+}
+
+
+/*
+** Prepare a numerical for loop (opcode OP_FORPREP).
+** Return true to skip the loop. Otherwise,
+** after preparation, stack will be as follows:
+** ra : internal index (safe copy of the control variable)
+** ra + 1 : loop counter (integer loops) or limit (float loops)
+** ra + 2 : step
+** ra + 3 : control variable
+*/
+static int forprep (lua_State *L, StkId ra) {
+ TValue *pinit = s2v(ra);
+ TValue *plimit = s2v(ra + 1);
+ TValue *pstep = s2v(ra + 2);
+ if (ttisinteger(pinit) && ttisinteger(pstep)) { /* integer loop? */
+ lua_Integer init = ivalue(pinit);
+ lua_Integer step = ivalue(pstep);
+ lua_Integer limit;
+ if (step == 0)
+ luaG_runerror(L, "'for' step is zero");
+ setivalue(s2v(ra + 3), init); /* control variable */
+ if (forlimit(L, init, plimit, &limit, step))
+ return 1; /* skip the loop */
+ else { /* prepare loop counter */
+ lua_Unsigned count;
+ if (step > 0) { /* ascending loop? */
+ count = l_castS2U(limit) - l_castS2U(init);
+ if (step != 1) /* avoid division in the too common case */
+ count /= l_castS2U(step);
+ }
+ else { /* step < 0; descending loop */
+ count = l_castS2U(init) - l_castS2U(limit);
+ /* 'step+1' avoids negating 'mininteger' */
+ count /= l_castS2U(-(step + 1)) + 1u;
+ }
+ /* store the counter in place of the limit (which won't be
+ needed anymore) */
+ setivalue(plimit, l_castU2S(count));
+ }
+ }
+ else { /* try making all values floats */
+ lua_Number init; lua_Number limit; lua_Number step;
+ if (l_unlikely(!tonumber(plimit, &limit)))
+ luaG_forerror(L, plimit, "limit");
+ if (l_unlikely(!tonumber(pstep, &step)))
+ luaG_forerror(L, pstep, "step");
+ if (l_unlikely(!tonumber(pinit, &init)))
+ luaG_forerror(L, pinit, "initial value");
+ if (step == 0)
+ luaG_runerror(L, "'for' step is zero");
+ if (luai_numlt(0, step) ? luai_numlt(limit, init)
+ : luai_numlt(init, limit))
+ return 1; /* skip the loop */
+ else {
+ /* make sure internal values are all floats */
+ setfltvalue(plimit, limit);
+ setfltvalue(pstep, step);
+ setfltvalue(s2v(ra), init); /* internal index */
+ setfltvalue(s2v(ra + 3), init); /* control variable */
+ }
+ }
+ return 0;
+}
+
+
+/*
+** Execute a step of a float numerical for loop, returning
+** true iff the loop must continue. (The integer case is
+** written online with opcode OP_FORLOOP, for performance.)
+*/
+static int floatforloop (StkId ra) {
+ lua_Number step = fltvalue(s2v(ra + 2));
+ lua_Number limit = fltvalue(s2v(ra + 1));
+ lua_Number idx = fltvalue(s2v(ra)); /* internal index */
+ idx = luai_numadd(L, idx, step); /* increment index */
+ if (luai_numlt(0, step) ? luai_numle(idx, limit)
+ : luai_numle(limit, idx)) {
+ chgfltvalue(s2v(ra), idx); /* update internal index */
+ setfltvalue(s2v(ra + 3), idx); /* and control variable */
+ return 1; /* jump back */
+ }
+ else
+ return 0; /* finish the loop */
+}
+
+
+/*
+** Finish the table access 'val = t[key]'.
+** if 'slot' is NULL, 't' is not a table; otherwise, 'slot' points to
+** t[k] entry (which must be empty).
+*/
+void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val,
+ const TValue *slot) {
+ int loop; /* counter to avoid infinite loops */
+ const TValue *tm; /* metamethod */
+ for (loop = 0; loop < MAXTAGLOOP; loop++) {
+ if (slot == NULL) { /* 't' is not a table? */
+ lua_assert(!ttistable(t));
+ tm = luaT_gettmbyobj(L, t, TM_INDEX);
+ if (l_unlikely(notm(tm)))
+ luaG_typeerror(L, t, "index"); /* no metamethod */
+ /* else will try the metamethod */
+ }
+ else { /* 't' is a table */
+ lua_assert(isempty(slot));
+ tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */
+ if (tm == NULL) { /* no metamethod? */
+ setnilvalue(s2v(val)); /* result is nil */
+ return;
+ }
+ /* else will try the metamethod */
+ }
+ if (ttisfunction(tm)) { /* is metamethod a function? */
+ luaT_callTMres(L, tm, t, key, val); /* call it */
+ return;
+ }
+ t = tm; /* else try to access 'tm[key]' */
+ if (luaV_fastget(L, t, key, slot, luaH_get)) { /* fast track? */
+ setobj2s(L, val, slot); /* done */
+ return;
+ }
+ /* else repeat (tail call 'luaV_finishget') */
+ }
+ luaG_runerror(L, "'__index' chain too long; possible loop");
+}
+
+
+/*
+** Finish a table assignment 't[key] = val'.
+** If 'slot' is NULL, 't' is not a table. Otherwise, 'slot' points
+** to the entry 't[key]', or to a value with an absent key if there
+** is no such entry. (The value at 'slot' must be empty, otherwise
+** 'luaV_fastget' would have done the job.)
+*/
+void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
+ TValue *val, const TValue *slot) {
+ int loop; /* counter to avoid infinite loops */
+ for (loop = 0; loop < MAXTAGLOOP; loop++) {
+ const TValue *tm; /* '__newindex' metamethod */
+ if (slot != NULL) { /* is 't' a table? */
+ Table *h = hvalue(t); /* save 't' table */
+ lua_assert(isempty(slot)); /* slot must be empty */
+ tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */
+ if (tm == NULL) { /* no metamethod? */
+ luaH_finishset(L, h, key, slot, val); /* set new value */
+ invalidateTMcache(h);
+ luaC_barrierback(L, obj2gco(h), val);
+ return;
+ }
+ /* else will try the metamethod */
+ }
+ else { /* not a table; check metamethod */
+ tm = luaT_gettmbyobj(L, t, TM_NEWINDEX);
+ if (l_unlikely(notm(tm)))
+ luaG_typeerror(L, t, "index");
+ }
+ /* try the metamethod */
+ if (ttisfunction(tm)) {
+ luaT_callTM(L, tm, t, key, val);
+ return;
+ }
+ t = tm; /* else repeat assignment over 'tm' */
+ if (luaV_fastget(L, t, key, slot, luaH_get)) {
+ luaV_finishfastset(L, t, slot, val);
+ return; /* done */
+ }
+ /* else 'return luaV_finishset(L, t, key, val, slot)' (loop) */
+ }
+ luaG_runerror(L, "'__newindex' chain too long; possible loop");
+}
+
+
+/*
+** Compare two strings 'ls' x 'rs', returning an integer less-equal-
+** -greater than zero if 'ls' is less-equal-greater than 'rs'.
+** The code is a little tricky because it allows '\0' in the strings
+** and it uses 'strcoll' (to respect locales) for each segments
+** of the strings.
+*/
+static int l_strcmp (const TString *ls, const TString *rs) {
+ const char *l = getstr(ls);
+ size_t ll = tsslen(ls);
+ const char *r = getstr(rs);
+ size_t lr = tsslen(rs);
+ for (;;) { /* for each segment */
+ int temp = strcoll(l, r);
+ if (temp != 0) /* not equal? */
+ return temp; /* done */
+ else { /* strings are equal up to a '\0' */
+ size_t len = strlen(l); /* index of first '\0' in both strings */
+ if (len == lr) /* 'rs' is finished? */
+ return (len == ll) ? 0 : 1; /* check 'ls' */
+ else if (len == ll) /* 'ls' is finished? */
+ return -1; /* 'ls' is less than 'rs' ('rs' is not finished) */
+ /* both strings longer than 'len'; go on comparing after the '\0' */
+ len++;
+ l += len; ll -= len; r += len; lr -= len;
+ }
+ }
+}
+
+
+/*
+** Check whether integer 'i' is less than float 'f'. If 'i' has an
+** exact representation as a float ('l_intfitsf'), compare numbers as
+** floats. Otherwise, use the equivalence 'i < f <=> i < ceil(f)'.
+** If 'ceil(f)' is out of integer range, either 'f' is greater than
+** all integers or less than all integers.
+** (The test with 'l_intfitsf' is only for performance; the else
+** case is correct for all values, but it is slow due to the conversion
+** from float to int.)
+** When 'f' is NaN, comparisons must result in false.
+*/
+l_sinline int LTintfloat (lua_Integer i, lua_Number f) {
+ if (l_intfitsf(i))
+ return luai_numlt(cast_num(i), f); /* compare them as floats */
+ else { /* i < f <=> i < ceil(f) */
+ lua_Integer fi;
+ if (luaV_flttointeger(f, &fi, F2Iceil)) /* fi = ceil(f) */
+ return i < fi; /* compare them as integers */
+ else /* 'f' is either greater or less than all integers */
+ return f > 0; /* greater? */
+ }
+}
+
+
+/*
+** Check whether integer 'i' is less than or equal to float 'f'.
+** See comments on previous function.
+*/
+l_sinline int LEintfloat (lua_Integer i, lua_Number f) {
+ if (l_intfitsf(i))
+ return luai_numle(cast_num(i), f); /* compare them as floats */
+ else { /* i <= f <=> i <= floor(f) */
+ lua_Integer fi;
+ if (luaV_flttointeger(f, &fi, F2Ifloor)) /* fi = floor(f) */
+ return i <= fi; /* compare them as integers */
+ else /* 'f' is either greater or less than all integers */
+ return f > 0; /* greater? */
+ }
+}
+
+
+/*
+** Check whether float 'f' is less than integer 'i'.
+** See comments on previous function.
+*/
+l_sinline int LTfloatint (lua_Number f, lua_Integer i) {
+ if (l_intfitsf(i))
+ return luai_numlt(f, cast_num(i)); /* compare them as floats */
+ else { /* f < i <=> floor(f) < i */
+ lua_Integer fi;
+ if (luaV_flttointeger(f, &fi, F2Ifloor)) /* fi = floor(f) */
+ return fi < i; /* compare them as integers */
+ else /* 'f' is either greater or less than all integers */
+ return f < 0; /* less? */
+ }
+}
+
+
+/*
+** Check whether float 'f' is less than or equal to integer 'i'.
+** See comments on previous function.
+*/
+l_sinline int LEfloatint (lua_Number f, lua_Integer i) {
+ if (l_intfitsf(i))
+ return luai_numle(f, cast_num(i)); /* compare them as floats */
+ else { /* f <= i <=> ceil(f) <= i */
+ lua_Integer fi;
+ if (luaV_flttointeger(f, &fi, F2Iceil)) /* fi = ceil(f) */
+ return fi <= i; /* compare them as integers */
+ else /* 'f' is either greater or less than all integers */
+ return f < 0; /* less? */
+ }
+}
+
+
+/*
+** Return 'l < r', for numbers.
+*/
+l_sinline int LTnum (const TValue *l, const TValue *r) {
+ lua_assert(ttisnumber(l) && ttisnumber(r));
+ if (ttisinteger(l)) {
+ lua_Integer li = ivalue(l);
+ if (ttisinteger(r))
+ return li < ivalue(r); /* both are integers */
+ else /* 'l' is int and 'r' is float */
+ return LTintfloat(li, fltvalue(r)); /* l < r ? */
+ }
+ else {
+ lua_Number lf = fltvalue(l); /* 'l' must be float */
+ if (ttisfloat(r))
+ return luai_numlt(lf, fltvalue(r)); /* both are float */
+ else /* 'l' is float and 'r' is int */
+ return LTfloatint(lf, ivalue(r));
+ }
+}
+
+
+/*
+** Return 'l <= r', for numbers.
+*/
+l_sinline int LEnum (const TValue *l, const TValue *r) {
+ lua_assert(ttisnumber(l) && ttisnumber(r));
+ if (ttisinteger(l)) {
+ lua_Integer li = ivalue(l);
+ if (ttisinteger(r))
+ return li <= ivalue(r); /* both are integers */
+ else /* 'l' is int and 'r' is float */
+ return LEintfloat(li, fltvalue(r)); /* l <= r ? */
+ }
+ else {
+ lua_Number lf = fltvalue(l); /* 'l' must be float */
+ if (ttisfloat(r))
+ return luai_numle(lf, fltvalue(r)); /* both are float */
+ else /* 'l' is float and 'r' is int */
+ return LEfloatint(lf, ivalue(r));
+ }
+}
+
+
+/*
+** return 'l < r' for non-numbers.
+*/
+static int lessthanothers (lua_State *L, const TValue *l, const TValue *r) {
+ lua_assert(!ttisnumber(l) || !ttisnumber(r));
+ if (ttisstring(l) && ttisstring(r)) /* both are strings? */
+ return l_strcmp(tsvalue(l), tsvalue(r)) < 0;
+ else
+ return luaT_callorderTM(L, l, r, TM_LT);
+}
+
+
+/*
+** Main operation less than; return 'l < r'.
+*/
+int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) {
+ if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */
+ return LTnum(l, r);
+ else return lessthanothers(L, l, r);
+}
+
+
+/*
+** return 'l <= r' for non-numbers.
+*/
+static int lessequalothers (lua_State *L, const TValue *l, const TValue *r) {
+ lua_assert(!ttisnumber(l) || !ttisnumber(r));
+ if (ttisstring(l) && ttisstring(r)) /* both are strings? */
+ return l_strcmp(tsvalue(l), tsvalue(r)) <= 0;
+ else
+ return luaT_callorderTM(L, l, r, TM_LE);
+}
+
+
+/*
+** Main operation less than or equal to; return 'l <= r'.
+*/
+int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) {
+ if (ttisnumber(l) && ttisnumber(r)) /* both operands are numbers? */
+ return LEnum(l, r);
+ else return lessequalothers(L, l, r);
+}
+
+
+/*
+** Main operation for equality of Lua values; return 't1 == t2'.
+** L == NULL means raw equality (no metamethods)
+*/
+int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
+ const TValue *tm;
+ if (ttypetag(t1) != ttypetag(t2)) { /* not the same variant? */
+ if (ttype(t1) != ttype(t2) || ttype(t1) != LUA_TNUMBER)
+ return 0; /* only numbers can be equal with different variants */
+ else { /* two numbers with different variants */
+ /* One of them is an integer. If the other does not have an
+ integer value, they cannot be equal; otherwise, compare their
+ integer values. */
+ lua_Integer i1, i2;
+ return (luaV_tointegerns(t1, &i1, F2Ieq) &&
+ luaV_tointegerns(t2, &i2, F2Ieq) &&
+ i1 == i2);
+ }
+ }
+ /* values have same type and same variant */
+ switch (ttypetag(t1)) {
+ case LUA_VNIL: case LUA_VFALSE: case LUA_VTRUE: return 1;
+ case LUA_VNUMINT: return (ivalue(t1) == ivalue(t2));
+ case LUA_VNUMFLT: return luai_numeq(fltvalue(t1), fltvalue(t2));
+ case LUA_VLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
+ case LUA_VLCF: return fvalue(t1) == fvalue(t2);
+ case LUA_VSHRSTR: return eqshrstr(tsvalue(t1), tsvalue(t2));
+ case LUA_VLNGSTR: return luaS_eqlngstr(tsvalue(t1), tsvalue(t2));
+ case LUA_VUSERDATA: {
+ if (uvalue(t1) == uvalue(t2)) return 1;
+ else if (L == NULL) return 0;
+ tm = fasttm(L, uvalue(t1)->metatable, TM_EQ);
+ if (tm == NULL)
+ tm = fasttm(L, uvalue(t2)->metatable, TM_EQ);
+ break; /* will try TM */
+ }
+ case LUA_VTABLE: {
+ if (hvalue(t1) == hvalue(t2)) return 1;
+ else if (L == NULL) return 0;
+ tm = fasttm(L, hvalue(t1)->metatable, TM_EQ);
+ if (tm == NULL)
+ tm = fasttm(L, hvalue(t2)->metatable, TM_EQ);
+ break; /* will try TM */
+ }
+ default:
+ return gcvalue(t1) == gcvalue(t2);
+ }
+ if (tm == NULL) /* no TM? */
+ return 0; /* objects are different */
+ else {
+ luaT_callTMres(L, tm, t1, t2, L->top.p); /* call TM */
+ return !l_isfalse(s2v(L->top.p));
+ }
+}
+
+
+/* macro used by 'luaV_concat' to ensure that element at 'o' is a string */
+#define tostring(L,o) \
+ (ttisstring(o) || (cvt2str(o) && (luaO_tostring(L, o), 1)))
+
+#define isemptystr(o) (ttisshrstring(o) && tsvalue(o)->shrlen == 0)
+
+/* copy strings in stack from top - n up to top - 1 to buffer */
+static void copy2buff (StkId top, int n, char *buff) {
+ size_t tl = 0; /* size already copied */
+ do {
+ size_t l = vslen(s2v(top - n)); /* length of string being copied */
+ memcpy(buff + tl, svalue(s2v(top - n)), l * sizeof(char));
+ tl += l;
+ } while (--n > 0);
+}
+
+
+/*
+** Main operation for concatenation: concat 'total' values in the stack,
+** from 'L->top.p - total' up to 'L->top.p - 1'.
+*/
+void luaV_concat (lua_State *L, int total) {
+ if (total == 1)
+ return; /* "all" values already concatenated */
+ do {
+ StkId top = L->top.p;
+ int n = 2; /* number of elements handled in this pass (at least 2) */
+ if (!(ttisstring(s2v(top - 2)) || cvt2str(s2v(top - 2))) ||
+ !tostring(L, s2v(top - 1)))
+ luaT_tryconcatTM(L); /* may invalidate 'top' */
+ else if (isemptystr(s2v(top - 1))) /* second operand is empty? */
+ cast_void(tostring(L, s2v(top - 2))); /* result is first operand */
+ else if (isemptystr(s2v(top - 2))) { /* first operand is empty string? */
+ setobjs2s(L, top - 2, top - 1); /* result is second op. */
+ }
+ else {
+ /* at least two non-empty string values; get as many as possible */
+ size_t tl = vslen(s2v(top - 1));
+ TString *ts;
+ /* collect total length and number of strings */
+ for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) {
+ size_t l = vslen(s2v(top - n - 1));
+ if (l_unlikely(l >= (MAX_SIZE/sizeof(char)) - tl)) {
+ L->top.p = top - total; /* pop strings to avoid wasting stack */
+ luaG_runerror(L, "string length overflow");
+ }
+ tl += l;
+ }
+ if (tl <= LUAI_MAXSHORTLEN) { /* is result a short string? */
+ char buff[LUAI_MAXSHORTLEN];
+ copy2buff(top, n, buff); /* copy strings to buffer */
+ ts = luaS_newlstr(L, buff, tl);
+ }
+ else { /* long string; copy strings directly to final result */
+ ts = luaS_createlngstrobj(L, tl);
+ copy2buff(top, n, getstr(ts));
+ }
+ setsvalue2s(L, top - n, ts); /* create result */
+ }
+ total -= n - 1; /* got 'n' strings to create one new */
+ L->top.p -= n - 1; /* popped 'n' strings and pushed one */
+ } while (total > 1); /* repeat until only 1 result left */
+}
+
+
+/*
+** Main operation 'ra = #rb'.
+*/
+void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) {
+ const TValue *tm;
+ switch (ttypetag(rb)) {
+ case LUA_VTABLE: {
+ Table *h = hvalue(rb);
+ tm = fasttm(L, h->metatable, TM_LEN);
+ if (tm) break; /* metamethod? break switch to call it */
+ setivalue(s2v(ra), luaH_getn(h)); /* else primitive len */
+ return;
+ }
+ case LUA_VSHRSTR: {
+ setivalue(s2v(ra), tsvalue(rb)->shrlen);
+ return;
+ }
+ case LUA_VLNGSTR: {
+ setivalue(s2v(ra), tsvalue(rb)->u.lnglen);
+ return;
+ }
+ default: { /* try metamethod */
+ tm = luaT_gettmbyobj(L, rb, TM_LEN);
+ if (l_unlikely(notm(tm))) /* no metamethod? */
+ luaG_typeerror(L, rb, "get length of");
+ break;
+ }
+ }
+ luaT_callTMres(L, tm, rb, rb, ra);
+}
+
+
+/*
+** Integer division; return 'm // n', that is, floor(m/n).
+** C division truncates its result (rounds towards zero).
+** 'floor(q) == trunc(q)' when 'q >= 0' or when 'q' is integer,
+** otherwise 'floor(q) == trunc(q) - 1'.
+*/
+lua_Integer luaV_idiv (lua_State *L, lua_Integer m, lua_Integer n) {
+ if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
+ if (n == 0)
+ luaG_runerror(L, "attempt to divide by zero");
+ return intop(-, 0, m); /* n==-1; avoid overflow with 0x80000...//-1 */
+ }
+ else {
+ lua_Integer q = m / n; /* perform C division */
+ if ((m ^ n) < 0 && m % n != 0) /* 'm/n' would be negative non-integer? */
+ q -= 1; /* correct result for different rounding */
+ return q;
+ }
+}
+
+
+/*
+** Integer modulus; return 'm % n'. (Assume that C '%' with
+** negative operands follows C99 behavior. See previous comment
+** about luaV_idiv.)
+*/
+lua_Integer luaV_mod (lua_State *L, lua_Integer m, lua_Integer n) {
+ if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */
+ if (n == 0)
+ luaG_runerror(L, "attempt to perform 'n%%0'");
+ return 0; /* m % -1 == 0; avoid overflow with 0x80000...%-1 */
+ }
+ else {
+ lua_Integer r = m % n;
+ if (r != 0 && (r ^ n) < 0) /* 'm/n' would be non-integer negative? */
+ r += n; /* correct result for different rounding */
+ return r;
+ }
+}
+
+
+/*
+** Float modulus
+*/
+lua_Number luaV_modf (lua_State *L, lua_Number m, lua_Number n) {
+ lua_Number r;
+ luai_nummod(L, m, n, r);
+ return r;
+}
+
+
+/* number of bits in an integer */
+#define NBITS cast_int(sizeof(lua_Integer) * CHAR_BIT)
+
+
+/*
+** Shift left operation. (Shift right just negates 'y'.)
+*/
+lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y) {
+ if (y < 0) { /* shift right? */
+ if (y <= -NBITS) return 0;
+ else return intop(>>, x, -y);
+ }
+ else { /* shift left */
+ if (y >= NBITS) return 0;
+ else return intop(<<, x, y);
+ }
+}
+
+
+/*
+** create a new Lua closure, push it in the stack, and initialize
+** its upvalues.
+*/
+static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base,
+ StkId ra) {
+ int nup = p->sizeupvalues;
+ Upvaldesc *uv = p->upvalues;
+ int i;
+ LClosure *ncl = luaF_newLclosure(L, nup);
+ ncl->p = p;
+ setclLvalue2s(L, ra, ncl); /* anchor new closure in stack */
+ for (i = 0; i < nup; i++) { /* fill in its upvalues */
+ if (uv[i].instack) /* upvalue refers to local variable? */
+ ncl->upvals[i] = luaF_findupval(L, base + uv[i].idx);
+ else /* get upvalue from enclosing function */
+ ncl->upvals[i] = encup[uv[i].idx];
+ luaC_objbarrier(L, ncl, ncl->upvals[i]);
+ }
+}
+
+
+/*
+** finish execution of an opcode interrupted by a yield
+*/
+void luaV_finishOp (lua_State *L) {
+ CallInfo *ci = L->ci;
+ StkId base = ci->func.p + 1;
+ Instruction inst = *(ci->u.l.savedpc - 1); /* interrupted instruction */
+ OpCode op = GET_OPCODE(inst);
+ switch (op) { /* finish its execution */
+ case OP_MMBIN: case OP_MMBINI: case OP_MMBINK: {
+ setobjs2s(L, base + GETARG_A(*(ci->u.l.savedpc - 2)), --L->top.p);
+ break;
+ }
+ case OP_UNM: case OP_BNOT: case OP_LEN:
+ case OP_GETTABUP: case OP_GETTABLE: case OP_GETI:
+ case OP_GETFIELD: case OP_SELF: {
+ setobjs2s(L, base + GETARG_A(inst), --L->top.p);
+ break;
+ }
+ case OP_LT: case OP_LE:
+ case OP_LTI: case OP_LEI:
+ case OP_GTI: case OP_GEI:
+ case OP_EQ: { /* note that 'OP_EQI'/'OP_EQK' cannot yield */
+ int res = !l_isfalse(s2v(L->top.p - 1));
+ L->top.p--;
+#if defined(LUA_COMPAT_LT_LE)
+ if (ci->callstatus & CIST_LEQ) { /* "<=" using "<" instead? */
+ ci->callstatus ^= CIST_LEQ; /* clear mark */
+ res = !res; /* negate result */
+ }
+#endif
+ lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP);
+ if (res != GETARG_k(inst)) /* condition failed? */
+ ci->u.l.savedpc++; /* skip jump instruction */
+ break;
+ }
+ case OP_CONCAT: {
+ StkId top = L->top.p - 1; /* top when 'luaT_tryconcatTM' was called */
+ int a = GETARG_A(inst); /* first element to concatenate */
+ int total = cast_int(top - 1 - (base + a)); /* yet to concatenate */
+ setobjs2s(L, top - 2, top); /* put TM result in proper position */
+ L->top.p = top - 1; /* top is one after last element (at top-2) */
+ luaV_concat(L, total); /* concat them (may yield again) */
+ break;
+ }
+ case OP_CLOSE: { /* yielded closing variables */
+ ci->u.l.savedpc--; /* repeat instruction to close other vars. */
+ break;
+ }
+ case OP_RETURN: { /* yielded closing variables */
+ StkId ra = base + GETARG_A(inst);
+ /* adjust top to signal correct number of returns, in case the
+ return is "up to top" ('isIT') */
+ L->top.p = ra + ci->u2.nres;
+ /* repeat instruction to close other vars. and complete the return */
+ ci->u.l.savedpc--;
+ break;
+ }
+ default: {
+ /* only these other opcodes can yield */
+ lua_assert(op == OP_TFORCALL || op == OP_CALL ||
+ op == OP_TAILCALL || op == OP_SETTABUP || op == OP_SETTABLE ||
+ op == OP_SETI || op == OP_SETFIELD);
+ break;
+ }
+ }
+}
+
+
+
+
+/*
+** {==================================================================
+** Macros for arithmetic/bitwise/comparison opcodes in 'luaV_execute'
+** ===================================================================
+*/
+
+#define l_addi(L,a,b) intop(+, a, b)
+#define l_subi(L,a,b) intop(-, a, b)
+#define l_muli(L,a,b) intop(*, a, b)
+#define l_band(a,b) intop(&, a, b)
+#define l_bor(a,b) intop(|, a, b)
+#define l_bxor(a,b) intop(^, a, b)
+
+#define l_lti(a,b) (a < b)
+#define l_lei(a,b) (a <= b)
+#define l_gti(a,b) (a > b)
+#define l_gei(a,b) (a >= b)
+
+
+/*
+** Arithmetic operations with immediate operands. 'iop' is the integer
+** operation, 'fop' is the float operation.
+*/
+#define op_arithI(L,iop,fop) { \
+ StkId ra = RA(i); \
+ TValue *v1 = vRB(i); \
+ int imm = GETARG_sC(i); \
+ if (ttisinteger(v1)) { \
+ lua_Integer iv1 = ivalue(v1); \
+ pc++; setivalue(s2v(ra), iop(L, iv1, imm)); \
+ } \
+ else if (ttisfloat(v1)) { \
+ lua_Number nb = fltvalue(v1); \
+ lua_Number fimm = cast_num(imm); \
+ pc++; setfltvalue(s2v(ra), fop(L, nb, fimm)); \
+ }}
+
+
+/*
+** Auxiliary function for arithmetic operations over floats and others
+** with two register operands.
+*/
+#define op_arithf_aux(L,v1,v2,fop) { \
+ lua_Number n1; lua_Number n2; \
+ if (tonumberns(v1, n1) && tonumberns(v2, n2)) { \
+ pc++; setfltvalue(s2v(ra), fop(L, n1, n2)); \
+ }}
+
+
+/*
+** Arithmetic operations over floats and others with register operands.
+*/
+#define op_arithf(L,fop) { \
+ StkId ra = RA(i); \
+ TValue *v1 = vRB(i); \
+ TValue *v2 = vRC(i); \
+ op_arithf_aux(L, v1, v2, fop); }
+
+
+/*
+** Arithmetic operations with K operands for floats.
+*/
+#define op_arithfK(L,fop) { \
+ StkId ra = RA(i); \
+ TValue *v1 = vRB(i); \
+ TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \
+ op_arithf_aux(L, v1, v2, fop); }
+
+
+/*
+** Arithmetic operations over integers and floats.
+*/
+#define op_arith_aux(L,v1,v2,iop,fop) { \
+ StkId ra = RA(i); \
+ if (ttisinteger(v1) && ttisinteger(v2)) { \
+ lua_Integer i1 = ivalue(v1); lua_Integer i2 = ivalue(v2); \
+ pc++; setivalue(s2v(ra), iop(L, i1, i2)); \
+ } \
+ else op_arithf_aux(L, v1, v2, fop); }
+
+
+/*
+** Arithmetic operations with register operands.
+*/
+#define op_arith(L,iop,fop) { \
+ TValue *v1 = vRB(i); \
+ TValue *v2 = vRC(i); \
+ op_arith_aux(L, v1, v2, iop, fop); }
+
+
+/*
+** Arithmetic operations with K operands.
+*/
+#define op_arithK(L,iop,fop) { \
+ TValue *v1 = vRB(i); \
+ TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \
+ op_arith_aux(L, v1, v2, iop, fop); }
+
+
+/*
+** Bitwise operations with constant operand.
+*/
+#define op_bitwiseK(L,op) { \
+ StkId ra = RA(i); \
+ TValue *v1 = vRB(i); \
+ TValue *v2 = KC(i); \
+ lua_Integer i1; \
+ lua_Integer i2 = ivalue(v2); \
+ if (tointegerns(v1, &i1)) { \
+ pc++; setivalue(s2v(ra), op(i1, i2)); \
+ }}
+
+
+/*
+** Bitwise operations with register operands.
+*/
+#define op_bitwise(L,op) { \
+ StkId ra = RA(i); \
+ TValue *v1 = vRB(i); \
+ TValue *v2 = vRC(i); \
+ lua_Integer i1; lua_Integer i2; \
+ if (tointegerns(v1, &i1) && tointegerns(v2, &i2)) { \
+ pc++; setivalue(s2v(ra), op(i1, i2)); \
+ }}
+
+
+/*
+** Order operations with register operands. 'opn' actually works
+** for all numbers, but the fast track improves performance for
+** integers.
+*/
+#define op_order(L,opi,opn,other) { \
+ StkId ra = RA(i); \
+ int cond; \
+ TValue *rb = vRB(i); \
+ if (ttisinteger(s2v(ra)) && ttisinteger(rb)) { \
+ lua_Integer ia = ivalue(s2v(ra)); \
+ lua_Integer ib = ivalue(rb); \
+ cond = opi(ia, ib); \
+ } \
+ else if (ttisnumber(s2v(ra)) && ttisnumber(rb)) \
+ cond = opn(s2v(ra), rb); \
+ else \
+ Protect(cond = other(L, s2v(ra), rb)); \
+ docondjump(); }
+
+
+/*
+** Order operations with immediate operand. (Immediate operand is
+** always small enough to have an exact representation as a float.)
+*/
+#define op_orderI(L,opi,opf,inv,tm) { \
+ StkId ra = RA(i); \
+ int cond; \
+ int im = GETARG_sB(i); \
+ if (ttisinteger(s2v(ra))) \
+ cond = opi(ivalue(s2v(ra)), im); \
+ else if (ttisfloat(s2v(ra))) { \
+ lua_Number fa = fltvalue(s2v(ra)); \
+ lua_Number fim = cast_num(im); \
+ cond = opf(fa, fim); \
+ } \
+ else { \
+ int isf = GETARG_C(i); \
+ Protect(cond = luaT_callorderiTM(L, s2v(ra), im, inv, isf, tm)); \
+ } \
+ docondjump(); }
+
+/* }================================================================== */
+
+
+/*
+** {==================================================================
+** Function 'luaV_execute': main interpreter loop
+** ===================================================================
+*/
+
+/*
+** some macros for common tasks in 'luaV_execute'
+*/
+
+
+#define RA(i) (base+GETARG_A(i))
+#define RB(i) (base+GETARG_B(i))
+#define vRB(i) s2v(RB(i))
+#define KB(i) (k+GETARG_B(i))
+#define RC(i) (base+GETARG_C(i))
+#define vRC(i) s2v(RC(i))
+#define KC(i) (k+GETARG_C(i))
+#define RKC(i) ((TESTARG_k(i)) ? k + GETARG_C(i) : s2v(base + GETARG_C(i)))
+
+
+
+#define updatetrap(ci) (trap = ci->u.l.trap)
+
+#define updatebase(ci) (base = ci->func.p + 1)
+
+
+#define updatestack(ci) \
+ { if (l_unlikely(trap)) { updatebase(ci); ra = RA(i); } }
+
+
+/*
+** Execute a jump instruction. The 'updatetrap' allows signals to stop
+** tight loops. (Without it, the local copy of 'trap' could never change.)
+*/
+#define dojump(ci,i,e) { pc += GETARG_sJ(i) + e; updatetrap(ci); }
+
+
+/* for test instructions, execute the jump instruction that follows it */
+#define donextjump(ci) { Instruction ni = *pc; dojump(ci, ni, 1); }
+
+/*
+** do a conditional jump: skip next instruction if 'cond' is not what
+** was expected (parameter 'k'), else do next instruction, which must
+** be a jump.
+*/
+#define docondjump() if (cond != GETARG_k(i)) pc++; else donextjump(ci);
+
+
+/*
+** Correct global 'pc'.
+*/
+#define savepc(L) (ci->u.l.savedpc = pc)
+
+
+/*
+** Whenever code can raise errors, the global 'pc' and the global
+** 'top' must be correct to report occasional errors.
+*/
+#define savestate(L,ci) (savepc(L), L->top.p = ci->top.p)
+
+
+/*
+** Protect code that, in general, can raise errors, reallocate the
+** stack, and change the hooks.
+*/
+#define Protect(exp) (savestate(L,ci), (exp), updatetrap(ci))
+
+/* special version that does not change the top */
+#define ProtectNT(exp) (savepc(L), (exp), updatetrap(ci))
+
+/*
+** Protect code that can only raise errors. (That is, it cannot change
+** the stack or hooks.)
+*/
+#define halfProtect(exp) (savestate(L,ci), (exp))
+
+/* 'c' is the limit of live values in the stack */
+#define checkGC(L,c) \
+ { luaC_condGC(L, (savepc(L), L->top.p = (c)), \
+ updatetrap(ci)); \
+ luai_threadyield(L); }
+
+
+/* fetch an instruction and prepare its execution */
+#define vmfetch() { \
+ if (l_unlikely(trap)) { /* stack reallocation or hooks? */ \
+ trap = luaG_traceexec(L, pc); /* handle hooks */ \
+ updatebase(ci); /* correct stack */ \
+ } \
+ i = *(pc++); \
+}
+
+#define vmdispatch(o) switch(o)
+#define vmcase(l) case l:
+#define vmbreak break
+
+
+void luaV_execute (lua_State *L, CallInfo *ci) {
+ LClosure *cl;
+ TValue *k;
+ StkId base;
+ const Instruction *pc;
+ int trap;
+#if LUA_USE_JUMPTABLE
+#include "ljumptab.h"
+#endif
+ startfunc:
+ trap = L->hookmask;
+ returning: /* trap already set */
+ cl = clLvalue(s2v(ci->func.p));
+ k = cl->p->k;
+ pc = ci->u.l.savedpc;
+ if (l_unlikely(trap)) {
+ if (pc == cl->p->code) { /* first instruction (not resuming)? */
+ if (cl->p->is_vararg)
+ trap = 0; /* hooks will start after VARARGPREP instruction */
+ else /* check 'call' hook */
+ luaD_hookcall(L, ci);
+ }
+ ci->u.l.trap = 1; /* assume trap is on, for now */
+ }
+ base = ci->func.p + 1;
+ /* main loop of interpreter */
+ for (;;) {
+ Instruction i; /* instruction being executed */
+ vmfetch();
+ #if 0
+ /* low-level line tracing for debugging Lua */
+ printf("line: %d\n", luaG_getfuncline(cl->p, pcRel(pc, cl->p)));
+ #endif
+ lua_assert(base == ci->func.p + 1);
+ lua_assert(base <= L->top.p && L->top.p <= L->stack_last.p);
+ /* invalidate top for instructions not expecting it */
+ lua_assert(isIT(i) || (cast_void(L->top.p = base), 1));
+ vmdispatch (GET_OPCODE(i)) {
+ vmcase(OP_MOVE) {
+ StkId ra = RA(i);
+ setobjs2s(L, ra, RB(i));
+ vmbreak;
+ }
+ vmcase(OP_LOADI) {
+ StkId ra = RA(i);
+ lua_Integer b = GETARG_sBx(i);
+ setivalue(s2v(ra), b);
+ vmbreak;
+ }
+ vmcase(OP_LOADF) {
+ StkId ra = RA(i);
+ int b = GETARG_sBx(i);
+ setfltvalue(s2v(ra), cast_num(b));
+ vmbreak;
+ }
+ vmcase(OP_LOADK) {
+ StkId ra = RA(i);
+ TValue *rb = k + GETARG_Bx(i);
+ setobj2s(L, ra, rb);
+ vmbreak;
+ }
+ vmcase(OP_LOADKX) {
+ StkId ra = RA(i);
+ TValue *rb;
+ rb = k + GETARG_Ax(*pc); pc++;
+ setobj2s(L, ra, rb);
+ vmbreak;
+ }
+ vmcase(OP_LOADFALSE) {
+ StkId ra = RA(i);
+ setbfvalue(s2v(ra));
+ vmbreak;
+ }
+ vmcase(OP_LFALSESKIP) {
+ StkId ra = RA(i);
+ setbfvalue(s2v(ra));
+ pc++; /* skip next instruction */
+ vmbreak;
+ }
+ vmcase(OP_LOADTRUE) {
+ StkId ra = RA(i);
+ setbtvalue(s2v(ra));
+ vmbreak;
+ }
+ vmcase(OP_LOADNIL) {
+ StkId ra = RA(i);
+ int b = GETARG_B(i);
+ do {
+ setnilvalue(s2v(ra++));
+ } while (b--);
+ vmbreak;
+ }
+ vmcase(OP_GETUPVAL) {
+ StkId ra = RA(i);
+ int b = GETARG_B(i);
+ setobj2s(L, ra, cl->upvals[b]->v.p);
+ vmbreak;
+ }
+ vmcase(OP_SETUPVAL) {
+ StkId ra = RA(i);
+ UpVal *uv = cl->upvals[GETARG_B(i)];
+ setobj(L, uv->v.p, s2v(ra));
+ luaC_barrier(L, uv, s2v(ra));
+ vmbreak;
+ }
+ vmcase(OP_GETTABUP) {
+ StkId ra = RA(i);
+ const TValue *slot;
+ TValue *upval = cl->upvals[GETARG_B(i)]->v.p;
+ TValue *rc = KC(i);
+ TString *key = tsvalue(rc); /* key must be a string */
+ if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) {
+ setobj2s(L, ra, slot);
+ }
+ else
+ Protect(luaV_finishget(L, upval, rc, ra, slot));
+ vmbreak;
+ }
+ vmcase(OP_GETTABLE) {
+ StkId ra = RA(i);
+ const TValue *slot;
+ TValue *rb = vRB(i);
+ TValue *rc = vRC(i);
+ lua_Unsigned n;
+ if (ttisinteger(rc) /* fast track for integers? */
+ ? (cast_void(n = ivalue(rc)), luaV_fastgeti(L, rb, n, slot))
+ : luaV_fastget(L, rb, rc, slot, luaH_get)) {
+ setobj2s(L, ra, slot);
+ }
+ else
+ Protect(luaV_finishget(L, rb, rc, ra, slot));
+ vmbreak;
+ }
+ vmcase(OP_GETI) {
+ StkId ra = RA(i);
+ const TValue *slot;
+ TValue *rb = vRB(i);
+ int c = GETARG_C(i);
+ if (luaV_fastgeti(L, rb, c, slot)) {
+ setobj2s(L, ra, slot);
+ }
+ else {
+ TValue key;
+ setivalue(&key, c);
+ Protect(luaV_finishget(L, rb, &key, ra, slot));
+ }
+ vmbreak;
+ }
+ vmcase(OP_GETFIELD) {
+ StkId ra = RA(i);
+ const TValue *slot;
+ TValue *rb = vRB(i);
+ TValue *rc = KC(i);
+ TString *key = tsvalue(rc); /* key must be a string */
+ if (luaV_fastget(L, rb, key, slot, luaH_getshortstr)) {
+ setobj2s(L, ra, slot);
+ }
+ else
+ Protect(luaV_finishget(L, rb, rc, ra, slot));
+ vmbreak;
+ }
+ vmcase(OP_SETTABUP) {
+ const TValue *slot;
+ TValue *upval = cl->upvals[GETARG_A(i)]->v.p;
+ TValue *rb = KB(i);
+ TValue *rc = RKC(i);
+ TString *key = tsvalue(rb); /* key must be a string */
+ if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) {
+ luaV_finishfastset(L, upval, slot, rc);
+ }
+ else
+ Protect(luaV_finishset(L, upval, rb, rc, slot));
+ vmbreak;
+ }
+ vmcase(OP_SETTABLE) {
+ StkId ra = RA(i);
+ const TValue *slot;
+ TValue *rb = vRB(i); /* key (table is in 'ra') */
+ TValue *rc = RKC(i); /* value */
+ lua_Unsigned n;
+ if (ttisinteger(rb) /* fast track for integers? */
+ ? (cast_void(n = ivalue(rb)), luaV_fastgeti(L, s2v(ra), n, slot))
+ : luaV_fastget(L, s2v(ra), rb, slot, luaH_get)) {
+ luaV_finishfastset(L, s2v(ra), slot, rc);
+ }
+ else
+ Protect(luaV_finishset(L, s2v(ra), rb, rc, slot));
+ vmbreak;
+ }
+ vmcase(OP_SETI) {
+ StkId ra = RA(i);
+ const TValue *slot;
+ int c = GETARG_B(i);
+ TValue *rc = RKC(i);
+ if (luaV_fastgeti(L, s2v(ra), c, slot)) {
+ luaV_finishfastset(L, s2v(ra), slot, rc);
+ }
+ else {
+ TValue key;
+ setivalue(&key, c);
+ Protect(luaV_finishset(L, s2v(ra), &key, rc, slot));
+ }
+ vmbreak;
+ }
+ vmcase(OP_SETFIELD) {
+ StkId ra = RA(i);
+ const TValue *slot;
+ TValue *rb = KB(i);
+ TValue *rc = RKC(i);
+ TString *key = tsvalue(rb); /* key must be a string */
+ if (luaV_fastget(L, s2v(ra), key, slot, luaH_getshortstr)) {
+ luaV_finishfastset(L, s2v(ra), slot, rc);
+ }
+ else
+ Protect(luaV_finishset(L, s2v(ra), rb, rc, slot));
+ vmbreak;
+ }
+ vmcase(OP_NEWTABLE) {
+ StkId ra = RA(i);
+ int b = GETARG_B(i); /* log2(hash size) + 1 */
+ int c = GETARG_C(i); /* array size */
+ Table *t;
+ if (b > 0)
+ b = 1 << (b - 1); /* size is 2^(b - 1) */
+ lua_assert((!TESTARG_k(i)) == (GETARG_Ax(*pc) == 0));
+ if (TESTARG_k(i)) /* non-zero extra argument? */
+ c += GETARG_Ax(*pc) * (MAXARG_C + 1); /* add it to size */
+ pc++; /* skip extra argument */
+ L->top.p = ra + 1; /* correct top in case of emergency GC */
+ t = luaH_new(L); /* memory allocation */
+ sethvalue2s(L, ra, t);
+ if (b != 0 || c != 0)
+ luaH_resize(L, t, c, b); /* idem */
+ checkGC(L, ra + 1);
+ vmbreak;
+ }
+ vmcase(OP_SELF) {
+ StkId ra = RA(i);
+ const TValue *slot;
+ TValue *rb = vRB(i);
+ TValue *rc = RKC(i);
+ TString *key = tsvalue(rc); /* key must be a string */
+ setobj2s(L, ra + 1, rb);
+ if (luaV_fastget(L, rb, key, slot, luaH_getstr)) {
+ setobj2s(L, ra, slot);
+ }
+ else
+ Protect(luaV_finishget(L, rb, rc, ra, slot));
+ vmbreak;
+ }
+ vmcase(OP_ADDI) {
+ op_arithI(L, l_addi, luai_numadd);
+ vmbreak;
+ }
+ vmcase(OP_ADDK) {
+ op_arithK(L, l_addi, luai_numadd);
+ vmbreak;
+ }
+ vmcase(OP_SUBK) {
+ op_arithK(L, l_subi, luai_numsub);
+ vmbreak;
+ }
+ vmcase(OP_MULK) {
+ op_arithK(L, l_muli, luai_nummul);
+ vmbreak;
+ }
+ vmcase(OP_MODK) {
+ savestate(L, ci); /* in case of division by 0 */
+ op_arithK(L, luaV_mod, luaV_modf);
+ vmbreak;
+ }
+ vmcase(OP_POWK) {
+ op_arithfK(L, luai_numpow);
+ vmbreak;
+ }
+ vmcase(OP_DIVK) {
+ op_arithfK(L, luai_numdiv);
+ vmbreak;
+ }
+ vmcase(OP_IDIVK) {
+ savestate(L, ci); /* in case of division by 0 */
+ op_arithK(L, luaV_idiv, luai_numidiv);
+ vmbreak;
+ }
+ vmcase(OP_BANDK) {
+ op_bitwiseK(L, l_band);
+ vmbreak;
+ }
+ vmcase(OP_BORK) {
+ op_bitwiseK(L, l_bor);
+ vmbreak;
+ }
+ vmcase(OP_BXORK) {
+ op_bitwiseK(L, l_bxor);
+ vmbreak;
+ }
+ vmcase(OP_SHRI) {
+ StkId ra = RA(i);
+ TValue *rb = vRB(i);
+ int ic = GETARG_sC(i);
+ lua_Integer ib;
+ if (tointegerns(rb, &ib)) {
+ pc++; setivalue(s2v(ra), luaV_shiftl(ib, -ic));
+ }
+ vmbreak;
+ }
+ vmcase(OP_SHLI) {
+ StkId ra = RA(i);
+ TValue *rb = vRB(i);
+ int ic = GETARG_sC(i);
+ lua_Integer ib;
+ if (tointegerns(rb, &ib)) {
+ pc++; setivalue(s2v(ra), luaV_shiftl(ic, ib));
+ }
+ vmbreak;
+ }
+ vmcase(OP_ADD) {
+ op_arith(L, l_addi, luai_numadd);
+ vmbreak;
+ }
+ vmcase(OP_SUB) {
+ op_arith(L, l_subi, luai_numsub);
+ vmbreak;
+ }
+ vmcase(OP_MUL) {
+ op_arith(L, l_muli, luai_nummul);
+ vmbreak;
+ }
+ vmcase(OP_MOD) {
+ savestate(L, ci); /* in case of division by 0 */
+ op_arith(L, luaV_mod, luaV_modf);
+ vmbreak;
+ }
+ vmcase(OP_POW) {
+ op_arithf(L, luai_numpow);
+ vmbreak;
+ }
+ vmcase(OP_DIV) { /* float division (always with floats) */
+ op_arithf(L, luai_numdiv);
+ vmbreak;
+ }
+ vmcase(OP_IDIV) { /* floor division */
+ savestate(L, ci); /* in case of division by 0 */
+ op_arith(L, luaV_idiv, luai_numidiv);
+ vmbreak;
+ }
+ vmcase(OP_BAND) {
+ op_bitwise(L, l_band);
+ vmbreak;
+ }
+ vmcase(OP_BOR) {
+ op_bitwise(L, l_bor);
+ vmbreak;
+ }
+ vmcase(OP_BXOR) {
+ op_bitwise(L, l_bxor);
+ vmbreak;
+ }
+ vmcase(OP_SHR) {
+ op_bitwise(L, luaV_shiftr);
+ vmbreak;
+ }
+ vmcase(OP_SHL) {
+ op_bitwise(L, luaV_shiftl);
+ vmbreak;
+ }
+ vmcase(OP_MMBIN) {
+ StkId ra = RA(i);
+ Instruction pi = *(pc - 2); /* original arith. expression */
+ TValue *rb = vRB(i);
+ TMS tm = (TMS)GETARG_C(i);
+ StkId result = RA(pi);
+ lua_assert(OP_ADD <= GET_OPCODE(pi) && GET_OPCODE(pi) <= OP_SHR);
+ Protect(luaT_trybinTM(L, s2v(ra), rb, result, tm));
+ vmbreak;
+ }
+ vmcase(OP_MMBINI) {
+ StkId ra = RA(i);
+ Instruction pi = *(pc - 2); /* original arith. expression */
+ int imm = GETARG_sB(i);
+ TMS tm = (TMS)GETARG_C(i);
+ int flip = GETARG_k(i);
+ StkId result = RA(pi);
+ Protect(luaT_trybiniTM(L, s2v(ra), imm, flip, result, tm));
+ vmbreak;
+ }
+ vmcase(OP_MMBINK) {
+ StkId ra = RA(i);
+ Instruction pi = *(pc - 2); /* original arith. expression */
+ TValue *imm = KB(i);
+ TMS tm = (TMS)GETARG_C(i);
+ int flip = GETARG_k(i);
+ StkId result = RA(pi);
+ Protect(luaT_trybinassocTM(L, s2v(ra), imm, flip, result, tm));
+ vmbreak;
+ }
+ vmcase(OP_UNM) {
+ StkId ra = RA(i);
+ TValue *rb = vRB(i);
+ lua_Number nb;
+ if (ttisinteger(rb)) {
+ lua_Integer ib = ivalue(rb);
+ setivalue(s2v(ra), intop(-, 0, ib));
+ }
+ else if (tonumberns(rb, nb)) {
+ setfltvalue(s2v(ra), luai_numunm(L, nb));
+ }
+ else
+ Protect(luaT_trybinTM(L, rb, rb, ra, TM_UNM));
+ vmbreak;
+ }
+ vmcase(OP_BNOT) {
+ StkId ra = RA(i);
+ TValue *rb = vRB(i);
+ lua_Integer ib;
+ if (tointegerns(rb, &ib)) {
+ setivalue(s2v(ra), intop(^, ~l_castS2U(0), ib));
+ }
+ else
+ Protect(luaT_trybinTM(L, rb, rb, ra, TM_BNOT));
+ vmbreak;
+ }
+ vmcase(OP_NOT) {
+ StkId ra = RA(i);
+ TValue *rb = vRB(i);
+ if (l_isfalse(rb))
+ setbtvalue(s2v(ra));
+ else
+ setbfvalue(s2v(ra));
+ vmbreak;
+ }
+ vmcase(OP_LEN) {
+ StkId ra = RA(i);
+ Protect(luaV_objlen(L, ra, vRB(i)));
+ vmbreak;
+ }
+ vmcase(OP_CONCAT) {
+ StkId ra = RA(i);
+ int n = GETARG_B(i); /* number of elements to concatenate */
+ L->top.p = ra + n; /* mark the end of concat operands */
+ ProtectNT(luaV_concat(L, n));
+ checkGC(L, L->top.p); /* 'luaV_concat' ensures correct top */
+ vmbreak;
+ }
+ vmcase(OP_CLOSE) {
+ StkId ra = RA(i);
+ Protect(luaF_close(L, ra, LUA_OK, 1));
+ vmbreak;
+ }
+ vmcase(OP_TBC) {
+ StkId ra = RA(i);
+ /* create new to-be-closed upvalue */
+ halfProtect(luaF_newtbcupval(L, ra));
+ vmbreak;
+ }
+ vmcase(OP_JMP) {
+ dojump(ci, i, 0);
+ vmbreak;
+ }
+ vmcase(OP_EQ) {
+ StkId ra = RA(i);
+ int cond;
+ TValue *rb = vRB(i);
+ Protect(cond = luaV_equalobj(L, s2v(ra), rb));
+ docondjump();
+ vmbreak;
+ }
+ vmcase(OP_LT) {
+ op_order(L, l_lti, LTnum, lessthanothers);
+ vmbreak;
+ }
+ vmcase(OP_LE) {
+ op_order(L, l_lei, LEnum, lessequalothers);
+ vmbreak;
+ }
+ vmcase(OP_EQK) {
+ StkId ra = RA(i);
+ TValue *rb = KB(i);
+ /* basic types do not use '__eq'; we can use raw equality */
+ int cond = luaV_rawequalobj(s2v(ra), rb);
+ docondjump();
+ vmbreak;
+ }
+ vmcase(OP_EQI) {
+ StkId ra = RA(i);
+ int cond;
+ int im = GETARG_sB(i);
+ if (ttisinteger(s2v(ra)))
+ cond = (ivalue(s2v(ra)) == im);
+ else if (ttisfloat(s2v(ra)))
+ cond = luai_numeq(fltvalue(s2v(ra)), cast_num(im));
+ else
+ cond = 0; /* other types cannot be equal to a number */
+ docondjump();
+ vmbreak;
+ }
+ vmcase(OP_LTI) {
+ op_orderI(L, l_lti, luai_numlt, 0, TM_LT);
+ vmbreak;
+ }
+ vmcase(OP_LEI) {
+ op_orderI(L, l_lei, luai_numle, 0, TM_LE);
+ vmbreak;
+ }
+ vmcase(OP_GTI) {
+ op_orderI(L, l_gti, luai_numgt, 1, TM_LT);
+ vmbreak;
+ }
+ vmcase(OP_GEI) {
+ op_orderI(L, l_gei, luai_numge, 1, TM_LE);
+ vmbreak;
+ }
+ vmcase(OP_TEST) {
+ StkId ra = RA(i);
+ int cond = !l_isfalse(s2v(ra));
+ docondjump();
+ vmbreak;
+ }
+ vmcase(OP_TESTSET) {
+ StkId ra = RA(i);
+ TValue *rb = vRB(i);
+ if (l_isfalse(rb) == GETARG_k(i))
+ pc++;
+ else {
+ setobj2s(L, ra, rb);
+ donextjump(ci);
+ }
+ vmbreak;
+ }
+ vmcase(OP_CALL) {
+ StkId ra = RA(i);
+ CallInfo *newci;
+ int b = GETARG_B(i);
+ int nresults = GETARG_C(i) - 1;
+ if (b != 0) /* fixed number of arguments? */
+ L->top.p = ra + b; /* top signals number of arguments */
+ /* else previous instruction set top */
+ savepc(L); /* in case of errors */
+ if ((newci = luaD_precall(L, ra, nresults)) == NULL)
+ updatetrap(ci); /* C call; nothing else to be done */
+ else { /* Lua call: run function in this same C frame */
+ ci = newci;
+ goto startfunc;
+ }
+ vmbreak;
+ }
+ vmcase(OP_TAILCALL) {
+ StkId ra = RA(i);
+ int b = GETARG_B(i); /* number of arguments + 1 (function) */
+ int n; /* number of results when calling a C function */
+ int nparams1 = GETARG_C(i);
+ /* delta is virtual 'func' - real 'func' (vararg functions) */
+ int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0;
+ if (b != 0)
+ L->top.p = ra + b;
+ else /* previous instruction set top */
+ b = cast_int(L->top.p - ra);
+ savepc(ci); /* several calls here can raise errors */
+ if (TESTARG_k(i)) {
+ luaF_closeupval(L, base); /* close upvalues from current call */
+ lua_assert(L->tbclist.p < base); /* no pending tbc variables */
+ lua_assert(base == ci->func.p + 1);
+ }
+ if ((n = luaD_pretailcall(L, ci, ra, b, delta)) < 0) /* Lua function? */
+ goto startfunc; /* execute the callee */
+ else { /* C function? */
+ ci->func.p -= delta; /* restore 'func' (if vararg) */
+ luaD_poscall(L, ci, n); /* finish caller */
+ updatetrap(ci); /* 'luaD_poscall' can change hooks */
+ goto ret; /* caller returns after the tail call */
+ }
+ }
+ vmcase(OP_RETURN) {
+ StkId ra = RA(i);
+ int n = GETARG_B(i) - 1; /* number of results */
+ int nparams1 = GETARG_C(i);
+ if (n < 0) /* not fixed? */
+ n = cast_int(L->top.p - ra); /* get what is available */
+ savepc(ci);
+ if (TESTARG_k(i)) { /* may there be open upvalues? */
+ ci->u2.nres = n; /* save number of returns */
+ if (L->top.p < ci->top.p)
+ L->top.p = ci->top.p;
+ luaF_close(L, base, CLOSEKTOP, 1);
+ updatetrap(ci);
+ updatestack(ci);
+ }
+ if (nparams1) /* vararg function? */
+ ci->func.p -= ci->u.l.nextraargs + nparams1;
+ L->top.p = ra + n; /* set call for 'luaD_poscall' */
+ luaD_poscall(L, ci, n);
+ updatetrap(ci); /* 'luaD_poscall' can change hooks */
+ goto ret;
+ }
+ vmcase(OP_RETURN0) {
+ if (l_unlikely(L->hookmask)) {
+ StkId ra = RA(i);
+ L->top.p = ra;
+ savepc(ci);
+ luaD_poscall(L, ci, 0); /* no hurry... */
+ trap = 1;
+ }
+ else { /* do the 'poscall' here */
+ int nres;
+ L->ci = ci->previous; /* back to caller */
+ L->top.p = base - 1;
+ for (nres = ci->nresults; l_unlikely(nres > 0); nres--)
+ setnilvalue(s2v(L->top.p++)); /* all results are nil */
+ }
+ goto ret;
+ }
+ vmcase(OP_RETURN1) {
+ if (l_unlikely(L->hookmask)) {
+ StkId ra = RA(i);
+ L->top.p = ra + 1;
+ savepc(ci);
+ luaD_poscall(L, ci, 1); /* no hurry... */
+ trap = 1;
+ }
+ else { /* do the 'poscall' here */
+ int nres = ci->nresults;
+ L->ci = ci->previous; /* back to caller */
+ if (nres == 0)
+ L->top.p = base - 1; /* asked for no results */
+ else {
+ StkId ra = RA(i);
+ setobjs2s(L, base - 1, ra); /* at least this result */
+ L->top.p = base;
+ for (; l_unlikely(nres > 1); nres--)
+ setnilvalue(s2v(L->top.p++)); /* complete missing results */
+ }
+ }
+ ret: /* return from a Lua function */
+ if (ci->callstatus & CIST_FRESH)
+ return; /* end this frame */
+ else {
+ ci = ci->previous;
+ goto returning; /* continue running caller in this frame */
+ }
+ }
+ vmcase(OP_FORLOOP) {
+ StkId ra = RA(i);
+ if (ttisinteger(s2v(ra + 2))) { /* integer loop? */
+ lua_Unsigned count = l_castS2U(ivalue(s2v(ra + 1)));
+ if (count > 0) { /* still more iterations? */
+ lua_Integer step = ivalue(s2v(ra + 2));
+ lua_Integer idx = ivalue(s2v(ra)); /* internal index */
+ chgivalue(s2v(ra + 1), count - 1); /* update counter */
+ idx = intop(+, idx, step); /* add step to index */
+ chgivalue(s2v(ra), idx); /* update internal index */
+ setivalue(s2v(ra + 3), idx); /* and control variable */
+ pc -= GETARG_Bx(i); /* jump back */
+ }
+ }
+ else if (floatforloop(ra)) /* float loop */
+ pc -= GETARG_Bx(i); /* jump back */
+ updatetrap(ci); /* allows a signal to break the loop */
+ vmbreak;
+ }
+ vmcase(OP_FORPREP) {
+ StkId ra = RA(i);
+ savestate(L, ci); /* in case of errors */
+ if (forprep(L, ra))
+ pc += GETARG_Bx(i) + 1; /* skip the loop */
+ vmbreak;
+ }
+ vmcase(OP_TFORPREP) {
+ StkId ra = RA(i);
+ /* create to-be-closed upvalue (if needed) */
+ halfProtect(luaF_newtbcupval(L, ra + 3));
+ pc += GETARG_Bx(i);
+ i = *(pc++); /* go to next instruction */
+ lua_assert(GET_OPCODE(i) == OP_TFORCALL && ra == RA(i));
+ goto l_tforcall;
+ }
+ vmcase(OP_TFORCALL) {
+ l_tforcall: {
+ StkId ra = RA(i);
+ /* 'ra' has the iterator function, 'ra + 1' has the state,
+ 'ra + 2' has the control variable, and 'ra + 3' has the
+ to-be-closed variable. The call will use the stack after
+ these values (starting at 'ra + 4')
+ */
+ /* push function, state, and control variable */
+ memcpy(ra + 4, ra, 3 * sizeof(*ra));
+ L->top.p = ra + 4 + 3;
+ ProtectNT(luaD_call(L, ra + 4, GETARG_C(i))); /* do the call */
+ updatestack(ci); /* stack may have changed */
+ i = *(pc++); /* go to next instruction */
+ lua_assert(GET_OPCODE(i) == OP_TFORLOOP && ra == RA(i));
+ goto l_tforloop;
+ }}
+ vmcase(OP_TFORLOOP) {
+ l_tforloop: {
+ StkId ra = RA(i);
+ if (!ttisnil(s2v(ra + 4))) { /* continue loop? */
+ setobjs2s(L, ra + 2, ra + 4); /* save control variable */
+ pc -= GETARG_Bx(i); /* jump back */
+ }
+ vmbreak;
+ }}
+ vmcase(OP_SETLIST) {
+ StkId ra = RA(i);
+ int n = GETARG_B(i);
+ unsigned int last = GETARG_C(i);
+ Table *h = hvalue(s2v(ra));
+ if (n == 0)
+ n = cast_int(L->top.p - ra) - 1; /* get up to the top */
+ else
+ L->top.p = ci->top.p; /* correct top in case of emergency GC */
+ last += n;
+ if (TESTARG_k(i)) {
+ last += GETARG_Ax(*pc) * (MAXARG_C + 1);
+ pc++;
+ }
+ if (last > luaH_realasize(h)) /* needs more space? */
+ luaH_resizearray(L, h, last); /* preallocate it at once */
+ for (; n > 0; n--) {
+ TValue *val = s2v(ra + n);
+ setobj2t(L, &h->array[last - 1], val);
+ last--;
+ luaC_barrierback(L, obj2gco(h), val);
+ }
+ vmbreak;
+ }
+ vmcase(OP_CLOSURE) {
+ StkId ra = RA(i);
+ Proto *p = cl->p->p[GETARG_Bx(i)];
+ halfProtect(pushclosure(L, p, cl->upvals, base, ra));
+ checkGC(L, ra + 1);
+ vmbreak;
+ }
+ vmcase(OP_VARARG) {
+ StkId ra = RA(i);
+ int n = GETARG_C(i) - 1; /* required results */
+ Protect(luaT_getvarargs(L, ci, ra, n));
+ vmbreak;
+ }
+ vmcase(OP_VARARGPREP) {
+ ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p));
+ if (l_unlikely(trap)) { /* previous "Protect" updated trap */
+ luaD_hookcall(L, ci);
+ L->oldpc = 1; /* next opcode will be seen as a "new" line */
+ }
+ updatebase(ci); /* function has new base after adjustment */
+ vmbreak;
+ }
+ vmcase(OP_EXTRAARG) {
+ lua_assert(0);
+ vmbreak;
+ }
+ }
+ }
+}
+
+/* }================================================================== */
diff --git a/src/libs/3rdparty/lua/src/lvm.h b/src/libs/3rdparty/lua/src/lvm.h
new file mode 100644
index 0000000000..dba1ad2770
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lvm.h
@@ -0,0 +1,141 @@
+/*
+** $Id: lvm.h $
+** Lua virtual machine
+** See Copyright Notice in lua.h
+*/
+
+#ifndef lvm_h
+#define lvm_h
+
+
+#include "ldo.h"
+#include "lobject.h"
+#include "ltm.h"
+
+
+#if !defined(LUA_NOCVTN2S)
+#define cvt2str(o) ttisnumber(o)
+#else
+#define cvt2str(o) 0 /* no conversion from numbers to strings */
+#endif
+
+
+#if !defined(LUA_NOCVTS2N)
+#define cvt2num(o) ttisstring(o)
+#else
+#define cvt2num(o) 0 /* no conversion from strings to numbers */
+#endif
+
+
+/*
+** You can define LUA_FLOORN2I if you want to convert floats to integers
+** by flooring them (instead of raising an error if they are not
+** integral values)
+*/
+#if !defined(LUA_FLOORN2I)
+#define LUA_FLOORN2I F2Ieq
+#endif
+
+
+/*
+** Rounding modes for float->integer coercion
+ */
+typedef enum {
+ F2Ieq, /* no rounding; accepts only integral values */
+ F2Ifloor, /* takes the floor of the number */
+ F2Iceil /* takes the ceil of the number */
+} F2Imod;
+
+
+/* convert an object to a float (including string coercion) */
+#define tonumber(o,n) \
+ (ttisfloat(o) ? (*(n) = fltvalue(o), 1) : luaV_tonumber_(o,n))
+
+
+/* convert an object to a float (without string coercion) */
+#define tonumberns(o,n) \
+ (ttisfloat(o) ? ((n) = fltvalue(o), 1) : \
+ (ttisinteger(o) ? ((n) = cast_num(ivalue(o)), 1) : 0))
+
+
+/* convert an object to an integer (including string coercion) */
+#define tointeger(o,i) \
+ (l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \
+ : luaV_tointeger(o,i,LUA_FLOORN2I))
+
+
+/* convert an object to an integer (without string coercion) */
+#define tointegerns(o,i) \
+ (l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \
+ : luaV_tointegerns(o,i,LUA_FLOORN2I))
+
+
+#define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2))
+
+#define luaV_rawequalobj(t1,t2) luaV_equalobj(NULL,t1,t2)
+
+
+/*
+** fast track for 'gettable': if 't' is a table and 't[k]' is present,
+** return 1 with 'slot' pointing to 't[k]' (position of final result).
+** Otherwise, return 0 (meaning it will have to check metamethod)
+** with 'slot' pointing to an empty 't[k]' (if 't' is a table) or NULL
+** (otherwise). 'f' is the raw get function to use.
+*/
+#define luaV_fastget(L,t,k,slot,f) \
+ (!ttistable(t) \
+ ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \
+ : (slot = f(hvalue(t), k), /* else, do raw access */ \
+ !isempty(slot))) /* result not empty? */
+
+
+/*
+** Special case of 'luaV_fastget' for integers, inlining the fast case
+** of 'luaH_getint'.
+*/
+#define luaV_fastgeti(L,t,k,slot) \
+ (!ttistable(t) \
+ ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \
+ : (slot = (l_castS2U(k) - 1u < hvalue(t)->alimit) \
+ ? &hvalue(t)->array[k - 1] : luaH_getint(hvalue(t), k), \
+ !isempty(slot))) /* result not empty? */
+
+
+/*
+** Finish a fast set operation (when fast get succeeds). In that case,
+** 'slot' points to the place to put the value.
+*/
+#define luaV_finishfastset(L,t,slot,v) \
+ { setobj2t(L, cast(TValue *,slot), v); \
+ luaC_barrierback(L, gcvalue(t), v); }
+
+
+/*
+** Shift right is the same as shift left with a negative 'y'
+*/
+#define luaV_shiftr(x,y) luaV_shiftl(x,intop(-, 0, y))
+
+
+
+LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2);
+LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r);
+LUAI_FUNC int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r);
+LUAI_FUNC int luaV_tonumber_ (const TValue *obj, lua_Number *n);
+LUAI_FUNC int luaV_tointeger (const TValue *obj, lua_Integer *p, F2Imod mode);
+LUAI_FUNC int luaV_tointegerns (const TValue *obj, lua_Integer *p,
+ F2Imod mode);
+LUAI_FUNC int luaV_flttointeger (lua_Number n, lua_Integer *p, F2Imod mode);
+LUAI_FUNC void luaV_finishget (lua_State *L, const TValue *t, TValue *key,
+ StkId val, const TValue *slot);
+LUAI_FUNC void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
+ TValue *val, const TValue *slot);
+LUAI_FUNC void luaV_finishOp (lua_State *L);
+LUAI_FUNC void luaV_execute (lua_State *L, CallInfo *ci);
+LUAI_FUNC void luaV_concat (lua_State *L, int total);
+LUAI_FUNC lua_Integer luaV_idiv (lua_State *L, lua_Integer x, lua_Integer y);
+LUAI_FUNC lua_Integer luaV_mod (lua_State *L, lua_Integer x, lua_Integer y);
+LUAI_FUNC lua_Number luaV_modf (lua_State *L, lua_Number x, lua_Number y);
+LUAI_FUNC lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y);
+LUAI_FUNC void luaV_objlen (lua_State *L, StkId ra, const TValue *rb);
+
+#endif
diff --git a/src/libs/3rdparty/lua/src/lzio.c b/src/libs/3rdparty/lua/src/lzio.c
new file mode 100644
index 0000000000..cd0a02d5f9
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lzio.c
@@ -0,0 +1,68 @@
+/*
+** $Id: lzio.c $
+** Buffered streams
+** See Copyright Notice in lua.h
+*/
+
+#define lzio_c
+#define LUA_CORE
+
+#include "lprefix.h"
+
+
+#include <string.h>
+
+#include "lua.h"
+
+#include "llimits.h"
+#include "lmem.h"
+#include "lstate.h"
+#include "lzio.h"
+
+
+int luaZ_fill (ZIO *z) {
+ size_t size;
+ lua_State *L = z->L;
+ const char *buff;
+ lua_unlock(L);
+ buff = z->reader(L, z->data, &size);
+ lua_lock(L);
+ if (buff == NULL || size == 0)
+ return EOZ;
+ z->n = size - 1; /* discount char being returned */
+ z->p = buff;
+ return cast_uchar(*(z->p++));
+}
+
+
+void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) {
+ z->L = L;
+ z->reader = reader;
+ z->data = data;
+ z->n = 0;
+ z->p = NULL;
+}
+
+
+/* --------------------------------------------------------------- read --- */
+size_t luaZ_read (ZIO *z, void *b, size_t n) {
+ while (n) {
+ size_t m;
+ if (z->n == 0) { /* no bytes in buffer? */
+ if (luaZ_fill(z) == EOZ) /* try to read more */
+ return n; /* no more input; return number of missing bytes */
+ else {
+ z->n++; /* luaZ_fill consumed first byte; put it back */
+ z->p--;
+ }
+ }
+ m = (n <= z->n) ? n : z->n; /* min. between n and z->n */
+ memcpy(b, z->p, m);
+ z->n -= m;
+ z->p += m;
+ b = (char *)b + m;
+ n -= m;
+ }
+ return 0;
+}
+
diff --git a/src/libs/3rdparty/lua/src/lzio.h b/src/libs/3rdparty/lua/src/lzio.h
new file mode 100644
index 0000000000..38f397fd28
--- /dev/null
+++ b/src/libs/3rdparty/lua/src/lzio.h
@@ -0,0 +1,66 @@
+/*
+** $Id: lzio.h $
+** Buffered streams
+** See Copyright Notice in lua.h
+*/
+
+
+#ifndef lzio_h
+#define lzio_h
+
+#include "lua.h"
+
+#include "lmem.h"
+
+
+#define EOZ (-1) /* end of stream */
+
+typedef struct Zio ZIO;
+
+#define zgetc(z) (((z)->n--)>0 ? cast_uchar(*(z)->p++) : luaZ_fill(z))
+
+
+typedef struct Mbuffer {
+ char *buffer;
+ size_t n;
+ size_t buffsize;
+} Mbuffer;
+
+#define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0)
+
+#define luaZ_buffer(buff) ((buff)->buffer)
+#define luaZ_sizebuffer(buff) ((buff)->buffsize)
+#define luaZ_bufflen(buff) ((buff)->n)
+
+#define luaZ_buffremove(buff,i) ((buff)->n -= (i))
+#define luaZ_resetbuffer(buff) ((buff)->n = 0)
+
+
+#define luaZ_resizebuffer(L, buff, size) \
+ ((buff)->buffer = luaM_reallocvchar(L, (buff)->buffer, \
+ (buff)->buffsize, size), \
+ (buff)->buffsize = size)
+
+#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0)
+
+
+LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader,
+ void *data);
+LUAI_FUNC size_t luaZ_read (ZIO* z, void *b, size_t n); /* read next n bytes */
+
+
+
+/* --------- Private Part ------------------ */
+
+struct Zio {
+ size_t n; /* bytes still unread */
+ const char *p; /* current position in buffer */
+ lua_Reader reader; /* reader function */
+ void *data; /* additional data */
+ lua_State *L; /* Lua state (for reader) */
+};
+
+
+LUAI_FUNC int luaZ_fill (ZIO *z);
+
+#endif
diff --git a/src/libs/3rdparty/sol2/CMakeLists.txt b/src/libs/3rdparty/sol2/CMakeLists.txt
new file mode 100644
index 0000000000..1afdb1bdbb
--- /dev/null
+++ b/src/libs/3rdparty/sol2/CMakeLists.txt
@@ -0,0 +1,12 @@
+add_library(sol2 INTERFACE)
+
+target_include_directories(sol2 INTERFACE
+ $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+ $<INSTALL_INTERFACE:include>
+)
+
+install(TARGETS sol2 EXPORT QtCreator)
+
+qtc_add_public_header(include/sol/sol.hpp)
+qtc_add_public_header(include/sol/config.hpp)
+qtc_add_public_header(include/sol/forward.hpp)
diff --git a/src/libs/3rdparty/sol2/LICENSE.txt b/src/libs/3rdparty/sol2/LICENSE.txt
new file mode 100644
index 0000000000..5813440548
--- /dev/null
+++ b/src/libs/3rdparty/sol2/LICENSE.txt
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-2022 Rapptz, ThePhD, and contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/src/libs/3rdparty/sol2/include/sol/config.hpp b/src/libs/3rdparty/sol2/include/sol/config.hpp
new file mode 100644
index 0000000000..36ef3fe47e
--- /dev/null
+++ b/src/libs/3rdparty/sol2/include/sol/config.hpp
@@ -0,0 +1,57 @@
+// The MIT License (MIT)
+
+// Copyright (c) 2013-2020 Rapptz, ThePhD and contributors
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// This file was generated with a script.
+// Generated 2024-01-13 14:25:56.570832 UTC
+// This header was generated with sol v3.3.1 (revision 9c882a28)
+// https://github.com/ThePhD/sol2
+
+#ifndef SOL_SINGLE_SOL_CONFIG_HPP
+#define SOL_SINGLE_SOL_CONFIG_HPP
+
+// beginning of sol/config.hpp
+
+/* Base, empty configuration file!
+
+ To override, place a file in your include paths of the form:
+
+. (your include path here)
+| sol (directory, or equivalent)
+ | config.hpp (your config.hpp file)
+
+ So that when sol2 includes the file
+
+#include <sol/config.hpp>
+
+ it gives you the configuration values you desire. Configuration values can be
+seen in the safety.rst of the doc/src, or at
+https://sol2.readthedocs.io/en/latest/safety.html ! You can also pass them through
+the build system, or the command line options of your compiler.
+
+*/
+
+#define SOL_SAFE_FUNCTIONS 1
+#define SOL_SAFE_USERTYPE 1
+#define SOL_NO_NIL 1
+
+// end of sol/config.hpp
+
+#endif // SOL_SINGLE_SOL_CONFIG_HPP
diff --git a/src/libs/3rdparty/sol2/include/sol/forward.hpp b/src/libs/3rdparty/sol2/include/sol/forward.hpp
new file mode 100644
index 0000000000..83fba4d254
--- /dev/null
+++ b/src/libs/3rdparty/sol2/include/sol/forward.hpp
@@ -0,0 +1,1340 @@
+// The MIT License (MIT)
+
+// Copyright (c) 2013-2020 Rapptz, ThePhD and contributors
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// This file was generated with a script.
+// Generated 2024-01-13 14:25:56.569197 UTC
+// This header was generated with sol v3.3.1 (revision 9c882a28)
+// https://github.com/ThePhD/sol2
+
+#ifndef SOL_SINGLE_INCLUDE_SOL_FORWARD_HPP
+#define SOL_SINGLE_INCLUDE_SOL_FORWARD_HPP
+
+// beginning of sol/forward.hpp
+
+#ifndef SOL_FORWARD_HPP
+#define SOL_FORWARD_HPP
+
+// beginning of sol/version.hpp
+
+#include "config.hpp"
+
+#define SOL_VERSION_MAJOR 3
+#define SOL_VERSION_MINOR 2
+#define SOL_VERSION_PATCH 3
+#define SOL_VERSION_STRING "3.2.3"
+#define SOL_VERSION ((SOL_VERSION_MAJOR * 100000) + (SOL_VERSION_MINOR * 100) + (SOL_VERSION_PATCH))
+
+#define SOL_TOKEN_TO_STRING_POST_EXPANSION_I_(_TOKEN) #_TOKEN
+#define SOL_TOKEN_TO_STRING_I_(_TOKEN) SOL_TOKEN_TO_STRING_POST_EXPANSION_I_(_TOKEN)
+
+#define SOL_CONCAT_TOKENS_POST_EXPANSION_I_(_LEFT, _RIGHT) _LEFT##_RIGHT
+#define SOL_CONCAT_TOKENS_I_(_LEFT, _RIGHT) SOL_CONCAT_TOKENS_POST_EXPANSION_I_(_LEFT, _RIGHT)
+
+#define SOL_RAW_IS_ON(OP_SYMBOL) ((3 OP_SYMBOL 3) != 0)
+#define SOL_RAW_IS_OFF(OP_SYMBOL) ((3 OP_SYMBOL 3) == 0)
+#define SOL_RAW_IS_DEFAULT_ON(OP_SYMBOL) ((3 OP_SYMBOL 3) > 3)
+#define SOL_RAW_IS_DEFAULT_OFF(OP_SYMBOL) ((3 OP_SYMBOL 3 OP_SYMBOL 3) < 0)
+
+#define SOL_IS_ON(OP_SYMBOL) SOL_RAW_IS_ON(OP_SYMBOL ## _I_)
+#define SOL_IS_OFF(OP_SYMBOL) SOL_RAW_IS_OFF(OP_SYMBOL ## _I_)
+#define SOL_IS_DEFAULT_ON(OP_SYMBOL) SOL_RAW_IS_DEFAULT_ON(OP_SYMBOL ## _I_)
+#define SOL_IS_DEFAULT_OFF(OP_SYMBOL) SOL_RAW_IS_DEFAULT_OFF(OP_SYMBOL ## _I_)
+
+#define SOL_ON |
+#define SOL_OFF ^
+#define SOL_DEFAULT_ON +
+#define SOL_DEFAULT_OFF -
+
+#if defined(SOL_BUILD_CXX_MODE)
+ #if (SOL_BUILD_CXX_MODE != 0)
+ #define SOL_BUILD_CXX_MODE_I_ SOL_ON
+ #else
+ #define SOL_BUILD_CXX_MODE_I_ SOL_OFF
+ #endif
+#elif defined(__cplusplus)
+ #define SOL_BUILD_CXX_MODE_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_BUILD_CXX_MODE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_BUILD_C_MODE)
+ #if (SOL_BUILD_C_MODE != 0)
+ #define SOL_BUILD_C_MODE_I_ SOL_ON
+ #else
+ #define SOL_BUILD_C_MODE_I_ SOL_OFF
+ #endif
+#elif defined(__STDC__)
+ #define SOL_BUILD_C_MODE_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_BUILD_C_MODE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if SOL_IS_ON(SOL_BUILD_C_MODE)
+ #include <stddef.h>
+ #include <stdint.h>
+ #include <limits.h>
+#else
+ #include <cstddef>
+ #include <cstdint>
+ #include <climits>
+#endif
+
+#if defined(SOL_HAS_BUILTIN)
+ #define SOL_HAS_BUILTIN_I_(...) SOL_HAS_BUILTIN(__VA_ARGS__)
+#elif defined(__has_builtin)
+ #define SOL_HAS_BUILTIN_I_(...) __has_builtin(__VA_ARGS__)
+#else
+ #define SOL_HAS_BUILTIN_I_(...) 0
+#endif
+
+#if defined(SOL_COMPILER_VCXX)
+ #if defined(SOL_COMPILER_VCXX != 0)
+ #define SOL_COMPILER_VCXX_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_VCXX_I_ SOL_OFF
+ #endif
+#elif defined(_MSC_VER)
+ #define SOL_COMPILER_VCXX_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_COMPILER_VCXX_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_COMPILER_GCC)
+ #if defined(SOL_COMPILER_GCC != 0)
+ #define SOL_COMPILER_GCC_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_GCC_I_ SOL_OFF
+ #endif
+#elif defined(__GNUC__)
+ #define SOL_COMPILER_GCC_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_COMPILER_GCC_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_COMPILER_CLANG)
+ #if defined(SOL_COMPILER_CLANG != 0)
+ #define SOL_COMPILER_CLANG_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_CLANG_I_ SOL_OFF
+ #endif
+#elif defined(__clang__)
+ #define SOL_COMPILER_CLANG_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_COMPILER_CLANG_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_COMPILER_EDG)
+ #if defined(SOL_COMPILER_EDG != 0)
+ #define SOL_COMPILER_EDG_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_EDG_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_COMPILER_EDG_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_COMPILER_MINGW)
+ #if (SOL_COMPILER_MINGW != 0)
+ #define SOL_COMPILER_MINGW_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_MINGW_I_ SOL_OFF
+ #endif
+#elif defined(__MINGW32__)
+ #define SOL_COMPILER_MINGW_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_COMPILER_MINGW_I_ SOL_DEFAULT_OFF
+#endif
+
+#if SIZE_MAX <= 0xFFFFULL
+ #define SOL_PLATFORM_X16_I_ SOL_ON
+ #define SOL_PLATFORM_X86_I_ SOL_OFF
+ #define SOL_PLATFORM_X64_I_ SOL_OFF
+#elif SIZE_MAX <= 0xFFFFFFFFULL
+ #define SOL_PLATFORM_X16_I_ SOL_OFF
+ #define SOL_PLATFORM_X86_I_ SOL_ON
+ #define SOL_PLATFORM_X64_I_ SOL_OFF
+#else
+ #define SOL_PLATFORM_X16_I_ SOL_OFF
+ #define SOL_PLATFORM_X86_I_ SOL_OFF
+ #define SOL_PLATFORM_X64_I_ SOL_ON
+#endif
+
+#define SOL_PLATFORM_ARM32_I_ SOL_OFF
+#define SOL_PLATFORM_ARM64_I_ SOL_OFF
+
+#if defined(SOL_PLATFORM_WINDOWS)
+ #if (SOL_PLATFORM_WINDOWS != 0)
+ #define SOL_PLATFORM_WINDOWS_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_WINDOWS_I_ SOL_OFF
+ #endif
+#elif defined(_WIN32)
+ #define SOL_PLATFORM_WINDOWS_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_PLATFORM_WINDOWS_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_PLATFORM_CYGWIN)
+ #if (SOL_PLATFORM_CYGWIN != 0)
+ #define SOL_PLATFORM_CYGWIN_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_CYGWIN_I_ SOL_ON
+ #endif
+#elif defined(__CYGWIN__)
+ #define SOL_PLATFORM_CYGWIN_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_PLATFORM_CYGWIN_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_PLATFORM_APPLE)
+ #if (SOL_PLATFORM_APPLE != 0)
+ #define SOL_PLATFORM_APPLE_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_APPLE_I_ SOL_OFF
+ #endif
+#elif defined(__APPLE__)
+ #define SOL_PLATFORM_APPLE_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_PLATFORM_APPLE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_PLATFORM_UNIX)
+ #if (SOL_PLATFORM_UNIX != 0)
+ #define SOL_PLATFORM_UNIXLIKE_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_UNIXLIKE_I_ SOL_OFF
+ #endif
+#elif defined(__unix__)
+ #define SOL_PLATFORM_UNIXLIKE_I_ SOL_DEFAUKT_ON
+#else
+ #define SOL_PLATFORM_UNIXLIKE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_PLATFORM_LINUX)
+ #if (SOL_PLATFORM_LINUX != 0)
+ #define SOL_PLATFORM_LINUXLIKE_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_LINUXLIKE_I_ SOL_OFF
+ #endif
+#elif defined(__LINUX__)
+ #define SOL_PLATFORM_LINUXLIKE_I_ SOL_DEFAUKT_ON
+#else
+ #define SOL_PLATFORM_LINUXLIKE_I_ SOL_DEFAULT_OFF
+#endif
+
+#define SOL_PLATFORM_APPLE_IPHONE_I_ SOL_OFF
+#define SOL_PLATFORM_BSDLIKE_I_ SOL_OFF
+
+#if defined(SOL_IN_DEBUG_DETECTED)
+ #if SOL_IN_DEBUG_DETECTED != 0
+ #define SOL_DEBUG_BUILD_I_ SOL_ON
+ #else
+ #define SOL_DEBUG_BUILD_I_ SOL_OFF
+ #endif
+#elif !defined(NDEBUG)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX) && defined(_DEBUG)
+ #define SOL_DEBUG_BUILD_I_ SOL_ON
+ #elif (SOL_IS_ON(SOL_COMPILER_CLANG) || SOL_IS_ON(SOL_COMPILER_GCC)) && !defined(__OPTIMIZE__)
+ #define SOL_DEBUG_BUILD_I_ SOL_ON
+ #else
+ #define SOL_DEBUG_BUILD_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_DEBUG_BUILD_I_ SOL_DEFAULT_OFF
+#endif // We are in a debug mode of some sort
+
+#if defined(SOL_NO_EXCEPTIONS)
+ #if (SOL_NO_EXCEPTIONS != 0)
+ #define SOL_EXCEPTIONS_I_ SOL_OFF
+ #else
+ #define SOL_EXCEPTIONS_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if !defined(_CPPUNWIND)
+ #define SOL_EXCEPTIONS_I_ SOL_OFF
+ #else
+ #define SOL_EXCEPTIONS_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_CLANG) || SOL_IS_ON(SOL_COMPILER_GCC)
+ #if !defined(__EXCEPTIONS)
+ #define SOL_EXCEPTIONS_I_ SOL_OFF
+ #else
+ #define SOL_EXCEPTIONS_I_ SOL_ON
+ #endif
+#else
+ #define SOL_EXCEPTIONS_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_NO_RTTI)
+ #if (SOL_NO_RTTI != 0)
+ #define SOL_RTTI_I_ SOL_OFF
+ #else
+ #define SOL_RTTI_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if !defined(_CPPRTTI)
+ #define SOL_RTTI_I_ SOL_OFF
+ #else
+ #define SOL_RTTI_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_CLANG) || SOL_IS_ON(SOL_COMPILER_GCC)
+ #if !defined(__GXX_RTTI)
+ #define SOL_RTTI_I_ SOL_OFF
+ #else
+ #define SOL_RTTI_I_ SOL_ON
+ #endif
+#else
+ #define SOL_RTTI_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_NO_THREAD_LOCAL)
+ #if SOL_NO_THREAD_LOCAL != 0
+ #define SOL_USE_THREAD_LOCAL_I_ SOL_OFF
+ #else
+ #define SOL_USE_THREAD_LOCAL_I_ SOL_ON
+ #endif
+#else
+ #define SOL_USE_THREAD_LOCAL_I_ SOL_DEFAULT_ON
+#endif // thread_local keyword is bjorked on some platforms
+
+#if defined(SOL_ALL_SAFETIES_ON)
+ #if SOL_ALL_SAFETIES_ON != 0
+ #define SOL_ALL_SAFETIES_ON_I_ SOL_ON
+ #else
+ #define SOL_ALL_SAFETIES_ON_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_ALL_SAFETIES_ON_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_SAFE_GETTER)
+ #if SOL_SAFE_GETTER != 0
+ #define SOL_SAFE_GETTER_I_ SOL_ON
+ #else
+ #define SOL_SAFE_GETTER_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_GETTER_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_GETTER_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_GETTER_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_USERTYPE)
+ #if SOL_SAFE_USERTYPE != 0
+ #define SOL_SAFE_USERTYPE_I_ SOL_ON
+ #else
+ #define SOL_SAFE_USERTYPE_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_USERTYPE_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_USERTYPE_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_USERTYPE_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_REFERENCES)
+ #if SOL_SAFE_REFERENCES != 0
+ #define SOL_SAFE_REFERENCES_I_ SOL_ON
+ #else
+ #define SOL_SAFE_REFERENCES_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_REFERENCES_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_REFERENCES_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_REFERENCES_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_FUNCTIONS)
+ #if SOL_SAFE_FUNCTIONS != 0
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON
+ #else
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_OFF
+ #endif
+#elif defined (SOL_SAFE_FUNCTION_OBJECTS)
+ #if SOL_SAFE_FUNCTION_OBJECTS != 0
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON
+ #else
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_FUNCTION_CALLS)
+ #if SOL_SAFE_FUNCTION_CALLS != 0
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_ON
+ #else
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_PROXIES)
+ #if SOL_SAFE_PROXIES != 0
+ #define SOL_SAFE_PROXIES_I_ SOL_ON
+ #else
+ #define SOL_SAFE_PROXIES_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_PROXIES_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_PROXIES_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_PROXIES_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_NUMERICS)
+ #if SOL_SAFE_NUMERICS != 0
+ #define SOL_SAFE_NUMERICS_I_ SOL_ON
+ #else
+ #define SOL_SAFE_NUMERICS_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_NUMERICS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_NUMERICS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_NUMERICS_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_ALL_INTEGER_VALUES_FIT)
+ #if (SOL_ALL_INTEGER_VALUES_FIT != 0)
+ #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_ON
+ #else
+ #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_OFF
+ #endif
+#elif !SOL_IS_DEFAULT_OFF(SOL_SAFE_NUMERICS) && SOL_IS_OFF(SOL_SAFE_NUMERICS)
+ // if numerics is intentionally turned off, flip this on
+ #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_DEFAULT_ON
+#else
+ // default to off
+ #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_SAFE_STACK_CHECK)
+ #if SOL_SAFE_STACK_CHECK != 0
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_ON
+ #else
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_NO_CHECK_NUMBER_PRECISION)
+ #if SOL_NO_CHECK_NUMBER_PRECISION != 0
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_OFF
+ #else
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON
+ #endif
+#elif defined(SOL_NO_CHECKING_NUMBER_PRECISION)
+ #if SOL_NO_CHECKING_NUMBER_PRECISION != 0
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_OFF
+ #else
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_SAFE_NUMERICS)
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_STRINGS_ARE_NUMBERS)
+ #if (SOL_STRINGS_ARE_NUMBERS != 0)
+ #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_ON
+ #else
+ #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_ENABLE_INTEROP)
+ #if SOL_ENABLE_INTEROP != 0
+ #define SOL_USE_INTEROP_I_ SOL_ON
+ #else
+ #define SOL_USE_INTEROP_I_ SOL_OFF
+ #endif
+#elif defined(SOL_USE_INTEROP)
+ #if SOL_USE_INTEROP != 0
+ #define SOL_USE_INTEROP_I_ SOL_ON
+ #else
+ #define SOL_USE_INTEROP_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USE_INTEROP_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_NO_NIL)
+ #if (SOL_NO_NIL != 0)
+ #define SOL_NIL_I_ SOL_OFF
+ #else
+ #define SOL_NIL_I_ SOL_ON
+ #endif
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) || defined(__OBJC__) || defined(nil)
+ #define SOL_NIL_I_ SOL_DEFAULT_OFF
+#else
+ #define SOL_NIL_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_USERTYPE_TYPE_BINDING_INFO)
+ #if (SOL_USERTYPE_TYPE_BINDING_INFO != 0)
+ #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_ON
+ #else
+ #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_DEFAULT_ON
+#endif // We should generate a my_type.__type table with lots of class information for usertypes
+
+#if defined(SOL_AUTOMAGICAL_TYPES_BY_DEFAULT)
+ #if (SOL_AUTOMAGICAL_TYPES_BY_DEFAULT != 0)
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_ON
+ #else
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_OFF
+ #endif
+#elif defined(SOL_DEFAULT_AUTOMAGICAL_USERTYPES)
+ #if (SOL_DEFAULT_AUTOMAGICAL_USERTYPES != 0)
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_ON
+ #else
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_DEFAULT_ON
+#endif // make is_automagical on/off by default
+
+#if defined(SOL_STD_VARIANT)
+ #if (SOL_STD_VARIANT != 0)
+ #define SOL_STD_VARIANT_I_ SOL_ON
+ #else
+ #define SOL_STD_VARIANT_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_COMPILER_CLANG) && SOL_IS_ON(SOL_PLATFORM_APPLE)
+ #if defined(__has_include)
+ #if __has_include(<variant>)
+ #define SOL_STD_VARIANT_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_STD_VARIANT_I_ SOL_DEFAULT_OFF
+ #endif
+ #else
+ #define SOL_STD_VARIANT_I_ SOL_DEFAULT_OFF
+ #endif
+ #else
+ #define SOL_STD_VARIANT_I_ SOL_DEFAULT_ON
+ #endif
+#endif // make is_automagical on/off by default
+
+#if defined(SOL_NOEXCEPT_FUNCTION_TYPE)
+ #if (SOL_NOEXCEPT_FUNCTION_TYPE != 0)
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_ON
+ #else
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_OFF
+ #endif
+#else
+ #if defined(__cpp_noexcept_function_type)
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_COMPILER_VCXX) && (defined(_MSVC_LANG) && (_MSVC_LANG < 201403L))
+ // There is a bug in the VC++ compiler??
+ // on /std:c++latest under x86 conditions (VS 15.5.2),
+ // compiler errors are tossed for noexcept markings being on function types
+ // that are identical in every other way to their non-noexcept marked types function types...
+ // VS 2019: There is absolutely a bug.
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_OFF
+ #else
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_DEFAULT_ON
+ #endif
+#endif // noexcept is part of a function's type
+
+#if defined(SOL_STACK_STRING_OPTIMIZATION_SIZE) && SOL_STACK_STRING_OPTIMIZATION_SIZE > 0
+ #define SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_ SOL_STACK_STRING_OPTIMIZATION_SIZE
+#else
+ #define SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_ 1024
+#endif
+
+#if defined(SOL_ID_SIZE) && SOL_ID_SIZE > 0
+ #define SOL_ID_SIZE_I_ SOL_ID_SIZE
+#else
+ #define SOL_ID_SIZE_I_ 512
+#endif
+
+#if defined(LUA_IDSIZE) && LUA_IDSIZE > 0
+ #define SOL_FILE_ID_SIZE_I_ LUA_IDSIZE
+#elif defined(SOL_ID_SIZE) && SOL_ID_SIZE > 0
+ #define SOL_FILE_ID_SIZE_I_ SOL_FILE_ID_SIZE
+#else
+ #define SOL_FILE_ID_SIZE_I_ 2048
+#endif
+
+#if defined(SOL_PRINT_ERRORS)
+ #if (SOL_PRINT_ERRORS != 0)
+ #define SOL_PRINT_ERRORS_I_ SOL_ON
+ #else
+ #define SOL_PRINT_ERRORS_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_PRINT_ERRORS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_PRINT_ERRORS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_PRINT_ERRORS_I_ SOL_OFF
+ #endif
+#endif
+
+#if defined(SOL_DEFAULT_PASS_ON_ERROR)
+ #if (SOL_DEFAULT_PASS_ON_ERROR != 0)
+ #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_ON
+ #else
+ #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_USING_CXX_LUA)
+ #if (SOL_USING_CXX_LUA != 0)
+ #define SOL_USING_CXX_LUA_I_ SOL_ON
+ #else
+ #define SOL_USING_CXX_LUA_I_ SOL_OFF
+ #endif
+#elif defined(SOL_USE_CXX_LUA)
+ // alternative spelling
+ #if (SOL_USE_CXX_LUA != 0)
+ #define SOL_USING_CXX_LUA_I_ SOL_ON
+ #else
+ #define SOL_USING_CXX_LUA_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USING_CXX_LUA_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_USING_CXX_LUAJIT)
+ #if (SOL_USING_CXX_LUAJIT != 0)
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_ON
+ #else
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_OFF
+ #endif
+#elif defined(SOL_USE_CXX_LUAJIT)
+ #if (SOL_USE_CXX_LUAJIT != 0)
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_ON
+ #else
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_NO_LUA_HPP)
+ #if (SOL_NO_LUA_HPP != 0)
+ #define SOL_USE_LUA_HPP_I_ SOL_OFF
+ #else
+ #define SOL_USE_LUA_HPP_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_USING_CXX_LUA)
+ #define SOL_USE_LUA_HPP_I_ SOL_OFF
+#elif defined(__has_include)
+ #if __has_include(<lua.hpp>)
+ #define SOL_USE_LUA_HPP_I_ SOL_ON
+ #else
+ #define SOL_USE_LUA_HPP_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USE_LUA_HPP_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_CONTAINERS_START)
+ #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINERS_START
+#elif defined(SOL_CONTAINERS_START_INDEX)
+ #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINERS_START_INDEX
+#elif defined(SOL_CONTAINER_START_INDEX)
+ #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINER_START_INDEX
+#else
+ #define SOL_CONTAINER_START_INDEX_I_ 1
+#endif
+
+#if defined (SOL_NO_MEMORY_ALIGNMENT)
+ #if (SOL_NO_MEMORY_ALIGNMENT != 0)
+ #define SOL_ALIGN_MEMORY_I_ SOL_OFF
+ #else
+ #define SOL_ALIGN_MEMORY_I_ SOL_ON
+ #endif
+#else
+ #define SOL_ALIGN_MEMORY_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_USE_BOOST)
+ #if (SOL_USE_BOOST != 0)
+ #define SOL_USE_BOOST_I_ SOL_ON
+ #else
+ #define SOL_USE_BOOST_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USE_BOOST_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_USE_UNSAFE_BASE_LOOKUP)
+ #if (SOL_USE_UNSAFE_BASE_LOOKUP != 0)
+ #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_ON
+ #else
+ #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_INSIDE_UNREAL)
+ #if (SOL_INSIDE_UNREAL != 0)
+ #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_ON
+ #else
+ #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_OFF
+ #endif
+#else
+ #if defined(UE_BUILD_DEBUG) || defined(UE_BUILD_DEVELOPMENT) || defined(UE_BUILD_TEST) || defined(UE_BUILD_SHIPPING) || defined(UE_SERVER)
+ #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_NO_COMPAT)
+ #if (SOL_NO_COMPAT != 0)
+ #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_OFF
+ #else
+ #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_ON
+ #endif
+#else
+ #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ #if (SOL_GET_FUNCTION_POINTER_UNSAFE != 0)
+ #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_ON
+ #else
+ #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_CONTAINER_CHECK_IS_EXHAUSTIVE)
+ #if (SOL_CONTAINER_CHECK_IS_EXHAUSTIVE != 0)
+ #define SOL_CONTAINER_CHECK_IS_EXHAUSTIVE_I_ SOL_ON
+ #else
+ #define SOL_CONTAINER_CHECK_IS_EXHAUSTIVE_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_CONTAINER_CHECK_IS_EXHAUSTIVE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_FUNCTION_CALL_VALUE_SEMANTICS)
+ #if (SOL_FUNCTION_CALL_VALUE_SEMANTICS != 0)
+ #define SOL_FUNCTION_CALL_VALUE_SEMANTICS_I_ SOL_ON
+ #else
+ #define SOL_FUNCTION_CALL_VALUE_SEMANTICS_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_FUNCTION_CALL_VALUE_SEMANTICS_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_MINGW_CCTYPE_IS_POISONED)
+ #if (SOL_MINGW_CCTYPE_IS_POISONED != 0)
+ #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_ON
+ #else
+ #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_OFF
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_MINGW) && defined(__GNUC__) && (__GNUC__ < 6)
+ // MinGW is off its rocker in some places...
+ #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_CHAR8_T)
+ #if (SOL_CHAR8_T != 0)
+ #define SOL_CHAR8_T_I_ SOL_ON
+ #else
+ #define SOL_CHAR8_T_I_ SOL_OFF
+ #endif
+#else
+ #if defined(__cpp_char8_t)
+ #define SOL_CHAR8_T_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_CHAR8_T_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if SOL_IS_ON(SOL_USE_BOOST)
+ #include <boost/version.hpp>
+
+ #if BOOST_VERSION >= 107500 // Since Boost 1.75.0 boost::none is constexpr
+ #define SOL_BOOST_NONE_CONSTEXPR_I_ constexpr
+ #else
+ #define SOL_BOOST_NONE_CONSTEXPR_I_ const
+ #endif // BOOST_VERSION
+#else
+ // assume boost isn't using a garbage version
+ #define SOL_BOOST_NONE_CONSTEXPR_I_ constexpr
+#endif
+
+#if defined(SOL2_CI)
+ #if (SOL2_CI != 0)
+ #define SOL2_CI_I_ SOL_ON
+ #else
+ #define SOL2_CI_I_ SOL_OFF
+ #endif
+#else
+ #define SOL2_CI_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_ASSERT)
+ #define SOL_USER_ASSERT_I_ SOL_ON
+#else
+ #define SOL_USER_ASSERT_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_ASSERT_MSG)
+ #define SOL_USER_ASSERT_MSG_I_ SOL_ON
+#else
+ #define SOL_USER_ASSERT_MSG_I_ SOL_DEFAULT_OFF
+#endif
+
+// beginning of sol/prologue.hpp
+
+#if defined(SOL_PROLOGUE_I_)
+ #error "[sol2] Library Prologue was already included in translation unit and not properly ended with an epilogue."
+#endif
+
+#define SOL_PROLOGUE_I_ 1
+
+#if SOL_IS_ON(SOL_BUILD_CXX_MODE)
+ #define _FWD(...) static_cast<decltype( __VA_ARGS__ )&&>( __VA_ARGS__ )
+
+ #if SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define _MOVE(...) static_cast<__typeof( __VA_ARGS__ )&&>( __VA_ARGS__ )
+ #else
+ #include <type_traits>
+
+ #define _MOVE(...) static_cast<::std::remove_reference_t<( __VA_ARGS__ )>&&>( __VA_OPT__(,) )
+ #endif
+#endif
+
+// end of sol/prologue.hpp
+
+// beginning of sol/epilogue.hpp
+
+#if !defined(SOL_PROLOGUE_I_)
+ #error "[sol2] Library Prologue is missing from this translation unit."
+#else
+ #undef SOL_PROLOGUE_I_
+#endif
+
+#if SOL_IS_ON(SOL_BUILD_CXX_MODE)
+ #undef _FWD
+ #undef _MOVE
+#endif
+
+// end of sol/epilogue.hpp
+
+// beginning of sol/detail/build_version.hpp
+
+#if defined(SOL_DLL)
+ #if (SOL_DLL != 0)
+ #define SOL_DLL_I_ SOL_ON
+ #else
+ #define SOL_DLL_I_ SOL_OFF
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_VCXX) && (defined(DLL_) || defined(_DLL))
+ #define SOL_DLL_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_DLL_I_ SOL_DEFAULT_OFF
+#endif // DLL definition
+
+#if defined(SOL_HEADER_ONLY)
+ #if (SOL_HEADER_ONLY != 0)
+ #define SOL_HEADER_ONLY_I_ SOL_ON
+ #else
+ #define SOL_HEADER_ONLY_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_HEADER_ONLY_I_ SOL_DEFAULT_OFF
+#endif // Header only library
+
+#if defined(SOL_BUILD)
+ #if (SOL_BUILD != 0)
+ #define SOL_BUILD_I_ SOL_ON
+ #else
+ #define SOL_BUILD_I_ SOL_OFF
+ #endif
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_BUILD_I_ SOL_DEFAULT_OFF
+#else
+ #define SOL_BUILD_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_UNITY_BUILD)
+ #if (SOL_UNITY_BUILD != 0)
+ #define SOL_UNITY_BUILD_I_ SOL_ON
+ #else
+ #define SOL_UNITY_BUILD_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_UNITY_BUILD_I_ SOL_DEFAULT_OFF
+#endif // Header only library
+
+#if defined(SOL_C_FUNCTION_LINKAGE)
+ #define SOL_C_FUNCTION_LINKAGE_I_ SOL_C_FUNCTION_LINKAGE
+#else
+ #if SOL_IS_ON(SOL_BUILD_CXX_MODE)
+ // C++
+ #define SOL_C_FUNCTION_LINKAGE_I_ extern "C"
+ #else
+ // normal
+ #define SOL_C_FUNCTION_LINKAGE_I_
+ #endif // C++ or not
+#endif // Linkage specification for C functions
+
+#if defined(SOL_API_LINKAGE)
+ #define SOL_API_LINKAGE_I_ SOL_API_LINKAGE
+#else
+ #if SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX) || SOL_IS_ON(SOL_PLATFORM_WINDOWS) || SOL_IS_ON(SOL_PLATFORM_CYGWIN)
+ // MSVC Compiler; or, Windows, or Cygwin platforms
+ #if SOL_IS_ON(SOL_BUILD)
+ // Building the library
+ #if SOL_IS_ON(SOL_COMPILER_GCC)
+ // Using GCC
+ #define SOL_API_LINKAGE_I_ __attribute__((dllexport))
+ #else
+ // Using Clang, MSVC, etc...
+ #define SOL_API_LINKAGE_I_ __declspec(dllexport)
+ #endif
+ #else
+ #if SOL_IS_ON(SOL_COMPILER_GCC)
+ #define SOL_API_LINKAGE_I_ __attribute__((dllimport))
+ #else
+ #define SOL_API_LINKAGE_I_ __declspec(dllimport)
+ #endif
+ #endif
+ #else
+ // extern if building normally on non-MSVC
+ #define SOL_API_LINKAGE_I_ extern
+ #endif
+ #elif SOL_IS_ON(SOL_UNITY_BUILD)
+ // Built-in library, like how stb typical works
+ #if SOL_IS_ON(SOL_HEADER_ONLY)
+ // Header only, so functions are defined "inline"
+ #define SOL_API_LINKAGE_I_ inline
+ #else
+ // Not header only, so seperately compiled files
+ #define SOL_API_LINKAGE_I_ extern
+ #endif
+ #else
+ // Normal static library
+ #if SOL_IS_ON(SOL_BUILD_CXX_MODE)
+ #define SOL_API_LINKAGE_I_
+ #else
+ #define SOL_API_LINKAGE_I_ extern
+ #endif
+ #endif // DLL or not
+#endif // Build definitions
+
+#if defined(SOL_PUBLIC_FUNC_DECL)
+ #define SOL_PUBLIC_FUNC_DECL_I_ SOL_PUBLIC_FUNC_DECL
+#else
+ #define SOL_PUBLIC_FUNC_DECL_I_ SOL_API_LINKAGE_I_
+#endif
+
+#if defined(SOL_INTERNAL_FUNC_DECL_)
+ #define SOL_INTERNAL_FUNC_DECL_I_ SOL_INTERNAL_FUNC_DECL_
+#else
+ #define SOL_INTERNAL_FUNC_DECL_I_ SOL_API_LINKAGE_I_
+#endif
+
+#if defined(SOL_PUBLIC_FUNC_DEF)
+ #define SOL_PUBLIC_FUNC_DEF_I_ SOL_PUBLIC_FUNC_DEF
+#else
+ #define SOL_PUBLIC_FUNC_DEF_I_ SOL_API_LINKAGE_I_
+#endif
+
+#if defined(SOL_INTERNAL_FUNC_DEF)
+ #define SOL_INTERNAL_FUNC_DEF_I_ SOL_INTERNAL_FUNC_DEF
+#else
+ #define SOL_INTERNAL_FUNC_DEF_I_ SOL_API_LINKAGE_I_
+#endif
+
+#if defined(SOL_FUNC_DECL)
+ #define SOL_FUNC_DECL_I_ SOL_FUNC_DECL
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_FUNC_DECL_I_
+#elif SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if SOL_IS_ON(SOL_BUILD)
+ #define SOL_FUNC_DECL_I_ extern __declspec(dllexport)
+ #else
+ #define SOL_FUNC_DECL_I_ extern __declspec(dllimport)
+ #endif
+ #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define SOL_FUNC_DECL_I_ extern __attribute__((visibility("default")))
+ #else
+ #define SOL_FUNC_DECL_I_ extern
+ #endif
+#endif
+
+#if defined(SOL_FUNC_DEFN)
+ #define SOL_FUNC_DEFN_I_ SOL_FUNC_DEFN
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_FUNC_DEFN_I_ inline
+#elif SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if SOL_IS_ON(SOL_BUILD)
+ #define SOL_FUNC_DEFN_I_ __declspec(dllexport)
+ #else
+ #define SOL_FUNC_DEFN_I_ __declspec(dllimport)
+ #endif
+ #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define SOL_FUNC_DEFN_I_ __attribute__((visibility("default")))
+ #else
+ #define SOL_FUNC_DEFN_I_
+ #endif
+#endif
+
+#if defined(SOL_HIDDEN_FUNC_DECL)
+ #define SOL_HIDDEN_FUNC_DECL_I_ SOL_HIDDEN_FUNC_DECL
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_HIDDEN_FUNC_DECL_I_
+#elif SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if SOL_IS_ON(SOL_BUILD)
+ #define SOL_HIDDEN_FUNC_DECL_I_ extern __declspec(dllexport)
+ #else
+ #define SOL_HIDDEN_FUNC_DECL_I_ extern __declspec(dllimport)
+ #endif
+ #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define SOL_HIDDEN_FUNC_DECL_I_ extern __attribute__((visibility("default")))
+ #else
+ #define SOL_HIDDEN_FUNC_DECL_I_ extern
+ #endif
+#endif
+
+#if defined(SOL_HIDDEN_FUNC_DEFN)
+ #define SOL_HIDDEN_FUNC_DEFN_I_ SOL_HIDDEN_FUNC_DEFN
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_HIDDEN_FUNC_DEFN_I_ inline
+#elif SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if SOL_IS_ON(SOL_BUILD)
+ #define SOL_HIDDEN_FUNC_DEFN_I_
+ #else
+ #define SOL_HIDDEN_FUNC_DEFN_I_
+ #endif
+ #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define SOL_HIDDEN_FUNC_DEFN_I_ __attribute__((visibility("hidden")))
+ #else
+ #define SOL_HIDDEN_FUNC_DEFN_I_
+ #endif
+#endif
+
+// end of sol/detail/build_version.hpp
+
+// end of sol/version.hpp
+
+#include <utility>
+#include <type_traits>
+#include <string_view>
+
+#if SOL_IS_ON(SOL_USING_CXX_LUA) || SOL_IS_ON(SOL_USING_CXX_LUAJIT)
+struct lua_State;
+#else
+extern "C" {
+struct lua_State;
+}
+#endif // C++ Mangling for Lua vs. Not
+
+namespace sol {
+
+ enum class type;
+
+ class stateless_reference;
+ template <bool b>
+ class basic_reference;
+ using reference = basic_reference<false>;
+ using main_reference = basic_reference<true>;
+ class stateless_stack_reference;
+ class stack_reference;
+
+ template <typename A>
+ class basic_bytecode;
+
+ struct lua_value;
+
+ struct proxy_base_tag;
+ template <typename>
+ struct proxy_base;
+ template <typename, typename>
+ struct table_proxy;
+
+ template <bool, typename>
+ class basic_table_core;
+ template <bool b>
+ using table_core = basic_table_core<b, reference>;
+ template <bool b>
+ using main_table_core = basic_table_core<b, main_reference>;
+ template <bool b>
+ using stack_table_core = basic_table_core<b, stack_reference>;
+ template <typename base_type>
+ using basic_table = basic_table_core<false, base_type>;
+ using table = table_core<false>;
+ using global_table = table_core<true>;
+ using main_table = main_table_core<false>;
+ using main_global_table = main_table_core<true>;
+ using stack_table = stack_table_core<false>;
+ using stack_global_table = stack_table_core<true>;
+
+ template <typename>
+ struct basic_lua_table;
+ using lua_table = basic_lua_table<reference>;
+ using stack_lua_table = basic_lua_table<stack_reference>;
+
+ template <typename T, typename base_type>
+ class basic_usertype;
+ template <typename T>
+ using usertype = basic_usertype<T, reference>;
+ template <typename T>
+ using stack_usertype = basic_usertype<T, stack_reference>;
+
+ template <typename base_type>
+ class basic_metatable;
+ using metatable = basic_metatable<reference>;
+ using stack_metatable = basic_metatable<stack_reference>;
+
+ template <typename base_t>
+ struct basic_environment;
+ using environment = basic_environment<reference>;
+ using main_environment = basic_environment<main_reference>;
+ using stack_environment = basic_environment<stack_reference>;
+
+ template <typename T, bool>
+ class basic_function;
+ template <typename T, bool, typename H>
+ class basic_protected_function;
+ using unsafe_function = basic_function<reference, false>;
+ using safe_function = basic_protected_function<reference, false, reference>;
+ using main_unsafe_function = basic_function<main_reference, false>;
+ using main_safe_function = basic_protected_function<main_reference, false, reference>;
+ using stack_unsafe_function = basic_function<stack_reference, false>;
+ using stack_safe_function = basic_protected_function<stack_reference, false, reference>;
+ using stack_aligned_unsafe_function = basic_function<stack_reference, true>;
+ using stack_aligned_safe_function = basic_protected_function<stack_reference, true, reference>;
+ using protected_function = safe_function;
+ using main_protected_function = main_safe_function;
+ using stack_protected_function = stack_safe_function;
+ using stack_aligned_protected_function = stack_aligned_safe_function;
+#if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS)
+ using function = protected_function;
+ using main_function = main_protected_function;
+ using stack_function = stack_protected_function;
+ using stack_aligned_function = stack_aligned_safe_function;
+#else
+ using function = unsafe_function;
+ using main_function = main_unsafe_function;
+ using stack_function = stack_unsafe_function;
+ using stack_aligned_function = stack_aligned_unsafe_function;
+#endif
+ using stack_aligned_stack_handler_function = basic_protected_function<stack_reference, true, stack_reference>;
+
+ struct unsafe_function_result;
+ struct protected_function_result;
+ using safe_function_result = protected_function_result;
+#if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS)
+ using function_result = safe_function_result;
+#else
+ using function_result = unsafe_function_result;
+#endif
+
+ template <typename base_t>
+ class basic_object_base;
+ template <typename base_t>
+ class basic_object;
+ template <typename base_t>
+ class basic_userdata;
+ template <typename base_t>
+ class basic_lightuserdata;
+ template <typename base_t>
+ class basic_coroutine;
+ template <typename base_t>
+ class basic_packaged_coroutine;
+ template <typename base_t>
+ class basic_thread;
+
+ using object = basic_object<reference>;
+ using userdata = basic_userdata<reference>;
+ using lightuserdata = basic_lightuserdata<reference>;
+ using thread = basic_thread<reference>;
+ using coroutine = basic_coroutine<reference>;
+ using packaged_coroutine = basic_packaged_coroutine<reference>;
+ using main_object = basic_object<main_reference>;
+ using main_userdata = basic_userdata<main_reference>;
+ using main_lightuserdata = basic_lightuserdata<main_reference>;
+ using main_coroutine = basic_coroutine<main_reference>;
+ using stack_object = basic_object<stack_reference>;
+ using stack_userdata = basic_userdata<stack_reference>;
+ using stack_lightuserdata = basic_lightuserdata<stack_reference>;
+ using stack_thread = basic_thread<stack_reference>;
+ using stack_coroutine = basic_coroutine<stack_reference>;
+
+ struct stack_proxy_base;
+ struct stack_proxy;
+ struct variadic_args;
+ struct variadic_results;
+ struct stack_count;
+ struct this_state;
+ struct this_main_state;
+ struct this_environment;
+
+ class state_view;
+ class state;
+
+ template <typename T>
+ struct as_table_t;
+ template <typename T>
+ struct as_container_t;
+ template <typename T>
+ struct nested;
+ template <typename T>
+ struct light;
+ template <typename T>
+ struct user;
+ template <typename T>
+ struct as_args_t;
+ template <typename T>
+ struct protect_t;
+ template <typename F, typename... Policies>
+ struct policy_wrapper;
+
+ template <typename T>
+ struct usertype_traits;
+ template <typename T>
+ struct unique_usertype_traits;
+
+ template <typename... Args>
+ struct types {
+ typedef std::make_index_sequence<sizeof...(Args)> indices;
+ static constexpr std::size_t size() {
+ return sizeof...(Args);
+ }
+ };
+
+ template <typename T>
+ struct derive : std::false_type {
+ typedef types<> type;
+ };
+
+ template <typename T>
+ struct base : std::false_type {
+ typedef types<> type;
+ };
+
+ template <typename T>
+ struct weak_derive {
+ static bool value;
+ };
+
+ template <typename T>
+ bool weak_derive<T>::value = false;
+
+ namespace stack {
+ struct record;
+ }
+
+#if SOL_IS_OFF(SOL_USE_BOOST)
+ template <class T>
+ class optional;
+
+ template <class T>
+ class optional<T&>;
+#endif
+
+ using check_handler_type = int(lua_State*, int, type, type, const char*);
+
+} // namespace sol
+
+#define SOL_BASE_CLASSES(T, ...) \
+ namespace sol { \
+ template <> \
+ struct base<T> : std::true_type { \
+ typedef ::sol::types<__VA_ARGS__> type; \
+ }; \
+ } \
+ static_assert(true, "")
+#define SOL_DERIVED_CLASSES(T, ...) \
+ namespace sol { \
+ template <> \
+ struct derive<T> : std::true_type { \
+ typedef ::sol::types<__VA_ARGS__> type; \
+ }; \
+ } \
+ static_assert(true, "")
+
+#endif // SOL_FORWARD_HPP
+// end of sol/forward.hpp
+
+#endif // SOL_SINGLE_INCLUDE_SOL_FORWARD_HPP
diff --git a/src/libs/3rdparty/sol2/include/sol/sol.hpp b/src/libs/3rdparty/sol2/include/sol/sol.hpp
new file mode 100644
index 0000000000..85665a50c9
--- /dev/null
+++ b/src/libs/3rdparty/sol2/include/sol/sol.hpp
@@ -0,0 +1,29202 @@
+// The MIT License (MIT)
+
+// Copyright (c) 2013-2020 Rapptz, ThePhD and contributors
+
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+// This file was generated with a script.
+// Generated 2024-01-13 14:25:56.532275 UTC
+// This header was generated with sol v3.3.1 (revision 9c882a28)
+// https://github.com/ThePhD/sol2
+
+#ifndef SOL_SINGLE_INCLUDE_SOL_HPP
+#define SOL_SINGLE_INCLUDE_SOL_HPP
+
+// beginning of sol/sol.hpp
+
+#ifndef SOL_HPP
+#define SOL_HPP
+
+// beginning of sol/version.hpp
+
+#include "config.hpp"
+
+#define SOL_VERSION_MAJOR 3
+#define SOL_VERSION_MINOR 2
+#define SOL_VERSION_PATCH 3
+#define SOL_VERSION_STRING "3.2.3"
+#define SOL_VERSION ((SOL_VERSION_MAJOR * 100000) + (SOL_VERSION_MINOR * 100) + (SOL_VERSION_PATCH))
+
+#define SOL_TOKEN_TO_STRING_POST_EXPANSION_I_(_TOKEN) #_TOKEN
+#define SOL_TOKEN_TO_STRING_I_(_TOKEN) SOL_TOKEN_TO_STRING_POST_EXPANSION_I_(_TOKEN)
+
+#define SOL_CONCAT_TOKENS_POST_EXPANSION_I_(_LEFT, _RIGHT) _LEFT##_RIGHT
+#define SOL_CONCAT_TOKENS_I_(_LEFT, _RIGHT) SOL_CONCAT_TOKENS_POST_EXPANSION_I_(_LEFT, _RIGHT)
+
+#define SOL_RAW_IS_ON(OP_SYMBOL) ((3 OP_SYMBOL 3) != 0)
+#define SOL_RAW_IS_OFF(OP_SYMBOL) ((3 OP_SYMBOL 3) == 0)
+#define SOL_RAW_IS_DEFAULT_ON(OP_SYMBOL) ((3 OP_SYMBOL 3) > 3)
+#define SOL_RAW_IS_DEFAULT_OFF(OP_SYMBOL) ((3 OP_SYMBOL 3 OP_SYMBOL 3) < 0)
+
+#define SOL_IS_ON(OP_SYMBOL) SOL_RAW_IS_ON(OP_SYMBOL ## _I_)
+#define SOL_IS_OFF(OP_SYMBOL) SOL_RAW_IS_OFF(OP_SYMBOL ## _I_)
+#define SOL_IS_DEFAULT_ON(OP_SYMBOL) SOL_RAW_IS_DEFAULT_ON(OP_SYMBOL ## _I_)
+#define SOL_IS_DEFAULT_OFF(OP_SYMBOL) SOL_RAW_IS_DEFAULT_OFF(OP_SYMBOL ## _I_)
+
+#define SOL_ON |
+#define SOL_OFF ^
+#define SOL_DEFAULT_ON +
+#define SOL_DEFAULT_OFF -
+
+#if defined(SOL_BUILD_CXX_MODE)
+ #if (SOL_BUILD_CXX_MODE != 0)
+ #define SOL_BUILD_CXX_MODE_I_ SOL_ON
+ #else
+ #define SOL_BUILD_CXX_MODE_I_ SOL_OFF
+ #endif
+#elif defined(__cplusplus)
+ #define SOL_BUILD_CXX_MODE_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_BUILD_CXX_MODE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_BUILD_C_MODE)
+ #if (SOL_BUILD_C_MODE != 0)
+ #define SOL_BUILD_C_MODE_I_ SOL_ON
+ #else
+ #define SOL_BUILD_C_MODE_I_ SOL_OFF
+ #endif
+#elif defined(__STDC__)
+ #define SOL_BUILD_C_MODE_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_BUILD_C_MODE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if SOL_IS_ON(SOL_BUILD_C_MODE)
+ #include <stddef.h>
+ #include <stdint.h>
+ #include <limits.h>
+#else
+ #include <cstddef>
+ #include <cstdint>
+ #include <climits>
+#endif
+
+#if defined(SOL_HAS_BUILTIN)
+ #define SOL_HAS_BUILTIN_I_(...) SOL_HAS_BUILTIN(__VA_ARGS__)
+#elif defined(__has_builtin)
+ #define SOL_HAS_BUILTIN_I_(...) __has_builtin(__VA_ARGS__)
+#else
+ #define SOL_HAS_BUILTIN_I_(...) 0
+#endif
+
+#if defined(SOL_COMPILER_VCXX)
+ #if defined(SOL_COMPILER_VCXX != 0)
+ #define SOL_COMPILER_VCXX_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_VCXX_I_ SOL_OFF
+ #endif
+#elif defined(_MSC_VER)
+ #define SOL_COMPILER_VCXX_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_COMPILER_VCXX_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_COMPILER_GCC)
+ #if defined(SOL_COMPILER_GCC != 0)
+ #define SOL_COMPILER_GCC_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_GCC_I_ SOL_OFF
+ #endif
+#elif defined(__GNUC__)
+ #define SOL_COMPILER_GCC_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_COMPILER_GCC_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_COMPILER_CLANG)
+ #if defined(SOL_COMPILER_CLANG != 0)
+ #define SOL_COMPILER_CLANG_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_CLANG_I_ SOL_OFF
+ #endif
+#elif defined(__clang__)
+ #define SOL_COMPILER_CLANG_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_COMPILER_CLANG_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_COMPILER_EDG)
+ #if defined(SOL_COMPILER_EDG != 0)
+ #define SOL_COMPILER_EDG_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_EDG_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_COMPILER_EDG_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_COMPILER_MINGW)
+ #if (SOL_COMPILER_MINGW != 0)
+ #define SOL_COMPILER_MINGW_I_ SOL_ON
+ #else
+ #define SOL_COMPILER_MINGW_I_ SOL_OFF
+ #endif
+#elif defined(__MINGW32__)
+ #define SOL_COMPILER_MINGW_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_COMPILER_MINGW_I_ SOL_DEFAULT_OFF
+#endif
+
+#if SIZE_MAX <= 0xFFFFULL
+ #define SOL_PLATFORM_X16_I_ SOL_ON
+ #define SOL_PLATFORM_X86_I_ SOL_OFF
+ #define SOL_PLATFORM_X64_I_ SOL_OFF
+#elif SIZE_MAX <= 0xFFFFFFFFULL
+ #define SOL_PLATFORM_X16_I_ SOL_OFF
+ #define SOL_PLATFORM_X86_I_ SOL_ON
+ #define SOL_PLATFORM_X64_I_ SOL_OFF
+#else
+ #define SOL_PLATFORM_X16_I_ SOL_OFF
+ #define SOL_PLATFORM_X86_I_ SOL_OFF
+ #define SOL_PLATFORM_X64_I_ SOL_ON
+#endif
+
+#define SOL_PLATFORM_ARM32_I_ SOL_OFF
+#define SOL_PLATFORM_ARM64_I_ SOL_OFF
+
+#if defined(SOL_PLATFORM_WINDOWS)
+ #if (SOL_PLATFORM_WINDOWS != 0)
+ #define SOL_PLATFORM_WINDOWS_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_WINDOWS_I_ SOL_OFF
+ #endif
+#elif defined(_WIN32)
+ #define SOL_PLATFORM_WINDOWS_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_PLATFORM_WINDOWS_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_PLATFORM_CYGWIN)
+ #if (SOL_PLATFORM_CYGWIN != 0)
+ #define SOL_PLATFORM_CYGWIN_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_CYGWIN_I_ SOL_ON
+ #endif
+#elif defined(__CYGWIN__)
+ #define SOL_PLATFORM_CYGWIN_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_PLATFORM_CYGWIN_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_PLATFORM_APPLE)
+ #if (SOL_PLATFORM_APPLE != 0)
+ #define SOL_PLATFORM_APPLE_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_APPLE_I_ SOL_OFF
+ #endif
+#elif defined(__APPLE__)
+ #define SOL_PLATFORM_APPLE_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_PLATFORM_APPLE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_PLATFORM_UNIX)
+ #if (SOL_PLATFORM_UNIX != 0)
+ #define SOL_PLATFORM_UNIXLIKE_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_UNIXLIKE_I_ SOL_OFF
+ #endif
+#elif defined(__unix__)
+ #define SOL_PLATFORM_UNIXLIKE_I_ SOL_DEFAUKT_ON
+#else
+ #define SOL_PLATFORM_UNIXLIKE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_PLATFORM_LINUX)
+ #if (SOL_PLATFORM_LINUX != 0)
+ #define SOL_PLATFORM_LINUXLIKE_I_ SOL_ON
+ #else
+ #define SOL_PLATFORM_LINUXLIKE_I_ SOL_OFF
+ #endif
+#elif defined(__LINUX__)
+ #define SOL_PLATFORM_LINUXLIKE_I_ SOL_DEFAUKT_ON
+#else
+ #define SOL_PLATFORM_LINUXLIKE_I_ SOL_DEFAULT_OFF
+#endif
+
+#define SOL_PLATFORM_APPLE_IPHONE_I_ SOL_OFF
+#define SOL_PLATFORM_BSDLIKE_I_ SOL_OFF
+
+#if defined(SOL_IN_DEBUG_DETECTED)
+ #if SOL_IN_DEBUG_DETECTED != 0
+ #define SOL_DEBUG_BUILD_I_ SOL_ON
+ #else
+ #define SOL_DEBUG_BUILD_I_ SOL_OFF
+ #endif
+#elif !defined(NDEBUG)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX) && defined(_DEBUG)
+ #define SOL_DEBUG_BUILD_I_ SOL_ON
+ #elif (SOL_IS_ON(SOL_COMPILER_CLANG) || SOL_IS_ON(SOL_COMPILER_GCC)) && !defined(__OPTIMIZE__)
+ #define SOL_DEBUG_BUILD_I_ SOL_ON
+ #else
+ #define SOL_DEBUG_BUILD_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_DEBUG_BUILD_I_ SOL_DEFAULT_OFF
+#endif // We are in a debug mode of some sort
+
+#if defined(SOL_NO_EXCEPTIONS)
+ #if (SOL_NO_EXCEPTIONS != 0)
+ #define SOL_EXCEPTIONS_I_ SOL_OFF
+ #else
+ #define SOL_EXCEPTIONS_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if !defined(_CPPUNWIND)
+ #define SOL_EXCEPTIONS_I_ SOL_OFF
+ #else
+ #define SOL_EXCEPTIONS_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_CLANG) || SOL_IS_ON(SOL_COMPILER_GCC)
+ #if !defined(__EXCEPTIONS)
+ #define SOL_EXCEPTIONS_I_ SOL_OFF
+ #else
+ #define SOL_EXCEPTIONS_I_ SOL_ON
+ #endif
+#else
+ #define SOL_EXCEPTIONS_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_NO_RTTI)
+ #if (SOL_NO_RTTI != 0)
+ #define SOL_RTTI_I_ SOL_OFF
+ #else
+ #define SOL_RTTI_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if !defined(_CPPRTTI)
+ #define SOL_RTTI_I_ SOL_OFF
+ #else
+ #define SOL_RTTI_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_CLANG) || SOL_IS_ON(SOL_COMPILER_GCC)
+ #if !defined(__GXX_RTTI)
+ #define SOL_RTTI_I_ SOL_OFF
+ #else
+ #define SOL_RTTI_I_ SOL_ON
+ #endif
+#else
+ #define SOL_RTTI_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_NO_THREAD_LOCAL)
+ #if SOL_NO_THREAD_LOCAL != 0
+ #define SOL_USE_THREAD_LOCAL_I_ SOL_OFF
+ #else
+ #define SOL_USE_THREAD_LOCAL_I_ SOL_ON
+ #endif
+#else
+ #define SOL_USE_THREAD_LOCAL_I_ SOL_DEFAULT_ON
+#endif // thread_local keyword is bjorked on some platforms
+
+#if defined(SOL_ALL_SAFETIES_ON)
+ #if SOL_ALL_SAFETIES_ON != 0
+ #define SOL_ALL_SAFETIES_ON_I_ SOL_ON
+ #else
+ #define SOL_ALL_SAFETIES_ON_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_ALL_SAFETIES_ON_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_SAFE_GETTER)
+ #if SOL_SAFE_GETTER != 0
+ #define SOL_SAFE_GETTER_I_ SOL_ON
+ #else
+ #define SOL_SAFE_GETTER_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_GETTER_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_GETTER_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_GETTER_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_USERTYPE)
+ #if SOL_SAFE_USERTYPE != 0
+ #define SOL_SAFE_USERTYPE_I_ SOL_ON
+ #else
+ #define SOL_SAFE_USERTYPE_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_USERTYPE_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_USERTYPE_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_USERTYPE_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_REFERENCES)
+ #if SOL_SAFE_REFERENCES != 0
+ #define SOL_SAFE_REFERENCES_I_ SOL_ON
+ #else
+ #define SOL_SAFE_REFERENCES_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_REFERENCES_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_REFERENCES_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_REFERENCES_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_FUNCTIONS)
+ #if SOL_SAFE_FUNCTIONS != 0
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON
+ #else
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_OFF
+ #endif
+#elif defined (SOL_SAFE_FUNCTION_OBJECTS)
+ #if SOL_SAFE_FUNCTION_OBJECTS != 0
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON
+ #else
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_FUNCTION_OBJECTS_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_FUNCTION_CALLS)
+ #if SOL_SAFE_FUNCTION_CALLS != 0
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_ON
+ #else
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_FUNCTION_CALLS_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_PROXIES)
+ #if SOL_SAFE_PROXIES != 0
+ #define SOL_SAFE_PROXIES_I_ SOL_ON
+ #else
+ #define SOL_SAFE_PROXIES_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_PROXIES_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_PROXIES_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_PROXIES_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_SAFE_NUMERICS)
+ #if SOL_SAFE_NUMERICS != 0
+ #define SOL_SAFE_NUMERICS_I_ SOL_ON
+ #else
+ #define SOL_SAFE_NUMERICS_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_NUMERICS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_NUMERICS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_NUMERICS_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_ALL_INTEGER_VALUES_FIT)
+ #if (SOL_ALL_INTEGER_VALUES_FIT != 0)
+ #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_ON
+ #else
+ #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_OFF
+ #endif
+#elif !SOL_IS_DEFAULT_OFF(SOL_SAFE_NUMERICS) && SOL_IS_OFF(SOL_SAFE_NUMERICS)
+ // if numerics is intentionally turned off, flip this on
+ #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_DEFAULT_ON
+#else
+ // default to off
+ #define SOL_ALL_INTEGER_VALUES_FIT_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_SAFE_STACK_CHECK)
+ #if SOL_SAFE_STACK_CHECK != 0
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_ON
+ #else
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_SAFE_STACK_CHECK_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_NO_CHECK_NUMBER_PRECISION)
+ #if SOL_NO_CHECK_NUMBER_PRECISION != 0
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_OFF
+ #else
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON
+ #endif
+#elif defined(SOL_NO_CHECKING_NUMBER_PRECISION)
+ #if SOL_NO_CHECKING_NUMBER_PRECISION != 0
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_OFF
+ #else
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_SAFE_NUMERICS)
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_NUMBER_PRECISION_CHECKS_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_STRINGS_ARE_NUMBERS)
+ #if (SOL_STRINGS_ARE_NUMBERS != 0)
+ #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_ON
+ #else
+ #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_STRINGS_ARE_NUMBERS_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_ENABLE_INTEROP)
+ #if SOL_ENABLE_INTEROP != 0
+ #define SOL_USE_INTEROP_I_ SOL_ON
+ #else
+ #define SOL_USE_INTEROP_I_ SOL_OFF
+ #endif
+#elif defined(SOL_USE_INTEROP)
+ #if SOL_USE_INTEROP != 0
+ #define SOL_USE_INTEROP_I_ SOL_ON
+ #else
+ #define SOL_USE_INTEROP_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USE_INTEROP_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_NO_NIL)
+ #if (SOL_NO_NIL != 0)
+ #define SOL_NIL_I_ SOL_OFF
+ #else
+ #define SOL_NIL_I_ SOL_ON
+ #endif
+#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED) || defined(__OBJC__) || defined(nil)
+ #define SOL_NIL_I_ SOL_DEFAULT_OFF
+#else
+ #define SOL_NIL_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_USERTYPE_TYPE_BINDING_INFO)
+ #if (SOL_USERTYPE_TYPE_BINDING_INFO != 0)
+ #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_ON
+ #else
+ #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USERTYPE_TYPE_BINDING_INFO_I_ SOL_DEFAULT_ON
+#endif // We should generate a my_type.__type table with lots of class information for usertypes
+
+#if defined(SOL_AUTOMAGICAL_TYPES_BY_DEFAULT)
+ #if (SOL_AUTOMAGICAL_TYPES_BY_DEFAULT != 0)
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_ON
+ #else
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_OFF
+ #endif
+#elif defined(SOL_DEFAULT_AUTOMAGICAL_USERTYPES)
+ #if (SOL_DEFAULT_AUTOMAGICAL_USERTYPES != 0)
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_ON
+ #else
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_DEFAULT_AUTOMAGICAL_USERTYPES_I_ SOL_DEFAULT_ON
+#endif // make is_automagical on/off by default
+
+#if defined(SOL_STD_VARIANT)
+ #if (SOL_STD_VARIANT != 0)
+ #define SOL_STD_VARIANT_I_ SOL_ON
+ #else
+ #define SOL_STD_VARIANT_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_COMPILER_CLANG) && SOL_IS_ON(SOL_PLATFORM_APPLE)
+ #if defined(__has_include)
+ #if __has_include(<variant>)
+ #define SOL_STD_VARIANT_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_STD_VARIANT_I_ SOL_DEFAULT_OFF
+ #endif
+ #else
+ #define SOL_STD_VARIANT_I_ SOL_DEFAULT_OFF
+ #endif
+ #else
+ #define SOL_STD_VARIANT_I_ SOL_DEFAULT_ON
+ #endif
+#endif // make is_automagical on/off by default
+
+#if defined(SOL_NOEXCEPT_FUNCTION_TYPE)
+ #if (SOL_NOEXCEPT_FUNCTION_TYPE != 0)
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_ON
+ #else
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_OFF
+ #endif
+#else
+ #if defined(__cpp_noexcept_function_type)
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_COMPILER_VCXX) && (defined(_MSVC_LANG) && (_MSVC_LANG < 201403L))
+ // There is a bug in the VC++ compiler??
+ // on /std:c++latest under x86 conditions (VS 15.5.2),
+ // compiler errors are tossed for noexcept markings being on function types
+ // that are identical in every other way to their non-noexcept marked types function types...
+ // VS 2019: There is absolutely a bug.
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_OFF
+ #else
+ #define SOL_USE_NOEXCEPT_FUNCTION_TYPE_I_ SOL_DEFAULT_ON
+ #endif
+#endif // noexcept is part of a function's type
+
+#if defined(SOL_STACK_STRING_OPTIMIZATION_SIZE) && SOL_STACK_STRING_OPTIMIZATION_SIZE > 0
+ #define SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_ SOL_STACK_STRING_OPTIMIZATION_SIZE
+#else
+ #define SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_ 1024
+#endif
+
+#if defined(SOL_ID_SIZE) && SOL_ID_SIZE > 0
+ #define SOL_ID_SIZE_I_ SOL_ID_SIZE
+#else
+ #define SOL_ID_SIZE_I_ 512
+#endif
+
+#if defined(LUA_IDSIZE) && LUA_IDSIZE > 0
+ #define SOL_FILE_ID_SIZE_I_ LUA_IDSIZE
+#elif defined(SOL_ID_SIZE) && SOL_ID_SIZE > 0
+ #define SOL_FILE_ID_SIZE_I_ SOL_FILE_ID_SIZE
+#else
+ #define SOL_FILE_ID_SIZE_I_ 2048
+#endif
+
+#if defined(SOL_PRINT_ERRORS)
+ #if (SOL_PRINT_ERRORS != 0)
+ #define SOL_PRINT_ERRORS_I_ SOL_ON
+ #else
+ #define SOL_PRINT_ERRORS_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_ALL_SAFETIES_ON)
+ #define SOL_PRINT_ERRORS_I_ SOL_ON
+ #elif SOL_IS_ON(SOL_DEBUG_BUILD)
+ #define SOL_PRINT_ERRORS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_PRINT_ERRORS_I_ SOL_OFF
+ #endif
+#endif
+
+#if defined(SOL_DEFAULT_PASS_ON_ERROR)
+ #if (SOL_DEFAULT_PASS_ON_ERROR != 0)
+ #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_ON
+ #else
+ #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_DEFAULT_PASS_ON_ERROR_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_USING_CXX_LUA)
+ #if (SOL_USING_CXX_LUA != 0)
+ #define SOL_USING_CXX_LUA_I_ SOL_ON
+ #else
+ #define SOL_USING_CXX_LUA_I_ SOL_OFF
+ #endif
+#elif defined(SOL_USE_CXX_LUA)
+ // alternative spelling
+ #if (SOL_USE_CXX_LUA != 0)
+ #define SOL_USING_CXX_LUA_I_ SOL_ON
+ #else
+ #define SOL_USING_CXX_LUA_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USING_CXX_LUA_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_USING_CXX_LUAJIT)
+ #if (SOL_USING_CXX_LUAJIT != 0)
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_ON
+ #else
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_OFF
+ #endif
+#elif defined(SOL_USE_CXX_LUAJIT)
+ #if (SOL_USE_CXX_LUAJIT != 0)
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_ON
+ #else
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USING_CXX_LUAJIT_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_NO_LUA_HPP)
+ #if (SOL_NO_LUA_HPP != 0)
+ #define SOL_USE_LUA_HPP_I_ SOL_OFF
+ #else
+ #define SOL_USE_LUA_HPP_I_ SOL_ON
+ #endif
+#elif SOL_IS_ON(SOL_USING_CXX_LUA)
+ #define SOL_USE_LUA_HPP_I_ SOL_OFF
+#elif defined(__has_include)
+ #if __has_include(<lua.hpp>)
+ #define SOL_USE_LUA_HPP_I_ SOL_ON
+ #else
+ #define SOL_USE_LUA_HPP_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USE_LUA_HPP_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_CONTAINERS_START)
+ #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINERS_START
+#elif defined(SOL_CONTAINERS_START_INDEX)
+ #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINERS_START_INDEX
+#elif defined(SOL_CONTAINER_START_INDEX)
+ #define SOL_CONTAINER_START_INDEX_I_ SOL_CONTAINER_START_INDEX
+#else
+ #define SOL_CONTAINER_START_INDEX_I_ 1
+#endif
+
+#if defined (SOL_NO_MEMORY_ALIGNMENT)
+ #if (SOL_NO_MEMORY_ALIGNMENT != 0)
+ #define SOL_ALIGN_MEMORY_I_ SOL_OFF
+ #else
+ #define SOL_ALIGN_MEMORY_I_ SOL_ON
+ #endif
+#else
+ #define SOL_ALIGN_MEMORY_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_USE_BOOST)
+ #if (SOL_USE_BOOST != 0)
+ #define SOL_USE_BOOST_I_ SOL_ON
+ #else
+ #define SOL_USE_BOOST_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USE_BOOST_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_USE_UNSAFE_BASE_LOOKUP)
+ #if (SOL_USE_UNSAFE_BASE_LOOKUP != 0)
+ #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_ON
+ #else
+ #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_USE_UNSAFE_BASE_LOOKUP_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_INSIDE_UNREAL)
+ #if (SOL_INSIDE_UNREAL != 0)
+ #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_ON
+ #else
+ #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_OFF
+ #endif
+#else
+ #if defined(UE_BUILD_DEBUG) || defined(UE_BUILD_DEVELOPMENT) || defined(UE_BUILD_TEST) || defined(UE_BUILD_SHIPPING) || defined(UE_SERVER)
+ #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_INSIDE_UNREAL_ENGINE_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_NO_COMPAT)
+ #if (SOL_NO_COMPAT != 0)
+ #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_OFF
+ #else
+ #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_ON
+ #endif
+#else
+ #define SOL_USE_COMPATIBILITY_LAYER_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ #if (SOL_GET_FUNCTION_POINTER_UNSAFE != 0)
+ #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_ON
+ #else
+ #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_GET_FUNCTION_POINTER_UNSAFE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_CONTAINER_CHECK_IS_EXHAUSTIVE)
+ #if (SOL_CONTAINER_CHECK_IS_EXHAUSTIVE != 0)
+ #define SOL_CONTAINER_CHECK_IS_EXHAUSTIVE_I_ SOL_ON
+ #else
+ #define SOL_CONTAINER_CHECK_IS_EXHAUSTIVE_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_CONTAINER_CHECK_IS_EXHAUSTIVE_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_FUNCTION_CALL_VALUE_SEMANTICS)
+ #if (SOL_FUNCTION_CALL_VALUE_SEMANTICS != 0)
+ #define SOL_FUNCTION_CALL_VALUE_SEMANTICS_I_ SOL_ON
+ #else
+ #define SOL_FUNCTION_CALL_VALUE_SEMANTICS_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_FUNCTION_CALL_VALUE_SEMANTICS_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_MINGW_CCTYPE_IS_POISONED)
+ #if (SOL_MINGW_CCTYPE_IS_POISONED != 0)
+ #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_ON
+ #else
+ #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_OFF
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_MINGW) && defined(__GNUC__) && (__GNUC__ < 6)
+ // MinGW is off its rocker in some places...
+ #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_MINGW_CCTYPE_IS_POISONED_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_CHAR8_T)
+ #if (SOL_CHAR8_T != 0)
+ #define SOL_CHAR8_T_I_ SOL_ON
+ #else
+ #define SOL_CHAR8_T_I_ SOL_OFF
+ #endif
+#else
+ #if defined(__cpp_char8_t)
+ #define SOL_CHAR8_T_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_CHAR8_T_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if SOL_IS_ON(SOL_USE_BOOST)
+ #include <boost/version.hpp>
+
+ #if BOOST_VERSION >= 107500 // Since Boost 1.75.0 boost::none is constexpr
+ #define SOL_BOOST_NONE_CONSTEXPR_I_ constexpr
+ #else
+ #define SOL_BOOST_NONE_CONSTEXPR_I_ const
+ #endif // BOOST_VERSION
+#else
+ // assume boost isn't using a garbage version
+ #define SOL_BOOST_NONE_CONSTEXPR_I_ constexpr
+#endif
+
+#if defined(SOL2_CI)
+ #if (SOL2_CI != 0)
+ #define SOL2_CI_I_ SOL_ON
+ #else
+ #define SOL2_CI_I_ SOL_OFF
+ #endif
+#else
+ #define SOL2_CI_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_ASSERT)
+ #define SOL_USER_ASSERT_I_ SOL_ON
+#else
+ #define SOL_USER_ASSERT_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_ASSERT_MSG)
+ #define SOL_USER_ASSERT_MSG_I_ SOL_ON
+#else
+ #define SOL_USER_ASSERT_MSG_I_ SOL_DEFAULT_OFF
+#endif
+
+// beginning of sol/prologue.hpp
+
+#if defined(SOL_PROLOGUE_I_)
+ #error "[sol2] Library Prologue was already included in translation unit and not properly ended with an epilogue."
+#endif
+
+#define SOL_PROLOGUE_I_ 1
+
+#if SOL_IS_ON(SOL_BUILD_CXX_MODE)
+ #define _FWD(...) static_cast<decltype( __VA_ARGS__ )&&>( __VA_ARGS__ )
+
+ #if SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define _MOVE(...) static_cast<__typeof( __VA_ARGS__ )&&>( __VA_ARGS__ )
+ #else
+ #include <type_traits>
+
+ #define _MOVE(...) static_cast<::std::remove_reference_t<( __VA_ARGS__ )>&&>( __VA_OPT__(,) )
+ #endif
+#endif
+
+// end of sol/prologue.hpp
+
+// beginning of sol/epilogue.hpp
+
+#if !defined(SOL_PROLOGUE_I_)
+ #error "[sol2] Library Prologue is missing from this translation unit."
+#else
+ #undef SOL_PROLOGUE_I_
+#endif
+
+#if SOL_IS_ON(SOL_BUILD_CXX_MODE)
+ #undef _FWD
+ #undef _MOVE
+#endif
+
+// end of sol/epilogue.hpp
+
+// beginning of sol/detail/build_version.hpp
+
+#if defined(SOL_DLL)
+ #if (SOL_DLL != 0)
+ #define SOL_DLL_I_ SOL_ON
+ #else
+ #define SOL_DLL_I_ SOL_OFF
+ #endif
+#elif SOL_IS_ON(SOL_COMPILER_VCXX) && (defined(DLL_) || defined(_DLL))
+ #define SOL_DLL_I_ SOL_DEFAULT_ON
+#else
+ #define SOL_DLL_I_ SOL_DEFAULT_OFF
+#endif // DLL definition
+
+#if defined(SOL_HEADER_ONLY)
+ #if (SOL_HEADER_ONLY != 0)
+ #define SOL_HEADER_ONLY_I_ SOL_ON
+ #else
+ #define SOL_HEADER_ONLY_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_HEADER_ONLY_I_ SOL_DEFAULT_OFF
+#endif // Header only library
+
+#if defined(SOL_BUILD)
+ #if (SOL_BUILD != 0)
+ #define SOL_BUILD_I_ SOL_ON
+ #else
+ #define SOL_BUILD_I_ SOL_OFF
+ #endif
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_BUILD_I_ SOL_DEFAULT_OFF
+#else
+ #define SOL_BUILD_I_ SOL_DEFAULT_ON
+#endif
+
+#if defined(SOL_UNITY_BUILD)
+ #if (SOL_UNITY_BUILD != 0)
+ #define SOL_UNITY_BUILD_I_ SOL_ON
+ #else
+ #define SOL_UNITY_BUILD_I_ SOL_OFF
+ #endif
+#else
+ #define SOL_UNITY_BUILD_I_ SOL_DEFAULT_OFF
+#endif // Header only library
+
+#if defined(SOL_C_FUNCTION_LINKAGE)
+ #define SOL_C_FUNCTION_LINKAGE_I_ SOL_C_FUNCTION_LINKAGE
+#else
+ #if SOL_IS_ON(SOL_BUILD_CXX_MODE)
+ // C++
+ #define SOL_C_FUNCTION_LINKAGE_I_ extern "C"
+ #else
+ // normal
+ #define SOL_C_FUNCTION_LINKAGE_I_
+ #endif // C++ or not
+#endif // Linkage specification for C functions
+
+#if defined(SOL_API_LINKAGE)
+ #define SOL_API_LINKAGE_I_ SOL_API_LINKAGE
+#else
+ #if SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX) || SOL_IS_ON(SOL_PLATFORM_WINDOWS) || SOL_IS_ON(SOL_PLATFORM_CYGWIN)
+ // MSVC Compiler; or, Windows, or Cygwin platforms
+ #if SOL_IS_ON(SOL_BUILD)
+ // Building the library
+ #if SOL_IS_ON(SOL_COMPILER_GCC)
+ // Using GCC
+ #define SOL_API_LINKAGE_I_ __attribute__((dllexport))
+ #else
+ // Using Clang, MSVC, etc...
+ #define SOL_API_LINKAGE_I_ __declspec(dllexport)
+ #endif
+ #else
+ #if SOL_IS_ON(SOL_COMPILER_GCC)
+ #define SOL_API_LINKAGE_I_ __attribute__((dllimport))
+ #else
+ #define SOL_API_LINKAGE_I_ __declspec(dllimport)
+ #endif
+ #endif
+ #else
+ // extern if building normally on non-MSVC
+ #define SOL_API_LINKAGE_I_ extern
+ #endif
+ #elif SOL_IS_ON(SOL_UNITY_BUILD)
+ // Built-in library, like how stb typical works
+ #if SOL_IS_ON(SOL_HEADER_ONLY)
+ // Header only, so functions are defined "inline"
+ #define SOL_API_LINKAGE_I_ inline
+ #else
+ // Not header only, so seperately compiled files
+ #define SOL_API_LINKAGE_I_ extern
+ #endif
+ #else
+ // Normal static library
+ #if SOL_IS_ON(SOL_BUILD_CXX_MODE)
+ #define SOL_API_LINKAGE_I_
+ #else
+ #define SOL_API_LINKAGE_I_ extern
+ #endif
+ #endif // DLL or not
+#endif // Build definitions
+
+#if defined(SOL_PUBLIC_FUNC_DECL)
+ #define SOL_PUBLIC_FUNC_DECL_I_ SOL_PUBLIC_FUNC_DECL
+#else
+ #define SOL_PUBLIC_FUNC_DECL_I_ SOL_API_LINKAGE_I_
+#endif
+
+#if defined(SOL_INTERNAL_FUNC_DECL_)
+ #define SOL_INTERNAL_FUNC_DECL_I_ SOL_INTERNAL_FUNC_DECL_
+#else
+ #define SOL_INTERNAL_FUNC_DECL_I_ SOL_API_LINKAGE_I_
+#endif
+
+#if defined(SOL_PUBLIC_FUNC_DEF)
+ #define SOL_PUBLIC_FUNC_DEF_I_ SOL_PUBLIC_FUNC_DEF
+#else
+ #define SOL_PUBLIC_FUNC_DEF_I_ SOL_API_LINKAGE_I_
+#endif
+
+#if defined(SOL_INTERNAL_FUNC_DEF)
+ #define SOL_INTERNAL_FUNC_DEF_I_ SOL_INTERNAL_FUNC_DEF
+#else
+ #define SOL_INTERNAL_FUNC_DEF_I_ SOL_API_LINKAGE_I_
+#endif
+
+#if defined(SOL_FUNC_DECL)
+ #define SOL_FUNC_DECL_I_ SOL_FUNC_DECL
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_FUNC_DECL_I_
+#elif SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if SOL_IS_ON(SOL_BUILD)
+ #define SOL_FUNC_DECL_I_ extern __declspec(dllexport)
+ #else
+ #define SOL_FUNC_DECL_I_ extern __declspec(dllimport)
+ #endif
+ #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define SOL_FUNC_DECL_I_ extern __attribute__((visibility("default")))
+ #else
+ #define SOL_FUNC_DECL_I_ extern
+ #endif
+#endif
+
+#if defined(SOL_FUNC_DEFN)
+ #define SOL_FUNC_DEFN_I_ SOL_FUNC_DEFN
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_FUNC_DEFN_I_ inline
+#elif SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if SOL_IS_ON(SOL_BUILD)
+ #define SOL_FUNC_DEFN_I_ __declspec(dllexport)
+ #else
+ #define SOL_FUNC_DEFN_I_ __declspec(dllimport)
+ #endif
+ #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define SOL_FUNC_DEFN_I_ __attribute__((visibility("default")))
+ #else
+ #define SOL_FUNC_DEFN_I_
+ #endif
+#endif
+
+#if defined(SOL_HIDDEN_FUNC_DECL)
+ #define SOL_HIDDEN_FUNC_DECL_I_ SOL_HIDDEN_FUNC_DECL
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_HIDDEN_FUNC_DECL_I_
+#elif SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if SOL_IS_ON(SOL_BUILD)
+ #define SOL_HIDDEN_FUNC_DECL_I_ extern __declspec(dllexport)
+ #else
+ #define SOL_HIDDEN_FUNC_DECL_I_ extern __declspec(dllimport)
+ #endif
+ #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define SOL_HIDDEN_FUNC_DECL_I_ extern __attribute__((visibility("default")))
+ #else
+ #define SOL_HIDDEN_FUNC_DECL_I_ extern
+ #endif
+#endif
+
+#if defined(SOL_HIDDEN_FUNC_DEFN)
+ #define SOL_HIDDEN_FUNC_DEFN_I_ SOL_HIDDEN_FUNC_DEFN
+#elif SOL_IS_ON(SOL_HEADER_ONLY)
+ #define SOL_HIDDEN_FUNC_DEFN_I_ inline
+#elif SOL_IS_ON(SOL_DLL)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX)
+ #if SOL_IS_ON(SOL_BUILD)
+ #define SOL_HIDDEN_FUNC_DEFN_I_
+ #else
+ #define SOL_HIDDEN_FUNC_DEFN_I_
+ #endif
+ #elif SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ #define SOL_HIDDEN_FUNC_DEFN_I_ __attribute__((visibility("hidden")))
+ #else
+ #define SOL_HIDDEN_FUNC_DEFN_I_
+ #endif
+#endif
+
+// end of sol/detail/build_version.hpp
+
+// end of sol/version.hpp
+
+#if SOL_IS_ON(SOL_INSIDE_UNREAL_ENGINE)
+#ifdef check
+#pragma push_macro("check")
+#undef check
+#endif
+#endif // Unreal Engine 4 Bullshit
+
+#if SOL_IS_ON(SOL_COMPILER_GCC)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+#pragma GCC diagnostic ignored "-Wconversion"
+#if __GNUC__ > 6
+#pragma GCC diagnostic ignored "-Wnoexcept-type"
+#endif
+#elif SOL_IS_ON(SOL_COMPILER_CLANG)
+#elif SOL_IS_ON(SOL_COMPILER_VCXX)
+#pragma warning(push)
+#pragma warning(disable : 4505) // unreferenced local function has been removed GEE THANKS
+#endif // clang++ vs. g++ vs. VC++
+
+// beginning of sol/forward.hpp
+
+#ifndef SOL_FORWARD_HPP
+#define SOL_FORWARD_HPP
+
+#include <utility>
+#include <type_traits>
+#include <string_view>
+
+#if SOL_IS_ON(SOL_USING_CXX_LUA) || SOL_IS_ON(SOL_USING_CXX_LUAJIT)
+struct lua_State;
+#else
+extern "C" {
+struct lua_State;
+}
+#endif // C++ Mangling for Lua vs. Not
+
+namespace sol {
+
+ enum class type;
+
+ class stateless_reference;
+ template <bool b>
+ class basic_reference;
+ using reference = basic_reference<false>;
+ using main_reference = basic_reference<true>;
+ class stateless_stack_reference;
+ class stack_reference;
+
+ template <typename A>
+ class basic_bytecode;
+
+ struct lua_value;
+
+ struct proxy_base_tag;
+ template <typename>
+ struct proxy_base;
+ template <typename, typename>
+ struct table_proxy;
+
+ template <bool, typename>
+ class basic_table_core;
+ template <bool b>
+ using table_core = basic_table_core<b, reference>;
+ template <bool b>
+ using main_table_core = basic_table_core<b, main_reference>;
+ template <bool b>
+ using stack_table_core = basic_table_core<b, stack_reference>;
+ template <typename base_type>
+ using basic_table = basic_table_core<false, base_type>;
+ using table = table_core<false>;
+ using global_table = table_core<true>;
+ using main_table = main_table_core<false>;
+ using main_global_table = main_table_core<true>;
+ using stack_table = stack_table_core<false>;
+ using stack_global_table = stack_table_core<true>;
+
+ template <typename>
+ struct basic_lua_table;
+ using lua_table = basic_lua_table<reference>;
+ using stack_lua_table = basic_lua_table<stack_reference>;
+
+ template <typename T, typename base_type>
+ class basic_usertype;
+ template <typename T>
+ using usertype = basic_usertype<T, reference>;
+ template <typename T>
+ using stack_usertype = basic_usertype<T, stack_reference>;
+
+ template <typename base_type>
+ class basic_metatable;
+ using metatable = basic_metatable<reference>;
+ using stack_metatable = basic_metatable<stack_reference>;
+
+ template <typename base_t>
+ struct basic_environment;
+ using environment = basic_environment<reference>;
+ using main_environment = basic_environment<main_reference>;
+ using stack_environment = basic_environment<stack_reference>;
+
+ template <typename T, bool>
+ class basic_function;
+ template <typename T, bool, typename H>
+ class basic_protected_function;
+ using unsafe_function = basic_function<reference, false>;
+ using safe_function = basic_protected_function<reference, false, reference>;
+ using main_unsafe_function = basic_function<main_reference, false>;
+ using main_safe_function = basic_protected_function<main_reference, false, reference>;
+ using stack_unsafe_function = basic_function<stack_reference, false>;
+ using stack_safe_function = basic_protected_function<stack_reference, false, reference>;
+ using stack_aligned_unsafe_function = basic_function<stack_reference, true>;
+ using stack_aligned_safe_function = basic_protected_function<stack_reference, true, reference>;
+ using protected_function = safe_function;
+ using main_protected_function = main_safe_function;
+ using stack_protected_function = stack_safe_function;
+ using stack_aligned_protected_function = stack_aligned_safe_function;
+#if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS)
+ using function = protected_function;
+ using main_function = main_protected_function;
+ using stack_function = stack_protected_function;
+ using stack_aligned_function = stack_aligned_safe_function;
+#else
+ using function = unsafe_function;
+ using main_function = main_unsafe_function;
+ using stack_function = stack_unsafe_function;
+ using stack_aligned_function = stack_aligned_unsafe_function;
+#endif
+ using stack_aligned_stack_handler_function = basic_protected_function<stack_reference, true, stack_reference>;
+
+ struct unsafe_function_result;
+ struct protected_function_result;
+ using safe_function_result = protected_function_result;
+#if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS)
+ using function_result = safe_function_result;
+#else
+ using function_result = unsafe_function_result;
+#endif
+
+ template <typename base_t>
+ class basic_object_base;
+ template <typename base_t>
+ class basic_object;
+ template <typename base_t>
+ class basic_userdata;
+ template <typename base_t>
+ class basic_lightuserdata;
+ template <typename base_t>
+ class basic_coroutine;
+ template <typename base_t>
+ class basic_packaged_coroutine;
+ template <typename base_t>
+ class basic_thread;
+
+ using object = basic_object<reference>;
+ using userdata = basic_userdata<reference>;
+ using lightuserdata = basic_lightuserdata<reference>;
+ using thread = basic_thread<reference>;
+ using coroutine = basic_coroutine<reference>;
+ using packaged_coroutine = basic_packaged_coroutine<reference>;
+ using main_object = basic_object<main_reference>;
+ using main_userdata = basic_userdata<main_reference>;
+ using main_lightuserdata = basic_lightuserdata<main_reference>;
+ using main_coroutine = basic_coroutine<main_reference>;
+ using stack_object = basic_object<stack_reference>;
+ using stack_userdata = basic_userdata<stack_reference>;
+ using stack_lightuserdata = basic_lightuserdata<stack_reference>;
+ using stack_thread = basic_thread<stack_reference>;
+ using stack_coroutine = basic_coroutine<stack_reference>;
+
+ struct stack_proxy_base;
+ struct stack_proxy;
+ struct variadic_args;
+ struct variadic_results;
+ struct stack_count;
+ struct this_state;
+ struct this_main_state;
+ struct this_environment;
+
+ class state_view;
+ class state;
+
+ template <typename T>
+ struct as_table_t;
+ template <typename T>
+ struct as_container_t;
+ template <typename T>
+ struct nested;
+ template <typename T>
+ struct light;
+ template <typename T>
+ struct user;
+ template <typename T>
+ struct as_args_t;
+ template <typename T>
+ struct protect_t;
+ template <typename F, typename... Policies>
+ struct policy_wrapper;
+
+ template <typename T>
+ struct usertype_traits;
+ template <typename T>
+ struct unique_usertype_traits;
+
+ template <typename... Args>
+ struct types {
+ typedef std::make_index_sequence<sizeof...(Args)> indices;
+ static constexpr std::size_t size() {
+ return sizeof...(Args);
+ }
+ };
+
+ template <typename T>
+ struct derive : std::false_type {
+ typedef types<> type;
+ };
+
+ template <typename T>
+ struct base : std::false_type {
+ typedef types<> type;
+ };
+
+ template <typename T>
+ struct weak_derive {
+ static bool value;
+ };
+
+ template <typename T>
+ bool weak_derive<T>::value = false;
+
+ namespace stack {
+ struct record;
+ }
+
+#if SOL_IS_OFF(SOL_USE_BOOST)
+ template <class T>
+ class optional;
+
+ template <class T>
+ class optional<T&>;
+#endif
+
+ using check_handler_type = int(lua_State*, int, type, type, const char*);
+
+} // namespace sol
+
+#define SOL_BASE_CLASSES(T, ...) \
+ namespace sol { \
+ template <> \
+ struct base<T> : std::true_type { \
+ typedef ::sol::types<__VA_ARGS__> type; \
+ }; \
+ } \
+ static_assert(true, "")
+#define SOL_DERIVED_CLASSES(T, ...) \
+ namespace sol { \
+ template <> \
+ struct derive<T> : std::true_type { \
+ typedef ::sol::types<__VA_ARGS__> type; \
+ }; \
+ } \
+ static_assert(true, "")
+
+#endif // SOL_FORWARD_HPP
+// end of sol/forward.hpp
+
+// beginning of sol/forward_detail.hpp
+
+#ifndef SOL_FORWARD_DETAIL_HPP
+#define SOL_FORWARD_DETAIL_HPP
+
+// beginning of sol/traits.hpp
+
+// beginning of sol/tuple.hpp
+
+// beginning of sol/base_traits.hpp
+
+#include <type_traits>
+
+namespace sol {
+ namespace detail {
+ struct unchecked_t { };
+ const unchecked_t unchecked = unchecked_t {};
+ } // namespace detail
+
+ namespace meta {
+ using sfinae_yes_t = std::true_type;
+ using sfinae_no_t = std::false_type;
+
+ template <typename...>
+ using void_t = void;
+
+ template <typename T>
+ using unqualified = std::remove_cv<std::remove_reference_t<T>>;
+
+ template <typename T>
+ using unqualified_t = typename unqualified<T>::type;
+
+ namespace meta_detail {
+ template <typename T>
+ struct unqualified_non_alias : unqualified<T> { };
+
+ template <template <class...> class Test, class, class... Args>
+ struct is_detected : std::false_type { };
+
+ template <template <class...> class Test, class... Args>
+ struct is_detected<Test, void_t<Test<Args...>>, Args...> : std::true_type { };
+ } // namespace meta_detail
+
+ template <template <class...> class Trait, class... Args>
+ using is_detected = typename meta_detail::is_detected<Trait, void, Args...>::type;
+
+ template <template <class...> class Trait, class... Args>
+ constexpr inline bool is_detected_v = is_detected<Trait, Args...>::value;
+
+ template <typename _Default, typename _Void, template <typename...> typename _Op, typename... _Args>
+ class detector {
+ public:
+ using value_t = ::std::false_type;
+ using type = _Default;
+ };
+
+ template <typename _Default, template <typename...> typename _Op, typename... _Args>
+ class detector<_Default, void_t<_Op<_Args...>>, _Op, _Args...> {
+ public:
+ using value_t = ::std::true_type;
+ using type = _Op<_Args...>;
+ };
+
+ class nonesuch {
+ public:
+ ~nonesuch() = delete;
+ nonesuch(nonesuch const&) = delete;
+ nonesuch& operator=(nonesuch const&) = delete;
+ };
+
+ template <template <typename...> typename _Op, typename... _Args>
+ using detected_t = typename detector<nonesuch, void, _Op, _Args...>::type;
+
+ template <typename _Default, template <typename...> typename _Op, typename... _Args>
+ using detected_or = detector<_Default, void, _Op, _Args...>;
+
+ template <typename _Default, template <typename...> typename _Op, typename... _Args>
+ using detected_or_t = typename detector<_Default, void, _Op, _Args...>::type;
+
+ template <typename _Default, template <typename...> typename _Op, typename... _Args>
+ constexpr inline bool detected_or_v = detector<_Default, void, _Op, _Args...>::value;
+
+ template <std::size_t I>
+ using index_value = std::integral_constant<std::size_t, I>;
+
+ template <bool>
+ struct conditional {
+ template <typename T, typename U>
+ using type = T;
+ };
+
+ template <>
+ struct conditional<false> {
+ template <typename T, typename U>
+ using type = U;
+ };
+
+ template <bool B, typename T, typename U>
+ using conditional_t = typename conditional<B>::template type<T, U>;
+
+ namespace meta_detail {
+ template <typename T, template <typename...> class Templ>
+ struct is_specialization_of : std::false_type { };
+ template <typename... T, template <typename...> class Templ>
+ struct is_specialization_of<Templ<T...>, Templ> : std::true_type { };
+ } // namespace meta_detail
+
+ template <typename T, template <typename...> class Templ>
+ using is_specialization_of = meta_detail::is_specialization_of<std::remove_cv_t<T>, Templ>;
+
+ template <typename T, template <typename...> class Templ>
+ inline constexpr bool is_specialization_of_v = is_specialization_of<std::remove_cv_t<T>, Templ>::value;
+
+ template <typename T>
+ struct identity {
+ typedef T type;
+ };
+
+ template <typename T>
+ using identity_t = typename identity<T>::type;
+
+ template <typename T>
+ using is_builtin_type = std::integral_constant<bool, std::is_arithmetic<T>::value || std::is_pointer<T>::value || std::is_array<T>::value>;
+
+ namespace meta_detail {
+ template <typename T, typename = void>
+ struct has_internal_marker_impl : std::false_type { };
+ template <typename T>
+ struct has_internal_marker_impl<T, void_t<typename T::SOL_INTERNAL_UNSPECIALIZED_MARKER_>> : std::true_type { };
+
+ template <typename T>
+ using has_internal_marker = has_internal_marker_impl<T>;
+
+ template <typename T>
+ constexpr inline bool has_internal_marker_v = has_internal_marker<T>::value;
+ } // namespace meta_detail
+
+ } // namespace meta
+} // namespace sol
+
+// end of sol/base_traits.hpp
+
+#include <tuple>
+#include <cstddef>
+
+namespace sol {
+ namespace detail {
+ using swallow = std::initializer_list<int>;
+ } // namespace detail
+
+ namespace meta {
+ template <typename T>
+ using is_tuple = is_specialization_of<T, std::tuple>;
+
+ template <typename T>
+ constexpr inline bool is_tuple_v = is_tuple<T>::value;
+
+ namespace detail {
+ template <typename... Args>
+ struct tuple_types_ {
+ typedef types<Args...> type;
+ };
+
+ template <typename... Args>
+ struct tuple_types_<std::tuple<Args...>> {
+ typedef types<Args...> type;
+ };
+ } // namespace detail
+
+ template <typename... Args>
+ using tuple_types = typename detail::tuple_types_<Args...>::type;
+
+ template <typename Arg>
+ struct pop_front_type;
+
+ template <typename Arg>
+ using pop_front_type_t = typename pop_front_type<Arg>::type;
+
+ template <typename... Args>
+ struct pop_front_type<types<Args...>> {
+ typedef void front_type;
+ typedef types<Args...> type;
+ };
+
+ template <typename Arg, typename... Args>
+ struct pop_front_type<types<Arg, Args...>> {
+ typedef Arg front_type;
+ typedef types<Args...> type;
+ };
+
+ template <std::size_t N, typename Tuple>
+ using tuple_element = std::tuple_element<N, std::remove_reference_t<Tuple>>;
+
+ template <std::size_t N, typename Tuple>
+ using tuple_element_t = std::tuple_element_t<N, std::remove_reference_t<Tuple>>;
+
+ template <std::size_t N, typename Tuple>
+ using unqualified_tuple_element = unqualified<tuple_element_t<N, Tuple>>;
+
+ template <std::size_t N, typename Tuple>
+ using unqualified_tuple_element_t = unqualified_t<tuple_element_t<N, Tuple>>;
+
+ } // namespace meta
+} // namespace sol
+
+// end of sol/tuple.hpp
+
+// beginning of sol/bind_traits.hpp
+
+namespace sol { namespace meta {
+ namespace meta_detail {
+ template <typename F>
+ using detect_deducible_signature = decltype(&F::operator());
+ } // namespace meta_detail
+
+ template <typename F>
+ using call_operator_deducible = typename is_detected<meta_detail::detect_deducible_signature, F>::type;
+
+ template <typename F>
+ constexpr inline bool call_operator_deducible_v = call_operator_deducible<F>::value;
+
+ namespace meta_detail {
+
+ template <std::size_t I, typename T>
+ struct void_tuple_element : meta::tuple_element<I, T> { };
+
+ template <std::size_t I>
+ struct void_tuple_element<I, std::tuple<>> {
+ typedef void type;
+ };
+
+ template <std::size_t I, typename T>
+ using void_tuple_element_t = typename void_tuple_element<I, T>::type;
+
+ template <bool it_is_noexcept, bool has_c_variadic, typename T, typename R, typename... Args>
+ struct basic_traits {
+ private:
+ using first_type = meta::conditional_t<std::is_void<T>::value, int, T>&;
+
+ public:
+ inline static constexpr const bool is_noexcept = it_is_noexcept;
+ inline static constexpr bool is_member_function = std::is_void<T>::value;
+ inline static constexpr bool has_c_var_arg = has_c_variadic;
+ inline static constexpr std::size_t arity = sizeof...(Args);
+ inline static constexpr std::size_t free_arity = sizeof...(Args) + static_cast<std::size_t>(!std::is_void<T>::value);
+ typedef types<Args...> args_list;
+ typedef std::tuple<Args...> args_tuple;
+ typedef T object_type;
+ typedef R return_type;
+ typedef tuple_types<R> returns_list;
+ typedef R(function_type)(Args...);
+ typedef meta::conditional_t<std::is_void<T>::value, args_list, types<first_type, Args...>> free_args_list;
+ typedef meta::conditional_t<std::is_void<T>::value, R(Args...), R(first_type, Args...)> free_function_type;
+ typedef meta::conditional_t<std::is_void<T>::value, R (*)(Args...), R (*)(first_type, Args...)> free_function_pointer_type;
+ typedef std::remove_pointer_t<free_function_pointer_type> signature_type;
+ template <std::size_t i>
+ using arg_at = void_tuple_element_t<i, args_tuple>;
+ };
+
+ template <typename Signature, bool b = call_operator_deducible<Signature>::value>
+ struct fx_traits : public basic_traits<false, false, void, void> { };
+
+ // Free Functions
+ template <typename R, typename... Args>
+ struct fx_traits<R(Args...), false> : public basic_traits<false, false, void, R, Args...> {
+ typedef R (*function_pointer_type)(Args...);
+ };
+
+ template <typename R, typename... Args>
+ struct fx_traits<R (*)(Args...), false> : public basic_traits<false, false, void, R, Args...> {
+ typedef R (*function_pointer_type)(Args...);
+ };
+
+ template <typename R, typename... Args>
+ struct fx_traits<R(Args..., ...), false> : public basic_traits<false, true, void, R, Args...> {
+ typedef R (*function_pointer_type)(Args..., ...);
+ };
+
+ template <typename R, typename... Args>
+ struct fx_traits<R (*)(Args..., ...), false> : public basic_traits<false, true, void, R, Args...> {
+ typedef R (*function_pointer_type)(Args..., ...);
+ };
+
+ // Member Functions
+ /* C-Style Variadics */
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...), false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...);
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...), false> : public basic_traits<false, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...);
+ };
+
+ /* Const Volatile */
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const, false> : public basic_traits<false, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const volatile, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const volatile;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const volatile, false> : public basic_traits<false, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const volatile;
+ };
+
+ /* Member Function Qualifiers */
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...)&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) &;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...)&, false> : public basic_traits<false, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) &;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const&, false> : public basic_traits<false, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const volatile&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const volatile&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const volatile&, false> : public basic_traits<false, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const volatile&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...)&&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) &&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...)&&, false> : public basic_traits<false, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) &&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const&&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const&&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const&&, false> : public basic_traits<false, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const&&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const volatile&&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const volatile&&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const volatile&&, false> : public basic_traits<false, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const volatile&&;
+ };
+
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+
+ template <typename R, typename... Args>
+ struct fx_traits<R(Args...) noexcept, false> : public basic_traits<true, false, void, R, Args...> {
+ typedef R (*function_pointer_type)(Args...) noexcept;
+ };
+
+ template <typename R, typename... Args>
+ struct fx_traits<R (*)(Args...) noexcept, false> : public basic_traits<true, false, void, R, Args...> {
+ typedef R (*function_pointer_type)(Args...) noexcept;
+ };
+
+ template <typename R, typename... Args>
+ struct fx_traits<R(Args..., ...) noexcept, false> : public basic_traits<true, true, void, R, Args...> {
+ typedef R (*function_pointer_type)(Args..., ...) noexcept;
+ };
+
+ template <typename R, typename... Args>
+ struct fx_traits<R (*)(Args..., ...) noexcept, false> : public basic_traits<true, true, void, R, Args...> {
+ typedef R (*function_pointer_type)(Args..., ...) noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) noexcept;
+ };
+
+ /* Const Volatile */
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const volatile noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const volatile noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const volatile noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const volatile noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...)& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) & noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...)& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) & noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const& noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const& noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const volatile& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const volatile& noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const volatile& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const volatile& noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...)&& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) && noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...)&& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) && noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const&& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const&& noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const&& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const&& noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args...) const volatile&& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args...) const volatile&& noexcept;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (T::*)(Args..., ...) const volatile&& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (T::*function_pointer_type)(Args..., ...) const volatile&& noexcept;
+ };
+
+#endif // noexcept is part of a function's type
+
+#if SOL_IS_ON(SOL_COMPILER_VCXX) && SOL_IS_ON(SOL_PLATFORM_X86)
+ template <typename R, typename... Args>
+ struct fx_traits<R __stdcall(Args...), false> : public basic_traits<false, false, void, R, Args...> {
+ typedef R(__stdcall* function_pointer_type)(Args...);
+ };
+
+ template <typename R, typename... Args>
+ struct fx_traits<R(__stdcall*)(Args...), false> : public basic_traits<false, false, void, R, Args...> {
+ typedef R(__stdcall* function_pointer_type)(Args...);
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...), false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...);
+ };
+
+ /* Const Volatile */
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const volatile, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const volatile;
+ };
+
+ /* Member Function Qualifiers */
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...)&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) &;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const volatile&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const volatile&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...)&&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) &&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const&&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const&&;
+ };
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const volatile&&, false> : public basic_traits<false, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const volatile&&;
+ };
+
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+
+ template <typename R, typename... Args>
+ struct fx_traits<R __stdcall(Args...) noexcept, false> : public basic_traits<true, false, void, R, Args...> {
+ typedef R(__stdcall* function_pointer_type)(Args...) noexcept;
+ };
+
+ template <typename R, typename... Args>
+ struct fx_traits<R(__stdcall*)(Args...) noexcept, false> : public basic_traits<true, false, void, R, Args...> {
+ typedef R(__stdcall* function_pointer_type)(Args...) noexcept;
+ };
+
+ /* __stdcall cannot be applied to functions with varargs*/
+ /*template <typename R, typename... Args>
+ struct fx_traits<__stdcall R(Args..., ...) noexcept, false> : public basic_traits<true, true, void, R, Args...> {
+ typedef R(__stdcall* function_pointer_type)(Args..., ...) noexcept;
+ };
+
+ template <typename R, typename... Args>
+ struct fx_traits<R (__stdcall *)(Args..., ...) noexcept, false> : public basic_traits<true, true, void, R, Args...> {
+ typedef R(__stdcall* function_pointer_type)(Args..., ...) noexcept;
+ };*/
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) noexcept;
+ };
+
+ /* __stdcall does not work with varargs */
+ /*template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args..., ...) noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args..., ...) noexcept;
+ };*/
+
+ /* Const Volatile */
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const noexcept;
+ };
+
+ /* __stdcall does not work with varargs */
+ /*template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args..., ...) const noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args..., ...) const noexcept;
+ };*/
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const volatile noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const volatile noexcept;
+ };
+
+ /* __stdcall does not work with varargs */
+ /*template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args..., ...) const volatile noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args..., ...) const volatile noexcept;
+ };*/
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...)& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) & noexcept;
+ };
+
+ /* __stdcall does not work with varargs */
+ /*template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args..., ...) & noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args..., ...) & noexcept;
+ };*/
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const& noexcept;
+ };
+
+ /* __stdcall does not work with varargs */
+ /*template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args..., ...) const& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args..., ...) const& noexcept;
+ };*/
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const volatile& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const volatile& noexcept;
+ };
+
+ /* __stdcall does not work with varargs */
+ /*template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args..., ...) const volatile& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args..., ...) const volatile& noexcept;
+ };*/
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...)&& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) && noexcept;
+ };
+
+ /* __stdcall does not work with varargs */
+ /*template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args..., ...) && noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args..., ...) && noexcept;
+ };*/
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const&& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const&& noexcept;
+ };
+
+ /* __stdcall does not work with varargs */
+ /*template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args..., ...) const&& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args..., ...) const&& noexcept;
+ };*/
+
+ template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args...) const volatile&& noexcept, false> : public basic_traits<true, false, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args...) const volatile&& noexcept;
+ };
+
+ /* __stdcall does not work with varargs */
+ /*template <typename T, typename R, typename... Args>
+ struct fx_traits<R (__stdcall T::*)(Args..., ...) const volatile&& noexcept, false> : public basic_traits<true, true, T, R, Args...> {
+ typedef R (__stdcall T::*function_pointer_type)(Args..., ...) const volatile&& noexcept;
+ };*/
+#endif // noexcept is part of a function's type
+#endif // __stdcall x86 VC++ bug
+
+ template <typename Signature>
+ struct fx_traits<Signature, true> : public fx_traits<typename fx_traits<decltype(&Signature::operator())>::function_type, false> { };
+
+ template <typename Signature, bool b = std::is_member_object_pointer<Signature>::value>
+ struct callable_traits : public fx_traits<std::decay_t<Signature>> { };
+
+ template <typename R, typename T>
+ struct callable_traits<R(T::*), true> {
+ typedef meta::conditional_t<std::is_array_v<R>, std::add_lvalue_reference_t<R>, R> return_type;
+ typedef return_type Arg;
+ typedef T object_type;
+ using signature_type = R(T::*);
+ inline static constexpr bool is_noexcept = false;
+ inline static constexpr bool is_member_function = false;
+ inline static constexpr std::size_t arity = 1;
+ inline static constexpr std::size_t free_arity = 2;
+ typedef std::tuple<Arg> args_tuple;
+ typedef types<Arg> args_list;
+ typedef types<T, Arg> free_args_list;
+ typedef meta::tuple_types<return_type> returns_list;
+ typedef return_type(function_type)(T&, return_type);
+ typedef return_type (*function_pointer_type)(T&, Arg);
+ typedef return_type (*free_function_pointer_type)(T&, Arg);
+ template <std::size_t i>
+ using arg_at = void_tuple_element_t<i, args_tuple>;
+ };
+
+ } // namespace meta_detail
+
+ template <typename Signature>
+ using bind_traits = meta_detail::callable_traits<Signature>;
+
+ namespace meta_detail {
+ template <typename, bool>
+ struct is_probably_stateless_lambda : std::false_type { };
+
+ template <typename T>
+ struct is_probably_stateless_lambda<T, true> : std::is_convertible<T, typename bind_traits<T>::function_type*>::type { };
+ } // namespace meta_detail
+
+ template <typename T>
+ using is_probably_stateless_lambda = typename meta_detail::is_probably_stateless_lambda<T, std::is_empty_v<T> && call_operator_deducible_v<T>>::type;
+
+ template <typename T>
+ inline constexpr bool is_probably_stateless_lambda_v = is_probably_stateless_lambda<T>::value;
+
+ template <typename Signature>
+ using function_args_t = typename bind_traits<Signature>::args_list;
+
+ template <typename Signature>
+ using function_signature_t = typename bind_traits<Signature>::signature_type;
+
+ template <typename Signature>
+ using function_return_t = typename bind_traits<Signature>::return_type;
+}} // namespace sol::meta
+
+// end of sol/bind_traits.hpp
+
+// beginning of sol/pointer_like.hpp
+
+#include <utility>
+#include <type_traits>
+#include <memory>
+
+namespace sol {
+
+ namespace meta {
+ namespace meta_detail {
+ template <typename T>
+ using is_dereferenceable_test = decltype(*std::declval<T>());
+
+ template <typename T>
+ using is_explicitly_dereferenceable_test = decltype(std::declval<T>().operator*());
+ } // namespace meta_detail
+
+ template <typename T>
+ using is_pointer_like = std::integral_constant<bool,
+ !std::is_array_v<T> && (std::is_pointer_v<T> || is_detected_v<meta_detail::is_explicitly_dereferenceable_test, T>)>;
+
+ template <typename T>
+ constexpr inline bool is_pointer_like_v = is_pointer_like<T>::value;
+ } // namespace meta
+
+ namespace detail {
+
+ template <typename T>
+ auto unwrap(T&& item) -> decltype(std::forward<T>(item)) {
+ return std::forward<T>(item);
+ }
+
+ template <typename T>
+ T& unwrap(std::reference_wrapper<T> arg) {
+ return arg.get();
+ }
+
+ template <typename T>
+ inline decltype(auto) deref(T&& item) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::is_pointer_like_v<Tu>) {
+ return *std::forward<T>(item);
+ }
+ else {
+ return std::forward<T>(item);
+ }
+ }
+
+ template <typename T>
+ inline decltype(auto) deref_move_only(T&& item) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::is_pointer_like_v<Tu> && !std::is_pointer_v<Tu> && !std::is_copy_constructible_v<Tu>) {
+ return *std::forward<T>(item);
+ }
+ else {
+ return std::forward<T>(item);
+ }
+ }
+
+ template <typename T>
+ inline T* ptr(T& val) {
+ return std::addressof(val);
+ }
+
+ template <typename T>
+ inline T* ptr(std::reference_wrapper<T> val) {
+ return std::addressof(val.get());
+ }
+
+ template <typename T>
+ inline T* ptr(T* val) {
+ return val;
+ }
+ } // namespace detail
+} // namespace sol
+
+// end of sol/pointer_like.hpp
+
+// beginning of sol/string_view.hpp
+
+#include <cstddef>
+#include <string>
+#include <string_view>
+#include <functional>
+
+namespace sol {
+ template <typename C, typename T = std::char_traits<C>>
+ using basic_string_view = std::basic_string_view<C, T>;
+
+ typedef std::string_view string_view;
+ typedef std::wstring_view wstring_view;
+ typedef std::u16string_view u16string_view;
+ typedef std::u32string_view u32string_view;
+ typedef std::hash<std::string_view> string_view_hash;
+} // namespace sol
+
+// end of sol/string_view.hpp
+
+#include <type_traits>
+#include <cstdint>
+#include <memory>
+#include <functional>
+#include <array>
+#include <iterator>
+#include <iosfwd>
+#if SOL_IS_ON(SOL_STD_VARIANT)
+#include <variant>
+#endif // variant is weird on XCode, thanks XCode
+
+namespace sol { namespace meta {
+ template <typename T>
+ struct unwrapped {
+ typedef T type;
+ };
+
+ template <typename T>
+ struct unwrapped<std::reference_wrapper<T>> {
+ typedef T type;
+ };
+
+ template <typename T>
+ using unwrapped_t = typename unwrapped<T>::type;
+
+ template <typename T>
+ struct unwrap_unqualified : unwrapped<unqualified_t<T>> { };
+
+ template <typename T>
+ using unwrap_unqualified_t = typename unwrap_unqualified<T>::type;
+
+ template <typename T>
+ struct remove_member_pointer;
+
+ template <typename R, typename T>
+ struct remove_member_pointer<R T::*> {
+ typedef R type;
+ };
+
+ template <typename R, typename T>
+ struct remove_member_pointer<R T::*const> {
+ typedef R type;
+ };
+
+ template <typename T>
+ using remove_member_pointer_t = remove_member_pointer<T>;
+
+ template <typename T, typename...>
+ struct all_same : std::true_type { };
+
+ template <typename T, typename U, typename... Args>
+ struct all_same<T, U, Args...> : std::integral_constant<bool, std::is_same<T, U>::value && all_same<T, Args...>::value> { };
+
+ template <typename T, typename...>
+ struct any_same : std::false_type { };
+
+ template <typename T, typename U, typename... Args>
+ struct any_same<T, U, Args...> : std::integral_constant<bool, std::is_same<T, U>::value || any_same<T, Args...>::value> { };
+
+ template <typename T, typename... Args>
+ constexpr inline bool any_same_v = any_same<T, Args...>::value;
+
+ template <bool B>
+ using boolean = std::integral_constant<bool, B>;
+
+ template <bool B>
+ constexpr inline bool boolean_v = boolean<B>::value;
+
+ template <typename T>
+ using neg = boolean<!T::value>;
+
+ template <typename T>
+ constexpr inline bool neg_v = neg<T>::value;
+
+ template <typename... Args>
+ struct all : boolean<true> { };
+
+ template <typename T, typename... Args>
+ struct all<T, Args...> : std::conditional_t<T::value, all<Args...>, boolean<false>> { };
+
+ template <typename... Args>
+ struct any : boolean<false> { };
+
+ template <typename T, typename... Args>
+ struct any<T, Args...> : std::conditional_t<T::value, boolean<true>, any<Args...>> { };
+
+ template <typename... Args>
+ constexpr inline bool all_v = all<Args...>::value;
+
+ template <typename... Args>
+ constexpr inline bool any_v = any<Args...>::value;
+
+ enum class enable_t { _ };
+
+ constexpr const auto enabler = enable_t::_;
+
+ template <bool value, typename T = void>
+ using disable_if_t = std::enable_if_t<!value, T>;
+
+ template <typename... Args>
+ using enable = std::enable_if_t<all<Args...>::value, enable_t>;
+
+ template <typename... Args>
+ using disable = std::enable_if_t<neg<all<Args...>>::value, enable_t>;
+
+ template <typename... Args>
+ using enable_any = std::enable_if_t<any<Args...>::value, enable_t>;
+
+ template <typename... Args>
+ using disable_any = std::enable_if_t<neg<any<Args...>>::value, enable_t>;
+
+ template <typename V, typename... Vs>
+ struct find_in_pack_v : boolean<false> { };
+
+ template <typename V, typename Vs1, typename... Vs>
+ struct find_in_pack_v<V, Vs1, Vs...> : any<boolean<(V::value == Vs1::value)>, find_in_pack_v<V, Vs...>> { };
+
+ namespace meta_detail {
+ template <std::size_t I, typename T, typename... Args>
+ struct index_in_pack : std::integral_constant<std::size_t, SIZE_MAX> { };
+
+ template <std::size_t I, typename T, typename T1, typename... Args>
+ struct index_in_pack<I, T, T1, Args...>
+ : conditional_t<std::is_same<T, T1>::value, std::integral_constant<std::ptrdiff_t, I>, index_in_pack<I + 1, T, Args...>> { };
+ } // namespace meta_detail
+
+ template <typename T, typename... Args>
+ struct index_in_pack : meta_detail::index_in_pack<0, T, Args...> { };
+
+ template <typename T, typename List>
+ struct index_in : meta_detail::index_in_pack<0, T, List> { };
+
+ template <typename T, typename... Args>
+ struct index_in<T, types<Args...>> : meta_detail::index_in_pack<0, T, Args...> { };
+
+ template <std::size_t I, typename... Args>
+ struct at_in_pack { };
+
+ template <std::size_t I, typename... Args>
+ using at_in_pack_t = typename at_in_pack<I, Args...>::type;
+
+ template <std::size_t I, typename Arg, typename... Args>
+ struct at_in_pack<I, Arg, Args...> : std::conditional<I == 0, Arg, at_in_pack_t<I - 1, Args...>> { };
+
+ template <typename Arg, typename... Args>
+ struct at_in_pack<0, Arg, Args...> {
+ typedef Arg type;
+ };
+
+ namespace meta_detail {
+ template <typename, typename TI>
+ using on_even = meta::boolean<(TI::value % 2) == 0>;
+
+ template <typename, typename TI>
+ using on_odd = meta::boolean<(TI::value % 2) == 1>;
+
+ template <typename, typename>
+ using on_always = std::true_type;
+
+ template <template <typename...> class When, std::size_t Limit, std::size_t I, template <typename...> class Pred, typename... Ts>
+ struct count_when_for_pack : std::integral_constant<std::size_t, 0> { };
+ template <template <typename...> class When, std::size_t Limit, std::size_t I, template <typename...> class Pred, typename T, typename... Ts>
+ struct count_when_for_pack<When, Limit, I, Pred, T, Ts...> : conditional_t < sizeof...(Ts)
+ == 0
+ || Limit<2, std::integral_constant<std::size_t, I + static_cast<std::size_t>(Limit != 0 && Pred<T>::value)>,
+ count_when_for_pack<When, Limit - static_cast<std::size_t>(When<T, std::integral_constant<std::size_t, I>>::value),
+ I + static_cast<std::size_t>(When<T, std::integral_constant<std::size_t, I>>::value&& Pred<T>::value), Pred, Ts...>> { };
+ } // namespace meta_detail
+
+ template <template <typename...> class Pred, typename... Ts>
+ struct count_for_pack : meta_detail::count_when_for_pack<meta_detail::on_always, sizeof...(Ts), 0, Pred, Ts...> { };
+
+ template <template <typename...> class Pred, typename... Ts>
+ inline constexpr std::size_t count_for_pack_v = count_for_pack<Pred, Ts...>::value;
+
+ template <template <typename...> class Pred, typename List>
+ struct count_for;
+
+ template <template <typename...> class Pred, typename... Args>
+ struct count_for<Pred, types<Args...>> : count_for_pack<Pred, Args...> { };
+
+ template <std::size_t Limit, template <typename...> class Pred, typename... Ts>
+ struct count_for_to_pack : meta_detail::count_when_for_pack<meta_detail::on_always, Limit, 0, Pred, Ts...> { };
+
+ template <std::size_t Limit, template <typename...> class Pred, typename... Ts>
+ inline constexpr std::size_t count_for_to_pack_v = count_for_to_pack<Limit, Pred, Ts...>::value;
+
+ template <template <typename...> class When, std::size_t Limit, template <typename...> class Pred, typename... Ts>
+ struct count_when_for_to_pack : meta_detail::count_when_for_pack<When, Limit, 0, Pred, Ts...> { };
+
+ template <template <typename...> class When, std::size_t Limit, template <typename...> class Pred, typename... Ts>
+ inline constexpr std::size_t count_when_for_to_pack_v = count_when_for_to_pack<When, Limit, Pred, Ts...>::value;
+
+ template <template <typename...> class Pred, typename... Ts>
+ using count_even_for_pack = count_when_for_to_pack<meta_detail::on_even, sizeof...(Ts), Pred, Ts...>;
+
+ template <template <typename...> class Pred, typename... Ts>
+ inline constexpr std::size_t count_even_for_pack_v = count_even_for_pack<Pred, Ts...>::value;
+
+ template <template <typename...> class Pred, typename... Ts>
+ using count_odd_for_pack = count_when_for_to_pack<meta_detail::on_odd, sizeof...(Ts), Pred, Ts...>;
+
+ template <template <typename...> class Pred, typename... Ts>
+ inline constexpr std::size_t count_odd_for_pack_v = count_odd_for_pack<Pred, Ts...>::value;
+
+ template <typename... Args>
+ struct return_type {
+ typedef std::tuple<Args...> type;
+ };
+
+ template <typename T>
+ struct return_type<T> {
+ typedef T type;
+ };
+
+ template <>
+ struct return_type<> {
+ typedef void type;
+ };
+
+ template <typename... Args>
+ using return_type_t = typename return_type<Args...>::type;
+
+ namespace meta_detail {
+ template <typename>
+ struct always_true : std::true_type { };
+ struct is_invokable_tester {
+ template <typename Fun, typename... Args>
+ static always_true<decltype(std::declval<Fun>()(std::declval<Args>()...))> test(int);
+ template <typename...>
+ static std::false_type test(...);
+ };
+ } // namespace meta_detail
+
+ template <typename T>
+ struct is_invokable;
+ template <typename Fun, typename... Args>
+ struct is_invokable<Fun(Args...)> : decltype(meta_detail::is_invokable_tester::test<Fun, Args...>(0)) { };
+
+ namespace meta_detail {
+
+ template <typename T, typename = void>
+ struct is_invocable : std::is_function<std::remove_pointer_t<T>> { };
+
+ template <typename T>
+ struct is_invocable<T,
+ std::enable_if_t<std::is_final<unqualified_t<T>>::value && std::is_class<unqualified_t<T>>::value
+ && std::is_same<decltype(void(&T::operator())), void>::value>> { };
+
+ template <typename T>
+ struct is_invocable<T,
+ std::enable_if_t<!std::is_final<unqualified_t<T>>::value && std::is_class<unqualified_t<T>>::value
+ && std::is_destructible<unqualified_t<T>>::value>> {
+ struct F {
+ void operator()() {};
+ };
+ struct Derived : T, F { };
+ template <typename U, U>
+ struct Check;
+
+ template <typename V>
+ static sfinae_no_t test(Check<void (F::*)(), &V::operator()>*);
+
+ template <typename>
+ static sfinae_yes_t test(...);
+
+ static constexpr bool value = std::is_same_v<decltype(test<Derived>(0)), sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct is_invocable<T,
+ std::enable_if_t<!std::is_final<unqualified_t<T>>::value && std::is_class<unqualified_t<T>>::value
+ && !std::is_destructible<unqualified_t<T>>::value>> {
+ struct F {
+ void operator()() {};
+ };
+ struct Derived : T, F {
+ ~Derived() = delete;
+ };
+ template <typename U, U>
+ struct Check;
+
+ template <typename V>
+ static sfinae_no_t test(Check<void (F::*)(), &V::operator()>*);
+
+ template <typename>
+ static sfinae_yes_t test(...);
+
+ static constexpr bool value = std::is_same_v<decltype(test<Derived>(0)), sfinae_yes_t>;
+ };
+
+ struct has_begin_end_impl {
+ template <typename T, typename U = unqualified_t<T>, typename B = decltype(std::declval<U&>().begin()),
+ typename E = decltype(std::declval<U&>().end())>
+ static std::true_type test(int);
+
+ template <typename...>
+ static std::false_type test(...);
+ };
+
+ struct has_key_type_impl {
+ template <typename T, typename U = unqualified_t<T>, typename V = typename U::key_type>
+ static std::true_type test(int);
+
+ template <typename...>
+ static std::false_type test(...);
+ };
+
+ struct has_key_comp_impl {
+ template <typename T, typename V = decltype(std::declval<unqualified_t<T>>().key_comp())>
+ static std::true_type test(int);
+
+ template <typename...>
+ static std::false_type test(...);
+ };
+
+ struct has_load_factor_impl {
+ template <typename T, typename V = decltype(std::declval<unqualified_t<T>>().load_factor())>
+ static std::true_type test(int);
+
+ template <typename...>
+ static std::false_type test(...);
+ };
+
+ struct has_mapped_type_impl {
+ template <typename T, typename V = typename unqualified_t<T>::mapped_type>
+ static std::true_type test(int);
+
+ template <typename...>
+ static std::false_type test(...);
+ };
+
+ struct has_value_type_impl {
+ template <typename T, typename V = typename unqualified_t<T>::value_type>
+ static std::true_type test(int);
+
+ template <typename...>
+ static std::false_type test(...);
+ };
+
+ struct has_iterator_impl {
+ template <typename T, typename V = typename unqualified_t<T>::iterator>
+ static std::true_type test(int);
+
+ template <typename...>
+ static std::false_type test(...);
+ };
+
+ struct has_key_value_pair_impl {
+ template <typename T, typename U = unqualified_t<T>, typename V = typename U::value_type, typename F = decltype(std::declval<V&>().first),
+ typename S = decltype(std::declval<V&>().second)>
+ static std::true_type test(int);
+
+ template <typename...>
+ static std::false_type test(...);
+ };
+
+ template <typename T>
+ struct has_push_back_test {
+ private:
+ template <typename C>
+ static sfinae_yes_t test(decltype(std::declval<C>().push_back(std::declval<std::add_rvalue_reference_t<typename C::value_type>>()))*);
+ template <typename C>
+ static sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_insert_with_iterator_test {
+ private:
+ template <typename C>
+ static sfinae_yes_t test(decltype(std::declval<C>().insert(
+ std::declval<std::add_rvalue_reference_t<typename C::iterator>>(), std::declval<std::add_rvalue_reference_t<typename C::value_type>>()))*);
+ template <typename C>
+ static sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = !std::is_same_v<decltype(test<T>(0)), sfinae_no_t>;
+ };
+
+ template <typename T>
+ struct has_insert_test {
+ private:
+ template <typename C>
+ static sfinae_yes_t test(decltype(std::declval<C>().insert(std::declval<std::add_rvalue_reference_t<typename C::value_type>>()))*);
+ template <typename C>
+ static sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = !std::is_same_v<decltype(test<T>(0)), sfinae_no_t>;
+ };
+
+ template <typename T>
+ struct has_insert_after_test {
+ private:
+ template <typename C>
+ static sfinae_yes_t test(decltype(std::declval<C>().insert_after(std::declval<std::add_rvalue_reference_t<typename C::const_iterator>>(),
+ std::declval<std::add_rvalue_reference_t<typename C::value_type>>()))*);
+ template <typename C>
+ static sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_size_test {
+ private:
+ template <typename C>
+ static sfinae_yes_t test(decltype(std::declval<C>().size())*);
+ template <typename C>
+ static sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_max_size_test {
+ private:
+ template <typename C>
+ static sfinae_yes_t test(decltype(std::declval<C>().max_size())*);
+ template <typename C>
+ static sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_to_string_test {
+ private:
+ template <typename C>
+ static sfinae_yes_t test(decltype(std::declval<C>().to_string())*);
+ template <typename C>
+ static sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), sfinae_yes_t>;
+ };
+
+ template <typename T, typename U, typename = void>
+ class supports_op_less_test : public std::false_type { };
+ template <typename T, typename U>
+ class supports_op_less_test<T, U, void_t<decltype(std::declval<T&>() < std::declval<U&>())>>
+ : public std::integral_constant<bool,
+#if SOL_IS_ON(SOL_STD_VARIANT)
+ !is_specialization_of_v<unqualified_t<T>, std::variant> && !is_specialization_of_v<unqualified_t<U>, std::variant>
+#else
+ true
+#endif
+ > {
+ };
+
+ template <typename T, typename U, typename = void>
+ class supports_op_equal_test : public std::false_type { };
+ template <typename T, typename U>
+ class supports_op_equal_test<T, U, void_t<decltype(std::declval<T&>() == std::declval<U&>())>>
+ : public std::integral_constant<bool,
+#if SOL_IS_ON(SOL_STD_VARIANT)
+ !is_specialization_of_v<unqualified_t<T>, std::variant> && !is_specialization_of_v<unqualified_t<U>, std::variant>
+#else
+ true
+#endif
+ > {
+ };
+
+ template <typename T, typename U, typename = void>
+ class supports_op_less_equal_test : public std::false_type { };
+ template <typename T, typename U>
+ class supports_op_less_equal_test<T, U, void_t<decltype(std::declval<T&>() <= std::declval<U&>())>>
+ : public std::integral_constant<bool,
+#if SOL_IS_ON(SOL_STD_VARIANT)
+ !is_specialization_of_v<unqualified_t<T>, std::variant> && !is_specialization_of_v<unqualified_t<U>, std::variant>
+#else
+ true
+#endif
+ > {
+ };
+
+ template <typename T, typename U, typename = void>
+ class supports_op_left_shift_test : public std::false_type { };
+ template <typename T, typename U>
+ class supports_op_left_shift_test<T, U, void_t<decltype(std::declval<T&>() << std::declval<U&>())>> : public std::true_type { };
+
+ template <typename T, typename = void>
+ class supports_adl_to_string_test : public std::false_type { };
+ template <typename T>
+ class supports_adl_to_string_test<T, void_t<decltype(to_string(std::declval<const T&>()))>> : public std::true_type { };
+
+ template <typename T, bool b>
+ struct is_matched_lookup_impl : std::false_type { };
+ template <typename T>
+ struct is_matched_lookup_impl<T, true> : std::is_same<typename T::key_type, typename T::value_type> { };
+
+ template <typename T>
+ using non_void_t = meta::conditional_t<std::is_void_v<T>, ::sol::detail::unchecked_t, T>;
+
+ template <typename T>
+ using detect_sentinel = typename T::sentinel;
+ } // namespace meta_detail
+
+ template <typename T, typename Fallback>
+ class sentinel_or {
+ public:
+ using type = detected_or_t<Fallback, meta_detail::detect_sentinel, T>;
+ };
+
+ template <typename T, typename Fallback>
+ using sentinel_or_t = typename sentinel_or<T, Fallback>::type;
+
+ template <typename T, typename U = T>
+ class supports_op_less : public meta_detail::supports_op_less_test<T, U> { };
+
+ template <typename T, typename U = T>
+ class supports_op_equal : public meta_detail::supports_op_equal_test<T, U> { };
+
+ template <typename T, typename U = T>
+ class supports_op_less_equal : public meta_detail::supports_op_less_equal_test<T, U> { };
+
+ template <typename T, typename U = T>
+ class supports_op_left_shift : public meta_detail::supports_op_left_shift_test<T, U> { };
+
+ template <typename T>
+ class supports_adl_to_string : public meta_detail::supports_adl_to_string_test<T> { };
+
+ template <typename T>
+ class supports_to_string_member : public meta::boolean<meta_detail::has_to_string_test<meta_detail::non_void_t<T>>::value> { };
+
+ template <typename T>
+ using is_invocable = boolean<meta_detail::is_invocable<T>::value>;
+
+ template <typename T>
+ constexpr inline bool is_invocable_v = is_invocable<T>::value;
+
+ template <typename T>
+ struct has_begin_end : decltype(meta_detail::has_begin_end_impl::test<T>(0)) { };
+
+ template <typename T>
+ constexpr inline bool has_begin_end_v = has_begin_end<T>::value;
+
+ template <typename T>
+ struct has_key_value_pair : decltype(meta_detail::has_key_value_pair_impl::test<T>(0)) { };
+
+ template <typename T>
+ struct has_key_type : decltype(meta_detail::has_key_type_impl::test<T>(0)) { };
+
+ template <typename T>
+ struct has_key_comp : decltype(meta_detail::has_key_comp_impl::test<T>(0)) { };
+
+ template <typename T>
+ struct has_load_factor : decltype(meta_detail::has_load_factor_impl::test<T>(0)) { };
+
+ template <typename T>
+ struct has_mapped_type : decltype(meta_detail::has_mapped_type_impl::test<T>(0)) { };
+
+ template <typename T>
+ struct has_iterator : decltype(meta_detail::has_iterator_impl::test<T>(0)) { };
+
+ template <typename T>
+ struct has_value_type : decltype(meta_detail::has_value_type_impl::test<T>(0)) { };
+
+ template <typename T>
+ using has_push_back = meta::boolean<meta_detail::has_push_back_test<T>::value>;
+
+ template <typename T>
+ using has_max_size = meta::boolean<meta_detail::has_max_size_test<T>::value>;
+
+ template <typename T>
+ using has_insert = meta::boolean<meta_detail::has_insert_test<T>::value>;
+
+ template <typename T>
+ using has_insert_with_iterator = meta::boolean<meta_detail::has_insert_with_iterator_test<T>::value>;
+
+ template <typename T>
+ using has_insert_after = meta::boolean<meta_detail::has_insert_after_test<T>::value>;
+
+ template <typename T>
+ using has_size = meta::boolean<meta_detail::has_size_test<T>::value>;
+
+ template <typename T>
+ using is_associative = meta::all<has_key_type<T>, has_key_value_pair<T>, has_mapped_type<T>>;
+
+ template <typename T>
+ using is_lookup = meta::all<has_key_type<T>, has_value_type<T>>;
+
+ template <typename T>
+ using is_ordered = meta::all<has_key_comp<T>, meta::neg<has_load_factor<T>>>;
+
+ template <typename T>
+ using is_matched_lookup = meta_detail::is_matched_lookup_impl<T, is_lookup<T>::value>;
+
+ template <typename T>
+ using is_initializer_list = meta::is_specialization_of<T, std::initializer_list>;
+
+ template <typename T>
+ constexpr inline bool is_initializer_list_v = is_initializer_list<T>::value;
+
+ template <typename T, typename CharT = char>
+ using is_string_literal_array_of = boolean<std::is_array_v<T> && std::is_same_v<std::remove_all_extents_t<T>, CharT>>;
+
+ template <typename T, typename CharT = char>
+ constexpr inline bool is_string_literal_array_of_v = is_string_literal_array_of<T, CharT>::value;
+
+ template <typename T>
+ using is_string_literal_array = boolean<std::is_array_v<T>
+ && any_same_v<std::remove_all_extents_t<T>, char,
+#if SOL_IS_ON(SOL_CHAR8_T)
+ char8_t,
+#endif
+ char16_t, char32_t, wchar_t>>;
+
+ template <typename T>
+ constexpr inline bool is_string_literal_array_v = is_string_literal_array<T>::value;
+
+ template <typename T, typename CharT>
+ struct is_string_of : std::false_type { };
+
+ template <typename CharT, typename CharTargetT, typename TraitsT, typename AllocT>
+ struct is_string_of<std::basic_string<CharT, TraitsT, AllocT>, CharTargetT> : std::is_same<CharT, CharTargetT> { };
+
+ template <typename T, typename CharT>
+ constexpr inline bool is_string_of_v = is_string_of<T, CharT>::value;
+
+ template <typename T, typename CharT>
+ struct is_string_view_of : std::false_type { };
+
+ template <typename CharT, typename CharTargetT, typename TraitsT>
+ struct is_string_view_of<std::basic_string_view<CharT, TraitsT>, CharTargetT> : std::is_same<CharT, CharTargetT> { };
+
+ template <typename T, typename CharT>
+ constexpr inline bool is_string_view_of_v = is_string_view_of<T, CharT>::value;
+
+ template <typename T>
+ using is_string_like
+ = meta::boolean<is_specialization_of_v<T, std::basic_string> || is_specialization_of_v<T, std::basic_string_view> || is_string_literal_array_v<T>>;
+
+ template <typename T>
+ constexpr inline bool is_string_like_v = is_string_like<T>::value;
+
+ template <typename T, typename CharT = char>
+ using is_string_constructible = meta::boolean<is_string_literal_array_of_v<T, CharT> || std::is_same_v<T, const CharT*> || std::is_same_v<T, CharT>
+ || is_string_of_v<T, CharT> || std::is_same_v<T, std::initializer_list<CharT>> || is_string_view_of_v<T, CharT> || std::is_null_pointer_v<T>>;
+
+ template <typename T, typename CharT = char>
+ constexpr inline bool is_string_constructible_v = is_string_constructible<T, CharT>::value;
+
+ template <typename T>
+ using is_string_like_or_constructible = meta::boolean<is_string_like_v<T> || is_string_constructible_v<T>>;
+
+ template <typename T>
+ struct is_pair : std::false_type { };
+
+ template <typename T1, typename T2>
+ struct is_pair<std::pair<T1, T2>> : std::true_type { };
+
+ template <typename T, typename Char>
+ using is_c_str_of = any<std::is_same<T, const Char*>, std::is_same<T, Char const* const>, std::is_same<T, Char*>, is_string_literal_array_of<T, Char>>;
+
+ template <typename T, typename Char>
+ constexpr inline bool is_c_str_of_v = is_c_str_of<T, Char>::value;
+
+ template <typename T>
+ using is_c_str = is_c_str_of<T, char>;
+
+ template <typename T>
+ constexpr inline bool is_c_str_v = is_c_str<T>::value;
+
+ template <typename T, typename Char>
+ using is_c_str_or_string_of = any<is_c_str_of<T, Char>, is_string_of<T, Char>>;
+
+ template <typename T, typename Char>
+ constexpr inline bool is_c_str_or_string_of_v = is_c_str_or_string_of<T, Char>::value;
+
+ template <typename T>
+ using is_c_str_or_string = is_c_str_or_string_of<T, char>;
+
+ template <typename T>
+ constexpr inline bool is_c_str_or_string_v = is_c_str_or_string<T>::value;
+
+ template <typename T>
+ struct is_move_only : all<neg<std::is_reference<T>>, neg<std::is_copy_constructible<unqualified_t<T>>>, std::is_move_constructible<unqualified_t<T>>> { };
+
+ template <typename T>
+ using is_not_move_only = neg<is_move_only<T>>;
+
+ namespace meta_detail {
+ template <typename T>
+ decltype(auto) force_tuple(T&& x) {
+ if constexpr (meta::is_specialization_of_v<meta::unqualified_t<T>, std::tuple>) {
+ return std::forward<T>(x);
+ }
+ else {
+ return std::tuple<T>(std::forward<T>(x));
+ }
+ }
+ } // namespace meta_detail
+
+ template <typename... X>
+ decltype(auto) tuplefy(X&&... x) {
+ return std::tuple_cat(meta_detail::force_tuple(std::forward<X>(x))...);
+ }
+
+ template <typename T, typename = void>
+ struct iterator_tag {
+ using type = std::input_iterator_tag;
+ };
+
+ template <typename T>
+ struct iterator_tag<T, conditional_t<false, typename std::iterator_traits<T>::iterator_category, void>> {
+ using type = typename std::iterator_traits<T>::iterator_category;
+ };
+}} // namespace sol::meta
+
+// end of sol/traits.hpp
+
+namespace sol {
+ namespace detail {
+ const bool default_safe_function_calls =
+#if SOL_IS_ON(SOL_SAFE_FUNCTION_CALLS)
+ true;
+#else
+ false;
+#endif
+ } // namespace detail
+
+ namespace meta { namespace meta_detail {
+ }} // namespace meta::meta_detail
+
+ namespace stack { namespace stack_detail {
+ using undefined_method_func = void (*)(stack_reference);
+
+ template <typename T>
+ void set_undefined_methods_on(stack_reference);
+
+ struct undefined_metatable;
+ }} // namespace stack::stack_detail
+} // namespace sol
+
+#endif // SOL_FORWARD_DETAIL_HPP
+// end of sol/forward_detail.hpp
+
+// beginning of sol/assert.hpp
+
+#if SOL_IS_ON(SOL2_CI)
+
+struct pre_main {
+ pre_main() {
+#ifdef _MSC_VER
+ _set_abort_behavior(0, _WRITE_ABORT_MSG);
+#endif
+ }
+} inline sol2_ci_dont_lock_ci_please = {};
+
+#endif // Prevent lockup when doing Continuous Integration
+
+#if SOL_IS_ON(SOL_USER_ASSERT)
+ #define SOL_ASSERT(...) SOL_C_ASSERT(__VA_ARGS__)
+#else
+ #if SOL_IS_ON(SOL_DEBUG_BUILD)
+ #include <exception>
+ #include <iostream>
+ #include <cstdlib>
+
+ #define SOL_ASSERT(...) \
+ do { \
+ if (!(__VA_ARGS__)) { \
+ std::cerr << "Assertion `" #__VA_ARGS__ "` failed in " << __FILE__ << " line " << __LINE__ << std::endl; \
+ std::terminate(); \
+ } \
+ } while (false)
+ #else
+ #define SOL_ASSERT(...) \
+ do { \
+ if (false) { \
+ (void)(__VA_ARGS__); \
+ } \
+ } while (false)
+ #endif
+#endif
+
+#if SOL_IS_ON(SOL_USER_ASSERT_MSG)
+ #define SOL_ASSERT_MSG(message, ...) SOL_ASSERT_MSG(message, __VA_ARGS__)
+#else
+ #if SOL_IS_ON(SOL_DEBUG_BUILD)
+ #include <exception>
+ #include <iostream>
+ #include <cstdlib>
+
+ #define SOL_ASSERT_MSG(message, ...) \
+ do { \
+ if (!(__VA_ARGS__)) { \
+ std::cerr << "Assertion `" #__VA_ARGS__ "` failed in " << __FILE__ << " line " << __LINE__ << ": " << message << std::endl; \
+ std::terminate(); \
+ } \
+ } while (false)
+ #else
+ #define SOL_ASSERT_MSG(message, ...) \
+ do { \
+ if (false) { \
+ (void)(__VA_ARGS__); \
+ (void)sizeof(message); \
+ } \
+ } while (false)
+ #endif
+#endif
+
+// end of sol/assert.hpp
+
+// beginning of sol/bytecode.hpp
+
+// beginning of sol/compatibility.hpp
+
+// beginning of sol/compatibility/lua_version.hpp
+
+#if SOL_IS_ON(SOL_USING_CXX_LUA)
+ #include <lua.h>
+ #include <lualib.h>
+ #include <lauxlib.h>
+#elif SOL_IS_ON(SOL_USE_LUA_HPP)
+ #include <lua.hpp>
+#else
+ extern "C" {
+ #include <lua.h>
+ #include <lauxlib.h>
+ #include <lualib.h>
+ }
+#endif // C++ Mangling for Lua vs. Not
+
+#if defined(SOL_LUAJIT)
+ #if (SOL_LUAJIT != 0)
+ #define SOL_USE_LUAJIT_I_ SOL_ON
+ #else
+ #define SOL_USE_LUAJIT_I_ SOL_OFF
+ #endif
+#elif defined(LUAJIT_VERSION)
+ #define SOL_USE_LUAJIT_I_ SOL_ON
+#else
+ #define SOL_USE_LUAJIT_I_ SOL_DEFAULT_OFF
+#endif // luajit
+
+#if SOL_IS_ON(SOL_USING_CXX_LUAJIT)
+ #include <luajit.h>
+#elif SOL_IS_ON(SOL_USE_LUAJIT)
+ extern "C" {
+ #include <luajit.h>
+ }
+#endif // C++ LuaJIT ... whatever that means
+
+#if defined(SOL_LUAJIT_VERSION)
+ #define SOL_LUAJIT_VERSION_I_ SOL_LUAJIT_VERSION
+#elif SOL_IS_ON(SOL_USE_LUAJIT)
+ #define SOL_LUAJIT_VERSION_I_ LUAJIT_VERSION_NUM
+#else
+ #define SOL_LUAJIT_VERSION_I_ 0
+#endif
+
+#if defined(SOL_LUAJIT_FFI_DISABLED)
+ #define SOL_LUAJIT_FFI_DISABLED_I_ SOL_ON
+#elif defined(LUAJIT_DISABLE_FFI)
+ #define SOL_LUAJIT_FFI_DISABLED_I_ SOL_ON
+#else
+ #define SOL_LUAJIT_FFI_DISABLED_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(MOONJIT_VERSION)
+ #define SOL_USE_MOONJIT_I_ SOL_ON
+#else
+ #define SOL_USE_MOONJIT_I_ SOL_OFF
+#endif
+
+#if !defined(SOL_LUA_VERSION)
+ #if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 502
+ #define SOL_LUA_VERSION LUA_VERSION_NUM
+ #elif defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
+ #define SOL_LUA_VERSION LUA_VERSION_NUM
+ #elif !defined(LUA_VERSION_NUM) || !(LUA_VERSION_NUM)
+ // Definitely 5.0
+ #define SOL_LUA_VERSION 500
+ #else
+ // ??? Not sure, assume latest?
+ #define SOL_LUA_VERSION 504
+ #endif // Lua Version 503, 502, 501 || luajit, 500
+#endif // SOL_LUA_VERSION
+
+#if defined(SOL_LUA_VERSION)
+ #define SOL_LUA_VERSION_I_ SOL_LUA_VERSION
+#else
+ #define SOL_LUA_VERSION_I_ 504
+#endif
+
+#if defined(SOL_EXCEPTIONS_ALWAYS_UNSAFE)
+ #if (SOL_EXCEPTIONS_ALWAYS_UNSAFE != 0)
+ #define SOL_PROPAGATE_EXCEPTIONS_I_ SOL_OFF
+ #else
+ #define SOL_PROPAGATE_EXCEPTIONS_I_ SOL_ON
+ #endif
+#elif defined(SOL_EXCEPTIONS_SAFE_PROPAGATION)
+ #if (SOL_EXCEPTIONS_SAFE_PROPAGATION != 0)
+ #define SOL_PROPAGATE_EXCEPTIONS_I_ SOL_ON
+ #else
+ #define SOL_PROPAGATE_EXCEPTIONS_I_ SOL_OFF
+ #endif
+#elif SOL_LUAJIT_VERSION_I_ >= 20100
+ // LuaJIT 2.1.0-beta3 and better have exception support locked in for all platforms (mostly)
+ #define SOL_PROPAGATE_EXCEPTIONS_I_ SOL_DEFAULT_ON
+#elif SOL_LUAJIT_VERSION_I_ >= 20000
+ // LuaJIT 2.0.x have exception support only on x64 builds
+ #if SOL_IS_ON(SOL_PLATFORM_X64)
+ #define SOL_PROPAGATE_EXCEPTIONS_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_PROPAGATE_EXCEPTIONS_I_ SOL_OFF
+ #endif
+#else
+ // otherwise, there is no exception safety for
+ // shoving exceptions through Lua and errors should
+ // always be serialized
+ #define SOL_PROPAGATE_EXCEPTIONS_I_ SOL_DEFAULT_OFF
+#endif
+
+#if defined(SOL_EXCEPTIONS_CATCH_ALL)
+ #if (SOL_EXCEPTIONS_CATCH_ALL != 0)
+ #define SOL_EXCEPTIONS_CATCH_ALL_I_ SOL_ON
+ #else
+ #define SOL_EXCEPTIONS_CATCH_ALL_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_ON(SOL_USE_LUAJIT)
+ #define SOL_EXCEPTIONS_CATCH_ALL_I_ SOL_DEFAULT_OFF
+ #elif SOL_IS_ON(SOL_USING_CXX_LUAJIT)
+ #define SOL_EXCEPTIONS_CATCH_ALL_I_ SOL_DEFAULT_OFF
+ #elif SOL_IS_ON(SOL_USING_CXX_LUA)
+ #define SOL_EXCEPTIONS_CATCH_ALL_I_ SOL_DEFAULT_OFF
+ #else
+ #define SOL_EXCEPTIONS_CATCH_ALL_I_ SOL_DEFAULT_ON
+ #endif
+#endif
+
+#if defined(SOL_LUAJIT_USE_EXCEPTION_TRAMPOLINE)
+ #if (SOL_LUAJIT_USE_EXCEPTION_TRAMPOLINE != 0)
+ #define SOL_USE_LUAJIT_EXCEPTION_TRAMPOLINE_I_ SOL_ON
+ #else
+ #define SOL_USE_LUAJIT_EXCEPTION_TRAMPOLINE_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_OFF(SOL_PROPAGATE_EXCEPTIONS) && SOL_IS_ON(SOL_USE_LUAJIT)
+ #define SOL_USE_LUAJIT_EXCEPTION_TRAMPOLINE_I_ SOL_ON
+ #else
+ #define SOL_USE_LUAJIT_EXCEPTION_TRAMPOLINE_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined(SOL_LUAL_STREAM_HAS_CLOSE_FUNCTION)
+ #if (SOL_LUAL_STREAM_HAS_CLOSE_FUNCTION != 0)
+ #define SOL_LUAL_STREAM_USE_CLOSE_FUNCTION_I_ SOL_ON
+ #else
+ #define SOL_LUAL_STREAM_USE_CLOSE_FUNCTION_I_ SOL_OFF
+ #endif
+#else
+ #if SOL_IS_OFF(SOL_USE_LUAJIT) && (SOL_LUA_VERSION > 501)
+ #define SOL_LUAL_STREAM_USE_CLOSE_FUNCTION_I_ SOL_ON
+ #else
+ #define SOL_LUAL_STREAM_USE_CLOSE_FUNCTION_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined (SOL_LUA_BIT32_LIB)
+ #if SOL_LUA_BIT32_LIB != 0
+ #define SOL_LUA_BIT32_LIB_I_ SOL_ON
+ #else
+ #define SOL_LUA_BIT32_LIB_I_ SOL_OFF
+ #endif
+#else
+ // Lua 5.2 only (deprecated in 5.3 (503)) (Can be turned on with Compat flags)
+ // Lua 5.2, or other versions of Lua with the compat flag, or Lua that is not 5.2 with the specific define (5.4.1 either removed it entirely or broke it)
+ #if (SOL_LUA_VERSION_I_ == 502)
+ #define SOL_LUA_BIT32_LIB_I_ SOL_ON
+ #elif defined(LUA_COMPAT_BITLIB)
+ #define SOL_LUA_BIT32_LIB_I_ SOL_ON
+ #elif (SOL_LUA_VERSION_I_ < 504 && defined(LUA_COMPAT_5_2))
+ #define SOL_LUA_BIT32_LIB_I_ SOL_ON
+ #else
+ #define SOL_LUA_BIT32_LIB_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+#if defined (SOL_LUA_NIL_IN_TABLES)
+ #if SOL_LUA_NIL_IN_TABLES != 0
+ #define SOL_LUA_NIL_IN_TABLES_I_ SOL_ON
+ #else
+ #define SOL_LUA_NIL_IN_TABLES_I_ SOL_OFF
+ #endif
+#else
+ #if defined(LUA_NILINTABLE) && (LUA_NILINTABLE != 0)
+ #define SOL_LUA_NIL_IN_TABLES_I_ SOL_DEFAULT_ON
+ #else
+ #define SOL_LUA_NIL_IN_TABLES_I_ SOL_DEFAULT_OFF
+ #endif
+#endif
+
+// end of sol/compatibility/lua_version.hpp
+
+#if SOL_IS_ON(SOL_USE_COMPATIBILITY_LAYER)
+
+#if SOL_IS_ON(SOL_USING_CXX_LUA) || SOL_IS_ON(SOL_USING_CXX_LUAJIT)
+ #ifndef COMPAT53_LUA_CPP
+ #define COMPAT53_LUA_CPP 1
+ #endif // Build Lua Compat layer as C++
+#endif
+ #ifndef COMPAT53_INCLUDE_SOURCE
+ #define COMPAT53_INCLUDE_SOURCE 1
+ #endif // Build Compat Layer Inline
+// beginning of sol/compatibility/compat-5.3.h
+
+#ifndef KEPLER_PROJECT_COMPAT53_H_
+#define KEPLER_PROJECT_COMPAT53_H_
+
+#include <stddef.h>
+#include <limits.h>
+#include <string.h>
+#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP)
+extern "C" {
+#endif
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP)
+}
+#endif
+
+#ifndef COMPAT53_PREFIX
+/* we chose this name because many other lua bindings / libs have
+* their own compatibility layer, and that use the compat53 declaration
+* frequently, causing all kinds of linker / compiler issues
+*/
+# define COMPAT53_PREFIX kp_compat53
+#endif // COMPAT53_PREFIX
+
+#ifndef COMPAT53_API
+# if defined(COMPAT53_INCLUDE_SOURCE) && COMPAT53_INCLUDE_SOURCE
+# if defined(__GNUC__) || defined(__clang__)
+# define COMPAT53_API __attribute__((__unused__)) static inline
+# else
+# define COMPAT53_API static inline
+# endif /* Clang/GCC */
+# else /* COMPAT53_INCLUDE_SOURCE */
+/* we are not including source, so everything is extern */
+# define COMPAT53_API extern
+# endif /* COMPAT53_INCLUDE_SOURCE */
+#endif /* COMPAT53_PREFIX */
+
+#define COMPAT53_CONCAT_HELPER(a, b) a##b
+#define COMPAT53_CONCAT(a, b) COMPAT53_CONCAT_HELPER(a, b)
+
+/* declarations for Lua 5.1 */
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
+
+/* XXX not implemented:
+* lua_arith (new operators)
+* lua_upvalueid
+* lua_upvaluejoin
+* lua_version
+* lua_yieldk
+*/
+
+#ifndef LUA_OK
+# define LUA_OK 0
+#endif
+#ifndef LUA_OPADD
+# define LUA_OPADD 0
+#endif
+#ifndef LUA_OPSUB
+# define LUA_OPSUB 1
+#endif
+#ifndef LUA_OPMUL
+# define LUA_OPMUL 2
+#endif
+#ifndef LUA_OPDIV
+# define LUA_OPDIV 3
+#endif
+#ifndef LUA_OPMOD
+# define LUA_OPMOD 4
+#endif
+#ifndef LUA_OPPOW
+# define LUA_OPPOW 5
+#endif
+#ifndef LUA_OPUNM
+# define LUA_OPUNM 6
+#endif
+#ifndef LUA_OPEQ
+# define LUA_OPEQ 0
+#endif
+#ifndef LUA_OPLT
+# define LUA_OPLT 1
+#endif
+#ifndef LUA_OPLE
+# define LUA_OPLE 2
+#endif
+
+/* LuaJIT/Lua 5.1 does not have the updated
+* error codes for thread status/function returns (but some patched versions do)
+* define it only if it's not found
+*/
+#if !defined(LUA_ERRGCMM)
+/* Use + 2 because in some versions of Lua (Lua 5.1)
+* LUA_ERRFILE is defined as (LUA_ERRERR+1)
+* so we need to avoid it (LuaJIT might have something at this
+* integer value too)
+*/
+# define LUA_ERRGCMM (LUA_ERRERR + 2)
+#endif /* LUA_ERRGCMM define */
+
+#if !defined(MOONJIT_VERSION)
+typedef size_t lua_Unsigned;
+#endif
+
+typedef struct luaL_Buffer_53 {
+ luaL_Buffer b; /* make incorrect code crash! */
+ char *ptr;
+ size_t nelems;
+ size_t capacity;
+ lua_State *L2;
+} luaL_Buffer_53;
+#define luaL_Buffer luaL_Buffer_53
+
+/* In PUC-Rio 5.1, userdata is a simple FILE*
+* In LuaJIT, it's a struct where the first member is a FILE*
+* We can't support the `closef` member
+*/
+typedef struct luaL_Stream {
+ FILE *f;
+} luaL_Stream;
+
+#define lua_absindex COMPAT53_CONCAT(COMPAT53_PREFIX, _absindex)
+COMPAT53_API int lua_absindex(lua_State *L, int i);
+
+#define lua_arith COMPAT53_CONCAT(COMPAT53_PREFIX, _arith)
+COMPAT53_API void lua_arith(lua_State *L, int op);
+
+#define lua_compare COMPAT53_CONCAT(COMPAT53_PREFIX, _compare)
+COMPAT53_API int lua_compare(lua_State *L, int idx1, int idx2, int op);
+
+#define lua_copy COMPAT53_CONCAT(COMPAT53_PREFIX, _copy)
+COMPAT53_API void lua_copy(lua_State *L, int from, int to);
+
+#define lua_getuservalue(L, i) \
+ (lua_getfenv((L), (i)), lua_type((L), -1))
+#define lua_setuservalue(L, i) \
+ (luaL_checktype((L), -1, LUA_TTABLE), lua_setfenv((L), (i)))
+
+#define lua_len COMPAT53_CONCAT(COMPAT53_PREFIX, _len)
+COMPAT53_API void lua_len(lua_State *L, int i);
+
+#define lua_pushstring(L, s) \
+ (lua_pushstring((L), (s)), lua_tostring((L), -1))
+
+#define lua_pushlstring(L, s, len) \
+ ((((len) == 0) ? lua_pushlstring((L), "", 0) : lua_pushlstring((L), (s), (len))), lua_tostring((L), -1))
+
+#ifndef luaL_newlibtable
+# define luaL_newlibtable(L, l) \
+ (lua_createtable((L), 0, sizeof((l))/sizeof(*(l))-1))
+#endif
+#ifndef luaL_newlib
+# define luaL_newlib(L, l) \
+ (luaL_newlibtable((L), (l)), luaL_register((L), NULL, (l)))
+#endif
+
+#ifndef lua_pushglobaltable
+# define lua_pushglobaltable(L) \
+ lua_pushvalue((L), LUA_GLOBALSINDEX)
+#endif
+#define lua_rawgetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawgetp)
+COMPAT53_API int lua_rawgetp(lua_State *L, int i, const void *p);
+
+#define lua_rawsetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawsetp)
+COMPAT53_API void lua_rawsetp(lua_State *L, int i, const void *p);
+
+#define lua_rawlen(L, i) lua_objlen((L), (i))
+
+#define lua_tointeger(L, i) lua_tointegerx((L), (i), NULL)
+
+#define lua_tonumberx COMPAT53_CONCAT(COMPAT53_PREFIX, _tonumberx)
+COMPAT53_API lua_Number lua_tonumberx(lua_State *L, int i, int *isnum);
+
+#define luaL_checkversion COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkversion)
+COMPAT53_API void luaL_checkversion(lua_State *L);
+
+#define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53)
+COMPAT53_API int lua_load(lua_State *L, lua_Reader reader, void *data, const char* source, const char* mode);
+
+#define luaL_loadfilex COMPAT53_CONCAT(COMPAT53_PREFIX, L_loadfilex)
+COMPAT53_API int luaL_loadfilex(lua_State *L, const char *filename, const char *mode);
+
+#define luaL_loadbufferx COMPAT53_CONCAT(COMPAT53_PREFIX, L_loadbufferx)
+COMPAT53_API int luaL_loadbufferx(lua_State *L, const char *buff, size_t sz, const char *name, const char *mode);
+
+#define luaL_checkstack COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkstack_53)
+COMPAT53_API void luaL_checkstack(lua_State *L, int sp, const char *msg);
+
+#define luaL_getsubtable COMPAT53_CONCAT(COMPAT53_PREFIX, L_getsubtable)
+COMPAT53_API int luaL_getsubtable(lua_State* L, int i, const char *name);
+
+#define luaL_len COMPAT53_CONCAT(COMPAT53_PREFIX, L_len)
+COMPAT53_API lua_Integer luaL_len(lua_State *L, int i);
+
+#define luaL_setfuncs COMPAT53_CONCAT(COMPAT53_PREFIX, L_setfuncs)
+COMPAT53_API void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup);
+
+#define luaL_setmetatable COMPAT53_CONCAT(COMPAT53_PREFIX, L_setmetatable)
+COMPAT53_API void luaL_setmetatable(lua_State *L, const char *tname);
+
+#define luaL_testudata COMPAT53_CONCAT(COMPAT53_PREFIX, L_testudata)
+COMPAT53_API void *luaL_testudata(lua_State *L, int i, const char *tname);
+
+#define luaL_traceback COMPAT53_CONCAT(COMPAT53_PREFIX, L_traceback)
+COMPAT53_API void luaL_traceback(lua_State *L, lua_State *L1, const char *msg, int level);
+
+#define luaL_fileresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_fileresult)
+COMPAT53_API int luaL_fileresult(lua_State *L, int stat, const char *fname);
+
+#define luaL_execresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_execresult)
+COMPAT53_API int luaL_execresult(lua_State *L, int stat);
+
+#define lua_callk(L, na, nr, ctx, cont) \
+ ((void)(ctx), (void)(cont), lua_call((L), (na), (nr)))
+#define lua_pcallk(L, na, nr, err, ctx, cont) \
+ ((void)(ctx), (void)(cont), lua_pcall((L), (na), (nr), (err)))
+
+#define lua_resume(L, from, nargs) \
+ ((void)(from), lua_resume((L), (nargs)))
+
+#define luaL_buffinit COMPAT53_CONCAT(COMPAT53_PREFIX, _buffinit_53)
+COMPAT53_API void luaL_buffinit(lua_State *L, luaL_Buffer_53 *B);
+
+#define luaL_prepbuffsize COMPAT53_CONCAT(COMPAT53_PREFIX, _prepbufsize_53)
+COMPAT53_API char *luaL_prepbuffsize(luaL_Buffer_53 *B, size_t s);
+
+#define luaL_addlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _addlstring_53)
+COMPAT53_API void luaL_addlstring(luaL_Buffer_53 *B, const char *s, size_t l);
+
+#define luaL_addvalue COMPAT53_CONCAT(COMPAT53_PREFIX, _addvalue_53)
+COMPAT53_API void luaL_addvalue(luaL_Buffer_53 *B);
+
+#define luaL_pushresult COMPAT53_CONCAT(COMPAT53_PREFIX, _pushresult_53)
+COMPAT53_API void luaL_pushresult(luaL_Buffer_53 *B);
+
+#undef luaL_buffinitsize
+#define luaL_buffinitsize(L, B, s) \
+ (luaL_buffinit((L), (B)), luaL_prepbuffsize((B), (s)))
+
+#undef luaL_prepbuffer
+#define luaL_prepbuffer(B) \
+ luaL_prepbuffsize((B), LUAL_BUFFERSIZE)
+
+#undef luaL_addchar
+#define luaL_addchar(B, c) \
+ ((void)((B)->nelems < (B)->capacity || luaL_prepbuffsize((B), 1)), \
+ ((B)->ptr[(B)->nelems++] = (c)))
+
+#undef luaL_addsize
+#define luaL_addsize(B, s) \
+ ((B)->nelems += (s))
+
+#undef luaL_addstring
+#define luaL_addstring(B, s) \
+ luaL_addlstring((B), (s), strlen((s)))
+
+#undef luaL_pushresultsize
+#define luaL_pushresultsize(B, s) \
+ (luaL_addsize((B), (s)), luaL_pushresult((B)))
+
+#if defined(LUA_COMPAT_APIINTCASTS)
+#define lua_pushunsigned(L, n) \
+ lua_pushinteger((L), (lua_Integer)(n))
+#define lua_tounsignedx(L, i, is) \
+ ((lua_Unsigned)lua_tointegerx((L), (i), (is)))
+#define lua_tounsigned(L, i) \
+ lua_tounsignedx((L), (i), NULL)
+#define luaL_checkunsigned(L, a) \
+ ((lua_Unsigned)luaL_checkinteger((L), (a)))
+#define luaL_optunsigned(L, a, d) \
+ ((lua_Unsigned)luaL_optinteger((L), (a), (lua_Integer)(d)))
+#endif
+
+#endif /* Lua 5.1 only */
+
+/* declarations for Lua 5.1 and 5.2 */
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 502
+
+typedef int lua_KContext;
+
+typedef int(*lua_KFunction)(lua_State *L, int status, lua_KContext ctx);
+
+#define lua_dump(L, w, d, s) \
+ ((void)(s), lua_dump((L), (w), (d)))
+
+#define lua_getfield(L, i, k) \
+ (lua_getfield((L), (i), (k)), lua_type((L), -1))
+
+#define lua_gettable(L, i) \
+ (lua_gettable((L), (i)), lua_type((L), -1))
+
+#define lua_geti COMPAT53_CONCAT(COMPAT53_PREFIX, _geti)
+COMPAT53_API int lua_geti(lua_State *L, int index, lua_Integer i);
+
+#define lua_isinteger COMPAT53_CONCAT(COMPAT53_PREFIX, _isinteger)
+COMPAT53_API int lua_isinteger(lua_State *L, int index);
+
+#define lua_tointegerx COMPAT53_CONCAT(COMPAT53_PREFIX, _tointegerx_53)
+COMPAT53_API lua_Integer lua_tointegerx(lua_State *L, int i, int *isnum);
+
+#define lua_numbertointeger(n, p) \
+ ((*(p) = (lua_Integer)(n)), 1)
+
+#define lua_rawget(L, i) \
+ (lua_rawget((L), (i)), lua_type((L), -1))
+
+#define lua_rawgeti(L, i, n) \
+ (lua_rawgeti((L), (i), (n)), lua_type((L), -1))
+
+#define lua_rotate COMPAT53_CONCAT(COMPAT53_PREFIX, _rotate)
+COMPAT53_API void lua_rotate(lua_State *L, int idx, int n);
+
+#define lua_seti COMPAT53_CONCAT(COMPAT53_PREFIX, _seti)
+COMPAT53_API void lua_seti(lua_State *L, int index, lua_Integer i);
+
+#define lua_stringtonumber COMPAT53_CONCAT(COMPAT53_PREFIX, _stringtonumber)
+COMPAT53_API size_t lua_stringtonumber(lua_State *L, const char *s);
+
+#define luaL_tolstring COMPAT53_CONCAT(COMPAT53_PREFIX, L_tolstring)
+COMPAT53_API const char *luaL_tolstring(lua_State *L, int idx, size_t *len);
+
+#define luaL_getmetafield(L, o, e) \
+ (luaL_getmetafield((L), (o), (e)) ? lua_type((L), -1) : LUA_TNIL)
+
+#define luaL_newmetatable(L, tn) \
+ (luaL_newmetatable((L), (tn)) ? (lua_pushstring((L), (tn)), lua_setfield((L), -2, "__name"), 1) : 0)
+
+#define luaL_requiref COMPAT53_CONCAT(COMPAT53_PREFIX, L_requiref_53)
+COMPAT53_API void luaL_requiref(lua_State *L, const char *modname,
+ lua_CFunction openf, int glb);
+
+#endif /* Lua 5.1 and Lua 5.2 */
+
+/* declarations for Lua 5.2 */
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 502
+
+/* XXX not implemented:
+* lua_isyieldable
+* lua_getextraspace
+* lua_arith (new operators)
+* lua_pushfstring (new formats)
+*/
+
+#define lua_getglobal(L, n) \
+ (lua_getglobal((L), (n)), lua_type((L), -1))
+
+#define lua_getuservalue(L, i) \
+ (lua_getuservalue((L), (i)), lua_type((L), -1))
+
+#define lua_pushlstring(L, s, len) \
+ (((len) == 0) ? lua_pushlstring((L), "", 0) : lua_pushlstring((L), (s), (len)))
+
+#define lua_rawgetp(L, i, p) \
+ (lua_rawgetp((L), (i), (p)), lua_type((L), -1))
+
+#define LUA_KFUNCTION(_name) \
+ static int (_name)(lua_State *L, int status, lua_KContext ctx); \
+ static int (_name ## _52)(lua_State *L) { \
+ lua_KContext ctx; \
+ int status = lua_getctx(L, &ctx); \
+ return (_name)(L, status, ctx); \
+ } \
+ static int (_name)(lua_State *L, int status, lua_KContext ctx)
+
+#define lua_pcallk(L, na, nr, err, ctx, cont) \
+ lua_pcallk((L), (na), (nr), (err), (ctx), cont ## _52)
+
+#define lua_callk(L, na, nr, ctx, cont) \
+ lua_callk((L), (na), (nr), (ctx), cont ## _52)
+
+#define lua_yieldk(L, nr, ctx, cont) \
+ lua_yieldk((L), (nr), (ctx), cont ## _52)
+
+#ifdef lua_call
+# undef lua_call
+# define lua_call(L, na, nr) \
+ (lua_callk)((L), (na), (nr), 0, NULL)
+#endif
+
+#ifdef lua_pcall
+# undef lua_pcall
+# define lua_pcall(L, na, nr, err) \
+ (lua_pcallk)((L), (na), (nr), (err), 0, NULL)
+#endif
+
+#ifdef lua_yield
+# undef lua_yield
+# define lua_yield(L, nr) \
+ (lua_yieldk)((L), (nr), 0, NULL)
+#endif
+
+#endif /* Lua 5.2 only */
+
+/* other Lua versions */
+#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 501 || LUA_VERSION_NUM > 504
+
+# error "unsupported Lua version (i.e. not Lua 5.1, 5.2, 5.3, or 5.4)"
+
+#endif /* other Lua versions except 5.1, 5.2, 5.3, and 5.4 */
+
+/* helper macro for defining continuation functions (for every version
+* *except* Lua 5.2) */
+#ifndef LUA_KFUNCTION
+#define LUA_KFUNCTION(_name) \
+ static int (_name)(lua_State *L, int status, lua_KContext ctx)
+#endif
+
+#if defined(COMPAT53_INCLUDE_SOURCE) && COMPAT53_INCLUDE_SOURCE == 1
+// beginning of sol/compatibility/compat-5.3.c.h
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+
+/* don't compile it again if it already is included via compat53.h */
+#ifndef KEPLER_PROJECT_COMPAT53_C_
+#define KEPLER_PROJECT_COMPAT53_C_
+
+/* definitions for Lua 5.1 only */
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501
+
+#ifndef COMPAT53_FOPEN_NO_LOCK
+#if defined(_MSC_VER)
+#define COMPAT53_FOPEN_NO_LOCK 1
+#else /* otherwise */
+#define COMPAT53_FOPEN_NO_LOCK 0
+#endif /* VC++ only so far */
+#endif /* No-lock fopen_s usage if possible */
+
+#if defined(_MSC_VER) && COMPAT53_FOPEN_NO_LOCK
+#include <share.h>
+#endif /* VC++ _fsopen for share-allowed file read */
+
+#ifndef COMPAT53_HAVE_STRERROR_R
+#if defined(__GLIBC__) || defined(_POSIX_VERSION) || defined(__APPLE__) || (!defined(__MINGW32__) && defined(__GNUC__) && (__GNUC__ < 6))
+#define COMPAT53_HAVE_STRERROR_R 1
+#else /* none of the defines matched: define to 0 */
+#define COMPAT53_HAVE_STRERROR_R 0
+#endif /* have strerror_r of some form */
+#endif /* strerror_r */
+
+#ifndef COMPAT53_HAVE_STRERROR_S
+#if defined(_MSC_VER) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || (defined(__STDC_LIB_EXT1__) && __STDC_LIB_EXT1__)
+#define COMPAT53_HAVE_STRERROR_S 1
+#else /* not VC++ or C11 */
+#define COMPAT53_HAVE_STRERROR_S 0
+#endif /* strerror_s from VC++ or C11 */
+#endif /* strerror_s */
+
+#ifndef COMPAT53_LUA_FILE_BUFFER_SIZE
+#define COMPAT53_LUA_FILE_BUFFER_SIZE 4096
+#endif /* Lua File Buffer Size */
+
+static char* compat53_strerror(int en, char* buff, size_t sz) {
+#if COMPAT53_HAVE_STRERROR_R
+ /* use strerror_r here, because it's available on these specific platforms */
+ if (sz > 0) {
+ buff[0] = '\0';
+ /* we don't care whether the GNU version or the XSI version is used: */
+ if (strerror_r(en, buff, sz)) {
+ /* Yes, we really DO want to ignore the return value!
+ * GCC makes that extra hard, not even a (void) cast will do. */
+ }
+ if (buff[0] == '\0') {
+ /* Buffer is unchanged, so we probably have called GNU strerror_r which
+ * returned a static constant string. Chances are that strerror will
+ * return the same static constant string and therefore be thread-safe. */
+ return strerror(en);
+ }
+ }
+ return buff; /* sz is 0 *or* strerror_r wrote into the buffer */
+#elif COMPAT53_HAVE_STRERROR_S
+ /* for MSVC and other C11 implementations, use strerror_s since it's
+ * provided by default by the libraries */
+ strerror_s(buff, sz, en);
+ return buff;
+#else
+ /* fallback, but strerror is not guaranteed to be threadsafe due to modifying
+ * errno itself and some impls not locking a static buffer for it ... but most
+ * known systems have threadsafe errno: this might only change if the locale
+ * is changed out from under someone while this function is being called */
+ (void)buff;
+ (void)sz;
+ return strerror(en);
+#endif
+}
+
+COMPAT53_API int lua_absindex(lua_State* L, int i) {
+ if (i < 0 && i > LUA_REGISTRYINDEX)
+ i += lua_gettop(L) + 1;
+ return i;
+}
+
+static void compat53_call_lua(lua_State* L, char const code[], size_t len, int nargs, int nret) {
+ lua_rawgetp(L, LUA_REGISTRYINDEX, (void*)code);
+ if (lua_type(L, -1) != LUA_TFUNCTION) {
+ lua_pop(L, 1);
+ if (luaL_loadbuffer(L, code, len, "=none"))
+ lua_error(L);
+ lua_pushvalue(L, -1);
+ lua_rawsetp(L, LUA_REGISTRYINDEX, (void*)code);
+ }
+ lua_insert(L, -nargs - 1);
+ lua_call(L, nargs, nret);
+}
+
+COMPAT53_API void lua_arith(lua_State* L, int op) {
+ static const char compat53_arith_code[]
+ = "local op,a,b=...\n"
+ "if op==0 then return a+b\n"
+ "elseif op==1 then return a-b\n"
+ "elseif op==2 then return a*b\n"
+ "elseif op==3 then return a/b\n"
+ "elseif op==4 then return a%b\n"
+ "elseif op==5 then return a^b\n"
+ "elseif op==6 then return -a\n"
+ "end\n";
+
+ if (op < LUA_OPADD || op > LUA_OPUNM)
+ luaL_error(L, "invalid 'op' argument for lua_arith");
+ luaL_checkstack(L, 5, "not enough stack slots");
+ if (op == LUA_OPUNM)
+ lua_pushvalue(L, -1);
+ lua_pushnumber(L, op);
+ lua_insert(L, -3);
+ compat53_call_lua(L, compat53_arith_code, sizeof(compat53_arith_code) - 1, 3, 1);
+}
+
+COMPAT53_API int lua_compare(lua_State* L, int idx1, int idx2, int op) {
+ static const char compat53_compare_code[]
+ = "local a,b=...\n"
+ "return a<=b\n";
+
+ int result = 0;
+ switch (op) {
+ case LUA_OPEQ:
+ return lua_equal(L, idx1, idx2);
+ case LUA_OPLT:
+ return lua_lessthan(L, idx1, idx2);
+ case LUA_OPLE:
+ luaL_checkstack(L, 5, "not enough stack slots");
+ idx1 = lua_absindex(L, idx1);
+ idx2 = lua_absindex(L, idx2);
+ lua_pushvalue(L, idx1);
+ lua_pushvalue(L, idx2);
+ compat53_call_lua(L, compat53_compare_code, sizeof(compat53_compare_code) - 1, 2, 1);
+ result = lua_toboolean(L, -1);
+ lua_pop(L, 1);
+ return result;
+ default:
+ luaL_error(L, "invalid 'op' argument for lua_compare");
+ }
+ return 0;
+}
+
+COMPAT53_API void lua_copy(lua_State* L, int from, int to) {
+ int abs_to = lua_absindex(L, to);
+ luaL_checkstack(L, 1, "not enough stack slots");
+ lua_pushvalue(L, from);
+ lua_replace(L, abs_to);
+}
+
+COMPAT53_API void lua_len(lua_State* L, int i) {
+ switch (lua_type(L, i)) {
+ case LUA_TSTRING:
+ lua_pushnumber(L, (lua_Number)lua_objlen(L, i));
+ break;
+ case LUA_TTABLE:
+ if (!luaL_callmeta(L, i, "__len"))
+ lua_pushnumber(L, (lua_Number)lua_objlen(L, i));
+ break;
+ case LUA_TUSERDATA:
+ if (luaL_callmeta(L, i, "__len"))
+ break;
+ /* FALLTHROUGH */
+ default:
+ luaL_error(L, "attempt to get length of a %s value", lua_typename(L, lua_type(L, i)));
+ }
+}
+
+COMPAT53_API int lua_rawgetp(lua_State* L, int i, const void* p) {
+ int abs_i = lua_absindex(L, i);
+ lua_pushlightuserdata(L, (void*)p);
+ lua_rawget(L, abs_i);
+ return lua_type(L, -1);
+}
+
+COMPAT53_API void lua_rawsetp(lua_State* L, int i, const void* p) {
+ int abs_i = lua_absindex(L, i);
+ luaL_checkstack(L, 1, "not enough stack slots");
+ lua_pushlightuserdata(L, (void*)p);
+ lua_insert(L, -2);
+ lua_rawset(L, abs_i);
+}
+
+COMPAT53_API lua_Number lua_tonumberx(lua_State* L, int i, int* isnum) {
+ lua_Number n = lua_tonumber(L, i);
+ if (isnum != NULL) {
+ *isnum = (n != 0 || lua_isnumber(L, i));
+ }
+ return n;
+}
+
+COMPAT53_API void luaL_checkversion(lua_State* L) {
+ (void)L;
+}
+
+COMPAT53_API void luaL_checkstack(lua_State* L, int sp, const char* msg) {
+ if (!lua_checkstack(L, sp + LUA_MINSTACK)) {
+ if (msg != NULL)
+ luaL_error(L, "stack overflow (%s)", msg);
+ else {
+ lua_pushliteral(L, "stack overflow");
+ lua_error(L);
+ }
+ }
+}
+
+COMPAT53_API int luaL_getsubtable(lua_State* L, int i, const char* name) {
+ int abs_i = lua_absindex(L, i);
+ luaL_checkstack(L, 3, "not enough stack slots");
+ lua_pushstring(L, name);
+ lua_gettable(L, abs_i);
+ if (lua_istable(L, -1))
+ return 1;
+ lua_pop(L, 1);
+ lua_newtable(L);
+ lua_pushstring(L, name);
+ lua_pushvalue(L, -2);
+ lua_settable(L, abs_i);
+ return 0;
+}
+
+COMPAT53_API lua_Integer luaL_len(lua_State* L, int i) {
+ lua_Integer res = 0;
+ int isnum = 0;
+ luaL_checkstack(L, 1, "not enough stack slots");
+ lua_len(L, i);
+ res = lua_tointegerx(L, -1, &isnum);
+ lua_pop(L, 1);
+ if (!isnum)
+ luaL_error(L, "object length is not an integer");
+ return res;
+}
+
+COMPAT53_API void luaL_setfuncs(lua_State* L, const luaL_Reg* l, int nup) {
+ luaL_checkstack(L, nup + 1, "too many upvalues");
+ for (; l->name != NULL; l++) { /* fill the table with given functions */
+ int i;
+ lua_pushstring(L, l->name);
+ for (i = 0; i < nup; i++) /* copy upvalues to the top */
+ lua_pushvalue(L, -(nup + 1));
+ lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
+ lua_settable(L, -(nup + 3)); /* table must be below the upvalues, the name and the closure */
+ }
+ lua_pop(L, nup); /* remove upvalues */
+}
+
+COMPAT53_API void luaL_setmetatable(lua_State* L, const char* tname) {
+ luaL_checkstack(L, 1, "not enough stack slots");
+ luaL_getmetatable(L, tname);
+ lua_setmetatable(L, -2);
+}
+
+COMPAT53_API void* luaL_testudata(lua_State* L, int i, const char* tname) {
+ void* p = lua_touserdata(L, i);
+ luaL_checkstack(L, 2, "not enough stack slots");
+ if (p == NULL || !lua_getmetatable(L, i))
+ return NULL;
+ else {
+ int res = 0;
+ luaL_getmetatable(L, tname);
+ res = lua_rawequal(L, -1, -2);
+ lua_pop(L, 2);
+ if (!res)
+ p = NULL;
+ }
+ return p;
+}
+
+static int compat53_countlevels(lua_State* L) {
+ lua_Debug ar;
+ int li = 1, le = 1;
+ /* find an upper bound */
+ while (lua_getstack(L, le, &ar)) {
+ li = le;
+ le *= 2;
+ }
+ /* do a binary search */
+ while (li < le) {
+ int m = (li + le) / 2;
+ if (lua_getstack(L, m, &ar))
+ li = m + 1;
+ else
+ le = m;
+ }
+ return le - 1;
+}
+
+static int compat53_findfield(lua_State* L, int objidx, int level) {
+ if (level == 0 || !lua_istable(L, -1))
+ return 0; /* not found */
+ lua_pushnil(L); /* start 'next' loop */
+ while (lua_next(L, -2)) { /* for each pair in table */
+ if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */
+ if (lua_rawequal(L, objidx, -1)) { /* found object? */
+ lua_pop(L, 1); /* remove value (but keep name) */
+ return 1;
+ }
+ else if (compat53_findfield(L, objidx, level - 1)) { /* try recursively */
+ lua_remove(L, -2); /* remove table (but keep name) */
+ lua_pushliteral(L, ".");
+ lua_insert(L, -2); /* place '.' between the two names */
+ lua_concat(L, 3);
+ return 1;
+ }
+ }
+ lua_pop(L, 1); /* remove value */
+ }
+ return 0; /* not found */
+}
+
+static int compat53_pushglobalfuncname(lua_State* L, lua_Debug* ar) {
+ int top = lua_gettop(L);
+ lua_getinfo(L, "f", ar); /* push function */
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ if (compat53_findfield(L, top + 1, 2)) {
+ lua_copy(L, -1, top + 1); /* move name to proper place */
+ lua_pop(L, 2); /* remove pushed values */
+ return 1;
+ }
+ else {
+ lua_settop(L, top); /* remove function and global table */
+ return 0;
+ }
+}
+
+static void compat53_pushfuncname(lua_State* L, lua_Debug* ar) {
+ if (*ar->namewhat != '\0') /* is there a name? */
+ lua_pushfstring(L, "function " LUA_QS, ar->name);
+ else if (*ar->what == 'm') /* main? */
+ lua_pushliteral(L, "main chunk");
+ else if (*ar->what == 'C') {
+ if (compat53_pushglobalfuncname(L, ar)) {
+ lua_pushfstring(L, "function " LUA_QS, lua_tostring(L, -1));
+ lua_remove(L, -2); /* remove name */
+ }
+ else
+ lua_pushliteral(L, "?");
+ }
+ else
+ lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined);
+}
+
+#define COMPAT53_LEVELS1 12 /* size of the first part of the stack */
+#define COMPAT53_LEVELS2 10 /* size of the second part of the stack */
+
+COMPAT53_API void luaL_traceback(lua_State* L, lua_State* L1, const char* msg, int level) {
+ lua_Debug ar;
+ int top = lua_gettop(L);
+ int numlevels = compat53_countlevels(L1);
+ int mark = (numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2) ? COMPAT53_LEVELS1 : 0;
+ if (msg)
+ lua_pushfstring(L, "%s\n", msg);
+ lua_pushliteral(L, "stack traceback:");
+ while (lua_getstack(L1, level++, &ar)) {
+ if (level == mark) { /* too many levels? */
+ lua_pushliteral(L, "\n\t..."); /* add a '...' */
+ level = numlevels - COMPAT53_LEVELS2; /* and skip to last ones */
+ }
+ else {
+ lua_getinfo(L1, "Slnt", &ar);
+ lua_pushfstring(L, "\n\t%s:", ar.short_src);
+ if (ar.currentline > 0)
+ lua_pushfstring(L, "%d:", ar.currentline);
+ lua_pushliteral(L, " in ");
+ compat53_pushfuncname(L, &ar);
+ lua_concat(L, lua_gettop(L) - top);
+ }
+ }
+ lua_concat(L, lua_gettop(L) - top);
+}
+
+COMPAT53_API int luaL_fileresult(lua_State* L, int stat, const char* fname) {
+ const char* serr = NULL;
+ int en = errno; /* calls to Lua API may change this value */
+ char buf[512] = { 0 };
+ if (stat) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ else {
+ lua_pushnil(L);
+ serr = compat53_strerror(en, buf, sizeof(buf));
+ if (fname)
+ lua_pushfstring(L, "%s: %s", fname, serr);
+ else
+ lua_pushstring(L, serr);
+ lua_pushnumber(L, (lua_Number)en);
+ return 3;
+ }
+}
+
+static int compat53_checkmode(lua_State* L, const char* mode, const char* modename, int err) {
+ if (mode && strchr(mode, modename[0]) == NULL) {
+ lua_pushfstring(L, "attempt to load a %s chunk (mode is '%s')", modename, mode);
+ return err;
+ }
+ return LUA_OK;
+}
+
+typedef struct {
+ lua_Reader reader;
+ void* ud;
+ int has_peeked_data;
+ const char* peeked_data;
+ size_t peeked_data_size;
+} compat53_reader_data;
+
+static const char* compat53_reader(lua_State* L, void* ud, size_t* size) {
+ compat53_reader_data* data = (compat53_reader_data*)ud;
+ if (data->has_peeked_data) {
+ data->has_peeked_data = 0;
+ *size = data->peeked_data_size;
+ return data->peeked_data;
+ }
+ else
+ return data->reader(L, data->ud, size);
+}
+
+COMPAT53_API int lua_load(lua_State* L, lua_Reader reader, void* data, const char* source, const char* mode) {
+ int status = LUA_OK;
+ compat53_reader_data compat53_data = { reader, data, 1, 0, 0 };
+ compat53_data.peeked_data = reader(L, data, &(compat53_data.peeked_data_size));
+ if (compat53_data.peeked_data && compat53_data.peeked_data_size && compat53_data.peeked_data[0] == LUA_SIGNATURE[0]) /* binary file? */
+ status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX);
+ else
+ status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX);
+ if (status != LUA_OK)
+ return status;
+ /* we need to call the original 5.1 version of lua_load! */
+#undef lua_load
+ return lua_load(L, compat53_reader, &compat53_data, source);
+#define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53)
+}
+
+typedef struct {
+ int n; /* number of pre-read characters */
+ FILE* f; /* file being read */
+ char buff[COMPAT53_LUA_FILE_BUFFER_SIZE]; /* area for reading file */
+} compat53_LoadF;
+
+static const char* compat53_getF(lua_State* L, void* ud, size_t* size) {
+ compat53_LoadF* lf = (compat53_LoadF*)ud;
+ (void)L; /* not used */
+ if (lf->n > 0) { /* are there pre-read characters to be read? */
+ *size = lf->n; /* return them (chars already in buffer) */
+ lf->n = 0; /* no more pre-read characters */
+ }
+ else { /* read a block from file */
+ /* 'fread' can return > 0 *and* set the EOF flag. If next call to
+ 'compat53_getF' called 'fread', it might still wait for user input.
+ The next check avoids this problem. */
+ if (feof(lf->f))
+ return NULL;
+ *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */
+ }
+ return lf->buff;
+}
+
+static int compat53_errfile(lua_State* L, const char* what, int fnameindex) {
+ char buf[512] = { 0 };
+ const char* serr = compat53_strerror(errno, buf, sizeof(buf));
+ const char* filename = lua_tostring(L, fnameindex) + 1;
+ lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr);
+ lua_remove(L, fnameindex);
+ return LUA_ERRFILE;
+}
+
+static int compat53_skipBOM(compat53_LoadF* lf) {
+ const char* p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */
+ int c;
+ lf->n = 0;
+ do {
+ c = getc(lf->f);
+ if (c == EOF || c != *(const unsigned char*)p++)
+ return c;
+ lf->buff[lf->n++] = (char)c; /* to be read by the parser */
+ } while (*p != '\0');
+ lf->n = 0; /* prefix matched; discard it */
+ return getc(lf->f); /* return next character */
+}
+
+/*
+** reads the first character of file 'f' and skips an optional BOM mark
+** in its beginning plus its first line if it starts with '#'. Returns
+** true if it skipped the first line. In any case, '*cp' has the
+** first "valid" character of the file (after the optional BOM and
+** a first-line comment).
+*/
+static int compat53_skipcomment(compat53_LoadF* lf, int* cp) {
+ int c = *cp = compat53_skipBOM(lf);
+ if (c == '#') { /* first line is a comment (Unix exec. file)? */
+ do { /* skip first line */
+ c = getc(lf->f);
+ } while (c != EOF && c != '\n');
+ *cp = getc(lf->f); /* skip end-of-line, if present */
+ return 1; /* there was a comment */
+ }
+ else
+ return 0; /* no comment */
+}
+
+COMPAT53_API int luaL_loadfilex(lua_State* L, const char* filename, const char* mode) {
+ compat53_LoadF lf;
+ int status, readstatus;
+ int c;
+ int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
+ if (filename == NULL) {
+ lua_pushliteral(L, "=stdin");
+ lf.f = stdin;
+ }
+ else {
+ lua_pushfstring(L, "@%s", filename);
+#if defined(_MSC_VER)
+ /* This code is here to stop a deprecation error that stops builds
+ * if a certain macro is defined. While normally not caring would
+ * be best, some header-only libraries and builds can't afford to
+ * dictate this to the user. A quick check shows that fopen_s this
+ * goes back to VS 2005, and _fsopen goes back to VS 2003 .NET,
+ * possibly even before that so we don't need to do any version
+ * number checks, since this has been there since forever. */
+
+ /* TO USER: if you want the behavior of typical fopen_s/fopen,
+ * which does lock the file on VC++, define the macro used below to 0 */
+#if COMPAT53_FOPEN_NO_LOCK
+ lf.f = _fsopen(filename, "r", _SH_DENYNO); /* do not lock the file in any way */
+ if (lf.f == NULL)
+ return compat53_errfile(L, "open", fnameindex);
+#else /* use default locking version */
+ if (fopen_s(&lf.f, filename, "r") != 0)
+ return compat53_errfile(L, "open", fnameindex);
+#endif /* Locking vs. No-locking fopen variants */
+#else
+ lf.f = fopen(filename, "r"); /* default stdlib doesn't forcefully lock files here */
+ if (lf.f == NULL)
+ return compat53_errfile(L, "open", fnameindex);
+#endif
+ }
+ if (compat53_skipcomment(&lf, &c)) /* read initial portion */
+ lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */
+ if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
+#if defined(_MSC_VER)
+ if (freopen_s(&lf.f, filename, "rb", lf.f) != 0)
+ return compat53_errfile(L, "reopen", fnameindex);
+#else
+ lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
+ if (lf.f == NULL)
+ return compat53_errfile(L, "reopen", fnameindex);
+#endif
+ compat53_skipcomment(&lf, &c); /* re-read initial portion */
+ }
+ if (c != EOF)
+ lf.buff[lf.n++] = (char)c; /* 'c' is the first character of the stream */
+ status = lua_load(L, &compat53_getF, &lf, lua_tostring(L, -1), mode);
+ readstatus = ferror(lf.f);
+ if (filename)
+ fclose(lf.f); /* close file (even in case of errors) */
+ if (readstatus) {
+ lua_settop(L, fnameindex); /* ignore results from 'lua_load' */
+ return compat53_errfile(L, "read", fnameindex);
+ }
+ lua_remove(L, fnameindex);
+ return status;
+}
+
+COMPAT53_API int luaL_loadbufferx(lua_State* L, const char* buff, size_t sz, const char* name, const char* mode) {
+ int status = LUA_OK;
+ if (sz > 0 && buff[0] == LUA_SIGNATURE[0]) {
+ status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX);
+ }
+ else {
+ status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX);
+ }
+ if (status != LUA_OK)
+ return status;
+ return luaL_loadbuffer(L, buff, sz, name);
+}
+
+#if !defined(l_inspectstat) \
+ && (defined(unix) || defined(__unix) || defined(__unix__) || defined(__TOS_AIX__) || defined(_SYSTYPE_BSD) || (defined(__APPLE__) && defined(__MACH__)))
+/* some form of unix; check feature macros in unistd.h for details */
+#include <unistd.h>
+/* check posix version; the relevant include files and macros probably
+ * were available before 2001, but I'm not sure */
+#if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L
+#include <sys/wait.h>
+#define l_inspectstat(stat, what) \
+ if (WIFEXITED(stat)) { \
+ stat = WEXITSTATUS(stat); \
+ } \
+ else if (WIFSIGNALED(stat)) { \
+ stat = WTERMSIG(stat); \
+ what = "signal"; \
+ }
+#endif
+#endif
+
+/* provide default (no-op) version */
+#if !defined(l_inspectstat)
+#define l_inspectstat(stat, what) ((void)0)
+#endif
+
+COMPAT53_API int luaL_execresult(lua_State* L, int stat) {
+ const char* what = "exit";
+ if (stat == -1)
+ return luaL_fileresult(L, 0, NULL);
+ else {
+ l_inspectstat(stat, what);
+ if (*what == 'e' && stat == 0)
+ lua_pushboolean(L, 1);
+ else
+ lua_pushnil(L);
+ lua_pushstring(L, what);
+ lua_pushinteger(L, stat);
+ return 3;
+ }
+}
+
+COMPAT53_API void luaL_buffinit(lua_State* L, luaL_Buffer_53* B) {
+ /* make it crash if used via pointer to a 5.1-style luaL_Buffer */
+ B->b.p = NULL;
+ B->b.L = NULL;
+ B->b.lvl = 0;
+ /* reuse the buffer from the 5.1-style luaL_Buffer though! */
+ B->ptr = B->b.buffer;
+ B->capacity = LUAL_BUFFERSIZE;
+ B->nelems = 0;
+ B->L2 = L;
+}
+
+COMPAT53_API char* luaL_prepbuffsize(luaL_Buffer_53* B, size_t s) {
+ if (B->capacity - B->nelems < s) { /* needs to grow */
+ char* newptr = NULL;
+ size_t newcap = B->capacity * 2;
+ if (newcap - B->nelems < s)
+ newcap = B->nelems + s;
+ if (newcap < B->capacity) /* overflow */
+ luaL_error(B->L2, "buffer too large");
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 504
+ newptr = (char*)lua_newuserdatauv(B->L2, newcap, 0);
+#else
+ newptr = (char*)lua_newuserdata(B->L2, newcap);
+#endif
+ memcpy(newptr, B->ptr, B->nelems);
+ if (B->ptr != B->b.buffer)
+ lua_replace(B->L2, -2); /* remove old buffer */
+ B->ptr = newptr;
+ B->capacity = newcap;
+ }
+ return B->ptr + B->nelems;
+}
+
+COMPAT53_API void luaL_addlstring(luaL_Buffer_53* B, const char* s, size_t l) {
+ memcpy(luaL_prepbuffsize(B, l), s, l);
+ luaL_addsize(B, l);
+}
+
+COMPAT53_API void luaL_addvalue(luaL_Buffer_53* B) {
+ size_t len = 0;
+ const char* s = lua_tolstring(B->L2, -1, &len);
+ if (!s)
+ luaL_error(B->L2, "cannot convert value to string");
+ if (B->ptr != B->b.buffer)
+ lua_insert(B->L2, -2); /* userdata buffer must be at stack top */
+ luaL_addlstring(B, s, len);
+ lua_remove(B->L2, B->ptr != B->b.buffer ? -2 : -1);
+}
+
+void luaL_pushresult(luaL_Buffer_53* B) {
+ lua_pushlstring(B->L2, B->ptr, B->nelems);
+ if (B->ptr != B->b.buffer)
+ lua_replace(B->L2, -2); /* remove userdata buffer */
+}
+
+#endif /* Lua 5.1 */
+
+/* definitions for Lua 5.1 and Lua 5.2 */
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 502
+
+COMPAT53_API int lua_geti(lua_State* L, int index, lua_Integer i) {
+ index = lua_absindex(L, index);
+ lua_pushinteger(L, i);
+ lua_gettable(L, index);
+ return lua_type(L, -1);
+}
+
+COMPAT53_API int lua_isinteger(lua_State* L, int index) {
+ if (lua_type(L, index) == LUA_TNUMBER) {
+ lua_Number n = lua_tonumber(L, index);
+ lua_Integer i = lua_tointeger(L, index);
+ if (i == n)
+ return 1;
+ }
+ return 0;
+}
+
+COMPAT53_API lua_Integer lua_tointegerx(lua_State* L, int i, int* isnum) {
+ int ok = 0;
+ lua_Number n = lua_tonumberx(L, i, &ok);
+ if (ok) {
+ if (n == (lua_Integer)n) {
+ if (isnum)
+ *isnum = 1;
+ return (lua_Integer)n;
+ }
+ }
+ if (isnum)
+ *isnum = 0;
+ return 0;
+}
+
+static void compat53_reverse(lua_State* L, int a, int b) {
+ for (; a < b; ++a, --b) {
+ lua_pushvalue(L, a);
+ lua_pushvalue(L, b);
+ lua_replace(L, a);
+ lua_replace(L, b);
+ }
+}
+
+COMPAT53_API void lua_rotate(lua_State* L, int idx, int n) {
+ int n_elems = 0;
+ idx = lua_absindex(L, idx);
+ n_elems = lua_gettop(L) - idx + 1;
+ if (n < 0)
+ n += n_elems;
+ if (n > 0 && n < n_elems) {
+ luaL_checkstack(L, 2, "not enough stack slots available");
+ n = n_elems - n;
+ compat53_reverse(L, idx, idx + n - 1);
+ compat53_reverse(L, idx + n, idx + n_elems - 1);
+ compat53_reverse(L, idx, idx + n_elems - 1);
+ }
+}
+
+COMPAT53_API void lua_seti(lua_State* L, int index, lua_Integer i) {
+ luaL_checkstack(L, 1, "not enough stack slots available");
+ index = lua_absindex(L, index);
+ lua_pushinteger(L, i);
+ lua_insert(L, -2);
+ lua_settable(L, index);
+}
+
+#if !defined(lua_str2number)
+#define lua_str2number(s, p) strtod((s), (p))
+#endif
+
+COMPAT53_API size_t lua_stringtonumber(lua_State* L, const char* s) {
+ char* endptr;
+ lua_Number n = lua_str2number(s, &endptr);
+ if (endptr != s) {
+ while (*endptr != '\0' && isspace((unsigned char)*endptr))
+ ++endptr;
+ if (*endptr == '\0') {
+ lua_pushnumber(L, n);
+ return endptr - s + 1;
+ }
+ }
+ return 0;
+}
+
+COMPAT53_API const char* luaL_tolstring(lua_State* L, int idx, size_t* len) {
+ if (!luaL_callmeta(L, idx, "__tostring")) {
+ int t = lua_type(L, idx), tt = 0;
+ char const* name = NULL;
+ switch (t) {
+ case LUA_TNIL:
+ lua_pushliteral(L, "nil");
+ break;
+ case LUA_TSTRING:
+ case LUA_TNUMBER:
+ lua_pushvalue(L, idx);
+ break;
+ case LUA_TBOOLEAN:
+ if (lua_toboolean(L, idx))
+ lua_pushliteral(L, "true");
+ else
+ lua_pushliteral(L, "false");
+ break;
+ default:
+ tt = luaL_getmetafield(L, idx, "__name");
+ name = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : lua_typename(L, t);
+ lua_pushfstring(L, "%s: %p", name, lua_topointer(L, idx));
+ if (tt != LUA_TNIL)
+ lua_replace(L, -2);
+ break;
+ }
+ }
+ else {
+ if (!lua_isstring(L, -1))
+ luaL_error(L, "'__tostring' must return a string");
+ }
+ return lua_tolstring(L, -1, len);
+}
+
+COMPAT53_API void luaL_requiref(lua_State* L, const char* modname, lua_CFunction openf, int glb) {
+ luaL_checkstack(L, 3, "not enough stack slots available");
+ luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED");
+ if (lua_getfield(L, -1, modname) == LUA_TNIL) {
+ lua_pop(L, 1);
+ lua_pushcfunction(L, openf);
+ lua_pushstring(L, modname);
+ lua_call(L, 1, 1);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -3, modname);
+ }
+ if (glb) {
+ lua_pushvalue(L, -1);
+ lua_setglobal(L, modname);
+ }
+ lua_replace(L, -2);
+}
+
+#endif /* Lua 5.1 and 5.2 */
+
+#endif /* KEPLER_PROJECT_COMPAT53_C_ */
+
+/*********************************************************************
+ * This file contains parts of Lua 5.2's and Lua 5.3's source code:
+ *
+ * Copyright (C) 1994-2014 Lua.org, PUC-Rio.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *********************************************************************/
+// end of sol/compatibility/compat-5.3.c.h
+
+#endif
+
+#endif /* KEPLER_PROJECT_COMPAT53_H_ */
+
+// end of sol/compatibility/compat-5.3.h
+
+// beginning of sol/compatibility/compat-5.4.h
+
+#ifndef NOT_KEPLER_PROJECT_COMPAT54_H_
+#define NOT_KEPLER_PROJECT_COMPAT54_H_
+
+#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP)
+extern "C" {
+#endif
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP)
+}
+#endif
+
+#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 504
+
+#if !defined(LUA_ERRGCMM)
+/* So Lua 5.4 actually removes this, which breaks sol2...
+ man, this API is quite unstable...!
+*/
+# define LUA_ERRGCMM (LUA_ERRERR + 2)
+#endif /* LUA_ERRGCMM define */
+
+#endif // Lua 5.4 only
+
+#endif // NOT_KEPLER_PROJECT_COMPAT54_H_// end of sol/compatibility/compat-5.4.h
+
+#endif
+
+// end of sol/compatibility.hpp
+
+#include <vector>
+#include <cstdint>
+#include <cstddef>
+
+namespace sol {
+
+ template <typename Allocator = std::allocator<std::byte>>
+ class basic_bytecode : private std::vector<std::byte, Allocator> {
+ private:
+ using base_t = std::vector<std::byte, Allocator>;
+
+ public:
+ using typename base_t::allocator_type;
+ using typename base_t::const_iterator;
+ using typename base_t::const_pointer;
+ using typename base_t::const_reference;
+ using typename base_t::const_reverse_iterator;
+ using typename base_t::difference_type;
+ using typename base_t::iterator;
+ using typename base_t::pointer;
+ using typename base_t::reference;
+ using typename base_t::reverse_iterator;
+ using typename base_t::size_type;
+ using typename base_t::value_type;
+
+ using base_t::base_t;
+ using base_t::operator=;
+
+ using base_t::data;
+ using base_t::empty;
+ using base_t::max_size;
+ using base_t::size;
+
+ using base_t::at;
+ using base_t::operator[];
+ using base_t::back;
+ using base_t::front;
+
+ using base_t::begin;
+ using base_t::cbegin;
+ using base_t::cend;
+ using base_t::end;
+
+ using base_t::crbegin;
+ using base_t::crend;
+ using base_t::rbegin;
+ using base_t::rend;
+
+ using base_t::get_allocator;
+ using base_t::swap;
+
+ using base_t::clear;
+ using base_t::emplace;
+ using base_t::emplace_back;
+ using base_t::erase;
+ using base_t::insert;
+ using base_t::pop_back;
+ using base_t::push_back;
+ using base_t::reserve;
+ using base_t::resize;
+ using base_t::shrink_to_fit;
+
+ string_view as_string_view() const {
+ return string_view(reinterpret_cast<const char*>(this->data()), this->size());
+ }
+ };
+
+ template <typename Container>
+ inline int basic_insert_dump_writer(lua_State*, const void* memory, size_t memory_size, void* userdata_pointer) {
+ using storage_t = Container;
+ const std::byte* p_code = static_cast<const std::byte*>(memory);
+ storage_t& bc = *static_cast<storage_t*>(userdata_pointer);
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ bc.insert(bc.cend(), p_code, p_code + memory_size);
+#else
+ try {
+ bc.insert(bc.cend(), p_code, p_code + memory_size);
+ }
+ catch (...) {
+ return -1;
+ }
+#endif
+ return 0;
+ }
+
+ using bytecode = basic_bytecode<>;
+
+ constexpr inline auto bytecode_dump_writer = &basic_insert_dump_writer<bytecode>;
+
+} // namespace sol
+
+// end of sol/bytecode.hpp
+
+// beginning of sol/stack.hpp
+
+// beginning of sol/trampoline.hpp
+
+// beginning of sol/types.hpp
+
+// beginning of sol/error.hpp
+
+#include <stdexcept>
+#include <string>
+#include <array>
+
+namespace sol {
+ namespace detail {
+ struct direct_error_tag { };
+ const auto direct_error = direct_error_tag {};
+
+ struct error_result {
+ int results;
+ const char* format_string;
+ std::array<const char*, 4> argument_strings;
+
+ error_result() : results(0), format_string(nullptr) {
+ }
+
+ error_result(int results_) : results(results_), format_string(nullptr) {
+ }
+
+ error_result(const char* format_string_, const char* first_message_) : results(0), format_string(format_string_), argument_strings() {
+ argument_strings[0] = first_message_;
+ }
+ };
+
+ inline int handle_errors(lua_State* L, const error_result& er) {
+ if (er.format_string == nullptr) {
+ return er.results;
+ }
+ return luaL_error(L, er.format_string, er.argument_strings[0], er.argument_strings[1], er.argument_strings[2], er.argument_strings[3]);
+ }
+ } // namespace detail
+
+ class error : public std::runtime_error {
+ private:
+ // Because VC++ is upsetting, most of the time!
+ std::string what_reason;
+
+ public:
+ error(const std::string& str) : error(detail::direct_error, "lua: error: " + str) {
+ }
+ error(std::string&& str) : error(detail::direct_error, "lua: error: " + std::move(str)) {
+ }
+ error(detail::direct_error_tag, const std::string& str) : std::runtime_error(""), what_reason(str) {
+ }
+ error(detail::direct_error_tag, std::string&& str) : std::runtime_error(""), what_reason(std::move(str)) {
+ }
+
+ error(const error& e) = default;
+ error(error&& e) = default;
+ error& operator=(const error& e) = default;
+ error& operator=(error&& e) = default;
+
+ virtual const char* what() const noexcept override {
+ return what_reason.c_str();
+ }
+ };
+
+} // namespace sol
+
+// end of sol/error.hpp
+
+// beginning of sol/optional.hpp
+
+// beginning of sol/in_place.hpp
+
+#include <cstddef>
+#include <utility>
+
+namespace sol {
+
+ using in_place_t = std::in_place_t;
+ constexpr std::in_place_t in_place {};
+ constexpr std::in_place_t in_place_of {};
+
+ template <typename T>
+ using in_place_type_t = std::in_place_type_t<T>;
+ template <typename T>
+ constexpr std::in_place_type_t<T> in_place_type {};
+
+ template <size_t I>
+ using in_place_index_t = std::in_place_index_t<I>;
+ template <size_t I>
+ constexpr in_place_index_t<I> in_place_index {};
+
+} // namespace sol
+
+// end of sol/in_place.hpp
+
+#if SOL_IS_ON(SOL_USE_BOOST)
+#include <boost/optional.hpp>
+#else
+// beginning of sol/optional_implementation.hpp
+
+#define SOL_TL_OPTIONAL_VERSION_MAJOR 0
+#define SOL_TL_OPTIONAL_VERSION_MINOR 5
+
+#include <exception>
+#include <functional>
+#include <new>
+#include <type_traits>
+#include <utility>
+#include <cstdlib>
+#include <optional>
+
+#if (defined(_MSC_VER) && _MSC_VER == 1900)
+#define SOL_TL_OPTIONAL_MSVC2015
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && !defined(__clang__))
+#define SOL_TL_OPTIONAL_GCC49
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && !defined(__clang__))
+#define SOL_TL_OPTIONAL_GCC54
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && !defined(__clang__))
+#define SOL_TL_OPTIONAL_GCC55
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && !defined(__clang__))
+#define SOL_TL_OPTIONAL_NO_CONSTRR
+
+#define SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) std::has_trivial_copy_constructor<T>::value
+#define SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::has_trivial_copy_assign<T>::value
+
+#define SOL_TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible<T>::value
+
+#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
+#ifndef SOL_TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+#define SOL_TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+namespace sol { namespace detail {
+ template <class T>
+ struct is_trivially_copy_constructible : std::is_trivially_copy_constructible<T> { };
+#ifdef _GLIBCXX_VECTOR
+ template <class T, class A>
+ struct is_trivially_copy_constructible<std::vector<T, A>> : std::is_trivially_copy_constructible<T> { };
+#endif
+}} // namespace sol::detail
+#endif
+
+#define SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) sol::detail::is_trivially_copy_constructible<T>::value
+#define SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::is_trivially_copy_assignable<T>::value
+#define SOL_TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible<T>::value
+#else
+#define SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) std::is_trivially_copy_constructible<T>::value
+#define SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::is_trivially_copy_assignable<T>::value
+#define SOL_TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible<T>::value
+#endif
+
+#if __cplusplus > 201103L
+#define SOL_TL_OPTIONAL_CXX14
+#endif
+
+#if (__cplusplus == 201103L || defined(SOL_TL_OPTIONAL_MSVC2015) || defined(SOL_TL_OPTIONAL_GCC49))
+#define SOL_TL_OPTIONAL_11_CONSTEXPR
+#else
+ /// \exclude
+#define SOL_TL_OPTIONAL_11_CONSTEXPR constexpr
+#endif
+
+namespace sol {
+#ifndef SOL_TL_MONOSTATE_INPLACE_MUTEX
+#define SOL_TL_MONOSTATE_INPLACE_MUTEX
+ /// \brief Used to represent an optional with no data; essentially a bool
+ class monostate { };
+#endif
+
+ template <class T>
+ class optional;
+
+ /// \exclude
+ namespace detail {
+#ifndef SOL_TL_TRAITS_MUTEX
+#define SOL_TL_TRAITS_MUTEX
+ // C++14-style aliases for brevity
+ template <class T>
+ using remove_const_t = typename std::remove_const<T>::type;
+ template <class T>
+ using remove_reference_t = typename std::remove_reference<T>::type;
+ template <class T>
+ using decay_t = typename std::decay<T>::type;
+ template <bool E, class T = void>
+ using enable_if_t = typename std::enable_if<E, T>::type;
+ template <bool B, class T, class F>
+ using conditional_t = typename std::conditional<B, T, F>::type;
+
+ // std::conjunction from C++17
+ template <class...>
+ struct conjunction : std::true_type { };
+ template <class B>
+ struct conjunction<B> : B { };
+ template <class B, class... Bs>
+ struct conjunction<B, Bs...> : std::conditional<bool(B::value), conjunction<Bs...>, B>::type { };
+
+#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
+#define SOL_TL_OPTIONAL_LIBCXX_MEM_FN_WORKAROUND
+#endif
+
+#ifdef SOL_TL_OPTIONAL_LIBCXX_MEM_FN_WORKAROUND
+ template <class T>
+ struct is_pointer_to_non_const_member_func : std::false_type { };
+ template <class T, class Ret, class... Args>
+ struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)> : std::true_type { };
+ template <class T, class Ret, class... Args>
+ struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...)&> : std::true_type { };
+ template <class T, class Ret, class... Args>
+ struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) &&> : std::true_type { };
+ template <class T, class Ret, class... Args>
+ struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile> : std::true_type { };
+ template <class T, class Ret, class... Args>
+ struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&> : std::true_type { };
+ template <class T, class Ret, class... Args>
+ struct is_pointer_to_non_const_member_func<Ret (T::*)(Args...) volatile&&> : std::true_type { };
+
+ template <class T>
+ struct is_const_or_const_ref : std::false_type { };
+ template <class T>
+ struct is_const_or_const_ref<T const&> : std::true_type { };
+ template <class T>
+ struct is_const_or_const_ref<T const> : std::true_type { };
+#endif
+
+ // std::invoke from C++17
+ // https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
+ template <typename Fn, typename... Args,
+#ifdef SOL_TL_OPTIONAL_LIBCXX_MEM_FN_WORKAROUND
+ typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value && is_const_or_const_ref<Args...>::value)>,
+#endif
+ typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, int = 0>
+ constexpr auto invoke(Fn&& f, Args&&... args) noexcept(noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
+ -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
+ return std::mem_fn(f)(std::forward<Args>(args)...);
+ }
+
+ template <typename Fn, typename... Args, typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
+ constexpr auto invoke(Fn&& f, Args&&... args) noexcept(noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
+ -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
+ return std::forward<Fn>(f)(std::forward<Args>(args)...);
+ }
+
+ // std::invoke_result from C++17
+ template <class F, class, class... Us>
+ struct invoke_result_impl;
+
+ template <class F, class... Us>
+ struct invoke_result_impl<F, decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()), Us...> {
+ using type = decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
+ };
+
+ template <class F, class... Us>
+ using invoke_result = invoke_result_impl<F, void, Us...>;
+
+ template <class F, class... Us>
+ using invoke_result_t = typename invoke_result<F, Us...>::type;
+#endif
+
+ // std::void_t from C++17
+ template <class...>
+ struct voider {
+ using type = void;
+ };
+ template <class... Ts>
+ using void_t = typename voider<Ts...>::type;
+
+ // Trait for checking if a type is a sol::optional
+ template <class T>
+ struct is_optional_impl : std::false_type { };
+ template <class T>
+ struct is_optional_impl<optional<T>> : std::true_type { };
+ template <class T>
+ using is_optional = is_optional_impl<decay_t<T>>;
+
+ // Change void to sol::monostate
+ template <class U>
+ using fixup_void = conditional_t<std::is_void<U>::value, monostate, U>;
+
+ template <class F, class U, class = invoke_result_t<F, U>>
+ using get_map_return = optional<fixup_void<invoke_result_t<F, U>>>;
+
+ // Check if invoking F for some Us returns void
+ template <class F, class = void, class... U>
+ struct returns_void_impl;
+ template <class F, class... U>
+ struct returns_void_impl<F, void_t<invoke_result_t<F, U...>>, U...> : std::is_void<invoke_result_t<F, U...>> { };
+ template <class F, class... U>
+ using returns_void = returns_void_impl<F, void, U...>;
+
+ template <class T, class... U>
+ using enable_if_ret_void = enable_if_t<returns_void<T&&, U...>::value>;
+
+ template <class T, class... U>
+ using disable_if_ret_void = enable_if_t<!returns_void<T&&, U...>::value>;
+
+ template <class T, class U>
+ using enable_forward_value = detail::enable_if_t<std::is_constructible<T, U&&>::value && !std::is_same<detail::decay_t<U>, in_place_t>::value
+ && !std::is_same<optional<T>, detail::decay_t<U>>::value>;
+
+ template <class T, class U, class Other>
+ using enable_from_other = detail::enable_if_t<std::is_constructible<T, Other>::value && !std::is_constructible<T, optional<U>&>::value
+ && !std::is_constructible<T, optional<U>&&>::value && !std::is_constructible<T, const optional<U>&>::value
+ && !std::is_constructible<T, const optional<U>&&>::value && !std::is_convertible<optional<U>&, T>::value
+ && !std::is_convertible<optional<U>&&, T>::value && !std::is_convertible<const optional<U>&, T>::value
+ && !std::is_convertible<const optional<U>&&, T>::value>;
+
+ template <class T, class U>
+ using enable_assign_forward = detail::enable_if_t<!std::is_same<optional<T>, detail::decay_t<U>>::value
+ && !detail::conjunction<std::is_scalar<T>, std::is_same<T, detail::decay_t<U>>>::value && std::is_constructible<T, U>::value
+ && std::is_assignable<T&, U>::value>;
+
+ template <class T, class U, class Other>
+ using enable_assign_from_other = detail::enable_if_t<std::is_constructible<T, Other>::value && std::is_assignable<T&, Other>::value
+ && !std::is_constructible<T, optional<U>&>::value && !std::is_constructible<T, optional<U>&&>::value
+ && !std::is_constructible<T, const optional<U>&>::value && !std::is_constructible<T, const optional<U>&&>::value
+ && !std::is_convertible<optional<U>&, T>::value && !std::is_convertible<optional<U>&&, T>::value
+ && !std::is_convertible<const optional<U>&, T>::value && !std::is_convertible<const optional<U>&&, T>::value
+ && !std::is_assignable<T&, optional<U>&>::value && !std::is_assignable<T&, optional<U>&&>::value
+ && !std::is_assignable<T&, const optional<U>&>::value && !std::is_assignable<T&, const optional<U>&&>::value>;
+
+#ifdef _MSC_VER
+ // TODO make a version which works with MSVC
+ template <class T, class U = T>
+ struct is_swappable : std::true_type { };
+
+ template <class T, class U = T>
+ struct is_nothrow_swappable : std::true_type { };
+#else
+ // https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
+ namespace swap_adl_tests {
+ // if swap ADL finds this then it would call std::swap otherwise (same
+ // signature)
+ struct tag { };
+
+ template <class T>
+ tag swap(T&, T&);
+ template <class T, std::size_t N>
+ tag swap(T (&a)[N], T (&b)[N]);
+
+ // helper functions to test if an unqualified swap is possible, and if it
+ // becomes std::swap
+ template <class, class>
+ std::false_type can_swap(...) noexcept(false);
+ template <class T, class U, class = decltype(swap(std::declval<T&>(), std::declval<U&>()))>
+ std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T&>(), std::declval<U&>())));
+
+ template <class, class>
+ std::false_type uses_std(...);
+ template <class T, class U>
+ std::is_same<decltype(swap(std::declval<T&>(), std::declval<U&>())), tag> uses_std(int);
+
+ template <class T>
+ struct is_std_swap_noexcept
+ : std::integral_constant<bool, std::is_nothrow_move_constructible<T>::value && std::is_nothrow_move_assignable<T>::value> { };
+
+ template <class T, std::size_t N>
+ struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> { };
+
+ template <class T, class U>
+ struct is_adl_swap_noexcept : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> { };
+ } // namespace swap_adl_tests
+
+ template <class T, class U = T>
+ struct is_swappable : std::integral_constant<bool,
+ decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value
+ && (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value
+ || (std::is_move_assignable<T>::value && std::is_move_constructible<T>::value))> { };
+
+ template <class T, std::size_t N>
+ struct is_swappable<T[N], T[N]> : std::integral_constant<bool,
+ decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value
+ && (!decltype(detail::swap_adl_tests::uses_std<T[N], T[N]>(0))::value || is_swappable<T, T>::value)> { };
+
+ template <class T, class U = T>
+ struct is_nothrow_swappable
+ : std::integral_constant<bool,
+ is_swappable<T, U>::value
+ && ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value&& detail::swap_adl_tests::is_std_swap_noexcept<T>::value)
+ || (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value&& detail::swap_adl_tests::is_adl_swap_noexcept<T, U>::value))> { };
+#endif
+
+ // The storage base manages the actual storage, and correctly propagates
+ // trivial destroyion from T. This case is for when T is not trivially
+ // destructible.
+ template <class T, bool = ::std::is_trivially_destructible<T>::value>
+ struct optional_storage_base {
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept : m_dummy(), m_has_value(false) {
+ }
+
+ template <class... U>
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U&&... u) : m_value(std::forward<U>(u)...), m_has_value(true) {
+ }
+
+ ~optional_storage_base() {
+ if (m_has_value) {
+ m_value.~T();
+ m_has_value = false;
+ }
+ }
+
+ struct dummy { };
+ union {
+ dummy m_dummy;
+ T m_value;
+ };
+
+ bool m_has_value;
+ };
+
+ // This case is for when T is trivially destructible.
+ template <class T>
+ struct optional_storage_base<T, true> {
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept : m_dummy(), m_has_value(false) {
+ }
+
+ template <class... U>
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U&&... u) : m_value(std::forward<U>(u)...), m_has_value(true) {
+ }
+
+ // No destructor, so this class is trivially destructible
+
+ struct dummy { };
+ union {
+ dummy m_dummy;
+ T m_value;
+ };
+
+ bool m_has_value = false;
+ };
+
+ // This base class provides some handy member functions which can be used in
+ // further derived classes
+ template <class T>
+ struct optional_operations_base : optional_storage_base<T> {
+ using optional_storage_base<T>::optional_storage_base;
+
+ void hard_reset() noexcept {
+ get().~T();
+ this->m_has_value = false;
+ }
+
+ template <class... Args>
+ void construct(Args&&... args) noexcept {
+ new (std::addressof(this->m_value)) T(std::forward<Args>(args)...);
+ this->m_has_value = true;
+ }
+
+ template <class Opt>
+ void assign(Opt&& rhs) {
+ if (this->has_value()) {
+ if (rhs.has_value()) {
+ this->m_value = std::forward<Opt>(rhs).get();
+ }
+ else {
+ this->m_value.~T();
+ this->m_has_value = false;
+ }
+ }
+
+ else if (rhs.has_value()) {
+ construct(std::forward<Opt>(rhs).get());
+ }
+ }
+
+ bool has_value() const {
+ return this->m_has_value;
+ }
+
+ SOL_TL_OPTIONAL_11_CONSTEXPR T& get() & {
+ return this->m_value;
+ }
+ SOL_TL_OPTIONAL_11_CONSTEXPR const T& get() const& {
+ return this->m_value;
+ }
+ SOL_TL_OPTIONAL_11_CONSTEXPR T&& get() && {
+ return std::move(this->m_value);
+ }
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ constexpr const T&& get() const&& {
+ return std::move(this->m_value);
+ }
+#endif
+ };
+
+ // This class manages conditionally having a trivial copy constructor
+ // This specialization is for when T is trivially copy constructible
+ template <class T, bool = SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>
+ struct optional_copy_base : optional_operations_base<T> {
+ using optional_operations_base<T>::optional_operations_base;
+ };
+
+ // This specialization is for when T is not trivially copy constructible
+ template <class T>
+ struct optional_copy_base<T, false> : optional_operations_base<T> {
+ using base_t = optional_operations_base<T>;
+
+ using base_t::base_t;
+
+ optional_copy_base() = default;
+ optional_copy_base(const optional_copy_base& rhs) : base_t() {
+ if (rhs.has_value()) {
+ this->construct(rhs.get());
+ }
+ else {
+ this->m_has_value = false;
+ }
+ }
+
+ optional_copy_base(optional_copy_base&& rhs) = default;
+ optional_copy_base& operator=(const optional_copy_base& rhs) = default;
+ optional_copy_base& operator=(optional_copy_base&& rhs) = default;
+ };
+
+#ifndef SOL_TL_OPTIONAL_GCC49
+ template <class T, bool = std::is_trivially_move_constructible<T>::value>
+ struct optional_move_base : optional_copy_base<T> {
+ using optional_copy_base<T>::optional_copy_base;
+ };
+#else
+ template <class T, bool = false>
+ struct optional_move_base;
+#endif
+ template <class T>
+ struct optional_move_base<T, false> : optional_copy_base<T> {
+ using optional_copy_base<T>::optional_copy_base;
+
+ optional_move_base() = default;
+ optional_move_base(const optional_move_base& rhs) = default;
+
+ optional_move_base(optional_move_base&& rhs) noexcept(std::is_nothrow_move_constructible<T>::value) {
+ if (rhs.has_value()) {
+ this->construct(std::move(rhs.get()));
+ }
+ else {
+ this->m_has_value = false;
+ }
+ }
+ optional_move_base& operator=(const optional_move_base& rhs) = default;
+ optional_move_base& operator=(optional_move_base&& rhs) = default;
+ };
+
+ // This class manages conditionally having a trivial copy assignment operator
+ template <class T,
+ bool = SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) && SOL_TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)
+ && SOL_TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T)>
+ struct optional_copy_assign_base : optional_move_base<T> {
+ using optional_move_base<T>::optional_move_base;
+ };
+
+ template <class T>
+ struct optional_copy_assign_base<T, false> : optional_move_base<T> {
+ using optional_move_base<T>::optional_move_base;
+
+ optional_copy_assign_base() = default;
+ optional_copy_assign_base(const optional_copy_assign_base& rhs) = default;
+
+ optional_copy_assign_base(optional_copy_assign_base&& rhs) = default;
+ optional_copy_assign_base& operator=(const optional_copy_assign_base& rhs) {
+ this->assign(rhs);
+ return *this;
+ }
+ optional_copy_assign_base& operator=(optional_copy_assign_base&& rhs) = default;
+ };
+
+#ifndef SOL_TL_OPTIONAL_GCC49
+ template <class T,
+ bool = std::is_trivially_destructible<T>::value&& std::is_trivially_move_constructible<T>::value&& std::is_trivially_move_assignable<T>::value>
+ struct optional_move_assign_base : optional_copy_assign_base<T> {
+ using optional_copy_assign_base<T>::optional_copy_assign_base;
+ };
+#else
+ template <class T, bool = false>
+ struct optional_move_assign_base;
+#endif
+
+ template <class T>
+ struct optional_move_assign_base<T, false> : optional_copy_assign_base<T> {
+ using optional_copy_assign_base<T>::optional_copy_assign_base;
+
+ optional_move_assign_base() = default;
+ optional_move_assign_base(const optional_move_assign_base& rhs) = default;
+
+ optional_move_assign_base(optional_move_assign_base&& rhs) = default;
+
+ optional_move_assign_base& operator=(const optional_move_assign_base& rhs) = default;
+
+ optional_move_assign_base& operator=(optional_move_assign_base&& rhs) noexcept(
+ std::is_nothrow_move_constructible<T>::value&& std::is_nothrow_move_assignable<T>::value) {
+ this->assign(std::move(rhs));
+ return *this;
+ }
+ };
+
+ // optional_delete_ctor_base will conditionally delete copy and move
+ // constructors depending on whether T is copy/move constructible
+ template <class T, bool EnableCopy = std::is_copy_constructible<T>::value, bool EnableMove = std::is_move_constructible<T>::value>
+ struct optional_delete_ctor_base {
+ optional_delete_ctor_base() = default;
+ optional_delete_ctor_base(const optional_delete_ctor_base&) = default;
+ optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = default;
+ optional_delete_ctor_base& operator=(const optional_delete_ctor_base&) = default;
+ optional_delete_ctor_base& operator=(optional_delete_ctor_base&&) noexcept = default;
+ };
+
+ template <class T>
+ struct optional_delete_ctor_base<T, true, false> {
+ optional_delete_ctor_base() = default;
+ optional_delete_ctor_base(const optional_delete_ctor_base&) = default;
+ optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = delete;
+ optional_delete_ctor_base& operator=(const optional_delete_ctor_base&) = default;
+ optional_delete_ctor_base& operator=(optional_delete_ctor_base&&) noexcept = default;
+ };
+
+ template <class T>
+ struct optional_delete_ctor_base<T, false, true> {
+ optional_delete_ctor_base() = default;
+ optional_delete_ctor_base(const optional_delete_ctor_base&) = delete;
+ optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = default;
+ optional_delete_ctor_base& operator=(const optional_delete_ctor_base&) = default;
+ optional_delete_ctor_base& operator=(optional_delete_ctor_base&&) noexcept = default;
+ };
+
+ template <class T>
+ struct optional_delete_ctor_base<T, false, false> {
+ optional_delete_ctor_base() = default;
+ optional_delete_ctor_base(const optional_delete_ctor_base&) = delete;
+ optional_delete_ctor_base(optional_delete_ctor_base&&) noexcept = delete;
+ optional_delete_ctor_base& operator=(const optional_delete_ctor_base&) = default;
+ optional_delete_ctor_base& operator=(optional_delete_ctor_base&&) noexcept = default;
+ };
+
+ // optional_delete_assign_base will conditionally delete copy and move
+ // constructors depending on whether T is copy/move constructible + assignable
+ template <class T, bool EnableCopy = (std::is_copy_constructible<T>::value && std::is_copy_assignable<T>::value),
+ bool EnableMove = (std::is_move_constructible<T>::value && std::is_move_assignable<T>::value)>
+ struct optional_delete_assign_base {
+ optional_delete_assign_base() = default;
+ optional_delete_assign_base(const optional_delete_assign_base&) = default;
+ optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default;
+ optional_delete_assign_base& operator=(const optional_delete_assign_base&) = default;
+ optional_delete_assign_base& operator=(optional_delete_assign_base&&) noexcept = default;
+ };
+
+ template <class T>
+ struct optional_delete_assign_base<T, true, false> {
+ optional_delete_assign_base() = default;
+ optional_delete_assign_base(const optional_delete_assign_base&) = default;
+ optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default;
+ optional_delete_assign_base& operator=(const optional_delete_assign_base&) = default;
+ optional_delete_assign_base& operator=(optional_delete_assign_base&&) noexcept = delete;
+ };
+
+ template <class T>
+ struct optional_delete_assign_base<T, false, true> {
+ optional_delete_assign_base() = default;
+ optional_delete_assign_base(const optional_delete_assign_base&) = default;
+ optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default;
+ optional_delete_assign_base& operator=(const optional_delete_assign_base&) = delete;
+ optional_delete_assign_base& operator=(optional_delete_assign_base&&) noexcept = default;
+ };
+
+ template <class T>
+ struct optional_delete_assign_base<T, false, false> {
+ optional_delete_assign_base() = default;
+ optional_delete_assign_base(const optional_delete_assign_base&) = default;
+ optional_delete_assign_base(optional_delete_assign_base&&) noexcept = default;
+ optional_delete_assign_base& operator=(const optional_delete_assign_base&) = delete;
+ optional_delete_assign_base& operator=(optional_delete_assign_base&&) noexcept = delete;
+ };
+
+ } // namespace detail
+
+ /// \brief A tag type to represent an empty optional
+ using nullopt_t = std::nullopt_t;
+
+ /// \brief Represents an empty optional
+ /// \synopsis static constexpr nullopt_t nullopt;
+ ///
+ /// *Examples*:
+ /// ```
+ /// sol::optional<int> a = sol::nullopt;
+ /// void foo (sol::optional<int>);
+ /// foo(sol::nullopt); //pass an empty optional
+ /// ```
+ using std::nullopt;
+
+ /// @brief An exception for when an optional is accessed through specific methods while it is not engaged.
+ class bad_optional_access : public std::exception {
+ public:
+ /// @brief Default-constructs an optional exception.
+ bad_optional_access() = default;
+ /// @brief Returns a pointer to a null-terminated string containing the reason for the exception.
+ const char* what() const noexcept override {
+ return "Optional has no value";
+ }
+ };
+
+ /// An optional object is an object that contains the storage for another
+ /// object and manages the lifetime of this contained object, if any. The
+ /// contained object may be initialized after the optional object has been
+ /// initialized, and may be destroyed before the optional object has been
+ /// destroyed. The initialization state of the contained object is tracked by
+ /// the optional object.
+ template <class T>
+ class optional : private detail::optional_move_assign_base<T>,
+ private detail::optional_delete_ctor_base<T>,
+ private detail::optional_delete_assign_base<T> {
+ using base = detail::optional_move_assign_base<T>;
+
+ static_assert(!std::is_same<T, in_place_t>::value, "instantiation of optional with in_place_t is ill-formed");
+ static_assert(!std::is_same<detail::decay_t<T>, nullopt_t>::value, "instantiation of optional with nullopt_t is ill-formed");
+
+ public:
+#if defined(SOL_TL_OPTIONAL_CXX14) && !defined(SOL_TL_OPTIONAL_GCC49) && !defined(SOL_TL_OPTIONAL_GCC54) && !defined(SOL_TL_OPTIONAL_GCC55)
+ /// \group and_then
+ /// Carries out some operation which returns an optional on the stored
+ /// object if there is one. \requires `std::invoke(std::forward<F>(f),
+ /// value())` returns a `std::optional<U>` for some `U`. \returns Let `U` be
+ /// the result of `std::invoke(std::forward<F>(f), value())`. Returns a
+ /// `std::optional<U>`. The return value is empty if `*this` is empty,
+ /// otherwise the return value of `std::invoke(std::forward<F>(f), value())`
+ /// is returned.
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) &;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) & {
+ using result = detail::invoke_result_t<F, T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) &&;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) && {
+ using result = detail::invoke_result_t<F, T&&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : result(nullopt);
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) const &;
+ template <class F>
+ constexpr auto and_then(F&& f) const& {
+ using result = detail::invoke_result_t<F, const T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) const &&;
+ template <class F>
+ constexpr auto and_then(F&& f) const&& {
+ using result = detail::invoke_result_t<F, const T&&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : result(nullopt);
+ }
+#endif
+#else
+ /// \group and_then
+ /// Carries out some operation which returns an optional on the stored
+ /// object if there is one. \requires `std::invoke(std::forward<F>(f),
+ /// value())` returns a `std::optional<U>` for some `U`.
+ /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
+ /// value())`. Returns a `std::optional<U>`. The return value is empty if
+ /// `*this` is empty, otherwise the return value of
+ /// `std::invoke(std::forward<F>(f), value())` is returned.
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) &;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T&> and_then(F&& f) & {
+ using result = detail::invoke_result_t<F, T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) &&;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T&&> and_then(F&& f) && {
+ using result = detail::invoke_result_t<F, T&&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : result(nullopt);
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) const &;
+ template <class F>
+ constexpr detail::invoke_result_t<F, const T&> and_then(F&& f) const& {
+ using result = detail::invoke_result_t<F, const T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) const &&;
+ template <class F>
+ constexpr detail::invoke_result_t<F, const T&&> and_then(F&& f) const&& {
+ using result = detail::invoke_result_t<F, const T&&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : result(nullopt);
+ }
+#endif
+#endif
+
+#if defined(SOL_TL_OPTIONAL_CXX14) && !defined(SOL_TL_OPTIONAL_GCC49) && !defined(SOL_TL_OPTIONAL_GCC54) && !defined(SOL_TL_OPTIONAL_GCC55)
+ /// \brief Carries out some operation on the stored object if there is one.
+ /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
+ /// value())`. Returns a `std::optional<U>`. The return value is empty if
+ /// `*this` is empty, otherwise an `optional<U>` is constructed from the
+ /// return value of `std::invoke(std::forward<F>(f), value())` and is
+ /// returned.
+ ///
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) &;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) & {
+ return optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) &&;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) && {
+ return optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) const&;
+ template <class F>
+ constexpr auto map(F&& f) const& {
+ return optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) const&&;
+ template <class F>
+ constexpr auto map(F&& f) const&& {
+ return optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#else
+ /// \brief Carries out some operation on the stored object if there is one.
+ /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
+ /// value())`. Returns a `std::optional<U>`. The return value is empty if
+ /// `*this` is empty, otherwise an `optional<U>` is constructed from the
+ /// return value of `std::invoke(std::forward<F>(f), value())` and is
+ /// returned.
+ ///
+ /// \group map
+ /// \synopsis template <class F> auto map(F &&f) &;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval<optional&>(), std::declval<F&&>())) map(F&& f) & {
+ return optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> auto map(F &&f) &&;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval<optional&&>(), std::declval<F&&>())) map(F&& f) && {
+ return optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> auto map(F &&f) const&;
+ template <class F>
+ constexpr decltype(optional_map_impl(std::declval<const optional&>(), std::declval<F&&>())) map(F&& f) const& {
+ return optional_map_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group map
+ /// \synopsis template <class F> auto map(F &&f) const&&;
+ template <class F>
+ constexpr decltype(optional_map_impl(std::declval<const optional&&>(), std::declval<F&&>())) map(F&& f) const&& {
+ return optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+
+ /// \brief Calls `f` if the optional is empty
+ /// \requires `std::invoke_result_t<F>` must be void or convertible to
+ /// `optional<T>`.
+ /// \effects If `*this` has a value, returns `*this`.
+ /// Otherwise, if `f` returns `void`, calls `std::forward<F>(f)` and returns
+ /// `std::nullopt`. Otherwise, returns `std::forward<F>(f)()`.
+ ///
+ /// \group or_else
+ /// \synopsis template <class F> optional<T> or_else (F &&f) &;
+ template <class F, detail::enable_if_ret_void<F>* = nullptr>
+ optional<T> SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) & {
+ if (has_value())
+ return *this;
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ /// \exclude
+ template <class F, detail::disable_if_ret_void<F>* = nullptr>
+ optional<T> SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) & {
+ return has_value() ? *this : std::forward<F>(f)();
+ }
+
+ /// \group or_else
+ /// \synopsis template <class F> optional<T> or_else (F &&f) &&;
+ template <class F, detail::enable_if_ret_void<F>* = nullptr>
+ optional<T> or_else(F&& f) && {
+ if (has_value())
+ return std::move(*this);
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ /// \exclude
+ template <class F, detail::disable_if_ret_void<F>* = nullptr>
+ optional<T> SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) && {
+ return has_value() ? std::move(*this) : std::forward<F>(f)();
+ }
+
+ /// \group or_else
+ /// \synopsis template <class F> optional<T> or_else (F &&f) const &;
+ template <class F, detail::enable_if_ret_void<F>* = nullptr>
+ optional<T> or_else(F&& f) const& {
+ if (has_value())
+ return *this;
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ /// \exclude
+ template <class F, detail::disable_if_ret_void<F>* = nullptr>
+ optional<T> SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) const& {
+ return has_value() ? *this : std::forward<F>(f)();
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \exclude
+ template <class F, detail::enable_if_ret_void<F>* = nullptr>
+ optional<T> or_else(F&& f) const&& {
+ if (has_value())
+ return std::move(*this);
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ /// \exclude
+ template <class F, detail::disable_if_ret_void<F>* = nullptr>
+ optional<T> or_else(F&& f) const&& {
+ return has_value() ? std::move(*this) : std::forward<F>(f)();
+ }
+#endif
+
+ /// \brief Maps the stored value with `f` if there is one, otherwise returns
+ /// `u`.
+ ///
+ /// \details If there is a value stored, then `f` is called with `**this`
+ /// and the value is returned. Otherwise `u` is returned.
+ ///
+ /// \group map_or
+ template <class F, class U>
+ U map_or(F&& f, U&& u) & {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : std::forward<U>(u);
+ }
+
+ /// \group map_or
+ template <class F, class U>
+ U map_or(F&& f, U&& u) && {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : std::forward<U>(u);
+ }
+
+ /// \group map_or
+ template <class F, class U>
+ U map_or(F&& f, U&& u) const& {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : std::forward<U>(u);
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group map_or
+ template <class F, class U>
+ U map_or(F&& f, U&& u) const&& {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : std::forward<U>(u);
+ }
+#endif
+
+ /// \brief Maps the stored value with `f` if there is one, otherwise calls
+ /// `u` and returns the result.
+ ///
+ /// \details If there is a value stored, then `f` is
+ /// called with `**this` and the value is returned. Otherwise
+ /// `std::forward<U>(u)()` is returned.
+ ///
+ /// \group map_or_else
+ /// \synopsis template <class F, class U> \n auto map_or_else(F &&f, U &&u) &;
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F&& f, U&& u) & {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : std::forward<U>(u)();
+ }
+
+ /// \group map_or_else
+ /// \synopsis template <class F, class U> \n auto map_or_else(F &&f, U &&u)
+ /// &&;
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F&& f, U&& u) && {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : std::forward<U>(u)();
+ }
+
+ /// \group map_or_else
+ /// \synopsis template <class F, class U> \n auto map_or_else(F &&f, U &&u)
+ /// const &;
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F&& f, U&& u) const& {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : std::forward<U>(u)();
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group map_or_else
+ /// \synopsis template <class F, class U> \n auto map_or_else(F &&f, U &&u)
+ /// const &&;
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F&& f, U&& u) const&& {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : std::forward<U>(u)();
+ }
+#endif
+
+ /// \returns `u` if `*this` has a value, otherwise an empty optional.
+ template <class U>
+ constexpr optional<typename std::decay<U>::type> conjunction(U&& u) const {
+ using result = optional<detail::decay_t<U>>;
+ return has_value() ? result { u } : result { nullopt };
+ }
+
+ /// \returns `rhs` if `*this` is empty, otherwise the current value.
+ /// \group disjunction
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) & {
+ return has_value() ? *this : rhs;
+ }
+
+ /// \group disjunction
+ constexpr optional disjunction(const optional& rhs) const& {
+ return has_value() ? *this : rhs;
+ }
+
+ /// \group disjunction
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) && {
+ return has_value() ? std::move(*this) : rhs;
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group disjunction
+ constexpr optional disjunction(const optional& rhs) const&& {
+ return has_value() ? std::move(*this) : rhs;
+ }
+#endif
+
+ /// \group disjunction
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) & {
+ return has_value() ? *this : std::move(rhs);
+ }
+
+ /// \group disjunction
+ constexpr optional disjunction(optional&& rhs) const& {
+ return has_value() ? *this : std::move(rhs);
+ }
+
+ /// \group disjunction
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) && {
+ return has_value() ? std::move(*this) : std::move(rhs);
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group disjunction
+ constexpr optional disjunction(optional&& rhs) const&& {
+ return has_value() ? std::move(*this) : std::move(rhs);
+ }
+#endif
+
+ /// Takes the value out of the optional, leaving it empty
+ /// \group take
+ optional take() & {
+ optional ret = *this;
+ reset();
+ return ret;
+ }
+
+ /// \group take
+ optional take() const& {
+ optional ret = *this;
+ reset();
+ return ret;
+ }
+
+ /// \group take
+ optional take() && {
+ optional ret = std::move(*this);
+ reset();
+ return ret;
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group take
+ optional take() const&& {
+ optional ret = std::move(*this);
+ reset();
+ return ret;
+ }
+#endif
+
+ using value_type = T;
+
+ /// Constructs an optional that does not contain a value.
+ /// \group ctor_empty
+ constexpr optional() noexcept = default;
+
+ /// \group ctor_empty
+ constexpr optional(nullopt_t) noexcept {
+ }
+
+ /// Copy constructor
+ ///
+ /// If `rhs` contains a value, the stored value is direct-initialized with
+ /// it. Otherwise, the constructed optional is empty.
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional(const optional& rhs) = default;
+
+ /// Move constructor
+ ///
+ /// If `rhs` contains a value, the stored value is direct-initialized with
+ /// it. Otherwise, the constructed optional is empty.
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional(optional&& rhs) = default;
+
+ /// Constructs the stored value in-place using the given arguments.
+ /// \group in_place
+ /// \synopsis template <class... Args> constexpr explicit optional(in_place_t, Args&&... args);
+ template <class... Args>
+ constexpr explicit optional(detail::enable_if_t<std::is_constructible<T, Args...>::value, in_place_t>, Args&&... args)
+ : base(in_place, std::forward<Args>(args)...) {
+ }
+
+ /// \group in_place
+ /// \synopsis template <class U, class... Args> \n constexpr explicit optional(in_place_t, std::initializer_list<U>&, Args&&... args);
+ template <class U, class... Args>
+ SOL_TL_OPTIONAL_11_CONSTEXPR explicit optional(detail::enable_if_t<std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value, in_place_t>,
+ std::initializer_list<U> il, Args&&... args) {
+ this->construct(il, std::forward<Args>(args)...);
+ }
+
+#if 0 // SOL_MODIFICATION
+ /// Constructs the stored value with `u`.
+ /// \synopsis template <class U=T> constexpr optional(U &&u);
+ template <class U = T, detail::enable_if_t<std::is_convertible<U&&, T>::value>* = nullptr, detail::enable_forward_value<T, U>* = nullptr>
+ constexpr optional(U&& u) : base(in_place, std::forward<U>(u)) {
+ }
+
+ /// \exclude
+ template <class U = T, detail::enable_if_t<!std::is_convertible<U&&, T>::value>* = nullptr, detail::enable_forward_value<T, U>* = nullptr>
+ constexpr explicit optional(U&& u) : base(in_place, std::forward<U>(u)) {
+ }
+#else
+ /// Constructs the stored value with `u`.
+ /// \synopsis template <class U=T> constexpr optional(U &&u);
+ constexpr optional(T&& u) : base(in_place, std::move(u)) {
+ }
+
+ /// \exclude
+ constexpr optional(const T& u) : base(in_place, u) {
+ }
+#endif // sol2 modification
+
+ /// Converting copy constructor.
+ /// \synopsis template <class U> optional(const optional<U> &rhs);
+ template <class U, detail::enable_from_other<T, U, const U&>* = nullptr, detail::enable_if_t<std::is_convertible<const U&, T>::value>* = nullptr>
+ optional(const optional<U>& rhs) {
+ if (rhs.has_value()) {
+ this->construct(*rhs);
+ }
+ }
+
+ /// \exclude
+ template <class U, detail::enable_from_other<T, U, const U&>* = nullptr, detail::enable_if_t<!std::is_convertible<const U&, T>::value>* = nullptr>
+ explicit optional(const optional<U>& rhs) {
+ if (rhs.has_value()) {
+ this->construct(*rhs);
+ }
+ }
+
+ /// Converting move constructor.
+ /// \synopsis template <class U> optional(optional<U> &&rhs);
+ template <class U, detail::enable_from_other<T, U, U&&>* = nullptr, detail::enable_if_t<std::is_convertible<U&&, T>::value>* = nullptr>
+ optional(optional<U>&& rhs) {
+ if (rhs.has_value()) {
+ this->construct(std::move(*rhs));
+ }
+ }
+
+ /// \exclude
+ template <class U, detail::enable_from_other<T, U, U&&>* = nullptr, detail::enable_if_t<!std::is_convertible<U&&, T>::value>* = nullptr>
+ explicit optional(optional<U>&& rhs) {
+ this->construct(std::move(*rhs));
+ }
+
+ /// Destroys the stored value if there is one.
+ ~optional() = default;
+
+ /// Assignment to empty.
+ ///
+ /// Destroys the current value if there is one.
+ optional& operator=(nullopt_t) noexcept {
+ if (has_value()) {
+ this->m_value.~T();
+ this->m_has_value = false;
+ }
+
+ return *this;
+ }
+
+ /// Copy assignment.
+ ///
+ /// Copies the value from `rhs` if there is one. Otherwise resets the stored
+ /// value in `*this`.
+ optional& operator=(const optional& rhs) = default;
+
+ /// Move assignment.
+ ///
+ /// Moves the value from `rhs` if there is one. Otherwise resets the stored
+ /// value in `*this`.
+ optional& operator=(optional&& rhs) = default;
+
+ /// Assigns the stored value from `u`, destroying the old value if there was
+ /// one.
+ /// \synopsis optional &operator=(U &&u);
+ template <class U = T, detail::enable_assign_forward<T, U>* = nullptr>
+ optional& operator=(U&& u) {
+ if (has_value()) {
+ this->m_value = std::forward<U>(u);
+ }
+ else {
+ this->construct(std::forward<U>(u));
+ }
+
+ return *this;
+ }
+
+ /// Converting copy assignment operator.
+ ///
+ /// Copies the value from `rhs` if there is one. Otherwise resets the stored
+ /// value in `*this`.
+ /// \synopsis optional &operator=(const optional<U> & rhs);
+ template <class U, detail::enable_assign_from_other<T, U, const U&>* = nullptr>
+ optional& operator=(const optional<U>& rhs) {
+ if (has_value()) {
+ if (rhs.has_value()) {
+ this->m_value = *rhs;
+ }
+ else {
+ this->hard_reset();
+ }
+ }
+
+ if (rhs.has_value()) {
+ this->construct(*rhs);
+ }
+
+ return *this;
+ }
+
+ // TODO check exception guarantee
+ /// Converting move assignment operator.
+ ///
+ /// Moves the value from `rhs` if there is one. Otherwise resets the stored
+ /// value in `*this`.
+ /// \synopsis optional &operator=(optional<U> && rhs);
+ template <class U, detail::enable_assign_from_other<T, U, U>* = nullptr>
+ optional& operator=(optional<U>&& rhs) {
+ if (has_value()) {
+ if (rhs.has_value()) {
+ this->m_value = std::move(*rhs);
+ }
+ else {
+ this->hard_reset();
+ }
+ }
+
+ if (rhs.has_value()) {
+ this->construct(std::move(*rhs));
+ }
+
+ return *this;
+ }
+
+ /// Constructs the value in-place, destroying the current one if there is
+ /// one.
+ /// \group emplace
+ template <class... Args>
+ T& emplace(Args&&... args) {
+ static_assert(std::is_constructible<T, Args&&...>::value, "T must be constructible with Args");
+
+ *this = nullopt;
+ this->construct(std::forward<Args>(args)...);
+ return value();
+ }
+
+ /// \group emplace
+ /// \synopsis template <class U, class... Args> \n T& emplace(std::initializer_list<U> il, Args &&... args);
+ template <class U, class... Args>
+ detail::enable_if_t<std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value, T&> emplace(std::initializer_list<U> il, Args&&... args) {
+ *this = nullopt;
+ this->construct(il, std::forward<Args>(args)...);
+ return value();
+ }
+
+ /// Swaps this optional with the other.
+ ///
+ /// If neither optionals have a value, nothing happens.
+ /// If both have a value, the values are swapped.
+ /// If one has a value, it is moved to the other and the movee is left
+ /// valueless.
+ void swap(optional& rhs) noexcept(std::is_nothrow_move_constructible<T>::value&& detail::is_nothrow_swappable<T>::value) {
+ if (has_value()) {
+ if (rhs.has_value()) {
+ using std::swap;
+ swap(**this, *rhs);
+ }
+ else {
+ new (std::addressof(rhs.m_value)) T(std::move(this->m_value));
+ this->m_value.T::~T();
+ }
+ }
+ else if (rhs.has_value()) {
+ new (std::addressof(this->m_value)) T(std::move(rhs.m_value));
+ rhs.m_value.T::~T();
+ }
+ }
+
+ /// \returns a pointer to the stored value
+ /// \requires a value is stored
+ /// \group pointer
+ /// \synopsis constexpr const T *operator->() const;
+ constexpr const T* operator->() const {
+ return std::addressof(this->m_value);
+ }
+
+ /// \group pointer
+ /// \synopsis constexpr T *operator->();
+ SOL_TL_OPTIONAL_11_CONSTEXPR T* operator->() {
+ return std::addressof(this->m_value);
+ }
+
+ /// \returns the stored value
+ /// \requires a value is stored
+ /// \group deref
+ /// \synopsis constexpr T &operator*();
+ SOL_TL_OPTIONAL_11_CONSTEXPR T& operator*() & {
+ return this->m_value;
+ }
+
+ /// \group deref
+ /// \synopsis constexpr const T &operator*() const;
+ constexpr const T& operator*() const& {
+ return this->m_value;
+ }
+
+ /// \exclude
+ SOL_TL_OPTIONAL_11_CONSTEXPR T&& operator*() && {
+ return std::move(this->m_value);
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \exclude
+ constexpr const T&& operator*() const&& {
+ return std::move(this->m_value);
+ }
+#endif
+
+ /// \returns whether or not the optional has a value
+ /// \group has_value
+ constexpr bool has_value() const noexcept {
+ return this->m_has_value;
+ }
+
+ /// \group has_value
+ constexpr explicit operator bool() const noexcept {
+ return this->m_has_value;
+ }
+
+ /// \returns the contained value if there is one, otherwise throws
+ /// [bad_optional_access]
+ /// \group value
+ /// \synopsis constexpr T &value();
+ SOL_TL_OPTIONAL_11_CONSTEXPR T& value() & {
+ if (has_value())
+ return this->m_value;
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ std::abort();
+#else
+ throw bad_optional_access();
+#endif // No exceptions allowed
+ }
+ /// \group value
+ /// \synopsis constexpr const T &value() const;
+ SOL_TL_OPTIONAL_11_CONSTEXPR const T& value() const& {
+ if (has_value())
+ return this->m_value;
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ std::abort();
+#else
+ throw bad_optional_access();
+#endif // No exceptions allowed
+ }
+ /// \exclude
+ SOL_TL_OPTIONAL_11_CONSTEXPR T&& value() && {
+ if (has_value())
+ return std::move(this->m_value);
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ std::abort();
+#else
+ throw bad_optional_access();
+#endif // No exceptions allowed
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \exclude
+ SOL_TL_OPTIONAL_11_CONSTEXPR const T&& value() const&& {
+ if (has_value())
+ return std::move(this->m_value);
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ std::abort();
+#else
+ throw bad_optional_access();
+#endif // No exceptions allowed
+ }
+#endif
+
+ /// \returns the stored value if there is one, otherwise returns `u`
+ /// \group value_or
+ template <class U>
+ constexpr T value_or(U&& u) const& {
+ static_assert(std::is_copy_constructible<T>::value && std::is_convertible<U&&, T>::value, "T must be copy constructible and convertible from U");
+ return has_value() ? **this : static_cast<T>(std::forward<U>(u));
+ }
+
+ /// \group value_or
+ template <class U>
+ SOL_TL_OPTIONAL_11_CONSTEXPR T value_or(U&& u) && {
+ static_assert(std::is_move_constructible<T>::value && std::is_convertible<U&&, T>::value, "T must be move constructible and convertible from U");
+ return has_value() ? **this : static_cast<T>(std::forward<U>(u));
+ }
+
+ /// Destroys the stored value if one exists, making the optional empty
+ void reset() noexcept {
+ if (has_value()) {
+ this->m_value.~T();
+ this->m_has_value = false;
+ }
+ }
+ }; // namespace sol
+
+ /// \group relop
+ /// \brief Compares two optional objects
+ /// \details If both optionals contain a value, they are compared with `T`s
+ /// relational operators. Otherwise `lhs` and `rhs` are equal only if they are
+ /// both empty, and `lhs` is less than `rhs` only if `rhs` is empty and `lhs`
+ /// is not.
+ template <class T, class U>
+ inline constexpr bool operator==(const optional<T>& lhs, const optional<U>& rhs) {
+ return lhs.has_value() == rhs.has_value() && (!lhs.has_value() || *lhs == *rhs);
+ }
+ /// \group relop
+ template <class T, class U>
+ inline constexpr bool operator!=(const optional<T>& lhs, const optional<U>& rhs) {
+ return lhs.has_value() != rhs.has_value() || (lhs.has_value() && *lhs != *rhs);
+ }
+ /// \group relop
+ template <class T, class U>
+ inline constexpr bool operator<(const optional<T>& lhs, const optional<U>& rhs) {
+ return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs);
+ }
+ /// \group relop
+ template <class T, class U>
+ inline constexpr bool operator>(const optional<T>& lhs, const optional<U>& rhs) {
+ return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs);
+ }
+ /// \group relop
+ template <class T, class U>
+ inline constexpr bool operator<=(const optional<T>& lhs, const optional<U>& rhs) {
+ return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs);
+ }
+ /// \group relop
+ template <class T, class U>
+ inline constexpr bool operator>=(const optional<T>& lhs, const optional<U>& rhs) {
+ return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs);
+ }
+
+ /// \group relop_nullopt
+ /// \brief Compares an optional to a `nullopt`
+ /// \details Equivalent to comparing the optional to an empty optional
+ template <class T>
+ inline constexpr bool operator==(const optional<T>& lhs, nullopt_t) noexcept {
+ return !lhs.has_value();
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator==(nullopt_t, const optional<T>& rhs) noexcept {
+ return !rhs.has_value();
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator!=(const optional<T>& lhs, nullopt_t) noexcept {
+ return lhs.has_value();
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator!=(nullopt_t, const optional<T>& rhs) noexcept {
+ return rhs.has_value();
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator<(const optional<T>&, nullopt_t) noexcept {
+ return false;
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator<(nullopt_t, const optional<T>& rhs) noexcept {
+ return rhs.has_value();
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator<=(const optional<T>& lhs, nullopt_t) noexcept {
+ return !lhs.has_value();
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator<=(nullopt_t, const optional<T>&) noexcept {
+ return true;
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator>(const optional<T>& lhs, nullopt_t) noexcept {
+ return lhs.has_value();
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator>(nullopt_t, const optional<T>&) noexcept {
+ return false;
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator>=(const optional<T>&, nullopt_t) noexcept {
+ return true;
+ }
+ /// \group relop_nullopt
+ template <class T>
+ inline constexpr bool operator>=(nullopt_t, const optional<T>& rhs) noexcept {
+ return !rhs.has_value();
+ }
+
+ /// \group relop_t
+ /// \brief Compares the optional with a value.
+ /// \details If the optional has a value, it is compared with the other value
+ /// using `T`s relational operators. Otherwise, the optional is considered
+ /// less than the value.
+ template <class T, class U>
+ inline constexpr bool operator==(const optional<T>& lhs, const U& rhs) {
+ return lhs.has_value() ? *lhs == rhs : false;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator==(const U& lhs, const optional<T>& rhs) {
+ return rhs.has_value() ? lhs == *rhs : false;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator!=(const optional<T>& lhs, const U& rhs) {
+ return lhs.has_value() ? *lhs != rhs : true;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator!=(const U& lhs, const optional<T>& rhs) {
+ return rhs.has_value() ? lhs != *rhs : true;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator<(const optional<T>& lhs, const U& rhs) {
+ return lhs.has_value() ? *lhs < rhs : true;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator<(const U& lhs, const optional<T>& rhs) {
+ return rhs.has_value() ? lhs < *rhs : false;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator<=(const optional<T>& lhs, const U& rhs) {
+ return lhs.has_value() ? *lhs <= rhs : true;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator<=(const U& lhs, const optional<T>& rhs) {
+ return rhs.has_value() ? lhs <= *rhs : false;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator>(const optional<T>& lhs, const U& rhs) {
+ return lhs.has_value() ? *lhs > rhs : false;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator>(const U& lhs, const optional<T>& rhs) {
+ return rhs.has_value() ? lhs > *rhs : true;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator>=(const optional<T>& lhs, const U& rhs) {
+ return lhs.has_value() ? *lhs >= rhs : false;
+ }
+ /// \group relop_t
+ template <class T, class U>
+ inline constexpr bool operator>=(const U& lhs, const optional<T>& rhs) {
+ return rhs.has_value() ? lhs >= *rhs : true;
+ }
+
+ /// \synopsis template <class T> \n void swap(optional<T> &lhs, optional<T> &rhs);
+ template <class T, detail::enable_if_t<std::is_move_constructible<T>::value>* = nullptr, detail::enable_if_t<detail::is_swappable<T>::value>* = nullptr>
+ void swap(optional<T>& lhs, optional<T>& rhs) noexcept(noexcept(lhs.swap(rhs))) {
+ return lhs.swap(rhs);
+ }
+
+ namespace detail {
+ struct i_am_secret { };
+ } // namespace detail
+
+ template <class T = detail::i_am_secret, class U, class Ret = detail::conditional_t<std::is_same<T, detail::i_am_secret>::value, detail::decay_t<U>, T>>
+ inline constexpr optional<Ret> make_optional(U&& v) {
+ return optional<Ret>(std::forward<U>(v));
+ }
+
+ template <class T, class... Args>
+ inline constexpr optional<T> make_optional(Args&&... args) {
+ return optional<T>(in_place, std::forward<Args>(args)...);
+ }
+ template <class T, class U, class... Args>
+ inline constexpr optional<T> make_optional(std::initializer_list<U> il, Args&&... args) {
+ return optional<T>(in_place, il, std::forward<Args>(args)...);
+ }
+
+#if __cplusplus >= 201703L
+ template <class T>
+ optional(T) -> optional<T>;
+#endif
+
+ /// \exclude
+ namespace detail {
+#ifdef SOL_TL_OPTIONAL_CXX14
+ template <class Opt, class F, class Ret = decltype(detail::invoke(std::declval<F>(), *std::declval<Opt>())),
+ detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
+ constexpr auto optional_map_impl(Opt&& opt, F&& f) {
+ return opt.has_value() ? detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt)) : optional<Ret>(nullopt);
+ }
+
+ template <class Opt, class F, class Ret = decltype(detail::invoke(std::declval<F>(), *std::declval<Opt>())),
+ detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
+ auto optional_map_impl(Opt&& opt, F&& f) {
+ if (opt.has_value()) {
+ detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt));
+ return make_optional(monostate {});
+ }
+
+ return optional<monostate>(nullopt);
+ }
+#else
+ template <class Opt, class F, class Ret = decltype(detail::invoke(std::declval<F>(), *std::declval<Opt>())),
+ detail::enable_if_t<!std::is_void<Ret>::value>* = nullptr>
+
+ constexpr auto optional_map_impl(Opt&& opt, F&& f) -> optional<Ret> {
+ return opt.has_value() ? detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt)) : optional<Ret>(nullopt);
+ }
+
+ template <class Opt, class F, class Ret = decltype(detail::invoke(std::declval<F>(), *std::declval<Opt>())),
+ detail::enable_if_t<std::is_void<Ret>::value>* = nullptr>
+
+ auto optional_map_impl(Opt&& opt, F&& f) -> optional<monostate> {
+ if (opt.has_value()) {
+ detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt));
+ return monostate {};
+ }
+
+ return nullopt;
+ }
+#endif
+ } // namespace detail
+
+ /// Specialization for when `T` is a reference. `optional<T&>` acts similarly
+ /// to a `T*`, but provides more operations and shows intent more clearly.
+ ///
+ /// *Examples*:
+ ///
+ /// ```
+ /// int i = 42;
+ /// sol::optional<int&> o = i;
+ /// *o == 42; //true
+ /// i = 12;
+ /// *o = 12; //true
+ /// &*o == &i; //true
+ /// ```
+ ///
+ /// Assignment has rebind semantics rather than assign-through semantics:
+ ///
+ /// ```
+ /// int j = 8;
+ /// o = j;
+ ///
+ /// &*o == &j; //true
+ /// ```
+ template <class T>
+ class optional<T&> {
+ public:
+#if defined(SOL_TL_OPTIONAL_CXX14) && !defined(SOL_TL_OPTIONAL_GCC49) && !defined(SOL_TL_OPTIONAL_GCC54) && !defined(SOL_TL_OPTIONAL_GCC55)
+ /// \group and_then
+ /// Carries out some operation which returns an optional on the stored
+ /// object if there is one. \requires `std::invoke(std::forward<F>(f),
+ /// value())` returns a `std::optional<U>` for some `U`. \returns Let `U` be
+ /// the result of `std::invoke(std::forward<F>(f), value())`. Returns a
+ /// `std::optional<U>`. The return value is empty if `*this` is empty,
+ /// otherwise the return value of `std::invoke(std::forward<F>(f), value())`
+ /// is returned.
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) &;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) & {
+ using result = detail::invoke_result_t<F, T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) &&;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR auto and_then(F&& f) && {
+ using result = detail::invoke_result_t<F, T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) const &;
+ template <class F>
+ constexpr auto and_then(F&& f) const& {
+ using result = detail::invoke_result_t<F, const T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) const &&;
+ template <class F>
+ constexpr auto and_then(F&& f) const&& {
+ using result = detail::invoke_result_t<F, const T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+#endif
+#else
+ /// \group and_then
+ /// Carries out some operation which returns an optional on the stored
+ /// object if there is one. \requires `std::invoke(std::forward<F>(f),
+ /// value())` returns a `std::optional<U>` for some `U`. \returns Let `U` be
+ /// the result of `std::invoke(std::forward<F>(f), value())`. Returns a
+ /// `std::optional<U>`. The return value is empty if `*this` is empty,
+ /// otherwise the return value of `std::invoke(std::forward<F>(f), value())`
+ /// is returned.
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) &;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T&> and_then(F&& f) & {
+ using result = detail::invoke_result_t<F, T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) &&;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T&> and_then(F&& f) && {
+ using result = detail::invoke_result_t<F, T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) const &;
+ template <class F>
+ constexpr detail::invoke_result_t<F, const T&> and_then(F&& f) const& {
+ using result = detail::invoke_result_t<F, const T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group and_then
+ /// \synopsis template <class F> \n constexpr auto and_then(F &&f) const &&;
+ template <class F>
+ constexpr detail::invoke_result_t<F, const T&> and_then(F&& f) const&& {
+ using result = detail::invoke_result_t<F, const T&>;
+ static_assert(detail::is_optional<result>::value, "F must return an optional");
+
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : result(nullopt);
+ }
+#endif
+#endif
+
+#if defined(SOL_TL_OPTIONAL_CXX14) && !defined(SOL_TL_OPTIONAL_GCC49) && !defined(SOL_TL_OPTIONAL_GCC54) && !defined(SOL_TL_OPTIONAL_GCC55)
+ /// \brief Carries out some operation on the stored object if there is one.
+ /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
+ /// value())`. Returns a `std::optional<U>`. The return value is empty if
+ /// `*this` is empty, otherwise an `optional<U>` is constructed from the
+ /// return value of `std::invoke(std::forward<F>(f), value())` and is
+ /// returned.
+ ///
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) &;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) & {
+ return detail::optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) &&;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR auto map(F&& f) && {
+ return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) const&;
+ template <class F>
+ constexpr auto map(F&& f) const& {
+ return detail::optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> constexpr auto map(F &&f) const&&;
+ template <class F>
+ constexpr auto map(F&& f) const&& {
+ return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#else
+ /// \brief Carries out some operation on the stored object if there is one.
+ /// \returns Let `U` be the result of `std::invoke(std::forward<F>(f),
+ /// value())`. Returns a `std::optional<U>`. The return value is empty if
+ /// `*this` is empty, otherwise an `optional<U>` is constructed from the
+ /// return value of `std::invoke(std::forward<F>(f), value())` and is
+ /// returned.
+ ///
+ /// \group map
+ /// \synopsis template <class F> auto map(F &&f) &;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval<optional&>(), std::declval<F&&>())) map(F&& f) & {
+ return detail::optional_map_impl(*this, std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> auto map(F &&f) &&;
+ template <class F>
+ SOL_TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval<optional&&>(), std::declval<F&&>())) map(F&& f) && {
+ return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+
+ /// \group map
+ /// \synopsis template <class F> auto map(F &&f) const&;
+ template <class F>
+ constexpr decltype(detail::optional_map_impl(std::declval<const optional&>(), std::declval<F&&>())) map(F&& f) const& {
+ return detail::optional_map_impl(*this, std::forward<F>(f));
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group map
+ /// \synopsis template <class F> auto map(F &&f) const&&;
+ template <class F>
+ constexpr decltype(detail::optional_map_impl(std::declval<const optional&&>(), std::declval<F&&>())) map(F&& f) const&& {
+ return detail::optional_map_impl(std::move(*this), std::forward<F>(f));
+ }
+#endif
+#endif
+
+ /// \brief Calls `f` if the optional is empty
+ /// \requires `std::invoke_result_t<F>` must be void or convertible to
+ /// `optional<T>`. \effects If `*this` has a value, returns `*this`.
+ /// Otherwise, if `f` returns `void`, calls `std::forward<F>(f)` and returns
+ /// `std::nullopt`. Otherwise, returns `std::forward<F>(f)()`.
+ ///
+ /// \group or_else
+ /// \synopsis template <class F> optional<T> or_else (F &&f) &;
+ template <class F, detail::enable_if_ret_void<F>* = nullptr>
+ optional<T> SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) & {
+ if (has_value())
+ return *this;
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ /// \exclude
+ template <class F, detail::disable_if_ret_void<F>* = nullptr>
+ optional<T> SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) & {
+ return has_value() ? *this : std::forward<F>(f)();
+ }
+
+ /// \group or_else
+ /// \synopsis template <class F> optional<T> or_else (F &&f) &&;
+ template <class F, detail::enable_if_ret_void<F>* = nullptr>
+ optional<T> or_else(F&& f) && {
+ if (has_value())
+ return std::move(*this);
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ /// \exclude
+ template <class F, detail::disable_if_ret_void<F>* = nullptr>
+ optional<T> SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) && {
+ return has_value() ? std::move(*this) : std::forward<F>(f)();
+ }
+
+ /// \group or_else
+ /// \synopsis template <class F> optional<T> or_else (F &&f) const &;
+ template <class F, detail::enable_if_ret_void<F>* = nullptr>
+ optional<T> or_else(F&& f) const& {
+ if (has_value())
+ return *this;
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ /// \exclude
+ template <class F, detail::disable_if_ret_void<F>* = nullptr>
+ optional<T> SOL_TL_OPTIONAL_11_CONSTEXPR or_else(F&& f) const& {
+ return has_value() ? *this : std::forward<F>(f)();
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \exclude
+ template <class F, detail::enable_if_ret_void<F>* = nullptr>
+ optional<T> or_else(F&& f) const&& {
+ if (has_value())
+ return std::move(*this);
+
+ std::forward<F>(f)();
+ return nullopt;
+ }
+
+ /// \exclude
+ template <class F, detail::disable_if_ret_void<F>* = nullptr>
+ optional<T> or_else(F&& f) const&& {
+ return has_value() ? std::move(*this) : std::forward<F>(f)();
+ }
+#endif
+
+ /// \brief Maps the stored value with `f` if there is one, otherwise returns
+ /// `u`.
+ ///
+ /// \details If there is a value stored, then `f` is called with `**this`
+ /// and the value is returned. Otherwise `u` is returned.
+ ///
+ /// \group map_or
+ template <class F, class U>
+ U map_or(F&& f, U&& u) & {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : std::forward<U>(u);
+ }
+
+ /// \group map_or
+ template <class F, class U>
+ U map_or(F&& f, U&& u) && {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : std::forward<U>(u);
+ }
+
+ /// \group map_or
+ template <class F, class U>
+ U map_or(F&& f, U&& u) const& {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : std::forward<U>(u);
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group map_or
+ template <class F, class U>
+ U map_or(F&& f, U&& u) const&& {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : std::forward<U>(u);
+ }
+#endif
+
+ /// \brief Maps the stored value with `f` if there is one, otherwise calls
+ /// `u` and returns the result.
+ ///
+ /// \details If there is a value stored, then `f` is
+ /// called with `**this` and the value is returned. Otherwise
+ /// `std::forward<U>(u)()` is returned.
+ ///
+ /// \group map_or_else
+ /// \synopsis template <class F, class U> \n auto map_or_else(F &&f, U &&u) &;
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F&& f, U&& u) & {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : std::forward<U>(u)();
+ }
+
+ /// \group map_or_else
+ /// \synopsis template <class F, class U> \n auto map_or_else(F &&f, U &&u)
+ /// &&;
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F&& f, U&& u) && {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : std::forward<U>(u)();
+ }
+
+ /// \group map_or_else
+ /// \synopsis template <class F, class U> \n auto map_or_else(F &&f, U &&u)
+ /// const &;
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F&& f, U&& u) const& {
+ return has_value() ? detail::invoke(std::forward<F>(f), **this) : std::forward<U>(u)();
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group map_or_else
+ /// \synopsis template <class F, class U> \n auto map_or_else(F &&f, U &&u)
+ /// const &&;
+ template <class F, class U>
+ detail::invoke_result_t<U> map_or_else(F&& f, U&& u) const&& {
+ return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) : std::forward<U>(u)();
+ }
+#endif
+
+ /// \returns `u` if `*this` has a value, otherwise an empty optional.
+ template <class U>
+ constexpr optional<typename std::decay<U>::type> conjunction(U&& u) const {
+ using result = optional<detail::decay_t<U>>;
+ return has_value() ? result { u } : result { nullopt };
+ }
+
+ /// \returns `rhs` if `*this` is empty, otherwise the current value.
+ /// \group disjunction
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) & {
+ return has_value() ? *this : rhs;
+ }
+
+ /// \group disjunction
+ constexpr optional disjunction(const optional& rhs) const& {
+ return has_value() ? *this : rhs;
+ }
+
+ /// \group disjunction
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional& rhs) && {
+ return has_value() ? std::move(*this) : rhs;
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group disjunction
+ constexpr optional disjunction(const optional& rhs) const&& {
+ return has_value() ? std::move(*this) : rhs;
+ }
+#endif
+
+ /// \group disjunction
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) & {
+ return has_value() ? *this : std::move(rhs);
+ }
+
+ /// \group disjunction
+ constexpr optional disjunction(optional&& rhs) const& {
+ return has_value() ? *this : std::move(rhs);
+ }
+
+ /// \group disjunction
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional&& rhs) && {
+ return has_value() ? std::move(*this) : std::move(rhs);
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group disjunction
+ constexpr optional disjunction(optional&& rhs) const&& {
+ return has_value() ? std::move(*this) : std::move(rhs);
+ }
+#endif
+
+ /// Takes the value out of the optional, leaving it empty
+ /// \group take
+ optional take() & {
+ optional ret = *this;
+ reset();
+ return ret;
+ }
+
+ /// \group take
+ optional take() const& {
+ optional ret = *this;
+ reset();
+ return ret;
+ }
+
+ /// \group take
+ optional take() && {
+ optional ret = std::move(*this);
+ reset();
+ return ret;
+ }
+
+#ifndef SOL_TL_OPTIONAL_NO_CONSTRR
+ /// \group take
+ optional take() const&& {
+ optional ret = std::move(*this);
+ reset();
+ return ret;
+ }
+#endif
+
+ using value_type = T&;
+
+ /// Constructs an optional that does not contain a value.
+ /// \group ctor_empty
+ constexpr optional() noexcept : m_value(nullptr) {
+ }
+
+ /// \group ctor_empty
+ constexpr optional(nullopt_t) noexcept : m_value(nullptr) {
+ }
+
+ /// Copy constructor
+ ///
+ /// If `rhs` contains a value, the stored value is direct-initialized with
+ /// it. Otherwise, the constructed optional is empty.
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional(const optional& rhs) noexcept = default;
+
+ /// Move constructor
+ ///
+ /// If `rhs` contains a value, the stored value is direct-initialized with
+ /// it. Otherwise, the constructed optional is empty.
+ SOL_TL_OPTIONAL_11_CONSTEXPR optional(optional&& rhs) = default;
+
+ /// Constructs the stored value with `u`.
+ /// \synopsis template <class U=T> constexpr optional(U &&u);
+ template <class U = T, detail::enable_if_t<!detail::is_optional<detail::decay_t<U>>::value>* = nullptr>
+ constexpr optional(U&& u) : m_value(std::addressof(u)) {
+ static_assert(std::is_lvalue_reference<U>::value, "U must be an lvalue");
+ }
+
+ /// \exclude
+ template <class U>
+ constexpr explicit optional(const optional<U>& rhs) : optional(*rhs) {
+ }
+
+ /// No-op
+ ~optional() = default;
+
+ /// Assignment to empty.
+ ///
+ /// Destroys the current value if there is one.
+ optional& operator=(nullopt_t) noexcept {
+ m_value = nullptr;
+ return *this;
+ }
+
+ /// Copy assignment.
+ ///
+ /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise
+ /// resets the stored value in `*this`.
+ optional& operator=(const optional& rhs) = default;
+
+ /// Rebinds this optional to `u`.
+ ///
+ /// \requires `U` must be an lvalue reference.
+ /// \synopsis optional &operator=(U &&u);
+ template <class U = T, detail::enable_if_t<!detail::is_optional<detail::decay_t<U>>::value>* = nullptr>
+ optional& operator=(U&& u) {
+ static_assert(std::is_lvalue_reference<U>::value, "U must be an lvalue");
+ m_value = std::addressof(u);
+ return *this;
+ }
+
+ /// Converting copy assignment operator.
+ ///
+ /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise
+ /// resets the stored value in `*this`.
+ template <class U>
+ optional& operator=(const optional<U>& rhs) {
+ m_value = std::addressof(rhs.value());
+ return *this;
+ }
+
+ /// Constructs the value in-place, destroying the current one if there is
+ /// one.
+ ///
+ /// \group emplace
+ template <class... Args>
+ T& emplace(Args&&... args) noexcept {
+ static_assert(std::is_constructible<T, Args&&...>::value, "T must be constructible with Args");
+
+ *this = nullopt;
+ this->construct(std::forward<Args>(args)...);
+ }
+
+ /// Swaps this optional with the other.
+ ///
+ /// If neither optionals have a value, nothing happens.
+ /// If both have a value, the values are swapped.
+ /// If one has a value, it is moved to the other and the movee is left
+ /// valueless.
+ void swap(optional& rhs) noexcept {
+ std::swap(m_value, rhs.m_value);
+ }
+
+ /// \returns a pointer to the stored value
+ /// \requires a value is stored
+ /// \group pointer
+ /// \synopsis constexpr const T *operator->() const;
+ constexpr const T* operator->() const {
+ return m_value;
+ }
+
+ /// \group pointer
+ /// \synopsis constexpr T *operator->();
+ SOL_TL_OPTIONAL_11_CONSTEXPR T* operator->() {
+ return m_value;
+ }
+
+ /// \returns the stored value
+ /// \requires a value is stored
+ /// \group deref
+ /// \synopsis constexpr T &operator*();
+ SOL_TL_OPTIONAL_11_CONSTEXPR T& operator*() {
+ return *m_value;
+ }
+
+ /// \group deref
+ /// \synopsis constexpr const T &operator*() const;
+ constexpr const T& operator*() const {
+ return *m_value;
+ }
+
+ /// \returns whether or not the optional has a value
+ /// \group has_value
+ constexpr bool has_value() const noexcept {
+ return m_value != nullptr;
+ }
+
+ /// \group has_value
+ constexpr explicit operator bool() const noexcept {
+ return m_value != nullptr;
+ }
+
+ /// \returns the contained value if there is one, otherwise throws
+ /// [bad_optional_access]
+ /// \group value
+ /// synopsis constexpr T &value();
+ SOL_TL_OPTIONAL_11_CONSTEXPR T& value() {
+ if (has_value())
+ return *m_value;
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ std::abort();
+#else
+ throw bad_optional_access();
+#endif // No exceptions allowed
+ }
+ /// \group value
+ /// \synopsis constexpr const T &value() const;
+ SOL_TL_OPTIONAL_11_CONSTEXPR const T& value() const {
+ if (has_value())
+ return *m_value;
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ std::abort();
+#else
+ throw bad_optional_access();
+#endif // No exceptions allowed
+ }
+
+ /// \returns the stored value if there is one, otherwise returns `u`
+ /// \group value_or
+ template <class U>
+ constexpr T& value_or(U&& u) const {
+ static_assert(std::is_convertible<U&&, T&>::value, "T must be convertible from U");
+ return has_value() ? const_cast<T&>(**this) : static_cast<T&>(std::forward<U>(u));
+ }
+
+ /// Destroys the stored value if one exists, making the optional empty
+ void reset() noexcept {
+ m_value = nullptr;
+ }
+
+ private:
+ T* m_value;
+ };
+
+} // namespace sol
+
+namespace std {
+ // TODO SFINAE
+ template <class T>
+ struct hash<::sol::optional<T>> {
+ ::std::size_t operator()(const ::sol::optional<T>& o) const {
+ if (!o.has_value())
+ return 0;
+
+ return ::std::hash<::sol::detail::remove_const_t<T>>()(*o);
+ }
+ };
+} // namespace std
+
+// end of sol/optional_implementation.hpp
+
+#endif // Boost vs. Better optional
+
+#include <optional>
+
+namespace sol {
+
+#if SOL_IS_ON(SOL_USE_BOOST)
+ template <typename T>
+ using optional = boost::optional<T>;
+ using nullopt_t = boost::none_t;
+ SOL_BOOST_NONE_CONSTEXPR_I_ nullopt_t nullopt = boost::none;
+#endif // Boost vs. Better optional
+
+ namespace meta {
+ template <typename T>
+ using is_optional = any<is_specialization_of<T, optional>, is_specialization_of<T, std::optional>>;
+
+ template <typename T>
+ constexpr inline bool is_optional_v = is_optional<T>::value;
+ } // namespace meta
+
+ namespace detail {
+ template <typename T>
+ struct associated_nullopt {
+ inline static constexpr std::nullopt_t value = std::nullopt;
+ };
+
+#if SOL_IS_ON(SOL_USE_BOOST)
+ template <typename T>
+ struct associated_nullopt<boost::optional<T>> {
+ inline static SOL_BOOST_NONE_CONSTEXPR_I_ boost::none_t value = boost::none;
+ };
+#endif // Boost nullopt
+
+#if SOL_IS_ON(SOL_USE_BOOST)
+ template <typename T>
+ inline SOL_BOOST_NONE_CONSTEXPR_I_ auto associated_nullopt_v = associated_nullopt<T>::value;
+#else
+ template <typename T>
+ inline constexpr auto associated_nullopt_v = associated_nullopt<T>::value;
+#endif // Boost continues to lag behind, to not many people's surprise...
+ } // namespace detail
+} // namespace sol
+
+#if SOL_IS_ON(SOL_USE_BOOST)
+#undef SOL_BOOST_NONE_CONSTEXPR_I_
+#endif
+
+// end of sol/optional.hpp
+
+// beginning of sol/raii.hpp
+
+#include <memory>
+
+namespace sol {
+ namespace detail {
+ struct default_construct {
+ template <typename T, typename... Args>
+ static void construct(T&& obj, Args&&... args) {
+ typedef meta::unqualified_t<T> Tu;
+ std::allocator<Tu> alloc {};
+ std::allocator_traits<std::allocator<Tu>>::construct(alloc, std::forward<T>(obj), std::forward<Args>(args)...);
+ }
+
+ template <typename T, typename... Args>
+ void operator()(T&& obj, Args&&... args) const {
+ construct(std::forward<T>(obj), std::forward<Args>(args)...);
+ }
+ };
+
+ struct default_destroy {
+ template <typename T>
+ static void destroy(T&& obj) {
+ std::allocator<meta::unqualified_t<T>> alloc {};
+ alloc.destroy(obj);
+ }
+
+ template <typename T>
+ void operator()(T&& obj) const {
+ destroy(std::forward<T>(obj));
+ }
+ };
+
+ struct deleter {
+ template <typename T>
+ void operator()(T* p) const {
+ delete p;
+ }
+ };
+
+ struct state_deleter {
+ void operator()(lua_State* L) const {
+ lua_close(L);
+ }
+ };
+
+ template <typename T, typename Dx, typename... Args>
+ inline std::unique_ptr<T, Dx> make_unique_deleter(Args&&... args) {
+ return std::unique_ptr<T, Dx>(new T(std::forward<Args>(args)...));
+ }
+
+ template <typename Tag, typename T>
+ struct tagged {
+ private:
+ T value_;
+
+ public:
+ template <typename Arg, typename... Args, meta::disable<std::is_same<meta::unqualified_t<Arg>, tagged>> = meta::enabler>
+ tagged(Arg&& arg, Args&&... args) : value_(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+
+ T& value() & {
+ return value_;
+ }
+
+ T const& value() const& {
+ return value_;
+ }
+
+ T&& value() && {
+ return std::move(value_);
+ }
+ };
+ } // namespace detail
+
+ template <typename... Args>
+ struct constructor_list { };
+
+ template <typename... Args>
+ using constructors = constructor_list<Args...>;
+
+ const auto default_constructor = constructors<types<>> {};
+
+ struct no_construction { };
+ const auto no_constructor = no_construction {};
+
+ struct call_construction { };
+ const auto call_constructor = call_construction {};
+
+ template <typename... Functions>
+ struct constructor_wrapper {
+ std::tuple<Functions...> functions;
+ template <typename Arg, typename... Args, meta::disable<std::is_same<meta::unqualified_t<Arg>, constructor_wrapper>> = meta::enabler>
+ constructor_wrapper(Arg&& arg, Args&&... args) : functions(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+ };
+
+ template <typename... Functions>
+ inline auto initializers(Functions&&... functions) {
+ return constructor_wrapper<std::decay_t<Functions>...>(std::forward<Functions>(functions)...);
+ }
+
+ template <typename... Functions>
+ struct factory_wrapper {
+ std::tuple<Functions...> functions;
+ template <typename Arg, typename... Args, meta::disable<std::is_same<meta::unqualified_t<Arg>, factory_wrapper>> = meta::enabler>
+ factory_wrapper(Arg&& arg, Args&&... args) : functions(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+ };
+
+ template <typename... Functions>
+ inline auto factories(Functions&&... functions) {
+ return factory_wrapper<std::decay_t<Functions>...>(std::forward<Functions>(functions)...);
+ }
+
+ template <typename Function>
+ struct destructor_wrapper {
+ Function fx;
+ destructor_wrapper(Function f) : fx(std::move(f)) {
+ }
+ };
+
+ template <>
+ struct destructor_wrapper<void> { };
+
+ const destructor_wrapper<void> default_destructor {};
+
+ template <typename Fx>
+ inline auto destructor(Fx&& fx) {
+ return destructor_wrapper<std::decay_t<Fx>>(std::forward<Fx>(fx));
+ }
+
+} // namespace sol
+
+// end of sol/raii.hpp
+
+// beginning of sol/policies.hpp
+
+#include <array>
+
+namespace sol {
+ namespace detail {
+ struct policy_base_tag { };
+ } // namespace detail
+
+ template <int Target, int... In>
+ struct static_stack_dependencies : detail::policy_base_tag { };
+ typedef static_stack_dependencies<-1, 1> self_dependency;
+ template <int... In>
+ struct returns_self_with : detail::policy_base_tag { };
+ typedef returns_self_with<> returns_self;
+
+ struct stack_dependencies : detail::policy_base_tag {
+ int target;
+ std::array<int, 64> stack_indices;
+ std::size_t len;
+
+ template <typename... Args>
+ stack_dependencies(int stack_target, Args&&... args) : target(stack_target), stack_indices(), len(sizeof...(Args)) {
+ std::size_t i = 0;
+ (void)detail::swallow { int(), (stack_indices[i++] = static_cast<int>(std::forward<Args>(args)), int())... };
+ }
+
+ int& operator[](std::size_t i) {
+ return stack_indices[i];
+ }
+
+ const int& operator[](std::size_t i) const {
+ return stack_indices[i];
+ }
+
+ std::size_t size() const {
+ return len;
+ }
+ };
+
+ template <typename F, typename... Policies>
+ struct policy_wrapper {
+ typedef std::index_sequence_for<Policies...> indices;
+
+ F value;
+ std::tuple<Policies...> policies;
+
+ template <typename Fx, typename... Args, meta::enable<meta::neg<std::is_same<meta::unqualified_t<Fx>, policy_wrapper>>> = meta::enabler>
+ policy_wrapper(Fx&& fx, Args&&... args) : value(std::forward<Fx>(fx)), policies(std::forward<Args>(args)...) {
+ }
+
+ policy_wrapper(const policy_wrapper&) = default;
+ policy_wrapper& operator=(const policy_wrapper&) = default;
+ policy_wrapper(policy_wrapper&&) = default;
+ policy_wrapper& operator=(policy_wrapper&&) = default;
+ };
+
+ template <typename F, typename... Args>
+ auto policies(F&& f, Args&&... args) {
+ return policy_wrapper<std::decay_t<F>, std::decay_t<Args>...>(std::forward<F>(f), std::forward<Args>(args)...);
+ }
+
+ namespace detail {
+ template <typename T>
+ using is_policy = meta::is_specialization_of<T, policy_wrapper>;
+
+ template <typename T>
+ inline constexpr bool is_policy_v = is_policy<T>::value;
+ } // namespace detail
+} // namespace sol
+
+// end of sol/policies.hpp
+
+// beginning of sol/ebco.hpp
+
+#include <type_traits>
+#include <utility>
+#include <memory>
+
+namespace sol { namespace detail {
+
+ template <typename T, std::size_t tag = 0, typename = void>
+ struct ebco {
+ T m_value;
+
+ ebco() = default;
+ ebco(const ebco&) = default;
+ ebco(ebco&&) = default;
+ ebco& operator=(const ebco&) = default;
+ ebco& operator=(ebco&&) = default;
+ ebco(const T& v) noexcept(std::is_nothrow_copy_constructible_v<T>) : m_value(v) {};
+ ebco(T&& v) noexcept(std::is_nothrow_move_constructible_v<T>) : m_value(std::move(v)) {};
+ ebco& operator=(const T& v) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ m_value = v;
+ return *this;
+ }
+ ebco& operator=(T&& v) noexcept(std::is_nothrow_move_assignable_v<T>) {
+ m_value = std::move(v);
+ return *this;
+ };
+ template <typename Arg, typename... Args,
+ typename = std::enable_if_t<
+ !std::is_same_v<std::remove_reference_t<std::remove_cv_t<Arg>>,
+ ebco> && !std::is_same_v<std::remove_reference_t<std::remove_cv_t<Arg>>, T> && (sizeof...(Args) > 0 || !std::is_convertible_v<Arg, T>)>>
+ ebco(Arg&& arg, Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Arg, Args...>)
+ : m_value(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+
+ T& value() & noexcept {
+ return m_value;
+ }
+
+ T const& value() const& noexcept {
+ return m_value;
+ }
+
+ T&& value() && noexcept {
+ return std::move(m_value);
+ }
+ };
+
+ template <typename T, std::size_t tag>
+ struct ebco<T, tag, std::enable_if_t<!std::is_reference_v<T> && std::is_class_v<T> && !std::is_final_v<T>>> : T {
+ ebco() = default;
+ ebco(const ebco&) = default;
+ ebco(ebco&&) = default;
+ ebco(const T& v) noexcept(std::is_nothrow_copy_constructible_v<T>) : T(v) {};
+ ebco(T&& v) noexcept(std::is_nothrow_move_constructible_v<T>) : T(std::move(v)) {};
+ template <typename Arg, typename... Args,
+ typename = std::enable_if_t<
+ !std::is_same_v<std::remove_reference_t<std::remove_cv_t<Arg>>,
+ ebco> && !std::is_same_v<std::remove_reference_t<std::remove_cv_t<Arg>>, T> && (sizeof...(Args) > 0 || !std::is_convertible_v<Arg, T>)>>
+ ebco(Arg&& arg, Args&&... args) noexcept(std::is_nothrow_constructible_v<T, Arg, Args...>) : T(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+
+ ebco& operator=(const ebco&) = default;
+ ebco& operator=(ebco&&) = default;
+ ebco& operator=(const T& v) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ static_cast<T&>(*this) = v;
+ return *this;
+ }
+ ebco& operator=(T&& v) noexcept(std::is_nothrow_move_assignable_v<T>) {
+ static_cast<T&>(*this) = std::move(v);
+ return *this;
+ };
+
+ T& value() & noexcept {
+ return static_cast<T&>(*this);
+ }
+
+ T const& value() const& noexcept {
+ return static_cast<T const&>(*this);
+ }
+
+ T&& value() && noexcept {
+ return std::move(static_cast<T&>(*this));
+ }
+ };
+
+ template <typename T, std::size_t tag>
+ struct ebco<T&, tag> {
+ private:
+ T* m_ref;
+
+ public:
+ ebco() = default;
+ ebco(const ebco&) = default;
+ ebco(ebco&&) = default;
+ ebco(T& v) noexcept : m_ref(std::addressof(v)) {};
+
+ ebco& operator=(const ebco&) = default;
+ ebco& operator=(ebco&&) = default;
+ ebco& operator=(T& v) noexcept {
+ m_ref = std::addressof(v);
+ return *this;
+ }
+
+ T& value() const noexcept {
+ return *(const_cast<ebco<T&, tag>&>(*this).m_ref);
+ }
+ };
+
+ template <typename T, std::size_t tag>
+ struct ebco<T&&, tag> {
+ T&& ref;
+
+ ebco() = default;
+ ebco(const ebco&) = delete;
+ ebco(ebco&&) = default;
+ ebco(T&& v) noexcept : ref(v) {};
+
+ ebco& operator=(const ebco&) = delete;
+ ebco& operator=(ebco&&) = delete;
+
+ T& value() & noexcept {
+ return ref;
+ }
+
+ const T& value() const& noexcept {
+ return ref;
+ }
+
+ T&& value() && noexcept {
+ return std::move(ref);
+ }
+ };
+
+}} // namespace sol::detail
+
+// end of sol/ebco.hpp
+
+#include <array>
+#include <initializer_list>
+#include <string>
+#include <string_view>
+#include <limits>
+#include <optional>
+#include <memory>
+#if SOL_IS_ON(SOL_STD_VARIANT)
+#include <variant>
+#endif // variant shenanigans (thanks, Mac OSX)
+
+namespace sol {
+ namespace d {
+ // shortest possible hidden detail namespace
+ // when types are transcribed, this saves
+ // quite a bit of space, actually.
+ // it's a little unfortunate, but here we are?
+ template <typename T>
+ struct u { };
+ } // namespace d
+
+ namespace detail {
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ typedef int (*lua_CFunction_noexcept)(lua_State* L) noexcept;
+#else
+ typedef int (*lua_CFunction_noexcept)(lua_State* L);
+#endif // noexcept function type for lua_CFunction
+
+ template <typename T>
+ struct implicit_wrapper {
+ T& value;
+
+ implicit_wrapper(T* value_) : value(*value_) {
+ }
+
+ implicit_wrapper(T& value_) : value(value_) {
+ }
+
+ operator T&() {
+ return value;
+ }
+
+ operator T*() {
+ return std::addressof(value);
+ }
+ };
+
+ struct yield_tag_t { };
+ inline constexpr yield_tag_t yield_tag {};
+ } // namespace detail
+
+ struct lua_nil_t { };
+ inline constexpr lua_nil_t lua_nil {};
+ inline bool operator==(lua_nil_t, lua_nil_t) {
+ return true;
+ }
+ inline bool operator!=(lua_nil_t, lua_nil_t) {
+ return false;
+ }
+#if SOL_IS_ON(SOL_NIL)
+ using nil_t = lua_nil_t;
+ inline constexpr const nil_t& nil = lua_nil;
+#endif
+
+ namespace detail {
+ struct non_lua_nil_t { };
+ } // namespace detail
+
+ struct metatable_key_t { };
+ inline constexpr metatable_key_t metatable_key {};
+
+ struct global_tag_t {
+ } inline constexpr global_tag {};
+
+ struct env_key_t { };
+ inline constexpr env_key_t env_key {};
+
+ struct no_metatable_t { };
+ inline constexpr no_metatable_t no_metatable {};
+
+ template <typename T>
+ struct yielding_t {
+ T func;
+
+ yielding_t() = default;
+ yielding_t(const yielding_t&) = default;
+ yielding_t(yielding_t&&) = default;
+ yielding_t& operator=(const yielding_t&) = default;
+ yielding_t& operator=(yielding_t&&) = default;
+ template <typename Arg,
+ meta::enable<meta::neg<std::is_same<meta::unqualified_t<Arg>, yielding_t>>,
+ meta::neg<std::is_base_of<proxy_base_tag, meta::unqualified_t<Arg>>>> = meta::enabler>
+ yielding_t(Arg&& arg) : func(std::forward<Arg>(arg)) {
+ }
+ template <typename Arg0, typename Arg1, typename... Args>
+ yielding_t(Arg0&& arg0, Arg1&& arg1, Args&&... args) : func(std::forward<Arg0>(arg0), std::forward<Arg1>(arg1), std::forward<Args>(args)...) {
+ }
+ };
+
+ template <typename F>
+ inline yielding_t<std::decay_t<F>> yielding(F&& f) {
+ return yielding_t<std::decay_t<F>>(std::forward<F>(f));
+ }
+
+ typedef std::remove_pointer_t<lua_CFunction> lua_CFunction_ref;
+
+ template <typename T>
+ struct non_null { };
+
+ template <typename... Args>
+ struct function_sig { };
+
+ struct upvalue_index {
+ int index;
+ upvalue_index(int idx) : index(lua_upvalueindex(idx)) {
+ }
+
+ operator int() const {
+ return index;
+ }
+ };
+
+ struct raw_index {
+ int index;
+ raw_index(int i) : index(i) {
+ }
+
+ operator int() const {
+ return index;
+ }
+ };
+
+ struct absolute_index {
+ int index;
+ absolute_index(lua_State* L, int idx) : index(lua_absindex(L, idx)) {
+ }
+
+ operator int() const {
+ return index;
+ }
+ };
+
+ struct ref_index {
+ int index;
+ ref_index(int idx) : index(idx) {
+ }
+
+ operator int() const {
+ return index;
+ }
+ };
+
+ struct stack_count {
+ int count;
+
+ stack_count(int cnt) : count(cnt) {
+ }
+ };
+
+ struct lightuserdata_value {
+ void* value;
+ lightuserdata_value(void* data) : value(data) {
+ }
+ operator void*() const {
+ return value;
+ }
+ };
+
+ struct userdata_value {
+ private:
+ void* m_value;
+
+ public:
+ userdata_value(void* data) : m_value(data) {
+ }
+
+ void* value() const {
+ return m_value;
+ }
+
+ operator void*() const {
+ return value();
+ }
+ };
+
+ template <typename T>
+ struct light {
+ private:
+ static_assert(!std::is_void_v<T>, "the type for light will never be void");
+ T* m_value;
+
+ public:
+ light(T& x) : m_value(std::addressof(x)) {
+ }
+ light(T* x) : m_value(x) {
+ }
+ explicit light(void* x) : m_value(static_cast<T*>(x)) {
+ }
+
+ T* value() const {
+ return m_value;
+ }
+
+ operator T*() const {
+ return m_value;
+ }
+ operator T&() const {
+ return *m_value;
+ }
+
+ void* void_value() const {
+ return m_value;
+ }
+ };
+
+ template <typename T>
+ auto make_light(T& l) {
+ typedef meta::unwrapped_t<std::remove_pointer_t<std::remove_pointer_t<T>>> L;
+ return light<L>(l);
+ }
+
+ template <typename T>
+ struct user : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ using base_t::base_t;
+
+ using base_t::value;
+
+ operator std::add_pointer_t<std::remove_reference_t<T>>() {
+ return std::addressof(this->base_t::value());
+ }
+
+ operator std::add_pointer_t<std::add_const_t<std::remove_reference_t<T>>>() const {
+ return std::addressof(this->base_t::value());
+ }
+
+ operator std::add_lvalue_reference_t<T>() {
+ return this->base_t::value();
+ }
+
+ operator std::add_const_t<std::add_lvalue_reference_t<T>>&() const {
+ return this->base_t::value();
+ }
+ };
+
+ template <typename T>
+ auto make_user(T&& u) {
+ typedef meta::unwrapped_t<meta::unqualified_t<T>> U;
+ return user<U>(std::forward<T>(u));
+ }
+
+ template <typename T>
+ struct metatable_registry_key : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ using base_t::base_t;
+
+ using base_t::value;
+ };
+
+ template <typename T>
+ auto meta_registry_key(T&& key) {
+ typedef meta::unqualified_t<T> K;
+ return metatable_registry_key<K>(std::forward<T>(key));
+ }
+
+ template <typename... Upvalues>
+ struct closure {
+ lua_CFunction c_function;
+ std::tuple<Upvalues...> upvalues;
+ closure(lua_CFunction f, Upvalues... targetupvalues) : c_function(f), upvalues(std::forward<Upvalues>(targetupvalues)...) {
+ }
+ };
+
+ template <>
+ struct closure<> {
+ lua_CFunction c_function;
+ int upvalues;
+ closure(lua_CFunction f, int upvalue_count = 0) : c_function(f), upvalues(upvalue_count) {
+ }
+ };
+
+ typedef closure<> c_closure;
+
+ template <typename... Args>
+ closure<Args...> make_closure(lua_CFunction f, Args&&... args) {
+ return closure<Args...>(f, std::forward<Args>(args)...);
+ }
+
+ template <typename Sig, typename... Ps>
+ struct function_arguments {
+ std::tuple<Ps...> arguments;
+ template <typename Arg, typename... Args, meta::disable<std::is_same<meta::unqualified_t<Arg>, function_arguments>> = meta::enabler>
+ function_arguments(Arg&& arg, Args&&... args) : arguments(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+ };
+
+ template <typename Sig = function_sig<>, typename... Args>
+ auto as_function(Args&&... args) {
+ return function_arguments<Sig, std::decay_t<Args>...>(std::forward<Args>(args)...);
+ }
+
+ template <typename Sig = function_sig<>, typename... Args>
+ auto as_function_reference(Args&&... args) {
+ return function_arguments<Sig, Args...>(std::forward<Args>(args)...);
+ }
+
+ template <typename T>
+ struct as_table_t : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ as_table_t() = default;
+ as_table_t(const as_table_t&) = default;
+ as_table_t(as_table_t&&) = default;
+ as_table_t& operator=(const as_table_t&) = default;
+ as_table_t& operator=(as_table_t&&) = default;
+ as_table_t(const meta::unqualified_t<T>& obj) noexcept(std::is_nothrow_constructible_v<base_t, const meta::unqualified_t<T>&>) : base_t(obj) {
+ }
+ as_table_t(meta::unqualified_t<T>&& obj) noexcept(std::is_nothrow_constructible_v<base_t, meta::unqualified_t<T>&&>) : base_t(std::move(obj)) {
+ }
+ template <typename Arg, typename... Args,
+ std::enable_if_t<
+ !std::is_same_v<as_table_t, meta::unqualified_t<Arg>> && !std::is_same_v<meta::unqualified_t<T>, meta::unqualified_t<Arg>>>* = nullptr>
+ as_table_t(Arg&& arg, Args&&... args) noexcept(std::is_nothrow_constructible_v<base_t, Arg, Args...>)
+ : base_t(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+
+ using base_t::value;
+
+ operator std::add_lvalue_reference_t<T>() {
+ return this->base_t::value();
+ }
+
+ operator std::add_const_t<std::add_lvalue_reference_t<T>>() const {
+ return this->base_t::value();
+ }
+ };
+
+ template <typename T>
+ struct nested : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ using nested_type = T;
+
+ nested() = default;
+ nested(const nested&) = default;
+ nested(nested&&) = default;
+ nested& operator=(const nested&) = default;
+ nested& operator=(nested&&) = default;
+ nested(const meta::unqualified_t<T>& obj) noexcept(std::is_nothrow_constructible_v<base_t, const meta::unqualified_t<T>&>) : base_t(obj) {
+ }
+ nested(meta::unqualified_t<T>&& obj) noexcept(std::is_nothrow_constructible_v<base_t, meta::unqualified_t<T>&&>) : base_t(std::move(obj)) {
+ }
+ template <typename Arg, typename... Args,
+ std::enable_if_t<
+ !std::is_same_v<nested, meta::unqualified_t<Arg>> && !std::is_same_v<meta::unqualified_t<T>, meta::unqualified_t<Arg>>>* = nullptr>
+ nested(Arg&& arg, Args&&... args) noexcept(std::is_nothrow_constructible_v<base_t, Arg, Args...>)
+ : base_t(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+
+ using base_t::value;
+
+ operator std::add_lvalue_reference_t<T>() {
+ return this->base_t::value();
+ }
+
+ operator std::add_const_t<std::add_lvalue_reference_t<T>>() const {
+ return this->base_t::value();
+ }
+ };
+
+ struct nested_tag_t { };
+ constexpr inline nested_tag_t nested_tag {};
+
+ template <typename T>
+ as_table_t<T> as_table_ref(T&& container) {
+ return as_table_t<T>(std::forward<T>(container));
+ }
+
+ template <typename T>
+ as_table_t<meta::unqualified_t<T>> as_table(T&& container) {
+ return as_table_t<meta::unqualified_t<T>>(std::forward<T>(container));
+ }
+
+ template <typename T>
+ nested<T> as_nested_ref(T&& container) {
+ return nested<T>(std::forward<T>(container));
+ }
+
+ template <typename T>
+ nested<meta::unqualified_t<T>> as_nested(T&& container) {
+ return nested<meta::unqualified_t<T>>(std::forward<T>(container));
+ }
+
+ template <typename T>
+ struct as_container_t : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ using type = T;
+
+ as_container_t() = default;
+ as_container_t(const as_container_t&) = default;
+ as_container_t(as_container_t&&) = default;
+ as_container_t& operator=(const as_container_t&) = default;
+ as_container_t& operator=(as_container_t&&) = default;
+
+ using base_t::base_t;
+
+ using base_t::value;
+
+ operator std::add_lvalue_reference_t<T>() {
+ return value();
+ }
+ };
+
+ template <typename T>
+ auto as_container(T&& value) {
+ return as_container_t<T>(std::forward<T>(value));
+ }
+
+ template <typename T, std::size_t Limit = 15>
+ struct exhaustive_until : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ using base_t::base_t;
+
+ using base_t::value;
+
+ operator std::add_pointer_t<std::remove_reference_t<T>>() {
+ return std::addressof(this->base_t::value());
+ }
+
+ operator std::add_pointer_t<std::add_const_t<std::remove_reference_t<T>>>() const {
+ return std::addressof(this->base_t::value());
+ }
+
+ operator std::add_lvalue_reference_t<T>() {
+ return this->base_t::value();
+ }
+
+ operator std::add_const_t<std::add_lvalue_reference_t<T>>&() const {
+ return this->base_t::value();
+ }
+ };
+
+ template <typename T>
+ using exhaustive = exhaustive_until<T, (std::numeric_limits<size_t>::max)()>;
+
+ template <typename T>
+ struct non_exhaustive : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ using base_t::base_t;
+
+ using base_t::value;
+
+ operator std::add_pointer_t<std::remove_reference_t<T>>() {
+ return std::addressof(this->base_t::value());
+ }
+
+ operator std::add_pointer_t<std::add_const_t<std::remove_reference_t<T>>>() const {
+ return std::addressof(this->base_t::value());
+ }
+
+ operator std::add_lvalue_reference_t<T>() {
+ return this->base_t::value();
+ }
+
+ operator std::add_const_t<std::add_lvalue_reference_t<T>>&() const {
+ return this->base_t::value();
+ }
+ };
+
+ template <typename T>
+ struct push_invoke_t : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ push_invoke_t() = default;
+ push_invoke_t(const push_invoke_t&) = default;
+ push_invoke_t(push_invoke_t&&) = default;
+ push_invoke_t& operator=(const push_invoke_t&) = default;
+ push_invoke_t& operator=(push_invoke_t&&) = default;
+
+ using base_t::base_t;
+
+ using base_t::value;
+ };
+
+ template <typename Fx>
+ auto push_invoke(Fx&& fx) {
+ return push_invoke_t<Fx>(std::forward<Fx>(fx));
+ }
+
+ template <typename T>
+ struct forward_as_value_t : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ forward_as_value_t() = default;
+ forward_as_value_t(const forward_as_value_t&) = default;
+ forward_as_value_t(forward_as_value_t&&) = default;
+ forward_as_value_t& operator=(const forward_as_value_t&) = default;
+ forward_as_value_t& operator=(forward_as_value_t&&) = default;
+
+ using base_t::base_t;
+
+ using base_t::value;
+ };
+
+ template <typename T>
+ auto pass_as_value(T& value_ref_) {
+ return forward_as_value_t<T>(value_ref_);
+ }
+
+ struct override_value_t { };
+ constexpr inline override_value_t override_value = override_value_t();
+ struct update_if_empty_t { };
+ constexpr inline update_if_empty_t update_if_empty = update_if_empty_t();
+ struct create_if_nil_t { };
+ constexpr inline create_if_nil_t create_if_nil = create_if_nil_t();
+
+ namespace detail {
+ enum insert_mode { none = 0x0, update_if_empty = 0x01, override_value = 0x02, create_if_nil = 0x04 };
+
+ template <typename T, typename...>
+ using is_insert_mode = std::integral_constant<bool,
+ std::is_same_v<T, override_value_t> || std::is_same_v<T, update_if_empty_t> || std::is_same_v<T, create_if_nil_t>>;
+
+ template <typename T, typename...>
+ using is_not_insert_mode = meta::neg<is_insert_mode<T>>;
+ } // namespace detail
+
+ struct this_state {
+ lua_State* L;
+
+ this_state(lua_State* Ls) : L(Ls) {
+ }
+
+ operator lua_State*() const noexcept {
+ return lua_state();
+ }
+
+ lua_State* operator->() const noexcept {
+ return lua_state();
+ }
+
+ lua_State* lua_state() const noexcept {
+ return L;
+ }
+ };
+
+ struct this_main_state {
+ lua_State* L;
+
+ this_main_state(lua_State* Ls) : L(Ls) {
+ }
+
+ operator lua_State*() const noexcept {
+ return lua_state();
+ }
+
+ lua_State* operator->() const noexcept {
+ return lua_state();
+ }
+
+ lua_State* lua_state() const noexcept {
+ return L;
+ }
+ };
+
+ struct new_table {
+ int sequence_hint = 0;
+ int map_hint = 0;
+
+ new_table() = default;
+ new_table(const new_table&) = default;
+ new_table(new_table&&) = default;
+ new_table& operator=(const new_table&) = default;
+ new_table& operator=(new_table&&) = default;
+
+ new_table(int sequence_hint_, int map_hint_ = 0) noexcept : sequence_hint(sequence_hint_), map_hint(map_hint_) {
+ }
+ };
+
+ const new_table create = {};
+
+ enum class lib : unsigned char {
+ // print, assert, and other base functions
+ base,
+ // require and other package functions
+ package,
+ // coroutine functions and utilities
+ coroutine,
+ // string library
+ string,
+ // functionality from the OS
+ os,
+ // all things math
+ math,
+ // the table manipulator and observer functions
+ table,
+ // the debug library
+ debug,
+ // the bit library: different based on which you're using
+ bit32,
+ // input/output library
+ io,
+ // LuaJIT only
+ ffi,
+ // LuaJIT only
+ jit,
+ // library for handling utf8: new to Lua
+ utf8,
+ // do not use
+ count
+ };
+
+ enum class call_syntax { dot = 0, colon = 1 };
+
+ enum class load_mode {
+ any = 0,
+ text = 1,
+ binary = 2,
+ };
+
+ enum class call_status : int {
+ ok = LUA_OK,
+ yielded = LUA_YIELD,
+ runtime = LUA_ERRRUN,
+ memory = LUA_ERRMEM,
+ handler = LUA_ERRERR,
+ gc = LUA_ERRGCMM,
+ syntax = LUA_ERRSYNTAX,
+ file = LUA_ERRFILE,
+ };
+
+ enum class thread_status : int {
+ ok = LUA_OK,
+ yielded = LUA_YIELD,
+ runtime = LUA_ERRRUN,
+ memory = LUA_ERRMEM,
+ gc = LUA_ERRGCMM,
+ handler = LUA_ERRERR,
+ dead = -1,
+ };
+
+ enum class load_status : int {
+ ok = LUA_OK,
+ syntax = LUA_ERRSYNTAX,
+ memory = LUA_ERRMEM,
+ gc = LUA_ERRGCMM,
+ file = LUA_ERRFILE,
+ };
+
+ enum class gc_mode : int {
+ incremental = 0,
+ generational = 1,
+ default_value = incremental,
+ };
+
+ enum class type : int {
+ none = LUA_TNONE,
+ lua_nil = LUA_TNIL,
+#if SOL_IS_ON(SOL_NIL)
+ nil = lua_nil,
+#endif // Objective C/C++ Keyword that's found in OSX SDK and OBJC -- check for all forms to protect
+ string = LUA_TSTRING,
+ number = LUA_TNUMBER,
+ thread = LUA_TTHREAD,
+ boolean = LUA_TBOOLEAN,
+ function = LUA_TFUNCTION,
+ userdata = LUA_TUSERDATA,
+ lightuserdata = LUA_TLIGHTUSERDATA,
+ table = LUA_TTABLE,
+ poly = -0xFFFF
+ };
+
+ inline const std::string& to_string(call_status c) {
+ static const std::array<std::string, 10> names { { "ok",
+ "yielded",
+ "runtime",
+ "memory",
+ "handler",
+ "gc",
+ "syntax",
+ "file",
+ "CRITICAL_EXCEPTION_FAILURE",
+ "CRITICAL_INDETERMINATE_STATE_FAILURE" } };
+ switch (c) {
+ case call_status::ok:
+ return names[0];
+ case call_status::yielded:
+ return names[1];
+ case call_status::runtime:
+ return names[2];
+ case call_status::memory:
+ return names[3];
+ case call_status::handler:
+ return names[4];
+ case call_status::gc:
+ return names[5];
+ case call_status::syntax:
+ return names[6];
+ case call_status::file:
+ return names[7];
+ }
+ if (static_cast<std::ptrdiff_t>(c) == -1) {
+ // One of the many cases where a critical exception error has occurred
+ return names[8];
+ }
+ return names[9];
+ }
+
+ inline bool is_indeterminate_call_failure(call_status c) {
+ switch (c) {
+ case call_status::ok:
+ case call_status::yielded:
+ case call_status::runtime:
+ case call_status::memory:
+ case call_status::handler:
+ case call_status::gc:
+ case call_status::syntax:
+ case call_status::file:
+ return false;
+ }
+ return true;
+ }
+
+ inline const std::string& to_string(load_status c) {
+ static const std::array<std::string, 7> names {
+ { "ok", "memory", "gc", "syntax", "file", "CRITICAL_EXCEPTION_FAILURE", "CRITICAL_INDETERMINATE_STATE_FAILURE" }
+ };
+ switch (c) {
+ case load_status::ok:
+ return names[0];
+ case load_status::memory:
+ return names[1];
+ case load_status::gc:
+ return names[2];
+ case load_status::syntax:
+ return names[3];
+ case load_status::file:
+ return names[4];
+ }
+ if (static_cast<int>(c) == -1) {
+ // One of the many cases where a critical exception error has occurred
+ return names[5];
+ }
+ return names[6];
+ }
+
+ inline const std::string& to_string(load_mode c) {
+ static const std::array<std::string, 3> names { {
+ "bt",
+ "t",
+ "b",
+ } };
+ return names[static_cast<std::size_t>(c)];
+ }
+
+ enum class meta_function : unsigned {
+ construct,
+ index,
+ new_index,
+ mode,
+ call,
+ call_function = call,
+ metatable,
+ to_string,
+ length,
+ unary_minus,
+ addition,
+ subtraction,
+ multiplication,
+ division,
+ modulus,
+ power_of,
+ involution = power_of,
+ concatenation,
+ equal_to,
+ less_than,
+ less_than_or_equal_to,
+ garbage_collect,
+ floor_division,
+ bitwise_left_shift,
+ bitwise_right_shift,
+ bitwise_not,
+ bitwise_and,
+ bitwise_or,
+ bitwise_xor,
+ pairs,
+ ipairs,
+ next,
+ type,
+ type_info,
+ call_construct,
+ storage,
+ gc_names,
+ static_index,
+ static_new_index,
+ };
+
+ typedef meta_function meta_method;
+
+ inline const std::array<std::string, 37>& meta_function_names() {
+ static const std::array<std::string, 37> names = { { "new",
+ "__index",
+ "__newindex",
+ "__mode",
+ "__call",
+ "__metatable",
+ "__tostring",
+ "__len",
+ "__unm",
+ "__add",
+ "__sub",
+ "__mul",
+ "__div",
+ "__mod",
+ "__pow",
+ "__concat",
+ "__eq",
+ "__lt",
+ "__le",
+ "__gc",
+
+ "__idiv",
+ "__shl",
+ "__shr",
+ "__bnot",
+ "__band",
+ "__bor",
+ "__bxor",
+
+ "__pairs",
+ "__ipairs",
+ "next",
+
+ "__type",
+ "__typeinfo",
+ "__sol.call_new",
+ "__sol.storage",
+ "__sol.gc_names",
+ "__sol.static_index",
+ "__sol.static_new_index" } };
+ return names;
+ }
+
+ inline const std::string& to_string(meta_function mf) {
+ return meta_function_names()[static_cast<std::size_t>(mf)];
+ }
+
+ inline type type_of(lua_State* L, int index) {
+ return static_cast<type>(lua_type(L, index));
+ }
+
+ inline std::string type_name(lua_State* L, type t) {
+ return lua_typename(L, static_cast<int>(t));
+ }
+
+ template <typename T>
+ struct is_stateless_lua_reference
+ : std::integral_constant<bool,
+ (std::is_base_of_v<stateless_stack_reference, T> || std::is_base_of_v<stateless_reference, T>)&&(
+ !std::is_base_of_v<stack_reference, T> && !std::is_base_of_v<reference, T> && !std::is_base_of_v<main_reference, T>)> { };
+
+ template <typename T>
+ inline constexpr bool is_stateless_lua_reference_v = is_stateless_lua_reference<T>::value;
+
+ template <typename T>
+ struct is_lua_reference
+ : std::integral_constant<bool,
+ std::is_base_of_v<reference,
+ T> || std::is_base_of_v<main_reference, T> || std::is_base_of_v<stack_reference, T> || std::is_base_of_v<stateless_stack_reference, T> || std::is_base_of_v<stateless_reference, T>> {
+ };
+
+ template <typename T>
+ inline constexpr bool is_lua_reference_v = is_lua_reference<T>::value;
+
+ template <typename T>
+ struct is_lua_reference_or_proxy : std::integral_constant<bool, is_lua_reference_v<T> || meta::is_specialization_of_v<T, table_proxy>> { };
+
+ template <typename T>
+ inline constexpr bool is_lua_reference_or_proxy_v = is_lua_reference_or_proxy<T>::value;
+
+ template <typename T>
+ struct is_transparent_argument
+ : std::integral_constant<bool,
+ std::is_same_v<meta::unqualified_t<T>,
+ this_state> || std::is_same_v<meta::unqualified_t<T>, this_main_state> || std::is_same_v<meta::unqualified_t<T>, this_environment> || std::is_same_v<meta::unqualified_t<T>, variadic_args>> {
+ };
+
+ template <typename T>
+ constexpr inline bool is_transparent_argument_v = is_transparent_argument<T>::value;
+
+ template <typename T>
+ struct is_variadic_arguments : meta::any<std::is_same<T, variadic_args>, meta::is_optional<T>> { };
+
+ template <typename T>
+ struct is_container
+ : std::integral_constant<bool,
+ !std::is_same_v<state_view,
+ T> && !std::is_same_v<state, T> && !meta::is_initializer_list_v<T> && !meta::is_string_like_v<T> && !meta::is_string_literal_array_v<T> && !is_transparent_argument_v<T> && !is_lua_reference_v<T> && (meta::has_begin_end_v<T> || std::is_array_v<T>)> {
+ };
+
+ template <typename T>
+ constexpr inline bool is_container_v = is_container<T>::value;
+
+ template <typename T>
+ struct is_to_stringable : meta::any<meta::supports_to_string_member<meta::unqualified_t<T>>, meta::supports_adl_to_string<meta::unqualified_t<T>>,
+ meta::supports_op_left_shift<std::ostream, meta::unqualified_t<T>>> { };
+
+ template <typename T>
+ inline constexpr bool is_to_stringable_v = is_to_stringable<T>::value;
+
+ template <typename T>
+ struct is_callable : std::true_type { };
+
+ template <typename T>
+ inline constexpr bool is_callable_v = is_callable<T>::value;
+
+ namespace detail {
+ template <typename T, typename = void>
+ struct lua_type_of : std::integral_constant<type, type::userdata> { };
+
+ template <typename C, typename T, typename A>
+ struct lua_type_of<std::basic_string<C, T, A>> : std::integral_constant<type, type::string> { };
+
+ template <typename C, typename T>
+ struct lua_type_of<basic_string_view<C, T>> : std::integral_constant<type, type::string> { };
+
+ template <std::size_t N>
+ struct lua_type_of<char[N]> : std::integral_constant<type, type::string> { };
+
+ template <std::size_t N>
+ struct lua_type_of<wchar_t[N]> : std::integral_constant<type, type::string> { };
+
+#if SOL_IS_ON(SOL_CHAR8_T)
+ template <std::size_t N>
+ struct lua_type_of<char8_t[N]> : std::integral_constant<type, type::string> { };
+#endif
+
+ template <std::size_t N>
+ struct lua_type_of<char16_t[N]> : std::integral_constant<type, type::string> { };
+
+ template <std::size_t N>
+ struct lua_type_of<char32_t[N]> : std::integral_constant<type, type::string> { };
+
+ template <>
+ struct lua_type_of<char> : std::integral_constant<type, type::string> { };
+
+ template <>
+ struct lua_type_of<wchar_t> : std::integral_constant<type, type::string> { };
+
+#if SOL_IS_ON(SOL_CHAR8_T)
+ template <>
+ struct lua_type_of<char8_t> : std::integral_constant<type, type::string> { };
+#endif
+
+ template <>
+ struct lua_type_of<char16_t> : std::integral_constant<type, type::string> { };
+
+ template <>
+ struct lua_type_of<char32_t> : std::integral_constant<type, type::string> { };
+
+ template <>
+ struct lua_type_of<const char*> : std::integral_constant<type, type::string> { };
+
+ template <>
+ struct lua_type_of<const wchar_t*> : std::integral_constant<type, type::string> { };
+
+#if SOL_IS_ON(SOL_CHAR8_T)
+ template <>
+ struct lua_type_of<const char8_t*> : std::integral_constant<type, type::string> { };
+#endif
+
+ template <>
+ struct lua_type_of<const char16_t*> : std::integral_constant<type, type::string> { };
+
+ template <>
+ struct lua_type_of<const char32_t*> : std::integral_constant<type, type::string> { };
+
+ template <>
+ struct lua_type_of<bool> : std::integral_constant<type, type::boolean> { };
+
+ template <>
+ struct lua_type_of<lua_nil_t> : std::integral_constant<type, type::lua_nil> { };
+
+ template <>
+ struct lua_type_of<nullopt_t> : std::integral_constant<type, type::lua_nil> { };
+
+ template <>
+ struct lua_type_of<lua_value> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<detail::non_lua_nil_t> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<std::nullptr_t> : std::integral_constant<type, type::lua_nil> { };
+
+ template <>
+ struct lua_type_of<error> : std::integral_constant<type, type::string> { };
+
+ template <bool b, typename Base>
+ struct lua_type_of<basic_table_core<b, Base>> : std::integral_constant<type, type::table> { };
+
+ template <typename Base>
+ struct lua_type_of<basic_lua_table<Base>> : std::integral_constant<type, type::table> { };
+
+ template <typename Base>
+ struct lua_type_of<basic_metatable<Base>> : std::integral_constant<type, type::table> { };
+
+ template <typename T, typename Base>
+ struct lua_type_of<basic_usertype<T, Base>> : std::integral_constant<type, type::table> { };
+
+ template <>
+ struct lua_type_of<metatable_key_t> : std::integral_constant<type, type::table> { };
+
+ template <typename B>
+ struct lua_type_of<basic_environment<B>> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<env_key_t> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<new_table> : std::integral_constant<type, type::table> { };
+
+ template <typename T>
+ struct lua_type_of<as_table_t<T>> : std::integral_constant<type, type::table> { };
+
+ template <typename T>
+ struct lua_type_of<std::initializer_list<T>> : std::integral_constant<type, type::table> { };
+
+ template <bool b>
+ struct lua_type_of<basic_reference<b>> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<stack_reference> : std::integral_constant<type, type::poly> { };
+
+ template <typename Base>
+ struct lua_type_of<basic_object<Base>> : std::integral_constant<type, type::poly> { };
+
+ template <typename... Args>
+ struct lua_type_of<std::tuple<Args...>> : std::integral_constant<type, type::poly> { };
+
+ template <typename A, typename B>
+ struct lua_type_of<std::pair<A, B>> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<void*> : std::integral_constant<type, type::lightuserdata> { };
+
+ template <>
+ struct lua_type_of<const void*> : std::integral_constant<type, type::lightuserdata> { };
+
+ template <>
+ struct lua_type_of<lightuserdata_value> : std::integral_constant<type, type::lightuserdata> { };
+
+ template <>
+ struct lua_type_of<userdata_value> : std::integral_constant<type, type::userdata> { };
+
+ template <typename T>
+ struct lua_type_of<light<T>> : std::integral_constant<type, type::lightuserdata> { };
+
+ template <typename T>
+ struct lua_type_of<user<T>> : std::integral_constant<type, type::userdata> { };
+
+ template <typename Base>
+ struct lua_type_of<basic_lightuserdata<Base>> : std::integral_constant<type, type::lightuserdata> { };
+
+ template <typename Base>
+ struct lua_type_of<basic_userdata<Base>> : std::integral_constant<type, type::userdata> { };
+
+ template <>
+ struct lua_type_of<lua_CFunction> : std::integral_constant<type, type::function> { };
+
+ template <>
+ struct lua_type_of<std::remove_pointer_t<lua_CFunction>> : std::integral_constant<type, type::function> { };
+
+ template <typename Base, bool aligned>
+ struct lua_type_of<basic_function<Base, aligned>> : std::integral_constant<type, type::function> { };
+
+ template <typename Base, bool aligned, typename Handler>
+ struct lua_type_of<basic_protected_function<Base, aligned, Handler>> : std::integral_constant<type, type::function> { };
+
+ template <typename Base>
+ struct lua_type_of<basic_coroutine<Base>> : std::integral_constant<type, type::function> { };
+
+ template <typename Base>
+ struct lua_type_of<basic_thread<Base>> : std::integral_constant<type, type::thread> { };
+
+ template <typename Signature>
+ struct lua_type_of<std::function<Signature>> : std::integral_constant<type, type::function> { };
+
+ template <typename T>
+ struct lua_type_of<optional<T>> : std::integral_constant<type, type::poly> { };
+
+ template <typename T>
+ struct lua_type_of<std::optional<T>> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<variadic_args> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<variadic_results> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<stack_count> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<this_state> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<this_main_state> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<this_environment> : std::integral_constant<type, type::poly> { };
+
+ template <>
+ struct lua_type_of<type> : std::integral_constant<type, type::poly> { };
+
+#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ template <typename T>
+ struct lua_type_of<T*> : std::integral_constant<type, std::is_function_v<T> ? type::function : type::userdata> { };
+#else
+ template <typename T>
+ struct lua_type_of<T*> : std::integral_constant<type, type::userdata> { };
+#endif
+
+ template <typename T>
+ struct lua_type_of<T, std::enable_if_t<std::is_arithmetic_v<T> || std::is_same_v<T, lua_Number> || std::is_same_v<T, lua_Integer>>>
+ : std::integral_constant<type, type::number> { };
+
+ template <typename T>
+ struct lua_type_of<T, std::enable_if_t<std::is_function_v<T>>> : std::integral_constant<type, type::function> { };
+
+ template <typename T>
+ struct lua_type_of<T, std::enable_if_t<std::is_enum_v<T>>> : std::integral_constant<type, type::number> { };
+
+ template <>
+ struct lua_type_of<meta_function> : std::integral_constant<type, type::string> { };
+
+#if SOL_IS_ON(SOL_STD_VARIANT)
+ template <typename... Tn>
+ struct lua_type_of<std::variant<Tn...>> : std::integral_constant<type, type::poly> { };
+#endif // std::variant deployment sucks on Clang
+
+ template <typename T>
+ struct lua_type_of<nested<T>> : meta::conditional_t<::sol::is_container_v<T>, std::integral_constant<type, type::table>, lua_type_of<T>> { };
+
+ template <typename C, C v, template <typename...> class V, typename... Args>
+ struct accumulate : std::integral_constant<C, v> { };
+
+ template <typename C, C v, template <typename...> class V, typename T, typename... Args>
+ struct accumulate<C, v, V, T, Args...> : accumulate<C, v + V<T>::value, V, Args...> { };
+
+ template <typename C, C v, template <typename...> class V, typename List>
+ struct accumulate_list;
+
+ template <typename C, C v, template <typename...> class V, typename... Args>
+ struct accumulate_list<C, v, V, types<Args...>> : accumulate<C, v, V, Args...> { };
+ } // namespace detail
+
+ template <typename T>
+ struct lua_type_of : detail::lua_type_of<T> {
+ typedef int SOL_INTERNAL_UNSPECIALIZED_MARKER_;
+ };
+
+ template <typename T>
+ inline constexpr type lua_type_of_v = lua_type_of<T>::value;
+
+ template <typename T>
+ struct lua_size : std::integral_constant<int, 1> {
+ typedef int SOL_INTERNAL_UNSPECIALIZED_MARKER_;
+ };
+
+ template <typename A, typename B>
+ struct lua_size<std::pair<A, B>> : std::integral_constant<int, lua_size<A>::value + lua_size<B>::value> { };
+
+ template <typename... Args>
+ struct lua_size<std::tuple<Args...>> : std::integral_constant<int, detail::accumulate<int, 0, lua_size, Args...>::value> { };
+
+ template <typename T>
+ inline constexpr int lua_size_v = lua_size<T>::value;
+
+ namespace detail {
+ // MSVC's decltype detection is broken, which breaks other
+ // parts of the code. So we add more workarounds. The moment it's fixed,
+ // we take it away and break everyone that doesn't upgrade.
+ template <typename T>
+ using is_msvc_callable_rigged = meta::any<meta::is_specialization_of<T, push_invoke_t>, meta::is_specialization_of<T, as_table_t>,
+ meta::is_specialization_of<T, forward_as_value_t>, meta::is_specialization_of<T, as_container_t>, meta::is_specialization_of<T, nested>,
+ meta::is_specialization_of<T, yielding_t>>;
+
+ template <typename T>
+ inline constexpr bool is_msvc_callable_rigged_v = is_msvc_callable_rigged<T>::value;
+ } // namespace detail
+
+ template <typename T>
+ struct is_lua_primitive : std::integral_constant<bool,
+ type::userdata != lua_type_of_v<T> // cf
+ || ((type::userdata == lua_type_of_v<T>) // cf
+ &&meta::meta_detail::has_internal_marker_v<lua_type_of<T>> // cf
+ && !meta::meta_detail::has_internal_marker_v<lua_size<T>>) // cf
+ || is_lua_reference_or_proxy_v<T> // cf
+ || meta::is_specialization_of_v<T, std::tuple> // cf
+ || meta::is_specialization_of_v<T, std::pair>> { };
+
+ template <typename T>
+ constexpr inline bool is_lua_primitive_v = is_lua_primitive<T>::value;
+
+ template <typename T>
+ struct is_value_semantic_for_function
+#if SOL_IS_ON(SOL_FUNCTION_CALL_VALUE_SEMANTICS)
+ : std::true_type {
+ };
+#else
+ : std::false_type {
+ };
+#endif
+
+ template <typename T>
+ constexpr inline bool is_value_semantic_for_function_v = is_value_semantic_for_function<T>::value;
+
+ template <typename T>
+ struct is_main_threaded : std::is_base_of<main_reference, T> { };
+
+ template <typename T>
+ inline constexpr bool is_main_threaded_v = is_main_threaded<T>::value;
+
+ template <typename T>
+ struct is_stack_based : std::is_base_of<stack_reference, T> { };
+ template <>
+ struct is_stack_based<variadic_args> : std::true_type { };
+ template <>
+ struct is_stack_based<unsafe_function_result> : std::true_type { };
+ template <>
+ struct is_stack_based<protected_function_result> : std::true_type { };
+ template <>
+ struct is_stack_based<stack_proxy> : std::true_type { };
+ template <>
+ struct is_stack_based<stack_proxy_base> : std::true_type { };
+ template <>
+ struct is_stack_based<stack_count> : std::true_type { };
+
+ template <typename T>
+ constexpr inline bool is_stack_based_v = is_stack_based<T>::value;
+
+ template <typename T>
+ struct is_lua_primitive<T*> : std::true_type { };
+ template <>
+ struct is_lua_primitive<unsafe_function_result> : std::true_type { };
+ template <>
+ struct is_lua_primitive<protected_function_result> : std::true_type { };
+ template <typename T>
+ struct is_lua_primitive<std::reference_wrapper<T>> : std::true_type { };
+ template <typename T>
+ struct is_lua_primitive<user<T>> : std::true_type { };
+ template <typename T>
+ struct is_lua_primitive<light<T>> : is_lua_primitive<T*> { };
+ template <typename T>
+ struct is_lua_primitive<optional<T>> : std::true_type { };
+ template <typename T>
+ struct is_lua_primitive<std::optional<T>> : std::true_type { };
+ template <typename T>
+ struct is_lua_primitive<as_table_t<T>> : std::true_type { };
+ template <typename T>
+ struct is_lua_primitive<nested<T>> : std::true_type { };
+ template <>
+ struct is_lua_primitive<userdata_value> : std::true_type { };
+ template <>
+ struct is_lua_primitive<lightuserdata_value> : std::true_type { };
+ template <>
+ struct is_lua_primitive<stack_proxy> : std::true_type { };
+ template <>
+ struct is_lua_primitive<stack_proxy_base> : std::true_type { };
+ template <typename T>
+ struct is_lua_primitive<non_null<T>> : is_lua_primitive<T*> { };
+
+ template <typename T>
+ struct is_lua_index : std::is_integral<T> { };
+ template <>
+ struct is_lua_index<raw_index> : std::true_type { };
+ template <>
+ struct is_lua_index<absolute_index> : std::true_type { };
+ template <>
+ struct is_lua_index<ref_index> : std::true_type { };
+ template <>
+ struct is_lua_index<upvalue_index> : std::true_type { };
+
+ template <typename Signature>
+ struct lua_bind_traits : meta::bind_traits<Signature> {
+ private:
+ typedef meta::bind_traits<Signature> base_t;
+
+ public:
+ typedef std::integral_constant<bool, meta::count_for<is_variadic_arguments, typename base_t::args_list>::value != 0> runtime_variadics_t;
+ static const std::size_t true_arity = base_t::arity;
+ static const std::size_t arity = detail::accumulate_list<std::size_t, 0, lua_size, typename base_t::args_list>::value
+ - meta::count_for<is_transparent_argument, typename base_t::args_list>::value;
+ static const std::size_t true_free_arity = base_t::free_arity;
+ static const std::size_t free_arity = detail::accumulate_list<std::size_t, 0, lua_size, typename base_t::free_args_list>::value
+ - meta::count_for<is_transparent_argument, typename base_t::args_list>::value;
+ };
+
+ template <typename T>
+ struct is_table : std::false_type { };
+ template <bool x, typename T>
+ struct is_table<basic_table_core<x, T>> : std::true_type { };
+ template <typename T>
+ struct is_table<basic_lua_table<T>> : std::true_type { };
+
+ template <typename T>
+ inline constexpr bool is_table_v = is_table<T>::value;
+
+ template <typename T>
+ struct is_global_table : std::false_type { };
+ template <typename T>
+ struct is_global_table<basic_table_core<true, T>> : std::true_type { };
+
+ template <typename T>
+ inline constexpr bool is_global_table_v = is_global_table<T>::value;
+
+ template <typename T>
+ struct is_stack_table : std::false_type { };
+ template <bool x, typename T>
+ struct is_stack_table<basic_table_core<x, T>> : std::integral_constant<bool, std::is_base_of_v<stack_reference, T>> { };
+ template <typename T>
+ struct is_stack_table<basic_lua_table<T>> : std::integral_constant<bool, std::is_base_of_v<stack_reference, T>> { };
+
+ template <typename T>
+ inline constexpr bool is_stack_table_v = is_stack_table<T>::value;
+
+ template <typename T>
+ struct is_function : std::false_type { };
+ template <typename T, bool aligned>
+ struct is_function<basic_function<T, aligned>> : std::true_type { };
+ template <typename T, bool aligned, typename Handler>
+ struct is_function<basic_protected_function<T, aligned, Handler>> : std::true_type { };
+
+ template <typename T>
+ using is_lightuserdata = meta::is_specialization_of<T, basic_lightuserdata>;
+
+ template <typename T>
+ inline constexpr bool is_lightuserdata_v = is_lightuserdata<T>::value;
+
+ template <typename T>
+ using is_userdata = meta::is_specialization_of<T, basic_userdata>;
+
+ template <typename T>
+ inline constexpr bool is_userdata_v = is_userdata<T>::value;
+
+ template <typename T>
+ using is_environment = std::integral_constant<bool, is_userdata_v<T> || is_table_v<T> || meta::is_specialization_of_v<T, basic_environment>>;
+
+ template <typename T>
+ inline constexpr bool is_environment_v = is_environment<T>::value;
+
+ template <typename T>
+ using is_table_like = std::integral_constant<bool, is_table_v<T> || is_environment_v<T> || is_userdata_v<T>>;
+
+ template <typename T>
+ inline constexpr bool is_table_like_v = is_table_like<T>::value;
+
+ template <typename T>
+ struct is_automagical
+ : std::integral_constant<bool,
+ (SOL_IS_ON(SOL_DEFAULT_AUTOMAGICAL_USERTYPES))
+ || (std::is_array_v<
+ meta::unqualified_t<T>> || (!std::is_same_v<meta::unqualified_t<T>, state> && !std::is_same_v<meta::unqualified_t<T>, state_view>))> {
+ };
+
+ template <typename T>
+ inline type type_of() {
+ return lua_type_of<meta::unqualified_t<T>>::value;
+ }
+
+ namespace detail {
+ template <typename T>
+ struct is_non_factory_constructor : std::false_type { };
+
+ template <typename... Args>
+ struct is_non_factory_constructor<constructors<Args...>> : std::true_type { };
+
+ template <typename... Args>
+ struct is_non_factory_constructor<constructor_wrapper<Args...>> : std::true_type { };
+
+ template <>
+ struct is_non_factory_constructor<no_construction> : std::true_type { };
+
+ template <typename T>
+ inline constexpr bool is_non_factory_constructor_v = is_non_factory_constructor<T>::value;
+
+ template <typename T>
+ struct is_constructor : is_non_factory_constructor<T> { };
+
+ template <typename... Args>
+ struct is_constructor<factory_wrapper<Args...>> : std::true_type { };
+
+ template <typename T>
+ struct is_constructor<protect_t<T>> : is_constructor<meta::unqualified_t<T>> { };
+
+ template <typename F, typename... Policies>
+ struct is_constructor<policy_wrapper<F, Policies...>> : is_constructor<meta::unqualified_t<F>> { };
+
+ template <typename T>
+ inline constexpr bool is_constructor_v = is_constructor<T>::value;
+
+ template <typename... Args>
+ using any_is_constructor = meta::any<is_constructor<meta::unqualified_t<Args>>...>;
+
+ template <typename... Args>
+ inline constexpr bool any_is_constructor_v = any_is_constructor<Args...>::value;
+
+ template <typename T>
+ struct is_destructor : std::false_type { };
+
+ template <typename Fx>
+ struct is_destructor<destructor_wrapper<Fx>> : std::true_type { };
+
+ template <typename... Args>
+ using any_is_destructor = meta::any<is_destructor<meta::unqualified_t<Args>>...>;
+
+ template <typename... Args>
+ inline constexpr bool any_is_destructor_v = any_is_destructor<Args...>::value;
+ } // namespace detail
+
+ template <typename T>
+ using is_lua_c_function = meta::any<std::is_same<lua_CFunction, T>, std::is_same<detail::lua_CFunction_noexcept, T>, std::is_same<lua_CFunction_ref, T>>;
+
+ template <typename T>
+ inline constexpr bool is_lua_c_function_v = is_lua_c_function<T>::value;
+
+ enum class automagic_flags : unsigned {
+ none = 0x000u,
+ default_constructor = 0x001,
+ destructor = 0x002u,
+ pairs_operator = 0x004u,
+ to_string_operator = 0x008u,
+ call_operator = 0x010u,
+ less_than_operator = 0x020u,
+ less_than_or_equal_to_operator = 0x040u,
+ length_operator = 0x080u,
+ equal_to_operator = 0x100u,
+ all = default_constructor | destructor | pairs_operator | to_string_operator | call_operator | less_than_operator | less_than_or_equal_to_operator
+ | length_operator | equal_to_operator
+ };
+
+ inline constexpr automagic_flags operator|(automagic_flags left, automagic_flags right) noexcept {
+ return static_cast<automagic_flags>(
+ static_cast<std::underlying_type_t<automagic_flags>>(left) | static_cast<std::underlying_type_t<automagic_flags>>(right));
+ }
+
+ inline constexpr automagic_flags operator&(automagic_flags left, automagic_flags right) noexcept {
+ return static_cast<automagic_flags>(
+ static_cast<std::underlying_type_t<automagic_flags>>(left) & static_cast<std::underlying_type_t<automagic_flags>>(right));
+ }
+
+ inline constexpr automagic_flags& operator|=(automagic_flags& left, automagic_flags right) noexcept {
+ left = left | right;
+ return left;
+ }
+
+ inline constexpr automagic_flags& operator&=(automagic_flags& left, automagic_flags right) noexcept {
+ left = left & right;
+ return left;
+ }
+
+ template <typename Left, typename Right>
+ constexpr bool has_flag(Left left, Right right) noexcept {
+ return (left & right) == right;
+ }
+
+ template <typename Left, typename Right>
+ constexpr bool has_any_flag(Left left, Right right) noexcept {
+ return (left & right) != static_cast<Left>(static_cast<std::underlying_type_t<Left>>(0));
+ }
+
+ template <typename Left, typename Right>
+ constexpr auto clear_flags(Left left, Right right) noexcept {
+ return static_cast<Left>(static_cast<std::underlying_type_t<Left>>(left) & ~static_cast<std::underlying_type_t<Right>>(right));
+ }
+
+ struct automagic_enrollments {
+ bool default_constructor = true;
+ bool destructor = true;
+ bool pairs_operator = true;
+ bool to_string_operator = true;
+ bool call_operator = true;
+ bool less_than_operator = true;
+ bool less_than_or_equal_to_operator = true;
+ bool length_operator = true;
+ bool equal_to_operator = true;
+ };
+
+ template <automagic_flags compile_time_defaults = automagic_flags::all>
+ struct constant_automagic_enrollments : public automagic_enrollments { };
+
+} // namespace sol
+
+// end of sol/types.hpp
+
+#include <exception>
+#include <cstring>
+
+#if SOL_IS_ON(SOL_PRINT_ERRORS)
+#include <iostream>
+#endif
+
+namespace sol {
+ // must push a single object to be the error object
+ // NOTE: the VAST MAJORITY of all Lua libraries -- C or otherwise -- expect a string for the type of error
+ // break this convention at your own risk
+ using exception_handler_function = int (*)(lua_State*, optional<const std::exception&>, string_view);
+
+ namespace detail {
+ inline const char (&default_exception_handler_name())[11] {
+ static const char name[11] = "sol.\xE2\x98\xA2\xE2\x98\xA2";
+ return name;
+ }
+
+ // must push at least 1 object on the stack
+ inline int default_exception_handler(lua_State* L, optional<const std::exception&>, string_view what) {
+#if SOL_IS_ON(SOL_PRINT_ERRORS)
+ std::cerr << "[sol2] An exception occurred: ";
+ std::cerr.write(what.data(), static_cast<std::streamsize>(what.size()));
+ std::cerr << std::endl;
+#endif
+ lua_pushlstring(L, what.data(), what.size());
+ return 1;
+ }
+
+ inline int call_exception_handler(lua_State* L, optional<const std::exception&> maybe_ex, string_view what) {
+ lua_getglobal(L, default_exception_handler_name());
+ type t = static_cast<type>(lua_type(L, -1));
+ if (t != type::lightuserdata) {
+ lua_pop(L, 1);
+ return default_exception_handler(L, std::move(maybe_ex), std::move(what));
+ }
+ void* vfunc = lua_touserdata(L, -1);
+ lua_pop(L, 1);
+ if (vfunc == nullptr) {
+ return default_exception_handler(L, std::move(maybe_ex), std::move(what));
+ }
+ exception_handler_function exfunc = reinterpret_cast<exception_handler_function>(vfunc);
+ return exfunc(L, std::move(maybe_ex), std::move(what));
+ }
+
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ template <lua_CFunction f>
+ int static_trampoline(lua_State* L) noexcept {
+ return f(L);
+ }
+
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ template <lua_CFunction_noexcept f>
+ int static_trampoline_noexcept(lua_State* L) noexcept {
+ return f(L);
+ }
+#else
+ template <lua_CFunction f>
+ int static_trampoline_noexcept(lua_State* L) noexcept {
+ return f(L);
+ }
+#endif
+
+ template <typename Fx, typename... Args>
+ int trampoline(lua_State* L, Fx&& f, Args&&... args) noexcept {
+ return f(L, std::forward<Args>(args)...);
+ }
+
+ inline int c_trampoline(lua_State* L, lua_CFunction f) noexcept {
+ return trampoline(L, f);
+ }
+#else
+
+ inline int lua_cfunction_trampoline(lua_State* L, lua_CFunction f) {
+#if SOL_IS_ON(SOL_PROPAGATE_EXCEPTIONS)
+ return f(L);
+#else
+ try {
+ return f(L);
+ }
+ catch (const char* cs) {
+ call_exception_handler(L, optional<const std::exception&>(nullopt), string_view(cs));
+ }
+ catch (const std::string& s) {
+ call_exception_handler(L, optional<const std::exception&>(nullopt), string_view(s.c_str(), s.size()));
+ }
+ catch (const std::exception& e) {
+ call_exception_handler(L, optional<const std::exception&>(e), e.what());
+ }
+#if SOL_IS_ON(SOL_EXCEPTIONS_CATCH_ALL)
+ // LuaJIT cannot have the catchall when the safe propagation is on
+ // but LuaJIT will swallow all C++ errors
+ // if we don't at least catch std::exception ones
+ catch (...) {
+ call_exception_handler(L, optional<const std::exception&>(nullopt), "caught (...) exception");
+ }
+#endif // LuaJIT cannot have the catchall, but we must catch std::exceps for it
+ return lua_error(L);
+#endif // Safe exceptions
+ }
+
+ template <lua_CFunction f>
+ int static_trampoline(lua_State* L) {
+ return lua_cfunction_trampoline(L, f);
+ }
+
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ template <lua_CFunction_noexcept f>
+ int static_trampoline_noexcept(lua_State* L) noexcept {
+ return f(L);
+ }
+#else
+ template <lua_CFunction f>
+ int static_trampoline_noexcept(lua_State* L) noexcept {
+ return f(L);
+ }
+#endif
+
+ template <typename Fx, typename... Args>
+ int trampoline(lua_State* L, Fx&& f, Args&&... args) {
+ if constexpr (meta::bind_traits<meta::unqualified_t<Fx>>::is_noexcept) {
+ return f(L, std::forward<Args>(args)...);
+ }
+ else {
+#if SOL_IS_ON(SOL_PROPAGATE_EXCEPTIONS)
+ return f(L, std::forward<Args>(args)...);
+#else
+ try {
+ return f(L, std::forward<Args>(args)...);
+ }
+ catch (const char* cs) {
+ call_exception_handler(L, optional<const std::exception&>(nullopt), string_view(cs));
+ }
+ catch (const std::string& s) {
+ call_exception_handler(L, optional<const std::exception&>(nullopt), string_view(s.c_str(), s.size()));
+ }
+ catch (const std::exception& e) {
+ call_exception_handler(L, optional<const std::exception&>(e), e.what());
+ }
+#if SOL_IS_ON(SOL_EXCEPTIONS_CATCH_ALL)
+ // LuaJIT cannot have the catchall when the safe propagation is on
+ // but LuaJIT will swallow all C++ errors
+ // if we don't at least catch std::exception ones
+ catch (...) {
+ call_exception_handler(L, optional<const std::exception&>(nullopt), "caught (...) exception");
+ }
+#endif
+ return lua_error(L);
+#endif
+ }
+ }
+
+ inline int c_trampoline(lua_State* L, lua_CFunction f) {
+ return trampoline(L, f);
+ }
+#endif // Exceptions vs. No Exceptions
+
+ template <typename F, F fx>
+ inline int typed_static_trampoline(lua_State* L) {
+#if 0
+ // TODO: you must evaluate the get/check_get of every
+ // argument, to ensure it doesn't throw
+ // (e.g., for the sol_lua_check_access extension point!)
+ // This incluudes properly noexcept-ing all the above
+ // trampolines / safety nets
+ if constexpr (meta::bind_traits<F>::is_noexcept) {
+ return static_trampoline_noexcept<fx>(L);
+ }
+ else
+#endif
+ { return static_trampoline<fx>(L); }
+ }
+ } // namespace detail
+
+ inline void set_default_exception_handler(lua_State* L, exception_handler_function exf = &detail::default_exception_handler) {
+ static_assert(sizeof(void*) >= sizeof(exception_handler_function),
+ "void* storage is too small to transport the exception handler: please file a bug on the sol2 issue tracker to get this looked at!");
+ void* storage;
+ std::memcpy(&storage, &exf, sizeof(exception_handler_function));
+ lua_pushlightuserdata(L, storage);
+ lua_setglobal(L, detail::default_exception_handler_name());
+ }
+} // namespace sol
+
+// end of sol/trampoline.hpp
+
+// beginning of sol/stack_core.hpp
+
+// beginning of sol/inheritance.hpp
+
+// beginning of sol/usertype_traits.hpp
+
+// beginning of sol/demangle.hpp
+
+#include <string>
+#include <array>
+#include <cctype>
+#if SOL_IS_ON(SOL_MINGW_CCTYPE_IS_POISONED)
+extern "C" {
+#include <ctype.h>
+}
+#endif // MinGW is on some stuff
+#include <locale>
+
+namespace sol { namespace detail {
+ inline constexpr std::array<string_view, 9> removals { { "{anonymous}",
+ "(anonymous namespace)",
+ "public:",
+ "private:",
+ "protected:",
+ "struct ",
+ "class ",
+ "`anonymous-namespace'",
+ "`anonymous namespace'" } };
+
+#if SOL_IS_ON(SOL_COMPILER_GCC) || SOL_IS_ON(SOL_COMPILER_CLANG)
+ inline std::string ctti_get_type_name_from_sig(std::string name) {
+ // cardinal sins from MINGW
+ using namespace std;
+ std::size_t start = name.find_first_of('[');
+ start = name.find_first_of('=', start);
+ std::size_t end = name.find_last_of(']');
+ if (end == std::string::npos)
+ end = name.size();
+ if (start == std::string::npos)
+ start = 0;
+ if (start < name.size() - 1)
+ start += 1;
+ name = name.substr(start, end - start);
+ start = name.rfind("seperator_mark");
+ if (start != std::string::npos) {
+ name.erase(start - 2, name.length());
+ }
+ while (!name.empty() && isblank(name.front()))
+ name.erase(name.begin());
+ while (!name.empty() && isblank(name.back()))
+ name.pop_back();
+
+ for (std::size_t r = 0; r < removals.size(); ++r) {
+ auto found = name.find(removals[r]);
+ while (found != std::string::npos) {
+ name.erase(found, removals[r].size());
+ found = name.find(removals[r]);
+ }
+ }
+
+ return name;
+ }
+
+ template <typename T, class seperator_mark = int>
+ inline std::string ctti_get_type_name() {
+ return ctti_get_type_name_from_sig(__PRETTY_FUNCTION__);
+ }
+#elif SOL_IS_ON(SOL_COMPILER_VCXX)
+ inline std::string ctti_get_type_name_from_sig(std::string name) {
+ std::size_t start = name.find("get_type_name");
+ if (start == std::string::npos)
+ start = 0;
+ else
+ start += 13;
+ if (start < name.size() - 1)
+ start += 1;
+ std::size_t end = name.find_last_of('>');
+ if (end == std::string::npos)
+ end = name.size();
+ name = name.substr(start, end - start);
+ if (name.find("struct", 0) == 0)
+ name.replace(0, 6, "", 0);
+ if (name.find("class", 0) == 0)
+ name.replace(0, 5, "", 0);
+ while (!name.empty() && isblank(name.front()))
+ name.erase(name.begin());
+ while (!name.empty() && isblank(name.back()))
+ name.pop_back();
+
+ for (std::size_t r = 0; r < removals.size(); ++r) {
+ auto found = name.find(removals[r]);
+ while (found != std::string::npos) {
+ name.erase(found, removals[r].size());
+ found = name.find(removals[r]);
+ }
+ }
+
+ return name;
+ }
+
+ template <typename T>
+ std::string ctti_get_type_name() {
+ return ctti_get_type_name_from_sig(__FUNCSIG__);
+ }
+#else
+#error Compiler not supported for demangling
+#endif // compilers
+
+ template <typename T>
+ std::string demangle_once() {
+ std::string realname = ctti_get_type_name<T>();
+ return realname;
+ }
+
+ inline std::string short_demangle_from_type_name(std::string realname) {
+ // This isn't the most complete but it'll do for now...?
+ static const std::array<std::string, 10> ops = {
+ { "operator<", "operator<<", "operator<<=", "operator<=", "operator>", "operator>>", "operator>>=", "operator>=", "operator->", "operator->*" }
+ };
+ int level = 0;
+ std::size_t idx = 0;
+ for (idx = static_cast<std::size_t>(realname.empty() ? 0 : realname.size() - 1); idx > 0; --idx) {
+ if (level == 0 && realname[idx] == ':') {
+ break;
+ }
+ bool isleft = realname[idx] == '<';
+ bool isright = realname[idx] == '>';
+ if (!isleft && !isright)
+ continue;
+ bool earlybreak = false;
+ for (const auto& op : ops) {
+ std::size_t nisop = realname.rfind(op, idx);
+ if (nisop == std::string::npos)
+ continue;
+ std::size_t nisopidx = idx - op.size() + 1;
+ if (nisop == nisopidx) {
+ idx = static_cast<std::size_t>(nisopidx);
+ earlybreak = true;
+ }
+ break;
+ }
+ if (earlybreak) {
+ continue;
+ }
+ level += isleft ? -1 : 1;
+ }
+ if (idx > 0) {
+ realname.erase(0, realname.length() < static_cast<std::size_t>(idx) ? realname.length() : idx + 1);
+ }
+ return realname;
+ }
+
+ template <typename T>
+ std::string short_demangle_once() {
+ std::string realname = ctti_get_type_name<T>();
+ return short_demangle_from_type_name(realname);
+ }
+
+ template <typename T>
+ const std::string& demangle() {
+ static const std::string d = demangle_once<T>();
+ return d;
+ }
+
+ template <typename T>
+ const std::string& short_demangle() {
+ static const std::string d = short_demangle_once<T>();
+ return d;
+ }
+}} // namespace sol::detail
+
+// end of sol/demangle.hpp
+
+namespace sol {
+
+ template <typename T>
+ struct usertype_traits {
+ static const std::string& name() {
+ static const std::string& n = detail::short_demangle<T>();
+ return n;
+ }
+ static const std::string& qualified_name() {
+ static const std::string& q_n = detail::demangle<T>();
+ return q_n;
+ }
+ static const std::string& metatable() {
+ static const std::string m = std::string("sol.").append(detail::demangle<T>());
+ return m;
+ }
+ static const std::string& user_metatable() {
+ static const std::string u_m = std::string("sol.").append(detail::demangle<T>()).append(".user");
+ return u_m;
+ }
+ static const std::string& user_gc_metatable() {
+ static const std::string u_g_m = std::string("sol.").append(detail::demangle<T>()).append(".user\xE2\x99\xBB");
+ return u_g_m;
+ }
+ static const std::string& gc_table() {
+ static const std::string g_t = std::string("sol.").append(detail::demangle<T>()).append(".\xE2\x99\xBB");
+ return g_t;
+ }
+ };
+
+} // namespace sol
+
+// end of sol/usertype_traits.hpp
+
+// beginning of sol/unique_usertype_traits.hpp
+
+#include <memory>
+
+namespace sol {
+
+ namespace detail {
+ template <typename T>
+ struct unique_fallback {
+ using SOL_INTERNAL_UNSPECIALIZED_MARKER_ = int;
+ };
+
+ template <typename T>
+ struct unique_fallback<std::shared_ptr<T>> {
+ private:
+ using pointer = typename std::pointer_traits<std::shared_ptr<T>>::element_type*;
+
+ public:
+ // rebind is non-void
+ // if and only if unique usertype
+ // is cast-capable
+ template <typename X>
+ using rebind_actual_type = std::shared_ptr<X>;
+
+ static bool is_null(lua_State*, const std::shared_ptr<T>& p) noexcept {
+ return p == nullptr;
+ }
+
+ static pointer get(lua_State*, const std::shared_ptr<T>& p) noexcept {
+ return p.get();
+ }
+ };
+
+ template <typename T, typename D>
+ struct unique_fallback<std::unique_ptr<T, D>> {
+ private:
+ using pointer = typename std::unique_ptr<T, D>::pointer;
+
+ public:
+ static bool is_null(lua_State*, const std::unique_ptr<T, D>& p) noexcept {
+ return p == nullptr;
+ }
+
+ static pointer get(lua_State*, const std::unique_ptr<T, D>& p) noexcept {
+ return p.get();
+ }
+ };
+ } // namespace detail
+
+ namespace meta { namespace meta_detail {
+ template <typename T, typename = void>
+ struct unique_actual_type;
+
+ template <typename T>
+ struct unique_actual_type<T, meta::void_t<typename T::actual_type>> {
+ using type = typename T::actual_type;
+ };
+
+ template <typename T, typename... Rest, template <typename...> class Templ>
+ struct unique_actual_type<Templ<T, Rest...>> {
+ using type = T;
+ };
+
+ }} // namespace meta::meta_detail
+
+ template <typename T>
+ using unique_usertype_actual_t = typename meta::meta_detail::unique_actual_type<unique_usertype_traits<T>>::type;
+
+ namespace meta { namespace meta_detail {
+ template <typename T>
+ using value_test_t = decltype(T::value);
+
+ template <typename T>
+ using type_test_t = typename T::type;
+
+ template <typename T>
+ using type_element_type_t = typename T::element_type;
+
+ template <typename T, typename = void>
+ struct unique_element_type {
+ using type = typename std::pointer_traits<typename unique_actual_type<T>::type>::element_type;
+ };
+
+ template <typename T>
+ struct unique_element_type<T, std::enable_if_t<meta::is_detected_v<type_element_type_t, T>>> {
+ using type = typename T::element_type;
+ };
+
+ template <typename T>
+ struct unique_element_type<T, std::enable_if_t<meta::is_detected_v<type_test_t, T>>> {
+ using type = typename T::type;
+ };
+
+ template <typename T, typename = void>
+ struct unique_valid : std::integral_constant<bool, !has_internal_marker_v<T>> { };
+
+ template <typename T>
+ struct unique_valid<T, meta::void_t<decltype(T::value)>> : std::integral_constant<bool, T::value> { };
+ }} // namespace meta::meta_detail
+
+ template <typename T>
+ using unique_usertype_element_t = typename meta::meta_detail::unique_element_type<unique_usertype_traits<T>>::type;
+
+ template <typename T, typename Element = void>
+ using unique_usertype_rebind_actual_t = typename unique_usertype_traits<T>::template rebind_actual_type<Element>;
+
+ template <typename T>
+ struct unique_usertype_traits : public detail::unique_fallback<T> { };
+
+ template <typename T>
+ struct is_unique_usertype : std::integral_constant<bool, meta::meta_detail::unique_valid<unique_usertype_traits<T>>::value> { };
+
+ template <typename T>
+ inline constexpr bool is_unique_usertype_v = is_unique_usertype<T>::value;
+
+ namespace meta { namespace meta_detail {
+ template <typename T>
+ using adl_sol_lua_check_access_test_t
+ = decltype(sol_lua_check_access(types<T>(), static_cast<lua_State*>(nullptr), -1, std::declval<stack::record&>()));
+
+ template <typename T>
+ inline constexpr bool is_adl_sol_lua_check_access_v = meta::is_detected_v<adl_sol_lua_check_access_test_t, T>;
+
+ template <typename T>
+ using unique_usertype_get_with_state_test_t
+ = decltype(unique_usertype_traits<T>::get(static_cast<lua_State*>(nullptr), std::declval<unique_usertype_actual_t<T>>()));
+
+ template <typename T>
+ inline constexpr bool unique_usertype_get_with_state_v = meta::is_detected_v<unique_usertype_get_with_state_test_t, T>;
+
+ template <typename T>
+ using unique_usertype_is_null_with_state_test_t
+ = decltype(unique_usertype_traits<T>::is_null(static_cast<lua_State*>(nullptr), std::declval<unique_usertype_actual_t<T>>()));
+
+ template <typename T>
+ inline constexpr bool unique_usertype_is_null_with_state_v = meta::is_detected_v<unique_usertype_is_null_with_state_test_t, T>;
+ }} // namespace meta::meta_detail
+
+ namespace detail {
+ template <typename T>
+ constexpr bool unique_is_null_noexcept() noexcept {
+ if constexpr (meta::meta_detail::unique_usertype_is_null_with_state_v<std::remove_cv_t<T>>) {
+ return noexcept(
+ unique_usertype_traits<T>::is_null(static_cast<lua_State*>(nullptr), std::declval<unique_usertype_actual_t<std::remove_cv_t<T>>>()));
+ }
+ else {
+ return noexcept(unique_usertype_traits<T>::is_null(std::declval<unique_usertype_actual_t<std::remove_cv_t<T>>>()));
+ }
+ }
+
+ template <typename T>
+ bool unique_is_null(lua_State* L_, T& value_) noexcept(unique_is_null_noexcept<std::remove_cv_t<T>>()) {
+ using Tu = std::remove_cv_t<T>;
+ if constexpr (meta::meta_detail::unique_usertype_is_null_with_state_v<Tu>) {
+ return unique_usertype_traits<Tu>::is_null(L_, value_);
+ }
+ else {
+ return unique_usertype_traits<Tu>::is_null(value_);
+ }
+ }
+
+ template <typename T>
+ constexpr bool unique_get_noexcept() noexcept {
+ if constexpr (meta::meta_detail::unique_usertype_get_with_state_v<std::remove_cv_t<T>>) {
+ return noexcept(
+ unique_usertype_traits<T>::get(static_cast<lua_State*>(nullptr), std::declval<unique_usertype_actual_t<std::remove_cv_t<T>>>()));
+ }
+ else {
+ return noexcept(unique_usertype_traits<T>::get(std::declval<unique_usertype_actual_t<std::remove_cv_t<T>>>()));
+ }
+ }
+
+ template <typename T>
+ auto unique_get(lua_State* L_, T& value_) noexcept(unique_get_noexcept<std::remove_cv_t<T>>()) {
+ using Tu = std::remove_cv_t<T>;
+ if constexpr (meta::meta_detail::unique_usertype_get_with_state_v<Tu>) {
+ return unique_usertype_traits<Tu>::get(L_, value_);
+ }
+ else {
+ return unique_usertype_traits<Tu>::get(value_);
+ }
+ }
+ } // namespace detail
+
+ namespace meta { namespace meta_detail {
+ template <typename T, typename Element = void>
+ using is_rebind_actual_type_test_t = typename T::template rebind_actual_type<Element>;
+
+ template <typename T, typename Element = void>
+ using is_rebind_actual_type = meta::is_detected<is_rebind_actual_type_test_t, T, Element>;
+
+ template <typename T, typename Element = void>
+ inline constexpr bool is_rebind_actual_type_v = is_rebind_actual_type<T, Element>::value;
+
+ template <typename T, typename Element, bool = is_rebind_actual_type_v<T, Element>>
+ struct is_actual_type_rebindable_for_test : std::false_type { };
+
+ template <typename T, typename Element>
+ struct is_actual_type_rebindable_for_test<T, Element, true>
+ : std::integral_constant<bool, !std::is_void_v<typename T::template rebind_actual_type<Element>>> { };
+ }} // namespace meta::meta_detail
+
+ template <typename T, typename Element = void>
+ using is_actual_type_rebindable_for = typename meta::meta_detail::is_actual_type_rebindable_for_test<unique_usertype_traits<T>, Element>::type;
+
+ template <typename T, typename Element = void>
+ inline constexpr bool is_actual_type_rebindable_for_v = is_actual_type_rebindable_for<T, Element>::value;
+
+} // namespace sol
+
+// end of sol/unique_usertype_traits.hpp
+
+namespace sol {
+ template <typename... Args>
+ struct base_list { };
+ template <typename... Args>
+ using bases = base_list<Args...>;
+
+ typedef bases<> base_classes_tag;
+ const auto base_classes = base_classes_tag();
+
+ template <typename... Args>
+ struct is_to_stringable<base_list<Args...>> : std::false_type { };
+
+ namespace detail {
+
+ inline decltype(auto) base_class_check_key() {
+ static const auto& key = "class_check";
+ return key;
+ }
+
+ inline decltype(auto) base_class_cast_key() {
+ static const auto& key = "class_cast";
+ return key;
+ }
+
+ inline decltype(auto) base_class_index_propogation_key() {
+ static const auto& key = u8"\xF0\x9F\x8C\xB2.index";
+ return key;
+ }
+
+ inline decltype(auto) base_class_new_index_propogation_key() {
+ static const auto& key = u8"\xF0\x9F\x8C\xB2.new_index";
+ return key;
+ }
+
+ template <typename T>
+ struct inheritance {
+ typedef typename base<T>::type bases_t;
+
+ static bool type_check_bases(types<>, const string_view&) {
+ return false;
+ }
+
+ template <typename Base, typename... Args>
+ static bool type_check_bases(types<Base, Args...>, const string_view& ti) {
+ return ti == usertype_traits<Base>::qualified_name() || type_check_bases(types<Args...>(), ti);
+ }
+
+ static bool type_check(const string_view& ti) {
+ return ti == usertype_traits<T>::qualified_name() || type_check_bases(bases_t(), ti);
+ }
+
+ template <typename... Bases>
+ static bool type_check_with(const string_view& ti) {
+ return ti == usertype_traits<T>::qualified_name() || type_check_bases(types<Bases...>(), ti);
+ }
+
+ static void* type_cast_bases(types<>, T*, const string_view&) {
+ return nullptr;
+ }
+
+ template <typename Base, typename... Args>
+ static void* type_cast_bases(types<Base, Args...>, T* data, const string_view& ti) {
+ // Make sure to convert to T first, and then dynamic cast to the proper type
+ return ti != usertype_traits<Base>::qualified_name() ? type_cast_bases(types<Args...>(), data, ti)
+ : static_cast<void*>(static_cast<Base*>(data));
+ }
+
+ static void* type_cast(void* voiddata, const string_view& ti) {
+ T* data = static_cast<T*>(voiddata);
+ return static_cast<void*>(ti != usertype_traits<T>::qualified_name() ? type_cast_bases(bases_t(), data, ti) : data);
+ }
+
+ template <typename... Bases>
+ static void* type_cast_with(void* voiddata, const string_view& ti) {
+ T* data = static_cast<T*>(voiddata);
+ return static_cast<void*>(ti != usertype_traits<T>::qualified_name() ? type_cast_bases(types<Bases...>(), data, ti) : data);
+ }
+
+ template <typename U>
+ static bool type_unique_cast_bases(types<>, void*, void*, const string_view&) {
+ return 0;
+ }
+
+ template <typename U, typename Base, typename... Args>
+ static int type_unique_cast_bases(types<Base, Args...>, void* source_data, void* target_data, const string_view& ti) {
+ using uu_traits = unique_usertype_traits<U>;
+ using base_ptr = typename uu_traits::template rebind_actual_type<Base>;
+ string_view base_ti = usertype_traits<Base>::qualified_name();
+ if (base_ti == ti) {
+ if (target_data != nullptr) {
+ U* source = static_cast<U*>(source_data);
+ base_ptr* target = static_cast<base_ptr*>(target_data);
+ // perform proper derived -> base conversion
+ *target = *source;
+ }
+ return 2;
+ }
+ return type_unique_cast_bases<U>(types<Args...>(), source_data, target_data, ti);
+ }
+
+ template <typename U>
+ static int type_unique_cast(void* source_data, void* target_data, const string_view& ti, const string_view& rebind_ti) {
+ if constexpr (is_actual_type_rebindable_for_v<U>) {
+ using rebound_actual_type = unique_usertype_rebind_actual_t<U>;
+ using maybe_bases_or_empty = meta::conditional_t<std::is_void_v<rebound_actual_type>, types<>, bases_t>;
+ string_view this_rebind_ti = usertype_traits<rebound_actual_type>::qualified_name();
+ if (rebind_ti != this_rebind_ti) {
+ // this is not even of the same unique type
+ return 0;
+ }
+ string_view this_ti = usertype_traits<T>::qualified_name();
+ if (ti == this_ti) {
+ // direct match, return 1
+ return 1;
+ }
+ return type_unique_cast_bases<U>(maybe_bases_or_empty(), source_data, target_data, ti);
+ }
+ else {
+ (void)rebind_ti;
+ string_view this_ti = usertype_traits<T>::qualified_name();
+ if (ti == this_ti) {
+ // direct match, return 1
+ return 1;
+ }
+ return type_unique_cast_bases<U>(types<>(), source_data, target_data, ti);
+ }
+ }
+
+ template <typename U, typename... Bases>
+ static int type_unique_cast_with(void* source_data, void* target_data, const string_view& ti, const string_view& rebind_ti) {
+ using uc_bases_t = types<Bases...>;
+ if constexpr (is_actual_type_rebindable_for_v<U>) {
+ using rebound_actual_type = unique_usertype_rebind_actual_t<U>;
+ using cond_bases_t = meta::conditional_t<std::is_void_v<rebound_actual_type>, types<>, uc_bases_t>;
+ string_view this_rebind_ti = usertype_traits<rebound_actual_type>::qualified_name();
+ if (rebind_ti != this_rebind_ti) {
+ // this is not even of the same unique type
+ return 0;
+ }
+ string_view this_ti = usertype_traits<T>::qualified_name();
+ if (ti == this_ti) {
+ // direct match, return 1
+ return 1;
+ }
+ return type_unique_cast_bases<U>(cond_bases_t(), source_data, target_data, ti);
+ }
+ else {
+ (void)rebind_ti;
+ string_view this_ti = usertype_traits<T>::qualified_name();
+ if (ti == this_ti) {
+ // direct match, return 1
+ return 1;
+ }
+ return type_unique_cast_bases<U>(types<>(), source_data, target_data, ti);
+ }
+ }
+ };
+
+ using inheritance_check_function = decltype(&inheritance<void>::type_check);
+ using inheritance_cast_function = decltype(&inheritance<void>::type_cast);
+ using inheritance_unique_cast_function = decltype(&inheritance<void>::type_unique_cast<void>);
+ } // namespace detail
+} // namespace sol
+
+// end of sol/inheritance.hpp
+
+// beginning of sol/error_handler.hpp
+
+#include <cstdio>
+
+namespace sol {
+
+ namespace detail {
+ constexpr const char* not_a_number = "not a numeric type";
+ constexpr const char* not_a_number_or_number_string = "not a numeric type or numeric string";
+ constexpr const char* not_a_number_integral = "not a numeric type that fits exactly an integer (number maybe has significant decimals)";
+ constexpr const char* not_a_number_or_number_string_integral
+ = "not a numeric type or a numeric string that fits exactly an integer (e.g. number maybe has significant decimals)";
+
+ constexpr const char* not_enough_stack_space = "not enough space left on Lua stack";
+ constexpr const char* not_enough_stack_space_floating = "not enough space left on Lua stack for a floating point number";
+ constexpr const char* not_enough_stack_space_integral = "not enough space left on Lua stack for an integral number";
+ constexpr const char* not_enough_stack_space_string = "not enough space left on Lua stack for a string";
+ constexpr const char* not_enough_stack_space_meta_function_name = "not enough space left on Lua stack for the name of a meta_function";
+ constexpr const char* not_enough_stack_space_userdata = "not enough space left on Lua stack to create a sol2 userdata";
+ constexpr const char* not_enough_stack_space_generic = "not enough space left on Lua stack to push valuees";
+ constexpr const char* not_enough_stack_space_environment = "not enough space left on Lua stack to retrieve environment";
+ constexpr const char* protected_function_error = "caught (...) unknown error during protected_function call";
+
+ inline void accumulate_and_mark(const std::string& n, std::string& aux_message, int& marker) {
+ if (marker > 0) {
+ aux_message += ", ";
+ }
+ aux_message += n;
+ ++marker;
+ }
+ } // namespace detail
+
+ inline std::string associated_type_name(lua_State* L, int index, type t) {
+ switch (t) {
+ case type::poly:
+ return "anything";
+ case type::userdata: {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 2, "not enough space to push get the type name");
+#endif // make sure stack doesn't overflow
+ if (lua_getmetatable(L, index) == 0) {
+ break;
+ }
+ lua_pushlstring(L, "__name", 6);
+ lua_rawget(L, -2);
+ size_t sz;
+ const char* name = lua_tolstring(L, -1, &sz);
+ std::string tn(name, static_cast<std::string::size_type>(sz));
+ lua_pop(L, 2);
+ return tn;
+ }
+ default:
+ break;
+ }
+ return lua_typename(L, static_cast<int>(t));
+ }
+
+ inline int push_type_panic_string(lua_State* L, int index, type expected, type actual, string_view message, string_view aux_message) noexcept {
+ const char* err = message.size() == 0
+ ? (aux_message.size() == 0 ? "stack index %d, expected %s, received %s" : "stack index %d, expected %s, received %s: %s")
+ : "stack index %d, expected %s, received %s: %s %s";
+ const char* type_name = expected == type::poly ? "anything" : lua_typename(L, static_cast<int>(expected));
+ {
+ std::string actual_name = associated_type_name(L, index, actual);
+ lua_pushfstring(L, err, index, type_name, actual_name.c_str(), message.data(), aux_message.data());
+ }
+ return 1;
+ }
+
+ inline int type_panic_string(lua_State* L, int index, type expected, type actual, string_view message = "") noexcept(false) {
+ push_type_panic_string(L, index, expected, actual, message, "");
+ return lua_error(L);
+ }
+
+ inline int type_panic_c_str(lua_State* L, int index, type expected, type actual, const char* message = nullptr) noexcept(false) {
+ push_type_panic_string(L, index, expected, actual, message == nullptr ? "" : message, "");
+ return lua_error(L);
+ }
+
+ struct type_panic_t {
+ int operator()(lua_State* L, int index, type expected, type actual) const noexcept(false) {
+ return type_panic_c_str(L, index, expected, actual, nullptr);
+ }
+ int operator()(lua_State* L, int index, type expected, type actual, string_view message) const noexcept(false) {
+ return type_panic_c_str(L, index, expected, actual, message.data());
+ }
+ };
+
+ const type_panic_t type_panic = {};
+
+ struct constructor_handler {
+ int operator()(lua_State* L, int index, type expected, type actual, string_view message) const noexcept(false) {
+ push_type_panic_string(L, index, expected, actual, message, "(type check failed in constructor)");
+ return lua_error(L);
+ }
+ };
+
+ template <typename F = void>
+ struct argument_handler {
+ int operator()(lua_State* L, int index, type expected, type actual, string_view message) const noexcept(false) {
+ push_type_panic_string(L, index, expected, actual, message, "(bad argument to variable or function call)");
+ return lua_error(L);
+ }
+ };
+
+ template <typename R, typename... Args>
+ struct argument_handler<types<R, Args...>> {
+ int operator()(lua_State* L, int index, type expected, type actual, string_view message) const noexcept(false) {
+ {
+ std::string aux_message = "(bad argument into '";
+ aux_message += detail::demangle<R>();
+ aux_message += "(";
+ int marker = 0;
+ (void)detail::swallow { int(), (detail::accumulate_and_mark(detail::demangle<Args>(), aux_message, marker), int())... };
+ aux_message += ")')";
+ push_type_panic_string(L, index, expected, actual, message, aux_message);
+ }
+ return lua_error(L);
+ }
+ };
+
+ // Specify this function as the handler for lua::check if you know there's nothing wrong
+ inline int no_panic(lua_State*, int, type, type, const char* = nullptr) noexcept {
+ return 0;
+ }
+
+ inline void type_error(lua_State* L, int expected, int actual) noexcept(false) {
+ luaL_error(L, "expected %s, received %s", lua_typename(L, expected), lua_typename(L, actual));
+ }
+
+ inline void type_error(lua_State* L, type expected, type actual) noexcept(false) {
+ type_error(L, static_cast<int>(expected), static_cast<int>(actual));
+ }
+
+ inline void type_assert(lua_State* L, int index, type expected, type actual) noexcept(false) {
+ if (expected != type::poly && expected != actual) {
+ type_panic_c_str(L, index, expected, actual, nullptr);
+ }
+ }
+
+ inline void type_assert(lua_State* L, int index, type expected) {
+ type actual = type_of(L, index);
+ type_assert(L, index, expected, actual);
+ }
+
+} // namespace sol
+
+// end of sol/error_handler.hpp
+
+// beginning of sol/reference.hpp
+
+// beginning of sol/stack_reference.hpp
+
+namespace sol {
+ namespace detail {
+ inline bool xmovable(lua_State* leftL, lua_State* rightL) {
+ if (rightL == nullptr || leftL == nullptr || leftL == rightL) {
+ return false;
+ }
+ const void* leftregistry = lua_topointer(leftL, LUA_REGISTRYINDEX);
+ const void* rightregistry = lua_topointer(rightL, LUA_REGISTRYINDEX);
+ return leftregistry == rightregistry;
+ }
+ } // namespace detail
+
+ class stateless_stack_reference {
+ private:
+ friend class stack_reference;
+
+ int m_index = 0;
+
+ int registry_index() const noexcept {
+ return LUA_NOREF;
+ }
+
+ public:
+ stateless_stack_reference() noexcept = default;
+ stateless_stack_reference(lua_nil_t) noexcept : stateless_stack_reference() {};
+ stateless_stack_reference(lua_State* L_, int index_) noexcept : stateless_stack_reference(absolute_index(L_, index_)) {
+ }
+ stateless_stack_reference(lua_State*, absolute_index index_) noexcept : stateless_stack_reference(index_) {
+ }
+ stateless_stack_reference(lua_State*, raw_index index_) noexcept : stateless_stack_reference(index_) {
+ }
+ stateless_stack_reference(absolute_index index_) noexcept : m_index(index_) {
+ }
+ stateless_stack_reference(raw_index index_) noexcept : m_index(index_) {
+ }
+ stateless_stack_reference(lua_State*, ref_index) noexcept = delete;
+ stateless_stack_reference(ref_index) noexcept = delete;
+ stateless_stack_reference(const reference&) noexcept = delete;
+ stateless_stack_reference(const stateless_stack_reference&) noexcept = default;
+ stateless_stack_reference(stateless_stack_reference&& o) noexcept = default;
+ stateless_stack_reference& operator=(stateless_stack_reference&&) noexcept = default;
+ stateless_stack_reference& operator=(const stateless_stack_reference&) noexcept = default;
+
+ int push(lua_State* L_) const noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L_, 1, "not enough Lua stack space to push a single reference value");
+#endif // make sure stack doesn't overflow
+ lua_pushvalue(L_, m_index);
+ return 1;
+ }
+
+ void pop(lua_State* L_, int pop_count = 1) const noexcept {
+ lua_pop(L_, pop_count);
+ }
+
+ int stack_index() const noexcept {
+ return m_index;
+ }
+
+ const void* pointer(lua_State* L_) const noexcept {
+ const void* pointer_id = lua_topointer(L_, stack_index());
+ return pointer_id;
+ }
+
+ type get_type(lua_State* L_) const noexcept {
+ int untyped_value = lua_type(L_, stack_index());
+ return static_cast<type>(untyped_value);
+ }
+
+ bool valid(lua_State* L) const noexcept {
+ type t = get_type(L);
+ return t != type::lua_nil && t != type::none;
+ }
+
+ void reset(lua_State*) noexcept {
+ m_index = 0;
+ }
+
+ void reset(lua_State* L_, int index_) noexcept {
+ m_index = absolute_index(L_, index_);
+ }
+
+ void abandon(lua_State* = nullptr) noexcept {
+ m_index = 0;
+ }
+
+ stateless_stack_reference copy(lua_State* L_) const noexcept {
+ return stateless_stack_reference(L_, raw_index(m_index));
+ }
+
+ void copy_assign(lua_State*, const stateless_stack_reference& right) noexcept {
+ m_index = right.m_index;
+ }
+
+ bool equals(lua_State* L_, const stateless_stack_reference& r) const noexcept {
+ return lua_compare(L_, this->stack_index(), r.stack_index(), LUA_OPEQ) == 1;
+ }
+
+ bool equals(lua_State* L_, lua_nil_t) const noexcept {
+ return valid(L_);
+ }
+ };
+
+ class stack_reference : public stateless_stack_reference {
+ private:
+ lua_State* luastate = nullptr;
+
+ public:
+ stack_reference() noexcept = default;
+ stack_reference(lua_nil_t) noexcept : stack_reference() {};
+ stack_reference(lua_State* L, lua_nil_t) noexcept : stateless_stack_reference(L, 0), luastate(L) {
+ }
+ stack_reference(lua_State* L, int i) noexcept : stateless_stack_reference(L, i), luastate(L) {
+ }
+ stack_reference(lua_State* L, absolute_index i) noexcept : stateless_stack_reference(L, i), luastate(L) {
+ }
+ stack_reference(lua_State* L, raw_index i) noexcept : stateless_stack_reference(L, i), luastate(L) {
+ }
+ stack_reference(lua_State* L, ref_index i) noexcept = delete;
+ stack_reference(lua_State* L, const reference& r) noexcept = delete;
+ stack_reference(lua_State* L, const stack_reference& r) noexcept : luastate(L) {
+ if (!r.valid()) {
+ m_index = 0;
+ return;
+ }
+ int i = r.stack_index();
+ if (detail::xmovable(lua_state(), r.lua_state())) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, "not enough Lua stack space to push a single reference value");
+#endif // make sure stack doesn't overflow
+ lua_pushvalue(r.lua_state(), r.stack_index());
+ lua_xmove(r.lua_state(), luastate, 1);
+ i = absolute_index(luastate, -1);
+ }
+ m_index = i;
+ }
+ stack_reference(stack_reference&& o) noexcept = default;
+ stack_reference& operator=(stack_reference&&) noexcept = default;
+ stack_reference(const stack_reference&) noexcept = default;
+ stack_reference& operator=(const stack_reference&) noexcept = default;
+
+ int push() const noexcept {
+ return push(lua_state());
+ }
+
+ int push(lua_State* L_) const noexcept {
+ return stateless_stack_reference::push(L_);
+ }
+
+ void pop() const noexcept {
+ pop(lua_state());
+ }
+
+ void pop(lua_State* L_, int pop_count_ = 1) const noexcept {
+ stateless_stack_reference::pop(L_, pop_count_);
+ }
+
+ const void* pointer() const noexcept {
+ return stateless_stack_reference::pointer(lua_state());
+ }
+
+ type get_type() const noexcept {
+ return stateless_stack_reference::get_type(lua_state());
+ }
+
+ lua_State* lua_state() const noexcept {
+ return luastate;
+ }
+
+ bool valid() const noexcept {
+ return stateless_stack_reference::valid(lua_state());
+ }
+
+ void abandon() {
+ stateless_stack_reference::abandon(lua_state());
+ }
+ };
+
+ inline bool operator==(const stack_reference& l, const stack_reference& r) {
+ return lua_compare(l.lua_state(), l.stack_index(), r.stack_index(), LUA_OPEQ) == 1;
+ }
+
+ inline bool operator!=(const stack_reference& l, const stack_reference& r) {
+ return !operator==(l, r);
+ }
+
+ inline bool operator==(const stack_reference& lhs, const lua_nil_t&) {
+ return !lhs.valid();
+ }
+
+ inline bool operator==(const lua_nil_t&, const stack_reference& rhs) {
+ return !rhs.valid();
+ }
+
+ inline bool operator!=(const stack_reference& lhs, const lua_nil_t&) {
+ return lhs.valid();
+ }
+
+ inline bool operator!=(const lua_nil_t&, const stack_reference& rhs) {
+ return rhs.valid();
+ }
+
+ inline bool operator==(const stateless_stack_reference& l, const stateless_stack_reference& r) {
+ return l.stack_index() == r.stack_index();
+ }
+
+ inline bool operator!=(const stateless_stack_reference& l, const stateless_stack_reference& r) {
+ return l.stack_index() != r.stack_index();
+ }
+
+ struct stateless_stack_reference_equals {
+ using is_transparent = std::true_type;
+
+ stateless_stack_reference_equals(lua_State* L_) noexcept : m_L(L_) {
+ }
+
+ lua_State* lua_state() const noexcept {
+ return m_L;
+ }
+
+ bool operator()(const stateless_stack_reference& lhs, const stateless_stack_reference& rhs) const {
+ return lhs.equals(lua_state(), rhs);
+ }
+
+ bool operator()(lua_nil_t lhs, const stateless_stack_reference& rhs) const {
+ return rhs.equals(lua_state(), lhs);
+ }
+
+ bool operator()(const stateless_stack_reference& lhs, lua_nil_t rhs) const {
+ return lhs.equals(lua_state(), rhs);
+ }
+
+ private:
+ lua_State* m_L;
+ };
+
+ struct stack_reference_equals {
+ using is_transparent = std::true_type;
+
+ bool operator()(const lua_nil_t& lhs, const stack_reference& rhs) const {
+ return lhs == rhs;
+ }
+
+ bool operator()(const stack_reference& lhs, const lua_nil_t& rhs) const {
+ return lhs == rhs;
+ }
+
+ bool operator()(const stack_reference& lhs, const stack_reference& rhs) const {
+ return lhs == rhs;
+ }
+ };
+
+ struct stateless_stack_reference_hash {
+ using argument_type = stateless_stack_reference;
+ using result_type = std::size_t;
+ using is_transparent = std::true_type;
+
+ stateless_stack_reference_hash(lua_State* L_) noexcept : m_L(L_) {
+ }
+
+ lua_State* lua_state() const noexcept {
+ return m_L;
+ }
+
+ result_type operator()(const argument_type& lhs) const noexcept {
+ std::hash<const void*> h;
+ return h(lhs.pointer(lua_state()));
+ }
+
+ private:
+ lua_State* m_L;
+ };
+
+ struct stack_reference_hash {
+ using argument_type = stack_reference;
+ using result_type = std::size_t;
+ using is_transparent = std::true_type;
+
+ result_type operator()(const argument_type& lhs) const noexcept {
+ std::hash<const void*> h;
+ return h(lhs.pointer());
+ }
+ };
+} // namespace sol
+
+// end of sol/stack_reference.hpp
+
+#include <functional>
+
+namespace sol {
+ namespace detail {
+ inline const char (&default_main_thread_name())[9] {
+ static const char name[9] = "sol.\xF0\x9F\x93\x8C";
+ return name;
+ }
+ } // namespace detail
+
+ namespace stack {
+ inline void remove(lua_State* L_, int rawindex, int count) {
+ if (count < 1)
+ return;
+ int top = lua_gettop(L_);
+ if (top < 1) {
+ return;
+ }
+ if (rawindex == -count || top == rawindex) {
+ // Slice them right off the top
+ lua_pop(L_, static_cast<int>(count));
+ return;
+ }
+
+ // Remove each item one at a time using stack operations
+ // Probably slower, maybe, haven't benchmarked,
+ // but necessary
+ int index = lua_absindex(L_, rawindex);
+ if (index < 0) {
+ index = lua_gettop(L_) + (index + 1);
+ }
+ int last = index + count;
+ for (int i = index; i < last; ++i) {
+ lua_remove(L_, index);
+ }
+ }
+
+ struct push_popper_at {
+ lua_State* L;
+ int index;
+ int count;
+ push_popper_at(lua_State* L_, int index_ = -1, int count_ = 1) : L(L_), index(index_), count(count_) {
+ }
+ ~push_popper_at() {
+ remove(L, index, count);
+ }
+ };
+
+ template <bool top_level>
+ struct push_popper_n {
+ lua_State* L;
+ int pop_count;
+ push_popper_n(lua_State* L_, int pop_count_) : L(L_), pop_count(pop_count_) {
+ }
+ push_popper_n(const push_popper_n&) = delete;
+ push_popper_n(push_popper_n&&) = default;
+ push_popper_n& operator=(const push_popper_n&) = delete;
+ push_popper_n& operator=(push_popper_n&&) = default;
+ ~push_popper_n() {
+ lua_pop(L, pop_count);
+ }
+ };
+
+ template <>
+ struct push_popper_n<true> {
+ push_popper_n(lua_State*, int) {
+ }
+ };
+
+ template <bool, typename T, typename = void>
+ struct push_popper {
+ using Tu = meta::unqualified_t<T>;
+ T m_object;
+ int m_index;
+
+ push_popper(T object_) noexcept : m_object(object_), m_index(lua_absindex(m_object.lua_state(), -m_object.push())) {
+ }
+
+ int index_of(const Tu&) const noexcept {
+ return m_index;
+ }
+
+ ~push_popper() {
+ m_object.pop();
+ }
+ };
+
+ template <typename T, typename C>
+ struct push_popper<true, T, C> {
+ using Tu = meta::unqualified_t<T>;
+
+ push_popper(T) noexcept {
+ }
+
+ int index_of(const Tu&) const noexcept {
+ return -1;
+ }
+
+ ~push_popper() {
+ }
+ };
+
+ template <typename T>
+ struct push_popper<false, T, std::enable_if_t<is_stack_based_v<meta::unqualified_t<T>>>> {
+ using Tu = meta::unqualified_t<T>;
+
+ push_popper(T) noexcept {
+ }
+
+ int index_of(const Tu& object_) const noexcept {
+ return object_.stack_index();
+ }
+
+ ~push_popper() {
+ }
+ };
+
+ template <bool, typename T, typename = void>
+ struct stateless_push_popper {
+ using Tu = meta::unqualified_t<T>;
+ lua_State* m_L;
+ T m_object;
+ int m_index;
+
+ stateless_push_popper(lua_State* L_, T object_) noexcept : m_L(L_), m_object(object_), m_index(lua_absindex(m_L, -m_object.push(m_L))) {
+ }
+
+ int index_of(const Tu&) const noexcept {
+ return m_index;
+ }
+
+ ~stateless_push_popper() {
+ m_object.pop(m_L);
+ }
+ };
+
+ template <typename T, typename C>
+ struct stateless_push_popper<true, T, C> {
+ using Tu = meta::unqualified_t<T>;
+
+ stateless_push_popper(lua_State*, T) noexcept {
+ }
+
+ int index_of(lua_State*, const Tu&) const noexcept {
+ return -1;
+ }
+
+ ~stateless_push_popper() {
+ }
+ };
+
+ template <typename T>
+ struct stateless_push_popper<false, T, std::enable_if_t<is_stack_based_v<meta::unqualified_t<T>>>> {
+ using Tu = meta::unqualified_t<T>;
+ lua_State* m_L;
+
+ stateless_push_popper(lua_State* L_, T) noexcept : m_L(L_) {
+ }
+
+ int index_of(const Tu& object_) const noexcept {
+ return object_.stack_index();
+ }
+
+ ~stateless_push_popper() {
+ }
+ };
+
+ template <bool top_level = false, typename T>
+ push_popper<top_level, T> push_pop(T&& x) {
+ return push_popper<top_level, T>(std::forward<T>(x));
+ }
+
+ template <bool top_level = false, typename T>
+ stateless_push_popper<top_level, T> push_pop(lua_State* L_, T&& object_) {
+ return stateless_push_popper<top_level, T>(L_, std::forward<T>(object_));
+ }
+
+ template <typename T>
+ push_popper_at push_pop_at(T&& object_) {
+ int push_count = object_.push();
+ lua_State* L = object_.lua_state();
+ return push_popper_at(L, lua_absindex(L, -push_count), push_count);
+ }
+
+ template <bool top_level = false>
+ push_popper_n<top_level> pop_n(lua_State* L_, int pop_count_) {
+ return push_popper_n<top_level>(L_, pop_count_);
+ }
+ } // namespace stack
+
+ inline lua_State* main_thread(lua_State* L_, lua_State* backup_if_unsupported_ = nullptr) {
+#if SOL_LUA_VERSION_I_ < 502
+ if (L_ == nullptr)
+ return backup_if_unsupported_;
+ lua_getglobal(L_, detail::default_main_thread_name());
+ auto pp = stack::pop_n(L_, 1);
+ if (type_of(L_, -1) == type::thread) {
+ return lua_tothread(L_, -1);
+ }
+ return backup_if_unsupported_;
+#else
+ if (L_ == nullptr)
+ return backup_if_unsupported_;
+ lua_rawgeti(L_, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD);
+ lua_State* Lmain = lua_tothread(L_, -1);
+ lua_pop(L_, 1);
+ return Lmain;
+#endif // Lua 5.2+ has the main thread unqualified_getter
+ }
+
+ namespace detail {
+ struct no_safety_tag {
+ } inline constexpr no_safety {};
+
+ template <bool b>
+ inline lua_State* pick_main_thread(lua_State* L_, lua_State* backup_if_unsupported = nullptr) {
+ (void)L_;
+ (void)backup_if_unsupported;
+ if (b) {
+ return main_thread(L_, backup_if_unsupported);
+ }
+ return L_;
+ }
+ } // namespace detail
+
+ class stateless_reference {
+ private:
+ template <bool o_main_only>
+ friend class basic_reference;
+
+ int ref = LUA_NOREF;
+
+ int copy_ref(lua_State* L_) const noexcept {
+ if (ref == LUA_NOREF)
+ return LUA_NOREF;
+ push(L_);
+ return luaL_ref(L_, LUA_REGISTRYINDEX);
+ }
+
+ lua_State* copy_assign_ref(lua_State* L_, lua_State* rL, const stateless_reference& r) {
+ if (valid(L_)) {
+ deref(L_);
+ }
+ ref = r.copy_ref(L_);
+ return rL;
+ }
+
+ lua_State* move_assign(lua_State* L_, lua_State* rL, stateless_reference&& r) {
+ if (valid(L_)) {
+ deref(L_);
+ }
+ ref = r.ref;
+ r.ref = LUA_NOREF;
+ return rL;
+ }
+
+ protected:
+ int stack_index() const noexcept {
+ return -1;
+ }
+
+ stateless_reference(lua_State* L_, global_tag_t) noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L_, 1, "not enough Lua stack space to push this reference value");
+#endif // make sure stack doesn't overflow
+ lua_pushglobaltable(L_);
+ ref = luaL_ref(L_, LUA_REGISTRYINDEX);
+ }
+
+ stateless_reference(int raw_ref_index) noexcept : ref(raw_ref_index) {
+ }
+
+ public:
+ stateless_reference() noexcept = default;
+ stateless_reference(lua_nil_t) noexcept : stateless_reference() {
+ }
+ stateless_reference(const stack_reference& r) noexcept : stateless_reference(r.lua_state(), r.stack_index()) {
+ }
+ stateless_reference(stack_reference&& r) noexcept : stateless_reference(r.lua_state(), r.stack_index()) {
+ }
+ stateless_reference(lua_State* L_, const stateless_reference& r) noexcept {
+ if (r.ref == LUA_REFNIL) {
+ ref = LUA_REFNIL;
+ return;
+ }
+ if (r.ref == LUA_NOREF || L_ == nullptr) {
+ ref = LUA_NOREF;
+ return;
+ }
+ ref = r.copy_ref(L_);
+ }
+
+ stateless_reference(lua_State* L_, stateless_reference&& r) noexcept {
+ if (r.ref == LUA_REFNIL) {
+ ref = LUA_REFNIL;
+ return;
+ }
+ if (r.ref == LUA_NOREF || L_ == nullptr) {
+ ref = LUA_NOREF;
+ return;
+ }
+ ref = r.ref;
+ r.ref = LUA_NOREF;
+ }
+
+ stateless_reference(lua_State* L_, const stack_reference& r) noexcept {
+ if (L_ == nullptr || r.lua_state() == nullptr || r.get_type() == type::none) {
+ ref = LUA_NOREF;
+ return;
+ }
+ if (r.get_type() == type::lua_nil) {
+ ref = LUA_REFNIL;
+ return;
+ }
+ if (L_ != r.lua_state() && !detail::xmovable(L_, r.lua_state())) {
+ return;
+ }
+ r.push(L_);
+ ref = luaL_ref(L_, LUA_REGISTRYINDEX);
+ }
+
+ stateless_reference(lua_State* L_, const stateless_stack_reference& r) noexcept : stateless_reference(L_, r.stack_index()) {
+ }
+
+ stateless_reference(lua_State* L_, int index = -1) noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L_, 1, "not enough Lua stack space to push this reference value");
+#endif // make sure stack doesn't overflow
+ lua_pushvalue(L_, index);
+ ref = luaL_ref(L_, LUA_REGISTRYINDEX);
+ }
+ stateless_reference(lua_State* L_, absolute_index index_) noexcept : stateless_reference(L_, index_.index) {
+ }
+ stateless_reference(lua_State* L_, ref_index index_) noexcept {
+ lua_rawgeti(L_, LUA_REGISTRYINDEX, index_.index);
+ ref = luaL_ref(L_, LUA_REGISTRYINDEX);
+ }
+ stateless_reference(lua_State*, lua_nil_t) noexcept {
+ }
+
+ ~stateless_reference() noexcept = default;
+
+ stateless_reference(const stateless_reference& o) noexcept = delete;
+ stateless_reference& operator=(const stateless_reference& r) noexcept = delete;
+
+ stateless_reference(stateless_reference&& o) noexcept : ref(o.ref) {
+ o.ref = LUA_NOREF;
+ }
+
+ stateless_reference& operator=(stateless_reference&& o) noexcept {
+ ref = o.ref;
+ o.ref = LUA_NOREF;
+ return *this;
+ }
+
+ int push(lua_State* L_) const noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L_, 1, "not enough Lua stack space to push this reference value");
+#endif // make sure stack doesn't overflow
+ lua_rawgeti(L_, LUA_REGISTRYINDEX, ref);
+ return 1;
+ }
+
+ void pop(lua_State* L_, int n = 1) const noexcept {
+ lua_pop(L_, n);
+ }
+
+ int registry_index() const noexcept {
+ return ref;
+ }
+
+ void reset(lua_State* L_) noexcept {
+ if (valid(L_)) {
+ deref(L_);
+ }
+ ref = LUA_NOREF;
+ }
+
+ void reset(lua_State* L_, int index_) noexcept {
+ reset(L_);
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L_, 1, "not enough Lua stack space to push this reference value");
+#endif // make sure stack doesn't overflow
+ lua_pushvalue(L_, index_);
+ ref = luaL_ref(L_, LUA_REGISTRYINDEX);
+ }
+
+ bool valid(lua_State*) const noexcept {
+ return !(ref == LUA_NOREF || ref == LUA_REFNIL);
+ }
+
+ const void* pointer(lua_State* L_) const noexcept {
+ int si = push(L_);
+ const void* vp = lua_topointer(L_, -si);
+ lua_pop(L_, si);
+ return vp;
+ }
+
+ type get_type(lua_State* L_) const noexcept {
+ int p = push(L_);
+ int result = lua_type(L_, -1);
+ pop(L_, p);
+ return static_cast<type>(result);
+ }
+
+ void abandon(lua_State* = nullptr) {
+ ref = LUA_NOREF;
+ }
+
+ void deref(lua_State* L_) const noexcept {
+ luaL_unref(L_, LUA_REGISTRYINDEX, ref);
+ }
+
+ stateless_reference copy(lua_State* L_) const noexcept {
+ if (!valid(L_)) {
+ return {};
+ }
+ return stateless_reference(copy_ref(L_));
+ }
+
+ void copy_assign(lua_State* L_, const stateless_reference& right) noexcept {
+ if (valid(L_)) {
+ deref(L_);
+ }
+ if (!right.valid(L_)) {
+ return;
+ }
+ ref = right.copy_ref(L_);
+ }
+
+ bool equals(lua_State* L_, const stateless_reference& r) const noexcept {
+ auto ppl = stack::push_pop(L_, *this);
+ auto ppr = stack::push_pop(L_, r);
+ return lua_compare(L_, -1, -2, LUA_OPEQ) == 1;
+ }
+
+ bool equals(lua_State* L_, const stateless_stack_reference& r) const noexcept {
+ auto ppl = stack::push_pop(L_, *this);
+ return lua_compare(L_, -1, r.stack_index(), LUA_OPEQ) == 1;
+ }
+
+ bool equals(lua_State* L_, lua_nil_t) const noexcept {
+ return valid(L_);
+ }
+ };
+
+ template <bool main_only = false>
+ class basic_reference : public stateless_reference {
+ private:
+ template <bool o_main_only>
+ friend class basic_reference;
+ lua_State* luastate = nullptr; // non-owning
+
+ template <bool r_main_only>
+ void copy_assign_complex(const basic_reference<r_main_only>& r) {
+ if (valid()) {
+ deref();
+ }
+ if (r.ref == LUA_REFNIL) {
+ luastate = detail::pick_main_thread < main_only && !r_main_only > (r.lua_state(), r.lua_state());
+ ref = LUA_REFNIL;
+ return;
+ }
+ if (r.ref == LUA_NOREF) {
+ luastate = r.luastate;
+ ref = LUA_NOREF;
+ return;
+ }
+ if (detail::xmovable(lua_state(), r.lua_state())) {
+ r.push(lua_state());
+ ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX);
+ return;
+ }
+ luastate = detail::pick_main_thread < main_only && !r_main_only > (r.lua_state(), r.lua_state());
+ ref = r.copy_ref();
+ }
+
+ template <bool r_main_only>
+ void move_assign(basic_reference<r_main_only>&& r) {
+ if (valid()) {
+ deref();
+ }
+ if (r.ref == LUA_REFNIL) {
+ luastate = detail::pick_main_thread < main_only && !r_main_only > (r.lua_state(), r.lua_state());
+ ref = LUA_REFNIL;
+ return;
+ }
+ if (r.ref == LUA_NOREF) {
+ luastate = r.luastate;
+ ref = LUA_NOREF;
+ return;
+ }
+ if (detail::xmovable(lua_state(), r.lua_state())) {
+ r.push(lua_state());
+ ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX);
+ return;
+ }
+
+ luastate = detail::pick_main_thread < main_only && !r_main_only > (r.lua_state(), r.lua_state());
+ ref = r.ref;
+ r.ref = LUA_NOREF;
+ r.luastate = nullptr;
+ }
+
+ protected:
+ basic_reference(lua_State* L_, global_tag_t) noexcept : basic_reference(detail::pick_main_thread<main_only>(L_, L_), global_tag, global_tag) {
+ }
+
+ basic_reference(lua_State* L_, global_tag_t, global_tag_t) noexcept : stateless_reference(L_, global_tag), luastate(L_) {
+ }
+
+ basic_reference(lua_State* oL, const basic_reference<!main_only>& o) noexcept : stateless_reference(oL, o), luastate(oL) {
+ }
+
+ void deref() const noexcept {
+ return stateless_reference::deref(lua_state());
+ }
+
+ int copy_ref() const noexcept {
+ return copy_ref(lua_state());
+ }
+
+ int copy_ref(lua_State* L_) const noexcept {
+ return stateless_reference::copy_ref(L_);
+ }
+
+ public:
+ basic_reference() noexcept = default;
+ basic_reference(lua_nil_t) noexcept : basic_reference() {
+ }
+ basic_reference(const stack_reference& r) noexcept : basic_reference(r.lua_state(), r.stack_index()) {
+ }
+ basic_reference(stack_reference&& r) noexcept : basic_reference(r.lua_state(), r.stack_index()) {
+ }
+ template <bool r_main_only>
+ basic_reference(lua_State* L_, const basic_reference<r_main_only>& r) noexcept : luastate(detail::pick_main_thread<main_only>(L_, L_)) {
+ if (r.ref == LUA_REFNIL) {
+ ref = LUA_REFNIL;
+ return;
+ }
+ if (r.ref == LUA_NOREF || lua_state() == nullptr) {
+ ref = LUA_NOREF;
+ return;
+ }
+ if (detail::xmovable(lua_state(), r.lua_state())) {
+ r.push(lua_state());
+ ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX);
+ return;
+ }
+ ref = r.copy_ref();
+ }
+
+ template <bool r_main_only>
+ basic_reference(lua_State* L_, basic_reference<r_main_only>&& r) noexcept : luastate(detail::pick_main_thread<main_only>(L_, L_)) {
+ if (r.ref == LUA_REFNIL) {
+ ref = LUA_REFNIL;
+ return;
+ }
+ if (r.ref == LUA_NOREF || lua_state() == nullptr) {
+ ref = LUA_NOREF;
+ return;
+ }
+ if (detail::xmovable(lua_state(), r.lua_state())) {
+ r.push(lua_state());
+ ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX);
+ return;
+ }
+ ref = r.ref;
+ r.ref = LUA_NOREF;
+ r.luastate = nullptr;
+ }
+
+ basic_reference(lua_State* L_, const stack_reference& r) noexcept : luastate(detail::pick_main_thread<main_only>(L_, L_)) {
+ if (lua_state() == nullptr || r.lua_state() == nullptr || r.get_type() == type::none) {
+ ref = LUA_NOREF;
+ return;
+ }
+ if (r.get_type() == type::lua_nil) {
+ ref = LUA_REFNIL;
+ return;
+ }
+ if (lua_state() != r.lua_state() && !detail::xmovable(lua_state(), r.lua_state())) {
+ return;
+ }
+ r.push(lua_state());
+ ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX);
+ }
+ basic_reference(lua_State* L_, int index = -1) noexcept : luastate(detail::pick_main_thread<main_only>(L_, L_)) {
+ // use L_ to stick with that state's execution stack
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L_, 1, "not enough Lua stack space to push this reference value");
+#endif // make sure stack doesn't overflow
+ lua_pushvalue(L_, index);
+ ref = luaL_ref(L_, LUA_REGISTRYINDEX);
+ }
+ basic_reference(lua_State* L_, ref_index index) noexcept : luastate(detail::pick_main_thread<main_only>(L_, L_)) {
+ lua_rawgeti(lua_state(), LUA_REGISTRYINDEX, index.index);
+ ref = luaL_ref(lua_state(), LUA_REGISTRYINDEX);
+ }
+ basic_reference(lua_State* L_, lua_nil_t) noexcept : luastate(detail::pick_main_thread<main_only>(L_, L_)) {
+ }
+
+ ~basic_reference() noexcept {
+ if (lua_state() == nullptr || ref == LUA_NOREF)
+ return;
+ deref();
+ }
+
+ basic_reference(const basic_reference& o) noexcept : stateless_reference(o.copy_ref()), luastate(o.lua_state()) {
+ }
+
+ basic_reference(basic_reference&& o) noexcept : stateless_reference(std::move(o)), luastate(o.lua_state()) {
+ o.luastate = nullptr;
+ }
+
+ basic_reference(const basic_reference<!main_only>& o) noexcept
+ : basic_reference(detail::pick_main_thread<main_only>(o.lua_state(), o.lua_state()), o) {
+ }
+
+ basic_reference(basic_reference<!main_only>&& o) noexcept
+ : stateless_reference(std::move(o)), luastate(detail::pick_main_thread<main_only>(o.lua_state(), o.lua_state())) {
+ o.luastate = nullptr;
+ o.ref = LUA_NOREF;
+ }
+
+ basic_reference& operator=(basic_reference&& r) noexcept {
+ move_assign(std::move(r));
+ return *this;
+ }
+
+ basic_reference& operator=(const basic_reference& r) noexcept {
+ copy_assign_complex(r);
+ return *this;
+ }
+
+ basic_reference& operator=(basic_reference<!main_only>&& r) noexcept {
+ move_assign(std::move(r));
+ return *this;
+ }
+
+ basic_reference& operator=(const basic_reference<!main_only>& r) noexcept {
+ copy_assign_complex(r);
+ return *this;
+ }
+
+ basic_reference& operator=(const lua_nil_t&) noexcept {
+ reset();
+ return *this;
+ }
+
+ template <typename Super>
+ basic_reference& operator=(proxy_base<Super>&& r);
+
+ template <typename Super>
+ basic_reference& operator=(const proxy_base<Super>& r);
+
+ int push() const noexcept {
+ return push(lua_state());
+ }
+
+ void reset() noexcept {
+ stateless_reference::reset(luastate);
+ luastate = nullptr;
+ }
+
+ int push(lua_State* L_) const noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L_, 1, "not enough Lua stack space to push this reference value");
+#endif // make sure stack doesn't overflow
+ if (lua_state() == nullptr) {
+ lua_pushnil(L_);
+ return 1;
+ }
+ lua_rawgeti(lua_state(), LUA_REGISTRYINDEX, ref);
+ if (L_ != lua_state()) {
+ lua_xmove(lua_state(), L_, 1);
+ }
+ return 1;
+ }
+
+ void pop() const noexcept {
+ pop(lua_state());
+ }
+
+ void pop(lua_State* L_, int n = 1) const noexcept {
+ stateless_reference::pop(L_, n);
+ }
+
+ int registry_index() const noexcept {
+ return stateless_reference::registry_index();
+ }
+
+ bool valid() const noexcept {
+ return stateless_reference::valid(lua_state());
+ }
+
+ bool valid(lua_State* L_) const noexcept {
+ return stateless_reference::valid(L_);
+ }
+
+ const void* pointer() const noexcept {
+ return stateless_reference::pointer(lua_state());
+ }
+
+ explicit operator bool() const noexcept {
+ return valid();
+ }
+
+ type get_type() const noexcept {
+ return stateless_reference::get_type(lua_state());
+ }
+
+ lua_State* lua_state() const noexcept {
+ return luastate;
+ }
+ };
+
+ template <bool lb, bool rb>
+ inline bool operator==(const basic_reference<lb>& l, const basic_reference<rb>& r) noexcept {
+ auto ppl = stack::push_pop(l);
+ auto ppr = stack::push_pop(r);
+ return lua_compare(l.lua_state(), -1, -2, LUA_OPEQ) == 1;
+ }
+
+ template <bool lb, bool rb>
+ inline bool operator!=(const basic_reference<lb>& l, const basic_reference<rb>& r) noexcept {
+ return !operator==(l, r);
+ }
+
+ template <bool lb>
+ inline bool operator==(const basic_reference<lb>& l, const stack_reference& r) noexcept {
+ auto ppl = stack::push_pop(l);
+ return lua_compare(l.lua_state(), -1, r.stack_index(), LUA_OPEQ) == 1;
+ }
+
+ template <bool lb>
+ inline bool operator!=(const basic_reference<lb>& l, const stack_reference& r) noexcept {
+ return !operator==(l, r);
+ }
+
+ template <bool rb>
+ inline bool operator==(const stack_reference& l, const basic_reference<rb>& r) noexcept {
+ auto ppr = stack::push_pop(r);
+ return lua_compare(l.lua_state(), -1, r.stack_index(), LUA_OPEQ) == 1;
+ }
+
+ template <bool rb>
+ inline bool operator!=(const stack_reference& l, const basic_reference<rb>& r) noexcept {
+ return !operator==(l, r);
+ }
+
+ template <bool lb>
+ inline bool operator==(const basic_reference<lb>& lhs, const lua_nil_t&) noexcept {
+ return !lhs.valid();
+ }
+
+ template <bool rb>
+ inline bool operator==(const lua_nil_t&, const basic_reference<rb>& rhs) noexcept {
+ return !rhs.valid();
+ }
+
+ template <bool lb>
+ inline bool operator!=(const basic_reference<lb>& lhs, const lua_nil_t&) noexcept {
+ return lhs.valid();
+ }
+
+ template <bool rb>
+ inline bool operator!=(const lua_nil_t&, const basic_reference<rb>& rhs) noexcept {
+ return rhs.valid();
+ }
+
+ inline bool operator==(const stateless_reference& l, const stateless_reference& r) noexcept {
+ return l.registry_index() == r.registry_index();
+ }
+
+ inline bool operator!=(const stateless_reference& l, const stateless_reference& r) noexcept {
+ return l.registry_index() != r.registry_index();
+ }
+
+ inline bool operator==(const stateless_reference& lhs, const lua_nil_t&) noexcept {
+ return lhs.registry_index() == LUA_REFNIL;
+ }
+
+ inline bool operator==(const lua_nil_t&, const stateless_reference& rhs) noexcept {
+ return rhs.registry_index() == LUA_REFNIL;
+ }
+
+ inline bool operator!=(const stateless_reference& lhs, const lua_nil_t&) noexcept {
+ return lhs.registry_index() != LUA_REFNIL;
+ }
+
+ inline bool operator!=(const lua_nil_t&, const stateless_reference& rhs) noexcept {
+ return rhs.registry_index() != LUA_REFNIL;
+ }
+
+ struct stateless_reference_equals : public stateless_stack_reference_equals {
+ using is_transparent = std::true_type;
+
+ stateless_reference_equals(lua_State* L_) noexcept : stateless_stack_reference_equals(L_) {
+ }
+
+ bool operator()(const lua_nil_t& lhs, const stateless_reference& rhs) const noexcept {
+ return rhs.equals(lua_state(), lhs);
+ }
+
+ bool operator()(const stateless_reference& lhs, const lua_nil_t& rhs) const noexcept {
+ return lhs.equals(lua_state(), rhs);
+ }
+
+ bool operator()(const stateless_reference& lhs, const stateless_reference& rhs) const noexcept {
+ return lhs.equals(lua_state(), rhs);
+ }
+ };
+
+ struct reference_equals : public stack_reference_equals {
+ using is_transparent = std::true_type;
+
+ template <bool rb>
+ bool operator()(const lua_nil_t& lhs, const basic_reference<rb>& rhs) const noexcept {
+ return lhs == rhs;
+ }
+
+ template <bool lb>
+ bool operator()(const basic_reference<lb>& lhs, const lua_nil_t& rhs) const noexcept {
+ return lhs == rhs;
+ }
+
+ template <bool lb, bool rb>
+ bool operator()(const basic_reference<lb>& lhs, const basic_reference<rb>& rhs) const noexcept {
+ return lhs == rhs;
+ }
+
+ template <bool lb>
+ bool operator()(const basic_reference<lb>& lhs, const stack_reference& rhs) const noexcept {
+ return lhs == rhs;
+ }
+
+ template <bool rb>
+ bool operator()(const stack_reference& lhs, const basic_reference<rb>& rhs) const noexcept {
+ return lhs == rhs;
+ }
+ };
+
+ struct stateless_reference_hash : public stateless_stack_reference_hash {
+ using argument_type = stateless_reference;
+ using result_type = std::size_t;
+ using is_transparent = std::true_type;
+
+ stateless_reference_hash(lua_State* L_) noexcept : stateless_stack_reference_hash(L_) {
+ }
+
+ result_type operator()(const stateless_reference& lhs) const noexcept {
+ std::hash<const void*> h;
+ return h(lhs.pointer(lua_state()));
+ }
+ };
+
+ struct reference_hash : public stack_reference_hash {
+ using argument_type = reference;
+ using result_type = std::size_t;
+ using is_transparent = std::true_type;
+
+ template <bool lb>
+ result_type operator()(const basic_reference<lb>& lhs) const noexcept {
+ std::hash<const void*> h;
+ return h(lhs.pointer());
+ }
+ };
+} // namespace sol
+
+// end of sol/reference.hpp
+
+// beginning of sol/tie.hpp
+
+namespace sol {
+
+ namespace detail {
+ template <typename T>
+ struct is_speshul : std::false_type { };
+ } // namespace detail
+
+ template <typename T>
+ struct tie_size : std::tuple_size<T> { };
+
+ template <typename T>
+ struct is_tieable : std::integral_constant<bool, (::sol::tie_size<T>::value > 0)> { };
+
+ template <typename... Tn>
+ struct tie_t : public std::tuple<std::add_lvalue_reference_t<Tn>...> {
+ private:
+ typedef std::tuple<std::add_lvalue_reference_t<Tn>...> base_t;
+
+ template <typename T>
+ void set(std::false_type, T&& target) {
+ std::get<0>(*this) = std::forward<T>(target);
+ }
+
+ template <typename T>
+ void set(std::true_type, T&& target) {
+ typedef tie_size<meta::unqualified_t<T>> value_size;
+ typedef tie_size<std::tuple<Tn...>> tie_size;
+ typedef meta::conditional_t<(value_size::value < tie_size::value), value_size, tie_size> indices_size;
+ typedef std::make_index_sequence<indices_size::value> indices;
+ set_extra(detail::is_speshul<meta::unqualified_t<T>>(), indices(), std::forward<T>(target));
+ }
+
+ template <std::size_t... I, typename T>
+ void set_extra(std::true_type, std::index_sequence<I...>, T&& target) {
+ using std::get;
+ (void)detail::swallow { 0, (get<I>(static_cast<base_t&>(*this)) = get<I>(types<Tn...>(), target), 0)..., 0 };
+ }
+
+ template <std::size_t... I, typename T>
+ void set_extra(std::false_type, std::index_sequence<I...>, T&& target) {
+ using std::get;
+ (void)detail::swallow { 0, (get<I>(static_cast<base_t&>(*this)) = get<I>(target), 0)..., 0 };
+ }
+
+ public:
+ using base_t::base_t;
+
+ template <typename T>
+ tie_t& operator=(T&& value) {
+ typedef is_tieable<meta::unqualified_t<T>> tieable;
+ set(tieable(), std::forward<T>(value));
+ return *this;
+ }
+ };
+
+ template <typename... Tn>
+ struct tie_size<tie_t<Tn...>> : std::tuple_size<std::tuple<Tn...>> { };
+
+ namespace adl_barrier_detail {
+ template <typename... Tn>
+ inline tie_t<std::remove_reference_t<Tn>...> tie(Tn&&... argn) {
+ return tie_t<std::remove_reference_t<Tn>...>(std::forward<Tn>(argn)...);
+ }
+ } // namespace adl_barrier_detail
+
+ using namespace adl_barrier_detail;
+
+} // namespace sol
+
+// end of sol/tie.hpp
+
+// beginning of sol/stack_guard.hpp
+
+#include <functional>
+
+namespace sol {
+ namespace detail {
+ inline void stack_fail(int, int) {
+#if SOL_IS_ON(SOL_EXCEPTIONS)
+ throw error(detail::direct_error, "imbalanced stack after operation finish");
+#else
+ // Lol, what do you want, an error printout? :3c
+ // There's no sane default here. The right way would be C-style abort(), and that's not acceptable, so
+ // hopefully someone will register their own stack_fail thing for the `fx` parameter of stack_guard.
+#endif // No Exceptions
+ }
+ } // namespace detail
+
+ struct stack_guard {
+ lua_State* L;
+ int top;
+ std::function<void(int, int)> on_mismatch;
+
+ stack_guard(lua_State* L) : stack_guard(L, lua_gettop(L)) {
+ }
+ stack_guard(lua_State* L, int top, std::function<void(int, int)> fx = detail::stack_fail) : L(L), top(top), on_mismatch(std::move(fx)) {
+ }
+ bool check_stack(int modification = 0) const {
+ int bottom = lua_gettop(L) + modification;
+ if (top == bottom) {
+ return true;
+ }
+ on_mismatch(top, bottom);
+ return false;
+ }
+ ~stack_guard() {
+ check_stack();
+ }
+ };
+} // namespace sol
+
+// end of sol/stack_guard.hpp
+
+#include <vector>
+#include <bitset>
+#include <forward_list>
+#include <string>
+#include <limits>
+#include <algorithm>
+#include <sstream>
+#include <optional>
+#include <type_traits>
+
+namespace sol {
+ namespace detail {
+ struct with_function_tag { };
+ struct as_reference_tag { };
+ template <typename T>
+ struct as_pointer_tag { };
+ template <typename T>
+ struct as_value_tag { };
+ template <typename T>
+ struct as_unique_tag { };
+ template <typename T>
+ struct as_table_tag { };
+
+ template <typename Tag>
+ inline constexpr bool is_tagged_v
+ = meta::is_specialization_of_v<Tag,
+ detail::
+ as_pointer_tag> || meta::is_specialization_of_v<Tag, as_value_tag> || meta::is_specialization_of_v<Tag, as_unique_tag> || meta::is_specialization_of_v<Tag, as_table_tag> || std::is_same_v<Tag, as_reference_tag> || std::is_same_v<Tag, with_function_tag>;
+
+ using lua_reg_table = luaL_Reg[64];
+
+ using unique_destructor = void (*)(void*);
+ using unique_tag = detail::inheritance_unique_cast_function;
+
+ inline void* alloc_newuserdata(lua_State* L, std::size_t bytesize) {
+#if SOL_LUA_VERSION_I_ >= 504
+ return lua_newuserdatauv(L, bytesize, 1);
+#else
+ return lua_newuserdata(L, bytesize);
+#endif
+ }
+
+ constexpr std::uintptr_t align(std::size_t alignment, std::uintptr_t ptr, std::size_t& space) {
+ // this handles arbitrary alignments...
+ // make this into a power-of-2-only?
+ // actually can't: this is a C++14-compatible framework,
+ // power of 2 alignment is C++17
+ std::uintptr_t offby = static_cast<std::uintptr_t>(ptr % alignment);
+ std::uintptr_t padding = (alignment - offby) % alignment;
+ ptr += padding;
+ space -= padding;
+ return ptr;
+ }
+
+ inline void* align(std::size_t alignment, void* ptr, std::size_t& space) {
+ return reinterpret_cast<void*>(align(alignment, reinterpret_cast<std::uintptr_t>(ptr), space));
+ }
+
+ constexpr std::uintptr_t align_one(std::size_t alignment, std::size_t size, std::uintptr_t ptr) {
+ std::size_t space = (std::numeric_limits<std::size_t>::max)();
+ return align(alignment, ptr, space) + size;
+ }
+
+ template <typename... Args>
+ constexpr std::size_t aligned_space_for(std::uintptr_t ptr) {
+ std::uintptr_t end = ptr;
+ ((end = align_one(alignof(Args), sizeof(Args), end)), ...);
+ return static_cast<std::size_t>(end - ptr);
+ }
+
+ template <typename... Args>
+ constexpr std::size_t aligned_space_for() {
+ static_assert(sizeof...(Args) > 0);
+
+ constexpr std::size_t max_arg_alignment = (std::max)({ alignof(Args)... });
+ if constexpr (max_arg_alignment <= alignof(std::max_align_t)) {
+ // If all types are `good enough`, simply calculate alignment in case of the worst allocator
+ std::size_t worst_required_size = 0;
+ for (std::size_t ptr = 0; ptr < max_arg_alignment; ptr++) {
+ worst_required_size = (std::max)(worst_required_size, aligned_space_for<Args...>(ptr));
+ }
+ return worst_required_size;
+ }
+ else {
+ // For over-aligned types let's assume that every Arg in Args starts at the worst aligned address
+ return (aligned_space_for<Args>(0x1) + ...);
+ }
+ }
+
+ inline void* align_usertype_pointer(void* ptr) {
+ using use_align = std::integral_constant<bool,
+#if SOL_IS_OFF(SOL_ALIGN_MEMORY)
+ false
+#else
+ (std::alignment_of<void*>::value > 1)
+#endif
+ >;
+ if (!use_align::value) {
+ return ptr;
+ }
+ std::size_t space = (std::numeric_limits<std::size_t>::max)();
+ return align(std::alignment_of<void*>::value, ptr, space);
+ }
+
+ template <bool pre_aligned = false, bool pre_shifted = false>
+ void* align_usertype_unique_destructor(void* ptr) {
+ using use_align = std::integral_constant<bool,
+#if SOL_IS_OFF(SOL_ALIGN_MEMORY)
+ false
+#else
+ (std::alignment_of<unique_destructor>::value > 1)
+#endif
+ >;
+ if (!pre_aligned) {
+ ptr = align_usertype_pointer(ptr);
+ }
+ if (!pre_shifted) {
+ ptr = static_cast<void*>(static_cast<char*>(ptr) + sizeof(void*));
+ }
+ if (!use_align::value) {
+ return static_cast<void*>(static_cast<void**>(ptr) + 1);
+ }
+ std::size_t space = (std::numeric_limits<std::size_t>::max)();
+ return align(std::alignment_of<unique_destructor>::value, ptr, space);
+ }
+
+ template <bool pre_aligned = false, bool pre_shifted = false>
+ void* align_usertype_unique_tag(void* ptr) {
+ using use_align = std::integral_constant<bool,
+#if SOL_IS_OFF(SOL_ALIGN_MEMORY)
+ false
+#else
+ (std::alignment_of<unique_tag>::value > 1)
+#endif
+ >;
+ if (!pre_aligned) {
+ ptr = align_usertype_unique_destructor(ptr);
+ }
+ if (!pre_shifted) {
+ ptr = static_cast<void*>(static_cast<char*>(ptr) + sizeof(unique_destructor));
+ }
+ if (!use_align::value) {
+ return ptr;
+ }
+ std::size_t space = (std::numeric_limits<std::size_t>::max)();
+ return align(std::alignment_of<unique_tag>::value, ptr, space);
+ }
+
+ template <typename T, bool pre_aligned = false, bool pre_shifted = false>
+ void* align_usertype_unique(void* ptr) {
+ typedef std::integral_constant<bool,
+#if SOL_IS_OFF(SOL_ALIGN_MEMORY)
+ false
+#else
+ (std::alignment_of_v<T> > 1)
+#endif
+ >
+ use_align;
+ if (!pre_aligned) {
+ ptr = align_usertype_unique_tag(ptr);
+ }
+ if (!pre_shifted) {
+ ptr = static_cast<void*>(static_cast<char*>(ptr) + sizeof(unique_tag));
+ }
+ if (!use_align::value) {
+ return ptr;
+ }
+ std::size_t space = (std::numeric_limits<std::size_t>::max)();
+ return align(std::alignment_of_v<T>, ptr, space);
+ }
+
+ template <typename T>
+ void* align_user(void* ptr) {
+ typedef std::integral_constant<bool,
+#if SOL_IS_OFF(SOL_ALIGN_MEMORY)
+ false
+#else
+ (std::alignment_of_v<T> > 1)
+#endif
+ >
+ use_align;
+ if (!use_align::value) {
+ return ptr;
+ }
+ std::size_t space = (std::numeric_limits<std::size_t>::max)();
+ return align(std::alignment_of_v<T>, ptr, space);
+ }
+
+ template <typename T>
+ T** usertype_allocate_pointer(lua_State* L) {
+ typedef std::integral_constant<bool,
+#if SOL_IS_OFF(SOL_ALIGN_MEMORY)
+ false
+#else
+ (std::alignment_of<T*>::value > 1)
+#endif
+ >
+ use_align;
+ if (!use_align::value) {
+ T** pointerpointer = static_cast<T**>(alloc_newuserdata(L, sizeof(T*)));
+ return pointerpointer;
+ }
+ constexpr std::size_t initial_size = aligned_space_for<T*>();
+
+ std::size_t allocated_size = initial_size;
+ void* unadjusted = alloc_newuserdata(L, initial_size);
+ void* adjusted = align(std::alignment_of<T*>::value, unadjusted, allocated_size);
+ if (adjusted == nullptr) {
+ // trash allocator can burn in hell
+ lua_pop(L, 1);
+ // luaL_error(L, "if you are the one that wrote this allocator you should feel bad for doing a
+ // worse job than malloc/realloc and should go read some books, yeah?");
+ luaL_error(L, "cannot properly align memory for '%s'", detail::demangle<T*>().data());
+ }
+ return static_cast<T**>(adjusted);
+ }
+
+ inline bool attempt_alloc(lua_State* L, std::size_t ptr_align, std::size_t ptr_size, std::size_t value_align,
+ std::size_t allocated_size, void*& pointer_adjusted, void*& data_adjusted) {
+ void* adjusted = alloc_newuserdata(L, allocated_size);
+ pointer_adjusted = align(ptr_align, adjusted, allocated_size);
+ if (pointer_adjusted == nullptr) {
+ lua_pop(L, 1);
+ return false;
+ }
+ // subtract size of what we're going to allocate there
+ allocated_size -= ptr_size;
+ adjusted = static_cast<void*>(static_cast<char*>(pointer_adjusted) + ptr_size);
+ data_adjusted = align(value_align, adjusted, allocated_size);
+ if (data_adjusted == nullptr) {
+ lua_pop(L, 1);
+ return false;
+ }
+ return true;
+ }
+
+ inline bool attempt_alloc_unique(lua_State* L, std::size_t ptr_align, std::size_t ptr_size, std::size_t real_align,
+ std::size_t allocated_size, void*& pointer_adjusted, void*& dx_adjusted, void*& id_adjusted, void*& data_adjusted) {
+ void* adjusted = alloc_newuserdata(L, allocated_size);
+ pointer_adjusted = align(ptr_align, adjusted, allocated_size);
+ if (pointer_adjusted == nullptr) {
+ lua_pop(L, 1);
+ return false;
+ }
+ allocated_size -= ptr_size;
+
+ adjusted = static_cast<void*>(static_cast<char*>(pointer_adjusted) + ptr_size);
+ dx_adjusted = align(std::alignment_of_v<unique_destructor>, adjusted, allocated_size);
+ if (dx_adjusted == nullptr) {
+ lua_pop(L, 1);
+ return false;
+ }
+ allocated_size -= sizeof(unique_destructor);
+
+ adjusted = static_cast<void*>(static_cast<char*>(dx_adjusted) + sizeof(unique_destructor));
+
+ id_adjusted = align(std::alignment_of_v<unique_tag>, adjusted, allocated_size);
+ if (id_adjusted == nullptr) {
+ lua_pop(L, 1);
+ return false;
+ }
+ allocated_size -= sizeof(unique_tag);
+
+ adjusted = static_cast<void*>(static_cast<char*>(id_adjusted) + sizeof(unique_tag));
+ data_adjusted = align(real_align, adjusted, allocated_size);
+ if (data_adjusted == nullptr) {
+ lua_pop(L, 1);
+ return false;
+ }
+ return true;
+ }
+
+ template <typename T>
+ T* usertype_allocate(lua_State* L) {
+ typedef std::integral_constant<bool,
+#if SOL_IS_OFF(SOL_ALIGN_MEMORY)
+ false
+#else
+ (std::alignment_of<T*>::value > 1 || std::alignment_of_v<T> > 1)
+#endif
+ >
+ use_align;
+ if (!use_align::value) {
+ T** pointerpointer = static_cast<T**>(alloc_newuserdata(L, sizeof(T*) + sizeof(T)));
+ T*& pointerreference = *pointerpointer;
+ T* allocationtarget = reinterpret_cast<T*>(pointerpointer + 1);
+ pointerreference = allocationtarget;
+ return allocationtarget;
+ }
+
+ constexpr std::size_t initial_size = aligned_space_for<T*, T>();
+
+ void* pointer_adjusted;
+ void* data_adjusted;
+ bool result
+ = attempt_alloc(L, std::alignment_of_v<T*>, sizeof(T*), std::alignment_of_v<T>, initial_size, pointer_adjusted, data_adjusted);
+ if (!result) {
+ if (pointer_adjusted == nullptr) {
+ luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle<T>().c_str());
+ }
+ else {
+ luaL_error(L, "aligned allocation of userdata block (data section) for '%s' failed", detail::demangle<T>().c_str());
+ }
+ return nullptr;
+ }
+
+ T** pointerpointer = reinterpret_cast<T**>(pointer_adjusted);
+ T*& pointerreference = *pointerpointer;
+ T* allocationtarget = reinterpret_cast<T*>(data_adjusted);
+ pointerreference = allocationtarget;
+ return allocationtarget;
+ }
+
+ template <typename T, typename Real>
+ Real* usertype_unique_allocate(lua_State* L, T**& pref, unique_destructor*& dx, unique_tag*& id) {
+ typedef std::integral_constant<bool,
+#if SOL_IS_OFF(SOL_ALIGN_MEMORY)
+ false
+#else
+ (std::alignment_of<T*>::value > 1 || std::alignment_of<unique_tag>::value > 1 || std::alignment_of<unique_destructor>::value > 1
+ || std::alignment_of<Real>::value > 1)
+#endif
+ >
+ use_align;
+ if (!use_align::value) {
+ pref = static_cast<T**>(alloc_newuserdata(L, sizeof(T*) + sizeof(detail::unique_destructor) + sizeof(unique_tag) + sizeof(Real)));
+ dx = static_cast<detail::unique_destructor*>(static_cast<void*>(pref + 1));
+ id = static_cast<unique_tag*>(static_cast<void*>(dx + 1));
+ Real* mem = static_cast<Real*>(static_cast<void*>(id + 1));
+ return mem;
+ }
+
+ constexpr std::size_t initial_size = aligned_space_for<T*, unique_destructor, unique_tag, Real>();
+
+ void* pointer_adjusted = nullptr;
+ void* dx_adjusted = nullptr;
+ void* id_adjusted = nullptr;
+ void* data_adjusted = nullptr;
+ bool result = attempt_alloc_unique(L,
+ std::alignment_of_v<T*>,
+ sizeof(T*),
+ std::alignment_of_v<Real>,
+ initial_size,
+ pointer_adjusted,
+ dx_adjusted,
+ id_adjusted,
+ data_adjusted);
+ if (!result) {
+ if (pointer_adjusted == nullptr) {
+ luaL_error(L, "aligned allocation of userdata block (pointer section) for '%s' failed", detail::demangle<T>().c_str());
+ }
+ else if (dx_adjusted == nullptr) {
+ luaL_error(L, "aligned allocation of userdata block (deleter section) for '%s' failed", detail::demangle<T>().c_str());
+ }
+ else {
+ luaL_error(L, "aligned allocation of userdata block (data section) for '%s' failed", detail::demangle<T>().c_str());
+ }
+ return nullptr;
+ }
+
+ pref = static_cast<T**>(pointer_adjusted);
+ dx = static_cast<detail::unique_destructor*>(dx_adjusted);
+ id = static_cast<unique_tag*>(id_adjusted);
+ Real* mem = static_cast<Real*>(data_adjusted);
+ return mem;
+ }
+
+ template <typename T>
+ T* user_allocate(lua_State* L) {
+ typedef std::integral_constant<bool,
+#if SOL_IS_OFF(SOL_ALIGN_MEMORY)
+ false
+#else
+ (std::alignment_of_v<T> > 1)
+#endif
+ >
+ use_align;
+ if (!use_align::value) {
+ T* pointer = static_cast<T*>(alloc_newuserdata(L, sizeof(T)));
+ return pointer;
+ }
+
+ constexpr std::size_t initial_size = aligned_space_for<T>();
+
+ std::size_t allocated_size = initial_size;
+ void* unadjusted = alloc_newuserdata(L, allocated_size);
+ void* adjusted = align(std::alignment_of_v<T>, unadjusted, allocated_size);
+ if (adjusted == nullptr) {
+ lua_pop(L, 1);
+ luaL_error(L, "cannot properly align memory for '%s'", detail::demangle<T>().data());
+ }
+ return static_cast<T*>(adjusted);
+ }
+
+ template <typename T>
+ int usertype_alloc_destroy(lua_State* L) noexcept {
+ void* memory = lua_touserdata(L, 1);
+ memory = align_usertype_pointer(memory);
+ T** pdata = static_cast<T**>(memory);
+ T* data = *pdata;
+ std::allocator<T> alloc {};
+ std::allocator_traits<std::allocator<T>>::destroy(alloc, data);
+ return 0;
+ }
+
+ template <typename T>
+ int unique_destroy(lua_State* L) noexcept {
+ void* memory = lua_touserdata(L, 1);
+ memory = align_usertype_unique_destructor(memory);
+ unique_destructor& dx = *static_cast<unique_destructor*>(memory);
+ memory = align_usertype_unique_tag<true>(memory);
+ (dx)(memory);
+ return 0;
+ }
+
+ template <typename T>
+ int user_alloc_destroy(lua_State* L) noexcept {
+ void* memory = lua_touserdata(L, 1);
+ void* aligned_memory = align_user<T>(memory);
+ T* typed_memory = static_cast<T*>(aligned_memory);
+ std::allocator<T> alloc;
+ std::allocator_traits<std::allocator<T>>::destroy(alloc, typed_memory);
+ return 0;
+ }
+
+ template <typename T, typename Real>
+ void usertype_unique_alloc_destroy(void* memory) {
+ void* aligned_memory = align_usertype_unique<Real, true>(memory);
+ Real* typed_memory = static_cast<Real*>(aligned_memory);
+ std::allocator<Real> alloc;
+ std::allocator_traits<std::allocator<Real>>::destroy(alloc, typed_memory);
+ }
+
+ template <typename T>
+ int cannot_destroy(lua_State* L) {
+ return luaL_error(L,
+ "cannot call the destructor for '%s': it is either hidden (protected/private) or removed with '= "
+ "delete' and thusly this type is being destroyed without properly destroying, invoking undefined "
+ "behavior: please bind a usertype and specify a custom destructor to define the behavior properly",
+ detail::demangle<T>().data());
+ }
+
+ template <typename T>
+ void reserve(T&, std::size_t) {
+ }
+
+ template <typename T, typename Al>
+ void reserve(std::vector<T, Al>& vec, std::size_t hint) {
+ vec.reserve(hint);
+ }
+
+ template <typename T, typename Tr, typename Al>
+ void reserve(std::basic_string<T, Tr, Al>& str, std::size_t hint) {
+ str.reserve(hint);
+ }
+
+ inline bool property_always_true(meta_function) {
+ return true;
+ }
+
+ struct properties_enrollment_allowed {
+ int& times_through;
+ std::bitset<64>& properties;
+ automagic_enrollments& enrollments;
+
+ properties_enrollment_allowed(int& times_through_, std::bitset<64>& properties_, automagic_enrollments& enrollments_)
+ : times_through(times_through_), properties(properties_), enrollments(enrollments_) {
+ }
+
+ bool operator()(meta_function mf) const {
+ bool p = properties[static_cast<std::size_t>(mf)];
+ if (times_through > 0) {
+ return p;
+ }
+ switch (mf) {
+ case meta_function::length:
+ return enrollments.length_operator && !p;
+ case meta_function::pairs:
+ return enrollments.pairs_operator && !p;
+ case meta_function::call:
+ return enrollments.call_operator && !p;
+ case meta_function::less_than:
+ return enrollments.less_than_operator && !p;
+ case meta_function::less_than_or_equal_to:
+ return enrollments.less_than_or_equal_to_operator && !p;
+ case meta_function::equal_to:
+ return enrollments.equal_to_operator && !p;
+ default:
+ break;
+ }
+ return !p;
+ }
+ };
+
+ struct indexed_insert {
+ lua_reg_table& registration_table;
+ int& index;
+
+ indexed_insert(lua_reg_table& registration_table_, int& index_ref_) : registration_table(registration_table_), index(index_ref_) {
+ }
+ void operator()(meta_function meta_function_name_, lua_CFunction c_function_) {
+ registration_table[index] = luaL_Reg { to_string(meta_function_name_).c_str(), c_function_ };
+ ++index;
+ }
+ };
+ } // namespace detail
+
+ namespace stack {
+
+ template <typename T, bool global = false, bool raw = false, typename = void>
+ struct field_getter;
+ template <typename T, typename P, bool global = false, bool raw = false, typename = void>
+ struct probe_field_getter;
+
+ template <typename T, bool global = false, bool raw = false, typename = void>
+ struct field_setter;
+
+ template <typename T, typename = void>
+ struct unqualified_getter;
+ template <typename T, typename = void>
+ struct qualified_getter;
+
+ template <typename T, typename = void>
+ struct qualified_interop_getter;
+ template <typename T, typename = void>
+ struct unqualified_interop_getter;
+
+ template <typename T, typename = void>
+ struct popper;
+
+ template <typename T, typename = void>
+ struct unqualified_pusher;
+
+ template <typename T, type t, typename = void>
+ struct unqualified_checker;
+ template <typename T, type t, typename = void>
+ struct qualified_checker;
+
+ template <typename T, typename = void>
+ struct unqualified_check_getter;
+ template <typename T, typename = void>
+ struct qualified_check_getter;
+
+ struct probe {
+ bool success;
+ int levels;
+
+ probe(bool s, int l) : success(s), levels(l) {
+ }
+
+ operator bool() const {
+ return success;
+ };
+ };
+
+ struct record {
+ int last;
+ int used;
+
+ record() noexcept : last(), used() {
+ }
+ void use(int count) noexcept {
+ last = count;
+ used += count;
+ }
+ };
+
+ namespace stack_detail {
+ template <typename Function>
+ Function* get_function_pointer(lua_State*, int, record&) noexcept;
+ template <typename Function, typename Handler>
+ bool check_function_pointer(lua_State* L, int index, Handler&& handler, record& tracking) noexcept;
+ } // namespace stack_detail
+
+ } // namespace stack
+
+ namespace meta { namespace meta_detail {
+ template <typename T>
+ using adl_sol_lua_get_test_t = decltype(sol_lua_get(types<T>(), static_cast<lua_State*>(nullptr), -1, std::declval<stack::record&>()));
+
+ template <typename T>
+ using adl_sol_lua_interop_get_test_t
+ = decltype(sol_lua_interop_get(types<T>(), static_cast<lua_State*>(nullptr), -1, static_cast<void*>(nullptr), std::declval<stack::record&>()));
+
+ template <typename T>
+ using adl_sol_lua_check_test_t = decltype(sol_lua_check(types<T>(), static_cast<lua_State*>(nullptr), -1, &no_panic, std::declval<stack::record&>()));
+
+ template <typename T>
+ using adl_sol_lua_interop_check_test_t
+ = decltype(sol_lua_interop_check(types<T>(), static_cast<lua_State*>(nullptr), -1, type::none, &no_panic, std::declval<stack::record&>()));
+
+ template <typename T>
+ using adl_sol_lua_check_get_test_t
+ = decltype(sol_lua_check_get(types<T>(), static_cast<lua_State*>(nullptr), -1, &no_panic, std::declval<stack::record&>()));
+
+ template <typename... Args>
+ using adl_sol_lua_push_test_t = decltype(sol_lua_push(static_cast<lua_State*>(nullptr), std::declval<Args>()...));
+
+ template <typename T, typename... Args>
+ using adl_sol_lua_push_exact_test_t = decltype(sol_lua_push(types<T>(), static_cast<lua_State*>(nullptr), std::declval<Args>()...));
+
+ template <typename T>
+ inline constexpr bool is_adl_sol_lua_get_v = meta::is_detected_v<adl_sol_lua_get_test_t, T>;
+
+ template <typename T>
+ inline constexpr bool is_adl_sol_lua_interop_get_v = meta::is_detected_v<adl_sol_lua_interop_get_test_t, T>;
+
+ template <typename T>
+ inline constexpr bool is_adl_sol_lua_check_v = meta::is_detected_v<adl_sol_lua_check_test_t, T>;
+
+ template <typename T>
+ inline constexpr bool is_adl_sol_lua_interop_check_v = meta::is_detected_v<adl_sol_lua_interop_check_test_t, T>;
+
+ template <typename T>
+ inline constexpr bool is_adl_sol_lua_check_get_v = meta::is_detected_v<adl_sol_lua_check_get_test_t, T>;
+
+ template <typename... Args>
+ inline constexpr bool is_adl_sol_lua_push_v = meta::is_detected_v<adl_sol_lua_push_test_t, Args...>;
+
+ template <typename T, typename... Args>
+ inline constexpr bool is_adl_sol_lua_push_exact_v = meta::is_detected_v<adl_sol_lua_push_exact_test_t, T, Args...>;
+ }} // namespace meta::meta_detail
+
+ namespace stack {
+ namespace stack_detail {
+ constexpr const char* not_enough_stack_space = "not enough space left on Lua stack";
+ constexpr const char* not_enough_stack_space_floating = "not enough space left on Lua stack for a floating point number";
+ constexpr const char* not_enough_stack_space_integral = "not enough space left on Lua stack for an integral number";
+ constexpr const char* not_enough_stack_space_string = "not enough space left on Lua stack for a string";
+ constexpr const char* not_enough_stack_space_meta_function_name = "not enough space left on Lua stack for the name of a meta_function";
+ constexpr const char* not_enough_stack_space_userdata = "not enough space left on Lua stack to create a sol2 userdata";
+ constexpr const char* not_enough_stack_space_generic = "not enough space left on Lua stack to push valuees";
+ constexpr const char* not_enough_stack_space_environment = "not enough space left on Lua stack to retrieve environment";
+
+ template <typename T>
+ struct strip {
+ typedef T type;
+ };
+ template <typename T>
+ struct strip<std::reference_wrapper<T>> {
+ typedef T& type;
+ };
+ template <typename T>
+ struct strip<user<T>> {
+ typedef T& type;
+ };
+ template <typename T>
+ struct strip<non_null<T>> {
+ typedef T type;
+ };
+ template <typename T>
+ using strip_t = typename strip<T>::type;
+
+ template <typename C>
+ static int get_size_hint(C& c) {
+ return static_cast<int>(c.size());
+ }
+
+ template <typename V, typename Al>
+ static int get_size_hint(const std::forward_list<V, Al>&) {
+ // forward_list makes me sad
+ return static_cast<int>(32);
+ }
+
+ template <typename T>
+ decltype(auto) unchecked_unqualified_get(lua_State* L, int index, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::meta_detail::is_adl_sol_lua_get_v<Tu>) {
+ return sol_lua_get(types<Tu>(), L, index, tracking);
+ }
+ else {
+ unqualified_getter<Tu> g {};
+ return g.get(L, index, tracking);
+ }
+ }
+
+ template <typename T>
+ decltype(auto) unchecked_get(lua_State* L, int index, record& tracking) {
+ if constexpr (meta::meta_detail::is_adl_sol_lua_get_v<T>) {
+ return sol_lua_get(types<T>(), L, index, tracking);
+ }
+ else {
+ qualified_getter<T> g {};
+ return g.get(L, index, tracking);
+ }
+ }
+
+ template <typename T>
+ decltype(auto) unqualified_interop_get(lua_State* L, int index, void* unadjusted_pointer, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::meta_detail::is_adl_sol_lua_interop_get_v<Tu>) {
+ return sol_lua_interop_get(types<Tu>(), L, index, unadjusted_pointer, tracking);
+ }
+ else {
+ (void)L;
+ (void)index;
+ (void)unadjusted_pointer;
+ (void)tracking;
+ using Ti = stack_detail::strip_t<Tu>;
+ return std::pair<bool, Ti*> { false, nullptr };
+ }
+ }
+
+ template <typename T>
+ decltype(auto) interop_get(lua_State* L, int index, void* unadjusted_pointer, record& tracking) {
+ if constexpr (meta::meta_detail::is_adl_sol_lua_interop_get_v<T>) {
+ return sol_lua_interop_get(types<T>(), L, index, unadjusted_pointer, tracking);
+ }
+ else {
+ return unqualified_interop_get<T>(L, index, unadjusted_pointer, tracking);
+ }
+ }
+
+ template <typename T, typename Handler>
+ bool unqualified_interop_check(lua_State* L, int index, type index_type, Handler&& handler, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::meta_detail::is_adl_sol_lua_interop_check_v<Tu>) {
+ return sol_lua_interop_check(types<Tu>(), L, index, index_type, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ (void)L;
+ (void)index;
+ (void)index_type;
+ (void)handler;
+ (void)tracking;
+ return false;
+ }
+ }
+
+ template <typename T, typename Handler>
+ bool interop_check(lua_State* L, int index, type index_type, Handler&& handler, record& tracking) {
+ if constexpr (meta::meta_detail::is_adl_sol_lua_interop_check_v<T>) {
+ return sol_lua_interop_check(types<T>(), L, index, index_type, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ return unqualified_interop_check<T>(L, index, index_type, std::forward<Handler>(handler), tracking);
+ }
+ }
+
+ using undefined_method_func = void (*)(stack_reference);
+
+ struct undefined_metatable {
+ lua_State* L;
+ const char* key;
+ undefined_method_func on_new_table;
+
+ undefined_metatable(lua_State* l, const char* k, undefined_method_func umf) : L(l), key(k), on_new_table(umf) {
+ }
+
+ void operator()() const {
+ if (luaL_newmetatable(L, key) == 1) {
+ on_new_table(stack_reference(L, -1));
+ }
+ lua_setmetatable(L, -2);
+ }
+ };
+ } // namespace stack_detail
+
+ inline bool maybe_indexable(lua_State* L, int index = -1) {
+ type t = type_of(L, index);
+ return t == type::userdata || t == type::table;
+ }
+
+ inline int top(lua_State* L) {
+ return lua_gettop(L);
+ }
+
+ inline bool is_main_thread(lua_State* L) {
+ int ismainthread = lua_pushthread(L);
+ lua_pop(L, 1);
+ return ismainthread == 1;
+ }
+
+ inline void coroutine_create_guard(lua_State* L) {
+ if (is_main_thread(L)) {
+ return;
+ }
+ int stacksize = lua_gettop(L);
+ if (stacksize < 1) {
+ return;
+ }
+ if (type_of(L, 1) != type::function) {
+ return;
+ }
+ // well now we're screwed...
+ // we can clean the stack and pray it doesn't destroy anything?
+ lua_pop(L, stacksize);
+ }
+
+ inline void clear(lua_State* L, int table_index) {
+ lua_pushnil(L);
+ while (lua_next(L, table_index) != 0) {
+ // remove value
+ lua_pop(L, 1);
+ // duplicate key to protect form rawset
+ lua_pushvalue(L, -1);
+ // push new value
+ lua_pushnil(L);
+ // table_index%[key] = nil
+ lua_rawset(L, table_index);
+ }
+ }
+
+ inline void clear(reference& r) {
+ auto pp = push_pop<false>(r);
+ int stack_index = pp.index_of(r);
+ clear(r.lua_state(), stack_index);
+ }
+
+ inline void clear(stack_reference& r) {
+ clear(r.lua_state(), r.stack_index());
+ }
+
+ inline void clear(lua_State* L_, stateless_reference& r) {
+ r.push(L_);
+ int stack_index = absolute_index(L_, -1);
+ clear(L_, stack_index);
+ r.pop(L_);
+ }
+
+ inline void clear(lua_State* L_, stateless_stack_reference& r) {
+ clear(L_, r.stack_index());
+ }
+
+ template <typename T, typename... Args>
+ int push(lua_State* L, T&& t, Args&&... args) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::meta_detail::is_adl_sol_lua_push_exact_v<T, T, Args...>) {
+ return sol_lua_push(types<T>(), L, std::forward<T>(t), std::forward<Args>(args)...);
+ }
+ else if constexpr (meta::meta_detail::is_adl_sol_lua_push_exact_v<Tu, T, Args...>) {
+ return sol_lua_push(types<Tu>(), L, std::forward<T>(t), std::forward<Args>(args)...);
+ }
+ else if constexpr (meta::meta_detail::is_adl_sol_lua_push_v<T, Args...>) {
+ return sol_lua_push(L, std::forward<T>(t), std::forward<Args>(args)...);
+ }
+ else {
+ unqualified_pusher<Tu> p {};
+ return p.push(L, std::forward<T>(t), std::forward<Args>(args)...);
+ }
+ }
+
+ // overload allows to use a pusher of a specific type, but pass in any kind of args
+ template <typename T, typename Arg, typename... Args, typename = std::enable_if_t<!std::is_same<T, Arg>::value>>
+ int push(lua_State* L, Arg&& arg, Args&&... args) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::meta_detail::is_adl_sol_lua_push_exact_v<T, Arg, Args...>) {
+ return sol_lua_push(types<T>(), L, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+ else if constexpr (meta::meta_detail::is_adl_sol_lua_push_exact_v<Tu, Arg, Args...>) {
+ return sol_lua_push(types<Tu>(), L, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+ else if constexpr (meta::meta_detail::is_adl_sol_lua_push_v<Arg, Args...> && !detail::is_tagged_v<Tu>) {
+ return sol_lua_push(L, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+ else {
+ unqualified_pusher<Tu> p {};
+ return p.push(L, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+ }
+
+ template <typename T, typename... Args>
+ int push_userdata(lua_State* L, T&& t, Args&&... args) {
+ using U = meta::unqualified_t<T>;
+ using Tr = meta::conditional_t<std::is_pointer_v<U>,
+ detail::as_pointer_tag<std::remove_pointer_t<U>>,
+ meta::conditional_t<is_unique_usertype_v<U>, detail::as_unique_tag<U>, detail::as_value_tag<U>>>;
+ return stack::push<Tr>(L, std::forward<T>(t), std::forward<Args>(args)...);
+ }
+
+ template <typename T, typename Arg, typename... Args>
+ int push_userdata(lua_State* L, Arg&& arg, Args&&... args) {
+ using U = meta::unqualified_t<T>;
+ using Tr = meta::conditional_t<std::is_pointer_v<U>,
+ detail::as_pointer_tag<std::remove_pointer_t<U>>,
+ meta::conditional_t<is_unique_usertype_v<U>, detail::as_unique_tag<U>, detail::as_value_tag<U>>>;
+ return stack::push<Tr>(L, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+
+ namespace stack_detail {
+
+ template <typename T, typename Arg, typename... Args>
+ int push_reference(lua_State* L, Arg&& arg, Args&&... args) {
+ // clang-format off
+ using use_reference_tag =
+ meta::all<
+ meta::neg<is_value_semantic_for_function<T>>
+#if SOL_IS_OFF(SOL_FUNCTION_CALL_VALUE_SEMANTICS)
+ , std::is_lvalue_reference<T>,
+ meta::neg<std::is_const<std::remove_reference_t<T>>>,
+ meta::neg<is_lua_primitive<meta::unqualified_t<T>>>,
+ meta::neg<is_unique_usertype<meta::unqualified_t<T>>>
+#endif
+ >;
+ // clang-format on
+ using Tr = meta::conditional_t<use_reference_tag::value, detail::as_reference_tag, meta::unqualified_t<T>>;
+ return stack::push<Tr>(L, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+
+ } // namespace stack_detail
+
+ template <typename T, typename... Args>
+ int push_reference(lua_State* L, T&& t, Args&&... args) {
+ return stack_detail::push_reference<T>(L, std::forward<T>(t), std::forward<Args>(args)...);
+ }
+
+ template <typename T, typename Arg, typename... Args>
+ int push_reference(lua_State* L, Arg&& arg, Args&&... args) {
+ return stack_detail::push_reference<T>(L, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+
+ inline int multi_push(lua_State*) {
+ // do nothing
+ return 0;
+ }
+
+ template <typename T, typename... Args>
+ int multi_push(lua_State* L, T&& t, Args&&... args) {
+ int pushcount = push(L, std::forward<T>(t));
+ void(detail::swallow { (pushcount += stack::push(L, std::forward<Args>(args)), 0)... });
+ return pushcount;
+ }
+
+ inline int multi_push_reference(lua_State*) {
+ // do nothing
+ return 0;
+ }
+
+ template <typename T, typename... Args>
+ int multi_push_reference(lua_State* L, T&& t, Args&&... args) {
+ int pushcount = stack::push_reference(L, std::forward<T>(t));
+ void(detail::swallow { (pushcount += stack::push_reference(L, std::forward<Args>(args)), 0)... });
+ return pushcount;
+ }
+
+ template <typename T, typename Handler>
+ bool unqualified_check(lua_State* L, int index, Handler&& handler, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::meta_detail::is_adl_sol_lua_check_v<Tu>) {
+ return sol_lua_check(types<Tu>(), L, index, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ unqualified_checker<Tu, lua_type_of_v<Tu>> c{};
+ return c.check(L, index, std::forward<Handler>(handler), tracking);
+ }
+ }
+
+ template <typename T, typename Handler>
+ bool unqualified_check(lua_State* L, int index, Handler&& handler) {
+ record tracking {};
+ return unqualified_check<T>(L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename T>
+ bool unqualified_check(lua_State* L, int index = -lua_size<meta::unqualified_t<T>>::value) {
+ auto handler = &no_panic;
+ return unqualified_check<T>(L, index, handler);
+ }
+
+ template <typename T, typename Handler>
+ bool check(lua_State* L, int index, Handler&& handler, record& tracking) {
+ if constexpr (meta::meta_detail::is_adl_sol_lua_check_v<T>) {
+ return sol_lua_check(types<T>(), L, index, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ using Tu = meta::unqualified_t<T>;
+ qualified_checker<T, lua_type_of_v<Tu>> c{};
+ return c.check(L, index, std::forward<Handler>(handler), tracking);
+ }
+ }
+
+ template <typename T, typename Handler>
+ bool check(lua_State* L, int index, Handler&& handler) {
+ record tracking {};
+ return check<T>(L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename T>
+ bool check(lua_State* L, int index = -lua_size<meta::unqualified_t<T>>::value) {
+ auto handler = &no_panic;
+ return check<T>(L, index, handler);
+ }
+
+ template <typename T, typename Handler>
+ bool check_usertype(lua_State* L, int index, type, Handler&& handler, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+ using detail_t = meta::conditional_t<std::is_pointer_v<T>, detail::as_pointer_tag<Tu>, detail::as_value_tag<Tu>>;
+ return check<detail_t>(L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename T, typename Handler>
+ bool check_usertype(lua_State* L, int index, Handler&& handler, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+ using detail_t = meta::conditional_t<std::is_pointer_v<T>, detail::as_pointer_tag<Tu>, detail::as_value_tag<Tu>>;
+ return check<detail_t>(L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename T, typename Handler>
+ bool check_usertype(lua_State* L, int index, Handler&& handler) {
+ record tracking {};
+ return check_usertype<T>(L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename T>
+ bool check_usertype(lua_State* L, int index = -lua_size<meta::unqualified_t<T>>::value) {
+ auto handler = &no_panic;
+ return check_usertype<T>(L, index, handler);
+ }
+
+ template <typename T, typename Handler>
+ decltype(auto) unqualified_check_get(lua_State* L, int index, Handler&& handler, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::meta_detail::is_adl_sol_lua_check_get_v<T>) {
+ return sol_lua_check_get(types<T>(), L, index, std::forward<Handler>(handler), tracking);
+ }
+ else if constexpr (meta::meta_detail::is_adl_sol_lua_check_get_v<Tu>) {
+ return sol_lua_check_get(types<Tu>(), L, index, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ unqualified_check_getter<Tu> cg {};
+ return cg.get(L, index, std::forward<Handler>(handler), tracking);
+ }
+ }
+
+ template <typename T, typename Handler>
+ decltype(auto) unqualified_check_get(lua_State* L, int index, Handler&& handler) {
+ record tracking {};
+ return unqualified_check_get<T>(L, index, handler, tracking);
+ }
+
+ template <typename T>
+ decltype(auto) unqualified_check_get(lua_State* L, int index = -lua_size<meta::unqualified_t<T>>::value) {
+ auto handler = &no_panic;
+ return unqualified_check_get<T>(L, index, handler);
+ }
+
+ template <typename T, typename Handler>
+ decltype(auto) check_get(lua_State* L, int index, Handler&& handler, record& tracking) {
+ if constexpr (meta::meta_detail::is_adl_sol_lua_check_get_v<T>) {
+ return sol_lua_check_get(types<T>(), L, index, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ qualified_check_getter<T> cg {};
+ return cg.get(L, index, std::forward<Handler>(handler), tracking);
+ }
+ }
+
+ template <typename T, typename Handler>
+ decltype(auto) check_get(lua_State* L, int index, Handler&& handler) {
+ record tracking {};
+ return check_get<T>(L, index, handler, tracking);
+ }
+
+ template <typename T>
+ decltype(auto) check_get(lua_State* L, int index = -lua_size<meta::unqualified_t<T>>::value) {
+ auto handler = &no_panic;
+ return check_get<T>(L, index, handler);
+ }
+
+ namespace stack_detail {
+
+ template <typename Handler>
+ bool check_types(lua_State*, int, Handler&&, record&) {
+ return true;
+ }
+
+ template <typename T, typename... Args, typename Handler>
+ bool check_types(lua_State* L, int firstargument, Handler&& handler, record& tracking) {
+ if (!stack::check<T>(L, firstargument + tracking.used, handler, tracking))
+ return false;
+ return check_types<Args...>(L, firstargument, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename... Args, typename Handler>
+ bool check_types(types<Args...>, lua_State* L, int index, Handler&& handler, record& tracking) {
+ return check_types<Args...>(L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ } // namespace stack_detail
+
+ template <typename... Args, typename Handler>
+ bool multi_check(lua_State* L, int index, Handler&& handler, record& tracking) {
+ return stack_detail::check_types<Args...>(L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename... Args, typename Handler>
+ bool multi_check(lua_State* L, int index, Handler&& handler) {
+ record tracking {};
+ return multi_check<Args...>(L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename... Args>
+ bool multi_check(lua_State* L, int index) {
+ return multi_check<Args...>(L, index);
+ }
+
+ template <typename T>
+ auto unqualified_get(lua_State* L, int index, record& tracking) -> decltype(stack_detail::unchecked_unqualified_get<T>(L, index, tracking)) {
+#if SOL_IS_ON(SOL_SAFE_GETTER)
+ static constexpr bool is_op = meta::is_optional_v<T>;
+ if constexpr (is_op) {
+ return stack_detail::unchecked_unqualified_get<T>(L, index, tracking);
+ }
+ else {
+ if (is_lua_reference<T>::value) {
+ return stack_detail::unchecked_unqualified_get<T>(L, index, tracking);
+ }
+ auto op = unqualified_check_get<T>(L, index, type_panic_c_str, tracking);
+ return *std::move(op);
+ }
+#else
+ return stack_detail::unchecked_unqualified_get<T>(L, index, tracking);
+#endif
+ }
+
+ template <typename T>
+ decltype(auto) unqualified_get(lua_State* L, int index = -lua_size<meta::unqualified_t<T>>::value) {
+ record tracking {};
+ return unqualified_get<T>(L, index, tracking);
+ }
+
+ template <typename T>
+ auto get(lua_State* L, int index, record& tracking) -> decltype(stack_detail::unchecked_get<T>(L, index, tracking)) {
+#if SOL_IS_ON(SOL_SAFE_GETTER)
+ static constexpr bool is_op = meta::is_optional_v<T>;
+ if constexpr (is_op) {
+ return stack_detail::unchecked_get<T>(L, index, tracking);
+ }
+ else {
+ if (is_lua_reference<T>::value) {
+ return stack_detail::unchecked_get<T>(L, index, tracking);
+ }
+ auto op = check_get<T>(L, index, type_panic_c_str, tracking);
+ return *std::move(op);
+ }
+#else
+ return stack_detail::unchecked_get<T>(L, index, tracking);
+#endif
+ }
+
+ template <typename T>
+ decltype(auto) get(lua_State* L, int index = -lua_size<meta::unqualified_t<T>>::value) {
+ record tracking {};
+ return get<T>(L, index, tracking);
+ }
+
+ template <typename T>
+ decltype(auto) get_usertype(lua_State* L, int index, record& tracking) {
+ using UT = meta::conditional_t<std::is_pointer<T>::value, detail::as_pointer_tag<std::remove_pointer_t<T>>, detail::as_value_tag<T>>;
+ return get<UT>(L, index, tracking);
+ }
+
+ template <typename T>
+ decltype(auto) get_usertype(lua_State* L, int index = -lua_size_v<meta::unqualified_t<T>>) {
+ record tracking {};
+ return get_usertype<T>(L, index, tracking);
+ }
+
+ template <typename T>
+ decltype(auto) pop(lua_State* L) {
+ return popper<T> {}.pop(L);
+ }
+
+ template <bool global = false, bool raw = false, typename Key>
+ void get_field(lua_State* L, Key&& key) {
+ field_getter<meta::unqualified_t<Key>, global, raw> {}.get(L, std::forward<Key>(key));
+ }
+
+ template <bool global = false, bool raw = false, typename Key>
+ void get_field(lua_State* L, Key&& key, int tableindex) {
+ field_getter<meta::unqualified_t<Key>, global, raw> {}.get(L, std::forward<Key>(key), tableindex);
+ }
+
+ template <bool global = false, typename Key>
+ void raw_get_field(lua_State* L, Key&& key) {
+ get_field<global, true>(L, std::forward<Key>(key));
+ }
+
+ template <bool global = false, typename Key>
+ void raw_get_field(lua_State* L, Key&& key, int tableindex) {
+ get_field<global, true>(L, std::forward<Key>(key), tableindex);
+ }
+
+ template <bool global = false, bool raw = false, typename C = detail::non_lua_nil_t, typename Key>
+ probe probe_get_field(lua_State* L, Key&& key) {
+ return probe_field_getter<meta::unqualified_t<Key>, C, global, raw> {}.get(L, std::forward<Key>(key));
+ }
+
+ template <bool global = false, bool raw = false, typename C = detail::non_lua_nil_t, typename Key>
+ probe probe_get_field(lua_State* L, Key&& key, int tableindex) {
+ return probe_field_getter<meta::unqualified_t<Key>, C, global, raw> {}.get(L, std::forward<Key>(key), tableindex);
+ }
+
+ template <bool global = false, typename C = detail::non_lua_nil_t, typename Key>
+ probe probe_raw_get_field(lua_State* L, Key&& key) {
+ return probe_get_field<global, true, C>(L, std::forward<Key>(key));
+ }
+
+ template <bool global = false, typename C = detail::non_lua_nil_t, typename Key>
+ probe probe_raw_get_field(lua_State* L, Key&& key, int tableindex) {
+ return probe_get_field<global, true, C>(L, std::forward<Key>(key), tableindex);
+ }
+
+ template <bool global = false, bool raw = false, typename Key, typename Value>
+ void set_field(lua_State* L, Key&& key, Value&& value) {
+ field_setter<meta::unqualified_t<Key>, global, raw> {}.set(L, std::forward<Key>(key), std::forward<Value>(value));
+ }
+
+ template <bool global = false, bool raw = false, typename Key, typename Value>
+ void set_field(lua_State* L, Key&& key, Value&& value, int tableindex) {
+ field_setter<meta::unqualified_t<Key>, global, raw> {}.set(L, std::forward<Key>(key), std::forward<Value>(value), tableindex);
+ }
+
+ template <bool global = false, typename Key, typename Value>
+ void raw_set_field(lua_State* L, Key&& key, Value&& value) {
+ set_field<global, true>(L, std::forward<Key>(key), std::forward<Value>(value));
+ }
+
+ template <bool global = false, typename Key, typename Value>
+ void raw_set_field(lua_State* L, Key&& key, Value&& value, int tableindex) {
+ set_field<global, true>(L, std::forward<Key>(key), std::forward<Value>(value), tableindex);
+ }
+
+ template <typename T, typename F>
+ void modify_unique_usertype_as(const stack_reference& obj, F&& f) {
+ void* raw = lua_touserdata(obj.lua_state(), obj.stack_index());
+ void* ptr_memory = detail::align_usertype_pointer(raw);
+ void* uu_memory = detail::align_usertype_unique<T>(raw);
+ T& uu = *static_cast<T*>(uu_memory);
+ f(uu);
+ *static_cast<void**>(ptr_memory) = static_cast<void*>(detail::unique_get(obj.lua_state(), uu));
+ }
+
+ template <typename F>
+ void modify_unique_usertype(const stack_reference& obj, F&& f) {
+ using bt = meta::bind_traits<meta::unqualified_t<F>>;
+ using T = typename bt::template arg_at<0>;
+ using Tu = meta::unqualified_t<T>;
+ modify_unique_usertype_as<Tu>(obj, std::forward<F>(f));
+ }
+
+ namespace stack_detail {
+ template <typename T, typename Handler>
+ decltype(auto) check_get_arg(lua_State* L_, int index_, Handler&& handler_, record& tracking_) {
+ if constexpr (meta::meta_detail::is_adl_sol_lua_check_access_v<T>) {
+ sol_lua_check_access(types<meta::unqualified_t<T>>(), L_, index_, tracking_);
+ }
+ return check_get<T>(L_, index_, std::forward<Handler>(handler_), tracking_);
+ }
+
+ template <typename T>
+ decltype(auto) unchecked_get_arg(lua_State* L_, int index_, record& tracking_) {
+ if constexpr (meta::meta_detail::is_adl_sol_lua_check_access_v<T>) {
+ sol_lua_check_access(types<meta::unqualified_t<T>>(), L_, index_, tracking_);
+ }
+ return unchecked_get<T>(L_, index_, tracking_);
+ }
+ } // namespace stack_detail
+
+ } // namespace stack
+
+ namespace detail {
+
+ template <typename T>
+ lua_CFunction make_destructor(std::true_type) {
+ if constexpr (is_unique_usertype_v<T>) {
+ return &unique_destroy<T>;
+ }
+ else if constexpr (!std::is_pointer_v<T>) {
+ return &usertype_alloc_destroy<T>;
+ }
+ else {
+ return &cannot_destroy<T>;
+ }
+ }
+
+ template <typename T>
+ lua_CFunction make_destructor(std::false_type) {
+ return &cannot_destroy<T>;
+ }
+
+ template <typename T>
+ lua_CFunction make_destructor() {
+ return make_destructor<T>(std::is_destructible<T>());
+ }
+
+ struct no_comp {
+ template <typename A, typename B>
+ bool operator()(A&&, B&&) const {
+ return false;
+ }
+ };
+
+ template <typename T>
+ int is_check(lua_State* L) {
+ return stack::push(L, stack::check<T>(L, 1, &no_panic));
+ }
+
+ template <typename T>
+ int member_default_to_string(std::true_type, lua_State* L) {
+ decltype(auto) ts = stack::get<T>(L, 1).to_string();
+ return stack::push(L, std::forward<decltype(ts)>(ts));
+ }
+
+ template <typename T>
+ int member_default_to_string(std::false_type, lua_State* L) {
+ return luaL_error(L,
+ "cannot perform to_string on '%s': no 'to_string' overload in namespace, 'to_string' member "
+ "function, or operator<<(ostream&, ...) present",
+ detail::demangle<T>().data());
+ }
+
+ template <typename T>
+ int adl_default_to_string(std::true_type, lua_State* L) {
+ using namespace std;
+ decltype(auto) ts = to_string(stack::get<T>(L, 1));
+ return stack::push(L, std::forward<decltype(ts)>(ts));
+ }
+
+ template <typename T>
+ int adl_default_to_string(std::false_type, lua_State* L) {
+ return member_default_to_string<T>(meta::supports_to_string_member<T>(), L);
+ }
+
+ template <typename T>
+ int oss_default_to_string(std::true_type, lua_State* L) {
+ std::ostringstream oss;
+ oss << stack::unqualified_get<T>(L, 1);
+ return stack::push(L, oss.str());
+ }
+
+ template <typename T>
+ int oss_default_to_string(std::false_type, lua_State* L) {
+ return adl_default_to_string<T>(meta::supports_adl_to_string<T>(), L);
+ }
+
+ template <typename T>
+ int default_to_string(lua_State* L) {
+ return oss_default_to_string<T>(meta::supports_op_left_shift<std::ostream, T>(), L);
+ }
+
+ template <typename T>
+ int default_size(lua_State* L) {
+ decltype(auto) self = stack::unqualified_get<T>(L, 1);
+ return stack::push(L, self.size());
+ }
+
+ template <typename T, typename Op>
+ int comparsion_operator_wrap(lua_State* L) {
+ if constexpr (std::is_void_v<T>) {
+ return stack::push(L, false);
+ }
+ else {
+ auto maybel = stack::unqualified_check_get<T>(L, 1);
+ if (!maybel) {
+ return stack::push(L, false);
+ }
+ auto mayber = stack::unqualified_check_get<T>(L, 2);
+ if (!mayber) {
+ return stack::push(L, false);
+ }
+ decltype(auto) l = *maybel;
+ decltype(auto) r = *mayber;
+ if constexpr (std::is_same_v<no_comp, Op>) {
+ std::equal_to<> op;
+ return stack::push(L, op(detail::ptr(l), detail::ptr(r)));
+ }
+ else {
+ if constexpr (std::is_same_v<std::equal_to<>, Op> // clang-format hack
+ || std::is_same_v<std::less_equal<>, Op> //
+ || std::is_same_v<std::less_equal<>, Op>) { //
+ if (detail::ptr(l) == detail::ptr(r)) {
+ return stack::push(L, true);
+ }
+ }
+ Op op;
+ return stack::push(L, op(detail::deref(l), detail::deref(r)));
+ }
+ }
+ }
+
+ template <typename T, typename IFx, typename Fx>
+ void insert_default_registrations(IFx&& ifx, Fx&& fx);
+
+ template <typename T, bool, bool>
+ struct get_is_primitive : is_lua_primitive<T> { };
+
+ template <typename T>
+ struct get_is_primitive<T, true, false>
+ : meta::neg<std::is_reference<decltype(sol_lua_get(types<T>(), nullptr, -1, std::declval<stack::record&>()))>> { };
+
+ template <typename T>
+ struct get_is_primitive<T, false, true>
+ : meta::neg<std::is_reference<decltype(sol_lua_get(types<meta::unqualified_t<T>>(), nullptr, -1, std::declval<stack::record&>()))>> { };
+
+ template <typename T>
+ struct get_is_primitive<T, true, true> : get_is_primitive<T, true, false> { };
+
+ } // namespace detail
+
+ template <typename T>
+ struct is_proxy_primitive
+ : detail::get_is_primitive<T, meta::meta_detail::is_adl_sol_lua_get_v<T>, meta::meta_detail::is_adl_sol_lua_get_v<meta::unqualified_t<T>>> { };
+
+} // namespace sol
+
+// end of sol/stack_core.hpp
+
+// beginning of sol/stack_check.hpp
+
+// beginning of sol/stack_check_unqualified.hpp
+
+#include <memory>
+#include <functional>
+#include <utility>
+#include <cmath>
+#include <optional>
+#if SOL_IS_ON(SOL_STD_VARIANT)
+#include <variant>
+#endif // variant shenanigans
+
+namespace sol { namespace stack {
+ template <typename Handler>
+ bool loose_table_check(lua_State* L_, int index, Handler&& handler, record& tracking) {
+ tracking.use(1);
+ type t = type_of(L_, index);
+ if (t == type::table) {
+ return true;
+ }
+ if (t != type::userdata) {
+ handler(L_, index, type::table, t, "value is not a table or a userdata that can behave like one");
+ return false;
+ }
+ return true;
+ }
+
+ namespace stack_detail {
+ inline bool impl_check_metatable(lua_State* L_, int index, const std::string& metakey, bool poptable) {
+ luaL_getmetatable(L_, &metakey[0]);
+ const type expectedmetatabletype = static_cast<type>(lua_type(L_, -1));
+ if (expectedmetatabletype != type::lua_nil) {
+ if (lua_rawequal(L_, -1, index) == 1) {
+ lua_pop(L_, 1 + static_cast<int>(poptable));
+ return true;
+ }
+ }
+ lua_pop(L_, 1);
+ return false;
+ }
+
+ template <typename T, bool poptable = true>
+ inline bool check_metatable(lua_State* L_, int index = -2) {
+ return impl_check_metatable(L_, index, usertype_traits<T>::metatable(), poptable);
+ }
+
+ template <type expected, int (*check_func)(lua_State*, int)>
+ struct basic_check {
+ template <typename Handler>
+ static bool check(lua_State* L_, int index, Handler&& handler, record& tracking) {
+ tracking.use(1);
+ bool success = check_func(L_, index) == 1;
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, expected, type_of(L_, index), "");
+ }
+ return success;
+ }
+ };
+ } // namespace stack_detail
+
+ template <typename T, typename>
+ struct unqualified_interop_checker {
+ template <typename Handler>
+ static bool check(lua_State*, int, type, Handler&&, record&) {
+ return false;
+ }
+ };
+
+ template <typename T, typename>
+ struct qualified_interop_checker {
+ template <typename Handler>
+ static bool check(lua_State* L_, int index, type index_type, Handler&& handler, record& tracking) {
+ return stack_detail::unqualified_interop_check<T>(L_, index, index_type, std::forward<Handler>(handler), tracking);
+ }
+ };
+
+ template <typename T, type expected, typename>
+ struct unqualified_checker {
+ template <typename Handler>
+ static bool check(lua_State* L_, int index, Handler&& handler, record& tracking) {
+ if constexpr (std::is_same_v<T, bool>) {
+ tracking.use(1);
+ bool success = lua_isboolean(L_, index) == 1;
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, expected, type_of(L_, index), "");
+ }
+ return success;
+ }
+ else if constexpr (meta::any_same_v<T,
+ char
+#if SOL_IS_ON(SOL_CHAR8_T)
+ ,
+ char8_t
+#endif
+ ,
+ char16_t,
+ char32_t>) {
+ return stack::check<std::basic_string<T>>(L_, index, std::forward<Handler>(handler), tracking);
+ }
+ else if constexpr (std::is_integral_v<T> || std::is_same_v<T, lua_Integer>) {
+ tracking.use(1);
+#if SOL_LUA_VERSION_I_ >= 503
+ // Lua 5.3 and greater checks for numeric precision
+#if SOL_IS_ON(SOL_STRINGS_ARE_NUMBERS)
+ // imprecise, sloppy conversions
+ int isnum = 0;
+ lua_tointegerx(L_, index, &isnum);
+ const bool success = isnum != 0;
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, type::number, type_of(L_, index), detail::not_a_number_or_number_string_integral);
+ }
+#elif SOL_IS_ON(SOL_NUMBER_PRECISION_CHECKS)
+ // this check is precise, do not convert
+ if (lua_isinteger(L_, index) == 1) {
+ return true;
+ }
+ const bool success = false;
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, type::number, type_of(L_, index), detail::not_a_number_integral);
+ }
+#else
+ // Numerics are neither safe nor string-convertible
+ type t = type_of(L_, index);
+ const bool success = t == type::number;
+#endif
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, type::number, type_of(L_, index), detail::not_a_number);
+ }
+ return success;
+#else
+ // Lua 5.2 and below checks
+#if SOL_IS_OFF(SOL_STRINGS_ARE_NUMBERS)
+ // must pre-check, because it will convert
+ type t = type_of(L_, index);
+ if (t != type::number) {
+ // expected type, actual type
+ handler(L_, index, type::number, t, detail::not_a_number);
+ return false;
+ }
+#endif // Do not allow strings to be numbers
+
+#if SOL_IS_ON(SOL_NUMBER_PRECISION_CHECKS)
+ int isnum = 0;
+ const lua_Number v = lua_tonumberx(L_, index, &isnum);
+ const bool success = isnum != 0 && static_cast<lua_Number>(llround(v)) == v;
+#else
+ const bool success = true;
+#endif // Safe numerics and number precision checking
+ if (!success) {
+ // Use defines to provide a better error message!
+#if SOL_IS_ON(SOL_STRINGS_ARE_NUMBERS)
+ handler(L_, index, type::number, type_of(L_, index), detail::not_a_number_or_number_string);
+#elif SOL_IS_ON(SOL_NUMBER_PRECISION_CHECKS)
+ handler(L_, index, type::number, t, detail::not_a_number_or_number_string);
+#else
+ handler(L_, index, type::number, t, detail::not_a_number);
+#endif
+ }
+ return success;
+#endif
+ }
+ else if constexpr (std::is_floating_point_v<T> || std::is_same_v<T, lua_Number>) {
+ tracking.use(1);
+#if SOL_IS_ON(SOL_STRINGS_ARE_NUMBERS)
+ bool success = lua_isnumber(L_, index) == 1;
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, type::number, type_of(L_, index), detail::not_a_number_or_number_string);
+ }
+ return success;
+#else
+ type t = type_of(L_, index);
+ bool success = t == type::number;
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, type::number, t, detail::not_a_number);
+ }
+ return success;
+#endif // Strings are Numbers
+ }
+ else if constexpr (meta::any_same_v<T, type, this_state, this_main_state, this_environment, variadic_args>) {
+ (void)L_;
+ (void)index;
+ (void)handler;
+ tracking.use(0);
+ return true;
+ }
+ else if constexpr (is_unique_usertype_v<T>) {
+ using element = unique_usertype_element_t<T>;
+ using actual = unique_usertype_actual_t<T>;
+ const type indextype = type_of(L_, index);
+ tracking.use(1);
+ if (indextype != type::userdata) {
+ handler(L_, index, type::userdata, indextype, "value is not a userdata");
+ return false;
+ }
+ if (lua_getmetatable(L_, index) == 0) {
+ return true;
+ }
+ int metatableindex = lua_gettop(L_);
+ if (stack_detail::check_metatable<d::u<element>>(L_, metatableindex)) {
+ void* memory = lua_touserdata(L_, index);
+ memory = detail::align_usertype_unique_destructor(memory);
+ detail::unique_destructor& pdx = *static_cast<detail::unique_destructor*>(memory);
+ bool success = &detail::usertype_unique_alloc_destroy<element, actual> == pdx;
+ if (!success) {
+ memory = detail::align_usertype_unique_tag<true>(memory);
+#if 0
+ // New version, one day
+#else
+ const char*& name_tag = *static_cast<const char**>(memory);
+ success = usertype_traits<T>::qualified_name() == name_tag;
+#endif
+ if (!success) {
+ handler(L_, index, type::userdata, indextype, "value is a userdata but is not the correct unique usertype");
+ }
+ }
+ return success;
+ }
+ lua_pop(L_, 1);
+ handler(L_, index, type::userdata, indextype, "unrecognized userdata (not pushed by sol?)");
+ return false;
+ }
+ else if constexpr (meta::any_same_v<T, lua_nil_t, std::nullopt_t, nullopt_t>) {
+ bool success = lua_isnil(L_, index);
+ if (success) {
+ tracking.use(1);
+ return success;
+ }
+ tracking.use(0);
+ success = lua_isnone(L_, index);
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, expected, type_of(L_, index), "");
+ }
+ return success;
+ }
+ else if constexpr (std::is_same_v<T, env_key_t>) {
+ tracking.use(1);
+ type t = type_of(L_, index);
+ if (t == type::table || t == type::none || t == type::lua_nil || t == type::userdata) {
+ return true;
+ }
+ handler(L_, index, type::table, t, "value cannot not have a valid environment");
+ return true;
+ }
+ else if constexpr (std::is_same_v<T, detail::non_lua_nil_t>) {
+ return !stack::unqualified_check<lua_nil_t>(L_, index, std::forward<Handler>(handler), tracking);
+ }
+ else if constexpr (meta::is_specialization_of_v<T, basic_lua_table>) {
+ tracking.use(1);
+ type t = type_of(L_, index);
+ if (t != type::table) {
+ handler(L_, index, type::table, t, "value is not a table");
+ return false;
+ }
+ return true;
+ }
+ else if constexpr (meta::is_specialization_of_v<T, basic_bytecode>) {
+ tracking.use(1);
+ type t = type_of(L_, index);
+ if (t != type::function) {
+ handler(L_, index, type::function, t, "value is not a function that can be dumped");
+ return false;
+ }
+ return true;
+ }
+ else if constexpr (meta::is_specialization_of_v<T, basic_environment>) {
+ tracking.use(1);
+ if (lua_getmetatable(L_, index) == 0) {
+ return true;
+ }
+ type t = type_of(L_, -1);
+ if (t == type::table || t == type::none || t == type::lua_nil) {
+ lua_pop(L_, 1);
+ return true;
+ }
+ if (t != type::userdata) {
+ lua_pop(L_, 1);
+ handler(L_, index, type::table, t, "value does not have a valid metatable");
+ return false;
+ }
+ return true;
+ }
+ else if constexpr (std::is_same_v<T, metatable_key_t>) {
+ tracking.use(1);
+ if (lua_getmetatable(L_, index) == 0) {
+ return true;
+ }
+ type t = type_of(L_, -1);
+ if (t == type::table || t == type::none || t == type::lua_nil) {
+ lua_pop(L_, 1);
+ return true;
+ }
+ if (t != type::userdata) {
+ lua_pop(L_, 1);
+ handler(L_, index, expected, t, "value does not have a valid metatable");
+ return false;
+ }
+ return true;
+ }
+ else if constexpr (std::is_same_v<T, luaL_Stream*> || std::is_same_v<T, luaL_Stream>) {
+ if (lua_getmetatable(L_, index) == 0) {
+ type t = type_of(L_, index);
+ handler(L_, index, expected, t, "value is not a valid luaL_Stream (has no metatable/is not a valid value)");
+ return false;
+ }
+ luaL_getmetatable(L_, LUA_FILEHANDLE);
+ if (type_of(L_, index) != type::table) {
+ type t = type_of(L_, index);
+ lua_pop(L_, 1);
+ handler(L_,
+ index,
+ expected,
+ t,
+ "value is not a valid luaL_Stream (there is no metatable for luaL_Stream -- did you forget to "
+ "my_lua_state.open_libraries(sol::lib::state) or equivalent?)");
+ return false;
+ }
+ int is_stream_table = lua_compare(L_, -1, -2, LUA_OPEQ);
+ lua_pop(L_, 2);
+ if (is_stream_table == 0) {
+ type t = type_of(L_, index);
+ handler(L_, index, expected, t, "value is not a valid luaL_Stream (incorrect metatable)");
+ return false;
+ }
+ return true;
+ }
+ else if constexpr (meta::is_optional_v<T>) {
+ using ValueType = typename T::value_type;
+ (void)handler;
+ type t = type_of(L_, index);
+ if (t == type::none) {
+ tracking.use(0);
+ return true;
+ }
+ if (t == type::lua_nil) {
+ tracking.use(1);
+ return true;
+ }
+ return stack::unqualified_check<ValueType>(L_, index, &no_panic, tracking);
+ }
+#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ else if constexpr (std::is_function_v<T> || (std::is_pointer_v<T> && std::is_function_v<std::remove_pointer_t<T>>)) {
+ return stack_detail::check_function_pointer<std::remove_pointer_t<T>>(L_, index, std::forward<Handler>(handler), tracking);
+ }
+#endif
+ else if constexpr (expected == type::userdata) {
+ if constexpr (meta::any_same_v<T, userdata_value> || meta::is_specialization_of_v<T, basic_userdata>) {
+ tracking.use(1);
+ type t = type_of(L_, index);
+ bool success = t == type::userdata;
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, type::userdata, t, "");
+ }
+ return success;
+ }
+ else if constexpr (meta::is_specialization_of_v<T, user>) {
+ unqualified_checker<lightuserdata_value, type::userdata> c;
+ (void)c;
+ return c.check(L_, index, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ if constexpr (std::is_pointer_v<T>) {
+ return check_usertype<T>(L_, index, std::forward<Handler>(handler), tracking);
+ }
+ else if constexpr (meta::is_specialization_of_v<T, std::reference_wrapper>) {
+ using T_internal = typename T::type;
+ return stack::check<T_internal>(L_, index, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ return check_usertype<T>(L_, index, std::forward<Handler>(handler), tracking);
+ }
+ }
+ }
+ else if constexpr (expected == type::poly) {
+ tracking.use(1);
+ bool success = is_lua_reference_v<T> || !lua_isnone(L_, index);
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, type::poly, type_of(L_, index), "");
+ }
+ return success;
+ }
+ else if constexpr (expected == type::lightuserdata) {
+ tracking.use(1);
+ type t = type_of(L_, index);
+ bool success = t == type::userdata || t == type::lightuserdata;
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, type::lightuserdata, t, "");
+ }
+ return success;
+ }
+ else if constexpr (expected == type::function) {
+ if constexpr (meta::any_same_v<T, lua_CFunction, std::remove_pointer_t<lua_CFunction>, c_closure>) {
+ tracking.use(1);
+ bool success = lua_iscfunction(L_, index) == 1;
+ if (!success) {
+ // expected type, actual type
+ handler(L_, index, expected, type_of(L_, index), "");
+ }
+ return success;
+ }
+ else {
+ tracking.use(1);
+ type t = type_of(L_, index);
+ if (t == type::lua_nil || t == type::none || t == type::function) {
+ // allow for lua_nil to be returned
+ return true;
+ }
+ if (t != type::userdata && t != type::table) {
+ handler(L_, index, type::function, t, "must be a function or table or a userdata");
+ return false;
+ }
+ // Do advanced check for call-style userdata?
+ static const auto& callkey = to_string(meta_function::call);
+ if (lua_getmetatable(L_, index) == 0) {
+ // No metatable, no __call key possible
+ handler(L_, index, type::function, t, "value is not a function and does not have overriden metatable");
+ return false;
+ }
+ if (lua_isnoneornil(L_, -1)) {
+ lua_pop(L_, 1);
+ handler(L_, index, type::function, t, "value is not a function and does not have valid metatable");
+ return false;
+ }
+ lua_getfield(L_, -1, &callkey[0]);
+ if (lua_isnoneornil(L_, -1)) {
+ lua_pop(L_, 2);
+ handler(L_, index, type::function, t, "value's metatable does not have __call overridden in metatable, cannot call this type");
+ return false;
+ }
+ // has call, is definitely a function
+ lua_pop(L_, 2);
+ return true;
+ }
+ }
+ else if constexpr (expected == type::table) {
+ return stack::loose_table_check(L_, index, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ tracking.use(1);
+ const type indextype = type_of(L_, index);
+ bool success = expected == indextype;
+ if (!success) {
+ // expected type, actual type, message
+ handler(L_, index, expected, indextype, "");
+ }
+ return success;
+ }
+ }
+ };
+
+ template <typename T>
+ struct unqualified_checker<non_null<T>, type::userdata> : unqualified_checker<T, lua_type_of_v<T>> { };
+
+ template <typename T>
+ struct unqualified_checker<detail::as_value_tag<T>, type::userdata> {
+ template <typename Handler>
+ static bool check(lua_State* L_, int index, Handler&& handler, record& tracking) {
+ const type indextype = type_of(L_, index);
+ return check(types<T>(), L_, index, indextype, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename U, typename Handler>
+ static bool check(types<U>, lua_State* L_, int index, type indextype, Handler&& handler, record& tracking) {
+ if constexpr (
+ std::is_same_v<T,
+ lightuserdata_value> || std::is_same_v<T, userdata_value> || std::is_same_v<T, userdata> || std::is_same_v<T, lightuserdata>) {
+ tracking.use(1);
+ if (indextype != type::userdata) {
+ handler(L_, index, type::userdata, indextype, "value is not a valid userdata");
+ return false;
+ }
+ return true;
+ }
+ else {
+#if SOL_IS_ON(SOL_USE_INTEROP)
+ if (stack_detail::interop_check<U>(L_, index, indextype, handler, tracking)) {
+ return true;
+ }
+#endif // interop extensibility
+ tracking.use(1);
+#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ if (lua_iscfunction(L_, index) != 0) {
+ // a potential match...
+ return true;
+ }
+#endif
+ if (indextype != type::userdata) {
+ handler(L_, index, type::userdata, indextype, "value is not a valid userdata");
+ return false;
+ }
+ if (lua_getmetatable(L_, index) == 0) {
+ return true;
+ }
+ int metatableindex = lua_gettop(L_);
+ if (stack_detail::check_metatable<U>(L_, metatableindex))
+ return true;
+ if (stack_detail::check_metatable<U*>(L_, metatableindex))
+ return true;
+ if (stack_detail::check_metatable<d::u<U>>(L_, metatableindex))
+ return true;
+ if (stack_detail::check_metatable<as_container_t<U>>(L_, metatableindex))
+ return true;
+ bool success = false;
+ bool has_derived = derive<T>::value || weak_derive<T>::value;
+ if (has_derived) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L_, 1, detail::not_enough_stack_space_string);
+#endif // make sure stack doesn't overflow
+ auto pn = stack::pop_n(L_, 1);
+ lua_pushstring(L_, &detail::base_class_check_key()[0]);
+ lua_rawget(L_, metatableindex);
+ if (type_of(L_, -1) != type::lua_nil) {
+ void* basecastdata = lua_touserdata(L_, -1);
+ detail::inheritance_check_function ic = reinterpret_cast<detail::inheritance_check_function>(basecastdata);
+ success = ic(usertype_traits<T>::qualified_name());
+ }
+ }
+ lua_pop(L_, 1);
+ if (!success) {
+ handler(L_, index, type::userdata, indextype, "value at this index does not properly reflect the desired type");
+ return false;
+ }
+ return true;
+ }
+ }
+ };
+
+ template <typename T>
+ struct unqualified_checker<detail::as_pointer_tag<T>, type::userdata> {
+ template <typename Handler>
+ static bool check(lua_State* L_, int index, type indextype, Handler&& handler, record& tracking) {
+ if (indextype == type::lua_nil) {
+ tracking.use(1);
+ return true;
+ }
+ return check_usertype<std::remove_pointer_t<T>>(L_, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename Handler>
+ static bool check(lua_State* L_, int index, Handler&& handler, record& tracking) {
+ const type indextype = type_of(L_, index);
+ return check(L_, index, indextype, std::forward<Handler>(handler), tracking);
+ }
+ };
+
+ template <typename T, std::size_t N, type expect>
+ struct unqualified_checker<exhaustive_until<T, N>, expect> {
+ template <typename K, typename V, typename Handler>
+ static bool check_two(types<K, V>, lua_State* arg_L, int relindex, type, Handler&& handler, record& tracking) {
+ tracking.use(1);
+
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(arg_L, 3, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+
+ int index = lua_absindex(arg_L, relindex);
+ lua_pushnil(arg_L);
+ while (lua_next(arg_L, index) != 0) {
+ const bool is_key_okay = stack::check<K>(arg_L, -2, std::forward<Handler>(handler), tracking);
+ if (!is_key_okay) {
+ lua_pop(arg_L, 2);
+ return false;
+ }
+ const bool is_value_okay = stack::check<V>(arg_L, -1, std::forward<Handler>(handler), tracking);
+ if (!is_value_okay) {
+ lua_pop(arg_L, 2);
+ return false;
+ }
+ lua_pop(arg_L, 1);
+ }
+ return true;
+ }
+
+ template <typename V, typename Handler>
+ static bool check_one(types<V>, lua_State* arg_L, int relindex, type, Handler&& handler, record& tracking) {
+ tracking.use(1);
+
+ size_t index = lua_absindex(arg_L, relindex);
+ // Zzzz slower but necessary thanks to the lower version API and missing functions qq
+ std::size_t idx = 0;
+ int vi = 0;
+ for (lua_Integer i = 0;; (void)(i += lua_size<V>::value), lua_pop(arg_L, static_cast<int>(vi))) {
+ vi = 0;
+ if (idx >= N) {
+ return true;
+ }
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(arg_L, 2, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ bool isnil = false;
+ for (; vi < static_cast<int>(lua_size<V>::value); ++vi) {
+ lua_pushinteger(arg_L, i);
+ lua_gettable(arg_L, static_cast<int>(index));
+ type vt = type_of(arg_L, -1);
+ isnil = vt == type::lua_nil;
+ if (isnil) {
+ if (i == 0) {
+ vi += 1;
+ goto loop_continue;
+ }
+ lua_pop(arg_L, static_cast<int>(vi + 1));
+ return true;
+ }
+ }
+ if (!stack::check<V>(arg_L, -lua_size<V>::value, std::forward<Handler>(handler), tracking)) {
+ lua_pop(arg_L, lua_size<V>::value);
+ return false;
+ }
+ ++idx;
+ loop_continue:;
+ }
+ }
+
+ template <typename Handler>
+ static bool check(lua_State* arg_L, int index, Handler&& handler, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (is_container_v<Tu>) {
+ if constexpr (meta::is_associative<Tu>::value) {
+ typedef typename Tu::value_type P;
+ typedef typename P::first_type K;
+ typedef typename P::second_type V;
+ return check_two(types<K, V>(), arg_L, index, expect, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ typedef typename Tu::value_type V;
+ return check_one(types<V>(), arg_L, index, expect, std::forward<Handler>(handler), tracking);
+ }
+ }
+ else {
+ unqualified_checker<Tu, expect> c {};
+ return c.check(arg_L, index, std::forward<Handler>(handler), tracking);
+ }
+ }
+ };
+
+ template <typename T, type expect>
+ struct unqualified_checker<non_exhaustive<T>, expect> {
+ template <typename Handler>
+ static bool check(lua_State* arg_L, int index, Handler&& handler, record& tracking) {
+ return stack::check<T>(arg_L, index, std::forward<Handler>(handler), tracking);
+ }
+ };
+
+ template <typename... Args>
+ struct unqualified_checker<std::tuple<Args...>, type::poly> {
+ template <typename Handler>
+ static bool check(lua_State* L_, int index, Handler&& handler, record& tracking) {
+ return stack::multi_check<Args...>(L_, index, std::forward<Handler>(handler), tracking);
+ }
+ };
+
+ template <typename A, typename B>
+ struct unqualified_checker<std::pair<A, B>, type::poly> {
+ template <typename Handler>
+ static bool check(lua_State* L_, int index, Handler&& handler, record& tracking) {
+ return stack::multi_check<A, B>(L_, index, std::forward<Handler>(handler), tracking);
+ }
+ };
+
+#if SOL_IS_ON(SOL_STD_VARIANT)
+
+ template <typename... Tn>
+ struct unqualified_checker<std::variant<Tn...>, type::poly> {
+ typedef std::variant<Tn...> V;
+ typedef std::variant_size<V> V_size;
+ typedef std::integral_constant<bool, V_size::value == 0> V_is_empty;
+
+ template <typename Handler>
+ static bool is_one(std::integral_constant<std::size_t, 0>, lua_State* L_, int index, Handler&& handler, record& tracking) {
+ if constexpr (V_is_empty::value) {
+ if (lua_isnone(L_, index)) {
+ return true;
+ }
+ }
+ tracking.use(1);
+ handler(L_, index, type::poly, type_of(L_, index), "value does not fit any type present in the variant");
+ return false;
+ }
+
+ template <std::size_t I, typename Handler>
+ static bool is_one(std::integral_constant<std::size_t, I>, lua_State* L_, int index, Handler&& handler, record& tracking) {
+ typedef std::variant_alternative_t<I - 1, V> T;
+ record temp_tracking = tracking;
+ if (stack::check<T>(L_, index, &no_panic, temp_tracking)) {
+ tracking = temp_tracking;
+ return true;
+ }
+ return is_one(std::integral_constant<std::size_t, I - 1>(), L_, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename Handler>
+ static bool check(lua_State* L_, int index, Handler&& handler, record& tracking) {
+ return is_one(std::integral_constant<std::size_t, V_size::value>(), L_, index, std::forward<Handler>(handler), tracking);
+ }
+ };
+
+#endif // variant shenanigans
+
+}} // namespace sol::stack
+
+// end of sol/stack_check_unqualified.hpp
+
+// beginning of sol/stack_check_qualified.hpp
+
+namespace sol { namespace stack {
+
+ template <typename X, type expected, typename>
+ struct qualified_checker {
+ template <typename Handler>
+ static bool check(lua_State* L, int index, Handler&& handler, record& tracking) {
+ using no_cv_X = meta::unqualified_t<X>;
+ if constexpr (!std::is_reference_v<X> && is_unique_usertype_v<no_cv_X>) {
+ using element = unique_usertype_element_t<no_cv_X>;
+ if constexpr (is_actual_type_rebindable_for_v<no_cv_X>) {
+ using rebound_actual_type = unique_usertype_rebind_actual_t<no_cv_X>;
+ // we have a unique pointer type that can be
+ // rebound to a base/derived type
+ const type indextype = type_of(L, index);
+ tracking.use(1);
+ if (indextype != type::userdata) {
+ handler(L, index, type::userdata, indextype, "value is not a userdata");
+ return false;
+ }
+ void* memory = lua_touserdata(L, index);
+ memory = detail::align_usertype_unique_destructor(memory);
+ detail::unique_destructor& pdx = *static_cast<detail::unique_destructor*>(memory);
+ if (&detail::usertype_unique_alloc_destroy<element, no_cv_X> == pdx) {
+ return true;
+ }
+ if constexpr (derive<element>::value) {
+ memory = detail::align_usertype_unique_tag<true, false>(memory);
+ detail::unique_tag& ic = *reinterpret_cast<detail::unique_tag*>(memory);
+ string_view ti = usertype_traits<element>::qualified_name();
+ string_view rebind_ti = usertype_traits<rebound_actual_type>::qualified_name();
+ if (ic(nullptr, nullptr, ti, rebind_ti) != 0) {
+ return true;
+ }
+ }
+ handler(L, index, type::userdata, indextype, "value is a userdata but is not the correct unique usertype");
+ return false;
+ }
+ else {
+ return stack::unqualified_check<X>(L, index, std::forward<Handler>(handler), tracking);
+ }
+ }
+ else if constexpr (!std::is_reference_v<X> && is_container_v<no_cv_X>) {
+ if (type_of(L, index) == type::userdata) {
+ return stack::unqualified_check<X>(L, index, std::forward<Handler>(handler), tracking);
+ }
+ else {
+ return stack::unqualified_check<nested<X>>(L, index, std::forward<Handler>(handler), tracking);
+ }
+ }
+ else if constexpr (!std::is_reference_v<X> && meta::is_specialization_of_v<X, nested>) {
+ using NestedX = typename meta::unqualified_t<X>::nested_type;
+ return stack::check<NestedX>(L, index, ::std::forward<Handler>(handler), tracking);
+ }
+ else {
+ return stack::unqualified_check<X>(L, index, std::forward<Handler>(handler), tracking);
+ }
+ }
+ };
+}} // namespace sol::stack
+
+// end of sol/stack_check_qualified.hpp
+
+// end of sol/stack_check.hpp
+
+// beginning of sol/stack_get.hpp
+
+// beginning of sol/stack_get_unqualified.hpp
+
+// beginning of sol/overload.hpp
+
+#include <utility>
+
+namespace sol {
+ template <typename... Functions>
+ struct overload_set {
+ std::tuple<Functions...> functions;
+ template <typename Arg, typename... Args, meta::disable<std::is_same<overload_set, meta::unqualified_t<Arg>>> = meta::enabler>
+ overload_set(Arg&& arg, Args&&... args) : functions(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+ overload_set(const overload_set&) = default;
+ overload_set(overload_set&&) = default;
+ overload_set& operator=(const overload_set&) = default;
+ overload_set& operator=(overload_set&&) = default;
+ };
+
+ template <typename... Args>
+ decltype(auto) overload(Args&&... args) {
+ return overload_set<std::decay_t<Args>...>(std::forward<Args>(args)...);
+ }
+} // namespace sol
+
+// end of sol/overload.hpp
+
+// beginning of sol/unicode.hpp
+
+#include <array>
+#include <cstring>
+
+namespace sol {
+ // Everything here was lifted pretty much straight out of
+ // ogonek, because fuck figuring it out=
+ namespace unicode {
+ enum class error_code {
+ ok = 0,
+ invalid_code_point,
+ invalid_code_unit,
+ invalid_leading_surrogate,
+ invalid_trailing_surrogate,
+ sequence_too_short,
+ overlong_sequence,
+ };
+
+ inline const string_view& to_string(error_code ec) {
+ static const string_view storage[7] = { "ok",
+ "invalid code points",
+ "invalid code unit",
+ "invalid leading surrogate",
+ "invalid trailing surrogate",
+ "sequence too short",
+ "overlong sequence" };
+ return storage[static_cast<std::size_t>(ec)];
+ }
+
+ template <typename It>
+ struct decoded_result {
+ error_code error;
+ char32_t codepoint;
+ It next;
+ };
+
+ template <typename C>
+ struct encoded_result {
+ error_code error;
+ std::size_t code_units_size;
+ std::array<C, 4> code_units;
+ };
+
+ struct unicode_detail {
+ // codepoint related
+ static constexpr char32_t last_code_point = 0x10FFFF;
+
+ static constexpr char32_t first_lead_surrogate = 0xD800;
+ static constexpr char32_t last_lead_surrogate = 0xDBFF;
+
+ static constexpr char32_t first_trail_surrogate = 0xDC00;
+ static constexpr char32_t last_trail_surrogate = 0xDFFF;
+
+ static constexpr char32_t first_surrogate = first_lead_surrogate;
+ static constexpr char32_t last_surrogate = last_trail_surrogate;
+
+ static constexpr bool is_lead_surrogate(char32_t u) {
+ return u >= first_lead_surrogate && u <= last_lead_surrogate;
+ }
+ static constexpr bool is_trail_surrogate(char32_t u) {
+ return u >= first_trail_surrogate && u <= last_trail_surrogate;
+ }
+ static constexpr bool is_surrogate(char32_t u) {
+ return u >= first_surrogate && u <= last_surrogate;
+ }
+
+ // utf8 related
+ static constexpr auto last_1byte_value = 0x7Fu;
+ static constexpr auto last_2byte_value = 0x7FFu;
+ static constexpr auto last_3byte_value = 0xFFFFu;
+
+ static constexpr auto start_2byte_mask = 0x80u;
+ static constexpr auto start_3byte_mask = 0xE0u;
+ static constexpr auto start_4byte_mask = 0xF0u;
+
+ static constexpr auto continuation_mask = 0xC0u;
+ static constexpr auto continuation_signature = 0x80u;
+
+ static constexpr bool is_invalid(unsigned char b) {
+ return b == 0xC0 || b == 0xC1 || b > 0xF4;
+ }
+
+ static constexpr bool is_continuation(unsigned char b) {
+ return (b & unicode_detail::continuation_mask) == unicode_detail::continuation_signature;
+ }
+
+ static constexpr bool is_overlong(char32_t u, std::size_t bytes) {
+ return u <= unicode_detail::last_1byte_value || (u <= unicode_detail::last_2byte_value && bytes > 2)
+ || (u <= unicode_detail::last_3byte_value && bytes > 3);
+ }
+
+ static constexpr int sequence_length(unsigned char b) {
+ return (b & start_2byte_mask) == 0 ? 1
+ : (b & start_3byte_mask) != start_3byte_mask ? 2
+ : (b & start_4byte_mask) != start_4byte_mask ? 3
+ : 4;
+ }
+
+ static constexpr char32_t decode(unsigned char b0, unsigned char b1) {
+ return (static_cast<char32_t>((b0 & 0x1Fu) << 6u) | static_cast<char32_t>(b1 & 0x3Fu));
+ }
+ static constexpr char32_t decode(unsigned char b0, unsigned char b1, unsigned char b2) {
+ return static_cast<char32_t>((b0 & 0x0Fu) << 12u) | static_cast<char32_t>((b1 & 0x3Fu) << 6u) | static_cast<char32_t>(b2 & 0x3Fu);
+ }
+ static constexpr char32_t decode(unsigned char b0, unsigned char b1, unsigned char b2, unsigned char b3) {
+ return static_cast<char32_t>(static_cast<char32_t>((b0 & 0x07u) << 18u) | static_cast<char32_t>((b1 & 0x3F) << 12)
+ | static_cast<char32_t>((b2 & 0x3Fu) << 6u) | static_cast<char32_t>(b3 & 0x3Fu));
+ }
+
+ // utf16 related
+ static constexpr char32_t last_bmp_value = 0xFFFF;
+ static constexpr char32_t normalizing_value = 0x10000;
+ static constexpr int lead_surrogate_bitmask = 0xFFC00;
+ static constexpr int trail_surrogate_bitmask = 0x3FF;
+ static constexpr int lead_shifted_bits = 10;
+ static constexpr char32_t replacement = 0xFFFD;
+
+ static char32_t combine_surrogates(char16_t lead, char16_t trail) {
+ auto hi = lead - first_lead_surrogate;
+ auto lo = trail - first_trail_surrogate;
+ return normalizing_value + ((hi << lead_shifted_bits) | lo);
+ }
+ };
+
+ inline encoded_result<char> code_point_to_utf8(char32_t codepoint) {
+ encoded_result<char> er;
+ er.error = error_code::ok;
+ if (codepoint <= unicode_detail::last_1byte_value) {
+ er.code_units_size = 1;
+ er.code_units = std::array<char, 4> { { static_cast<char>(codepoint) } };
+ }
+ else if (codepoint <= unicode_detail::last_2byte_value) {
+ er.code_units_size = 2;
+ er.code_units = std::array<char, 4> { {
+ static_cast<char>(0xC0 | ((codepoint & 0x7C0) >> 6)),
+ static_cast<char>(0x80 | (codepoint & 0x3F)),
+ } };
+ }
+ else if (codepoint <= unicode_detail::last_3byte_value) {
+ er.code_units_size = 3;
+ er.code_units = std::array<char, 4> { {
+ static_cast<char>(0xE0 | ((codepoint & 0xF000) >> 12)),
+ static_cast<char>(0x80 | ((codepoint & 0xFC0) >> 6)),
+ static_cast<char>(0x80 | (codepoint & 0x3F)),
+ } };
+ }
+ else {
+ er.code_units_size = 4;
+ er.code_units = std::array<char, 4> { {
+ static_cast<char>(0xF0 | ((codepoint & 0x1C0000) >> 18)),
+ static_cast<char>(0x80 | ((codepoint & 0x3F000) >> 12)),
+ static_cast<char>(0x80 | ((codepoint & 0xFC0) >> 6)),
+ static_cast<char>(0x80 | (codepoint & 0x3F)),
+ } };
+ }
+ return er;
+ }
+
+ inline encoded_result<char16_t> code_point_to_utf16(char32_t codepoint) {
+ encoded_result<char16_t> er;
+
+ if (codepoint <= unicode_detail::last_bmp_value) {
+ er.code_units_size = 1;
+ er.code_units = std::array<char16_t, 4> { { static_cast<char16_t>(codepoint) } };
+ er.error = error_code::ok;
+ }
+ else {
+ auto normal = codepoint - unicode_detail::normalizing_value;
+ auto lead = unicode_detail::first_lead_surrogate + ((normal & unicode_detail::lead_surrogate_bitmask) >> unicode_detail::lead_shifted_bits);
+ auto trail = unicode_detail::first_trail_surrogate + (normal & unicode_detail::trail_surrogate_bitmask);
+ er.code_units = std::array<char16_t, 4> { { static_cast<char16_t>(lead), static_cast<char16_t>(trail) } };
+ er.code_units_size = 2;
+ er.error = error_code::ok;
+ }
+ return er;
+ }
+
+ inline encoded_result<char32_t> code_point_to_utf32(char32_t codepoint) {
+ encoded_result<char32_t> er;
+ er.code_units_size = 1;
+ er.code_units[0] = codepoint;
+ er.error = error_code::ok;
+ return er;
+ }
+
+ template <typename It>
+ inline decoded_result<It> utf8_to_code_point(It it, It last) {
+ decoded_result<It> dr;
+ if (it == last) {
+ dr.next = it;
+ dr.error = error_code::sequence_too_short;
+ return dr;
+ }
+
+ unsigned char b0 = static_cast<unsigned char>(*it);
+ std::size_t length = static_cast<std::size_t>(unicode_detail::sequence_length(b0));
+
+ if (length == 1) {
+ dr.codepoint = static_cast<char32_t>(b0);
+ dr.error = error_code::ok;
+ ++it;
+ dr.next = it;
+ return dr;
+ }
+
+ if (unicode_detail::is_invalid(b0) || unicode_detail::is_continuation(b0)) {
+ dr.error = error_code::invalid_code_unit;
+ dr.next = it;
+ return dr;
+ }
+
+ ++it;
+ std::array<unsigned char, 4> b;
+ b[0] = b0;
+ for (std::size_t i = 1; i < length; ++i) {
+ b[i] = static_cast<unsigned char>(*it);
+ if (!unicode_detail::is_continuation(b[i])) {
+ dr.error = error_code::invalid_code_unit;
+ dr.next = it;
+ return dr;
+ }
+ ++it;
+ }
+
+ char32_t decoded;
+ switch (length) {
+ case 2:
+ decoded = unicode_detail::decode(b[0], b[1]);
+ break;
+ case 3:
+ decoded = unicode_detail::decode(b[0], b[1], b[2]);
+ break;
+ default:
+ decoded = unicode_detail::decode(b[0], b[1], b[2], b[3]);
+ break;
+ }
+
+ if (unicode_detail::is_overlong(decoded, length)) {
+ dr.error = error_code::overlong_sequence;
+ return dr;
+ }
+ if (unicode_detail::is_surrogate(decoded) || decoded > unicode_detail::last_code_point) {
+ dr.error = error_code::invalid_code_point;
+ return dr;
+ }
+
+ // then everything is fine
+ dr.codepoint = decoded;
+ dr.error = error_code::ok;
+ dr.next = it;
+ return dr;
+ }
+
+ template <typename It>
+ inline decoded_result<It> utf16_to_code_point(It it, It last) {
+ decoded_result<It> dr;
+ if (it == last) {
+ dr.next = it;
+ dr.error = error_code::sequence_too_short;
+ return dr;
+ }
+
+ char16_t lead = static_cast<char16_t>(*it);
+
+ if (!unicode_detail::is_surrogate(lead)) {
+ ++it;
+ dr.codepoint = static_cast<char32_t>(lead);
+ dr.next = it;
+ dr.error = error_code::ok;
+ return dr;
+ }
+ if (!unicode_detail::is_lead_surrogate(lead)) {
+ dr.error = error_code::invalid_leading_surrogate;
+ dr.next = it;
+ return dr;
+ }
+
+ ++it;
+ auto trail = *it;
+ if (!unicode_detail::is_trail_surrogate(trail)) {
+ dr.error = error_code::invalid_trailing_surrogate;
+ dr.next = it;
+ return dr;
+ }
+
+ dr.codepoint = unicode_detail::combine_surrogates(lead, trail);
+ dr.next = ++it;
+ dr.error = error_code::ok;
+ return dr;
+ }
+
+ template <typename It>
+ inline decoded_result<It> utf32_to_code_point(It it, It last) {
+ decoded_result<It> dr;
+ if (it == last) {
+ dr.next = it;
+ dr.error = error_code::sequence_too_short;
+ return dr;
+ }
+ dr.codepoint = static_cast<char32_t>(*it);
+ dr.next = ++it;
+ dr.error = error_code::ok;
+ return dr;
+ }
+ } // namespace unicode
+} // namespace sol
+// end of sol/unicode.hpp
+
+// beginning of sol/abort.hpp
+
+#include <cstdlib>
+
+#if SOL_IS_ON(SOL_DEBUG_BUILD)
+ #if SOL_IS_ON(SOL_COMPILER_VCXX)
+ #define SOL_DEBUG_ABORT() \
+ if (true) { ::std::abort(); } \
+ static_assert(true, "")
+ #else
+ #define SOL_DEBUG_ABORT() ::std::abort()
+ #endif
+#else
+ #define SOL_DEBUG_ABORT() static_assert(true, "")
+#endif
+
+// end of sol/abort.hpp
+
+#include <memory>
+#include <functional>
+#include <utility>
+#include <cstdlib>
+#include <cmath>
+#include <string_view>
+#if SOL_IS_ON(SOL_STD_VARIANT)
+#include <variant>
+#endif // Apple clang screwed up
+
+namespace sol { namespace stack {
+
+ namespace stack_detail {
+ template <typename Ch>
+ struct count_code_units_utf {
+ std::size_t needed_size;
+
+ count_code_units_utf() : needed_size(0) {
+ }
+
+ void operator()(const unicode::encoded_result<Ch> er) {
+ needed_size += er.code_units_size;
+ }
+ };
+
+ template <typename Ch, typename ErCh>
+ struct copy_code_units_utf {
+ Ch* target_;
+
+ copy_code_units_utf(Ch* target) : target_(target) {
+ }
+
+ void operator()(const unicode::encoded_result<ErCh> er) {
+ std::memcpy(target_, er.code_units.data(), er.code_units_size * sizeof(ErCh));
+ target_ += er.code_units_size;
+ }
+ };
+
+ template <typename Ch, typename F>
+ inline void convert(const char* strb, const char* stre, F&& f) {
+ char32_t cp = 0;
+ for (const char* strtarget = strb; strtarget < stre;) {
+ auto dr = unicode::utf8_to_code_point(strtarget, stre);
+ if (dr.error != unicode::error_code::ok) {
+ cp = unicode::unicode_detail::replacement;
+ ++strtarget;
+ }
+ else {
+ cp = dr.codepoint;
+ strtarget = dr.next;
+ }
+ if constexpr (std::is_same_v<Ch, char32_t>) {
+ auto er = unicode::code_point_to_utf32(cp);
+ f(er);
+ }
+ else {
+ auto er = unicode::code_point_to_utf16(cp);
+ f(er);
+ }
+ }
+ }
+
+ template <typename BaseCh, typename S>
+ inline S get_into(lua_State* L, int index, record& tracking) {
+ using Ch = typename S::value_type;
+ tracking.use(1);
+ size_t len;
+ auto utf8p = lua_tolstring(L, index, &len);
+ if (len < 1)
+ return S();
+ const char* strb = utf8p;
+ const char* stre = utf8p + len;
+ stack_detail::count_code_units_utf<BaseCh> count_units;
+ convert<BaseCh>(strb, stre, count_units);
+ S r(count_units.needed_size, static_cast<Ch>(0));
+ r.resize(count_units.needed_size);
+ Ch* target = &r[0];
+ stack_detail::copy_code_units_utf<Ch, BaseCh> copy_units(target);
+ convert<BaseCh>(strb, stre, copy_units);
+ return r;
+ }
+ } // namespace stack_detail
+
+ template <typename T, typename>
+ struct unqualified_getter {
+ static decltype(auto) get(lua_State* L, int index, record& tracking) {
+ if constexpr (std::is_same_v<T, bool>) {
+ tracking.use(1);
+ return lua_toboolean(L, index) != 0;
+ }
+ else if constexpr (std::is_enum_v<T>) {
+ tracking.use(1);
+ return static_cast<T>(lua_tointegerx(L, index, nullptr));
+ }
+ else if constexpr (std::is_integral_v<T> || std::is_same_v<T, lua_Integer>) {
+ tracking.use(1);
+#if SOL_LUA_VERSION_I_ >= 503
+ if (lua_isinteger(L, index) != 0) {
+ return static_cast<T>(lua_tointeger(L, index));
+ }
+#endif
+ return static_cast<T>(llround(lua_tonumber(L, index)));
+ }
+ else if constexpr (std::is_floating_point_v<T> || std::is_same_v<T, lua_Number>) {
+ tracking.use(1);
+ return static_cast<T>(lua_tonumber(L, index));
+ }
+ else if constexpr (is_lua_reference_v<T>) {
+ if constexpr (is_global_table_v<T>) {
+ tracking.use(1);
+ return T(L, global_tag);
+ }
+ else {
+ tracking.use(1);
+ return T(L, index);
+ }
+ }
+ else if constexpr (is_unique_usertype_v<T>) {
+ using actual = unique_usertype_actual_t<T>;
+
+ tracking.use(1);
+ void* memory = lua_touserdata(L, index);
+ void* aligned_memory = detail::align_usertype_unique<actual>(memory);
+ actual* typed_memory = static_cast<actual*>(aligned_memory);
+ return *typed_memory;
+ }
+ else if constexpr (meta::is_optional_v<T>) {
+ using ValueType = typename T::value_type;
+ return unqualified_check_getter<ValueType>::template get_using<T>(L, index, &no_panic, tracking);
+ }
+ else if constexpr (std::is_same_v<T, luaL_Stream*>) {
+ luaL_Stream* pstream = static_cast<luaL_Stream*>(lua_touserdata(L, index));
+ return pstream;
+ }
+ else if constexpr (std::is_same_v<T, luaL_Stream>) {
+ luaL_Stream* pstream = static_cast<luaL_Stream*>(lua_touserdata(L, index));
+ return *pstream;
+ }
+#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ else if constexpr (std::is_function_v<T> || (std::is_pointer_v<T> && std::is_function_v<std::remove_pointer_t<T>>)) {
+ return stack_detail::get_function_pointer<std::remove_pointer_t<T>>(L, index, tracking);
+ }
+#endif
+ else {
+ return stack_detail::unchecked_unqualified_get<detail::as_value_tag<T>>(L, index, tracking);
+ }
+ }
+ };
+
+ template <typename X, typename>
+ struct qualified_getter {
+ static decltype(auto) get(lua_State* L, int index, record& tracking) {
+ using Tu = meta::unqualified_t<X>;
+ static constexpr bool is_maybe_userdata_of_some_kind
+ = !std::is_reference_v<
+ X> && is_container_v<Tu> && std::is_default_constructible_v<Tu> && !is_lua_primitive_v<Tu> && !is_transparent_argument_v<Tu>;
+ if constexpr (is_maybe_userdata_of_some_kind) {
+ if (type_of(L, index) == type::userdata) {
+ return static_cast<Tu>(stack_detail::unchecked_unqualified_get<Tu>(L, index, tracking));
+ }
+ else {
+ return stack_detail::unchecked_unqualified_get<sol::nested<Tu>>(L, index, tracking);
+ }
+ }
+ else if constexpr (!std::is_reference_v<X> && is_unique_usertype_v<Tu> && !is_actual_type_rebindable_for_v<Tu>) {
+ using element = unique_usertype_element_t<Tu>;
+ using actual = unique_usertype_actual_t<Tu>;
+ tracking.use(1);
+ void* memory = lua_touserdata(L, index);
+ memory = detail::align_usertype_unique_destructor(memory);
+ detail::unique_destructor& pdx = *static_cast<detail::unique_destructor*>(memory);
+ if (&detail::usertype_unique_alloc_destroy<element, Tu> == pdx) {
+ memory = detail::align_usertype_unique_tag<true, false>(memory);
+ memory = detail::align_usertype_unique<actual, true, false>(memory);
+ actual* mem = static_cast<actual*>(memory);
+ return static_cast<actual>(*mem);
+ }
+ actual r {};
+ if constexpr (!derive<element>::value) {
+ // In debug mode we would rather abort you for this grave failure rather
+ // than let you deref a null pointer and fuck everything over
+ SOL_DEBUG_ABORT();
+ return static_cast<actual>(std::move(r));
+ }
+ else {
+ memory = detail::align_usertype_unique_tag<true, false>(memory);
+ detail::unique_tag& ic = *reinterpret_cast<detail::unique_tag*>(memory);
+ memory = detail::align_usertype_unique<actual, true, false>(memory);
+ string_view ti = usertype_traits<element>::qualified_name();
+ int cast_operation;
+ if constexpr (is_actual_type_rebindable_for_v<Tu>) {
+ using rebound_actual_type = unique_usertype_rebind_actual_t<Tu, void>;
+ string_view rebind_ti = usertype_traits<rebound_actual_type>::qualified_name();
+ cast_operation = ic(memory, &r, ti, rebind_ti);
+ }
+ else {
+ string_view rebind_ti("");
+ cast_operation = ic(memory, &r, ti, rebind_ti);
+ }
+ switch (cast_operation) {
+ case 1: {
+ // it's a perfect match,
+ // alias memory directly
+ actual* mem = static_cast<actual*>(memory);
+ return static_cast<actual>(*mem);
+ }
+ case 2:
+ // it's a base match, return the
+ // aliased creation
+ return static_cast<actual>(std::move(r));
+ default:
+ // uh oh..
+ break;
+ }
+ SOL_DEBUG_ABORT();
+ return static_cast<actual>(r);
+ }
+ }
+ else {
+ return stack_detail::unchecked_unqualified_get<Tu>(L, index, tracking);
+ }
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<as_table_t<T>> {
+ using Tu = meta::unqualified_t<T>;
+
+ template <typename V>
+ static void push_back_at_end(std::true_type, types<V>, lua_State* L, T& cont, std::size_t) {
+ cont.push_back(stack::get<V>(L, -lua_size<V>::value));
+ }
+
+ template <typename V>
+ static void push_back_at_end(std::false_type, types<V> t, lua_State* L, T& cont, std::size_t idx) {
+ insert_at_end(meta::has_insert<Tu>(), t, L, cont, idx);
+ }
+
+ template <typename V>
+ static void insert_at_end(std::true_type, types<V>, lua_State* L, T& cont, std::size_t) {
+ using std::cend;
+ cont.insert(cend(cont), stack::get<V>(L, -lua_size<V>::value));
+ }
+
+ template <typename V>
+ static void insert_at_end(std::false_type, types<V>, lua_State* L, T& cont, std::size_t idx) {
+ cont[idx] = stack::get<V>(L, -lua_size<V>::value);
+ }
+
+ static bool max_size_check(std::false_type, T&, std::size_t) {
+ return false;
+ }
+
+ static bool max_size_check(std::true_type, T& cont, std::size_t idx) {
+ return idx >= std::size_t(cont.max_size());
+ }
+
+ static T get(lua_State* L, int relindex, record& tracking) {
+ return get(meta::is_associative<Tu>(), L, relindex, tracking);
+ }
+
+ static T get(std::false_type, lua_State* L, int relindex, record& tracking) {
+ typedef typename Tu::value_type V;
+ return get(types<V>(), L, relindex, tracking);
+ }
+
+ template <typename V>
+ static T get(types<V> t, lua_State* L, int relindex, record& tracking) {
+ tracking.use(1);
+
+ // the W4 flag is really great,
+ // so great that it can tell my for loops (twice nested)
+ // below never actually terminate
+ // without hitting where the gotos have infested
+
+ // so now I would get the error W4XXX unreachable
+ // me that the return cont at the end of this function
+ // which is fair until other compilers complain
+ // that there isn't a return and that based on
+ // SOME MAGICAL FORCE
+ // control flow falls off the end of a non-void function
+ // so it needs to be there for the compilers that are
+ // too flimsy to analyze the basic blocks...
+ // (I'm sure I should file a bug but those compilers are already
+ // in the wild; it doesn't matter if I fix them,
+ // someone else is still going to get some old-ass compiler
+ // and then bother me about the unclean build for the 30th
+ // time)
+
+ // "Why not an IIFE?"
+ // Because additional lambdas / functions which serve as
+ // capture-all-and-then-invoke bloat binary sizes
+ // by an actually detectable amount
+ // (one user uses sol2 pretty heavily and 22 MB of binary size
+ // was saved by reducing reliance on lambdas in templates)
+
+ // This would really be solved by having break N;
+ // be a real, proper thing...
+ // but instead, we have to use labels and gotos
+ // and earn the universal vitriol of the dogmatic
+ // programming community
+
+ // all in all: W4 is great!~
+
+ int index = lua_absindex(L, relindex);
+ T cont;
+ std::size_t idx = 0;
+#if SOL_LUA_VERSION_I_ >= 503
+ // This method is HIGHLY performant over regular table iteration
+ // thanks to the Lua API changes in 5.3
+ // Questionable in 5.4
+ for (lua_Integer i = 0;; i += lua_size<V>::value) {
+ if (max_size_check(meta::has_max_size<Tu>(), cont, idx)) {
+ // see above comment
+ goto done;
+ }
+ bool isnil = false;
+ for (int vi = 0; vi < lua_size<V>::value; ++vi) {
+#if SOL_IS_ON(SOL_LUA_NIL_IN_TABLES) && SOL_LUA_VERSION_I_ >= 600
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushinteger(L, static_cast<lua_Integer>(i + vi));
+ if (lua_keyin(L, index) == 0) {
+ // it's time to stop
+ isnil = true;
+ }
+ else {
+ // we have a key, have to get the value
+ lua_geti(L, index, i + vi);
+ }
+#else
+ type vt = static_cast<type>(lua_geti(L, index, i + vi));
+ isnil = vt == type::none || vt == type::lua_nil;
+#endif
+ if (isnil) {
+ if (i == 0) {
+ break;
+ }
+#if SOL_IS_ON(SOL_LUA_NIL_IN_TABLES) && SOL_LUA_VERSION_I_ >= 600
+ lua_pop(L, vi);
+#else
+ lua_pop(L, (vi + 1));
+#endif
+ // see above comment
+ goto done;
+ }
+ }
+ if (isnil) {
+#if SOL_IS_ON(SOL_LUA_NIL_IN_TABLES) && SOL_LUA_VERSION_I_ >= 600
+#else
+ lua_pop(L, lua_size<V>::value);
+#endif
+ continue;
+ }
+
+ push_back_at_end(meta::has_push_back<Tu>(), t, L, cont, idx);
+ ++idx;
+ lua_pop(L, lua_size<V>::value);
+ }
+#else
+ // Zzzz slower but necessary thanks to the lower version API and missing functions qq
+ for (lua_Integer i = 0;; i += lua_size<V>::value, lua_pop(L, lua_size<V>::value)) {
+ if (idx >= cont.max_size()) {
+ // see above comment
+ goto done;
+ }
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 2, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ bool isnil = false;
+ for (int vi = 0; vi < lua_size<V>::value; ++vi) {
+ lua_pushinteger(L, i);
+ lua_gettable(L, index);
+ type vt = type_of(L, -1);
+ isnil = vt == type::lua_nil;
+ if (isnil) {
+ if (i == 0) {
+ break;
+ }
+ lua_pop(L, (vi + 1));
+ // see above comment
+ goto done;
+ }
+ }
+ if (isnil)
+ continue;
+ push_back_at_end(meta::has_push_back<Tu>(), t, L, cont, idx);
+ ++idx;
+ }
+#endif
+ done:
+ return cont;
+ }
+
+ static T get(std::true_type, lua_State* L, int index, record& tracking) {
+ typedef typename Tu::value_type P;
+ typedef typename P::first_type K;
+ typedef typename P::second_type V;
+ return get(types<K, V>(), L, index, tracking);
+ }
+
+ template <typename K, typename V>
+ static T get(types<K, V>, lua_State* L, int relindex, record& tracking) {
+ tracking.use(1);
+
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 3, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+
+ T associative;
+ int index = lua_absindex(L, relindex);
+ lua_pushnil(L);
+ while (lua_next(L, index) != 0) {
+ decltype(auto) key = stack::check_get<K>(L, -2);
+ if (!key) {
+ lua_pop(L, 1);
+ continue;
+ }
+ associative.emplace(std::forward<decltype(*key)>(*key), stack::get<V>(L, -1));
+ lua_pop(L, 1);
+ }
+ return associative;
+ }
+ };
+
+ template <typename T, typename Al>
+ struct unqualified_getter<as_table_t<std::forward_list<T, Al>>> {
+ typedef std::forward_list<T, Al> C;
+
+ static C get(lua_State* L, int relindex, record& tracking) {
+ return get(meta::has_key_value_pair<C>(), L, relindex, tracking);
+ }
+
+ static C get(std::true_type, lua_State* L, int index, record& tracking) {
+ typedef typename T::value_type P;
+ typedef typename P::first_type K;
+ typedef typename P::second_type V;
+ return get(types<K, V>(), L, index, tracking);
+ }
+
+ static C get(std::false_type, lua_State* L, int relindex, record& tracking) {
+ typedef typename C::value_type V;
+ return get(types<V>(), L, relindex, tracking);
+ }
+
+ template <typename V>
+ static C get(types<V>, lua_State* L, int relindex, record& tracking) {
+ tracking.use(1);
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 3, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+
+ int index = lua_absindex(L, relindex);
+ C cont;
+ auto at = cont.cbefore_begin();
+ std::size_t idx = 0;
+#if SOL_LUA_VERSION_I_ >= 503
+ // This method is HIGHLY performant over regular table iteration thanks to the Lua API changes in 5.3
+ for (lua_Integer i = 0;; i += lua_size<V>::value, lua_pop(L, lua_size<V>::value)) {
+ if (idx >= cont.max_size()) {
+ goto done;
+ }
+ bool isnil = false;
+ for (int vi = 0; vi < lua_size<V>::value; ++vi) {
+ type t = static_cast<type>(lua_geti(L, index, i + vi));
+ isnil = t == type::lua_nil;
+ if (isnil) {
+ if (i == 0) {
+ break;
+ }
+ lua_pop(L, (vi + 1));
+ goto done;
+ }
+ }
+ if (isnil)
+ continue;
+ at = cont.insert_after(at, stack::get<V>(L, -lua_size<V>::value));
+ ++idx;
+ }
+#else
+ // Zzzz slower but necessary thanks to the lower version API and missing functions qq
+ for (lua_Integer i = 0;; i += lua_size<V>::value, lua_pop(L, lua_size<V>::value)) {
+ if (idx >= cont.max_size()) {
+ goto done;
+ }
+ bool isnil = false;
+ for (int vi = 0; vi < lua_size<V>::value; ++vi) {
+ lua_pushinteger(L, i);
+ lua_gettable(L, index);
+ type t = type_of(L, -1);
+ isnil = t == type::lua_nil;
+ if (isnil) {
+ if (i == 0) {
+ break;
+ }
+ lua_pop(L, (vi + 1));
+ goto done;
+ }
+ }
+ if (isnil)
+ continue;
+ at = cont.insert_after(at, stack::get<V>(L, -lua_size<V>::value));
+ ++idx;
+ }
+#endif
+ done:
+ return cont;
+ }
+
+ template <typename K, typename V>
+ static C get(types<K, V>, lua_State* L, int relindex, record& tracking) {
+ tracking.use(1);
+
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 3, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+
+ C associative;
+ auto at = associative.cbefore_begin();
+ int index = lua_absindex(L, relindex);
+ lua_pushnil(L);
+ while (lua_next(L, index) != 0) {
+ decltype(auto) key = stack::check_get<K>(L, -2);
+ if (!key) {
+ lua_pop(L, 1);
+ continue;
+ }
+ at = associative.emplace_after(at, std::forward<decltype(*key)>(*key), stack::get<V>(L, -1));
+ lua_pop(L, 1);
+ }
+ return associative;
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<nested<T>> {
+ static T get(lua_State* L, int index, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (is_container_v<Tu>) {
+ if constexpr (meta::is_associative<Tu>::value) {
+ typedef typename Tu::value_type P;
+ typedef typename P::first_type K;
+ typedef typename P::second_type V;
+ unqualified_getter<as_table_t<T>> g {};
+ return g.get(types<K, nested<V>>(), L, index, tracking);
+ }
+ else {
+ typedef typename Tu::value_type V;
+ unqualified_getter<as_table_t<T>> g {};
+ return g.get(types<nested<V>>(), L, index, tracking);
+ }
+ }
+ else {
+ unqualified_getter<Tu> g {};
+ return g.get(L, index, tracking);
+ }
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<as_container_t<T>> {
+ static decltype(auto) get(lua_State* L, int index, record& tracking) {
+ return stack::unqualified_get<T>(L, index, tracking);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<as_container_t<T>*> {
+ static decltype(auto) get(lua_State* L, int index, record& tracking) {
+ return stack::unqualified_get<T*>(L, index, tracking);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<exhaustive<T>> {
+ static decltype(auto) get(lua_State* arg_L, int index, record& tracking) {
+ return stack::get<T>(arg_L, index, tracking);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<non_exhaustive<T>> {
+ static decltype(auto) get(lua_State* arg_L, int index, record& tracking) {
+ return stack::get<T>(arg_L, index, tracking);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<userdata_value> {
+ static userdata_value get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ return userdata_value(lua_touserdata(L, index));
+ }
+ };
+
+ template <>
+ struct unqualified_getter<lightuserdata_value> {
+ static lightuserdata_value get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ return lightuserdata_value(lua_touserdata(L, index));
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<light<T>> {
+ static light<T> get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ void* memory = lua_touserdata(L, index);
+ return light<T>(static_cast<T*>(memory));
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<user<T>> {
+ static std::add_lvalue_reference_t<T> get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ void* memory = lua_touserdata(L, index);
+ memory = detail::align_user<T>(memory);
+ return *static_cast<std::remove_reference_t<T>*>(memory);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<user<T*>> {
+ static T* get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ void* memory = lua_touserdata(L, index);
+ memory = detail::align_user<T*>(memory);
+ return static_cast<T*>(memory);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<type> {
+ static type get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ return static_cast<type>(lua_type(L, index));
+ }
+ };
+
+ template <>
+ struct unqualified_getter<std::string> {
+ static std::string get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ std::size_t len;
+ auto str = lua_tolstring(L, index, &len);
+ return std::string(str, len);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<const char*> {
+ static const char* get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ size_t sz;
+ return lua_tolstring(L, index, &sz);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<char> {
+ static char get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ size_t len;
+ auto str = lua_tolstring(L, index, &len);
+ return len > 0 ? str[0] : '\0';
+ }
+ };
+
+ template <typename Traits>
+ struct unqualified_getter<basic_string_view<char, Traits>> {
+ static string_view get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ size_t sz;
+ const char* str = lua_tolstring(L, index, &sz);
+ return basic_string_view<char, Traits>(str, sz);
+ }
+ };
+
+ template <typename Traits, typename Al>
+ struct unqualified_getter<std::basic_string<wchar_t, Traits, Al>> {
+ using S = std::basic_string<wchar_t, Traits, Al>;
+ static S get(lua_State* L, int index, record& tracking) {
+ using Ch = meta::conditional_t<sizeof(wchar_t) == 2, char16_t, char32_t>;
+ return stack_detail::get_into<Ch, S>(L, index, tracking);
+ }
+ };
+
+ template <typename Traits, typename Al>
+ struct unqualified_getter<std::basic_string<char16_t, Traits, Al>> {
+ static std::basic_string<char16_t, Traits, Al> get(lua_State* L, int index, record& tracking) {
+ return stack_detail::get_into<char16_t, std::basic_string<char16_t, Traits, Al>>(L, index, tracking);
+ }
+ };
+
+ template <typename Traits, typename Al>
+ struct unqualified_getter<std::basic_string<char32_t, Traits, Al>> {
+ static std::basic_string<char32_t, Traits, Al> get(lua_State* L, int index, record& tracking) {
+ return stack_detail::get_into<char32_t, std::basic_string<char32_t, Traits, Al>>(L, index, tracking);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<char16_t> {
+ static char16_t get(lua_State* L, int index, record& tracking) {
+ string_view utf8 = stack::get<string_view>(L, index, tracking);
+ const char* strb = utf8.data();
+ const char* stre = utf8.data() + utf8.size();
+ char32_t cp = 0;
+ auto dr = unicode::utf8_to_code_point(strb, stre);
+ if (dr.error != unicode::error_code::ok) {
+ cp = unicode::unicode_detail::replacement;
+ }
+ else {
+ cp = dr.codepoint;
+ }
+ auto er = unicode::code_point_to_utf16(cp);
+ return er.code_units[0];
+ }
+ };
+
+ template <>
+ struct unqualified_getter<char32_t> {
+ static char32_t get(lua_State* L, int index, record& tracking) {
+ string_view utf8 = stack::get<string_view>(L, index, tracking);
+ const char* strb = utf8.data();
+ const char* stre = utf8.data() + utf8.size();
+ char32_t cp = 0;
+ auto dr = unicode::utf8_to_code_point(strb, stre);
+ if (dr.error != unicode::error_code::ok) {
+ cp = unicode::unicode_detail::replacement;
+ }
+ else {
+ cp = dr.codepoint;
+ }
+ auto er = unicode::code_point_to_utf32(cp);
+ return er.code_units[0];
+ }
+ };
+
+ template <>
+ struct unqualified_getter<wchar_t> {
+ static wchar_t get(lua_State* L, int index, record& tracking) {
+ typedef meta::conditional_t<sizeof(wchar_t) == 2, char16_t, char32_t> Ch;
+ unqualified_getter<Ch> g;
+ (void)g;
+ auto c = g.get(L, index, tracking);
+ return static_cast<wchar_t>(c);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<meta_function> {
+ static meta_function get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ const char* name = unqualified_getter<const char*> {}.get(L, index, tracking);
+ const auto& mfnames = meta_function_names();
+ for (std::size_t i = 0; i < mfnames.size(); ++i)
+ if (mfnames[i] == name)
+ return static_cast<meta_function>(i);
+ return meta_function::construct;
+ }
+ };
+
+ template <>
+ struct unqualified_getter<lua_nil_t> {
+ static lua_nil_t get(lua_State*, int, record& tracking) {
+ tracking.use(1);
+ return lua_nil;
+ }
+ };
+
+ template <>
+ struct unqualified_getter<std::nullptr_t> {
+ static std::nullptr_t get(lua_State*, int, record& tracking) {
+ tracking.use(1);
+ return nullptr;
+ }
+ };
+
+ template <>
+ struct unqualified_getter<nullopt_t> {
+ static nullopt_t get(lua_State*, int, record& tracking) {
+ tracking.use(1);
+ return nullopt;
+ }
+ };
+
+ template <>
+ struct unqualified_getter<this_state> {
+ static this_state get(lua_State* L, int, record& tracking) {
+ tracking.use(0);
+ return this_state(L);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<this_main_state> {
+ static this_main_state get(lua_State* L, int, record& tracking) {
+ tracking.use(0);
+ return this_main_state(main_thread(L, L));
+ }
+ };
+
+ template <>
+ struct unqualified_getter<lua_CFunction> {
+ static lua_CFunction get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ return lua_tocfunction(L, index);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<c_closure> {
+ static c_closure get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ return c_closure(lua_tocfunction(L, index), -1);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<error> {
+ static error get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ size_t sz = 0;
+ const char* err = lua_tolstring(L, index, &sz);
+ if (err == nullptr) {
+ return error(detail::direct_error, "");
+ }
+ return error(detail::direct_error, std::string(err, sz));
+ }
+ };
+
+ template <>
+ struct unqualified_getter<void*> {
+ static void* get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ return lua_touserdata(L, index);
+ }
+ };
+
+ template <>
+ struct unqualified_getter<const void*> {
+ static const void* get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ return lua_touserdata(L, index);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<detail::as_value_tag<T>> {
+ static T* get_no_lua_nil(lua_State* L, int index, record& tracking) {
+ void* memory = lua_touserdata(L, index);
+#if SOL_IS_ON(SOL_USE_INTEROP)
+ auto ugr = stack_detail::interop_get<T>(L, index, memory, tracking);
+ if (ugr.first) {
+ return ugr.second;
+ }
+#endif // interop extensibility
+ tracking.use(1);
+ void* rawdata = detail::align_usertype_pointer(memory);
+ void** pudata = static_cast<void**>(rawdata);
+ void* udata = *pudata;
+ return get_no_lua_nil_from(L, udata, index, tracking);
+ }
+
+ static T* get_no_lua_nil_from(lua_State* L, void* udata, int index, record&) {
+ bool has_derived = derive<T>::value || weak_derive<T>::value;
+ if (has_derived) {
+ if (lua_getmetatable(L, index) == 1) {
+ lua_getfield(L, -1, &detail::base_class_cast_key()[0]);
+ if (type_of(L, -1) != type::lua_nil) {
+ void* basecastdata = lua_touserdata(L, -1);
+ detail::inheritance_cast_function ic = reinterpret_cast<detail::inheritance_cast_function>(basecastdata);
+ // use the casting function to properly adjust the pointer for the desired T
+ udata = ic(udata, usertype_traits<T>::qualified_name());
+ }
+ lua_pop(L, 2);
+ }
+ }
+ if constexpr (std::is_function_v<T>) {
+ T* func = reinterpret_cast<T*>(udata);
+ return func;
+ }
+ else {
+ T* obj = static_cast<T*>(udata);
+ return obj;
+ }
+ }
+
+ static T& get(lua_State* L, int index, record& tracking) {
+ return *get_no_lua_nil(L, index, tracking);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<detail::as_pointer_tag<T>> {
+ static T* get(lua_State* L, int index, record& tracking) {
+ type t = type_of(L, index);
+ if (t == type::lua_nil) {
+ tracking.use(1);
+ return nullptr;
+ }
+ unqualified_getter<detail::as_value_tag<T>> g{};
+ return g.get_no_lua_nil(L, index, tracking);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<non_null<T*>> {
+ static T* get(lua_State* L, int index, record& tracking) {
+ unqualified_getter<detail::as_value_tag<T>> g{};
+ return g.get_no_lua_nil(L, index, tracking);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<T&> {
+ static T& get(lua_State* L, int index, record& tracking) {
+ unqualified_getter<detail::as_value_tag<T>> g{};
+ return g.get(L, index, tracking);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<std::reference_wrapper<T>> {
+ static T& get(lua_State* L, int index, record& tracking) {
+ unqualified_getter<T&> g{};
+ return g.get(L, index, tracking);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_getter<T*> {
+ static T* get(lua_State* L, int index, record& tracking) {
+#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ if constexpr (std::is_function_v<T>) {
+ return stack_detail::get_function_pointer<T>(L, index, tracking);
+ }
+ else {
+ unqualified_getter<detail::as_pointer_tag<T>> g{};
+ return g.get(L, index, tracking);
+ }
+#else
+ unqualified_getter<detail::as_pointer_tag<T>> g{};
+ return g.get(L, index, tracking);
+#endif
+ }
+ };
+
+ template <typename... Tn>
+ struct unqualified_getter<std::tuple<Tn...>> {
+ typedef std::tuple<decltype(stack::get<Tn>(nullptr, 0))...> R;
+
+ template <typename... Args>
+ static R apply(std::index_sequence<>, lua_State*, int, record&, Args&&... args) {
+ // Fuck you too, VC++
+ return R { std::forward<Args>(args)... };
+ }
+
+ template <std::size_t I, std::size_t... Ix, typename... Args>
+ static R apply(std::index_sequence<I, Ix...>, lua_State* L, int index, record& tracking, Args&&... args) {
+ // Fuck you too, VC++
+ typedef std::tuple_element_t<I, std::tuple<Tn...>> T;
+ return apply(std::index_sequence<Ix...>(), L, index, tracking, std::forward<Args>(args)..., stack::get<T>(L, index + tracking.used, tracking));
+ }
+
+ static R get(lua_State* L, int index, record& tracking) {
+ return apply(std::make_index_sequence<sizeof...(Tn)>(), L, index, tracking);
+ }
+ };
+
+ template <typename A, typename B>
+ struct unqualified_getter<std::pair<A, B>> {
+ static decltype(auto) get(lua_State* L, int index, record& tracking) {
+ return std::pair<decltype(stack::get<A>(L, index)), decltype(stack::get<B>(L, index))> { stack::get<A>(L, index, tracking),
+ stack::get<B>(L, index + tracking.used, tracking) };
+ }
+ };
+
+#if SOL_IS_ON(SOL_STD_VARIANT)
+
+ template <typename... Tn>
+ struct unqualified_getter<std::variant<Tn...>> {
+ using V = std::variant<Tn...>;
+
+ static V get_one(std::integral_constant<std::size_t, std::variant_size_v<V>>, lua_State* L, int index, record& tracking) {
+ (void)L;
+ (void)index;
+ (void)tracking;
+ if constexpr (std::variant_size_v<V> == 0) {
+ return V();
+ }
+ else {
+ // using T = std::variant_alternative_t<0, V>;
+ std::abort();
+ // return V(std::in_place_index<0>, stack::get<T>(L, index, tracking));
+ }
+ }
+
+ template <std::size_t I>
+ static V get_one(std::integral_constant<std::size_t, I>, lua_State* L, int index, record& tracking) {
+ typedef std::variant_alternative_t<I, V> T;
+ record temp_tracking = tracking;
+ if (stack::check<T>(L, index, &no_panic, temp_tracking)) {
+ tracking = temp_tracking;
+ return V(std::in_place_index<I>, stack::get<T>(L, index));
+ }
+ return get_one(std::integral_constant<std::size_t, I + 1>(), L, index, tracking);
+ }
+
+ static V get(lua_State* L, int index, record& tracking) {
+ return get_one(std::integral_constant<std::size_t, 0>(), L, index, tracking);
+ }
+ };
+#endif // variant
+
+}} // namespace sol::stack
+
+// end of sol/stack_get_unqualified.hpp
+
+// beginning of sol/stack_get_qualified.hpp
+
+namespace sol { namespace stack {
+
+ // There are no more enable_ifs that can be used here,
+ // so this is just for posterity, I guess?
+ // maybe I'll fill this file in later.
+
+}} // namespace sol::stack
+
+// end of sol/stack_get_qualified.hpp
+
+// end of sol/stack_get.hpp
+
+// beginning of sol/stack_check_get.hpp
+
+// beginning of sol/stack_check_get_unqualified.hpp
+
+#include <cstdlib>
+#include <cmath>
+#include <optional>
+#if SOL_IS_ON(SOL_STD_VARIANT)
+#include <variant>
+#endif // variant shenanigans (thanks, Mac OSX)
+
+namespace sol { namespace stack {
+ template <typename T, typename>
+ struct unqualified_check_getter {
+ typedef decltype(stack_detail::unchecked_unqualified_get<T>(nullptr, -1, std::declval<record&>())) R;
+
+ template <typename Optional, typename Handler>
+ static Optional get_using(lua_State* L, int index, Handler&& handler, record& tracking) {
+ if constexpr (!meta::meta_detail::is_adl_sol_lua_check_v<T> && !meta::meta_detail::is_adl_sol_lua_get_v<T>) {
+ if constexpr (is_lua_reference_v<T>) {
+ if constexpr (is_global_table_v<T>) {
+ (void)L;
+ (void)index;
+ (void)handler;
+ tracking.use(1);
+ return true;
+ }
+ else {
+ // actually check if it's none here, otherwise
+ // we'll have a none object inside an optional!
+ bool success = lua_isnoneornil(L, index) == 0 && stack::check<T>(L, index, &no_panic);
+ if (!success) {
+ // expected type, actual type
+ tracking.use(static_cast<int>(success));
+ handler(L, index, type::poly, type_of(L, index), "");
+ return detail::associated_nullopt_v<Optional>;
+ }
+ return stack_detail::unchecked_get<T>(L, index, tracking);
+ }
+ }
+ else if constexpr ((std::is_integral_v<T> || std::is_same_v<T, lua_Integer>)&&!std::is_same_v<T, bool>) {
+#if SOL_LUA_VERSION_I_ >= 503
+ if (lua_isinteger(L, index) != 0) {
+ tracking.use(1);
+ return static_cast<T>(lua_tointeger(L, index));
+ }
+#endif
+ int isnum = 0;
+ const lua_Number value = lua_tonumberx(L, index, &isnum);
+ if (isnum != 0) {
+#if SOL_IS_ON(SOL_NUMBER_PRECISION_CHECKS)
+ const auto integer_value = llround(value);
+ if (static_cast<lua_Number>(integer_value) == value) {
+ tracking.use(1);
+ return static_cast<T>(integer_value);
+ }
+#else
+ tracking.use(1);
+ return static_cast<T>(value);
+#endif
+ }
+ const type t = type_of(L, index);
+ tracking.use(static_cast<int>(t != type::none));
+ handler(L, index, type::number, t, "not an integer");
+ return detail::associated_nullopt_v<Optional>;
+ }
+ else if constexpr (std::is_floating_point_v<T> || std::is_same_v<T, lua_Number>) {
+ int isnum = 0;
+ lua_Number value = lua_tonumberx(L, index, &isnum);
+ if (isnum == 0) {
+ type t = type_of(L, index);
+ tracking.use(static_cast<int>(t != type::none));
+ handler(L, index, type::number, t, "not a valid floating point number");
+ return detail::associated_nullopt_v<Optional>;
+ }
+ tracking.use(1);
+ return static_cast<T>(value);
+ }
+ else if constexpr (std::is_enum_v<T> && !meta::any_same_v<T, meta_function, type>) {
+ int isnum = 0;
+ lua_Integer value = lua_tointegerx(L, index, &isnum);
+ if (isnum == 0) {
+ type t = type_of(L, index);
+ tracking.use(static_cast<int>(t != type::none));
+ handler(L, index, type::number, t, "not a valid enumeration value");
+ return detail::associated_nullopt_v<Optional>;
+ }
+ tracking.use(1);
+ return static_cast<T>(value);
+ }
+ else {
+ if (!unqualified_check<T>(L, index, std::forward<Handler>(handler))) {
+ tracking.use(static_cast<int>(!lua_isnone(L, index)));
+ return detail::associated_nullopt_v<Optional>;
+ }
+ return stack_detail::unchecked_unqualified_get<T>(L, index, tracking);
+ }
+ }
+ else {
+ if (!unqualified_check<T>(L, index, std::forward<Handler>(handler))) {
+ tracking.use(static_cast<int>(!lua_isnone(L, index)));
+ return detail::associated_nullopt_v<Optional>;
+ }
+ return stack_detail::unchecked_unqualified_get<T>(L, index, tracking);
+ }
+ }
+
+ template <typename Handler>
+ static optional<R> get(lua_State* L, int index, Handler&& handler, record& tracking) {
+ return get_using<optional<R>>(L, index, std::forward<Handler>(handler), tracking);
+ }
+ };
+
+#if SOL_IS_ON(SOL_STD_VARIANT)
+ template <typename... Tn, typename C>
+ struct unqualified_check_getter<std::variant<Tn...>, C> {
+ typedef std::variant<Tn...> V;
+ typedef std::variant_size<V> V_size;
+ typedef std::integral_constant<bool, V_size::value == 0> V_is_empty;
+
+ template <typename Handler>
+ static optional<V> get_empty(std::true_type, lua_State*, int, Handler&&, record&) {
+ return nullopt;
+ }
+
+ template <typename Handler>
+ static optional<V> get_empty(std::false_type, lua_State* L, int index, Handler&& handler, record&) {
+ // This should never be reached...
+ // please check your code and understand what you did to bring yourself here
+ // maybe file a bug report, or 5
+ handler(
+ L, index, type::poly, type_of(L, index), "this variant code should never be reached: if it has, you have done something so terribly wrong");
+ return nullopt;
+ }
+
+ template <typename Handler>
+ static optional<V> get_one(std::integral_constant<std::size_t, 0>, lua_State* L, int index, Handler&& handler, record& tracking) {
+ return get_empty(V_is_empty(), L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <std::size_t I, typename Handler>
+ static optional<V> get_one(std::integral_constant<std::size_t, I>, lua_State* L, int index, Handler&& handler, record& tracking) {
+ typedef std::variant_alternative_t<I - 1, V> T;
+ if (stack::check<T>(L, index, &no_panic, tracking)) {
+ return V(std::in_place_index<I - 1>, stack::get<T>(L, index));
+ }
+ return get_one(std::integral_constant<std::size_t, I - 1>(), L, index, std::forward<Handler>(handler), tracking);
+ }
+
+ template <typename Handler>
+ static optional<V> get(lua_State* L, int index, Handler&& handler, record& tracking) {
+ return get_one(std::integral_constant<std::size_t, V_size::value>(), L, index, std::forward<Handler>(handler), tracking);
+ }
+ };
+#endif // standard variant
+}} // namespace sol::stack
+
+// end of sol/stack_check_get_unqualified.hpp
+
+// beginning of sol/stack_check_get_qualified.hpp
+
+namespace sol { namespace stack {
+
+#if SOL_IS_ON(SOL_COMPILER_GCC)
+#pragma GCC diagnostic push
+#if !SOL_IS_ON(SOL_COMPILER_CLANG)
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+#endif
+
+ namespace stack_detail {
+ template <typename OptionalType, typename T, typename Handler>
+ OptionalType get_optional(lua_State* L, int index, Handler&& handler, record& tracking) {
+ using Tu = meta::unqualified_t<T>;
+
+ if constexpr (is_lua_reference_v<T>) {
+ if constexpr (is_global_table_v<Tu>) {
+ (void)L;
+ (void)index;
+ (void)handler;
+ tracking.use(1);
+ return true;
+ }
+ else {
+ // actually check if it's none here, otherwise
+ // we'll have a none object inside an optional!
+ bool success = lua_isnoneornil(L, index) == 0 && stack::check<T>(L, index, &no_panic);
+ if (!success) {
+ // expected type, actual type
+ tracking.use(static_cast<int>(success));
+ handler(L, index, type::poly, type_of(L, index), "");
+ return {};
+ }
+ return OptionalType(stack_detail::unchecked_get<T>(L, index, tracking));
+ }
+ }
+ else if constexpr (!std::is_reference_v<T> && is_unique_usertype_v<Tu> && !is_actual_type_rebindable_for_v<Tu>) {
+ // we can take shortcuts here to save on separate checking, and just return nullopt!
+ using element = unique_usertype_element_t<Tu>;
+ using actual = unique_usertype_actual_t<Tu>;
+ tracking.use(1);
+ void* memory = lua_touserdata(L, index);
+ memory = detail::align_usertype_unique_destructor(memory);
+ detail::unique_destructor& pdx = *static_cast<detail::unique_destructor*>(memory);
+ if (&detail::usertype_unique_alloc_destroy<element, Tu> == pdx) {
+ memory = detail::align_usertype_unique_tag<true, false>(memory);
+ memory = detail::align_usertype_unique<actual, true, false>(memory);
+ actual* mem = static_cast<actual*>(memory);
+ return static_cast<actual>(*mem);
+ }
+ if constexpr (!derive<element>::value) {
+ return OptionalType();
+ }
+ else {
+ memory = detail::align_usertype_unique_tag<true, false>(memory);
+ detail::unique_tag& ic = *reinterpret_cast<detail::unique_tag*>(memory);
+ memory = detail::align_usertype_unique<actual, true, false>(memory);
+ string_view ti = usertype_traits<element>::qualified_name();
+ int cast_operation;
+ actual r {};
+ if constexpr (is_actual_type_rebindable_for_v<Tu>) {
+ using rebound_actual_type = unique_usertype_rebind_actual_t<Tu, void>;
+ string_view rebind_ti = usertype_traits<rebound_actual_type>::qualified_name();
+ cast_operation = ic(memory, &r, ti, rebind_ti);
+ }
+ else {
+ string_view rebind_ti("");
+ cast_operation = ic(memory, &r, ti, rebind_ti);
+ }
+ switch (cast_operation) {
+ case 1: {
+ // it's a perfect match,
+ // alias memory directly
+ actual* mem = static_cast<actual*>(memory);
+ return OptionalType(*mem);
+ }
+ case 2:
+ // it's a base match, return the
+ // aliased creation
+ return OptionalType(std::move(r));
+ default:
+ break;
+ }
+ return OptionalType();
+ }
+ }
+ else {
+ if (!check<T>(L, index, std::forward<Handler>(handler))) {
+ tracking.use(static_cast<int>(!lua_isnone(L, index)));
+ return OptionalType();
+ }
+ return OptionalType(stack_detail::unchecked_get<T>(L, index, tracking));
+ }
+ }
+ } // namespace stack_detail
+
+#if SOL_IS_ON(SOL_COMPILER_GCC)
+#pragma GCC diagnostic pop
+#endif
+
+ template <typename T, typename>
+ struct qualified_check_getter {
+ typedef decltype(stack_detail::unchecked_get<T>(nullptr, -1, std::declval<record&>())) R;
+
+ template <typename Handler>
+ optional<R> get(lua_State* L, int index, Handler&& handler, record& tracking) {
+ return stack_detail::get_optional<optional<R>, T>(L, index, std::forward<Handler>(handler), tracking);
+ }
+ };
+
+ template <typename Optional>
+ struct qualified_getter<Optional, std::enable_if_t<meta::is_optional_v<Optional>>> {
+ static Optional get(lua_State* L, int index, record& tracking) {
+ using T = typename meta::unqualified_t<Optional>::value_type;
+ return stack_detail::get_optional<Optional, T>(L, index, &no_panic, tracking);
+ }
+ };
+
+}} // namespace sol::stack
+
+// end of sol/stack_check_get_qualified.hpp
+
+// end of sol/stack_check_get.hpp
+
+// beginning of sol/stack_push.hpp
+
+#include <memory>
+#include <type_traits>
+#include <cassert>
+#include <limits>
+#include <cmath>
+#include <string_view>
+#if SOL_IS_ON(SOL_STD_VARIANT)
+#include <variant>
+#endif // Can use variant
+
+// beginning of sol/debug.hpp
+
+#include <iostream>
+
+namespace sol { namespace detail { namespace debug {
+ inline std::string dump_types(lua_State* L) {
+ std::string visual;
+ std::size_t size = lua_gettop(L) + 1;
+ for (std::size_t i = 1; i < size; ++i) {
+ if (i != 1) {
+ visual += " | ";
+ }
+ visual += type_name(L, stack::get<type>(L, static_cast<int>(i)));
+ }
+ return visual;
+ }
+
+ inline void print_stack(lua_State* L) {
+ std::cout << dump_types(L) << std::endl;
+ }
+
+ inline void print_section(const std::string& message, lua_State* L) {
+ std::cout << "-- " << message << " -- [ " << dump_types(L) << " ]" << std::endl;
+ }
+}}} // namespace sol::detail::debug
+
+// end of sol/debug.hpp
+
+namespace sol { namespace stack {
+ namespace stack_detail {
+ template <typename T>
+ inline bool integer_value_fits(const T& value) {
+ // We check if we can rely on casts or a lack of padding bits to satisfy
+ // the requirements here
+ // If it lacks padding bits, we can jump back and forth between lua_Integer and whatever type without
+ // loss of information
+ constexpr bool is_same_signedness
+ = (std::is_signed_v<T> && std::is_signed_v<lua_Integer>) || (std::is_unsigned_v<T> && std::is_unsigned_v<lua_Integer>);
+ constexpr bool probaby_fits_within_lua_Integer = sizeof(T) == sizeof(lua_Integer)
+#if SOL_IS_ON(SOL_ALL_INTEGER_VALUES_FIT)
+ && ((std::has_unique_object_representations_v<T> && std::has_unique_object_representations_v<lua_Integer>) ? true : is_same_signedness)
+#else
+ && is_same_signedness
+#endif
+ ;
+ if constexpr (sizeof(T) < sizeof(lua_Integer) || probaby_fits_within_lua_Integer) {
+ (void)value;
+ return true;
+ }
+ else {
+ auto u_min = static_cast<std::intmax_t>((std::numeric_limits<lua_Integer>::min)());
+ auto u_max = static_cast<std::uintmax_t>((std::numeric_limits<lua_Integer>::max)());
+ auto t_min = static_cast<std::intmax_t>((std::numeric_limits<T>::min)());
+ auto t_max = static_cast<std::uintmax_t>((std::numeric_limits<T>::max)());
+ return (u_min <= t_min || value >= static_cast<T>(u_min)) && (u_max >= t_max || value <= static_cast<T>(u_max));
+ }
+ }
+
+ template <typename T>
+ int msvc_is_ass_with_if_constexpr_push_enum(std::true_type, lua_State* L, const T& value) {
+ if constexpr (meta::any_same_v<std::underlying_type_t<T>,
+ char
+#if SOL_IS_ON(SOL_CHAR8_T)
+ ,
+ char8_t
+#endif
+ ,
+ char16_t,
+ char32_t>) {
+ if constexpr (std::is_signed_v<T>) {
+ return stack::push(L, static_cast<std::int_least32_t>(value));
+ }
+ else {
+ return stack::push(L, static_cast<std::uint_least32_t>(value));
+ }
+ }
+ else {
+ return stack::push(L, static_cast<std::underlying_type_t<T>>(value));
+ }
+ }
+
+ template <typename T>
+ int msvc_is_ass_with_if_constexpr_push_enum(std::false_type, lua_State*, const T&) {
+ return 0;
+ }
+ } // namespace stack_detail
+
+ inline int push_environment_of(lua_State* L, int target_index = -1) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_environment);
+#endif // make sure stack doesn't overflow
+#if SOL_LUA_VERSION_I_ < 502
+ // Use lua_getfenv
+ lua_getfenv(L, target_index);
+#else
+
+ if (lua_iscfunction(L, target_index) != 0) {
+ const char* maybe_upvalue_name = lua_getupvalue(L, target_index, 1);
+ if (maybe_upvalue_name != nullptr) {
+ // it worked, take this one
+ return 1;
+ }
+ }
+ // Nominally, we search for the `"_ENV"` value.
+ // If we don't find it.... uh, well. We've got a problem?
+ for (int upvalue_index = 1;; ++upvalue_index) {
+ const char* maybe_upvalue_name = lua_getupvalue(L, target_index, upvalue_index);
+ if (maybe_upvalue_name == nullptr) {
+ push(L, lua_nil);
+ break;
+ }
+
+ string_view upvalue_name(maybe_upvalue_name);
+ if (upvalue_name == "_ENV") {
+ // Keep this one!
+ break;
+ }
+ // Discard what we received, loop back around
+ lua_pop(L, 1);
+ }
+#endif
+ return 1;
+ }
+
+ template <typename T>
+ int push_environment_of(const T& target) {
+ lua_State* target_L = target.lua_state();
+ int target_index = absolute_index(target_L, -target.push());
+ int env_count = push_environment_of(target_L, target_index);
+ SOL_ASSERT(env_count == 1);
+ lua_rotate(target_L, target_index, 1);
+ lua_pop(target_L, 1);
+ return env_count;
+ }
+
+ template <typename T>
+ struct unqualified_pusher<detail::as_value_tag<T>> {
+ template <typename F, typename... Args>
+ static int push_fx(lua_State* L, F&& f, Args&&... args) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_userdata);
+#endif // make sure stack doesn't overflow
+ // Basically, we store all user-data like this:
+ // If it's a movable/copyable value (no std::ref(x)), then we store the pointer to the new
+ // data in the first sizeof(T*) bytes, and then however many bytes it takes to
+ // do the actual object. Things that are std::ref or plain T* are stored as
+ // just the sizeof(T*), and nothing else.
+ T* obj = detail::usertype_allocate<T>(L);
+ f();
+ std::allocator<T> alloc {};
+ std::allocator_traits<std::allocator<T>>::construct(alloc, obj, std::forward<Args>(args)...);
+ return 1;
+ }
+
+ template <typename K, typename... Args>
+ static int push_keyed(lua_State* L, K&& k, Args&&... args) {
+ stack_detail::undefined_metatable fx(L, &k[0], &stack::stack_detail::set_undefined_methods_on<T>);
+ return push_fx(L, fx, std::forward<Args>(args)...);
+ }
+
+ template <typename Arg, typename... Args>
+ static int push(lua_State* L, Arg&& arg, Args&&... args) {
+ if constexpr (std::is_same_v<meta::unqualified_t<Arg>, detail::with_function_tag>) {
+ (void)arg;
+ return push_fx(L, std::forward<Args>(args)...);
+ }
+ else {
+ return push_keyed(L, usertype_traits<T>::metatable(), std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+ }
+
+ static int push(lua_State* L) {
+ return push_keyed(L, usertype_traits<T>::metatable());
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<detail::as_pointer_tag<T>> {
+ typedef meta::unqualified_t<T> U;
+
+ template <typename F>
+ static int push_fx(lua_State* L, F&& f, T* obj) {
+ if (obj == nullptr)
+ return stack::push(L, lua_nil);
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_userdata);
+#endif // make sure stack doesn't overflow
+ T** pref = detail::usertype_allocate_pointer<T>(L);
+ f();
+ *pref = obj;
+ return 1;
+ }
+
+ template <typename K>
+ static int push_keyed(lua_State* L, K&& k, T* obj) {
+ stack_detail::undefined_metatable fx(L, &k[0], &stack::stack_detail::set_undefined_methods_on<U*>);
+ return push_fx(L, fx, obj);
+ }
+
+ template <typename Arg, typename... Args>
+ static int push(lua_State* L, Arg&& arg, Args&&... args) {
+ if constexpr (std::is_same_v<meta::unqualified_t<Arg>, detail::with_function_tag>) {
+ (void)arg;
+ return push_fx(L, std::forward<Args>(args)...);
+ }
+ else {
+ return push_keyed(L, usertype_traits<U*>::metatable(), std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<detail::as_reference_tag> {
+ template <typename T>
+ static int push(lua_State* L, T&& obj) {
+ return stack::push(L, detail::ptr(obj));
+ }
+ };
+
+ namespace stack_detail {
+ template <typename T>
+ struct uu_pusher {
+ using element = unique_usertype_element_t<T>;
+ using actual = unique_usertype_actual_t<T>;
+
+ template <typename Arg, typename... Args>
+ static int push(lua_State* L, Arg&& arg, Args&&... args) {
+ if constexpr (std::is_base_of_v<actual, meta::unqualified_t<Arg>>) {
+ if (detail::unique_is_null(L, arg)) {
+ return stack::push(L, lua_nil);
+ }
+ return push_deep(L, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+ else {
+ return push_deep(L, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+ }
+
+ template <typename... Args>
+ static int push_deep(lua_State* L, Args&&... args) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_userdata);
+#endif // make sure stack doesn't overflow
+ element** pointer_to_memory = nullptr;
+ detail::unique_destructor* fx = nullptr;
+ detail::unique_tag* id = nullptr;
+ actual* typed_memory = detail::usertype_unique_allocate<element, actual>(L, pointer_to_memory, fx, id);
+ if (luaL_newmetatable(L, &usertype_traits<d::u<std::remove_cv_t<element>>>::metatable()[0]) == 1) {
+ detail::lua_reg_table registration_table {};
+ int index = 0;
+ detail::indexed_insert insert_callable(registration_table, index);
+ detail::insert_default_registrations<element>(insert_callable, detail::property_always_true);
+ registration_table[index] = { to_string(meta_function::garbage_collect).c_str(), detail::make_destructor<T>() };
+ luaL_setfuncs(L, registration_table, 0);
+ }
+ lua_setmetatable(L, -2);
+ *fx = detail::usertype_unique_alloc_destroy<element, actual>;
+ *id = &detail::inheritance<element>::template type_unique_cast<actual>;
+ detail::default_construct::construct(typed_memory, std::forward<Args>(args)...);
+ *pointer_to_memory = detail::unique_get<T>(L, *typed_memory);
+ return 1;
+ }
+ };
+ } // namespace stack_detail
+
+ template <typename T>
+ struct unqualified_pusher<detail::as_unique_tag<T>> {
+ template <typename... Args>
+ static int push(lua_State* L, Args&&... args) {
+ stack_detail::uu_pusher<T> p;
+ (void)p;
+ return p.push(L, std::forward<Args>(args)...);
+ }
+ };
+
+ template <typename T, typename>
+ struct unqualified_pusher {
+ template <typename... Args>
+ static int push(lua_State* L, Args&&... args) {
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (is_lua_reference_v<Tu>) {
+ using int_arr = int[];
+ int_arr p { (std::forward<Args>(args).push(L))... };
+ return p[0];
+ }
+ else if constexpr (std::is_same_v<Tu, bool>) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushboolean(L, std::forward<Args>(args)...);
+ return 1;
+ }
+ else if constexpr (std::is_integral_v<Tu> || std::is_same_v<Tu, lua_Integer>) {
+ const Tu& value(std::forward<Args>(args)...);
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_integral);
+#endif // make sure stack doesn't overflow
+#if SOL_LUA_VERSION_I_ >= 503
+ if (stack_detail::integer_value_fits<Tu>(value)) {
+ lua_pushinteger(L, static_cast<lua_Integer>(value));
+ return 1;
+ }
+#endif // Lua 5.3 and above
+#if SOL_IS_ON(SOL_NUMBER_PRECISION_CHECKS)
+ if (static_cast<T>(llround(static_cast<lua_Number>(value))) != value) {
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ // Is this really worth it?
+ SOL_ASSERT_MSG(false, "integer value will be misrepresented in lua");
+ lua_pushinteger(L, static_cast<lua_Integer>(value));
+ return 1;
+#else
+ throw error(detail::direct_error, "integer value will be misrepresented in lua");
+#endif // No Exceptions
+ }
+#endif // Safe Numerics and Number Precision Check
+ lua_pushnumber(L, static_cast<lua_Number>(value));
+ return 1;
+ }
+ else if constexpr (std::is_floating_point_v<Tu> || std::is_same_v<Tu, lua_Number>) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_floating);
+#endif // make sure stack doesn't overflow
+ lua_pushnumber(L, std::forward<Args>(args)...);
+ return 1;
+ }
+ else if constexpr (std::is_same_v<Tu, luaL_Stream*>) {
+ luaL_Stream* source { std::forward<Args>(args)... };
+ luaL_Stream* stream = static_cast<luaL_Stream*>(detail::alloc_newuserdata(L, sizeof(luaL_Stream)));
+ stream->f = source->f;
+#if SOL_IS_ON(SOL_LUAL_STREAM_USE_CLOSE_FUNCTION)
+ stream->closef = source->closef;
+#endif // LuaJIT and Lua 5.1 and below do not have
+ return 1;
+ }
+ else if constexpr (std::is_same_v<Tu, luaL_Stream>) {
+ luaL_Stream& source(std::forward<Args>(args)...);
+ luaL_Stream* stream = static_cast<luaL_Stream*>(detail::alloc_newuserdata(L, sizeof(luaL_Stream)));
+ stream->f = source.f;
+#if SOL_IS_ON(SOL_LUAL_STREAM_USE_CLOSE_FUNCTION)
+ stream->closef = source.closef;
+#endif // LuaJIT and Lua 5.1 and below do not have
+ return 1;
+ }
+ else if constexpr (std::is_enum_v<Tu>) {
+ return stack_detail::msvc_is_ass_with_if_constexpr_push_enum(std::true_type(), L, std::forward<Args>(args)...);
+ }
+ else if constexpr (std::is_pointer_v<Tu>) {
+ return stack::push<detail::as_pointer_tag<std::remove_pointer_t<T>>>(L, std::forward<Args>(args)...);
+ }
+ else if constexpr (is_unique_usertype_v<Tu>) {
+ return stack::push<detail::as_unique_tag<T>>(L, std::forward<Args>(args)...);
+ }
+ else {
+ return stack::push<detail::as_value_tag<T>>(L, std::forward<Args>(args)...);
+ }
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<std::reference_wrapper<T>> {
+ static int push(lua_State* L, const std::reference_wrapper<T>& t) {
+ return stack::push(L, std::addressof(detail::deref(t.get())));
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<detail::as_table_tag<T>> {
+ using has_kvp = meta::has_key_value_pair<meta::unqualified_t<std::remove_pointer_t<T>>>;
+
+ static int push(lua_State* L, const T& tablecont) {
+ return push(has_kvp(), std::false_type(), L, tablecont);
+ }
+
+ static int push(lua_State* L, const T& tablecont, nested_tag_t) {
+ return push(has_kvp(), std::true_type(), L, tablecont);
+ }
+
+ static int push(std::true_type, lua_State* L, const T& tablecont) {
+ return push(has_kvp(), std::true_type(), L, tablecont);
+ }
+
+ static int push(std::false_type, lua_State* L, const T& tablecont) {
+ return push(has_kvp(), std::false_type(), L, tablecont);
+ }
+
+ template <bool is_nested>
+ static int push(std::true_type, std::integral_constant<bool, is_nested>, lua_State* L, const T& tablecont) {
+ auto& cont = detail::deref(detail::unwrap(tablecont));
+ lua_createtable(L, static_cast<int>(cont.size()), 0);
+ int tableindex = lua_gettop(L);
+ for (const auto& pair : cont) {
+ if (is_nested) {
+ set_field(L, pair.first, as_nested_ref(pair.second), tableindex);
+ }
+ else {
+ set_field(L, pair.first, pair.second, tableindex);
+ }
+ }
+ return 1;
+ }
+
+ template <bool is_nested>
+ static int push(std::false_type, std::integral_constant<bool, is_nested>, lua_State* L, const T& tablecont) {
+ auto& cont = detail::deref(detail::unwrap(tablecont));
+ lua_createtable(L, stack_detail::get_size_hint(cont), 0);
+ int tableindex = lua_gettop(L);
+ std::size_t index = 1;
+ for (const auto& i : cont) {
+#if SOL_LUA_VERSION_I_ >= 503
+ int p = is_nested ? stack::push(L, as_nested_ref(i)) : stack::push(L, i);
+ for (int pi = 0; pi < p; ++pi) {
+ lua_seti(L, tableindex, static_cast<lua_Integer>(index++));
+ }
+#else
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushinteger(L, static_cast<lua_Integer>(index));
+ int p = is_nested ? stack::push(L, as_nested_ref(i)) : stack::push(L, i);
+ if (p == 1) {
+ ++index;
+ lua_settable(L, tableindex);
+ }
+ else {
+ int firstindex = tableindex + 1 + 1;
+ for (int pi = 0; pi < p; ++pi) {
+ stack::push(L, index);
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushvalue(L, firstindex);
+ lua_settable(L, tableindex);
+ ++index;
+ ++firstindex;
+ }
+ lua_pop(L, 1 + p);
+ }
+#endif // Lua Version 5.3 and others
+ }
+ // TODO: figure out a better way to do this...?
+ // set_field(L, -1, cont.size());
+ return 1;
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<as_table_t<T>> {
+ static int push(lua_State* L, const as_table_t<T>& value_) {
+ using inner_t = std::remove_pointer_t<meta::unwrap_unqualified_t<T>>;
+ if constexpr (is_container_v<inner_t>) {
+ return stack::push<detail::as_table_tag<T>>(L, value_.value());
+ }
+ else {
+ return stack::push(L, value_.value());
+ }
+ }
+
+ static int push(lua_State* L, const T& value_) {
+ using inner_t = std::remove_pointer_t<meta::unwrap_unqualified_t<T>>;
+ if constexpr (is_container_v<inner_t>) {
+ return stack::push<detail::as_table_tag<T>>(L, value_);
+ }
+ else {
+ return stack::push(L, value_);
+ }
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<nested<T>> {
+ static int push(lua_State* L, const T& nested_value) noexcept {
+ using Tu = meta::unwrap_unqualified_t<T>;
+ using inner_t = std::remove_pointer_t<Tu>;
+ if constexpr (is_container_v<inner_t>) {
+ return stack::push<detail::as_table_tag<T>>(L, nested_value, nested_tag);
+ }
+ else {
+ return stack::push<Tu>(L, nested_value);
+ }
+ }
+
+ static int push(lua_State* L, const nested<T>& nested_wrapper_) noexcept {
+ using Tu = meta::unwrap_unqualified_t<T>;
+ using inner_t = std::remove_pointer_t<Tu>;
+ if constexpr (is_container_v<inner_t>) {
+ return stack::push<detail::as_table_tag<T>>(L, nested_wrapper_.value(), nested_tag);
+ }
+ else {
+ return stack::push<Tu>(L, nested_wrapper_.value());
+ }
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<std::initializer_list<T>> {
+ static int push(lua_State* L, const std::initializer_list<T>& il) noexcept {
+ unqualified_pusher<detail::as_table_tag<std::initializer_list<T>>> p {};
+ return p.push(L, il);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<lua_nil_t> {
+ static int push(lua_State* L, lua_nil_t) noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushnil(L);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<stack_count> {
+ static int push(lua_State*, stack_count st) noexcept {
+ return st.count;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<metatable_key_t> {
+ static int push(lua_State* L, metatable_key_t) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushlstring(L, to_string(meta_function::metatable).c_str(), 4);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<std::remove_pointer_t<lua_CFunction>> {
+ static int push(lua_State* L, lua_CFunction func, int n = 0) noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushcclosure(L, func, n);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<lua_CFunction> {
+ static int push(lua_State* L, lua_CFunction func, int n = 0) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushcclosure(L, func, n);
+ return 1;
+ }
+ };
+
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ template <>
+ struct unqualified_pusher<std::remove_pointer_t<detail::lua_CFunction_noexcept>> {
+ static int push(lua_State* L, detail::lua_CFunction_noexcept func, int n = 0) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushcclosure(L, func, n);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<detail::lua_CFunction_noexcept> {
+ static int push(lua_State* L, detail::lua_CFunction_noexcept func, int n = 0) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushcclosure(L, func, n);
+ return 1;
+ }
+ };
+#endif // noexcept function type
+
+ template <>
+ struct unqualified_pusher<c_closure> {
+ static int push(lua_State* L, c_closure cc) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushcclosure(L, cc.c_function, cc.upvalues);
+ return 1;
+ }
+ };
+
+ template <typename Arg, typename... Args>
+ struct unqualified_pusher<closure<Arg, Args...>> {
+ template <std::size_t... I, typename T>
+ static int push(std::index_sequence<I...>, lua_State* L, T&& c) {
+ using f_tuple = decltype(std::forward<T>(c).upvalues);
+ int pushcount = multi_push(L, std::get<I>(std::forward<f_tuple>(std::forward<T>(c).upvalues))...);
+ return stack::push(L, c_closure(c.c_function, pushcount));
+ }
+
+ template <typename T>
+ static int push(lua_State* L, T&& c) {
+ return push(std::make_index_sequence<1 + sizeof...(Args)>(), L, std::forward<T>(c));
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<void*> {
+ static int push(lua_State* L, void* userdata) noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushlightuserdata(L, userdata);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<const void*> {
+ static int push(lua_State* L, const void* userdata) noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushlightuserdata(L, const_cast<void*>(userdata));
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<lightuserdata_value> {
+ static int push(lua_State* L, lightuserdata_value userdata) noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushlightuserdata(L, userdata);
+ return 1;
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<light<T>> {
+ static int push(lua_State* L, light<T> l) noexcept {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushlightuserdata(L, static_cast<void*>(l.value()));
+ return 1;
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<user<T>> {
+ template <bool with_meta = true, typename Key, typename... Args>
+ static int push_with(lua_State* L, Key&& name, Args&&... args) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_userdata);
+#endif // make sure stack doesn't overflow
+ // A dumb pusher
+ T* data = detail::user_allocate<T>(L);
+ if (with_meta) {
+ // Make sure we have a plain GC set for this data
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ if (luaL_newmetatable(L, name) != 0) {
+ lua_CFunction cdel = detail::user_alloc_destroy<T>;
+ lua_pushcclosure(L, cdel, 0);
+ lua_setfield(L, -2, "__gc");
+ }
+ lua_setmetatable(L, -2);
+ }
+ std::allocator<T> alloc {};
+ std::allocator_traits<std::allocator<T>>::construct(alloc, data, std::forward<Args>(args)...);
+ return 1;
+ }
+
+ template <typename Arg, typename... Args>
+ static int push(lua_State* L, Arg&& arg, Args&&... args) {
+ if constexpr (std::is_same_v<meta::unqualified_t<Arg>, metatable_key_t>) {
+ const auto name = &arg[0];
+ return push_with<true>(L, name, std::forward<Args>(args)...);
+ }
+ else if constexpr (std::is_same_v<meta::unqualified_t<Arg>, no_metatable_t>) {
+ (void)arg;
+ const auto name = &usertype_traits<meta::unqualified_t<T>>::user_gc_metatable()[0];
+ return push_with<false>(L, name, std::forward<Args>(args)...);
+ }
+ else {
+ const auto name = &usertype_traits<meta::unqualified_t<T>>::user_gc_metatable()[0];
+ return push_with(L, name, std::forward<Arg>(arg), std::forward<Args>(args)...);
+ }
+ }
+
+ static int push(lua_State* L, const user<T>& u) {
+ const auto name = &usertype_traits<meta::unqualified_t<T>>::user_gc_metatable()[0];
+ return push_with(L, name, u.value);
+ }
+
+ static int push(lua_State* L, user<T>&& u) {
+ const auto name = &usertype_traits<meta::unqualified_t<T>>::user_gc_metatable()[0];
+ return push_with(L, name, std::move(u.value()));
+ }
+
+ static int push(lua_State* L, no_metatable_t, const user<T>& u) {
+ const auto name = &usertype_traits<meta::unqualified_t<T>>::user_gc_metatable()[0];
+ return push_with<false>(L, name, u.value());
+ }
+
+ static int push(lua_State* L, no_metatable_t, user<T>&& u) {
+ const auto name = &usertype_traits<meta::unqualified_t<T>>::user_gc_metatable()[0];
+ return push_with<false>(L, name, std::move(u.value()));
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<userdata_value> {
+ static int push(lua_State* L, userdata_value data) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_userdata);
+#endif // make sure stack doesn't overflow
+ void** ud = detail::usertype_allocate_pointer<void>(L);
+ *ud = data.value();
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<const char*> {
+ static int push_sized(lua_State* L, const char* str, std::size_t len) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_string);
+#endif // make sure stack doesn't overflow
+ lua_pushlstring(L, str, len);
+ return 1;
+ }
+
+ static int push(lua_State* L, const char* str) {
+ if (str == nullptr)
+ return stack::push(L, lua_nil);
+ return push_sized(L, str, std::char_traits<char>::length(str));
+ }
+
+ static int push(lua_State* L, const char* strb, const char* stre) {
+ return push_sized(L, strb, static_cast<std::size_t>(stre - strb));
+ }
+
+ static int push(lua_State* L, const char* str, std::size_t len) {
+ return push_sized(L, str, len);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<char*> {
+ static int push_sized(lua_State* L, const char* str, std::size_t len) {
+ unqualified_pusher<const char*> p {};
+ (void)p;
+ return p.push_sized(L, str, len);
+ }
+
+ static int push(lua_State* L, const char* str) {
+ unqualified_pusher<const char*> p {};
+ (void)p;
+ return p.push(L, str);
+ }
+
+ static int push(lua_State* L, const char* strb, const char* stre) {
+ unqualified_pusher<const char*> p {};
+ (void)p;
+ return p.push(L, strb, stre);
+ }
+
+ static int push(lua_State* L, const char* str, std::size_t len) {
+ unqualified_pusher<const char*> p {};
+ (void)p;
+ return p.push(L, str, len);
+ }
+ };
+
+ template <size_t N>
+ struct unqualified_pusher<char[N]> {
+ static int push(lua_State* L, const char (&str)[N]) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_string);
+#endif // make sure stack doesn't overflow
+ lua_pushlstring(L, str, std::char_traits<char>::length(str));
+ return 1;
+ }
+
+ static int push(lua_State* L, const char (&str)[N], std::size_t sz) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_string);
+#endif // make sure stack doesn't overflow
+ lua_pushlstring(L, str, sz);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<char> {
+ static int push(lua_State* L, char c) {
+ const char str[2] = { c, '\0' };
+ return stack::push(L, static_cast<const char*>(str), 1u);
+ }
+ };
+
+#if SOL_IS_ON(SOL_CHAR8_T)
+ template <>
+ struct unqualified_pusher<const char8_t*> {
+ static int push_sized(lua_State* L, const char8_t* str, std::size_t len) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_string);
+#endif // make sure stack doesn't overflow
+ lua_pushlstring(L, reinterpret_cast<const char*>(str), len);
+ return 1;
+ }
+
+ static int push(lua_State* L, const char8_t* str) {
+ if (str == nullptr)
+ return stack::push(L, lua_nil);
+ return push_sized(L, str, std::char_traits<char>::length(reinterpret_cast<const char*>(str)));
+ }
+
+ static int push(lua_State* L, const char8_t* strb, const char8_t* stre) {
+ return push_sized(L, strb, static_cast<std::size_t>(stre - strb));
+ }
+
+ static int push(lua_State* L, const char8_t* str, std::size_t len) {
+ return push_sized(L, str, len);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<char8_t*> {
+ static int push_sized(lua_State* L, const char8_t* str, std::size_t len) {
+ unqualified_pusher<const char8_t*> p {};
+ (void)p;
+ return p.push_sized(L, str, len);
+ }
+
+ static int push(lua_State* L, const char8_t* str) {
+ unqualified_pusher<const char8_t*> p {};
+ (void)p;
+ return p.push(L, str);
+ }
+
+ static int push(lua_State* L, const char8_t* strb, const char8_t* stre) {
+ unqualified_pusher<const char8_t*> p {};
+ (void)p;
+ return p.push(L, strb, stre);
+ }
+
+ static int push(lua_State* L, const char8_t* str, std::size_t len) {
+ unqualified_pusher<const char8_t*> p {};
+ (void)p;
+ return p.push(L, str, len);
+ }
+ };
+
+ template <size_t N>
+ struct unqualified_pusher<char8_t[N]> {
+ static int push(lua_State* L, const char8_t (&str)[N]) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_string);
+#endif // make sure stack doesn't overflow
+ const char* str_as_char = reinterpret_cast<const char*>(static_cast<const char8_t*>(str));
+ lua_pushlstring(L, str_as_char, std::char_traits<char>::length(str_as_char));
+ return 1;
+ }
+
+ static int push(lua_State* L, const char8_t (&str)[N], std::size_t sz) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_string);
+#endif // make sure stack doesn't overflow
+ lua_pushlstring(L, str, sz);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<char8_t> {
+ static int push(lua_State* L, char8_t c) {
+ const char8_t str[2] = { c, '\0' };
+ return stack::push(L, static_cast<const char8_t*>(str), 1u);
+ }
+ };
+#endif // char8_t
+
+ template <typename Ch, typename Traits, typename Al>
+ struct unqualified_pusher<std::basic_string<Ch, Traits, Al>> {
+ static int push(lua_State* L, const std::basic_string<Ch, Traits, Al>& str) {
+ if constexpr (!std::is_same_v<Ch, char>) {
+ return stack::push(L, str.data(), str.size());
+ }
+ else {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_string);
+#endif // make sure stack doesn't overflow
+ lua_pushlstring(L, str.c_str(), str.size());
+ return 1;
+ }
+ }
+
+ static int push(lua_State* L, const std::basic_string<Ch, Traits, Al>& str, std::size_t sz) {
+ if constexpr (!std::is_same_v<Ch, char>) {
+ return stack::push(L, str.data(), sz);
+ }
+ else {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_string);
+#endif // make sure stack doesn't overflow
+ lua_pushlstring(L, str.c_str(), sz);
+ return 1;
+ }
+ }
+ };
+
+ template <typename Ch, typename Traits>
+ struct unqualified_pusher<basic_string_view<Ch, Traits>> {
+ static int push(lua_State* L, const basic_string_view<Ch, Traits>& sv) {
+ return stack::push(L, sv.data(), sv.length());
+ }
+
+ static int push(lua_State* L, const basic_string_view<Ch, Traits>& sv, std::size_t n) {
+ return stack::push(L, sv.data(), n);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<meta_function> {
+ static int push(lua_State* L, meta_function m) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_meta_function_name);
+#endif // make sure stack doesn't overflow
+ const std::string& str = to_string(m);
+ lua_pushlstring(L, str.c_str(), str.size());
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<absolute_index> {
+ static int push(lua_State* L, absolute_index ai) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushvalue(L, ai);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<raw_index> {
+ static int push(lua_State* L, raw_index ri) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushvalue(L, ri);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<ref_index> {
+ static int push(lua_State* L, ref_index ri) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_rawgeti(L, LUA_REGISTRYINDEX, ri);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<const wchar_t*> {
+ static int push(lua_State* L, const wchar_t* wstr) {
+ return push(L, wstr, std::char_traits<wchar_t>::length(wstr));
+ }
+
+ static int push(lua_State* L, const wchar_t* wstr, std::size_t sz) {
+ return push(L, wstr, wstr + sz);
+ }
+
+ static int push(lua_State* L, const wchar_t* strb, const wchar_t* stre) {
+ if constexpr (sizeof(wchar_t) == 2) {
+ const char16_t* sb = reinterpret_cast<const char16_t*>(strb);
+ const char16_t* se = reinterpret_cast<const char16_t*>(stre);
+ return stack::push(L, sb, se);
+ }
+ else {
+ const char32_t* sb = reinterpret_cast<const char32_t*>(strb);
+ const char32_t* se = reinterpret_cast<const char32_t*>(stre);
+ return stack::push(L, sb, se);
+ }
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<wchar_t*> {
+ static int push(lua_State* L, const wchar_t* str) {
+ unqualified_pusher<const wchar_t*> p {};
+ (void)p;
+ return p.push(L, str);
+ }
+
+ static int push(lua_State* L, const wchar_t* strb, const wchar_t* stre) {
+ unqualified_pusher<const wchar_t*> p {};
+ (void)p;
+ return p.push(L, strb, stre);
+ }
+
+ static int push(lua_State* L, const wchar_t* str, std::size_t len) {
+ unqualified_pusher<const wchar_t*> p {};
+ (void)p;
+ return p.push(L, str, len);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<const char16_t*> {
+ static int convert_into(lua_State* L, char* start, std::size_t, const char16_t* strb, const char16_t* stre) {
+ char* target = start;
+ char32_t cp = 0;
+ for (const char16_t* strtarget = strb; strtarget < stre;) {
+ auto dr = unicode::utf16_to_code_point(strtarget, stre);
+ if (dr.error != unicode::error_code::ok) {
+ cp = unicode::unicode_detail::replacement;
+ }
+ else {
+ cp = dr.codepoint;
+ }
+ auto er = unicode::code_point_to_utf8(cp);
+ const char* utf8data = er.code_units.data();
+ std::memcpy(target, utf8data, er.code_units_size);
+ target += er.code_units_size;
+ strtarget = dr.next;
+ }
+
+ return stack::push(L, start, target);
+ }
+
+ static int push(lua_State* L, const char16_t* u16str) {
+ return push(L, u16str, std::char_traits<char16_t>::length(u16str));
+ }
+
+ static int push(lua_State* L, const char16_t* u16str, std::size_t sz) {
+ return push(L, u16str, u16str + sz);
+ }
+
+ static int push(lua_State* L, const char16_t* strb, const char16_t* stre) {
+ char sbo[SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_];
+ // if our max string space is small enough, use SBO
+ // right off the bat
+ std::size_t max_possible_code_units = static_cast<std::size_t>(static_cast<std::size_t>(stre - strb) * static_cast<std::size_t>(4));
+ if (max_possible_code_units <= SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_) {
+ return convert_into(L, sbo, max_possible_code_units, strb, stre);
+ }
+ // otherwise, we must manually count/check size
+ std::size_t needed_size = 0;
+ for (const char16_t* strtarget = strb; strtarget < stre;) {
+ auto dr = unicode::utf16_to_code_point(strtarget, stre);
+ auto er = unicode::code_point_to_utf8(dr.codepoint);
+ needed_size += er.code_units_size;
+ strtarget = dr.next;
+ }
+ if (needed_size < SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_) {
+ return convert_into(L, sbo, needed_size, strb, stre);
+ }
+ std::string u8str("", 0);
+ u8str.resize(needed_size);
+ char* target = const_cast<char*>(u8str.data());
+ return convert_into(L, target, needed_size, strb, stre);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<char16_t*> {
+ static int push(lua_State* L, const char16_t* str) {
+ unqualified_pusher<const char16_t*> p {};
+ (void)p;
+ return p.push(L, str);
+ }
+
+ static int push(lua_State* L, const char16_t* strb, const char16_t* stre) {
+ unqualified_pusher<const char16_t*> p {};
+ (void)p;
+ return p.push(L, strb, stre);
+ }
+
+ static int push(lua_State* L, const char16_t* str, std::size_t len) {
+ unqualified_pusher<const char16_t*> p {};
+ (void)p;
+ return p.push(L, str, len);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<const char32_t*> {
+ static int convert_into(lua_State* L, char* start, std::size_t, const char32_t* strb, const char32_t* stre) {
+ char* target = start;
+ char32_t cp = 0;
+ for (const char32_t* strtarget = strb; strtarget < stre;) {
+ auto dr = unicode::utf32_to_code_point(strtarget, stre);
+ if (dr.error != unicode::error_code::ok) {
+ cp = unicode::unicode_detail::replacement;
+ }
+ else {
+ cp = dr.codepoint;
+ }
+ auto er = unicode::code_point_to_utf8(cp);
+ const char* data = er.code_units.data();
+ std::memcpy(target, data, er.code_units_size);
+ target += er.code_units_size;
+ strtarget = dr.next;
+ }
+ return stack::push(L, start, target);
+ }
+
+ static int push(lua_State* L, const char32_t* u32str) {
+ return push(L, u32str, u32str + std::char_traits<char32_t>::length(u32str));
+ }
+
+ static int push(lua_State* L, const char32_t* u32str, std::size_t sz) {
+ return push(L, u32str, u32str + sz);
+ }
+
+ static int push(lua_State* L, const char32_t* strb, const char32_t* stre) {
+ char sbo[SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_];
+ // if our max string space is small enough, use SBO
+ // right off the bat
+ std::size_t max_possible_code_units = static_cast<std::size_t>(static_cast<std::size_t>(stre - strb) * static_cast<std::size_t>(4));
+ if (max_possible_code_units <= SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_) {
+ return convert_into(L, sbo, max_possible_code_units, strb, stre);
+ }
+ // otherwise, we must manually count/check size
+ std::size_t needed_size = 0;
+ for (const char32_t* strtarget = strb; strtarget < stre;) {
+ auto dr = unicode::utf32_to_code_point(strtarget, stre);
+ auto er = unicode::code_point_to_utf8(dr.codepoint);
+ needed_size += er.code_units_size;
+ strtarget = dr.next;
+ }
+ if (needed_size < SOL_OPTIMIZATION_STRING_CONVERSION_STACK_SIZE_I_) {
+ return convert_into(L, sbo, needed_size, strb, stre);
+ }
+ std::string u8str("", 0);
+ u8str.resize(needed_size);
+ char* target = const_cast<char*>(u8str.data());
+ return convert_into(L, target, needed_size, strb, stre);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<char32_t*> {
+ static int push(lua_State* L, const char32_t* str) {
+ unqualified_pusher<const char32_t*> p {};
+ (void)p;
+ return p.push(L, str);
+ }
+
+ static int push(lua_State* L, const char32_t* strb, const char32_t* stre) {
+ unqualified_pusher<const char32_t*> p {};
+ (void)p;
+ return p.push(L, strb, stre);
+ }
+
+ static int push(lua_State* L, const char32_t* str, std::size_t len) {
+ unqualified_pusher<const char32_t*> p {};
+ (void)p;
+ return p.push(L, str, len);
+ }
+ };
+
+ template <size_t N>
+ struct unqualified_pusher<wchar_t[N]> {
+ static int push(lua_State* L, const wchar_t (&str)[N]) {
+ return push(L, str, std::char_traits<wchar_t>::length(str));
+ }
+
+ static int push(lua_State* L, const wchar_t (&str)[N], std::size_t sz) {
+ const wchar_t* str_ptr = static_cast<const wchar_t*>(str);
+ return stack::push<const wchar_t*>(L, str_ptr, str_ptr + sz);
+ }
+ };
+
+ template <size_t N>
+ struct unqualified_pusher<char16_t[N]> {
+ static int push(lua_State* L, const char16_t (&str)[N]) {
+ return push(L, str, std::char_traits<char16_t>::length(str));
+ }
+
+ static int push(lua_State* L, const char16_t (&str)[N], std::size_t sz) {
+ const char16_t* str_ptr = static_cast<const char16_t*>(str);
+ return stack::push<const char16_t*>(L, str_ptr, str_ptr + sz);
+ }
+ };
+
+ template <size_t N>
+ struct unqualified_pusher<char32_t[N]> {
+ static int push(lua_State* L, const char32_t (&str)[N]) {
+ return push(L, str, std::char_traits<char32_t>::length(str));
+ }
+
+ static int push(lua_State* L, const char32_t (&str)[N], std::size_t sz) {
+ const char32_t* str_ptr = static_cast<const char32_t*>(str);
+ return stack::push<const char32_t*>(L, str_ptr, str_ptr + sz);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<wchar_t> {
+ static int push(lua_State* L, wchar_t c) {
+ const wchar_t str[2] = { c, '\0' };
+ return stack::push(L, static_cast<const wchar_t*>(str), 1u);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<char16_t> {
+ static int push(lua_State* L, char16_t c) {
+ const char16_t str[2] = { c, '\0' };
+ return stack::push(L, static_cast<const char16_t*>(str), 1u);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<char32_t> {
+ static int push(lua_State* L, char32_t c) {
+ const char32_t str[2] = { c, '\0' };
+ return stack::push(L, static_cast<const char32_t*>(str), 1u);
+ }
+ };
+
+ template <typename... Args>
+ struct unqualified_pusher<std::tuple<Args...>> {
+ template <std::size_t... I, typename T>
+ static int push(std::index_sequence<I...>, lua_State* L, T&& t) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, static_cast<int>(sizeof...(I)), detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ int pushcount = 0;
+ (void)detail::swallow { 0, (pushcount += stack::push(L, std::get<I>(std::forward<T>(t))), 0)... };
+ return pushcount;
+ }
+
+ template <typename T>
+ static int push(lua_State* L, T&& t) {
+ return push(std::index_sequence_for<Args...>(), L, std::forward<T>(t));
+ }
+ };
+
+ template <typename A, typename B>
+ struct unqualified_pusher<std::pair<A, B>> {
+ template <typename T>
+ static int push(lua_State* L, T&& t) {
+ int pushcount = stack::push(L, std::get<0>(std::forward<T>(t)));
+ pushcount += stack::push(L, std::get<1>(std::forward<T>(t)));
+ return pushcount;
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<T, std::enable_if_t<meta::is_optional_v<T>>> {
+ using ValueType = typename meta::unqualified_t<T>::value_type;
+
+ template <typename Optional>
+ static int push(lua_State* L, Optional&& op) {
+ using QualifiedValueType = meta::conditional_t<std::is_lvalue_reference_v<Optional>, ValueType&, ValueType&&>;
+ if (!op) {
+ return stack::push(L, nullopt);
+ }
+ return stack::push(L, static_cast<QualifiedValueType>(op.value()));
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<forward_as_value_t<T>> {
+ static int push(lua_State* L, const forward_as_value_t<T>& value_) {
+ return stack::push<T>(L, value_.value());
+ }
+
+ static int push(lua_State* L, forward_as_value_t<T>&& value_) {
+ return stack::push<T>(L, std::move(value_).value());
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<nullopt_t> {
+ static int push(lua_State* L, nullopt_t) noexcept {
+ return stack::push(L, lua_nil);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<std::nullptr_t> {
+ static int push(lua_State* L, std::nullptr_t) noexcept {
+ return stack::push(L, lua_nil);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<this_state> {
+ static int push(lua_State*, const this_state&) noexcept {
+ return 0;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<this_main_state> {
+ static int push(lua_State*, const this_main_state&) noexcept {
+ return 0;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<new_table> {
+ static int push(lua_State* L, const new_table& nt) {
+ lua_createtable(L, nt.sequence_hint, nt.map_hint);
+ return 1;
+ }
+ };
+
+ template <typename Allocator>
+ struct unqualified_pusher<basic_bytecode<Allocator>> {
+ template <typename T>
+ static int push(lua_State* L, T&& bc, const char* bytecode_name) {
+ const auto first = bc.data();
+ const auto bcsize = bc.size();
+ // pushes either the function, or an error
+ // if it errors, shit goes south, and people can test that upstream
+ (void)luaL_loadbuffer(
+ L, reinterpret_cast<const char*>(first), static_cast<std::size_t>(bcsize * (sizeof(*first) / sizeof(const char))), bytecode_name);
+ return 1;
+ }
+
+ template <typename T>
+ static int push(lua_State* L, T&& bc) {
+ return push(L, std::forward<bc>(bc), "bytecode");
+ }
+ };
+
+#if SOL_IS_ON(SOL_STD_VARIANT)
+ namespace stack_detail {
+
+ struct push_function {
+ lua_State* L;
+
+ push_function(lua_State* L_) noexcept : L(L_) {
+ }
+
+ template <typename T>
+ int operator()(T&& value) const {
+ return stack::push<T>(L, std::forward<T>(value));
+ }
+ };
+
+ } // namespace stack_detail
+
+ template <typename... Tn>
+ struct unqualified_pusher<std::variant<Tn...>> {
+ static int push(lua_State* L, const std::variant<Tn...>& v) {
+ return std::visit(stack_detail::push_function(L), v);
+ }
+
+ static int push(lua_State* L, std::variant<Tn...>&& v) {
+ return std::visit(stack_detail::push_function(L), std::move(v));
+ }
+ };
+#endif // Variant because Clang is terrible
+
+}} // namespace sol::stack
+
+// end of sol/stack_push.hpp
+
+// beginning of sol/stack_pop.hpp
+
+#include <utility>
+#include <tuple>
+
+namespace sol { namespace stack {
+ template <typename T, typename>
+ struct popper {
+ inline static decltype(auto) pop(lua_State* L) {
+ if constexpr (is_stack_based_v<meta::unqualified_t<T>>) {
+ static_assert(!is_stack_based_v<meta::unqualified_t<T>>,
+ "You cannot pop something that lives solely on the stack: it will not remain on the stack when popped and thusly will go out of "
+ "scope!");
+ }
+ else {
+ record tracking {};
+ decltype(auto) r = get<T>(L, -lua_size<T>::value, tracking);
+ lua_pop(L, tracking.used);
+ return r;
+ }
+ }
+ };
+}} // namespace sol::stack
+
+// end of sol/stack_pop.hpp
+
+// beginning of sol/stack_field.hpp
+
+namespace sol { namespace stack {
+
+ namespace stack_detail {
+ template <typename T, bool global, bool raw>
+ inline constexpr bool is_get_direct_tableless_v = (global && !raw && meta::is_c_str_or_string_v<T>);
+
+ template <typename T, bool global, bool raw>
+ inline constexpr bool is_get_direct_v = (is_get_direct_tableless_v<T, global, raw>) // cf-hack
+ || (!global && !raw && (meta::is_c_str_or_string_v<T> || meta::is_string_of_v<T, char>)) // cf-hack
+ || (!global && raw && (std::is_integral_v<T> && !std::is_same_v<T, bool>))
+#if SOL_LUA_VERSION_I_ >= 503
+ || (!global && !raw && (std::is_integral_v<T> && !std::is_same_v<T, bool>))
+#endif // integer keys 5.3 or better
+#if SOL_LUA_VERSION_I_ >= 502
+ || (!global && raw && std::is_pointer_v<T> && std::is_void_v<std::remove_pointer_t<T>>)
+#endif // void pointer keys 5.2 or better
+ ;
+
+ template <typename T, bool global, bool raw>
+ inline constexpr bool is_set_direct_tableless_v = (global && !raw && meta::is_c_str_or_string_v<T>);
+
+ template <typename T, bool global, bool raw>
+ inline constexpr bool is_set_direct_v = (is_set_direct_tableless_v<T, global, raw>) // cf-hack
+ || (!global && !raw && (meta::is_c_str_or_string_v<T> || meta::is_string_of_v<T, char>)) // cf-hack
+ || (!global && raw && (std::is_integral_v<T> && !std::is_same_v<T, bool>)) // cf-hack
+#if SOL_LUA_VERSION_I_ >= 503
+ || (!global && !raw && (std::is_integral_v<T> && !std::is_same_v<T, bool>))
+#endif // integer keys 5.3 or better
+#if SOL_LUA_VERSION_I_ >= 502
+ || (!global && raw && (std::is_pointer_v<T> && std::is_void_v<std::remove_pointer_t<T>>))
+#endif // void pointer keys 5.2 or better
+ ;
+ } // namespace stack_detail
+
+ template <typename T, bool global, bool raw, typename>
+ struct field_getter {
+ static inline constexpr int default_table_index
+ = meta::conditional_t<stack_detail::is_get_direct_v<T, global, raw>, std::integral_constant<int, -1>, std::integral_constant<int, -2>>::value;
+
+ template <typename Key>
+ void get(lua_State* L, Key&& key, int tableindex = default_table_index) {
+ if constexpr (std::is_same_v<T, update_if_empty_t> || std::is_same_v<T, override_value_t> || std::is_same_v<T, create_if_nil_t>) {
+ (void)L;
+ (void)key;
+ (void)tableindex;
+ }
+ else if constexpr (std::is_same_v<T, env_key_t>) {
+ (void)key;
+#if SOL_LUA_VERSION_I_ < 502
+ // Use lua_setfenv
+ lua_getfenv(L, tableindex);
+#else
+ // Use upvalues as explained in Lua 5.2 and beyond's manual
+ if (lua_getupvalue(L, tableindex, 1) == nullptr) {
+ push(L, lua_nil);
+ }
+#endif
+ }
+ else if constexpr (std::is_same_v<T, metatable_key_t>) {
+ (void)key;
+ if (lua_getmetatable(L, tableindex) == 0)
+ push(L, lua_nil);
+ }
+ else if constexpr (raw) {
+ if constexpr (std::is_integral_v<T> && !std::is_same_v<bool, T>) {
+ lua_rawgeti(L, tableindex, static_cast<lua_Integer>(key));
+ }
+#if SOL_LUA_VERSION_I_ >= 502
+ else if constexpr (std::is_pointer_v<T> && std::is_void_v<std::remove_pointer_t<T>>) {
+ lua_rawgetp(L, tableindex, key);
+ }
+#endif // Lua 5.2.x+
+ else {
+ push(L, std::forward<Key>(key));
+ lua_rawget(L, tableindex);
+ }
+ }
+ else {
+ if constexpr (meta::is_c_str_or_string_v<T>) {
+ if constexpr (global) {
+ (void)tableindex;
+ lua_getglobal(L, &key[0]);
+ }
+ else {
+ lua_getfield(L, tableindex, &key[0]);
+ }
+ }
+ else if constexpr (std::is_same_v<T, meta_function>) {
+ const auto& real_key = to_string(key);
+ lua_getfield(L, tableindex, &real_key[0]);
+ }
+#if SOL_LUA_VERSION_I_ >= 503
+ else if constexpr (std::is_integral_v<T> && !std::is_same_v<bool, T>) {
+ lua_geti(L, tableindex, static_cast<lua_Integer>(key));
+ }
+#endif // Lua 5.3.x+
+ else {
+ push(L, std::forward<Key>(key));
+ lua_gettable(L, tableindex);
+ }
+ }
+ }
+ };
+
+ template <typename... Args, bool b, bool raw, typename C>
+ struct field_getter<std::tuple<Args...>, b, raw, C> {
+ template <std::size_t... I, typename Keys>
+ void apply(std::index_sequence<0, I...>, lua_State* L, Keys&& keys, int tableindex) {
+ get_field<b, raw>(L, std::get<0>(std::forward<Keys>(keys)), tableindex);
+ void(detail::swallow { (get_field<false, raw>(L, std::get<I>(std::forward<Keys>(keys))), 0)... });
+ reference saved(L, -1);
+ lua_pop(L, static_cast<int>(sizeof...(I)));
+ saved.push();
+ }
+
+ template <typename Keys>
+ void get(lua_State* L, Keys&& keys) {
+ apply(std::make_index_sequence<sizeof...(Args)>(), L, std::forward<Keys>(keys), lua_absindex(L, -1));
+ }
+
+ template <typename Keys>
+ void get(lua_State* L, Keys&& keys, int tableindex) {
+ apply(std::make_index_sequence<sizeof...(Args)>(), L, std::forward<Keys>(keys), tableindex);
+ }
+ };
+
+ template <typename A, typename B, bool b, bool raw, typename C>
+ struct field_getter<std::pair<A, B>, b, raw, C> {
+ template <typename Keys>
+ void get(lua_State* L, Keys&& keys, int tableindex) {
+ get_field<b, raw>(L, std::get<0>(std::forward<Keys>(keys)), tableindex);
+ get_field<false, raw>(L, std::get<1>(std::forward<Keys>(keys)));
+ reference saved(L, -1);
+ lua_pop(L, static_cast<int>(2));
+ saved.push();
+ }
+
+ template <typename Keys>
+ void get(lua_State* L, Keys&& keys) {
+ get_field<b, raw>(L, std::get<0>(std::forward<Keys>(keys)));
+ get_field<false, raw>(L, std::get<1>(std::forward<Keys>(keys)));
+ reference saved(L, -1);
+ lua_pop(L, static_cast<int>(2));
+ saved.push();
+ }
+ };
+
+ template <typename T, bool global, bool raw, typename>
+ struct field_setter {
+ static constexpr int default_table_index
+ = meta::conditional_t<stack_detail::is_set_direct_v<T, global, raw>, std::integral_constant<int, -2>, std::integral_constant<int, -3>>::value;
+
+ template <typename Key, typename Value>
+ void set(lua_State* L, Key&& key, Value&& value, int tableindex = default_table_index) {
+ if constexpr (std::is_same_v<T, update_if_empty_t> || std::is_same_v<T, override_value_t>) {
+ (void)L;
+ (void)key;
+ (void)value;
+ (void)tableindex;
+ }
+ else if constexpr (std::is_same_v<T, metatable_key_t>) {
+ (void)key;
+ push(L, std::forward<Value>(value));
+ lua_setmetatable(L, tableindex);
+ }
+ else if constexpr (raw) {
+ if constexpr (std::is_integral_v<T> && !std::is_same_v<bool, T>) {
+ push(L, std::forward<Value>(value));
+ lua_rawseti(L, tableindex, static_cast<lua_Integer>(key));
+ }
+#if SOL_LUA_VERSION_I_ >= 502
+ else if constexpr (std::is_pointer_v<T> && std::is_void_v<std::remove_pointer_t<T>>) {
+ push(L, std::forward<Value>(value));
+ lua_rawsetp(L, tableindex, std::forward<Key>(key));
+ }
+#endif // Lua 5.2.x
+ else {
+ push(L, std::forward<Key>(key));
+ push(L, std::forward<Value>(value));
+ lua_rawset(L, tableindex);
+ }
+ }
+ else {
+ if constexpr (meta::is_c_str_or_string_v<T>) {
+ if constexpr (global) {
+ push(L, std::forward<Value>(value));
+ lua_setglobal(L, &key[0]);
+ (void)tableindex;
+ }
+ else {
+ push(L, std::forward<Value>(value));
+ lua_setfield(L, tableindex, &key[0]);
+ }
+ }
+#if SOL_LUA_VERSION_I_ >= 503
+ else if constexpr (std::is_integral_v<T> && !std::is_same_v<bool, T>) {
+ push(L, std::forward<Value>(value));
+ lua_seti(L, tableindex, static_cast<lua_Integer>(key));
+ }
+#endif // Lua 5.3.x
+ else {
+ push(L, std::forward<Key>(key));
+ push(L, std::forward<Value>(value));
+ lua_settable(L, tableindex);
+ }
+ }
+ }
+ };
+
+ template <typename... Args, bool b, bool raw, typename C>
+ struct field_setter<std::tuple<Args...>, b, raw, C> {
+ template <bool g, std::size_t I, typename Keys, typename Value>
+ void apply(std::index_sequence<I>, lua_State* L, Keys&& keys, Value&& value, int tableindex) {
+ I < 1 ? set_field<g, raw>(L, std::get<I>(std::forward<Keys>(keys)), std::forward<Value>(value), tableindex)
+ : set_field<g, raw>(L, std::get<I>(std::forward<Keys>(keys)), std::forward<Value>(value));
+ }
+
+ template <bool g, std::size_t I0, std::size_t I1, std::size_t... I, typename Keys, typename Value>
+ void apply(std::index_sequence<I0, I1, I...>, lua_State* L, Keys&& keys, Value&& value, int tableindex) {
+ I0 < 1 ? get_field<g, raw>(L, std::get<I0>(std::forward<Keys>(keys)), tableindex)
+ : get_field<g, raw>(L, std::get<I0>(std::forward<Keys>(keys)), -1);
+ apply<false>(std::index_sequence<I1, I...>(), L, std::forward<Keys>(keys), std::forward<Value>(value), -1);
+ }
+
+ template <bool g, std::size_t I0, std::size_t... I, typename Keys, typename Value>
+ void top_apply(std::index_sequence<I0, I...>, lua_State* L, Keys&& keys, Value&& value, int tableindex) {
+ apply<g>(std::index_sequence<I0, I...>(), L, std::forward<Keys>(keys), std::forward<Value>(value), tableindex);
+ lua_pop(L, static_cast<int>(sizeof...(I)));
+ }
+
+ template <typename Keys, typename Value>
+ void set(lua_State* L, Keys&& keys, Value&& value, int tableindex = -3) {
+ top_apply<b>(std::make_index_sequence<sizeof...(Args)>(), L, std::forward<Keys>(keys), std::forward<Value>(value), tableindex);
+ }
+ };
+
+ template <typename A, typename B, bool b, bool raw, typename C>
+ struct field_setter<std::pair<A, B>, b, raw, C> {
+ template <typename Keys, typename Value>
+ void set(lua_State* L, Keys&& keys, Value&& value, int tableindex = -1) {
+ get_field<b, raw>(L, std::get<0>(std::forward<Keys>(keys)), tableindex);
+ set_field<false, raw>(L, std::get<1>(std::forward<Keys>(keys)), std::forward<Value>(value), lua_gettop(L));
+ lua_pop(L, 1);
+ }
+ };
+}} // namespace sol::stack
+
+// end of sol/stack_field.hpp
+
+// beginning of sol/stack_probe.hpp
+
+namespace sol { namespace stack {
+ template <typename T, typename P, bool b, bool raw, typename>
+ struct probe_field_getter {
+ template <typename Key>
+ probe get(lua_State* L, Key&& key, int tableindex = -2) {
+ if constexpr (!b) {
+ if (!maybe_indexable(L, tableindex)) {
+ return probe(false, 0);
+ }
+ }
+ get_field<b, raw>(L, std::forward<Key>(key), tableindex);
+ return probe(check<P>(L), 1);
+ }
+ };
+
+ template <typename A, typename B, typename P, bool b, bool raw, typename C>
+ struct probe_field_getter<std::pair<A, B>, P, b, raw, C> {
+ template <typename Keys>
+ probe get(lua_State* L, Keys&& keys, int tableindex = -2) {
+ if (!b && !maybe_indexable(L, tableindex)) {
+ return probe(false, 0);
+ }
+ get_field<b, raw>(L, std::get<0>(keys), tableindex);
+ if (!maybe_indexable(L)) {
+ return probe(false, 1);
+ }
+ get_field<false, raw>(L, std::get<1>(keys), tableindex);
+ return probe(check<P>(L), 2);
+ }
+ };
+
+ template <typename... Args, typename P, bool b, bool raw, typename C>
+ struct probe_field_getter<std::tuple<Args...>, P, b, raw, C> {
+ template <std::size_t I, typename Keys>
+ probe apply(std::index_sequence<I>, int sofar, lua_State* L, Keys&& keys, int tableindex) {
+ get_field<(I < 1) && b, raw>(L, std::get<I>(keys), tableindex);
+ return probe(check<P>(L), sofar);
+ }
+
+ template <std::size_t I, std::size_t I1, std::size_t... In, typename Keys>
+ probe apply(std::index_sequence<I, I1, In...>, int sofar, lua_State* L, Keys&& keys, int tableindex) {
+ get_field < I<1 && b, raw>(L, std::get<I>(keys), tableindex);
+ if (!maybe_indexable(L)) {
+ return probe(false, sofar);
+ }
+ return apply(std::index_sequence<I1, In...>(), sofar + 1, L, std::forward<Keys>(keys), -1);
+ }
+
+ template <typename Keys>
+ probe get(lua_State* L, Keys&& keys, int tableindex = -2) {
+ if constexpr (!b) {
+ if (!maybe_indexable(L, tableindex)) {
+ return probe(false, 0);
+ }
+ return apply(std::index_sequence_for<Args...>(), 1, L, std::forward<Keys>(keys), tableindex);
+ }
+ else {
+ return apply(std::index_sequence_for<Args...>(), 1, L, std::forward<Keys>(keys), tableindex);
+ }
+ }
+ };
+}} // namespace sol::stack
+
+// end of sol/stack_probe.hpp
+
+#include <cstring>
+#include <array>
+
+namespace sol {
+ namespace detail {
+ using typical_chunk_name_t = char[SOL_ID_SIZE_I_];
+ using typical_file_chunk_name_t = char[SOL_FILE_ID_SIZE_I_];
+
+ inline const std::string& default_chunk_name() {
+ static const std::string name = "";
+ return name;
+ }
+
+ template <std::size_t N>
+ const char* make_chunk_name(const string_view& code, const std::string& chunkname, char (&basechunkname)[N]) {
+ if (chunkname.empty()) {
+ auto it = code.cbegin();
+ auto e = code.cend();
+ std::size_t i = 0;
+ static const std::size_t n = N - 4;
+ for (i = 0; i < n && it != e; ++i, ++it) {
+ basechunkname[i] = *it;
+ }
+ if (it != e) {
+ for (std::size_t c = 0; c < 3; ++i, ++c) {
+ basechunkname[i] = '.';
+ }
+ }
+ basechunkname[i] = '\0';
+ return &basechunkname[0];
+ }
+ else {
+ return chunkname.c_str();
+ }
+ }
+
+ inline void clear_entries(stack_reference r) {
+ stack::push(r.lua_state(), lua_nil);
+ while (lua_next(r.lua_state(), -2)) {
+ absolute_index key(r.lua_state(), -2);
+ auto pn = stack::pop_n(r.lua_state(), 1);
+ stack::set_field<false, true>(r.lua_state(), key, lua_nil, r.stack_index());
+ }
+ }
+
+ inline void clear_entries(const reference& registry_reference) {
+ auto pp = stack::push_pop(registry_reference);
+ stack_reference ref(registry_reference.lua_state(), -1);
+ clear_entries(ref);
+ }
+ } // namespace detail
+
+ namespace stack {
+ namespace stack_detail {
+ template <typename T>
+ inline int push_as_upvalues(lua_State* L, T& item) {
+ typedef std::decay_t<T> TValue;
+ static const std::size_t itemsize = sizeof(TValue);
+ static const std::size_t voidsize = sizeof(void*);
+ static const std::size_t voidsizem1 = voidsize - 1;
+ static const std::size_t data_t_count = (sizeof(TValue) + voidsizem1) / voidsize;
+ typedef std::array<void*, data_t_count> data_t;
+
+ data_t data { {} };
+ std::memcpy(&data[0], std::addressof(item), itemsize);
+ int pushcount = 0;
+ for (const auto& v : data) {
+ lua_pushlightuserdata(L, v);
+ pushcount += 1;
+ }
+ return pushcount;
+ }
+
+ template <typename T>
+ inline std::pair<T, int> get_as_upvalues(lua_State* L, int index = 2) {
+ static const std::size_t data_t_count = (sizeof(T) + (sizeof(void*) - 1)) / sizeof(void*);
+ typedef std::array<void*, data_t_count> data_t;
+ data_t voiddata { {} };
+ for (std::size_t i = 0, d = 0; d < sizeof(T); ++i, d += sizeof(void*)) {
+ voiddata[i] = lua_touserdata(L, upvalue_index(index++));
+ }
+ return std::pair<T, int>(*reinterpret_cast<T*>(static_cast<void*>(voiddata.data())), index);
+ }
+
+ template <typename T>
+ inline std::pair<T, int> get_as_upvalues_using_function(lua_State* L, int function_index = -1) {
+ static const std::size_t data_t_count = (sizeof(T) + (sizeof(void*) - 1)) / sizeof(void*);
+ typedef std::array<void*, data_t_count> data_t;
+ function_index = lua_absindex(L, function_index);
+ int index = 0;
+ data_t voiddata { {} };
+ for (std::size_t d = 0; d < sizeof(T); d += sizeof(void*)) {
+ // first upvalue is nullptr to respect environment shenanigans
+ // So +2 instead of +1
+ const char* upvalue_name = lua_getupvalue(L, function_index, index + 2);
+ if (upvalue_name == nullptr) {
+ // We should freak out here...
+ break;
+ }
+ voiddata[index] = lua_touserdata(L, -1);
+ ++index;
+ }
+ lua_pop(L, index);
+ return std::pair<T, int>(*reinterpret_cast<T*>(static_cast<void*>(voiddata.data())), index);
+ }
+
+ template <bool checked, typename Handler, typename Fx, typename... Args>
+ static decltype(auto) eval(types<>, std::index_sequence<>, lua_State*, int, Handler&&, record&, Fx&& fx, Args&&... args) {
+ return std::forward<Fx>(fx)(std::forward<Args>(args)...);
+ }
+
+ template <bool checked, typename Arg, typename... Args, std::size_t I, std::size_t... Is, typename Handler, typename Fx, typename... FxArgs>
+ static decltype(auto) eval(types<Arg, Args...>, std::index_sequence<I, Is...>, lua_State* L_, int start_index_, Handler&& handler_,
+ record& tracking_, Fx&& fx_, FxArgs&&... fxargs_) {
+#if 0 && SOL_IS_ON(SOL_PROPAGATE_EXCEPTIONS)
+ // NOTE: THIS IS TERMPORARILY TURNED OFF BECAUSE IT IMPACTS ACTUAL SEMANTICS W.R.T. THINGS LIKE LUAJIT,
+ // SO IT MUST REMAIN OFF UNTIL WE CAN ESTABLISH SIMILAR BEHAVIOR IN MODES WHERE `checked == false`!
+
+ // We can save performance/time by letting errors unwind produced arguments
+ // rather than checking everything once, and then potentially re-doing work
+ if constexpr (checked) {
+ return eval<checked>(types<Args...>(),
+ std::index_sequence<Is...>(),
+ L_,
+ start_index_,
+ std::forward<Handler>(handler_),
+ tracking_,
+ std::forward<Fx>(fx_),
+ std::forward<FxArgs>(fxargs_)...,
+ *stack_detail::check_get_arg<Arg>(L_, start_index_ + tracking_.used, handler_, tracking_));
+ }
+ else
+#endif
+ {
+ return eval<checked>(types<Args...>(),
+ std::index_sequence<Is...>(),
+ L_,
+ start_index_,
+ std::forward<Handler>(handler_),
+ tracking_,
+ std::forward<Fx>(fx_),
+ std::forward<FxArgs>(fxargs_)...,
+ stack_detail::unchecked_get_arg<Arg>(L_, start_index_ + tracking_.used, tracking_));
+ }
+ }
+
+ template <bool checkargs = detail::default_safe_function_calls, std::size_t... I, typename R, typename... Args, typename Fx, typename... FxArgs>
+ inline decltype(auto) call(types<R>, types<Args...> argument_types_, std::index_sequence<I...> argument_indices_, lua_State* L_,
+ int start_index_, Fx&& fx_, FxArgs&&... args_) {
+ static_assert(meta::all_v<meta::is_not_move_only<Args>...>,
+ "One of the arguments being bound is a move-only type, and it is not being taken by reference: this will break your code. Please take "
+ "a reference and std::move it manually if this was your intention.");
+ argument_handler<types<R, Args...>> handler {};
+ record tracking {};
+#if SOL_IS_OFF(SOL_PROPAGATE_EXCEPTIONS)
+ if constexpr (checkargs) {
+ multi_check<Args...>(L_, start_index_, handler);
+ }
+#endif
+ if constexpr (std::is_void_v<R>) {
+ eval<checkargs>(
+ argument_types_, argument_indices_, L_, start_index_, handler, tracking, std::forward<Fx>(fx_), std::forward<FxArgs>(args_)...);
+ }
+ else {
+ return eval<checkargs>(
+ argument_types_, argument_indices_, L_, start_index_, handler, tracking, std::forward<Fx>(fx_), std::forward<FxArgs>(args_)...);
+ }
+ }
+
+ template <typename T>
+ void raw_table_set(lua_State* L, T&& arg, int tableindex = -2) {
+ int push_count = push(L, std::forward<T>(arg));
+ SOL_ASSERT(push_count == 1);
+ std::size_t unique_index = static_cast<std::size_t>(luaL_len(L, tableindex) + 1u);
+ lua_rawseti(L, tableindex, static_cast<int>(unique_index));
+ }
+
+ } // namespace stack_detail
+
+ template <typename T>
+ int set_ref(lua_State* L, T&& arg, int tableindex = -2) {
+ int push_count = push(L, std::forward<T>(arg));
+ SOL_ASSERT(push_count == 1);
+ return luaL_ref(L, tableindex);
+ }
+
+ template <bool check_args = detail::default_safe_function_calls, typename R, typename... Args, typename Fx, typename... FxArgs>
+ inline decltype(auto) call(types<R> tr, types<Args...> ta, lua_State* L, int start, Fx&& fx, FxArgs&&... args) {
+ using args_indices = std::make_index_sequence<sizeof...(Args)>;
+ if constexpr (std::is_void_v<R>) {
+ stack_detail::call<check_args>(tr, ta, args_indices(), L, start, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
+ }
+ else {
+ return stack_detail::call<check_args>(tr, ta, args_indices(), L, start, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
+ }
+ }
+
+ template <bool check_args = detail::default_safe_function_calls, typename R, typename... Args, typename Fx, typename... FxArgs>
+ inline decltype(auto) call(types<R> tr, types<Args...> ta, lua_State* L, Fx&& fx, FxArgs&&... args) {
+ if constexpr (std::is_void_v<R>) {
+ call<check_args>(tr, ta, L, 1, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
+ }
+ else {
+ return call<check_args>(tr, ta, L, 1, std::forward<Fx>(fx), std::forward<FxArgs>(args)...);
+ }
+ }
+
+ template <bool check_args = detail::default_safe_function_calls, typename R, typename... Args, typename Fx, typename... FxArgs>
+ inline decltype(auto) call_from_top(types<R> tr, types<Args...> ta, lua_State* L, Fx&& fx, FxArgs&&... args) {
+ using expected_count_t = meta::count_for_pack<lua_size, Args...>;
+ if constexpr (std::is_void_v<R>) {
+ call<check_args>(tr,
+ ta,
+ L,
+ (std::max)(static_cast<int>(lua_gettop(L) - expected_count_t::value), static_cast<int>(0)),
+ std::forward<Fx>(fx),
+ std::forward<FxArgs>(args)...);
+ }
+ else {
+ return call<check_args>(tr,
+ ta,
+ L,
+ (std::max)(static_cast<int>(lua_gettop(L) - expected_count_t::value), static_cast<int>(0)),
+ std::forward<Fx>(fx),
+ std::forward<FxArgs>(args)...);
+ }
+ }
+
+ template <bool check_args = detail::default_safe_function_calls, bool clean_stack = true, typename Ret0, typename... Ret, typename... Args,
+ typename Fx, typename... FxArgs>
+ inline int call_into_lua(types<Ret0, Ret...> tr, types<Args...> ta, lua_State* L, int start, Fx&& fx, FxArgs&&... fxargs) {
+ if constexpr (std::is_void_v<Ret0>) {
+ call<check_args>(tr, ta, L, start, std::forward<Fx>(fx), std::forward<FxArgs>(fxargs)...);
+ if constexpr (clean_stack) {
+ lua_settop(L, 0);
+ }
+ return 0;
+ }
+ else {
+ (void)tr;
+ decltype(auto) r
+ = call<check_args>(types<meta::return_type_t<Ret0, Ret...>>(), ta, L, start, std::forward<Fx>(fx), std::forward<FxArgs>(fxargs)...);
+ using R = meta::unqualified_t<decltype(r)>;
+ using is_stack = meta::any<is_stack_based<R>, std::is_same<R, absolute_index>, std::is_same<R, ref_index>, std::is_same<R, raw_index>>;
+ if constexpr (clean_stack && !is_stack::value) {
+ lua_settop(L, 0);
+ }
+ return push_reference(L, std::forward<decltype(r)>(r));
+ }
+ }
+
+ template <bool check_args = detail::default_safe_function_calls, bool clean_stack = true, typename Fx, typename... FxArgs>
+ inline int call_lua(lua_State* L, int start, Fx&& fx, FxArgs&&... fxargs) {
+ using traits_type = lua_bind_traits<meta::unqualified_t<Fx>>;
+ using args_list = typename traits_type::args_list;
+ using returns_list = typename traits_type::returns_list;
+ return call_into_lua<check_args, clean_stack>(returns_list(), args_list(), L, start, std::forward<Fx>(fx), std::forward<FxArgs>(fxargs)...);
+ }
+
+ inline call_syntax get_call_syntax(lua_State* L, const string_view& key, int index) {
+ if (lua_gettop(L) < 1) {
+ return call_syntax::dot;
+ }
+ luaL_getmetatable(L, key.data());
+ auto pn = pop_n(L, 1);
+ if (lua_compare(L, -1, index, LUA_OPEQ) != 1) {
+ return call_syntax::dot;
+ }
+ return call_syntax::colon;
+ }
+
+ inline void script(
+ lua_State* L, lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname);
+ if (lua_load(L, reader, data, chunknametarget, to_string(mode).c_str()) || lua_pcall(L, 0, LUA_MULTRET, 0)) {
+ lua_error(L);
+ }
+ }
+
+ inline void script(
+ lua_State* L, const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname);
+ if (luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str()) || lua_pcall(L, 0, LUA_MULTRET, 0)) {
+ lua_error(L);
+ }
+ }
+
+ inline void script_file(lua_State* L, const std::string& filename, load_mode mode = load_mode::any) {
+ if (luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str()) || lua_pcall(L, 0, LUA_MULTRET, 0)) {
+ lua_error(L);
+ }
+ }
+
+ inline void luajit_exception_handler(lua_State* L, int (*handler)(lua_State*, lua_CFunction) = detail::c_trampoline) {
+#if SOL_IS_ON(SOL_USE_LUAJIT_EXCEPTION_TRAMPOLINE)
+ if (L == nullptr) {
+ return;
+ }
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushlightuserdata(L, (void*)handler);
+ auto pn = pop_n(L, 1);
+ luaJIT_setmode(L, -1, LUAJIT_MODE_WRAPCFUNC | LUAJIT_MODE_ON);
+#else
+ (void)L;
+ (void)handler;
+#endif
+ }
+
+ inline void luajit_exception_off(lua_State* L) {
+#if SOL_IS_ON(SOL_USE_LUAJIT_EXCEPTION_TRAMPOLINE)
+ if (L == nullptr) {
+ return;
+ }
+ luaJIT_setmode(L, -1, LUAJIT_MODE_WRAPCFUNC | LUAJIT_MODE_OFF);
+#else
+ (void)L;
+#endif
+ }
+ } // namespace stack
+} // namespace sol
+
+// end of sol/stack.hpp
+
+// beginning of sol/object.hpp
+
+// beginning of sol/make_reference.hpp
+
+namespace sol {
+
+ template <typename R = reference, bool should_pop = !is_stack_based_v<R>, typename T>
+ R make_reference(lua_State* L, T&& value) {
+ int backpedal = stack::push(L, std::forward<T>(value));
+ R r = stack::get<R>(L, -backpedal);
+ if (should_pop) {
+ lua_pop(L, backpedal);
+ }
+ return r;
+ }
+
+ template <typename T, typename R = reference, bool should_pop = !is_stack_based_v<R>, typename... Args>
+ R make_reference(lua_State* L, Args&&... args) {
+ int backpedal = stack::push<T>(L, std::forward<Args>(args)...);
+ R r = stack::get<R>(L, -backpedal);
+ if (should_pop) {
+ lua_pop(L, backpedal);
+ }
+ return r;
+ }
+
+ template <typename R = reference, bool should_pop = !is_stack_based_v<R>, typename T>
+ R make_reference_userdata(lua_State* L, T&& value) {
+ int backpedal = stack::push_userdata(L, std::forward<T>(value));
+ R r = stack::get<R>(L, -backpedal);
+ if (should_pop) {
+ lua_pop(L, backpedal);
+ }
+ return r;
+ }
+
+ template <typename T, typename R = reference, bool should_pop = !is_stack_based_v<R>, typename... Args>
+ R make_reference_userdata(lua_State* L, Args&&... args) {
+ int backpedal = stack::push_userdata<T>(L, std::forward<Args>(args)...);
+ R r = stack::get<R>(L, -backpedal);
+ if (should_pop) {
+ lua_pop(L, backpedal);
+ }
+ return r;
+ }
+
+} // namespace sol
+
+// end of sol/make_reference.hpp
+
+// beginning of sol/object_base.hpp
+
+namespace sol {
+
+ template <typename ref_t>
+ class basic_object_base : public ref_t {
+ private:
+ using base_t = ref_t;
+
+ template <typename T>
+ decltype(auto) as_stack(std::true_type) const {
+ return stack::get<T>(base_t::lua_state(), base_t::stack_index());
+ }
+
+ template <typename T>
+ decltype(auto) as_stack(std::false_type) const {
+ base_t::push();
+ return stack::pop<T>(base_t::lua_state());
+ }
+
+ template <typename T>
+ bool is_stack(std::true_type) const {
+ return stack::check<T>(base_t::lua_state(), base_t::stack_index(), &no_panic);
+ }
+
+ template <typename T>
+ bool is_stack(std::false_type) const {
+ int r = base_t::registry_index();
+ if (r == LUA_REFNIL)
+ return meta::any_same<meta::unqualified_t<T>, lua_nil_t, nullopt_t, std::nullptr_t>::value ? true : false;
+ if (r == LUA_NOREF)
+ return false;
+ auto pp = stack::push_pop(*this);
+ return stack::check<T>(base_t::lua_state(), -1, &no_panic);
+ }
+
+ public:
+ basic_object_base() noexcept = default;
+ basic_object_base(const basic_object_base&) = default;
+ basic_object_base(basic_object_base&&) = default;
+ basic_object_base& operator=(const basic_object_base&) = default;
+ basic_object_base& operator=(basic_object_base&&) = default;
+ template <typename T, typename... Args, meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_object_base>>> = meta::enabler>
+ basic_object_base(T&& arg, Args&&... args) : base_t(std::forward<T>(arg), std::forward<Args>(args)...) {
+ }
+
+ template <typename T>
+ decltype(auto) as() const {
+ return as_stack<T>(is_stack_based<base_t>());
+ }
+
+ template <typename T>
+ bool is() const {
+ return is_stack<T>(is_stack_based<base_t>());
+ }
+ };
+} // namespace sol
+
+// end of sol/object_base.hpp
+
+namespace sol {
+
+ template <typename base_type>
+ class basic_object : public basic_object_base<base_type> {
+ private:
+ typedef basic_object_base<base_type> base_t;
+
+ template <bool invert_and_pop = false>
+ basic_object(std::integral_constant<bool, invert_and_pop>, lua_State* L_, int index_ = -1) noexcept : base_t(L_, index_) {
+ if (invert_and_pop) {
+ lua_pop(L_, -index_);
+ }
+ }
+
+ protected:
+ basic_object(detail::no_safety_tag, lua_nil_t n) : base_t(n) {
+ }
+ basic_object(detail::no_safety_tag, lua_State* L_, int index_) : base_t(L_, index_) {
+ }
+ basic_object(detail::no_safety_tag, lua_State* L_, ref_index index_) : base_t(L_, index_) {
+ }
+ template <typename T,
+ meta::enable<meta::neg<meta::any_same<meta::unqualified_t<T>, basic_object>>, meta::neg<std::is_same<base_type, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_object(detail::no_safety_tag, T&& r) noexcept : base_t(std::forward<T>(r)) {
+ }
+
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_object(detail::no_safety_tag, lua_State* L_, T&& r) noexcept : base_t(L_, std::forward<T>(r)) {
+ }
+
+ public:
+ basic_object() noexcept = default;
+ template <typename T,
+ meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_object>>, meta::neg<std::is_same<base_type, stack_reference>>,
+ is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_object(T&& r) : base_t(std::forward<T>(r)) {
+ }
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_object(lua_State* L_, T&& r) : base_t(L_, std::forward<T>(r)) {
+ }
+ basic_object(lua_State* L_, global_tag_t t) : base_t(L_, t) {
+ }
+ basic_object(lua_nil_t r) : base_t(r) {
+ }
+ basic_object(const basic_object&) = default;
+ basic_object(basic_object&&) = default;
+ basic_object(const stack_reference& r) noexcept : basic_object(r.lua_state(), r.stack_index()) {
+ }
+ basic_object(stack_reference&& r) noexcept : basic_object(r.lua_state(), r.stack_index()) {
+ }
+ template <typename Super>
+ basic_object(const proxy_base<Super>& r) noexcept : basic_object(r.operator basic_object()) {
+ }
+ template <typename Super>
+ basic_object(proxy_base<Super>&& r) noexcept : basic_object(r.operator basic_object()) {
+ }
+ basic_object(lua_State* L_, lua_nil_t r) noexcept : base_t(L_, r) {
+ }
+ basic_object(lua_State* L_, int index_ = -1) noexcept : base_t(L_, index_) {
+ }
+ basic_object(lua_State* L_, absolute_index index_) noexcept : base_t(L_, index_) {
+ }
+ basic_object(lua_State* L_, raw_index index_) noexcept : base_t(L_, index_) {
+ }
+ basic_object(lua_State* L_, ref_index index_) noexcept : base_t(L_, index_) {
+ }
+ template <typename T, typename... Args>
+ basic_object(lua_State* L_, in_place_type_t<T>, Args&&... args) noexcept
+ : basic_object(std::integral_constant<bool, !is_stack_based<base_t>::value>(), L_, -stack::push<T>(L_, std::forward<Args>(args)...)) {
+ }
+ template <typename T, typename... Args>
+ basic_object(lua_State* L_, in_place_t, T&& arg, Args&&... args) noexcept
+ : basic_object(L_, in_place_type<T>, std::forward<T>(arg), std::forward<Args>(args)...) {
+ }
+ basic_object& operator=(const basic_object&) = default;
+ basic_object& operator=(basic_object&&) = default;
+ basic_object& operator=(const base_type& b) {
+ base_t::operator=(b);
+ return *this;
+ }
+ basic_object& operator=(base_type&& b) {
+ base_t::operator=(std::move(b));
+ return *this;
+ }
+ template <typename Super>
+ basic_object& operator=(const proxy_base<Super>& r) {
+ this->operator=(r.operator basic_object());
+ return *this;
+ }
+ template <typename Super>
+ basic_object& operator=(proxy_base<Super>&& r) {
+ this->operator=(r.operator basic_object());
+ return *this;
+ }
+ };
+
+ template <typename T>
+ object make_object(lua_State* L_, T&& value) {
+ return make_reference<object, true>(L_, std::forward<T>(value));
+ }
+
+ template <typename T, typename... Args>
+ object make_object(lua_State* L_, Args&&... args) {
+ return make_reference<T, object, true>(L_, std::forward<Args>(args)...);
+ }
+
+ template <typename T>
+ object make_object_userdata(lua_State* L_, T&& value) {
+ return make_reference_userdata<object, true>(L_, std::forward<T>(value));
+ }
+
+ template <typename T, typename... Args>
+ object make_object_userdata(lua_State* L_, Args&&... args) {
+ return make_reference_userdata<T, object, true>(L_, std::forward<Args>(args)...);
+ }
+} // namespace sol
+
+// end of sol/object.hpp
+
+// beginning of sol/function.hpp
+
+// beginning of sol/unsafe_function.hpp
+
+// beginning of sol/function_result.hpp
+
+// beginning of sol/protected_function_result.hpp
+
+// beginning of sol/proxy_base.hpp
+
+namespace sol {
+ struct proxy_base_tag { };
+
+ namespace detail {
+ template <typename T>
+ using proxy_key_t = meta::conditional_t<meta::is_specialization_of_v<meta::unqualified_t<T>, std::tuple>, T,
+ std::tuple<meta::conditional_t<std::is_array_v<meta::unqualified_t<T>>, std::remove_reference_t<T>&, meta::unqualified_t<T>>>>;
+ }
+
+ template <typename Super>
+ struct proxy_base : public proxy_base_tag {
+ lua_State* lua_state() const {
+ const Super& super = *static_cast<const Super*>(static_cast<const void*>(this));
+ return super.lua_state();
+ }
+
+ operator std::string() const {
+ const Super& super = *static_cast<const Super*>(static_cast<const void*>(this));
+ return super.template get<std::string>();
+ }
+
+ template <typename T, meta::enable<meta::neg<meta::is_string_constructible<T>>, is_proxy_primitive<meta::unqualified_t<T>>> = meta::enabler>
+ operator T() const {
+ const Super& super = *static_cast<const Super*>(static_cast<const void*>(this));
+ return super.template get<T>();
+ }
+
+ template <typename T,
+ meta::enable<meta::neg<meta::is_string_constructible<T>>, meta::neg<is_proxy_primitive<meta::unqualified_t<T>>>> = meta::enabler>
+ operator T&() const {
+ const Super& super = *static_cast<const Super*>(static_cast<const void*>(this));
+ return super.template get<T&>();
+ }
+ };
+
+} // namespace sol
+
+// end of sol/proxy_base.hpp
+
+// beginning of sol/stack_iterator.hpp
+
+#include <limits>
+#include <iterator>
+
+namespace sol {
+ template <typename proxy_t, bool is_const>
+ struct stack_iterator {
+ typedef meta::conditional_t<is_const, const proxy_t, proxy_t> reference;
+ typedef meta::conditional_t<is_const, const proxy_t*, proxy_t*> pointer;
+ typedef proxy_t value_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef std::random_access_iterator_tag iterator_category;
+ lua_State* L;
+ int index;
+ int stacktop;
+ proxy_t sp;
+
+ stack_iterator() : L(nullptr), index((std::numeric_limits<int>::max)()), stacktop((std::numeric_limits<int>::max)()), sp() {
+ }
+ stack_iterator(const stack_iterator<proxy_t, true>& r) : L(r.L), index(r.index), stacktop(r.stacktop), sp(r.sp) {
+ }
+ stack_iterator(lua_State* luastate, int idx, int topidx) : L(luastate), index(idx), stacktop(topidx), sp(luastate, idx) {
+ }
+
+ reference operator*() {
+ return proxy_t(L, index);
+ }
+
+ reference operator*() const {
+ return proxy_t(L, index);
+ }
+
+ pointer operator->() {
+ sp = proxy_t(L, index);
+ return &sp;
+ }
+
+ pointer operator->() const {
+ const_cast<proxy_t&>(sp) = proxy_t(L, index);
+ return &sp;
+ }
+
+ stack_iterator& operator++() {
+ ++index;
+ return *this;
+ }
+
+ stack_iterator operator++(int) {
+ auto r = *this;
+ this->operator++();
+ return r;
+ }
+
+ stack_iterator& operator--() {
+ --index;
+ return *this;
+ }
+
+ stack_iterator operator--(int) {
+ auto r = *this;
+ this->operator--();
+ return r;
+ }
+
+ stack_iterator& operator+=(difference_type idx) {
+ index += static_cast<int>(idx);
+ return *this;
+ }
+
+ stack_iterator& operator-=(difference_type idx) {
+ index -= static_cast<int>(idx);
+ return *this;
+ }
+
+ difference_type operator-(const stack_iterator& r) const {
+ return index - r.index;
+ }
+
+ stack_iterator operator+(difference_type idx) const {
+ stack_iterator r = *this;
+ r += idx;
+ return r;
+ }
+
+ reference operator[](difference_type idx) const {
+ return proxy_t(L, index + static_cast<int>(idx));
+ }
+
+ bool operator==(const stack_iterator& r) const {
+ if (stacktop == (std::numeric_limits<int>::max)()) {
+ return r.index == r.stacktop;
+ }
+ else if (r.stacktop == (std::numeric_limits<int>::max)()) {
+ return index == stacktop;
+ }
+ return index == r.index;
+ }
+
+ bool operator!=(const stack_iterator& r) const {
+ return !(this->operator==(r));
+ }
+
+ bool operator<(const stack_iterator& r) const {
+ return index < r.index;
+ }
+
+ bool operator>(const stack_iterator& r) const {
+ return index > r.index;
+ }
+
+ bool operator<=(const stack_iterator& r) const {
+ return index <= r.index;
+ }
+
+ bool operator>=(const stack_iterator& r) const {
+ return index >= r.index;
+ }
+ };
+
+ template <typename proxy_t, bool is_const>
+ inline stack_iterator<proxy_t, is_const> operator+(
+ typename stack_iterator<proxy_t, is_const>::difference_type n, const stack_iterator<proxy_t, is_const>& r) {
+ return r + n;
+ }
+} // namespace sol
+
+// end of sol/stack_iterator.hpp
+
+// beginning of sol/stack_proxy.hpp
+
+// beginning of sol/stack_proxy_base.hpp
+
+namespace sol {
+ struct stack_proxy_base : public proxy_base<stack_proxy_base> {
+ private:
+ lua_State* m_L;
+ int m_index;
+
+ public:
+ stack_proxy_base() : m_L(nullptr), m_index(0) {
+ }
+ stack_proxy_base(lua_State* L_, int index_) : m_L(L_), m_index(index_) {
+ }
+
+ template <typename T>
+ decltype(auto) get() const {
+ return stack::get<T>(m_L, stack_index());
+ }
+
+ template <typename T>
+ bool is() const {
+ return stack::check<T>(m_L, stack_index());
+ }
+
+ template <typename T>
+ decltype(auto) as() const {
+ return get<T>();
+ }
+
+ type get_type() const noexcept {
+ return type_of(lua_state(), stack_index());
+ }
+
+ int push() const {
+ return push(m_L);
+ }
+
+ int push(lua_State* L_) const {
+ lua_pushvalue(L_, m_index);
+ return 1;
+ }
+
+ lua_State* lua_state() const {
+ return m_L;
+ }
+ int stack_index() const {
+ return m_index;
+ }
+ };
+
+ namespace stack {
+ template <>
+ struct unqualified_getter<stack_proxy_base> {
+ static stack_proxy_base get(lua_State* L_, int index_ = -1) {
+ return stack_proxy_base(L_, index_);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<stack_proxy_base> {
+ static int push(lua_State*, const stack_proxy_base& proxy_reference) {
+ return proxy_reference.push();
+ }
+ };
+ } // namespace stack
+
+} // namespace sol
+
+// end of sol/stack_proxy_base.hpp
+
+namespace sol {
+ struct stack_proxy : public stack_proxy_base {
+ public:
+ stack_proxy() : stack_proxy_base() {
+ }
+ stack_proxy(lua_State* L, int index) : stack_proxy_base(L, index) {
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) call(Args&&... args);
+
+ template <typename... Args>
+ decltype(auto) operator()(Args&&... args) {
+ return call<>(std::forward<Args>(args)...);
+ }
+ };
+
+ namespace stack {
+ template <>
+ struct unqualified_getter<stack_proxy> {
+ static stack_proxy get(lua_State* L, int index, record& tracking) {
+ tracking.use(0);
+ return stack_proxy(L, index);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<stack_proxy> {
+ static int push(lua_State*, const stack_proxy& ref) {
+ return ref.push();
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/stack_proxy.hpp
+
+#include <cstdint>
+
+namespace sol {
+ struct protected_function_result : public proxy_base<protected_function_result> {
+ private:
+ lua_State* L;
+ int index;
+ int returncount;
+ int popcount;
+ call_status err;
+
+ public:
+ typedef stack_proxy reference_type;
+ typedef stack_proxy value_type;
+ typedef stack_proxy* pointer;
+ typedef std::ptrdiff_t difference_type;
+ typedef std::size_t size_type;
+ typedef stack_iterator<stack_proxy, false> iterator;
+ typedef stack_iterator<stack_proxy, true> const_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ protected_function_result() noexcept : protected_function_result(nullptr) {}
+ protected_function_result(lua_State* Ls, int idx = -1, int retnum = 0, int popped = 0, call_status pferr = call_status::ok) noexcept
+ : L(Ls), index(idx), returncount(retnum), popcount(popped), err(pferr) {
+ }
+
+ // We do not want anyone to copy these around willy-nilly
+ // Will likely break people, but also will probably get rid of quiet bugs that have
+ // been lurking. (E.g., Vanilla Lua will just quietly discard over-pops and under-pops:
+ // LuaJIT and other Lua engines will implode and segfault at random later times.)
+ protected_function_result(const protected_function_result&) = delete;
+ protected_function_result& operator=(const protected_function_result&) = delete;
+
+ protected_function_result(protected_function_result&& o) noexcept
+ : L(o.L), index(o.index), returncount(o.returncount), popcount(o.popcount), err(o.err) {
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but we will be thorough
+ o.abandon();
+ }
+ protected_function_result& operator=(protected_function_result&& o) noexcept {
+ L = o.L;
+ index = o.index;
+ returncount = o.returncount;
+ popcount = o.popcount;
+ err = o.err;
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but we will be thorough
+ o.abandon();
+ return *this;
+ }
+
+ protected_function_result(const unsafe_function_result& o) = delete;
+ protected_function_result& operator=(const unsafe_function_result& o) = delete;
+ protected_function_result(unsafe_function_result&& o) noexcept;
+ protected_function_result& operator=(unsafe_function_result&& o) noexcept;
+
+ call_status status() const noexcept {
+ return err;
+ }
+
+ bool valid() const noexcept {
+ return status() == call_status::ok || status() == call_status::yielded;
+ }
+
+#if SOL_IS_ON(SOL_COMPILER_GCC)
+#pragma GCC diagnostic push
+#if !SOL_IS_ON(SOL_COMPILER_CLANG)
+#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
+#endif
+#endif
+
+ template <typename T>
+ decltype(auto) get(int index_offset = 0) const {
+ using UT = meta::unqualified_t<T>;
+ int target = index + index_offset;
+ if constexpr (meta::is_optional_v<UT>) {
+ using ValueType = typename UT::value_type;
+ if constexpr (std::is_same_v<ValueType, error>) {
+ if (valid()) {
+ return UT();
+ }
+ return UT(error(detail::direct_error, stack::get<std::string>(L, target)));
+ }
+ else {
+ if (!valid()) {
+ return UT();
+ }
+ return stack::get<UT>(L, target);
+ }
+ }
+ else {
+ if constexpr (std::is_same_v<T, error>) {
+#if SOL_IS_ON(SOL_SAFE_PROXIES)
+ if (valid()) {
+ type t = type_of(L, target);
+ type_panic_c_str(L, target, t, type::none, "bad get from protected_function_result (is an error)");
+ }
+#endif // Check Argument Safety
+ return error(detail::direct_error, stack::get<std::string>(L, target));
+ }
+ else {
+#if SOL_IS_ON(SOL_SAFE_PROXIES)
+ if (!valid()) {
+ type t = type_of(L, target);
+ type_panic_c_str(L, target, t, type::none, "bad get from protected_function_result (is not an error)");
+ }
+#endif // Check Argument Safety
+ return stack::get<T>(L, target);
+ }
+ }
+ }
+
+#if SOL_IS_ON(SOL_COMPILER_GCC)
+#pragma GCC diagnostic pop
+#endif
+
+ type get_type(int index_offset = 0) const noexcept {
+ return type_of(L, index + static_cast<int>(index_offset));
+ }
+
+ stack_proxy operator[](difference_type index_offset) const {
+ return stack_proxy(L, index + static_cast<int>(index_offset));
+ }
+
+ iterator begin() {
+ return iterator(L, index, stack_index() + return_count());
+ }
+ iterator end() {
+ return iterator(L, stack_index() + return_count(), stack_index() + return_count());
+ }
+ const_iterator begin() const {
+ return const_iterator(L, index, stack_index() + return_count());
+ }
+ const_iterator end() const {
+ return const_iterator(L, stack_index() + return_count(), stack_index() + return_count());
+ }
+ const_iterator cbegin() const {
+ return begin();
+ }
+ const_iterator cend() const {
+ return end();
+ }
+
+ reverse_iterator rbegin() {
+ return std::reverse_iterator<iterator>(begin());
+ }
+ reverse_iterator rend() {
+ return std::reverse_iterator<iterator>(end());
+ }
+ const_reverse_iterator rbegin() const {
+ return std::reverse_iterator<const_iterator>(begin());
+ }
+ const_reverse_iterator rend() const {
+ return std::reverse_iterator<const_iterator>(end());
+ }
+ const_reverse_iterator crbegin() const {
+ return std::reverse_iterator<const_iterator>(cbegin());
+ }
+ const_reverse_iterator crend() const {
+ return std::reverse_iterator<const_iterator>(cend());
+ }
+
+ lua_State* lua_state() const noexcept {
+ return L;
+ };
+ int stack_index() const noexcept {
+ return index;
+ };
+ int return_count() const noexcept {
+ return returncount;
+ };
+ int pop_count() const noexcept {
+ return popcount;
+ };
+ void abandon() noexcept {
+ // L = nullptr;
+ index = 0;
+ returncount = 0;
+ popcount = 0;
+ err = call_status::runtime;
+ }
+ ~protected_function_result() {
+ if (L == nullptr)
+ return;
+ stack::remove(L, index, popcount);
+ }
+ };
+
+ namespace stack {
+ template <>
+ struct unqualified_pusher<protected_function_result> {
+ static int push(lua_State* L, const protected_function_result& pfr) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, static_cast<int>(pfr.pop_count()), detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ int p = 0;
+ for (int i = 0; i < pfr.pop_count(); ++i) {
+ lua_pushvalue(L, i + pfr.stack_index());
+ ++p;
+ }
+ return p;
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/protected_function_result.hpp
+
+// beginning of sol/unsafe_function_result.hpp
+
+#include <cstdint>
+
+namespace sol {
+ struct unsafe_function_result : public proxy_base<unsafe_function_result> {
+ private:
+ lua_State* L;
+ int index;
+ int returncount;
+
+ public:
+ typedef stack_proxy reference_type;
+ typedef stack_proxy value_type;
+ typedef stack_proxy* pointer;
+ typedef std::ptrdiff_t difference_type;
+ typedef std::size_t size_type;
+ typedef stack_iterator<stack_proxy, false> iterator;
+ typedef stack_iterator<stack_proxy, true> const_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ unsafe_function_result() noexcept : unsafe_function_result(nullptr) {}
+ unsafe_function_result(lua_State* Ls, int idx = -1, int retnum = 0) noexcept : L(Ls), index(idx), returncount(retnum) {
+ }
+
+ // We do not want anyone to copy these around willy-nilly
+ // Will likely break people, but also will probably get rid of quiet bugs that have
+ // been lurking. (E.g., Vanilla Lua will just quietly discard over-pops and under-pops:
+ // LuaJIT and other Lua engines will implode and segfault at random later times.)
+ unsafe_function_result(const unsafe_function_result&) = delete;
+ unsafe_function_result& operator=(const unsafe_function_result&) = delete;
+
+ unsafe_function_result(unsafe_function_result&& o) noexcept : L(o.L), index(o.index), returncount(o.returncount) {
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but will be thorough
+ o.abandon();
+ }
+ unsafe_function_result& operator=(unsafe_function_result&& o) noexcept {
+ L = o.L;
+ index = o.index;
+ returncount = o.returncount;
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but will be thorough
+ o.abandon();
+ return *this;
+ }
+
+ unsafe_function_result(const protected_function_result& o) = delete;
+ unsafe_function_result& operator=(const protected_function_result& o) = delete;
+ unsafe_function_result(protected_function_result&& o) noexcept;
+ unsafe_function_result& operator=(protected_function_result&& o) noexcept;
+
+ template <typename T>
+ decltype(auto) get(difference_type index_offset = 0) const {
+ return stack::get<T>(L, index + static_cast<int>(index_offset));
+ }
+
+ type get_type(difference_type index_offset = 0) const noexcept {
+ return type_of(L, index + static_cast<int>(index_offset));
+ }
+
+ stack_proxy operator[](difference_type index_offset) const {
+ return stack_proxy(L, index + static_cast<int>(index_offset));
+ }
+
+ iterator begin() {
+ return iterator(L, index, stack_index() + return_count());
+ }
+ iterator end() {
+ return iterator(L, stack_index() + return_count(), stack_index() + return_count());
+ }
+ const_iterator begin() const {
+ return const_iterator(L, index, stack_index() + return_count());
+ }
+ const_iterator end() const {
+ return const_iterator(L, stack_index() + return_count(), stack_index() + return_count());
+ }
+ const_iterator cbegin() const {
+ return begin();
+ }
+ const_iterator cend() const {
+ return end();
+ }
+
+ reverse_iterator rbegin() {
+ return std::reverse_iterator<iterator>(begin());
+ }
+ reverse_iterator rend() {
+ return std::reverse_iterator<iterator>(end());
+ }
+ const_reverse_iterator rbegin() const {
+ return std::reverse_iterator<const_iterator>(begin());
+ }
+ const_reverse_iterator rend() const {
+ return std::reverse_iterator<const_iterator>(end());
+ }
+ const_reverse_iterator crbegin() const {
+ return std::reverse_iterator<const_iterator>(cbegin());
+ }
+ const_reverse_iterator crend() const {
+ return std::reverse_iterator<const_iterator>(cend());
+ }
+
+ call_status status() const noexcept {
+ return call_status::ok;
+ }
+
+ bool valid() const noexcept {
+ return status() == call_status::ok || status() == call_status::yielded;
+ }
+
+ lua_State* lua_state() const {
+ return L;
+ };
+ int stack_index() const {
+ return index;
+ };
+ int return_count() const {
+ return returncount;
+ };
+ void abandon() noexcept {
+ // L = nullptr;
+ index = 0;
+ returncount = 0;
+ }
+ ~unsafe_function_result() {
+ if (L != nullptr) {
+ lua_pop(L, returncount);
+ }
+ }
+ };
+
+ namespace stack {
+ template <>
+ struct unqualified_pusher<unsafe_function_result> {
+ static int push(lua_State* L, const unsafe_function_result& fr) {
+ int p = 0;
+ for (int i = 0; i < fr.return_count(); ++i) {
+ lua_pushvalue(L, i + fr.stack_index());
+ ++p;
+ }
+ return p;
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/unsafe_function_result.hpp
+
+#include <cstdint>
+
+namespace sol {
+
+ namespace detail {
+ template <>
+ struct is_speshul<unsafe_function_result> : std::true_type { };
+ template <>
+ struct is_speshul<protected_function_result> : std::true_type { };
+
+ template <std::size_t I, typename... Args, typename T>
+ stack_proxy get(types<Args...>, meta::index_value<0>, meta::index_value<I>, const T& fr) {
+ return stack_proxy(fr.lua_state(), fr.stack_index() + static_cast<int>(I));
+ }
+
+ template <std::size_t I, std::size_t N, typename Arg, typename... Args, typename T, meta::enable<meta::boolean<(N > 0)>> = meta::enabler>
+ stack_proxy get(types<Arg, Args...>, meta::index_value<N>, meta::index_value<I>, const T& fr) {
+ return get(types<Args...>(), meta::index_value<N - 1>(), meta::index_value<I + lua_size<Arg>::value>(), fr);
+ }
+ } // namespace detail
+
+ template <>
+ struct tie_size<unsafe_function_result> : std::integral_constant<std::size_t, SIZE_MAX> { };
+
+ template <>
+ struct tie_size<protected_function_result> : std::integral_constant<std::size_t, SIZE_MAX> { };
+
+ template <std::size_t I>
+ stack_proxy get(const unsafe_function_result& fr) {
+ return stack_proxy(fr.lua_state(), fr.stack_index() + static_cast<int>(I));
+ }
+
+ template <std::size_t I, typename... Args>
+ stack_proxy get(types<Args...> t, const unsafe_function_result& fr) {
+ return detail::get(t, meta::index_value<I>(), meta::index_value<0>(), fr);
+ }
+
+ template <std::size_t I>
+ stack_proxy get(const protected_function_result& fr) {
+ return stack_proxy(fr.lua_state(), fr.stack_index() + static_cast<int>(I));
+ }
+
+ template <std::size_t I, typename... Args>
+ stack_proxy get(types<Args...> t, const protected_function_result& fr) {
+ return detail::get(t, meta::index_value<I>(), meta::index_value<0>(), fr);
+ }
+} // namespace sol
+
+// end of sol/function_result.hpp
+
+// beginning of sol/function_types.hpp
+
+// beginning of sol/function_types_core.hpp
+
+// beginning of sol/wrapper.hpp
+
+namespace sol {
+
+ namespace detail {
+ template <typename T>
+ using array_return_type = meta::conditional_t<std::is_array<T>::value, std::add_lvalue_reference_t<T>, T>;
+ }
+
+ template <typename F, typename = void>
+ struct wrapper {
+ typedef lua_bind_traits<meta::unqualified_t<F>> traits_type;
+ typedef typename traits_type::args_list args_list;
+ typedef typename traits_type::args_list free_args_list;
+ typedef typename traits_type::returns_list returns_list;
+
+ template <typename... Args>
+ static decltype(auto) call(F& f, Args&&... args) {
+ return f(std::forward<Args>(args)...);
+ }
+
+ struct caller {
+ template <typename... Args>
+ decltype(auto) operator()(F& fx, Args&&... args) const {
+ return call(fx, std::forward<Args>(args)...);
+ }
+ };
+ };
+
+ template <typename F>
+ struct wrapper<F, std::enable_if_t<std::is_function<std::remove_pointer_t<meta::unqualified_t<F>>>::value>> {
+ typedef lua_bind_traits<std::remove_pointer_t<meta::unqualified_t<F>>> traits_type;
+ typedef typename traits_type::args_list args_list;
+ typedef typename traits_type::args_list free_args_list;
+ typedef typename traits_type::returns_list returns_list;
+
+ template <F fx, typename... Args>
+ static decltype(auto) invoke(Args&&... args) {
+ return fx(std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ static decltype(auto) call(F& fx, Args&&... args) {
+ return fx(std::forward<Args>(args)...);
+ }
+
+ struct caller {
+ template <typename... Args>
+ decltype(auto) operator()(F& fx, Args&&... args) const {
+ return call(fx, std::forward<Args>(args)...);
+ }
+ };
+
+ template <F fx>
+ struct invoker {
+ template <typename... Args>
+ decltype(auto) operator()(Args&&... args) const {
+ return invoke<fx>(std::forward<Args>(args)...);
+ }
+ };
+ };
+
+ template <typename F>
+ struct wrapper<F, std::enable_if_t<std::is_member_object_pointer<meta::unqualified_t<F>>::value>> {
+ typedef lua_bind_traits<meta::unqualified_t<F>> traits_type;
+ typedef typename traits_type::object_type object_type;
+ typedef typename traits_type::return_type return_type;
+ typedef typename traits_type::args_list args_list;
+ typedef types<object_type&, return_type> free_args_list;
+ typedef typename traits_type::returns_list returns_list;
+
+ template <F fx>
+ static auto call(object_type& mem) -> detail::array_return_type<decltype(mem.*fx)> {
+ return mem.*fx;
+ }
+
+ template <F fx, typename Arg, typename... Args>
+ static decltype(auto) invoke(object_type& mem, Arg&& arg, Args&&...) {
+ return mem.*fx = std::forward<Arg>(arg);
+ }
+
+ template <typename Fx>
+ static auto call(Fx&& fx, object_type& mem) -> detail::array_return_type<decltype(mem.*fx)> {
+ return mem.*fx;
+ }
+
+ template <typename Fx, typename Arg, typename... Args>
+ static void call(Fx&& fx, object_type& mem, Arg&& arg, Args&&...) {
+ using actual_type = meta::unqualified_t<detail::array_return_type<decltype(mem.*fx)>>;
+ if constexpr (std::is_array_v<actual_type>) {
+ using std::cbegin;
+ using std::cend;
+ auto first = cbegin(arg);
+ auto last = cend(arg);
+ for (std::size_t i = 0; first != last; ++i, ++first) {
+ (mem.*fx)[i] = *first;
+ }
+ }
+ else {
+ (mem.*fx) = std::forward<Arg>(arg);
+ }
+ }
+
+ struct caller {
+ template <typename Fx, typename... Args>
+ decltype(auto) operator()(Fx&& fx, object_type& mem, Args&&... args) const {
+ return call(std::forward<Fx>(fx), mem, std::forward<Args>(args)...);
+ }
+ };
+
+ template <F fx>
+ struct invoker {
+ template <typename... Args>
+ decltype(auto) operator()(Args&&... args) const {
+ return invoke<fx>(std::forward<Args>(args)...);
+ }
+ };
+ };
+
+ template <typename F, typename R, typename O, typename... FArgs>
+ struct member_function_wrapper {
+ typedef O object_type;
+ typedef lua_bind_traits<F> traits_type;
+ typedef typename traits_type::args_list args_list;
+ typedef types<object_type&, FArgs...> free_args_list;
+ typedef meta::tuple_types<R> returns_list;
+
+ template <F fx, typename... Args>
+ static R invoke(O& mem, Args&&... args) {
+ return (mem.*fx)(std::forward<Args>(args)...);
+ }
+
+ template <typename Fx, typename... Args>
+ static R call(Fx&& fx, O& mem, Args&&... args) {
+ return (mem.*fx)(std::forward<Args>(args)...);
+ }
+
+ struct caller {
+ template <typename Fx, typename... Args>
+ decltype(auto) operator()(Fx&& fx, O& mem, Args&&... args) const {
+ return call(std::forward<Fx>(fx), mem, std::forward<Args>(args)...);
+ }
+ };
+
+ template <F fx>
+ struct invoker {
+ template <typename... Args>
+ decltype(auto) operator()(O& mem, Args&&... args) const {
+ return invoke<fx>(mem, std::forward<Args>(args)...);
+ }
+ };
+ };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...)> : public member_function_wrapper<R (O::*)(Args...), R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const> : public member_function_wrapper<R (O::*)(Args...) const, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const volatile> : public member_function_wrapper<R (O::*)(Args...) const volatile, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...)&> : public member_function_wrapper<R (O::*)(Args...)&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const&> : public member_function_wrapper<R (O::*)(Args...) const&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const volatile&> : public member_function_wrapper<R (O::*)(Args...) const volatile&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...)&> : public member_function_wrapper<R (O::*)(Args..., ...)&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...) const&> : public member_function_wrapper<R (O::*)(Args..., ...) const&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...) const volatile&> : public member_function_wrapper<R (O::*)(Args..., ...) const volatile&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) &&> : public member_function_wrapper<R (O::*)(Args...)&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const&&> : public member_function_wrapper<R (O::*)(Args...) const&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const volatile&&> : public member_function_wrapper<R (O::*)(Args...) const volatile&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...) &&> : public member_function_wrapper<R (O::*)(Args..., ...)&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...) const&&> : public member_function_wrapper<R (O::*)(Args..., ...) const&, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...) const volatile&&> : public member_function_wrapper<R (O::*)(Args..., ...) const volatile&, R, O, Args...> { };
+
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ // noexcept has become a part of a function's type
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) noexcept> : public member_function_wrapper<R (O::*)(Args...) noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const noexcept> : public member_function_wrapper<R (O::*)(Args...) const noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const volatile noexcept> : public member_function_wrapper<R (O::*)(Args...) const volatile noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...)& noexcept> : public member_function_wrapper<R (O::*)(Args...)& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const& noexcept> : public member_function_wrapper<R (O::*)(Args...) const& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const volatile& noexcept> : public member_function_wrapper<R (O::*)(Args...) const volatile& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...)& noexcept> : public member_function_wrapper<R (O::*)(Args..., ...)& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...) const& noexcept> : public member_function_wrapper<R (O::*)(Args..., ...) const& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...) const volatile& noexcept>
+ : public member_function_wrapper<R (O::*)(Args..., ...) const volatile& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...)&& noexcept> : public member_function_wrapper<R (O::*)(Args...)& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const&& noexcept> : public member_function_wrapper<R (O::*)(Args...) const& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args...) const volatile&& noexcept> : public member_function_wrapper<R (O::*)(Args...) const volatile& noexcept, R, O, Args...> {
+ };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...)&& noexcept> : public member_function_wrapper<R (O::*)(Args..., ...)& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...) const&& noexcept> : public member_function_wrapper<R (O::*)(Args..., ...) const& noexcept, R, O, Args...> { };
+
+ template <typename R, typename O, typename... Args>
+ struct wrapper<R (O::*)(Args..., ...) const volatile&& noexcept>
+ : public member_function_wrapper<R (O::*)(Args..., ...) const volatile& noexcept, R, O, Args...> { };
+
+#endif // noexcept is part of a function's type
+
+} // namespace sol
+
+// end of sol/wrapper.hpp
+
+#include <memory>
+
+namespace sol { namespace function_detail {
+ template <typename Fx, int start = 1, bool is_yielding = false>
+ int call(lua_State* L) {
+ Fx& fx = stack::get<user<Fx>>(L, upvalue_index(start));
+ int nr = fx(L);
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+}} // namespace sol::function_detail
+
+// end of sol/function_types_core.hpp
+
+// beginning of sol/function_types_templated.hpp
+
+// beginning of sol/call.hpp
+
+// beginning of sol/property.hpp
+
+#include <type_traits>
+#include <utility>
+
+namespace sol {
+ namespace detail {
+ struct no_prop { };
+ } // namespace detail
+
+ template <typename R, typename W>
+ struct property_wrapper : detail::ebco<R, 0>, detail::ebco<W, 1> {
+ private:
+ using read_base_t = detail::ebco<R, 0>;
+ using write_base_t = detail::ebco<W, 1>;
+
+ public:
+ template <typename Rx, typename Wx>
+ property_wrapper(Rx&& r, Wx&& w) : read_base_t(std::forward<Rx>(r)), write_base_t(std::forward<Wx>(w)) {
+ }
+
+ W& write() {
+ return write_base_t::value();
+ }
+
+ const W& write() const {
+ return write_base_t::value();
+ }
+
+ R& read() {
+ return read_base_t::value();
+ }
+
+ const R& read() const {
+ return read_base_t::value();
+ }
+ };
+
+ template <typename F, typename G>
+ inline decltype(auto) property(F&& f, G&& g) {
+ typedef lua_bind_traits<meta::unqualified_t<F>> left_traits;
+ typedef lua_bind_traits<meta::unqualified_t<G>> right_traits;
+ if constexpr (left_traits::free_arity < right_traits::free_arity) {
+ return property_wrapper<std::decay_t<F>, std::decay_t<G>>(std::forward<F>(f), std::forward<G>(g));
+ }
+ else {
+ return property_wrapper<std::decay_t<G>, std::decay_t<F>>(std::forward<G>(g), std::forward<F>(f));
+ }
+ }
+
+ template <typename F>
+ inline decltype(auto) property(F&& f) {
+ typedef lua_bind_traits<meta::unqualified_t<F>> left_traits;
+ if constexpr (left_traits::free_arity < 2) {
+ return property_wrapper<std::decay_t<F>, detail::no_prop>(std::forward<F>(f), detail::no_prop());
+ }
+ else {
+ return property_wrapper<detail::no_prop, std::decay_t<F>>(detail::no_prop(), std::forward<F>(f));
+ }
+ }
+
+ template <typename F>
+ inline decltype(auto) readonly_property(F&& f) {
+ return property_wrapper<std::decay_t<F>, detail::no_prop>(std::forward<F>(f), detail::no_prop());
+ }
+
+ template <typename F>
+ inline decltype(auto) writeonly_property(F&& f) {
+ return property_wrapper<detail::no_prop, std::decay_t<F>>(detail::no_prop(), std::forward<F>(f));
+ }
+
+ template <typename T>
+ struct readonly_wrapper : detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ using base_t::base_t;
+
+ operator T&() {
+ return base_t::value();
+ }
+ operator const T&() const {
+ return base_t::value();
+ }
+ };
+
+ // Allow someone to make a member variable readonly (const)
+ template <typename R, typename T>
+ inline auto readonly(R T::*v) {
+ return readonly_wrapper<meta::unqualified_t<decltype(v)>>(v);
+ }
+
+ template <typename T>
+ struct var_wrapper : detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ using base_t::base_t;
+ };
+
+ template <typename V>
+ inline auto var(V&& v) {
+ typedef std::decay_t<V> T;
+ return var_wrapper<T>(std::forward<V>(v));
+ }
+
+ namespace meta {
+ template <typename T>
+ using is_member_object = std::integral_constant<bool, std::is_member_object_pointer_v<T> || is_specialization_of_v<T, readonly_wrapper>>;
+
+ template <typename T>
+ inline constexpr bool is_member_object_v = is_member_object<T>::value;
+
+ template <typename T>
+ using is_member_object_or_function = std::integral_constant<bool, is_member_object_v<T> || std::is_member_pointer_v<T>>;
+
+ template <typename T>
+ inline constexpr bool is_member_object_or_function_v = is_member_object_or_function<T>::value;
+ } // namespace meta
+
+} // namespace sol
+
+// end of sol/property.hpp
+
+// beginning of sol/protect.hpp
+
+#include <utility>
+
+namespace sol {
+
+ template <typename T>
+ struct protect_t {
+ T value;
+
+ template <typename Arg, typename... Args, meta::disable<std::is_same<protect_t, meta::unqualified_t<Arg>>> = meta::enabler>
+ protect_t(Arg&& arg, Args&&... args) : value(std::forward<Arg>(arg), std::forward<Args>(args)...) {
+ }
+
+ protect_t(const protect_t&) = default;
+ protect_t(protect_t&&) = default;
+ protect_t& operator=(const protect_t&) = default;
+ protect_t& operator=(protect_t&&) = default;
+ };
+
+ template <typename T>
+ auto protect(T&& value) {
+ return protect_t<std::decay_t<T>>(std::forward<T>(value));
+ }
+
+} // namespace sol
+
+// end of sol/protect.hpp
+
+namespace sol {
+ namespace u_detail {
+
+ } // namespace u_detail
+
+ namespace policy_detail {
+ template <int I, int... In>
+ inline void handle_policy(static_stack_dependencies<I, In...>, lua_State* L, int&) {
+ if constexpr (sizeof...(In) == 0) {
+ (void)L;
+ return;
+ }
+ else {
+ absolute_index ai(L, I);
+ if (type_of(L, ai) != type::userdata) {
+ return;
+ }
+ lua_createtable(L, static_cast<int>(sizeof...(In)), 0);
+ stack_reference deps(L, -1);
+ auto per_dep = [&L, &deps](int i) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushvalue(L, i);
+ luaL_ref(L, deps.stack_index());
+ };
+ (void)per_dep;
+ (void)detail::swallow { int(), (per_dep(In), int())... };
+ lua_setuservalue(L, ai);
+ }
+ }
+
+ template <int... In>
+ inline void handle_policy(returns_self_with<In...>, lua_State* L, int& pushed) {
+ pushed = stack::push(L, raw_index(1));
+ handle_policy(static_stack_dependencies<-1, In...>(), L, pushed);
+ }
+
+ inline void handle_policy(const stack_dependencies& sdeps, lua_State* L, int&) {
+ absolute_index ai(L, sdeps.target);
+ if (type_of(L, ai) != type::userdata) {
+ return;
+ }
+ lua_createtable(L, static_cast<int>(sdeps.size()), 0);
+ stack_reference deps(L, -1);
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, static_cast<int>(sdeps.size()), detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ for (std::size_t i = 0; i < sdeps.size(); ++i) {
+ lua_pushvalue(L, sdeps.stack_indices[i]);
+ luaL_ref(L, deps.stack_index());
+ }
+ lua_setuservalue(L, ai);
+ }
+
+ template <typename P, meta::disable<std::is_base_of<detail::policy_base_tag, meta::unqualified_t<P>>> = meta::enabler>
+ inline void handle_policy(P&& p, lua_State* L, int& pushed) {
+ pushed = std::forward<P>(p)(L, pushed);
+ }
+ } // namespace policy_detail
+
+ namespace function_detail {
+ inline int no_construction_error(lua_State* L) {
+ return luaL_error(L, "sol: cannot call this constructor (tagged as non-constructible)");
+ }
+ } // namespace function_detail
+
+ namespace call_detail {
+
+ template <typename R, typename W>
+ inline auto& pick(std::true_type, property_wrapper<R, W>& f) {
+ return f.read();
+ }
+
+ template <typename R, typename W>
+ inline auto& pick(std::false_type, property_wrapper<R, W>& f) {
+ return f.write();
+ }
+
+ template <typename T, typename List>
+ struct void_call : void_call<T, meta::function_args_t<List>> { };
+
+ template <typename T, typename... Args>
+ struct void_call<T, types<Args...>> {
+ static void call(Args...) {
+ }
+ };
+
+ template <typename T, bool checked, bool clean_stack>
+ struct constructor_match {
+ T* obj_;
+ reference* obj_lua_ref_;
+ stack::stack_detail::undefined_metatable* p_umf_;
+
+ constructor_match(T* obj_ptr, reference& obj_lua_ref, stack::stack_detail::undefined_metatable& umf)
+ : obj_(obj_ptr), obj_lua_ref_(&obj_lua_ref), p_umf_(&umf) {
+ }
+
+ template <typename Fx, std::size_t I, typename... R, typename... Args>
+ int operator()(types<Fx>, meta::index_value<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start) const {
+ detail::default_construct func {};
+ int result = stack::call_into_lua<checked, clean_stack>(r, a, L, start, func, this->obj_);
+ // construct userdata table
+ // SPECIFICALLY, after we've created it successfully.
+ // If the constructor exits for any reason we have to break things down...
+ if constexpr (clean_stack) {
+ obj_lua_ref_->push();
+ (*this->p_umf_)();
+ obj_lua_ref_->pop();
+ }
+ else {
+ (*this->p_umf_)();
+ }
+ return result;
+ }
+ };
+
+ namespace overload_detail {
+ template <std::size_t... M, typename Match, typename... Args>
+ inline int overload_match_arity(types<>, std::index_sequence<>, std::index_sequence<M...>, Match&&, lua_State* L, int, int, Args&&...) {
+ return luaL_error(L, "sol: no matching function call takes this number of arguments and the specified types");
+ }
+
+ template <typename Fx, typename... Fxs, std::size_t I, std::size_t... In, std::size_t... M, typename Match, typename... Args>
+ inline int overload_match_arity(types<Fx, Fxs...>, std::index_sequence<I, In...>, std::index_sequence<M...>, Match&& matchfx, lua_State* L,
+ int fxarity, int start, Args&&... args) {
+ typedef lua_bind_traits<meta::unwrap_unqualified_t<Fx>> traits;
+ typedef meta::tuple_types<typename traits::return_type> return_types;
+ typedef typename traits::free_args_list args_list;
+ // compile-time eliminate any functions that we know ahead of time are of improper arity
+ if constexpr (!traits::runtime_variadics_t::value
+ && meta::find_in_pack_v<meta::index_value<traits::free_arity>, meta::index_value<M>...>::value) {
+ return overload_match_arity(types<Fxs...>(),
+ std::index_sequence<In...>(),
+ std::index_sequence<M...>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+ else {
+ if constexpr (!traits::runtime_variadics_t::value) {
+ if (traits::free_arity != fxarity) {
+ return overload_match_arity(types<Fxs...>(),
+ std::index_sequence<In...>(),
+ std::index_sequence<traits::free_arity, M...>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+ }
+ stack::record tracking {};
+ if (!stack::stack_detail::check_types(args_list(), L, start, &no_panic, tracking)) {
+ return overload_match_arity(types<Fxs...>(),
+ std::index_sequence<In...>(),
+ std::index_sequence<M...>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+ return matchfx(types<Fx>(), meta::index_value<I>(), return_types(), args_list(), L, fxarity, start, std::forward<Args>(args)...);
+ }
+ }
+
+ template <std::size_t... M, typename Match, typename... Args>
+ inline int overload_match_arity_single(
+ types<>, std::index_sequence<>, std::index_sequence<M...>, Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) {
+ return overload_match_arity(types<>(),
+ std::index_sequence<>(),
+ std::index_sequence<M...>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+
+ template <typename Fx, std::size_t I, std::size_t... M, typename Match, typename... Args>
+ inline int overload_match_arity_single(
+ types<Fx>, std::index_sequence<I>, std::index_sequence<M...>, Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) {
+ typedef lua_bind_traits<meta::unwrap_unqualified_t<Fx>> traits;
+ typedef meta::tuple_types<typename traits::return_type> return_types;
+ typedef typename traits::free_args_list args_list;
+ // compile-time eliminate any functions that we know ahead of time are of improper arity
+ if constexpr (!traits::runtime_variadics_t::value
+ && meta::find_in_pack_v<meta::index_value<traits::free_arity>, meta::index_value<M>...>::value) {
+ return overload_match_arity(types<>(),
+ std::index_sequence<>(),
+ std::index_sequence<M...>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+ if constexpr (!traits::runtime_variadics_t::value) {
+ if (traits::free_arity != fxarity) {
+ return overload_match_arity(types<>(),
+ std::index_sequence<>(),
+ std::index_sequence<traits::free_arity, M...>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+ }
+ return matchfx(types<Fx>(), meta::index_value<I>(), return_types(), args_list(), L, fxarity, start, std::forward<Args>(args)...);
+ }
+
+ template <typename Fx, typename Fx1, typename... Fxs, std::size_t I, std::size_t I1, std::size_t... In, std::size_t... M, typename Match,
+ typename... Args>
+ inline int overload_match_arity_single(types<Fx, Fx1, Fxs...>, std::index_sequence<I, I1, In...>, std::index_sequence<M...>, Match&& matchfx,
+ lua_State* L, int fxarity, int start, Args&&... args) {
+ typedef lua_bind_traits<meta::unwrap_unqualified_t<Fx>> traits;
+ typedef meta::tuple_types<typename traits::return_type> return_types;
+ typedef typename traits::free_args_list args_list;
+ // compile-time eliminate any functions that we know ahead of time are of improper arity
+ if constexpr (!traits::runtime_variadics_t::value
+ && meta::find_in_pack_v<meta::index_value<traits::free_arity>, meta::index_value<M>...>::value) {
+ return overload_match_arity(types<Fx1, Fxs...>(),
+ std::index_sequence<I1, In...>(),
+ std::index_sequence<M...>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+ else {
+ if constexpr (!traits::runtime_variadics_t::value) {
+ if (traits::free_arity != fxarity) {
+ return overload_match_arity(types<Fx1, Fxs...>(),
+ std::index_sequence<I1, In...>(),
+ std::index_sequence<traits::free_arity, M...>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+ }
+ stack::record tracking {};
+ if (!stack::stack_detail::check_types(args_list(), L, start, &no_panic, tracking)) {
+ return overload_match_arity(types<Fx1, Fxs...>(),
+ std::index_sequence<I1, In...>(),
+ std::index_sequence<M...>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+ return matchfx(types<Fx>(), meta::index_value<I>(), return_types(), args_list(), L, fxarity, start, std::forward<Args>(args)...);
+ }
+ }
+ } // namespace overload_detail
+
+ template <typename... Functions, typename Match, typename... Args>
+ inline int overload_match_arity(Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) {
+ return overload_detail::overload_match_arity_single(types<Functions...>(),
+ std::make_index_sequence<sizeof...(Functions)>(),
+ std::index_sequence<>(),
+ std::forward<Match>(matchfx),
+ L,
+ fxarity,
+ start,
+ std::forward<Args>(args)...);
+ }
+
+ template <typename... Functions, typename Match, typename... Args>
+ inline int overload_match(Match&& matchfx, lua_State* L, int start, Args&&... args) {
+ int fxarity = lua_gettop(L) - (start - 1);
+ return overload_match_arity<Functions...>(std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
+ }
+
+ template <typename T, typename... TypeLists, typename Match, typename... Args>
+ inline int construct_match(Match&& matchfx, lua_State* L, int fxarity, int start, Args&&... args) {
+ // use same overload resolution matching as all other parts of the framework
+ return overload_match_arity<decltype(void_call<T, TypeLists>::call)...>(
+ std::forward<Match>(matchfx), L, fxarity, start, std::forward<Args>(args)...);
+ }
+
+ template <typename T, bool checked, bool clean_stack, typename... TypeLists>
+ inline int construct_trampolined(lua_State* L) {
+ static const auto& meta = usertype_traits<T>::metatable();
+ int argcount = lua_gettop(L);
+ call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, usertype_traits<T>::user_metatable(), 1) : call_syntax::dot;
+ argcount -= static_cast<int>(syntax);
+
+ T* obj = detail::usertype_allocate<T>(L);
+ reference userdataref(L, -1);
+ stack::stack_detail::undefined_metatable umf(L, &meta[0], &stack::stack_detail::set_undefined_methods_on<T>);
+
+ // put userdata at the first index
+ lua_insert(L, 1);
+ construct_match<T, TypeLists...>(constructor_match<T, checked, clean_stack>(obj, userdataref, umf), L, argcount, 1 + static_cast<int>(syntax));
+
+ userdataref.push();
+ return 1;
+ }
+
+ template <typename T, bool checked, bool clean_stack, typename... TypeLists>
+ inline int construct(lua_State* L) {
+ return detail::static_trampoline<&construct_trampolined<T, checked, clean_stack, TypeLists...>>(L);
+ }
+
+ template <typename F, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename = void>
+ struct agnostic_lua_call_wrapper {
+ template <typename Fx, typename... Args>
+ static int call(lua_State* L, Fx&& f, Args&&... args) {
+ using uFx = meta::unqualified_t<Fx>;
+ static constexpr bool is_ref = is_lua_reference_v<uFx>;
+ if constexpr (is_ref) {
+ if constexpr (is_index) {
+ return stack::push(L, std::forward<Fx>(f), std::forward<Args>(args)...);
+ }
+ else {
+ std::forward<Fx>(f) = stack::unqualified_get<F>(L, boost + (is_variable ? 3 : 1));
+ return 0;
+ }
+ }
+ else {
+ using wrap = wrapper<uFx>;
+ using traits_type = typename wrap::traits_type;
+ using fp_t = typename traits_type::function_pointer_type;
+ constexpr bool is_function_pointer_convertible = std::is_class_v<uFx> && std::is_convertible_v<std::decay_t<Fx>, fp_t>;
+ if constexpr (is_function_pointer_convertible) {
+ fp_t fx = f;
+ return agnostic_lua_call_wrapper<fp_t, is_index, is_variable, checked, boost, clean_stack> {}.call(
+ L, fx, std::forward<Args>(args)...);
+ }
+ else {
+ using returns_list = typename wrap::returns_list;
+ using args_list = typename wrap::free_args_list;
+ using caller = typename wrap::caller;
+ return stack::call_into_lua<checked, clean_stack>(
+ returns_list(), args_list(), L, boost + 1, caller(), std::forward<Fx>(f), std::forward<Args>(args)...);
+ }
+ }
+ }
+ };
+
+ template <typename T, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct agnostic_lua_call_wrapper<var_wrapper<T>, is_index, is_variable, checked, boost, clean_stack, C> {
+ template <typename F>
+ static int call(lua_State* L, F&& f) {
+ if constexpr (is_index) {
+ constexpr bool is_stack = is_stack_based_v<meta::unqualified_t<decltype(detail::unwrap(f.value()))>>;
+ if constexpr (clean_stack && !is_stack) {
+ lua_settop(L, 0);
+ }
+ return stack::push_reference(L, detail::unwrap(f.value()));
+ }
+ else {
+ if constexpr (std::is_const_v<meta::unwrapped_t<T>>) {
+ (void)f;
+ return luaL_error(L, "sol: cannot write to a readonly (const) variable");
+ }
+ else {
+ using R = meta::unwrapped_t<T>;
+ if constexpr (std::is_assignable_v<std::add_lvalue_reference_t<meta::unqualified_t<R>>, R>) {
+ detail::unwrap(f.value()) = stack::unqualified_get<meta::unwrapped_t<T>>(L, boost + (is_variable ? 3 : 1));
+ if (clean_stack) {
+ lua_settop(L, 0);
+ }
+ return 0;
+ }
+ else {
+ return luaL_error(L, "sol: cannot write to this variable: copy assignment/constructor not available");
+ }
+ }
+ }
+ }
+ };
+
+ template <bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct agnostic_lua_call_wrapper<lua_CFunction_ref, is_index, is_variable, checked, boost, clean_stack, C> {
+ static int call(lua_State* L, lua_CFunction_ref f) {
+ return f(L);
+ }
+ };
+
+ template <bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct agnostic_lua_call_wrapper<lua_CFunction, is_index, is_variable, checked, boost, clean_stack, C> {
+ static int call(lua_State* L, lua_CFunction f) {
+ return f(L);
+ }
+ };
+
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ template <bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct agnostic_lua_call_wrapper<detail::lua_CFunction_noexcept, is_index, is_variable, checked, boost, clean_stack, C> {
+ static int call(lua_State* L, detail::lua_CFunction_noexcept f) {
+ return f(L);
+ }
+ };
+#endif // noexcept function types
+
+ template <bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct agnostic_lua_call_wrapper<detail::no_prop, is_index, is_variable, checked, boost, clean_stack, C> {
+ static int call(lua_State* L, const detail::no_prop&) {
+ return luaL_error(L, is_index ? "sol: cannot read from a writeonly property" : "sol: cannot write to a readonly property");
+ }
+ };
+
+ template <bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct agnostic_lua_call_wrapper<no_construction, is_index, is_variable, checked, boost, clean_stack, C> {
+ static int call(lua_State* L, const no_construction&) {
+ return function_detail::no_construction_error(L);
+ }
+ };
+
+ template <typename... Args, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct agnostic_lua_call_wrapper<bases<Args...>, is_index, is_variable, checked, boost, clean_stack, C> {
+ static int call(lua_State*, const bases<Args...>&) {
+ // Uh. How did you even call this, lul
+ return 0;
+ }
+ };
+
+ template <typename T, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct agnostic_lua_call_wrapper<std::reference_wrapper<T>, is_index, is_variable, checked, boost, clean_stack, C> {
+ static int call(lua_State* L, std::reference_wrapper<T> f) {
+ agnostic_lua_call_wrapper<T, is_index, is_variable, checked, boost, clean_stack> alcw {};
+ return alcw.call(L, f.get());
+ }
+ };
+
+ template <typename T, typename F, bool is_index, bool is_variable, bool checked = detail::default_safe_function_calls, int boost = 0,
+ bool clean_stack = true, typename = void>
+ struct lua_call_wrapper {
+ template <typename Fx, typename... Args>
+ static int call(lua_State* L, Fx&& fx, Args&&... args) {
+ if constexpr (std::is_member_function_pointer_v<F>) {
+ using wrap = wrapper<F>;
+ using object_type = typename wrap::object_type;
+ if constexpr (sizeof...(Args) < 1) {
+ using Ta = meta::conditional_t<std::is_void_v<T>, object_type, T>;
+ static_assert(std::is_base_of_v<object_type, Ta>,
+ "It seems like you might have accidentally bound a class type with a member function method that does not correspond to the "
+ "class. For example, there could be a small type in your new_usertype<T>(...) binding, where you specify one class \"T\" "
+ "but then bind member methods from a complete unrelated class. Check things over!");
+#if SOL_IS_ON(SOL_SAFE_USERTYPE)
+ auto maybeo = stack::check_get<Ta*>(L, 1);
+ if (!maybeo || maybeo.value() == nullptr) {
+ return luaL_error(L,
+ "sol: received nil for 'self' argument (use ':' for accessing member functions, make sure member variables are "
+ "preceeded by the "
+ "actual object with '.' syntax)");
+ }
+ object_type* o = static_cast<object_type*>(maybeo.value());
+ return call(L, std::forward<Fx>(fx), *o);
+#else
+ object_type& o = static_cast<object_type&>(*stack::unqualified_get<non_null<Ta*>>(L, 1));
+ return call(L, std::forward<Fx>(fx), o);
+#endif // Safety
+ }
+ else {
+ using returns_list = typename wrap::returns_list;
+ using args_list = typename wrap::args_list;
+ using caller = typename wrap::caller;
+ return stack::call_into_lua<checked, clean_stack>(
+ returns_list(), args_list(), L, boost + (is_variable ? 3 : 2), caller(), std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ }
+ else if constexpr (std::is_member_object_pointer_v<F>) {
+ using wrap = wrapper<F>;
+ using object_type = typename wrap::object_type;
+ if constexpr (is_index) {
+ if constexpr (sizeof...(Args) < 1) {
+ using Ta = meta::conditional_t<std::is_void_v<T>, object_type, T>;
+ static_assert(std::is_base_of_v<object_type, Ta>,
+ "It seems like you might have accidentally bound a class type with a member function method that does not correspond "
+ "to the class. For example, there could be a small type in your new_usertype<T>(...) binding, where you specify one "
+ "class \"T\" but then bind member methods from a complete unrelated class. Check things over!");
+#if SOL_IS_ON(SOL_SAFE_USERTYPE)
+ auto maybeo = stack::check_get<Ta*>(L, 1);
+ if (!maybeo || maybeo.value() == nullptr) {
+ if (is_variable) {
+ return luaL_error(L, "sol: 'self' argument is lua_nil (bad '.' access?)");
+ }
+ return luaL_error(L, "sol: 'self' argument is lua_nil (pass 'self' as first argument)");
+ }
+ object_type* o = static_cast<object_type*>(maybeo.value());
+ return call(L, std::forward<Fx>(fx), *o);
+#else
+ object_type& o = static_cast<object_type&>(*stack::get<non_null<Ta*>>(L, 1));
+ return call(L, std::forward<Fx>(fx), o);
+#endif // Safety
+ }
+ else {
+ using returns_list = typename wrap::returns_list;
+ using caller = typename wrap::caller;
+ return stack::call_into_lua<checked, clean_stack>(returns_list(),
+ types<>(),
+ L,
+ boost + (is_variable ? 3 : 2),
+ caller(),
+ std::forward<Fx>(fx),
+ std::forward<Args>(args)...);
+ }
+ }
+ else {
+ using traits_type = lua_bind_traits<F>;
+ using return_type = typename traits_type::return_type;
+ constexpr bool ret_is_const = std::is_const_v<std::remove_reference_t<return_type>>;
+ if constexpr (ret_is_const) {
+ (void)fx;
+ (void)detail::swallow { 0, (static_cast<void>(args), 0)... };
+ return luaL_error(L, "sol: cannot write to a readonly (const) variable");
+ }
+ else {
+ using u_return_type = meta::unqualified_t<return_type>;
+ constexpr bool is_assignable = std::is_copy_assignable_v<u_return_type> || std::is_array_v<u_return_type>;
+ if constexpr (!is_assignable) {
+ (void)fx;
+ (void)detail::swallow { 0, ((void)args, 0)... };
+ return luaL_error(L, "sol: cannot write to this variable: copy assignment/constructor not available");
+ }
+ else {
+ using args_list = typename wrap::args_list;
+ using caller = typename wrap::caller;
+ if constexpr (sizeof...(Args) > 0) {
+ return stack::call_into_lua<checked, clean_stack>(types<void>(),
+ args_list(),
+ L,
+ boost + (is_variable ? 3 : 2),
+ caller(),
+ std::forward<Fx>(fx),
+ std::forward<Args>(args)...);
+ }
+ else {
+ using Ta = meta::conditional_t<std::is_void_v<T>, object_type, T>;
+#if SOL_IS_ON(SOL_SAFE_USERTYPE)
+ auto maybeo = stack::check_get<Ta*>(L, 1);
+ if (!maybeo || maybeo.value() == nullptr) {
+ if (is_variable) {
+ return luaL_error(L, "sol: received nil for 'self' argument (bad '.' access?)");
+ }
+ return luaL_error(L, "sol: received nil for 'self' argument (pass 'self' as first argument)");
+ }
+ object_type* po = static_cast<object_type*>(maybeo.value());
+ object_type& o = *po;
+#else
+ object_type& o = static_cast<object_type&>(*stack::get<non_null<Ta*>>(L, 1));
+#endif // Safety
+
+ return stack::call_into_lua<checked, clean_stack>(
+ types<void>(), args_list(), L, boost + (is_variable ? 3 : 2), caller(), std::forward<Fx>(fx), o);
+ }
+ }
+ }
+ }
+ }
+ else {
+ agnostic_lua_call_wrapper<F, is_index, is_variable, checked, boost, clean_stack> alcw {};
+ return alcw.call(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ }
+ };
+
+ template <typename T, typename F, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, readonly_wrapper<F>, is_index, is_variable, checked, boost, clean_stack, C> {
+ using traits_type = lua_bind_traits<F>;
+ using wrap = wrapper<F>;
+ using object_type = typename wrap::object_type;
+
+ static int call(lua_State* L, readonly_wrapper<F>&& rw) {
+ if constexpr (!is_index) {
+ (void)rw;
+ return luaL_error(L, "sol: cannot write to a sol::readonly variable");
+ }
+ else {
+ lua_call_wrapper<T, F, true, is_variable, checked, boost, clean_stack, C> lcw;
+ return lcw.call(L, std::move(rw.value()));
+ }
+ }
+
+ static int call(lua_State* L, readonly_wrapper<F>&& rw, object_type& o) {
+ if constexpr (!is_index) {
+ (void)o;
+ return call(L, std::move(rw));
+ }
+ else {
+ lua_call_wrapper<T, F, true, is_variable, checked, boost, clean_stack, C> lcw;
+ return lcw.call(L, rw.value(), o);
+ }
+ }
+
+ static int call(lua_State* L, const readonly_wrapper<F>& rw) {
+ if constexpr (!is_index) {
+ (void)rw;
+ return luaL_error(L, "sol: cannot write to a sol::readonly variable");
+ }
+ else {
+ lua_call_wrapper<T, F, true, is_variable, checked, boost, clean_stack, C> lcw;
+ return lcw.call(L, rw.value());
+ }
+ }
+
+ static int call(lua_State* L, const readonly_wrapper<F>& rw, object_type& o) {
+ if constexpr (!is_index) {
+ (void)o;
+ return call(L, rw);
+ }
+ else {
+ lua_call_wrapper<T, F, true, is_variable, checked, boost, clean_stack, C> lcw;
+ return lcw.call(L, rw.value(), o);
+ }
+ }
+ };
+
+ template <typename T, typename... Args, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, constructor_list<Args...>, is_index, is_variable, checked, boost, clean_stack, C> {
+ typedef constructor_list<Args...> F;
+
+ static int call(lua_State* L, F&) {
+ const auto& meta = usertype_traits<T>::metatable();
+ int argcount = lua_gettop(L);
+ call_syntax syntax = argcount > 0 ? stack::get_call_syntax(L, usertype_traits<T>::user_metatable(), 1) : call_syntax::dot;
+ argcount -= static_cast<int>(syntax);
+
+ T* obj = detail::usertype_allocate<T>(L);
+ reference userdataref(L, -1);
+ stack::stack_detail::undefined_metatable umf(L, &meta[0], &stack::stack_detail::set_undefined_methods_on<T>);
+
+ // put userdata at the first index
+ lua_insert(L, 1);
+ // Because of the way constructors work,
+ // we have to kill the data, but only if the cosntructor is successfulyl invoked...
+ // if it's not successfully invoked and we panic,
+ // we cannot actually deallcoate/delete the data.
+ construct_match<T, Args...>(
+ constructor_match<T, checked, clean_stack>(obj, userdataref, umf), L, argcount, boost + 1 + 1 + static_cast<int>(syntax));
+
+ userdataref.push();
+ return 1;
+ }
+ };
+
+ template <typename T, typename... Cxs, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, constructor_wrapper<Cxs...>, is_index, is_variable, checked, boost, clean_stack, C> {
+ typedef constructor_wrapper<Cxs...> F;
+
+ struct onmatch {
+ template <typename Fx, std::size_t I, typename... R, typename... Args>
+ int operator()(types<Fx>, meta::index_value<I>, types<R...> r, types<Args...> a, lua_State* L, int, int start, F& f) {
+ const auto& meta = usertype_traits<T>::metatable();
+ T* obj = detail::usertype_allocate<T>(L);
+ reference userdataref(L, -1);
+ stack::stack_detail::undefined_metatable umf(L, &meta[0], &stack::stack_detail::set_undefined_methods_on<T>);
+ umf();
+
+ auto& func = std::get<I>(f.functions);
+ // put userdata at the first index
+ lua_insert(L, 1);
+ stack::call_into_lua<checked, clean_stack>(r, a, L, boost + 1 + start, func, detail::implicit_wrapper<T>(obj));
+
+ userdataref.push();
+ return 1;
+ }
+ };
+
+ static int call(lua_State* L, F& f) {
+ call_syntax syntax = stack::get_call_syntax(L, usertype_traits<T>::user_metatable(), 1);
+ int syntaxval = static_cast<int>(syntax);
+ int argcount = lua_gettop(L) - syntaxval;
+ return construct_match<T, meta::pop_front_type_t<meta::function_args_t<Cxs>>...>(onmatch(), L, argcount, 1 + syntaxval, f);
+ }
+ };
+
+ template <typename T, typename Fx, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, destructor_wrapper<Fx>, is_index, is_variable, checked, boost, clean_stack, C> {
+
+ template <typename F>
+ static int call(lua_State* L, F&& f) {
+ if constexpr (std::is_void_v<Fx>) {
+ return detail::usertype_alloc_destroy<T>(L);
+ }
+ else {
+ using uFx = meta::unqualified_t<Fx>;
+ lua_call_wrapper<T, uFx, is_index, is_variable, checked, boost, clean_stack> lcw {};
+ return lcw.call(L, std::forward<F>(f).fx);
+ }
+ }
+ };
+
+ template <typename T, typename... Fs, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, overload_set<Fs...>, is_index, is_variable, checked, boost, clean_stack, C> {
+ typedef overload_set<Fs...> F;
+
+ struct on_match {
+ template <typename Fx, std::size_t I, typename... R, typename... Args>
+ int operator()(types<Fx>, meta::index_value<I>, types<R...>, types<Args...>, lua_State* L, int, int, F& fx) {
+ auto& f = std::get<I>(fx.functions);
+ return lua_call_wrapper<T, Fx, is_index, is_variable, checked, boost> {}.call(L, f);
+ }
+ };
+
+ static int call(lua_State* L, F& fx) {
+ return overload_match_arity<Fs...>(on_match(), L, lua_gettop(L), 1, fx);
+ }
+ };
+
+ template <typename T, typename... Fs, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, factory_wrapper<Fs...>, is_index, is_variable, checked, boost, clean_stack, C> {
+ typedef factory_wrapper<Fs...> F;
+
+ struct on_match {
+ template <typename Fx, std::size_t I, typename... R, typename... Args>
+ int operator()(types<Fx>, meta::index_value<I>, types<R...>, types<Args...>, lua_State* L, int, int, F& fx) {
+ auto& f = std::get<I>(fx.functions);
+ return lua_call_wrapper<T, Fx, is_index, is_variable, checked, boost, clean_stack> {}.call(L, f);
+ }
+ };
+
+ static int call(lua_State* L, F& fx) {
+ return overload_match_arity<Fs...>(on_match(), L, lua_gettop(L) - boost, 1 + boost, fx);
+ }
+ };
+
+ template <typename T, typename R, typename W, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, property_wrapper<R, W>, is_index, is_variable, checked, boost, clean_stack, C> {
+ typedef meta::conditional_t<is_index, R, W> P;
+ typedef meta::unqualified_t<P> U;
+ typedef wrapper<U> wrap;
+ typedef lua_bind_traits<U> traits_type;
+ typedef meta::unqualified_t<typename traits_type::template arg_at<0>> object_type;
+
+ template <typename F, typename... Args>
+ static int call(lua_State* L, F&& f, Args&&... args) {
+ constexpr bool is_specialized = meta::any<std::is_same<U, detail::no_prop>,
+ meta::is_specialization_of<U, var_wrapper>,
+ meta::is_specialization_of<U, constructor_wrapper>,
+ meta::is_specialization_of<U, constructor_list>,
+ std::is_member_pointer<U>>::value;
+ if constexpr (is_specialized) {
+ if constexpr (is_index) {
+ decltype(auto) p = f.read();
+ lua_call_wrapper<T, meta::unqualified_t<decltype(p)>, is_index, is_variable, checked, boost, clean_stack> lcw {};
+ return lcw.call(L, p, std::forward<Args>(args)...);
+ }
+ else {
+ decltype(auto) p = f.write();
+ lua_call_wrapper<T, meta::unqualified_t<decltype(p)>, is_index, is_variable, checked, boost, clean_stack> lcw {};
+ return lcw.call(L, p, std::forward<Args>(args)...);
+ }
+ }
+ else {
+ constexpr bool non_class_object_type = meta::any<std::is_void<object_type>,
+ meta::boolean<lua_type_of<meta::unwrap_unqualified_t<object_type>>::value != type::userdata>>::value;
+ if constexpr (non_class_object_type) {
+ // The type being void means we don't have any arguments, so it might be a free functions?
+ using args_list = typename traits_type::free_args_list;
+ using returns_list = typename wrap::returns_list;
+ using caller = typename wrap::caller;
+ if constexpr (is_index) {
+ decltype(auto) pf = f.read();
+ return stack::call_into_lua<checked, clean_stack>(
+ returns_list(), args_list(), L, boost + (is_variable ? 3 : 2), caller(), pf);
+ }
+ else {
+ decltype(auto) pf = f.write();
+ return stack::call_into_lua<checked, clean_stack>(
+ returns_list(), args_list(), L, boost + (is_variable ? 3 : 2), caller(), pf);
+ }
+ }
+ else {
+ using args_list = meta::pop_front_type_t<typename traits_type::free_args_list>;
+ using Ta = T;
+ using Oa = std::remove_pointer_t<object_type>;
+#if SOL_IS_ON(SOL_SAFE_USERTYPE)
+ auto maybeo = stack::check_get<Ta*>(L, 1);
+ if (!maybeo || maybeo.value() == nullptr) {
+ if (is_variable) {
+ return luaL_error(L, "sol: 'self' argument is lua_nil (bad '.' access?)");
+ }
+ return luaL_error(L, "sol: 'self' argument is lua_nil (pass 'self' as first argument)");
+ }
+ Oa* o = static_cast<Oa*>(maybeo.value());
+#else
+ Oa* o = static_cast<Oa*>(stack::get<non_null<Ta*>>(L, 1));
+#endif // Safety
+ using returns_list = typename wrap::returns_list;
+ using caller = typename wrap::caller;
+ if constexpr (is_index) {
+ decltype(auto) pf = f.read();
+ return stack::call_into_lua<checked, clean_stack>(
+ returns_list(), args_list(), L, boost + (is_variable ? 3 : 2), caller(), pf, detail::implicit_wrapper<Oa>(*o));
+ }
+ else {
+ decltype(auto) pf = f.write();
+ return stack::call_into_lua<checked, clean_stack>(
+ returns_list(), args_list(), L, boost + (is_variable ? 3 : 2), caller(), pf, detail::implicit_wrapper<Oa>(*o));
+ }
+ }
+ }
+ }
+ };
+
+ template <typename T, typename V, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, protect_t<V>, is_index, is_variable, checked, boost, clean_stack, C> {
+ typedef protect_t<V> F;
+
+ template <typename... Args>
+ static int call(lua_State* L, F& fx, Args&&... args) {
+ return lua_call_wrapper<T, V, is_index, is_variable, true, boost, clean_stack> {}.call(L, fx.value, std::forward<Args>(args)...);
+ }
+ };
+
+ template <typename T, typename F, typename... Policies, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, policy_wrapper<F, Policies...>, is_index, is_variable, checked, boost, clean_stack, C> {
+ typedef policy_wrapper<F, Policies...> P;
+
+ template <std::size_t... In>
+ static int call(std::index_sequence<In...>, lua_State* L, P& fx) {
+ int pushed = lua_call_wrapper<T, F, is_index, is_variable, checked, boost, false, C> {}.call(L, fx.value);
+ (void)detail::swallow { int(), (policy_detail::handle_policy(std::get<In>(fx.policies), L, pushed), int())... };
+ return pushed;
+ }
+
+ static int call(lua_State* L, P& fx) {
+ typedef typename P::indices indices;
+ return call(indices(), L, fx);
+ }
+ };
+
+ template <typename T, typename Y, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, yielding_t<Y>, is_index, is_variable, checked, boost, clean_stack, C> {
+ template <typename F>
+ static int call(lua_State* L, F&& f) {
+ return lua_call_wrapper<T, meta::unqualified_t<Y>, is_index, is_variable, checked, boost, clean_stack> {}.call(L, f.func);
+ }
+ };
+
+ template <typename T, typename Sig, typename P, bool is_index, bool is_variable, bool checked, int boost, bool clean_stack, typename C>
+ struct lua_call_wrapper<T, function_arguments<Sig, P>, is_index, is_variable, checked, boost, clean_stack, C> {
+ static int call(lua_State* L, const function_arguments<Sig, P>& f) {
+ lua_call_wrapper<T, meta::unqualified_t<P>, is_index, is_variable, checked, boost, clean_stack> lcw {};
+ return lcw.call(L, std::get<0>(f.arguments));
+ }
+
+ static int call(lua_State* L, function_arguments<Sig, P>& f) {
+ lua_call_wrapper<T, meta::unqualified_t<P>, is_index, is_variable, checked, boost, clean_stack> lcw {};
+ return lcw.call(L, std::get<0>(f.arguments));
+ }
+
+ static int call(lua_State* L, function_arguments<Sig, P>&& f) {
+ lua_call_wrapper<T, meta::unqualified_t<P>, is_index, is_variable, checked, boost, clean_stack> lcw {};
+ return lcw.call(L, std::get<0>(std::move(f.arguments)));
+ }
+ };
+
+ template <typename T, bool is_index, bool is_variable, int boost = 0, bool checked = detail::default_safe_function_calls, bool clean_stack = true,
+ typename Fx, typename... Args>
+ inline int call_wrapped(lua_State* L, Fx&& fx, Args&&... args) {
+ using uFx = meta::unqualified_t<Fx>;
+ if constexpr (meta::is_specialization_of_v<uFx, yielding_t>) {
+ using real_fx = meta::unqualified_t<decltype(std::forward<Fx>(fx).func)>;
+ lua_call_wrapper<T, real_fx, is_index, is_variable, checked, boost, clean_stack> lcw {};
+ return lcw.call(L, std::forward<Fx>(fx).func, std::forward<Args>(args)...);
+ }
+ else {
+ lua_call_wrapper<T, uFx, is_index, is_variable, checked, boost, clean_stack> lcw {};
+ return lcw.call(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ }
+
+ template <typename T, bool is_index, bool is_variable, typename F, int start = 1, bool checked = detail::default_safe_function_calls,
+ bool clean_stack = true>
+ inline int call_user(lua_State* L) {
+ auto& fx = stack::unqualified_get<user<F>>(L, upvalue_index(start));
+ using uFx = meta::unqualified_t<F>;
+ int nr = call_wrapped<T, is_index, is_variable, 0, checked, clean_stack>(L, fx);
+ if constexpr (meta::is_specialization_of_v<uFx, yielding_t>) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ template <typename T, typename = void>
+ struct is_var_bind : std::false_type { };
+
+ template <typename T>
+ struct is_var_bind<T, std::enable_if_t<std::is_member_object_pointer<T>::value>> : std::true_type { };
+
+ template <typename T>
+ struct is_var_bind<T, std::enable_if_t<is_lua_reference_or_proxy<T>::value>> : std::true_type { };
+
+ template <>
+ struct is_var_bind<detail::no_prop> : std::true_type { };
+
+ template <typename R, typename W>
+ struct is_var_bind<property_wrapper<R, W>> : std::true_type { };
+
+ template <typename T>
+ struct is_var_bind<var_wrapper<T>> : std::true_type { };
+
+ template <typename T>
+ struct is_var_bind<readonly_wrapper<T>> : is_var_bind<meta::unqualified_t<T>> { };
+
+ template <typename F, typename... Policies>
+ struct is_var_bind<policy_wrapper<F, Policies...>> : is_var_bind<meta::unqualified_t<F>> { };
+ } // namespace call_detail
+
+ template <typename T>
+ struct is_variable_binding : call_detail::is_var_bind<meta::unqualified_t<T>> { };
+
+ template <typename T>
+ using is_var_wrapper = meta::is_specialization_of<T, var_wrapper>;
+
+ template <typename T>
+ struct is_function_binding : meta::neg<is_variable_binding<T>> { };
+
+} // namespace sol
+
+// end of sol/call.hpp
+
+namespace sol {
+ namespace function_detail {
+ template <typename F, F fx>
+ inline int call_wrapper_variable(std::false_type, lua_State* L) {
+ typedef meta::bind_traits<meta::unqualified_t<F>> traits_type;
+ typedef typename traits_type::args_list args_list;
+ typedef meta::tuple_types<typename traits_type::return_type> return_type;
+ return stack::call_into_lua(return_type(), args_list(), L, 1, fx);
+ }
+
+ template <typename R, typename V, V, typename T>
+ inline int call_set_assignable(std::false_type, T&&, lua_State* L) {
+ return luaL_error(L, "cannot write to this type: copy assignment/constructor not available");
+ }
+
+ template <typename R, typename V, V variable, typename T>
+ inline int call_set_assignable(std::true_type, lua_State* L, T&& mem) {
+ (mem.*variable) = stack::get<R>(L, 2);
+ return 0;
+ }
+
+ template <typename R, typename V, V, typename T>
+ inline int call_set_variable(std::false_type, lua_State* L, T&&) {
+ return luaL_error(L, "cannot write to a const variable");
+ }
+
+ template <typename R, typename V, V variable, typename T>
+ inline int call_set_variable(std::true_type, lua_State* L, T&& mem) {
+ return call_set_assignable<R, V, variable>(std::is_assignable<std::add_lvalue_reference_t<R>, R>(), L, std::forward<T>(mem));
+ }
+
+ template <typename V, V variable>
+ inline int call_wrapper_variable(std::true_type, lua_State* L) {
+ typedef meta::bind_traits<meta::unqualified_t<V>> traits_type;
+ typedef typename traits_type::object_type T;
+ typedef typename traits_type::return_type R;
+ auto& mem = stack::get<T>(L, 1);
+ switch (lua_gettop(L)) {
+ case 1: {
+ decltype(auto) r = (mem.*variable);
+ stack::push_reference(L, std::forward<decltype(r)>(r));
+ return 1;
+ }
+ case 2:
+ return call_set_variable<R, V, variable>(meta::neg<std::is_const<R>>(), L, mem);
+ default:
+ return luaL_error(L, "incorrect number of arguments to member variable function call");
+ }
+ }
+
+ template <typename F, F fx>
+ inline int call_wrapper_function(std::false_type, lua_State* L) {
+ return call_wrapper_variable<F, fx>(std::is_member_object_pointer<F>(), L);
+ }
+
+ template <typename F, F fx>
+ inline int call_wrapper_function(std::true_type, lua_State* L) {
+ return call_detail::call_wrapped<void, false, false>(L, fx);
+ }
+
+ template <typename F, F fx>
+ int call_wrapper_entry(lua_State* L) noexcept(meta::bind_traits<F>::is_noexcept) {
+ return call_wrapper_function<F, fx>(std::is_member_function_pointer<meta::unqualified_t<F>>(), L);
+ }
+
+ template <typename... Fxs>
+ struct c_call_matcher {
+ template <typename Fx, std::size_t I, typename R, typename... Args>
+ int operator()(types<Fx>, meta::index_value<I>, types<R>, types<Args...>, lua_State* L, int, int) const {
+ typedef meta::at_in_pack_t<I, Fxs...> target;
+ return target::call(L);
+ }
+ };
+
+ template <typename F, F fx>
+ inline int c_call_raw(std::true_type, lua_State* L) {
+ return fx(L);
+ }
+
+ template <typename F, F fx>
+ inline int c_call_raw(std::false_type, lua_State* L) {
+#ifdef __clang__
+ return detail::trampoline(L, function_detail::call_wrapper_entry<F, fx>);
+#else
+ return detail::typed_static_trampoline<decltype(&function_detail::call_wrapper_entry<F, fx>), (&function_detail::call_wrapper_entry<F, fx>)>(L);
+#endif // fuck you clang :c
+ }
+
+ } // namespace function_detail
+
+ template <typename F, F fx>
+ inline int c_call(lua_State* L) {
+ typedef meta::unqualified_t<F> Fu;
+ typedef std::integral_constant<bool,
+ std::is_same<Fu, lua_CFunction>::value
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ || std::is_same<Fu, detail::lua_CFunction_noexcept>::value
+#endif
+ >
+ is_raw;
+ return function_detail::c_call_raw<F, fx>(is_raw(), L);
+ }
+
+ template <typename F, F f>
+ struct wrap {
+ typedef F type;
+
+ static int call(lua_State* L) noexcept(noexcept(c_call<type, f>(L))) {
+ return c_call<type, f>(L);
+ }
+ };
+
+ template <typename... Fxs>
+ inline int c_call(lua_State* L) {
+ if constexpr (sizeof...(Fxs) < 2) {
+ using target = meta::at_in_pack_t<0, Fxs...>;
+ return target::call(L);
+ }
+ else {
+ return call_detail::overload_match_arity<typename Fxs::type...>(function_detail::c_call_matcher<Fxs...>(), L, lua_gettop(L), 1);
+ }
+ }
+
+} // namespace sol
+
+// end of sol/function_types_templated.hpp
+
+// beginning of sol/function_types_stateless.hpp
+
+namespace sol { namespace function_detail {
+ template <typename Function>
+ struct upvalue_free_function {
+ using function_type = std::remove_pointer_t<std::decay_t<Function>>;
+ using traits_type = meta::bind_traits<function_type>;
+
+ static int real_call(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ auto udata = stack::stack_detail::get_as_upvalues<function_type*>(L);
+ function_type* fx = udata.first;
+ return call_detail::call_wrapped<void, true, false>(L, fx);
+ }
+
+ template <bool is_yielding, bool no_trampoline>
+ static int call(lua_State* L) {
+ int nr;
+ if constexpr (no_trampoline) {
+ nr = real_call(L);
+ }
+ else {
+ nr = detail::typed_static_trampoline<decltype(&real_call), (&real_call)>(L);
+ }
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+ };
+
+ template <typename T, typename Function>
+ struct upvalue_member_function {
+ typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
+ typedef lua_bind_traits<function_type> traits_type;
+
+ static int real_call(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ // Layout:
+ // idx 1...n: verbatim data of member function pointer
+ // idx n + 1: is the object's void pointer
+ // We don't need to store the size, because the other side is templated
+ // with the same member function pointer type
+ function_type& memfx = stack::get<user<function_type>>(L, upvalue_index(2));
+ auto& item = *static_cast<T*>(stack::get<void*>(L, upvalue_index(3)));
+ return call_detail::call_wrapped<T, true, false, -1>(L, memfx, item);
+ }
+
+ template <bool is_yielding, bool no_trampoline>
+ static int call(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ int nr;
+ if constexpr (no_trampoline) {
+ nr = real_call(L);
+ }
+ else {
+ nr = detail::typed_static_trampoline<decltype(&real_call), (&real_call)>(L);
+ }
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ int operator()(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ return call(L);
+ }
+ };
+
+ template <typename T, typename Function>
+ struct upvalue_member_variable {
+ typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
+ typedef lua_bind_traits<function_type> traits_type;
+
+ static int real_call(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ // Layout:
+ // idx 1...n: verbatim data of member variable pointer
+ // idx n + 1: is the object's void pointer
+ // We don't need to store the size, because the other side is templated
+ // with the same member function pointer type
+ auto memberdata = stack::stack_detail::get_as_upvalues<function_type>(L);
+ auto objdata = stack::stack_detail::get_as_upvalues<T*>(L, memberdata.second);
+ auto& mem = *objdata.first;
+ function_type& var = memberdata.first;
+ switch (lua_gettop(L)) {
+ case 0:
+ return call_detail::call_wrapped<T, true, false, -1>(L, var, mem);
+ case 1:
+ return call_detail::call_wrapped<T, false, false, -1>(L, var, mem);
+ default:
+ return luaL_error(L, "sol: incorrect number of arguments to member variable function");
+ }
+ }
+
+ template <bool is_yielding, bool no_trampoline>
+ static int call(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ int nr;
+ if constexpr (no_trampoline) {
+ nr = real_call(L);
+ }
+ else {
+ nr = detail::typed_static_trampoline<decltype(&real_call), (&real_call)>(L);
+ }
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ int operator()(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ return call(L);
+ }
+ };
+
+ template <typename T, typename Function>
+ struct upvalue_member_variable<T, readonly_wrapper<Function>> {
+ typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
+ typedef lua_bind_traits<function_type> traits_type;
+
+ static int real_call(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ // Layout:
+ // idx 1...n: verbatim data of member variable pointer
+ // idx n + 1: is the object's void pointer
+ // We don't need to store the size, because the other side is templated
+ // with the same member function pointer type
+ auto memberdata = stack::stack_detail::get_as_upvalues<function_type>(L);
+ auto objdata = stack::stack_detail::get_as_upvalues<T*>(L, memberdata.second);
+ auto& mem = *objdata.first;
+ function_type& var = memberdata.first;
+ switch (lua_gettop(L)) {
+ case 0:
+ return call_detail::call_wrapped<T, true, false, -1>(L, var, mem);
+ default:
+ return luaL_error(L, "sol: incorrect number of arguments to member variable function");
+ }
+ }
+
+ template <bool is_yielding, bool no_trampoline>
+ static int call(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ int nr;
+ if constexpr (no_trampoline) {
+ nr = real_call(L);
+ }
+ else {
+ nr = detail::typed_static_trampoline<decltype(&real_call), (&real_call)>(L);
+ }
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ int operator()(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ return call(L);
+ }
+ };
+
+ template <typename T, typename Function>
+ struct upvalue_this_member_function {
+ typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
+ typedef lua_bind_traits<function_type> traits_type;
+
+ static int real_call(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ // Layout:
+ // idx 1...n: verbatim data of member variable pointer
+ function_type& memfx = stack::get<user<function_type>>(L, upvalue_index(2));
+ return call_detail::call_wrapped<T, false, false>(L, memfx);
+ }
+
+ template <bool is_yielding, bool no_trampoline>
+ static int call(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ int nr;
+ if constexpr (no_trampoline) {
+ nr = real_call(L);
+ }
+ else {
+ nr = detail::typed_static_trampoline<decltype(&real_call), (&real_call)>(L);
+ }
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ int operator()(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ return call(L);
+ }
+ };
+
+ template <typename T, typename Function>
+ struct upvalue_this_member_variable {
+ typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
+
+ static int real_call(lua_State* L) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ // Layout:
+ // idx 1...n: verbatim data of member variable pointer
+ auto memberdata = stack::stack_detail::get_as_upvalues<function_type>(L);
+ function_type& var = memberdata.first;
+ switch (lua_gettop(L)) {
+ case 1:
+ return call_detail::call_wrapped<T, true, false>(L, var);
+ case 2:
+ return call_detail::call_wrapped<T, false, false>(L, var);
+ default:
+ return luaL_error(L, "sol: incorrect number of arguments to member variable function");
+ }
+ }
+
+ template <bool is_yielding, bool no_trampoline>
+ static int call(lua_State* L) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ int nr;
+ if constexpr (no_trampoline) {
+ nr = real_call(L);
+ }
+ else {
+ nr = detail::typed_static_trampoline<decltype(&real_call), (&real_call)>(L);
+ }
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ int operator()(lua_State* L) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ return call(L);
+ }
+ };
+
+ template <typename T, typename Function>
+ struct upvalue_this_member_variable<T, readonly_wrapper<Function>> {
+ typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
+ typedef lua_bind_traits<function_type> traits_type;
+
+ static int real_call(lua_State* L) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ // Layout:
+ // idx 1...n: verbatim data of member variable pointer
+ auto memberdata = stack::stack_detail::get_as_upvalues<function_type>(L);
+ function_type& var = memberdata.first;
+ switch (lua_gettop(L)) {
+ case 1:
+ return call_detail::call_wrapped<T, true, false>(L, var);
+ default:
+ return luaL_error(L, "sol: incorrect number of arguments to member variable function");
+ }
+ }
+
+ template <bool is_yielding, bool no_trampoline>
+ static int call(lua_State* L) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ int nr;
+ if constexpr (no_trampoline) {
+ nr = real_call(L);
+ }
+ else {
+ nr = detail::typed_static_trampoline<decltype(&real_call), (&real_call)>(L);
+ }
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ int operator()(lua_State* L) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ return call(L);
+ }
+ };
+}} // namespace sol::function_detail
+
+// end of sol/function_types_stateless.hpp
+
+// beginning of sol/function_types_stateful.hpp
+
+namespace sol { namespace function_detail {
+ template <typename Func, bool is_yielding, bool no_trampoline>
+ struct functor_function {
+ typedef std::decay_t<meta::unwrap_unqualified_t<Func>> function_type;
+ function_type invocation;
+
+ template <typename... Args>
+ functor_function(function_type f, Args&&... args) noexcept(std::is_nothrow_constructible_v<function_type, function_type, Args...>)
+ : invocation(std::move(f), std::forward<Args>(args)...) {
+ }
+
+ static int call(lua_State* L, functor_function& self) noexcept(noexcept(call_detail::call_wrapped<void, true, false>(L, self.invocation))) {
+ int nr = call_detail::call_wrapped<void, true, false>(L, self.invocation);
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ int operator()(lua_State* L) noexcept(noexcept(call_detail::call_wrapped<void, true, false>(L, invocation))) {
+ if constexpr (no_trampoline) {
+ return call(L, *this);
+ }
+ else {
+ return detail::trampoline(L, &call, *this);
+ }
+ }
+ };
+
+ template <typename T, typename Function, bool is_yielding, bool no_trampoline>
+ struct member_function {
+ typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
+ typedef meta::function_return_t<function_type> return_type;
+ typedef meta::function_args_t<function_type> args_lists;
+ using traits_type = meta::bind_traits<function_type>;
+ function_type invocation;
+ T member;
+
+ template <typename... Args>
+ member_function(function_type f, Args&&... args) noexcept(
+ std::is_nothrow_constructible_v<function_type, function_type>&& std::is_nothrow_constructible_v<T, Args...>)
+ : invocation(std::move(f)), member(std::forward<Args>(args)...) {
+ }
+
+ static int call(lua_State* L, member_function& self)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ int nr = call_detail::call_wrapped<T, true, false, -1>(L, self.invocation, detail::unwrap(detail::deref(self.member)));
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ int operator()(lua_State* L)
+#if SOL_IS_ON(SOL_COMPILER_VCXX)
+ // MSVC is broken, what a surprise...
+#else
+ noexcept(traits_type::is_noexcept)
+#endif
+ {
+ if constexpr (no_trampoline) {
+ return call(L, *this);
+ }
+ else {
+ return detail::trampoline(L, &call, *this);
+ }
+ }
+ };
+
+ template <typename T, typename Function, bool is_yielding, bool no_trampoline>
+ struct member_variable {
+ typedef std::remove_pointer_t<std::decay_t<Function>> function_type;
+ typedef typename meta::bind_traits<function_type>::return_type return_type;
+ typedef typename meta::bind_traits<function_type>::args_list args_lists;
+ function_type var;
+ T member;
+ typedef std::add_lvalue_reference_t<meta::unwrapped_t<std::remove_reference_t<decltype(detail::deref(member))>>> M;
+
+ template <typename... Args>
+ member_variable(function_type v, Args&&... args) noexcept(
+ std::is_nothrow_constructible_v<function_type, function_type>&& std::is_nothrow_constructible_v<T, Args...>)
+ : var(std::move(v)), member(std::forward<Args>(args)...) {
+ }
+
+ static int call(lua_State* L, member_variable& self) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ int nr;
+ {
+ M mem = detail::unwrap(detail::deref(self.member));
+ switch (lua_gettop(L)) {
+ case 0:
+ nr = call_detail::call_wrapped<T, true, false, -1>(L, self.var, mem);
+ break;
+ case 1:
+ nr = call_detail::call_wrapped<T, false, false, -1>(L, self.var, mem);
+ break;
+ default:
+ nr = luaL_error(L, "sol: incorrect number of arguments to member variable function");
+ break;
+ }
+ }
+ if (is_yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ int operator()(lua_State* L) noexcept(std::is_nothrow_copy_assignable_v<T>) {
+ if constexpr (no_trampoline) {
+ return call(L, *this);
+ }
+ else {
+ return detail::trampoline(L, &call, *this);
+ }
+ }
+ };
+}} // namespace sol::function_detail
+
+// end of sol/function_types_stateful.hpp
+
+// beginning of sol/function_types_overloaded.hpp
+
+namespace sol { namespace function_detail {
+ template <int start_skew, typename... Functions>
+ struct overloaded_function {
+ typedef std::tuple<Functions...> overload_list;
+ typedef std::make_index_sequence<sizeof...(Functions)> indices;
+ overload_list overloads;
+
+ overloaded_function(overload_list set) : overloads(std::move(set)) {
+ }
+
+ overloaded_function(Functions... fxs) : overloads(fxs...) {
+ }
+
+ template <typename Fx, std::size_t I, typename... R, typename... Args>
+ static int call(types<Fx>, meta::index_value<I>, types<R...>, types<Args...>, lua_State* L, int, int, overload_list& ol) {
+ auto& func = std::get<I>(ol);
+ int nr = call_detail::call_wrapped<void, true, false, start_skew>(L, func);
+ return nr;
+ }
+
+ struct on_success {
+ template <typename... Args>
+ int operator()(Args&&... args) const {
+ return call(std::forward<Args>(args)...);
+ }
+ };
+
+ int operator()(lua_State* L) {
+ on_success call_obj {};
+ return call_detail::overload_match<Functions...>(call_obj, L, 1 + start_skew, overloads);
+ }
+ };
+}} // namespace sol::function_detail
+
+// end of sol/function_types_overloaded.hpp
+
+// beginning of sol/resolve.hpp
+
+namespace sol {
+
+#ifndef __clang__
+ // constexpr is fine for not-clang
+
+ namespace detail {
+ template <typename R, typename... Args, typename F, typename = std::invoke_result_t<meta::unqualified_t<F>, Args...>>
+ inline constexpr auto resolve_i(types<R(Args...)>, F&&) -> R (meta::unqualified_t<F>::*)(Args...) {
+ using Sig = R(Args...);
+ typedef meta::unqualified_t<F> Fu;
+ return static_cast<Sig Fu::*>(&Fu::operator());
+ }
+
+ template <typename F, typename U = meta::unqualified_t<F>>
+ inline constexpr auto resolve_f(std::true_type, F&& f)
+ -> decltype(resolve_i(types<meta::function_signature_t<decltype(&U::operator())>>(), std::forward<F>(f))) {
+ return resolve_i(types<meta::function_signature_t<decltype(&U::operator())>>(), std::forward<F>(f));
+ }
+
+ template <typename F>
+ inline constexpr void resolve_f(std::false_type, F&&) {
+ static_assert(meta::call_operator_deducible_v<F>, "Cannot use no-template-parameter call with an overloaded functor: specify the signature");
+ }
+
+ template <typename F, typename U = meta::unqualified_t<F>>
+ inline constexpr auto resolve_i(types<>, F&& f) -> decltype(resolve_f(meta::call_operator_deducible<U>(), std::forward<F>(f))) {
+ return resolve_f(meta::call_operator_deducible<U> {}, std::forward<F>(f));
+ }
+
+ template <typename... Args, typename F, typename R = std::invoke_result_t<F&, Args...>>
+ inline constexpr auto resolve_i(types<Args...>, F&& f) -> decltype(resolve_i(types<R(Args...)>(), std::forward<F>(f))) {
+ return resolve_i(types<R(Args...)>(), std::forward<F>(f));
+ }
+
+ template <typename Sig, typename C>
+ inline constexpr Sig C::*resolve_v(std::false_type, Sig C::*mem_func_ptr) {
+ return mem_func_ptr;
+ }
+
+ template <typename Sig, typename C>
+ inline constexpr Sig C::*resolve_v(std::true_type, Sig C::*mem_variable_ptr) {
+ return mem_variable_ptr;
+ }
+ } // namespace detail
+
+ template <typename... Args, typename R>
+ inline constexpr auto resolve(R fun_ptr(Args...)) -> R (*)(Args...) {
+ return fun_ptr;
+ }
+
+ template <typename Sig>
+ inline constexpr Sig* resolve(Sig* fun_ptr) {
+ return fun_ptr;
+ }
+
+ template <typename... Args, typename R, typename C>
+ inline constexpr auto resolve(R (C::*mem_ptr)(Args...)) -> R (C::*)(Args...) {
+ return mem_ptr;
+ }
+
+ template <typename Sig, typename C>
+ inline constexpr Sig C::*resolve(Sig C::*mem_ptr) {
+ return detail::resolve_v(std::is_member_object_pointer<Sig C::*>(), mem_ptr);
+ }
+
+ template <typename... Sig, typename F, meta::disable<std::is_function<meta::unqualified_t<F>>> = meta::enabler>
+ inline constexpr auto resolve(F&& f) -> decltype(detail::resolve_i(types<Sig...>(), std::forward<F>(f))) {
+ return detail::resolve_i(types<Sig...>(), std::forward<F>(f));
+ }
+#else
+
+ // Clang has distinct problems with constexpr arguments,
+ // so don't use the constexpr versions inside of clang.
+
+ namespace detail {
+ template <typename R, typename... Args, typename F, typename = std::invoke_result_t<meta::unqualified_t<F>, Args...>>
+ inline auto resolve_i(types<R(Args...)>, F&&) -> R (meta::unqualified_t<F>::*)(Args...) {
+ using Sig = R(Args...);
+ typedef meta::unqualified_t<F> Fu;
+ return static_cast<Sig Fu::*>(&Fu::operator());
+ }
+
+ template <typename F, typename U = meta::unqualified_t<F>>
+ inline auto resolve_f(std::true_type, F&& f)
+ -> decltype(resolve_i(types<meta::function_signature_t<decltype(&U::operator())>>(), std::forward<F>(f))) {
+ return resolve_i(types<meta::function_signature_t<decltype(&U::operator())>>(), std::forward<F>(f));
+ }
+
+ template <typename F>
+ inline void resolve_f(std::false_type, F&&) {
+ static_assert(meta::call_operator_deducible_v<F>, "Cannot use no-template-parameter call with an overloaded functor: specify the signature");
+ }
+
+ template <typename F, typename U = meta::unqualified_t<F>>
+ inline auto resolve_i(types<>, F&& f) -> decltype(resolve_f(meta::call_operator_deducible<U>(), std::forward<F>(f))) {
+ return resolve_f(meta::call_operator_deducible<U> {}, std::forward<F>(f));
+ }
+
+ template <typename... Args, typename F, typename R = std::invoke_result_t<F&, Args...>>
+ inline auto resolve_i(types<Args...>, F&& f) -> decltype(resolve_i(types<R(Args...)>(), std::forward<F>(f))) {
+ return resolve_i(types<R(Args...)>(), std::forward<F>(f));
+ }
+
+ template <typename Sig, typename C>
+ inline Sig C::*resolve_v(std::false_type, Sig C::*mem_func_ptr) {
+ return mem_func_ptr;
+ }
+
+ template <typename Sig, typename C>
+ inline Sig C::*resolve_v(std::true_type, Sig C::*mem_variable_ptr) {
+ return mem_variable_ptr;
+ }
+ } // namespace detail
+
+ template <typename... Args, typename R>
+ inline auto resolve(R fun_ptr(Args...)) -> R (*)(Args...) {
+ return fun_ptr;
+ }
+
+ template <typename Sig>
+ inline Sig* resolve(Sig* fun_ptr) {
+ return fun_ptr;
+ }
+
+ template <typename... Args, typename R, typename C>
+ inline auto resolve(R (C::*mem_ptr)(Args...)) -> R (C::*)(Args...) {
+ return mem_ptr;
+ }
+
+ template <typename Sig, typename C>
+ inline Sig C::*resolve(Sig C::*mem_ptr) {
+ return detail::resolve_v(std::is_member_object_pointer<Sig C::*>(), mem_ptr);
+ }
+
+ template <typename... Sig, typename F>
+ inline auto resolve(F&& f) -> decltype(detail::resolve_i(types<Sig...>(), std::forward<F>(f))) {
+ return detail::resolve_i(types<Sig...>(), std::forward<F>(f));
+ }
+
+#endif
+
+} // namespace sol
+
+// end of sol/resolve.hpp
+
+namespace sol {
+ namespace function_detail {
+ template <typename T>
+ struct class_indicator {
+ using type = T;
+ };
+
+ struct call_indicator { };
+
+ template <bool yielding>
+ int lua_c_wrapper(lua_State* L) {
+ lua_CFunction cf = lua_tocfunction(L, lua_upvalueindex(2));
+ int nr = cf(L);
+ if constexpr (yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ template <bool yielding>
+ int lua_c_noexcept_wrapper(lua_State* L) noexcept {
+ detail::lua_CFunction_noexcept cf = reinterpret_cast<detail::lua_CFunction_noexcept>(lua_tocfunction(L, lua_upvalueindex(2)));
+ int nr = cf(L);
+ if constexpr (yielding) {
+ return lua_yield(L, nr);
+ }
+ else {
+ return nr;
+ }
+ }
+
+ struct c_function_invocation { };
+
+ template <bool is_yielding, bool no_trampoline, typename Fx, typename... Args>
+ void select(lua_State* L, Fx&& fx, Args&&... args);
+
+ template <bool is_yielding, bool no_trampoline, typename Fx, typename... Args>
+ void select_set_fx(lua_State* L, Args&&... args) {
+ lua_CFunction freefunc = no_trampoline ? function_detail::call<meta::unqualified_t<Fx>, 2, is_yielding>
+ : detail::static_trampoline<function_detail::call<meta::unqualified_t<Fx>, 2, is_yielding>>;
+
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<Fx>>(L, std::forward<Args>(args)...);
+ stack::push(L, c_closure(freefunc, upvalues));
+ }
+
+ template <bool is_yielding, bool no_trampoline, typename R, typename... A, typename Fx, typename... Args>
+ void select_convertible(types<R(A...)>, lua_State* L, Fx&& fx, Args&&... args) {
+ using dFx = std::decay_t<meta::unwrap_unqualified_t<Fx>>;
+ using fx_ptr_t = R (*)(A...);
+ constexpr bool is_convertible = std::is_convertible_v<dFx, fx_ptr_t>;
+ if constexpr (is_convertible) {
+ fx_ptr_t fxptr = detail::unwrap(std::forward<Fx>(fx));
+ select<is_yielding, no_trampoline>(L, std::move(fxptr), std::forward<Args>(args)...);
+ }
+ else {
+ using F = function_detail::functor_function<dFx, false, true>;
+ select_set_fx<is_yielding, no_trampoline, F>(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ }
+
+ template <bool is_yielding, bool no_trampoline, typename Fx, typename... Args>
+ void select_convertible(types<>, lua_State* L, Fx&& fx, Args&&... args) {
+ typedef meta::function_signature_t<meta::unwrap_unqualified_t<Fx>> Sig;
+ select_convertible<is_yielding, no_trampoline>(types<Sig>(), L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+
+ template <bool is_yielding, bool no_trampoline, typename Fx, typename... Args>
+ void select_member_variable(lua_State* L, Fx&& fx, Args&&... args) {
+ using uFx = meta::unqualified_t<Fx>;
+ if constexpr (sizeof...(Args) < 1) {
+ using C = typename meta::bind_traits<uFx>::object_type;
+ lua_CFunction freefunc = &function_detail::upvalue_this_member_variable<C, Fx>::template call<is_yielding, no_trampoline>;
+
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::stack_detail::push_as_upvalues(L, fx);
+ stack::push(L, c_closure(freefunc, upvalues));
+ }
+ else if constexpr (sizeof...(Args) < 2) {
+ using Tu = typename meta::meta_detail::unqualified_non_alias<Args...>::type;
+ constexpr bool is_reference = meta::is_specialization_of_v<Tu, std::reference_wrapper> || std::is_pointer_v<Tu>;
+ if constexpr (meta::is_specialization_of_v<Tu, function_detail::class_indicator>) {
+ lua_CFunction freefunc
+ = &function_detail::upvalue_this_member_variable<typename Tu::type, Fx>::template call<is_yielding, no_trampoline>;
+
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::stack_detail::push_as_upvalues(L, fx);
+ stack::push(L, c_closure(freefunc, upvalues));
+ }
+ else if constexpr (is_reference) {
+ typedef std::decay_t<Fx> dFx;
+ dFx memfxptr(std::forward<Fx>(fx));
+ auto userptr = detail::ptr(std::forward<Args>(args)...);
+ lua_CFunction freefunc = &function_detail::upvalue_member_variable<std::decay_t<decltype(*userptr)>,
+ meta::unqualified_t<Fx>>::template call<is_yielding, no_trampoline>;
+
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::stack_detail::push_as_upvalues(L, memfxptr);
+ upvalues += stack::push(L, static_cast<void const*>(userptr));
+ stack::push(L, c_closure(freefunc, upvalues));
+ }
+ else {
+ using clean_fx = std::remove_pointer_t<std::decay_t<Fx>>;
+ using F = function_detail::member_variable<Tu, clean_fx, is_yielding, no_trampoline>;
+ select_set_fx<is_yielding, no_trampoline, F>(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ }
+ else {
+ using C = typename meta::bind_traits<uFx>::object_type;
+ using clean_fx = std::remove_pointer_t<std::decay_t<Fx>>;
+ using F = function_detail::member_variable<C, clean_fx, is_yielding, no_trampoline>;
+ select_set_fx<is_yielding, no_trampoline, F>(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ }
+
+ template <bool is_yielding, bool no_trampoline, typename Fx, typename T, typename... Args>
+ void select_member_function_with(lua_State* L, Fx&& fx, T&& obj, Args&&... args) {
+ using dFx = std::decay_t<Fx>;
+ using Tu = meta::unqualified_t<T>;
+ if constexpr (meta::is_specialization_of_v<Tu, function_detail::class_indicator>) {
+ (void)obj;
+ using C = typename Tu::type;
+ lua_CFunction freefunc = &function_detail::upvalue_this_member_function<C, dFx>::template call<is_yielding, no_trampoline>;
+
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<dFx>>(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ stack::push(L, c_closure(freefunc, upvalues));
+ }
+ else {
+ constexpr bool is_reference = meta::is_specialization_of_v<Tu, std::reference_wrapper> || std::is_pointer_v<Tu>;
+ if constexpr (is_reference) {
+ auto userptr = detail::ptr(std::forward<T>(obj));
+ lua_CFunction freefunc
+ = &function_detail::upvalue_member_function<std::decay_t<decltype(*userptr)>, dFx>::template call<is_yielding, no_trampoline>;
+
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<dFx>>(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ upvalues += stack::push(L, lightuserdata_value(static_cast<void*>(userptr)));
+ stack::push(L, c_closure(freefunc, upvalues));
+ }
+ else {
+ using F = function_detail::member_function<Tu, dFx, is_yielding, no_trampoline>;
+ select_set_fx<is_yielding, no_trampoline, F>(L, std::forward<Fx>(fx), std::forward<T>(obj), std::forward<Args>(args)...);
+ }
+ }
+ }
+
+ template <bool is_yielding, bool no_trampoline, typename Fx, typename... Args>
+ void select_member_function(lua_State* L, Fx&& fx, Args&&... args) {
+ using dFx = std::decay_t<Fx>;
+ if constexpr (sizeof...(Args) < 1) {
+ using C = typename meta::bind_traits<meta::unqualified_t<Fx>>::object_type;
+ lua_CFunction freefunc = &function_detail::upvalue_this_member_function<C, dFx>::template call<is_yielding, no_trampoline>;
+
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<dFx>>(L, std::forward<Fx>(fx));
+ stack::push(L, c_closure(freefunc, upvalues));
+ }
+ else {
+ select_member_function_with<is_yielding, no_trampoline>(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ }
+
+ template <bool is_yielding, bool no_trampoline, typename Fx, typename... Args>
+ void select(lua_State* L, Fx&& fx, Args&&... args) {
+ using uFx = meta::unqualified_t<Fx>;
+ if constexpr (is_lua_reference_v<uFx>) {
+ // TODO: hoist into lambda in this case for yielding???
+ stack::push(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ else if constexpr (is_lua_c_function_v<uFx>) {
+ if constexpr (no_trampoline) {
+ if (is_yielding) {
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push(L, std::forward<Fx>(fx));
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ if constexpr (std::is_nothrow_invocable_r_v<int, uFx, lua_State*>) {
+ detail::lua_CFunction_noexcept cf = &lua_c_noexcept_wrapper<true>;
+ lua_pushcclosure(L, reinterpret_cast<lua_CFunction>(cf), upvalues);
+ }
+ else
+#endif
+ {
+ lua_CFunction cf = &function_detail::lua_c_wrapper<true>;
+ lua_pushcclosure(L, cf, upvalues);
+ }
+ }
+ else {
+ lua_pushcclosure(L, std::forward<Fx>(fx), 0);
+ }
+ }
+ else {
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push(L, std::forward<Fx>(fx));
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ if constexpr (std::is_nothrow_invocable_r_v<int, uFx, lua_State*>) {
+ detail::lua_CFunction_noexcept cf = &lua_c_noexcept_wrapper<is_yielding>;
+ lua_pushcclosure(L, reinterpret_cast<lua_CFunction>(cf), upvalues);
+ }
+ else {
+ lua_CFunction cf = &function_detail::lua_c_wrapper<is_yielding>;
+ lua_pushcclosure(L, cf, upvalues);
+ }
+#else
+ lua_CFunction cf = &function_detail::lua_c_wrapper<is_yielding>;
+ lua_pushcclosure(L, cf, upvalues);
+#endif
+ }
+ }
+ else if constexpr (std::is_function_v<std::remove_pointer_t<uFx>>) {
+ std::decay_t<Fx> target(std::forward<Fx>(fx), std::forward<Args>(args)...);
+ lua_CFunction freefunc = &function_detail::upvalue_free_function<Fx>::template call<is_yielding, no_trampoline>;
+
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::stack_detail::push_as_upvalues(L, target);
+ stack::push(L, c_closure(freefunc, upvalues));
+ }
+ else if constexpr (std::is_member_function_pointer_v<uFx>) {
+ select_member_function<is_yielding, no_trampoline>(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ else if constexpr (meta::is_member_object_v<uFx>) {
+ select_member_variable<is_yielding, no_trampoline>(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ else {
+ select_convertible<is_yielding, no_trampoline>(types<>(), L, std::forward<Fx>(fx), std::forward<Args>(args)...);
+ }
+ }
+ } // namespace function_detail
+
+ namespace stack {
+ template <typename... Sigs>
+ struct unqualified_pusher<function_sig<Sigs...>> {
+ template <bool is_yielding, typename Arg0, typename... Args>
+ static int push_yielding(lua_State* L, Arg0&& arg0, Args&&... args) {
+ if constexpr (meta::is_specialization_of_v<meta::unqualified_t<Arg0>, std::function>) {
+ if constexpr (is_yielding) {
+ return stack::push<meta::unqualified_t<Arg0>>(L, detail::yield_tag, std::forward<Arg0>(arg0), std::forward<Args>(args)...);
+ }
+ else {
+ return stack::push(L, std::forward<Arg0>(arg0), std::forward<Args>(args)...);
+ }
+ }
+ else {
+ function_detail::select<is_yielding, false>(L, std::forward<Arg0>(arg0), std::forward<Args>(args)...);
+ return 1;
+ }
+ }
+
+ template <typename Arg0, typename... Args>
+ static int push(lua_State* L, Arg0&& arg0, Args&&... args) {
+ if constexpr (std::is_same_v<meta::unqualified_t<Arg0>, detail::yield_tag_t>) {
+ push_yielding<true>(L, std::forward<Args>(args)...);
+ }
+ else if constexpr (meta::is_specialization_of_v<meta::unqualified_t<Arg0>, yielding_t>) {
+ push_yielding<true>(L, std::forward<Arg0>(arg0).func, std::forward<Args>(args)...);
+ }
+ else {
+ push_yielding<false>(L, std::forward<Arg0>(arg0), std::forward<Args>(args)...);
+ }
+ return 1;
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<yielding_t<T>> {
+ template <typename... Args>
+ static int push(lua_State* L, const yielding_t<T>& f, Args&&... args) {
+ if constexpr (meta::is_specialization_of_v<meta::unqualified_t<T>, std::function>) {
+ return stack::push<T>(L, detail::yield_tag, f.func, std::forward<Args>(args)...);
+ }
+ else {
+ function_detail::select<true, false>(L, f.func, std::forward<Args>(args)...);
+ return 1;
+ }
+ }
+
+ template <typename... Args>
+ static int push(lua_State* L, yielding_t<T>&& f, Args&&... args) {
+ if constexpr (meta::is_specialization_of_v<meta::unqualified_t<T>, std::function>) {
+ return stack::push<T>(L, detail::yield_tag, std::move(f.func), std::forward<Args>(args)...);
+ }
+ else {
+ function_detail::select<true, false>(L, std::move(f.func), std::forward<Args>(args)...);
+ return 1;
+ }
+ }
+ };
+
+ template <typename T, typename... Args>
+ struct unqualified_pusher<function_arguments<T, Args...>> {
+ template <std::size_t... I, typename FP>
+ static int push_func(std::index_sequence<I...>, lua_State* L, FP&& fp) {
+ return stack::push<T>(L, std::get<I>(std::forward<FP>(fp).arguments)...);
+ }
+
+ static int push(lua_State* L, const function_arguments<T, Args...>& fp) {
+ return push_func(std::make_index_sequence<sizeof...(Args)>(), L, fp);
+ }
+
+ static int push(lua_State* L, function_arguments<T, Args...>&& fp) {
+ return push_func(std::make_index_sequence<sizeof...(Args)>(), L, std::move(fp));
+ }
+ };
+
+ template <typename Signature>
+ struct unqualified_pusher<std::function<Signature>> {
+ using TargetFunctor = function_detail::functor_function<std::function<Signature>, false, true>;
+
+ static int push(lua_State* L, detail::yield_tag_t, const std::function<Signature>& fx) {
+ if (fx) {
+ function_detail::select_set_fx<true, false, TargetFunctor>(L, fx);
+ return 1;
+ }
+ return stack::push(L, lua_nil);
+ }
+
+ static int push(lua_State* L, detail::yield_tag_t, std::function<Signature>&& fx) {
+ if (fx) {
+ function_detail::select_set_fx<true, false, TargetFunctor>(L, std::move(fx));
+ return 1;
+ }
+ return stack::push(L, lua_nil);
+ }
+
+ static int push(lua_State* L, const std::function<Signature>& fx) {
+ if (fx) {
+ function_detail::select_set_fx<false, false, TargetFunctor>(L, fx);
+ return 1;
+ }
+ return stack::push(L, lua_nil);
+ }
+
+ static int push(lua_State* L, std::function<Signature>&& fx) {
+ if (fx) {
+ function_detail::select_set_fx<false, false, TargetFunctor>(L, std::move(fx));
+ return 1;
+ }
+ return stack::push(L, lua_nil);
+ }
+ };
+
+ template <typename Signature>
+ struct unqualified_pusher<Signature, std::enable_if_t<meta::is_member_object_or_function_v<Signature>>> {
+ template <typename... Args>
+ static int push(lua_State* L, Args&&... args) {
+ function_detail::select<false, false>(L, std::forward<Args>(args)...);
+ return 1;
+ }
+ };
+
+ template <typename Signature>
+ struct unqualified_pusher<Signature,
+ std::enable_if_t<meta::all<std::is_function<std::remove_pointer_t<Signature>>, meta::neg<std::is_same<Signature, lua_CFunction>>,
+ meta::neg<std::is_same<Signature, std::remove_pointer_t<lua_CFunction>>>
+#if SOL_IS_ON(SOL_USE_NOEXCEPT_FUNCTION_TYPE)
+ ,
+ meta::neg<std::is_same<Signature, detail::lua_CFunction_noexcept>>,
+ meta::neg<std::is_same<Signature, std::remove_pointer_t<detail::lua_CFunction_noexcept>>>
+#endif // noexcept function types
+ >::value>> {
+ template <typename F>
+ static int push(lua_State* L, F&& f) {
+ function_detail::select<false, true>(L, std::forward<F>(f));
+ return 1;
+ }
+ };
+
+ template <typename... Functions>
+ struct unqualified_pusher<overload_set<Functions...>> {
+ static int push(lua_State* L, overload_set<Functions...>&& set) {
+ using F = function_detail::overloaded_function<0, Functions...>;
+ function_detail::select_set_fx<false, false, F>(L, std::move(set.functions));
+ return 1;
+ }
+
+ static int push(lua_State* L, const overload_set<Functions...>& set) {
+ using F = function_detail::overloaded_function<0, Functions...>;
+ function_detail::select_set_fx<false, false, F>(L, set.functions);
+ return 1;
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<protect_t<T>> {
+ static int push(lua_State* L, protect_t<T>&& pw) {
+ lua_CFunction cf = call_detail::call_user<void, false, false, protect_t<T>, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<protect_t<T>>>(L, std::move(pw.value));
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+
+ static int push(lua_State* L, const protect_t<T>& pw) {
+ lua_CFunction cf = call_detail::call_user<void, false, false, protect_t<T>, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<protect_t<T>>>(L, pw.value);
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+ };
+
+ template <typename F, typename G>
+ struct unqualified_pusher<property_wrapper<F, G>> {
+ static int push(lua_State* L, property_wrapper<F, G>&& pw) {
+ if constexpr (std::is_void_v<F>) {
+ return stack::push(L, std::move(pw.write()));
+ }
+ else if constexpr (std::is_void_v<G>) {
+ return stack::push(L, std::move(pw.read()));
+ }
+ else {
+ return stack::push(L, overload(std::move(pw.read()), std::move(pw.write())));
+ }
+ }
+
+ static int push(lua_State* L, const property_wrapper<F, G>& pw) {
+ if constexpr (std::is_void_v<F>) {
+ return stack::push(L, pw.write);
+ }
+ else if constexpr (std::is_void_v<G>) {
+ return stack::push(L, pw.read);
+ }
+ else {
+ return stack::push(L, overload(pw.read, pw.write));
+ }
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<var_wrapper<T>> {
+ static int push(lua_State* L, var_wrapper<T>&& vw) {
+ return stack::push(L, std::move(vw.value()));
+ }
+ static int push(lua_State* L, const var_wrapper<T>& vw) {
+ return stack::push(L, vw.value());
+ }
+ };
+
+ template <typename... Functions>
+ struct unqualified_pusher<factory_wrapper<Functions...>> {
+ static int push(lua_State* L, const factory_wrapper<Functions...>& fw) {
+ using F = function_detail::overloaded_function<0, Functions...>;
+ function_detail::select_set_fx<false, false, F>(L, fw.functions);
+ return 1;
+ }
+
+ static int push(lua_State* L, factory_wrapper<Functions...>&& fw) {
+ using F = function_detail::overloaded_function<0, Functions...>;
+ function_detail::select_set_fx<false, false, F>(L, std::move(fw.functions));
+ return 1;
+ }
+
+ static int push(lua_State* L, const factory_wrapper<Functions...>& fw, function_detail::call_indicator) {
+ using F = function_detail::overloaded_function<1, Functions...>;
+ function_detail::select_set_fx<false, false, F>(L, fw.functions);
+ return 1;
+ }
+
+ static int push(lua_State* L, factory_wrapper<Functions...>&& fw, function_detail::call_indicator) {
+ using F = function_detail::overloaded_function<1, Functions...>;
+ function_detail::select_set_fx<false, false, F>(L, std::move(fw.functions));
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<no_construction> {
+ static int push(lua_State* L, no_construction) {
+ lua_CFunction cf = &function_detail::no_construction_error;
+ return stack::push(L, cf);
+ }
+
+ static int push(lua_State* L, no_construction c, function_detail::call_indicator) {
+ return push(L, c);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<detail::tagged<T, no_construction>> {
+ static int push(lua_State* L, detail::tagged<T, no_construction>) {
+ lua_CFunction cf = &function_detail::no_construction_error;
+ return stack::push(L, cf);
+ }
+
+ static int push(lua_State* L, no_construction, function_detail::call_indicator) {
+ lua_CFunction cf = &function_detail::no_construction_error;
+ return stack::push(L, cf);
+ }
+ };
+
+ template <typename T, typename... Lists>
+ struct unqualified_pusher<detail::tagged<T, constructor_list<Lists...>>> {
+ static int push(lua_State* L, detail::tagged<T, constructor_list<Lists...>>) {
+ lua_CFunction cf = call_detail::construct<T, detail::default_safe_function_calls, true, Lists...>;
+ return stack::push(L, cf);
+ }
+
+ static int push(lua_State* L, constructor_list<Lists...>) {
+ lua_CFunction cf = call_detail::construct<T, detail::default_safe_function_calls, true, Lists...>;
+ return stack::push(L, cf);
+ }
+ };
+
+ template <typename L0, typename... Lists>
+ struct unqualified_pusher<constructor_list<L0, Lists...>> {
+ typedef constructor_list<L0, Lists...> cl_t;
+ static int push(lua_State* L, cl_t cl) {
+ typedef typename meta::bind_traits<L0>::return_type T;
+ return stack::push<detail::tagged<T, cl_t>>(L, cl);
+ }
+ };
+
+ template <typename T, typename... Fxs>
+ struct unqualified_pusher<detail::tagged<T, constructor_wrapper<Fxs...>>> {
+ static int push(lua_State* L, detail::tagged<T, constructor_wrapper<Fxs...>>&& c) {
+ return push(L, std::move(c.value()));
+ }
+
+ static int push(lua_State* L, const detail::tagged<T, const constructor_wrapper<Fxs...>>& c) {
+ return push(L, c.value());
+ }
+
+ static int push(lua_State* L, constructor_wrapper<Fxs...>&& c) {
+ lua_CFunction cf = call_detail::call_user<T, false, false, constructor_wrapper<Fxs...>, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<constructor_wrapper<Fxs...>>>(L, std::move(c));
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+
+ static int push(lua_State* L, const constructor_wrapper<Fxs...>& c) {
+ lua_CFunction cf = call_detail::call_user<T, false, false, constructor_wrapper<Fxs...>, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<constructor_wrapper<Fxs...>>>(L, c);
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+ };
+
+ template <typename F, typename... Fxs>
+ struct unqualified_pusher<constructor_wrapper<F, Fxs...>> {
+ static int push(lua_State* L, const constructor_wrapper<F, Fxs...>& c) {
+ typedef typename meta::bind_traits<F>::template arg_at<0> arg0;
+ typedef meta::unqualified_t<std::remove_pointer_t<arg0>> T;
+ return stack::push<detail::tagged<T, constructor_wrapper<F, Fxs...>>>(L, c);
+ }
+
+ static int push(lua_State* L, constructor_wrapper<F, Fxs...>&& c) {
+ typedef typename meta::bind_traits<F>::template arg_at<0> arg0;
+ typedef meta::unqualified_t<std::remove_pointer_t<arg0>> T;
+ return stack::push<detail::tagged<T, constructor_wrapper<F, Fxs...>>>(L, std::move(c));
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<detail::tagged<T, destructor_wrapper<void>>> {
+ static int push(lua_State* L, destructor_wrapper<void>) {
+ lua_CFunction cf = detail::usertype_alloc_destroy<T>;
+ return stack::push(L, cf);
+ }
+ };
+
+ template <typename T, typename Fx>
+ struct unqualified_pusher<detail::tagged<T, destructor_wrapper<Fx>>> {
+ static int push(lua_State* L, destructor_wrapper<Fx>&& c) {
+ lua_CFunction cf = call_detail::call_user<T, false, false, destructor_wrapper<Fx>, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<destructor_wrapper<Fx>>>(L, std::move(c));
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+
+ static int push(lua_State* L, const destructor_wrapper<Fx>& c) {
+ lua_CFunction cf = call_detail::call_user<T, false, false, destructor_wrapper<Fx>, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<destructor_wrapper<Fx>>>(L, c);
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+ };
+
+ template <typename Fx>
+ struct unqualified_pusher<destructor_wrapper<Fx>> {
+ static int push(lua_State* L, destructor_wrapper<Fx>&& c) {
+ lua_CFunction cf = call_detail::call_user<void, false, false, destructor_wrapper<Fx>, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<destructor_wrapper<Fx>>>(L, std::move(c));
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+
+ static int push(lua_State* L, const destructor_wrapper<Fx>& c) {
+ lua_CFunction cf = call_detail::call_user<void, false, false, destructor_wrapper<Fx>, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<destructor_wrapper<Fx>>>(L, c);
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+ };
+
+ template <typename F, typename... Policies>
+ struct unqualified_pusher<policy_wrapper<F, Policies...>> {
+ using P = policy_wrapper<F, Policies...>;
+
+ static int push(lua_State* L, const P& p) {
+ lua_CFunction cf = call_detail::call_user<void, false, false, P, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<P>>(L, p);
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+
+ static int push(lua_State* L, P&& p) {
+ lua_CFunction cf = call_detail::call_user<void, false, false, P, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<P>>(L, std::move(p));
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+ };
+
+ template <typename T, typename F, typename... Policies>
+ struct unqualified_pusher<detail::tagged<T, policy_wrapper<F, Policies...>>> {
+ using P = policy_wrapper<F, Policies...>;
+ using Tagged = detail::tagged<T, P>;
+
+ static int push(lua_State* L, const Tagged& p) {
+ lua_CFunction cf = call_detail::call_user<T, false, false, P, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<P>>(L, p.value());
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+
+ static int push(lua_State* L, Tagged&& p) {
+ lua_CFunction cf = call_detail::call_user<T, false, false, P, 2>;
+ int upvalues = 0;
+ upvalues += stack::push(L, nullptr);
+ upvalues += stack::push<user<P>>(L, std::move(p.value()));
+ return stack::push(L, c_closure(cf, upvalues));
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<push_invoke_t<T>> {
+ static int push(lua_State* L, push_invoke_t<T>&& pi) {
+ if constexpr (std::is_invocable_v<std::add_rvalue_reference_t<T>, lua_State*>) {
+ return stack::push(L, std::move(pi.value())(L));
+ }
+ else {
+ return stack::push(L, std::move(pi.value())());
+ }
+ }
+
+ static int push(lua_State* L, const push_invoke_t<T>& pi) {
+ if constexpr (std::is_invocable_v<const T, lua_State*>) {
+ return stack::push(L, pi.value()(L));
+ }
+ else {
+ return stack::push(L, pi.value()());
+ }
+ }
+ };
+
+ namespace stack_detail {
+ template <typename Function, typename Handler>
+ bool check_function_pointer(lua_State* L, int index, Handler&& handler, record& tracking) noexcept {
+#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ tracking.use(1);
+ bool success = lua_iscfunction(L, index) == 1;
+ if (success) {
+ // there must be at LEAST 2 upvalues; otherwise, we didn't serialize it.
+ const char* upvalue_name = lua_getupvalue(L, index, 2);
+ lua_pop(L, 1);
+ success = upvalue_name != nullptr;
+ }
+ if (!success) {
+ // expected type, actual type
+ handler(
+ L, index, type::function, type_of(L, index), "type must be a Lua C Function gotten from a function pointer serialized by sol2");
+ }
+ return success;
+#else
+ (void)L;
+ (void)index;
+ (void)handler;
+ (void)tracking;
+ return false;
+#endif
+ }
+
+ template <typename Function>
+ Function* get_function_pointer(lua_State* L, int index, record& tracking) noexcept {
+#if SOL_IS_ON(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ tracking.use(1);
+ auto udata = stack::stack_detail::get_as_upvalues_using_function<Function*>(L, index);
+ Function* fx = udata.first;
+ return fx;
+#else
+ (void)L;
+ (void)index;
+ (void)tracking;
+ static_assert(meta::meta_detail::always_true<Function>::value,
+#if SOL_IS_DEFAULT_OFF(SOL_GET_FUNCTION_POINTER_UNSAFE)
+ "You are attempting to retrieve a function pointer type. "
+ "This is inherently unsafe in sol2. In order to do this, you must turn on the "
+ "SOL_GET_FUNCTION_POINTER_UNSAFE configuration macro, as detailed in the documentation. "
+ "Please be careful!"
+#else
+ "You are attempting to retrieve a function pointer type. "
+ "You explicitly turned off the ability to do this by defining "
+ "SOL_GET_FUNCTION_POINTER_UNSAFE or similar to be off. "
+ "Please reconsider this!"
+#endif
+ );
+ return nullptr;
+#endif
+ }
+ } // namespace stack_detail
+ } // namespace stack
+} // namespace sol
+
+// end of sol/function_types.hpp
+
+// beginning of sol/dump_handler.hpp
+
+#include <cstdint>
+#include <exception>
+
+namespace sol {
+
+ class dump_error : public error {
+ private:
+ int m_ec;
+
+ public:
+ dump_error(int error_code_) : error("dump returned non-zero error of " + std::to_string(error_code_)), m_ec(error_code_) {
+ }
+
+ int error_code() const {
+ return m_ec;
+ }
+ };
+
+ inline int dump_pass_on_error(lua_State* L_, int result_code, lua_Writer writer_function, void* userdata_pointer_, bool strip) {
+ (void)L_;
+ (void)writer_function;
+ (void)userdata_pointer_;
+ (void)strip;
+ return result_code;
+ }
+
+ inline int dump_panic_on_error(lua_State* L_, int result_code, lua_Writer writer_function, void* userdata_pointer_, bool strip) {
+ (void)L_;
+ (void)writer_function;
+ (void)userdata_pointer_;
+ (void)strip;
+ return luaL_error(L_, "a non-zero error code (%d) was returned by the lua_Writer for the dump function", result_code);
+ }
+
+ inline int dump_throw_on_error(lua_State* L_, int result_code, lua_Writer writer_function, void* userdata_pointer_, bool strip) {
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ return dump_panic_on_error(L_, result_code, writer_function, userdata_pointer_, strip);
+#else
+ (void)L_;
+ (void)writer_function;
+ (void)userdata_pointer_;
+ (void)strip;
+ throw dump_error(result_code);
+#endif // no exceptions stuff
+ }
+
+} // namespace sol
+
+// end of sol/dump_handler.hpp
+
+#include <cstdint>
+
+namespace sol {
+ template <typename ref_t, bool aligned = false>
+ class basic_function : public basic_object<ref_t> {
+ private:
+ using base_t = basic_object<ref_t>;
+
+ void luacall(std::ptrdiff_t argcount, std::ptrdiff_t resultcount) const {
+ lua_call(lua_state(), static_cast<int>(argcount), static_cast<int>(resultcount));
+ }
+
+ template <std::size_t... I, typename... Ret>
+ auto invoke(types<Ret...>, std::index_sequence<I...>, std::ptrdiff_t n) const {
+ luacall(n, lua_size<std::tuple<Ret...>>::value);
+ return stack::pop<std::tuple<Ret...>>(lua_state());
+ }
+
+ template <std::size_t I, typename Ret, meta::enable<meta::neg<std::is_void<Ret>>> = meta::enabler>
+ Ret invoke(types<Ret>, std::index_sequence<I>, std::ptrdiff_t n) const {
+ luacall(n, lua_size<Ret>::value);
+ return stack::pop<Ret>(lua_state());
+ }
+
+ template <std::size_t I>
+ void invoke(types<void>, std::index_sequence<I>, std::ptrdiff_t n) const {
+ luacall(n, 0);
+ }
+
+ unsafe_function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n) const {
+ int stacksize = lua_gettop(lua_state());
+ int firstreturn = (std::max)(1, stacksize - static_cast<int>(n));
+ luacall(n, LUA_MULTRET);
+ int poststacksize = lua_gettop(lua_state());
+ int returncount = poststacksize - (firstreturn - 1);
+ return unsafe_function_result(lua_state(), firstreturn, returncount);
+ }
+
+ public:
+ using base_t::lua_state;
+
+ basic_function() = default;
+ template <typename T,
+ meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_function>>, meta::neg<std::is_same<base_t, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_function(T&& r) noexcept : base_t(std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_function<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_function>(lua_state(), -1, handler);
+ }
+#endif // Safety
+ }
+ basic_function(const basic_function&) = default;
+ basic_function& operator=(const basic_function&) = default;
+ basic_function(basic_function&&) = default;
+ basic_function& operator=(basic_function&&) = default;
+ basic_function(const stack_reference& r) : basic_function(r.lua_state(), r.stack_index()) {
+ }
+ basic_function(stack_reference&& r) : basic_function(r.lua_state(), r.stack_index()) {
+ }
+ basic_function(lua_nil_t n) : base_t(n) {
+ }
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_function(lua_State* L, T&& r) : base_t(L, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_function>(lua_state(), -1, handler);
+#endif // Safety
+ }
+ basic_function(lua_State* L, int index = -1) : base_t(L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_function>(L, index, handler);
+#endif // Safety
+ }
+ basic_function(lua_State* L, ref_index index) : base_t(L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_function>(lua_state(), -1, handler);
+#endif // Safety
+ }
+
+ template <typename Fx>
+ int dump(lua_Writer writer, void* userdata, bool strip, Fx&& on_error) const {
+ this->push();
+ auto ppn = stack::push_popper_n<false>(this->lua_state(), 1);
+ int r = lua_dump(this->lua_state(), writer, userdata, strip ? 1 : 0);
+ if (r != 0) {
+ return on_error(this->lua_state(), r, writer, userdata, strip);
+ }
+ return r;
+ }
+
+ int dump(lua_Writer writer, void* userdata, bool strip = false) const {
+ return dump(writer, userdata, strip, &dump_throw_on_error);
+ }
+
+ template <typename Container = bytecode>
+ Container dump() const {
+ Container bc;
+ (void)dump(static_cast<lua_Writer>(&basic_insert_dump_writer<Container>), static_cast<void*>(&bc), false, &dump_panic_on_error);
+ return bc;
+ }
+
+ template <typename Container = bytecode, typename Fx>
+ Container dump(Fx&& on_error) const {
+ Container bc;
+ (void)dump(static_cast<lua_Writer>(&basic_insert_dump_writer<Container>), static_cast<void*>(&bc), false, std::forward<Fx>(on_error));
+ return bc;
+ }
+
+ template <typename... Args>
+ unsafe_function_result operator()(Args&&... args) const {
+ return call<>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) operator()(types<Ret...>, Args&&... args) const {
+ return call<Ret...>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) call(Args&&... args) const {
+ if (!aligned) {
+ base_t::push();
+ }
+ int pushcount = stack::multi_push_reference(lua_state(), std::forward<Args>(args)...);
+ return invoke(types<Ret...>(), std::make_index_sequence<sizeof...(Ret)>(), static_cast<std::ptrdiff_t>(pushcount));
+ }
+ };
+} // namespace sol
+
+// end of sol/unsafe_function.hpp
+
+// beginning of sol/protected_function.hpp
+
+// beginning of sol/protected_handler.hpp
+
+#include <cstdint>
+
+namespace sol { namespace detail {
+ inline const char (&default_handler_name())[9] {
+ static const char name[9] = "sol.\xF0\x9F\x94\xA9";
+ return name;
+ }
+
+ template <bool ShouldPush, typename Target = reference>
+ struct protected_handler {
+ lua_State* m_L;
+ const Target& target;
+ int stack_index;
+
+ protected_handler(std::false_type, lua_State* L_, const Target& target_) : m_L(L_), target(target_), stack_index(0) {
+ if (ShouldPush) {
+ stack_index = lua_gettop(L_) + 1;
+ target.push(L_);
+ }
+ }
+
+ protected_handler(std::true_type, lua_State* L_, const Target& target_) : m_L(L_), target(target_), stack_index(0) {
+ if (ShouldPush) {
+ stack_index = target.stack_index();
+ }
+ }
+
+ protected_handler(lua_State* L_, const Target& target_) : protected_handler(meta::boolean<is_stack_based_v<Target>>(), L_, target_) {
+ }
+
+ bool valid() const noexcept {
+ return ShouldPush;
+ }
+
+ ~protected_handler() {
+ if constexpr (!is_stack_based_v<Target>) {
+ if (stack_index != 0) {
+ lua_remove(m_L, stack_index);
+ }
+ }
+ }
+ };
+
+ template <typename Base, typename T>
+ inline basic_function<Base> force_cast(T& p) {
+ return p;
+ }
+
+ template <typename Reference, bool IsMainReference = false>
+ inline Reference get_default_handler(lua_State* L_) {
+ if (is_stack_based_v<Reference> || L_ == nullptr)
+ return Reference(L_, lua_nil);
+ L_ = IsMainReference ? main_thread(L_, L_) : L_;
+ lua_getglobal(L_, default_handler_name());
+ auto pp = stack::pop_n(L_, 1);
+ return Reference(L_, -1);
+ }
+
+ template <typename T>
+ inline void set_default_handler(lua_State* L, const T& ref) {
+ if (L == nullptr) {
+ return;
+ }
+ if (!ref.valid()) {
+#if SOL_IS_ON(SOL_SAFE_STACK_CHECK)
+ luaL_checkstack(L, 1, detail::not_enough_stack_space_generic);
+#endif // make sure stack doesn't overflow
+ lua_pushnil(L);
+ lua_setglobal(L, default_handler_name());
+ }
+ else {
+ ref.push(L);
+ lua_setglobal(L, default_handler_name());
+ }
+ }
+}} // namespace sol::detail
+
+// end of sol/protected_handler.hpp
+
+#include <cstdint>
+#include <algorithm>
+
+namespace sol {
+
+ namespace detail {
+ template <bool ShouldPush_, typename Handler_>
+ inline void handle_protected_exception(
+ lua_State* L_, optional<const std::exception&> maybe_ex, const char* error, detail::protected_handler<ShouldPush_, Handler_>& handler_) {
+ handler_.stack_index = 0;
+ if (ShouldPush_) {
+ handler_.target.push(L_);
+ detail::call_exception_handler(L_, maybe_ex, error);
+ lua_call(L_, 1, 1);
+ }
+ else {
+ detail::call_exception_handler(L_, maybe_ex, error);
+ }
+ }
+ } // namespace detail
+
+ template <typename Reference, bool Aligned = false, typename Handler = reference>
+ class basic_protected_function : public basic_object<Reference> {
+ private:
+ using base_t = basic_object<Reference>;
+ using handler_t = Handler;
+ inline static constexpr bool is_stack_handler_v = is_stack_based_v<handler_t>;
+
+ basic_protected_function(std::true_type, const basic_protected_function& other_) noexcept
+ : base_t(other_), m_error_handler(other_.m_error_handler.copy(lua_state())) {
+ }
+
+ basic_protected_function(std::false_type, const basic_protected_function& other_) noexcept : base_t(other_), m_error_handler(other_.m_error_handler) {
+ }
+
+ public:
+ basic_protected_function() = default;
+ template <typename T,
+ meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_protected_function>>,
+ meta::neg<std::is_base_of<proxy_base_tag, meta::unqualified_t<T>>>, meta::neg<std::is_same<base_t, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_protected_function(T&& r) noexcept : base_t(std::forward<T>(r)), m_error_handler(get_default_handler(r.lua_state())) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_function<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_protected_function>(lua_state(), -1, handler);
+ }
+#endif // Safety
+ }
+ basic_protected_function(const basic_protected_function& other_) noexcept
+ : basic_protected_function(meta::boolean<is_stateless_lua_reference_v<Handler>>(), other_) {
+ }
+ basic_protected_function& operator=(const basic_protected_function& other_) {
+ base_t::operator=(other_);
+ if constexpr (is_stateless_lua_reference_v<Handler>) {
+ m_error_handler.copy_assign(lua_state(), other_.m_error_handler);
+ }
+ else {
+ m_error_handler = other_.m_error_handler;
+ }
+ return *this;
+ }
+ basic_protected_function(basic_protected_function&&) = default;
+ basic_protected_function& operator=(basic_protected_function&&) = default;
+ basic_protected_function(const basic_function<base_t>& b) : basic_protected_function(b, get_default_handler(b.lua_state())) {
+ }
+ basic_protected_function(basic_function<base_t>&& b) : basic_protected_function(std::move(b), get_default_handler(b.lua_state())) {
+ }
+ basic_protected_function(const basic_function<base_t>& b, handler_t eh) : base_t(b), m_error_handler(std::move(eh)) {
+ }
+ basic_protected_function(basic_function<base_t>&& b, handler_t eh) : base_t(std::move(b)), m_error_handler(std::move(eh)) {
+ }
+ basic_protected_function(const stack_reference& r) : basic_protected_function(r.lua_state(), r.stack_index(), get_default_handler(r.lua_state())) {
+ }
+ basic_protected_function(stack_reference&& r) : basic_protected_function(r.lua_state(), r.stack_index(), get_default_handler(r.lua_state())) {
+ }
+ basic_protected_function(const stack_reference& r, handler_t eh) : basic_protected_function(r.lua_state(), r.stack_index(), std::move(eh)) {
+ }
+ basic_protected_function(stack_reference&& r, handler_t eh) : basic_protected_function(r.lua_state(), r.stack_index(), std::move(eh)) {
+ }
+
+ template <typename Super>
+ basic_protected_function(const proxy_base<Super>& p) : basic_protected_function(p, get_default_handler(p.lua_state())) {
+ }
+ template <typename Super>
+ basic_protected_function(proxy_base<Super>&& p) : basic_protected_function(std::move(p), get_default_handler(p.lua_state())) {
+ }
+ template <typename Proxy, typename HandlerReference,
+ meta::enable<std::is_base_of<proxy_base_tag, meta::unqualified_t<Proxy>>,
+ meta::neg<is_lua_index<meta::unqualified_t<HandlerReference>>>> = meta::enabler>
+ basic_protected_function(Proxy&& p, HandlerReference&& eh)
+ : basic_protected_function(detail::force_cast<base_t>(p), make_reference<handler_t>(p.lua_state(), std::forward<HandlerReference>(eh))) {
+ }
+
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_protected_function(lua_State* L_, T&& r) : basic_protected_function(L_, std::forward<T>(r), get_default_handler(L_)) {
+ }
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_protected_function(lua_State* L_, T&& r, handler_t eh) : base_t(L_, std::forward<T>(r)), m_error_handler(std::move(eh)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_protected_function>(lua_state(), -1, handler);
+#endif // Safety
+ }
+
+ basic_protected_function(lua_nil_t n) : base_t(n), m_error_handler(n) {
+ }
+
+ basic_protected_function(lua_State* L_, int index_ = -1) : basic_protected_function(L_, index_, get_default_handler(L_)) {
+ }
+ basic_protected_function(lua_State* L_, int index_, handler_t eh) : base_t(L_, index_), m_error_handler(std::move(eh)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_protected_function>(L_, index_, handler);
+#endif // Safety
+ }
+ basic_protected_function(lua_State* L_, absolute_index index_) : basic_protected_function(L_, index_, get_default_handler(L_)) {
+ }
+ basic_protected_function(lua_State* L_, absolute_index index_, handler_t eh) : base_t(L_, index_), m_error_handler(std::move(eh)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_protected_function>(L_, index_, handler);
+#endif // Safety
+ }
+ basic_protected_function(lua_State* L_, raw_index index_) : basic_protected_function(L_, index_, get_default_handler(L_)) {
+ }
+ basic_protected_function(lua_State* L_, raw_index index_, handler_t eh) : base_t(L_, index_), m_error_handler(std::move(eh)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_protected_function>(L_, index_, handler);
+#endif // Safety
+ }
+ basic_protected_function(lua_State* L_, ref_index index_) : basic_protected_function(L_, index_, get_default_handler(L_)) {
+ }
+ basic_protected_function(lua_State* L_, ref_index index_, handler_t eh) : base_t(L_, index_), m_error_handler(std::move(eh)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_protected_function>(lua_state(), -1, handler);
+#endif // Safety
+ }
+
+ using base_t::lua_state;
+
+ template <typename Fx>
+ int dump(lua_Writer writer, void* userdata_pointer_, bool strip, Fx&& on_error) const {
+ this->push();
+ auto ppn = stack::push_popper_n<false>(this->lua_state(), 1);
+ int r = lua_dump(this->lua_state(), writer, userdata_pointer_, strip ? 1 : 0);
+ if (r != 0) {
+ return on_error(this->lua_state(), r, writer, userdata_pointer_, strip);
+ }
+ return r;
+ }
+
+ int dump(lua_Writer writer, void* userdata_pointer_, bool strip = false) const {
+ return dump(writer, userdata_pointer_, strip, &dump_pass_on_error);
+ }
+
+ template <typename Container = bytecode>
+ Container dump() const {
+ Container bc;
+ (void)dump(static_cast<lua_Writer>(&basic_insert_dump_writer<Container>), static_cast<void*>(&bc), false, &dump_throw_on_error);
+ return bc;
+ }
+
+ template <typename Container = bytecode, typename Fx>
+ Container dump(Fx&& on_error) const {
+ Container bc;
+ (void)dump(static_cast<lua_Writer>(&basic_insert_dump_writer<Container>), static_cast<void*>(&bc), false, std::forward<Fx>(on_error));
+ return bc;
+ }
+
+ template <typename... Args>
+ protected_function_result operator()(Args&&... args) const {
+ return call<>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) operator()(types<Ret...>, Args&&... args) const {
+ return call<Ret...>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) call(Args&&... args) const {
+ if constexpr (!Aligned) {
+ // we do not expect the function to already be on the stack: push it
+ if (m_error_handler.valid(lua_state())) {
+ detail::protected_handler<true, handler_t> h(lua_state(), m_error_handler);
+ base_t::push();
+ int pushcount = stack::multi_push_reference(lua_state(), std::forward<Args>(args)...);
+ return invoke(types<Ret...>(), std::make_index_sequence<sizeof...(Ret)>(), pushcount, h);
+ }
+ else {
+ detail::protected_handler<false, handler_t> h(lua_state(), m_error_handler);
+ base_t::push();
+ int pushcount = stack::multi_push_reference(lua_state(), std::forward<Args>(args)...);
+ return invoke(types<Ret...>(), std::make_index_sequence<sizeof...(Ret)>(), pushcount, h);
+ }
+ }
+ else {
+ // the function is already on the stack at the right location
+ if (m_error_handler.valid()) {
+ // the handler will be pushed onto the stack manually,
+ // since it's not already on the stack this means we need to push our own
+ // function on the stack too and swap things to be in-place
+ if constexpr (!is_stack_handler_v) {
+ // so, we need to remove the function at the top and then dump the handler out ourselves
+ base_t::push();
+ }
+ detail::protected_handler<true, handler_t> h(lua_state(), m_error_handler);
+ if constexpr (!is_stack_handler_v) {
+ lua_replace(lua_state(), -3);
+ h.stack_index = lua_absindex(lua_state(), -2);
+ }
+ int pushcount = stack::multi_push_reference(lua_state(), std::forward<Args>(args)...);
+ return invoke(types<Ret...>(), std::make_index_sequence<sizeof...(Ret)>(), pushcount, h);
+ }
+ else {
+ detail::protected_handler<false, handler_t> h(lua_state(), m_error_handler);
+ int pushcount = stack::multi_push_reference(lua_state(), std::forward<Args>(args)...);
+ return invoke(types<Ret...>(), std::make_index_sequence<sizeof...(Ret)>(), pushcount, h);
+ }
+ }
+ }
+
+ ~basic_protected_function() {
+ if constexpr (is_stateless_lua_reference_v<handler_t>) {
+ this->m_error_handler.reset(lua_state());
+ }
+ }
+
+ static handler_t get_default_handler(lua_State* L_) {
+ return detail::get_default_handler<handler_t, is_main_threaded_v<base_t>>(L_);
+ }
+
+ template <typename T>
+ static void set_default_handler(const T& ref) {
+ detail::set_default_handler(ref.lua_state(), ref);
+ }
+
+ auto get_error_handler() const noexcept {
+ if constexpr (is_stateless_lua_reference_v<handler_t>) {
+ if constexpr (is_stack_based_v<handler_t>) {
+ return stack_reference(lua_state(), m_error_handler.stack_index());
+ }
+ else {
+ return basic_reference<is_main_threaded_v<base_t>>(lua_state(), ref_index(m_error_handler.registry_index()));
+ }
+ }
+ else {
+ return m_error_handler;
+ }
+ }
+
+ template <typename ErrorHandler_>
+ void set_error_handler(ErrorHandler_&& error_handler_) noexcept {
+ static_assert(!is_stack_based_v<handler_t> || is_stack_based_v<ErrorHandler_>,
+ "A stack-based error handler can only be set from a parameter that is also stack-based.");
+ if constexpr (std::is_rvalue_reference_v<ErrorHandler_>) {
+ m_error_handler = std::forward<ErrorHandler_>(error_handler_);
+ }
+ else {
+ m_error_handler.copy_assign(lua_state(), std::forward<ErrorHandler_>(error_handler_));
+ }
+ }
+
+ void abandon () noexcept {
+ this->m_error_handler.abandon();
+ base_t::abandon();
+ }
+
+ private:
+ handler_t m_error_handler;
+
+ template <bool b>
+ call_status luacall(std::ptrdiff_t argcount, std::ptrdiff_t result_count_, detail::protected_handler<b, handler_t>& h) const {
+ return static_cast<call_status>(lua_pcall(lua_state(), static_cast<int>(argcount), static_cast<int>(result_count_), h.stack_index));
+ }
+
+ template <std::size_t... I, bool b, typename... Ret>
+ auto invoke(types<Ret...>, std::index_sequence<I...>, std::ptrdiff_t n, detail::protected_handler<b, handler_t>& h) const {
+ luacall(n, sizeof...(Ret), h);
+ return stack::pop<std::tuple<Ret...>>(lua_state());
+ }
+
+ template <std::size_t I, bool b, typename Ret>
+ Ret invoke(types<Ret>, std::index_sequence<I>, std::ptrdiff_t n, detail::protected_handler<b, handler_t>& h) const {
+ luacall(n, 1, h);
+ return stack::pop<Ret>(lua_state());
+ }
+
+ template <std::size_t I, bool b>
+ void invoke(types<void>, std::index_sequence<I>, std::ptrdiff_t n, detail::protected_handler<b, handler_t>& h) const {
+ luacall(n, 0, h);
+ }
+
+ template <bool b>
+ protected_function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n, detail::protected_handler<b, handler_t>& h) const {
+ int stacksize = lua_gettop(lua_state());
+ int poststacksize = stacksize;
+ int firstreturn = 1;
+ int returncount = 0;
+ call_status code = call_status::ok;
+#if SOL_IS_ON(SOL_EXCEPTIONS) && SOL_IS_OFF(SOL_PROPAGATE_EXCEPTIONS)
+ try {
+#endif // No Exceptions
+ firstreturn = (std::max)(1, static_cast<int>(stacksize - n - static_cast<int>(h.valid() && !is_stack_handler_v)));
+ code = luacall(n, LUA_MULTRET, h);
+ poststacksize = lua_gettop(lua_state()) - static_cast<int>(h.valid() && !is_stack_handler_v);
+ returncount = poststacksize - (firstreturn - 1);
+#if SOL_IS_ON(SOL_EXCEPTIONS) && SOL_IS_OFF(SOL_PROPAGATE_EXCEPTIONS)
+ }
+ // Handle C++ errors thrown from C++ functions bound inside of lua
+ catch (const char* error) {
+ detail::handle_protected_exception(lua_state(), optional<const std::exception&>(nullopt), error, h);
+ firstreturn = lua_gettop(lua_state());
+ return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime);
+ }
+ catch (const std::string& error) {
+ detail::handle_protected_exception(lua_state(), optional<const std::exception&>(nullopt), error.c_str(), h);
+ firstreturn = lua_gettop(lua_state());
+ return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime);
+ }
+ catch (const std::exception& error) {
+ detail::handle_protected_exception(lua_state(), optional<const std::exception&>(error), error.what(), h);
+ firstreturn = lua_gettop(lua_state());
+ return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime);
+ }
+#if SOL_IS_ON(SOL_EXCEPTIONS_CATCH_ALL)
+ // LuaJIT cannot have the catchall when the safe propagation is on
+ // but LuaJIT will swallow all C++ errors
+ // if we don't at least catch std::exception ones
+ catch (...) {
+ detail::handle_protected_exception(lua_state(), optional<const std::exception&>(nullopt), detail::protected_function_error, h);
+ firstreturn = lua_gettop(lua_state());
+ return protected_function_result(lua_state(), firstreturn, 0, 1, call_status::runtime);
+ }
+#endif // Always catch edge case
+#else
+ // do not handle exceptions: they can be propogated into C++ and keep all type information / rich information
+#endif // Exceptions vs. No Exceptions
+ return protected_function_result(lua_state(), firstreturn, returncount, returncount, code);
+ }
+ };
+} // namespace sol
+
+// end of sol/protected_function.hpp
+
+#include <functional>
+
+namespace sol {
+ template <typename... Ret, typename... Args>
+ decltype(auto) stack_proxy::call(Args&&... args) {
+ stack_function sf(this->lua_state(), this->stack_index());
+ return sf.template call<Ret...>(std::forward<Args>(args)...);
+ }
+
+ inline protected_function_result::protected_function_result(unsafe_function_result&& o) noexcept
+ : L(o.lua_state()), index(o.stack_index()), returncount(o.return_count()), popcount(o.return_count()), err(o.status()) {
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but we will be thorough
+ o.abandon();
+ }
+
+ inline protected_function_result& protected_function_result::operator=(unsafe_function_result&& o) noexcept {
+ L = o.lua_state();
+ index = o.stack_index();
+ returncount = o.return_count();
+ popcount = o.return_count();
+ err = o.status();
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but we will be thorough
+ o.abandon();
+ return *this;
+ }
+
+ inline unsafe_function_result::unsafe_function_result(protected_function_result&& o) noexcept
+ : L(o.lua_state()), index(o.stack_index()), returncount(o.return_count()) {
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but we will be thorough
+ o.abandon();
+ }
+ inline unsafe_function_result& unsafe_function_result::operator=(protected_function_result&& o) noexcept {
+ L = o.lua_state();
+ index = o.stack_index();
+ returncount = o.return_count();
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but we will be thorough
+ o.abandon();
+ return *this;
+ }
+
+ namespace detail {
+ template <typename... R>
+ struct std_shim {
+ unsafe_function lua_func_;
+
+ std_shim(unsafe_function lua_func) : lua_func_(std::move(lua_func)) {
+ }
+
+ template <typename... Args>
+ meta::return_type_t<R...> operator()(Args&&... args) {
+ return lua_func_.call<R...>(std::forward<Args>(args)...);
+ }
+ };
+
+ template <>
+ struct std_shim<void> {
+ unsafe_function lua_func_;
+
+ std_shim(unsafe_function lua_func) : lua_func_(std::move(lua_func)) {
+ }
+
+ template <typename... Args>
+ void operator()(Args&&... args) {
+ lua_func_.call<void>(std::forward<Args>(args)...);
+ }
+ };
+ } // namespace detail
+
+ namespace stack {
+ template <typename Signature>
+ struct unqualified_getter<std::function<Signature>> {
+ typedef meta::bind_traits<Signature> fx_t;
+ typedef typename fx_t::args_list args_lists;
+ typedef meta::tuple_types<typename fx_t::return_type> return_types;
+
+ template <typename... R>
+ static std::function<Signature> get_std_func(types<R...>, lua_State* L, int index) {
+ detail::std_shim<R...> fx(unsafe_function(L, index));
+ return fx;
+ }
+
+ static std::function<Signature> get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ type t = type_of(L, index);
+ if (t == type::none || t == type::lua_nil) {
+ return nullptr;
+ }
+ return get_std_func(return_types(), L, index);
+ }
+ };
+
+ template <typename Allocator>
+ struct unqualified_getter<basic_bytecode<Allocator>> {
+ static basic_bytecode<Allocator> get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ stack_function sf(L, index);
+ return sf.dump(&dump_panic_on_error);
+ }
+ };
+ } // namespace stack
+
+} // namespace sol
+
+// end of sol/function.hpp
+
+// beginning of sol/usertype.hpp
+
+// beginning of sol/usertype_core.hpp
+
+// beginning of sol/deprecate.hpp
+
+#ifndef SOL_DEPRECATED
+#ifdef _MSC_VER
+#define SOL_DEPRECATED __declspec(deprecated)
+#elif __GNUC__
+#define SOL_DEPRECATED __attribute__((deprecated))
+#else
+#define SOL_DEPRECATED [[deprecated]]
+#endif // compilers
+#endif // SOL_DEPRECATED
+
+namespace sol { namespace detail {
+ template <typename T>
+ struct SOL_DEPRECATED deprecate_type {
+ using type = T;
+ };
+}} // namespace sol::detail
+
+// end of sol/deprecate.hpp
+
+// beginning of sol/usertype_container_launch.hpp
+
+// beginning of sol/usertype_container.hpp
+
+namespace sol {
+
+ template <typename T>
+ struct usertype_container;
+
+ namespace container_detail {
+
+ template <typename T>
+ struct has_clear_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::clear));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_empty_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::empty));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_erase_after_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(
+ decltype(std::declval<C>().erase_after(std::declval<std::add_rvalue_reference_t<typename C::const_iterator>>()))*);
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T, typename = void>
+ struct has_find_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(std::declval<C>().find(std::declval<std::add_rvalue_reference_t<typename C::value_type>>()))*);
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_find_test<T, std::enable_if_t<meta::is_lookup<T>::value>> {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(std::declval<C>().find(std::declval<std::add_rvalue_reference_t<typename C::key_type>>()))*);
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_erase_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(std::declval<C>().erase(std::declval<typename C::iterator>()))*);
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_erase_key_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(std::declval<C>().erase(std::declval<typename C::key_type>()))*);
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_find_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::find));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_index_of_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::index_of));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_insert_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::insert));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_erase_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::erase));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_index_set_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::index_set));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_index_get_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::index_get));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_set_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::set));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_get_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::get));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_at_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::at));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_pairs_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::pairs));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_ipairs_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::ipairs));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_next_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::next));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_add_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::add));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ struct has_traits_size_test {
+ private:
+ template <typename C>
+ static meta::sfinae_yes_t test(decltype(&C::size));
+ template <typename C>
+ static meta::sfinae_no_t test(...);
+
+ public:
+ static constexpr bool value = std::is_same_v<decltype(test<T>(0)), meta::sfinae_yes_t>;
+ };
+
+ template <typename T>
+ using has_clear = meta::boolean<has_clear_test<T>::value>;
+
+ template <typename T>
+ using has_empty = meta::boolean<has_empty_test<T>::value>;
+
+ template <typename T>
+ using has_find = meta::boolean<has_find_test<T>::value>;
+
+ template <typename T>
+ using has_erase = meta::boolean<has_erase_test<T>::value>;
+
+ template <typename T>
+ using has_erase_key = meta::boolean<has_erase_key_test<T>::value>;
+
+ template <typename T>
+ using has_erase_after = meta::boolean<has_erase_after_test<T>::value>;
+
+ template <typename T>
+ using has_traits_get = meta::boolean<has_traits_get_test<T>::value>;
+
+ template <typename T>
+ using has_traits_at = meta::boolean<has_traits_at_test<T>::value>;
+
+ template <typename T>
+ using has_traits_set = meta::boolean<has_traits_set_test<T>::value>;
+
+ template <typename T>
+ using has_traits_index_get = meta::boolean<has_traits_index_get_test<T>::value>;
+
+ template <typename T>
+ using has_traits_index_set = meta::boolean<has_traits_index_set_test<T>::value>;
+
+ template <typename T>
+ using has_traits_pairs = meta::boolean<has_traits_pairs_test<T>::value>;
+
+ template <typename T>
+ using has_traits_ipairs = meta::boolean<has_traits_ipairs_test<T>::value>;
+
+ template <typename T>
+ using has_traits_next = meta::boolean<has_traits_next_test<T>::value>;
+
+ template <typename T>
+ using has_traits_add = meta::boolean<has_traits_add_test<T>::value>;
+
+ template <typename T>
+ using has_traits_size = meta::boolean<has_traits_size_test<T>::value>;
+
+ template <typename T>
+ using has_traits_clear = has_clear<T>;
+
+ template <typename T>
+ using has_traits_empty = has_empty<T>;
+
+ template <typename T>
+ using has_traits_find = meta::boolean<has_traits_find_test<T>::value>;
+
+ template <typename T>
+ using has_traits_index_of = meta::boolean<has_traits_index_of_test<T>::value>;
+
+ template <typename T>
+ using has_traits_insert = meta::boolean<has_traits_insert_test<T>::value>;
+
+ template <typename T>
+ using has_traits_erase = meta::boolean<has_traits_erase_test<T>::value>;
+
+ template <typename T>
+ struct is_forced_container : is_container<T> { };
+
+ template <typename T>
+ struct is_forced_container<as_container_t<T>> : std::true_type { };
+
+ template <typename T>
+ struct container_decay {
+ typedef T type;
+ };
+
+ template <typename T>
+ struct container_decay<as_container_t<T>> {
+ typedef T type;
+ };
+
+ template <typename T>
+ using container_decay_t = typename container_decay<meta::unqualified_t<T>>::type;
+
+ template <typename T>
+ decltype(auto) get_key(std::false_type, T&& t) {
+ return std::forward<T>(t);
+ }
+
+ template <typename T>
+ decltype(auto) get_key(std::true_type, T&& t) {
+ return t.first;
+ }
+
+ template <typename T>
+ decltype(auto) get_value(std::false_type, T&& t) {
+ return std::forward<T>(t);
+ }
+
+ template <typename T>
+ decltype(auto) get_value(std::true_type, T&& t) {
+ return t.second;
+ }
+
+ template <typename X, typename = void>
+ struct usertype_container_default {
+ private:
+ typedef std::remove_pointer_t<meta::unwrap_unqualified_t<X>> T;
+
+ public:
+ typedef lua_nil_t iterator;
+ typedef iterator sentinel;
+ typedef lua_nil_t value_type;
+
+ static int at(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'at(index)' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int get(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'get(key)' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int index_get(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'container[key]' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int set(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'set(key, value)' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int index_set(lua_State* L_) {
+ return luaL_error(
+ L_, "sol: cannot call 'container[key] = value' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int add(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'add' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int insert(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'insert' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int find(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'find' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int index_of(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'index_of' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int size(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'end' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int clear(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'clear' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int empty(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'empty' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int erase(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'erase' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int next(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'next' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int pairs(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call '__pairs/pairs' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static int ipairs(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call '__ipairs' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ }
+
+ static iterator begin(lua_State* L_, T&) {
+ luaL_error(L_, "sol: cannot call 'being' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ return lua_nil;
+ }
+
+ static sentinel end(lua_State* L_, T&) {
+ luaL_error(L_, "sol: cannot call 'end' on type '%s': it is not recognized as a container", detail::demangle<T>().c_str());
+ return lua_nil;
+ }
+ };
+
+ template <typename X>
+ struct usertype_container_default<X,
+ std::enable_if_t<meta::all<is_forced_container<meta::unqualified_t<X>>, meta::has_value_type<meta::unqualified_t<container_decay_t<X>>>,
+ meta::has_iterator<meta::unqualified_t<container_decay_t<X>>>>::value>> {
+ private:
+ using T = std::remove_pointer_t<meta::unwrap_unqualified_t<container_decay_t<X>>>;
+
+ private:
+ using deferred_uc = usertype_container<X>;
+ using is_associative = meta::is_associative<T>;
+ using is_lookup = meta::is_lookup<T>;
+ using is_ordered = meta::is_ordered<T>;
+ using is_matched_lookup = meta::is_matched_lookup<T>;
+ using iterator = typename T::iterator;
+ using sentinel = meta::sentinel_or_t<T, iterator>;
+ using value_type = typename T::value_type;
+ typedef meta::conditional_t<is_matched_lookup::value, std::pair<value_type, value_type>,
+ meta::conditional_t<is_associative::value || is_lookup::value, value_type, std::pair<std::ptrdiff_t, value_type>>>
+ KV;
+ typedef typename KV::first_type K;
+ typedef typename KV::second_type V;
+ typedef meta::conditional_t<is_matched_lookup::value, std::ptrdiff_t, K> next_K;
+ typedef decltype(*std::declval<iterator&>()) iterator_return;
+ typedef meta::conditional_t<is_associative::value || is_matched_lookup::value, std::add_lvalue_reference_t<V>,
+ meta::conditional_t<is_lookup::value, V, iterator_return>>
+ captured_type;
+ typedef typename meta::iterator_tag<iterator>::type iterator_category;
+ typedef std::is_same<iterator_category, std::input_iterator_tag> is_input_iterator;
+ typedef meta::conditional_t<is_input_iterator::value, V, decltype(detail::deref_move_only(std::declval<captured_type>()))> push_type;
+ typedef std::is_copy_assignable<V> is_copyable;
+ typedef meta::neg<meta::any<std::is_const<V>, std::is_const<std::remove_reference_t<iterator_return>>, meta::neg<is_copyable>>> is_writable;
+ typedef meta::unqualified_t<decltype(get_key(is_associative(), std::declval<std::add_lvalue_reference_t<value_type>>()))> key_type;
+ typedef meta::all<std::is_integral<K>, meta::neg<meta::any<is_associative, is_lookup>>> is_linear_integral;
+
+ struct iter : detail::ebco<iterator, 0>, detail::ebco<sentinel, 1> {
+ using it_base = detail::ebco<iterator, 0>;
+ using sen_base = detail::ebco<sentinel, 1>;
+ main_reference keep_alive;
+ std::size_t index;
+
+ iter(lua_State* L_, int stack_index_, iterator it_, sentinel sen_) noexcept
+ : it_base(std::move(it_)), sen_base(std::move(sen_)), keep_alive(L_, stack_index_), index(0) {
+ }
+
+ iterator& it() noexcept {
+ return it_base::value();
+ }
+
+ const iterator& it() const noexcept {
+ return it_base::value();
+ }
+
+ sentinel& sen() noexcept {
+ return sen_base::value();
+ }
+
+ const sentinel& sen() const noexcept {
+ return sen_base::value();
+ }
+ };
+
+ static auto& get_src(lua_State* L_) {
+#if SOL_IS_ON(SOL_SAFE_USERTYPE)
+ auto p = stack::unqualified_check_get<T*>(L_, 1);
+ if (!p) {
+ luaL_error(L_,
+ "sol: 'self' is not of type '%s' (pass 'self' as first argument with ':' or call on proper type)",
+ detail::demangle<T>().c_str());
+ }
+ if (p.value() == nullptr) {
+ luaL_error(
+ L_, "sol: 'self' argument is nil (pass 'self' as first argument with ':' or call on a '%s' type)", detail::demangle<T>().c_str());
+ }
+ return *p.value();
+#else
+ return stack::unqualified_get<T>(L_, 1);
+#endif // Safe getting with error
+ }
+
+ static detail::error_result at_category(std::input_iterator_tag, lua_State* L_, T& self, std::ptrdiff_t pos) {
+ pos += deferred_uc::index_adjustment(L_, self);
+ if (pos < 0) {
+ return stack::push(L_, lua_nil);
+ }
+ auto it = deferred_uc::begin(L_, self);
+ auto e = deferred_uc::end(L_, self);
+ if (it == e) {
+ return stack::push(L_, lua_nil);
+ }
+ while (pos > 0) {
+ --pos;
+ ++it;
+ if (it == e) {
+ return stack::push(L_, lua_nil);
+ }
+ }
+ return get_associative(is_associative(), L_, it);
+ }
+
+ static detail::error_result at_category(std::random_access_iterator_tag, lua_State* L_, T& self, std::ptrdiff_t pos) {
+ std::ptrdiff_t len = static_cast<std::ptrdiff_t>(size_start(L_, self));
+ pos += deferred_uc::index_adjustment(L_, self);
+ if (pos < 0 || pos >= len) {
+ return stack::push(L_, lua_nil);
+ }
+ auto it = std::next(deferred_uc::begin(L_, self), pos);
+ return get_associative(is_associative(), L_, it);
+ }
+
+ static detail::error_result at_start(lua_State* L_, T& self, std::ptrdiff_t pos) {
+ return at_category(iterator_category(), L_, self, pos);
+ }
+
+ template <typename Iter>
+ static detail::error_result get_associative(std::true_type, lua_State* L_, Iter& it) {
+ decltype(auto) v = *it;
+ return stack::stack_detail::push_reference<push_type>(L_, detail::deref_move_only(v.second));
+ }
+
+ template <typename Iter>
+ static detail::error_result get_associative(std::false_type, lua_State* L_, Iter& it) {
+ return stack::stack_detail::push_reference<push_type>(L_, detail::deref_move_only(*it));
+ }
+
+ static detail::error_result get_category(std::input_iterator_tag, lua_State* L_, T& self, K& key) {
+ key = static_cast<K>(key + deferred_uc::index_adjustment(L_, self));
+ if (key < 0) {
+ return stack::push(L_, lua_nil);
+ }
+ auto it = deferred_uc::begin(L_, self);
+ auto e = deferred_uc::end(L_, self);
+ if (it == e) {
+ return stack::push(L_, lua_nil);
+ }
+ while (key > 0) {
+ --key;
+ ++it;
+ if (it == e) {
+ return stack::push(L_, lua_nil);
+ }
+ }
+ return get_associative(is_associative(), L_, it);
+ }
+
+ static detail::error_result get_category(std::random_access_iterator_tag, lua_State* L_, T& self, K& key) {
+ std::ptrdiff_t len = static_cast<std::ptrdiff_t>(size_start(L_, self));
+ key = static_cast<K>(static_cast<std::ptrdiff_t>(key) + deferred_uc::index_adjustment(L_, self));
+ if (key < 0 || key >= len) {
+ return stack::push(L_, lua_nil);
+ }
+ auto it = std::next(deferred_uc::begin(L_, self), key);
+ return get_associative(is_associative(), L_, it);
+ }
+
+ static detail::error_result get_it(std::true_type, lua_State* L_, T& self, K& key) {
+ return get_category(iterator_category(), L_, self, key);
+ }
+
+ static detail::error_result get_comparative(std::true_type, lua_State* L_, T& self, K& key) {
+ auto fx = [&](const value_type& r) -> bool { return key == get_key(is_associative(), r); };
+ auto e = deferred_uc::end(L_, self);
+ auto it = std::find_if(deferred_uc::begin(L_, self), e, std::ref(fx));
+ if (it == e) {
+ return stack::push(L_, lua_nil);
+ }
+ return get_associative(is_associative(), L_, it);
+ }
+
+ static detail::error_result get_comparative(std::false_type, lua_State*, T&, K&) {
+ return detail::error_result("cannot get this key on '%s': no suitable way to increment iterator and compare to key value '%s'",
+ detail::demangle<T>().data(),
+ detail::demangle<K>().data());
+ }
+
+ static detail::error_result get_it(std::false_type, lua_State* L_, T& self, K& key) {
+ return get_comparative(meta::supports_op_equal<K, key_type>(), L_, self, key);
+ }
+
+ static detail::error_result set_associative(std::true_type, iterator& it, stack_object value) {
+ decltype(auto) v = *it;
+ v.second = value.as<V>();
+ return {};
+ }
+
+ static detail::error_result set_associative(std::false_type, iterator& it, stack_object value) {
+ decltype(auto) v = *it;
+ v = value.as<V>();
+ return {};
+ }
+
+ static detail::error_result set_writable(std::true_type, lua_State*, T&, iterator& it, stack_object value) {
+ return set_associative(is_associative(), it, std::move(value));
+ }
+
+ static detail::error_result set_writable(std::false_type, lua_State*, T&, iterator&, stack_object) {
+ return detail::error_result(
+ "cannot perform a 'set': '%s's iterator reference is not writable (non-copy-assignable or const)", detail::demangle<T>().data());
+ }
+
+ static detail::error_result set_category(std::input_iterator_tag, lua_State* L_, T& self, stack_object okey, stack_object value) {
+ decltype(auto) key = okey.as<K>();
+ key = static_cast<K>(static_cast<std::ptrdiff_t>(key) + deferred_uc::index_adjustment(L_, self));
+ auto e = deferred_uc::end(L_, self);
+ auto it = deferred_uc::begin(L_, self);
+ auto backit = it;
+ for (; key > 0 && it != e; --key, ++it) {
+ backit = it;
+ }
+ if (it == e) {
+ if (key == 0) {
+ return add_copyable(is_copyable(), L_, self, std::move(value), meta::has_insert_after<T>::value ? backit : it);
+ }
+ return detail::error_result("out of bounds (too big) for set on '%s'", detail::demangle<T>().c_str());
+ }
+ return set_writable(is_writable(), L_, self, it, std::move(value));
+ }
+
+ static detail::error_result set_category(std::random_access_iterator_tag, lua_State* L_, T& self, stack_object okey, stack_object value) {
+ decltype(auto) key = okey.as<K>();
+ key = static_cast<K>(static_cast<std::ptrdiff_t>(key) + deferred_uc::index_adjustment(L_, self));
+ if (key < 0) {
+ return detail::error_result("sol: out of bounds (too small) for set on '%s'", detail::demangle<T>().c_str());
+ }
+ std::ptrdiff_t len = static_cast<std::ptrdiff_t>(size_start(L_, self));
+ if (key == len) {
+ return add_copyable(is_copyable(), L_, self, std::move(value));
+ }
+ else if (key >= len) {
+ return detail::error_result("sol: out of bounds (too big) for set on '%s'", detail::demangle<T>().c_str());
+ }
+ auto it = std::next(deferred_uc::begin(L_, self), key);
+ return set_writable(is_writable(), L_, self, it, std::move(value));
+ }
+
+ static detail::error_result set_comparative(std::true_type, lua_State* L_, T& self, stack_object okey, stack_object value) {
+ decltype(auto) key = okey.as<K>();
+ if (!is_writable::value) {
+ return detail::error_result(
+ "cannot perform a 'set': '%s's iterator reference is not writable (non-copy-assignable or const)", detail::demangle<T>().data());
+ }
+ auto fx = [&](const value_type& r) -> bool { return key == get_key(is_associative(), r); };
+ auto e = deferred_uc::end(L_, self);
+ auto it = std::find_if(deferred_uc::begin(L_, self), e, std::ref(fx));
+ if (it == e) {
+ return {};
+ }
+ return set_writable(is_writable(), L_, self, it, std::move(value));
+ }
+
+ static detail::error_result set_comparative(std::false_type, lua_State*, T&, stack_object, stack_object) {
+ return detail::error_result("cannot set this value on '%s': no suitable way to increment iterator or compare to '%s' key",
+ detail::demangle<T>().data(),
+ detail::demangle<K>().data());
+ }
+
+ template <typename Iter>
+ static detail::error_result set_associative_insert(std::true_type, lua_State*, T& self, Iter& it, K& key, stack_object value) {
+ if constexpr (meta::has_insert_with_iterator<T>::value) {
+ self.insert(it, value_type(key, value.as<V>()));
+ return {};
+ }
+ else if constexpr (meta::has_insert<T>::value) {
+ self.insert(value_type(key, value.as<V>()));
+ return {};
+ }
+ else {
+ (void)self;
+ (void)it;
+ (void)key;
+ return detail::error_result(
+ "cannot call 'set' on '%s': there is no 'insert' function on this associative type", detail::demangle<T>().c_str());
+ }
+ }
+
+ template <typename Iter>
+ static detail::error_result set_associative_insert(std::false_type, lua_State*, T& self, Iter& it, K& key, stack_object) {
+ if constexpr (meta::has_insert_with_iterator<T>::value) {
+ self.insert(it, key);
+ return {};
+ }
+ else if constexpr (meta::has_insert<T>::value) {
+ self.insert(key);
+ return {};
+ }
+ else {
+ (void)self;
+ (void)it;
+ (void)key;
+ return detail::error_result(
+ "cannot call 'set' on '%s': there is no 'insert' function on this non-associative type", detail::demangle<T>().c_str());
+ }
+ }
+
+ static detail::error_result set_associative_find(std::true_type, lua_State* L_, T& self, stack_object okey, stack_object value) {
+ decltype(auto) key = okey.as<K>();
+ auto it = self.find(key);
+ if (it == deferred_uc::end(L_, self)) {
+ return set_associative_insert(is_associative(), L_, self, it, key, std::move(value));
+ }
+ return set_writable(is_writable(), L_, self, it, std::move(value));
+ }
+
+ static detail::error_result set_associative_find(std::false_type, lua_State* L_, T& self, stack_object key, stack_object value) {
+ return set_comparative(meta::supports_op_equal<K, key_type>(), L_, self, std::move(key), std::move(value));
+ }
+
+ static detail::error_result set_it(std::true_type, lua_State* L_, T& self, stack_object key, stack_object value) {
+ return set_category(iterator_category(), L_, self, std::move(key), std::move(value));
+ }
+
+ static detail::error_result set_it(std::false_type, lua_State* L_, T& self, stack_object key, stack_object value) {
+ return set_associative_find(meta::all<has_find<T>, meta::any<is_associative, is_lookup>>(), L_, self, std::move(key), std::move(value));
+ }
+
+ template <bool idx_of = false>
+ static detail::error_result find_has_associative_lookup(std::true_type, lua_State* L_, T& self) {
+ if constexpr (!is_ordered::value && idx_of) {
+ (void)L_;
+ (void)self;
+ return detail::error_result("cannot perform an 'index_of': '%s's is not an ordered container", detail::demangle<T>().data());
+ }
+ else {
+ decltype(auto) key = stack::unqualified_get<K>(L_, 2);
+ auto it = self.find(key);
+ if (it == deferred_uc::end(L_, self)) {
+ return stack::push(L_, lua_nil);
+ }
+ if constexpr (idx_of) {
+ auto dist = std::distance(deferred_uc::begin(L_, self), it);
+ dist -= deferred_uc::index_adjustment(L_, self);
+ return stack::push(L_, dist);
+ }
+ else {
+ return get_associative(is_associative(), L_, it);
+ }
+ }
+ }
+
+ template <bool idx_of = false>
+ static detail::error_result find_has_associative_lookup(std::false_type, lua_State* L_, T& self) {
+ if constexpr (!is_ordered::value && idx_of) {
+ (void)L_;
+ (void)self;
+ return detail::error_result("cannot perform an 'index_of': '%s's is not an ordered container", detail::demangle<T>().data());
+ }
+ else {
+ decltype(auto) value = stack::unqualified_get<V>(L_, 2);
+ auto it = self.find(value);
+ if (it == deferred_uc::end(L_, self)) {
+ return stack::push(L_, lua_nil);
+ }
+ if constexpr (idx_of) {
+ auto dist = std::distance(deferred_uc::begin(L_, self), it);
+ dist -= deferred_uc::index_adjustment(L_, self);
+ return stack::push(L_, dist);
+ }
+ else {
+ return get_associative(is_associative(), L_, it);
+ }
+ }
+ }
+
+ template <bool idx_of = false>
+ static detail::error_result find_has(std::true_type, lua_State* L_, T& self) {
+ return find_has_associative_lookup<idx_of>(meta::any<is_lookup, is_associative>(), L_, self);
+ }
+
+ template <typename Iter>
+ static detail::error_result find_associative_lookup(std::true_type, lua_State* L_, T&, Iter& it, std::size_t) {
+ return get_associative(is_associative(), L_, it);
+ }
+
+ template <typename Iter>
+ static detail::error_result find_associative_lookup(std::false_type, lua_State* L_, T& self, Iter&, std::size_t idx) {
+ idx = static_cast<std::size_t>(static_cast<std::ptrdiff_t>(idx) - deferred_uc::index_adjustment(L_, self));
+ return stack::push(L_, idx);
+ }
+
+ template <bool = false>
+ static detail::error_result find_comparative(std::false_type, lua_State*, T&) {
+ return detail::error_result("cannot call 'find' on '%s': there is no 'find' function and the value_type is not equality comparable",
+ detail::demangle<T>().c_str());
+ }
+
+ template <bool idx_of = false>
+ static detail::error_result find_comparative(std::true_type, lua_State* L_, T& self) {
+ decltype(auto) value = stack::unqualified_get<V>(L_, 2);
+ auto it = deferred_uc::begin(L_, self);
+ auto e = deferred_uc::end(L_, self);
+ std::size_t idx = 0;
+ for (;; ++it, ++idx) {
+ if (it == e) {
+ return stack::push(L_, lua_nil);
+ }
+ if (value == get_value(is_associative(), *it)) {
+ break;
+ }
+ }
+ return find_associative_lookup(meta::all<meta::boolean<!idx_of>, meta::any<is_lookup, is_associative>>(), L_, self, it, idx);
+ }
+
+ template <bool idx_of = false>
+ static detail::error_result find_has(std::false_type, lua_State* L_, T& self) {
+ return find_comparative<idx_of>(meta::supports_op_equal<V>(), L_, self);
+ }
+
+ template <typename Iter>
+ static detail::error_result add_insert_after(std::false_type, lua_State* L_, T& self, stack_object value, Iter&) {
+ return add_insert_after(std::false_type(), L_, self, value);
+ }
+
+ static detail::error_result add_insert_after(std::false_type, lua_State*, T&, stack_object) {
+ return detail::error_result("cannot call 'add' on type '%s': no suitable insert/push_back C++ functions", detail::demangle<T>().data());
+ }
+
+ template <typename Iter>
+ static detail::error_result add_insert_after(std::true_type, lua_State*, T& self, stack_object value, Iter& pos) {
+ self.insert_after(pos, value.as<V>());
+ return {};
+ }
+
+ static detail::error_result add_insert_after(std::true_type, lua_State* L_, T& self, stack_object value) {
+ auto backit = self.before_begin();
+ {
+ auto e = deferred_uc::end(L_, self);
+ for (auto it = deferred_uc::begin(L_, self); it != e; ++backit, ++it) { }
+ }
+ return add_insert_after(std::true_type(), L_, self, value, backit);
+ }
+
+ template <typename Iter>
+ static detail::error_result add_insert(std::true_type, lua_State*, T& self, stack_object value, Iter& pos) {
+ self.insert(pos, value.as<V>());
+ return {};
+ }
+
+ static detail::error_result add_insert(std::true_type, lua_State* L_, T& self, stack_object value) {
+ auto pos = deferred_uc::end(L_, self);
+ return add_insert(std::true_type(), L_, self, value, pos);
+ }
+
+ template <typename Iter>
+ static detail::error_result add_insert(std::false_type, lua_State* L_, T& self, stack_object value, Iter& pos) {
+ return add_insert_after(meta::has_insert_after<T>(), L_, self, std::move(value), pos);
+ }
+
+ static detail::error_result add_insert(std::false_type, lua_State* L_, T& self, stack_object value) {
+ return add_insert_after(meta::has_insert_after<T>(), L_, self, std::move(value));
+ }
+
+ template <typename Iter>
+ static detail::error_result add_push_back(std::true_type, lua_State*, T& self, stack_object value, Iter&) {
+ self.push_back(value.as<V>());
+ return {};
+ }
+
+ static detail::error_result add_push_back(std::true_type, lua_State*, T& self, stack_object value) {
+ self.push_back(value.as<V>());
+ return {};
+ }
+
+ template <typename Iter>
+ static detail::error_result add_push_back(std::false_type, lua_State* L_, T& self, stack_object value, Iter& pos) {
+ return add_insert(
+ std::integral_constant < bool, meta::has_insert<T>::value || meta::has_insert_with_iterator<T>::value > (), L_, self, value, pos);
+ }
+
+ static detail::error_result add_push_back(std::false_type, lua_State* L_, T& self, stack_object value) {
+ return add_insert(
+ std::integral_constant < bool, meta::has_insert<T>::value || meta::has_insert_with_iterator<T>::value > (), L_, self, value);
+ }
+
+ template <typename Iter>
+ static detail::error_result add_associative(std::true_type, lua_State* L_, T& self, stack_object key, Iter& pos) {
+ if constexpr (meta::has_insert_with_iterator<T>::value) {
+ self.insert(pos, value_type(key.as<K>(), stack::unqualified_get<V>(L_, 3)));
+ return {};
+ }
+ else if constexpr (meta::has_insert<T>::value) {
+ self.insert(value_type(key.as<K>(), stack::unqualified_get<V>(L_, 3)));
+ return {};
+ }
+ else {
+ (void)L_;
+ (void)self;
+ (void)key;
+ (void)pos;
+ return detail::error_result(
+ "cannot call 'insert' on '%s': there is no 'insert' function on this associative type", detail::demangle<T>().c_str());
+ }
+ }
+
+ static detail::error_result add_associative(std::true_type, lua_State* L_, T& self, stack_object key) {
+ auto pos = deferred_uc::end(L_, self);
+ return add_associative(std::true_type(), L_, self, std::move(key), pos);
+ }
+
+ template <typename Iter>
+ static detail::error_result add_associative(std::false_type, lua_State* L_, T& self, stack_object value, Iter& pos) {
+ return add_push_back(meta::has_push_back<T>(), L_, self, value, pos);
+ }
+
+ static detail::error_result add_associative(std::false_type, lua_State* L_, T& self, stack_object value) {
+ return add_push_back(meta::has_push_back<T>(), L_, self, value);
+ }
+
+ template <typename Iter>
+ static detail::error_result add_copyable(std::true_type, lua_State* L_, T& self, stack_object value, Iter& pos) {
+ return add_associative(is_associative(), L_, self, std::move(value), pos);
+ }
+
+ static detail::error_result add_copyable(std::true_type, lua_State* L_, T& self, stack_object value) {
+ return add_associative(is_associative(), L_, self, value);
+ }
+
+ template <typename Iter>
+ static detail::error_result add_copyable(std::false_type, lua_State* L_, T& self, stack_object value, Iter&) {
+ return add_copyable(std::false_type(), L_, self, std::move(value));
+ }
+
+ static detail::error_result add_copyable(std::false_type, lua_State*, T&, stack_object) {
+ return detail::error_result("cannot call 'add' on '%s': value_type is non-copyable", detail::demangle<T>().data());
+ }
+
+ static detail::error_result insert_lookup(std::true_type, lua_State* L_, T& self, stack_object, stack_object value) {
+ // TODO: should we warn or error about someone calling insert on an ordered / lookup container with no associativity?
+ return add_copyable(std::true_type(), L_, self, std::move(value));
+ }
+
+ static detail::error_result insert_lookup(std::false_type, lua_State* L_, T& self, stack_object where, stack_object value) {
+ auto it = deferred_uc::begin(L_, self);
+ auto key = where.as<K>();
+ key = static_cast<K>(static_cast<std::ptrdiff_t>(key) + deferred_uc::index_adjustment(L_, self));
+ std::advance(it, key);
+ self.insert(it, value.as<V>());
+ return {};
+ }
+
+ static detail::error_result insert_after_has(std::true_type, lua_State* L_, T& self, stack_object where, stack_object value) {
+ auto key = where.as<K>();
+ auto backit = self.before_begin();
+ {
+ key = static_cast<K>(static_cast<std::ptrdiff_t>(key) + deferred_uc::index_adjustment(L_, self));
+ auto e = deferred_uc::end(L_, self);
+ for (auto it = deferred_uc::begin(L_, self); key > 0; ++backit, ++it, --key) {
+ if (backit == e) {
+ return detail::error_result("sol: out of bounds (too big) for set on '%s'", detail::demangle<T>().c_str());
+ }
+ }
+ }
+ self.insert_after(backit, value.as<V>());
+ return {};
+ }
+
+ static detail::error_result insert_after_has(std::false_type, lua_State*, T&, stack_object, stack_object) {
+ return detail::error_result(
+ "cannot call 'insert' on '%s': no suitable or similar functionality detected on this container", detail::demangle<T>().data());
+ }
+
+ static detail::error_result insert_has(std::true_type, lua_State* L_, T& self, stack_object key, stack_object value) {
+ return insert_lookup(meta::any<is_associative, is_lookup>(), L_, self, std::move(key), std::move(value));
+ }
+
+ static detail::error_result insert_has(std::false_type, lua_State* L_, T& self, stack_object where, stack_object value) {
+ return insert_after_has(meta::has_insert_after<T>(), L_, self, where, value);
+ }
+
+ static detail::error_result insert_copyable(std::true_type, lua_State* L_, T& self, stack_object key, stack_object value) {
+ return insert_has(std::integral_constant < bool,
+ meta::has_insert<T>::value || meta::has_insert_with_iterator<T>::value > (),
+ L_,
+ self,
+ std::move(key),
+ std::move(value));
+ }
+
+ static detail::error_result insert_copyable(std::false_type, lua_State*, T&, stack_object, stack_object) {
+ return detail::error_result("cannot call 'insert' on '%s': value_type is non-copyable", detail::demangle<T>().data());
+ }
+
+ static detail::error_result erase_integral(std::true_type, lua_State* L_, T& self, K& key) {
+ auto it = deferred_uc::begin(L_, self);
+ key = (static_cast<std::ptrdiff_t>(key) + deferred_uc::index_adjustment(L_, self));
+ std::advance(it, key);
+ self.erase(it);
+
+ return {};
+ }
+
+ static detail::error_result erase_integral(std::false_type, lua_State* L_, T& self, const K& key) {
+ auto fx = [&](const value_type& r) -> bool { return key == r; };
+ auto e = deferred_uc::end(L_, self);
+ auto it = std::find_if(deferred_uc::begin(L_, self), e, std::ref(fx));
+ if (it == e) {
+ return {};
+ }
+ self.erase(it);
+
+ return {};
+ }
+
+ static detail::error_result erase_associative_lookup(std::true_type, lua_State*, T& self, const K& key) {
+ self.erase(key);
+ return {};
+ }
+
+ static detail::error_result erase_associative_lookup(std::false_type, lua_State* L_, T& self, K& key) {
+ return erase_integral(std::is_integral<K>(), L_, self, key);
+ }
+
+ static detail::error_result erase_after_has(std::true_type, lua_State* L_, T& self, K& key) {
+ auto backit = self.before_begin();
+ {
+ key = static_cast<K>(static_cast<std::ptrdiff_t>(key) + deferred_uc::index_adjustment(L_, self));
+ auto e = deferred_uc::end(L_, self);
+ for (auto it = deferred_uc::begin(L_, self); key > 0; ++backit, ++it, --key) {
+ if (backit == e) {
+ return detail::error_result("sol: out of bounds for erase on '%s'", detail::demangle<T>().c_str());
+ }
+ }
+ }
+ self.erase_after(backit);
+ return {};
+ }
+
+ static detail::error_result erase_after_has(std::false_type, lua_State*, T&, const K&) {
+ return detail::error_result("sol: cannot call erase on '%s'", detail::demangle<T>().c_str());
+ }
+
+ static detail::error_result erase_key_has(std::true_type, lua_State* L_, T& self, K& key) {
+ return erase_associative_lookup(meta::any<is_associative, is_lookup>(), L_, self, key);
+ }
+
+ static detail::error_result erase_key_has(std::false_type, lua_State* L_, T& self, K& key) {
+ return erase_after_has(has_erase_after<T>(), L_, self, key);
+ }
+
+ static detail::error_result erase_has(std::true_type, lua_State* L_, T& self, K& key) {
+ return erase_associative_lookup(meta::any<is_associative, is_lookup>(), L_, self, key);
+ }
+
+ static detail::error_result erase_has(std::false_type, lua_State* L_, T& self, K& key) {
+ return erase_key_has(has_erase_key<T>(), L_, self, key);
+ }
+
+ static auto size_has(std::false_type, lua_State* L_, T& self) {
+ return std::distance(deferred_uc::begin(L_, self), deferred_uc::end(L_, self));
+ }
+
+ static auto size_has(std::true_type, lua_State*, T& self) {
+ return self.size();
+ }
+
+ static void clear_has(std::true_type, lua_State*, T& self) {
+ self.clear();
+ }
+
+ static void clear_has(std::false_type, lua_State* L_, T&) {
+ luaL_error(L_, "sol: cannot call clear on '%s'", detail::demangle<T>().c_str());
+ }
+
+ static bool empty_has(std::true_type, lua_State*, T& self) {
+ return self.empty();
+ }
+
+ static bool empty_has(std::false_type, lua_State* L_, T& self) {
+ return deferred_uc::begin(L_, self) == deferred_uc::end(L_, self);
+ }
+
+ static detail::error_result get_associative_find(std::true_type, lua_State* L_, T& self, K& key) {
+ auto it = self.find(key);
+ if (it == deferred_uc::end(L_, self)) {
+ stack::push(L_, lua_nil);
+ return {};
+ }
+ return get_associative(std::true_type(), L_, it);
+ }
+
+ static detail::error_result get_associative_find(std::false_type, lua_State* L_, T& self, K& key) {
+ return get_it(is_linear_integral(), L_, self, key);
+ }
+
+ static detail::error_result get_start(lua_State* L_, T& self, K& key) {
+ return get_associative_find(std::integral_constant < bool, is_associative::value&& has_find<T>::value > (), L_, self, key);
+ }
+
+ static detail::error_result set_start(lua_State* L_, T& self, stack_object key, stack_object value) {
+ return set_it(is_linear_integral(), L_, self, std::move(key), std::move(value));
+ }
+
+ static std::size_t size_start(lua_State* L_, T& self) {
+ return static_cast<std::size_t>(size_has(meta::has_size<T>(), L_, self));
+ }
+
+ static void clear_start(lua_State* L_, T& self) {
+ clear_has(has_clear<T>(), L_, self);
+ }
+
+ static bool empty_start(lua_State* L_, T& self) {
+ return empty_has(has_empty<T>(), L_, self);
+ }
+
+ static detail::error_result erase_start(lua_State* L_, T& self, K& key) {
+ return erase_has(has_erase<T>(), L_, self, key);
+ }
+
+ template <bool ip>
+ static int next_associative(std::true_type, lua_State* L_) {
+ iter& i = stack::unqualified_get<user<iter>>(L_, 1);
+ auto& it = i.it;
+ auto& end = i.end;
+ if (it == end) {
+ return stack::push(L_, lua_nil);
+ }
+ int p;
+ if constexpr (ip) {
+ ++i.index;
+ p = stack::push_reference(L_, i.index);
+ }
+ else {
+ p = stack::push_reference(L_, it->first);
+ }
+ p += stack::stack_detail::push_reference<push_type>(L_, detail::deref_move_only(it->second));
+ std::advance(it, 1);
+ return p;
+ }
+
+ template <bool>
+ static int next_associative(std::false_type, lua_State* L_) {
+ iter& i = stack::unqualified_get<user<iter>>(L_, 1);
+ auto& it = i.it();
+ auto& end = i.sen();
+ next_K k = stack::unqualified_get<next_K>(L_, 2);
+ if (it == end) {
+ return stack::push(L_, lua_nil);
+ }
+ int p;
+ if constexpr (std::is_integral_v<next_K>) {
+ p = stack::push_reference(L_, k + 1);
+ }
+ else {
+ p = stack::stack_detail::push_reference(L_, k + 1);
+ }
+ p += stack::stack_detail::push_reference<push_type>(L_, detail::deref_move_only(*it));
+ std::advance(it, 1);
+ return p;
+ }
+
+ template <bool ip>
+ static int next_iter(lua_State* L_) {
+ typedef meta::any<is_associative, meta::all<is_lookup, meta::neg<is_matched_lookup>>> is_assoc;
+ return next_associative<ip>(is_assoc(), L_);
+ }
+
+ template <bool ip>
+ static int pairs_associative(std::true_type, lua_State* L_) {
+ auto& src = get_src(L_);
+ stack::push(L_, next_iter<ip>);
+ stack::push<user<iter>>(L_, L_, 1, deferred_uc::begin(L_, src), deferred_uc::begin(L_, src));
+ stack::push(L_, lua_nil);
+ return 3;
+ }
+
+ template <bool ip>
+ static int pairs_associative(std::false_type, lua_State* L_) {
+ auto& src = get_src(L_);
+ stack::push(L_, next_iter<ip>);
+ stack::push<user<iter>>(L_, L_, 1, deferred_uc::begin(L_, src), deferred_uc::end(L_, src));
+ stack::push(L_, 0);
+ return 3;
+ }
+
+ public:
+ static int at(lua_State* L_) {
+ auto& self = get_src(L_);
+ detail::error_result er;
+ {
+ std::ptrdiff_t pos = stack::unqualified_get<std::ptrdiff_t>(L_, 2);
+ er = at_start(L_, self, pos);
+ }
+ return handle_errors(L_, er);
+ }
+
+ static int get(lua_State* L_) {
+ auto& self = get_src(L_);
+ detail::error_result er;
+ {
+ decltype(auto) key = stack::unqualified_get<K>(L_);
+ er = get_start(L_, self, key);
+ }
+ return handle_errors(L_, er);
+ }
+
+ static int index_get(lua_State* L_) {
+ return get(L_);
+ }
+
+ static int set(lua_State* L_) {
+ stack_object value = stack_object(L_, raw_index(3));
+ if constexpr (is_linear_integral::value) {
+ // for non-associative containers,
+ // erasure only happens if it is the
+ // last index in the container
+ auto key = stack::get<K>(L_, 2);
+ auto self_size = deferred_uc::size(L_);
+ if (key == static_cast<K>(self_size)) {
+ if (type_of(L_, 3) == type::lua_nil) {
+ return erase(L_);
+ }
+ }
+ }
+ else {
+ if (type_of(L_, 3) == type::lua_nil) {
+ return erase(L_);
+ }
+ }
+ auto& self = get_src(L_);
+ detail::error_result er = set_start(L_, self, stack_object(L_, raw_index(2)), std::move(value));
+ return handle_errors(L_, er);
+ }
+
+ static int index_set(lua_State* L_) {
+ return set(L_);
+ }
+
+ static int add(lua_State* L_) {
+ auto& self = get_src(L_);
+ detail::error_result er = add_copyable(is_copyable(), L_, self, stack_object(L_, raw_index(2)));
+ return handle_errors(L_, er);
+ }
+
+ static int insert(lua_State* L_) {
+ auto& self = get_src(L_);
+ detail::error_result er = insert_copyable(is_copyable(), L_, self, stack_object(L_, raw_index(2)), stack_object(L_, raw_index(3)));
+ return handle_errors(L_, er);
+ }
+
+ static int find(lua_State* L_) {
+ auto& self = get_src(L_);
+ detail::error_result er = find_has(has_find<T>(), L_, self);
+ return handle_errors(L_, er);
+ }
+
+ static int index_of(lua_State* L_) {
+ auto& self = get_src(L_);
+ detail::error_result er = find_has<true>(has_find<T>(), L_, self);
+ return handle_errors(L_, er);
+ }
+
+ static iterator begin(lua_State*, T& self) {
+ if constexpr (meta::has_begin_end_v<T>) {
+ return self.begin();
+ }
+ else {
+ using std::begin;
+ return begin(self);
+ }
+ }
+
+ static sentinel end(lua_State*, T& self) {
+ if constexpr (meta::has_begin_end_v<T>) {
+ return self.end();
+ }
+ else {
+ using std::end;
+ return end(self);
+ }
+ }
+
+ static int size(lua_State* L_) {
+ auto& self = get_src(L_);
+ std::size_t r = size_start(L_, self);
+ return stack::push(L_, r);
+ }
+
+ static int clear(lua_State* L_) {
+ auto& self = get_src(L_);
+ clear_start(L_, self);
+ return 0;
+ }
+
+ static int erase(lua_State* L_) {
+ auto& self = get_src(L_);
+ detail::error_result er;
+ {
+ decltype(auto) key = stack::unqualified_get<K>(L_, 2);
+ er = erase_start(L_, self, key);
+ }
+ return handle_errors(L_, er);
+ }
+
+ static int empty(lua_State* L_) {
+ auto& self = get_src(L_);
+ return stack::push(L_, empty_start(L_, self));
+ }
+
+ static std::ptrdiff_t index_adjustment(lua_State*, T&) {
+ return static_cast<std::ptrdiff_t>((SOL_CONTAINER_START_INDEX_I_) == 0 ? 0 : -(SOL_CONTAINER_START_INDEX_I_));
+ }
+
+ static int pairs(lua_State* L_) {
+ typedef meta::any<is_associative, meta::all<is_lookup, meta::neg<is_matched_lookup>>> is_assoc;
+ return pairs_associative<false>(is_assoc(), L_);
+ }
+
+ static int ipairs(lua_State* L_) {
+ typedef meta::any<is_associative, meta::all<is_lookup, meta::neg<is_matched_lookup>>> is_assoc;
+ return pairs_associative<true>(is_assoc(), L_);
+ }
+
+ static int next(lua_State* L_) {
+ return stack::push(L_, next_iter<false>);
+ }
+ };
+
+ template <typename X>
+ struct usertype_container_default<X, std::enable_if_t<std::is_array<std::remove_pointer_t<meta::unwrap_unqualified_t<X>>>::value>> {
+ private:
+ typedef std::remove_pointer_t<meta::unwrap_unqualified_t<X>> T;
+ typedef usertype_container<X> deferred_uc;
+
+ public:
+ typedef std::remove_extent_t<T> value_type;
+ typedef value_type* iterator;
+ typedef iterator sentinel;
+
+ private:
+ struct iter : detail::ebco<iterator, 0>, detail::ebco<sentinel, 1> {
+ using it_base = detail::ebco<iterator, 0>;
+ using sen_base = detail::ebco<sentinel, 1>;
+ reference keep_alive;
+
+ iter(lua_State* L_, int stack_index_, iterator it_, sentinel sen_) noexcept
+ : it_base(std::move(it_)), sen_base(std::move(sen_)), keep_alive(sol::main_thread(L_, L_), stack_index_) {
+ }
+
+ iterator& it() noexcept {
+ return it_base::value();
+ }
+
+ const iterator& it() const noexcept {
+ return it_base::value();
+ }
+
+ sentinel& sen() noexcept {
+ return sen_base::value();
+ }
+
+ const sentinel& sen() const noexcept {
+ return sen_base::value();
+ }
+ };
+
+ static auto& get_src(lua_State* L_) {
+ auto p = stack::unqualified_check_get<T*>(L_, 1);
+#if SOL_IS_ON(SOL_SAFE_USERTYPE)
+ if (!p) {
+ luaL_error(L_,
+ "sol: 'self' is not of type '%s' (pass 'self' as first argument with ':' or call on proper type)",
+ detail::demangle<T>().c_str());
+ }
+ if (p.value() == nullptr) {
+ luaL_error(
+ L_, "sol: 'self' argument is nil (pass 'self' as first argument with ':' or call on a '%s' type)", detail::demangle<T>().c_str());
+ }
+#endif // Safe getting with error
+ return *p.value();
+ }
+
+ static int find(std::true_type, lua_State* L_) {
+ T& self = get_src(L_);
+ decltype(auto) value = stack::unqualified_get<value_type>(L_, 2);
+ std::size_t N = std::extent<T>::value;
+ for (std::size_t idx = 0; idx < N; ++idx) {
+ using v_t = std::add_const_t<decltype(self[idx])>;
+ v_t v = self[idx];
+ if (v == value) {
+ idx = static_cast<std::size_t>(static_cast<std::ptrdiff_t>(idx) - deferred_uc::index_adjustment(L_, self));
+ return stack::push(L_, idx);
+ }
+ }
+ return stack::push(L_, lua_nil);
+ }
+
+ static int find(std::false_type, lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'find' on '%s': no supported comparison operator for the value type", detail::demangle<T>().c_str());
+ }
+
+ static int next_iter(lua_State* L_) {
+ iter& i = stack::unqualified_get<user<iter>>(L_, 1);
+ auto& it = i.it();
+ auto& end = i.sen();
+ std::size_t k = stack::unqualified_get<std::size_t>(L_, 2);
+ if (it == end) {
+ return 0;
+ }
+ int p;
+ p = stack::push(L_, k + 1);
+ p += stack::push_reference(L_, detail::deref_move_only(*it));
+ std::advance(it, 1);
+ return p;
+ }
+
+ public:
+ static int clear(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'clear' on type '%s': cannot remove all items from a fixed array", detail::demangle<T>().c_str());
+ }
+
+ static int erase(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'erase' on type '%s': cannot remove an item from fixed arrays", detail::demangle<T>().c_str());
+ }
+
+ static int add(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'add' on type '%s': cannot add to fixed arrays", detail::demangle<T>().c_str());
+ }
+
+ static int insert(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot call 'insert' on type '%s': cannot insert new entries into fixed arrays", detail::demangle<T>().c_str());
+ }
+
+ static int at(lua_State* L_) {
+ return get(L_);
+ }
+
+ static int get(lua_State* L_) {
+ T& self = get_src(L_);
+ std::ptrdiff_t idx = stack::unqualified_get<std::ptrdiff_t>(L_, 2);
+ idx += deferred_uc::index_adjustment(L_, self);
+ if (idx >= static_cast<std::ptrdiff_t>(std::extent<T>::value) || idx < 0) {
+ return stack::push(L_, lua_nil);
+ }
+ return stack::push_reference(L_, detail::deref_move_only(self[idx]));
+ }
+
+ static int index_get(lua_State* L_) {
+ return get(L_);
+ }
+
+ static int set(lua_State* L_) {
+ T& self = get_src(L_);
+ std::ptrdiff_t idx = stack::unqualified_get<std::ptrdiff_t>(L_, 2);
+ idx += deferred_uc::index_adjustment(L_, self);
+ if (idx >= static_cast<std::ptrdiff_t>(std::extent<T>::value)) {
+ return luaL_error(L_, "sol: index out of bounds (too big) for set on '%s'", detail::demangle<T>().c_str());
+ }
+ if (idx < 0) {
+ return luaL_error(L_, "sol: index out of bounds (too small) for set on '%s'", detail::demangle<T>().c_str());
+ }
+ self[idx] = stack::unqualified_get<value_type>(L_, 3);
+ return 0;
+ }
+
+ static int index_set(lua_State* L_) {
+ return set(L_);
+ }
+
+ static int index_of(lua_State* L_) {
+ return find(L_);
+ }
+
+ static int find(lua_State* L_) {
+ return find(meta::supports_op_equal<value_type, value_type>(), L_);
+ }
+
+ static int size(lua_State* L_) {
+ return stack::push(L_, std::extent<T>::value);
+ }
+
+ static int empty(lua_State* L_) {
+ return stack::push(L_, std::extent<T>::value > 0);
+ }
+
+ static int pairs(lua_State* L_) {
+ auto& src = get_src(L_);
+ stack::push(L_, next_iter);
+ stack::push<user<iter>>(L_, L_, 1, deferred_uc::begin(L_, src), deferred_uc::end(L_, src));
+ stack::push(L_, 0);
+ return 3;
+ }
+
+ static int ipairs(lua_State* L_) {
+ return pairs(L_);
+ }
+
+ static int next(lua_State* L_) {
+ return stack::push(L_, next_iter);
+ }
+
+ static std::ptrdiff_t index_adjustment(lua_State*, T&) {
+ return (SOL_CONTAINER_START_INDEX_I_) == 0 ? 0 : -(SOL_CONTAINER_START_INDEX_I_);
+ }
+
+ static iterator begin(lua_State*, T& self) {
+ return std::addressof(self[0]);
+ }
+
+ static sentinel end(lua_State*, T& self) {
+ return std::addressof(self[0]) + std::extent<T>::value;
+ }
+ };
+
+ template <typename X>
+ struct usertype_container_default<usertype_container<X>> : usertype_container_default<X> { };
+ } // namespace container_detail
+
+ template <typename T>
+ struct usertype_container : container_detail::usertype_container_default<T> { };
+
+} // namespace sol
+
+// end of sol/usertype_container.hpp
+
+#include <unordered_map>
+
+namespace sol {
+
+ namespace container_detail {
+ template <typename X>
+ struct u_c_launch {
+ using T = std::remove_pointer_t<meta::unqualified_t<X>>;
+ using uc = usertype_container<T>;
+ using default_uc = usertype_container_default<T>;
+
+ static inline int real_index_get_traits(std::true_type, lua_State* L) {
+ return uc::index_get(L);
+ }
+
+ static inline int real_index_get_traits(std::false_type, lua_State* L) {
+ return default_uc::index_get(L);
+ }
+
+ static inline int real_index_call(lua_State* L) {
+ static const std::unordered_map<string_view, lua_CFunction> calls {
+ { "at", &real_at_call },
+ { "get", &real_get_call },
+ { "set", &real_set_call },
+ { "size", &real_length_call },
+ { "add", &real_add_call },
+ { "empty", &real_empty_call },
+ { "insert", &real_insert_call },
+ { "clear", &real_clear_call },
+ { "find", &real_find_call },
+ { "index_of", &real_index_of_call },
+ { "erase", &real_erase_call },
+ { "pairs", &pairs_call },
+ { "next", &next_call },
+ };
+ auto maybenameview = stack::unqualified_check_get<string_view>(L, 2);
+ if (maybenameview) {
+ const string_view& name = *maybenameview;
+ auto it = calls.find(name);
+ if (it != calls.cend()) {
+ return stack::push(L, it->second);
+ }
+ }
+ return real_index_get_traits(container_detail::has_traits_index_get<uc>(), L);
+ }
+
+ static inline int real_at_traits(std::true_type, lua_State* L) {
+ return uc::at(L);
+ }
+
+ static inline int real_at_traits(std::false_type, lua_State* L) {
+ return default_uc::at(L);
+ }
+
+ static inline int real_at_call(lua_State* L) {
+ return real_at_traits(container_detail::has_traits_at<uc>(), L);
+ }
+
+ static inline int real_get_traits(std::true_type, lua_State* L) {
+ return uc::get(L);
+ }
+
+ static inline int real_get_traits(std::false_type, lua_State* L) {
+ return default_uc::get(L);
+ }
+
+ static inline int real_get_call(lua_State* L) {
+ return real_get_traits(container_detail::has_traits_get<uc>(), L);
+ }
+
+ static inline int real_set_traits(std::true_type, lua_State* L) {
+ return uc::set(L);
+ }
+
+ static inline int real_set_traits(std::false_type, lua_State* L) {
+ return default_uc::set(L);
+ }
+
+ static inline int real_set_call(lua_State* L) {
+ return real_set_traits(container_detail::has_traits_set<uc>(), L);
+ }
+
+ static inline int real_index_set_traits(std::true_type, lua_State* L) {
+ return uc::index_set(L);
+ }
+
+ static inline int real_index_set_traits(std::false_type, lua_State* L) {
+ return default_uc::index_set(L);
+ }
+
+ static inline int real_new_index_call(lua_State* L) {
+ return real_index_set_traits(container_detail::has_traits_index_set<uc>(), L);
+ }
+
+ static inline int real_pairs_traits(std::true_type, lua_State* L) {
+ return uc::pairs(L);
+ }
+
+ static inline int real_pairs_traits(std::false_type, lua_State* L) {
+ return default_uc::pairs(L);
+ }
+
+ static inline int real_pairs_call(lua_State* L) {
+ return real_pairs_traits(container_detail::has_traits_pairs<uc>(), L);
+ }
+
+ static inline int real_ipairs_traits(std::true_type, lua_State* L) {
+ return uc::ipairs(L);
+ }
+
+ static inline int real_ipairs_traits(std::false_type, lua_State* L) {
+ return default_uc::ipairs(L);
+ }
+
+ static inline int real_ipairs_call(lua_State* L) {
+ return real_ipairs_traits(container_detail::has_traits_ipairs<uc>(), L);
+ }
+
+ static inline int real_next_traits(std::true_type, lua_State* L) {
+ return uc::next(L);
+ }
+
+ static inline int real_next_traits(std::false_type, lua_State* L) {
+ return default_uc::next(L);
+ }
+
+ static inline int real_next_call(lua_State* L) {
+ return real_next_traits(container_detail::has_traits_next<uc>(), L);
+ }
+
+ static inline int real_size_traits(std::true_type, lua_State* L) {
+ return uc::size(L);
+ }
+
+ static inline int real_size_traits(std::false_type, lua_State* L) {
+ return default_uc::size(L);
+ }
+
+ static inline int real_length_call(lua_State* L) {
+ return real_size_traits(container_detail::has_traits_size<uc>(), L);
+ }
+
+ static inline int real_add_traits(std::true_type, lua_State* L) {
+ return uc::add(L);
+ }
+
+ static inline int real_add_traits(std::false_type, lua_State* L) {
+ return default_uc::add(L);
+ }
+
+ static inline int real_add_call(lua_State* L) {
+ return real_add_traits(container_detail::has_traits_add<uc>(), L);
+ }
+
+ static inline int real_insert_traits(std::true_type, lua_State* L) {
+ return uc::insert(L);
+ }
+
+ static inline int real_insert_traits(std::false_type, lua_State* L) {
+ return default_uc::insert(L);
+ }
+
+ static inline int real_insert_call(lua_State* L) {
+ return real_insert_traits(container_detail::has_traits_insert<uc>(), L);
+ }
+
+ static inline int real_clear_traits(std::true_type, lua_State* L) {
+ return uc::clear(L);
+ }
+
+ static inline int real_clear_traits(std::false_type, lua_State* L) {
+ return default_uc::clear(L);
+ }
+
+ static inline int real_clear_call(lua_State* L) {
+ return real_clear_traits(container_detail::has_traits_clear<uc>(), L);
+ }
+
+ static inline int real_empty_traits(std::true_type, lua_State* L) {
+ return uc::empty(L);
+ }
+
+ static inline int real_empty_traits(std::false_type, lua_State* L) {
+ return default_uc::empty(L);
+ }
+
+ static inline int real_empty_call(lua_State* L) {
+ return real_empty_traits(container_detail::has_traits_empty<uc>(), L);
+ }
+
+ static inline int real_erase_traits(std::true_type, lua_State* L) {
+ return uc::erase(L);
+ }
+
+ static inline int real_erase_traits(std::false_type, lua_State* L) {
+ return default_uc::erase(L);
+ }
+
+ static inline int real_erase_call(lua_State* L) {
+ return real_erase_traits(container_detail::has_traits_erase<uc>(), L);
+ }
+
+ static inline int real_find_traits(std::true_type, lua_State* L) {
+ return uc::find(L);
+ }
+
+ static inline int real_find_traits(std::false_type, lua_State* L) {
+ return default_uc::find(L);
+ }
+
+ static inline int real_find_call(lua_State* L) {
+ return real_find_traits(container_detail::has_traits_find<uc>(), L);
+ }
+
+ static inline int real_index_of_call(lua_State* L) {
+ if constexpr (container_detail::has_traits_index_of<uc>()) {
+ return uc::index_of(L);
+ }
+ else {
+ return default_uc::index_of(L);
+ }
+ }
+
+ static inline int add_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_add_call), (&real_add_call)>(L);
+ }
+
+ static inline int erase_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_erase_call), (&real_erase_call)>(L);
+ }
+
+ static inline int insert_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_insert_call), (&real_insert_call)>(L);
+ }
+
+ static inline int clear_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_clear_call), (&real_clear_call)>(L);
+ }
+
+ static inline int empty_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_empty_call), (&real_empty_call)>(L);
+ }
+
+ static inline int find_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_find_call), (&real_find_call)>(L);
+ }
+
+ static inline int index_of_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_index_of_call), (&real_index_of_call)>(L);
+ }
+
+ static inline int length_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_length_call), (&real_length_call)>(L);
+ }
+
+ static inline int pairs_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_pairs_call), (&real_pairs_call)>(L);
+ }
+
+ static inline int ipairs_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_ipairs_call), (&real_ipairs_call)>(L);
+ }
+
+ static inline int next_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_next_call), (&real_next_call)>(L);
+ }
+
+ static inline int at_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_at_call), (&real_at_call)>(L);
+ }
+
+ static inline int get_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_get_call), (&real_get_call)>(L);
+ }
+
+ static inline int set_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_set_call), (&real_set_call)>(L);
+ }
+
+ static inline int index_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_index_call), (&real_index_call)>(L);
+ }
+
+ static inline int new_index_call(lua_State* L) {
+ return detail::typed_static_trampoline<decltype(&real_new_index_call), (&real_new_index_call)>(L);
+ }
+ };
+ } // namespace container_detail
+
+ namespace stack {
+ namespace stack_detail {
+ template <typename T, bool is_shim = false>
+ struct metatable_setup {
+ lua_State* L;
+
+ metatable_setup(lua_State* L) : L(L) {
+ }
+
+ void operator()() {
+ using meta_usertype_container
+ = container_detail::u_c_launch<meta::conditional_t<is_shim, as_container_t<std::remove_pointer_t<T>>, std::remove_pointer_t<T>>>;
+ static const char* metakey
+ = is_shim ? &usertype_traits<as_container_t<std::remove_pointer_t<T>>>::metatable()[0] : &usertype_traits<T>::metatable()[0];
+ static const std::array<luaL_Reg, 20> reg = { {
+ // clang-format off
+ { "__pairs", &meta_usertype_container::pairs_call },
+ { "__ipairs", &meta_usertype_container::ipairs_call },
+ { "__len", &meta_usertype_container::length_call },
+ { "__index", &meta_usertype_container::index_call },
+ { "__newindex", &meta_usertype_container::new_index_call },
+ { "pairs", &meta_usertype_container::pairs_call },
+ { "next", &meta_usertype_container::next_call },
+ { "at", &meta_usertype_container::at_call },
+ { "get", &meta_usertype_container::get_call },
+ { "set", &meta_usertype_container::set_call },
+ { "size", &meta_usertype_container::length_call },
+ { "empty", &meta_usertype_container::empty_call },
+ { "clear", &meta_usertype_container::clear_call },
+ { "insert", &meta_usertype_container::insert_call },
+ { "add", &meta_usertype_container::add_call },
+ { "find", &meta_usertype_container::find_call },
+ { "index_of", &meta_usertype_container::index_of_call },
+ { "erase", &meta_usertype_container::erase_call },
+ std::is_pointer<T>::value ? luaL_Reg{ nullptr, nullptr } : luaL_Reg{ "__gc", &detail::usertype_alloc_destroy<T> },
+ { nullptr, nullptr }
+ // clang-format on
+ } };
+
+ if (luaL_newmetatable(L, metakey) == 1) {
+ luaL_setfuncs(L, reg.data(), 0);
+ }
+ lua_setmetatable(L, -2);
+ }
+ };
+ } // namespace stack_detail
+
+ template <typename T>
+ struct unqualified_pusher<as_container_t<T>> {
+ using C = meta::unqualified_t<T>;
+
+ static int push_lvalue(std::true_type, lua_State* L, const C& cont) {
+ stack_detail::metatable_setup<C*, true> fx(L);
+ return stack::push<detail::as_pointer_tag<const C>>(L, detail::with_function_tag(), fx, detail::ptr(cont));
+ }
+
+ static int push_lvalue(std::false_type, lua_State* L, const C& cont) {
+ stack_detail::metatable_setup<C, true> fx(L);
+ return stack::push<detail::as_value_tag<C>>(L, detail::with_function_tag(), fx, cont);
+ }
+
+ static int push_rvalue(std::true_type, lua_State* L, C&& cont) {
+ stack_detail::metatable_setup<C, true> fx(L);
+ return stack::push<detail::as_value_tag<C>>(L, detail::with_function_tag(), fx, std::move(cont));
+ }
+
+ static int push_rvalue(std::false_type, lua_State* L, const C& cont) {
+ return push_lvalue(std::is_lvalue_reference<T>(), L, cont);
+ }
+
+ static int push(lua_State* L, const as_container_t<T>& as_cont) {
+ return push_lvalue(std::is_lvalue_reference<T>(), L, as_cont.value());
+ }
+
+ static int push(lua_State* L, as_container_t<T>&& as_cont) {
+ return push_rvalue(meta::all<std::is_rvalue_reference<T>, meta::neg<std::is_lvalue_reference<T>>>(), L, std::forward<T>(as_cont.value()));
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<as_container_t<T*>> {
+ using C = std::add_pointer_t<meta::unqualified_t<std::remove_pointer_t<T>>>;
+
+ static int push(lua_State* L, T* cont) {
+ stack_detail::metatable_setup<C> fx(L);
+ return stack::push<detail::as_pointer_tag<T>>(L, detail::with_function_tag(), fx, cont);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<T, std::enable_if_t<is_container_v<T>>> {
+ using C = T;
+
+ template <typename... Args>
+ static int push(lua_State* L, Args&&... args) {
+ stack_detail::metatable_setup<C> fx(L);
+ return stack::push<detail::as_value_tag<T>>(L, detail::with_function_tag(), fx, std::forward<Args>(args)...);
+ }
+ };
+
+ template <typename T>
+ struct unqualified_pusher<T*, std::enable_if_t<is_container_v<T>>> {
+ using C = std::add_pointer_t<meta::unqualified_t<std::remove_pointer_t<T>>>;
+
+ static int push(lua_State* L, T* cont) {
+ stack_detail::metatable_setup<C> fx(L);
+ return stack::push<detail::as_pointer_tag<T>>(L, detail::with_function_tag(), fx, cont);
+ }
+ };
+ } // namespace stack
+
+} // namespace sol
+
+// end of sol/usertype_container_launch.hpp
+
+#include <sstream>
+#include <type_traits>
+
+namespace sol {
+ namespace u_detail {
+ constexpr const lua_Integer toplevel_magic = static_cast<lua_Integer>(0xCCC2CCC1);
+
+ constexpr const int environment_index = 1;
+ constexpr const int usertype_storage_index = 2;
+ constexpr const int usertype_storage_base_index = 3;
+ constexpr const int exact_function_index = 4;
+ constexpr const int magic_index = 5;
+
+ constexpr const int simple_usertype_storage_index = 2;
+ constexpr const int index_function_index = 3;
+ constexpr const int new_index_function_index = 4;
+
+ constexpr const int base_walking_failed_index = -32467;
+ constexpr const int lookup_failed_index = -42469;
+
+ enum class submetatable_type {
+ // must be sequential
+ value,
+ reference,
+ unique,
+ const_reference,
+ const_value,
+ // must be LAST!
+ named
+ };
+
+ inline auto make_string_view(string_view s) {
+ return s;
+ }
+
+#if SOL_IS_ON(SOL_CHAR8_T)
+ inline auto make_string_view(const char8_t* s) {
+ return string_view(reinterpret_cast<const char*>(s));
+ }
+#endif
+
+ inline auto make_string_view(call_construction) {
+ return string_view(to_string(meta_function::call_function));
+ }
+
+ inline auto make_string_view(meta_function mf) {
+ return string_view(to_string(mf));
+ }
+
+ inline auto make_string_view(base_classes_tag) {
+ return string_view(detail::base_class_cast_key());
+ }
+
+ template <typename Arg>
+ inline std::string make_string(Arg&& arg) {
+ string_view s = make_string_view(arg);
+ return std::string(s.data(), s.size());
+ }
+
+ inline int is_indexer(string_view s) {
+ if (s == to_string(meta_function::index)) {
+ return 1;
+ }
+ else if (s == to_string(meta_function::new_index)) {
+ return 2;
+ }
+ return 0;
+ }
+
+ inline int is_indexer(meta_function mf) {
+ if (mf == meta_function::index) {
+ return 1;
+ }
+ else if (mf == meta_function::new_index) {
+ return 2;
+ }
+ return 0;
+ }
+
+ inline int is_indexer(call_construction) {
+ return 0;
+ }
+ } // namespace u_detail
+
+ namespace detail {
+
+ template <typename T, typename IFx, typename Fx>
+ inline void insert_default_registrations(IFx&& ifx, Fx&& fx) {
+ (void)ifx;
+ (void)fx;
+ if constexpr (is_automagical<T>::value) {
+ if (fx(meta_function::less_than)) {
+ if constexpr (meta::supports_op_less<T>::value) {
+ lua_CFunction f = &comparsion_operator_wrap<T, std::less<>>;
+ ifx(meta_function::less_than, f);
+ }
+ }
+ if (fx(meta_function::less_than_or_equal_to)) {
+ if constexpr (meta::supports_op_less_equal<T>::value) {
+ lua_CFunction f = &comparsion_operator_wrap<T, std::less_equal<>>;
+ ifx(meta_function::less_than_or_equal_to, f);
+ }
+ }
+ if (fx(meta_function::equal_to)) {
+ if constexpr (meta::supports_op_equal<T>::value) {
+ lua_CFunction f = &comparsion_operator_wrap<T, std::equal_to<>>;
+ ifx(meta_function::equal_to, f);
+ }
+ else {
+ lua_CFunction f = &comparsion_operator_wrap<T, no_comp>;
+ ifx(meta_function::equal_to, f);
+ }
+ }
+ if (fx(meta_function::pairs)) {
+ ifx(meta_function::pairs, &container_detail::u_c_launch<as_container_t<T>>::pairs_call);
+ }
+ if (fx(meta_function::length)) {
+ if constexpr (meta::has_size<const T>::value || meta::has_size<T>::value) {
+ auto f = &default_size<T>;
+ ifx(meta_function::length, f);
+ }
+ }
+ if (fx(meta_function::to_string)) {
+ if constexpr (is_to_stringable_v<T>) {
+ if constexpr (!meta::is_probably_stateless_lambda_v<T> && !std::is_member_pointer_v<T>) {
+ auto f = &detail::static_trampoline<&default_to_string<T>>;
+ ifx(meta_function::to_string, f);
+ }
+ }
+ }
+ if (fx(meta_function::call_function)) {
+ if constexpr (is_callable_v<T>) {
+ if constexpr (meta::call_operator_deducible_v<T>) {
+ auto f = &c_call<decltype(&T::operator()), &T::operator()>;
+ ifx(meta_function::call_function, f);
+ }
+ }
+ }
+ }
+ }
+ } // namespace detail
+
+ namespace stack { namespace stack_detail {
+ template <typename X>
+ void set_undefined_methods_on(stack_reference t) {
+ using T = std::remove_pointer_t<X>;
+
+ lua_State* L = t.lua_state();
+
+ t.push();
+
+ detail::lua_reg_table l {};
+ int index = 0;
+ detail::indexed_insert insert_fx(l, index);
+ detail::insert_default_registrations<T>(insert_fx, detail::property_always_true);
+ if constexpr (!std::is_pointer_v<X>) {
+ l[index] = luaL_Reg { to_string(meta_function::garbage_collect).c_str(), detail::make_destructor<T>() };
+ }
+ luaL_setfuncs(L, l, 0);
+
+ // __type table
+ lua_createtable(L, 0, 2);
+ const std::string& name = detail::demangle<T>();
+ lua_pushlstring(L, name.c_str(), name.size());
+ lua_setfield(L, -2, "name");
+ lua_CFunction is_func = &detail::is_check<T>;
+ lua_pushcclosure(L, is_func, 0);
+ lua_setfield(L, -2, "is");
+ lua_setfield(L, t.stack_index(), to_string(meta_function::type).c_str());
+
+ t.pop();
+ }
+ }} // namespace stack::stack_detail
+} // namespace sol
+
+// end of sol/usertype_core.hpp
+
+// beginning of sol/usertype_storage.hpp
+
+#include <bitset>
+#include <unordered_map>
+#include <memory>
+
+namespace sol { namespace u_detail {
+
+ struct usertype_storage_base;
+ template <typename T>
+ struct usertype_storage;
+
+ optional<usertype_storage_base&> maybe_get_usertype_storage_base(lua_State* L_, int index);
+ usertype_storage_base& get_usertype_storage_base(lua_State* L_, const char* gcmetakey);
+ template <typename T>
+ optional<usertype_storage<T>&> maybe_get_usertype_storage(lua_State* L_);
+ template <typename T>
+ usertype_storage<T>& get_usertype_storage(lua_State* L_);
+
+ using index_call_function = int(lua_State*, void*);
+ using change_indexing_mem_func = void (usertype_storage_base::*)(
+ lua_State*, submetatable_type, void*, stateless_stack_reference&, lua_CFunction, lua_CFunction, lua_CFunction, lua_CFunction);
+
+ struct index_call_storage {
+ index_call_function* index;
+ index_call_function* new_index;
+ void* binding_data;
+ };
+
+ struct new_index_call_storage : index_call_storage {
+ void* new_binding_data;
+ };
+
+ struct binding_base {
+ virtual void* data() = 0;
+ virtual ~binding_base() {
+ }
+ };
+
+ template <typename K, typename Fq, typename T = void>
+ struct binding : binding_base {
+ using uF = meta::unqualified_t<Fq>;
+ using F = meta::conditional_t<meta::is_c_str_of_v<uF, char>
+#if SOL_IS_ON(SOL_CHAR8_T)
+ || meta::is_c_str_of_v<uF, char8_t>
+#endif
+ || meta::is_c_str_of_v<uF, char16_t> || meta::is_c_str_of_v<uF, char32_t> || meta::is_c_str_of_v<uF, wchar_t>,
+ std::add_pointer_t<std::add_const_t<std::remove_all_extents_t<Fq>>>, std::decay_t<Fq>>;
+ F data_;
+
+ template <typename... Args>
+ binding(Args&&... args) : data_(std::forward<Args>(args)...) {
+ }
+
+ virtual void* data() override {
+ return static_cast<void*>(std::addressof(data_));
+ }
+
+ template <bool is_index = true, bool is_variable = false>
+ static inline int call_with_(lua_State* L_, void* target) {
+ constexpr int boost = !detail::is_non_factory_constructor<F>::value && std::is_same<K, call_construction>::value ? 1 : 0;
+ auto& f = *static_cast<F*>(target);
+ return call_detail::call_wrapped<T, is_index, is_variable, boost>(L_, f);
+ }
+
+ template <bool is_index = true, bool is_variable = false>
+ static inline int call_(lua_State* L_) {
+ void* f = stack::get<void*>(L_, upvalue_index(usertype_storage_index));
+ return call_with_<is_index, is_variable>(L_, f);
+ }
+
+ template <bool is_index = true, bool is_variable = false>
+ static inline int call(lua_State* L_) {
+ int r = detail::typed_static_trampoline<decltype(&call_<is_index, is_variable>), (&call_<is_index, is_variable>)>(L_);
+ if constexpr (meta::is_specialization_of_v<uF, yielding_t>) {
+ return lua_yield(L_, r);
+ }
+ else {
+ return r;
+ }
+ }
+
+ template <bool is_index = true, bool is_variable = false>
+ static inline int index_call_with_(lua_State* L_, void* target) {
+ if constexpr (!is_variable) {
+ if constexpr (is_lua_c_function_v<std::decay_t<F>>) {
+ auto& f = *static_cast<std::decay_t<F>*>(target);
+ return stack::push(L_, f);
+ }
+ else {
+ // set up upvalues
+ // for a chained call
+ int upvalues = 0;
+ upvalues += stack::push(L_, nullptr);
+ upvalues += stack::push(L_, target);
+ auto cfunc = &call<is_index, is_variable>;
+ return stack::push(L_, c_closure(cfunc, upvalues));
+ }
+ }
+ else {
+ constexpr int boost = !detail::is_non_factory_constructor<F>::value && std::is_same<K, call_construction>::value ? 1 : 0;
+ auto& f = *static_cast<F*>(target);
+ return call_detail::call_wrapped<T, is_index, is_variable, boost>(L_, f);
+ }
+ }
+
+ template <bool is_index = true, bool is_variable = false>
+ static inline int index_call_(lua_State* L_) {
+ void* f = stack::get<void*>(L_, upvalue_index(usertype_storage_index));
+ return index_call_with_<is_index, is_variable>(L_, f);
+ }
+
+ template <bool is_index = true, bool is_variable = false>
+ static inline int index_call(lua_State* L_) {
+ int r = detail::typed_static_trampoline<decltype(&index_call_<is_index, is_variable>), (&index_call_<is_index, is_variable>)>(L_);
+ if constexpr (meta::is_specialization_of_v<uF, yielding_t>) {
+ return lua_yield(L_, r);
+ }
+ else {
+ return r;
+ }
+ }
+ };
+
+ inline int index_fail(lua_State* L_) {
+ if (lua_getmetatable(L_, 1) == 1) {
+ int metatarget = lua_gettop(L_);
+ stack::get_field<false, true>(L_, stack_reference(L_, raw_index(2)), metatarget);
+ return 1;
+ }
+ // With runtime extensibility, we can't
+ // hard-error things. They have to
+ // return nil, like regular table types
+ return stack::push(L_, lua_nil);
+ }
+
+ inline int index_target_fail(lua_State* L_, void*) {
+ return index_fail(L_);
+ }
+
+ inline int new_index_fail(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot set (new_index) into this object: no defined new_index operation on usertype");
+ }
+
+ inline int new_index_target_fail(lua_State* L_, void*) {
+ return new_index_fail(L_);
+ }
+
+ struct string_for_each_metatable_func {
+ bool is_destruction = false;
+ bool is_index = false;
+ bool is_new_index = false;
+ bool is_static_index = false;
+ bool is_static_new_index = false;
+ bool poison_indexing = false;
+ bool is_unqualified_lua_CFunction = false;
+ bool is_unqualified_lua_reference = false;
+ std::string* p_key = nullptr;
+ reference* p_binding_ref = nullptr;
+ lua_CFunction call_func = nullptr;
+ index_call_storage* p_ics = nullptr;
+ usertype_storage_base* p_usb = nullptr;
+ void* p_derived_usb = nullptr;
+ lua_CFunction idx_call = nullptr, new_idx_call = nullptr, meta_idx_call = nullptr, meta_new_idx_call = nullptr;
+ change_indexing_mem_func change_indexing;
+
+ void operator()(lua_State* L_, submetatable_type smt_, stateless_reference& fast_index_table_) {
+ std::string& key = *p_key;
+ usertype_storage_base& usb = *p_usb;
+ index_call_storage& ics = *p_ics;
+
+ if (smt_ == submetatable_type::named) {
+ // do not override __call or
+ // other specific meta functions on named metatable:
+ // we need that for call construction
+ // and other amenities
+ return;
+ }
+ int fast_index_table_push = fast_index_table_.push(L_);
+ stateless_stack_reference t(L_, -fast_index_table_push);
+ if (poison_indexing) {
+ (usb.*change_indexing)(L_, smt_, p_derived_usb, t, idx_call, new_idx_call, meta_idx_call, meta_new_idx_call);
+ }
+ if (is_destruction
+ && (smt_ == submetatable_type::reference || smt_ == submetatable_type::const_reference || smt_ == submetatable_type::named
+ || smt_ == submetatable_type::unique)) {
+ // gc does not apply to us here
+ // for reference types (raw T*, std::ref)
+ // for the named metatable itself,
+ // or for unique_usertypes, which do their own custom destroyion
+ t.pop(L_);
+ return;
+ }
+ if (is_index || is_new_index || is_static_index || is_static_new_index) {
+ // do not serialize the new_index and index functions here directly
+ // we control those...
+ t.pop(L_);
+ return;
+ }
+ if (is_unqualified_lua_CFunction) {
+ stack::set_field<false, true>(L_, key, call_func, t.stack_index());
+ }
+ else if (is_unqualified_lua_reference) {
+ reference& binding_ref = *p_binding_ref;
+ stack::set_field<false, true>(L_, key, binding_ref, t.stack_index());
+ }
+ else {
+ stack::set_field<false, true>(L_, key, make_closure(call_func, nullptr, ics.binding_data), t.stack_index());
+ }
+ t.pop(L_);
+ }
+ };
+
+ struct lua_reference_func {
+ reference key;
+ reference value;
+
+ void operator()(lua_State* L_, submetatable_type smt_, stateless_reference& fast_index_table_) {
+ if (smt_ == submetatable_type::named) {
+ return;
+ }
+ int fast_index_table_push = fast_index_table_.push(L_);
+ stateless_stack_reference t(L_, -fast_index_table_push);
+ stack::set_field<false, true>(L_, key, value, t.stack_index());
+ t.pop(L_);
+ }
+ };
+
+ struct update_bases_func {
+ detail::inheritance_check_function base_class_check_func;
+ detail::inheritance_cast_function base_class_cast_func;
+ lua_CFunction idx_call, new_idx_call, meta_idx_call, meta_new_idx_call;
+ usertype_storage_base* p_usb;
+ void* p_derived_usb;
+ change_indexing_mem_func change_indexing;
+
+ void operator()(lua_State* L_, submetatable_type smt_, stateless_reference& fast_index_table_) {
+ int fast_index_table_push = fast_index_table_.push(L_);
+ stateless_stack_reference t(L_, -fast_index_table_push);
+ stack::set_field(L_, detail::base_class_check_key(), reinterpret_cast<void*>(base_class_check_func), t.stack_index());
+ stack::set_field(L_, detail::base_class_cast_key(), reinterpret_cast<void*>(base_class_cast_func), t.stack_index());
+ // change indexing, forcefully
+ (p_usb->*change_indexing)(L_, smt_, p_derived_usb, t, idx_call, new_idx_call, meta_idx_call, meta_new_idx_call);
+ t.pop(L_);
+ }
+ };
+
+ struct binding_data_equals {
+ void* binding_data;
+
+ binding_data_equals(void* b) : binding_data(b) {
+ }
+
+ bool operator()(const std::unique_ptr<binding_base>& ptr) const {
+ return binding_data == ptr->data();
+ }
+ };
+
+ struct usertype_storage_base {
+ public:
+ lua_State* m_L;
+ std::vector<std::unique_ptr<binding_base>> storage;
+ std::vector<std::unique_ptr<char[]>> string_keys_storage;
+ std::unordered_map<string_view, index_call_storage> string_keys;
+ std::unordered_map<stateless_reference, stateless_reference, stateless_reference_hash, stateless_reference_equals> auxiliary_keys;
+ stateless_reference value_index_table;
+ stateless_reference reference_index_table;
+ stateless_reference unique_index_table;
+ stateless_reference const_reference_index_table;
+ stateless_reference const_value_index_table;
+ stateless_reference named_index_table;
+ stateless_reference type_table;
+ stateless_reference gc_names_table;
+ stateless_reference named_metatable;
+ new_index_call_storage base_index;
+ new_index_call_storage static_base_index;
+ bool is_using_index;
+ bool is_using_new_index;
+ std::bitset<64> properties;
+
+ usertype_storage_base(lua_State* L_)
+ : m_L(L_)
+ , storage()
+ , string_keys_storage()
+ , string_keys()
+ , auxiliary_keys(0, stateless_reference_hash(L_), stateless_reference_equals(L_))
+ , value_index_table()
+ , reference_index_table()
+ , unique_index_table()
+ , const_reference_index_table()
+ , const_value_index_table()
+ , named_index_table()
+ , type_table(make_reference<stateless_reference>(L_, create))
+ , gc_names_table(make_reference<stateless_reference>(L_, create))
+ , named_metatable(make_reference<stateless_reference>(L_, create))
+ , base_index()
+ , static_base_index()
+ , is_using_index(false)
+ , is_using_new_index(false)
+ , properties() {
+ base_index.binding_data = nullptr;
+ base_index.index = index_target_fail;
+ base_index.new_index = new_index_target_fail;
+ base_index.new_binding_data = nullptr;
+ static_base_index.binding_data = nullptr;
+ static_base_index.index = index_target_fail;
+ static_base_index.new_binding_data = this;
+ static_base_index.new_index = new_index_target_set;
+ }
+
+ template <typename Fx>
+ void for_each_table(lua_State* L_, Fx&& fx) {
+ for (int i = 0; i < 6; ++i) {
+ submetatable_type smt = static_cast<submetatable_type>(i);
+ stateless_reference* p_fast_index_table = nullptr;
+ switch (smt) {
+ case submetatable_type::const_value:
+ p_fast_index_table = &this->const_value_index_table;
+ break;
+ case submetatable_type::reference:
+ p_fast_index_table = &this->reference_index_table;
+ break;
+ case submetatable_type::unique:
+ p_fast_index_table = &this->unique_index_table;
+ break;
+ case submetatable_type::const_reference:
+ p_fast_index_table = &this->const_reference_index_table;
+ break;
+ case submetatable_type::named:
+ p_fast_index_table = &this->named_index_table;
+ break;
+ case submetatable_type::value:
+ default:
+ p_fast_index_table = &this->value_index_table;
+ break;
+ }
+ fx(L_, smt, *p_fast_index_table);
+ }
+ }
+
+ void add_entry(string_view sv, index_call_storage ics) {
+ string_keys_storage.emplace_back(new char[sv.size()]);
+ std::unique_ptr<char[]>& sv_storage = string_keys_storage.back();
+ std::memcpy(sv_storage.get(), sv.data(), sv.size());
+ string_view stored_sv(sv_storage.get(), sv.size());
+ string_keys.insert_or_assign(std::move(stored_sv), std::move(ics));
+ }
+
+ template <typename T, typename... Bases>
+ void update_bases(lua_State* L_, bases<Bases...>) {
+ static_assert(sizeof(void*) <= sizeof(detail::inheritance_check_function),
+ "The size of this data pointer is too small to fit the inheritance checking function: Please file "
+ "a bug report.");
+ static_assert(sizeof(void*) <= sizeof(detail::inheritance_cast_function),
+ "The size of this data pointer is too small to fit the inheritance checking function: Please file "
+ "a bug report.");
+ static_assert(!meta::any_same<T, Bases...>::value, "base classes cannot list the original class as part of the bases");
+ if constexpr (sizeof...(Bases) > 0) {
+ (void)detail::swallow { 0, ((weak_derive<Bases>::value = true), 0)... };
+
+ void* derived_this = static_cast<void*>(static_cast<usertype_storage<T>*>(this));
+
+ update_bases_func for_each_fx;
+ for_each_fx.base_class_check_func = &detail::inheritance<T>::template type_check_with<Bases...>;
+ for_each_fx.base_class_cast_func = &detail::inheritance<T>::template type_cast_with<Bases...>;
+ for_each_fx.idx_call = &usertype_storage<T>::template index_call_with_bases<false, Bases...>;
+ for_each_fx.new_idx_call = &usertype_storage<T>::template index_call_with_bases<true, Bases...>;
+ for_each_fx.meta_idx_call = &usertype_storage<T>::template meta_index_call_with_bases<false, Bases...>;
+ for_each_fx.meta_new_idx_call = &usertype_storage<T>::template meta_index_call_with_bases<true, Bases...>;
+ for_each_fx.p_usb = this;
+ for_each_fx.p_derived_usb = derived_this;
+ for_each_fx.change_indexing = &usertype_storage_base::change_indexing;
+ for_each_fx.p_derived_usb = derived_this;
+ this->for_each_table(L_, for_each_fx);
+ }
+ else {
+ (void)L_;
+ }
+ }
+
+ void clear() {
+ if (value_index_table.valid(m_L)) {
+ stack::clear(m_L, value_index_table);
+ }
+ if (reference_index_table.valid(m_L)) {
+ stack::clear(m_L, reference_index_table);
+ }
+ if (unique_index_table.valid(m_L)) {
+ stack::clear(m_L, unique_index_table);
+ }
+ if (const_reference_index_table.valid(m_L)) {
+ stack::clear(m_L, const_reference_index_table);
+ }
+ if (const_value_index_table.valid(m_L)) {
+ stack::clear(m_L, const_value_index_table);
+ }
+ if (named_index_table.valid(m_L)) {
+ stack::clear(m_L, named_index_table);
+ }
+ if (type_table.valid(m_L)) {
+ stack::clear(m_L, type_table);
+ }
+ if (gc_names_table.valid(m_L)) {
+ stack::clear(m_L, gc_names_table);
+ }
+ if (named_metatable.valid(m_L)) {
+ auto pp = stack::push_pop(m_L, named_metatable);
+ int named_metatable_index = pp.index_of(named_metatable);
+ if (lua_getmetatable(m_L, named_metatable_index) == 1) {
+ stack::clear(m_L, absolute_index(m_L, -1));
+ }
+ stack::clear(m_L, named_metatable);
+ }
+
+ value_index_table.reset(m_L);
+ reference_index_table.reset(m_L);
+ unique_index_table.reset(m_L);
+ const_reference_index_table.reset(m_L);
+ const_value_index_table.reset(m_L);
+ named_index_table.reset(m_L);
+ type_table.reset(m_L);
+ gc_names_table.reset(m_L);
+ named_metatable.reset(m_L);
+
+ storage.clear();
+ string_keys.clear();
+ auxiliary_keys.clear();
+ string_keys_storage.clear();
+ }
+
+ template <bool is_new_index, typename Base>
+ static void base_walk_index(lua_State* L_, usertype_storage_base& self, bool& keep_going, int& base_result) {
+ using bases = typename base<Base>::type;
+ if (!keep_going) {
+ return;
+ }
+ (void)L_;
+ (void)self;
+#if SOL_IS_ON(SOL_USE_UNSAFE_BASE_LOOKUP)
+ usertype_storage_base& base_storage = get_usertype_storage<Base>(L_);
+ base_result = self_index_call<is_new_index, true>(bases(), L_, base_storage);
+#else
+ optional<usertype_storage<Base>&> maybe_base_storage = maybe_get_usertype_storage<Base>(L_);
+ if (static_cast<bool>(maybe_base_storage)) {
+ base_result = self_index_call<is_new_index, true>(bases(), L_, *maybe_base_storage);
+ keep_going = base_result == base_walking_failed_index;
+ }
+#endif // Fast versus slow, safe base lookup
+ }
+
+ template <bool is_new_index = false, bool base_walking = false, bool from_named_metatable = false, typename... Bases>
+ static inline int self_index_call(types<Bases...>, lua_State* L, usertype_storage_base& self) {
+ if constexpr (!from_named_metatable || !is_new_index) {
+ type k_type = stack::get<type>(L, 2);
+ if (k_type == type::string) {
+ index_call_storage* target = nullptr;
+ string_view k = stack::get<string_view>(L, 2);
+ {
+ auto it = self.string_keys.find(k);
+ if (it != self.string_keys.cend()) {
+ target = &it->second;
+ }
+ }
+ if (target != nullptr) {
+ // let the target decide what to do, unless it's named...
+ if constexpr (is_new_index) {
+ return (target->new_index)(L, target->binding_data);
+ }
+ else {
+ return (target->index)(L, target->binding_data);
+ }
+ }
+ }
+ else if (k_type != type::lua_nil && k_type != type::none) {
+ stateless_reference* target = nullptr;
+ {
+ stack_reference k = stack::get<stack_reference>(L, 2);
+ auto it = self.auxiliary_keys.find(k);
+ if (it != self.auxiliary_keys.cend()) {
+ target = &it->second;
+ }
+ }
+ if (target != nullptr) {
+ if constexpr (is_new_index) {
+ // set value and return
+ target->reset(L, 3);
+ return 0;
+ }
+ else {
+ // push target to return
+ // what we found
+ return stack::push(L, *target);
+ }
+ }
+ }
+ }
+
+ // retrieve bases and walk through them.
+ bool keep_going = true;
+ int base_result;
+ (void)keep_going;
+ (void)base_result;
+ (void)detail::swallow { 1, (base_walk_index<is_new_index, Bases>(L, self, keep_going, base_result), 1)... };
+ if constexpr (sizeof...(Bases) > 0) {
+ if (!keep_going) {
+ return base_result;
+ }
+ }
+ if constexpr (base_walking) {
+ // if we're JUST base-walking then don't index-fail, just
+ // return the false bits
+ return base_walking_failed_index;
+ }
+ else if constexpr (from_named_metatable) {
+ if constexpr (is_new_index) {
+ return self.static_base_index.new_index(L, self.static_base_index.new_binding_data);
+ }
+ else {
+ return self.static_base_index.index(L, self.static_base_index.binding_data);
+ }
+ }
+ else {
+ if constexpr (is_new_index) {
+ return self.base_index.new_index(L, self.base_index.new_binding_data);
+ }
+ else {
+ return self.base_index.index(L, self.base_index.binding_data);
+ }
+ }
+ }
+
+ void change_indexing(lua_State* L_, submetatable_type submetatable_, void* derived_this_, stateless_stack_reference& t_, lua_CFunction index_,
+ lua_CFunction new_index_, lua_CFunction meta_index_, lua_CFunction meta_new_index_) {
+ usertype_storage_base& this_base = *this;
+ void* base_this = static_cast<void*>(&this_base);
+
+ this->is_using_index |= true;
+ this->is_using_new_index |= true;
+ if (submetatable_ == submetatable_type::named) {
+ stack::set_field(L_, metatable_key, named_index_table, t_.stack_index());
+ stateless_stack_reference stack_metametatable(L_, -named_metatable.push(L_));
+ stack::set_field<false, true>(L_,
+ meta_function::index,
+ make_closure(meta_index_, nullptr, derived_this_, base_this, nullptr, toplevel_magic),
+ stack_metametatable.stack_index());
+ stack::set_field<false, true>(L_,
+ meta_function::new_index,
+ make_closure(meta_new_index_, nullptr, derived_this_, base_this, nullptr, toplevel_magic),
+ stack_metametatable.stack_index());
+ stack_metametatable.pop(L_);
+ }
+ else {
+ stack::set_field<false, true>(
+ L_, meta_function::index, make_closure(index_, nullptr, derived_this_, base_this, nullptr, toplevel_magic), t_.stack_index());
+ stack::set_field<false, true>(
+ L_, meta_function::new_index, make_closure(new_index_, nullptr, derived_this_, base_this, nullptr, toplevel_magic), t_.stack_index());
+ }
+ }
+
+ template <typename T = void, typename Key, typename Value>
+ void set(lua_State* L, Key&& key, Value&& value);
+
+ static int new_index_target_set(lua_State* L, void* target) {
+ usertype_storage_base& self = *static_cast<usertype_storage_base*>(target);
+ self.set(L, reference(L, raw_index(2)), reference(L, raw_index(3)));
+ return 0;
+ }
+
+ ~usertype_storage_base() {
+ value_index_table.reset(m_L);
+ reference_index_table.reset(m_L);
+ unique_index_table.reset(m_L);
+ const_reference_index_table.reset(m_L);
+ const_value_index_table.reset(m_L);
+ named_index_table.reset(m_L);
+ type_table.reset(m_L);
+ gc_names_table.reset(m_L);
+ named_metatable.reset(m_L);
+
+ auto auxiliary_first = auxiliary_keys.cbegin();
+ auto auxiliary_last = auxiliary_keys.cend();
+ while (auxiliary_first != auxiliary_last) {
+ // save a copy to what we're going to destroy
+ auto auxiliary_target = auxiliary_first;
+ // move the iterator up by 1
+ ++auxiliary_first;
+ // extract the node and destroy the key
+ auto extracted_node = auxiliary_keys.extract(auxiliary_target);
+ extracted_node.key().reset(m_L);
+ extracted_node.mapped().reset(m_L);
+ // continue if auxiliary_first hasn't been exhausted
+ }
+ }
+ };
+
+ template <typename T>
+ struct usertype_storage : usertype_storage_base {
+
+ using usertype_storage_base::usertype_storage_base;
+
+ template <bool is_new_index, bool from_named_metatable>
+ static inline int index_call_(lua_State* L) {
+ using bases = typename base<T>::type;
+ usertype_storage_base& self = stack::get<light<usertype_storage_base>>(L, upvalue_index(usertype_storage_index));
+ return self_index_call<is_new_index, false, from_named_metatable>(bases(), L, self);
+ }
+
+ template <bool is_new_index, bool from_named_metatable, typename... Bases>
+ static inline int index_call_with_bases_(lua_State* L) {
+ using bases = types<Bases...>;
+ usertype_storage_base& self = stack::get<light<usertype_storage_base>>(L, upvalue_index(usertype_storage_index));
+ return self_index_call<is_new_index, false, from_named_metatable>(bases(), L, self);
+ }
+
+ template <bool is_new_index>
+ static inline int index_call(lua_State* L) {
+ return detail::static_trampoline<&index_call_<is_new_index, false>>(L);
+ }
+
+ template <bool is_new_index, typename... Bases>
+ static inline int index_call_with_bases(lua_State* L) {
+ return detail::static_trampoline<&index_call_with_bases_<is_new_index, false, Bases...>>(L);
+ }
+
+ template <bool is_new_index>
+ static inline int meta_index_call(lua_State* L) {
+ return detail::static_trampoline<&index_call_<is_new_index, true>>(L);
+ }
+
+ template <bool is_new_index, typename... Bases>
+ static inline int meta_index_call_with_bases(lua_State* L) {
+ return detail::static_trampoline<&index_call_with_bases_<is_new_index, true, Bases...>>(L);
+ }
+
+ template <typename Key, typename Value>
+ inline void set(lua_State* L, Key&& key, Value&& value);
+ };
+
+ template <typename T, typename Key, typename Value>
+ void usertype_storage_base::set(lua_State* L, Key&& key, Value&& value) {
+ using ValueU = meta::unwrap_unqualified_t<Value>;
+ using KeyU = meta::unwrap_unqualified_t<Key>;
+ using Binding = binding<KeyU, ValueU, T>;
+ using is_var_bind = is_variable_binding<ValueU>;
+ if constexpr (std::is_same_v<KeyU, call_construction>) {
+ (void)key;
+ std::unique_ptr<Binding> p_binding = std::make_unique<Binding>(std::forward<Value>(value));
+ Binding& b = *p_binding;
+ this->storage.push_back(std::move(p_binding));
+
+ this->named_index_table.push(L);
+ absolute_index metametatable_index(L, -1);
+ std::string_view call_metamethod_name = to_string(meta_function::call);
+ lua_pushlstring(L, call_metamethod_name.data(), call_metamethod_name.size());
+ stack::push(L, nullptr);
+ stack::push(L, b.data());
+ lua_CFunction target_func = &b.template call<false, false>;
+ lua_pushcclosure(L, target_func, 2);
+ lua_rawset(L, metametatable_index);
+ this->named_index_table.pop(L);
+ }
+ else if constexpr (std::is_same_v<KeyU, base_classes_tag>) {
+ (void)key;
+ this->update_bases<T>(L, std::forward<Value>(value));
+ }
+ else if constexpr ((meta::is_string_like_or_constructible<KeyU>::value || std::is_same_v<KeyU, meta_function>)) {
+ std::string s = u_detail::make_string(std::forward<Key>(key));
+ auto storage_it = this->storage.end();
+ auto string_it = this->string_keys.find(s);
+ if (string_it != this->string_keys.cend()) {
+ const auto& binding_data = string_it->second.binding_data;
+ storage_it = std::find_if(this->storage.begin(), this->storage.end(), binding_data_equals(binding_data));
+ this->string_keys.erase(string_it);
+ }
+
+ std::unique_ptr<Binding> p_binding = std::make_unique<Binding>(std::forward<Value>(value));
+ Binding& b = *p_binding;
+ if (storage_it != this->storage.cend()) {
+ *storage_it = std::move(p_binding);
+ }
+ else {
+ this->storage.push_back(std::move(p_binding));
+ }
+
+ bool is_index = (s == to_string(meta_function::index));
+ bool is_new_index = (s == to_string(meta_function::new_index));
+ bool is_static_index = (s == to_string(meta_function::static_index));
+ bool is_static_new_index = (s == to_string(meta_function::static_new_index));
+ bool is_destruction = s == to_string(meta_function::garbage_collect);
+ bool poison_indexing = (!is_using_index || !is_using_new_index) && (is_var_bind::value || is_index || is_new_index);
+ void* derived_this = static_cast<void*>(static_cast<usertype_storage<T>*>(this));
+ index_call_storage ics;
+ ics.binding_data = b.data();
+ ics.index = is_index || is_static_index ? &Binding::template call_with_<true, is_var_bind::value>
+ : &Binding::template index_call_with_<true, is_var_bind::value>;
+ ics.new_index = is_new_index || is_static_new_index ? &Binding::template call_with_<false, is_var_bind::value>
+ : &Binding::template index_call_with_<false, is_var_bind::value>;
+
+ string_for_each_metatable_func for_each_fx;
+ for_each_fx.is_destruction = is_destruction;
+ for_each_fx.is_index = is_index;
+ for_each_fx.is_new_index = is_new_index;
+ for_each_fx.is_static_index = is_static_index;
+ for_each_fx.is_static_new_index = is_static_new_index;
+ for_each_fx.poison_indexing = poison_indexing;
+ for_each_fx.p_key = &s;
+ for_each_fx.p_ics = &ics;
+ if constexpr (is_lua_c_function_v<ValueU>) {
+ for_each_fx.is_unqualified_lua_CFunction = true;
+ for_each_fx.call_func = *static_cast<lua_CFunction*>(ics.binding_data);
+ }
+ else if constexpr (is_lua_reference_or_proxy_v<ValueU>) {
+ for_each_fx.is_unqualified_lua_reference = true;
+ for_each_fx.p_binding_ref = static_cast<reference*>(ics.binding_data);
+ }
+ else {
+ for_each_fx.call_func = &b.template call<false, is_var_bind::value>;
+ }
+ for_each_fx.p_usb = this;
+ for_each_fx.p_derived_usb = derived_this;
+ for_each_fx.idx_call = &usertype_storage<T>::template index_call<false>;
+ for_each_fx.new_idx_call = &usertype_storage<T>::template index_call<true>;
+ for_each_fx.meta_idx_call = &usertype_storage<T>::template meta_index_call<false>;
+ for_each_fx.meta_new_idx_call = &usertype_storage<T>::template meta_index_call<true>;
+ for_each_fx.change_indexing = &usertype_storage_base::change_indexing;
+ // set base index and base new_index
+ // functions here
+ if (is_index) {
+ this->base_index.index = ics.index;
+ this->base_index.binding_data = ics.binding_data;
+ }
+ if (is_new_index) {
+ this->base_index.new_index = ics.new_index;
+ this->base_index.new_binding_data = ics.binding_data;
+ }
+ if (is_static_index) {
+ this->static_base_index.index = ics.index;
+ this->static_base_index.binding_data = ics.binding_data;
+ }
+ if (is_static_new_index) {
+ this->static_base_index.new_index = ics.new_index;
+ this->static_base_index.new_binding_data = ics.binding_data;
+ }
+ this->for_each_table(L, for_each_fx);
+ this->add_entry(s, std::move(ics));
+ }
+ else {
+ // the reference-based implementation might compare poorly and hash
+ // poorly in some cases...
+ if constexpr (is_lua_reference_v<KeyU> && is_lua_reference_v<ValueU>) {
+ if (key.get_type() == type::string) {
+ stack::push(L, key);
+ std::string string_key = stack::pop<std::string>(L);
+ this->set<T>(L, string_key, std::forward<Value>(value));
+ }
+ else {
+ lua_reference_func ref_additions_fx { key, value };
+
+ this->for_each_table(L, ref_additions_fx);
+ this->auxiliary_keys.insert_or_assign(std::forward<Key>(key), std::forward<Value>(value));
+ }
+ }
+ else {
+ reference ref_key = make_reference(L, std::forward<Key>(key));
+ reference ref_value = make_reference(L, std::forward<Value>(value));
+ lua_reference_func ref_additions_fx { ref_key, ref_value };
+
+ this->for_each_table(L, ref_additions_fx);
+ this->auxiliary_keys.insert_or_assign(std::move(ref_key), std::move(ref_value));
+ }
+ }
+ }
+
+ template <typename T>
+ template <typename Key, typename Value>
+ void usertype_storage<T>::set(lua_State* L, Key&& key, Value&& value) {
+ static_cast<usertype_storage_base&>(*this).set<T>(L, std::forward<Key>(key), std::forward<Value>(value));
+ }
+
+ template <typename T>
+ inline void clear_usertype_registry_names(lua_State* L) {
+ using u_traits = usertype_traits<T>;
+ using u_const_traits = usertype_traits<const T>;
+ using u_unique_traits = usertype_traits<d::u<T>>;
+ using u_ref_traits = usertype_traits<T*>;
+ using u_const_ref_traits = usertype_traits<T const*>;
+
+ stack_reference registry(L, raw_index(LUA_REGISTRYINDEX));
+ registry.push();
+ // eliminate all named entries for this usertype
+ // in the registry (luaL_newmetatable does
+ // [name] = new table
+ // in registry upon creation
+ stack::set_field(L, &u_traits::metatable()[0], lua_nil, registry.stack_index());
+ stack::set_field(L, &u_const_traits::metatable()[0], lua_nil, registry.stack_index());
+ stack::set_field(L, &u_const_ref_traits::metatable()[0], lua_nil, registry.stack_index());
+ stack::set_field(L, &u_ref_traits::metatable()[0], lua_nil, registry.stack_index());
+ stack::set_field(L, &u_unique_traits::metatable()[0], lua_nil, registry.stack_index());
+ registry.pop();
+ }
+
+ template <typename T>
+ inline int destroy_usertype_storage(lua_State* L) noexcept {
+ clear_usertype_registry_names<T>(L);
+ return detail::user_alloc_destroy<usertype_storage<T>>(L);
+ }
+
+ template <typename T>
+ inline usertype_storage<T>& create_usertype_storage(lua_State* L) {
+ const char* gcmetakey = &usertype_traits<T>::gc_table()[0];
+
+ // Make sure userdata's memory is properly in lua first,
+ // otherwise all the light userdata we make later will become invalid
+ int usertype_storage_push_count = stack::push<user<usertype_storage<T>>>(L, no_metatable, L);
+ stack_reference usertype_storage_ref(L, -usertype_storage_push_count);
+
+ // create and push onto the stack a table to use as metatable for this GC
+ // we create a metatable to attach to the regular gc_table
+ // so that the destructor is called for the usertype storage
+ int usertype_storage_metatabe_count = stack::push(L, new_table(0, 1));
+ stack_reference usertype_storage_metatable(L, -usertype_storage_metatabe_count);
+ // set the destroyion routine on the metatable
+ stack::set_field(L, meta_function::garbage_collect, &destroy_usertype_storage<T>, usertype_storage_metatable.stack_index());
+ // set the metatable on the usertype storage userdata
+ stack::set_field(L, metatable_key, usertype_storage_metatable, usertype_storage_ref.stack_index());
+ usertype_storage_metatable.pop();
+
+ // set the usertype storage and its metatable
+ // into the global table...
+ stack::set_field<true>(L, gcmetakey, usertype_storage_ref);
+ usertype_storage_ref.pop();
+
+ // then retrieve the lua-stored version so we have a well-pinned
+ // reference that does not die
+ stack::get_field<true>(L, gcmetakey);
+ usertype_storage<T>& target_umt = stack::pop<user<usertype_storage<T>>>(L);
+ return target_umt;
+ }
+
+ inline optional<usertype_storage_base&> maybe_as_usertype_storage_base(lua_State* L, int index) {
+ if (type_of(L, index) != type::lightuserdata) {
+ return nullopt;
+ }
+ usertype_storage_base& base_storage = *static_cast<usertype_storage_base*>(stack::get<void*>(L, index));
+ return base_storage;
+ }
+
+ inline optional<usertype_storage_base&> maybe_get_usertype_storage_base_inside(lua_State* L, int index) {
+ // okay, maybe we're looking at a table that is nested?
+ if (type_of(L, index) != type::table) {
+ return nullopt;
+ }
+ stack::get_field(L, meta_function::storage, index);
+ auto maybe_storage_base = maybe_as_usertype_storage_base(L, -1);
+ lua_pop(L, 1);
+ return maybe_storage_base;
+ }
+
+ inline optional<usertype_storage_base&> maybe_get_usertype_storage_base(lua_State* L, int index) {
+ // If we can get the index directly as this type, go for it
+ auto maybe_already_is_usertype_storage_base = maybe_as_usertype_storage_base(L, index);
+ if (maybe_already_is_usertype_storage_base) {
+ return maybe_already_is_usertype_storage_base;
+ }
+ return maybe_get_usertype_storage_base_inside(L, index);
+ }
+
+ inline optional<usertype_storage_base&> maybe_get_usertype_storage_base(lua_State* L, const char* gcmetakey) {
+ stack::get_field<true>(L, gcmetakey);
+ auto maybe_storage = maybe_as_usertype_storage_base(L, lua_gettop(L));
+ lua_pop(L, 1);
+ return maybe_storage;
+ }
+
+ inline usertype_storage_base& get_usertype_storage_base(lua_State* L, const char* gcmetakey) {
+ stack::get_field<true>(L, gcmetakey);
+ stack::record tracking;
+ usertype_storage_base& target_umt = stack::stack_detail::unchecked_unqualified_get<user<usertype_storage_base>>(L, -1, tracking);
+ lua_pop(L, 1);
+ return target_umt;
+ }
+
+ template <typename T>
+ inline optional<usertype_storage<T>&> maybe_get_usertype_storage(lua_State* L) {
+ const char* gcmetakey = &usertype_traits<T>::gc_table()[0];
+ stack::get_field<true>(L, gcmetakey);
+ int target = lua_gettop(L);
+ if (!stack::check<user<usertype_storage<T>>>(L, target)) {
+ return nullopt;
+ }
+ usertype_storage<T>& target_umt = stack::pop<user<usertype_storage<T>>>(L);
+ return target_umt;
+ }
+
+ template <typename T>
+ inline usertype_storage<T>& get_usertype_storage(lua_State* L) {
+ const char* gcmetakey = &usertype_traits<T>::gc_table()[0];
+ stack::get_field<true>(L, gcmetakey);
+ usertype_storage<T>& target_umt = stack::pop<user<usertype_storage<T>>>(L);
+ return target_umt;
+ }
+
+ template <typename T>
+ inline void clear_usertype_storage(lua_State* L) {
+ using u_traits = usertype_traits<T>;
+
+ const char* gcmetakey = &u_traits::gc_table()[0];
+ stack::get_field<true>(L, gcmetakey);
+ if (!stack::check<user<usertype_storage<T>>>(L)) {
+ lua_pop(L, 1);
+ return;
+ }
+ usertype_storage<T>& target_umt = stack::pop<user<usertype_storage<T>>>(L);
+ target_umt.clear();
+
+ clear_usertype_registry_names<T>(L);
+
+ stack::set_field<true>(L, gcmetakey, lua_nil);
+ }
+
+ template <typename T, automagic_flags enrollment_flags>
+ inline int register_usertype(lua_State* L_, automagic_enrollments enrollments_ = {}) {
+ using u_traits = usertype_traits<T>;
+ using u_const_traits = usertype_traits<const T>;
+ using u_unique_traits = usertype_traits<d::u<T>>;
+ using u_ref_traits = usertype_traits<T*>;
+ using u_const_ref_traits = usertype_traits<T const*>;
+ using uts = usertype_storage<T>;
+
+ // always have __new_index point to usertype_storage method
+ // have __index always point to regular fast-lookup
+ // meta_method table
+ // if __new_index is invoked, runtime-swap
+ // to slow __index if necessary
+ // (no speed penalty because function calls
+ // are all read-only -- only depend on __index
+ // to retrieve function and then call happens VIA Lua)
+
+ // __type entry:
+ // table contains key -> value lookup,
+ // where key is entry in metatable
+ // and value is type information as a string as
+ // best as we can give it
+
+ // name entry:
+ // string that contains raw class name,
+ // as defined from C++
+
+ // is entry:
+ // checks if argument supplied is of type T
+
+ // __storage entry:
+ // a light userdata pointing to the storage
+ // mostly to enable this new abstraction
+ // to not require the type name `T`
+ // to get at the C++ usertype storage within
+
+ // we then let typical definitions potentially override these intrinsics
+ // it's the user's fault if they override things or screw them up:
+ // these names have been reserved and documented since sol2
+
+ // STEP 0: tell the old usertype (if it exists)
+ // to fuck off
+ clear_usertype_storage<T>(L_);
+
+ // STEP 1: Create backing store for usertype storage
+ // Pretty much the most important step.
+ // STEP 2: Create Lua tables used for fast method indexing.
+ // This is done inside of the storage table's constructor
+ usertype_storage<T>& storage = create_usertype_storage<T>(L_);
+ usertype_storage_base& base_storage = storage;
+ void* light_storage = static_cast<void*>(&storage);
+ void* light_base_storage = static_cast<void*>(&base_storage);
+
+ // STEP 3: set up GC escape hatch table entirely
+ storage.gc_names_table.push(L_);
+ stateless_stack_reference gnt(L_, -1);
+ stack::set_field(L_, submetatable_type::named, &u_traits::gc_table()[0], gnt.stack_index());
+ stack::set_field(L_, submetatable_type::const_value, &u_const_traits::metatable()[0], gnt.stack_index());
+ stack::set_field(L_, submetatable_type::const_reference, &u_const_ref_traits::metatable()[0], gnt.stack_index());
+ stack::set_field(L_, submetatable_type::reference, &u_ref_traits::metatable()[0], gnt.stack_index());
+ stack::set_field(L_, submetatable_type::unique, &u_unique_traits::metatable()[0], gnt.stack_index());
+ stack::set_field(L_, submetatable_type::value, &u_traits::metatable()[0], gnt.stack_index());
+ gnt.pop(L_);
+
+ // STEP 4: add some useful information to the type table
+ stateless_stack_reference stacked_type_table(L_, -storage.type_table.push(L_));
+ stack::set_field(L_, "name", detail::demangle<T>(), stacked_type_table.stack_index());
+ stack::set_field(L_, "is", &detail::is_check<T>, stacked_type_table.stack_index());
+ stacked_type_table.pop(L_);
+
+ // STEP 5: create and hook up metatable,
+ // add intrinsics
+ // this one is the actual meta-handling table,
+ // the next one will be the one for
+ int for_each_backing_metatable_calls = 0;
+ auto for_each_backing_metatable = [&](lua_State* L_, submetatable_type smt_, stateless_reference& fast_index_table_) {
+ // Pointer types, AKA "references" from C++
+ const char* metakey = nullptr;
+ switch (smt_) {
+ case submetatable_type::const_value:
+ metakey = &u_const_traits::metatable()[0];
+ break;
+ case submetatable_type::reference:
+ metakey = &u_ref_traits::metatable()[0];
+ break;
+ case submetatable_type::unique:
+ metakey = &u_unique_traits::metatable()[0];
+ break;
+ case submetatable_type::const_reference:
+ metakey = &u_const_ref_traits::metatable()[0];
+ break;
+ case submetatable_type::named:
+ metakey = &u_traits::user_metatable()[0];
+ break;
+ case submetatable_type::value:
+ default:
+ metakey = &u_traits::metatable()[0];
+ break;
+ }
+
+ luaL_newmetatable(L_, metakey);
+ if (smt_ == submetatable_type::named) {
+ // the named table itself
+ // gets the associated name value
+ storage.named_metatable.reset(L_, -1);
+ lua_pop(L_, 1);
+ // but the thing we perform the methods on
+ // is still the metatable of the named
+ // table
+ lua_createtable(L_, 0, 6);
+ }
+ stateless_stack_reference t(L_, -1);
+ fast_index_table_.reset(L_, t.stack_index());
+ stack::set_field<false, true>(L_, meta_function::type, storage.type_table, t.stack_index());
+ // destructible? serialize default destructor here
+ // otherwise, not destructible: serialize a "hey you messed up"
+ switch (smt_) {
+ case submetatable_type::const_reference:
+ case submetatable_type::reference:
+ case submetatable_type::named:
+ break;
+ case submetatable_type::unique:
+ if constexpr (std::is_destructible_v<T>) {
+ stack::set_field<false, true>(L_, meta_function::garbage_collect, &detail::unique_destroy<T>, t.stack_index());
+ }
+ else {
+ stack::set_field<false, true>(L_, meta_function::garbage_collect, &detail::cannot_destroy<T>, t.stack_index());
+ }
+ break;
+ case submetatable_type::value:
+ case submetatable_type::const_value:
+ default:
+ if constexpr (std::is_destructible_v<T>) {
+ stack::set_field<false, true>(L_, meta_function::garbage_collect, detail::make_destructor<T>(), t.stack_index());
+ }
+ else {
+ stack::set_field<false, true>(L_, meta_function::garbage_collect, &detail::cannot_destroy<T>, t.stack_index());
+ }
+ break;
+ }
+
+ static_assert(sizeof(void*) <= sizeof(detail::inheritance_check_function),
+ "The size of this data pointer is too small to fit the inheritance checking function: file a bug "
+ "report.");
+ static_assert(sizeof(void*) <= sizeof(detail::inheritance_cast_function),
+ "The size of this data pointer is too small to fit the inheritance checking function: file a bug "
+ "report.");
+ stack::set_field<false, true>(L_, detail::base_class_check_key(), reinterpret_cast<void*>(&detail::inheritance<T>::type_check), t.stack_index());
+ stack::set_field<false, true>(L_, detail::base_class_cast_key(), reinterpret_cast<void*>(&detail::inheritance<T>::type_cast), t.stack_index());
+
+ auto prop_fx = detail::properties_enrollment_allowed(for_each_backing_metatable_calls, storage.properties, enrollments_);
+ auto insert_fx = [&L_, &t, &storage](meta_function mf, lua_CFunction reg) {
+ stack::set_field<false, true>(L_, mf, reg, t.stack_index());
+ storage.properties[static_cast<std::size_t>(mf)] = true;
+ };
+ detail::insert_default_registrations<T>(insert_fx, prop_fx);
+
+ // There are no variables, so serialize the fast function stuff
+ // be sure to reset the index stuff to the non-fast version
+ // if the user ever adds something later!
+ if (smt_ == submetatable_type::named) {
+ // add escape hatch storage pointer and gc names
+ stack::set_field<false, true>(L_, meta_function::storage, light_base_storage, t.stack_index());
+ stack::set_field<false, true>(L_, meta_function::gc_names, storage.gc_names_table, t.stack_index());
+
+ // fancy new_indexing when using the named table
+ {
+ absolute_index named_metatable_index(L_, -storage.named_metatable.push(L_));
+ stack::set_field<false, true>(L_, metatable_key, t, named_metatable_index);
+ storage.named_metatable.pop(L_);
+ }
+ stack_reference stack_metametatable(L_, -storage.named_index_table.push(L_));
+ stack::set_field<false, true>(L_,
+ meta_function::index,
+ make_closure(uts::template meta_index_call<false>, nullptr, light_storage, light_base_storage, nullptr, toplevel_magic),
+ stack_metametatable.stack_index());
+ stack::set_field<false, true>(L_,
+ meta_function::new_index,
+ make_closure(uts::template meta_index_call<true>, nullptr, light_storage, light_base_storage, nullptr, toplevel_magic),
+ stack_metametatable.stack_index());
+ stack_metametatable.pop();
+ }
+ else {
+ // otherwise just plain for index,
+ // and elaborated for new_index
+ stack::set_field<false, true>(L_, meta_function::index, t, t.stack_index());
+ stack::set_field<false, true>(L_,
+ meta_function::new_index,
+ make_closure(uts::template index_call<true>, nullptr, light_storage, light_base_storage, nullptr, toplevel_magic),
+ t.stack_index());
+ storage.is_using_new_index = true;
+ }
+
+ ++for_each_backing_metatable_calls;
+ fast_index_table_.reset(L_, t.stack_index());
+ t.pop(L_);
+ };
+
+ storage.for_each_table(L_, for_each_backing_metatable);
+
+ // can only use set AFTER we initialize all the metatables
+ if constexpr (std::is_default_constructible_v<T> && has_flag(enrollment_flags, automagic_flags::default_constructor)) {
+ if (enrollments_.default_constructor) {
+ storage.set(L_, meta_function::construct, constructors<T()>());
+ }
+ }
+
+ // return the named metatable we want names linked into
+ storage.named_metatable.push(L_);
+ return 1;
+ }
+}} // namespace sol::u_detail
+
+// end of sol/usertype_storage.hpp
+
+// beginning of sol/usertype_proxy.hpp
+
+namespace sol {
+ template <typename Table, typename Key>
+ struct usertype_proxy : public proxy_base<usertype_proxy<Table, Key>> {
+ private:
+ using key_type = detail::proxy_key_t<Key>;
+
+ template <typename T, std::size_t... I>
+ decltype(auto) tuple_get(std::index_sequence<I...>) const& {
+ return tbl.template traverse_get<T>(std::get<I>(key)...);
+ }
+
+ template <typename T, std::size_t... I>
+ decltype(auto) tuple_get(std::index_sequence<I...>) && {
+ return tbl.template traverse_get<T>(std::get<I>(std::move(key))...);
+ }
+
+ template <std::size_t... I, typename T>
+ void tuple_set(std::index_sequence<I...>, T&& value) & {
+ if constexpr (sizeof...(I) > 1) {
+ tbl.traverse_set(std::get<I>(key)..., std::forward<T>(value));
+ }
+ else {
+ tbl.set(std::get<I>(key)..., std::forward<T>(value));
+ }
+ }
+
+ template <std::size_t... I, typename T>
+ void tuple_set(std::index_sequence<I...>, T&& value) && {
+ if constexpr (sizeof...(I) > 1) {
+ tbl.traverse_set(std::get<I>(std::move(key))..., std::forward<T>(value));
+ }
+ else {
+ tbl.set(std::get<I>(std::move(key))..., std::forward<T>(value));
+ }
+ }
+
+ public:
+ Table tbl;
+ key_type key;
+
+ template <typename T>
+ usertype_proxy(Table table, T&& k) : tbl(table), key(std::forward<T>(k)) {
+ }
+
+ template <typename T>
+ usertype_proxy& set(T&& item) & {
+ using idx_seq = std::make_index_sequence<std::tuple_size_v<meta::unqualified_t<key_type>>>;
+ tuple_set(idx_seq(), std::forward<T>(item));
+ return *this;
+ }
+
+ template <typename T>
+ usertype_proxy&& set(T&& item) && {
+ using idx_seq = std::make_index_sequence<std::tuple_size_v<meta::unqualified_t<key_type>>>;
+ std::move(*this).tuple_set(idx_seq(), std::forward<T>(item));
+ return std::move(*this);
+ }
+
+ template <typename T>
+ usertype_proxy& operator=(T&& other) & {
+ return set(std::forward<T>(other));
+ }
+
+ template <typename T>
+ usertype_proxy&& operator=(T&& other) && {
+ return std::move(*this).set(std::forward<T>(other));
+ }
+
+ template <typename T>
+ usertype_proxy& operator=(std::initializer_list<T> other) & {
+ return set(std::move(other));
+ }
+
+ template <typename T>
+ usertype_proxy&& operator=(std::initializer_list<T> other) && {
+ return std::move(*this).set(std::move(other));
+ }
+
+ template <typename T>
+ decltype(auto) get() const& {
+ using idx_seq = std::make_index_sequence<std::tuple_size_v<meta::unqualified_t<key_type>>>;
+ return tuple_get<T>(idx_seq());
+ }
+
+ template <typename T>
+ decltype(auto) get() && {
+ using idx_seq = std::make_index_sequence<std::tuple_size_v<meta::unqualified_t<key_type>>>;
+ return std::move(*this).template tuple_get<T>(idx_seq());
+ }
+
+ template <typename K>
+ decltype(auto) operator[](K&& k) const& {
+ auto keys = meta::tuplefy(key, std::forward<K>(k));
+ return usertype_proxy<Table, decltype(keys)>(tbl, std::move(keys));
+ }
+
+ template <typename K>
+ decltype(auto) operator[](K&& k) & {
+ auto keys = meta::tuplefy(key, std::forward<K>(k));
+ return usertype_proxy<Table, decltype(keys)>(tbl, std::move(keys));
+ }
+
+ template <typename K>
+ decltype(auto) operator[](K&& k) && {
+ auto keys = meta::tuplefy(std::move(key), std::forward<K>(k));
+ return usertype_proxy<Table, decltype(keys)>(tbl, std::move(keys));
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) call(Args&&... args) {
+#if !defined(__clang__) && defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 191200000
+ // MSVC is ass sometimes
+ return get<function>().call<Ret...>(std::forward<Args>(args)...);
+#else
+ return get<function>().template call<Ret...>(std::forward<Args>(args)...);
+#endif
+ }
+
+ template <typename... Args>
+ decltype(auto) operator()(Args&&... args) {
+ return call<>(std::forward<Args>(args)...);
+ }
+
+ bool valid() const {
+ auto pp = stack::push_pop(tbl);
+ auto p = stack::probe_get_field<std::is_same<meta::unqualified_t<Table>, global_table>::value>(lua_state(), key, lua_gettop(lua_state()));
+ lua_pop(lua_state(), p.levels);
+ return p;
+ }
+
+ int push() const noexcept {
+ return push(this->lua_state());
+ }
+
+ int push(lua_State* L) const noexcept {
+ return get<reference>().push(L);
+ }
+
+ type get_type() const {
+ type t = type::none;
+ auto pp = stack::push_pop(tbl);
+ auto p = stack::probe_get_field<std::is_same<meta::unqualified_t<Table>, global_table>::value>(lua_state(), key, lua_gettop(lua_state()));
+ if (p) {
+ t = type_of(lua_state(), -1);
+ }
+ lua_pop(lua_state(), p.levels);
+ return t;
+ }
+
+ lua_State* lua_state() const {
+ return tbl.lua_state();
+ }
+ };
+} // namespace sol
+
+// end of sol/usertype_proxy.hpp
+
+// beginning of sol/metatable.hpp
+
+// beginning of sol/table_core.hpp
+
+// beginning of sol/table_proxy.hpp
+
+namespace sol {
+
+ template <typename Table, typename Key>
+ struct table_proxy : public proxy_base<table_proxy<Table, Key>> {
+ private:
+ using key_type = detail::proxy_key_t<Key>;
+
+ template <typename T, std::size_t... I>
+ decltype(auto) tuple_get(std::index_sequence<I...>) const& {
+ return tbl.template traverse_get<T>(std::get<I>(key)...);
+ }
+
+ template <typename T, std::size_t... I>
+ decltype(auto) tuple_get(std::index_sequence<I...>) && {
+ return tbl.template traverse_get<T>(std::get<I>(std::move(key))...);
+ }
+
+ template <std::size_t... I, typename T>
+ void tuple_set(std::index_sequence<I...>, T&& value) & {
+ tbl.traverse_set(std::get<I>(key)..., std::forward<T>(value));
+ }
+
+ template <std::size_t... I, typename T>
+ void tuple_set(std::index_sequence<I...>, T&& value) && {
+ tbl.traverse_set(std::get<I>(std::move(key))..., std::forward<T>(value));
+ }
+
+ auto setup_table(std::true_type) {
+ auto p = stack::probe_get_field<std::is_same_v<meta::unqualified_t<Table>, global_table>>(lua_state(), key, tbl.stack_index());
+ lua_pop(lua_state(), p.levels);
+ return p;
+ }
+
+ bool is_valid(std::false_type) {
+ auto pp = stack::push_pop(tbl);
+ auto p = stack::probe_get_field<std::is_same_v<meta::unqualified_t<Table>, global_table>>(lua_state(), key, lua_gettop(lua_state()));
+ lua_pop(lua_state(), p.levels);
+ return p;
+ }
+
+ public:
+ Table tbl;
+ key_type key;
+
+ template <typename T>
+ table_proxy(Table table, T&& k) : tbl(table), key(std::forward<T>(k)) {
+ }
+
+ table_proxy(const table_proxy&) = default;
+ table_proxy(table_proxy&&) = default;
+ table_proxy& operator=(const table_proxy& right) {
+ return set(right);
+ }
+ table_proxy& operator=(table_proxy&& right) {
+ return set(std::move(right));
+ }
+
+ template <typename T>
+ table_proxy& set(T&& item) & {
+ tuple_set(std::make_index_sequence<std::tuple_size_v<meta::unqualified_t<key_type>>>(), std::forward<T>(item));
+ return *this;
+ }
+
+ template <typename T>
+ table_proxy&& set(T&& item) && {
+ std::move(*this).tuple_set(std::make_index_sequence<std::tuple_size_v<meta::unqualified_t<key_type>>>(), std::forward<T>(item));
+ return std::move(*this);
+ }
+
+ template <typename... Args>
+ table_proxy& set_function(Args&&... args) & {
+ tbl.set_function(key, std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename... Args>
+ table_proxy&& set_function(Args&&... args) && {
+ tbl.set_function(std::move(key), std::forward<Args>(args)...);
+ return std::move(*this);
+ }
+
+ template <typename T, std::enable_if_t<!std::is_same_v<meta::unqualified_t<T>, table_proxy>>* = nullptr>
+ table_proxy& operator=(T&& other) & {
+ using Tu = meta::unwrap_unqualified_t<T>;
+ if constexpr (!is_lua_reference_or_proxy_v<Tu> && meta::is_invocable_v<Tu>) {
+ return set_function(std::forward<T>(other));
+ }
+ else {
+ return set(std::forward<T>(other));
+ }
+ }
+
+ template <typename T, std::enable_if_t<!std::is_same_v<meta::unqualified_t<T>, table_proxy>>* = nullptr>
+ table_proxy&& operator=(T&& other) && {
+ using Tu = meta::unwrap_unqualified_t<T>;
+ if constexpr (!is_lua_reference_or_proxy_v<Tu> && meta::is_invocable_v<Tu> && !detail::is_msvc_callable_rigged_v<T>) {
+ return std::move(*this).set_function(std::forward<T>(other));
+ }
+ else {
+ return std::move(*this).set(std::forward<T>(other));
+ }
+ }
+
+ template <typename T>
+ table_proxy& operator=(std::initializer_list<T> other) & {
+ return set(std::move(other));
+ }
+
+ template <typename T>
+ table_proxy&& operator=(std::initializer_list<T> other) && {
+ return std::move(*this).set(std::move(other));
+ }
+
+ template <typename T>
+ bool is() const {
+ typedef decltype(get<T>()) U;
+ optional<U> option = this->get<optional<U>>();
+ return option.has_value();
+ }
+
+ template <typename T>
+ decltype(auto) get() const& {
+ using idx_seq = std::make_index_sequence<std::tuple_size_v<meta::unqualified_t<key_type>>>;
+ return tuple_get<T>(idx_seq());
+ }
+
+ template <typename T>
+ decltype(auto) get() && {
+ using idx_seq = std::make_index_sequence<std::tuple_size_v<meta::unqualified_t<key_type>>>;
+ return std::move(*this).template tuple_get<T>(idx_seq());
+ }
+
+ template <typename T>
+ decltype(auto) get_or(T&& otherwise) const {
+ typedef decltype(get<T>()) U;
+ optional<U> option = get<optional<U>>();
+ if (option) {
+ return static_cast<U>(option.value());
+ }
+ return static_cast<U>(std::forward<T>(otherwise));
+ }
+
+ template <typename T, typename D>
+ decltype(auto) get_or(D&& otherwise) const {
+ optional<T> option = get<optional<T>>();
+ if (option) {
+ return static_cast<T>(option.value());
+ }
+ return static_cast<T>(std::forward<D>(otherwise));
+ }
+
+ template <typename T>
+ decltype(auto) get_or_create() {
+ return get_or_create<T>(new_table());
+ }
+
+ template <typename T, typename Otherwise>
+ decltype(auto) get_or_create(Otherwise&& other) {
+ if (!this->valid()) {
+ this->set(std::forward<Otherwise>(other));
+ }
+ return get<T>();
+ }
+
+ template <typename K>
+ decltype(auto) operator[](K&& k) const& {
+ auto keys = meta::tuplefy(key, std::forward<K>(k));
+ return table_proxy<Table, decltype(keys)>(tbl, std::move(keys));
+ }
+
+ template <typename K>
+ decltype(auto) operator[](K&& k) & {
+ auto keys = meta::tuplefy(key, std::forward<K>(k));
+ return table_proxy<Table, decltype(keys)>(tbl, std::move(keys));
+ }
+
+ template <typename K>
+ decltype(auto) operator[](K&& k) && {
+ auto keys = meta::tuplefy(std::move(key), std::forward<K>(k));
+ return table_proxy<Table, decltype(keys)>(tbl, std::move(keys));
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) call(Args&&... args) {
+ lua_State* L = this->lua_state();
+ push(L);
+ int idx = lua_gettop(L);
+ stack_aligned_function func(L, idx);
+ return func.call<Ret...>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ decltype(auto) operator()(Args&&... args) {
+ return call<>(std::forward<Args>(args)...);
+ }
+
+ bool valid() const {
+ auto pp = stack::push_pop(tbl);
+ auto p = stack::probe_get_field<std::is_same<meta::unqualified_t<Table>, global_table>::value>(lua_state(), key, lua_gettop(lua_state()));
+ lua_pop(lua_state(), p.levels);
+ return p;
+ }
+
+ int push() const noexcept {
+ return push(this->lua_state());
+ }
+
+ int push(lua_State* L) const noexcept {
+ if constexpr (std::is_same_v<meta::unqualified_t<Table>, global_table> || is_stack_table_v<meta::unqualified_t<Table>>) {
+ auto pp = stack::push_pop<true>(tbl);
+ int tableindex = pp.index_of(tbl);
+ int top_index = lua_gettop(L);
+ stack::get_field<true>(lua_state(), key, tableindex);
+ lua_replace(L, top_index + 1);
+ lua_settop(L, top_index + 1);
+ }
+ else {
+ auto pp = stack::push_pop<false>(tbl);
+ int tableindex = pp.index_of(tbl);
+ int aftertableindex = lua_gettop(L);
+ stack::get_field<false>(lua_state(), key, tableindex);
+ lua_replace(L, tableindex);
+ lua_settop(L, aftertableindex + 1);
+ }
+ return 1;
+ }
+
+ type get_type() const {
+ type t = type::none;
+ auto pp = stack::push_pop(tbl);
+ auto p = stack::probe_get_field<std::is_same<meta::unqualified_t<Table>, global_table>::value>(lua_state(), key, lua_gettop(lua_state()));
+ if (p) {
+ t = type_of(lua_state(), -1);
+ }
+ lua_pop(lua_state(), p.levels);
+ return t;
+ }
+
+ lua_State* lua_state() const {
+ return tbl.lua_state();
+ }
+
+ table_proxy& force() {
+ if (!this->valid()) {
+ this->set(new_table());
+ }
+ return *this;
+ }
+ };
+
+ template <typename Table, typename Key, typename T>
+ inline bool operator==(T&& left, const table_proxy<Table, Key>& right) {
+ using G = decltype(stack::get<T>(nullptr, 0));
+ return right.template get<optional<G>>() == left;
+ }
+
+ template <typename Table, typename Key, typename T>
+ inline bool operator==(const table_proxy<Table, Key>& right, T&& left) {
+ using G = decltype(stack::get<T>(nullptr, 0));
+ return right.template get<optional<G>>() == left;
+ }
+
+ template <typename Table, typename Key, typename T>
+ inline bool operator!=(T&& left, const table_proxy<Table, Key>& right) {
+ using G = decltype(stack::get<T>(nullptr, 0));
+ return right.template get<optional<G>>() != left;
+ }
+
+ template <typename Table, typename Key, typename T>
+ inline bool operator!=(const table_proxy<Table, Key>& right, T&& left) {
+ using G = decltype(stack::get<T>(nullptr, 0));
+ return right.template get<optional<G>>() != left;
+ }
+
+ template <typename Table, typename Key>
+ inline bool operator==(lua_nil_t, const table_proxy<Table, Key>& right) {
+ return !right.valid();
+ }
+
+ template <typename Table, typename Key>
+ inline bool operator==(const table_proxy<Table, Key>& right, lua_nil_t) {
+ return !right.valid();
+ }
+
+ template <typename Table, typename Key>
+ inline bool operator!=(lua_nil_t, const table_proxy<Table, Key>& right) {
+ return right.valid();
+ }
+
+ template <typename Table, typename Key>
+ inline bool operator!=(const table_proxy<Table, Key>& right, lua_nil_t) {
+ return right.valid();
+ }
+
+ template <bool b>
+ template <typename Super>
+ basic_reference<b>& basic_reference<b>::operator=(proxy_base<Super>&& r) {
+ basic_reference<b> v = r;
+ this->operator=(std::move(v));
+ return *this;
+ }
+
+ template <bool b>
+ template <typename Super>
+ basic_reference<b>& basic_reference<b>::operator=(const proxy_base<Super>& r) {
+ basic_reference<b> v = r;
+ this->operator=(std::move(v));
+ return *this;
+ }
+
+ namespace stack {
+ template <typename Table, typename Key>
+ struct unqualified_pusher<table_proxy<Table, Key>> {
+ static int push(lua_State* L, const table_proxy<Table, Key>& p) {
+ return p.push(L);
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/table_proxy.hpp
+
+// beginning of sol/table_iterator.hpp
+
+#include <iterator>
+
+namespace sol {
+
+ template <typename reference_type>
+ class basic_table_iterator {
+ public:
+ typedef object key_type;
+ typedef object mapped_type;
+ typedef std::pair<object, object> value_type;
+ typedef std::input_iterator_tag iterator_category;
+ typedef std::ptrdiff_t difference_type;
+ typedef value_type* pointer;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+
+ private:
+ std::pair<object, object> kvp;
+ reference_type ref;
+ int tableidx = 0;
+ int keyidx = 0;
+ std::ptrdiff_t idx = 0;
+
+ public:
+ basic_table_iterator() noexcept : keyidx(-1), idx(-1) {
+ }
+
+ basic_table_iterator(reference_type x) noexcept : ref(std::move(x)) {
+ ref.push();
+ tableidx = lua_gettop(ref.lua_state());
+ stack::push(ref.lua_state(), lua_nil);
+ this->operator++();
+ if (idx == -1) {
+ return;
+ }
+ --idx;
+ }
+
+ basic_table_iterator& operator++() noexcept {
+ if (idx == -1)
+ return *this;
+
+ if (lua_next(ref.lua_state(), tableidx) == 0) {
+ idx = -1;
+ keyidx = -1;
+ return *this;
+ }
+ ++idx;
+ kvp.first = object(ref.lua_state(), -2);
+ kvp.second = object(ref.lua_state(), -1);
+ lua_pop(ref.lua_state(), 1);
+ // leave key on the stack
+ keyidx = lua_gettop(ref.lua_state());
+ return *this;
+ }
+
+ basic_table_iterator operator++(int) noexcept {
+ auto saved = *this;
+ this->operator++();
+ return saved;
+ }
+
+ reference operator*() const noexcept {
+ return const_cast<reference>(kvp);
+ }
+
+ bool operator==(const basic_table_iterator& right) const noexcept {
+ return idx == right.idx;
+ }
+
+ bool operator!=(const basic_table_iterator& right) const noexcept {
+ return idx != right.idx;
+ }
+
+ ~basic_table_iterator() {
+ if (keyidx != -1) {
+ stack::remove(ref.lua_state(), keyidx, 1);
+ }
+ if (ref.lua_state() != nullptr && ref.valid()) {
+ stack::remove(ref.lua_state(), tableidx, 1);
+ }
+ }
+ };
+
+} // namespace sol
+
+// end of sol/table_iterator.hpp
+
+// beginning of sol/pairs_iterator.hpp
+
+// beginning of sol/stack/detail/pairs.hpp
+
+#include <optional>
+
+namespace sol { namespace stack { namespace stack_detail {
+
+ inline bool maybe_push_lua_next_function(lua_State* L_) {
+ stack::get_field<true, false>(L_, "next");
+ bool is_next = stack::check<protected_function>(L_);
+ if (is_next) {
+ return true;
+ }
+ stack::get_field<true, false>(L_, "table");
+ stack::record tracking{};
+ if (!stack::loose_table_check(L_, -1, &no_panic, tracking)) {
+ return false;
+ }
+ lua_getfield(L_, -1, "next");
+ bool is_table_next_func = stack::check<protected_function>(L_, -1);
+ if (is_table_next_func) {
+ return true;
+ }
+ lua_pop(L_, 1);
+ return false;
+ }
+
+ inline std::optional<protected_function> find_lua_next_function(lua_State* L_) {
+ if (maybe_push_lua_next_function(L_)) {
+ return stack::pop<protected_function>(L_);
+ }
+ return std::nullopt;
+ }
+
+ inline int c_lua_next(lua_State* L_) noexcept {
+ stack_reference table_stack_ref(L_, raw_index(1));
+ stateless_stack_reference key_stack_ref(L_, raw_index(2));
+ int result = lua_next(table_stack_ref.lua_state(), table_stack_ref.stack_index());
+ if (result == 0) {
+ stack::push(L_, lua_nil);
+ return 1;
+ }
+ return 2;
+ }
+
+ inline int readonly_pairs(lua_State* L_) noexcept {
+ int pushed = 0;
+ if (!maybe_push_lua_next_function(L_)) {
+ // we do not have the "next" function in the global namespace
+ // from the "table" global entiry, use our own
+ pushed += stack::push(L_, &c_lua_next);
+ }
+ else {
+ pushed += 1;
+ }
+ int metatable_exists = lua_getmetatable(L_, 1);
+ SOL_ASSERT(metatable_exists == 1);
+ const auto& index_key = to_string(sol::meta_function::index);
+ lua_getfield(L_, lua_gettop(L_), index_key.c_str());
+ lua_remove(L_, -2);
+ pushed += 1;
+ pushed += stack::push(L_, lua_nil);
+ return pushed;
+ }
+
+}}} // sol::stack::stack_detail
+
+// end of sol/stack/detail/pairs.hpp
+
+namespace sol {
+
+ struct pairs_sentinel { };
+
+ class pairs_iterator {
+ private:
+ inline static constexpr int empty_key_index = -1;
+
+ public:
+ using key_type = object;
+ using mapped_type = object;
+ using value_type = std::pair<object, object>;
+ using iterator_category = std::input_iterator_tag;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using const_pointer = value_type const*;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+
+ pairs_iterator() noexcept
+ : m_L(nullptr)
+ , m_next_function_ref(lua_nil)
+ , m_table_ref(lua_nil)
+ , m_cached_key_value_pair({ lua_nil, lua_nil })
+ , m_key_index(empty_key_index)
+ , m_iteration_index(0) {
+ }
+
+ pairs_iterator(const pairs_iterator&) = delete;
+ pairs_iterator& operator=(const pairs_iterator&) = delete;
+
+ pairs_iterator(pairs_iterator&& right) noexcept
+ : m_L(right.m_L)
+ , m_next_function_ref(std::move(right.m_next_function_ref))
+ , m_table_ref(std::move(right.m_table_ref))
+ , m_cached_key_value_pair(std::move(right.m_cached_key_value_pair))
+ , m_key_index(right.m_key_index)
+ , m_iteration_index(right.m_iteration_index) {
+ right.m_key_index = empty_key_index;
+ }
+
+ pairs_iterator& operator=(pairs_iterator&& right) noexcept {
+ m_L = right.m_L;
+ m_next_function_ref = std::move(right.m_next_function_ref);
+ m_table_ref = std::move(right.m_table_ref);
+ m_cached_key_value_pair = std::move(right.m_cached_key_value_pair);
+ m_key_index = right.m_key_index;
+ m_iteration_index = right.m_iteration_index;
+ right.m_key_index = empty_key_index;
+ return *this;
+ }
+
+ template <typename Source>
+ pairs_iterator(const Source& source_) noexcept : m_L(source_.lua_state()), m_key_index(empty_key_index), m_iteration_index(0) {
+ if (m_L == nullptr || !source_.valid()) {
+ m_key_index = empty_key_index;
+ return;
+ }
+ int source_index = -source_.push(m_L);
+ int abs_source_index = lua_absindex(m_L, source_index);
+ int metatable_exists = lua_getmetatable(m_L, abs_source_index);
+ lua_remove(m_L, abs_source_index);
+ if (metatable_exists == 1) {
+ // just has a metatable, but does it have __pairs ?
+ stack_reference metatable(m_L, raw_index(abs_source_index));
+ stack::get_field<is_global_table_v<Source>, true>(m_L, meta_function::pairs, metatable.stack_index());
+ optional<protected_function> maybe_pairs_function = stack::pop<optional<protected_function>>(m_L);
+ if (maybe_pairs_function.has_value()) {
+ protected_function& pairs_function = *maybe_pairs_function;
+ protected_function_result next_fn_and_table_and_first_key = pairs_function(source_);
+ if (next_fn_and_table_and_first_key.valid()) {
+ m_next_function_ref = next_fn_and_table_and_first_key.get<protected_function>(0);
+ m_table_ref = next_fn_and_table_and_first_key.get<sol::reference>(1);
+ m_key_index = next_fn_and_table_and_first_key.stack_index() - 1;
+ // remove next function and table
+ lua_remove(m_L, m_key_index);
+ lua_remove(m_L, m_key_index);
+ next_fn_and_table_and_first_key.abandon();
+ lua_remove(m_L, abs_source_index);
+ this->operator++();
+ m_iteration_index = 0;
+ return;
+ }
+ }
+ }
+
+ {
+ auto maybe_next = stack::stack_detail::find_lua_next_function(m_L);
+ if (maybe_next.has_value()) {
+ m_next_function_ref = std::move(*maybe_next);
+ m_table_ref = source_;
+
+ stack::push(m_L, lua_nil);
+ m_key_index = lua_gettop(m_L);
+ this->operator++();
+ m_iteration_index = 0;
+ return;
+ }
+ }
+
+ // okay, so none of the above worked and now we need to create
+ // a shim / polyfill instead
+ stack::push(m_L, &stack::stack_detail::c_lua_next);
+ m_next_function_ref = stack::pop<protected_function>(m_L);
+ m_table_ref = source_;
+ stack::push(m_L, lua_nil);
+ m_key_index = lua_gettop(m_L);
+ this->operator++();
+ m_iteration_index = 0;
+ }
+
+ pairs_iterator& operator++() {
+ if (m_key_index == empty_key_index) {
+ return *this;
+ }
+ {
+ sol::protected_function_result next_results = m_next_function_ref(m_table_ref, stack_reference(m_L, m_key_index));
+ if (!next_results.valid()) {
+ // TODO: abort, or throw an error?
+ m_clear();
+ m_key_index = empty_key_index;
+ return *this;
+ }
+ int next_results_count = next_results.return_count();
+ if (next_results_count < 2) {
+ // iteration is over!
+ next_results.abandon();
+ lua_settop(m_L, m_key_index - 1);
+ m_key_index = empty_key_index;
+ ++m_iteration_index;
+ return *this;
+ }
+ else {
+ lua_remove(m_L, m_key_index);
+ m_key_index = next_results.stack_index() - 1;
+ m_cached_key_value_pair.first = stack::get<object>(m_L, m_key_index);
+ m_cached_key_value_pair.second = stack::get<object>(m_L, m_key_index + 1);
+ lua_settop(m_L, m_key_index);
+ next_results.abandon();
+ }
+ }
+ ++m_iteration_index;
+ return *this;
+ }
+
+ std::ptrdiff_t index() const {
+ return static_cast<std::ptrdiff_t>(m_iteration_index);
+ }
+
+ const_reference operator*() const noexcept {
+ return m_cached_key_value_pair;
+ }
+
+ reference operator*() noexcept {
+ return m_cached_key_value_pair;
+ }
+
+ friend bool operator==(const pairs_iterator& left, const pairs_iterator& right) noexcept {
+ return left.m_table_ref == right.m_table_ref && left.m_iteration_index == right.m_iteration_index;
+ }
+
+ friend bool operator!=(const pairs_iterator& left, const pairs_iterator& right) noexcept {
+ return left.m_table_ref != right.m_table_ref || left.m_iteration_index != right.m_iteration_index;
+ }
+
+ friend bool operator==(const pairs_iterator& left, const pairs_sentinel&) noexcept {
+ return left.m_key_index == empty_key_index;
+ }
+
+ friend bool operator!=(const pairs_iterator& left, const pairs_sentinel&) noexcept {
+ return left.m_key_index != empty_key_index;
+ }
+
+ friend bool operator==(const pairs_sentinel&, const pairs_iterator& left) noexcept {
+ return left.m_key_index == empty_key_index;
+ }
+
+ friend bool operator!=(const pairs_sentinel&, const pairs_iterator& left) noexcept {
+ return left.m_key_index != empty_key_index;
+ }
+
+ ~pairs_iterator() {
+ if (m_key_index != empty_key_index) {
+ m_clear();
+ }
+ }
+
+ private:
+ void m_clear() noexcept {
+ lua_remove(m_L, m_key_index);
+ }
+
+ lua_State* m_L;
+ protected_function m_next_function_ref;
+ sol::reference m_table_ref;
+ std::pair<object, object> m_cached_key_value_pair;
+ int m_key_index;
+ int m_iteration_index;
+ };
+
+ template <typename Source>
+ class basic_pairs_range {
+ private:
+ using source_t = std::add_lvalue_reference_t<Source>;
+ source_t m_source;
+
+ public:
+ using iterator = pairs_iterator;
+ using const_iterator = pairs_iterator;
+
+ basic_pairs_range(source_t source_) noexcept : m_source(source_) {
+ }
+
+ iterator begin() noexcept {
+ return iterator(m_source);
+ }
+
+ iterator begin() const noexcept {
+ return iterator(m_source);
+ }
+
+ const_iterator cbegin() const noexcept {
+ return const_iterator(m_source);
+ }
+
+ pairs_sentinel end() noexcept {
+ return {};
+ }
+
+ pairs_sentinel end() const noexcept {
+ return {};
+ }
+
+ pairs_sentinel cend() const noexcept {
+ return {};
+ }
+ };
+} // namespace sol
+
+// end of sol/pairs_iterator.hpp
+
+namespace sol {
+ namespace detail {
+ template <std::size_t n>
+ struct clean {
+ lua_State* L;
+ clean(lua_State* luastate) : L(luastate) {
+ }
+ ~clean() {
+ lua_pop(L, static_cast<int>(n));
+ }
+ };
+
+ struct ref_clean {
+ lua_State* L;
+ int& pop_count;
+
+ ref_clean(lua_State* L_, int& pop_count_) noexcept : L(L_), pop_count(pop_count_) {
+ }
+ ~ref_clean() {
+ lua_pop(L, static_cast<int>(pop_count));
+ }
+ };
+
+ inline int fail_on_newindex(lua_State* L_) {
+ return luaL_error(L_, "sol: cannot modify the elements of an enumeration table");
+ }
+
+ } // namespace detail
+
+ template <bool top_level, typename ref_t>
+ class basic_table_core : public basic_object<ref_t> {
+ private:
+ using base_t = basic_object<ref_t>;
+
+ friend class state;
+ friend class state_view;
+ template <typename, typename>
+ friend class basic_usertype;
+ template <typename>
+ friend class basic_metatable;
+
+ template <typename T>
+ using is_get_direct_tableless = meta::boolean<stack::stack_detail::is_get_direct_tableless_v<T, top_level, false>>;
+
+ template <typename T>
+ using is_raw_get_direct_tableless = std::false_type;
+
+ template <typename T>
+ using is_set_direct_tableless = meta::boolean<stack::stack_detail::is_set_direct_tableless_v<T, top_level, false>>;
+
+ template <typename T>
+ using is_raw_set_direct_tableless = std::false_type;
+
+ template <bool raw, typename... Ret, typename... Keys>
+ decltype(auto) tuple_get(int table_index, Keys&&... keys) const {
+ if constexpr (sizeof...(Ret) < 2) {
+ return traverse_get_single_maybe_tuple<raw, Ret...>(table_index, std::forward<Keys>(keys)...);
+ }
+ else {
+ using multi_ret = decltype(stack::pop<std::tuple<Ret...>>(nullptr));
+ return multi_ret(traverse_get_single_maybe_tuple<raw, Ret>(table_index, std::forward<Keys>(keys))...);
+ }
+ }
+
+ template <bool raw, typename Ret, size_t... I, typename Key>
+ decltype(auto) traverse_get_single_tuple(int table_index, std::index_sequence<I...>, Key&& key) const {
+ return traverse_get_single<raw, Ret>(table_index, std::get<I>(std::forward<Key>(key))...);
+ }
+
+ template <bool raw, typename Ret, typename Key>
+ decltype(auto) traverse_get_single_maybe_tuple(int table_index, Key&& key) const {
+ if constexpr (meta::is_tuple_v<meta::unqualified_t<Key>>) {
+ return traverse_get_single_tuple<raw, Ret>(
+ table_index, std::make_index_sequence<std::tuple_size_v<meta::unqualified_t<Key>>>(), std::forward<Key>(key));
+ }
+ else {
+ return traverse_get_single<raw, Ret>(table_index, std::forward<Key>(key));
+ }
+ }
+
+ template <bool raw, typename Ret, typename... Keys>
+ decltype(auto) traverse_get_single(int table_index, Keys&&... keys) const {
+ constexpr static bool global = (meta::count_for_to_pack_v < 1, is_get_direct_tableless, meta::unqualified_t<Keys>... >> 0);
+ if constexpr (meta::is_optional_v<meta::unqualified_t<Ret>>) {
+ int popcount = 0;
+ detail::ref_clean c(base_t::lua_state(), popcount);
+ return traverse_get_deep_optional<global, raw, detail::insert_mode::none, Ret>(popcount, table_index, std::forward<Keys>(keys)...);
+ }
+ else {
+ detail::clean<sizeof...(Keys) - meta::count_for_pack_v<detail::is_insert_mode, meta::unqualified_t<Keys>...>> c(base_t::lua_state());
+ return traverse_get_deep<global, raw, detail::insert_mode::none, Ret>(table_index, std::forward<Keys>(keys)...);
+ }
+ }
+
+ template <bool raw, typename Pairs, std::size_t... I>
+ void tuple_set(std::index_sequence<I...>, Pairs&& pairs) {
+ constexpr static bool global = (meta::count_even_for_pack_v < is_set_direct_tableless,
+ meta::unqualified_t<decltype(std::get<I * 2>(std::forward<Pairs>(pairs)))>... >> 0);
+ auto pp = stack::push_pop<global>(*this);
+ int table_index = pp.index_of(*this);
+ lua_State* L = base_t::lua_state();
+ (void)table_index;
+ (void)L;
+ void(detail::swallow { (stack::set_field<(top_level), raw>(
+ L, std::get<I * 2>(std::forward<Pairs>(pairs)), std::get<I * 2 + 1>(std::forward<Pairs>(pairs)), table_index),
+ 0)... });
+ }
+
+ template <bool global, bool raw, detail::insert_mode mode, typename T, typename Key, typename... Keys>
+ decltype(auto) traverse_get_deep(int table_index, Key&& key, Keys&&... keys) const {
+ if constexpr (std::is_same_v<meta::unqualified_t<Key>, create_if_nil_t>) {
+ (void)key;
+ return traverse_get_deep<false, raw, static_cast<detail::insert_mode>(mode | detail::insert_mode::create_if_nil), T>(
+ table_index, std::forward<Keys>(keys)...);
+ }
+ else {
+ lua_State* L = base_t::lua_state();
+ stack::get_field<global, raw>(L, std::forward<Key>(key), table_index);
+ if constexpr (sizeof...(Keys) > 0) {
+ if constexpr ((mode & detail::insert_mode::create_if_nil) == detail::insert_mode::create_if_nil) {
+ type t = type_of(L, -1);
+ if (t == type::lua_nil || t == type::none) {
+ lua_pop(L, 1);
+ stack::push(L, new_table(0, 0));
+ }
+ }
+ return traverse_get_deep<false, raw, mode, T>(lua_gettop(L), std::forward<Keys>(keys)...);
+ }
+ else {
+ if constexpr ((mode & detail::insert_mode::create_if_nil) == detail::insert_mode::create_if_nil) {
+ type t = type_of(L, -1);
+ if ((t == type::lua_nil || t == type::none) && (is_table_like_v<T>)) {
+ lua_pop(L, 1);
+ stack::push(L, new_table(0, 0));
+ }
+ }
+ return stack::get<T>(L);
+ }
+ }
+ }
+
+ template <bool global, bool raw, detail::insert_mode mode, typename T, typename Key, typename... Keys>
+ decltype(auto) traverse_get_deep_optional(int& popcount, int table_index, Key&& key, Keys&&... keys) const {
+ if constexpr (std::is_same_v<meta::unqualified_t<Key>, create_if_nil_t>) {
+ constexpr detail::insert_mode new_mode = static_cast<detail::insert_mode>(mode | detail::insert_mode::create_if_nil);
+ (void)key;
+ return traverse_get_deep_optional<global, raw, new_mode, T>(popcount, table_index, std::forward<Keys>(keys)...);
+ }
+ else if constexpr (std::is_same_v<meta::unqualified_t<Key>, update_if_empty_t>) {
+ constexpr detail::insert_mode new_mode = static_cast<detail::insert_mode>(mode | detail::insert_mode::update_if_empty);
+ (void)key;
+ return traverse_get_deep_optional<global, raw, new_mode, T>(popcount, table_index, std::forward<Keys>(keys)...);
+ }
+ else if constexpr (std::is_same_v<meta::unqualified_t<Key>, override_value_t>) {
+ constexpr detail::insert_mode new_mode = static_cast<detail::insert_mode>(mode | detail::insert_mode::override_value);
+ (void)key;
+ return traverse_get_deep_optional<global, raw, new_mode, T>(popcount, table_index, std::forward<Keys>(keys)...);
+ }
+ else {
+ if constexpr (sizeof...(Keys) > 0) {
+ lua_State* L = base_t::lua_state();
+ auto p = stack::probe_get_field<global, raw>(L, std::forward<Key>(key), table_index);
+ popcount += p.levels;
+ if (!p.success) {
+ if constexpr ((mode & detail::insert_mode::create_if_nil) == detail::insert_mode::create_if_nil) {
+ lua_pop(L, 1);
+ constexpr bool is_seq = meta::count_for_to_pack_v < 1, std::is_integral, Keys... >> 0;
+ stack::push(L, new_table(static_cast<int>(is_seq), static_cast<int>(!is_seq)));
+ stack::set_field<global, raw>(L, std::forward<Key>(key), stack_reference(L, -1), table_index);
+ }
+ else {
+ return T(nullopt);
+ }
+ }
+ return traverse_get_deep_optional<false, raw, mode, T>(popcount, lua_gettop(L), std::forward<Keys>(keys)...);
+ }
+ else {
+ using R = decltype(stack::get<T>(nullptr));
+ using value_type = typename meta::unqualified_t<R>::value_type;
+ lua_State* L = base_t::lua_state();
+ auto p = stack::probe_get_field<global, raw, value_type>(L, key, table_index);
+ popcount += p.levels;
+ if (!p.success) {
+ if constexpr ((mode & detail::insert_mode::create_if_nil) == detail::insert_mode::create_if_nil) {
+ lua_pop(L, 1);
+ stack::push(L, new_table(0, 0));
+ stack::set_field<global, raw>(L, std::forward<Key>(key), stack_reference(L, -1), table_index);
+ if (stack::check<value_type>(L, lua_gettop(L), &no_panic)) {
+ return stack::get<T>(L);
+ }
+ }
+ return R(nullopt);
+ }
+ return stack::get<T>(L);
+ }
+ }
+ }
+
+ template <bool global, bool raw, detail::insert_mode mode, typename Key, typename... Keys>
+ void traverse_set_deep(int table_index, Key&& key, Keys&&... keys) const {
+ using KeyU = meta::unqualified_t<Key>;
+ if constexpr (std::is_same_v<KeyU, update_if_empty_t>) {
+ (void)key;
+ traverse_set_deep<global, raw, static_cast<detail::insert_mode>(mode | detail::insert_mode::update_if_empty)>(
+ table_index, std::forward<Keys>(keys)...);
+ }
+ else if constexpr (std::is_same_v<KeyU, create_if_nil_t>) {
+ (void)key;
+ traverse_set_deep<global, raw, static_cast<detail::insert_mode>(mode | detail::insert_mode::create_if_nil)>(
+ table_index, std::forward<Keys>(keys)...);
+ }
+ else if constexpr (std::is_same_v<KeyU, override_value_t>) {
+ (void)key;
+ traverse_set_deep<global, raw, static_cast<detail::insert_mode>(mode | detail::insert_mode::override_value)>(
+ table_index, std::forward<Keys>(keys)...);
+ }
+ else {
+ lua_State* L = base_t::lua_state();
+ if constexpr (sizeof...(Keys) == 1) {
+ if constexpr ((mode & detail::insert_mode::update_if_empty) == detail::insert_mode::update_if_empty) {
+ auto p = stack::probe_get_field<global, raw>(L, key, table_index);
+ lua_pop(L, p.levels);
+ if (!p.success) {
+ stack::set_field<global, raw>(L, std::forward<Key>(key), std::forward<Keys>(keys)..., table_index);
+ }
+ }
+ else {
+ stack::set_field<global, raw>(L, std::forward<Key>(key), std::forward<Keys>(keys)..., table_index);
+ }
+ }
+ else {
+ if constexpr (mode != detail::insert_mode::none) {
+ stack::get_field<global, raw>(L, key, table_index);
+ type vt = type_of(L, -1);
+ if constexpr ((mode & detail::insert_mode::update_if_empty) == detail::insert_mode::update_if_empty
+ || (mode & detail::insert_mode::create_if_nil) == detail::insert_mode::create_if_nil) {
+ if (vt == type::lua_nil || vt == type::none) {
+ constexpr bool is_seq = meta::count_for_to_pack_v < 1, std::is_integral, Keys... >> 0;
+ lua_pop(L, 1);
+ stack::push(L, new_table(static_cast<int>(is_seq), static_cast<int>(!is_seq)));
+ stack::set_field<global, raw>(L, std::forward<Key>(key), stack_reference(L, -1), table_index);
+ }
+ }
+ else {
+ if (vt != type::table) {
+ constexpr bool is_seq = meta::count_for_to_pack_v < 1, std::is_integral, Keys... >> 0;
+ lua_pop(L, 1);
+ stack::push(L, new_table(static_cast<int>(is_seq), static_cast<int>(!is_seq)));
+ stack::set_field<global, raw>(L, std::forward<Key>(key), stack_reference(L, -1), table_index);
+ }
+ }
+ }
+ else {
+ stack::get_field<global, raw>(L, std::forward<Key>(key), table_index);
+ }
+ traverse_set_deep<false, raw, mode>(lua_gettop(L), std::forward<Keys>(keys)...);
+ }
+ }
+ }
+
+ protected:
+ basic_table_core(detail::no_safety_tag, lua_nil_t n) : base_t(n) {
+ }
+ basic_table_core(detail::no_safety_tag, lua_State* L, int index) : base_t(L, index) {
+ }
+ basic_table_core(detail::no_safety_tag, lua_State* L, ref_index index) : base_t(L, index) {
+ }
+ template <typename T,
+ meta::enable<meta::neg<meta::any_same<meta::unqualified_t<T>, basic_table_core>>, meta::neg<std::is_same<ref_t, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_table_core(detail::no_safety_tag, T&& r) noexcept : base_t(std::forward<T>(r)) {
+ }
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_table_core(detail::no_safety_tag, lua_State* L, T&& r) noexcept : base_t(L, std::forward<T>(r)) {
+ }
+
+ public:
+ using iterator = basic_table_iterator<ref_t>;
+ using const_iterator = iterator;
+
+ using base_t::lua_state;
+
+ basic_table_core() noexcept = default;
+ basic_table_core(const basic_table_core&) = default;
+ basic_table_core(basic_table_core&&) = default;
+ basic_table_core& operator=(const basic_table_core&) = default;
+ basic_table_core& operator=(basic_table_core&&) = default;
+
+ basic_table_core(const stack_reference& r) : basic_table_core(r.lua_state(), r.stack_index()) {
+ }
+
+ basic_table_core(stack_reference&& r) : basic_table_core(r.lua_state(), r.stack_index()) {
+ }
+
+ template <typename T, meta::enable_any<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_table_core(lua_State* L, T&& r) : base_t(L, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ int table_index = pp.index_of(*this);
+ constructor_handler handler {};
+ stack::check<basic_table_core>(lua_state(), table_index, handler);
+#endif // Safety
+ }
+
+ basic_table_core(lua_State* L, const new_table& nt) : base_t(L, -stack::push(L, nt)) {
+ if (!is_stack_based<meta::unqualified_t<ref_t>>::value) {
+ lua_pop(L, 1);
+ }
+ }
+
+ basic_table_core(lua_State* L, int index = -1) : basic_table_core(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_table_core>(L, index, handler);
+#endif // Safety
+ }
+
+ basic_table_core(lua_State* L, ref_index index) : basic_table_core(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ int table_index = pp.index_of(*this);
+ constructor_handler handler {};
+ stack::check<basic_table_core>(lua_state(), table_index, handler);
+#endif // Safety
+ }
+
+ template <typename T,
+ meta::enable<meta::neg<meta::any_same<meta::unqualified_t<T>, basic_table_core>>, meta::neg<std::is_same<ref_t, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_table_core(T&& r) noexcept : basic_table_core(detail::no_safety, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_table<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ int table_index = pp.index_of(*this);
+ constructor_handler handler {};
+ stack::check<basic_table_core>(lua_state(), table_index, handler);
+ }
+#endif // Safety
+ }
+
+ basic_table_core(lua_nil_t r) noexcept : basic_table_core(detail::no_safety, r) {
+ }
+
+ basic_table_core(lua_State* L, global_tag_t t) noexcept : base_t(L, t) {
+ }
+
+ iterator begin() const {
+ if (this->get_type() == type::table) {
+ return iterator(*this);
+ }
+ return iterator();
+ }
+
+ iterator end() const {
+ return iterator();
+ }
+
+ const_iterator cbegin() const {
+ return begin();
+ }
+
+ const_iterator cend() const {
+ return end();
+ }
+
+ basic_pairs_range<basic_table_core> pairs() noexcept {
+ return basic_pairs_range<basic_table_core>(*this);
+ }
+
+ basic_pairs_range<const basic_table_core> pairs() const noexcept {
+ return basic_pairs_range<const basic_table_core>(*this);
+ }
+
+ void clear() {
+ auto pp = stack::push_pop<false>(*this);
+ int table_index = pp.index_of(*this);
+ stack::clear(lua_state(), table_index);
+ }
+
+ template <typename... Ret, typename... Keys>
+ decltype(auto) get(Keys&&... keys) const {
+ static_assert(sizeof...(Keys) == sizeof...(Ret), "number of keys and number of return types do not match");
+ constexpr static bool global = meta::all<meta::boolean<top_level>, is_get_direct_tableless<meta::unqualified_t<Keys>>...>::value;
+ auto pp = stack::push_pop<global>(*this);
+ int table_index = pp.index_of(*this);
+ return tuple_get<false, Ret...>(table_index, std::forward<Keys>(keys)...);
+ }
+
+ template <typename T, typename Key>
+ decltype(auto) get_or(Key&& key, T&& otherwise) const {
+ typedef decltype(get<T>("")) U;
+ optional<U> option = get<optional<U>>(std::forward<Key>(key));
+ if (option) {
+ return static_cast<U>(option.value());
+ }
+ return static_cast<U>(std::forward<T>(otherwise));
+ }
+
+ template <typename T, typename Key, typename D>
+ decltype(auto) get_or(Key&& key, D&& otherwise) const {
+ optional<T> option = get<optional<T>>(std::forward<Key>(key));
+ if (option) {
+ return static_cast<T>(option.value());
+ }
+ return static_cast<T>(std::forward<D>(otherwise));
+ }
+
+ template <typename T, typename... Keys>
+ decltype(auto) traverse_get(Keys&&... keys) const {
+ static_assert(sizeof...(Keys) > 0, "must pass at least 1 key to get");
+ constexpr static bool global = (meta::count_for_to_pack_v < 1, is_get_direct_tableless, meta::unqualified_t<Keys>... >> 0);
+ auto pp = stack::push_pop<global>(*this);
+ int table_index = pp.index_of(*this);
+ return traverse_get_single<false, T>(table_index, std::forward<Keys>(keys)...);
+ }
+
+ template <typename... Keys>
+ basic_table_core& traverse_set(Keys&&... keys) {
+ static_assert(sizeof...(Keys) > 1, "must pass at least 1 key and 1 value to set");
+ constexpr static bool global
+ = (meta::count_when_for_to_pack_v < detail::is_not_insert_mode, 1, is_set_direct_tableless, meta::unqualified_t<Keys>... >> 0);
+ auto pp = stack::push_pop<global>(*this);
+ int table_index = pp.index_of(*this);
+ lua_State* L = base_t::lua_state();
+ auto pn = stack::pop_n(L, static_cast<int>(sizeof...(Keys) - 2 - meta::count_for_pack_v<detail::is_insert_mode, meta::unqualified_t<Keys>...>));
+ traverse_set_deep<top_level, false, detail::insert_mode::none>(table_index, std::forward<Keys>(keys)...);
+ return *this;
+ }
+
+ template <typename... Args>
+ basic_table_core& set(Args&&... args) {
+ if constexpr (sizeof...(Args) == 2) {
+ traverse_set(std::forward<Args>(args)...);
+ }
+ else {
+ tuple_set<false>(std::make_index_sequence<sizeof...(Args) / 2>(), std::forward_as_tuple(std::forward<Args>(args)...));
+ }
+ return *this;
+ }
+
+ template <typename... Ret, typename... Keys>
+ decltype(auto) raw_get(Keys&&... keys) const {
+ static_assert(sizeof...(Keys) == sizeof...(Ret), "number of keys and number of return types do not match");
+ constexpr static bool global = (meta::count_for_to_pack_v < 1, is_raw_get_direct_tableless, meta::unqualified_t<Keys>... >> 0);
+ auto pp = stack::push_pop<global>(*this);
+ int table_index = pp.index_of(*this);
+ return tuple_get<true, Ret...>(table_index, std::forward<Keys>(keys)...);
+ }
+
+ template <typename T, typename Key>
+ decltype(auto) raw_get_or(Key&& key, T&& otherwise) const {
+ typedef decltype(raw_get<T>("")) U;
+ optional<U> option = raw_get<optional<U>>(std::forward<Key>(key));
+ if (option) {
+ return static_cast<U>(option.value());
+ }
+ return static_cast<U>(std::forward<T>(otherwise));
+ }
+
+ template <typename T, typename Key, typename D>
+ decltype(auto) raw_get_or(Key&& key, D&& otherwise) const {
+ optional<T> option = raw_get<optional<T>>(std::forward<Key>(key));
+ if (option) {
+ return static_cast<T>(option.value());
+ }
+ return static_cast<T>(std::forward<D>(otherwise));
+ }
+
+ template <typename T, typename... Keys>
+ decltype(auto) traverse_raw_get(Keys&&... keys) const {
+ constexpr static bool global = (meta::count_for_to_pack_v < 1, is_raw_get_direct_tableless, meta::unqualified_t<Keys>... >> 0);
+ auto pp = stack::push_pop<global>(*this);
+ int table_index = pp.index_of(*this);
+ return traverse_get_single<true, T>(table_index, std::forward<Keys>(keys)...);
+ }
+
+ template <typename... Keys>
+ basic_table_core& traverse_raw_set(Keys&&... keys) {
+ constexpr static bool global = (meta::count_for_to_pack_v < 1, is_raw_set_direct_tableless, meta::unqualified_t<Keys>... >> 0);
+ auto pp = stack::push_pop<global>(*this);
+ lua_State* L = base_t::lua_state();
+ auto pn = stack::pop_n(L, static_cast<int>(sizeof...(Keys) - 2 - meta::count_for_pack_v<detail::is_insert_mode, meta::unqualified_t<Keys>...>));
+ traverse_set_deep<top_level, true, false>(std::forward<Keys>(keys)...);
+ return *this;
+ }
+
+ template <typename... Args>
+ basic_table_core& raw_set(Args&&... args) {
+ tuple_set<true>(std::make_index_sequence<sizeof...(Args) / 2>(), std::forward_as_tuple(std::forward<Args>(args)...));
+ return *this;
+ }
+
+ template <typename Class, typename Key>
+ usertype<Class> new_usertype(Key&& key);
+
+ template <typename Class, typename Key, automagic_flags enrollment_flags>
+ usertype<Class> new_usertype(Key&& key, constant_automagic_enrollments<enrollment_flags> enrollment);
+
+ template <typename Class, typename Key>
+ usertype<Class> new_usertype(Key&& key, automagic_enrollments enrollment);
+
+ template <typename Class, typename Key, typename Arg, typename... Args,
+ typename = std::enable_if_t<!std::is_base_of_v<automagic_enrollments, meta::unqualified_t<Arg>>>>
+ usertype<Class> new_usertype(Key&& key, Arg&& arg, Args&&... args);
+
+ template <bool read_only = true, typename... Args>
+ table new_enum(const string_view& name, Args&&... args) {
+ table target = create_with(std::forward<Args>(args)...);
+ if constexpr (read_only) {
+ // Need to create a special iterator to handle this
+ table x
+ = create_with(meta_function::new_index, detail::fail_on_newindex, meta_function::index, target, meta_function::pairs, stack::stack_detail::readonly_pairs);
+ table shim = create_named(name, metatable_key, x);
+ return shim;
+ }
+ else {
+ set(name, target);
+ return target;
+ }
+ }
+
+ template <typename T, bool read_only = true>
+ table new_enum(const string_view& name, std::initializer_list<std::pair<string_view, T>> items) {
+ table target = create(static_cast<int>(items.size()), static_cast<int>(0));
+ for (const auto& kvp : items) {
+ target.set(kvp.first, kvp.second);
+ }
+ if constexpr (read_only) {
+ table x = create_with(meta_function::new_index, detail::fail_on_newindex, meta_function::index, target);
+ table shim = create_named(name, metatable_key, x);
+ return shim;
+ }
+ else {
+ set(name, target);
+ return target;
+ }
+ }
+
+ template <typename Key = object, typename Value = object, typename Fx>
+ void for_each(Fx&& fx) const {
+ lua_State* L = base_t::lua_state();
+ if constexpr (std::is_invocable_v<Fx, Key, Value>) {
+ auto pp = stack::push_pop(*this);
+ int table_index = pp.index_of(*this);
+ stack::push(L, lua_nil);
+ while (lua_next(L, table_index)) {
+ Key key(L, -2);
+ Value value(L, -1);
+ auto pn = stack::pop_n(L, 1);
+ fx(key, value);
+ }
+ }
+ else {
+ auto pp = stack::push_pop(*this);
+ int table_index = pp.index_of(*this);
+ stack::push(L, lua_nil);
+ while (lua_next(L, table_index)) {
+ Key key(L, -2);
+ Value value(L, -1);
+ auto pn = stack::pop_n(L, 1);
+ std::pair<Key&, Value&> keyvalue(key, value);
+ fx(keyvalue);
+ }
+ }
+ }
+
+ size_t size() const {
+ auto pp = stack::push_pop(*this);
+ int table_index = pp.index_of(*this);
+ lua_State* L = base_t::lua_state();
+ lua_len(L, table_index);
+ return stack::pop<size_t>(L);
+ }
+
+ bool empty() const {
+ return cbegin() == cend();
+ }
+
+ template <typename T>
+ auto operator[](T&& key) & {
+ return table_proxy<basic_table_core&, detail::proxy_key_t<T>>(*this, std::forward<T>(key));
+ }
+
+ template <typename T>
+ auto operator[](T&& key) const& {
+ return table_proxy<const basic_table_core&, detail::proxy_key_t<T>>(*this, std::forward<T>(key));
+ }
+
+ template <typename T>
+ auto operator[](T&& key) && {
+ return table_proxy<basic_table_core, detail::proxy_key_t<T>>(std::move(*this), std::forward<T>(key));
+ }
+
+ template <typename Sig, typename Key, typename... Args>
+ basic_table_core& set_function(Key&& key, Args&&... args) {
+ set_fx(types<Sig>(), std::forward<Key>(key), std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename Key, typename... Args>
+ basic_table_core& set_function(Key&& key, Args&&... args) {
+ set_fx(types<>(), std::forward<Key>(key), std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename... Args>
+ basic_table_core& add(Args&&... args) {
+ auto pp = stack::push_pop(*this);
+ int table_index = pp.index_of(*this);
+ lua_State* L = base_t::lua_state();
+ (void)detail::swallow { 0, (stack::stack_detail::raw_table_set(L, std::forward<Args>(args), table_index), 0)... };
+ return *this;
+ }
+
+ private:
+ template <typename R, typename... Args, typename Fx, typename Key, typename = std::invoke_result_t<Fx, Args...>>
+ void set_fx(types<R(Args...)>, Key&& key, Fx&& fx) {
+ set_resolved_function<R(Args...)>(std::forward<Key>(key), std::forward<Fx>(fx));
+ }
+
+ template <typename Fx, typename Key, meta::enable<meta::is_specialization_of<meta::unqualified_t<Fx>, overload_set>> = meta::enabler>
+ void set_fx(types<>, Key&& key, Fx&& fx) {
+ set(std::forward<Key>(key), std::forward<Fx>(fx));
+ }
+
+ template <typename Fx, typename Key, typename... Args,
+ meta::disable<meta::is_specialization_of<meta::unqualified_t<Fx>, overload_set>> = meta::enabler>
+ void set_fx(types<>, Key&& key, Fx&& fx, Args&&... args) {
+ set(std::forward<Key>(key), as_function_reference(std::forward<Fx>(fx), std::forward<Args>(args)...));
+ }
+
+ template <typename... Sig, typename... Args, typename Key>
+ void set_resolved_function(Key&& key, Args&&... args) {
+ set(std::forward<Key>(key), as_function_reference<function_sig<Sig...>>(std::forward<Args>(args)...));
+ }
+
+ public:
+ static inline table create(lua_State* L, int narr = 0, int nrec = 0) {
+ lua_createtable(L, narr, nrec);
+ table result(L);
+ lua_pop(L, 1);
+ return result;
+ }
+
+ template <typename Key, typename Value, typename... Args>
+ static inline table create(lua_State* L, int narr, int nrec, Key&& key, Value&& value, Args&&... args) {
+ lua_createtable(L, narr, nrec);
+ table result(L);
+ result.set(std::forward<Key>(key), std::forward<Value>(value), std::forward<Args>(args)...);
+ lua_pop(L, 1);
+ return result;
+ }
+
+ template <typename... Args>
+ static inline table create_with(lua_State* L, Args&&... args) {
+ static_assert(sizeof...(Args) % 2 == 0, "You must have an even number of arguments for a key, value ... list.");
+ constexpr int narr = static_cast<int>(meta::count_odd_for_pack_v<std::is_integral, Args...>);
+ return create(L, narr, static_cast<int>((sizeof...(Args) / 2) - narr), std::forward<Args>(args)...);
+ }
+
+ table create(int narr = 0, int nrec = 0) {
+ return create(base_t::lua_state(), narr, nrec);
+ }
+
+ template <typename Key, typename Value, typename... Args>
+ table create(int narr, int nrec, Key&& key, Value&& value, Args&&... args) {
+ return create(base_t::lua_state(), narr, nrec, std::forward<Key>(key), std::forward<Value>(value), std::forward<Args>(args)...);
+ }
+
+ template <typename Name>
+ table create(Name&& name, int narr = 0, int nrec = 0) {
+ table x = create(base_t::lua_state(), narr, nrec);
+ this->set(std::forward<Name>(name), x);
+ return x;
+ }
+
+ template <typename Name, typename Key, typename Value, typename... Args>
+ table create(Name&& name, int narr, int nrec, Key&& key, Value&& value, Args&&... args) {
+ table x = create(base_t::lua_state(), narr, nrec, std::forward<Key>(key), std::forward<Value>(value), std::forward<Args>(args)...);
+ this->set(std::forward<Name>(name), x);
+ return x;
+ }
+
+ template <typename... Args>
+ table create_with(Args&&... args) {
+ return create_with(base_t::lua_state(), std::forward<Args>(args)...);
+ }
+
+ template <typename Name, typename... Args>
+ table create_named(Name&& name, Args&&... args) {
+ static const int narr = static_cast<int>(meta::count_even_for_pack_v<std::is_integral, Args...>);
+ return create(std::forward<Name>(name), narr, (sizeof...(Args) / 2) - narr, std::forward<Args>(args)...);
+ }
+ };
+} // namespace sol
+
+// end of sol/table_core.hpp
+
+namespace sol {
+
+ template <typename base_type>
+ class basic_metatable : public basic_table<base_type> {
+ typedef basic_table<base_type> base_t;
+ friend class state;
+ friend class state_view;
+
+ protected:
+ basic_metatable(detail::no_safety_tag, lua_nil_t n) : base_t(n) {
+ }
+ basic_metatable(detail::no_safety_tag, lua_State* L, int index) : base_t(L, index) {
+ }
+ basic_metatable(detail::no_safety_tag, lua_State* L, ref_index index) : base_t(L, index) {
+ }
+ template <typename T,
+ meta::enable<meta::neg<meta::any_same<meta::unqualified_t<T>, basic_metatable>>, meta::neg<std::is_same<base_type, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_metatable(detail::no_safety_tag, T&& r) noexcept : base_t(std::forward<T>(r)) {
+ }
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_metatable(detail::no_safety_tag, lua_State* L, T&& r) noexcept : base_t(L, std::forward<T>(r)) {
+ }
+
+ template <typename R, typename... Args, typename Fx, typename Key, typename = std::invoke_result_t<Fx, Args...>>
+ void set_fx(types<R(Args...)>, Key&& key, Fx&& fx) {
+ set_resolved_function<R(Args...)>(std::forward<Key>(key), std::forward<Fx>(fx));
+ }
+
+ template <typename Fx, typename Key, meta::enable<meta::is_specialization_of<meta::unqualified_t<Fx>, overload_set>> = meta::enabler>
+ void set_fx(types<>, Key&& key, Fx&& fx) {
+ set(std::forward<Key>(key), std::forward<Fx>(fx));
+ }
+
+ template <typename Fx, typename Key, typename... Args,
+ meta::disable<meta::is_specialization_of<meta::unqualified_t<Fx>, overload_set>> = meta::enabler>
+ void set_fx(types<>, Key&& key, Fx&& fx, Args&&... args) {
+ set(std::forward<Key>(key), as_function_reference(std::forward<Fx>(fx), std::forward<Args>(args)...));
+ }
+
+ template <typename... Sig, typename... Args, typename Key>
+ void set_resolved_function(Key&& key, Args&&... args) {
+ set(std::forward<Key>(key), as_function_reference<function_sig<Sig...>>(std::forward<Args>(args)...));
+ }
+
+ public:
+ using base_t::lua_state;
+
+ basic_metatable() noexcept = default;
+ basic_metatable(const basic_metatable&) = default;
+ basic_metatable(basic_metatable&&) = default;
+ basic_metatable& operator=(const basic_metatable&) = default;
+ basic_metatable& operator=(basic_metatable&&) = default;
+ basic_metatable(const stack_reference& r) : basic_metatable(r.lua_state(), r.stack_index()) {
+ }
+ basic_metatable(stack_reference&& r) : basic_metatable(r.lua_state(), r.stack_index()) {
+ }
+ template <typename T, meta::enable_any<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_metatable(lua_State* L, T&& r) : base_t(L, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_metatable>(lua_state(), -1, handler);
+#endif // Safety
+ }
+ basic_metatable(lua_State* L, int index = -1) : basic_metatable(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_metatable>(L, index, handler);
+#endif // Safety
+ }
+ basic_metatable(lua_State* L, ref_index index) : basic_metatable(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_metatable>(lua_state(), -1, handler);
+#endif // Safety
+ }
+ template <typename T,
+ meta::enable<meta::neg<meta::any_same<meta::unqualified_t<T>, basic_metatable>>, meta::neg<std::is_same<base_type, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_metatable(T&& r) noexcept : basic_metatable(detail::no_safety, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_table<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_metatable>(base_t::lua_state(), -1, handler);
+ }
+#endif // Safety
+ }
+ basic_metatable(lua_nil_t r) noexcept : basic_metatable(detail::no_safety, r) {
+ }
+
+ template <typename Key, typename Value>
+ basic_metatable<base_type>& set(Key&& key, Value&& value);
+
+ template <typename Sig, typename Key, typename... Args>
+ basic_metatable& set_function(Key&& key, Args&&... args) {
+ set_fx(types<Sig>(), std::forward<Key>(key), std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename Key, typename... Args>
+ basic_metatable& set_function(Key&& key, Args&&... args) {
+ set_fx(types<>(), std::forward<Key>(key), std::forward<Args>(args)...);
+ return *this;
+ }
+
+ void unregister() {
+ using ustorage_base = u_detail::usertype_storage_base;
+
+ lua_State* L = this->lua_state();
+
+ auto pp = stack::push_pop(*this);
+ int top = lua_gettop(L);
+
+ stack_reference mt(L, -1);
+ stack::get_field(L, meta_function::gc_names, mt.stack_index());
+ if (type_of(L, -1) != type::table) {
+ lua_settop(L, top);
+ return;
+ }
+ stack_reference gc_names_table(L, -1);
+ stack::get_field(L, meta_function::storage, mt.stack_index());
+ if (type_of(L, -1) != type::lightuserdata) {
+ lua_settop(L, top);
+ return;
+ }
+ ustorage_base& base_storage = *static_cast<ustorage_base*>(stack::get<void*>(L, -1));
+ std::array<string_view, 6> registry_traits;
+ for (std::size_t i = 0; i < registry_traits.size(); ++i) {
+ u_detail::submetatable_type smt = static_cast<u_detail::submetatable_type>(i);
+ stack::get_field<false, true>(L, smt, gc_names_table.stack_index());
+ registry_traits[i] = stack::get<string_view>(L, -1);
+ }
+
+ // get the registry
+ stack_reference registry(L, raw_index(LUA_REGISTRYINDEX));
+ registry.push();
+ // eliminate all named entries for this usertype
+ // in the registry (luaL_newmetatable does
+ // [name] = new table
+ // in registry upon creation)
+ for (std::size_t i = 0; i < registry_traits.size(); ++i) {
+ u_detail::submetatable_type smt = static_cast<u_detail::submetatable_type>(i);
+ const string_view& gcmetakey = registry_traits[i];
+ if (smt == u_detail::submetatable_type::named) {
+ // use .data() to make it treat it like a c string,
+ // which it is...
+ stack::set_field<true>(L, gcmetakey.data(), lua_nil);
+ }
+ else {
+ // do not change the values in the registry: they need to be present
+ // no matter what, for safety's sake
+ // stack::set_field(L, gcmetakey, lua_nil, registry.stack_index());
+ }
+ }
+
+ // destroy all storage and tables
+ base_storage.clear();
+
+ // 6 strings from gc_names table,
+ // + 1 registry,
+ // + 1 gc_names table
+ // + 1 light userdata of storage
+ // + 1 registry
+ // 10 total, 4 left since popping off 6 gc_names tables
+ lua_settop(L, top);
+ }
+ };
+
+} // namespace sol
+
+// end of sol/metatable.hpp
+
+namespace sol {
+
+ template <typename T, typename base_type>
+ class basic_usertype : private basic_metatable<base_type> {
+ private:
+ using base_t = basic_metatable<base_type>;
+ using table_base_t = basic_table<base_type>;
+
+ template <typename>
+ friend class basic_metatable;
+
+ template <bool, typename>
+ friend class basic_table_core;
+
+ template <std::size_t... I, typename... Args>
+ void tuple_set(std::index_sequence<I...>, std::tuple<Args...>&& args) {
+ (void)args;
+ (void)detail::swallow { 0, (this->set(std::get<I * 2>(std::move(args)), std::get<I * 2 + 1>(std::move(args))), 0)... };
+ }
+
+ template <typename R, typename... Args, typename Fx, typename Key, typename = std::invoke_result_t<Fx, Args...>>
+ void set_fx(types<R(Args...)>, Key&& key, Fx&& fx) {
+ set_resolved_function<R(Args...)>(std::forward<Key>(key), std::forward<Fx>(fx));
+ }
+
+ template <typename Fx, typename Key, meta::enable<meta::is_specialization_of<meta::unqualified_t<Fx>, overload_set>> = meta::enabler>
+ void set_fx(types<>, Key&& key, Fx&& fx) {
+ set(std::forward<Key>(key), std::forward<Fx>(fx));
+ }
+
+ template <typename Fx, typename Key, typename... Args,
+ meta::disable<meta::is_specialization_of<meta::unqualified_t<Fx>, overload_set>> = meta::enabler>
+ void set_fx(types<>, Key&& key, Fx&& fx, Args&&... args) {
+ set(std::forward<Key>(key), as_function_reference(std::forward<Fx>(fx), std::forward<Args>(args)...));
+ }
+
+ template <typename... Sig, typename... Args, typename Key>
+ void set_resolved_function(Key&& key, Args&&... args) {
+ set(std::forward<Key>(key), as_function_reference<function_sig<Sig...>>(std::forward<Args>(args)...));
+ }
+
+ public:
+ using base_t::base_t;
+
+ using base_t::get;
+ using base_t::lua_state;
+ using base_t::pop;
+ using base_t::push;
+ using base_t::traverse_get;
+ using base_t::traverse_set;
+ using base_t::unregister;
+
+ template <typename Key, typename Value>
+ basic_usertype& set(Key&& key, Value&& value) {
+ optional<u_detail::usertype_storage<T>&> maybe_uts = u_detail::maybe_get_usertype_storage<T>(this->lua_state());
+ if (maybe_uts) {
+ u_detail::usertype_storage<T>& uts = *maybe_uts;
+ uts.set(this->lua_state(), std::forward<Key>(key), std::forward<Value>(value));
+ }
+ else {
+ using ValueU = meta::unqualified_t<Value>;
+ // cannot get metatable: try regular table set?
+ if constexpr (detail::is_non_factory_constructor_v<ValueU> || detail::is_policy_v<ValueU>) {
+ // tag constructors so we don't get destroyed by lack of info
+ table_base_t::set(std::forward<Key>(key), detail::tagged<T, Value>(std::forward<Value>(value)));
+ }
+ else {
+ table_base_t::set(std::forward<Key>(key), std::forward<Value>(value));
+ }
+ }
+ return *this;
+ }
+
+ template <typename Sig, typename Key, typename... Args>
+ basic_usertype& set_function(Key&& key, Args&&... args) {
+ set_fx(types<Sig>(), std::forward<Key>(key), std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename Key, typename... Args>
+ basic_usertype& set_function(Key&& key, Args&&... args) {
+ set_fx(types<>(), std::forward<Key>(key), std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename Key>
+ usertype_proxy<basic_usertype&, std::decay_t<Key>> operator[](Key&& key) {
+ return usertype_proxy<basic_usertype&, std::decay_t<Key>>(*this, std::forward<Key>(key));
+ }
+
+ template <typename Key>
+ usertype_proxy<const basic_usertype&, std::decay_t<Key>> operator[](Key&& key) const {
+ return usertype_proxy<const basic_usertype&, std::decay_t<Key>>(*this, std::forward<Key>(key));
+ }
+ };
+
+} // namespace sol
+
+// end of sol/usertype.hpp
+
+// beginning of sol/table.hpp
+
+// beginning of sol/lua_table.hpp
+
+namespace sol {
+
+ template <typename ref_t>
+ struct basic_lua_table : basic_table_core<false, ref_t> {
+ private:
+ using base_t = basic_table_core<false, ref_t>;
+
+ friend class state;
+ friend class state_view;
+
+ public:
+ using base_t::lua_state;
+
+ basic_lua_table() noexcept = default;
+ basic_lua_table(const basic_lua_table&) = default;
+ basic_lua_table(basic_lua_table&&) = default;
+ basic_lua_table& operator=(const basic_lua_table&) = default;
+ basic_lua_table& operator=(basic_lua_table&&) = default;
+ basic_lua_table(const stack_reference& r) : basic_lua_table(r.lua_state(), r.stack_index()) {
+ }
+ basic_lua_table(stack_reference&& r) : basic_lua_table(r.lua_state(), r.stack_index()) {
+ }
+ template <typename T, meta::enable_any<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_lua_table(lua_State* L, T&& r) : base_t(L, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_lua_table>(lua_state(), -1, handler);
+#endif // Safety
+ }
+ basic_lua_table(lua_State* L, const new_table& nt) : base_t(L, nt) {
+ if (!is_stack_based<meta::unqualified_t<ref_t>>::value) {
+ lua_pop(L, 1);
+ }
+ }
+ basic_lua_table(lua_State* L, int index = -1) : base_t(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_lua_table>(L, index, handler);
+#endif // Safety
+ }
+ basic_lua_table(lua_State* L, ref_index index) : base_t(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_lua_table>(lua_state(), -1, handler);
+#endif // Safety
+ }
+ template <typename T,
+ meta::enable<meta::neg<meta::any_same<meta::unqualified_t<T>, basic_lua_table>>, meta::neg<std::is_same<ref_t, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_lua_table(T&& r) noexcept : basic_lua_table(detail::no_safety, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_table<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_lua_table>(lua_state(), -1, handler);
+ }
+#endif // Safety
+ }
+ basic_lua_table(lua_nil_t r) noexcept : basic_lua_table(detail::no_safety, r) {
+ }
+ };
+
+} // namespace sol
+
+// end of sol/lua_table.hpp
+
+namespace sol {
+ typedef table_core<false> table;
+
+ template <bool is_global, typename base_type>
+ template <typename Class, typename Key>
+ usertype<Class> basic_table_core<is_global, base_type>::new_usertype(Key&& key) {
+ constant_automagic_enrollments<> enrollments {};
+ return this->new_usertype<Class>(std::forward<Key>(key), std::move(enrollments));
+ }
+
+ template <bool is_global, typename base_type>
+ template <typename Class, typename Key, automagic_flags enrollment_flags>
+ usertype<Class> basic_table_core<is_global, base_type>::new_usertype(Key&& key, constant_automagic_enrollments<enrollment_flags> enrollments) {
+ int mt_index = u_detail::register_usertype<Class, enrollment_flags>(this->lua_state(), std::move(enrollments));
+ usertype<Class> mt(this->lua_state(), -mt_index);
+ lua_pop(this->lua_state(), 1);
+ set(std::forward<Key>(key), mt);
+ return mt;
+ }
+
+ template <bool is_global, typename base_type>
+ template <typename Class, typename Key>
+ usertype<Class> basic_table_core<is_global, base_type>::new_usertype(Key&& key, automagic_enrollments enrollments) {
+ int mt_index = u_detail::register_usertype<Class, automagic_flags::all>(this->lua_state(), std::move(enrollments));
+ usertype<Class> mt(this->lua_state(), -mt_index);
+ lua_pop(this->lua_state(), 1);
+ set(std::forward<Key>(key), mt);
+ return mt;
+ }
+
+ template <bool is_global, typename base_type>
+ template <typename Class, typename Key, typename Arg, typename... Args, typename>
+ usertype<Class> basic_table_core<is_global, base_type>::new_usertype(Key&& key, Arg&& arg, Args&&... args) {
+ constexpr automagic_flags enrollment_flags = meta::any_same_v<no_construction, meta::unqualified_t<Arg>, meta::unqualified_t<Args>...>
+ ? clear_flags(automagic_flags::all, automagic_flags::default_constructor)
+ : automagic_flags::all;
+ constant_automagic_enrollments<enrollment_flags> enrollments;
+ enrollments.default_constructor = !detail::any_is_constructor_v<Arg, Args...>;
+ enrollments.destructor = !detail::any_is_destructor_v<Arg, Args...>;
+ usertype<Class> ut = this->new_usertype<Class>(std::forward<Key>(key), std::move(enrollments));
+ static_assert(sizeof...(Args) % 2 == static_cast<std::size_t>(!detail::any_is_constructor_v<Arg>),
+ "you must pass an even number of arguments to new_usertype after first passing a constructor");
+ if constexpr (detail::any_is_constructor_v<Arg>) {
+ ut.set(meta_function::construct, std::forward<Arg>(arg));
+ ut.tuple_set(std::make_index_sequence<(sizeof...(Args)) / 2>(), std::forward_as_tuple(std::forward<Args>(args)...));
+ }
+ else {
+ ut.tuple_set(std::make_index_sequence<(sizeof...(Args) + 1) / 2>(), std::forward_as_tuple(std::forward<Arg>(arg), std::forward<Args>(args)...));
+ }
+ return ut;
+ }
+
+ template <typename base_type>
+ template <typename Key, typename Value>
+ basic_metatable<base_type>& basic_metatable<base_type>::set(Key&& key, Value&& value) {
+ this->push();
+ lua_State* L = this->lua_state();
+ int target = lua_gettop(L);
+ optional<u_detail::usertype_storage_base&> maybe_uts = nullopt;
+ maybe_uts = u_detail::maybe_get_usertype_storage_base(L, target);
+ if (maybe_uts) {
+ u_detail::usertype_storage_base& uts = *maybe_uts;
+ uts.set(L, std::forward<Key>(key), std::forward<Value>(value));
+ return *this;
+ }
+ else {
+ base_t::set(std::forward<Key>(key), std::forward<Value>(value));
+ }
+ this->pop();
+ return *this;
+ }
+
+ namespace stack {
+ template <>
+ struct unqualified_getter<metatable_key_t> {
+ static metatable get(lua_State* L, int index = -1) {
+ if (lua_getmetatable(L, index) == 0) {
+ return metatable(L, ref_index(LUA_REFNIL));
+ }
+ return metatable(L, -1);
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/table.hpp
+
+// beginning of sol/state.hpp
+
+// beginning of sol/state_view.hpp
+
+// beginning of sol/environment.hpp
+
+namespace sol {
+
+ template <typename base_type>
+ struct basic_environment : basic_table<base_type> {
+ private:
+ typedef basic_table<base_type> base_t;
+
+ public:
+ using base_t::lua_state;
+
+ basic_environment() noexcept = default;
+ basic_environment(const basic_environment&) = default;
+ basic_environment(basic_environment&&) = default;
+ basic_environment& operator=(const basic_environment&) = default;
+ basic_environment& operator=(basic_environment&&) = default;
+ basic_environment(const stack_reference& r) : basic_environment(r.lua_state(), r.stack_index()) {
+ }
+ basic_environment(stack_reference&& r) : basic_environment(r.lua_state(), r.stack_index()) {
+ }
+
+ basic_environment(lua_State* L, new_table nt) : base_t(L, std::move(nt)) {
+ }
+ template <bool b>
+ basic_environment(lua_State* L, new_table t, const basic_reference<b>& fallback) : basic_environment(L, std::move(t)) {
+ stack_table mt(L, new_table(0, 1));
+ mt.set(meta_function::index, fallback);
+ this->set(metatable_key, mt);
+ mt.pop();
+ }
+
+ basic_environment(env_key_t, const stack_reference& extraction_target)
+ : base_t(detail::no_safety, extraction_target.lua_state(), (stack::push_environment_of(extraction_target), -1)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<env_key_t>(this->lua_state(), -1, handler);
+#endif // Safety
+ lua_pop(this->lua_state(), 1);
+ }
+ template <bool b>
+ basic_environment(env_key_t, const basic_reference<b>& extraction_target)
+ : base_t(detail::no_safety, extraction_target.lua_state(), (stack::push_environment_of(extraction_target), -1)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<env_key_t>(this->lua_state(), -1, handler);
+#endif // Safety
+ lua_pop(this->lua_state(), 1);
+ }
+ basic_environment(lua_State* L, int index = -1) : base_t(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_environment>(L, index, handler);
+#endif // Safety
+ }
+ basic_environment(lua_State* L, ref_index index) : base_t(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_environment>(L, -1, handler);
+#endif // Safety
+ }
+ template <typename T,
+ meta::enable<meta::neg<meta::any_same<meta::unqualified_t<T>, basic_environment>>, meta::neg<std::is_same<base_type, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_environment(T&& r) noexcept : base_t(detail::no_safety, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_environment<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_environment>(lua_state(), -1, handler);
+ }
+#endif // Safety
+ }
+ basic_environment(lua_nil_t r) noexcept : base_t(detail::no_safety, r) {
+ }
+
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_environment(lua_State* L, T&& r) noexcept : base_t(detail::no_safety, L, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_environment<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_environment>(lua_state(), -1, handler);
+ }
+#endif // Safety
+ }
+
+ template <typename T>
+ bool set_on(const T& target) const {
+ lua_State* L = target.lua_state();
+ auto pp = stack::push_pop(target);
+ int target_index = pp.index_of(target);
+#if SOL_LUA_VERSION_I_ < 502
+ // Use lua_setfenv
+ this->push();
+ int success_result = lua_setfenv(L, target_index);
+ return success_result != 0;
+#else
+ // If this is a C function, the environment is always placed in
+ // the first value, as is expected of sol2 (all upvalues have an empty name, "")
+ if (lua_iscfunction(L, target_index) != 0) {
+ const char* maybe_upvalue_name = lua_getupvalue(L, target_index, 1);
+ if (maybe_upvalue_name == nullptr) {
+ return false;
+ }
+ string_view upvalue_name(maybe_upvalue_name);
+ if (upvalue_name == "") {
+ this->push();
+ const char* success = lua_setupvalue(L, target_index, 1);
+ if (success == nullptr) {
+ // left things alone on the stack, pop them off
+ lua_pop(L, 2);
+ return false;
+ }
+ lua_pop(L, 1);
+ return true;
+ }
+ lua_pop(L, 1);
+ return false;
+ }
+ else {
+ // Must search for the right upvalue target on index
+ for (int upvalue_index = 1;; ++upvalue_index) {
+ const char* maybe_upvalue_name = lua_getupvalue(L, target_index, upvalue_index);
+ if (maybe_upvalue_name == nullptr) {
+ break;
+ }
+ string_view upvalue_name(maybe_upvalue_name);
+ if (upvalue_name == "_ENV") {
+ lua_pop(L, 1);
+ this->push();
+ const char* success = lua_setupvalue(L, target_index, upvalue_index);
+ if (success == nullptr) {
+ // left things alone on the stack, pop them off
+ lua_pop(L, 1);
+ break;
+ }
+ // whether or not we succeeded, we found _ENV
+ // so we need to break
+ return true;
+ }
+ lua_pop(L, 1);
+ }
+ // if we get here,
+ // we did not find an _ENV here...
+ return false;
+ }
+#endif
+ }
+ };
+
+ template <typename T, typename E>
+ bool set_environment(const basic_environment<E>& env, const T& target) {
+ return env.set_on(target);
+ }
+
+ template <typename E = reference, typename T>
+ basic_environment<E> get_environment(const T& target) {
+ lua_State* L = target.lua_state();
+ auto pp = stack::pop_n(L, stack::push_environment_of(target));
+ return basic_environment<E>(L, -1);
+ }
+
+ struct this_environment {
+ optional<environment> env;
+
+ this_environment() : env(nullopt) {
+ }
+ this_environment(environment e) : env(std::move(e)) {
+ }
+ this_environment(const this_environment&) = default;
+ this_environment(this_environment&&) = default;
+ this_environment& operator=(const this_environment&) = default;
+ this_environment& operator=(this_environment&&) = default;
+
+ explicit operator bool() const {
+ return static_cast<bool>(env);
+ }
+
+ operator optional<environment>&() {
+ return env;
+ }
+
+ operator const optional<environment>&() const {
+ return env;
+ }
+
+ operator environment&() {
+ return env.value();
+ }
+
+ operator const environment&() const {
+ return env.value();
+ }
+ };
+
+ namespace stack {
+ template <>
+ struct unqualified_getter<env_key_t> {
+ static environment get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ return get_environment(stack_reference(L, raw_index(index)));
+ }
+ };
+
+ template <>
+ struct unqualified_getter<this_environment> {
+ static this_environment get(lua_State* L, int, record& tracking) {
+ tracking.use(0);
+ lua_Debug info;
+ // Level 0 means current function (this C function, which may or may not be useful for us?)
+ // Level 1 means next call frame up the stack. (Can be nothing if function called directly from C++ with lua_p/call)
+ int pre_stack_size = lua_gettop(L);
+ if (lua_getstack(L, 1, &info) != 1) {
+ if (lua_getstack(L, 0, &info) != 1) {
+ lua_settop(L, pre_stack_size);
+ return this_environment();
+ }
+ }
+ if (lua_getinfo(L, "f", &info) == 0) {
+ lua_settop(L, pre_stack_size);
+ return this_environment();
+ }
+
+ stack_reference f(L, -1);
+ environment env(env_key, f);
+ if (!env.valid()) {
+ lua_settop(L, pre_stack_size);
+ return this_environment();
+ }
+ return this_environment(std::move(env));
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/environment.hpp
+
+// beginning of sol/load_result.hpp
+
+#include <cstdint>
+
+namespace sol {
+ struct load_result : public proxy_base<load_result> {
+ private:
+ lua_State* L;
+ int index;
+ int returncount;
+ int popcount;
+ load_status err;
+
+ public:
+ load_result() noexcept : load_result(nullptr) {}
+ load_result(lua_State* Ls, int stackindex = -1, int retnum = 0, int popnum = 0, load_status lerr = load_status::ok) noexcept
+ : L(Ls), index(stackindex), returncount(retnum), popcount(popnum), err(lerr) {
+ }
+
+ // We do not want anyone to copy these around willy-nilly
+ // Will likely break people, but also will probably get rid of quiet bugs that have
+ // been lurking. (E.g., Vanilla Lua will just quietly discard over-pops and under-pops:
+ // LuaJIT and other Lua engines will implode and segfault at random later times.)
+ load_result(const load_result&) = delete;
+ load_result& operator=(const load_result&) = delete;
+
+ load_result(load_result&& o) noexcept : L(o.L), index(o.index), returncount(o.returncount), popcount(o.popcount), err(o.err) {
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but we will be thorough
+ o.L = nullptr;
+ o.index = 0;
+ o.returncount = 0;
+ o.popcount = 0;
+ o.err = load_status::syntax;
+ }
+ load_result& operator=(load_result&& o) noexcept {
+ L = o.L;
+ index = o.index;
+ returncount = o.returncount;
+ popcount = o.popcount;
+ err = o.err;
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but we will be thorough
+ o.L = nullptr;
+ o.index = 0;
+ o.returncount = 0;
+ o.popcount = 0;
+ o.err = load_status::syntax;
+ return *this;
+ }
+
+ load_status status() const noexcept {
+ return err;
+ }
+
+ bool valid() const noexcept {
+ return status() == load_status::ok;
+ }
+
+ template <typename T>
+ T get() const {
+ using UT = meta::unqualified_t<T>;
+ if constexpr (meta::is_optional_v<UT>) {
+ using ValueType = typename UT::value_type;
+ if constexpr (std::is_same_v<ValueType, error>) {
+ if (valid()) {
+ return UT(nullopt);
+ }
+ return error(detail::direct_error, stack::get<std::string>(L, index));
+ }
+ else {
+ if (!valid()) {
+ return UT(nullopt);
+ }
+ return stack::get<UT>(L, index);
+ }
+ }
+ else {
+ if constexpr (std::is_same_v<T, error>) {
+#if SOL_IS_ON(SOL_SAFE_PROXIES)
+ if (valid()) {
+ type_panic_c_str(L, index, type_of(L, index), type::none, "expecting an error type (a string, from Lua)");
+ }
+#endif // Check proxy type's safety
+ return error(detail::direct_error, stack::get<std::string>(L, index));
+ }
+ else {
+#if SOL_IS_ON(SOL_SAFE_PROXIES)
+ if (!valid()) {
+ type_panic_c_str(L, index, type_of(L, index), type::none);
+ }
+#endif // Check proxy type's safety
+ return stack::get<T>(L, index);
+ }
+ }
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) call(Args&&... args) {
+ return get<protected_function>().template call<Ret...>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ decltype(auto) operator()(Args&&... args) {
+ return call<>(std::forward<Args>(args)...);
+ }
+
+ lua_State* lua_state() const noexcept {
+ return L;
+ };
+ int stack_index() const noexcept {
+ return index;
+ };
+
+ ~load_result() {
+ if (L != nullptr) {
+ stack::remove(L, index, popcount);
+ }
+ }
+ };
+} // namespace sol
+
+// end of sol/load_result.hpp
+
+// beginning of sol/state_handling.hpp
+
+// beginning of sol/lua_value.hpp
+
+namespace sol {
+ struct lua_value {
+ public:
+ struct arr : detail::ebco<std::initializer_list<lua_value>> {
+ private:
+ using base_t = detail::ebco<std::initializer_list<lua_value>>;
+
+ public:
+ using base_t::base_t;
+ };
+
+ private:
+ template <typename T>
+ using is_reference_or_lua_value_init_list
+ = meta::any<meta::is_specialization_of<T, std::initializer_list>, std::is_same<T, reference>, std::is_same<T, arr>>;
+
+ template <typename T>
+ using is_lua_value_single_constructible = meta::any<std::is_same<T, lua_value>, is_reference_or_lua_value_init_list<T>>;
+
+ static lua_State*& thread_local_lua_state() {
+#if SOL_IS_ON(SOL_USE_THREAD_LOCAL)
+ static thread_local lua_State* L = nullptr;
+#else
+ static lua_State* L = nullptr;
+#endif
+ return L;
+ }
+
+ reference ref_value;
+
+ public:
+ static void set_lua_state(lua_State* L) {
+ thread_local_lua_state() = L;
+ }
+
+ template <typename T, meta::disable<is_reference_or_lua_value_init_list<meta::unqualified_t<T>>> = meta::enabler>
+ lua_value(lua_State* L_, T&& value) : lua_value(((set_lua_state(L_)), std::forward<T>(value))) {
+ }
+
+ template <typename T, meta::disable<is_lua_value_single_constructible<meta::unqualified_t<T>>> = meta::enabler>
+ lua_value(T&& value) : ref_value(make_reference(thread_local_lua_state(), std::forward<T>(value))) {
+ }
+
+ lua_value(lua_State* L_, std::initializer_list<std::pair<lua_value, lua_value>> il)
+ : lua_value([&L_, &il]() {
+ set_lua_state(L_);
+ return std::move(il);
+ }()) {
+ }
+
+ lua_value(std::initializer_list<std::pair<lua_value, lua_value>> il) : ref_value(make_reference(thread_local_lua_state(), std::move(il))) {
+ }
+
+ lua_value(lua_State* L_, arr il)
+ : lua_value([&L_, &il]() {
+ set_lua_state(L_);
+ return std::move(il);
+ }()) {
+ }
+
+ lua_value(arr il) : ref_value(make_reference(thread_local_lua_state(), std::move(il.value()))) {
+ }
+
+ lua_value(lua_State* L_, reference r)
+ : lua_value([&L_, &r]() {
+ set_lua_state(L_);
+ return std::move(r);
+ }()) {
+ }
+
+ lua_value(reference r) : ref_value(std::move(r)) {
+ }
+
+ lua_value(const lua_value&) noexcept = default;
+ lua_value(lua_value&&) = default;
+ lua_value& operator=(const lua_value&) = default;
+ lua_value& operator=(lua_value&&) = default;
+
+ const reference& value() const& {
+ return ref_value;
+ }
+
+ reference& value() & {
+ return ref_value;
+ }
+
+ reference&& value() && {
+ return std::move(ref_value);
+ }
+
+ template <typename T>
+ decltype(auto) as() const {
+ ref_value.push();
+ return stack::pop<T>(ref_value.lua_state());
+ }
+
+ template <typename T>
+ bool is() const {
+ int r = ref_value.registry_index();
+ if (r == LUA_REFNIL)
+ return meta::any_same<meta::unqualified_t<T>, lua_nil_t, nullopt_t, std::nullptr_t>::value ? true : false;
+ if (r == LUA_NOREF)
+ return false;
+ auto pp = stack::push_pop(ref_value);
+ return stack::check<T>(ref_value.lua_state(), -1, &no_panic);
+ }
+ };
+
+ using array_value = typename lua_value::arr;
+
+ namespace stack {
+ template <>
+ struct unqualified_pusher<lua_value> {
+ static int push(lua_State* L, const lua_value& lv) {
+ return stack::push(L, lv.value());
+ }
+
+ static int push(lua_State* L, lua_value&& lv) {
+ return stack::push(L, std::move(lv).value());
+ }
+ };
+
+ template <>
+ struct unqualified_getter<lua_value> {
+ static lua_value get(lua_State* L, int index, record& tracking) {
+ return lua_value(L, stack::get<reference>(L, index, tracking));
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/lua_value.hpp
+
+#if SOL_IS_ON(SOL_PRINT_ERRORS)
+#include <iostream>
+#endif
+
+namespace sol {
+ inline void register_main_thread(lua_State* L) {
+#if SOL_LUA_VERSION_I_ < 502
+ if (L == nullptr) {
+ lua_pushnil(L);
+ lua_setglobal(L, detail::default_main_thread_name());
+ return;
+ }
+ lua_pushthread(L);
+ lua_setglobal(L, detail::default_main_thread_name());
+#else
+ (void)L;
+#endif
+ }
+
+ inline int default_at_panic(lua_State* L) {
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ (void)L;
+ return -1;
+#else
+ size_t messagesize;
+ const char* message = lua_tolstring(L, -1, &messagesize);
+ if (message) {
+ std::string err(message, messagesize);
+ lua_settop(L, 0);
+#if SOL_IS_ON(SOL_PRINT_ERRORS)
+ std::cerr << "[sol2] An error occurred and panic has been invoked: ";
+ std::cerr << err;
+ std::cerr << std::endl;
+#endif
+ throw error(err);
+ }
+ lua_settop(L, 0);
+ throw error(std::string("An unexpected error occurred and panic has been invoked"));
+#endif // Printing Errors
+ }
+
+ inline int default_traceback_error_handler(lua_State* L) {
+ std::string msg = "An unknown error has triggered the default error handler";
+ optional<string_view> maybetopmsg = stack::unqualified_check_get<string_view>(L, 1, &no_panic);
+ if (maybetopmsg) {
+ const string_view& topmsg = maybetopmsg.value();
+ msg.assign(topmsg.data(), topmsg.size());
+ }
+ luaL_traceback(L, L, msg.c_str(), 1);
+ optional<string_view> maybetraceback = stack::unqualified_check_get<string_view>(L, -1, &no_panic);
+ if (maybetraceback) {
+ const string_view& traceback = maybetraceback.value();
+ msg.assign(traceback.data(), traceback.size());
+ }
+#if SOL_IS_ON(SOL_PRINT_ERRORS)
+ // std::cerr << "[sol2] An error occurred and was caught in traceback: ";
+ // std::cerr << msg;
+ // std::cerr << std::endl;
+#endif // Printing
+ return stack::push(L, msg);
+ }
+
+ inline void set_default_state(lua_State* L, lua_CFunction panic_function = &default_at_panic,
+ lua_CFunction traceback_function = c_call<decltype(&default_traceback_error_handler), &default_traceback_error_handler>,
+ exception_handler_function exf = detail::default_exception_handler) {
+ lua_atpanic(L, panic_function);
+ protected_function::set_default_handler(object(L, in_place, traceback_function));
+ set_default_exception_handler(L, exf);
+ register_main_thread(L);
+ stack::luajit_exception_handler(L);
+ lua_value::set_lua_state(L);
+ }
+
+ inline std::size_t total_memory_used(lua_State* L) {
+ std::size_t kb = static_cast<std::size_t>(lua_gc(L, LUA_GCCOUNT, 0));
+ kb *= 1024;
+ kb += static_cast<std::size_t>(lua_gc(L, LUA_GCCOUNTB, 0));
+ return kb;
+ }
+
+ inline protected_function_result script_pass_on_error(lua_State*, protected_function_result result) {
+ return result;
+ }
+
+ inline protected_function_result script_throw_on_error(lua_State* L, protected_function_result result) {
+ type t = type_of(L, result.stack_index());
+ std::string err = "sol: ";
+ err += to_string(result.status());
+ err += " error";
+#if SOL_IS_ON(SOL_EXCEPTIONS)
+ std::exception_ptr eptr = std::current_exception();
+ if (eptr) {
+ err += " with a ";
+ try {
+ std::rethrow_exception(eptr);
+ }
+ catch (const std::exception& ex) {
+ err += "std::exception -- ";
+ err.append(ex.what());
+ }
+ catch (const std::string& message) {
+ err += "thrown message -- ";
+ err.append(message);
+ }
+ catch (const char* message) {
+ err += "thrown message -- ";
+ err.append(message);
+ }
+ catch (...) {
+ err.append("thrown but unknown type, cannot serialize into error message");
+ }
+ }
+#endif // serialize exception information if possible
+ if (t == type::string) {
+ err += ": ";
+ string_view serr = stack::unqualified_get<string_view>(L, result.stack_index());
+ err.append(serr.data(), serr.size());
+ }
+#if SOL_IS_ON(SOL_PRINT_ERRORS)
+ std::cerr << "[sol2] An error occurred and has been passed to an error handler: ";
+ std::cerr << err;
+ std::cerr << std::endl;
+#endif
+ // replacing information of stack error into pfr
+ int target = result.stack_index();
+ if (result.pop_count() > 0) {
+ stack::remove(L, target, result.pop_count());
+ }
+ stack::push(L, err);
+ int top = lua_gettop(L);
+ int towards = top - target;
+ if (towards != 0) {
+ lua_rotate(L, top, towards);
+ }
+#if SOL_IS_OFF(SOL_EXCEPTIONS)
+ return result;
+#else
+ // just throw our error
+ throw error(detail::direct_error, err);
+#endif // If exceptions are allowed
+ }
+
+ inline protected_function_result script_default_on_error(lua_State* L, protected_function_result pfr) {
+#if SOL_IS_ON(SOL_DEFAULT_PASS_ON_ERROR)
+ return script_pass_on_error(L, std::move(pfr));
+#else
+ return script_throw_on_error(L, std::move(pfr));
+#endif
+ }
+
+ namespace stack {
+ inline error get_traceback_or_errors(lua_State* L) {
+ int p = default_traceback_error_handler(L);
+ sol::error err = stack::get<sol::error>(L, -p);
+ lua_pop(L, p);
+ return err;
+ }
+ } // namespace stack
+} // namespace sol
+
+// end of sol/state_handling.hpp
+
+#include <memory>
+#include <cstddef>
+
+namespace sol {
+
+ class state_view {
+ private:
+ lua_State* L;
+ table reg;
+ global_table global;
+
+ optional<object> is_loaded_package(const std::string& key) {
+ auto loaded = reg.traverse_get<optional<object>>("_LOADED", key);
+ bool is53mod = loaded && !(loaded->is<bool>() && !loaded->as<bool>());
+ if (is53mod)
+ return loaded;
+#if SOL_LUA_VERSION_I_ <= 501
+ auto loaded51 = global.traverse_get<optional<object>>("package", "loaded", key);
+ bool is51mod = loaded51 && !(loaded51->is<bool>() && !loaded51->as<bool>());
+ if (is51mod)
+ return loaded51;
+#endif
+ return nullopt;
+ }
+
+ template <typename T>
+ void ensure_package(const std::string& key, T&& sr) {
+#if SOL_LUA_VERSION_I_ <= 501
+ auto pkg = global["package"];
+ if (!pkg.valid()) {
+ pkg = create_table_with("loaded", create_table_with(key, sr));
+ }
+ else {
+ auto ld = pkg["loaded"];
+ if (!ld.valid()) {
+ ld = create_table_with(key, sr);
+ }
+ else {
+ ld[key] = sr;
+ }
+ }
+#endif
+ auto loaded = reg["_LOADED"];
+ if (!loaded.valid()) {
+ loaded = create_table_with(key, sr);
+ }
+ else {
+ loaded[key] = sr;
+ }
+ }
+
+ template <typename Fx>
+ object require_core(const std::string& key, Fx&& action, bool create_global = true) {
+ optional<object> loaded = is_loaded_package(key);
+ if (loaded && loaded->valid())
+ return std::move(*loaded);
+ int before = lua_gettop(L);
+ action();
+ int after = lua_gettop(L);
+ if (before == after) {
+ // I mean, you were supposed to return
+ // something, ANYTHING, from your requires script. I guess I'll just
+ // but some trash in here, it's on you after that?
+ ensure_package(key, static_cast<void*>(L));
+ return object(L, lua_nil);
+ }
+ stack_reference sr(L, -1);
+ if (create_global)
+ set(key, sr);
+ ensure_package(key, sr);
+ return stack::pop<object>(L);
+ }
+
+ public:
+ using iterator = typename global_table::iterator;
+ using const_iterator = typename global_table::const_iterator;
+
+ state_view(lua_State* Ls) : L(Ls), reg(Ls, LUA_REGISTRYINDEX), global(Ls, global_tag) {
+ }
+
+ state_view(this_state Ls) : state_view(Ls.L) {
+ }
+
+ lua_State* lua_state() const {
+ return L;
+ }
+
+ template <typename... Args>
+ void open_libraries(Args&&... args) {
+ static_assert(meta::all_same<lib, meta::unqualified_t<Args>...>::value, "all types must be libraries");
+ if constexpr (sizeof...(args) == 0) {
+ luaL_openlibs(L);
+ return;
+ }
+ else {
+ lib libraries[1 + sizeof...(args)] = { lib::count, std::forward<Args>(args)... };
+
+ for (auto&& library : libraries) {
+ switch (library) {
+#if SOL_LUA_VERSION_I_ <= 501 && SOL_IS_ON(SOL_USE_LUAJIT)
+ case lib::coroutine:
+#endif // luajit opens coroutine base stuff
+ case lib::base:
+ luaL_requiref(L, "base", luaopen_base, 1);
+ lua_pop(L, 1);
+ break;
+ case lib::package:
+ luaL_requiref(L, "package", luaopen_package, 1);
+ lua_pop(L, 1);
+ break;
+#if SOL_IS_OFF(SOL_USE_LUAJIT)
+ case lib::coroutine:
+#if SOL_LUA_VERSION_I_ > 501
+ luaL_requiref(L, "coroutine", luaopen_coroutine, 1);
+ lua_pop(L, 1);
+#endif // Lua 5.2+ only
+ break;
+#endif // Not LuaJIT - comes builtin
+ case lib::string:
+ luaL_requiref(L, "string", luaopen_string, 1);
+ lua_pop(L, 1);
+ break;
+ case lib::table:
+ luaL_requiref(L, "table", luaopen_table, 1);
+ lua_pop(L, 1);
+ break;
+ case lib::math:
+ luaL_requiref(L, "math", luaopen_math, 1);
+ lua_pop(L, 1);
+ break;
+ case lib::bit32:
+#if SOL_IS_ON(SOL_USE_LUAJIT)
+ luaL_requiref(L, "bit32", luaopen_bit, 1);
+ lua_pop(L, 1);
+#elif SOL_IS_ON(SOL_LUA_BIT32_LIB)
+ luaL_requiref(L, "bit32", luaopen_bit32, 1);
+ lua_pop(L, 1);
+#else
+#endif
+ break;
+ case lib::io:
+ luaL_requiref(L, "io", luaopen_io, 1);
+ lua_pop(L, 1);
+ break;
+ case lib::os:
+ luaL_requiref(L, "os", luaopen_os, 1);
+ lua_pop(L, 1);
+ break;
+ case lib::debug:
+ luaL_requiref(L, "debug", luaopen_debug, 1);
+ lua_pop(L, 1);
+ break;
+ case lib::utf8:
+#if SOL_LUA_VERSION_I_ > 502 && SOL_IS_OFF(SOL_USE_LUAJIT)
+ luaL_requiref(L, "utf8", luaopen_utf8, 1);
+ lua_pop(L, 1);
+#endif // Lua 5.3+ only
+ break;
+ case lib::ffi:
+#if SOL_IS_ON(SOL_USE_LUAJIT) && SOL_IS_OFF(SOL_LUAJIT_FFI_DISABLED)
+ luaL_requiref(L, "ffi", luaopen_ffi, 1);
+ lua_pop(L, 1);
+#endif // LuaJIT only
+ break;
+ case lib::jit:
+#if SOL_IS_ON(SOL_USE_LUAJIT)
+ luaL_requiref(L, "jit", luaopen_jit, 0);
+ lua_pop(L, 1);
+#endif // LuaJIT Only
+ break;
+ case lib::count:
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ object require(const std::string& key, lua_CFunction open_function, bool create_global = true) {
+ luaL_requiref(L, key.c_str(), open_function, create_global ? 1 : 0);
+ return stack::pop<object>(L);
+ }
+
+ object require_script(const std::string& key, const string_view& code, bool create_global = true,
+ const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ auto action = [this, &code, &chunkname, &mode]() { stack::script(L, code, chunkname, mode); };
+ return require_core(key, action, create_global);
+ }
+
+ object require_file(const std::string& key, const std::string& filename, bool create_global = true, load_mode mode = load_mode::any) {
+ auto action = [this, &filename, &mode]() { stack::script_file(L, filename, mode); };
+ return require_core(key, action, create_global);
+ }
+
+ void clear_package_loaders() {
+ optional<table> maybe_package = this->global["package"];
+ if (!maybe_package) {
+ // package lib wasn't opened
+ // open package lib
+ return;
+ }
+ table& package = *maybe_package;
+ // yay for version differences...
+ // one day Lua 5.1 will die a peaceful death
+ // and its old bones will find blissful rest
+ auto loaders_proxy = package
+#if SOL_LUA_VERSION_I_ < 502
+ ["loaders"]
+#else
+ ["searchers"]
+#endif
+ ;
+ if (!loaders_proxy.valid()) {
+ // nothing to clear
+ return;
+ }
+ // we need to create the table for loaders
+ // table does not exist, so create and move forward
+ loaders_proxy = new_table(1, 0);
+ }
+
+ template <typename Fx>
+ void add_package_loader(Fx&& fx, bool clear_all_package_loaders = false) {
+ optional<table> maybe_package = this->global["package"];
+ if (!maybe_package) {
+ // package lib wasn't opened
+ // open package lib
+ return;
+ }
+ table& package = *maybe_package;
+ // yay for version differences...
+ // one day Lua 5.1 will die a peaceful death
+ // and its old bones will find blissful rest
+ auto loaders_proxy = package
+#if SOL_LUA_VERSION_I_ < 502
+ ["loaders"]
+#else
+ ["searchers"]
+#endif
+ ;
+ bool make_new_table = clear_all_package_loaders || !loaders_proxy.valid();
+ if (make_new_table) {
+ // we need to create the table for loaders
+ // table does not exist, so create and move forward
+ loaders_proxy = new_table(1, 0);
+ }
+ optional<table> maybe_loaders = loaders_proxy;
+ if (!maybe_loaders) {
+ // loaders/searches
+ // thing exists in package, but it
+ // ain't a table or a table-alike...!
+ return;
+ }
+ table loaders = loaders_proxy;
+ loaders.add(std::forward<Fx>(fx));
+ }
+
+ template <typename E>
+ protected_function_result do_reader(lua_Reader reader, void* data, const basic_environment<E>& env,
+ const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname);
+ load_status x = static_cast<load_status>(lua_load(L, reader, data, chunknametarget, to_string(mode).c_str()));
+ if (x != load_status::ok) {
+ return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast<call_status>(x));
+ }
+ stack_aligned_protected_function pf(L, -1);
+ set_environment(env, pf);
+ return pf();
+ }
+
+ protected_function_result do_reader(
+ lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname);
+ load_status x = static_cast<load_status>(lua_load(L, reader, data, chunknametarget, to_string(mode).c_str()));
+ if (x != load_status::ok) {
+ return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast<call_status>(x));
+ }
+ stack_aligned_protected_function pf(L, -1);
+ return pf();
+ }
+
+ template <typename E>
+ protected_function_result do_string(const string_view& code, const basic_environment<E>& env,
+ const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname);
+ load_status x = static_cast<load_status>(luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str()));
+ if (x != load_status::ok) {
+ return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast<call_status>(x));
+ }
+ stack_aligned_protected_function pf(L, -1);
+ set_environment(env, pf);
+ return pf();
+ }
+
+ protected_function_result do_string(
+ const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname);
+ load_status x = static_cast<load_status>(luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str()));
+ if (x != load_status::ok) {
+ return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast<call_status>(x));
+ }
+ stack_aligned_protected_function pf(L, -1);
+ return pf();
+ }
+
+ template <typename E>
+ protected_function_result do_file(const std::string& filename, const basic_environment<E>& env, load_mode mode = load_mode::any) {
+ load_status x = static_cast<load_status>(luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str()));
+ if (x != load_status::ok) {
+ return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast<call_status>(x));
+ }
+ stack_aligned_protected_function pf(L, -1);
+ set_environment(env, pf);
+ return pf();
+ }
+
+ protected_function_result do_file(const std::string& filename, load_mode mode = load_mode::any) {
+ load_status x = static_cast<load_status>(luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str()));
+ if (x != load_status::ok) {
+ return protected_function_result(L, absolute_index(L, -1), 0, 1, static_cast<call_status>(x));
+ }
+ stack_aligned_protected_function pf(L, -1);
+ return pf();
+ }
+
+ template <typename Fx,
+ meta::disable_any<meta::is_string_constructible<meta::unqualified_t<Fx>>,
+ meta::is_specialization_of<meta::unqualified_t<Fx>, basic_environment>> = meta::enabler>
+ protected_function_result safe_script(
+ lua_Reader reader, void* data, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ protected_function_result pfr = do_reader(reader, data, chunkname, mode);
+ if (!pfr.valid()) {
+ return on_error(L, std::move(pfr));
+ }
+ return pfr;
+ }
+
+ protected_function_result safe_script(
+ lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return safe_script(reader, data, script_default_on_error, chunkname, mode);
+ }
+
+ template <typename Fx,
+ meta::disable_any<meta::is_string_constructible<meta::unqualified_t<Fx>>,
+ meta::is_specialization_of<meta::unqualified_t<Fx>, basic_environment>> = meta::enabler>
+ protected_function_result safe_script(
+ const string_view& code, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ protected_function_result pfr = do_string(code, chunkname, mode);
+ if (!pfr.valid()) {
+ return on_error(L, std::move(pfr));
+ }
+ return pfr;
+ }
+
+ template <typename Fx, typename E>
+ protected_function_result safe_script(const string_view& code, const basic_environment<E>& env, Fx&& on_error,
+ const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ protected_function_result pfr = do_string(code, env, chunkname, mode);
+ if (!pfr.valid()) {
+ return on_error(L, std::move(pfr));
+ }
+ return pfr;
+ }
+
+ template <typename E>
+ protected_function_result safe_script(const string_view& code, const basic_environment<E>& env,
+ const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return safe_script(code, env, script_default_on_error, chunkname, mode);
+ }
+
+ protected_function_result safe_script(
+ const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return safe_script(code, script_default_on_error, chunkname, mode);
+ }
+
+ template <typename Fx,
+ meta::disable_any<meta::is_string_constructible<meta::unqualified_t<Fx>>,
+ meta::is_specialization_of<meta::unqualified_t<Fx>, basic_environment>> = meta::enabler>
+ protected_function_result safe_script_file(const std::string& filename, Fx&& on_error, load_mode mode = load_mode::any) {
+ protected_function_result pfr = do_file(filename, mode);
+ if (!pfr.valid()) {
+ return on_error(L, std::move(pfr));
+ }
+ return pfr;
+ }
+
+ template <typename Fx, typename E>
+ protected_function_result safe_script_file(
+ const std::string& filename, const basic_environment<E>& env, Fx&& on_error, load_mode mode = load_mode::any) {
+ protected_function_result pfr = do_file(filename, env, mode);
+ if (!pfr.valid()) {
+ return on_error(L, std::move(pfr));
+ }
+ return pfr;
+ }
+
+ template <typename E>
+ protected_function_result safe_script_file(const std::string& filename, const basic_environment<E>& env, load_mode mode = load_mode::any) {
+ return safe_script_file(filename, env, script_default_on_error, mode);
+ }
+
+ protected_function_result safe_script_file(const std::string& filename, load_mode mode = load_mode::any) {
+ return safe_script_file(filename, script_default_on_error, mode);
+ }
+
+ template <typename E>
+ unsafe_function_result unsafe_script(lua_Reader reader, void* data, const basic_environment<E>& env,
+ const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname);
+ int index = lua_gettop(L);
+ if (lua_load(L, reader, data, chunknametarget, to_string(mode).c_str())) {
+ lua_error(L);
+ }
+ set_environment(env, stack_reference(L, raw_index(index + 1)));
+ if (lua_pcall(L, 0, LUA_MULTRET, 0)) {
+ lua_error(L);
+ }
+ int postindex = lua_gettop(L);
+ int returns = postindex - index;
+ return unsafe_function_result(L, (std::max)(postindex - (returns - 1), 1), returns);
+ }
+
+ unsafe_function_result unsafe_script(
+ lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ int index = lua_gettop(L);
+ stack::script(L, reader, data, chunkname, mode);
+ int postindex = lua_gettop(L);
+ int returns = postindex - index;
+ return unsafe_function_result(L, (std::max)(postindex - (returns - 1), 1), returns);
+ }
+
+ template <typename E>
+ unsafe_function_result unsafe_script(const string_view& code, const basic_environment<E>& env,
+ const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname);
+ int index = lua_gettop(L);
+ if (luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str())) {
+ lua_error(L);
+ }
+ set_environment(env, stack_reference(L, raw_index(index + 1)));
+ if (lua_pcall(L, 0, LUA_MULTRET, 0)) {
+ lua_error(L);
+ }
+ int postindex = lua_gettop(L);
+ int returns = postindex - index;
+ return unsafe_function_result(L, (std::max)(postindex - (returns - 1), 1), returns);
+ }
+
+ unsafe_function_result unsafe_script(
+ const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ int index = lua_gettop(L);
+ stack::script(L, code, chunkname, mode);
+ int postindex = lua_gettop(L);
+ int returns = postindex - index;
+ return unsafe_function_result(L, (std::max)(postindex - (returns - 1), 1), returns);
+ }
+
+ template <typename E>
+ unsafe_function_result unsafe_script_file(const std::string& filename, const basic_environment<E>& env, load_mode mode = load_mode::any) {
+ int index = lua_gettop(L);
+ if (luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str())) {
+ lua_error(L);
+ }
+ set_environment(env, stack_reference(L, raw_index(index + 1)));
+ if (lua_pcall(L, 0, LUA_MULTRET, 0)) {
+ lua_error(L);
+ }
+ int postindex = lua_gettop(L);
+ int returns = postindex - index;
+ return unsafe_function_result(L, (std::max)(postindex - (returns - 1), 1), returns);
+ }
+
+ unsafe_function_result unsafe_script_file(const std::string& filename, load_mode mode = load_mode::any) {
+ int index = lua_gettop(L);
+ stack::script_file(L, filename, mode);
+ int postindex = lua_gettop(L);
+ int returns = postindex - index;
+ return unsafe_function_result(L, (std::max)(postindex - (returns - 1), 1), returns);
+ }
+
+ template <typename Fx,
+ meta::disable_any<meta::is_string_constructible<meta::unqualified_t<Fx>>,
+ meta::is_specialization_of<meta::unqualified_t<Fx>, basic_environment>> = meta::enabler>
+ protected_function_result script(
+ const string_view& code, Fx&& on_error, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return safe_script(code, std::forward<Fx>(on_error), chunkname, mode);
+ }
+
+ template <typename Fx,
+ meta::disable_any<meta::is_string_constructible<meta::unqualified_t<Fx>>,
+ meta::is_specialization_of<meta::unqualified_t<Fx>, basic_environment>> = meta::enabler>
+ protected_function_result script_file(const std::string& filename, Fx&& on_error, load_mode mode = load_mode::any) {
+ return safe_script_file(filename, std::forward<Fx>(on_error), mode);
+ }
+
+ template <typename Fx, typename E>
+ protected_function_result script(const string_view& code, const basic_environment<E>& env, Fx&& on_error,
+ const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return safe_script(code, env, std::forward<Fx>(on_error), chunkname, mode);
+ }
+
+ template <typename Fx, typename E>
+ protected_function_result script_file(const std::string& filename, const basic_environment<E>& env, Fx&& on_error, load_mode mode = load_mode::any) {
+ return safe_script_file(filename, env, std::forward<Fx>(on_error), mode);
+ }
+
+ protected_function_result script(
+ const string_view& code, const environment& env, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return safe_script(code, env, script_default_on_error, chunkname, mode);
+ }
+
+ protected_function_result script_file(const std::string& filename, const environment& env, load_mode mode = load_mode::any) {
+ return safe_script_file(filename, env, script_default_on_error, mode);
+ }
+
+#if SOL_IS_ON(SOL_SAFE_FUNCTION_OBJECTS)
+ protected_function_result script(
+ lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return safe_script(reader, data, chunkname, mode);
+ }
+
+ protected_function_result script(
+ const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return safe_script(code, chunkname, mode);
+ }
+
+ protected_function_result script_file(const std::string& filename, load_mode mode = load_mode::any) {
+ return safe_script_file(filename, mode);
+ }
+#else
+ unsafe_function_result script(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return unsafe_script(code, chunkname, mode);
+ }
+
+ unsafe_function_result script_file(const std::string& filename, load_mode mode = load_mode::any) {
+ return unsafe_script_file(filename, mode);
+ }
+#endif
+ load_result load(const string_view& code, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name(code, chunkname, basechunkname);
+ load_status x = static_cast<load_status>(luaL_loadbufferx(L, code.data(), code.size(), chunknametarget, to_string(mode).c_str()));
+ return load_result(L, absolute_index(L, -1), 1, 1, x);
+ }
+
+ load_result load_buffer(const char* buff, size_t size, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return load(string_view(buff, size), chunkname, mode);
+ }
+
+ load_result load_buffer(
+ const std::byte* buff, size_t size, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ return load(string_view(reinterpret_cast<const char*>(buff), size), chunkname, mode);
+ }
+
+ load_result load_file(const std::string& filename, load_mode mode = load_mode::any) {
+ load_status x = static_cast<load_status>(luaL_loadfilex(L, filename.c_str(), to_string(mode).c_str()));
+ return load_result(L, absolute_index(L, -1), 1, 1, x);
+ }
+
+ load_result load(lua_Reader reader, void* data, const std::string& chunkname = detail::default_chunk_name(), load_mode mode = load_mode::any) {
+ detail::typical_chunk_name_t basechunkname = {};
+ const char* chunknametarget = detail::make_chunk_name("lua_Reader", chunkname, basechunkname);
+ load_status x = static_cast<load_status>(lua_load(L, reader, data, chunknametarget, to_string(mode).c_str()));
+ return load_result(L, absolute_index(L, -1), 1, 1, x);
+ }
+
+ iterator begin() const {
+ return global.begin();
+ }
+
+ iterator end() const {
+ return global.end();
+ }
+
+ const_iterator cbegin() const {
+ return global.cbegin();
+ }
+
+ const_iterator cend() const {
+ return global.cend();
+ }
+
+ global_table globals() const {
+ // if we return a reference
+ // we'll be screwed a bit
+ return global;
+ }
+
+ global_table& globals() {
+ return global;
+ }
+
+ table registry() const {
+ return reg;
+ }
+
+ std::size_t memory_used() const {
+ return total_memory_used(lua_state());
+ }
+
+ int stack_top() const {
+ return stack::top(L);
+ }
+
+ int stack_clear() {
+ int s = stack_top();
+ lua_pop(L, s);
+ return s;
+ }
+
+ bool supports_gc_mode(gc_mode mode) const noexcept {
+#if SOL_LUA_VERSION_I_ >= 504
+ // supports all modes
+ (void)mode;
+ return true;
+#endif
+ return mode == gc_mode::default_value;
+ }
+
+ bool is_gc_on() const {
+#if SOL_LUA_VERSION_I_ >= 502
+ return lua_gc(lua_state(), LUA_GCISRUNNING, 0) == 1;
+#else
+ // You cannot turn it off in Lua 5.1
+ return true;
+#endif
+ }
+
+ void collect_garbage() {
+ lua_gc(lua_state(), LUA_GCCOLLECT, 0);
+ }
+
+ void collect_gc() {
+ collect_garbage();
+ }
+
+ bool step_gc(int step_size_kilobytes) {
+ // THOUGHT: std::chrono-alikes to map "kilobyte size" here...?
+ // Make it harder to give MB or KB to a B parameter...?
+ // Probably overkill for now.
+#if SOL_LUA_VERSION_I_ >= 504
+ // The manual implies that this function is almost always successful...
+ // is it?? It could depend on the GC mode...
+ return lua_gc(lua_state(), LUA_GCSTEP, step_size_kilobytes) != 0;
+#else
+ return lua_gc(lua_state(), LUA_GCSTEP, step_size_kilobytes) == 1;
+#endif
+ }
+
+ void restart_gc() {
+ lua_gc(lua_state(), LUA_GCRESTART, 0);
+ }
+
+ void stop_gc() {
+ lua_gc(lua_state(), LUA_GCSTOP, 0);
+ }
+
+ // Returns the old GC mode. Check support using the supports_gc_mode function.
+ gc_mode change_gc_mode_incremental(int pause, int step_multiplier, int step_byte_size) {
+ // "What the fuck does any of this mean??"
+ // http://www.lua.org/manual/5.4/manual.html#2.5.1
+
+ // THOUGHT: std::chrono-alikes to map "byte size" here...?
+ // Make it harder to give MB or KB to a B parameter...?
+ // Probably overkill for now.
+#if SOL_LUA_VERSION_I_ >= 504
+ int old_mode = lua_gc(lua_state(), LUA_GCINC, pause, step_multiplier, step_byte_size);
+ if (old_mode == LUA_GCGEN) {
+ return gc_mode::generational;
+ }
+ else if (old_mode == LUA_GCINC) {
+ return gc_mode::incremental;
+ }
+#else
+ lua_gc(lua_state(), LUA_GCSETPAUSE, pause);
+ lua_gc(lua_state(), LUA_GCSETSTEPMUL, step_multiplier);
+ (void)step_byte_size; // means nothing in older versions
+#endif
+ return gc_mode::default_value;
+ }
+
+ // Returns the old GC mode. Check support using the supports_gc_mode function.
+ gc_mode change_gc_mode_generational(int minor_multiplier, int major_multiplier) {
+#if SOL_LUA_VERSION_I_ >= 504
+ // "What does this shit mean?"
+ // http://www.lua.org/manual/5.4/manual.html#2.5.2
+ int old_mode = lua_gc(lua_state(), LUA_GCGEN, minor_multiplier, major_multiplier);
+ if (old_mode == LUA_GCGEN) {
+ return gc_mode::generational;
+ }
+ else if (old_mode == LUA_GCINC) {
+ return gc_mode::incremental;
+ }
+#else
+ (void)minor_multiplier;
+ (void)major_multiplier;
+#endif
+ return gc_mode::default_value;
+ }
+
+ operator lua_State*() const {
+ return lua_state();
+ }
+
+ void set_panic(lua_CFunction panic) {
+ lua_atpanic(lua_state(), panic);
+ }
+
+ void set_exception_handler(exception_handler_function handler) {
+ set_default_exception_handler(lua_state(), handler);
+ }
+
+ template <typename... Args, typename... Keys>
+ decltype(auto) get(Keys&&... keys) const {
+ return global.get<Args...>(std::forward<Keys>(keys)...);
+ }
+
+ template <typename T, typename Key>
+ decltype(auto) get_or(Key&& key, T&& otherwise) const {
+ return global.get_or(std::forward<Key>(key), std::forward<T>(otherwise));
+ }
+
+ template <typename T, typename Key, typename D>
+ decltype(auto) get_or(Key&& key, D&& otherwise) const {
+ return global.get_or<T>(std::forward<Key>(key), std::forward<D>(otherwise));
+ }
+
+ template <typename... Args>
+ state_view& set(Args&&... args) {
+ global.set(std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename T, typename... Keys>
+ decltype(auto) traverse_get(Keys&&... keys) const {
+ return global.traverse_get<T>(std::forward<Keys>(keys)...);
+ }
+
+ template <typename... Args>
+ state_view& traverse_set(Args&&... args) {
+ global.traverse_set(std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename Class, typename... Args>
+ usertype<Class> new_usertype(Args&&... args) {
+ return global.new_usertype<Class>(std::forward<Args>(args)...);
+ }
+
+ template <bool read_only = true, typename... Args>
+ state_view& new_enum(const string_view& name, Args&&... args) {
+ global.new_enum<read_only>(name, std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename T, bool read_only = true>
+ state_view& new_enum(const string_view& name, std::initializer_list<std::pair<string_view, T>> items) {
+ global.new_enum<T, read_only>(name, std::move(items));
+ return *this;
+ }
+
+ template <typename Fx>
+ void for_each(Fx&& fx) {
+ global.for_each(std::forward<Fx>(fx));
+ }
+
+ template <typename T>
+ table_proxy<global_table&, detail::proxy_key_t<T>> operator[](T&& key) {
+ return global[std::forward<T>(key)];
+ }
+
+ template <typename T>
+ table_proxy<const global_table&, detail::proxy_key_t<T>> operator[](T&& key) const {
+ return global[std::forward<T>(key)];
+ }
+
+ template <typename Sig, typename... Args, typename Key>
+ state_view& set_function(Key&& key, Args&&... args) {
+ global.set_function<Sig>(std::forward<Key>(key), std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename... Args, typename Key>
+ state_view& set_function(Key&& key, Args&&... args) {
+ global.set_function(std::forward<Key>(key), std::forward<Args>(args)...);
+ return *this;
+ }
+
+ template <typename Name>
+ table create_table(Name&& name, int narr = 0, int nrec = 0) {
+ return global.create(std::forward<Name>(name), narr, nrec);
+ }
+
+ template <typename Name, typename Key, typename Value, typename... Args>
+ table create_table(Name&& name, int narr, int nrec, Key&& key, Value&& value, Args&&... args) {
+ return global.create(std::forward<Name>(name), narr, nrec, std::forward<Key>(key), std::forward<Value>(value), std::forward<Args>(args)...);
+ }
+
+ template <typename Name, typename... Args>
+ table create_named_table(Name&& name, Args&&... args) {
+ table x = global.create_with(std::forward<Args>(args)...);
+ global.set(std::forward<Name>(name), x);
+ return x;
+ }
+
+ table create_table(int narr = 0, int nrec = 0) {
+ return create_table(lua_state(), narr, nrec);
+ }
+
+ template <typename Key, typename Value, typename... Args>
+ table create_table(int narr, int nrec, Key&& key, Value&& value, Args&&... args) {
+ return create_table(lua_state(), narr, nrec, std::forward<Key>(key), std::forward<Value>(value), std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ table create_table_with(Args&&... args) {
+ return create_table_with(lua_state(), std::forward<Args>(args)...);
+ }
+
+ static inline table create_table(lua_State* L, int narr = 0, int nrec = 0) {
+ return global_table::create(L, narr, nrec);
+ }
+
+ template <typename Key, typename Value, typename... Args>
+ static inline table create_table(lua_State* L, int narr, int nrec, Key&& key, Value&& value, Args&&... args) {
+ return global_table::create(L, narr, nrec, std::forward<Key>(key), std::forward<Value>(value), std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ static inline table create_table_with(lua_State* L, Args&&... args) {
+ return global_table::create_with(L, std::forward<Args>(args)...);
+ }
+ };
+} // namespace sol
+
+// end of sol/state_view.hpp
+
+// beginning of sol/thread.hpp
+
+namespace sol {
+ struct lua_thread_state {
+ lua_State* L;
+
+ lua_thread_state(lua_State* Ls) : L(Ls) {
+ }
+
+ lua_State* lua_state() const noexcept {
+ return L;
+ }
+ operator lua_State*() const noexcept {
+ return lua_state();
+ }
+ lua_State* operator->() const noexcept {
+ return lua_state();
+ }
+ };
+
+ namespace stack {
+ template <>
+ struct unqualified_pusher<lua_thread_state> {
+ int push(lua_State*, lua_thread_state lts) {
+ lua_pushthread(lts.L);
+ return 1;
+ }
+ };
+
+ template <>
+ struct unqualified_getter<lua_thread_state> {
+ lua_thread_state get(lua_State* L, int index, record& tracking) {
+ tracking.use(1);
+ lua_thread_state lts(lua_tothread(L, index));
+ return lts;
+ }
+ };
+
+ template <>
+ struct unqualified_check_getter<lua_thread_state> {
+ template <typename Handler>
+ optional<lua_thread_state> get(lua_State* L, int index, Handler&& handler, record& tracking) {
+ lua_thread_state lts(lua_tothread(L, index));
+ if (lts.lua_state() == nullptr) {
+ handler(L, index, type::thread, type_of(L, index), "value is not a valid thread type");
+ return nullopt;
+ }
+ tracking.use(1);
+ return lts;
+ }
+ };
+ } // namespace stack
+
+ template <typename ref_t>
+ class basic_thread : public basic_object<ref_t> {
+ private:
+ using base_t = basic_object<ref_t>;
+
+ public:
+ using base_t::lua_state;
+
+ basic_thread() noexcept = default;
+ basic_thread(const basic_thread&) = default;
+ basic_thread(basic_thread&&) = default;
+ template <typename T,
+ meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_thread>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_thread(T&& r) : base_t(std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_thread>(lua_state(), -1, handler);
+#endif // Safety
+ }
+ basic_thread(const stack_reference& r) : basic_thread(r.lua_state(), r.stack_index()) {};
+ basic_thread(stack_reference&& r) : basic_thread(r.lua_state(), r.stack_index()) {};
+ basic_thread& operator=(const basic_thread&) = default;
+ basic_thread& operator=(basic_thread&&) = default;
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_thread(lua_State* L, T&& r) : base_t(L, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_thread>(lua_state(), -1, handler);
+#endif // Safety
+ }
+ basic_thread(lua_State* L, int index = -1) : base_t(L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_thread>(L, index, handler);
+#endif // Safety
+ }
+ basic_thread(lua_State* L, ref_index index) : base_t(L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_thread>(lua_state(), -1, handler);
+#endif // Safety
+ }
+ basic_thread(lua_State* L, lua_State* actualthread) : basic_thread(L, lua_thread_state { actualthread }) {
+ }
+ basic_thread(lua_State* L, this_state actualthread) : basic_thread(L, lua_thread_state { actualthread.L }) {
+ }
+ basic_thread(lua_State* L, lua_thread_state actualthread) : base_t(L, -stack::push(L, actualthread)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_thread>(lua_state(), -1, handler);
+#endif // Safety
+ if (!is_stack_based<base_t>::value) {
+ lua_pop(lua_state(), 1);
+ }
+ }
+
+ state_view state() const {
+ return state_view(this->thread_state());
+ }
+
+ bool is_main_thread() const {
+ return stack::is_main_thread(this->thread_state());
+ }
+
+ lua_State* thread_state() const {
+ auto pp = stack::push_pop(*this);
+ lua_State* lthread = lua_tothread(lua_state(), -1);
+ return lthread;
+ }
+
+ thread_status status() const {
+ lua_State* lthread = thread_state();
+ auto lstat = static_cast<thread_status>(lua_status(lthread));
+ if (lstat == thread_status::ok) {
+ lua_Debug ar;
+ if (lua_getstack(lthread, 0, &ar) > 0)
+ return thread_status::ok;
+ else if (lua_gettop(lthread) == 0)
+ return thread_status::dead;
+ else
+ return thread_status::yielded;
+ }
+ return lstat;
+ }
+
+ basic_thread create() {
+ return create(lua_state());
+ }
+
+ static basic_thread create(lua_State* L) {
+ lua_newthread(L);
+ basic_thread result(L);
+ if (!is_stack_based<base_t>::value) {
+ lua_pop(L, 1);
+ }
+ return result;
+ }
+ };
+
+ typedef basic_thread<reference> thread;
+ typedef basic_thread<stack_reference> stack_thread;
+} // namespace sol
+
+// end of sol/thread.hpp
+
+namespace sol {
+
+ class state : private std::unique_ptr<lua_State, detail::state_deleter>, public state_view {
+ private:
+ typedef std::unique_ptr<lua_State, detail::state_deleter> unique_base;
+
+ public:
+ state(lua_CFunction panic = default_at_panic) : unique_base(luaL_newstate()), state_view(unique_base::get()) {
+ set_default_state(unique_base::get(), panic);
+ }
+
+ state(lua_CFunction panic, lua_Alloc alfunc, void* alpointer = nullptr)
+ : unique_base(lua_newstate(alfunc, alpointer)), state_view(unique_base::get()) {
+ set_default_state(unique_base::get(), panic);
+ }
+
+ state(const state&) = delete;
+ state(state&&) = default;
+ state& operator=(const state&) = delete;
+ state& operator=(state&& that) {
+ state_view::operator=(std::move(that));
+ unique_base::operator=(std::move(that));
+ return *this;
+ }
+
+ using state_view::get;
+
+ ~state() {
+ }
+ };
+} // namespace sol
+
+// end of sol/state.hpp
+
+// beginning of sol/coroutine.hpp
+
+namespace sol {
+ template <typename Reference>
+ class basic_coroutine : public basic_object<Reference> {
+ private:
+ using base_t = basic_object<Reference>;
+ using handler_t = reference;
+
+ private:
+ call_status stats = call_status::yielded;
+
+ void luacall(std::ptrdiff_t argcount, std::ptrdiff_t) {
+#if SOL_LUA_VERSION_I_ >= 504
+ int nresults;
+ stats = static_cast<call_status>(lua_resume(lua_state(), nullptr, static_cast<int>(argcount), &nresults));
+#else
+ stats = static_cast<call_status>(lua_resume(lua_state(), nullptr, static_cast<int>(argcount)));
+#endif
+ }
+
+ template <std::size_t... I, typename... Ret>
+ auto invoke(types<Ret...>, std::index_sequence<I...>, std::ptrdiff_t n) {
+ luacall(n, sizeof...(Ret));
+ return stack::pop<std::tuple<Ret...>>(lua_state());
+ }
+
+ template <std::size_t I, typename Ret>
+ Ret invoke(types<Ret>, std::index_sequence<I>, std::ptrdiff_t n) {
+ luacall(n, 1);
+ return stack::pop<Ret>(lua_state());
+ }
+
+ template <std::size_t I>
+ void invoke(types<void>, std::index_sequence<I>, std::ptrdiff_t n) {
+ luacall(n, 0);
+ }
+
+ protected_function_result invoke(types<>, std::index_sequence<>, std::ptrdiff_t n) {
+ int firstreturn = 1;
+ luacall(n, LUA_MULTRET);
+ int poststacksize = lua_gettop(this->lua_state());
+ int returncount = poststacksize - (firstreturn - 1);
+ if (error()) {
+ if (m_error_handler.valid()) {
+ string_view err = stack::get<string_view>(this->lua_state(), poststacksize);
+ m_error_handler.push();
+ stack::push(this->lua_state(), err);
+ lua_call(lua_state(), 1, 1);
+ }
+ return protected_function_result(this->lua_state(), lua_absindex(this->lua_state(), -1), 1, returncount, status());
+ }
+ return protected_function_result(this->lua_state(), firstreturn, returncount, returncount, status());
+ }
+
+ public:
+ using base_t::lua_state;
+
+ basic_coroutine() = default;
+ template <typename T,
+ meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_coroutine>>,
+ meta::neg<std::is_base_of<proxy_base_tag, meta::unqualified_t<T>>>, meta::neg<std::is_same<base_t, stack_reference>>,
+ meta::neg<std::is_same<lua_nil_t, meta::unqualified_t<T>>>, is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_coroutine(T&& r) noexcept
+ : base_t(std::forward<T>(r)), m_error_handler(detail::get_default_handler<reference, is_main_threaded<base_t>::value>(r.lua_state())) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_function<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_coroutine>(lua_state(), -1, handler);
+ }
+#endif // Safety
+ }
+
+ basic_coroutine(const basic_coroutine& other) = default;
+ basic_coroutine& operator=(const basic_coroutine&) = default;
+
+ basic_coroutine(basic_coroutine&& other) noexcept : base_t(std::move(other)), m_error_handler(this->lua_state(), std::move(other.m_error_handler)) {
+ }
+
+ basic_coroutine& operator=(basic_coroutine&& other) noexcept {
+ base_t::operator=(std::move(other));
+ // must change the state, since it could change on the coroutine type
+ m_error_handler = handler_t(this->lua_state(), std::move(other.m_error_handler));
+ return *this;
+ }
+
+ basic_coroutine(const basic_function<base_t>& b) noexcept
+ : basic_coroutine(b, detail::get_default_handler<reference, is_main_threaded<base_t>::value>(b.lua_state())) {
+ }
+ basic_coroutine(basic_function<base_t>&& b) noexcept
+ : basic_coroutine(std::move(b), detail::get_default_handler<reference, is_main_threaded<base_t>::value>(b.lua_state())) {
+ }
+ basic_coroutine(const basic_function<base_t>& b, handler_t eh) noexcept : base_t(b), m_error_handler(std::move(eh)) {
+ }
+ basic_coroutine(basic_function<base_t>&& b, handler_t eh) noexcept : base_t(std::move(b)), m_error_handler(std::move(eh)) {
+ }
+ basic_coroutine(const stack_reference& r) noexcept
+ : basic_coroutine(r.lua_state(), r.stack_index(), detail::get_default_handler<reference, is_main_threaded<base_t>::value>(r.lua_state())) {
+ }
+ basic_coroutine(stack_reference&& r) noexcept
+ : basic_coroutine(r.lua_state(), r.stack_index(), detail::get_default_handler<reference, is_main_threaded<base_t>::value>(r.lua_state())) {
+ }
+ basic_coroutine(const stack_reference& r, handler_t eh) noexcept : basic_coroutine(r.lua_state(), r.stack_index(), std::move(eh)) {
+ }
+ basic_coroutine(stack_reference&& r, handler_t eh) noexcept : basic_coroutine(r.lua_state(), r.stack_index(), std::move(eh)) {
+ }
+
+ template <typename Super>
+ basic_coroutine(const proxy_base<Super>& p)
+ : basic_coroutine(p, detail::get_default_handler<reference, is_main_threaded<base_t>::value>(p.lua_state())) {
+ }
+ template <typename Super>
+ basic_coroutine(proxy_base<Super>&& p)
+ : basic_coroutine(std::move(p), detail::get_default_handler<reference, is_main_threaded<base_t>::value>(p.lua_state())) {
+ }
+ template <typename Proxy, typename HandlerReference,
+ meta::enable<std::is_base_of<proxy_base_tag, meta::unqualified_t<Proxy>>,
+ meta::neg<is_lua_index<meta::unqualified_t<HandlerReference>>>> = meta::enabler>
+ basic_coroutine(Proxy&& p, HandlerReference&& eh) : basic_coroutine(detail::force_cast<base_t>(p), std::forward<HandlerReference>(eh)) {
+ }
+
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_coroutine(lua_State* L, T&& r) noexcept
+ : basic_coroutine(L, std::forward<T>(r), detail::get_default_handler<reference, is_main_threaded<base_t>::value>(L)) {
+ }
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_coroutine(lua_State* L, T&& r, handler_t eh) : base_t(L, std::forward<T>(r)), m_error_handler(std::move(eh)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_coroutine>(lua_state(), -1, handler);
+#endif // Safety
+ }
+
+ basic_coroutine(lua_nil_t n) : base_t(n), m_error_handler(n) {
+ }
+
+ basic_coroutine(lua_State* L, int index = -1)
+ : basic_coroutine(L, index, detail::get_default_handler<reference, is_main_threaded<base_t>::value>(L)) {
+ }
+ basic_coroutine(lua_State* L, int index, handler_t eh) : base_t(L, index), m_error_handler(std::move(eh)) {
+#ifdef SOL_SAFE_REFERENCES
+ constructor_handler handler {};
+ stack::check<basic_coroutine>(L, index, handler);
+#endif // Safety
+ }
+ basic_coroutine(lua_State* L, absolute_index index)
+ : basic_coroutine(L, index, detail::get_default_handler<reference, is_main_threaded<base_t>::value>(L)) {
+ }
+ basic_coroutine(lua_State* L, absolute_index index, handler_t eh) : base_t(L, index), m_error_handler(std::move(eh)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_coroutine>(L, index, handler);
+#endif // Safety
+ }
+ basic_coroutine(lua_State* L, raw_index index)
+ : basic_coroutine(L, index, detail::get_default_handler<reference, is_main_threaded<base_t>::value>(L)) {
+ }
+ basic_coroutine(lua_State* L, raw_index index, handler_t eh) : base_t(L, index), m_error_handler(std::move(eh)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_coroutine>(L, index, handler);
+#endif // Safety
+ }
+ basic_coroutine(lua_State* L, ref_index index)
+ : basic_coroutine(L, index, detail::get_default_handler<reference, is_main_threaded<base_t>::value>(L)) {
+ }
+ basic_coroutine(lua_State* L, ref_index index, handler_t eh) : base_t(L, index), m_error_handler(std::move(eh)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_coroutine>(lua_state(), -1, handler);
+#endif // Safety
+ }
+
+ call_status status() const noexcept {
+ return stats;
+ }
+
+ bool error() const noexcept {
+ call_status cs = status();
+ return cs != call_status::ok && cs != call_status::yielded;
+ }
+
+ bool runnable() const noexcept {
+ return base_t::valid() && (status() == call_status::yielded);
+ }
+
+ explicit operator bool() const noexcept {
+ return runnable();
+ }
+
+ template <typename... Args>
+ protected_function_result operator()(Args&&... args) {
+ return call<>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) operator()(types<Ret...>, Args&&... args) {
+ return call<Ret...>(std::forward<Args>(args)...);
+ }
+
+ template <typename... Ret, typename... Args>
+ decltype(auto) call(Args&&... args) {
+ // some users screw up coroutine.create
+ // and try to use it with sol::coroutine without ever calling the first resume in Lua
+ // this makes the stack incompatible with other kinds of stacks: protect against this
+ // make sure coroutines don't screw us over
+ base_t::push();
+ int pushcount = stack::multi_push_reference(lua_state(), std::forward<Args>(args)...);
+ return invoke(types<Ret...>(), std::make_index_sequence<sizeof...(Ret)>(), pushcount);
+ }
+
+ private:
+ handler_t m_error_handler;
+ };
+} // namespace sol
+
+// end of sol/coroutine.hpp
+
+// beginning of sol/userdata.hpp
+
+namespace sol {
+ template <typename base_type>
+ class basic_userdata : public basic_table<base_type> {
+ private:
+ using base_t = basic_table<base_type>;
+
+ public:
+ using base_t::lua_state;
+
+ basic_userdata() noexcept = default;
+ template <typename T,
+ meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_userdata>>, meta::neg<std::is_same<base_t, stack_reference>>,
+ is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_userdata(T&& r) noexcept : base_t(std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_userdata<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ type_assert(lua_state(), -1, type::userdata);
+ }
+#endif // Safety
+ }
+ basic_userdata(const basic_userdata&) = default;
+ basic_userdata(basic_userdata&&) = default;
+ basic_userdata& operator=(const basic_userdata&) = default;
+ basic_userdata& operator=(basic_userdata&&) = default;
+ basic_userdata(const stack_reference& r) : basic_userdata(r.lua_state(), r.stack_index()) {
+ }
+ basic_userdata(stack_reference&& r) : basic_userdata(r.lua_state(), r.stack_index()) {
+ }
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_userdata(lua_State* L, T&& r) : base_t(L, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_userdata>(L, -1, handler);
+#endif // Safety
+ }
+ basic_userdata(lua_State* L, int index = -1) : base_t(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_userdata>(L, index, handler);
+#endif // Safety
+ }
+ basic_userdata(lua_State* L, ref_index index) : base_t(detail::no_safety, L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_userdata>(L, -1, handler);
+#endif // Safety
+ }
+ };
+
+ template <typename base_type>
+ class basic_lightuserdata : public basic_object_base<base_type> {
+ typedef basic_object_base<base_type> base_t;
+
+ public:
+ using base_t::lua_state;
+
+ basic_lightuserdata() noexcept = default;
+ template <typename T,
+ meta::enable<meta::neg<std::is_same<meta::unqualified_t<T>, basic_lightuserdata>>, meta::neg<std::is_same<base_t, stack_reference>>,
+ is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_lightuserdata(T&& r) noexcept : base_t(std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ if (!is_lightuserdata<meta::unqualified_t<T>>::value) {
+ auto pp = stack::push_pop(*this);
+ type_assert(lua_state(), -1, type::lightuserdata);
+ }
+#endif // Safety
+ }
+ basic_lightuserdata(const basic_lightuserdata&) = default;
+ basic_lightuserdata(basic_lightuserdata&&) = default;
+ basic_lightuserdata& operator=(const basic_lightuserdata&) = default;
+ basic_lightuserdata& operator=(basic_lightuserdata&&) = default;
+ basic_lightuserdata(const stack_reference& r) : basic_lightuserdata(r.lua_state(), r.stack_index()) {
+ }
+ basic_lightuserdata(stack_reference&& r) : basic_lightuserdata(r.lua_state(), r.stack_index()) {
+ }
+ template <typename T, meta::enable<is_lua_reference<meta::unqualified_t<T>>> = meta::enabler>
+ basic_lightuserdata(lua_State* L, T&& r) : basic_lightuserdata(L, std::forward<T>(r)) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_lightuserdata>(lua_state(), -1, handler);
+#endif // Safety
+ }
+ basic_lightuserdata(lua_State* L, int index = -1) : base_t(L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ constructor_handler handler {};
+ stack::check<basic_lightuserdata>(L, index, handler);
+#endif // Safety
+ }
+ basic_lightuserdata(lua_State* L, ref_index index) : base_t(L, index) {
+#if SOL_IS_ON(SOL_SAFE_REFERENCES)
+ auto pp = stack::push_pop(*this);
+ constructor_handler handler {};
+ stack::check<basic_lightuserdata>(lua_state(), index, handler);
+#endif // Safety
+ }
+ };
+
+} // namespace sol
+
+// end of sol/userdata.hpp
+
+// beginning of sol/as_args.hpp
+
+namespace sol {
+ template <typename T>
+ struct as_args_t {
+ T src;
+ };
+
+ template <typename Source>
+ auto as_args(Source&& source) {
+ return as_args_t<Source> { std::forward<Source>(source) };
+ }
+
+ namespace stack {
+ template <typename T>
+ struct unqualified_pusher<as_args_t<T>> {
+ int push(lua_State* L, const as_args_t<T>& e) {
+ int p = 0;
+ for (const auto& i : e.src) {
+ p += stack::push(L, i);
+ }
+ return p;
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/as_args.hpp
+
+// beginning of sol/variadic_args.hpp
+
+#include <limits>
+#include <iterator>
+
+namespace sol {
+ struct variadic_args {
+ private:
+ lua_State* L;
+ int index;
+ int stacktop;
+
+ public:
+ typedef stack_proxy reference_type;
+ typedef stack_proxy value_type;
+ typedef stack_proxy* pointer;
+ typedef std::ptrdiff_t difference_type;
+ typedef std::size_t size_type;
+ typedef stack_iterator<stack_proxy, false> iterator;
+ typedef stack_iterator<stack_proxy, true> const_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+
+ variadic_args() = default;
+ variadic_args(lua_State* luastate, int stackindex = -1) : L(luastate), index(lua_absindex(luastate, stackindex)), stacktop(lua_gettop(luastate)) {
+ }
+ variadic_args(lua_State* luastate, int stackindex, int lastindex) : L(luastate), index(lua_absindex(luastate, stackindex)), stacktop(lastindex) {
+ }
+ variadic_args(const variadic_args&) = default;
+ variadic_args& operator=(const variadic_args&) = default;
+ variadic_args(variadic_args&& o) : L(o.L), index(o.index), stacktop(o.stacktop) {
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but will be thorough
+ o.L = nullptr;
+ o.index = 0;
+ o.stacktop = 0;
+ }
+ variadic_args& operator=(variadic_args&& o) {
+ L = o.L;
+ index = o.index;
+ stacktop = o.stacktop;
+ // Must be manual, otherwise destructor will screw us
+ // return count being 0 is enough to keep things clean
+ // but will be thorough
+ o.L = nullptr;
+ o.index = 0;
+ o.stacktop = 0;
+ return *this;
+ }
+
+ iterator begin() {
+ return iterator(L, index, stacktop + 1);
+ }
+ iterator end() {
+ return iterator(L, stacktop + 1, stacktop + 1);
+ }
+ const_iterator begin() const {
+ return const_iterator(L, index, stacktop + 1);
+ }
+ const_iterator end() const {
+ return const_iterator(L, stacktop + 1, stacktop + 1);
+ }
+ const_iterator cbegin() const {
+ return begin();
+ }
+ const_iterator cend() const {
+ return end();
+ }
+
+ reverse_iterator rbegin() {
+ return std::reverse_iterator<iterator>(begin());
+ }
+ reverse_iterator rend() {
+ return std::reverse_iterator<iterator>(end());
+ }
+ const_reverse_iterator rbegin() const {
+ return std::reverse_iterator<const_iterator>(begin());
+ }
+ const_reverse_iterator rend() const {
+ return std::reverse_iterator<const_iterator>(end());
+ }
+ const_reverse_iterator crbegin() const {
+ return std::reverse_iterator<const_iterator>(cbegin());
+ }
+ const_reverse_iterator crend() const {
+ return std::reverse_iterator<const_iterator>(cend());
+ }
+
+ int push() const {
+ return push(L);
+ }
+
+ int push(lua_State* target) const {
+ int pushcount = 0;
+ for (int i = index; i <= stacktop; ++i) {
+ lua_pushvalue(L, i);
+ pushcount += 1;
+ }
+ if (target != L) {
+ lua_xmove(L, target, pushcount);
+ }
+ return pushcount;
+ }
+
+ template <typename T>
+ decltype(auto) get(difference_type index_offset = 0) const {
+ return stack::get<T>(L, index + static_cast<int>(index_offset));
+ }
+
+ type get_type(difference_type index_offset = 0) const noexcept {
+ return type_of(L, index + static_cast<int>(index_offset));
+ }
+
+ stack_proxy operator[](difference_type index_offset) const {
+ return stack_proxy(L, index + static_cast<int>(index_offset));
+ }
+
+ lua_State* lua_state() const {
+ return L;
+ };
+ int stack_index() const {
+ return index;
+ };
+ int leftover_count() const {
+ return stacktop - (index - 1);
+ }
+ std::size_t size() const {
+ return static_cast<std::size_t>(leftover_count());
+ }
+ int top() const {
+ return stacktop;
+ }
+ };
+
+ namespace stack {
+ template <>
+ struct unqualified_getter<variadic_args> {
+ static variadic_args get(lua_State* L, int index, record& tracking) {
+ tracking.last = 0;
+ return variadic_args(L, index);
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<variadic_args> {
+ static int push(lua_State* L, const variadic_args& ref) {
+ return ref.push(L);
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/variadic_args.hpp
+
+// beginning of sol/variadic_results.hpp
+
+// beginning of sol/as_returns.hpp
+
+namespace sol {
+ template <typename T>
+ struct as_returns_t : private detail::ebco<T> {
+ private:
+ using base_t = detail::ebco<T>;
+
+ public:
+ using base_t::base_t;
+ using base_t::value;
+ };
+
+ template <typename Source>
+ auto as_returns(Source&& source) {
+ return as_returns_t<std::decay_t<Source>> { std::forward<Source>(source) };
+ }
+
+ namespace stack {
+ template <typename T>
+ struct unqualified_pusher<as_returns_t<T>> {
+ int push(lua_State* L, const as_returns_t<T>& e) {
+ auto& src = detail::unwrap(e.value());
+ int p = 0;
+ for (const auto& i : src) {
+ p += stack::push(L, i);
+ }
+ return p;
+ }
+ };
+ } // namespace stack
+} // namespace sol
+
+// end of sol/as_returns.hpp
+
+#include <vector>
+
+namespace sol {
+
+ template <typename Al = typename std::allocator<object>>
+ struct basic_variadic_results : public std::vector<object, Al> {
+ private:
+ using base_t = std::vector<object, Al>;
+
+ public:
+ basic_variadic_results() : base_t() {
+ }
+
+ basic_variadic_results(unsafe_function_result fr) : base_t() {
+ this->reserve(fr.return_count());
+ this->insert(this->cend(), fr.begin(), fr.end());
+ }
+
+ basic_variadic_results(protected_function_result fr) : base_t() {
+ this->reserve(fr.return_count());
+ this->insert(this->cend(), fr.begin(), fr.end());
+ }
+
+ template <typename Arg0, typename... Args,
+ meta::disable_any<std::is_same<meta::unqualified_t<Arg0>, basic_variadic_results>, std::is_same<meta::unqualified_t<Arg0>, function_result>,
+ std::is_same<meta::unqualified_t<Arg0>, protected_function_result>> = meta::enabler>
+ basic_variadic_results(Arg0&& arg0, Args&&... args) : base_t(std::forward<Arg0>(arg0), std::forward<Args>(args)...) {
+ }
+
+ basic_variadic_results(const basic_variadic_results&) = default;
+ basic_variadic_results(basic_variadic_results&&) = default;
+ };
+
+ struct variadic_results : public basic_variadic_results<> {
+ private:
+ using base_t = basic_variadic_results<>;
+
+ public:
+ using base_t::base_t;
+ };
+
+ template <typename Al>
+ struct is_container<basic_variadic_results<Al>> : std::false_type { };
+
+ template <>
+ struct is_container<variadic_results> : std::false_type { };
+
+ namespace stack {
+ template <typename Al>
+ struct unqualified_pusher<basic_variadic_results<Al>> {
+ int push(lua_State* L, const basic_variadic_results<Al>& e) {
+ int p = 0;
+ for (const auto& i : e) {
+ p += stack::push(L, i);
+ }
+ return p;
+ }
+ };
+
+ template <>
+ struct unqualified_pusher<variadic_results> {
+ int push(lua_State* L, const variadic_results& r) {
+ using base_t = basic_variadic_results<>;
+ return stack::push(L, static_cast<const base_t&>(r));
+ }
+ };
+ } // namespace stack
+
+} // namespace sol
+
+// end of sol/variadic_results.hpp
+
+#if SOL_IS_ON(SOL_COMPILER_GCC)
+#pragma GCC diagnostic pop
+#elif SOL_IS_ON(SOL_COMPILER_CLANG)
+#elif SOL_IS_ON(SOL_COMPILER_VCXX)
+#pragma warning(pop)
+#endif // g++
+
+#if SOL_IS_ON(SOL_INSIDE_UNREAL_ENGINE)
+#undef check
+#pragma pop_macro("check")
+#endif // Unreal Engine 4 Bullshit
+
+#endif // SOL_HPP
+// end of sol/sol.hpp
+
+#endif // SOL_SINGLE_INCLUDE_SOL_HPP
diff --git a/src/libs/3rdparty/sol2/sol2.qbs b/src/libs/3rdparty/sol2/sol2.qbs
new file mode 100644
index 0000000000..fb435308f7
--- /dev/null
+++ b/src/libs/3rdparty/sol2/sol2.qbs
@@ -0,0 +1,17 @@
+Product {
+ name: "sol2"
+
+ Group {
+ prefix: "include/"
+ files: [
+ "sol/config.hpp",
+ "sol/forward.hpp",
+ "sol/sol.hpp",
+ ]
+ }
+
+ Export {
+ Depends { name: "cpp" }
+ cpp.includePaths: project.ide_source_tree + "/src/libs/3rdparty/sol2/include"
+ }
+}
diff --git a/src/libs/3rdparty/sqlite/carray.c b/src/libs/3rdparty/sqlite/carray.c
index 709c894f27..b1caa98c3f 100644
--- a/src/libs/3rdparty/sqlite/carray.c
+++ b/src/libs/3rdparty/sqlite/carray.c
@@ -409,6 +409,11 @@ static sqlite3_module carrayModule = {
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0, /* xRollbackTo */
+ 0, /* xShadow */
+ 0 /* xIntegrity */
};
/*
diff --git a/src/libs/3rdparty/sqlite/sqlite3.c b/src/libs/3rdparty/sqlite/sqlite3.c
index 1884b08239..eaa24a1310 100644
--- a/src/libs/3rdparty/sqlite/sqlite3.c
+++ b/src/libs/3rdparty/sqlite/sqlite3.c
@@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-** version 3.43.1. By combining all the individual C code files into this
+** version 3.46.0. By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit. This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately. Performance improvements
@@ -18,7 +18,7 @@
** separate file. This file contains only code for the core SQLite library.
**
** The content in this amalgamation comes from Fossil check-in
-** d3a40c05c49e1a49264912b1a05bc2143ac.
+** 96c92aba00c8375bc32fafcdf12429c58bd8.
*/
#define SQLITE_CORE 1
#define SQLITE_AMALGAMATION 1
@@ -459,9 +459,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.43.1"
-#define SQLITE_VERSION_NUMBER 3043001
-#define SQLITE_SOURCE_ID "2023-09-11 12:01:27 2d3a40c05c49e1a49264912b1a05bc2143ac0e7c3df588276ce80a4cbc9bd1b0"
+#define SQLITE_VERSION "3.46.0"
+#define SQLITE_VERSION_NUMBER 3046000
+#define SQLITE_SOURCE_ID "2024-05-23 13:25:27 96c92aba00c8375bc32fafcdf12429c58bd8aabfcadab6683e35bbb9cdebf19e"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -733,6 +733,8 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running.
** <li> The application must not modify the SQL statement text passed into
** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running.
+** <li> The application must not dereference the arrays or string pointers
+** passed as the 3rd and 4th callback parameters after it returns.
** </ul>
*/
SQLITE_API int sqlite3_exec(
@@ -1075,11 +1077,11 @@ struct sqlite3_file {
** </ul>
** xLock() upgrades the database file lock. In other words, xLock() moves the
** database file lock in the direction NONE toward EXCLUSIVE. The argument to
-** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never
+** xLock() is always one of SHARED, RESERVED, PENDING, or EXCLUSIVE, never
** SQLITE_LOCK_NONE. If the database file lock is already at or above the
** requested lock, then the call to xLock() is a no-op.
** xUnlock() downgrades the database file lock to either SHARED or NONE.
-* If the lock is already at or below the requested lock state, then the call
+** If the lock is already at or below the requested lock state, then the call
** to xUnlock() is a no-op.
** The xCheckReservedLock() method checks whether any database connection,
** either in this process or in some other process, is holding a RESERVED,
@@ -2440,7 +2442,7 @@ struct sqlite3_mem_methods {
** is stored in each sorted record and the required column values loaded
** from the database as records are returned in sorted order. The default
** value for this option is to never use this optimization. Specifying a
-** negative value for this option restores the default behaviour.
+** negative value for this option restores the default behavior.
** This option is only available if SQLite is compiled with the
** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option.
**
@@ -2454,6 +2456,22 @@ struct sqlite3_mem_methods {
** configuration setting is never used, then the default maximum is determined
** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that
** compile-time option is not set, then the default maximum is 1073741824.
+**
+** [[SQLITE_CONFIG_ROWID_IN_VIEW]]
+** <dt>SQLITE_CONFIG_ROWID_IN_VIEW
+** <dd>The SQLITE_CONFIG_ROWID_IN_VIEW option enables or disables the ability
+** for VIEWs to have a ROWID. The capability can only be enabled if SQLite is
+** compiled with -DSQLITE_ALLOW_ROWID_IN_VIEW, in which case the capability
+** defaults to on. This configuration option queries the current setting or
+** changes the setting to off or on. The argument is a pointer to an integer.
+** If that integer initially holds a value of 1, then the ability for VIEWs to
+** have ROWIDs is activated. If the integer initially holds zero, then the
+** ability is deactivated. Any other initial value for the integer leaves the
+** setting unchanged. After changes, if any, the integer is written with
+** a 1 or 0, if the ability for VIEWs to have ROWIDs is on or off. If SQLite
+** is compiled without -DSQLITE_ALLOW_ROWID_IN_VIEW (which is the usual and
+** recommended case) then the integer is always filled with zero, regardless
+** if its initial value.
** </dl>
*/
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@@ -2485,6 +2503,7 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */
+#define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */
/*
** CAPI3REF: Database Connection Configuration Options
@@ -2615,7 +2634,7 @@ struct sqlite3_mem_methods {
** database handle, SQLite checks if this will mean that there are now no
** connections at all to the database. If so, it performs a checkpoint
** operation before closing the connection. This option may be used to
-** override this behaviour. The first parameter passed to this operation
+** override this behavior. The first parameter passed to this operation
** is an integer - positive to disable checkpoints-on-close, or zero (the
** default) to enable them, and negative to leave the setting unchanged.
** The second parameter is a pointer to an integer
@@ -3599,8 +3618,8 @@ SQLITE_API int sqlite3_set_authorizer(
#define SQLITE_RECURSIVE 33 /* NULL NULL */
/*
-** CAPI3REF: Tracing And Profiling Functions
-** METHOD: sqlite3
+** CAPI3REF: Deprecated Tracing And Profiling Functions
+** DEPRECATED
**
** These routines are deprecated. Use the [sqlite3_trace_v2()] interface
** instead of the routines described here.
@@ -4267,14 +4286,17 @@ SQLITE_API void sqlite3_free_filename(sqlite3_filename);
** </ul>
**
** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
-** text that describes the error, as either UTF-8 or UTF-16 respectively.
+** text that describes the error, as either UTF-8 or UTF-16 respectively,
+** or NULL if no error message is available.
+** (See how SQLite handles [invalid UTF] for exceptions to this rule.)
** ^(Memory to hold the error message string is managed internally.
** The application does not need to worry about freeing the result.
** However, the error string might be overwritten or deallocated by
** subsequent calls to other SQLite interface functions.)^
**
-** ^The sqlite3_errstr() interface returns the English-language text
-** that describes the [result code], as UTF-8.
+** ^The sqlite3_errstr(E) interface returns the English-language text
+** that describes the [result code] E, as UTF-8, or NULL if E is not an
+** result code for which a text error message is available.
** ^(Memory to hold the error message string is managed internally
** and must not be freed by the application)^.
**
@@ -5638,6 +5660,7 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt);
*/
SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
+
/*
** CAPI3REF: Create Or Redefine SQL Functions
** KEYWORDS: {function creation routines}
@@ -5884,13 +5907,27 @@ SQLITE_API int sqlite3_create_window_function(
** </dd>
**
** [[SQLITE_SUBTYPE]] <dt>SQLITE_SUBTYPE</dt><dd>
-** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call
+** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call
** [sqlite3_value_subtype()] to inspect the sub-types of its arguments.
-** Specifying this flag makes no difference for scalar or aggregate user
-** functions. However, if it is not specified for a user-defined window
-** function, then any sub-types belonging to arguments passed to the window
-** function may be discarded before the window function is called (i.e.
-** sqlite3_value_subtype() will always return 0).
+** This flag instructs SQLite to omit some corner-case optimizations that
+** might disrupt the operation of the [sqlite3_value_subtype()] function,
+** causing it to return zero rather than the correct subtype().
+** SQL functions that invokes [sqlite3_value_subtype()] should have this
+** property. If the SQLITE_SUBTYPE property is omitted, then the return
+** value from [sqlite3_value_subtype()] might sometimes be zero even though
+** a non-zero subtype was specified by the function argument expression.
+**
+** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd>
+** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call
+** [sqlite3_result_subtype()] to cause a sub-type to be associated with its
+** result.
+** Every function that invokes [sqlite3_result_subtype()] should have this
+** property. If it does not, then the call to [sqlite3_result_subtype()]
+** might become a no-op if the function is used as term in an
+** [expression index]. On the other hand, SQL functions that never invoke
+** [sqlite3_result_subtype()] should avoid setting this property, as the
+** purpose of this property is to disable certain optimizations that are
+** incompatible with subtypes.
** </dd>
** </dl>
*/
@@ -5898,6 +5935,7 @@ SQLITE_API int sqlite3_create_window_function(
#define SQLITE_DIRECTONLY 0x000080000
#define SQLITE_SUBTYPE 0x000100000
#define SQLITE_INNOCUOUS 0x000200000
+#define SQLITE_RESULT_SUBTYPE 0x001000000
/*
** CAPI3REF: Deprecated Functions
@@ -6094,6 +6132,12 @@ SQLITE_API int sqlite3_value_encoding(sqlite3_value*);
** information can be used to pass a limited amount of context from
** one SQL function to another. Use the [sqlite3_result_subtype()]
** routine to set the subtype for the return value of an SQL function.
+**
+** Every [application-defined SQL function] that invoke this interface
+** should include the [SQLITE_SUBTYPE] property in the text
+** encoding argument when the function is [sqlite3_create_function|registered].
+** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype()
+** might return zero instead of the upstream subtype in some corner cases.
*/
SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
@@ -6192,48 +6236,56 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
** METHOD: sqlite3_context
**
** These functions may be used by (non-aggregate) SQL functions to
-** associate metadata with argument values. If the same value is passed to
-** multiple invocations of the same SQL function during query execution, under
-** some circumstances the associated metadata may be preserved. An example
-** of where this might be useful is in a regular-expression matching
-** function. The compiled version of the regular expression can be stored as
-** metadata associated with the pattern string.
+** associate auxiliary data with argument values. If the same argument
+** value is passed to multiple invocations of the same SQL function during
+** query execution, under some circumstances the associated auxiliary data
+** might be preserved. An example of where this might be useful is in a
+** regular-expression matching function. The compiled version of the regular
+** expression can be stored as auxiliary data associated with the pattern string.
** Then as long as the pattern string remains the same,
** the compiled regular expression can be reused on multiple
** invocations of the same function.
**
-** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the metadata
+** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary data
** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument
** value to the application-defined function. ^N is zero for the left-most
-** function argument. ^If there is no metadata
+** function argument. ^If there is no auxiliary data
** associated with the function argument, the sqlite3_get_auxdata(C,N) interface
** returns a NULL pointer.
**
-** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th
-** argument of the application-defined function. ^Subsequent
+** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the
+** N-th argument of the application-defined function. ^Subsequent
** calls to sqlite3_get_auxdata(C,N) return P from the most recent
-** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or
-** NULL if the metadata has been discarded.
+** sqlite3_set_auxdata(C,N,P,X) call if the auxiliary data is still valid or
+** NULL if the auxiliary data has been discarded.
** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL,
** SQLite will invoke the destructor function X with parameter P exactly
-** once, when the metadata is discarded.
-** SQLite is free to discard the metadata at any time, including: <ul>
+** once, when the auxiliary data is discarded.
+** SQLite is free to discard the auxiliary data at any time, including: <ul>
** <li> ^(when the corresponding function parameter changes)^, or
** <li> ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the
** SQL statement)^, or
** <li> ^(when sqlite3_set_auxdata() is invoked again on the same
** parameter)^, or
** <li> ^(during the original sqlite3_set_auxdata() call when a memory
-** allocation error occurs.)^ </ul>
+** allocation error occurs.)^
+** <li> ^(during the original sqlite3_set_auxdata() call if the function
+** is evaluated during query planning instead of during query execution,
+** as sometimes happens with [SQLITE_ENABLE_STAT4].)^ </ul>
**
-** Note the last bullet in particular. The destructor X in
+** Note the last two bullets in particular. The destructor X in
** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the
** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata()
** should be called near the end of the function implementation and the
** function implementation should not make any use of P after
-** sqlite3_set_auxdata() has been called.
-**
-** ^(In practice, metadata is preserved between function calls for
+** sqlite3_set_auxdata() has been called. Furthermore, a call to
+** sqlite3_get_auxdata() that occurs immediately after a corresponding call
+** to sqlite3_set_auxdata() might still return NULL if an out-of-memory
+** condition occurred during the sqlite3_set_auxdata() call or if the
+** function is being evaluated during query planning rather than during
+** query execution.
+**
+** ^(In practice, auxiliary data is preserved between function calls for
** function parameters that are compile-time constants, including literal
** values and [parameters] and expressions composed from the same.)^
**
@@ -6243,10 +6295,67 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
**
** These routines must be called from the same thread in which
** the SQL function is running.
+**
+** See also: [sqlite3_get_clientdata()] and [sqlite3_set_clientdata()].
*/
SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N);
SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*));
+/*
+** CAPI3REF: Database Connection Client Data
+** METHOD: sqlite3
+**
+** These functions are used to associate one or more named pointers
+** with a [database connection].
+** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P
+** to be attached to [database connection] D using name N. Subsequent
+** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P
+** or a NULL pointer if there were no prior calls to
+** sqlite3_set_clientdata() with the same values of D and N.
+** Names are compared using strcmp() and are thus case sensitive.
+**
+** If P and X are both non-NULL, then the destructor X is invoked with
+** argument P on the first of the following occurrences:
+** <ul>
+** <li> An out-of-memory error occurs during the call to
+** sqlite3_set_clientdata() which attempts to register pointer P.
+** <li> A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made
+** with the same D and N parameters.
+** <li> The database connection closes. SQLite does not make any guarantees
+** about the order in which destructors are called, only that all
+** destructors will be called exactly once at some point during the
+** database connection closing process.
+** </ul>
+**
+** SQLite does not do anything with client data other than invoke
+** destructors on the client data at the appropriate time. The intended
+** use for client data is to provide a mechanism for wrapper libraries
+** to store additional information about an SQLite database connection.
+**
+** There is no limit (other than available memory) on the number of different
+** client data pointers (with different names) that can be attached to a
+** single database connection. However, the implementation is optimized
+** for the case of having only one or two different client data names.
+** Applications and wrapper libraries are discouraged from using more than
+** one client data name each.
+**
+** There is no way to enumerate the client data pointers
+** associated with a database connection. The N parameter can be thought
+** of as a secret key such that only code that knows the secret key is able
+** to access the associated data.
+**
+** Security Warning: These interfaces should not be exposed in scripting
+** languages or in other circumstances where it might be possible for an
+** an attacker to invoke them. Any agent that can invoke these interfaces
+** can probably also take control of the process.
+**
+** Database connection client data is only available for SQLite
+** version 3.44.0 ([dateof:3.44.0]) and later.
+**
+** See also: [sqlite3_set_auxdata()] and [sqlite3_get_auxdata()].
+*/
+SQLITE_API void *sqlite3_get_clientdata(sqlite3*,const char*);
+SQLITE_API int sqlite3_set_clientdata(sqlite3*, const char*, void*, void(*)(void*));
/*
** CAPI3REF: Constants Defining Special Destructor Behavior
@@ -6448,6 +6557,20 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
** higher order bits are discarded.
** The number of subtype bytes preserved by SQLite might increase
** in future releases of SQLite.
+**
+** Every [application-defined SQL function] that invokes this interface
+** should include the [SQLITE_RESULT_SUBTYPE] property in its
+** text encoding argument when the SQL function is
+** [sqlite3_create_function|registered]. If the [SQLITE_RESULT_SUBTYPE]
+** property is omitted from the function that invokes sqlite3_result_subtype(),
+** then in some cases the sqlite3_result_subtype() might fail to set
+** the result subtype.
+**
+** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any
+** SQL function that invokes the sqlite3_result_subtype() interface
+** and that does not have the SQLITE_RESULT_SUBTYPE property will raise
+** an error. Future versions of SQLite might enable -DSQLITE_STRICT_SUBTYPE=1
+** by default.
*/
SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
@@ -6879,7 +7002,7 @@ SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName);
SQLITE_API int sqlite3_txn_state(sqlite3*,const char *zSchema);
/*
-** CAPI3REF: Allowed return values from [sqlite3_txn_state()]
+** CAPI3REF: Allowed return values from sqlite3_txn_state()
** KEYWORDS: {transaction state}
**
** These constants define the current transaction state of a database file.
@@ -7011,7 +7134,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
** ^Each call to the sqlite3_autovacuum_pages() interface overrides all
** previous invocations for that database connection. ^If the callback
** argument (C) to sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer,
-** then the autovacuum steps callback is cancelled. The return value
+** then the autovacuum steps callback is canceled. The return value
** from sqlite3_autovacuum_pages() is normally SQLITE_OK, but might
** be some other error code if something goes wrong. The current
** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other
@@ -7077,6 +7200,12 @@ SQLITE_API int sqlite3_autovacuum_pages(
** The exceptions defined in this paragraph might change in a future
** release of SQLite.
**
+** Whether the update hook is invoked before or after the
+** corresponding change is currently unspecified and may differ
+** depending on the type of change. Do not rely on the order of the
+** hook call with regards to the final result of the operation which
+** triggers the hook.
+**
** The update hook implementation must not do anything that will modify
** the database connection that invoked the update hook. Any actions
** to modify the database connection must be deferred until after the
@@ -7530,6 +7659,10 @@ struct sqlite3_module {
/* The methods above are in versions 1 and 2 of the sqlite_module object.
** Those below are for version 3 and greater. */
int (*xShadowName)(const char*);
+ /* The methods above are in versions 1 through 3 of the sqlite_module object.
+ ** Those below are for version 4 and greater. */
+ int (*xIntegrity)(sqlite3_vtab *pVTab, const char *zSchema,
+ const char *zTabName, int mFlags, char **pzErr);
};
/*
@@ -8017,7 +8150,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
** code is returned and the transaction rolled back.
**
** Calling this function with an argument that is not a NULL pointer or an
-** open blob handle results in undefined behaviour. ^Calling this routine
+** open blob handle results in undefined behavior. ^Calling this routine
** with a null pointer (such as would be returned by a failed call to
** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function
** is passed a valid open blob handle, the values returned by the
@@ -8244,9 +8377,11 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
**
** ^(Some systems (for example, Windows 95) do not support the operation
** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try()
-** will always return SQLITE_BUSY. The SQLite core only ever uses
-** sqlite3_mutex_try() as an optimization so this is acceptable
-** behavior.)^
+** will always return SQLITE_BUSY. In most cases the SQLite core only uses
+** sqlite3_mutex_try() as an optimization, so this is acceptable
+** behavior. The exceptions are unix builds that set the
+** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working
+** sqlite3_mutex_try() is required.)^
**
** ^The sqlite3_mutex_leave() routine exits a mutex that was
** previously entered by the same thread. The behavior
@@ -8497,6 +8632,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_PRNG_SAVE 5
#define SQLITE_TESTCTRL_PRNG_RESTORE 6
#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
+#define SQLITE_TESTCTRL_FK_NO_ACTION 7
#define SQLITE_TESTCTRL_BITVEC_TEST 8
#define SQLITE_TESTCTRL_FAULT_INSTALL 9
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
@@ -8504,6 +8640,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_ASSERT 12
#define SQLITE_TESTCTRL_ALWAYS 13
#define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */
+#define SQLITE_TESTCTRL_JSON_SELFCHECK 14
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
#define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
@@ -8539,7 +8676,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
** The sqlite3_keyword_count() interface returns the number of distinct
** keywords understood by SQLite.
**
-** The sqlite3_keyword_name(N,Z,L) interface finds the N-th keyword and
+** The sqlite3_keyword_name(N,Z,L) interface finds the 0-based N-th keyword and
** makes *Z point to that keyword expressed as UTF8 and writes the number
** of bytes in the keyword into *L. The string that *Z points to is not
** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns
@@ -9558,8 +9695,8 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);
** blocked connection already has a registered unlock-notify callback,
** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is
** called with a NULL pointer as its second argument, then any existing
-** unlock-notify callback is cancelled. ^The blocked connections
-** unlock-notify callback may also be cancelled by closing the blocked
+** unlock-notify callback is canceled. ^The blocked connections
+** unlock-notify callback may also be canceled by closing the blocked
** connection using [sqlite3_close()].
**
** The unlock-notify callback is not reentrant. If an application invokes
@@ -10118,24 +10255,45 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
** <li value="2"><p>
** ^(If the sqlite3_vtab_distinct() interface returns 2, that means
** that the query planner does not need the rows returned in any particular
-** order, as long as rows with the same values in all "aOrderBy" columns
-** are adjacent.)^ ^(Furthermore, only a single row for each particular
-** combination of values in the columns identified by the "aOrderBy" field
-** needs to be returned.)^ ^It is always ok for two or more rows with the same
-** values in all "aOrderBy" columns to be returned, as long as all such rows
-** are adjacent. ^The virtual table may, if it chooses, omit extra rows
-** that have the same value for all columns identified by "aOrderBy".
-** ^However omitting the extra rows is optional.
+** order, as long as rows with the same values in all columns identified
+** by "aOrderBy" are adjacent.)^ ^(Furthermore, when two or more rows
+** contain the same values for all columns identified by "colUsed", all but
+** one such row may optionally be omitted from the result.)^
+** The virtual table is not required to omit rows that are duplicates
+** over the "colUsed" columns, but if the virtual table can do that without
+** too much extra effort, it could potentially help the query to run faster.
** This mode is used for a DISTINCT query.
** <li value="3"><p>
-** ^(If the sqlite3_vtab_distinct() interface returns 3, that means
-** that the query planner needs only distinct rows but it does need the
-** rows to be sorted.)^ ^The virtual table implementation is free to omit
-** rows that are identical in all aOrderBy columns, if it wants to, but
-** it is not required to omit any rows. This mode is used for queries
+** ^(If the sqlite3_vtab_distinct() interface returns 3, that means the
+** virtual table must return rows in the order defined by "aOrderBy" as
+** if the sqlite3_vtab_distinct() interface had returned 0. However if
+** two or more rows in the result have the same values for all columns
+** identified by "colUsed", then all but one such row may optionally be
+** omitted.)^ Like when the return value is 2, the virtual table
+** is not required to omit rows that are duplicates over the "colUsed"
+** columns, but if the virtual table can do that without
+** too much extra effort, it could potentially help the query to run faster.
+** This mode is used for queries
** that have both DISTINCT and ORDER BY clauses.
** </ol>
**
+** <p>The following table summarizes the conditions under which the
+** virtual table is allowed to set the "orderByConsumed" flag based on
+** the value returned by sqlite3_vtab_distinct(). This table is a
+** restatement of the previous four paragraphs:
+**
+** <table border=1 cellspacing=0 cellpadding=10 width="90%">
+** <tr>
+** <td valign="top">sqlite3_vtab_distinct() return value
+** <td valign="top">Rows are returned in aOrderBy order
+** <td valign="top">Rows with the same value in all aOrderBy columns are adjacent
+** <td valign="top">Duplicates over all colUsed columns may be omitted
+** <tr><td>0<td>yes<td>yes<td>no
+** <tr><td>1<td>no<td>yes<td>no
+** <tr><td>2<td>no<td>yes<td>yes
+** <tr><td>3<td>yes<td>yes<td>yes
+** </table>
+**
** ^For the purposes of comparing virtual table output values to see if the
** values are same value for sorting purposes, two NULL values are considered
** to be the same. In other words, the comparison operator is "IS"
@@ -10862,6 +11020,13 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c
** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy
** of the database exists.
**
+** After the call, if the SQLITE_SERIALIZE_NOCOPY bit had been set,
+** the returned buffer content will remain accessible and unchanged
+** until either the next write operation on the connection or when
+** the connection is closed, and applications must not modify the
+** buffer. If the bit had been clear, the returned buffer will not
+** be accessed by SQLite after the call.
+**
** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the
** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory
** allocation error occurs.
@@ -10910,6 +11075,9 @@ SQLITE_API unsigned char *sqlite3_serialize(
** SQLite will try to increase the buffer size using sqlite3_realloc64()
** if writes on the database cause it to grow larger than M bytes.
**
+** Applications must not modify the buffer P or invalidate it before
+** the database connection D is closed.
+**
** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the
** database is currently in a read transaction or is involved in a backup
** operation.
@@ -10918,6 +11086,13 @@ SQLITE_API unsigned char *sqlite3_serialize(
** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the
** function returns SQLITE_ERROR.
**
+** The deserialized database should not be in [WAL mode]. If the database
+** is in WAL mode, then any attempt to use the database file will result
+** in an [SQLITE_CANTOPEN] error. The application can set the
+** [file format version numbers] (bytes 18 and 19) of the input database P
+** to 0x01 prior to invoking sqlite3_deserialize(D,S,P,N,M,F) to force the
+** database file into rollback mode and work around this limitation.
+**
** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the
** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then
** [sqlite3_free()] is invoked on argument P prior to returning.
@@ -11991,6 +12166,18 @@ SQLITE_API int sqlite3changeset_concat(
/*
+** CAPI3REF: Upgrade the Schema of a Changeset/Patchset
+*/
+SQLITE_API int sqlite3changeset_upgrade(
+ sqlite3 *db,
+ const char *zDb,
+ int nIn, const void *pIn, /* Input changeset */
+ int *pnOut, void **ppOut /* OUT: Inverse of input */
+);
+
+
+
+/*
** CAPI3REF: Changegroup Handle
**
** A changegroup is an object used to combine two or more
@@ -12037,6 +12224,38 @@ typedef struct sqlite3_changegroup sqlite3_changegroup;
SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp);
/*
+** CAPI3REF: Add a Schema to a Changegroup
+** METHOD: sqlite3_changegroup_schema
+**
+** This method may be used to optionally enforce the rule that the changesets
+** added to the changegroup handle must match the schema of database zDb
+** ("main", "temp", or the name of an attached database). If
+** sqlite3changegroup_add() is called to add a changeset that is not compatible
+** with the configured schema, SQLITE_SCHEMA is returned and the changegroup
+** object is left in an undefined state.
+**
+** A changeset schema is considered compatible with the database schema in
+** the same way as for sqlite3changeset_apply(). Specifically, for each
+** table in the changeset, there exists a database table with:
+**
+** <ul>
+** <li> The name identified by the changeset, and
+** <li> at least as many columns as recorded in the changeset, and
+** <li> the primary key columns in the same position as recorded in
+** the changeset.
+** </ul>
+**
+** The output of the changegroup object always has the same schema as the
+** database nominated using this function. In cases where changesets passed
+** to sqlite3changegroup_add() have fewer columns than the corresponding table
+** in the database schema, these are filled in using the default column
+** values from the database schema. This makes it possible to combined
+** changesets that have different numbers of columns for a single table
+** within a changegroup, provided that they are otherwise compatible.
+*/
+SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb);
+
+/*
** CAPI3REF: Add A Changeset To A Changegroup
** METHOD: sqlite3_changegroup
**
@@ -12104,17 +12323,46 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp);
** If the new changeset contains changes to a table that is already present
** in the changegroup, then the number of columns and the position of the
** primary key columns for the table must be consistent. If this is not the
-** case, this function fails with SQLITE_SCHEMA. If the input changeset
-** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is
-** returned. Or, if an out-of-memory condition occurs during processing, this
-** function returns SQLITE_NOMEM. In all cases, if an error occurs the state
-** of the final contents of the changegroup is undefined.
+** case, this function fails with SQLITE_SCHEMA. Except, if the changegroup
+** object has been configured with a database schema using the
+** sqlite3changegroup_schema() API, then it is possible to combine changesets
+** with different numbers of columns for a single table, provided that
+** they are otherwise compatible.
+**
+** If the input changeset appears to be corrupt and the corruption is
+** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition
+** occurs during processing, this function returns SQLITE_NOMEM.
**
-** If no error occurs, SQLITE_OK is returned.
+** In all cases, if an error occurs the state of the final contents of the
+** changegroup is undefined. If no error occurs, SQLITE_OK is returned.
*/
SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
/*
+** CAPI3REF: Add A Single Change To A Changegroup
+** METHOD: sqlite3_changegroup
+**
+** This function adds the single change currently indicated by the iterator
+** passed as the second argument to the changegroup object. The rules for
+** adding the change are just as described for [sqlite3changegroup_add()].
+**
+** If the change is successfully added to the changegroup, SQLITE_OK is
+** returned. Otherwise, an SQLite error code is returned.
+**
+** The iterator must point to a valid entry when this function is called.
+** If it does not, SQLITE_ERROR is returned and no change is added to the
+** changegroup. Additionally, the iterator must not have been opened with
+** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also
+** returned.
+*/
+SQLITE_API int sqlite3changegroup_add_change(
+ sqlite3_changegroup*,
+ sqlite3_changeset_iter*
+);
+
+
+
+/*
** CAPI3REF: Obtain A Composite Changeset From A Changegroup
** METHOD: sqlite3_changegroup
**
@@ -12375,10 +12623,17 @@ SQLITE_API int sqlite3changeset_apply_v2(
** <li>an insert change if all fields of the conflicting row match
** the row being inserted.
** </ul>
+**
+** <dt>SQLITE_CHANGESETAPPLY_FKNOACTION <dd>
+** If this flag it set, then all foreign key constraints in the target
+** database behave as if they were declared with "ON UPDATE NO ACTION ON
+** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL
+** or SET DEFAULT.
*/
#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
#define SQLITE_CHANGESETAPPLY_INVERT 0x0002
#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004
+#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008
/*
** CAPI3REF: Constants Passed To The Conflict Handler
@@ -12911,8 +13166,8 @@ struct Fts5PhraseIter {
** EXTENSION API FUNCTIONS
**
** xUserData(pFts):
-** Return a copy of the context pointer the extension function was
-** registered with.
+** Return a copy of the pUserData pointer passed to the xCreateFunction()
+** API when the extension function was registered.
**
** xColumnTotalSize(pFts, iCol, pnToken):
** If parameter iCol is less than zero, set output variable *pnToken
@@ -12944,8 +13199,11 @@ struct Fts5PhraseIter {
** created with the "columnsize=0" option.
**
** xColumnText:
-** This function attempts to retrieve the text of column iCol of the
-** current document. If successful, (*pz) is set to point to a buffer
+** If parameter iCol is less than zero, or greater than or equal to the
+** number of columns in the table, SQLITE_RANGE is returned.
+**
+** Otherwise, this function attempts to retrieve the text of column iCol of
+** the current document. If successful, (*pz) is set to point to a buffer
** containing the text in utf-8 encoding, (*pn) is set to the size in bytes
** (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
** if an error occurs, an SQLite error code is returned and the final values
@@ -12955,8 +13213,10 @@ struct Fts5PhraseIter {
** Returns the number of phrases in the current query expression.
**
** xPhraseSize:
-** Returns the number of tokens in phrase iPhrase of the query. Phrases
-** are numbered starting from zero.
+** If parameter iCol is less than zero, or greater than or equal to the
+** number of phrases in the current query, as returned by xPhraseCount,
+** 0 is returned. Otherwise, this function returns the number of tokens in
+** phrase iPhrase of the query. Phrases are numbered starting from zero.
**
** xInstCount:
** Set *pnInst to the total number of occurrences of all phrases within
@@ -12972,12 +13232,13 @@ struct Fts5PhraseIter {
** Query for the details of phrase match iIdx within the current row.
** Phrase matches are numbered starting from zero, so the iIdx argument
** should be greater than or equal to zero and smaller than the value
-** output by xInstCount().
+** output by xInstCount(). If iIdx is less than zero or greater than
+** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned.
**
-** Usually, output parameter *piPhrase is set to the phrase number, *piCol
+** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol
** to the column in which it occurs and *piOff the token offset of the
-** first token of the phrase. Returns SQLITE_OK if successful, or an error
-** code (i.e. SQLITE_NOMEM) if an error occurs.
+** first token of the phrase. SQLITE_OK is returned if successful, or an
+** error code (i.e. SQLITE_NOMEM) if an error occurs.
**
** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option.
@@ -13003,6 +13264,10 @@ struct Fts5PhraseIter {
** Invoking Api.xUserData() returns a copy of the pointer passed as
** the third argument to pUserData.
**
+** If parameter iPhrase is less than zero, or greater than or equal to
+** the number of phrases in the query, as returned by xPhraseCount(),
+** this function returns SQLITE_RANGE.
+**
** If the callback function returns any value other than SQLITE_OK, the
** query is abandoned and the xQueryPhrase function returns immediately.
** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
@@ -13117,9 +13382,42 @@ struct Fts5PhraseIter {
**
** xPhraseNextColumn()
** See xPhraseFirstColumn above.
+**
+** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken)
+** This is used to access token iToken of phrase iPhrase of the current
+** query. Before returning, output parameter *ppToken is set to point
+** to a buffer containing the requested token, and *pnToken to the
+** size of this buffer in bytes.
+**
+** If iPhrase or iToken are less than zero, or if iPhrase is greater than
+** or equal to the number of phrases in the query as reported by
+** xPhraseCount(), or if iToken is equal to or greater than the number of
+** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken
+ are both zeroed.
+**
+** The output text is not a copy of the query text that specified the
+** token. It is the output of the tokenizer module. For tokendata=1
+** tables, this includes any embedded 0x00 and trailing data.
+**
+** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken)
+** This is used to access token iToken of phrase hit iIdx within the
+** current row. If iIdx is less than zero or greater than or equal to the
+** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
+** output variable (*ppToken) is set to point to a buffer containing the
+** matching document token, and (*pnToken) to the size of that buffer in
+** bytes. This API is not available if the specified token matches a
+** prefix query term. In that case both output variables are always set
+** to 0.
+**
+** The output text is not a copy of the document text that was tokenized.
+** It is the output of the tokenizer module. For tokendata=1 tables, this
+** includes any embedded 0x00 and trailing data.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option.
*/
struct Fts5ExtensionApi {
- int iVersion; /* Currently always set to 2 */
+ int iVersion; /* Currently always set to 3 */
void *(*xUserData)(Fts5Context*);
@@ -13154,6 +13452,13 @@ struct Fts5ExtensionApi {
int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
+
+ /* Below this point are iVersion>=3 only */
+ int (*xQueryToken)(Fts5Context*,
+ int iPhrase, int iToken,
+ const char **ppToken, int *pnToken
+ );
+ int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
};
/*
@@ -13640,7 +13945,7 @@ struct fts5_api {
** max_page_count macro.
*/
#ifndef SQLITE_MAX_PAGE_COUNT
-# define SQLITE_MAX_PAGE_COUNT 1073741823
+# define SQLITE_MAX_PAGE_COUNT 0xfffffffe /* 4294967294 */
#endif
/*
@@ -13770,6 +14075,29 @@ struct fts5_api {
#endif
/*
+** Enable SQLITE_USE_SEH by default on MSVC builds. Only omit
+** SEH support if the -DSQLITE_OMIT_SEH option is given.
+*/
+#if defined(_MSC_VER) && !defined(SQLITE_OMIT_SEH)
+# define SQLITE_USE_SEH 1
+#else
+# undef SQLITE_USE_SEH
+#endif
+
+/*
+** Enable SQLITE_DIRECT_OVERFLOW_READ, unless the build explicitly
+** disables it using -DSQLITE_DIRECT_OVERFLOW_READ=0
+*/
+#if defined(SQLITE_DIRECT_OVERFLOW_READ) && SQLITE_DIRECT_OVERFLOW_READ+1==1
+ /* Disable if -DSQLITE_DIRECT_OVERFLOW_READ=0 */
+# undef SQLITE_DIRECT_OVERFLOW_READ
+#else
+ /* In all other cases, enable */
+# define SQLITE_DIRECT_OVERFLOW_READ 1
+#endif
+
+
+/*
** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2.
** 0 means mutexes are permanently disable and the library is never
** threadsafe. 1 means the library is serialized which is the highest
@@ -14037,6 +14365,8 @@ struct fts5_api {
# define SQLITE_OMIT_ALTERTABLE
#endif
+#define SQLITE_DIGIT_SEPARATOR '_'
+
/*
** Return true (non-zero) if the input is an integer that is too large
** to fit in 32-bits. This macro is used inside of various testcase()
@@ -14329,8 +14659,8 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
#define TK_TRUEFALSE 170
#define TK_ISNOT 171
#define TK_FUNCTION 172
-#define TK_UMINUS 173
-#define TK_UPLUS 174
+#define TK_UPLUS 173
+#define TK_UMINUS 174
#define TK_TRUTH 175
#define TK_REGISTER 176
#define TK_VECTOR 177
@@ -14339,8 +14669,9 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
#define TK_ASTERISK 180
#define TK_SPAN 181
#define TK_ERROR 182
-#define TK_SPACE 183
-#define TK_ILLEGAL 184
+#define TK_QNUMBER 183
+#define TK_SPACE 184
+#define TK_ILLEGAL 185
/************** End of parse.h ***********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
@@ -14602,7 +14933,7 @@ typedef INT16_TYPE LogEst;
# define SQLITE_PTRSIZE __SIZEOF_POINTER__
# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \
defined(_M_ARM) || defined(__arm__) || defined(__x86) || \
- (defined(__APPLE__) && defined(__POWERPC__)) || \
+ (defined(__APPLE__) && defined(__ppc__)) || \
(defined(__TOS_AIX__) && !defined(__64BIT__))
# define SQLITE_PTRSIZE 4
# else
@@ -14662,16 +14993,33 @@ typedef INT16_TYPE LogEst;
** using C-preprocessor macros. If that is unsuccessful, or if
** -DSQLITE_BYTEORDER=0 is set, then byte-order is determined
** at run-time.
+**
+** If you are building SQLite on some obscure platform for which the
+** following ifdef magic does not work, you can always include either:
+**
+** -DSQLITE_BYTEORDER=1234
+**
+** or
+**
+** -DSQLITE_BYTEORDER=4321
+**
+** to cause the build to work for little-endian or big-endian processors,
+** respectively.
*/
-#ifndef SQLITE_BYTEORDER
-# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \
+#ifndef SQLITE_BYTEORDER /* Replicate changes at tag-20230904a */
+# if defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
+# define SQLITE_BYTEORDER 4321
+# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
+# define SQLITE_BYTEORDER 1234
+# elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1
+# define SQLITE_BYTEORDER 4321
+# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \
defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64)
-# define SQLITE_BYTEORDER 1234
-# elif defined(sparc) || defined(__ppc__) || \
- defined(__ARMEB__) || defined(__AARCH64EB__)
-# define SQLITE_BYTEORDER 4321
+# define SQLITE_BYTEORDER 1234
+# elif defined(sparc) || defined(__ARMEB__) || defined(__AARCH64EB__)
+# define SQLITE_BYTEORDER 4321
# else
# define SQLITE_BYTEORDER 0
# endif
@@ -14822,6 +15170,7 @@ SQLITE_PRIVATE u32 sqlite3TreeTrace;
** 0x00010000 Beginning of DELETE/INSERT/UPDATE processing
** 0x00020000 Transform DISTINCT into GROUP BY
** 0x00040000 SELECT tree dump after all code has been generated
+** 0x00080000 NOT NULL strength reduction
*/
/*
@@ -14852,7 +15201,7 @@ SQLITE_PRIVATE u32 sqlite3WhereTrace;
** 0x00000010 Display sqlite3_index_info xBestIndex calls
** 0x00000020 Range an equality scan metrics
** 0x00000040 IN operator decisions
-** 0x00000080 WhereLoop cost adjustements
+** 0x00000080 WhereLoop cost adjustments
** 0x00000100
** 0x00000200 Covering index decisions
** 0x00000400 OR optimization
@@ -14995,6 +15344,7 @@ typedef struct Column Column;
typedef struct Cte Cte;
typedef struct CteUse CteUse;
typedef struct Db Db;
+typedef struct DbClientData DbClientData;
typedef struct DbFixer DbFixer;
typedef struct Schema Schema;
typedef struct Expr Expr;
@@ -15633,7 +15983,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*);
SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*);
SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*);
SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*);
-SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *);
+SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, u64*);
SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*);
SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *);
@@ -16000,6 +16350,7 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck(
sqlite3 *db, /* Database connection that is running the check */
Btree *p, /* The btree to be checked */
Pgno *aRoot, /* An array of root pages numbers for individual trees */
+ sqlite3_value *aCnt, /* OUT: entry counts for each btree in aRoot[] */
int nRoot, /* Number of entries in aRoot[] */
int mxErr, /* Stop reporting errors after this many */
int *pnErr, /* OUT: Write number of errors seen to this variable */
@@ -16220,6 +16571,7 @@ typedef struct VdbeOpList VdbeOpList;
#define P4_INT64 (-13) /* P4 is a 64-bit signed integer */
#define P4_INTARRAY (-14) /* P4 is a vector of 32-bit integers */
#define P4_FUNCCTX (-15) /* P4 is a pointer to an sqlite3_context object */
+#define P4_TABLEREF (-16) /* Like P4_TABLE, but reference counted */
/* Error message codes for OP_Halt */
#define P5_ConstraintNotNull 1
@@ -16269,12 +16621,12 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_Vacuum 5
#define OP_VFilter 6 /* jump, synopsis: iplan=r[P3] zplan='P4' */
#define OP_VUpdate 7 /* synopsis: data=r[P3@P2] */
-#define OP_Init 8 /* jump, synopsis: Start at P2 */
+#define OP_Init 8 /* jump0, synopsis: Start at P2 */
#define OP_Goto 9 /* jump */
#define OP_Gosub 10 /* jump */
-#define OP_InitCoroutine 11 /* jump */
-#define OP_Yield 12 /* jump */
-#define OP_MustBeInt 13 /* jump */
+#define OP_InitCoroutine 11 /* jump0 */
+#define OP_Yield 12 /* jump0 */
+#define OP_MustBeInt 13 /* jump0 */
#define OP_Jump 14 /* jump */
#define OP_Once 15 /* jump */
#define OP_If 16 /* jump */
@@ -16282,22 +16634,22 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_IsType 18 /* jump, synopsis: if typeof(P1.P3) in P5 goto P2 */
#define OP_Not 19 /* same as TK_NOT, synopsis: r[P2]= !r[P1] */
#define OP_IfNullRow 20 /* jump, synopsis: if P1.nullRow then r[P3]=NULL, goto P2 */
-#define OP_SeekLT 21 /* jump, synopsis: key=r[P3@P4] */
-#define OP_SeekLE 22 /* jump, synopsis: key=r[P3@P4] */
-#define OP_SeekGE 23 /* jump, synopsis: key=r[P3@P4] */
-#define OP_SeekGT 24 /* jump, synopsis: key=r[P3@P4] */
+#define OP_SeekLT 21 /* jump0, synopsis: key=r[P3@P4] */
+#define OP_SeekLE 22 /* jump0, synopsis: key=r[P3@P4] */
+#define OP_SeekGE 23 /* jump0, synopsis: key=r[P3@P4] */
+#define OP_SeekGT 24 /* jump0, synopsis: key=r[P3@P4] */
#define OP_IfNotOpen 25 /* jump, synopsis: if( !csr[P1] ) goto P2 */
#define OP_IfNoHope 26 /* jump, synopsis: key=r[P3@P4] */
#define OP_NoConflict 27 /* jump, synopsis: key=r[P3@P4] */
#define OP_NotFound 28 /* jump, synopsis: key=r[P3@P4] */
#define OP_Found 29 /* jump, synopsis: key=r[P3@P4] */
-#define OP_SeekRowid 30 /* jump, synopsis: intkey=r[P3] */
+#define OP_SeekRowid 30 /* jump0, synopsis: intkey=r[P3] */
#define OP_NotExists 31 /* jump, synopsis: intkey=r[P3] */
-#define OP_Last 32 /* jump */
-#define OP_IfSmaller 33 /* jump */
+#define OP_Last 32 /* jump0 */
+#define OP_IfSizeBetween 33 /* jump */
#define OP_SorterSort 34 /* jump */
#define OP_Sort 35 /* jump */
-#define OP_Rewind 36 /* jump */
+#define OP_Rewind 36 /* jump0 */
#define OP_SorterNext 37 /* jump */
#define OP_Prev 38 /* jump */
#define OP_Next 39 /* jump */
@@ -16309,7 +16661,7 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_IdxGE 45 /* jump, synopsis: key=r[P3@P4] */
#define OP_RowSetRead 46 /* jump, synopsis: r[P3]=rowset(P1) */
#define OP_RowSetTest 47 /* jump, synopsis: if r[P3] in rowset(P1) goto P2 */
-#define OP_Program 48 /* jump */
+#define OP_Program 48 /* jump0 */
#define OP_FkIfZero 49 /* jump, synopsis: if fkctr[P1]==0 goto P2 */
#define OP_IsNull 50 /* jump, same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */
#define OP_NotNull 51 /* jump, same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */
@@ -16339,7 +16691,7 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_Null 75 /* synopsis: r[P2..P3]=NULL */
#define OP_SoftNull 76 /* synopsis: r[P1]=NULL */
#define OP_Blob 77 /* synopsis: r[P2]=P4 (len=P1) */
-#define OP_Variable 78 /* synopsis: r[P2]=parameter(P1,P4) */
+#define OP_Variable 78 /* synopsis: r[P2]=parameter(P1) */
#define OP_Move 79 /* synopsis: r[P2@P3]=r[P1@P3] */
#define OP_Copy 80 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */
#define OP_SCopy 81 /* synopsis: r[P2]=r[P1] */
@@ -16435,19 +16787,22 @@ typedef struct VdbeOpList VdbeOpList;
#define OP_VCreate 171
#define OP_VDestroy 172
#define OP_VOpen 173
-#define OP_VInitIn 174 /* synopsis: r[P2]=ValueList(P1,P3) */
-#define OP_VColumn 175 /* synopsis: r[P3]=vcolumn(P2) */
-#define OP_VRename 176
-#define OP_Pagecount 177
-#define OP_MaxPgcnt 178
-#define OP_ClrSubtype 179 /* synopsis: r[P1].subtype = 0 */
-#define OP_FilterAdd 180 /* synopsis: filter(P1) += key(P3@P4) */
-#define OP_Trace 181
-#define OP_CursorHint 182
-#define OP_ReleaseReg 183 /* synopsis: release r[P1@P2] mask P3 */
-#define OP_Noop 184
-#define OP_Explain 185
-#define OP_Abortable 186
+#define OP_VCheck 174
+#define OP_VInitIn 175 /* synopsis: r[P2]=ValueList(P1,P3) */
+#define OP_VColumn 176 /* synopsis: r[P3]=vcolumn(P2) */
+#define OP_VRename 177
+#define OP_Pagecount 178
+#define OP_MaxPgcnt 179
+#define OP_ClrSubtype 180 /* synopsis: r[P1].subtype = 0 */
+#define OP_GetSubtype 181 /* synopsis: r[P2] = r[P1].subtype */
+#define OP_SetSubtype 182 /* synopsis: r[P2].subtype = r[P1] */
+#define OP_FilterAdd 183 /* synopsis: filter(P1) += key(P3@P4) */
+#define OP_Trace 184
+#define OP_CursorHint 185
+#define OP_ReleaseReg 186 /* synopsis: release r[P1@P2] mask P3 */
+#define OP_Noop 187
+#define OP_Explain 188
+#define OP_Abortable 189
/* Properties such as "out2" or "jump" that are specified in
** comments following the "case" for each opcode in the vdbe.c
@@ -16460,14 +16815,15 @@ typedef struct VdbeOpList VdbeOpList;
#define OPFLG_OUT2 0x10 /* out2: P2 is an output */
#define OPFLG_OUT3 0x20 /* out3: P3 is an output */
#define OPFLG_NCYCLE 0x40 /* ncycle:Cycles count against P1 */
+#define OPFLG_JUMP0 0x80 /* jump0: P2 might be zero */
#define OPFLG_INITIALIZER {\
/* 0 */ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x41, 0x00,\
-/* 8 */ 0x01, 0x01, 0x01, 0x01, 0x03, 0x03, 0x01, 0x01,\
-/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0x49, 0x49, 0x49,\
-/* 24 */ 0x49, 0x01, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49,\
-/* 32 */ 0x41, 0x01, 0x41, 0x41, 0x41, 0x01, 0x41, 0x41,\
+/* 8 */ 0x81, 0x01, 0x01, 0x81, 0x83, 0x83, 0x01, 0x01,\
+/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0xc9, 0xc9, 0xc9,\
+/* 24 */ 0xc9, 0x01, 0x49, 0x49, 0x49, 0x49, 0xc9, 0x49,\
+/* 32 */ 0xc1, 0x01, 0x41, 0x41, 0xc1, 0x01, 0x41, 0x41,\
/* 40 */ 0x41, 0x41, 0x41, 0x26, 0x26, 0x41, 0x23, 0x0b,\
-/* 48 */ 0x01, 0x01, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\
+/* 48 */ 0x81, 0x01, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\
/* 56 */ 0x0b, 0x0b, 0x01, 0x03, 0x03, 0x03, 0x01, 0x41,\
/* 64 */ 0x01, 0x00, 0x00, 0x02, 0x02, 0x08, 0x00, 0x10,\
/* 72 */ 0x10, 0x10, 0x00, 0x10, 0x00, 0x10, 0x10, 0x00,\
@@ -16482,9 +16838,9 @@ typedef struct VdbeOpList VdbeOpList;
/* 144 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\
/* 152 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\
/* 160 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x50, 0x40,\
-/* 176 */ 0x00, 0x10, 0x10, 0x02, 0x00, 0x00, 0x00, 0x00,\
-/* 184 */ 0x00, 0x00, 0x00,}
+/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x50,\
+/* 176 */ 0x40, 0x00, 0x10, 0x10, 0x02, 0x12, 0x12, 0x00,\
+/* 184 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}
/* The resolve3P2Values() routine is able to run faster if it knows
** the value of the largest JUMP opcode. The smaller the maximum
@@ -16627,6 +16983,8 @@ SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*);
SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);
SQLITE_PRIVATE int sqlite3VdbeHasSubProgram(Vdbe*);
+SQLITE_PRIVATE void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val);
+
SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*);
#ifdef SQLITE_ENABLE_BYTECODE_VTAB
SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*);
@@ -17214,6 +17572,10 @@ struct FuncDefHash {
};
#define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ)
+#if defined(SQLITE_USER_AUTHENTICATION)
+# warning "The SQLITE_USER_AUTHENTICATION extension is deprecated. \
+ See ext/userauth/user-auth.txt for details."
+#endif
#ifdef SQLITE_USER_AUTHENTICATION
/*
** Information held in the "sqlite3" database connection object and used
@@ -17393,6 +17755,7 @@ struct sqlite3 {
i64 nDeferredCons; /* Net deferred constraints this transaction. */
i64 nDeferredImmCons; /* Net deferred immediate constraints */
int *pnBytesFreed; /* If not NULL, increment this in DbFree() */
+ DbClientData *pDbData; /* sqlite3_set_clientdata() content */
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
/* The following variables are all protected by the STATIC_MAIN
** mutex, not by sqlite3.mutex. They are used by code in notify.c.
@@ -17475,6 +17838,7 @@ struct sqlite3 {
/* the count using a callback. */
#define SQLITE_CorruptRdOnly HI(0x00002) /* Prohibit writes due to error */
#define SQLITE_ReadUncommit HI(0x00004) /* READ UNCOMMITTED in shared-cache */
+#define SQLITE_FkNoAction HI(0x00008) /* Treat all FK as NO ACTION */
/* Flags used only if debugging */
#ifdef SQLITE_DEBUG
@@ -17515,7 +17879,7 @@ struct sqlite3 {
#define SQLITE_CursorHints 0x00000400 /* Add OP_CursorHint opcodes */
#define SQLITE_Stat4 0x00000800 /* Use STAT4 data */
/* TH3 expects this value ^^^^^^^^^^ to be 0x0000800. Don't change it */
-#define SQLITE_PushDown 0x00001000 /* The push-down optimization */
+#define SQLITE_PushDown 0x00001000 /* WHERE-clause push-down opt */
#define SQLITE_SimplifyJoin 0x00002000 /* Convert LEFT JOIN to JOIN */
#define SQLITE_SkipScan 0x00004000 /* Skip-scans */
#define SQLITE_PropagateConst 0x00008000 /* The constant propagation opt */
@@ -17643,14 +18007,15 @@ struct FuncDestructor {
#define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a
** single query - might change over time */
#define SQLITE_FUNC_TEST 0x4000 /* Built-in testing functions */
-/* 0x8000 -- available for reuse */
+#define SQLITE_FUNC_RUNONLY 0x8000 /* Cannot be used by valueFromFunction */
#define SQLITE_FUNC_WINDOW 0x00010000 /* Built-in window-only function */
#define SQLITE_FUNC_INTERNAL 0x00040000 /* For use by NestedParse() only */
#define SQLITE_FUNC_DIRECT 0x00080000 /* Not for use in TRIGGERs or VIEWs */
-#define SQLITE_FUNC_SUBTYPE 0x00100000 /* Result likely to have sub-type */
+/* SQLITE_SUBTYPE 0x00100000 // Consumer of subtypes */
#define SQLITE_FUNC_UNSAFE 0x00200000 /* Function has side effects */
#define SQLITE_FUNC_INLINE 0x00400000 /* Functions implemented in-line */
#define SQLITE_FUNC_BUILTIN 0x00800000 /* This is a built-in function */
+/* SQLITE_RESULT_SUBTYPE 0x01000000 // Generator of subtypes */
#define SQLITE_FUNC_ANYORDER 0x08000000 /* count/min/max aggregate */
/* Identifier numbers for each in-line function */
@@ -17742,10 +18107,11 @@ struct FuncDestructor {
#define MFUNCTION(zName, nArg, xPtr, xFunc) \
{nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \
xPtr, 0, xFunc, 0, 0, 0, #zName, {0} }
-#define JFUNCTION(zName, nArg, iArg, xFunc) \
- {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|\
- SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \
- SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
+#define JFUNCTION(zName, nArg, bUseCache, bWS, bRS, bJsonB, iArg, xFunc) \
+ {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_FUNC_CONSTANT|\
+ SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|\
+ ((bRS)*SQLITE_SUBTYPE)|((bWS)*SQLITE_RESULT_SUBTYPE), \
+ SQLITE_INT_TO_PTR(iArg|((bJsonB)*JSON_BLOB)),0,xFunc,0, 0, 0, #zName, {0} }
#define INLINE_FUNC(zName, nArg, iArg, mFlags) \
{nArg, SQLITE_FUNC_BUILTIN|\
SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \
@@ -18086,8 +18452,7 @@ struct Table {
#define TF_HasStored 0x00000040 /* Has one or more STORED columns */
#define TF_HasGenerated 0x00000060 /* Combo: HasVirtual + HasStored */
#define TF_WithoutRowid 0x00000080 /* No rowid. PRIMARY KEY is the key */
-#define TF_StatsUsed 0x00000100 /* Query planner decisions affected by
- ** Index.aiRowLogEst[] values */
+#define TF_MaybeReanalyze 0x00000100 /* Maybe run ANALYZE on this table */
#define TF_NoVisibleRowid 0x00000200 /* No user-visible "rowid" column */
#define TF_OOOHidden 0x00000400 /* Out-of-Order hidden columns */
#define TF_HasNotNull 0x00000800 /* Contains NOT NULL constraints */
@@ -18143,6 +18508,15 @@ struct Table {
#define HasRowid(X) (((X)->tabFlags & TF_WithoutRowid)==0)
#define VisibleRowid(X) (((X)->tabFlags & TF_NoVisibleRowid)==0)
+/* Macro is true if the SQLITE_ALLOW_ROWID_IN_VIEW (mis-)feature is
+** available. By default, this macro is false
+*/
+#ifndef SQLITE_ALLOW_ROWID_IN_VIEW
+# define ViewCanHaveRowid 0
+#else
+# define ViewCanHaveRowid (sqlite3Config.mNoVisibleRowid==0)
+#endif
+
/*
** Each foreign key constraint is an instance of the following structure.
**
@@ -18380,6 +18754,7 @@ struct Index {
unsigned isCovering:1; /* True if this is a covering index */
unsigned noSkipScan:1; /* Do not try to use skip-scan if true */
unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */
+ unsigned bLowQual:1; /* sqlite_stat1 says this is a low-quality index */
unsigned bNoQuery:1; /* Do not use this index to optimize queries */
unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */
unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */
@@ -18490,6 +18865,10 @@ struct AggInfo {
FuncDef *pFunc; /* The aggregate function implementation */
int iDistinct; /* Ephemeral table used to enforce DISTINCT */
int iDistAddr; /* Address of OP_OpenEphemeral */
+ int iOBTab; /* Ephemeral table to implement ORDER BY */
+ u8 bOBPayload; /* iOBTab has payload columns separate from key */
+ u8 bOBUnique; /* Enforce uniqueness on iOBTab keys */
+ u8 bUseSubtype; /* Transfer subtype info through sorter */
} *aFunc;
int nFunc; /* Number of entries in aFunc[] */
u32 selId; /* Select to which this AggInfo belongs */
@@ -18674,7 +19053,7 @@ struct Expr {
#define EP_Reduced 0x004000 /* Expr struct EXPR_REDUCEDSIZE bytes only */
#define EP_Win 0x008000 /* Contains window functions */
#define EP_TokenOnly 0x010000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */
- /* 0x020000 // Available for reuse */
+#define EP_FullSize 0x020000 /* Expr structure must remain full sized */
#define EP_IfNullRow 0x040000 /* The TK_IF_NULL_ROW opcode */
#define EP_Unlikely 0x080000 /* unlikely() or likelihood() function */
#define EP_ConstFunc 0x100000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
@@ -18704,6 +19083,7 @@ struct Expr {
#define ExprClearProperty(E,P) (E)->flags&=~(P)
#define ExprAlwaysTrue(E) (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue)
#define ExprAlwaysFalse(E) (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse)
+#define ExprIsFullSize(E) (((E)->flags&(EP_Reduced|EP_TokenOnly))==0)
/* Macros used to ensure that the correct members of unions are accessed
** in Expr.
@@ -18821,6 +19201,7 @@ struct ExprList {
#define ENAME_NAME 0 /* The AS clause of a result set */
#define ENAME_SPAN 1 /* Complete text of the result set expression */
#define ENAME_TAB 2 /* "DB.TABLE.NAME" for the result set */
+#define ENAME_ROWID 3 /* "DB.TABLE._rowid_" for * expansion of rowid */
/*
** An instance of this structure can hold a simple list of identifiers,
@@ -18871,10 +19252,12 @@ struct IdList {
**
** Union member validity:
**
-** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc
-** u1.pFuncArg fg.isTabFunc && !fg.isIndexedBy
-** u2.pIBIndex fg.isIndexedBy && !fg.isCte
-** u2.pCteUse fg.isCte && !fg.isIndexedBy
+** u1.zIndexedBy fg.isIndexedBy && !fg.isTabFunc
+** u1.pFuncArg fg.isTabFunc && !fg.isIndexedBy
+** u1.nRow !fg.isTabFunc && !fg.isIndexedBy
+**
+** u2.pIBIndex fg.isIndexedBy && !fg.isCte
+** u2.pCteUse fg.isCte && !fg.isIndexedBy
*/
struct SrcItem {
Schema *pSchema; /* Schema to which this item is fixed */
@@ -18902,6 +19285,7 @@ struct SrcItem {
unsigned isOn :1; /* u3.pOn was once valid and non-NULL */
unsigned isSynthUsing :1; /* u3.pUsing is synthesized from NATURAL */
unsigned isNestedFrom :1; /* pSelect is a SF_NestedFrom subquery */
+ unsigned rowidUsed :1; /* The ROWID of this table is referenced */
} fg;
int iCursor; /* The VDBE cursor number used to access this table */
union {
@@ -18912,6 +19296,7 @@ struct SrcItem {
union {
char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */
ExprList *pFuncArg; /* Arguments to table-valued-function */
+ u32 nRow; /* Number of rows in a VALUES clause */
} u1;
union {
Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */
@@ -19021,6 +19406,7 @@ struct NameContext {
int nRef; /* Number of names resolved by this context */
int nNcErr; /* Number of errors encountered while resolving names */
int ncFlags; /* Zero or more NC_* flags defined below */
+ u32 nNestedSelect; /* Number of nested selects using this NC */
Select *pWinSelect; /* SELECT statement for any window functions */
};
@@ -19054,6 +19440,7 @@ struct NameContext {
#define NC_InAggFunc 0x020000 /* True if analyzing arguments to an agg func */
#define NC_FromDDL 0x040000 /* SQL text comes from sqlite_schema */
#define NC_NoSelect 0x080000 /* Do not descend into sub-selects */
+#define NC_Where 0x100000 /* Processing WHERE clause of a SELECT */
#define NC_OrderAgg 0x8000000 /* Has an aggregate other than count/min/max */
/*
@@ -19077,6 +19464,7 @@ struct Upsert {
Expr *pUpsertWhere; /* WHERE clause for the ON CONFLICT UPDATE */
Upsert *pNextUpsert; /* Next ON CONFLICT clause in the list */
u8 isDoUpdate; /* True for DO UPDATE. False for DO NOTHING */
+ u8 isDup; /* True if 2nd or later with same pUpsertIdx */
/* Above this point is the parse tree for the ON CONFLICT clauses.
** The next group of fields stores intermediate data. */
void *pToFree; /* Free memory when deleting the Upsert object */
@@ -19166,11 +19554,12 @@ struct Select {
#define SF_View 0x0200000 /* SELECT statement is a view */
#define SF_NoopOrderBy 0x0400000 /* ORDER BY is ignored for this query */
#define SF_UFSrcCheck 0x0800000 /* Check pSrc as required by UPDATE...FROM */
-#define SF_PushDown 0x1000000 /* SELECT has be modified by push-down opt */
+#define SF_PushDown 0x1000000 /* Modified by WHERE-clause push-down opt */
#define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */
#define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */
#define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */
#define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */
+#define SF_Correlated 0x20000000 /* True if references the outer context */
/* True if S exists and has SF_NestedFrom */
#define IsNestedFrom(S) ((S)!=0 && ((S)->selFlags&SF_NestedFrom)!=0)
@@ -19410,6 +19799,7 @@ struct Parse {
u8 disableLookaside; /* Number of times lookaside has been disabled */
u8 prepFlags; /* SQLITE_PREPARE_* flags */
u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */
+ u8 bHasWith; /* True if statement contains WITH */
#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */
#endif
@@ -19429,6 +19819,7 @@ struct Parse {
int *aLabel; /* Space to hold the labels */
ExprList *pConstExpr;/* Constant expressions */
IndexedExpr *pIdxEpr;/* List of expressions used by active indexes */
+ IndexedExpr *pIdxPartExpr; /* Exprs constrained by index WHERE clauses */
Token constraintName;/* Name of the constraint currently being parsed */
yDbMask writeMask; /* Start a write transaction on these databases */
yDbMask cookieMask; /* Bitmask of schema verified databases */
@@ -19700,6 +20091,7 @@ struct Returning {
int iRetCur; /* Transient table holding RETURNING results */
int nRetCol; /* Number of in pReturnEL after expansion */
int iRetReg; /* Register array for holding a row of RETURNING */
+ char zName[40]; /* Name of trigger: "sqlite_returning_%p" */
};
/*
@@ -19735,6 +20127,9 @@ struct sqlite3_str {
**
** 3. Make a (read-only) copy of a read-only RCStr string using
** sqlite3RCStrRef().
+**
+** "String" is in the name, but an RCStr object can also be used to hold
+** binary data.
*/
struct RCStr {
u64 nRCRef; /* Number of references */
@@ -19793,6 +20188,9 @@ struct Sqlite3Config {
u8 bSmallMalloc; /* Avoid large memory allocations if true */
u8 bExtraSchemaChecks; /* Verify type,name,tbl_name in schema */
u8 bUseLongDouble; /* Make use of long double */
+#ifdef SQLITE_DEBUG
+ u8 bJsonSelfcheck; /* Double-check JSON parsing */
+#endif
int mxStrlen; /* Maximum string length */
int neverCorrupt; /* Database is always well-formed */
int szLookaside; /* Default lookaside buffer size */
@@ -19840,6 +20238,11 @@ struct Sqlite3Config {
#ifndef SQLITE_UNTESTABLE
int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */
#endif
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ u32 mNoVisibleRowid; /* TF_NoVisibleRowid if the ROWID_IN_VIEW
+ ** feature is disabled. 0 if rowids can
+ ** occur in views. */
+#endif
int bLocaltimeFault; /* True to fail localtime() calls */
int (*xAltLocaltime)(const void*,void*); /* Alternative localtime() routine */
int iOnceResetThreshold; /* When to reset OP_Once counters */
@@ -20000,6 +20403,16 @@ struct CteUse {
};
+/* Client data associated with sqlite3_set_clientdata() and
+** sqlite3_get_clientdata().
+*/
+struct DbClientData {
+ DbClientData *pNext; /* Next in a linked list */
+ void *pData; /* The data */
+ void (*xDestructor)(void*); /* Destructor. Might be NULL */
+ char zName[1]; /* Name of this client data. MUST BE LAST */
+};
+
#ifdef SQLITE_DEBUG
/*
** An instance of the TreeView object is used for printing the content of
@@ -20066,6 +20479,9 @@ struct Window {
** due to the SQLITE_SUBTYPE flag */
};
+SQLITE_PRIVATE Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow);
+SQLITE_PRIVATE void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal);
+
#ifndef SQLITE_OMIT_WINDOWFUNC
SQLITE_PRIVATE void sqlite3WindowDelete(sqlite3*, Window*);
SQLITE_PRIVATE void sqlite3WindowUnlinkFromSelect(Window*);
@@ -20285,10 +20701,13 @@ SQLITE_PRIVATE void sqlite3MutexWarnOnContention(sqlite3_mutex*);
# define EXP754 (((u64)0x7ff)<<52)
# define MAN754 ((((u64)1)<<52)-1)
# define IsNaN(X) (((X)&EXP754)==EXP754 && ((X)&MAN754)!=0)
+# define IsOvfl(X) (((X)&EXP754)==EXP754)
SQLITE_PRIVATE int sqlite3IsNaN(double);
+SQLITE_PRIVATE int sqlite3IsOverflow(double);
#else
-# define IsNaN(X) 0
-# define sqlite3IsNaN(X) 0
+# define IsNaN(X) 0
+# define sqlite3IsNaN(X) 0
+# define sqlite3IsOVerflow(X) 0
#endif
/*
@@ -20380,6 +20799,7 @@ SQLITE_PRIVATE int sqlite3ErrorToParser(sqlite3*,int);
SQLITE_PRIVATE void sqlite3Dequote(char*);
SQLITE_PRIVATE void sqlite3DequoteExpr(Expr*);
SQLITE_PRIVATE void sqlite3DequoteToken(Token*);
+SQLITE_PRIVATE void sqlite3DequoteNumber(Parse*, Expr*);
SQLITE_PRIVATE void sqlite3TokenInit(Token*,char*);
SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char*, int);
SQLITE_PRIVATE int sqlite3RunParser(Parse*, const char*);
@@ -20404,10 +20824,13 @@ SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse*, Expr*, Select*);
SQLITE_PRIVATE Expr *sqlite3ExprAnd(Parse*,Expr*, Expr*);
SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr*);
SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, const Token*, int);
+SQLITE_PRIVATE void sqlite3ExprAddFunctionOrderBy(Parse*,Expr*,ExprList*);
+SQLITE_PRIVATE void sqlite3ExprOrderByAggregateError(Parse*,Expr*);
SQLITE_PRIVATE void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*);
SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32);
SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*);
-SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse*, Expr*);
+SQLITE_PRIVATE void sqlite3ExprDeleteGeneric(sqlite3*,void*);
+SQLITE_PRIVATE int sqlite3ExprDeferredDelete(Parse*, Expr*);
SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse*, Expr*);
SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*);
SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*);
@@ -20416,6 +20839,7 @@ SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int,int);
SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,const Token*,int);
SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*);
SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*);
+SQLITE_PRIVATE void sqlite3ExprListDeleteGeneric(sqlite3*,void*);
SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList*);
SQLITE_PRIVATE int sqlite3IndexHasDuplicateRootPage(Index*);
SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**);
@@ -20506,6 +20930,7 @@ SQLITE_PRIVATE int sqlite3DbMaskAllZero(yDbMask);
SQLITE_PRIVATE void sqlite3DropTable(Parse*, SrcList*, int, int);
SQLITE_PRIVATE void sqlite3CodeDropTable(Parse*, Table*, int, int);
SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3*, Table*);
+SQLITE_PRIVATE void sqlite3DeleteTableGeneric(sqlite3*, void*);
SQLITE_PRIVATE void sqlite3FreeIndex(sqlite3*, Index*);
#ifndef SQLITE_OMIT_AUTOINCREMENT
SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse);
@@ -20542,6 +20967,7 @@ SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
Expr*,ExprList*,u32,Expr*);
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);
+SQLITE_PRIVATE void sqlite3SelectDeleteGeneric(sqlite3*,void*);
SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*);
SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, Trigger*);
SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
@@ -20627,12 +21053,10 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3*);
SQLITE_PRIVATE u32 sqlite3IsTrueOrFalse(const char*);
SQLITE_PRIVATE int sqlite3ExprIdToTrueFalse(Expr*);
SQLITE_PRIVATE int sqlite3ExprTruthValue(const Expr*);
-SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*);
-SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*);
+SQLITE_PRIVATE int sqlite3ExprIsConstant(Parse*,Expr*);
SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8);
SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse*, Expr*, ExprList*);
-SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int);
-SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int);
+SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(Expr*,const SrcList*,int,int);
#ifdef SQLITE_ENABLE_CURSOR_HINTS
SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*);
#endif
@@ -20640,6 +21064,7 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(const Expr*, int*);
SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*);
SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
SQLITE_PRIVATE int sqlite3IsRowid(const char*);
+SQLITE_PRIVATE const char *sqlite3RowidAlias(Table *pTab);
SQLITE_PRIVATE void sqlite3GenerateRowDelete(
Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int);
SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int);
@@ -20767,6 +21192,7 @@ SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar);
#endif
SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte);
SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**);
+SQLITE_PRIVATE int sqlite3Utf8ReadLimited(const u8*, int, u32*);
SQLITE_PRIVATE LogEst sqlite3LogEst(u64);
SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst,LogEst);
SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double);
@@ -20815,7 +21241,9 @@ SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...);
SQLITE_PRIVATE void sqlite3Error(sqlite3*,int);
SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3*);
SQLITE_PRIVATE void sqlite3SystemError(sqlite3*,int);
+#if !defined(SQLITE_OMIT_BLOB_LITERAL)
SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n);
+#endif
SQLITE_PRIVATE u8 sqlite3HexToInt(int h);
SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **);
@@ -20911,7 +21339,8 @@ SQLITE_PRIVATE int sqlite3MatchEName(
const struct ExprList_item*,
const char*,
const char*,
- const char*
+ const char*,
+ int*
);
SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr*);
SQLITE_PRIVATE u8 sqlite3StrIHash(const char*);
@@ -20968,7 +21397,7 @@ SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int);
SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *);
SQLITE_PRIVATE char *sqlite3RCStrRef(char*);
-SQLITE_PRIVATE void sqlite3RCStrUnref(char*);
+SQLITE_PRIVATE void sqlite3RCStrUnref(void*);
SQLITE_PRIVATE char *sqlite3RCStrNew(u64);
SQLITE_PRIVATE char *sqlite3RCStrResize(char*,u64);
@@ -21112,6 +21541,7 @@ SQLITE_PRIVATE Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8);
SQLITE_PRIVATE void sqlite3CteDelete(sqlite3*,Cte*);
SQLITE_PRIVATE With *sqlite3WithAdd(Parse*,With*,Cte*);
SQLITE_PRIVATE void sqlite3WithDelete(sqlite3*,With*);
+SQLITE_PRIVATE void sqlite3WithDeleteGeneric(sqlite3*,void*);
SQLITE_PRIVATE With *sqlite3WithPush(Parse*, With*, u8);
#else
# define sqlite3CteNew(P,T,E,S) ((void*)0)
@@ -21124,7 +21554,7 @@ SQLITE_PRIVATE With *sqlite3WithPush(Parse*, With*, u8);
SQLITE_PRIVATE Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*);
SQLITE_PRIVATE void sqlite3UpsertDelete(sqlite3*,Upsert*);
SQLITE_PRIVATE Upsert *sqlite3UpsertDup(sqlite3*,Upsert*);
-SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*);
+SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*,Upsert*);
SQLITE_PRIVATE void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int);
SQLITE_PRIVATE Upsert *sqlite3UpsertOfIndex(Upsert*,Index*);
SQLITE_PRIVATE int sqlite3UpsertNextIsIPK(Upsert*);
@@ -21514,6 +21944,9 @@ static const char * const sqlite3azCompileOpt[] = {
"ALLOW_COVERING_INDEX_SCAN=" CTIMEOPT_VAL(SQLITE_ALLOW_COVERING_INDEX_SCAN),
# endif
#endif
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ "ALLOW_ROWID_IN_VIEW",
+#endif
#ifdef SQLITE_ALLOW_URI_AUTHORITY
"ALLOW_URI_AUTHORITY",
#endif
@@ -21804,6 +22237,9 @@ static const char * const sqlite3azCompileOpt[] = {
#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
"EXPLAIN_ESTIMATED_ROWS",
#endif
+#ifdef SQLITE_EXTRA_AUTOEXT
+ "EXTRA_AUTOEXT=" CTIMEOPT_VAL(SQLITE_EXTRA_AUTOEXT),
+#endif
#ifdef SQLITE_EXTRA_IFNULLROW
"EXTRA_IFNULLROW",
#endif
@@ -22085,6 +22521,9 @@ static const char * const sqlite3azCompileOpt[] = {
#ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS
"OMIT_SCHEMA_VERSION_PRAGMAS",
#endif
+#ifdef SQLITE_OMIT_SEH
+ "OMIT_SEH",
+#endif
#ifdef SQLITE_OMIT_SHARED_CACHE
"OMIT_SHARED_CACHE",
#endif
@@ -22483,6 +22922,9 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
0, /* bSmallMalloc */
1, /* bExtraSchemaChecks */
sizeof(LONGDOUBLE_TYPE)>8, /* bUseLongDouble */
+#ifdef SQLITE_DEBUG
+ 0, /* bJsonSelfcheck */
+#endif
0x7ffffffe, /* mxStrlen */
0, /* neverCorrupt */
SQLITE_DEFAULT_LOOKASIDE, /* szLookaside, nLookaside */
@@ -22525,6 +22967,9 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
#ifndef SQLITE_UNTESTABLE
0, /* xTestCallback */
#endif
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ 0, /* mNoVisibleRowid. 0 == allow rowid-in-view */
+#endif
0, /* bLocaltimeFault */
0, /* xAltLocaltime */
0x7ffffffe, /* iOnceResetThreshold */
@@ -23735,7 +24180,7 @@ SQLITE_API int sqlite3_db_status(
case SQLITE_DBSTATUS_CACHE_MISS:
case SQLITE_DBSTATUS_CACHE_WRITE:{
int i;
- int nRet = 0;
+ u64 nRet = 0;
assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 );
assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 );
@@ -23748,7 +24193,7 @@ SQLITE_API int sqlite3_db_status(
*pHighwater = 0; /* IMP: R-42420-56072 */
/* IMP: R-54100-20147 */
/* IMP: R-29431-39229 */
- *pCurrent = nRet;
+ *pCurrent = (int)nRet & 0x7fffffff;
break;
}
@@ -23845,13 +24290,14 @@ struct DateTime {
int tz; /* Timezone offset in minutes */
double s; /* Seconds */
char validJD; /* True (1) if iJD is valid */
- char rawS; /* Raw numeric value stored in s */
char validYMD; /* True (1) if Y,M,D are valid */
char validHMS; /* True (1) if h,m,s are valid */
- char validTZ; /* True (1) if tz is valid */
- char tzSet; /* Timezone was set explicitly */
- char isError; /* An overflow has occurred */
- char useSubsec; /* Display subsecond precision */
+ char nFloor; /* Days to implement "floor" */
+ unsigned rawS : 1; /* Raw numeric value stored in s */
+ unsigned isError : 1; /* An overflow has occurred */
+ unsigned useSubsec : 1; /* Display subsecond precision */
+ unsigned isUtc : 1; /* Time is known to be UTC */
+ unsigned isLocal : 1; /* Time is known to be localtime */
};
@@ -23949,6 +24395,8 @@ static int parseTimezone(const char *zDate, DateTime *p){
sgn = +1;
}else if( c=='Z' || c=='z' ){
zDate++;
+ p->isLocal = 0;
+ p->isUtc = 1;
goto zulu_time;
}else{
return c!=0;
@@ -23961,7 +24409,6 @@ static int parseTimezone(const char *zDate, DateTime *p){
p->tz = sgn*(nMn + nHr*60);
zulu_time:
while( sqlite3Isspace(*zDate) ){ zDate++; }
- p->tzSet = 1;
return *zDate!=0;
}
@@ -24005,7 +24452,6 @@ static int parseHhMmSs(const char *zDate, DateTime *p){
p->m = m;
p->s = s + ms;
if( parseTimezone(zDate, p) ) return 1;
- p->validTZ = (p->tz!=0)?1:0;
return 0;
}
@@ -24052,16 +24498,41 @@ static void computeJD(DateTime *p){
p->validJD = 1;
if( p->validHMS ){
p->iJD += p->h*3600000 + p->m*60000 + (sqlite3_int64)(p->s*1000 + 0.5);
- if( p->validTZ ){
+ if( p->tz ){
p->iJD -= p->tz*60000;
p->validYMD = 0;
p->validHMS = 0;
- p->validTZ = 0;
+ p->tz = 0;
+ p->isUtc = 1;
+ p->isLocal = 0;
}
}
}
/*
+** Given the YYYY-MM-DD information current in p, determine if there
+** is day-of-month overflow and set nFloor to the number of days that
+** would need to be subtracted from the date in order to bring the
+** date back to the end of the month.
+*/
+static void computeFloor(DateTime *p){
+ assert( p->validYMD || p->isError );
+ assert( p->D>=0 && p->D<=31 );
+ assert( p->M>=0 && p->M<=12 );
+ if( p->D<=28 ){
+ p->nFloor = 0;
+ }else if( (1<<p->M) & 0x15aa ){
+ p->nFloor = 0;
+ }else if( p->M!=2 ){
+ p->nFloor = (p->D==31);
+ }else if( p->Y%4!=0 || (p->Y%100==0 && p->Y%400!=0) ){
+ p->nFloor = p->D - 28;
+ }else{
+ p->nFloor = p->D - 29;
+ }
+}
+
+/*
** Parse dates of the form
**
** YYYY-MM-DD HH:MM:SS.FFF
@@ -24099,12 +24570,16 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){
p->Y = neg ? -Y : Y;
p->M = M;
p->D = D;
- if( p->validTZ ){
+ computeFloor(p);
+ if( p->tz ){
computeJD(p);
}
return 0;
}
+
+static void clearYMD_HMS_TZ(DateTime *p); /* Forward declaration */
+
/*
** Set the time to the current time reported by the VFS.
**
@@ -24114,6 +24589,9 @@ static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){
p->iJD = sqlite3StmtCurrentTime(context);
if( p->iJD>0 ){
p->validJD = 1;
+ p->isUtc = 1;
+ p->isLocal = 0;
+ clearYMD_HMS_TZ(p);
return 0;
}else{
return 1;
@@ -24252,7 +24730,7 @@ static void computeYMD_HMS(DateTime *p){
static void clearYMD_HMS_TZ(DateTime *p){
p->validYMD = 0;
p->validHMS = 0;
- p->validTZ = 0;
+ p->tz = 0;
}
#ifndef SQLITE_OMIT_LOCALTIME
@@ -24384,7 +24862,7 @@ static int toLocaltime(
p->validHMS = 1;
p->validJD = 0;
p->rawS = 0;
- p->validTZ = 0;
+ p->tz = 0;
p->isError = 0;
return SQLITE_OK;
}
@@ -24404,12 +24882,12 @@ static const struct {
float rLimit; /* Maximum NNN value for this transform */
float rXform; /* Constant used for this transform */
} aXformType[] = {
- { 6, "second", 4.6427e+14, 1.0 },
- { 6, "minute", 7.7379e+12, 60.0 },
- { 4, "hour", 1.2897e+11, 3600.0 },
- { 3, "day", 5373485.0, 86400.0 },
- { 5, "month", 176546.0, 2592000.0 },
- { 4, "year", 14713.0, 31536000.0 },
+ /* 0 */ { 6, "second", 4.6427e+14, 1.0 },
+ /* 1 */ { 6, "minute", 7.7379e+12, 60.0 },
+ /* 2 */ { 4, "hour", 1.2897e+11, 3600.0 },
+ /* 3 */ { 3, "day", 5373485.0, 86400.0 },
+ /* 4 */ { 5, "month", 176546.0, 30.0*86400.0 },
+ /* 5 */ { 4, "year", 14713.0, 365.0*86400.0 },
};
/*
@@ -24441,14 +24919,20 @@ static void autoAdjustDate(DateTime *p){
** NNN.NNNN seconds
** NNN months
** NNN years
+** +/-YYYY-MM-DD HH:MM:SS.SSS
+** ceiling
+** floor
** start of month
** start of year
** start of week
** start of day
** weekday N
** unixepoch
+** auto
** localtime
** utc
+** subsec
+** subsecond
**
** Return 0 on success and 1 if there is any kind of error. If the error
** is in a system call (i.e. localtime()), then an error message is written
@@ -24479,6 +24963,37 @@ static int parseModifier(
}
break;
}
+ case 'c': {
+ /*
+ ** ceiling
+ **
+ ** Resolve day-of-month overflow by rolling forward into the next
+ ** month. As this is the default action, this modifier is really
+ ** a no-op that is only included for symmetry. See "floor".
+ */
+ if( sqlite3_stricmp(z, "ceiling")==0 ){
+ computeJD(p);
+ clearYMD_HMS_TZ(p);
+ rc = 0;
+ p->nFloor = 0;
+ }
+ break;
+ }
+ case 'f': {
+ /*
+ ** floor
+ **
+ ** Resolve day-of-month overflow by rolling back to the end of the
+ ** previous month.
+ */
+ if( sqlite3_stricmp(z, "floor")==0 ){
+ computeJD(p);
+ p->iJD -= p->nFloor*86400000;
+ clearYMD_HMS_TZ(p);
+ rc = 0;
+ }
+ break;
+ }
case 'j': {
/*
** julianday
@@ -24505,7 +25020,9 @@ static int parseModifier(
** show local time.
*/
if( sqlite3_stricmp(z, "localtime")==0 && sqlite3NotPureFunc(pCtx) ){
- rc = toLocaltime(p, pCtx);
+ rc = p->isLocal ? SQLITE_OK : toLocaltime(p, pCtx);
+ p->isUtc = 0;
+ p->isLocal = 1;
}
break;
}
@@ -24530,7 +25047,7 @@ static int parseModifier(
}
#ifndef SQLITE_OMIT_LOCALTIME
else if( sqlite3_stricmp(z, "utc")==0 && sqlite3NotPureFunc(pCtx) ){
- if( p->tzSet==0 ){
+ if( p->isUtc==0 ){
i64 iOrigJD; /* Original localtime */
i64 iGuess; /* Guess at the corresponding utc time */
int cnt = 0; /* Safety to prevent infinite loop */
@@ -24553,7 +25070,8 @@ static int parseModifier(
memset(p, 0, sizeof(*p));
p->iJD = iGuess;
p->validJD = 1;
- p->tzSet = 1;
+ p->isUtc = 1;
+ p->isLocal = 0;
}
rc = SQLITE_OK;
}
@@ -24573,7 +25091,7 @@ static int parseModifier(
&& r>=0.0 && r<7.0 && (n=(int)r)==r ){
sqlite3_int64 Z;
computeYMD_HMS(p);
- p->validTZ = 0;
+ p->tz = 0;
p->validJD = 0;
computeJD(p);
Z = ((p->iJD + 129600000)/86400000) % 7;
@@ -24613,7 +25131,7 @@ static int parseModifier(
p->h = p->m = 0;
p->s = 0.0;
p->rawS = 0;
- p->validTZ = 0;
+ p->tz = 0;
p->validJD = 0;
if( sqlite3_stricmp(z,"month")==0 ){
p->D = 1;
@@ -24684,6 +25202,7 @@ static int parseModifier(
x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12;
p->Y += x;
p->M -= x*12;
+ computeFloor(p);
computeJD(p);
p->validHMS = 0;
p->validYMD = 0;
@@ -24730,11 +25249,12 @@ static int parseModifier(
z += n;
while( sqlite3Isspace(*z) ) z++;
n = sqlite3Strlen30(z);
- if( n>10 || n<3 ) break;
+ if( n<3 || n>10 ) break;
if( sqlite3UpperToLower[(u8)z[n-1]]=='s' ) n--;
computeJD(p);
assert( rc==1 );
rRounder = r<0 ? -0.5 : +0.5;
+ p->nFloor = 0;
for(i=0; i<ArraySize(aXformType); i++){
if( aXformType[i].nName==n
&& sqlite3_strnicmp(aXformType[i].zName, z, n)==0
@@ -24742,21 +25262,24 @@ static int parseModifier(
){
switch( i ){
case 4: { /* Special processing to add months */
- assert( strcmp(aXformType[i].zName,"month")==0 );
+ assert( strcmp(aXformType[4].zName,"month")==0 );
computeYMD_HMS(p);
p->M += (int)r;
x = p->M>0 ? (p->M-1)/12 : (p->M-12)/12;
p->Y += x;
p->M -= x*12;
+ computeFloor(p);
p->validJD = 0;
r -= (int)r;
break;
}
case 5: { /* Special processing to add years */
int y = (int)r;
- assert( strcmp(aXformType[i].zName,"year")==0 );
+ assert( strcmp(aXformType[5].zName,"year")==0 );
computeYMD_HMS(p);
+ assert( p->M>=0 && p->M<=12 );
p->Y += y;
+ computeFloor(p);
p->validJD = 0;
r -= (int)r;
break;
@@ -24817,6 +25340,12 @@ static int isDate(
}
computeJD(p);
if( p->isError || !validJulianDay(p->iJD) ) return 1;
+ if( argc==1 && p->validYMD && p->D>28 ){
+ /* Make sure a YYYY-MM-DD is normalized.
+ ** Example: 2023-02-31 -> 2023-03-03 */
+ assert( p->validJD );
+ p->validYMD = 0;
+ }
return 0;
}
@@ -25005,21 +25534,82 @@ static void dateFunc(
}
/*
+** Compute the number of days after the most recent January 1.
+**
+** In other words, compute the zero-based day number for the
+** current year:
+**
+** Jan01 = 0, Jan02 = 1, ..., Jan31 = 30, Feb01 = 31, ...
+** Dec31 = 364 or 365.
+*/
+static int daysAfterJan01(DateTime *pDate){
+ DateTime jan01 = *pDate;
+ assert( jan01.validYMD );
+ assert( jan01.validHMS );
+ assert( pDate->validJD );
+ jan01.validJD = 0;
+ jan01.M = 1;
+ jan01.D = 1;
+ computeJD(&jan01);
+ return (int)((pDate->iJD-jan01.iJD+43200000)/86400000);
+}
+
+/*
+** Return the number of days after the most recent Monday.
+**
+** In other words, return the day of the week according
+** to this code:
+**
+** 0=Monday, 1=Tuesday, 2=Wednesday, ..., 6=Sunday.
+*/
+static int daysAfterMonday(DateTime *pDate){
+ assert( pDate->validJD );
+ return (int)((pDate->iJD+43200000)/86400000) % 7;
+}
+
+/*
+** Return the number of days after the most recent Sunday.
+**
+** In other words, return the day of the week according
+** to this code:
+**
+** 0=Sunday, 1=Monday, 2=Tues, ..., 6=Saturday
+*/
+static int daysAfterSunday(DateTime *pDate){
+ assert( pDate->validJD );
+ return (int)((pDate->iJD+129600000)/86400000) % 7;
+}
+
+/*
** strftime( FORMAT, TIMESTRING, MOD, MOD, ...)
**
** Return a string described by FORMAT. Conversions as follows:
**
-** %d day of month
+** %d day of month 01-31
+** %e day of month 1-31
** %f ** fractional seconds SS.SSS
+** %F ISO date. YYYY-MM-DD
+** %G ISO year corresponding to %V 0000-9999.
+** %g 2-digit ISO year corresponding to %V 00-99
** %H hour 00-24
-** %j day of year 000-366
+** %k hour 0-24 (leading zero converted to space)
+** %I hour 01-12
+** %j day of year 001-366
** %J ** julian day number
+** %l hour 1-12 (leading zero converted to space)
** %m month 01-12
** %M minute 00-59
+** %p "am" or "pm"
+** %P "AM" or "PM"
+** %R time as HH:MM
** %s seconds since 1970-01-01
** %S seconds 00-59
-** %w day of week 0-6 Sunday==0
-** %W week of year 00-53
+** %T time as HH:MM:SS
+** %u day of week 1-7 Monday==1, Sunday==7
+** %w day of week 0-6 Sunday==0, Monday==1
+** %U week of year 00-53 (First Sunday is start of week 01)
+** %V week of year 01-53 (First week containing Thursday is week 01)
+** %W week of year 00-53 (First Monday is start of week 01)
** %Y year 0000-9999
** %% %
*/
@@ -25044,44 +25634,61 @@ static void strftimeFunc(
computeJD(&x);
computeYMD_HMS(&x);
for(i=j=0; zFmt[i]; i++){
+ char cf;
if( zFmt[i]!='%' ) continue;
if( j<i ) sqlite3_str_append(&sRes, zFmt+j, (int)(i-j));
i++;
j = i + 1;
- switch( zFmt[i] ){
- case 'd': {
- sqlite3_str_appendf(&sRes, "%02d", x.D);
+ cf = zFmt[i];
+ switch( cf ){
+ case 'd': /* Fall thru */
+ case 'e': {
+ sqlite3_str_appendf(&sRes, cf=='d' ? "%02d" : "%2d", x.D);
break;
}
- case 'f': {
+ case 'f': { /* Fractional seconds. (Non-standard) */
double s = x.s;
if( s>59.999 ) s = 59.999;
sqlite3_str_appendf(&sRes, "%06.3f", s);
break;
}
- case 'H': {
- sqlite3_str_appendf(&sRes, "%02d", x.h);
+ case 'F': {
+ sqlite3_str_appendf(&sRes, "%04d-%02d-%02d", x.Y, x.M, x.D);
break;
}
- case 'W': /* Fall thru */
- case 'j': {
- int nDay; /* Number of days since 1st day of year */
+ case 'G': /* Fall thru */
+ case 'g': {
DateTime y = x;
- y.validJD = 0;
- y.M = 1;
- y.D = 1;
- computeJD(&y);
- nDay = (int)((x.iJD-y.iJD+43200000)/86400000);
- if( zFmt[i]=='W' ){
- int wd; /* 0=Monday, 1=Tuesday, ... 6=Sunday */
- wd = (int)(((x.iJD+43200000)/86400000)%7);
- sqlite3_str_appendf(&sRes,"%02d",(nDay+7-wd)/7);
+ assert( y.validJD );
+ /* Move y so that it is the Thursday in the same week as x */
+ y.iJD += (3 - daysAfterMonday(&x))*86400000;
+ y.validYMD = 0;
+ computeYMD(&y);
+ if( cf=='g' ){
+ sqlite3_str_appendf(&sRes, "%02d", y.Y%100);
}else{
- sqlite3_str_appendf(&sRes,"%03d",nDay+1);
+ sqlite3_str_appendf(&sRes, "%04d", y.Y);
}
break;
}
- case 'J': {
+ case 'H':
+ case 'k': {
+ sqlite3_str_appendf(&sRes, cf=='H' ? "%02d" : "%2d", x.h);
+ break;
+ }
+ case 'I': /* Fall thru */
+ case 'l': {
+ int h = x.h;
+ if( h>12 ) h -= 12;
+ if( h==0 ) h = 12;
+ sqlite3_str_appendf(&sRes, cf=='I' ? "%02d" : "%2d", h);
+ break;
+ }
+ case 'j': { /* Day of year. Jan01==1, Jan02==2, and so forth */
+ sqlite3_str_appendf(&sRes,"%03d",daysAfterJan01(&x)+1);
+ break;
+ }
+ case 'J': { /* Julian day number. (Non-standard) */
sqlite3_str_appendf(&sRes,"%.16g",x.iJD/86400000.0);
break;
}
@@ -25093,6 +25700,19 @@ static void strftimeFunc(
sqlite3_str_appendf(&sRes,"%02d",x.m);
break;
}
+ case 'p': /* Fall thru */
+ case 'P': {
+ if( x.h>=12 ){
+ sqlite3_str_append(&sRes, cf=='p' ? "PM" : "pm", 2);
+ }else{
+ sqlite3_str_append(&sRes, cf=='p' ? "AM" : "am", 2);
+ }
+ break;
+ }
+ case 'R': {
+ sqlite3_str_appendf(&sRes, "%02d:%02d", x.h, x.m);
+ break;
+ }
case 's': {
if( x.useSubsec ){
sqlite3_str_appendf(&sRes,"%.3f",
@@ -25107,9 +25727,35 @@ static void strftimeFunc(
sqlite3_str_appendf(&sRes,"%02d",(int)x.s);
break;
}
- case 'w': {
- sqlite3_str_appendchar(&sRes, 1,
- (char)(((x.iJD+129600000)/86400000) % 7) + '0');
+ case 'T': {
+ sqlite3_str_appendf(&sRes,"%02d:%02d:%02d", x.h, x.m, (int)x.s);
+ break;
+ }
+ case 'u': /* Day of week. 1 to 7. Monday==1, Sunday==7 */
+ case 'w': { /* Day of week. 0 to 6. Sunday==0, Monday==1 */
+ char c = (char)daysAfterSunday(&x) + '0';
+ if( c=='0' && cf=='u' ) c = '7';
+ sqlite3_str_appendchar(&sRes, 1, c);
+ break;
+ }
+ case 'U': { /* Week num. 00-53. First Sun of the year is week 01 */
+ sqlite3_str_appendf(&sRes,"%02d",
+ (daysAfterJan01(&x)-daysAfterSunday(&x)+7)/7);
+ break;
+ }
+ case 'V': { /* Week num. 01-53. First week with a Thur is week 01 */
+ DateTime y = x;
+ /* Adjust y so that is the Thursday in the same week as x */
+ assert( y.validJD );
+ y.iJD += (3 - daysAfterMonday(&x))*86400000;
+ y.validYMD = 0;
+ computeYMD(&y);
+ sqlite3_str_appendf(&sRes,"%02d", daysAfterJan01(&y)/7+1);
+ break;
+ }
+ case 'W': { /* Week num. 00-53. First Mon of the year is week 01 */
+ sqlite3_str_appendf(&sRes,"%02d",
+ (daysAfterJan01(&x)-daysAfterMonday(&x)+7)/7);
break;
}
case 'Y': {
@@ -25258,9 +25904,7 @@ static void timediffFunc(
d1.iJD = d2.iJD - d1.iJD;
d1.iJD += (u64)1486995408 * (u64)100000;
}
- d1.validYMD = 0;
- d1.validHMS = 0;
- d1.validTZ = 0;
+ clearYMD_HMS_TZ(&d1);
computeYMD_HMS(&d1);
sqlite3StrAccumInit(&sRes, 0, 0, 0, 100);
sqlite3_str_appendf(&sRes, "%c%04d-%02d-%02d %02d:%02d:%06.3f",
@@ -25329,6 +25973,36 @@ static void currentTimeFunc(
}
#endif
+#if !defined(SQLITE_OMIT_DATETIME_FUNCS) && defined(SQLITE_DEBUG)
+/*
+** datedebug(...)
+**
+** This routine returns JSON that describes the internal DateTime object.
+** Used for debugging and testing only. Subject to change.
+*/
+static void datedebugFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ DateTime x;
+ if( isDate(context, argc, argv, &x)==0 ){
+ char *zJson;
+ zJson = sqlite3_mprintf(
+ "{iJD:%lld,Y:%d,M:%d,D:%d,h:%d,m:%d,tz:%d,"
+ "s:%.3f,validJD:%d,validYMS:%d,validHMS:%d,"
+ "nFloor:%d,rawS:%d,isError:%d,useSubsec:%d,"
+ "isUtc:%d,isLocal:%d}",
+ x.iJD, x.Y, x.M, x.D, x.h, x.m, x.tz,
+ x.s, x.validJD, x.validYMD, x.validHMS,
+ x.nFloor, x.rawS, x.isError, x.useSubsec,
+ x.isUtc, x.isLocal);
+ sqlite3_result_text(context, zJson, -1, sqlite3_free);
+ }
+}
+#endif /* !SQLITE_OMIT_DATETIME_FUNCS && SQLITE_DEBUG */
+
+
/*
** This function registered all of the above C functions as SQL
** functions. This should be the only routine in this file with
@@ -25344,6 +26018,9 @@ SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){
PURE_DATE(datetime, -1, 0, 0, datetimeFunc ),
PURE_DATE(strftime, -1, 0, 0, strftimeFunc ),
PURE_DATE(timediff, 2, 0, 0, timediffFunc ),
+#ifdef SQLITE_DEBUG
+ PURE_DATE(datedebug, -1, 0, 0, datedebugFunc ),
+#endif
DFUNCTION(current_time, 0, 0, 0, ctimeFunc ),
DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc),
DFUNCTION(current_date, 0, 0, 0, cdateFunc ),
@@ -28198,7 +28875,7 @@ static void checkMutexFree(sqlite3_mutex *p){
assert( SQLITE_MUTEX_FAST<2 );
assert( SQLITE_MUTEX_WARNONCONTENTION<2 );
-#if SQLITE_ENABLE_API_ARMOR
+#ifdef SQLITE_ENABLE_API_ARMOR
if( ((CheckMutex*)p)->iType<2 )
#endif
{
@@ -28870,7 +29547,7 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
*/
static void pthreadMutexFree(sqlite3_mutex *p){
assert( p->nRef==0 );
-#if SQLITE_ENABLE_API_ARMOR
+#ifdef SQLITE_ENABLE_API_ARMOR
if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE )
#endif
{
@@ -29223,7 +29900,7 @@ SQLITE_PRIVATE void sqlite3MemoryBarrier(void){
SQLITE_MEMORY_BARRIER;
#elif defined(__GNUC__)
__sync_synchronize();
-#elif MSVC_VERSION>=1300
+#elif MSVC_VERSION>=1400
_ReadWriteBarrier();
#elif defined(MemoryBarrier)
MemoryBarrier();
@@ -29759,6 +30436,24 @@ static void sqlite3MallocAlarm(int nByte){
sqlite3_mutex_enter(mem0.mutex);
}
+#ifdef SQLITE_DEBUG
+/*
+** This routine is called whenever an out-of-memory condition is seen,
+** It's only purpose to to serve as a breakpoint for gdb or similar
+** code debuggers when working on out-of-memory conditions, for example
+** caused by PRAGMA hard_heap_limit=N.
+*/
+static SQLITE_NOINLINE void test_oom_breakpoint(u64 n){
+ static u64 nOomFault = 0;
+ nOomFault += n;
+ /* The assert() is never reached in a human lifetime. It is here mostly
+ ** to prevent code optimizers from optimizing out this function. */
+ assert( (nOomFault>>32) < 0xffffffff );
+}
+#else
+# define test_oom_breakpoint(X) /* No-op for production builds */
+#endif
+
/*
** Do a memory allocation with statistics and alarms. Assume the
** lock is already held.
@@ -29785,6 +30480,7 @@ static void mallocWithAlarm(int n, void **pp){
if( mem0.hardLimit ){
nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
if( nUsed >= mem0.hardLimit - nFull ){
+ test_oom_breakpoint(1);
*pp = 0;
return;
}
@@ -30073,6 +30769,7 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){
sqlite3MallocAlarm(nDiff);
if( mem0.hardLimit>0 && nUsed >= mem0.hardLimit - nDiff ){
sqlite3_mutex_leave(mem0.mutex);
+ test_oom_breakpoint(1);
return 0;
}
}
@@ -30434,7 +31131,7 @@ SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){
if( db->mallocFailed || rc ){
return apiHandleError(db, rc);
}
- return rc & db->errMask;
+ return 0;
}
/************** End of malloc.c **********************************************/
@@ -30939,6 +31636,7 @@ SQLITE_API void sqlite3_str_vappendf(
if( xtype==etFLOAT ){
iRound = -precision;
}else if( xtype==etGENERIC ){
+ if( precision==0 ) precision = 1;
iRound = precision;
}else{
iRound = precision+1;
@@ -30974,13 +31672,14 @@ SQLITE_API void sqlite3_str_vappendf(
}
exp = s.iDP-1;
- if( xtype==etGENERIC && precision>0 ) precision--;
/*
** If the field type is etGENERIC, then convert to either etEXP
** or etFLOAT, as appropriate.
*/
if( xtype==etGENERIC ){
+ assert( precision>0 );
+ precision--;
flag_rtz = !flag_alternateform;
if( exp<-4 || exp>precision ){
xtype = etEXP;
@@ -31296,9 +31995,13 @@ SQLITE_API void sqlite3_str_vappendf(
sqlite3_str_appendall(pAccum, pItem->zAlias);
}else{
Select *pSel = pItem->pSelect;
- assert( pSel!=0 );
+ assert( pSel!=0 ); /* Because of tag-20240424-1 */
if( pSel->selFlags & SF_NestedFrom ){
sqlite3_str_appendf(pAccum, "(join-%u)", pSel->selId);
+ }else if( pSel->selFlags & SF_MultiValue ){
+ assert( !pItem->fg.isTabFunc && !pItem->fg.isIndexedBy );
+ sqlite3_str_appendf(pAccum, "%u-ROW VALUES CLAUSE",
+ pItem->u1.nRow);
}else{
sqlite3_str_appendf(pAccum, "(subquery-%u)", pSel->selId);
}
@@ -31810,7 +32513,7 @@ SQLITE_API void sqlite3_str_appendf(StrAccum *p, const char *zFormat, ...){
/*****************************************************************************
-** Reference counted string storage
+** Reference counted string/blob storage
*****************************************************************************/
/*
@@ -31830,7 +32533,7 @@ SQLITE_PRIVATE char *sqlite3RCStrRef(char *z){
** Decrease the reference count by one. Free the string when the
** reference count reaches zero.
*/
-SQLITE_PRIVATE void sqlite3RCStrUnref(char *z){
+SQLITE_PRIVATE void sqlite3RCStrUnref(void *z){
RCStr *p = (RCStr*)z;
assert( p!=0 );
p--;
@@ -32075,8 +32778,10 @@ SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc)
x.printfFlags |= SQLITE_PRINTF_INTERNAL;
sqlite3_str_appendf(&x, "{%d:*} %!S", pItem->iCursor, pItem);
if( pItem->pTab ){
- sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx",
- pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab, pItem->colUsed);
+ sqlite3_str_appendf(&x, " tab=%Q nCol=%d ptr=%p used=%llx%s",
+ pItem->pTab->zName, pItem->pTab->nCol, pItem->pTab,
+ pItem->colUsed,
+ pItem->fg.rowidUsed ? "+rowid" : "");
}
if( (pItem->fg.jointype & (JT_LEFT|JT_RIGHT))==(JT_LEFT|JT_RIGHT) ){
sqlite3_str_appendf(&x, " FULL-OUTER-JOIN");
@@ -32116,12 +32821,14 @@ SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc)
sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING");
}
if( pItem->pSelect ){
+ sqlite3TreeViewPush(&pView, i+1<pSrc->nSrc);
if( pItem->pTab ){
Table *pTab = pItem->pTab;
sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1);
}
assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem->pSelect) );
sqlite3TreeViewSelect(pView, pItem->pSelect, (--n)>0);
+ sqlite3TreeViewPop(&pView);
}
if( pItem->fg.isTabFunc ){
sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:");
@@ -32225,7 +32932,7 @@ SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 m
sqlite3TreeViewItem(pView, "LIMIT", (n--)>0);
sqlite3TreeViewExpr(pView, p->pLimit->pLeft, p->pLimit->pRight!=0);
if( p->pLimit->pRight ){
- sqlite3TreeViewItem(pView, "OFFSET", (n--)>0);
+ sqlite3TreeViewItem(pView, "OFFSET", 0);
sqlite3TreeViewExpr(pView, p->pLimit->pRight, 0);
sqlite3TreeViewPop(&pView);
}
@@ -32293,6 +33000,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u
sqlite3TreeViewItem(pView, "FILTER", 1);
sqlite3TreeViewExpr(pView, pWin->pFilter, 0);
sqlite3TreeViewPop(&pView);
+ if( pWin->eFrmType==TK_FILTER ) return;
}
sqlite3TreeViewPush(&pView, more);
if( pWin->zName ){
@@ -32302,7 +33010,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u
}
if( pWin->zBase ) nElement++;
if( pWin->pOrderBy ) nElement++;
- if( pWin->eFrmType ) nElement++;
+ if( pWin->eFrmType!=0 && pWin->eFrmType!=TK_FILTER ) nElement++;
if( pWin->eExclude ) nElement++;
if( pWin->zBase ){
sqlite3TreeViewPush(&pView, (--nElement)>0);
@@ -32315,7 +33023,7 @@ SQLITE_PRIVATE void sqlite3TreeViewWindow(TreeView *pView, const Window *pWin, u
if( pWin->pOrderBy ){
sqlite3TreeViewExprList(pView, pWin->pOrderBy, (--nElement)>0, "ORDER-BY");
}
- if( pWin->eFrmType ){
+ if( pWin->eFrmType!=0 && pWin->eFrmType!=TK_FILTER ){
char zBuf[30];
const char *zFrmType = "ROWS";
if( pWin->eFrmType==TK_RANGE ) zFrmType = "RANGE";
@@ -32563,7 +33271,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
assert( ExprUseXList(pExpr) );
pFarg = pExpr->x.pList;
#ifndef SQLITE_OMIT_WINDOWFUNC
- pWin = ExprHasProperty(pExpr, EP_WinFunc) ? pExpr->y.pWin : 0;
+ pWin = IsWindowFunc(pExpr) ? pExpr->y.pWin : 0;
#else
pWin = 0;
#endif
@@ -32589,7 +33297,13 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
sqlite3TreeViewLine(pView, "FUNCTION %Q%s", pExpr->u.zToken, zFlgs);
}
if( pFarg ){
- sqlite3TreeViewExprList(pView, pFarg, pWin!=0, 0);
+ sqlite3TreeViewExprList(pView, pFarg, pWin!=0 || pExpr->pLeft, 0);
+ if( pExpr->pLeft ){
+ Expr *pOB = pExpr->pLeft;
+ assert( pOB->op==TK_ORDER );
+ assert( ExprUseXList(pOB) );
+ sqlite3TreeViewExprList(pView, pOB->x.pList, pWin!=0, "ORDERBY");
+ }
}
#ifndef SQLITE_OMIT_WINDOWFUNC
if( pWin ){
@@ -32598,6 +33312,10 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
#endif
break;
}
+ case TK_ORDER: {
+ sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, "ORDERBY");
+ break;
+ }
#ifndef SQLITE_OMIT_SUBQUERY
case TK_EXISTS: {
assert( ExprUseXSelect(pExpr) );
@@ -32651,7 +33369,7 @@ SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 m
assert( pExpr->x.pList->nExpr==2 );
pY = pExpr->x.pList->a[0].pExpr;
pZ = pExpr->x.pList->a[1].pExpr;
- sqlite3TreeViewLine(pView, "BETWEEN");
+ sqlite3TreeViewLine(pView, "BETWEEN%s", zFlgs);
sqlite3TreeViewExpr(pView, pX, 1);
sqlite3TreeViewExpr(pView, pY, 1);
sqlite3TreeViewExpr(pView, pZ, 0);
@@ -33786,7 +34504,38 @@ SQLITE_PRIVATE u32 sqlite3Utf8Read(
return c;
}
-
+/*
+** Read a single UTF8 character out of buffer z[], but reading no
+** more than n characters from the buffer. z[] is not zero-terminated.
+**
+** Return the number of bytes used to construct the character.
+**
+** Invalid UTF8 might generate a strange result. No effort is made
+** to detect invalid UTF8.
+**
+** At most 4 bytes will be read out of z[]. The return value will always
+** be between 1 and 4.
+*/
+SQLITE_PRIVATE int sqlite3Utf8ReadLimited(
+ const u8 *z,
+ int n,
+ u32 *piOut
+){
+ u32 c;
+ int i = 1;
+ assert( n>0 );
+ c = z[0];
+ if( c>=0xc0 ){
+ c = sqlite3Utf8Trans1[c-0xc0];
+ if( n>4 ) n = 4;
+ while( i<n && (z[i] & 0xc0)==0x80 ){
+ c = (c<<6) + (0x3f & z[i]);
+ i++;
+ }
+ }
+ *piOut = c;
+ return i;
+}
/*
@@ -34228,6 +34977,19 @@ SQLITE_PRIVATE int sqlite3IsNaN(double x){
}
#endif /* SQLITE_OMIT_FLOATING_POINT */
+#ifndef SQLITE_OMIT_FLOATING_POINT
+/*
+** Return true if the floating point value is NaN or +Inf or -Inf.
+*/
+SQLITE_PRIVATE int sqlite3IsOverflow(double x){
+ int rc; /* The value return */
+ u64 y;
+ memcpy(&y,&x,sizeof(y));
+ rc = IsOvfl(y);
+ return rc;
+}
+#endif /* SQLITE_OMIT_FLOATING_POINT */
+
/*
** Compute a string length that is limited to what can be stored in
** lower 30 bits of a 32-bit signed integer.
@@ -34301,7 +35063,7 @@ SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3 *db){
*/
SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){
if( rc==SQLITE_IOERR_NOMEM ) return;
-#ifdef SQLITE_USE_SEH
+#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL)
if( rc==SQLITE_IOERR_IN_PAGE ){
int ii;
int iErr;
@@ -34362,12 +35124,16 @@ SQLITE_PRIVATE void sqlite3ProgressCheck(Parse *p){
p->rc = SQLITE_INTERRUPT;
}
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- if( db->xProgress && (++p->nProgressSteps)>=db->nProgressOps ){
- if( db->xProgress(db->pProgressArg) ){
- p->nErr++;
- p->rc = SQLITE_INTERRUPT;
+ if( db->xProgress ){
+ if( p->rc==SQLITE_INTERRUPT ){
+ p->nProgressSteps = 0;
+ }else if( (++p->nProgressSteps)>=db->nProgressOps ){
+ if( db->xProgress(db->pProgressArg) ){
+ p->nErr++;
+ p->rc = SQLITE_INTERRUPT;
+ }
+ p->nProgressSteps = 0;
}
- p->nProgressSteps = 0;
}
#endif
}
@@ -34468,6 +35234,44 @@ SQLITE_PRIVATE void sqlite3DequoteExpr(Expr *p){
}
/*
+** Expression p is a QNUMBER (quoted number). Dequote the value in p->u.zToken
+** and set the type to INTEGER or FLOAT. "Quoted" integers or floats are those
+** that contain '_' characters that must be removed before further processing.
+*/
+SQLITE_PRIVATE void sqlite3DequoteNumber(Parse *pParse, Expr *p){
+ assert( p!=0 || pParse->db->mallocFailed );
+ if( p ){
+ const char *pIn = p->u.zToken;
+ char *pOut = p->u.zToken;
+ int bHex = (pIn[0]=='0' && (pIn[1]=='x' || pIn[1]=='X'));
+ int iValue;
+ assert( p->op==TK_QNUMBER );
+ p->op = TK_INTEGER;
+ do {
+ if( *pIn!=SQLITE_DIGIT_SEPARATOR ){
+ *pOut++ = *pIn;
+ if( *pIn=='e' || *pIn=='E' || *pIn=='.' ) p->op = TK_FLOAT;
+ }else{
+ if( (bHex==0 && (!sqlite3Isdigit(pIn[-1]) || !sqlite3Isdigit(pIn[1])))
+ || (bHex==1 && (!sqlite3Isxdigit(pIn[-1]) || !sqlite3Isxdigit(pIn[1])))
+ ){
+ sqlite3ErrorMsg(pParse, "unrecognized token: \"%s\"", p->u.zToken);
+ }
+ }
+ }while( *pIn++ );
+ if( bHex ) p->op = TK_INTEGER;
+
+ /* tag-20240227-a: If after dequoting, the number is an integer that
+ ** fits in 32 bits, then it must be converted into EP_IntValue. Other
+ ** parts of the code expect this. See also tag-20240227-b. */
+ if( p->op==TK_INTEGER && sqlite3GetInt32(p->u.zToken, &iValue) ){
+ p->u.iValue = iValue;
+ p->flags |= EP_IntValue;
+ }
+ }
+}
+
+/*
** If the input token p is quoted, try to adjust the token to remove
** the quotes. This is not always possible:
**
@@ -34783,6 +35587,9 @@ do_atof_calc:
u64 s2;
rr[0] = (double)s;
s2 = (u64)rr[0];
+#if defined(_MSC_VER) && _MSC_VER<1700
+ if( s2==0x8000000000000000LL ){ s2 = 2*(u64)(0.5*rr[0]); }
+#endif
rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s);
if( e>0 ){
while( e>=100 ){
@@ -35185,29 +35992,29 @@ SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRou
double rr[2];
rr[0] = r;
rr[1] = 0.0;
- if( rr[0]>1.84e+19 ){
- while( rr[0]>1.84e+119 ){
+ if( rr[0]>9.223372036854774784e+18 ){
+ while( rr[0]>9.223372036854774784e+118 ){
exp += 100;
dekkerMul2(rr, 1.0e-100, -1.99918998026028836196e-117);
}
- while( rr[0]>1.84e+29 ){
+ while( rr[0]>9.223372036854774784e+28 ){
exp += 10;
dekkerMul2(rr, 1.0e-10, -3.6432197315497741579e-27);
}
- while( rr[0]>1.84e+19 ){
+ while( rr[0]>9.223372036854774784e+18 ){
exp += 1;
dekkerMul2(rr, 1.0e-01, -5.5511151231257827021e-18);
}
}else{
- while( rr[0]<1.84e-82 ){
+ while( rr[0]<9.223372036854774784e-83 ){
exp -= 100;
dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83);
}
- while( rr[0]<1.84e+08 ){
+ while( rr[0]<9.223372036854774784e+07 ){
exp -= 10;
dekkerMul2(rr, 1.0e+10, 0.0);
}
- while( rr[0]<1.84e+18 ){
+ while( rr[0]<9.22337203685477478e+17 ){
exp -= 1;
dekkerMul2(rr, 1.0e+01, 0.0);
}
@@ -35225,7 +36032,7 @@ SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRou
assert( p->n>0 );
assert( p->n<sizeof(p->zBuf) );
p->iDP = p->n + exp;
- if( iRound<0 ){
+ if( iRound<=0 ){
iRound = p->iDP - iRound;
if( iRound==0 && p->zBuf[i+1]>='5' ){
iRound = 1;
@@ -35523,121 +36330,32 @@ SQLITE_PRIVATE u8 sqlite3GetVarint(const unsigned char *p, u64 *v){
** this function assumes the single-byte case has already been handled.
*/
SQLITE_PRIVATE u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){
- u32 a,b;
+ u64 v64;
+ u8 n;
- /* The 1-byte case. Overwhelmingly the most common. Handled inline
- ** by the getVarin32() macro */
- a = *p;
- /* a: p0 (unmasked) */
-#ifndef getVarint32
- if (!(a&0x80))
- {
- /* Values between 0 and 127 */
- *v = a;
- return 1;
- }
-#endif
+ /* Assume that the single-byte case has already been handled by
+ ** the getVarint32() macro */
+ assert( (p[0] & 0x80)!=0 );
- /* The 2-byte case */
- p++;
- b = *p;
- /* b: p1 (unmasked) */
- if (!(b&0x80))
- {
- /* Values between 128 and 16383 */
- a &= 0x7f;
- a = a<<7;
- *v = a | b;
+ if( (p[1] & 0x80)==0 ){
+ /* This is the two-byte case */
+ *v = ((p[0]&0x7f)<<7) | p[1];
return 2;
}
-
- /* The 3-byte case */
- p++;
- a = a<<14;
- a |= *p;
- /* a: p0<<14 | p2 (unmasked) */
- if (!(a&0x80))
- {
- /* Values between 16384 and 2097151 */
- a &= (0x7f<<14)|(0x7f);
- b &= 0x7f;
- b = b<<7;
- *v = a | b;
+ if( (p[2] & 0x80)==0 ){
+ /* This is the three-byte case */
+ *v = ((p[0]&0x7f)<<14) | ((p[1]&0x7f)<<7) | p[2];
return 3;
}
-
- /* A 32-bit varint is used to store size information in btrees.
- ** Objects are rarely larger than 2MiB limit of a 3-byte varint.
- ** A 3-byte varint is sufficient, for example, to record the size
- ** of a 1048569-byte BLOB or string.
- **
- ** We only unroll the first 1-, 2-, and 3- byte cases. The very
- ** rare larger cases can be handled by the slower 64-bit varint
- ** routine.
- */
-#if 1
- {
- u64 v64;
- u8 n;
-
- n = sqlite3GetVarint(p-2, &v64);
- assert( n>3 && n<=9 );
- if( (v64 & SQLITE_MAX_U32)!=v64 ){
- *v = 0xffffffff;
- }else{
- *v = (u32)v64;
- }
- return n;
- }
-
-#else
- /* For following code (kept for historical record only) shows an
- ** unrolling for the 3- and 4-byte varint cases. This code is
- ** slightly faster, but it is also larger and much harder to test.
- */
- p++;
- b = b<<14;
- b |= *p;
- /* b: p1<<14 | p3 (unmasked) */
- if (!(b&0x80))
- {
- /* Values between 2097152 and 268435455 */
- b &= (0x7f<<14)|(0x7f);
- a &= (0x7f<<14)|(0x7f);
- a = a<<7;
- *v = a | b;
- return 4;
- }
-
- p++;
- a = a<<14;
- a |= *p;
- /* a: p0<<28 | p2<<14 | p4 (unmasked) */
- if (!(a&0x80))
- {
- /* Values between 268435456 and 34359738367 */
- a &= SLOT_4_2_0;
- b &= SLOT_4_2_0;
- b = b<<7;
- *v = a | b;
- return 5;
- }
-
- /* We can only reach this point when reading a corrupt database
- ** file. In that case we are not in any hurry. Use the (relatively
- ** slow) general-purpose sqlite3GetVarint() routine to extract the
- ** value. */
- {
- u64 v64;
- u8 n;
-
- p -= 4;
- n = sqlite3GetVarint(p, &v64);
- assert( n>5 && n<=9 );
+ /* four or more bytes */
+ n = sqlite3GetVarint(p, &v64);
+ assert( n>3 && n<=9 );
+ if( (v64 & SQLITE_MAX_U32)!=v64 ){
+ *v = 0xffffffff;
+ }else{
*v = (u32)v64;
- return n;
}
-#endif
+ return n;
}
/*
@@ -36492,7 +37210,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 30 */ "SeekRowid" OpHelp("intkey=r[P3]"),
/* 31 */ "NotExists" OpHelp("intkey=r[P3]"),
/* 32 */ "Last" OpHelp(""),
- /* 33 */ "IfSmaller" OpHelp(""),
+ /* 33 */ "IfSizeBetween" OpHelp(""),
/* 34 */ "SorterSort" OpHelp(""),
/* 35 */ "Sort" OpHelp(""),
/* 36 */ "Rewind" OpHelp(""),
@@ -36537,7 +37255,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 75 */ "Null" OpHelp("r[P2..P3]=NULL"),
/* 76 */ "SoftNull" OpHelp("r[P1]=NULL"),
/* 77 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"),
- /* 78 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"),
+ /* 78 */ "Variable" OpHelp("r[P2]=parameter(P1)"),
/* 79 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"),
/* 80 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"),
/* 81 */ "SCopy" OpHelp("r[P2]=r[P1]"),
@@ -36633,19 +37351,22 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
/* 171 */ "VCreate" OpHelp(""),
/* 172 */ "VDestroy" OpHelp(""),
/* 173 */ "VOpen" OpHelp(""),
- /* 174 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"),
- /* 175 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
- /* 176 */ "VRename" OpHelp(""),
- /* 177 */ "Pagecount" OpHelp(""),
- /* 178 */ "MaxPgcnt" OpHelp(""),
- /* 179 */ "ClrSubtype" OpHelp("r[P1].subtype = 0"),
- /* 180 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"),
- /* 181 */ "Trace" OpHelp(""),
- /* 182 */ "CursorHint" OpHelp(""),
- /* 183 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"),
- /* 184 */ "Noop" OpHelp(""),
- /* 185 */ "Explain" OpHelp(""),
- /* 186 */ "Abortable" OpHelp(""),
+ /* 174 */ "VCheck" OpHelp(""),
+ /* 175 */ "VInitIn" OpHelp("r[P2]=ValueList(P1,P3)"),
+ /* 176 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
+ /* 177 */ "VRename" OpHelp(""),
+ /* 178 */ "Pagecount" OpHelp(""),
+ /* 179 */ "MaxPgcnt" OpHelp(""),
+ /* 180 */ "ClrSubtype" OpHelp("r[P1].subtype = 0"),
+ /* 181 */ "GetSubtype" OpHelp("r[P2] = r[P1].subtype"),
+ /* 182 */ "SetSubtype" OpHelp("r[P2].subtype = r[P1]"),
+ /* 183 */ "FilterAdd" OpHelp("filter(P1) += key(P3@P4)"),
+ /* 184 */ "Trace" OpHelp(""),
+ /* 185 */ "CursorHint" OpHelp(""),
+ /* 186 */ "ReleaseReg" OpHelp("release r[P1@P2] mask P3"),
+ /* 187 */ "Noop" OpHelp(""),
+ /* 188 */ "Explain" OpHelp(""),
+ /* 189 */ "Abortable" OpHelp(""),
};
return azName[i];
}
@@ -38932,8 +39653,12 @@ static int unixLogErrorAtLine(
** available, the error message will often be an empty string. Not a
** huge problem. Incorrectly concluding that the GNU version is available
** could lead to a segfault though.
+ **
+ ** Forum post 3f13857fa4062301 reports that the Android SDK may use
+ ** int-type return, depending on its version.
*/
-#if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU)
+#if (defined(STRERROR_R_CHAR_P) || defined(__USE_GNU)) \
+ && !defined(ANDROID) && !defined(__ANDROID__)
zErr =
# endif
strerror_r(iErrno, aErr, sizeof(aErr)-1);
@@ -40787,9 +41512,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
unixInodeInfo *pInode;
afpLockingContext *context = (afpLockingContext *) pFile->lockingContext;
int skipShared = 0;
-#ifdef SQLITE_TEST
- int h = pFile->h;
-#endif
assert( pFile );
OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (afp)\n", pFile->h, eFileLock,
@@ -40805,9 +41527,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
assert( pInode->nShared!=0 );
if( pFile->eFileLock>SHARED_LOCK ){
assert( pInode->eFileLock==pFile->eFileLock );
- SimulateIOErrorBenign(1);
- SimulateIOError( h=(-1) )
- SimulateIOErrorBenign(0);
#ifdef SQLITE_DEBUG
/* When reducing a lock such that other processes can start
@@ -40856,9 +41575,6 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
unsigned long long sharedLockByte = SHARED_FIRST+pInode->sharedByte;
pInode->nShared--;
if( pInode->nShared==0 ){
- SimulateIOErrorBenign(1);
- SimulateIOError( h=(-1) )
- SimulateIOErrorBenign(0);
if( !skipShared ){
rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 0);
}
@@ -41700,7 +42416,13 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
case SQLITE_FCNTL_LOCK_TIMEOUT: {
int iOld = pFile->iBusyTimeout;
+#if SQLITE_ENABLE_SETLK_TIMEOUT==1
pFile->iBusyTimeout = *(int*)pArg;
+#elif SQLITE_ENABLE_SETLK_TIMEOUT==2
+ pFile->iBusyTimeout = !!(*(int*)pArg);
+#else
+# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2"
+#endif
*(int*)pArg = iOld;
return SQLITE_OK;
}
@@ -41953,6 +42675,25 @@ static int unixGetpagesize(void){
** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and
** unixMutexHeld() is true when reading or writing any other field
** in this structure.
+**
+** aLock[SQLITE_SHM_NLOCK]:
+** This array records the various locks held by clients on each of the
+** SQLITE_SHM_NLOCK slots. If the aLock[] entry is set to 0, then no
+** locks are held by the process on this slot. If it is set to -1, then
+** some client holds an EXCLUSIVE lock on the locking slot. If the aLock[]
+** value is set to a positive value, then it is the number of shared
+** locks currently held on the slot.
+**
+** aMutex[SQLITE_SHM_NLOCK]:
+** Normally, when SQLITE_ENABLE_SETLK_TIMEOUT is not defined, mutex
+** pShmMutex is used to protect the aLock[] array and the right to
+** call fcntl() on unixShmNode.hShm to obtain or release locks.
+**
+** If SQLITE_ENABLE_SETLK_TIMEOUT is defined though, we use an array
+** of mutexes - one for each locking slot. To read or write locking
+** slot aLock[iSlot], the caller must hold the corresponding mutex
+** aMutex[iSlot]. Similarly, to call fcntl() to obtain or release a
+** lock corresponding to slot iSlot, mutex aMutex[iSlot] must be held.
*/
struct unixShmNode {
unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */
@@ -41966,10 +42707,11 @@ struct unixShmNode {
char **apRegion; /* Array of mapped shared-memory regions */
int nRef; /* Number of unixShm objects pointing to this */
unixShm *pFirst; /* All unixShm objects pointing to this */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ sqlite3_mutex *aMutex[SQLITE_SHM_NLOCK];
+#endif
int aLock[SQLITE_SHM_NLOCK]; /* # shared locks on slot, -1==excl lock */
#ifdef SQLITE_DEBUG
- u8 exclMask; /* Mask of exclusive locks held */
- u8 sharedMask; /* Mask of shared locks held */
u8 nextShmId; /* Next available unixShm.id value */
#endif
};
@@ -42052,16 +42794,35 @@ static int unixShmSystemLock(
struct flock f; /* The posix advisory locking structure */
int rc = SQLITE_OK; /* Result code form fcntl() */
- /* Access to the unixShmNode object is serialized by the caller */
pShmNode = pFile->pInode->pShmNode;
- assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) );
- assert( pShmNode->nRef>0 || unixMutexHeld() );
+
+ /* Assert that the parameters are within expected range and that the
+ ** correct mutex or mutexes are held. */
+ assert( pShmNode->nRef>=0 );
+ assert( (ofst==UNIX_SHM_DMS && n==1)
+ || (ofst>=UNIX_SHM_BASE && ofst+n<=(UNIX_SHM_BASE+SQLITE_SHM_NLOCK))
+ );
+ if( ofst==UNIX_SHM_DMS ){
+ assert( pShmNode->nRef>0 || unixMutexHeld() );
+ assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) );
+ }else{
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ int ii;
+ for(ii=ofst-UNIX_SHM_BASE; ii<ofst-UNIX_SHM_BASE+n; ii++){
+ assert( sqlite3_mutex_held(pShmNode->aMutex[ii]) );
+ }
+#else
+ assert( sqlite3_mutex_held(pShmNode->pShmMutex) );
+ assert( pShmNode->nRef>0 );
+#endif
+ }
/* Shared locks never span more than one byte */
assert( n==1 || lockType!=F_RDLCK );
/* Locks are within range */
assert( n>=1 && n<=SQLITE_SHM_NLOCK );
+ assert( ofst>=UNIX_SHM_BASE && ofst<=(UNIX_SHM_DMS+SQLITE_SHM_NLOCK) );
if( pShmNode->hShm>=0 ){
int res;
@@ -42072,7 +42833,7 @@ static int unixShmSystemLock(
f.l_len = n;
res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile);
if( res==-1 ){
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && SQLITE_ENABLE_SETLK_TIMEOUT==1
rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY);
#else
rc = SQLITE_BUSY;
@@ -42080,39 +42841,28 @@ static int unixShmSystemLock(
}
}
- /* Update the global lock state and do debug tracing */
+ /* Do debug tracing */
#ifdef SQLITE_DEBUG
- { u16 mask;
OSTRACE(("SHM-LOCK "));
- mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<<ofst);
if( rc==SQLITE_OK ){
if( lockType==F_UNLCK ){
- OSTRACE(("unlock %d ok", ofst));
- pShmNode->exclMask &= ~mask;
- pShmNode->sharedMask &= ~mask;
+ OSTRACE(("unlock %d..%d ok\n", ofst, ofst+n-1));
}else if( lockType==F_RDLCK ){
- OSTRACE(("read-lock %d ok", ofst));
- pShmNode->exclMask &= ~mask;
- pShmNode->sharedMask |= mask;
+ OSTRACE(("read-lock %d..%d ok\n", ofst, ofst+n-1));
}else{
assert( lockType==F_WRLCK );
- OSTRACE(("write-lock %d ok", ofst));
- pShmNode->exclMask |= mask;
- pShmNode->sharedMask &= ~mask;
+ OSTRACE(("write-lock %d..%d ok\n", ofst, ofst+n-1));
}
}else{
if( lockType==F_UNLCK ){
- OSTRACE(("unlock %d failed", ofst));
+ OSTRACE(("unlock %d..%d failed\n", ofst, ofst+n-1));
}else if( lockType==F_RDLCK ){
- OSTRACE(("read-lock failed"));
+ OSTRACE(("read-lock %d..%d failed\n", ofst, ofst+n-1));
}else{
assert( lockType==F_WRLCK );
- OSTRACE(("write-lock %d failed", ofst));
+ OSTRACE(("write-lock %d..%d failed\n", ofst, ofst+n-1));
}
}
- OSTRACE((" - afterwards %03x,%03x\n",
- pShmNode->sharedMask, pShmNode->exclMask));
- }
#endif
return rc;
@@ -42149,6 +42899,11 @@ static void unixShmPurge(unixFile *pFd){
int i;
assert( p->pInode==pFd->pInode );
sqlite3_mutex_free(p->pShmMutex);
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ for(i=0; i<SQLITE_SHM_NLOCK; i++){
+ sqlite3_mutex_free(p->aMutex[i]);
+ }
+#endif
for(i=0; i<p->nRegion; i+=nShmPerMap){
if( p->hShm>=0 ){
osMunmap(p->apRegion[i], p->szRegion);
@@ -42208,7 +42963,20 @@ static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){
pShmNode->isUnlocked = 1;
rc = SQLITE_READONLY_CANTINIT;
}else{
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ /* Do not use a blocking lock here. If the lock cannot be obtained
+ ** immediately, it means some other connection is truncating the
+ ** *-shm file. And after it has done so, it will not release its
+ ** lock, but only downgrade it to a shared lock. So no point in
+ ** blocking here. The call below to obtain the shared DMS lock may
+ ** use a blocking lock. */
+ int iSaveTimeout = pDbFd->iBusyTimeout;
+ pDbFd->iBusyTimeout = 0;
+#endif
rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1);
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ pDbFd->iBusyTimeout = iSaveTimeout;
+#endif
/* The first connection to attach must truncate the -shm file. We
** truncate to 3 bytes (an arbitrary small number, less than the
** -shm header size) rather than 0 as a system debugging aid, to
@@ -42329,6 +43097,18 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
rc = SQLITE_NOMEM_BKPT;
goto shm_open_err;
}
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ {
+ int ii;
+ for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){
+ pShmNode->aMutex[ii] = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+ if( pShmNode->aMutex[ii]==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ goto shm_open_err;
+ }
+ }
+ }
+#endif
}
if( pInode->bProcessLock==0 ){
@@ -42550,9 +43330,11 @@ shmpage_out:
*/
#ifdef SQLITE_DEBUG
static int assertLockingArrayOk(unixShmNode *pShmNode){
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ return 1;
+#else
unixShm *pX;
int aLock[SQLITE_SHM_NLOCK];
- assert( sqlite3_mutex_held(pShmNode->pShmMutex) );
memset(aLock, 0, sizeof(aLock));
for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
@@ -42570,13 +43352,14 @@ static int assertLockingArrayOk(unixShmNode *pShmNode){
assert( 0==memcmp(pShmNode->aLock, aLock, sizeof(aLock)) );
return (memcmp(pShmNode->aLock, aLock, sizeof(aLock))==0);
+#endif
}
#endif
/*
** Change the lock state for a shared-memory segment.
**
-** Note that the relationship between SHAREd and EXCLUSIVE locks is a little
+** Note that the relationship between SHARED and EXCLUSIVE locks is a little
** different here than in posix. In xShmLock(), one can go from unlocked
** to shared and back or from unlocked to exclusive and back. But one may
** not go from shared to exclusive or from exclusive to shared.
@@ -42591,7 +43374,7 @@ static int unixShmLock(
unixShm *p; /* The shared memory being locked */
unixShmNode *pShmNode; /* The underlying file iNode */
int rc = SQLITE_OK; /* Result code */
- u16 mask; /* Mask of locks to take or release */
+ u16 mask = (1<<(ofst+n)) - (1<<ofst); /* Mask of locks to take or release */
int *aLock;
p = pDbFd->pShm;
@@ -42626,88 +43409,151 @@ static int unixShmLock(
** It is not permitted to block on the RECOVER lock.
*/
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
- (ofst!=2) /* not RECOVER */
- && (ofst!=1 || (p->exclMask|p->sharedMask)==0)
- && (ofst!=0 || (p->exclMask|p->sharedMask)<3)
- && (ofst<3 || (p->exclMask|p->sharedMask)<(1<<ofst))
- ));
+ {
+ u16 lockMask = (p->exclMask|p->sharedMask);
+ assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
+ (ofst!=2) /* not RECOVER */
+ && (ofst!=1 || lockMask==0 || lockMask==2)
+ && (ofst!=0 || lockMask<3)
+ && (ofst<3 || lockMask<(1<<ofst))
+ ));
+ }
#endif
- mask = (1<<(ofst+n)) - (1<<ofst);
- assert( n>1 || mask==(1<<ofst) );
- sqlite3_mutex_enter(pShmNode->pShmMutex);
- assert( assertLockingArrayOk(pShmNode) );
- if( flags & SQLITE_SHM_UNLOCK ){
- if( (p->exclMask|p->sharedMask) & mask ){
- int ii;
- int bUnlock = 1;
+ /* Check if there is any work to do. There are three cases:
+ **
+ ** a) An unlock operation where there are locks to unlock,
+ ** b) An shared lock where the requested lock is not already held
+ ** c) An exclusive lock where the requested lock is not already held
+ **
+ ** The SQLite core never requests an exclusive lock that it already holds.
+ ** This is assert()ed below.
+ */
+ assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)
+ || 0==(p->exclMask & mask)
+ );
+ if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask))
+ || (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask))
+ || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK))
+ ){
- for(ii=ofst; ii<ofst+n; ii++){
- if( aLock[ii]>((p->sharedMask & (1<<ii)) ? 1 : 0) ){
- bUnlock = 0;
- }
+ /* Take the required mutexes. In SETLK_TIMEOUT mode (blocking locks), if
+ ** this is an attempt on an exclusive lock use sqlite3_mutex_try(). If any
+ ** other thread is holding this mutex, then it is either holding or about
+ ** to hold a lock exclusive to the one being requested, and we may
+ ** therefore return SQLITE_BUSY to the caller.
+ **
+ ** Doing this prevents some deadlock scenarios. For example, thread 1 may
+ ** be a checkpointer blocked waiting on the WRITER lock. And thread 2
+ ** may be a normal SQL client upgrading to a write transaction. In this
+ ** case thread 2 does a non-blocking request for the WRITER lock. But -
+ ** if it were to use sqlite3_mutex_enter() then it would effectively
+ ** become a (doomed) blocking request, as thread 2 would block until thread
+ ** 1 obtained WRITER and released the mutex. Since thread 2 already holds
+ ** a lock on a read-locking slot at this point, this breaks the
+ ** anti-deadlock rules (see above). */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ int iMutex;
+ for(iMutex=ofst; iMutex<ofst+n; iMutex++){
+ if( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) ){
+ rc = sqlite3_mutex_try(pShmNode->aMutex[iMutex]);
+ if( rc!=SQLITE_OK ) goto leave_shmnode_mutexes;
+ }else{
+ sqlite3_mutex_enter(pShmNode->aMutex[iMutex]);
}
+ }
+#else
+ sqlite3_mutex_enter(pShmNode->pShmMutex);
+#endif
- if( bUnlock ){
- rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
- if( rc==SQLITE_OK ){
- memset(&aLock[ofst], 0, sizeof(int)*n);
+ if( ALWAYS(rc==SQLITE_OK) ){
+ if( flags & SQLITE_SHM_UNLOCK ){
+ /* Case (a) - unlock. */
+ int bUnlock = 1;
+ assert( (p->exclMask & p->sharedMask)==0 );
+ assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask );
+ assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask );
+
+ /* If this is a SHARED lock being unlocked, it is possible that other
+ ** clients within this process are holding the same SHARED lock. In
+ ** this case, set bUnlock to 0 so that the posix lock is not removed
+ ** from the file-descriptor below. */
+ if( flags & SQLITE_SHM_SHARED ){
+ assert( n==1 );
+ assert( aLock[ofst]>=1 );
+ if( aLock[ofst]>1 ){
+ bUnlock = 0;
+ aLock[ofst]--;
+ p->sharedMask &= ~mask;
+ }
}
- }else if( ALWAYS(p->sharedMask & (1<<ofst)) ){
- assert( n==1 && aLock[ofst]>1 );
- aLock[ofst]--;
- }
- /* Undo the local locks */
- if( rc==SQLITE_OK ){
- p->exclMask &= ~mask;
- p->sharedMask &= ~mask;
- }
- }
- }else if( flags & SQLITE_SHM_SHARED ){
- assert( n==1 );
- assert( (p->exclMask & (1<<ofst))==0 );
- if( (p->sharedMask & mask)==0 ){
- if( aLock[ofst]<0 ){
- rc = SQLITE_BUSY;
- }else if( aLock[ofst]==0 ){
- rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n);
- }
+ if( bUnlock ){
+ rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
+ if( rc==SQLITE_OK ){
+ memset(&aLock[ofst], 0, sizeof(int)*n);
+ p->sharedMask &= ~mask;
+ p->exclMask &= ~mask;
+ }
+ }
+ }else if( flags & SQLITE_SHM_SHARED ){
+ /* Case (b) - a shared lock. */
- /* Get the local shared locks */
- if( rc==SQLITE_OK ){
- p->sharedMask |= mask;
- aLock[ofst]++;
- }
- }
- }else{
- /* Make sure no sibling connections hold locks that will block this
- ** lock. If any do, return SQLITE_BUSY right away. */
- int ii;
- for(ii=ofst; ii<ofst+n; ii++){
- assert( (p->sharedMask & mask)==0 );
- if( ALWAYS((p->exclMask & (1<<ii))==0) && aLock[ii] ){
- rc = SQLITE_BUSY;
- break;
- }
- }
+ if( aLock[ofst]<0 ){
+ /* An exclusive lock is held by some other connection. BUSY. */
+ rc = SQLITE_BUSY;
+ }else if( aLock[ofst]==0 ){
+ rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n);
+ }
- /* Get the exclusive locks at the system level. Then if successful
- ** also update the in-memory values. */
- if( rc==SQLITE_OK ){
- rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
- if( rc==SQLITE_OK ){
+ /* Get the local shared locks */
+ if( rc==SQLITE_OK ){
+ p->sharedMask |= mask;
+ aLock[ofst]++;
+ }
+ }else{
+ /* Case (c) - an exclusive lock. */
+ int ii;
+
+ assert( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) );
assert( (p->sharedMask & mask)==0 );
- p->exclMask |= mask;
+ assert( (p->exclMask & mask)==0 );
+
+ /* Make sure no sibling connections hold locks that will block this
+ ** lock. If any do, return SQLITE_BUSY right away. */
for(ii=ofst; ii<ofst+n; ii++){
- aLock[ii] = -1;
+ if( aLock[ii] ){
+ rc = SQLITE_BUSY;
+ break;
+ }
+ }
+
+ /* Get the exclusive locks at the system level. Then if successful
+ ** also update the in-memory values. */
+ if( rc==SQLITE_OK ){
+ rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
+ if( rc==SQLITE_OK ){
+ p->exclMask |= mask;
+ for(ii=ofst; ii<ofst+n; ii++){
+ aLock[ii] = -1;
+ }
+ }
}
}
+ assert( assertLockingArrayOk(pShmNode) );
}
+
+ /* Drop the mutexes acquired above. */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ leave_shmnode_mutexes:
+ for(iMutex--; iMutex>=ofst; iMutex--){
+ sqlite3_mutex_leave(pShmNode->aMutex[iMutex]);
+ }
+#else
+ sqlite3_mutex_leave(pShmNode->pShmMutex);
+#endif
}
- assert( assertLockingArrayOk(pShmNode) );
- sqlite3_mutex_leave(pShmNode->pShmMutex);
+
OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n",
p->id, osGetpid(0), p->sharedMask, p->exclMask));
return rc;
@@ -42957,11 +43803,16 @@ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
#if SQLITE_MAX_MMAP_SIZE>0
if( pFd->mmapSizeMax>0 ){
+ /* Ensure that there is always at least a 256 byte buffer of addressable
+ ** memory following the returned page. If the database is corrupt,
+ ** SQLite may overread the page slightly (in practice only a few bytes,
+ ** but 256 is safe, round, number). */
+ const int nEofBuffer = 256;
if( pFd->pMapRegion==0 ){
int rc = unixMapfile(pFd, -1);
if( rc!=SQLITE_OK ) return rc;
}
- if( pFd->mmapSize >= iOff+nAmt ){
+ if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){
*pp = &((u8 *)pFd->pMapRegion)[iOff];
pFd->nFetchOut++;
}
@@ -43905,12 +44756,19 @@ static int unixOpen(
rc = SQLITE_READONLY_DIRECTORY;
}else if( errno!=EISDIR && isReadWrite ){
/* Failed to open the file for read/write access. Try read-only. */
+ UnixUnusedFd *pReadonly = 0;
flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
openFlags &= ~(O_RDWR|O_CREAT);
flags |= SQLITE_OPEN_READONLY;
openFlags |= O_RDONLY;
isReadonly = 1;
- fd = robust_open(zName, openFlags, openMode);
+ pReadonly = findReusableFd(zName, flags);
+ if( pReadonly ){
+ fd = pReadonly->fd;
+ sqlite3_free(pReadonly);
+ }else{
+ fd = robust_open(zName, openFlags, openMode);
+ }
}
}
if( fd<0 ){
@@ -50314,6 +51172,11 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
#if SQLITE_MAX_MMAP_SIZE>0
if( pFd->mmapSizeMax>0 ){
+ /* Ensure that there is always at least a 256 byte buffer of addressable
+ ** memory following the returned page. If the database is corrupt,
+ ** SQLite may overread the page slightly (in practice only a few bytes,
+ ** but 256 is safe, round, number). */
+ const int nEofBuffer = 256;
if( pFd->pMapRegion==0 ){
int rc = winMapfile(pFd, -1);
if( rc!=SQLITE_OK ){
@@ -50322,7 +51185,7 @@ static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
return rc;
}
}
- if( pFd->mmapSize >= iOff+nAmt ){
+ if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){
assert( pFd->pMapRegion!=0 );
*pp = &((u8 *)pFd->pMapRegion)[iOff];
pFd->nFetchOut++;
@@ -52801,6 +53664,14 @@ SQLITE_API unsigned char *sqlite3_serialize(
pOut = 0;
}else{
sz = sqlite3_column_int64(pStmt, 0)*szPage;
+ if( sz==0 ){
+ sqlite3_reset(pStmt);
+ sqlite3_exec(db, "BEGIN IMMEDIATE; COMMIT;", 0, 0, 0);
+ rc = sqlite3_step(pStmt);
+ if( rc==SQLITE_ROW ){
+ sz = sqlite3_column_int64(pStmt, 0)*szPage;
+ }
+ }
if( piSize ) *piSize = sz;
if( mFlags & SQLITE_SERIALIZE_NOCOPY ){
pOut = 0;
@@ -56925,7 +57796,7 @@ struct Pager {
char *zJournal; /* Name of the journal file */
int (*xBusyHandler)(void*); /* Function to call when busy */
void *pBusyHandlerArg; /* Context argument for xBusyHandler */
- int aStat[4]; /* Total cache hits, misses, writes, spills */
+ u32 aStat[4]; /* Total cache hits, misses, writes, spills */
#ifdef SQLITE_TEST
int nRead; /* Database pages read */
#endif
@@ -57055,9 +57926,8 @@ SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){
#ifndef SQLITE_OMIT_WAL
if( pPager->pWal ){
u32 iRead = 0;
- int rc;
- rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
- return (rc==SQLITE_OK && iRead==0);
+ (void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
+ return iRead==0;
}
#endif
return 1;
@@ -57729,9 +58599,32 @@ static int writeJournalHdr(Pager *pPager){
memset(zHeader, 0, sizeof(aJournalMagic)+4);
}
+
+
/* The random check-hash initializer */
- sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
+ if( pPager->journalMode!=PAGER_JOURNALMODE_MEMORY ){
+ sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
+ }
+#ifdef SQLITE_DEBUG
+ else{
+ /* The Pager.cksumInit variable is usually randomized above to protect
+ ** against there being existing records in the journal file. This is
+ ** dangerous, as following a crash they may be mistaken for records
+ ** written by the current transaction and rolled back into the database
+ ** file, causing corruption. The following assert statements verify
+ ** that this is not required in "journal_mode=memory" mode, as in that
+ ** case the journal file is always 0 bytes in size at this point.
+ ** It is advantageous to avoid the sqlite3_randomness() call if possible
+ ** as it takes the global PRNG mutex. */
+ i64 sz = 0;
+ sqlite3OsFileSize(pPager->jfd, &sz);
+ assert( sz==0 );
+ assert( pPager->journalOff==journalHdrOffset(pPager) );
+ assert( sqlite3JournalIsInMemory(pPager->jfd) );
+ }
+#endif
put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit);
+
/* The initial database size */
put32bits(&zHeader[sizeof(aJournalMagic)+8], pPager->dbOrigSize);
/* The assumed sector size for this process */
@@ -58375,6 +59268,9 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){
return (rc==SQLITE_OK?rc2:rc);
}
+/* Forward reference */
+static int pager_playback(Pager *pPager, int isHot);
+
/*
** Execute a rollback if a transaction is active and unlock the
** database file.
@@ -58403,6 +59299,21 @@ static void pagerUnlockAndRollback(Pager *pPager){
assert( pPager->eState==PAGER_READER );
pager_end_transaction(pPager, 0, 0);
}
+ }else if( pPager->eState==PAGER_ERROR
+ && pPager->journalMode==PAGER_JOURNALMODE_MEMORY
+ && isOpen(pPager->jfd)
+ ){
+ /* Special case for a ROLLBACK due to I/O error with an in-memory
+ ** journal: We have to rollback immediately, before the journal is
+ ** closed, because once it is closed, all content is forgotten. */
+ int errCode = pPager->errCode;
+ u8 eLock = pPager->eLock;
+ pPager->eState = PAGER_OPEN;
+ pPager->errCode = SQLITE_OK;
+ pPager->eLock = EXCLUSIVE_LOCK;
+ pager_playback(pPager, 1);
+ pPager->errCode = errCode;
+ pPager->eLock = eLock;
}
pager_unlock(pPager);
}
@@ -61258,10 +62169,13 @@ act_like_temp_file:
*/
SQLITE_API sqlite3_file *sqlite3_database_file_object(const char *zName){
Pager *pPager;
+ const char *p;
while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){
zName--;
}
- pPager = *(Pager**)(zName - 4 - sizeof(Pager*));
+ p = zName - 4 - sizeof(Pager*);
+ assert( EIGHT_BYTE_ALIGNMENT(p) );
+ pPager = *(Pager**)p;
return pPager->fd;
}
@@ -61895,8 +62809,20 @@ SQLITE_PRIVATE int sqlite3PagerGet(
DbPage **ppPage, /* Write a pointer to the page here */
int flags /* PAGER_GET_XXX flags */
){
- /* printf("PAGE %u\n", pgno); fflush(stdout); */
+#if 0 /* Trace page fetch by setting to 1 */
+ int rc;
+ printf("PAGE %u\n", pgno);
+ fflush(stdout);
+ rc = pPager->xGet(pPager, pgno, ppPage, flags);
+ if( rc ){
+ printf("PAGE %u failed with 0x%02x\n", pgno, rc);
+ fflush(stdout);
+ }
+ return rc;
+#else
+ /* Normal, high-speed version of sqlite3PagerGet() */
return pPager->xGet(pPager, pgno, ppPage, flags);
+#endif
}
/*
@@ -62772,6 +63698,13 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_BEGIN_ATOMIC_WRITE, 0);
if( rc==SQLITE_OK ){
rc = pager_write_pagelist(pPager, pList);
+ if( rc==SQLITE_OK && pPager->dbSize>pPager->dbFileSize ){
+ char *pTmp = pPager->pTmpSpace;
+ int szPage = (int)pPager->pageSize;
+ memset(pTmp, 0, szPage);
+ rc = sqlite3OsWrite(pPager->fd, pTmp, szPage,
+ ((i64)pPager->dbSize*pPager->pageSize)-szPage);
+ }
if( rc==SQLITE_OK ){
rc = sqlite3OsFileControl(fd, SQLITE_FCNTL_COMMIT_ATOMIC_WRITE, 0);
}
@@ -63006,11 +63939,11 @@ SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){
a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize;
a[4] = pPager->eState;
a[5] = pPager->errCode;
- a[6] = pPager->aStat[PAGER_STAT_HIT];
- a[7] = pPager->aStat[PAGER_STAT_MISS];
+ a[6] = (int)pPager->aStat[PAGER_STAT_HIT] & 0x7fffffff;
+ a[7] = (int)pPager->aStat[PAGER_STAT_MISS] & 0x7fffffff;
a[8] = 0; /* Used to be pPager->nOvfl */
a[9] = pPager->nRead;
- a[10] = pPager->aStat[PAGER_STAT_WRITE];
+ a[10] = (int)pPager->aStat[PAGER_STAT_WRITE] & 0x7fffffff;
return a;
}
#endif
@@ -63026,7 +63959,7 @@ SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){
** reset parameter is non-zero, the cache hit or miss count is zeroed before
** returning.
*/
-SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){
+SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, u64 *pnVal){
assert( eStat==SQLITE_DBSTATUS_CACHE_HIT
|| eStat==SQLITE_DBSTATUS_CACHE_MISS
@@ -63262,7 +64195,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){
** This will be either the rollback journal or the WAL file.
*/
SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){
-#if SQLITE_OMIT_WAL
+#ifdef SQLITE_OMIT_WAL
return pPager->jfd;
#else
return pPager->pWal ? sqlite3WalFile(pPager->pWal) : pPager->jfd;
@@ -63583,7 +64516,7 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
}
assert( state==pPager->eState );
}
- }else if( eMode==PAGER_JOURNALMODE_OFF ){
+ }else if( eMode==PAGER_JOURNALMODE_OFF || eMode==PAGER_JOURNALMODE_MEMORY ){
sqlite3OsClose(pPager->jfd);
}
}
@@ -63966,7 +64899,7 @@ SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
}
#endif
-#ifdef SQLITE_USE_SEH
+#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL)
SQLITE_PRIVATE int sqlite3PagerWalSystemErrno(Pager *pPager){
return sqlite3WalSystemErrno(pPager->pWal);
}
@@ -65982,6 +66915,19 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){
}
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+
+
+/*
+** Attempt to enable blocking locks that block for nMs ms. Return 1 if
+** blocking locks are successfully enabled, or 0 otherwise.
+*/
+static int walEnableBlockingMs(Wal *pWal, int nMs){
+ int rc = sqlite3OsFileControl(
+ pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&nMs
+ );
+ return (rc==SQLITE_OK);
+}
+
/*
** Attempt to enable blocking locks. Blocking locks are enabled only if (a)
** they are supported by the VFS, and (b) the database handle is configured
@@ -65993,11 +66939,7 @@ static int walEnableBlocking(Wal *pWal){
if( pWal->db ){
int tmout = pWal->db->busyTimeout;
if( tmout ){
- int rc;
- rc = sqlite3OsFileControl(
- pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout
- );
- res = (rc==SQLITE_OK);
+ res = walEnableBlockingMs(pWal, tmout);
}
}
return res;
@@ -66046,20 +66988,10 @@ SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db){
pWal->db = db;
}
-/*
-** Take an exclusive WRITE lock. Blocking if so configured.
-*/
-static int walLockWriter(Wal *pWal){
- int rc;
- walEnableBlocking(pWal);
- rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
- walDisableBlocking(pWal);
- return rc;
-}
#else
# define walEnableBlocking(x) 0
# define walDisableBlocking(x)
-# define walLockWriter(pWal) walLockExclusive((pWal), WAL_WRITE_LOCK, 1)
+# define walEnableBlockingMs(pWal, ms) 0
# define sqlite3WalDb(pWal, db)
#endif /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */
@@ -66660,7 +67592,9 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){
}
}else{
int bWriteLock = pWal->writeLock;
- if( bWriteLock || SQLITE_OK==(rc = walLockWriter(pWal)) ){
+ if( bWriteLock
+ || SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1))
+ ){
pWal->writeLock = 1;
if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
badHdr = walIndexTryHdr(pWal, pChanged);
@@ -66668,7 +67602,8 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){
/* If the wal-index header is still malformed even while holding
** a WRITE lock, it can only mean that the header is corrupted and
** needs to be reconstructed. So run recovery to do exactly that.
- */
+ ** Disable blocking locks first. */
+ walDisableBlocking(pWal);
rc = walIndexRecover(pWal);
*pChanged = 1;
}
@@ -66879,6 +67814,37 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
}
/*
+** The final argument passed to walTryBeginRead() is of type (int*). The
+** caller should invoke walTryBeginRead as follows:
+**
+** int cnt = 0;
+** do {
+** rc = walTryBeginRead(..., &cnt);
+** }while( rc==WAL_RETRY );
+**
+** The final value of "cnt" is of no use to the caller. It is used by
+** the implementation of walTryBeginRead() as follows:
+**
+** + Each time walTryBeginRead() is called, it is incremented. Once
+** it reaches WAL_RETRY_PROTOCOL_LIMIT - indicating that walTryBeginRead()
+** has many times been invoked and failed with WAL_RETRY - walTryBeginRead()
+** returns SQLITE_PROTOCOL.
+**
+** + If SQLITE_ENABLE_SETLK_TIMEOUT is defined and walTryBeginRead() failed
+** because a blocking lock timed out (SQLITE_BUSY_TIMEOUT from the OS
+** layer), the WAL_RETRY_BLOCKED_MASK bit is set in "cnt". In this case
+** the next invocation of walTryBeginRead() may omit an expected call to
+** sqlite3OsSleep(). There has already been a delay when the previous call
+** waited on a lock.
+*/
+#define WAL_RETRY_PROTOCOL_LIMIT 100
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+# define WAL_RETRY_BLOCKED_MASK 0x10000000
+#else
+# define WAL_RETRY_BLOCKED_MASK 0
+#endif
+
+/*
** Attempt to start a read transaction. This might fail due to a race or
** other transient condition. When that happens, it returns WAL_RETRY to
** indicate to the caller that it is safe to retry immediately.
@@ -66928,13 +67894,16 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
** so it takes care to hold an exclusive lock on the corresponding
** WAL_READ_LOCK() while changing values.
*/
-static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
+static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */
u32 mxReadMark; /* Largest aReadMark[] value */
int mxI; /* Index of largest aReadMark[] value */
int i; /* Loop counter */
int rc = SQLITE_OK; /* Return code */
u32 mxFrame; /* Wal frame to lock to */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ int nBlockTmout = 0;
+#endif
assert( pWal->readLock<0 ); /* Not currently locked */
@@ -66958,14 +67927,34 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
** so that on the 100th (and last) RETRY we delay for 323 milliseconds.
** The total delay time before giving up is less than 10 seconds.
*/
- if( cnt>5 ){
+ (*pCnt)++;
+ if( *pCnt>5 ){
int nDelay = 1; /* Pause time in microseconds */
- if( cnt>100 ){
+ int cnt = (*pCnt & ~WAL_RETRY_BLOCKED_MASK);
+ if( cnt>WAL_RETRY_PROTOCOL_LIMIT ){
VVA_ONLY( pWal->lockError = 1; )
return SQLITE_PROTOCOL;
}
- if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39;
+ if( *pCnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39;
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ /* In SQLITE_ENABLE_SETLK_TIMEOUT builds, configure the file-descriptor
+ ** to block for locks for approximately nDelay us. This affects three
+ ** locks: (a) the shared lock taken on the DMS slot in os_unix.c (if
+ ** using os_unix.c), (b) the WRITER lock taken in walIndexReadHdr() if the
+ ** first attempted read fails, and (c) the shared lock taken on the
+ ** read-mark.
+ **
+ ** If the previous call failed due to an SQLITE_BUSY_TIMEOUT error,
+ ** then sleep for the minimum of 1us. The previous call already provided
+ ** an extra delay while it was blocking on the lock.
+ */
+ nBlockTmout = (nDelay+998) / 1000;
+ if( !useWal && walEnableBlockingMs(pWal, nBlockTmout) ){
+ if( *pCnt & WAL_RETRY_BLOCKED_MASK ) nDelay = 1;
+ }
+#endif
sqlite3OsSleep(pWal->pVfs, nDelay);
+ *pCnt &= ~WAL_RETRY_BLOCKED_MASK;
}
if( !useWal ){
@@ -66973,6 +67962,13 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
if( pWal->bShmUnreliable==0 ){
rc = walIndexReadHdr(pWal, pChanged);
}
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ walDisableBlocking(pWal);
+ if( rc==SQLITE_BUSY_TIMEOUT ){
+ rc = SQLITE_BUSY;
+ *pCnt |= WAL_RETRY_BLOCKED_MASK;
+ }
+#endif
if( rc==SQLITE_BUSY ){
/* If there is not a recovery running in another thread or process
** then convert BUSY errors to WAL_RETRY. If recovery is known to
@@ -67087,9 +68083,19 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
}
+ (void)walEnableBlockingMs(pWal, nBlockTmout);
rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
+ walDisableBlocking(pWal);
if( rc ){
- return rc==SQLITE_BUSY ? WAL_RETRY : rc;
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ if( rc==SQLITE_BUSY_TIMEOUT ){
+ *pCnt |= WAL_RETRY_BLOCKED_MASK;
+ }
+#else
+ assert( rc!=SQLITE_BUSY_TIMEOUT );
+#endif
+ assert( (rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT );
+ return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc;
}
/* Now that the read-lock has been obtained, check that neither the
** value in the aReadMark[] array or the contents of the wal-index
@@ -67277,7 +68283,7 @@ static int walBeginReadTransaction(Wal *pWal, int *pChanged){
#endif
do{
- rc = walTryBeginRead(pWal, pChanged, 0, ++cnt);
+ rc = walTryBeginRead(pWal, pChanged, 0, &cnt);
}while( rc==WAL_RETRY );
testcase( (rc&0xff)==SQLITE_BUSY );
testcase( (rc&0xff)==SQLITE_IOERR );
@@ -67458,6 +68464,7 @@ static int walFindFrame(
iRead = iFrame;
}
if( (nCollide--)==0 ){
+ *piRead = 0;
return SQLITE_CORRUPT_BKPT;
}
iKey = walNextHash(iKey);
@@ -67761,7 +68768,7 @@ static int walRestartLog(Wal *pWal){
cnt = 0;
do{
int notUsed;
- rc = walTryBeginRead(pWal, &notUsed, 1, ++cnt);
+ rc = walTryBeginRead(pWal, &notUsed, 1, &cnt);
}while( rc==WAL_RETRY );
assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
testcase( (rc&0xff)==SQLITE_IOERR );
@@ -68182,10 +69189,9 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint(
if( pWal->readOnly ) return SQLITE_READONLY;
WALTRACE(("WAL%p: checkpoint begins\n", pWal));
- /* Enable blocking locks, if possible. If blocking locks are successfully
- ** enabled, set xBusy2=0 so that the busy-handler is never invoked. */
+ /* Enable blocking locks, if possible. */
sqlite3WalDb(pWal, db);
- (void)walEnableBlocking(pWal);
+ if( xBusy2 ) (void)walEnableBlocking(pWal);
/* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive
** "checkpoint" lock on the database file.
@@ -68226,9 +69232,14 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint(
/* Read the wal-index header. */
SEH_TRY {
if( rc==SQLITE_OK ){
+ /* For a passive checkpoint, do not re-enable blocking locks after
+ ** reading the wal-index header. A passive checkpoint should not block
+ ** or invoke the busy handler. The only lock such a checkpoint may
+ ** attempt to obtain is a lock on a read-slot, and it should give up
+ ** immediately and do a partial checkpoint if it cannot obtain it. */
walDisableBlocking(pWal);
rc = walIndexReadHdr(pWal, &isChanged);
- (void)walEnableBlocking(pWal);
+ if( eMode2!=SQLITE_CHECKPOINT_PASSIVE ) (void)walEnableBlocking(pWal);
if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
}
@@ -68565,7 +69576,7 @@ SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){
** 22 1 Min embedded payload fraction (must be 32)
** 23 1 Min leaf payload fraction (must be 32)
** 24 4 File change counter
-** 28 4 Reserved for future use
+** 28 4 The size of the database in pages
** 32 4 First freelist page
** 36 4 Number of freelist pages in the file
** 40 60 15 4-byte meta values passed to higher layers
@@ -69196,7 +70207,7 @@ struct IntegrityCk {
BtShared *pBt; /* The tree being checked out */
Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */
u8 *aPgRef; /* 1 bit per page in the db (see above) */
- Pgno nPage; /* Number of pages in the database */
+ Pgno nCkPage; /* Pages in the database. 0 for partial check */
int mxErr; /* Stop accumulating errors when this reaches zero */
int nErr; /* Number of messages written to zErrMsg so far */
int rc; /* SQLITE_OK, SQLITE_NOMEM, or SQLITE_INTERRUPT */
@@ -69208,6 +70219,7 @@ struct IntegrityCk {
StrAccum errMsg; /* Accumulate the error message text here */
u32 *heap; /* Min-heap used for analyzing cell coverage */
sqlite3 *db; /* Database connection running the check */
+ i64 nRow; /* Number of rows visited in current tree */
};
/*
@@ -69529,7 +70541,6 @@ SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor *pCur){
/************** End of btmutex.c *********************************************/
/************** Begin file btree.c *******************************************/
-
/*
** 2004 April 6
**
@@ -69683,8 +70694,47 @@ int corruptPageError(int lineno, MemPage *p){
# define SQLITE_CORRUPT_PAGE(pMemPage) SQLITE_CORRUPT_PGNO(pMemPage->pgno)
#endif
+/* Default value for SHARED_LOCK_TRACE macro if shared-cache is disabled
+** or if the lock tracking is disabled. This is always the value for
+** release builds.
+*/
+#define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) /*no-op*/
+
#ifndef SQLITE_OMIT_SHARED_CACHE
+#if 0
+/* ^---- Change to 1 and recompile to enable shared-lock tracing
+** for debugging purposes.
+**
+** Print all shared-cache locks on a BtShared. Debugging use only.
+*/
+static void sharedLockTrace(
+ BtShared *pBt,
+ const char *zMsg,
+ int iRoot,
+ int eLockType
+){
+ BtLock *pLock;
+ if( iRoot>0 ){
+ printf("%s-%p %u%s:", zMsg, pBt, iRoot, eLockType==READ_LOCK?"R":"W");
+ }else{
+ printf("%s-%p:", zMsg, pBt);
+ }
+ for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){
+ printf(" %p/%u%s", pLock->pBtree, pLock->iTable,
+ pLock->eLock==READ_LOCK ? "R" : "W");
+ while( pLock->pNext && pLock->pBtree==pLock->pNext->pBtree ){
+ pLock = pLock->pNext;
+ printf(",%u%s", pLock->iTable, pLock->eLock==READ_LOCK ? "R" : "W");
+ }
+ }
+ printf("\n");
+ fflush(stdout);
+}
+#undef SHARED_LOCK_TRACE
+#define SHARED_LOCK_TRACE(X,MSG,TAB,TYPE) sharedLockTrace(X,MSG,TAB,TYPE)
+#endif /* Shared-lock tracing */
+
#ifdef SQLITE_DEBUG
/*
**** This function is only used as part of an assert() statement. ***
@@ -69761,6 +70811,8 @@ static int hasSharedCacheTableLock(
iTab = iRoot;
}
+ SHARED_LOCK_TRACE(pBtree->pBt,"hasLock",iRoot,eLockType);
+
/* Search for the required lock. Either a write-lock on root-page iTab, a
** write-lock on the schema table, or (if the client is reading) a
** read-lock on iTab will suffice. Return 1 if any of these are found. */
@@ -69894,6 +70946,8 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){
BtLock *pLock = 0;
BtLock *pIter;
+ SHARED_LOCK_TRACE(pBt,"setLock", iTable, eLock);
+
assert( sqlite3BtreeHoldsMutex(p) );
assert( eLock==READ_LOCK || eLock==WRITE_LOCK );
assert( p->db!=0 );
@@ -69961,6 +71015,8 @@ static void clearAllSharedCacheTableLocks(Btree *p){
assert( p->sharable || 0==*ppIter );
assert( p->inTrans>0 );
+ SHARED_LOCK_TRACE(pBt, "clearAllLocks", 0, 0);
+
while( *ppIter ){
BtLock *pLock = *ppIter;
assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree );
@@ -69999,6 +71055,9 @@ static void clearAllSharedCacheTableLocks(Btree *p){
*/
static void downgradeAllSharedCacheTableLocks(Btree *p){
BtShared *pBt = p->pBt;
+
+ SHARED_LOCK_TRACE(pBt, "downgradeLocks", 0, 0);
+
if( pBt->pWriter==p ){
BtLock *pLock;
pBt->pWriter = 0;
@@ -74612,9 +75671,12 @@ static int accessPayload(
if( pCur->aOverflow==0
|| nOvfl*(int)sizeof(Pgno) > sqlite3MallocSize(pCur->aOverflow)
){
- Pgno *aNew = (Pgno*)sqlite3Realloc(
- pCur->aOverflow, nOvfl*2*sizeof(Pgno)
- );
+ Pgno *aNew;
+ if( sqlite3FaultSim(413) ){
+ aNew = 0;
+ }else{
+ aNew = (Pgno*)sqlite3Realloc(pCur->aOverflow, nOvfl*2*sizeof(Pgno));
+ }
if( aNew==0 ){
return SQLITE_NOMEM_BKPT;
}else{
@@ -74624,6 +75686,12 @@ static int accessPayload(
memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno));
pCur->curFlags |= BTCF_ValidOvfl;
}else{
+ /* Sanity check the validity of the overflow page cache */
+ assert( pCur->aOverflow[0]==nextPage
+ || pCur->aOverflow[0]==0
+ || CORRUPT_DB );
+ assert( pCur->aOverflow[0]!=0 || pCur->aOverflow[offset/ovflSize]==0 );
+
/* If the overflow page-list cache has been allocated and the
** entry for the first required overflow page is valid, skip
** directly to it.
@@ -74693,7 +75761,6 @@ static int accessPayload(
assert( aWrite>=pBufStart ); /* due to (6) */
memcpy(aSave, aWrite, 4);
rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1));
- if( rc && nextPage>pBt->nPage ) rc = SQLITE_CORRUPT_BKPT;
nextPage = get4byte(aWrite);
memcpy(aWrite, aSave, 4);
}else
@@ -75106,6 +76173,23 @@ SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
return rc;
}
+#ifdef SQLITE_DEBUG
+/* The cursors is CURSOR_VALID and has BTCF_AtLast set. Verify that
+** this flags are true for a consistent database.
+**
+** This routine is is called from within assert() statements only.
+** It is an internal verification routine and does not appear in production
+** builds.
+*/
+static int cursorIsAtLastEntry(BtCursor *pCur){
+ int ii;
+ for(ii=0; ii<pCur->iPage; ii++){
+ if( pCur->aiIdx[ii]!=pCur->apPage[ii]->nCell ) return 0;
+ }
+ return pCur->ix==pCur->pPage->nCell-1 && pCur->pPage->leaf!=0;
+}
+#endif
+
/* Move the cursor to the last entry in the table. Return SQLITE_OK
** on success. Set *pRes to 0 if the cursor actually points to something
** or set *pRes to 1 if the table is empty.
@@ -75134,18 +76218,7 @@ SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
/* If the cursor already points to the last entry, this is a no-op. */
if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){
-#ifdef SQLITE_DEBUG
- /* This block serves to assert() that the cursor really does point
- ** to the last entry in the b-tree. */
- int ii;
- for(ii=0; ii<pCur->iPage; ii++){
- assert( pCur->aiIdx[ii]==pCur->apPage[ii]->nCell );
- }
- assert( pCur->ix==pCur->pPage->nCell-1 || CORRUPT_DB );
- testcase( pCur->ix!=pCur->pPage->nCell-1 );
- /* ^-- dbsqlfuzz b92b72e4de80b5140c30ab71372ca719b8feb618 */
- assert( pCur->pPage->leaf );
-#endif
+ assert( cursorIsAtLastEntry(pCur) || CORRUPT_DB );
*pRes = 0;
return SQLITE_OK;
}
@@ -75198,6 +76271,7 @@ SQLITE_PRIVATE int sqlite3BtreeTableMoveto(
}
if( pCur->info.nKey<intKey ){
if( (pCur->curFlags & BTCF_AtLast)!=0 ){
+ assert( cursorIsAtLastEntry(pCur) || CORRUPT_DB );
*pRes = -1;
return SQLITE_OK;
}
@@ -75664,10 +76738,10 @@ SQLITE_PRIVATE i64 sqlite3BtreeRowCountEst(BtCursor *pCur){
assert( cursorOwnsBtShared(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
- /* Currently this interface is only called by the OP_IfSmaller
- ** opcode, and it that case the cursor will always be valid and
- ** will always point to a leaf node. */
- if( NEVER(pCur->eState!=CURSOR_VALID) ) return -1;
+ /* Currently this interface is only called by the OP_IfSizeBetween
+ ** opcode and the OP_Count opcode with P3=1. In either case,
+ ** the cursor will always be valid unless the btree is empty. */
+ if( pCur->eState!=CURSOR_VALID ) return 0;
if( NEVER(pCur->pPage->leaf==0) ) return -1;
n = pCur->pPage->nCell;
@@ -75813,7 +76887,10 @@ static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur){
}
pPage = pCur->pPage;
- assert( pPage->isInit );
+ if( sqlite3FaultSim(412) ) pPage->isInit = 0;
+ if( !pPage->isInit ){
+ return SQLITE_CORRUPT_BKPT;
+ }
if( !pPage->leaf ){
int idx = pCur->ix;
rc = moveToChild(pCur, get4byte(findCell(pPage, idx)));
@@ -76486,7 +77563,10 @@ static int fillInCell(
n = nHeader + nPayload;
testcase( n==3 );
testcase( n==4 );
- if( n<4 ) n = 4;
+ if( n<4 ){
+ n = 4;
+ pPayload[nPayload] = 0;
+ }
*pnSize = n;
assert( nSrc<=nPayload );
testcase( nSrc<nPayload );
@@ -77024,9 +78104,10 @@ static int rebuildPage(
int k; /* Current slot in pCArray->apEnd[] */
u8 *pSrcEnd; /* Current pCArray->apEnd[k] value */
+ assert( nCell>0 );
assert( i<iEnd );
j = get2byte(&aData[hdr+5]);
- if( NEVER(j>(u32)usableSize) ){ j = 0; }
+ if( j>(u32)usableSize ){ j = 0; }
memcpy(&pTmp[j], &aData[j], usableSize - j);
for(k=0; ALWAYS(k<NB*2) && pCArray->ixNx[k]<=i; k++){}
@@ -77330,6 +78411,7 @@ static int editPage(
return SQLITE_OK;
editpage_fail:
/* Unable to edit this page. Rebuild it from scratch instead. */
+ if( nNew<1 ) return SQLITE_CORRUPT_BKPT;
populateCellCache(pCArray, iNew, nNew);
return rebuildPage(pCArray, iNew, nNew, pPg);
}
@@ -77790,7 +78872,7 @@ static int balance_nonroot(
** table-interior, index-leaf, or index-interior).
*/
if( pOld->aData[0]!=apOld[0]->aData[0] ){
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PAGE(pOld);
goto balance_cleanup;
}
@@ -77814,7 +78896,7 @@ static int balance_nonroot(
memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow));
if( pOld->nOverflow>0 ){
if( NEVER(limit<pOld->aiOvfl[0]) ){
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PAGE(pOld);
goto balance_cleanup;
}
limit = pOld->aiOvfl[0];
@@ -78457,7 +79539,7 @@ static int anotherValidCursor(BtCursor *pCur){
&& pOther->eState==CURSOR_VALID
&& pOther->pPage==pCur->pPage
){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pCur->pPage);
}
}
return SQLITE_OK;
@@ -78517,7 +79599,7 @@ static int balance(BtCursor *pCur){
/* The page being written is not a root page, and there is currently
** more than one reference to it. This only happens if the page is one
** of its own ancestor pages. Corruption. */
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PAGE(pPage);
}else{
MemPage * const pParent = pCur->apPage[iPage-1];
int const iIdx = pCur->aiIdx[iPage-1];
@@ -78681,7 +79763,7 @@ static SQLITE_NOINLINE int btreeOverwriteOverflowCell(
rc = btreeGetPage(pBt, ovflPgno, &pPage, 0);
if( rc ) return rc;
if( sqlite3PagerPageRefcount(pPage->pDbPage)!=1 || pPage->isInit ){
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PAGE(pPage);
}else{
if( iOffset+ovflPageSize<(u32)nTotal ){
ovflPgno = get4byte(pPage->aData);
@@ -78709,7 +79791,7 @@ static int btreeOverwriteCell(BtCursor *pCur, const BtreePayload *pX){
if( pCur->info.pPayload + pCur->info.nLocal > pPage->aDataEnd
|| pCur->info.pPayload < pPage->aData + pPage->cellOffset
){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
if( pCur->info.nLocal==nTotal ){
/* The entire cell is local */
@@ -78790,7 +79872,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
** Which can only happen if the SQLITE_NoSchemaError flag was set when
** the schema was loaded. This cannot be asserted though, as a user might
** set the flag, load the schema, and then unset the flag. */
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot);
}
}
@@ -78913,7 +79995,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
if( pPage->nFree<0 ){
if( NEVER(pCur->eState>CURSOR_INVALID) ){
/* ^^^^^--- due to the moveToRoot() call above */
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PAGE(pPage);
}else{
rc = btreeComputeFreeSpace(pPage);
}
@@ -78930,7 +80012,10 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
if( flags & BTREE_PREFORMAT ){
rc = SQLITE_OK;
szNew = p->pBt->nPreformatSize;
- if( szNew<4 ) szNew = 4;
+ if( szNew<4 ){
+ szNew = 4;
+ newCell[3] = 0;
+ }
if( ISAUTOVACUUM(p->pBt) && szNew>pPage->maxLocal ){
CellInfo info;
pPage->xParseCell(pPage, newCell, &info);
@@ -78952,7 +80037,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
CellInfo info;
assert( idx>=0 );
if( idx>=pPage->nCell ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc ){
@@ -78979,10 +80064,10 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
** necessary to add the PTRMAP_OVERFLOW1 pointer-map entry. */
assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */
if( oldCell < pPage->aData+pPage->hdrOffset+10 ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
if( oldCell+szNew > pPage->aDataEnd ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
memcpy(oldCell, newCell, szNew);
return SQLITE_OK;
@@ -78992,7 +80077,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
}else if( loc<0 && pPage->nCell>0 ){
assert( pPage->leaf );
idx = ++pCur->ix;
- pCur->curFlags &= ~BTCF_ValidNKey;
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
}else{
assert( pPage->leaf );
}
@@ -79022,7 +80107,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
*/
if( pPage->nOverflow ){
assert( rc==SQLITE_OK );
- pCur->curFlags &= ~(BTCF_ValidNKey);
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
rc = balance(pCur);
/* Must make sure nOverflow is reset to zero even if the balance()
@@ -79084,7 +80169,7 @@ SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64
nIn = pSrc->info.nLocal;
aIn = pSrc->info.pPayload;
if( aIn+nIn>pSrc->pPage->aDataEnd ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pSrc->pPage);
}
nRem = pSrc->info.nPayload;
if( nIn==nRem && nIn<pDest->pPage->maxLocal ){
@@ -79109,7 +80194,7 @@ SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64
if( nRem>nIn ){
if( aIn+nIn+4>pSrc->pPage->aDataEnd ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pSrc->pPage);
}
ovflIn = get4byte(&pSrc->info.pPayload[nIn]);
}
@@ -79205,7 +80290,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
assert( rc!=SQLITE_OK || CORRUPT_DB || pCur->eState==CURSOR_VALID );
if( rc || pCur->eState!=CURSOR_VALID ) return rc;
}else{
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PGNO(pCur->pgnoRoot);
}
}
assert( pCur->eState==CURSOR_VALID );
@@ -79214,14 +80299,14 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
iCellIdx = pCur->ix;
pPage = pCur->pPage;
if( pPage->nCell<=iCellIdx ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
pCell = findCell(pPage, iCellIdx);
if( pPage->nFree<0 && btreeComputeFreeSpace(pPage) ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
if( pCell<&pPage->aCellIdx[pPage->nCell] ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PAGE(pPage);
}
/* If the BTREE_SAVEPOSITION bit is on, then the cursor position must
@@ -79312,7 +80397,7 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
n = pCur->pPage->pgno;
}
pCell = findCell(pLeaf, pLeaf->nCell-1);
- if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_BKPT;
+ if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_PAGE(pLeaf);
nCell = pLeaf->xCellSize(pLeaf, pCell);
assert( MX_CELL_SIZE(pBt) >= nCell );
pTmp = pBt->pTmpSpace;
@@ -79428,7 +80513,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){
*/
sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot);
if( pgnoRoot>btreePagecount(pBt) ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PGNO(pgnoRoot);
}
pgnoRoot++;
@@ -79476,7 +80561,7 @@ static int btreeCreateTable(Btree *p, Pgno *piTable, int createTabFlags){
}
rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage);
if( eType==PTRMAP_ROOTPAGE || eType==PTRMAP_FREEPAGE ){
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PGNO(pgnoRoot);
}
if( rc!=SQLITE_OK ){
releasePage(pRoot);
@@ -79566,14 +80651,14 @@ static int clearDatabasePage(
assert( sqlite3_mutex_held(pBt->mutex) );
if( pgno>btreePagecount(pBt) ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PGNO(pgno);
}
rc = getAndInitPage(pBt, pgno, &pPage, 0);
if( rc ) return rc;
if( (pBt->openFlags & BTREE_SINGLE)==0
&& sqlite3PagerPageRefcount(pPage->pDbPage) != (1 + (pgno==1))
){
- rc = SQLITE_CORRUPT_BKPT;
+ rc = SQLITE_CORRUPT_PAGE(pPage);
goto cleardatabasepage_out;
}
hdr = pPage->hdrOffset;
@@ -79677,7 +80762,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
assert( p->inTrans==TRANS_WRITE );
assert( iTable>=2 );
if( iTable>btreePagecount(pBt) ){
- return SQLITE_CORRUPT_BKPT;
+ return SQLITE_CORRUPT_PGNO(iTable);
}
rc = sqlite3BtreeClearTable(p, iTable, 0);
@@ -79989,7 +81074,8 @@ static void checkAppendMsg(
** corresponds to page iPg is already set.
*/
static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){
- assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 );
+ assert( pCheck->aPgRef!=0 );
+ assert( iPg<=pCheck->nCkPage && sizeof(pCheck->aPgRef[0])==1 );
return (pCheck->aPgRef[iPg/8] & (1 << (iPg & 0x07)));
}
@@ -79997,7 +81083,8 @@ static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){
** Set the bit in the IntegrityCk.aPgRef[] array that corresponds to page iPg.
*/
static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){
- assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 );
+ assert( pCheck->aPgRef!=0 );
+ assert( iPg<=pCheck->nCkPage && sizeof(pCheck->aPgRef[0])==1 );
pCheck->aPgRef[iPg/8] |= (1 << (iPg & 0x07));
}
@@ -80011,7 +81098,7 @@ static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){
** Also check that the page number is in bounds.
*/
static int checkRef(IntegrityCk *pCheck, Pgno iPage){
- if( iPage>pCheck->nPage || iPage==0 ){
+ if( iPage>pCheck->nCkPage || iPage==0 ){
checkAppendMsg(pCheck, "invalid page number %u", iPage);
return 1;
}
@@ -80238,6 +81325,7 @@ static int checkTreePage(
if( (rc = btreeGetPage(pBt, iPage, &pPage, 0))!=0 ){
checkAppendMsg(pCheck,
"unable to get the page. error code=%d", rc);
+ if( rc==SQLITE_IOERR_NOMEM ) pCheck->rc = SQLITE_NOMEM;
goto end_of_check;
}
@@ -80268,6 +81356,9 @@ static int checkTreePage(
** number of cells on the page. */
nCell = get2byte(&data[hdr+3]);
assert( pPage->nCell==nCell );
+ if( pPage->leaf || pPage->intKey==0 ){
+ pCheck->nRow += nCell;
+ }
/* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page
** immediately follows the b-tree page header. */
@@ -80379,6 +81470,7 @@ static int checkTreePage(
btreeHeapInsert(heap, (pc<<16)|(pc+size-1));
}
}
+ assert( heap!=0 );
/* Add the freeblocks to the min-heap
**
** EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header
@@ -80478,6 +81570,7 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck(
sqlite3 *db, /* Database connection that is running the check */
Btree *p, /* The btree to be checked */
Pgno *aRoot, /* An array of root pages numbers for individual trees */
+ Mem *aCnt, /* Memory cells to write counts for each tree to */
int nRoot, /* Number of entries in aRoot[] */
int mxErr, /* Stop reporting errors after this many */
int *pnErr, /* OUT: Write number of errors seen to this variable */
@@ -80491,7 +81584,9 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck(
int bPartial = 0; /* True if not checking all btrees */
int bCkFreelist = 1; /* True to scan the freelist */
VVA_ONLY( int nRef );
+
assert( nRoot>0 );
+ assert( aCnt!=0 );
/* aRoot[0]==0 means this is a partial check */
if( aRoot[0]==0 ){
@@ -80508,15 +81603,15 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck(
sCheck.db = db;
sCheck.pBt = pBt;
sCheck.pPager = pBt->pPager;
- sCheck.nPage = btreePagecount(sCheck.pBt);
+ sCheck.nCkPage = btreePagecount(sCheck.pBt);
sCheck.mxErr = mxErr;
sqlite3StrAccumInit(&sCheck.errMsg, 0, zErr, sizeof(zErr), SQLITE_MAX_LENGTH);
sCheck.errMsg.printfFlags = SQLITE_PRINTF_INTERNAL;
- if( sCheck.nPage==0 ){
+ if( sCheck.nCkPage==0 ){
goto integrity_ck_cleanup;
}
- sCheck.aPgRef = sqlite3MallocZero((sCheck.nPage / 8)+ 1);
+ sCheck.aPgRef = sqlite3MallocZero((sCheck.nCkPage / 8)+ 1);
if( !sCheck.aPgRef ){
checkOom(&sCheck);
goto integrity_ck_cleanup;
@@ -80528,7 +81623,7 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck(
}
i = PENDING_BYTE_PAGE(pBt);
- if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i);
+ if( i<=sCheck.nCkPage ) setPageReferenced(&sCheck, i);
/* Check the integrity of the freelist
*/
@@ -80564,22 +81659,25 @@ SQLITE_PRIVATE int sqlite3BtreeIntegrityCheck(
testcase( pBt->db->flags & SQLITE_CellSizeCk );
pBt->db->flags &= ~(u64)SQLITE_CellSizeCk;
for(i=0; (int)i<nRoot && sCheck.mxErr; i++){
- i64 notUsed;
- if( aRoot[i]==0 ) continue;
+ sCheck.nRow = 0;
+ if( aRoot[i] ){
+ i64 notUsed;
#ifndef SQLITE_OMIT_AUTOVACUUM
- if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){
- checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
- }
+ if( pBt->autoVacuum && aRoot[i]>1 && !bPartial ){
+ checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
+ }
#endif
- sCheck.v0 = aRoot[i];
- checkTreePage(&sCheck, aRoot[i], &notUsed, LARGEST_INT64);
+ sCheck.v0 = aRoot[i];
+ checkTreePage(&sCheck, aRoot[i], &notUsed, LARGEST_INT64);
+ }
+ sqlite3MemSetArrayInt64(aCnt, i, sCheck.nRow);
}
pBt->db->flags = savedDbFlags;
/* Make sure every page in the file is referenced
*/
if( !bPartial ){
- for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
+ for(i=1; i<=sCheck.nCkPage && sCheck.mxErr; i++){
#ifdef SQLITE_OMIT_AUTOVACUUM
if( getPageReferenced(&sCheck, i)==0 ){
checkAppendMsg(&sCheck, "Page %u: never used", i);
@@ -82020,7 +83118,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemZeroTerminateIfAble(Mem *pMem){
pMem->flags |= MEM_Term;
return;
}
- if( pMem->xDel==(void(*)(void*))sqlite3RCStrUnref ){
+ if( pMem->xDel==sqlite3RCStrUnref ){
/* Blindly assume that all RCStr objects are zero-terminated */
pMem->flags |= MEM_Term;
return;
@@ -82627,6 +83725,13 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){
}
}
+/*
+** Set the iIdx'th entry of array aMem[] to contain integer value val.
+*/
+SQLITE_PRIVATE void sqlite3MemSetArrayInt64(sqlite3_value *aMem, int iIdx, i64 val){
+ sqlite3VdbeMemSetInt64(&aMem[iIdx], val);
+}
+
/* A no-op destructor */
SQLITE_PRIVATE void sqlite3NoopDestructor(void *p){ UNUSED_PARAMETER(p); }
@@ -83199,7 +84304,7 @@ static int valueFromFunction(
#endif
assert( pFunc );
if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0
- || (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)
+ || (pFunc->funcFlags & (SQLITE_FUNC_NEEDCOLL|SQLITE_FUNC_RUNONLY))!=0
){
return SQLITE_OK;
}
@@ -83315,14 +84420,20 @@ static int valueFromExpr(
}
/* Handle negative integers in a single step. This is needed in the
- ** case when the value is -9223372036854775808.
- */
- if( op==TK_UMINUS
- && (pExpr->pLeft->op==TK_INTEGER || pExpr->pLeft->op==TK_FLOAT) ){
- pExpr = pExpr->pLeft;
- op = pExpr->op;
- negInt = -1;
- zNeg = "-";
+ ** case when the value is -9223372036854775808. Except - do not do this
+ ** for hexadecimal literals. */
+ if( op==TK_UMINUS ){
+ Expr *pLeft = pExpr->pLeft;
+ if( (pLeft->op==TK_INTEGER || pLeft->op==TK_FLOAT) ){
+ if( ExprHasProperty(pLeft, EP_IntValue)
+ || pLeft->u.zToken[0]!='0' || (pLeft->u.zToken[1] & ~0x20)!='X'
+ ){
+ pExpr = pLeft;
+ op = pExpr->op;
+ negInt = -1;
+ zNeg = "-";
+ }
+ }
}
if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){
@@ -83331,12 +84442,26 @@ static int valueFromExpr(
if( ExprHasProperty(pExpr, EP_IntValue) ){
sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt);
}else{
- zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken);
- if( zVal==0 ) goto no_mem;
- sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC);
+ i64 iVal;
+ if( op==TK_INTEGER && 0==sqlite3DecOrHexToI64(pExpr->u.zToken, &iVal) ){
+ sqlite3VdbeMemSetInt64(pVal, iVal*negInt);
+ }else{
+ zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken);
+ if( zVal==0 ) goto no_mem;
+ sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC);
+ }
}
- if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_BLOB ){
- sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8);
+ if( affinity==SQLITE_AFF_BLOB ){
+ if( op==TK_FLOAT ){
+ assert( pVal && pVal->z && pVal->flags==(MEM_Str|MEM_Term) );
+ sqlite3AtoF(pVal->z, &pVal->u.r, pVal->n, SQLITE_UTF8);
+ pVal->flags = MEM_Real;
+ }else if( op==TK_INTEGER ){
+ /* This case is required by -9223372036854775808 and other strings
+ ** that look like integers but cannot be handled by the
+ ** sqlite3DecOrHexToI64() call above. */
+ sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8);
+ }
}else{
sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8);
}
@@ -83400,6 +84525,7 @@ static int valueFromExpr(
if( pVal ){
pVal->flags = MEM_Int;
pVal->u.i = pExpr->u.zToken[4]==0;
+ sqlite3ValueApplyAffinity(pVal, affinity, enc);
}
}
@@ -83605,17 +84731,17 @@ SQLITE_PRIVATE int sqlite3Stat4Column(
sqlite3_value **ppVal /* OUT: Extracted value */
){
u32 t = 0; /* a column type code */
- int nHdr; /* Size of the header in the record */
- int iHdr; /* Next unread header byte */
- int iField; /* Next unread data byte */
- int szField = 0; /* Size of the current data field */
+ u32 nHdr; /* Size of the header in the record */
+ u32 iHdr; /* Next unread header byte */
+ i64 iField; /* Next unread data byte */
+ u32 szField = 0; /* Size of the current data field */
int i; /* Column index */
u8 *a = (u8*)pRec; /* Typecast byte array */
Mem *pMem = *ppVal; /* Write result into this Mem object */
assert( iCol>0 );
iHdr = getVarint32(a, nHdr);
- if( nHdr>nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT;
+ if( nHdr>(u32)nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT;
iField = nHdr;
for(i=0; i<=iCol; i++){
iHdr += getVarint32(&a[iHdr], t);
@@ -83922,10 +85048,11 @@ static int growOpArray(Vdbe *v, int nOp){
** sqlite3CantopenError(lineno)
*/
static void test_addop_breakpoint(int pc, Op *pOp){
- static int n = 0;
+ static u64 n = 0;
(void)pc;
(void)pOp;
n++;
+ if( n==LARGEST_UINT64 ) abort(); /* so that n is used, preventing a warning */
}
#endif
@@ -84649,6 +85776,15 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
assert( aLabel!=0 ); /* True because of tag-20230419-1 */
pOp->p2 = aLabel[ADDR(pOp->p2)];
}
+
+ /* OPFLG_JUMP opcodes never have P2==0, though OPFLG_JUMP0 opcodes
+ ** might */
+ assert( pOp->p2>0
+ || (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP0)!=0 );
+
+ /* Jumps never go off the end of the bytecode array */
+ assert( pOp->p2<p->nOp
+ || (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)==0 );
break;
}
}
@@ -84713,6 +85849,10 @@ SQLITE_PRIVATE void sqlite3VdbeNoJumpsOutsideSubrtn(
int iDest = pOp->p2; /* Jump destination */
if( iDest==0 ) continue;
if( pOp->opcode==OP_Gosub ) continue;
+ if( pOp->p3==20230325 && pOp->opcode==OP_NotNull ){
+ /* This is a deliberately taken illegal branch. tag-20230325-2 */
+ continue;
+ }
if( iDest<0 ){
int j = ADDR(iDest);
assert( j>=0 );
@@ -85106,6 +86246,10 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){
if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4);
break;
}
+ case P4_TABLEREF: {
+ if( db->pnBytesFreed==0 ) sqlite3DeleteTable(db, (Table*)p4);
+ break;
+ }
}
}
@@ -85233,7 +86377,7 @@ static void SQLITE_NOINLINE vdbeChangeP4Full(
int n
){
if( pOp->p4type ){
- freeP4(p->db, pOp->p4type, pOp->p4.p);
+ assert( pOp->p4type > P4_FREE_IF_LE );
pOp->p4type = 0;
pOp->p4.p = 0;
}
@@ -87048,7 +88192,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
/* Check for immediate foreign key violations. */
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
- sqlite3VdbeCheckFk(p, 0);
+ (void)sqlite3VdbeCheckFk(p, 0);
}
/* If the auto-commit flag is set and this is the only active writer
@@ -87762,6 +88906,23 @@ static void serialGet(
pMem->flags = IsNaN(x) ? MEM_Null : MEM_Real;
}
}
+static int serialGet7(
+ const unsigned char *buf, /* Buffer to deserialize from */
+ Mem *pMem /* Memory cell to write value into */
+){
+ u64 x = FOUR_BYTE_UINT(buf);
+ u32 y = FOUR_BYTE_UINT(buf+4);
+ x = (x<<32) + y;
+ assert( sizeof(x)==8 && sizeof(pMem->u.r)==8 );
+ swapMixedEndianFloat(x);
+ memcpy(&pMem->u.r, &x, sizeof(x));
+ if( IsNaN(x) ){
+ pMem->flags = MEM_Null;
+ return 1;
+ }
+ pMem->flags = MEM_Real;
+ return 0;
+}
SQLITE_PRIVATE void sqlite3VdbeSerialGet(
const unsigned char *buf, /* Buffer to deserialize from */
u32 serial_type, /* Serial type to deserialize */
@@ -88172,32 +89333,44 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3BlobCompare(const Mem *pB1, const Mem
return n1 - n2;
}
+/* The following two functions are used only within testcase() to prove
+** test coverage. These functions do no exist for production builds.
+** We must use separate SQLITE_NOINLINE functions here, since otherwise
+** optimizer code movement causes gcov to become very confused.
+*/
+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_DEBUG)
+static int SQLITE_NOINLINE doubleLt(double a, double b){ return a<b; }
+static int SQLITE_NOINLINE doubleEq(double a, double b){ return a==b; }
+#endif
+
/*
** Do a comparison between a 64-bit signed integer and a 64-bit floating-point
** number. Return negative, zero, or positive if the first (i64) is less than,
** equal to, or greater than the second (double).
*/
SQLITE_PRIVATE int sqlite3IntFloatCompare(i64 i, double r){
- if( sizeof(LONGDOUBLE_TYPE)>8 ){
+ if( sqlite3IsNaN(r) ){
+ /* SQLite considers NaN to be a NULL. And all integer values are greater
+ ** than NULL */
+ return 1;
+ }
+ if( sqlite3Config.bUseLongDouble ){
LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i;
testcase( x<r );
testcase( x>r );
testcase( x==r );
- if( x<r ) return -1;
- if( x>r ) return +1; /*NO_TEST*/ /* work around bugs in gcov */
- return 0; /*NO_TEST*/ /* work around bugs in gcov */
+ return (x<r) ? -1 : (x>r);
}else{
i64 y;
- double s;
if( r<-9223372036854775808.0 ) return +1;
if( r>=9223372036854775808.0 ) return -1;
y = (i64)r;
if( i<y ) return -1;
if( i>y ) return +1;
- s = (double)i;
- if( s<r ) return -1;
- if( s>r ) return +1;
- return 0;
+ testcase( doubleLt(((double)i),r) );
+ testcase( doubleLt(r,((double)i)) );
+ testcase( doubleEq(r,((double)i)) );
+ return (((double)i)<r) ? -1 : (((double)i)>r);
}
}
@@ -88427,7 +89600,7 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
}else if( serial_type==0 ){
rc = -1;
}else if( serial_type==7 ){
- sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
+ serialGet7(&aKey1[d1], &mem1);
rc = -sqlite3IntFloatCompare(pRhs->u.i, mem1.u.r);
}else{
i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]);
@@ -88452,14 +89625,18 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
}else if( serial_type==0 ){
rc = -1;
}else{
- sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
if( serial_type==7 ){
- if( mem1.u.r<pRhs->u.r ){
+ if( serialGet7(&aKey1[d1], &mem1) ){
+ rc = -1; /* mem1 is a NaN */
+ }else if( mem1.u.r<pRhs->u.r ){
rc = -1;
}else if( mem1.u.r>pRhs->u.r ){
rc = +1;
+ }else{
+ assert( rc==0 );
}
}else{
+ sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
rc = sqlite3IntFloatCompare(mem1.u.i, pRhs->u.r);
}
}
@@ -88529,7 +89706,14 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
/* RHS is null */
else{
serial_type = aKey1[idx1];
- rc = (serial_type!=0 && serial_type!=10);
+ if( serial_type==0
+ || serial_type==10
+ || (serial_type==7 && serialGet7(&aKey1[d1], &mem1)!=0)
+ ){
+ assert( rc==0 );
+ }else{
+ rc = 1;
+ }
}
if( rc!=0 ){
@@ -89342,7 +90526,15 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt *pStmt){
int rc = SQLITE_OK;
Vdbe *p = (Vdbe*)pStmt;
#if SQLITE_THREADSAFE
- sqlite3_mutex *mutex = ((Vdbe*)pStmt)->db->mutex;
+ sqlite3_mutex *mutex;
+#endif
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pStmt==0 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+#if SQLITE_THREADSAFE
+ mutex = p->db->mutex;
#endif
sqlite3_mutex_enter(mutex);
for(i=0; i<p->nVar; i++){
@@ -89565,7 +90757,7 @@ SQLITE_API void sqlite3_value_free(sqlite3_value *pOld){
** is too big or if an OOM occurs.
**
** The invokeValueDestructor(P,X) routine invokes destructor function X()
-** on value P is not going to be used and need to be destroyed.
+** on value P if P is not going to be used and need to be destroyed.
*/
static void setResultStrOrError(
sqlite3_context *pCtx, /* Function context */
@@ -89595,7 +90787,7 @@ static void setResultStrOrError(
static int invokeValueDestructor(
const void *p, /* Value to destroy */
void (*xDel)(void*), /* The destructor */
- sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if no NULL */
+ sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if not NULL */
){
assert( xDel!=SQLITE_DYNAMIC );
if( xDel==0 ){
@@ -89605,7 +90797,14 @@ static int invokeValueDestructor(
}else{
xDel((void*)p);
}
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx!=0 ){
+ sqlite3_result_error_toobig(pCtx);
+ }
+#else
+ assert( pCtx!=0 );
sqlite3_result_error_toobig(pCtx);
+#endif
return SQLITE_TOOBIG;
}
SQLITE_API void sqlite3_result_blob(
@@ -89614,6 +90813,12 @@ SQLITE_API void sqlite3_result_blob(
int n,
void (*xDel)(void *)
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 || n<0 ){
+ invokeValueDestructor(z, xDel, pCtx);
+ return;
+ }
+#endif
assert( n>=0 );
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
setResultStrOrError(pCtx, z, n, 0, xDel);
@@ -89624,8 +90829,14 @@ SQLITE_API void sqlite3_result_blob64(
sqlite3_uint64 n,
void (*xDel)(void *)
){
- assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
assert( xDel!=SQLITE_DYNAMIC );
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ){
+ invokeValueDestructor(z, xDel, 0);
+ return;
+ }
+#endif
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
if( n>0x7fffffff ){
(void)invokeValueDestructor(z, xDel, pCtx);
}else{
@@ -89633,30 +90844,48 @@ SQLITE_API void sqlite3_result_blob64(
}
}
SQLITE_API void sqlite3_result_double(sqlite3_context *pCtx, double rVal){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetDouble(pCtx->pOut, rVal);
}
SQLITE_API void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
pCtx->isError = SQLITE_ERROR;
sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT);
}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
pCtx->isError = SQLITE_ERROR;
sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT);
}
#endif
SQLITE_API void sqlite3_result_int(sqlite3_context *pCtx, int iVal){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetInt64(pCtx->pOut, (i64)iVal);
}
SQLITE_API void sqlite3_result_int64(sqlite3_context *pCtx, i64 iVal){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetInt64(pCtx->pOut, iVal);
}
SQLITE_API void sqlite3_result_null(sqlite3_context *pCtx){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetNull(pCtx->pOut);
}
@@ -89666,14 +90895,37 @@ SQLITE_API void sqlite3_result_pointer(
const char *zPType,
void (*xDestructor)(void*)
){
- Mem *pOut = pCtx->pOut;
+ Mem *pOut;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ){
+ invokeValueDestructor(pPtr, xDestructor, 0);
+ return;
+ }
+#endif
+ pOut = pCtx->pOut;
assert( sqlite3_mutex_held(pOut->db->mutex) );
sqlite3VdbeMemRelease(pOut);
pOut->flags = MEM_Null;
sqlite3VdbeMemSetPointer(pOut, pPtr, zPType, xDestructor);
}
SQLITE_API void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){
- Mem *pOut = pCtx->pOut;
+ Mem *pOut;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
+#if defined(SQLITE_STRICT_SUBTYPE) && SQLITE_STRICT_SUBTYPE+0!=0
+ if( pCtx->pFunc!=0
+ && (pCtx->pFunc->funcFlags & SQLITE_RESULT_SUBTYPE)==0
+ ){
+ char zErr[200];
+ sqlite3_snprintf(sizeof(zErr), zErr,
+ "misuse of sqlite3_result_subtype() by %s()",
+ pCtx->pFunc->zName);
+ sqlite3_result_error(pCtx, zErr, -1);
+ return;
+ }
+#endif /* SQLITE_STRICT_SUBTYPE */
+ pOut = pCtx->pOut;
assert( sqlite3_mutex_held(pOut->db->mutex) );
pOut->eSubtype = eSubtype & 0xff;
pOut->flags |= MEM_Subtype;
@@ -89684,6 +90936,12 @@ SQLITE_API void sqlite3_result_text(
int n,
void (*xDel)(void *)
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ){
+ invokeValueDestructor(z, xDel, 0);
+ return;
+ }
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
setResultStrOrError(pCtx, z, n, SQLITE_UTF8, xDel);
}
@@ -89694,6 +90952,12 @@ SQLITE_API void sqlite3_result_text64(
void (*xDel)(void *),
unsigned char enc
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ){
+ invokeValueDestructor(z, xDel, 0);
+ return;
+ }
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
assert( xDel!=SQLITE_DYNAMIC );
if( enc!=SQLITE_UTF8 ){
@@ -89737,7 +91001,16 @@ SQLITE_API void sqlite3_result_text16le(
}
#endif /* SQLITE_OMIT_UTF16 */
SQLITE_API void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){
- Mem *pOut = pCtx->pOut;
+ Mem *pOut;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+ if( pValue==0 ){
+ sqlite3_result_null(pCtx);
+ return;
+ }
+#endif
+ pOut = pCtx->pOut;
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemCopy(pOut, pValue);
sqlite3VdbeChangeEncoding(pOut, pCtx->enc);
@@ -89749,7 +91022,12 @@ SQLITE_API void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){
sqlite3_result_zeroblob64(pCtx, n>0 ? n : 0);
}
SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context *pCtx, u64 n){
- Mem *pOut = pCtx->pOut;
+ Mem *pOut;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return SQLITE_MISUSE_BKPT;
+#endif
+ pOut = pCtx->pOut;
assert( sqlite3_mutex_held(pOut->db->mutex) );
if( n>(u64)pOut->db->aLimit[SQLITE_LIMIT_LENGTH] ){
sqlite3_result_error_toobig(pCtx);
@@ -89763,6 +91041,9 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context *pCtx, u64 n){
#endif
}
SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
pCtx->isError = errCode ? errCode : -1;
#ifdef SQLITE_DEBUG
if( pCtx->pVdbe ) pCtx->pVdbe->rcApp = errCode;
@@ -89775,6 +91056,9 @@ SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
/* Force an SQLITE_TOOBIG error. */
SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
pCtx->isError = SQLITE_TOOBIG;
sqlite3VdbeMemSetStr(pCtx->pOut, "string or blob too big", -1,
@@ -89783,6 +91067,9 @@ SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){
/* An SQLITE_NOMEM error. */
SQLITE_API void sqlite3_result_error_nomem(sqlite3_context *pCtx){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
sqlite3VdbeMemSetNull(pCtx->pOut);
pCtx->isError = SQLITE_NOMEM_BKPT;
@@ -90035,6 +91322,9 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){
** pointer to it.
*/
SQLITE_API void *sqlite3_user_data(sqlite3_context *p){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ) return 0;
+#endif
assert( p && p->pFunc );
return p->pFunc->pUserData;
}
@@ -90050,7 +91340,11 @@ SQLITE_API void *sqlite3_user_data(sqlite3_context *p){
** application defined function.
*/
SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ) return 0;
+#else
assert( p && p->pOut );
+#endif
return p->pOut->db;
}
@@ -90069,7 +91363,11 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){
** value, as a signal to the xUpdate routine that the column is unchanged.
*/
SQLITE_API int sqlite3_vtab_nochange(sqlite3_context *p){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ) return 0;
+#else
assert( p );
+#endif
return sqlite3_value_nochange(p->pOut);
}
@@ -90097,7 +91395,7 @@ static int valueFromValueList(
ValueList *pRhs;
*ppOut = 0;
- if( pVal==0 ) return SQLITE_MISUSE;
+ if( pVal==0 ) return SQLITE_MISUSE_BKPT;
if( (pVal->flags & MEM_Dyn)==0 || pVal->xDel!=sqlite3VdbeValueListFree ){
return SQLITE_ERROR;
}else{
@@ -90228,6 +91526,9 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){
SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){
AuxData *pAuxData;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return 0;
+#endif
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
#if SQLITE_ENABLE_STAT4
if( pCtx->pVdbe==0 ) return 0;
@@ -90260,8 +91561,12 @@ SQLITE_API void sqlite3_set_auxdata(
void (*xDelete)(void*)
){
AuxData *pAuxData;
- Vdbe *pVdbe = pCtx->pVdbe;
+ Vdbe *pVdbe;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCtx==0 ) return;
+#endif
+ pVdbe= pCtx->pVdbe;
assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
#ifdef SQLITE_ENABLE_STAT4
if( pVdbe==0 ) goto failed;
@@ -90698,7 +92003,7 @@ static int vdbeUnbind(Vdbe *p, unsigned int i){
}
sqlite3_mutex_enter(p->db->mutex);
if( p->eVdbeState!=VDBE_READY_STATE ){
- sqlite3Error(p->db, SQLITE_MISUSE);
+ sqlite3Error(p->db, SQLITE_MISUSE_BKPT);
sqlite3_mutex_leave(p->db->mutex);
sqlite3_log(SQLITE_MISUSE,
"bind on a busy prepared statement: [%s]", p->zSql);
@@ -90927,6 +92232,9 @@ SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){
SQLITE_API int sqlite3_bind_zeroblob64(sqlite3_stmt *pStmt, int i, sqlite3_uint64 n){
int rc;
Vdbe *p = (Vdbe *)pStmt;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(p->db->mutex);
if( n>(u64)p->db->aLimit[SQLITE_LIMIT_LENGTH] ){
rc = SQLITE_TOOBIG;
@@ -91053,6 +92361,9 @@ SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt){
SQLITE_API int sqlite3_stmt_explain(sqlite3_stmt *pStmt, int eMode){
Vdbe *v = (Vdbe*)pStmt;
int rc;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pStmt==0 ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(v->db->mutex);
if( ((int)v->explain)==eMode ){
rc = SQLITE_OK;
@@ -91219,10 +92530,16 @@ static UnpackedRecord *vdbeUnpackRecord(
** a field of the row currently being updated or deleted.
*/
SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
Mem *pMem;
int rc = SQLITE_OK;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( db==0 || ppValue==0 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+ p = db->pPreUpdate;
/* Test that this call is being made from within an SQLITE_DELETE or
** SQLITE_UPDATE pre-update callback, and that iIdx is within range. */
if( !p || p->op==SQLITE_INSERT ){
@@ -91283,7 +92600,12 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppVa
** the number of columns in the row being updated, deleted or inserted.
*/
SQLITE_API int sqlite3_preupdate_count(sqlite3 *db){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ p = db!=0 ? db->pPreUpdate : 0;
+#else
+ p = db->pPreUpdate;
+#endif
return (p ? p->keyinfo.nKeyField : 0);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
@@ -91301,7 +92623,12 @@ SQLITE_API int sqlite3_preupdate_count(sqlite3 *db){
** or SET DEFAULT action is considered a trigger.
*/
SQLITE_API int sqlite3_preupdate_depth(sqlite3 *db){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ p = db!=0 ? db->pPreUpdate : 0;
+#else
+ p = db->pPreUpdate;
+#endif
return (p ? p->v->nFrame : 0);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
@@ -91312,7 +92639,12 @@ SQLITE_API int sqlite3_preupdate_depth(sqlite3 *db){
** only.
*/
SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *db){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ p = db!=0 ? db->pPreUpdate : 0;
+#else
+ p = db->pPreUpdate;
+#endif
return (p ? p->iBlobWrite : -1);
}
#endif
@@ -91323,10 +92655,16 @@ SQLITE_API int sqlite3_preupdate_blobwrite(sqlite3 *db){
** a field of the row currently being updated or inserted.
*/
SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
- PreUpdate *p = db->pPreUpdate;
+ PreUpdate *p;
int rc = SQLITE_OK;
Mem *pMem;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( db==0 || ppValue==0 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+ p = db->pPreUpdate;
if( !p || p->op==SQLITE_DELETE ){
rc = SQLITE_MISUSE_BKPT;
goto preupdate_new_out;
@@ -91405,11 +92743,20 @@ SQLITE_API int sqlite3_stmt_scanstatus_v2(
void *pOut /* OUT: Write the answer here */
){
Vdbe *p = (Vdbe*)pStmt;
- VdbeOp *aOp = p->aOp;
- int nOp = p->nOp;
+ VdbeOp *aOp;
+ int nOp;
ScanStatus *pScan = 0;
int idx;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 || pOut==0
+ || iScanStatusOp<SQLITE_SCANSTAT_NLOOP
+ || iScanStatusOp>SQLITE_SCANSTAT_NCYCLE ){
+ return 1;
+ }
+#endif
+ aOp = p->aOp;
+ nOp = p->nOp;
if( p->pFrame ){
VdbeFrame *pFrame;
for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
@@ -91431,7 +92778,6 @@ SQLITE_API int sqlite3_stmt_scanstatus_v2(
}
if( flags & SQLITE_SCANSTAT_COMPLEX ){
idx = iScan;
- pScan = &p->aScan[idx];
}else{
/* If the COMPLEX flag is clear, then this function must ignore any
** ScanStatus structures with ScanStatus.addrLoop set to 0. */
@@ -91444,6 +92790,8 @@ SQLITE_API int sqlite3_stmt_scanstatus_v2(
}
}
if( idx>=p->nScan ) return 1;
+ assert( pScan==0 || pScan==&p->aScan[idx] );
+ pScan = &p->aScan[idx];
switch( iScanStatusOp ){
case SQLITE_SCANSTAT_NLOOP: {
@@ -91556,7 +92904,7 @@ SQLITE_API int sqlite3_stmt_scanstatus(
SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){
Vdbe *p = (Vdbe*)pStmt;
int ii;
- for(ii=0; ii<p->nOp; ii++){
+ for(ii=0; p!=0 && ii<p->nOp; ii++){
Op *pOp = &p->aOp[ii];
pOp->nExec = 0;
pOp->nCycle = 0;
@@ -91895,11 +93243,12 @@ SQLITE_API int sqlite3_found_count = 0;
** sqlite3CantopenError(lineno)
*/
static void test_trace_breakpoint(int pc, Op *pOp, Vdbe *v){
- static int n = 0;
+ static u64 n = 0;
(void)pc;
(void)pOp;
(void)v;
n++;
+ if( n==LARGEST_UINT64 ) abort(); /* So that n is used, preventing a warning */
}
#endif
@@ -92525,11 +93874,11 @@ static SQLITE_NOINLINE int vdbeColumnFromOverflow(
sqlite3RCStrRef(pBuf);
if( t&1 ){
rc = sqlite3VdbeMemSetStr(pDest, pBuf, len, encoding,
- (void(*)(void*))sqlite3RCStrUnref);
+ sqlite3RCStrUnref);
pDest->flags |= MEM_Term;
}else{
rc = sqlite3VdbeMemSetStr(pDest, pBuf, len, 0,
- (void(*)(void*))sqlite3RCStrUnref);
+ sqlite3RCStrUnref);
}
}else{
rc = sqlite3VdbeMemFromBtree(pC->uc.pCursor, iOffset, len, pDest);
@@ -92891,7 +94240,7 @@ case OP_Return: { /* in1 */
**
** See also: EndCoroutine
*/
-case OP_InitCoroutine: { /* jump */
+case OP_InitCoroutine: { /* jump0 */
assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
assert( pOp->p2>=0 && pOp->p2<p->nOp );
assert( pOp->p3>=0 && pOp->p3<p->nOp );
@@ -92914,7 +94263,9 @@ jump_to_p2:
**
** The instruction at the address in register P1 is a Yield.
** Jump to the P2 parameter of that Yield.
-** After the jump, register P1 becomes undefined.
+** After the jump, the value register P1 is left with a value
+** such that subsequent OP_Yields go back to the this same
+** OP_EndCoroutine instruction.
**
** See also: InitCoroutine
*/
@@ -92926,8 +94277,8 @@ case OP_EndCoroutine: { /* in1 */
pCaller = &aOp[pIn1->u.i];
assert( pCaller->opcode==OP_Yield );
assert( pCaller->p2>=0 && pCaller->p2<p->nOp );
+ pIn1->u.i = (int)(pOp - p->aOp) - 1;
pOp = &aOp[pCaller->p2 - 1];
- pIn1->flags = MEM_Undefined;
break;
}
@@ -92944,7 +94295,7 @@ case OP_EndCoroutine: { /* in1 */
**
** See also: InitCoroutine
*/
-case OP_Yield: { /* in1, jump */
+case OP_Yield: { /* in1, jump0 */
int pcDest;
pIn1 = &aMem[pOp->p1];
assert( VdbeMemDynamic(pIn1)==0 );
@@ -93274,19 +94625,15 @@ case OP_Blob: { /* out2 */
break;
}
-/* Opcode: Variable P1 P2 * P4 *
-** Synopsis: r[P2]=parameter(P1,P4)
+/* Opcode: Variable P1 P2 * * *
+** Synopsis: r[P2]=parameter(P1)
**
** Transfer the values of bound parameter P1 into register P2
-**
-** If the parameter is named, then its name appears in P4.
-** The P4 value is used by sqlite3_bind_parameter_name().
*/
case OP_Variable: { /* out2 */
Mem *pVar; /* Value being transferred */
assert( pOp->p1>0 && pOp->p1<=p->nVar );
- assert( pOp->p4.z==0 || pOp->p4.z==sqlite3VListNumToName(p->pVList,pOp->p1) );
pVar = &p->aVar[pOp->p1 - 1];
if( sqlite3VdbeMemTooBig(pVar) ){
goto too_big;
@@ -93796,7 +95143,7 @@ case OP_AddImm: { /* in1 */
pIn1 = &aMem[pOp->p1];
memAboutToChange(p, pIn1);
sqlite3VdbeMemIntegerify(pIn1);
- pIn1->u.i += pOp->p2;
+ *(u64*)&pIn1->u.i += (u64)pOp->p2;
break;
}
@@ -93807,7 +95154,7 @@ case OP_AddImm: { /* in1 */
** without data loss, then jump immediately to P2, or if P2==0
** raise an SQLITE_MISMATCH exception.
*/
-case OP_MustBeInt: { /* jump, in1 */
+case OP_MustBeInt: { /* jump0, in1 */
pIn1 = &aMem[pOp->p1];
if( (pIn1->flags & MEM_Int)==0 ){
applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding);
@@ -93848,7 +95195,7 @@ case OP_RealAffinity: { /* in1 */
}
#endif
-#ifndef SQLITE_OMIT_CAST
+#if !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_ANALYZE)
/* Opcode: Cast P1 P2 * * *
** Synopsis: affinity(r[P1])
**
@@ -94063,7 +95410,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
}
}
}else if( affinity==SQLITE_AFF_TEXT && ((flags1 | flags3) & MEM_Str)!=0 ){
- if( (flags1 & MEM_Str)==0 && (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
+ if( (flags1 & MEM_Str)!=0 ){
+ pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal);
+ }else if( (flags1&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
testcase( pIn1->flags & MEM_Int );
testcase( pIn1->flags & MEM_Real );
testcase( pIn1->flags & MEM_IntReal );
@@ -94072,7 +95421,9 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask);
if( NEVER(pIn1==pIn3) ) flags3 = flags1 | MEM_Str;
}
- if( (flags3 & MEM_Str)==0 && (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
+ if( (flags3 & MEM_Str)!=0 ){
+ pIn3->flags &= ~(MEM_Int|MEM_Real|MEM_IntReal);
+ }else if( (flags3&(MEM_Int|MEM_Real|MEM_IntReal))!=0 ){
testcase( pIn3->flags & MEM_Int );
testcase( pIn3->flags & MEM_Real );
testcase( pIn3->flags & MEM_IntReal );
@@ -95404,7 +96755,6 @@ case OP_MakeRecord: {
/* NULL value. No change in zPayload */
}else{
u64 v;
- u32 i;
if( serial_type==7 ){
assert( sizeof(v)==sizeof(pRec->u.r) );
memcpy(&v, &pRec->u.r, sizeof(v));
@@ -95412,12 +96762,22 @@ case OP_MakeRecord: {
}else{
v = pRec->u.i;
}
- len = i = sqlite3SmallTypeSizes[serial_type];
- assert( i>0 );
- while( 1 /*exit-by-break*/ ){
- zPayload[--i] = (u8)(v&0xFF);
- if( i==0 ) break;
- v >>= 8;
+ len = sqlite3SmallTypeSizes[serial_type];
+ assert( len>=1 && len<=8 && len!=5 && len!=7 );
+ switch( len ){
+ default: zPayload[7] = (u8)(v&0xff); v >>= 8;
+ zPayload[6] = (u8)(v&0xff); v >>= 8;
+ /* no break */ deliberate_fall_through
+ case 6: zPayload[5] = (u8)(v&0xff); v >>= 8;
+ zPayload[4] = (u8)(v&0xff); v >>= 8;
+ /* no break */ deliberate_fall_through
+ case 4: zPayload[3] = (u8)(v&0xff); v >>= 8;
+ /* no break */ deliberate_fall_through
+ case 3: zPayload[2] = (u8)(v&0xff); v >>= 8;
+ /* no break */ deliberate_fall_through
+ case 2: zPayload[1] = (u8)(v&0xff); v >>= 8;
+ /* no break */ deliberate_fall_through
+ case 1: zPayload[0] = (u8)(v&0xff);
}
zPayload += len;
}
@@ -96335,7 +97695,8 @@ case OP_SequenceTest: {
** is the only cursor opcode that works with a pseudo-table.
**
** P3 is the number of fields in the records that will be stored by
-** the pseudo-table.
+** the pseudo-table. If P2 is 0 or negative then the pseudo-cursor
+** will return NULL for every column.
*/
case OP_OpenPseudo: {
VdbeCursor *pCx;
@@ -96478,10 +97839,10 @@ case OP_ColumnsUsed: {
**
** See also: Found, NotFound, SeekGt, SeekGe, SeekLt
*/
-case OP_SeekLT: /* jump, in3, group, ncycle */
-case OP_SeekLE: /* jump, in3, group, ncycle */
-case OP_SeekGE: /* jump, in3, group, ncycle */
-case OP_SeekGT: { /* jump, in3, group, ncycle */
+case OP_SeekLT: /* jump0, in3, group, ncycle */
+case OP_SeekLE: /* jump0, in3, group, ncycle */
+case OP_SeekGE: /* jump0, in3, group, ncycle */
+case OP_SeekGT: { /* jump0, in3, group, ncycle */
int res; /* Comparison result */
int oc; /* Opcode */
VdbeCursor *pC; /* The cursor to seek */
@@ -97148,7 +98509,7 @@ case OP_Found: { /* jump, in3, ncycle */
**
** See also: Found, NotFound, NoConflict, SeekRowid
*/
-case OP_SeekRowid: { /* jump, in3, ncycle */
+case OP_SeekRowid: { /* jump0, in3, ncycle */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
@@ -97534,8 +98895,13 @@ case OP_RowCell: {
** the "primary" delete. The others are all on OPFLAG_FORDELETE
** cursors or else are marked with the AUXDELETE flag.
**
-** If the OPFLAG_NCHANGE flag of P2 (NB: P2 not P5) is set, then the row
-** change count is incremented (otherwise not).
+** If the OPFLAG_NCHANGE (0x01) flag of P2 (NB: P2 not P5) is set, then
+** the row change count is incremented (otherwise not).
+**
+** If the OPFLAG_ISNOOP (0x40) flag of P2 (not P5!) is set, then the
+** pre-update-hook for deletes is run, but the btree is otherwise unchanged.
+** This happens when the OP_Delete is to be shortly followed by an OP_Insert
+** with the same key, causing the btree entry to be overwritten.
**
** P1 must not be pseudo-table. It has to be a real table with
** multiple rows.
@@ -97902,7 +99268,7 @@ case OP_NullRow: {
** configured to use Prev, not Next.
*/
case OP_SeekEnd: /* ncycle */
-case OP_Last: { /* jump, ncycle */
+case OP_Last: { /* jump0, ncycle */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
@@ -97936,28 +99302,38 @@ case OP_Last: { /* jump, ncycle */
break;
}
-/* Opcode: IfSmaller P1 P2 P3 * *
+/* Opcode: IfSizeBetween P1 P2 P3 P4 *
+**
+** Let N be the approximate number of rows in the table or index
+** with cursor P1 and let X be 10*log2(N) if N is positive or -1
+** if N is zero.
**
-** Estimate the number of rows in the table P1. Jump to P2 if that
-** estimate is less than approximately 2**(0.1*P3).
+** Jump to P2 if X is in between P3 and P4, inclusive.
*/
-case OP_IfSmaller: { /* jump */
+case OP_IfSizeBetween: { /* jump */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
i64 sz;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ assert( pOp->p4type==P4_INT32 );
+ assert( pOp->p3>=-1 && pOp->p3<=640*2 );
+ assert( pOp->p4.i>=-1 && pOp->p4.i<=640*2 );
pC = p->apCsr[pOp->p1];
assert( pC!=0 );
pCrsr = pC->uc.pCursor;
assert( pCrsr );
rc = sqlite3BtreeFirst(pCrsr, &res);
if( rc ) goto abort_due_to_error;
- if( res==0 ){
+ if( res!=0 ){
+ sz = -1; /* -Infinity encoding */
+ }else{
sz = sqlite3BtreeRowCountEst(pCrsr);
- if( ALWAYS(sz>=0) && sqlite3LogEst((u64)sz)<pOp->p3 ) res = 1;
+ assert( sz>0 );
+ sz = sqlite3LogEst((u64)sz);
}
+ res = sz>=pOp->p3 && sz<=pOp->p4.i;
VdbeBranchTaken(res!=0,2);
if( res ) goto jump_to_p2;
break;
@@ -98010,7 +99386,7 @@ case OP_Sort: { /* jump ncycle */
** from the beginning toward the end. In other words, the cursor is
** configured to use Next, not Prev.
*/
-case OP_Rewind: { /* jump, ncycle */
+case OP_Rewind: { /* jump0, ncycle */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
@@ -98657,16 +100033,57 @@ case OP_CreateBtree: { /* out2 */
break;
}
-/* Opcode: SqlExec * * * P4 *
+/* Opcode: SqlExec P1 P2 * P4 *
**
** Run the SQL statement or statements specified in the P4 string.
+**
+** The P1 parameter is a bitmask of options:
+**
+** 0x0001 Disable Auth and Trace callbacks while the statements
+** in P4 are running.
+**
+** 0x0002 Set db->nAnalysisLimit to P2 while the statements in
+** P4 are running.
+**
*/
case OP_SqlExec: {
+ char *zErr;
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ sqlite3_xauth xAuth;
+#endif
+ u8 mTrace;
+ int savedAnalysisLimit;
+
sqlite3VdbeIncrWriteCounter(p, 0);
db->nSqlExec++;
- rc = sqlite3_exec(db, pOp->p4.z, 0, 0, 0);
+ zErr = 0;
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ xAuth = db->xAuth;
+#endif
+ mTrace = db->mTrace;
+ savedAnalysisLimit = db->nAnalysisLimit;
+ if( pOp->p1 & 0x0001 ){
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ db->xAuth = 0;
+#endif
+ db->mTrace = 0;
+ }
+ if( pOp->p1 & 0x0002 ){
+ db->nAnalysisLimit = pOp->p2;
+ }
+ rc = sqlite3_exec(db, pOp->p4.z, 0, 0, &zErr);
db->nSqlExec--;
- if( rc ) goto abort_due_to_error;
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ db->xAuth = xAuth;
+#endif
+ db->mTrace = mTrace;
+ db->nAnalysisLimit = savedAnalysisLimit;
+ if( zErr || rc ){
+ sqlite3VdbeError(p, "%s", zErr);
+ sqlite3_free(zErr);
+ if( rc==SQLITE_NOMEM ) goto no_mem;
+ goto abort_due_to_error;
+ }
break;
}
@@ -98812,11 +100229,11 @@ case OP_DropTrigger: {
/* Opcode: IntegrityCk P1 P2 P3 P4 P5
**
** Do an analysis of the currently open database. Store in
-** register P1 the text of an error message describing any problems.
-** If no problems are found, store a NULL in register P1.
+** register (P1+1) the text of an error message describing any problems.
+** If no problems are found, store a NULL in register (P1+1).
**
-** The register P3 contains one less than the maximum number of allowed errors.
-** At most reg(P3) errors will be reported.
+** The register (P1) contains one less than the maximum number of allowed
+** errors. At most reg(P1) errors will be reported.
** In other words, the analysis stops as soon as reg(P1) errors are
** seen. Reg(P1) is updated with the number of errors remaining.
**
@@ -98836,19 +100253,21 @@ case OP_IntegrityCk: {
Mem *pnErr; /* Register keeping track of errors remaining */
assert( p->bIsReader );
+ assert( pOp->p4type==P4_INTARRAY );
nRoot = pOp->p2;
aRoot = pOp->p4.ai;
assert( nRoot>0 );
+ assert( aRoot!=0 );
assert( aRoot[0]==(Pgno)nRoot );
- assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
- pnErr = &aMem[pOp->p3];
+ assert( pOp->p1>0 && (pOp->p1+1)<=(p->nMem+1 - p->nCursor) );
+ pnErr = &aMem[pOp->p1];
assert( (pnErr->flags & MEM_Int)!=0 );
assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 );
- pIn1 = &aMem[pOp->p1];
+ pIn1 = &aMem[pOp->p1+1];
assert( pOp->p5<db->nDb );
assert( DbMaskTest(p->btreeMask, pOp->p5) );
- rc = sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1], nRoot,
- (int)pnErr->u.i+1, &nErr, &z);
+ rc = sqlite3BtreeIntegrityCheck(db, db->aDb[pOp->p5].pBt, &aRoot[1],
+ &aMem[pOp->p3], nRoot, (int)pnErr->u.i+1, &nErr, &z);
sqlite3VdbeMemSetNull(pIn1);
if( nErr==0 ){
assert( z==0 );
@@ -98975,7 +100394,9 @@ case OP_RowSetTest: { /* jump, in1, in3 */
** P1 contains the address of the memory cell that contains the first memory
** cell in an array of values used as arguments to the sub-program. P2
** contains the address to jump to if the sub-program throws an IGNORE
-** exception using the RAISE() function. Register P3 contains the address
+** exception using the RAISE() function. P2 might be zero, if there is
+** no possibility that an IGNORE exception will be raised.
+** Register P3 contains the address
** of a memory cell in this (the parent) VM that is used to allocate the
** memory required by the sub-vdbe at runtime.
**
@@ -98983,7 +100404,7 @@ case OP_RowSetTest: { /* jump, in1, in3 */
**
** If P5 is non-zero, then recursive program invocation is enabled.
*/
-case OP_Program: { /* jump */
+case OP_Program: { /* jump0 */
int nMem; /* Number of memory registers for sub-program */
int nByte; /* Bytes of runtime space required for sub-program */
Mem *pRt; /* Register to allocate runtime space */
@@ -99888,6 +101309,52 @@ case OP_VOpen: { /* ncycle */
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifndef SQLITE_OMIT_VIRTUALTABLE
+/* Opcode: VCheck P1 P2 P3 P4 *
+**
+** P4 is a pointer to a Table object that is a virtual table in schema P1
+** that supports the xIntegrity() method. This opcode runs the xIntegrity()
+** method for that virtual table, using P3 as the integer argument. If
+** an error is reported back, the table name is prepended to the error
+** message and that message is stored in P2. If no errors are seen,
+** register P2 is set to NULL.
+*/
+case OP_VCheck: { /* out2 */
+ Table *pTab;
+ sqlite3_vtab *pVtab;
+ const sqlite3_module *pModule;
+ char *zErr = 0;
+
+ pOut = &aMem[pOp->p2];
+ sqlite3VdbeMemSetNull(pOut); /* Innocent until proven guilty */
+ assert( pOp->p4type==P4_TABLEREF );
+ pTab = pOp->p4.pTab;
+ assert( pTab!=0 );
+ assert( pTab->nTabRef>0 );
+ assert( IsVirtual(pTab) );
+ if( pTab->u.vtab.p==0 ) break;
+ pVtab = pTab->u.vtab.p->pVtab;
+ assert( pVtab!=0 );
+ pModule = pVtab->pModule;
+ assert( pModule!=0 );
+ assert( pModule->iVersion>=4 );
+ assert( pModule->xIntegrity!=0 );
+ sqlite3VtabLock(pTab->u.vtab.p);
+ assert( pOp->p1>=0 && pOp->p1<db->nDb );
+ rc = pModule->xIntegrity(pVtab, db->aDb[pOp->p1].zDbSName, pTab->zName,
+ pOp->p3, &zErr);
+ sqlite3VtabUnlock(pTab->u.vtab.p);
+ if( rc ){
+ sqlite3_free(zErr);
+ goto abort_due_to_error;
+ }
+ if( zErr ){
+ sqlite3VdbeMemSetStr(pOut, zErr, -1, SQLITE_UTF8, sqlite3_free);
+ }
+ break;
+}
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Opcode: VInitIn P1 P2 P3 * *
** Synopsis: r[P2]=ValueList(P1,P3)
**
@@ -100000,6 +101467,7 @@ case OP_VColumn: { /* ncycle */
const sqlite3_module *pModule;
Mem *pDest;
sqlite3_context sContext;
+ FuncDef nullFunc;
VdbeCursor *pCur = p->apCsr[pOp->p1];
assert( pCur!=0 );
@@ -100017,6 +101485,9 @@ case OP_VColumn: { /* ncycle */
memset(&sContext, 0, sizeof(sContext));
sContext.pOut = pDest;
sContext.enc = encoding;
+ nullFunc.pUserData = 0;
+ nullFunc.funcFlags = SQLITE_RESULT_SUBTYPE;
+ sContext.pFunc = &nullFunc;
assert( pOp->p5==OPFLAG_NOCHNG || pOp->p5==0 );
if( pOp->p5 & OPFLAG_NOCHNG ){
sqlite3VdbeMemSetNull(pDest);
@@ -100349,6 +101820,42 @@ case OP_ClrSubtype: { /* in1 */
break;
}
+/* Opcode: GetSubtype P1 P2 * * *
+** Synopsis: r[P2] = r[P1].subtype
+**
+** Extract the subtype value from register P1 and write that subtype
+** into register P2. If P1 has no subtype, then P1 gets a NULL.
+*/
+case OP_GetSubtype: { /* in1 out2 */
+ pIn1 = &aMem[pOp->p1];
+ pOut = &aMem[pOp->p2];
+ if( pIn1->flags & MEM_Subtype ){
+ sqlite3VdbeMemSetInt64(pOut, pIn1->eSubtype);
+ }else{
+ sqlite3VdbeMemSetNull(pOut);
+ }
+ break;
+}
+
+/* Opcode: SetSubtype P1 P2 * * *
+** Synopsis: r[P2].subtype = r[P1]
+**
+** Set the subtype value of register P2 to the integer from register P1.
+** If P1 is NULL, clear the subtype from p2.
+*/
+case OP_SetSubtype: { /* in1 out2 */
+ pIn1 = &aMem[pOp->p1];
+ pOut = &aMem[pOp->p2];
+ if( pIn1->flags & MEM_Null ){
+ pOut->flags &= ~MEM_Subtype;
+ }else{
+ assert( pIn1->flags & MEM_Int );
+ pOut->flags |= MEM_Subtype;
+ pOut->eSubtype = (u8)(pIn1->u.i & 0xff);
+ }
+ break;
+}
+
/* Opcode: FilterAdd P1 * P3 P4 *
** Synopsis: filter(P1) += key(P3@P4)
**
@@ -100446,7 +101953,7 @@ case OP_Filter: { /* jump */
** error is encountered.
*/
case OP_Trace:
-case OP_Init: { /* jump */
+case OP_Init: { /* jump0 */
int i;
#ifndef SQLITE_OMIT_TRACE
char *zTrace;
@@ -100833,8 +102340,7 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
/* Set the value of register r[1] in the SQL statement to integer iRow.
** This is done directly as a performance optimization
*/
- v->aMem[1].flags = MEM_Int;
- v->aMem[1].u.i = iRow;
+ sqlite3VdbeMemSetInt64(&v->aMem[1], iRow);
/* If the statement has been run before (and is paused at the OP_ResultRow)
** then back it up to the point where it does the OP_NotExists. This could
@@ -100917,7 +102423,7 @@ SQLITE_API int sqlite3_blob_open(
#endif
*ppBlob = 0;
#ifdef SQLITE_ENABLE_API_ARMOR
- if( !sqlite3SafetyCheckOk(db) || zTable==0 ){
+ if( !sqlite3SafetyCheckOk(db) || zTable==0 || zColumn==0 ){
return SQLITE_MISUSE_BKPT;
}
#endif
@@ -101479,7 +102985,7 @@ struct SorterFile {
struct SorterList {
SorterRecord *pList; /* Linked list of records */
u8 *aMemory; /* If non-NULL, bulk memory to hold pList */
- int szPMA; /* Size of pList as PMA in bytes */
+ i64 szPMA; /* Size of pList as PMA in bytes */
};
/*
@@ -101588,10 +103094,10 @@ typedef int (*SorterCompare)(SortSubtask*,int*,const void*,int,const void*,int);
struct SortSubtask {
SQLiteThread *pThread; /* Background thread, if any */
int bDone; /* Set if thread is finished but not joined */
+ int nPMA; /* Number of PMAs currently in file */
VdbeSorter *pSorter; /* Sorter that owns this sub-task */
UnpackedRecord *pUnpacked; /* Space to unpack a record */
SorterList list; /* List for thread to write to a PMA */
- int nPMA; /* Number of PMAs currently in file */
SorterCompare xCompare; /* Compare function to use */
SorterFile file; /* Temp file for level-0 PMAs */
SorterFile file2; /* Space for other PMAs */
@@ -103065,8 +104571,8 @@ SQLITE_PRIVATE int sqlite3VdbeSorterWrite(
int rc = SQLITE_OK; /* Return Code */
SorterRecord *pNew; /* New list element */
int bFlush; /* True to flush contents of memory to PMA */
- int nReq; /* Bytes of memory required */
- int nPMA; /* Bytes of PMA space required */
+ i64 nReq; /* Bytes of memory required */
+ i64 nPMA; /* Bytes of PMA space required */
int t; /* serial type of first record field */
assert( pCsr->eCurType==CURTYPE_SORTER );
@@ -104348,10 +105854,10 @@ static int bytecodevtabColumn(
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
case 9: /* nexec */
- sqlite3_result_int(ctx, pOp->nExec);
+ sqlite3_result_int64(ctx, pOp->nExec);
break;
case 10: /* ncycle */
- sqlite3_result_int(ctx, pOp->nCycle);
+ sqlite3_result_int64(ctx, pOp->nCycle);
break;
#else
case 9: /* nexec */
@@ -104490,7 +105996,8 @@ static sqlite3_module bytecodevtabModule = {
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
- /* xShadowName */ 0
+ /* xShadowName */ 0,
+ /* xIntegrity */ 0
};
@@ -105294,6 +106801,8 @@ static void resolveAlias(
assert( iCol>=0 && iCol<pEList->nExpr );
pOrig = pEList->a[iCol].pExpr;
assert( pOrig!=0 );
+ assert( !ExprHasProperty(pExpr, EP_Reduced|EP_TokenOnly) );
+ if( pExpr->pAggInfo ) return;
db = pParse->db;
pDup = sqlite3ExprDup(db, pOrig, 0);
if( db->mallocFailed ){
@@ -105319,21 +106828,36 @@ static void resolveAlias(
}
/*
-** Subqueries stores the original database, table and column names for their
-** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN".
-** Check to see if the zSpan given to this routine matches the zDb, zTab,
-** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will
-** match anything.
+** Subqueries store the original database, table and column names for their
+** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN",
+** and mark the expression-list item by setting ExprList.a[].fg.eEName
+** to ENAME_TAB.
+**
+** Check to see if the zSpan/eEName of the expression-list item passed to this
+** routine matches the zDb, zTab, and zCol. If any of zDb, zTab, and zCol are
+** NULL then those fields will match anything. Return true if there is a match,
+** or false otherwise.
+**
+** SF_NestedFrom subqueries also store an entry for the implicit rowid (or
+** _rowid_, or oid) column by setting ExprList.a[].fg.eEName to ENAME_ROWID,
+** and setting zSpan to "DATABASE.TABLE.<rowid-alias>". This type of pItem
+** argument matches if zCol is a rowid alias. If it is not NULL, (*pbRowid)
+** is set to 1 if there is this kind of match.
*/
SQLITE_PRIVATE int sqlite3MatchEName(
const struct ExprList_item *pItem,
const char *zCol,
const char *zTab,
- const char *zDb
+ const char *zDb,
+ int *pbRowid
){
int n;
const char *zSpan;
- if( pItem->fg.eEName!=ENAME_TAB ) return 0;
+ int eEName = pItem->fg.eEName;
+ if( eEName!=ENAME_TAB && (eEName!=ENAME_ROWID || NEVER(pbRowid==0)) ){
+ return 0;
+ }
+ assert( pbRowid==0 || *pbRowid==0 );
zSpan = pItem->zEName;
for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){}
if( zDb && (sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){
@@ -105345,9 +106869,11 @@ SQLITE_PRIVATE int sqlite3MatchEName(
return 0;
}
zSpan += n+1;
- if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){
- return 0;
+ if( zCol ){
+ if( eEName==ENAME_TAB && sqlite3StrICmp(zSpan, zCol)!=0 ) return 0;
+ if( eEName==ENAME_ROWID && sqlite3IsRowid(zCol)==0 ) return 0;
}
+ if( eEName==ENAME_ROWID ) *pbRowid = 1;
return 1;
}
@@ -105380,6 +106906,7 @@ SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr *pExpr){
assert( ExprUseYTab(pExpr) );
pExTab = pExpr->y.pTab;
assert( pExTab!=0 );
+ assert( n < pExTab->nCol );
if( (pExTab->tabFlags & TF_HasGenerated)!=0
&& (pExTab->aCol[n].colFlags & COLFLAG_GENERATED)!=0
){
@@ -105474,13 +107001,13 @@ static int lookupName(
Parse *pParse, /* The parsing context */
const char *zDb, /* Name of the database containing table, or NULL */
const char *zTab, /* Name of table containing column, or NULL */
- const char *zCol, /* Name of the column. */
+ const Expr *pRight, /* Name of the column. */
NameContext *pNC, /* The name context used to resolve the name */
Expr *pExpr /* Make this EXPR node point to the selected column */
){
int i, j; /* Loop counters */
int cnt = 0; /* Number of matching column names */
- int cntTab = 0; /* Number of matching table names */
+ int cntTab = 0; /* Number of potential "rowid" matches */
int nSubquery = 0; /* How many levels of subquery */
sqlite3 *db = pParse->db; /* The database connection */
SrcItem *pItem; /* Use for looping over pSrcList items */
@@ -105491,6 +107018,7 @@ static int lookupName(
Table *pTab = 0; /* Table holding the row */
Column *pCol; /* A column of pTab */
ExprList *pFJMatch = 0; /* Matches for FULL JOIN .. USING */
+ const char *zCol = pRight->u.zToken;
assert( pNC ); /* the name context cannot be NULL. */
assert( zCol ); /* The Z in X.Y.Z cannot be NULL */
@@ -105557,39 +107085,49 @@ static int lookupName(
assert( pEList!=0 );
assert( pEList->nExpr==pTab->nCol );
for(j=0; j<pEList->nExpr; j++){
- if( !sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb) ){
+ int bRowid = 0; /* True if possible rowid match */
+ if( !sqlite3MatchEName(&pEList->a[j], zCol, zTab, zDb, &bRowid) ){
continue;
}
- if( cnt>0 ){
- if( pItem->fg.isUsing==0
- || sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0
- ){
- /* Two or more tables have the same column name which is
- ** not joined by USING. This is an error. Signal as much
- ** by clearing pFJMatch and letting cnt go above 1. */
- sqlite3ExprListDelete(db, pFJMatch);
- pFJMatch = 0;
- }else
- if( (pItem->fg.jointype & JT_RIGHT)==0 ){
- /* An INNER or LEFT JOIN. Use the left-most table */
- continue;
- }else
- if( (pItem->fg.jointype & JT_LEFT)==0 ){
- /* A RIGHT JOIN. Use the right-most table */
- cnt = 0;
- sqlite3ExprListDelete(db, pFJMatch);
- pFJMatch = 0;
- }else{
- /* For a FULL JOIN, we must construct a coalesce() func */
- extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn);
+ if( bRowid==0 ){
+ if( cnt>0 ){
+ if( pItem->fg.isUsing==0
+ || sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0
+ ){
+ /* Two or more tables have the same column name which is
+ ** not joined by USING. This is an error. Signal as much
+ ** by clearing pFJMatch and letting cnt go above 1. */
+ sqlite3ExprListDelete(db, pFJMatch);
+ pFJMatch = 0;
+ }else
+ if( (pItem->fg.jointype & JT_RIGHT)==0 ){
+ /* An INNER or LEFT JOIN. Use the left-most table */
+ continue;
+ }else
+ if( (pItem->fg.jointype & JT_LEFT)==0 ){
+ /* A RIGHT JOIN. Use the right-most table */
+ cnt = 0;
+ sqlite3ExprListDelete(db, pFJMatch);
+ pFJMatch = 0;
+ }else{
+ /* For a FULL JOIN, we must construct a coalesce() func */
+ extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn);
+ }
}
+ cnt++;
+ hit = 1;
+ }else if( cnt>0 ){
+ /* This is a potential rowid match, but there has already been
+ ** a real match found. So this can be ignored. */
+ continue;
}
- cnt++;
- cntTab = 2;
+ cntTab++;
pMatch = pItem;
pExpr->iColumn = j;
pEList->a[j].fg.bUsed = 1;
- hit = 1;
+
+ /* rowid cannot be part of a USING clause - assert() this. */
+ assert( bRowid==0 || pEList->a[j].fg.bUsingTerm==0 );
if( pEList->a[j].fg.bUsingTerm ) break;
}
if( hit || zTab==0 ) continue;
@@ -105653,8 +107191,37 @@ static int lookupName(
}
}
if( 0==cnt && VisibleRowid(pTab) ){
+ /* pTab is a potential ROWID match. Keep track of it and match
+ ** the ROWID later if that seems appropriate. (Search for "cntTab"
+ ** to find related code.) Only allow a ROWID match if there is
+ ** a single ROWID match candidate.
+ */
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ /* In SQLITE_ALLOW_ROWID_IN_VIEW mode, allow a ROWID match
+ ** if there is a single VIEW candidate or if there is a single
+ ** non-VIEW candidate plus multiple VIEW candidates. In other
+ ** words non-VIEW candidate terms take precedence over VIEWs.
+ */
+ if( cntTab==0
+ || (cntTab==1
+ && ALWAYS(pMatch!=0)
+ && ALWAYS(pMatch->pTab!=0)
+ && (pMatch->pTab->tabFlags & TF_Ephemeral)!=0
+ && (pTab->tabFlags & TF_Ephemeral)==0)
+ ){
+ cntTab = 1;
+ pMatch = pItem;
+ }else{
+ cntTab++;
+ }
+#else
+ /* The (much more common) non-SQLITE_ALLOW_ROWID_IN_VIEW case is
+ ** simpler since we require exactly one candidate, which will
+ ** always be a non-VIEW
+ */
cntTab++;
pMatch = pItem;
+#endif
}
}
if( pMatch ){
@@ -105683,7 +107250,8 @@ static int lookupName(
if( pParse->bReturning ){
if( (pNC->ncFlags & NC_UBaseReg)!=0
&& ALWAYS(zTab==0
- || sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0)
+ || sqlite3StrICmp(zTab,pParse->pTriggerTab->zName)==0
+ || isValidSchemaTableName(zTab, pParse->pTriggerTab, 0))
){
pExpr->iTable = op!=TK_DELETE;
pTab = pParse->pTriggerTab;
@@ -105780,14 +107348,19 @@ static int lookupName(
** Perhaps the name is a reference to the ROWID
*/
if( cnt==0
- && cntTab==1
+ && cntTab>=1
&& pMatch
&& (pNC->ncFlags & (NC_IdxExpr|NC_GenCol))==0
&& sqlite3IsRowid(zCol)
- && ALWAYS(VisibleRowid(pMatch->pTab))
+ && ALWAYS(VisibleRowid(pMatch->pTab) || pMatch->fg.isNestedFrom)
){
- cnt = 1;
- pExpr->iColumn = -1;
+ cnt = cntTab;
+#if SQLITE_ALLOW_ROWID_IN_VIEW+0==2
+ if( pMatch->pTab!=0 && IsView(pMatch->pTab) ){
+ eNewExprOp = TK_NULL;
+ }
+#endif
+ if( pMatch->fg.isNestedFrom==0 ) pExpr->iColumn = -1;
pExpr->affExpr = SQLITE_AFF_INTEGER;
}
@@ -105940,12 +107513,17 @@ static int lookupName(
sqlite3ErrorMsg(pParse, "%s: %s.%s.%s", zErr, zDb, zTab, zCol);
}else if( zTab ){
sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol);
+ }else if( cnt==0 && ExprHasProperty(pRight,EP_DblQuoted) ){
+ sqlite3ErrorMsg(pParse, "%s: \"%s\" - should this be a"
+ " string literal in single-quotes?",
+ zErr, zCol);
}else{
sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol);
}
sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr);
pParse->checkSchema = 1;
pTopNC->nNcErr++;
+ eNewExprOp = TK_NULL;
}
assert( pFJMatch==0 );
@@ -105972,8 +107550,12 @@ static int lookupName(
** If a generated column is referenced, set bits for every column
** of the table.
*/
- if( pExpr->iColumn>=0 && pMatch!=0 ){
- pMatch->colUsed |= sqlite3ExprColUsed(pExpr);
+ if( pMatch ){
+ if( pExpr->iColumn>=0 ){
+ pMatch->colUsed |= sqlite3ExprColUsed(pExpr);
+ }else{
+ pMatch->fg.rowidUsed = 1;
+ }
}
pExpr->op = eNewExprOp;
@@ -106150,6 +107732,19 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
** resolved. This prevents "column" from being counted as having been
** referenced, which might prevent a SELECT from being erroneously
** marked as correlated.
+ **
+ ** 2024-03-28: Beware of aggregates. A bare column of aggregated table
+ ** can still evaluate to NULL even though it is marked as NOT NULL.
+ ** Example:
+ **
+ ** CREATE TABLE t1(a INT NOT NULL);
+ ** SELECT a, a IS NULL, a IS NOT NULL, count(*) FROM t1;
+ **
+ ** The "a IS NULL" and "a IS NOT NULL" expressions cannot be optimized
+ ** here because at the time this case is hit, we do not yet know whether
+ ** or not t1 is being aggregated. We have to assume the worst and omit
+ ** the optimization. The only time it is safe to apply this optimization
+ ** is within the WHERE clause.
*/
case TK_NOTNULL:
case TK_ISNULL: {
@@ -106160,19 +107755,36 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
anRef[i] = p->nRef;
}
sqlite3WalkExpr(pWalker, pExpr->pLeft);
- if( 0==sqlite3ExprCanBeNull(pExpr->pLeft) && !IN_RENAME_OBJECT ){
- testcase( ExprHasProperty(pExpr, EP_OuterON) );
- assert( !ExprHasProperty(pExpr, EP_IntValue) );
- pExpr->u.iValue = (pExpr->op==TK_NOTNULL);
- pExpr->flags |= EP_IntValue;
- pExpr->op = TK_INTEGER;
+ if( IN_RENAME_OBJECT ) return WRC_Prune;
+ if( sqlite3ExprCanBeNull(pExpr->pLeft) ){
+ /* The expression can be NULL. So the optimization does not apply */
+ return WRC_Prune;
+ }
- for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
- p->nRef = anRef[i];
+ for(i=0, p=pNC; p; p=p->pNext, i++){
+ if( (p->ncFlags & NC_Where)==0 ){
+ return WRC_Prune; /* Not in a WHERE clause. Unsafe to optimize. */
}
- sqlite3ExprDelete(pParse->db, pExpr->pLeft);
- pExpr->pLeft = 0;
}
+ testcase( ExprHasProperty(pExpr, EP_OuterON) );
+ assert( !ExprHasProperty(pExpr, EP_IntValue) );
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x80000 ){
+ sqlite3DebugPrintf(
+ "NOT NULL strength reduction converts the following to %d:\n",
+ pExpr->op==TK_NOTNULL
+ );
+ sqlite3ShowExpr(pExpr);
+ }
+#endif /* TREETRACE_ENABLED */
+ pExpr->u.iValue = (pExpr->op==TK_NOTNULL);
+ pExpr->flags |= EP_IntValue;
+ pExpr->op = TK_INTEGER;
+ for(i=0, p=pNC; p && i<ArraySize(anRef); p=p->pNext, i++){
+ p->nRef = anRef[i];
+ }
+ sqlite3ExprDelete(pParse->db, pExpr->pLeft);
+ pExpr->pLeft = 0;
return WRC_Prune;
}
@@ -106186,7 +107798,6 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
*/
case TK_ID:
case TK_DOT: {
- const char *zColumn;
const char *zTable;
const char *zDb;
Expr *pRight;
@@ -106195,7 +107806,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
zDb = 0;
zTable = 0;
assert( !ExprHasProperty(pExpr, EP_IntValue) );
- zColumn = pExpr->u.zToken;
+ pRight = pExpr;
}else{
Expr *pLeft = pExpr->pLeft;
testcase( pNC->ncFlags & NC_IdxExpr );
@@ -106214,14 +107825,13 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
}
assert( ExprUseUToken(pLeft) && ExprUseUToken(pRight) );
zTable = pLeft->u.zToken;
- zColumn = pRight->u.zToken;
assert( ExprUseYTab(pExpr) );
if( IN_RENAME_OBJECT ){
sqlite3RenameTokenRemap(pParse, (void*)pExpr, (void*)pRight);
sqlite3RenameTokenRemap(pParse, (void*)&pExpr->y.pTab, (void*)pLeft);
}
}
- return lookupName(pParse, zDb, zTable, zColumn, pNC, pExpr);
+ return lookupName(pParse, zDb, zTable, pRight, pNC, pExpr);
}
/* Resolve function names
@@ -106240,6 +107850,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
Window *pWin = (IsWindowFunc(pExpr) ? pExpr->y.pWin : 0);
#endif
assert( !ExprHasProperty(pExpr, EP_xIsSelect|EP_IntValue) );
+ assert( pExpr->pLeft==0 || pExpr->pLeft->op==TK_ORDER );
zId = pExpr->u.zToken;
pDef = sqlite3FindFunction(pParse->db, zId, n, enc, 0);
if( pDef==0 ){
@@ -106381,6 +107992,10 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
pNC->nNcErr++;
}
#endif
+ else if( is_agg==0 && pExpr->pLeft ){
+ sqlite3ExprOrderByAggregateError(pParse, pExpr);
+ pNC->nNcErr++;
+ }
if( is_agg ){
/* Window functions may not be arguments of aggregate functions.
** Or arguments of other window functions. But aggregate functions
@@ -106392,13 +108007,16 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
#endif
}
}
-#ifndef SQLITE_OMIT_WINDOWFUNC
- else if( ExprHasProperty(pExpr, EP_WinFunc) ){
+ else if( ExprHasProperty(pExpr, EP_WinFunc) || pExpr->pLeft ){
is_agg = 1;
}
-#endif
sqlite3WalkExprList(pWalker, pList);
if( is_agg ){
+ if( pExpr->pLeft ){
+ assert( pExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pExpr->pLeft) );
+ sqlite3WalkExprList(pWalker, pExpr->pLeft->x.pList);
+ }
#ifndef SQLITE_OMIT_WINDOWFUNC
if( pWin ){
Select *pSel = pNC->pWinSelect;
@@ -106427,11 +108045,12 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
while( pNC2
&& sqlite3ReferencesSrcList(pParse, pExpr, pNC2->pSrcList)==0
){
- pExpr->op2++;
+ pExpr->op2 += (1 + pNC2->nNestedSelect);
pNC2 = pNC2->pNext;
}
assert( pDef!=0 || IN_RENAME_OBJECT );
if( pNC2 && pDef ){
+ pExpr->op2 += pNC2->nNestedSelect;
assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
assert( SQLITE_FUNC_ANYORDER==NC_OrderAgg );
testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
@@ -106460,6 +108079,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
testcase( pNC->ncFlags & NC_PartIdx );
testcase( pNC->ncFlags & NC_IdxExpr );
testcase( pNC->ncFlags & NC_GenCol );
+ assert( pExpr->x.pSelect );
if( pNC->ncFlags & NC_SelfRef ){
notValidImpl(pParse, pNC, "subqueries", pExpr, pExpr);
}else{
@@ -106468,6 +108088,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
assert( pNC->nRef>=nRef );
if( nRef!=pNC->nRef ){
ExprSetProperty(pExpr, EP_VarSelect);
+ pExpr->x.pSelect->selFlags |= SF_Correlated;
}
pNC->ncFlags |= NC_Subquery;
}
@@ -106962,10 +108583,8 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
while( p ){
assert( (p->selFlags & SF_Expanded)!=0 );
assert( (p->selFlags & SF_Resolved)==0 );
- assert( db->suppressErr==0 ); /* SF_Resolved not set if errors suppressed */
p->selFlags |= SF_Resolved;
-
/* Resolve the expressions in the LIMIT and OFFSET clauses. These
** are not allowed to refer to any names, so pass an empty NameContext.
*/
@@ -106992,8 +108611,10 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
/* Recursively resolve names in all subqueries in the FROM clause
*/
+ if( pOuterNC ) pOuterNC->nNestedSelect++;
for(i=0; i<p->pSrc->nSrc; i++){
SrcItem *pItem = &p->pSrc->a[i];
+ assert( pItem->zName!=0 || pItem->pSelect!=0 );/* Test of tag-20240424-1*/
if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){
int nRef = pOuterNC ? pOuterNC->nRef : 0;
const char *zSavedContext = pParse->zAuthContext;
@@ -107016,6 +108637,9 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
}
}
}
+ if( pOuterNC && ALWAYS(pOuterNC->nNestedSelect>0) ){
+ pOuterNC->nNestedSelect--;
+ }
/* Set up the local name-context to pass to sqlite3ResolveExprNames() to
** resolve the result-set expression list.
@@ -107059,7 +108683,9 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
}
if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort;
}
+ sNC.ncFlags |= NC_Where;
if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort;
+ sNC.ncFlags &= ~NC_Where;
/* Resolve names in table-valued-function arguments */
for(i=0; i<p->pSrc->nSrc; i++){
@@ -107598,9 +109224,10 @@ SQLITE_PRIVATE Expr *sqlite3ExprSkipCollateAndLikely(Expr *pExpr){
assert( pExpr->x.pList->nExpr>0 );
assert( pExpr->op==TK_FUNCTION );
pExpr = pExpr->x.pList->a[0].pExpr;
- }else{
- assert( pExpr->op==TK_COLLATE );
+ }else if( pExpr->op==TK_COLLATE ){
pExpr = pExpr->pLeft;
+ }else{
+ break;
}
}
return pExpr;
@@ -107971,6 +109598,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprForVectorField(
*/
pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, 0, 0);
if( pRet ){
+ ExprSetProperty(pRet, EP_FullSize);
pRet->iTable = nField;
pRet->iColumn = iField;
pRet->pLeft = pVector;
@@ -108293,11 +109921,12 @@ SQLITE_PRIVATE void sqlite3ExprSetErrorOffset(Expr *pExpr, int iOfst){
** appear to be quoted. If the quotes were of the form "..." (double-quotes)
** then the EP_DblQuoted flag is set on the expression node.
**
-** Special case: If op==TK_INTEGER and pToken points to a string that
-** can be translated into a 32-bit integer, then the token is not
-** stored in u.zToken. Instead, the integer values is written
-** into u.iValue and the EP_IntValue flag is set. No extra storage
+** Special case (tag-20240227-a): If op==TK_INTEGER and pToken points to
+** a string that can be translated into a 32-bit integer, then the token is
+** not stored in u.zToken. Instead, the integer values is written
+** into u.iValue and the EP_IntValue flag is set. No extra storage
** is allocated to hold the integer text and the dequote flag is ignored.
+** See also tag-20240227-b.
*/
SQLITE_PRIVATE Expr *sqlite3ExprAlloc(
sqlite3 *db, /* Handle for sqlite3DbMallocRawNN() */
@@ -108313,7 +109942,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAlloc(
if( pToken ){
if( op!=TK_INTEGER || pToken->z==0
|| sqlite3GetInt32(pToken->z, &iValue)==0 ){
- nExtra = pToken->n+1;
+ nExtra = pToken->n+1; /* tag-20240227-a */
assert( iValue>=0 );
}
}
@@ -108562,6 +110191,67 @@ SQLITE_PRIVATE Expr *sqlite3ExprFunction(
}
/*
+** Report an error when attempting to use an ORDER BY clause within
+** the arguments of a non-aggregate function.
+*/
+SQLITE_PRIVATE void sqlite3ExprOrderByAggregateError(Parse *pParse, Expr *p){
+ sqlite3ErrorMsg(pParse,
+ "ORDER BY may not be used with non-aggregate %#T()", p
+ );
+}
+
+/*
+** Attach an ORDER BY clause to a function call.
+**
+** functionname( arguments ORDER BY sortlist )
+** \_____________________/ \______/
+** pExpr pOrderBy
+**
+** The ORDER BY clause is inserted into a new Expr node of type TK_ORDER
+** and added to the Expr.pLeft field of the parent TK_FUNCTION node.
+*/
+SQLITE_PRIVATE void sqlite3ExprAddFunctionOrderBy(
+ Parse *pParse, /* Parsing context */
+ Expr *pExpr, /* The function call to which ORDER BY is to be added */
+ ExprList *pOrderBy /* The ORDER BY clause to add */
+){
+ Expr *pOB;
+ sqlite3 *db = pParse->db;
+ if( NEVER(pOrderBy==0) ){
+ assert( db->mallocFailed );
+ return;
+ }
+ if( pExpr==0 ){
+ assert( db->mallocFailed );
+ sqlite3ExprListDelete(db, pOrderBy);
+ return;
+ }
+ assert( pExpr->op==TK_FUNCTION );
+ assert( pExpr->pLeft==0 );
+ assert( ExprUseXList(pExpr) );
+ if( pExpr->x.pList==0 || NEVER(pExpr->x.pList->nExpr==0) ){
+ /* Ignore ORDER BY on zero-argument aggregates */
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pOrderBy);
+ return;
+ }
+ if( IsWindowFunc(pExpr) ){
+ sqlite3ExprOrderByAggregateError(pParse, pExpr);
+ sqlite3ExprListDelete(db, pOrderBy);
+ return;
+ }
+
+ pOB = sqlite3ExprAlloc(db, TK_ORDER, 0, 0);
+ if( pOB==0 ){
+ sqlite3ExprListDelete(db, pOrderBy);
+ return;
+ }
+ pOB->x.pList = pOrderBy;
+ assert( ExprUseXList(pOB) );
+ pExpr->pLeft = pOB;
+ ExprSetProperty(pOB, EP_FullSize);
+}
+
+/*
** Check to see if a function is usable according to current access
** rules:
**
@@ -108684,6 +110374,7 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n
static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
assert( p!=0 );
assert( db!=0 );
+exprDeleteRestart:
assert( !ExprUseUValue(p) || p->u.iValue>=0 );
assert( !ExprUseYWin(p) || !ExprUseYSub(p) );
assert( !ExprUseYWin(p) || p->y.pWin!=0 || db->mallocFailed );
@@ -108699,7 +110390,6 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){
/* The Expr.x union is never used at the same time as Expr.pRight */
assert( (ExprUseXList(p) && p->x.pList==0) || p->pRight==0 );
- if( p->pLeft && p->op!=TK_SELECT_COLUMN ) sqlite3ExprDeleteNN(db, p->pLeft);
if( p->pRight ){
assert( !ExprHasProperty(p, EP_WinFunc) );
sqlite3ExprDeleteNN(db, p->pRight);
@@ -108714,6 +110404,19 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
}
#endif
}
+ if( p->pLeft && p->op!=TK_SELECT_COLUMN ){
+ Expr *pLeft = p->pLeft;
+ if( !ExprHasProperty(p, EP_Static)
+ && !ExprHasProperty(pLeft, EP_Static)
+ ){
+ /* Avoid unnecessary recursion on unary operators */
+ sqlite3DbNNFreeNN(db, p);
+ p = pLeft;
+ goto exprDeleteRestart;
+ }else{
+ sqlite3ExprDeleteNN(db, pLeft);
+ }
+ }
}
if( !ExprHasProperty(p, EP_Static) ){
sqlite3DbNNFreeNN(db, p);
@@ -108722,6 +110425,9 @@ static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){
if( p ) sqlite3ExprDeleteNN(db, p);
}
+SQLITE_PRIVATE void sqlite3ExprDeleteGeneric(sqlite3 *db, void *p){
+ if( ALWAYS(p) ) sqlite3ExprDeleteNN(db, (Expr*)p);
+}
/*
** Clear both elements of an OnOrUsing object
@@ -108743,13 +110449,11 @@ SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){
**
** The pExpr might be deleted immediately on an OOM error.
**
-** The deferred delete is (currently) implemented by adding the
-** pExpr to the pParse->pConstExpr list with a register number of 0.
+** Return 0 if the delete was successfully deferred. Return non-zero
+** if the delete happened immediately because of an OOM.
*/
-SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3ExprDelete,
- pExpr);
+SQLITE_PRIVATE int sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){
+ return 0==sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr);
}
/* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the
@@ -108814,11 +110518,7 @@ static int dupedExprStructSize(const Expr *p, int flags){
assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */
assert( EXPR_FULLSIZE<=0xfff );
assert( (0xfff & (EP_Reduced|EP_TokenOnly))==0 );
- if( 0==flags || p->op==TK_SELECT_COLUMN
-#ifndef SQLITE_OMIT_WINDOWFUNC
- || ExprHasProperty(p, EP_WinFunc)
-#endif
- ){
+ if( 0==flags || ExprHasProperty(p, EP_FullSize) ){
nSize = EXPR_FULLSIZE;
}else{
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
@@ -108849,56 +110549,93 @@ static int dupedExprNodeSize(const Expr *p, int flags){
/*
** Return the number of bytes required to create a duplicate of the
-** expression passed as the first argument. The second argument is a
-** mask containing EXPRDUP_XXX flags.
+** expression passed as the first argument.
**
** The value returned includes space to create a copy of the Expr struct
** itself and the buffer referred to by Expr.u.zToken, if any.
**
-** If the EXPRDUP_REDUCE flag is set, then the return value includes
-** space to duplicate all Expr nodes in the tree formed by Expr.pLeft
-** and Expr.pRight variables (but not for any structures pointed to or
-** descended from the Expr.x.pList or Expr.x.pSelect variables).
+** The return value includes space to duplicate all Expr nodes in the
+** tree formed by Expr.pLeft and Expr.pRight, but not any other
+** substructure such as Expr.x.pList, Expr.x.pSelect, and Expr.y.pWin.
*/
-static int dupedExprSize(const Expr *p, int flags){
- int nByte = 0;
- if( p ){
- nByte = dupedExprNodeSize(p, flags);
- if( flags&EXPRDUP_REDUCE ){
- nByte += dupedExprSize(p->pLeft, flags) + dupedExprSize(p->pRight, flags);
- }
- }
+static int dupedExprSize(const Expr *p){
+ int nByte;
+ assert( p!=0 );
+ nByte = dupedExprNodeSize(p, EXPRDUP_REDUCE);
+ if( p->pLeft ) nByte += dupedExprSize(p->pLeft);
+ if( p->pRight ) nByte += dupedExprSize(p->pRight);
+ assert( nByte==ROUND8(nByte) );
return nByte;
}
/*
-** This function is similar to sqlite3ExprDup(), except that if pzBuffer
-** is not NULL then *pzBuffer is assumed to point to a buffer large enough
-** to store the copy of expression p, the copies of p->u.zToken
-** (if applicable), and the copies of the p->pLeft and p->pRight expressions,
-** if any. Before returning, *pzBuffer is set to the first byte past the
-** portion of the buffer copied into by this function.
+** An EdupBuf is a memory allocation used to stored multiple Expr objects
+** together with their Expr.zToken content. This is used to help implement
+** compression while doing sqlite3ExprDup(). The top-level Expr does the
+** allocation for itself and many of its decendents, then passes an instance
+** of the structure down into exprDup() so that they decendents can have
+** access to that memory.
*/
-static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){
+typedef struct EdupBuf EdupBuf;
+struct EdupBuf {
+ u8 *zAlloc; /* Memory space available for storage */
+#ifdef SQLITE_DEBUG
+ u8 *zEnd; /* First byte past the end of memory */
+#endif
+};
+
+/*
+** This function is similar to sqlite3ExprDup(), except that if pEdupBuf
+** is not NULL then it points to memory that can be used to store a copy
+** of the input Expr p together with its p->u.zToken (if any). pEdupBuf
+** is updated with the new buffer tail prior to returning.
+*/
+static Expr *exprDup(
+ sqlite3 *db, /* Database connection (for memory allocation) */
+ const Expr *p, /* Expr tree to be duplicated */
+ int dupFlags, /* EXPRDUP_REDUCE for compression. 0 if not */
+ EdupBuf *pEdupBuf /* Preallocated storage space, or NULL */
+){
Expr *pNew; /* Value to return */
- u8 *zAlloc; /* Memory space from which to build Expr object */
+ EdupBuf sEdupBuf; /* Memory space from which to build Expr object */
u32 staticFlag; /* EP_Static if space not obtained from malloc */
+ int nToken = -1; /* Space needed for p->u.zToken. -1 means unknown */
assert( db!=0 );
assert( p );
assert( dupFlags==0 || dupFlags==EXPRDUP_REDUCE );
- assert( pzBuffer==0 || dupFlags==EXPRDUP_REDUCE );
+ assert( pEdupBuf==0 || dupFlags==EXPRDUP_REDUCE );
/* Figure out where to write the new Expr structure. */
- if( pzBuffer ){
- zAlloc = *pzBuffer;
+ if( pEdupBuf ){
+ sEdupBuf.zAlloc = pEdupBuf->zAlloc;
+#ifdef SQLITE_DEBUG
+ sEdupBuf.zEnd = pEdupBuf->zEnd;
+#endif
staticFlag = EP_Static;
- assert( zAlloc!=0 );
+ assert( sEdupBuf.zAlloc!=0 );
+ assert( dupFlags==EXPRDUP_REDUCE );
}else{
- zAlloc = sqlite3DbMallocRawNN(db, dupedExprSize(p, dupFlags));
+ int nAlloc;
+ if( dupFlags ){
+ nAlloc = dupedExprSize(p);
+ }else if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
+ nToken = sqlite3Strlen30NN(p->u.zToken)+1;
+ nAlloc = ROUND8(EXPR_FULLSIZE + nToken);
+ }else{
+ nToken = 0;
+ nAlloc = ROUND8(EXPR_FULLSIZE);
+ }
+ assert( nAlloc==ROUND8(nAlloc) );
+ sEdupBuf.zAlloc = sqlite3DbMallocRawNN(db, nAlloc);
+#ifdef SQLITE_DEBUG
+ sEdupBuf.zEnd = sEdupBuf.zAlloc ? sEdupBuf.zAlloc+nAlloc : 0;
+#endif
+
staticFlag = 0;
}
- pNew = (Expr *)zAlloc;
+ pNew = (Expr *)sEdupBuf.zAlloc;
+ assert( EIGHT_BYTE_ALIGNMENT(pNew) );
if( pNew ){
/* Set nNewSize to the size allocated for the structure pointed to
@@ -108907,22 +110644,27 @@ static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){
** by the copy of the p->u.zToken string (if any).
*/
const unsigned nStructSize = dupedExprStructSize(p, dupFlags);
- const int nNewSize = nStructSize & 0xfff;
- int nToken;
- if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
- nToken = sqlite3Strlen30(p->u.zToken) + 1;
- }else{
- nToken = 0;
+ int nNewSize = nStructSize & 0xfff;
+ if( nToken<0 ){
+ if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
+ nToken = sqlite3Strlen30(p->u.zToken) + 1;
+ }else{
+ nToken = 0;
+ }
}
if( dupFlags ){
+ assert( (int)(sEdupBuf.zEnd - sEdupBuf.zAlloc) >= nNewSize+nToken );
assert( ExprHasProperty(p, EP_Reduced)==0 );
- memcpy(zAlloc, p, nNewSize);
+ memcpy(sEdupBuf.zAlloc, p, nNewSize);
}else{
u32 nSize = (u32)exprStructSize(p);
- memcpy(zAlloc, p, nSize);
+ assert( (int)(sEdupBuf.zEnd - sEdupBuf.zAlloc) >=
+ (int)EXPR_FULLSIZE+nToken );
+ memcpy(sEdupBuf.zAlloc, p, nSize);
if( nSize<EXPR_FULLSIZE ){
- memset(&zAlloc[nSize], 0, EXPR_FULLSIZE-nSize);
+ memset(&sEdupBuf.zAlloc[nSize], 0, EXPR_FULLSIZE-nSize);
}
+ nNewSize = EXPR_FULLSIZE;
}
/* Set the EP_Reduced, EP_TokenOnly, and EP_Static flags appropriately. */
@@ -108935,44 +110677,50 @@ static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){
}
/* Copy the p->u.zToken string, if any. */
- if( nToken ){
- char *zToken = pNew->u.zToken = (char*)&zAlloc[nNewSize];
+ assert( nToken>=0 );
+ if( nToken>0 ){
+ char *zToken = pNew->u.zToken = (char*)&sEdupBuf.zAlloc[nNewSize];
memcpy(zToken, p->u.zToken, nToken);
+ nNewSize += nToken;
}
+ sEdupBuf.zAlloc += ROUND8(nNewSize);
+
+ if( ((p->flags|pNew->flags)&(EP_TokenOnly|EP_Leaf))==0 ){
- if( 0==((p->flags|pNew->flags) & (EP_TokenOnly|EP_Leaf)) ){
/* Fill in the pNew->x.pSelect or pNew->x.pList member. */
if( ExprUseXSelect(p) ){
pNew->x.pSelect = sqlite3SelectDup(db, p->x.pSelect, dupFlags);
}else{
- pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, dupFlags);
+ pNew->x.pList = sqlite3ExprListDup(db, p->x.pList,
+ p->op!=TK_ORDER ? dupFlags : 0);
}
- }
- /* Fill in pNew->pLeft and pNew->pRight. */
- if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly|EP_WinFunc) ){
- zAlloc += dupedExprNodeSize(p, dupFlags);
- if( !ExprHasProperty(pNew, EP_TokenOnly|EP_Leaf) ){
- pNew->pLeft = p->pLeft ?
- exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc) : 0;
- pNew->pRight = p->pRight ?
- exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc) : 0;
- }
#ifndef SQLITE_OMIT_WINDOWFUNC
if( ExprHasProperty(p, EP_WinFunc) ){
pNew->y.pWin = sqlite3WindowDup(db, pNew, p->y.pWin);
assert( ExprHasProperty(pNew, EP_WinFunc) );
}
#endif /* SQLITE_OMIT_WINDOWFUNC */
- if( pzBuffer ){
- *pzBuffer = zAlloc;
- }
- }else{
- if( !ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){
- if( pNew->op==TK_SELECT_COLUMN ){
+
+ /* Fill in pNew->pLeft and pNew->pRight. */
+ if( dupFlags ){
+ if( p->op==TK_SELECT_COLUMN ){
+ pNew->pLeft = p->pLeft;
+ assert( p->pRight==0
+ || p->pRight==p->pLeft
+ || ExprHasProperty(p->pLeft, EP_Subquery) );
+ }else{
+ pNew->pLeft = p->pLeft ?
+ exprDup(db, p->pLeft, EXPRDUP_REDUCE, &sEdupBuf) : 0;
+ }
+ pNew->pRight = p->pRight ?
+ exprDup(db, p->pRight, EXPRDUP_REDUCE, &sEdupBuf) : 0;
+ }else{
+ if( p->op==TK_SELECT_COLUMN ){
pNew->pLeft = p->pLeft;
- assert( p->pRight==0 || p->pRight==p->pLeft
- || ExprHasProperty(p->pLeft, EP_Subquery) );
+ assert( p->pRight==0
+ || p->pRight==p->pLeft
+ || ExprHasProperty(p->pLeft, EP_Subquery) );
}else{
pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0);
}
@@ -108980,6 +110728,8 @@ static Expr *exprDup(sqlite3 *db, const Expr *p, int dupFlags, u8 **pzBuffer){
}
}
}
+ if( pEdupBuf ) memcpy(pEdupBuf, &sEdupBuf, sizeof(sEdupBuf));
+ assert( sEdupBuf.zAlloc <= sEdupBuf.zEnd );
return pNew;
}
@@ -109139,17 +110889,19 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int fla
pNewItem->iCursor = pOldItem->iCursor;
pNewItem->addrFillSub = pOldItem->addrFillSub;
pNewItem->regReturn = pOldItem->regReturn;
+ pNewItem->regResult = pOldItem->regResult;
if( pNewItem->fg.isIndexedBy ){
pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy);
+ }else if( pNewItem->fg.isTabFunc ){
+ pNewItem->u1.pFuncArg =
+ sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags);
+ }else{
+ pNewItem->u1.nRow = pOldItem->u1.nRow;
}
pNewItem->u2 = pOldItem->u2;
if( pNewItem->fg.isCte ){
pNewItem->u2.pCteUse->nUse++;
}
- if( pNewItem->fg.isTabFunc ){
- pNewItem->u1.pFuncArg =
- sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags);
- }
pTab = pNewItem->pTab = pOldItem->pTab;
if( pTab ){
pTab->nTabRef++;
@@ -109244,11 +110996,7 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, const Select *p, int flags)
** initially NULL, then create a new expression list.
**
** The pList argument must be either NULL or a pointer to an ExprList
-** obtained from a prior call to sqlite3ExprListAppend(). This routine
-** may not be used with an ExprList obtained from sqlite3ExprListDup().
-** Reason: This routine assumes that the number of slots in pList->a[]
-** is a power of two. That is true for sqlite3ExprListAppend() returns
-** but is not necessarily true from the return value of sqlite3ExprListDup().
+** obtained from a prior call to sqlite3ExprListAppend().
**
** If a memory allocation error occurs, the entire list is freed and
** NULL is returned. If non-NULL is returned, then it is guaranteed
@@ -109513,6 +111261,9 @@ static SQLITE_NOINLINE void exprListDeleteNN(sqlite3 *db, ExprList *pList){
SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
if( pList ) exprListDeleteNN(db, pList);
}
+SQLITE_PRIVATE void sqlite3ExprListDeleteGeneric(sqlite3 *db, void *pList){
+ if( ALWAYS(pList) ) exprListDeleteNN(db, (ExprList*)pList);
+}
/*
** Return the bitwise-OR of all Expr.flags fields in the given
@@ -109616,6 +111367,54 @@ SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){
return pExpr;
}
+/*
+** pExpr is a TK_FUNCTION node. Try to determine whether or not the
+** function is a constant function. A function is constant if all of
+** the following are true:
+**
+** (1) It is a scalar function (not an aggregate or window function)
+** (2) It has either the SQLITE_FUNC_CONSTANT or SQLITE_FUNC_SLOCHNG
+** property.
+** (3) All of its arguments are constants
+**
+** This routine sets pWalker->eCode to 0 if pExpr is not a constant.
+** It makes no changes to pWalker->eCode if pExpr is constant. In
+** every case, it returns WRC_Abort.
+**
+** Called as a service subroutine from exprNodeIsConstant().
+*/
+static SQLITE_NOINLINE int exprNodeIsConstantFunction(
+ Walker *pWalker,
+ Expr *pExpr
+){
+ int n; /* Number of arguments */
+ ExprList *pList; /* List of arguments */
+ FuncDef *pDef; /* The function */
+ sqlite3 *db; /* The database */
+
+ assert( pExpr->op==TK_FUNCTION );
+ if( ExprHasProperty(pExpr, EP_TokenOnly)
+ || (pList = pExpr->x.pList)==0
+ ){;
+ n = 0;
+ }else{
+ n = pList->nExpr;
+ sqlite3WalkExprList(pWalker, pList);
+ if( pWalker->eCode==0 ) return WRC_Abort;
+ }
+ db = pWalker->pParse->db;
+ pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0);
+ if( pDef==0
+ || pDef->xFinalize!=0
+ || (pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0
+ || ExprHasProperty(pExpr, EP_WinFunc)
+ ){
+ pWalker->eCode = 0;
+ return WRC_Abort;
+ }
+ return WRC_Prune;
+}
+
/*
** These routines are Walker callbacks used to check expressions to
@@ -109644,6 +111443,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr *pExpr){
** malformed schema error.
*/
static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
+ assert( pWalker->eCode>0 );
/* If pWalker->eCode is 2 then any term of the expression that comes from
** the ON or USING clauses of an outer join disqualifies the expression
@@ -109663,6 +111463,8 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
){
if( pWalker->eCode==5 ) ExprSetProperty(pExpr, EP_FromDDL);
return WRC_Continue;
+ }else if( pWalker->pParse ){
+ return exprNodeIsConstantFunction(pWalker, pExpr);
}else{
pWalker->eCode = 0;
return WRC_Abort;
@@ -109691,9 +111493,11 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
case TK_IF_NULL_ROW:
case TK_REGISTER:
case TK_DOT:
+ case TK_RAISE:
testcase( pExpr->op==TK_REGISTER );
testcase( pExpr->op==TK_IF_NULL_ROW );
testcase( pExpr->op==TK_DOT );
+ testcase( pExpr->op==TK_RAISE );
pWalker->eCode = 0;
return WRC_Abort;
case TK_VARIABLE:
@@ -109715,15 +111519,15 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
return WRC_Continue;
}
}
-static int exprIsConst(Expr *p, int initFlag, int iCur){
+static int exprIsConst(Parse *pParse, Expr *p, int initFlag){
Walker w;
w.eCode = initFlag;
+ w.pParse = pParse;
w.xExprCallback = exprNodeIsConstant;
w.xSelectCallback = sqlite3SelectWalkFail;
#ifdef SQLITE_DEBUG
w.xSelectCallback2 = sqlite3SelectWalkAssert2;
#endif
- w.u.iCur = iCur;
sqlite3WalkExpr(&w, p);
return w.eCode;
}
@@ -109735,9 +111539,15 @@ static int exprIsConst(Expr *p, int initFlag, int iCur){
** For the purposes of this function, a double-quoted string (ex: "abc")
** is considered a variable but a single-quoted string (ex: 'abc') is
** a constant.
+**
+** The pParse parameter may be NULL. But if it is NULL, there is no way
+** to determine if function calls are constant or not, and hence all
+** function calls will be considered to be non-constant. If pParse is
+** not NULL, then a function call might be constant, depending on the
+** function and on its parameters.
*/
-SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr *p){
- return exprIsConst(p, 1, 0);
+SQLITE_PRIVATE int sqlite3ExprIsConstant(Parse *pParse, Expr *p){
+ return exprIsConst(pParse, p, 1);
}
/*
@@ -109753,8 +111563,24 @@ SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr *p){
** can be added to the pParse->pConstExpr list and evaluated once when
** the prepared statement starts up. See sqlite3ExprCodeRunJustOnce().
*/
-SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){
- return exprIsConst(p, 2, 0);
+static int sqlite3ExprIsConstantNotJoin(Parse *pParse, Expr *p){
+ return exprIsConst(pParse, p, 2);
+}
+
+/*
+** This routine examines sub-SELECT statements as an expression is being
+** walked as part of sqlite3ExprIsTableConstant(). Sub-SELECTs are considered
+** constant as long as they are uncorrelated - meaning that they do not
+** contain any terms from outer contexts.
+*/
+static int exprSelectWalkTableConstant(Walker *pWalker, Select *pSelect){
+ assert( pSelect!=0 );
+ assert( pWalker->eCode==3 || pWalker->eCode==0 );
+ if( (pSelect->selFlags & SF_Correlated)!=0 ){
+ pWalker->eCode = 0;
+ return WRC_Abort;
+ }
+ return WRC_Prune;
}
/*
@@ -109762,9 +111588,26 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){
** for any single row of the table with cursor iCur. In other words, the
** expression must not refer to any non-deterministic function nor any
** table other than iCur.
+**
+** Consider uncorrelated subqueries to be constants if the bAllowSubq
+** parameter is true.
*/
-SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){
- return exprIsConst(p, 3, iCur);
+static int sqlite3ExprIsTableConstant(Expr *p, int iCur, int bAllowSubq){
+ Walker w;
+ w.eCode = 3;
+ w.pParse = 0;
+ w.xExprCallback = exprNodeIsConstant;
+ if( bAllowSubq ){
+ w.xSelectCallback = exprSelectWalkTableConstant;
+ }else{
+ w.xSelectCallback = sqlite3SelectWalkFail;
+#ifdef SQLITE_DEBUG
+ w.xSelectCallback2 = sqlite3SelectWalkAssert2;
+#endif
+ }
+ w.u.iCur = iCur;
+ sqlite3WalkExpr(&w, p);
+ return w.eCode;
}
/*
@@ -109782,7 +111625,10 @@ SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){
**
** (1) pExpr cannot refer to any table other than pSrc->iCursor.
**
-** (2) pExpr cannot use subqueries or non-deterministic functions.
+** (2a) pExpr cannot use subqueries unless the bAllowSubq parameter is
+** true and the subquery is non-correlated
+**
+** (2b) pExpr cannot use non-deterministic functions.
**
** (3) pSrc cannot be part of the left operand for a RIGHT JOIN.
** (Is there some way to relax this constraint?)
@@ -109811,7 +111657,8 @@ SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){
SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(
Expr *pExpr, /* The constraint */
const SrcList *pSrcList, /* Complete FROM clause */
- int iSrc /* Which element of pSrcList to use */
+ int iSrc, /* Which element of pSrcList to use */
+ int bAllowSubq /* Allow non-correlated subqueries */
){
const SrcItem *pSrc = &pSrcList->a[iSrc];
if( pSrc->fg.jointype & JT_LTORJ ){
@@ -109836,7 +111683,8 @@ SQLITE_PRIVATE int sqlite3ExprIsSingleTableConstraint(
}
}
}
- return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor); /* rules (1), (2) */
+ /* Rules (1), (2a), and (2b) handled by the following: */
+ return sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor, bAllowSubq);
}
@@ -109921,7 +111769,7 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantOrGroupBy(Parse *pParse, Expr *p, ExprLi
*/
SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr *p, u8 isInit){
assert( isInit==0 || isInit==1 );
- return exprIsConst(p, 4+isInit, 0);
+ return exprIsConst(0, p, 4+isInit);
}
#ifdef SQLITE_ENABLE_CURSOR_HINTS
@@ -110011,10 +111859,14 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){
return 0;
case TK_COLUMN:
assert( ExprUseYTab(p) );
- return ExprHasProperty(p, EP_CanBeNull) ||
- p->y.pTab==0 || /* Reference to column of index on expression */
- (p->iColumn>=0
+ return ExprHasProperty(p, EP_CanBeNull)
+ || NEVER(p->y.pTab==0) /* Reference to column of index on expr */
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ || (p->iColumn==XN_ROWID && IsView(p->y.pTab))
+#endif
+ || (p->iColumn>=0
&& p->y.pTab->aCol!=0 /* Possible due to prior error */
+ && ALWAYS(p->iColumn<p->y.pTab->nCol)
&& p->y.pTab->aCol[p->iColumn].notNull==0);
default:
return 1;
@@ -110075,6 +111927,27 @@ SQLITE_PRIVATE int sqlite3IsRowid(const char *z){
}
/*
+** Return a pointer to a buffer containing a usable rowid alias for table
+** pTab. An alias is usable if there is not an explicit user-defined column
+** of the same name.
+*/
+SQLITE_PRIVATE const char *sqlite3RowidAlias(Table *pTab){
+ const char *azOpt[] = {"_ROWID_", "ROWID", "OID"};
+ int ii;
+ assert( VisibleRowid(pTab) );
+ for(ii=0; ii<ArraySize(azOpt); ii++){
+ int iCol;
+ for(iCol=0; iCol<pTab->nCol; iCol++){
+ if( sqlite3_stricmp(azOpt[ii], pTab->aCol[iCol].zCnName)==0 ) break;
+ }
+ if( iCol==pTab->nCol ){
+ return azOpt[ii];
+ }
+ }
+ return 0;
+}
+
+/*
** pX is the RHS of an IN operator. If pX is a SELECT statement
** that can be simplified to a direct table access, then return
** a pointer to the SELECT statement. If pX is not a SELECT statement,
@@ -110144,13 +112017,13 @@ static void sqlite3SetHasNullFlag(Vdbe *v, int iCur, int regHasNull){
** The argument is an IN operator with a list (not a subquery) on the
** right-hand side. Return TRUE if that list is constant.
*/
-static int sqlite3InRhsIsConstant(Expr *pIn){
+static int sqlite3InRhsIsConstant(Parse *pParse, Expr *pIn){
Expr *pLHS;
int res;
assert( !ExprHasProperty(pIn, EP_xIsSelect) );
pLHS = pIn->pLeft;
pIn->pLeft = 0;
- res = sqlite3ExprIsConstant(pIn);
+ res = sqlite3ExprIsConstant(pParse, pIn);
pIn->pLeft = pLHS;
return res;
}
@@ -110419,7 +112292,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex(
if( eType==0
&& (inFlags & IN_INDEX_NOOP_OK)
&& ExprUseXList(pX)
- && (!sqlite3InRhsIsConstant(pX) || pX->x.pList->nExpr<=2)
+ && (!sqlite3InRhsIsConstant(pParse,pX) || pX->x.pList->nExpr<=2)
){
pParse->nTab--; /* Back out the allocation of the unused cursor */
iTab = -1; /* Cursor is not allocated */
@@ -110702,7 +112575,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
** this code only executes once. Because for a non-constant
** expression we need to rerun this code each time.
*/
- if( addrOnce && !sqlite3ExprIsConstant(pE2) ){
+ if( addrOnce && !sqlite3ExprIsConstant(pParse, pE2) ){
sqlite3VdbeChangeToNoop(v, addrOnce-1);
sqlite3VdbeChangeToNoop(v, addrOnce);
ExprClearProperty(pExpr, EP_Subrtn);
@@ -111612,6 +113485,41 @@ static SQLITE_NOINLINE int sqlite3IndexedExprLookup(
/*
+** Expresion pExpr is guaranteed to be a TK_COLUMN or equivalent. This
+** function checks the Parse.pIdxPartExpr list to see if this column
+** can be replaced with a constant value. If so, it generates code to
+** put the constant value in a register (ideally, but not necessarily,
+** register iTarget) and returns the register number.
+**
+** Or, if the TK_COLUMN cannot be replaced by a constant, zero is
+** returned.
+*/
+static int exprPartidxExprLookup(Parse *pParse, Expr *pExpr, int iTarget){
+ IndexedExpr *p;
+ for(p=pParse->pIdxPartExpr; p; p=p->pIENext){
+ if( pExpr->iColumn==p->iIdxCol && pExpr->iTable==p->iDataCur ){
+ Vdbe *v = pParse->pVdbe;
+ int addr = 0;
+ int ret;
+
+ if( p->bMaybeNullRow ){
+ addr = sqlite3VdbeAddOp1(v, OP_IfNullRow, p->iIdxCur);
+ }
+ ret = sqlite3ExprCodeTarget(pParse, p->pExpr, iTarget);
+ sqlite3VdbeAddOp4(pParse->pVdbe, OP_Affinity, ret, 1, 0,
+ (const char*)&p->aff, 1);
+ if( addr ){
+ sqlite3VdbeJumpHere(v, addr);
+ sqlite3VdbeChangeP3(v, addr, ret);
+ }
+ return ret;
+ }
+ }
+ return 0;
+}
+
+
+/*
** Generate code into the current Vdbe to evaluate the given
** expression. Attempt to store the results in register "target".
** Return the register where results are stored.
@@ -111647,6 +113555,7 @@ expr_code_doover:
assert( !ExprHasVVAProperty(pExpr,EP_Immutable) );
op = pExpr->op;
}
+ assert( op!=TK_ORDER );
switch( op ){
case TK_AGG_COLUMN: {
AggInfo *pAggInfo = pExpr->pAggInfo;
@@ -111660,7 +113569,7 @@ expr_code_doover:
#ifdef SQLITE_VDBE_COVERAGE
/* Verify that the OP_Null above is exercised by tests
** tag-20230325-2 */
- sqlite3VdbeAddOp2(v, OP_NotNull, target, 1);
+ sqlite3VdbeAddOp3(v, OP_NotNull, target, 1, 20230325);
VdbeCoverageNeverTaken(v);
#endif
break;
@@ -111768,6 +113677,11 @@ expr_code_doover:
iTab = pParse->iSelfTab - 1;
}
}
+ else if( pParse->pIdxPartExpr
+ && 0!=(r1 = exprPartidxExprLookup(pParse, pExpr, target))
+ ){
+ return r1;
+ }
assert( ExprUseYTab(pExpr) );
assert( pExpr->y.pTab!=0 );
iReg = sqlite3ExprCodeGetColumn(pParse, pExpr->y.pTab,
@@ -111825,12 +113739,6 @@ expr_code_doover:
assert( pExpr->u.zToken!=0 );
assert( pExpr->u.zToken[0]!=0 );
sqlite3VdbeAddOp2(v, OP_Variable, pExpr->iColumn, target);
- if( pExpr->u.zToken[1]!=0 ){
- const char *z = sqlite3VListNumToName(pParse->pVList, pExpr->iColumn);
- assert( pExpr->u.zToken[0]=='?' || (z && !strcmp(pExpr->u.zToken, z)) );
- pParse->pVList[0] = 0; /* Indicate VList may no longer be enlarged */
- sqlite3VdbeAppendP4(v, (char*)z, P4_STATIC);
- }
return target;
}
case TK_REGISTER: {
@@ -112004,7 +113912,9 @@ expr_code_doover:
}
#endif
- if( ConstFactorOk(pParse) && sqlite3ExprIsConstantNotJoin(pExpr) ){
+ if( ConstFactorOk(pParse)
+ && sqlite3ExprIsConstantNotJoin(pParse,pExpr)
+ ){
/* SQL functions can be expensive. So try to avoid running them
** multiple times if we know they always give the same result */
return sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1);
@@ -112035,7 +113945,7 @@ expr_code_doover:
}
for(i=0; i<nFarg; i++){
- if( i<32 && sqlite3ExprIsConstant(pFarg->a[i].pExpr) ){
+ if( i<32 && sqlite3ExprIsConstant(pParse, pFarg->a[i].pExpr) ){
testcase( i==31 );
constMask |= MASKBIT32(i);
}
@@ -112177,8 +114087,9 @@ expr_code_doover:
if( !ExprHasProperty(pExpr, EP_Collate) ){
/* A TK_COLLATE Expr node without the EP_Collate tag is a so-called
** "SOFT-COLLATE" that is added to constraints that are pushed down
- ** from outer queries into sub-queries by the push-down optimization.
- ** Clear subtypes as subtypes may not cross a subquery boundary.
+ ** from outer queries into sub-queries by the WHERE-clause push-down
+ ** optimization. Clear subtypes as subtypes may not cross a subquery
+ ** boundary.
*/
assert( pExpr->pLeft );
sqlite3ExprCode(pParse, pExpr->pLeft, target);
@@ -112428,7 +114339,7 @@ expr_code_doover:
** once. If no functions are involved, then factor the code out and put it at
** the end of the prepared statement in the initialization section.
**
-** If regDest>=0 then the result is always stored in that register and the
+** If regDest>0 then the result is always stored in that register and the
** result is not reusable. If regDest<0 then this routine is free to
** store the value wherever it wants. The register where the expression
** is stored is returned. When regDest<0, two identical expressions might
@@ -112443,6 +114354,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce(
){
ExprList *p;
assert( ConstFactorOk(pParse) );
+ assert( regDest!=0 );
p = pParse->pConstExpr;
if( regDest<0 && p ){
struct ExprList_item *pItem;
@@ -112501,7 +114413,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){
if( ConstFactorOk(pParse)
&& ALWAYS(pExpr!=0)
&& pExpr->op!=TK_REGISTER
- && sqlite3ExprIsConstantNotJoin(pExpr)
+ && sqlite3ExprIsConstantNotJoin(pParse, pExpr)
){
*pReg = 0;
r2 = sqlite3ExprCodeRunJustOnce(pParse, pExpr, -1);
@@ -112533,8 +114445,10 @@ SQLITE_PRIVATE void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
if( inReg!=target ){
u8 op;
- if( ALWAYS(pExpr)
- && (ExprHasProperty(pExpr,EP_Subquery) || pExpr->op==TK_REGISTER)
+ Expr *pX = sqlite3ExprSkipCollateAndLikely(pExpr);
+ testcase( pX!=pExpr );
+ if( ALWAYS(pX)
+ && (ExprHasProperty(pX,EP_Subquery) || pX->op==TK_REGISTER)
){
op = OP_Copy;
}else{
@@ -112563,7 +114477,7 @@ SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse *pParse, Expr *pExpr, int target){
** might choose to code the expression at initialization time.
*/
SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse *pParse, Expr *pExpr, int target){
- if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pExpr) ){
+ if( pParse->okConstFactor && sqlite3ExprIsConstantNotJoin(pParse,pExpr) ){
sqlite3ExprCodeRunJustOnce(pParse, pExpr, target);
}else{
sqlite3ExprCodeCopy(pParse, pExpr, target);
@@ -112622,7 +114536,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeExprList(
sqlite3VdbeAddOp2(v, copyOp, j+srcReg-1, target+i);
}
}else if( (flags & SQLITE_ECEL_FACTOR)!=0
- && sqlite3ExprIsConstantNotJoin(pExpr)
+ && sqlite3ExprIsConstantNotJoin(pParse,pExpr)
){
sqlite3ExprCodeRunJustOnce(pParse, pExpr, target+i);
}else{
@@ -113254,8 +115168,8 @@ SQLITE_PRIVATE int sqlite3ExprListCompare(const ExprList *pA, const ExprList *pB
*/
SQLITE_PRIVATE int sqlite3ExprCompareSkip(Expr *pA,Expr *pB, int iTab){
return sqlite3ExprCompare(0,
- sqlite3ExprSkipCollateAndLikely(pA),
- sqlite3ExprSkipCollateAndLikely(pB),
+ sqlite3ExprSkipCollate(pA),
+ sqlite3ExprSkipCollate(pB),
iTab);
}
@@ -113727,6 +115641,12 @@ SQLITE_PRIVATE int sqlite3ReferencesSrcList(Parse *pParse, Expr *pExpr, SrcList
assert( pExpr->op==TK_AGG_FUNCTION );
assert( ExprUseXList(pExpr) );
sqlite3WalkExprList(&w, pExpr->x.pList);
+ if( pExpr->pLeft ){
+ assert( pExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pExpr->pLeft) );
+ assert( pExpr->pLeft->x.pList!=0 );
+ sqlite3WalkExprList(&w, pExpr->pLeft->x.pList);
+ }
#ifndef SQLITE_OMIT_WINDOWFUNC
if( ExprHasProperty(pExpr, EP_WinFunc) ){
sqlite3WalkExpr(&w, pExpr->y.pWin->pFilter);
@@ -113767,9 +115687,8 @@ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){
&& pAggInfo->aCol[iAgg].pCExpr==pExpr
){
pExpr = sqlite3ExprDup(db, pExpr, 0);
- if( pExpr ){
+ if( pExpr && !sqlite3ExprDeferredDelete(pParse, pExpr) ){
pAggInfo->aCol[iAgg].pCExpr = pExpr;
- sqlite3ExprDeferredDelete(pParse, pExpr);
}
}
}else{
@@ -113778,9 +115697,8 @@ static int agginfoPersistExprCb(Walker *pWalker, Expr *pExpr){
&& pAggInfo->aFunc[iAgg].pFExpr==pExpr
){
pExpr = sqlite3ExprDup(db, pExpr, 0);
- if( pExpr ){
+ if( pExpr && !sqlite3ExprDeferredDelete(pParse, pExpr) ){
pAggInfo->aFunc[iAgg].pFExpr = pExpr;
- sqlite3ExprDeferredDelete(pParse, pExpr);
}
}
}
@@ -113974,13 +115892,14 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
case TK_AGG_FUNCTION: {
if( (pNC->ncFlags & NC_InAggFunc)==0
&& pWalker->walkerDepth==pExpr->op2
+ && pExpr->pAggInfo==0
){
/* Check to see if pExpr is a duplicate of another aggregate
** function that is already in the pAggInfo structure
*/
struct AggInfo_func *pItem = pAggInfo->aFunc;
for(i=0; i<pAggInfo->nFunc; i++, pItem++){
- if( pItem->pFExpr==pExpr ) break;
+ if( NEVER(pItem->pFExpr==pExpr) ) break;
if( sqlite3ExprCompare(0, pItem->pFExpr, pExpr, -1)==0 ){
break;
}
@@ -113991,14 +115910,44 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
u8 enc = ENC(pParse->db);
i = addAggInfoFunc(pParse->db, pAggInfo);
if( i>=0 ){
+ int nArg;
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
pItem = &pAggInfo->aFunc[i];
pItem->pFExpr = pExpr;
assert( ExprUseUToken(pExpr) );
+ nArg = pExpr->x.pList ? pExpr->x.pList->nExpr : 0;
pItem->pFunc = sqlite3FindFunction(pParse->db,
- pExpr->u.zToken,
- pExpr->x.pList ? pExpr->x.pList->nExpr : 0, enc, 0);
- if( pExpr->flags & EP_Distinct ){
+ pExpr->u.zToken, nArg, enc, 0);
+ assert( pItem->bOBUnique==0 );
+ if( pExpr->pLeft
+ && (pItem->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)==0
+ ){
+ /* The NEEDCOLL test above causes any ORDER BY clause on
+ ** aggregate min() or max() to be ignored. */
+ ExprList *pOBList;
+ assert( nArg>0 );
+ assert( pExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pExpr->pLeft) );
+ pItem->iOBTab = pParse->nTab++;
+ pOBList = pExpr->pLeft->x.pList;
+ assert( pOBList->nExpr>0 );
+ assert( pItem->bOBUnique==0 );
+ if( pOBList->nExpr==1
+ && nArg==1
+ && sqlite3ExprCompare(0,pOBList->a[0].pExpr,
+ pExpr->x.pList->a[0].pExpr,0)==0
+ ){
+ pItem->bOBPayload = 0;
+ pItem->bOBUnique = ExprHasProperty(pExpr, EP_Distinct);
+ }else{
+ pItem->bOBPayload = 1;
+ }
+ pItem->bUseSubtype =
+ (pItem->pFunc->funcFlags & SQLITE_SUBTYPE)!=0;
+ }else{
+ pItem->iOBTab = -1;
+ }
+ if( ExprHasProperty(pExpr, EP_Distinct) && !pItem->bOBUnique ){
pItem->iDistinct = pParse->nTab++;
}else{
pItem->iDistinct = -1;
@@ -114634,14 +116583,19 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
/* Verify that constraints are still satisfied */
if( pNew->pCheck!=0
|| (pCol->notNull && (pCol->colFlags & COLFLAG_GENERATED)!=0)
+ || (pTab->tabFlags & TF_Strict)!=0
){
sqlite3NestedParse(pParse,
"SELECT CASE WHEN quick_check GLOB 'CHECK*'"
" THEN raise(ABORT,'CHECK constraint failed')"
+ " WHEN quick_check GLOB 'non-* value in*'"
+ " THEN raise(ABORT,'type mismatch on DEFAULT')"
" ELSE raise(ABORT,'NOT NULL constraint failed')"
" END"
" FROM pragma_quick_check(%Q,%Q)"
- " WHERE quick_check GLOB 'CHECK*' OR quick_check GLOB 'NULL*'",
+ " WHERE quick_check GLOB 'CHECK*'"
+ " OR quick_check GLOB 'NULL*'"
+ " OR quick_check GLOB 'non-* value in*'",
zTab, zDb
);
}
@@ -116445,7 +118399,12 @@ SQLITE_PRIVATE void sqlite3AlterDropColumn(Parse *pParse, SrcList *pSrc, const T
if( i==pTab->iPKey ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regOut);
}else{
+ char aff = pTab->aCol[i].affinity;
+ if( aff==SQLITE_AFF_REAL ){
+ pTab->aCol[i].affinity = SQLITE_AFF_NUMERIC;
+ }
sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOut);
+ pTab->aCol[i].affinity = aff;
}
nField++;
}
@@ -116756,9 +118715,9 @@ static void openStatTable(
typedef struct StatAccum StatAccum;
typedef struct StatSample StatSample;
struct StatSample {
- tRowcnt *anEq; /* sqlite_stat4.nEq */
tRowcnt *anDLt; /* sqlite_stat4.nDLt */
#ifdef SQLITE_ENABLE_STAT4
+ tRowcnt *anEq; /* sqlite_stat4.nEq */
tRowcnt *anLt; /* sqlite_stat4.nLt */
union {
i64 iRowid; /* Rowid in main table of the key */
@@ -116916,9 +118875,9 @@ static void statInit(
/* Allocate the space required for the StatAccum object */
n = sizeof(*p)
- + sizeof(tRowcnt)*nColUp /* StatAccum.anEq */
- + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */
+ + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */
#ifdef SQLITE_ENABLE_STAT4
+ n += sizeof(tRowcnt)*nColUp; /* StatAccum.anEq */
if( mxSample ){
n += sizeof(tRowcnt)*nColUp /* StatAccum.anLt */
+ sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */
@@ -116939,9 +118898,9 @@ static void statInit(
p->nKeyCol = nKeyCol;
p->nSkipAhead = 0;
p->current.anDLt = (tRowcnt*)&p[1];
- p->current.anEq = &p->current.anDLt[nColUp];
#ifdef SQLITE_ENABLE_STAT4
+ p->current.anEq = &p->current.anDLt[nColUp];
p->mxSample = p->nLimit==0 ? mxSample : 0;
if( mxSample ){
u8 *pSpace; /* Allocated space not yet assigned */
@@ -117208,7 +119167,9 @@ static void statPush(
if( p->nRow==0 ){
/* This is the first call to this function. Do initialization. */
+#ifdef SQLITE_ENABLE_STAT4
for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1;
+#endif
}else{
/* Second and subsequent calls get processed here */
#ifdef SQLITE_ENABLE_STAT4
@@ -117217,15 +119178,17 @@ static void statPush(
/* Update anDLt[], anLt[] and anEq[] to reflect the values that apply
** to the current row of the index. */
+#ifdef SQLITE_ENABLE_STAT4
for(i=0; i<iChng; i++){
p->current.anEq[i]++;
}
+#endif
for(i=iChng; i<p->nCol; i++){
p->current.anDLt[i]++;
#ifdef SQLITE_ENABLE_STAT4
if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i];
-#endif
p->current.anEq[i] = 1;
+#endif
}
}
@@ -117359,7 +119322,9 @@ static void statGet(
u64 iVal = (p->nRow + nDistinct - 1) / nDistinct;
if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1;
sqlite3_str_appendf(&sStat, " %llu", iVal);
- assert( p->current.anEq[i] );
+#ifdef SQLITE_ENABLE_STAT4
+ assert( p->current.anEq[i] || p->nRow==0 );
+#endif
}
sqlite3ResultStrAccum(context, &sStat);
}
@@ -117543,7 +119508,7 @@ static void analyzeOneTable(
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int nCol; /* Number of columns in pIdx. "N" */
- int addrRewind; /* Address of "OP_Rewind iIdxCur" */
+ int addrGotoEnd; /* Address of "OP_Rewind iIdxCur" */
int addrNextRow; /* Address of "next_row:" */
const char *zIdxName; /* Name of the index */
int nColTest; /* Number of columns to test for changes */
@@ -117567,9 +119532,14 @@ static void analyzeOneTable(
/*
** Pseudo-code for loop that calls stat_push():
**
- ** Rewind csr
- ** if eof(csr) goto end_of_scan;
** regChng = 0
+ ** Rewind csr
+ ** if eof(csr){
+ ** stat_init() with count = 0;
+ ** goto end_of_scan;
+ ** }
+ ** count()
+ ** stat_init()
** goto chng_addr_0;
**
** next_row:
@@ -117608,41 +119578,36 @@ static void analyzeOneTable(
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
VdbeComment((v, "%s", pIdx->zName));
- /* Invoke the stat_init() function. The arguments are:
+ /* Implementation of the following:
**
+ ** regChng = 0
+ ** Rewind csr
+ ** if eof(csr){
+ ** stat_init() with count = 0;
+ ** goto end_of_scan;
+ ** }
+ ** count()
+ ** stat_init()
+ ** goto chng_addr_0;
+ */
+ assert( regTemp2==regStat+4 );
+ sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2);
+
+ /* Arguments to stat_init():
** (1) the number of columns in the index including the rowid
** (or for a WITHOUT ROWID table, the number of PK columns),
** (2) the number of columns in the key without the rowid/pk
- ** (3) estimated number of rows in the index,
- */
+ ** (3) estimated number of rows in the index. */
sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1);
assert( regRowid==regStat+2 );
sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid);
-#ifdef SQLITE_ENABLE_STAT4
- if( OptimizationEnabled(db, SQLITE_Stat4) ){
- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regTemp);
- addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
- VdbeCoverage(v);
- }else
-#endif
- {
- addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
- VdbeCoverage(v);
- sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, 1);
- }
- assert( regTemp2==regStat+4 );
- sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2);
+ sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp,
+ OptimizationDisabled(db, SQLITE_Stat4));
sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4,
&statInitFuncdef, 0);
+ addrGotoEnd = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
+ VdbeCoverage(v);
- /* Implementation of the following:
- **
- ** Rewind csr
- ** if eof(csr) goto end_of_scan;
- ** regChng = 0
- ** goto next_push_0;
- **
- */
sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng);
addrNextRow = sqlite3VdbeCurrentAddr(v);
@@ -117749,6 +119714,12 @@ static void analyzeOneTable(
}
/* Add the entry to the stat1 table. */
+ if( pIdx->pPartIdxWhere ){
+ /* Partial indexes might get a zero-entry in sqlite_stat1. But
+ ** an empty table is omitted from sqlite_stat1. */
+ sqlite3VdbeJumpHere(v, addrGotoEnd);
+ addrGotoEnd = 0;
+ }
callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1);
assert( "BBB"[0]==SQLITE_AFF_TEXT );
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0);
@@ -117772,6 +119743,13 @@ static void analyzeOneTable(
int addrIsNull;
u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
+ /* No STAT4 data is generated if the number of rows is zero */
+ if( addrGotoEnd==0 ){
+ sqlite3VdbeAddOp2(v, OP_Cast, regStat1, SQLITE_AFF_INTEGER);
+ addrGotoEnd = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1);
+ VdbeCoverage(v);
+ }
+
if( doOnce ){
int mxCol = nCol;
Index *pX;
@@ -117824,7 +119802,7 @@ static void analyzeOneTable(
#endif /* SQLITE_ENABLE_STAT4 */
/* End of analysis */
- sqlite3VdbeJumpHere(v, addrRewind);
+ if( addrGotoEnd ) sqlite3VdbeJumpHere(v, addrGotoEnd);
}
@@ -118048,6 +120026,16 @@ static void decodeIntArray(
while( z[0]!=0 && z[0]!=' ' ) z++;
while( z[0]==' ' ) z++;
}
+
+ /* Set the bLowQual flag if the peak number of rows obtained
+ ** from a full equality match is so large that a full table scan
+ ** seems likely to be faster than using the index.
+ */
+ if( aLog[0] > 66 /* Index has more than 100 rows */
+ && aLog[0] <= aLog[nOut-1] /* And only a single value seen */
+ ){
+ pIndex->bLowQual = 1;
+ }
}
}
@@ -119563,7 +121551,7 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
}
sqlite3VdbeAddOp0(v, OP_Halt);
-#if SQLITE_USER_AUTHENTICATION
+#if SQLITE_USER_AUTHENTICATION && !defined(SQLITE_OMIT_SHARED_CACHE)
if( pParse->nTableLock>0 && db->init.busy==0 ){
sqlite3UserAuthInit(db);
if( db->auth.authLevel<UAUTH_User ){
@@ -119620,19 +121608,14 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
*/
if( pParse->pAinc ) sqlite3AutoincrementBegin(pParse);
- /* Code constant expressions that where factored out of inner loops.
- **
- ** The pConstExpr list might also contain expressions that we simply
- ** want to keep around until the Parse object is deleted. Such
- ** expressions have iConstExprReg==0. Do not generate code for
- ** those expressions, of course.
+ /* Code constant expressions that were factored out of inner loops.
*/
if( pParse->pConstExpr ){
ExprList *pEL = pParse->pConstExpr;
pParse->okConstFactor = 0;
for(i=0; i<pEL->nExpr; i++){
- int iReg = pEL->a[i].u.iConstExprReg;
- sqlite3ExprCode(pParse, pEL->a[i].pExpr, iReg);
+ assert( pEL->a[i].u.iConstExprReg>0 );
+ sqlite3ExprCode(pParse, pEL->a[i].pExpr, pEL->a[i].u.iConstExprReg);
}
}
@@ -120099,7 +122082,7 @@ SQLITE_PRIVATE void sqlite3ColumnSetExpr(
*/
SQLITE_PRIVATE Expr *sqlite3ColumnExpr(Table *pTab, Column *pCol){
if( pCol->iDflt==0 ) return 0;
- if( NEVER(!IsOrdinaryTable(pTab)) ) return 0;
+ if( !IsOrdinaryTable(pTab) ) return 0;
if( NEVER(pTab->u.tab.pDfltList==0) ) return 0;
if( NEVER(pTab->u.tab.pDfltList->nExpr<pCol->iDflt) ) return 0;
return pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr;
@@ -120252,6 +122235,9 @@ SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
if( db->pnBytesFreed==0 && (--pTable->nTabRef)>0 ) return;
deleteTable(db, pTable);
}
+SQLITE_PRIVATE void sqlite3DeleteTableGeneric(sqlite3 *db, void *pTable){
+ sqlite3DeleteTable(db, (Table*)pTable);
+}
/*
@@ -120787,19 +122773,13 @@ SQLITE_PRIVATE void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){
#endif
/*
-** Name of the special TEMP trigger used to implement RETURNING. The
-** name begins with "sqlite_" so that it is guaranteed not to collide
-** with any application-generated triggers.
-*/
-#define RETURNING_TRIGGER_NAME "sqlite_returning"
-
-/*
** Clean up the data structures associated with the RETURNING clause.
*/
-static void sqlite3DeleteReturning(sqlite3 *db, Returning *pRet){
+static void sqlite3DeleteReturning(sqlite3 *db, void *pArg){
+ Returning *pRet = (Returning*)pArg;
Hash *pHash;
pHash = &(db->aDb[1].pSchema->trigHash);
- sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, 0);
+ sqlite3HashInsert(pHash, pRet->zName, 0);
sqlite3ExprListDelete(db, pRet->pReturnEL);
sqlite3DbFree(db, pRet);
}
@@ -120838,11 +122818,12 @@ SQLITE_PRIVATE void sqlite3AddReturning(Parse *pParse, ExprList *pList){
pParse->u1.pReturning = pRet;
pRet->pParse = pParse;
pRet->pReturnEL = pList;
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3DeleteReturning, pRet);
+ sqlite3ParserAddCleanup(pParse, sqlite3DeleteReturning, pRet);
testcase( pParse->earlyCleanup );
if( db->mallocFailed ) return;
- pRet->retTrig.zName = RETURNING_TRIGGER_NAME;
+ sqlite3_snprintf(sizeof(pRet->zName), pRet->zName,
+ "sqlite_returning_%p", pParse);
+ pRet->retTrig.zName = pRet->zName;
pRet->retTrig.op = TK_RETURNING;
pRet->retTrig.tr_tm = TRIGGER_AFTER;
pRet->retTrig.bReturning = 1;
@@ -120853,9 +122834,9 @@ SQLITE_PRIVATE void sqlite3AddReturning(Parse *pParse, ExprList *pList){
pRet->retTStep.pTrig = &pRet->retTrig;
pRet->retTStep.pExprList = pList;
pHash = &(db->aDb[1].pSchema->trigHash);
- assert( sqlite3HashFind(pHash, RETURNING_TRIGGER_NAME)==0
+ assert( sqlite3HashFind(pHash, pRet->zName)==0
|| pParse->nErr || pParse->ifNotExists );
- if( sqlite3HashInsert(pHash, RETURNING_TRIGGER_NAME, &pRet->retTrig)
+ if( sqlite3HashInsert(pHash, pRet->zName, &pRet->retTrig)
==&pRet->retTrig ){
sqlite3OomFault(db);
}
@@ -121036,7 +123017,8 @@ SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn, Column *pCol){
assert( zIn!=0 );
while( zIn[0] ){
- h = (h<<8) + sqlite3UpperToLower[(*zIn)&0xff];
+ u8 x = *(u8*)zIn;
+ h = (h<<8) + sqlite3UpperToLower[x];
zIn++;
if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){ /* CHAR */
aff = SQLITE_AFF_TEXT;
@@ -122208,20 +124190,20 @@ SQLITE_PRIVATE void sqlite3EndTable(
int regRowid; /* Rowid of the next row to insert */
int addrInsLoop; /* Top of the loop for inserting rows */
Table *pSelTab; /* A table that describes the SELECT results */
+ int iCsr; /* Write cursor on the new table */
if( IN_SPECIAL_PARSE ){
pParse->rc = SQLITE_ERROR;
pParse->nErr++;
return;
}
+ iCsr = pParse->nTab++;
regYield = ++pParse->nMem;
regRec = ++pParse->nMem;
regRowid = ++pParse->nMem;
- assert(pParse->nTab==1);
sqlite3MayAbort(pParse);
- sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb);
+ sqlite3VdbeAddOp3(v, OP_OpenWrite, iCsr, pParse->regRoot, iDb);
sqlite3VdbeChangeP5(v, OPFLAG_P2ISREG);
- pParse->nTab = 2;
addrTop = sqlite3VdbeCurrentAddr(v) + 1;
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
if( pParse->nErr ) return;
@@ -122242,11 +124224,11 @@ SQLITE_PRIVATE void sqlite3EndTable(
VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_MakeRecord, dest.iSdst, dest.nSdst, regRec);
sqlite3TableAffinity(v, p, 0);
- sqlite3VdbeAddOp2(v, OP_NewRowid, 1, regRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, 1, regRec, regRowid);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, iCsr, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iCsr, regRec, regRowid);
sqlite3VdbeGoto(v, addrInsLoop);
sqlite3VdbeJumpHere(v, addrInsLoop);
- sqlite3VdbeAddOp1(v, OP_Close, 1);
+ sqlite3VdbeAddOp1(v, OP_Close, iCsr);
}
/* Compute the complete text of the CREATE statement */
@@ -122299,6 +124281,14 @@ SQLITE_PRIVATE void sqlite3EndTable(
/* Reparse everything to update our internal data structures */
sqlite3VdbeAddParseSchemaOp(v, iDb,
sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName),0);
+
+ /* Test for cycles in generated columns and illegal expressions
+ ** in CHECK constraints and in DEFAULT clauses. */
+ if( p->tabFlags & TF_HasGenerated ){
+ sqlite3VdbeAddOp4(v, OP_SqlExec, 0x0001, 0, 0,
+ sqlite3MPrintf(db, "SELECT*FROM\"%w\".\"%w\"",
+ db->aDb[iDb].zDbSName, p->zName), P4_DYNAMIC);
+ }
}
/* Add the table to the in-memory representation of the database.
@@ -122375,9 +124365,12 @@ SQLITE_PRIVATE void sqlite3CreateView(
** on a view, even though views do not have rowids. The following flag
** setting fixes this problem. But the fix can be disabled by compiling
** with -DSQLITE_ALLOW_ROWID_IN_VIEW in case there are legacy apps that
- ** depend upon the old buggy behavior. */
-#ifndef SQLITE_ALLOW_ROWID_IN_VIEW
- p->tabFlags |= TF_NoVisibleRowid;
+ ** depend upon the old buggy behavior. The ability can also be toggled
+ ** using sqlite3_config(SQLITE_CONFIG_ROWID_IN_VIEW,...) */
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ p->tabFlags |= sqlite3Config.mNoVisibleRowid; /* Optional. Allow by default */
+#else
+ p->tabFlags |= TF_NoVisibleRowid; /* Never allow rowid in view */
#endif
sqlite3TwoPartName(pParse, pName1, pName2, &pName);
@@ -124890,7 +126883,7 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
if( iDb<0 ) return;
z = sqlite3NameFromToken(db, pObjName);
if( z==0 ) return;
- zDb = db->aDb[iDb].zDbSName;
+ zDb = pName2->n ? db->aDb[iDb].zDbSName : 0;
pTab = sqlite3FindTable(db, z, zDb);
if( pTab ){
reindexTable(pParse, pTab, 0);
@@ -124900,6 +126893,7 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
pIndex = sqlite3FindIndex(db, z, zDb);
sqlite3DbFree(db, z);
if( pIndex ){
+ iDb = sqlite3SchemaToIndex(db, pIndex->pTable->pSchema);
sqlite3BeginWriteOperation(pParse, 0, iDb);
sqlite3RefillIndex(pParse, pIndex, -1);
return;
@@ -125065,6 +127059,9 @@ SQLITE_PRIVATE void sqlite3WithDelete(sqlite3 *db, With *pWith){
sqlite3DbFree(db, pWith);
}
}
+SQLITE_PRIVATE void sqlite3WithDeleteGeneric(sqlite3 *db, void *pWith){
+ sqlite3WithDelete(db, (With*)pWith);
+}
#endif /* !defined(SQLITE_OMIT_CTE) */
/************** End of build.c ***********************************************/
@@ -127746,13 +129743,13 @@ SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue){
double r1, r2;
const char *zVal;
r1 = sqlite3_value_double(pValue);
- sqlite3_str_appendf(pStr, "%!.15g", r1);
+ sqlite3_str_appendf(pStr, "%!0.15g", r1);
zVal = sqlite3_str_value(pStr);
if( zVal ){
sqlite3AtoF(zVal, &r2, pStr->nChar, SQLITE_UTF8);
if( r1!=r2 ){
sqlite3_str_reset(pStr);
- sqlite3_str_appendf(pStr, "%!.20e", r1);
+ sqlite3_str_appendf(pStr, "%!0.20e", r1);
}
}
break;
@@ -127901,7 +129898,8 @@ static void hexFunc(
*(z++) = hexdigits[c&0xf];
}
*z = 0;
- sqlite3_result_text(context, zHex, n*2, sqlite3_free);
+ sqlite3_result_text64(context, zHex, (u64)(z-zHex),
+ sqlite3_free, SQLITE_UTF8);
}
}
@@ -128053,7 +130051,7 @@ static void replaceFunc(
}
if( zPattern[0]==0 ){
assert( sqlite3_value_type(argv[1])!=SQLITE_NULL );
- sqlite3_result_value(context, argv[0]);
+ sqlite3_result_text(context, (const char*)zStr, nStr, SQLITE_TRANSIENT);
return;
}
nPattern = sqlite3_value_bytes(argv[1]);
@@ -128195,6 +130193,81 @@ static void trimFunc(
sqlite3_result_text(context, (char*)zIn, nIn, SQLITE_TRANSIENT);
}
+/* The core implementation of the CONCAT(...) and CONCAT_WS(SEP,...)
+** functions.
+**
+** Return a string value that is the concatenation of all non-null
+** entries in argv[]. Use zSep as the separator.
+*/
+static void concatFuncCore(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv,
+ int nSep,
+ const char *zSep
+){
+ i64 j, k, n = 0;
+ int i;
+ char *z;
+ for(i=0; i<argc; i++){
+ n += sqlite3_value_bytes(argv[i]);
+ }
+ n += (argc-1)*nSep;
+ z = sqlite3_malloc64(n+1);
+ if( z==0 ){
+ sqlite3_result_error_nomem(context);
+ return;
+ }
+ j = 0;
+ for(i=0; i<argc; i++){
+ k = sqlite3_value_bytes(argv[i]);
+ if( k>0 ){
+ const char *v = (const char*)sqlite3_value_text(argv[i]);
+ if( v!=0 ){
+ if( j>0 && nSep>0 ){
+ memcpy(&z[j], zSep, nSep);
+ j += nSep;
+ }
+ memcpy(&z[j], v, k);
+ j += k;
+ }
+ }
+ }
+ z[j] = 0;
+ assert( j<=n );
+ sqlite3_result_text64(context, z, j, sqlite3_free, SQLITE_UTF8);
+}
+
+/*
+** The CONCAT(...) function. Generate a string result that is the
+** concatentation of all non-null arguments.
+*/
+static void concatFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ concatFuncCore(context, argc, argv, 0, "");
+}
+
+/*
+** The CONCAT_WS(separator, ...) function.
+**
+** Generate a string that is the concatenation of 2nd through the Nth
+** argument. Use the first argument (which must be non-NULL) as the
+** separator.
+*/
+static void concatwsFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int nSep = sqlite3_value_bytes(argv[0]);
+ const char *zSep = (const char*)sqlite3_value_text(argv[0]);
+ if( zSep==0 ) return;
+ concatFuncCore(context, argc-1, argv+1, nSep, zSep);
+}
+
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
/*
@@ -128461,7 +130534,7 @@ static void sumFinalize(sqlite3_context *context){
if( p->approx ){
if( p->ovrfl ){
sqlite3_result_error(context,"integer overflow",-1);
- }else if( !sqlite3IsNaN(p->rErr) ){
+ }else if( !sqlite3IsOverflow(p->rErr) ){
sqlite3_result_double(context, p->rSum+p->rErr);
}else{
sqlite3_result_double(context, p->rSum);
@@ -128478,7 +130551,7 @@ static void avgFinalize(sqlite3_context *context){
double r;
if( p->approx ){
r = p->rSum;
- if( !sqlite3IsNaN(p->rErr) ) r += p->rErr;
+ if( !sqlite3IsOverflow(p->rErr) ) r += p->rErr;
}else{
r = (double)(p->iSum);
}
@@ -128492,7 +130565,7 @@ static void totalFinalize(sqlite3_context *context){
if( p ){
if( p->approx ){
r = p->rSum;
- if( !sqlite3IsNaN(p->rErr) ) r += p->rErr;
+ if( !sqlite3IsOverflow(p->rErr) ) r += p->rErr;
}else{
r = (double)(p->iSum);
}
@@ -128616,6 +130689,7 @@ static void minMaxFinalize(sqlite3_context *context){
/*
** group_concat(EXPR, ?SEPARATOR?)
+** string_agg(EXPR, SEPARATOR)
**
** The SEPARATOR goes before the EXPR string. This is tragic. The
** groupConcatInverse() implementation would have been easier if the
@@ -129206,6 +131280,11 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
FUNCTION(hex, 1, 0, 0, hexFunc ),
FUNCTION(unhex, 1, 0, 0, unhexFunc ),
FUNCTION(unhex, 2, 0, 0, unhexFunc ),
+ FUNCTION(concat, -1, 0, 0, concatFunc ),
+ FUNCTION(concat, 0, 0, 0, 0 ),
+ FUNCTION(concat_ws, -1, 0, 0, concatwsFunc ),
+ FUNCTION(concat_ws, 0, 0, 0, 0 ),
+ FUNCTION(concat_ws, 1, 0, 0, 0 ),
INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, 0 ),
VFUNCTION(random, 0, 0, 0, randomFunc ),
VFUNCTION(randomblob, 1, 0, 0, randomBlob ),
@@ -129235,6 +131314,8 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
WAGGREGATE(group_concat, 2, 0, 0, groupConcatStep,
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
+ WAGGREGATE(string_agg, 2, 0, 0, groupConcatStep,
+ groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
#ifdef SQLITE_CASE_SENSITIVE_LIKE
@@ -130177,6 +132258,7 @@ static int isSetNullAction(Parse *pParse, FKey *pFKey){
if( (p==pFKey->apTrigger[0] && pFKey->aAction[0]==OE_SetNull)
|| (p==pFKey->apTrigger[1] && pFKey->aAction[1]==OE_SetNull)
){
+ assert( (pTop->db->flags & SQLITE_FkNoAction)==0 );
return 1;
}
}
@@ -130371,6 +132453,8 @@ SQLITE_PRIVATE void sqlite3FkCheck(
}
if( regOld!=0 ){
int eAction = pFKey->aAction[aChange!=0];
+ if( (db->flags & SQLITE_FkNoAction) ) eAction = OE_None;
+
fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1);
/* If this is a deferred FK constraint, or a CASCADE or SET NULL
** action applies, then any foreign key violations caused by
@@ -130486,7 +132570,11 @@ SQLITE_PRIVATE int sqlite3FkRequired(
/* Check if any parent key columns are being modified. */
for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
if( fkParentIsModified(pTab, p, aChange, chngRowid) ){
- if( p->aAction[1]!=OE_None ) return 2;
+ if( (pParse->db->flags & SQLITE_FkNoAction)==0
+ && p->aAction[1]!=OE_None
+ ){
+ return 2;
+ }
bHaveFK = 1;
}
}
@@ -130536,6 +132624,7 @@ static Trigger *fkActionTrigger(
int iAction = (pChanges!=0); /* 1 for UPDATE, 0 for DELETE */
action = pFKey->aAction[iAction];
+ if( (db->flags & SQLITE_FkNoAction) ) action = OE_None;
if( action==OE_Restrict && (db->flags & SQLITE_DeferFKs) ){
return 0;
}
@@ -131373,6 +133462,195 @@ SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse){
# define autoIncStep(A,B,C)
#endif /* SQLITE_OMIT_AUTOINCREMENT */
+/*
+** If argument pVal is a Select object returned by an sqlite3MultiValues()
+** that was able to use the co-routine optimization, finish coding the
+** co-routine.
+*/
+SQLITE_PRIVATE void sqlite3MultiValuesEnd(Parse *pParse, Select *pVal){
+ if( ALWAYS(pVal) && pVal->pSrc->nSrc>0 ){
+ SrcItem *pItem = &pVal->pSrc->a[0];
+ sqlite3VdbeEndCoroutine(pParse->pVdbe, pItem->regReturn);
+ sqlite3VdbeJumpHere(pParse->pVdbe, pItem->addrFillSub - 1);
+ }
+}
+
+/*
+** Return true if all expressions in the expression-list passed as the
+** only argument are constant.
+*/
+static int exprListIsConstant(Parse *pParse, ExprList *pRow){
+ int ii;
+ for(ii=0; ii<pRow->nExpr; ii++){
+ if( 0==sqlite3ExprIsConstant(pParse, pRow->a[ii].pExpr) ) return 0;
+ }
+ return 1;
+}
+
+/*
+** Return true if all expressions in the expression-list passed as the
+** only argument are both constant and have no affinity.
+*/
+static int exprListIsNoAffinity(Parse *pParse, ExprList *pRow){
+ int ii;
+ if( exprListIsConstant(pParse,pRow)==0 ) return 0;
+ for(ii=0; ii<pRow->nExpr; ii++){
+ Expr *pExpr = pRow->a[ii].pExpr;
+ assert( pExpr->op!=TK_RAISE );
+ assert( pExpr->affExpr==0 );
+ if( 0!=sqlite3ExprAffinity(pExpr) ) return 0;
+ }
+ return 1;
+
+}
+
+/*
+** This function is called by the parser for the second and subsequent
+** rows of a multi-row VALUES clause. Argument pLeft is the part of
+** the VALUES clause already parsed, argument pRow is the vector of values
+** for the new row. The Select object returned represents the complete
+** VALUES clause, including the new row.
+**
+** There are two ways in which this may be achieved - by incremental
+** coding of a co-routine (the "co-routine" method) or by returning a
+** Select object equivalent to the following (the "UNION ALL" method):
+**
+** "pLeft UNION ALL SELECT pRow"
+**
+** If the VALUES clause contains a lot of rows, this compound Select
+** object may consume a lot of memory.
+**
+** When the co-routine method is used, each row that will be returned
+** by the VALUES clause is coded into part of a co-routine as it is
+** passed to this function. The returned Select object is equivalent to:
+**
+** SELECT * FROM (
+** Select object to read co-routine
+** )
+**
+** The co-routine method is used in most cases. Exceptions are:
+**
+** a) If the current statement has a WITH clause. This is to avoid
+** statements like:
+**
+** WITH cte AS ( VALUES('x'), ('y') ... )
+** SELECT * FROM cte AS a, cte AS b;
+**
+** This will not work, as the co-routine uses a hard-coded register
+** for its OP_Yield instructions, and so it is not possible for two
+** cursors to iterate through it concurrently.
+**
+** b) The schema is currently being parsed (i.e. the VALUES clause is part
+** of a schema item like a VIEW or TRIGGER). In this case there is no VM
+** being generated when parsing is taking place, and so generating
+** a co-routine is not possible.
+**
+** c) There are non-constant expressions in the VALUES clause (e.g.
+** the VALUES clause is part of a correlated sub-query).
+**
+** d) One or more of the values in the first row of the VALUES clause
+** has an affinity (i.e. is a CAST expression). This causes problems
+** because the complex rules SQLite uses (see function
+** sqlite3SubqueryColumnTypes() in select.c) to determine the effective
+** affinity of such a column for all rows require access to all values in
+** the column simultaneously.
+*/
+SQLITE_PRIVATE Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList *pRow){
+
+ if( pParse->bHasWith /* condition (a) above */
+ || pParse->db->init.busy /* condition (b) above */
+ || exprListIsConstant(pParse,pRow)==0 /* condition (c) above */
+ || (pLeft->pSrc->nSrc==0 &&
+ exprListIsNoAffinity(pParse,pLeft->pEList)==0) /* condition (d) above */
+ || IN_SPECIAL_PARSE
+ ){
+ /* The co-routine method cannot be used. Fall back to UNION ALL. */
+ Select *pSelect = 0;
+ int f = SF_Values | SF_MultiValue;
+ if( pLeft->pSrc->nSrc ){
+ sqlite3MultiValuesEnd(pParse, pLeft);
+ f = SF_Values;
+ }else if( pLeft->pPrior ){
+ /* In this case set the SF_MultiValue flag only if it was set on pLeft */
+ f = (f & pLeft->selFlags);
+ }
+ pSelect = sqlite3SelectNew(pParse, pRow, 0, 0, 0, 0, 0, f, 0);
+ pLeft->selFlags &= ~SF_MultiValue;
+ if( pSelect ){
+ pSelect->op = TK_ALL;
+ pSelect->pPrior = pLeft;
+ pLeft = pSelect;
+ }
+ }else{
+ SrcItem *p = 0; /* SrcItem that reads from co-routine */
+
+ if( pLeft->pSrc->nSrc==0 ){
+ /* Co-routine has not yet been started and the special Select object
+ ** that accesses the co-routine has not yet been created. This block
+ ** does both those things. */
+ Vdbe *v = sqlite3GetVdbe(pParse);
+ Select *pRet = sqlite3SelectNew(pParse, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ /* Ensure the database schema has been read. This is to ensure we have
+ ** the correct text encoding. */
+ if( (pParse->db->mDbFlags & DBFLAG_SchemaKnownOk)==0 ){
+ sqlite3ReadSchema(pParse);
+ }
+
+ if( pRet ){
+ SelectDest dest;
+ pRet->pSrc->nSrc = 1;
+ pRet->pPrior = pLeft->pPrior;
+ pRet->op = pLeft->op;
+ pLeft->pPrior = 0;
+ pLeft->op = TK_SELECT;
+ assert( pLeft->pNext==0 );
+ assert( pRet->pNext==0 );
+ p = &pRet->pSrc->a[0];
+ p->pSelect = pLeft;
+ p->fg.viaCoroutine = 1;
+ p->addrFillSub = sqlite3VdbeCurrentAddr(v) + 1;
+ p->regReturn = ++pParse->nMem;
+ p->iCursor = -1;
+ p->u1.nRow = 2;
+ sqlite3VdbeAddOp3(v,OP_InitCoroutine,p->regReturn,0,p->addrFillSub);
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, p->regReturn);
+
+ /* Allocate registers for the output of the co-routine. Do so so
+ ** that there are two unused registers immediately before those
+ ** used by the co-routine. This allows the code in sqlite3Insert()
+ ** to use these registers directly, instead of copying the output
+ ** of the co-routine to a separate array for processing. */
+ dest.iSdst = pParse->nMem + 3;
+ dest.nSdst = pLeft->pEList->nExpr;
+ pParse->nMem += 2 + dest.nSdst;
+
+ pLeft->selFlags |= SF_MultiValue;
+ sqlite3Select(pParse, pLeft, &dest);
+ p->regResult = dest.iSdst;
+ assert( pParse->nErr || dest.iSdst>0 );
+ pLeft = pRet;
+ }
+ }else{
+ p = &pLeft->pSrc->a[0];
+ assert( !p->fg.isTabFunc && !p->fg.isIndexedBy );
+ p->u1.nRow++;
+ }
+
+ if( pParse->nErr==0 ){
+ assert( p!=0 );
+ if( p->pSelect->pEList->nExpr!=pRow->nExpr ){
+ sqlite3SelectWrongNumTermsError(pParse, p->pSelect);
+ }else{
+ sqlite3ExprCodeExprList(pParse, pRow, p->regResult, 0, 0);
+ sqlite3VdbeAddOp1(pParse->pVdbe, OP_Yield, p->regReturn);
+ }
+ }
+ sqlite3ExprListDelete(pParse->db, pRow);
+ }
+
+ return pLeft;
+}
/* Forward declaration */
static int xferOptimization(
@@ -131709,25 +133987,40 @@ SQLITE_PRIVATE void sqlite3Insert(
if( pSelect ){
/* Data is coming from a SELECT or from a multi-row VALUES clause.
** Generate a co-routine to run the SELECT. */
- int regYield; /* Register holding co-routine entry-point */
- int addrTop; /* Top of the co-routine */
int rc; /* Result code */
- regYield = ++pParse->nMem;
- addrTop = sqlite3VdbeCurrentAddr(v) + 1;
- sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
- sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
- dest.iSdst = bIdListInOrder ? regData : 0;
- dest.nSdst = pTab->nCol;
- rc = sqlite3Select(pParse, pSelect, &dest);
- regFromSelect = dest.iSdst;
- assert( db->pParse==pParse );
- if( rc || pParse->nErr ) goto insert_cleanup;
- assert( db->mallocFailed==0 );
- sqlite3VdbeEndCoroutine(v, regYield);
- sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */
- assert( pSelect->pEList );
- nColumn = pSelect->pEList->nExpr;
+ if( pSelect->pSrc->nSrc==1
+ && pSelect->pSrc->a[0].fg.viaCoroutine
+ && pSelect->pPrior==0
+ ){
+ SrcItem *pItem = &pSelect->pSrc->a[0];
+ dest.iSDParm = pItem->regReturn;
+ regFromSelect = pItem->regResult;
+ nColumn = pItem->pSelect->pEList->nExpr;
+ ExplainQueryPlan((pParse, 0, "SCAN %S", pItem));
+ if( bIdListInOrder && nColumn==pTab->nCol ){
+ regData = regFromSelect;
+ regRowid = regData - 1;
+ regIns = regRowid - (IsVirtual(pTab) ? 1 : 0);
+ }
+ }else{
+ int addrTop; /* Top of the co-routine */
+ int regYield = ++pParse->nMem;
+ addrTop = sqlite3VdbeCurrentAddr(v) + 1;
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
+ dest.iSdst = bIdListInOrder ? regData : 0;
+ dest.nSdst = pTab->nCol;
+ rc = sqlite3Select(pParse, pSelect, &dest);
+ regFromSelect = dest.iSdst;
+ assert( db->pParse==pParse );
+ if( rc || pParse->nErr ) goto insert_cleanup;
+ assert( db->mallocFailed==0 );
+ sqlite3VdbeEndCoroutine(v, regYield);
+ sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */
+ assert( pSelect->pEList );
+ nColumn = pSelect->pEList->nExpr;
+ }
/* Set useTempTable to TRUE if the result of the SELECT statement
** should be written into a temporary table (template 4). Set to
@@ -131882,7 +134175,7 @@ SQLITE_PRIVATE void sqlite3Insert(
pNx->iDataCur = iDataCur;
pNx->iIdxCur = iIdxCur;
if( pNx->pUpsertTarget ){
- if( sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx) ){
+ if( sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx, pUpsert) ){
goto insert_cleanup;
}
}
@@ -133774,7 +136067,10 @@ static int xferOptimization(
}
}
#ifndef SQLITE_OMIT_CHECK
- if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ){
+ if( pDest->pCheck
+ && (db->mDbFlags & DBFLAG_Vacuum)==0
+ && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1)
+ ){
return 0; /* Tables have different CHECK constraints. Ticket #2252 */
}
#endif
@@ -134491,6 +136787,9 @@ struct sqlite3_api_routines {
int (*is_interrupted)(sqlite3*);
/* Version 3.43.0 and later */
int (*stmt_explain)(sqlite3_stmt*,int);
+ /* Version 3.44.0 and later */
+ void *(*get_clientdata)(sqlite3*,const char*);
+ int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
};
/*
@@ -134821,6 +137120,9 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_is_interrupted sqlite3_api->is_interrupted
/* Version 3.43.0 and later */
#define sqlite3_stmt_explain sqlite3_api->stmt_explain
+/* Version 3.44.0 and later */
+#define sqlite3_get_clientdata sqlite3_api->get_clientdata
+#define sqlite3_set_clientdata sqlite3_api->set_clientdata
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
@@ -135339,7 +137641,10 @@ static const sqlite3_api_routines sqlite3Apis = {
/* Version 3.41.0 and later */
sqlite3_is_interrupted,
/* Version 3.43.0 and later */
- sqlite3_stmt_explain
+ sqlite3_stmt_explain,
+ /* Version 3.44.0 and later */
+ sqlite3_get_clientdata,
+ sqlite3_set_clientdata
};
/* True if x is the directory separator character
@@ -135555,6 +137860,9 @@ SQLITE_PRIVATE void sqlite3CloseExtensions(sqlite3 *db){
** default so as not to open security holes in older applications.
*/
SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
if( onoff ){
db->flags |= SQLITE_LoadExtension|SQLITE_LoadExtFunc;
@@ -135604,6 +137912,9 @@ SQLITE_API int sqlite3_auto_extension(
void (*xInit)(void)
){
int rc = SQLITE_OK;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( xInit==0 ) return SQLITE_MISUSE_BKPT;
+#endif
#ifndef SQLITE_OMIT_AUTOINIT
rc = sqlite3_initialize();
if( rc ){
@@ -135656,6 +137967,9 @@ SQLITE_API int sqlite3_cancel_auto_extension(
int i;
int n = 0;
wsdAutoextInit;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( xInit==0 ) return 0;
+#endif
sqlite3_mutex_enter(mutex);
for(i=(int)wsdAutoext.nExt-1; i>=0; i--){
if( wsdAutoext.aExt[i]==xInit ){
@@ -136432,6 +138746,34 @@ static const PragmaName aPragmaName[] = {
/************** Continuing where we left off in pragma.c *********************/
/*
+** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands
+** will be run with an analysis_limit set to the lessor of the value of
+** the following macro or to the actual analysis_limit if it is non-zero,
+** in order to prevent PRAGMA optimize from running for too long.
+**
+** The value of 2000 is chosen emperically so that the worst-case run-time
+** for PRAGMA optimize does not exceed 100 milliseconds against a variety
+** of test databases on a RaspberryPI-4 compiled using -Os and without
+** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of
+** this paragraph, "worst-case" means that ANALYZE ends up being
+** run on every table in the database. The worst case typically only
+** happens if PRAGMA optimize is run on a database file for which ANALYZE
+** has not been previously run and the 0x10000 flag is included so that
+** all tables are analyzed. The usual case for PRAGMA optimize is that
+** no ANALYZE commands will be run at all, or if any ANALYZE happens it
+** will be against a single table, so that expected timing for PRAGMA
+** optimize on a PI-4 is more like 1 millisecond or less with the 0x10000
+** flag or less than 100 microseconds without the 0x10000 flag.
+**
+** An analysis limit of 2000 is almost always sufficient for the query
+** planner to fully characterize an index. The additional accuracy from
+** a larger analysis is not usually helpful.
+*/
+#ifndef SQLITE_DEFAULT_OPTIMIZE_LIMIT
+# define SQLITE_DEFAULT_OPTIMIZE_LIMIT 2000
+#endif
+
+/*
** Interpret the given string as a safety level. Return 0 for OFF,
** 1 for ON or NORMAL, 2 for FULL, and 3 for EXTRA. Return 1 for an empty or
** unrecognized string argument. The FULL and EXTRA option is disallowed
@@ -137525,7 +139867,11 @@ SQLITE_PRIVATE void sqlite3Pragma(
#endif
if( sqlite3GetBoolean(zRight, 0) ){
- db->flags |= mask;
+ if( (mask & SQLITE_WriteSchema)==0
+ || (db->flags & SQLITE_Defensive)==0
+ ){
+ db->flags |= mask;
+ }
}else{
db->flags &= ~mask;
if( mask==SQLITE_DeferFKs ) db->nDeferredImmCons = 0;
@@ -138072,7 +140418,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
/* Set the maximum error count */
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
if( zRight ){
- if( sqlite3GetInt32(zRight, &mxErr) ){
+ if( sqlite3GetInt32(pValue->z, &mxErr) ){
if( mxErr<=0 ){
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
}
@@ -138089,7 +140435,6 @@ SQLITE_PRIVATE void sqlite3Pragma(
Hash *pTbls; /* Set of all tables in the schema */
int *aRoot; /* Array of root page numbers of all btrees */
int cnt = 0; /* Number of entries in aRoot[] */
- int mxIdx = 0; /* Maximum number of indexes for any table */
if( OMIT_TEMPDB && i==1 ) continue;
if( iDb>=0 && i!=iDb ) continue;
@@ -138111,7 +140456,6 @@ SQLITE_PRIVATE void sqlite3Pragma(
if( pObjTab && pObjTab!=pTab ) continue;
if( HasRowid(pTab) ) cnt++;
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; }
- if( nIdx>mxIdx ) mxIdx = nIdx;
}
if( cnt==0 ) continue;
if( pObjTab ) cnt++;
@@ -138131,11 +140475,11 @@ SQLITE_PRIVATE void sqlite3Pragma(
aRoot[0] = cnt;
/* Make sure sufficient number of registers have been allocated */
- sqlite3TouchRegister(pParse, 8+mxIdx);
+ sqlite3TouchRegister(pParse, 8+cnt);
sqlite3ClearTempRegCache(pParse);
/* Do the b-tree integrity checks */
- sqlite3VdbeAddOp4(v, OP_IntegrityCk, 2, cnt, 1, (char*)aRoot,P4_INTARRAY);
+ sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY);
sqlite3VdbeChangeP5(v, (u8)i);
addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v);
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
@@ -138145,6 +140489,36 @@ SQLITE_PRIVATE void sqlite3Pragma(
integrityCheckResultRow(v);
sqlite3VdbeJumpHere(v, addr);
+ /* Check that the indexes all have the right number of rows */
+ cnt = pObjTab ? 1 : 0;
+ sqlite3VdbeLoadString(v, 2, "wrong # of entries in index ");
+ for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
+ int iTab = 0;
+ Table *pTab = sqliteHashData(x);
+ Index *pIdx;
+ if( pObjTab && pObjTab!=pTab ) continue;
+ if( HasRowid(pTab) ){
+ iTab = cnt++;
+ }else{
+ iTab = cnt;
+ for(pIdx=pTab->pIndex; ALWAYS(pIdx); pIdx=pIdx->pNext){
+ if( IsPrimaryKeyIndex(pIdx) ) break;
+ iTab++;
+ }
+ }
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( pIdx->pPartIdxWhere==0 ){
+ addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+cnt, 0, 8+iTab);
+ VdbeCoverageNeverNull(v);
+ sqlite3VdbeLoadString(v, 4, pIdx->zName);
+ sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3);
+ integrityCheckResultRow(v);
+ sqlite3VdbeJumpHere(v, addr);
+ }
+ cnt++;
+ }
+ }
+
/* Make sure all the indices are constructed correctly.
*/
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
@@ -138158,8 +140532,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
int r2; /* Previous key for WITHOUT ROWID tables */
int mxCol; /* Maximum non-virtual column number */
- if( !IsOrdinaryTable(pTab) ) continue;
if( pObjTab && pObjTab!=pTab ) continue;
+ if( !IsOrdinaryTable(pTab) ) continue;
if( isQuick || HasRowid(pTab) ){
pPk = 0;
r2 = 0;
@@ -138294,6 +140668,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** is REAL, we have to load the actual data using OP_Column
** to reliably determine if the value is a NULL. */
sqlite3VdbeAddOp3(v, OP_Column, p1, p3, 3);
+ sqlite3ColumnDefault(v, pTab, j, 3);
jmp3 = sqlite3VdbeAddOp2(v, OP_NotNull, 3, labelOk);
VdbeCoverage(v);
}
@@ -138467,23 +140842,43 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, loopTop-1);
- if( !isQuick ){
- sqlite3VdbeLoadString(v, 2, "wrong # of entries in index ");
- for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
- if( pPk==pIdx ) continue;
- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3);
- addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+j, 0, 3); VdbeCoverage(v);
- sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
- sqlite3VdbeLoadString(v, 4, pIdx->zName);
- sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3);
- integrityCheckResultRow(v);
- sqlite3VdbeJumpHere(v, addr);
- }
- if( pPk ){
- sqlite3ReleaseTempRange(pParse, r2, pPk->nKeyCol);
- }
+ if( pPk ){
+ assert( !isQuick );
+ sqlite3ReleaseTempRange(pParse, r2, pPk->nKeyCol);
}
}
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ /* Second pass to invoke the xIntegrity method on all virtual
+ ** tables.
+ */
+ for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
+ Table *pTab = sqliteHashData(x);
+ sqlite3_vtab *pVTab;
+ int a1;
+ if( pObjTab && pObjTab!=pTab ) continue;
+ if( IsOrdinaryTable(pTab) ) continue;
+ if( !IsVirtual(pTab) ) continue;
+ if( pTab->nCol<=0 ){
+ const char *zMod = pTab->u.vtab.azArg[0];
+ if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue;
+ }
+ sqlite3ViewGetColumnNames(pParse, pTab);
+ if( pTab->u.vtab.p==0 ) continue;
+ pVTab = pTab->u.vtab.p->pVtab;
+ if( NEVER(pVTab==0) ) continue;
+ if( NEVER(pVTab->pModule==0) ) continue;
+ if( pVTab->pModule->iVersion<4 ) continue;
+ if( pVTab->pModule->xIntegrity==0 ) continue;
+ sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick);
+ pTab->nTabRef++;
+ sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF);
+ a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v);
+ integrityCheckResultRow(v);
+ sqlite3VdbeJumpHere(v, a1);
+ continue;
+ }
+#endif
}
{
static const int iLn = VDBE_OFFSET_LINENO(2);
@@ -138747,44 +141142,63 @@ SQLITE_PRIVATE void sqlite3Pragma(
**
** The optional argument is a bitmask of optimizations to perform:
**
- ** 0x0001 Debugging mode. Do not actually perform any optimizations
- ** but instead return one line of text for each optimization
- ** that would have been done. Off by default.
+ ** 0x00001 Debugging mode. Do not actually perform any optimizations
+ ** but instead return one line of text for each optimization
+ ** that would have been done. Off by default.
**
- ** 0x0002 Run ANALYZE on tables that might benefit. On by default.
- ** See below for additional information.
+ ** 0x00002 Run ANALYZE on tables that might benefit. On by default.
+ ** See below for additional information.
**
- ** 0x0004 (Not yet implemented) Record usage and performance
- ** information from the current session in the
- ** database file so that it will be available to "optimize"
- ** pragmas run by future database connections.
+ ** 0x00010 Run all ANALYZE operations using an analysis_limit that
+ ** is the lessor of the current analysis_limit and the
+ ** SQLITE_DEFAULT_OPTIMIZE_LIMIT compile-time option.
+ ** The default value of SQLITE_DEFAULT_OPTIMIZE_LIMIT is
+ ** currently (2024-02-19) set to 2000, which is such that
+ ** the worst case run-time for PRAGMA optimize on a 100MB
+ ** database will usually be less than 100 milliseconds on
+ ** a RaspberryPI-4 class machine. On by default.
**
- ** 0x0008 (Not yet implemented) Create indexes that might have
- ** been helpful to recent queries
+ ** 0x10000 Look at tables to see if they need to be reanalyzed
+ ** due to growth or shrinkage even if they have not been
+ ** queried during the current connection. Off by default.
**
- ** The default MASK is and always shall be 0xfffe. 0xfffe means perform all
- ** of the optimizations listed above except Debug Mode, including new
- ** optimizations that have not yet been invented. If new optimizations are
- ** ever added that should be off by default, those off-by-default
- ** optimizations will have bitmasks of 0x10000 or larger.
+ ** The default MASK is and always shall be 0x0fffe. In the current
+ ** implementation, the default mask only covers the 0x00002 optimization,
+ ** though additional optimizations that are covered by 0x0fffe might be
+ ** added in the future. Optimizations that are off by default and must
+ ** be explicitly requested have masks of 0x10000 or greater.
**
** DETERMINATION OF WHEN TO RUN ANALYZE
**
** In the current implementation, a table is analyzed if only if all of
** the following are true:
**
- ** (1) MASK bit 0x02 is set.
+ ** (1) MASK bit 0x00002 is set.
**
- ** (2) The query planner used sqlite_stat1-style statistics for one or
- ** more indexes of the table at some point during the lifetime of
- ** the current connection.
+ ** (2) The table is an ordinary table, not a virtual table or view.
**
- ** (3) One or more indexes of the table are currently unanalyzed OR
- ** the number of rows in the table has increased by 25 times or more
- ** since the last time ANALYZE was run.
+ ** (3) The table name does not begin with "sqlite_".
+ **
+ ** (4) One or more of the following is true:
+ ** (4a) The 0x10000 MASK bit is set.
+ ** (4b) One or more indexes on the table lacks an entry
+ ** in the sqlite_stat1 table.
+ ** (4c) The query planner used sqlite_stat1-style statistics for one
+ ** or more indexes of the table at some point during the lifetime
+ ** of the current connection.
+ **
+ ** (5) One or more of the following is true:
+ ** (5a) One or more indexes on the table lacks an entry
+ ** in the sqlite_stat1 table. (Same as 4a)
+ ** (5b) The number of rows in the table has increased or decreased by
+ ** 10-fold. In other words, the current size of the table is
+ ** 10 times larger than the size in sqlite_stat1 or else the
+ ** current size is less than 1/10th the size in sqlite_stat1.
**
** The rules for when tables are analyzed are likely to change in
- ** future releases.
+ ** future releases. Future versions of SQLite might accept a string
+ ** literal argument to this pragma that contains a mnemonic description
+ ** of the options rather than a bitmap.
*/
case PragTyp_OPTIMIZE: {
int iDbLast; /* Loop termination point for the schema loop */
@@ -138796,6 +141210,10 @@ SQLITE_PRIVATE void sqlite3Pragma(
LogEst szThreshold; /* Size threshold above which reanalysis needed */
char *zSubSql; /* SQL statement for the OP_SqlExec opcode */
u32 opMask; /* Mask of operations to perform */
+ int nLimit; /* Analysis limit to use */
+ int nCheck = 0; /* Number of tables to be optimized */
+ int nBtree = 0; /* Number of btrees to scan */
+ int nIndex; /* Number of indexes on the current table */
if( zRight ){
opMask = (u32)sqlite3Atoi(zRight);
@@ -138803,6 +141221,14 @@ SQLITE_PRIVATE void sqlite3Pragma(
}else{
opMask = 0xfffe;
}
+ if( (opMask & 0x10)==0 ){
+ nLimit = 0;
+ }else if( db->nAnalysisLimit>0
+ && db->nAnalysisLimit<SQLITE_DEFAULT_OPTIMIZE_LIMIT ){
+ nLimit = 0;
+ }else{
+ nLimit = SQLITE_DEFAULT_OPTIMIZE_LIMIT;
+ }
iTabCur = pParse->nTab++;
for(iDbLast = zDb?iDb:db->nDb-1; iDb<=iDbLast; iDb++){
if( iDb==1 ) continue;
@@ -138811,23 +141237,61 @@ SQLITE_PRIVATE void sqlite3Pragma(
for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
pTab = (Table*)sqliteHashData(k);
- /* If table pTab has not been used in a way that would benefit from
- ** having analysis statistics during the current session, then skip it.
- ** This also has the effect of skipping virtual tables and views */
- if( (pTab->tabFlags & TF_StatsUsed)==0 ) continue;
+ /* This only works for ordinary tables */
+ if( !IsOrdinaryTable(pTab) ) continue;
+
+ /* Do not scan system tables */
+ if( 0==sqlite3StrNICmp(pTab->zName, "sqlite_", 7) ) continue;
- /* Reanalyze if the table is 25 times larger than the last analysis */
- szThreshold = pTab->nRowLogEst + 46; assert( sqlite3LogEst(25)==46 );
+ /* Find the size of the table as last recorded in sqlite_stat1.
+ ** If any index is unanalyzed, then the threshold is -1 to
+ ** indicate a new, unanalyzed index
+ */
+ szThreshold = pTab->nRowLogEst;
+ nIndex = 0;
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ nIndex++;
if( !pIdx->hasStat1 ){
- szThreshold = 0; /* Always analyze if any index lacks statistics */
- break;
+ szThreshold = -1; /* Always analyze if any index lacks statistics */
}
}
- if( szThreshold ){
- sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
- sqlite3VdbeAddOp3(v, OP_IfSmaller, iTabCur,
- sqlite3VdbeCurrentAddr(v)+2+(opMask&1), szThreshold);
+
+ /* If table pTab has not been used in a way that would benefit from
+ ** having analysis statistics during the current session, then skip it,
+ ** unless the 0x10000 MASK bit is set. */
+ if( (pTab->tabFlags & TF_MaybeReanalyze)!=0 ){
+ /* Check for size change if stat1 has been used for a query */
+ }else if( opMask & 0x10000 ){
+ /* Check for size change if 0x10000 is set */
+ }else if( pTab->pIndex!=0 && szThreshold<0 ){
+ /* Do analysis if unanalyzed indexes exists */
+ }else{
+ /* Otherwise, we can skip this table */
+ continue;
+ }
+
+ nCheck++;
+ if( nCheck==2 ){
+ /* If ANALYZE might be invoked two or more times, hold a write
+ ** transaction for efficiency */
+ sqlite3BeginWriteOperation(pParse, 0, iDb);
+ }
+ nBtree += nIndex+1;
+
+ /* Reanalyze if the table is 10 times larger or smaller than
+ ** the last analysis. Unconditional reanalysis if there are
+ ** unanalyzed indexes. */
+ sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
+ if( szThreshold>=0 ){
+ const LogEst iRange = 33; /* 10x size change */
+ sqlite3VdbeAddOp4Int(v, OP_IfSizeBetween, iTabCur,
+ sqlite3VdbeCurrentAddr(v)+2+(opMask&1),
+ szThreshold>=iRange ? szThreshold-iRange : -1,
+ szThreshold+iRange);
+ VdbeCoverage(v);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Rewind, iTabCur,
+ sqlite3VdbeCurrentAddr(v)+2+(opMask&1));
VdbeCoverage(v);
}
zSubSql = sqlite3MPrintf(db, "ANALYZE \"%w\".\"%w\"",
@@ -138837,11 +141301,27 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3VdbeAddOp4(v, OP_String8, 0, r1, 0, zSubSql, P4_DYNAMIC);
sqlite3VdbeAddOp2(v, OP_ResultRow, r1, 1);
}else{
- sqlite3VdbeAddOp4(v, OP_SqlExec, 0, 0, 0, zSubSql, P4_DYNAMIC);
+ sqlite3VdbeAddOp4(v, OP_SqlExec, nLimit ? 0x02 : 00, nLimit, 0,
+ zSubSql, P4_DYNAMIC);
}
}
}
sqlite3VdbeAddOp0(v, OP_Expire);
+
+ /* In a schema with a large number of tables and indexes, scale back
+ ** the analysis_limit to avoid excess run-time in the worst case.
+ */
+ if( !db->mallocFailed && nLimit>0 && nBtree>100 ){
+ int iAddr, iEnd;
+ VdbeOp *aOp;
+ nLimit = 100*nLimit/nBtree;
+ if( nLimit<100 ) nLimit = 100;
+ aOp = sqlite3VdbeGetOp(v, 0);
+ iEnd = sqlite3VdbeCurrentAddr(v);
+ for(iAddr=0; iAddr<iEnd; iAddr++){
+ if( aOp[iAddr].opcode==OP_SqlExec ) aOp[iAddr].p2 = nLimit;
+ }
+ }
break;
}
@@ -139105,9 +141585,9 @@ static int pragmaVtabBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
seen[0] = 0;
seen[1] = 0;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
- if( pConstraint->usable==0 ) continue;
- if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
if( pConstraint->iColumn < pTab->iHidden ) continue;
+ if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
+ if( pConstraint->usable==0 ) return SQLITE_CONSTRAINT;
j = pConstraint->iColumn - pTab->iHidden;
assert( j < 2 );
seen[j] = i+1;
@@ -139120,12 +141600,13 @@ static int pragmaVtabBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
j = seen[0]-1;
pIdxInfo->aConstraintUsage[j].argvIndex = 1;
pIdxInfo->aConstraintUsage[j].omit = 1;
- if( seen[1]==0 ) return SQLITE_OK;
pIdxInfo->estimatedCost = (double)20;
pIdxInfo->estimatedRows = 20;
- j = seen[1]-1;
- pIdxInfo->aConstraintUsage[j].argvIndex = 2;
- pIdxInfo->aConstraintUsage[j].omit = 1;
+ if( seen[1] ){
+ j = seen[1]-1;
+ pIdxInfo->aConstraintUsage[j].argvIndex = 2;
+ pIdxInfo->aConstraintUsage[j].omit = 1;
+ }
return SQLITE_OK;
}
@@ -139145,6 +141626,7 @@ static void pragmaVtabCursorClear(PragmaVtabCursor *pCsr){
int i;
sqlite3_finalize(pCsr->pPragma);
pCsr->pPragma = 0;
+ pCsr->iRowid = 0;
for(i=0; i<ArraySize(pCsr->azArg); i++){
sqlite3_free(pCsr->azArg[i]);
pCsr->azArg[i] = 0;
@@ -139285,7 +141767,8 @@ static const sqlite3_module pragmaVtabModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
/*
@@ -139909,8 +142392,6 @@ SQLITE_PRIVATE void sqlite3ParseObjectReset(Parse *pParse){
db->lookaside.sz = db->lookaside.bDisable ? 0 : db->lookaside.szTrue;
assert( pParse->db->pParse==pParse );
db->pParse = pParse->pOuterParse;
- pParse->db = 0;
- pParse->disableLookaside = 0;
}
/*
@@ -139946,7 +142427,13 @@ SQLITE_PRIVATE void *sqlite3ParserAddCleanup(
void (*xCleanup)(sqlite3*,void*), /* The cleanup routine */
void *pPtr /* Pointer to object to be cleaned up */
){
- ParseCleanup *pCleanup = sqlite3DbMallocRaw(pParse->db, sizeof(*pCleanup));
+ ParseCleanup *pCleanup;
+ if( sqlite3FaultSim(300) ){
+ pCleanup = 0;
+ sqlite3OomFault(pParse->db);
+ }else{
+ pCleanup = sqlite3DbMallocRaw(pParse->db, sizeof(*pCleanup));
+ }
if( pCleanup ){
pCleanup->pNext = pParse->pCleanup;
pParse->pCleanup = pCleanup;
@@ -140181,6 +142668,7 @@ static int sqlite3LockAndPrepare(
assert( (rc&db->errMask)==rc );
db->busyHandler.nBusy = 0;
sqlite3_mutex_leave(db->mutex);
+ assert( rc==SQLITE_OK || (*ppStmt)==0 );
return rc;
}
@@ -140578,6 +143066,9 @@ SQLITE_PRIVATE Select *sqlite3SelectNew(
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){
if( OK_IF_ALWAYS_TRUE(p) ) clearSelect(db, p, 1);
}
+SQLITE_PRIVATE void sqlite3SelectDeleteGeneric(sqlite3 *db, void *p){
+ if( ALWAYS(p) ) clearSelect(db, (Select*)p, 1);
+}
/*
** Return a pointer to the right-most SELECT statement in a compound.
@@ -140848,6 +143339,7 @@ static void unsetJoinExpr(Expr *p, int iTable, int nullable){
}
if( p->op==TK_FUNCTION ){
assert( ExprUseXList(p) );
+ assert( p->pLeft==0 );
if( p->x.pList ){
int i;
for(i=0; i<p->x.pList->nExpr; i++){
@@ -142063,9 +144555,16 @@ static void generateSortTail(
int addrExplain; /* Address of OP_Explain instruction */
#endif
- ExplainQueryPlan2(addrExplain, (pParse, 0,
- "USE TEMP B-TREE FOR %sORDER BY", pSort->nOBSat>0?"RIGHT PART OF ":"")
- );
+ nKey = pOrderBy->nExpr - pSort->nOBSat;
+ if( pSort->nOBSat==0 || nKey==1 ){
+ ExplainQueryPlan2(addrExplain, (pParse, 0,
+ "USE TEMP B-TREE FOR %sORDER BY", pSort->nOBSat?"LAST TERM OF ":""
+ ));
+ }else{
+ ExplainQueryPlan2(addrExplain, (pParse, 0,
+ "USE TEMP B-TREE FOR LAST %d TERMS OF ORDER BY", nKey
+ ));
+ }
sqlite3VdbeScanStatusRange(v, addrExplain,pSort->addrPush,pSort->addrPushEnd);
sqlite3VdbeScanStatusCounters(v, addrExplain, addrExplain, pSort->addrPush);
@@ -142103,7 +144602,6 @@ static void generateSortTail(
regRow = sqlite3GetTempRange(pParse, nColumn);
}
}
- nKey = pOrderBy->nExpr - pSort->nOBSat;
if( pSort->sortFlags & SORTFLAG_UseSorter ){
int regSortOut = ++pParse->nMem;
iSortTab = pParse->nTab++;
@@ -142343,11 +144841,7 @@ static const char *columnTypeImpl(
** data for the result-set column of the sub-select.
*/
if( iCol<pS->pEList->nExpr
-#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
- && iCol>=0
-#else
- && ALWAYS(iCol>=0)
-#endif
+ && (!ViewCanHaveRowid || iCol>=0)
){
/* If iCol is less than zero, then the expression requests the
** rowid of the sub-select or view. This expression is legal (see
@@ -142723,17 +145217,22 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
const char *zType;
i64 n;
+ int m = 0;
+ Select *pS2 = pSelect;
pTab->tabFlags |= (pCol->colFlags & COLFLAG_NOINSERT);
p = a[i].pExpr;
/* pCol->szEst = ... // Column size est for SELECT tables never used */
pCol->affinity = sqlite3ExprAffinity(p);
+ while( pCol->affinity<=SQLITE_AFF_NONE && pS2->pNext!=0 ){
+ m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr);
+ pS2 = pS2->pNext;
+ pCol->affinity = sqlite3ExprAffinity(pS2->pEList->a[i].pExpr);
+ }
if( pCol->affinity<=SQLITE_AFF_NONE ){
pCol->affinity = aff;
}
- if( pCol->affinity>=SQLITE_AFF_TEXT && pSelect->pNext ){
- int m = 0;
- Select *pS2;
- for(m=0, pS2=pSelect->pNext; pS2; pS2=pS2->pNext){
+ if( pCol->affinity>=SQLITE_AFF_TEXT && (pS2->pNext || pS2!=pSelect) ){
+ for(pS2=pS2->pNext; pS2; pS2=pS2->pNext){
m |= sqlite3ExprDataType(pS2->pEList->a[i].pExpr);
}
if( pCol->affinity==SQLITE_AFF_TEXT && (m&0x01)!=0 ){
@@ -142763,12 +145262,12 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(
}
}
if( zType ){
- i64 m = sqlite3Strlen30(zType);
+ const i64 k = sqlite3Strlen30(zType);
n = sqlite3Strlen30(pCol->zCnName);
- pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+m+2);
+ pCol->zCnName = sqlite3DbReallocOrFree(db, pCol->zCnName, n+k+2);
pCol->colFlags &= ~(COLFLAG_HASTYPE|COLFLAG_HASCOLL);
if( pCol->zCnName ){
- memcpy(&pCol->zCnName[n+1], zType, m+1);
+ memcpy(&pCol->zCnName[n+1], zType, k+1);
pCol->colFlags |= COLFLAG_HASTYPE;
}
}
@@ -143596,9 +146095,7 @@ multi_select_end:
pDest->iSdst = dest.iSdst;
pDest->nSdst = dest.nSdst;
if( pDelete ){
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3SelectDelete,
- pDelete);
+ sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pDelete);
}
return rc;
}
@@ -144149,8 +146646,7 @@ static int multiSelectOrderBy(
/* Make arrangements to free the 2nd and subsequent arms of the compound
** after the parse has finished */
if( pSplit->pPrior ){
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3SelectDelete, pSplit->pPrior);
+ sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pSplit->pPrior);
}
pSplit->pPrior = pPrior;
pPrior->pNext = pSplit;
@@ -144971,9 +147467,7 @@ static int flattenSubquery(
Table *pTabToDel = pSubitem->pTab;
if( pTabToDel->nTabRef==1 ){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
- sqlite3ParserAddCleanup(pToplevel,
- (void(*)(sqlite3*,void*))sqlite3DeleteTable,
- pTabToDel);
+ sqlite3ParserAddCleanup(pToplevel, sqlite3DeleteTableGeneric, pTabToDel);
testcase( pToplevel->earlyCleanup );
}else{
pTabToDel->nTabRef--;
@@ -145170,7 +147664,7 @@ static void constInsert(
){
int i;
assert( pColumn->op==TK_COLUMN );
- assert( sqlite3ExprIsConstant(pValue) );
+ assert( sqlite3ExprIsConstant(pConst->pParse, pValue) );
if( ExprHasProperty(pColumn, EP_FixedCol) ) return;
if( sqlite3ExprAffinity(pValue)!=0 ) return;
@@ -145228,10 +147722,10 @@ static void findConstInWhere(WhereConst *pConst, Expr *pExpr){
pLeft = pExpr->pLeft;
assert( pRight!=0 );
assert( pLeft!=0 );
- if( pRight->op==TK_COLUMN && sqlite3ExprIsConstant(pLeft) ){
+ if( pRight->op==TK_COLUMN && sqlite3ExprIsConstant(pConst->pParse, pLeft) ){
constInsert(pConst,pRight,pLeft,pExpr);
}
- if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pRight) ){
+ if( pLeft->op==TK_COLUMN && sqlite3ExprIsConstant(pConst->pParse, pRight) ){
constInsert(pConst,pLeft,pRight,pExpr);
}
}
@@ -145452,6 +147946,18 @@ static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){
** The hope is that the terms added to the inner query will make it more
** efficient.
**
+** NAME AMBIGUITY
+**
+** This optimization is called the "WHERE-clause push-down optimization".
+**
+** Do not confuse this optimization with another unrelated optimization
+** with a similar name: The "MySQL push-down optimization" causes WHERE
+** clause terms that can be evaluated using only the index and without
+** reference to the table are run first, so that if they are false,
+** unnecessary table seeks are avoided.
+**
+** RULES
+**
** Do not attempt this optimization if:
**
** (1) (** This restriction was removed on 2017-09-29. We used to
@@ -145517,15 +148023,19 @@ static int pushDownWindowCheck(Parse *pParse, Select *pSubq, Expr *pExpr){
** (9c) There is a RIGHT JOIN (or FULL JOIN) in between the ON/USING
** clause and the subquery.
**
-** Without this restriction, the push-down optimization might move
-** the ON/USING filter expression from the left side of a RIGHT JOIN
-** over to the right side, which leads to incorrect answers. See
-** also restriction (6) in sqlite3ExprIsSingleTableConstraint().
+** Without this restriction, the WHERE-clause push-down optimization
+** might move the ON/USING filter expression from the left side of a
+** RIGHT JOIN over to the right side, which leads to incorrect answers.
+** See also restriction (6) in sqlite3ExprIsSingleTableConstraint().
**
** (10) The inner query is not the right-hand table of a RIGHT JOIN.
**
** (11) The subquery is not a VALUES clause
**
+** (12) The WHERE clause is not "rowid ISNULL" or the equivalent. This
+** case only comes up if SQLite is compiled using
+** SQLITE_ALLOW_ROWID_IN_VIEW.
+**
** Return 0 if no changes are made and non-zero if one or more WHERE clause
** terms are duplicated into the subquery.
*/
@@ -145636,7 +148146,19 @@ static int pushDownWhereTerms(
}
#endif
- if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc) ){
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ if( ViewCanHaveRowid && (pWhere->op==TK_ISNULL || pWhere->op==TK_NOTNULL) ){
+ Expr *pLeft = pWhere->pLeft;
+ if( ALWAYS(pLeft)
+ && pLeft->op==TK_COLUMN
+ && pLeft->iColumn < 0
+ ){
+ return 0; /* Restriction (12) */
+ }
+ }
+#endif
+
+ if( sqlite3ExprIsSingleTableConstraint(pWhere, pSrcList, iSrc, 1) ){
nChng++;
pSubq->selFlags |= SF_PushDown;
while( pSubq ){
@@ -146020,8 +148542,7 @@ static struct Cte *searchWith(
SQLITE_PRIVATE With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
if( pWith ){
if( bFree ){
- pWith = (With*)sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3WithDelete,
+ pWith = (With*)sqlite3ParserAddCleanup(pParse, sqlite3WithDeleteGeneric,
pWith);
if( pWith==0 ) return 0;
}
@@ -146264,12 +148785,14 @@ SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse *pParse, SrcItem *pFrom){
while( pSel->pPrior ){ pSel = pSel->pPrior; }
sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol);
pTab->iPKey = -1;
+ pTab->eTabType = TABTYP_VIEW;
pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
#ifndef SQLITE_ALLOW_ROWID_IN_VIEW
/* The usual case - do not allow ROWID on a subquery */
pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid;
#else
- pTab->tabFlags |= TF_Ephemeral; /* Legacy compatibility mode */
+ /* Legacy compatibility mode */
+ pTab->tabFlags |= TF_Ephemeral | sqlite3Config.mNoVisibleRowid;
#endif
return pParse->nErr ? SQLITE_ERROR : SQLITE_OK;
}
@@ -146508,6 +149031,7 @@ static int selectExpander(Walker *pWalker, Select *p){
char *zTName = 0; /* text of name of TABLE */
int iErrOfst;
if( pE->op==TK_DOT ){
+ assert( (selFlags & SF_NestedFrom)==0 );
assert( pE->pLeft!=0 );
assert( !ExprHasProperty(pE->pLeft, EP_IntValue) );
zTName = pE->pLeft->u.zToken;
@@ -146518,6 +149042,7 @@ static int selectExpander(Walker *pWalker, Select *p){
iErrOfst = pE->w.iOfst;
}
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
+ int nAdd; /* Number of cols including rowid */
Table *pTab = pFrom->pTab; /* Table for this data source */
ExprList *pNestedFrom; /* Result-set of a nested FROM clause */
char *zTabName; /* AS name for this data source */
@@ -146535,6 +149060,7 @@ static int selectExpander(Walker *pWalker, Select *p){
pNestedFrom = pFrom->pSelect->pEList;
assert( pNestedFrom!=0 );
assert( pNestedFrom->nExpr==pTab->nCol );
+ assert( VisibleRowid(pTab)==0 || ViewCanHaveRowid );
}else{
if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
continue;
@@ -146565,33 +149091,49 @@ static int selectExpander(Walker *pWalker, Select *p){
}else{
pUsing = 0;
}
- for(j=0; j<pTab->nCol; j++){
- char *zName = pTab->aCol[j].zCnName;
+
+ nAdd = pTab->nCol;
+ if( VisibleRowid(pTab) && (selFlags & SF_NestedFrom)!=0 ) nAdd++;
+ for(j=0; j<nAdd; j++){
+ const char *zName;
struct ExprList_item *pX; /* Newly added ExprList term */
- assert( zName );
- if( zTName
- && pNestedFrom
- && sqlite3MatchEName(&pNestedFrom->a[j], 0, zTName, 0)==0
- ){
- continue;
- }
+ if( j==pTab->nCol ){
+ zName = sqlite3RowidAlias(pTab);
+ if( zName==0 ) continue;
+ }else{
+ zName = pTab->aCol[j].zCnName;
- /* If a column is marked as 'hidden', omit it from the expanded
- ** result-set list unless the SELECT has the SF_IncludeHidden
- ** bit set.
- */
- if( (p->selFlags & SF_IncludeHidden)==0
- && IsHiddenColumn(&pTab->aCol[j])
- ){
- continue;
- }
- if( (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0
- && zTName==0
- && (selFlags & (SF_NestedFrom))==0
- ){
- continue;
+ /* If pTab is actually an SF_NestedFrom sub-select, do not
+ ** expand any ENAME_ROWID columns. */
+ if( pNestedFrom && pNestedFrom->a[j].fg.eEName==ENAME_ROWID ){
+ continue;
+ }
+
+ if( zTName
+ && pNestedFrom
+ && sqlite3MatchEName(&pNestedFrom->a[j], 0, zTName, 0, 0)==0
+ ){
+ continue;
+ }
+
+ /* If a column is marked as 'hidden', omit it from the expanded
+ ** result-set list unless the SELECT has the SF_IncludeHidden
+ ** bit set.
+ */
+ if( (p->selFlags & SF_IncludeHidden)==0
+ && IsHiddenColumn(&pTab->aCol[j])
+ ){
+ continue;
+ }
+ if( (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0
+ && zTName==0
+ && (selFlags & (SF_NestedFrom))==0
+ ){
+ continue;
+ }
}
+ assert( zName );
tableSeen = 1;
if( i>0 && zTName==0 && (selFlags & SF_NestedFrom)==0 ){
@@ -146633,7 +149175,8 @@ static int selectExpander(Walker *pWalker, Select *p){
pX = &pNew->a[pNew->nExpr-1];
assert( pX->zEName==0 );
if( (selFlags & SF_NestedFrom)!=0 && !IN_RENAME_OBJECT ){
- if( pNestedFrom ){
+ if( pNestedFrom && (!ViewCanHaveRowid || j<pNestedFrom->nExpr) ){
+ assert( j<pNestedFrom->nExpr );
pX->zEName = sqlite3DbStrDup(db, pNestedFrom->a[j].zEName);
testcase( pX->zEName==0 );
}else{
@@ -146641,11 +149184,11 @@ static int selectExpander(Walker *pWalker, Select *p){
zSchemaName, zTabName, zName);
testcase( pX->zEName==0 );
}
- pX->fg.eEName = ENAME_TAB;
+ pX->fg.eEName = (j==pTab->nCol ? ENAME_ROWID : ENAME_TAB);
if( (pFrom->fg.isUsing
&& sqlite3IdListIndex(pFrom->u3.pUsing, zName)>=0)
|| (pUsing && sqlite3IdListIndex(pUsing, zName)>=0)
- || (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND)!=0
+ || (j<pTab->nCol && (pTab->aCol[j].colFlags & COLFLAG_NOEXPAND))
){
pX->fg.bNoExpand = 1;
}
@@ -146747,10 +149290,10 @@ static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
SrcList *pTabList;
SrcItem *pFrom;
- assert( p->selFlags & SF_Resolved );
if( p->selFlags & SF_HasTypeInfo ) return;
p->selFlags |= SF_HasTypeInfo;
pParse = pWalker->pParse;
+ assert( (p->selFlags & SF_Resolved) );
pTabList = p->pSrc;
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
Table *pTab = pFrom->pTab;
@@ -146820,6 +149363,8 @@ SQLITE_PRIVATE void sqlite3SelectPrep(
*/
static void printAggInfo(AggInfo *pAggInfo){
int ii;
+ sqlite3DebugPrintf("AggInfo %d/%p:\n",
+ pAggInfo->selId, pAggInfo);
for(ii=0; ii<pAggInfo->nColumn; ii++){
struct AggInfo_col *pCol = &pAggInfo->aCol[ii];
sqlite3DebugPrintf(
@@ -146866,8 +149411,14 @@ static void analyzeAggFuncArgs(
pNC->ncFlags |= NC_InAggFunc;
for(i=0; i<pAggInfo->nFunc; i++){
Expr *pExpr = pAggInfo->aFunc[i].pFExpr;
+ assert( pExpr->op==TK_FUNCTION || pExpr->op==TK_AGG_FUNCTION );
assert( ExprUseXList(pExpr) );
sqlite3ExprAnalyzeAggList(pNC, pExpr->x.pList);
+ if( pExpr->pLeft ){
+ assert( pExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pExpr->pLeft) );
+ sqlite3ExprAnalyzeAggList(pNC, pExpr->pLeft->x.pList);
+ }
#ifndef SQLITE_OMIT_WINDOWFUNC
assert( !IsWindowFunc(pExpr) );
if( ExprHasProperty(pExpr, EP_WinFunc) ){
@@ -147022,6 +149573,36 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
pFunc->pFunc->zName));
}
}
+ if( pFunc->iOBTab>=0 ){
+ ExprList *pOBList;
+ KeyInfo *pKeyInfo;
+ int nExtra = 0;
+ assert( pFunc->pFExpr->pLeft!=0 );
+ assert( pFunc->pFExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pFunc->pFExpr->pLeft) );
+ assert( pFunc->pFunc!=0 );
+ pOBList = pFunc->pFExpr->pLeft->x.pList;
+ if( !pFunc->bOBUnique ){
+ nExtra++; /* One extra column for the OP_Sequence */
+ }
+ if( pFunc->bOBPayload ){
+ /* extra columns for the function arguments */
+ assert( ExprUseXList(pFunc->pFExpr) );
+ nExtra += pFunc->pFExpr->x.pList->nExpr;
+ }
+ if( pFunc->bUseSubtype ){
+ nExtra += pFunc->pFExpr->x.pList->nExpr;
+ }
+ pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOBList, 0, nExtra);
+ if( !pFunc->bOBUnique && pParse->nErr==0 ){
+ pKeyInfo->nKeyField++;
+ }
+ sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
+ pFunc->iOBTab, pOBList->nExpr+nExtra, 0,
+ (char*)pKeyInfo, P4_KEYINFO);
+ ExplainQueryPlan((pParse, 0, "USE TEMP B-TREE FOR %s(ORDER BY)",
+ pFunc->pFunc->zName));
+ }
}
}
@@ -147037,13 +149618,56 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
ExprList *pList;
assert( ExprUseXList(pF->pFExpr) );
pList = pF->pFExpr->x.pList;
+ if( pF->iOBTab>=0 ){
+ /* For an ORDER BY aggregate, calls to OP_AggStep were deferred. Inputs
+ ** were stored in emphermal table pF->iOBTab. Here, we extract those
+ ** inputs (in ORDER BY order) and make all calls to OP_AggStep
+ ** before doing the OP_AggFinal call. */
+ int iTop; /* Start of loop for extracting columns */
+ int nArg; /* Number of columns to extract */
+ int nKey; /* Key columns to be skipped */
+ int regAgg; /* Extract into this array */
+ int j; /* Loop counter */
+
+ assert( pF->pFunc!=0 );
+ nArg = pList->nExpr;
+ regAgg = sqlite3GetTempRange(pParse, nArg);
+
+ if( pF->bOBPayload==0 ){
+ nKey = 0;
+ }else{
+ assert( pF->pFExpr->pLeft!=0 );
+ assert( ExprUseXList(pF->pFExpr->pLeft) );
+ assert( pF->pFExpr->pLeft->x.pList!=0 );
+ nKey = pF->pFExpr->pLeft->x.pList->nExpr;
+ if( ALWAYS(!pF->bOBUnique) ) nKey++;
+ }
+ iTop = sqlite3VdbeAddOp1(v, OP_Rewind, pF->iOBTab); VdbeCoverage(v);
+ for(j=nArg-1; j>=0; j--){
+ sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, nKey+j, regAgg+j);
+ }
+ if( pF->bUseSubtype ){
+ int regSubtype = sqlite3GetTempReg(pParse);
+ int iBaseCol = nKey + nArg + (pF->bOBPayload==0 && pF->bOBUnique==0);
+ for(j=nArg-1; j>=0; j--){
+ sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, iBaseCol+j, regSubtype);
+ sqlite3VdbeAddOp2(v, OP_SetSubtype, regSubtype, regAgg+j);
+ }
+ sqlite3ReleaseTempReg(pParse, regSubtype);
+ }
+ sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
+ sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, (u8)nArg);
+ sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1); VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, iTop);
+ sqlite3ReleaseTempRange(pParse, regAgg, nArg);
+ }
sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i),
pList ? pList->nExpr : 0);
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
}
}
-
/*
** Generate code that will update the accumulator memory cells for an
** aggregate based on the current cursor position.
@@ -147052,6 +149676,13 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
** in pAggInfo, then only populate the pAggInfo->nAccumulator accumulator
** registers if register regAcc contains 0. The caller will take care
** of setting and clearing regAcc.
+**
+** For an ORDER BY aggregate, the actual accumulator memory cell update
+** is deferred until after all input rows have been received, so that they
+** can be run in the requested order. In that case, instead of invoking
+** OP_AggStep to update the accumulator, just add the arguments that would
+** have been passed into OP_AggStep into the sorting ephemeral table
+** (along with the appropriate sort key).
*/
static void updateAccumulator(
Parse *pParse,
@@ -147073,9 +149704,12 @@ static void updateAccumulator(
int nArg;
int addrNext = 0;
int regAgg;
+ int regAggSz = 0;
+ int regDistinct = 0;
ExprList *pList;
assert( ExprUseXList(pF->pFExpr) );
assert( !IsWindowFunc(pF->pFExpr) );
+ assert( pF->pFunc!=0 );
pList = pF->pFExpr->x.pList;
if( ExprHasProperty(pF->pFExpr, EP_WinFunc) ){
Expr *pFilter = pF->pFExpr->y.pWin->pFilter;
@@ -147099,9 +149733,55 @@ static void updateAccumulator(
addrNext = sqlite3VdbeMakeLabel(pParse);
sqlite3ExprIfFalse(pParse, pFilter, addrNext, SQLITE_JUMPIFNULL);
}
- if( pList ){
+ if( pF->iOBTab>=0 ){
+ /* Instead of invoking AggStep, we must push the arguments that would
+ ** have been passed to AggStep onto the sorting table. */
+ int jj; /* Registered used so far in building the record */
+ ExprList *pOBList; /* The ORDER BY clause */
+ assert( pList!=0 );
+ nArg = pList->nExpr;
+ assert( nArg>0 );
+ assert( pF->pFExpr->pLeft!=0 );
+ assert( pF->pFExpr->pLeft->op==TK_ORDER );
+ assert( ExprUseXList(pF->pFExpr->pLeft) );
+ pOBList = pF->pFExpr->pLeft->x.pList;
+ assert( pOBList!=0 );
+ assert( pOBList->nExpr>0 );
+ regAggSz = pOBList->nExpr;
+ if( !pF->bOBUnique ){
+ regAggSz++; /* One register for OP_Sequence */
+ }
+ if( pF->bOBPayload ){
+ regAggSz += nArg;
+ }
+ if( pF->bUseSubtype ){
+ regAggSz += nArg;
+ }
+ regAggSz++; /* One extra register to hold result of MakeRecord */
+ regAgg = sqlite3GetTempRange(pParse, regAggSz);
+ regDistinct = regAgg;
+ sqlite3ExprCodeExprList(pParse, pOBList, regAgg, 0, SQLITE_ECEL_DUP);
+ jj = pOBList->nExpr;
+ if( !pF->bOBUnique ){
+ sqlite3VdbeAddOp2(v, OP_Sequence, pF->iOBTab, regAgg+jj);
+ jj++;
+ }
+ if( pF->bOBPayload ){
+ regDistinct = regAgg+jj;
+ sqlite3ExprCodeExprList(pParse, pList, regDistinct, 0, SQLITE_ECEL_DUP);
+ jj += nArg;
+ }
+ if( pF->bUseSubtype ){
+ int kk;
+ int regBase = pF->bOBPayload ? regDistinct : regAgg;
+ for(kk=0; kk<nArg; kk++, jj++){
+ sqlite3VdbeAddOp2(v, OP_GetSubtype, regBase+kk, regAgg+jj);
+ }
+ }
+ }else if( pList ){
nArg = pList->nExpr;
regAgg = sqlite3GetTempRange(pParse, nArg);
+ regDistinct = regAgg;
sqlite3ExprCodeExprList(pParse, pList, regAgg, 0, SQLITE_ECEL_DUP);
}else{
nArg = 0;
@@ -147112,26 +149792,37 @@ static void updateAccumulator(
addrNext = sqlite3VdbeMakeLabel(pParse);
}
pF->iDistinct = codeDistinct(pParse, eDistinctType,
- pF->iDistinct, addrNext, pList, regAgg);
- }
- if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
- CollSeq *pColl = 0;
- struct ExprList_item *pItem;
- int j;
- assert( pList!=0 ); /* pList!=0 if pF->pFunc has NEEDCOLL */
- for(j=0, pItem=pList->a; !pColl && j<nArg; j++, pItem++){
- pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
- }
- if( !pColl ){
- pColl = pParse->db->pDfltColl;
+ pF->iDistinct, addrNext, pList, regDistinct);
+ }
+ if( pF->iOBTab>=0 ){
+ /* Insert a new record into the ORDER BY table */
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regAgg, regAggSz-1,
+ regAgg+regAggSz-1);
+ sqlite3VdbeAddOp4Int(v, OP_IdxInsert, pF->iOBTab, regAgg+regAggSz-1,
+ regAgg, regAggSz-1);
+ sqlite3ReleaseTempRange(pParse, regAgg, regAggSz);
+ }else{
+ /* Invoke the AggStep function */
+ if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
+ CollSeq *pColl = 0;
+ struct ExprList_item *pItem;
+ int j;
+ assert( pList!=0 ); /* pList!=0 if pF->pFunc has NEEDCOLL */
+ for(j=0, pItem=pList->a; !pColl && j<nArg; j++, pItem++){
+ pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
+ }
+ if( !pColl ){
+ pColl = pParse->db->pDfltColl;
+ }
+ if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
+ sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0,
+ (char *)pColl, P4_COLLSEQ);
}
- if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
- sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ);
+ sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
+ sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, (u8)nArg);
+ sqlite3ReleaseTempRange(pParse, regAgg, nArg);
}
- sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
- sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, (u8)nArg);
- sqlite3ReleaseTempRange(pParse, regAgg, nArg);
if( addrNext ){
sqlite3VdbeResolveLabel(v, addrNext);
}
@@ -147290,7 +149981,8 @@ static SrcItem *isSelfJoinView(
/*
** Deallocate a single AggInfo object
*/
-static void agginfoFree(sqlite3 *db, AggInfo *p){
+static void agginfoFree(sqlite3 *db, void *pArg){
+ AggInfo *p = (AggInfo*)pArg;
sqlite3DbFree(db, p->aCol);
sqlite3DbFree(db, p->aFunc);
sqlite3DbFreeNN(db, p);
@@ -147364,7 +150056,7 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
pSub->selFlags |= SF_Aggregate;
pSub->selFlags &= ~SF_Compound;
pSub->nSelectRow = 0;
- sqlite3ExprListDelete(db, pSub->pEList);
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pSub->pEList);
pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount;
pSub->pEList = sqlite3ExprListAppend(pParse, 0, pTerm);
pTerm = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
@@ -147544,9 +150236,8 @@ SQLITE_PRIVATE int sqlite3Select(
sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY");
}
#endif
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3ExprListDelete,
- p->pOrderBy);
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric,
+ p->pOrderBy);
testcase( pParse->earlyCleanup );
p->pOrderBy = 0;
}
@@ -147652,6 +150343,7 @@ SQLITE_PRIVATE int sqlite3Select(
TREETRACE(0x1000,pParse,p,
("LEFT-JOIN simplifies to JOIN on term %d\n",i));
pItem->fg.jointype &= ~(JT_LEFT|JT_OUTER);
+ unsetJoinExpr(p->pWhere, pItem->iCursor, 0);
}
}
if( pItem->fg.jointype & JT_LTORJ ){
@@ -147666,17 +150358,15 @@ SQLITE_PRIVATE int sqlite3Select(
TREETRACE(0x1000,pParse,p,
("RIGHT-JOIN simplifies to JOIN on term %d\n",j));
pI2->fg.jointype &= ~(JT_RIGHT|JT_OUTER);
+ unsetJoinExpr(p->pWhere, pI2->iCursor, 1);
}
}
}
- for(j=pTabList->nSrc-1; j>=i; j--){
+ for(j=pTabList->nSrc-1; j>=0; j--){
pTabList->a[j].fg.jointype &= ~JT_LTORJ;
if( pTabList->a[j].fg.jointype & JT_RIGHT ) break;
}
}
- assert( pItem->iCursor>=0 );
- unsetJoinExpr(p->pWhere, pItem->iCursor,
- pTabList->a[0].fg.jointype & JT_LTORJ);
}
/* No further action if this term of the FROM clause is not a subquery */
@@ -147739,9 +150429,8 @@ SQLITE_PRIVATE int sqlite3Select(
){
TREETRACE(0x800,pParse,p,
("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1));
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))sqlite3ExprListDelete,
- pSub->pOrderBy);
+ sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric,
+ pSub->pOrderBy);
pSub->pOrderBy = 0;
}
@@ -147866,7 +150555,7 @@ SQLITE_PRIVATE int sqlite3Select(
/* Generate code for all sub-queries in the FROM clause
*/
pSub = pItem->pSelect;
- if( pSub==0 ) continue;
+ if( pSub==0 || pItem->addrFillSub!=0 ) continue;
/* The code for a subquery should only be generated once. */
assert( pItem->addrFillSub==0 );
@@ -147897,7 +150586,7 @@ SQLITE_PRIVATE int sqlite3Select(
#endif
assert( pItem->pSelect && (pItem->pSelect->selFlags & SF_PushDown)!=0 );
}else{
- TREETRACE(0x4000,pParse,p,("Push-down not possible\n"));
+ TREETRACE(0x4000,pParse,p,("WHERE-lcause push-down not possible\n"));
}
/* Convert unused result columns of the subquery into simple NULL
@@ -148270,8 +150959,7 @@ SQLITE_PRIVATE int sqlite3Select(
*/
pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) );
if( pAggInfo ){
- sqlite3ParserAddCleanup(pParse,
- (void(*)(sqlite3*,void*))agginfoFree, pAggInfo);
+ sqlite3ParserAddCleanup(pParse, agginfoFree, pAggInfo);
testcase( pParse->earlyCleanup );
}
if( db->mallocFailed ){
@@ -148779,6 +151467,12 @@ select_end:
sqlite3ExprListDelete(db, pMinMaxOrderBy);
#ifdef SQLITE_DEBUG
if( pAggInfo && !db->mallocFailed ){
+#if TREETRACE_ENABLED
+ if( sqlite3TreeTrace & 0x20 ){
+ TREETRACE(0x20,pParse,p,("Finished with AggInfo\n"));
+ printAggInfo(pAggInfo);
+ }
+#endif
for(i=0; i<pAggInfo->nColumn; i++){
Expr *pExpr = pAggInfo->aCol[i].pCExpr;
if( pExpr==0 ) continue;
@@ -149192,6 +151886,10 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
sqlite3ErrorMsg(pParse, "cannot create triggers on virtual tables");
goto trigger_orphan_error;
}
+ if( (pTab->tabFlags & TF_Shadow)!=0 && sqlite3ReadOnlyShadowTables(db) ){
+ sqlite3ErrorMsg(pParse, "cannot create triggers on shadow tables");
+ goto trigger_orphan_error;
+ }
/* Check that the trigger name is not reserved and that no trigger of the
** specified name exists */
@@ -149956,6 +152654,72 @@ static ExprList *sqlite3ExpandReturning(
return pNew;
}
+/* If the Expr node is a subquery or an EXISTS operator or an IN operator that
+** uses a subquery, and if the subquery is SF_Correlated, then mark the
+** expression as EP_VarSelect.
+*/
+static int sqlite3ReturningSubqueryVarSelect(Walker *NotUsed, Expr *pExpr){
+ UNUSED_PARAMETER(NotUsed);
+ if( ExprUseXSelect(pExpr)
+ && (pExpr->x.pSelect->selFlags & SF_Correlated)!=0
+ ){
+ testcase( ExprHasProperty(pExpr, EP_VarSelect) );
+ ExprSetProperty(pExpr, EP_VarSelect);
+ }
+ return WRC_Continue;
+}
+
+
+/*
+** If the SELECT references the table pWalker->u.pTab, then do two things:
+**
+** (1) Mark the SELECT as as SF_Correlated.
+** (2) Set pWalker->eCode to non-zero so that the caller will know
+** that (1) has happened.
+*/
+static int sqlite3ReturningSubqueryCorrelated(Walker *pWalker, Select *pSelect){
+ int i;
+ SrcList *pSrc;
+ assert( pSelect!=0 );
+ pSrc = pSelect->pSrc;
+ assert( pSrc!=0 );
+ for(i=0; i<pSrc->nSrc; i++){
+ if( pSrc->a[i].pTab==pWalker->u.pTab ){
+ testcase( pSelect->selFlags & SF_Correlated );
+ pSelect->selFlags |= SF_Correlated;
+ pWalker->eCode = 1;
+ break;
+ }
+ }
+ return WRC_Continue;
+}
+
+/*
+** Scan the expression list that is the argument to RETURNING looking
+** for subqueries that depend on the table which is being modified in the
+** statement that is hosting the RETURNING clause (pTab). Mark all such
+** subqueries as SF_Correlated. If the subqueries are part of an
+** expression, mark the expression as EP_VarSelect.
+**
+** https://sqlite.org/forum/forumpost/2c83569ce8945d39
+*/
+static void sqlite3ProcessReturningSubqueries(
+ ExprList *pEList,
+ Table *pTab
+){
+ Walker w;
+ memset(&w, 0, sizeof(w));
+ w.xExprCallback = sqlite3ExprWalkNoop;
+ w.xSelectCallback = sqlite3ReturningSubqueryCorrelated;
+ w.u.pTab = pTab;
+ sqlite3WalkExprList(&w, pEList);
+ if( w.eCode ){
+ w.xExprCallback = sqlite3ReturningSubqueryVarSelect;
+ w.xSelectCallback = sqlite3SelectWalkNoop;
+ sqlite3WalkExprList(&w, pEList);
+ }
+}
+
/*
** Generate code for the RETURNING trigger. Unlike other triggers
** that invoke a subprogram in the bytecode, the code for RETURNING
@@ -149975,16 +152739,24 @@ static void codeReturningTrigger(
SrcList sFrom;
assert( v!=0 );
- assert( pParse->bReturning );
+ if( !pParse->bReturning ){
+ /* This RETURNING trigger must be for a different statement as
+ ** this statement lacks a RETURNING clause. */
+ return;
+ }
assert( db->pParse==pParse );
pReturning = pParse->u1.pReturning;
- assert( pTrigger == &(pReturning->retTrig) );
+ if( pTrigger != &(pReturning->retTrig) ){
+ /* This RETURNING trigger is for a different statement */
+ return;
+ }
memset(&sSelect, 0, sizeof(sSelect));
memset(&sFrom, 0, sizeof(sFrom));
sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0);
sSelect.pSrc = &sFrom;
sFrom.nSrc = 1;
sFrom.a[0].pTab = pTab;
+ sFrom.a[0].zName = pTab->zName; /* tag-20240424-1 */
sFrom.a[0].iCursor = -1;
sqlite3SelectPrep(pParse, &sSelect, 0);
if( pParse->nErr==0 ){
@@ -150011,6 +152783,7 @@ static void codeReturningTrigger(
int i;
int nCol = pNew->nExpr;
int reg = pParse->nMem+1;
+ sqlite3ProcessReturningSubqueries(pNew, pTab);
pParse->nMem += nCol+2;
pReturning->iRetReg = reg;
for(i=0; i<nCol; i++){
@@ -151412,6 +154185,9 @@ SQLITE_PRIVATE void sqlite3Update(
}
}
if( chngRowid==0 && pPk==0 ){
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ if( isView ) sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid);
+#endif
sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid);
}
}
@@ -151949,7 +154725,8 @@ SQLITE_PRIVATE Upsert *sqlite3UpsertNew(
SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(
Parse *pParse, /* The parsing context */
SrcList *pTabList, /* Table into which we are inserting */
- Upsert *pUpsert /* The ON CONFLICT clauses */
+ Upsert *pUpsert, /* The ON CONFLICT clauses */
+ Upsert *pAll /* Complete list of all ON CONFLICT clauses */
){
Table *pTab; /* That table into which we are inserting */
int rc; /* Result code */
@@ -152052,6 +154829,14 @@ SQLITE_PRIVATE int sqlite3UpsertAnalyzeTarget(
continue;
}
pUpsert->pUpsertIdx = pIdx;
+ if( sqlite3UpsertOfIndex(pAll,pIdx)!=pUpsert ){
+ /* Really this should be an error. The isDup ON CONFLICT clause will
+ ** never fire. But this problem was not discovered until three years
+ ** after multi-CONFLICT upsert was added, and so we silently ignore
+ ** the problem to prevent breaking applications that might actually
+ ** have redundant ON CONFLICT clauses. */
+ pUpsert->isDup = 1;
+ }
break;
}
if( pUpsert->pUpsertIdx==0 ){
@@ -152078,9 +154863,13 @@ SQLITE_PRIVATE int sqlite3UpsertNextIsIPK(Upsert *pUpsert){
Upsert *pNext;
if( NEVER(pUpsert==0) ) return 0;
pNext = pUpsert->pNextUpsert;
- if( pNext==0 ) return 1;
- if( pNext->pUpsertTarget==0 ) return 1;
- if( pNext->pUpsertIdx==0 ) return 1;
+ while( 1 /*exit-by-return*/ ){
+ if( pNext==0 ) return 1;
+ if( pNext->pUpsertTarget==0 ) return 1;
+ if( pNext->pUpsertIdx==0 ) return 1;
+ if( !pNext->isDup ) return 0;
+ pNext = pNext->pNextUpsert;
+ }
return 0;
}
@@ -152909,7 +155698,6 @@ SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3 *db){
if( p ){
db->pDisconnect = 0;
- sqlite3ExpirePreparedStatements(db, 0);
do {
VTable *pNext = p->pNext;
sqlite3VtabUnlock(p);
@@ -153206,6 +155994,8 @@ static int vtabCallConstructor(
db->pVtabCtx = &sCtx;
pTab->nTabRef++;
rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr);
+ assert( pTab!=0 );
+ assert( pTab->nTabRef>1 || rc!=SQLITE_OK );
sqlite3DeleteTable(db, pTab);
db->pVtabCtx = sCtx.pPrior;
if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
@@ -153228,7 +156018,7 @@ static int vtabCallConstructor(
pVTable->nRef = 1;
if( sCtx.bDeclared==0 ){
const char *zFormat = "vtable constructor did not declare schema: %s";
- *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName);
+ *pzErr = sqlite3MPrintf(db, zFormat, zModuleName);
sqlite3VtabUnlock(pVTable);
rc = SQLITE_ERROR;
}else{
@@ -153406,19 +156196,38 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
Table *pTab;
Parse sParse;
int initBusy;
+ int i;
+ const unsigned char *z;
+ static const u8 aKeyword[] = { TK_CREATE, TK_TABLE, 0 };
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){
return SQLITE_MISUSE_BKPT;
}
#endif
+
+ /* Verify that the first two keywords in the CREATE TABLE statement
+ ** really are "CREATE" and "TABLE". If this is not the case, then
+ ** sqlite3_declare_vtab() is being misused.
+ */
+ z = (const unsigned char*)zCreateTable;
+ for(i=0; aKeyword[i]; i++){
+ int tokenType = 0;
+ do{ z += sqlite3GetToken(z, &tokenType); }while( tokenType==TK_SPACE );
+ if( tokenType!=aKeyword[i] ){
+ sqlite3ErrorWithMsg(db, SQLITE_ERROR, "syntax error");
+ return SQLITE_ERROR;
+ }
+ }
+
sqlite3_mutex_enter(db->mutex);
pCtx = db->pVtabCtx;
if( !pCtx || pCtx->bDeclared ){
- sqlite3Error(db, SQLITE_MISUSE);
+ sqlite3Error(db, SQLITE_MISUSE_BKPT);
sqlite3_mutex_leave(db->mutex);
return SQLITE_MISUSE_BKPT;
}
+
pTab = pCtx->pTab;
assert( IsVirtual(pTab) );
@@ -153432,11 +156241,10 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
initBusy = db->init.busy;
db->init.busy = 0;
sParse.nQueryLoop = 1;
- if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable)
- && ALWAYS(sParse.pNewTable!=0)
- && ALWAYS(!db->mallocFailed)
- && IsOrdinaryTable(sParse.pNewTable)
- ){
+ if( SQLITE_OK==sqlite3RunParser(&sParse, zCreateTable) ){
+ assert( sParse.pNewTable!=0 );
+ assert( !db->mallocFailed );
+ assert( IsOrdinaryTable(sParse.pNewTable) );
assert( sParse.zErrMsg==0 );
if( !pTab->aCol ){
Table *pNew = sParse.pNewTable;
@@ -154475,7 +157283,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet*,int);
#ifdef WHERETRACE_ENABLED
SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC);
SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm);
-SQLITE_PRIVATE void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC);
+SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC);
#endif
SQLITE_PRIVATE WhereTerm *sqlite3WhereFindTerm(
WhereClause *pWC, /* The WHERE clause to be searched */
@@ -154606,7 +157414,7 @@ SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, SrcItem*, WhereClause*);
#define WHERE_BLOOMFILTER 0x00400000 /* Consider using a Bloom-filter */
#define WHERE_SELFCULL 0x00800000 /* nOut reduced by extra WHERE terms */
#define WHERE_OMIT_OFFSET 0x01000000 /* Set offset counter to zero */
-#define WHERE_VIEWSCAN 0x02000000 /* A full-scan of a VIEW or subquery */
+ /* 0x02000000 -- available for reuse */
#define WHERE_EXPRIDX 0x04000000 /* Uses an index-on-expressions */
#endif /* !defined(SQLITE_WHEREINT_H) */
@@ -155932,6 +158740,27 @@ static SQLITE_NOINLINE void filterPullDown(
}
/*
+** Loop pLoop is a WHERE_INDEXED level that uses at least one IN(...)
+** operator. Return true if level pLoop is guaranteed to visit only one
+** row for each key generated for the index.
+*/
+static int whereLoopIsOneRow(WhereLoop *pLoop){
+ if( pLoop->u.btree.pIndex->onError
+ && pLoop->nSkip==0
+ && pLoop->u.btree.nEq==pLoop->u.btree.pIndex->nKeyCol
+ ){
+ int ii;
+ for(ii=0; ii<pLoop->u.btree.nEq; ii++){
+ if( pLoop->aLTerm[ii]->eOperator & (WO_IS|WO_ISNULL) ){
+ return 0;
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
+
+/*
** Generate code for the start of the iLevel-th loop in the WHERE clause
** implementation described by pWInfo.
*/
@@ -156009,7 +158838,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){
pLevel->iLeftJoin = ++pParse->nMem;
sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin);
- VdbeComment((v, "init LEFT JOIN no-match flag"));
+ VdbeComment((v, "init LEFT JOIN match flag"));
}
/* Compute a safe address to jump to if we discover that the table for
@@ -156678,7 +159507,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}
/* Record the instruction used to terminate the loop. */
- if( pLoop->wsFlags & WHERE_ONEROW ){
+ if( (pLoop->wsFlags & WHERE_ONEROW)
+ || (pLevel->u.in.nIn && regBignull==0 && whereLoopIsOneRow(pLoop))
+ ){
pLevel->op = OP_Noop;
}else if( bRev ){
pLevel->op = OP_Prev;
@@ -157068,6 +159899,12 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
** iLoop==3: Code all remaining expressions.
**
** An effort is made to skip unnecessary iterations of the loop.
+ **
+ ** This optimization of causing simple query restrictions to occur before
+ ** more complex one is call the "push-down" optimization in MySQL. Here
+ ** in SQLite, the name is "MySQL push-down", since there is also another
+ ** totally unrelated optimization called "WHERE-clause push-down".
+ ** Sometimes the qualifier is omitted, resulting in an ambiguity, so beware.
*/
iLoop = (pIdx ? 1 : 2);
do{
@@ -157318,7 +160155,16 @@ SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
pRJ->regReturn);
for(k=0; k<iLevel; k++){
int iIdxCur;
+ SrcItem *pRight;
+ assert( pWInfo->a[k].pWLoop->iTab == pWInfo->a[k].iFrom );
+ pRight = &pWInfo->pTabList->a[pWInfo->a[k].iFrom];
mAll |= pWInfo->a[k].pWLoop->maskSelf;
+ if( pRight->fg.viaCoroutine ){
+ sqlite3VdbeAddOp3(
+ v, OP_Null, 0, pRight->regResult,
+ pRight->regResult + pRight->pSelect->pEList->nExpr-1
+ );
+ }
sqlite3VdbeAddOp1(v, OP_NullRow, pWInfo->a[k].iTabCur);
iIdxCur = pWInfo->a[k].iIdxCur;
if( iIdxCur ){
@@ -158375,7 +161221,7 @@ static SQLITE_NOINLINE int exprMightBeIndexed2(
if( pIdx->aiColumn[i]!=XN_EXPR ) continue;
assert( pIdx->bHasExpr );
if( sqlite3ExprCompareSkip(pExpr,pIdx->aColExpr->a[i].pExpr,iCur)==0
- && pExpr->op!=TK_STRING
+ && !sqlite3ExprIsConstant(0,pIdx->aColExpr->a[i].pExpr)
){
aiCurCol[0] = iCur;
aiCurCol[1] = XN_EXPR;
@@ -159024,6 +161870,7 @@ SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Selec
continue;
}
if( pWC->a[ii].leftCursor!=iCsr ) return;
+ if( pWC->a[ii].prereqRight!=0 ) return;
}
/* Check condition (5). Return early if it is not met. */
@@ -159038,12 +161885,14 @@ SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3WhereAddLimit(WhereClause *pWC, Selec
/* All conditions are met. Add the terms to the where-clause object. */
assert( p->pLimit->op==TK_LIMIT );
- whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft,
- iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT);
- if( p->iOffset>0 ){
+ if( p->iOffset!=0 && (p->selFlags & SF_Compound)==0 ){
whereAddLimitExpr(pWC, p->iOffset, p->pLimit->pRight,
iCsr, SQLITE_INDEX_CONSTRAINT_OFFSET);
}
+ if( p->iOffset==0 || (p->selFlags & SF_Compound)==0 ){
+ whereAddLimitExpr(pWC, p->iLimit, p->pLimit->pLeft,
+ iCsr, SQLITE_INDEX_CONSTRAINT_LIMIT);
+ }
}
}
@@ -159562,6 +162411,42 @@ static Expr *whereRightSubexprIsColumn(Expr *p){
}
/*
+** Term pTerm is guaranteed to be a WO_IN term. It may be a component term
+** of a vector IN expression of the form "(x, y, ...) IN (SELECT ...)".
+** This function checks to see if the term is compatible with an index
+** column with affinity idxaff (one of the SQLITE_AFF_XYZ values). If so,
+** it returns a pointer to the name of the collation sequence (e.g. "BINARY"
+** or "NOCASE") used by the comparison in pTerm. If it is not compatible
+** with affinity idxaff, NULL is returned.
+*/
+static SQLITE_NOINLINE const char *indexInAffinityOk(
+ Parse *pParse,
+ WhereTerm *pTerm,
+ u8 idxaff
+){
+ Expr *pX = pTerm->pExpr;
+ Expr inexpr;
+
+ assert( pTerm->eOperator & WO_IN );
+
+ if( sqlite3ExprIsVector(pX->pLeft) ){
+ int iField = pTerm->u.x.iField - 1;
+ inexpr.flags = 0;
+ inexpr.op = TK_EQ;
+ inexpr.pLeft = pX->pLeft->x.pList->a[iField].pExpr;
+ assert( ExprUseXSelect(pX) );
+ inexpr.pRight = pX->x.pSelect->pEList->a[iField].pExpr;
+ pX = &inexpr;
+ }
+
+ if( sqlite3IndexAffinityOk(pX, idxaff) ){
+ CollSeq *pRet = sqlite3ExprCompareCollSeq(pParse, pX);
+ return pRet ? pRet->zName : sqlite3StrBINARY;
+ }
+ return 0;
+}
+
+/*
** Advance to the next WhereTerm that matches according to the criteria
** established when the pScan object was initialized by whereScanInit().
** Return NULL if there are no more matching WhereTerms.
@@ -159611,16 +162496,24 @@ static WhereTerm *whereScanNext(WhereScan *pScan){
if( (pTerm->eOperator & pScan->opMask)!=0 ){
/* Verify the affinity and collating sequence match */
if( pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0 ){
- CollSeq *pColl;
+ const char *zCollName;
Parse *pParse = pWC->pWInfo->pParse;
pX = pTerm->pExpr;
- if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){
- continue;
+
+ if( (pTerm->eOperator & WO_IN) ){
+ zCollName = indexInAffinityOk(pParse, pTerm, pScan->idxaff);
+ if( !zCollName ) continue;
+ }else{
+ CollSeq *pColl;
+ if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){
+ continue;
+ }
+ assert(pX->pLeft);
+ pColl = sqlite3ExprCompareCollSeq(pParse, pX);
+ zCollName = pColl ? pColl->zName : sqlite3StrBINARY;
}
- assert(pX->pLeft);
- pColl = sqlite3ExprCompareCollSeq(pParse, pX);
- if( pColl==0 ) pColl = pParse->db->pDfltColl;
- if( sqlite3StrICmp(pColl->zName, pScan->zCollName) ){
+
+ if( sqlite3StrICmp(zCollName, pScan->zCollName) ){
continue;
}
}
@@ -159937,12 +162830,22 @@ static void translateColumnToCopy(
for(; iStart<iEnd; iStart++, pOp++){
if( pOp->p1!=iTabCur ) continue;
if( pOp->opcode==OP_Column ){
+#ifdef SQLITE_DEBUG
+ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
+ printf("TRANSLATE OP_Column to OP_Copy at %d\n", iStart);
+ }
+#endif
pOp->opcode = OP_Copy;
pOp->p1 = pOp->p2 + iRegister;
pOp->p2 = pOp->p3;
pOp->p3 = 0;
pOp->p5 = 2; /* Cause the MEM_Subtype flag to be cleared */
}else if( pOp->opcode==OP_Rowid ){
+#ifdef SQLITE_DEBUG
+ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
+ printf("TRANSLATE OP_Rowid to OP_Sequence at %d\n", iStart);
+ }
+#endif
pOp->opcode = OP_Sequence;
pOp->p1 = iAutoidxCur;
#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
@@ -159962,9 +162865,13 @@ static void translateColumnToCopy(
** are no-ops.
*/
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(WHERETRACE_ENABLED)
-static void whereTraceIndexInfoInputs(sqlite3_index_info *p){
+static void whereTraceIndexInfoInputs(
+ sqlite3_index_info *p, /* The IndexInfo object */
+ Table *pTab /* The TABLE that is the virtual table */
+){
int i;
if( (sqlite3WhereTrace & 0x10)==0 ) return;
+ sqlite3DebugPrintf("sqlite3_index_info inputs for %s:\n", pTab->zName);
for(i=0; i<p->nConstraint; i++){
sqlite3DebugPrintf(
" constraint[%d]: col=%d termid=%d op=%d usabled=%d collseq=%s\n",
@@ -159982,9 +162889,13 @@ static void whereTraceIndexInfoInputs(sqlite3_index_info *p){
p->aOrderBy[i].desc);
}
}
-static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){
+static void whereTraceIndexInfoOutputs(
+ sqlite3_index_info *p, /* The IndexInfo object */
+ Table *pTab /* The TABLE that is the virtual table */
+){
int i;
if( (sqlite3WhereTrace & 0x10)==0 ) return;
+ sqlite3DebugPrintf("sqlite3_index_info outputs for %s:\n", pTab->zName);
for(i=0; i<p->nConstraint; i++){
sqlite3DebugPrintf(" usage[%d]: argvIdx=%d omit=%d\n",
i,
@@ -159998,8 +162909,8 @@ static void whereTraceIndexInfoOutputs(sqlite3_index_info *p){
sqlite3DebugPrintf(" estimatedRows=%lld\n", p->estimatedRows);
}
#else
-#define whereTraceIndexInfoInputs(A)
-#define whereTraceIndexInfoOutputs(A)
+#define whereTraceIndexInfoInputs(A,B)
+#define whereTraceIndexInfoOutputs(A,B)
#endif
/*
@@ -160183,7 +163094,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
** WHERE clause (or the ON clause of a LEFT join) that constrain which
** rows of the target table (pSrc) that can be used. */
if( (pTerm->wtFlags & TERM_VIRTUAL)==0
- && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom)
+ && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, pLevel->iFrom, 0)
){
pPartial = sqlite3ExprAnd(pParse, pPartial,
sqlite3ExprDup(pParse->db, pExpr, 0));
@@ -160225,7 +163136,7 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
** if they go out of sync.
*/
if( IsView(pTable) ){
- extraCols = ALLBITS;
+ extraCols = ALLBITS & ~idxCols;
}else{
extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1));
}
@@ -160401,13 +163312,17 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
WhereLoop *pLoop = pLevel->pWLoop; /* The loop being coded */
int iCur; /* Cursor for table getting the filter */
IndexedExpr *saved_pIdxEpr; /* saved copy of Parse.pIdxEpr */
+ IndexedExpr *saved_pIdxPartExpr; /* saved copy of Parse.pIdxPartExpr */
saved_pIdxEpr = pParse->pIdxEpr;
+ saved_pIdxPartExpr = pParse->pIdxPartExpr;
pParse->pIdxEpr = 0;
+ pParse->pIdxPartExpr = 0;
assert( pLoop!=0 );
assert( v!=0 );
assert( pLoop->wsFlags & WHERE_BLOOMFILTER );
+ assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 );
addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
do{
@@ -160448,7 +163363,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
for(pTerm=pWInfo->sWC.a; pTerm<pWCEnd; pTerm++){
Expr *pExpr = pTerm->pExpr;
if( (pTerm->wtFlags & TERM_VIRTUAL)==0
- && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc)
+ && sqlite3ExprIsSingleTableConstraint(pExpr, pTabList, iSrc, 0)
){
sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
}
@@ -160497,6 +163412,7 @@ static SQLITE_NOINLINE void sqlite3ConstructBloomFilter(
}while( iLevel < pWInfo->nLevel );
sqlite3VdbeJumpHere(v, addrOnce);
pParse->pIdxEpr = saved_pIdxEpr;
+ pParse->pIdxPartExpr = saved_pIdxPartExpr;
}
@@ -160573,7 +163489,7 @@ static sqlite3_index_info *allocateIndexInfo(
Expr *pE2;
/* Skip over constant terms in the ORDER BY clause */
- if( sqlite3ExprIsConstant(pExpr) ){
+ if( sqlite3ExprIsConstant(0, pExpr) ){
continue;
}
@@ -160608,7 +163524,7 @@ static sqlite3_index_info *allocateIndexInfo(
}
if( i==n ){
nOrderBy = n;
- if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) ){
+ if( (pWInfo->wctrlFlags & WHERE_DISTINCTBY) && !pSrc->fg.rowidUsed ){
eDistinct = 2 + ((pWInfo->wctrlFlags & WHERE_SORTBYGROUP)!=0);
}else if( pWInfo->wctrlFlags & WHERE_GROUPBY ){
eDistinct = 1;
@@ -160685,7 +163601,7 @@ static sqlite3_index_info *allocateIndexInfo(
pIdxInfo->nConstraint = j;
for(i=j=0; i<nOrderBy; i++){
Expr *pExpr = pOrderBy->a[i].pExpr;
- if( sqlite3ExprIsConstant(pExpr) ) continue;
+ if( sqlite3ExprIsConstant(0, pExpr) ) continue;
assert( pExpr->op==TK_COLUMN
|| (pExpr->op==TK_COLLATE && pExpr->pLeft->op==TK_COLUMN
&& pExpr->iColumn==pExpr->pLeft->iColumn) );
@@ -160737,11 +163653,11 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab;
int rc;
- whereTraceIndexInfoInputs(p);
+ whereTraceIndexInfoInputs(p, pTab);
pParse->db->nSchemaLock++;
rc = pVtab->pModule->xBestIndex(pVtab, p);
pParse->db->nSchemaLock--;
- whereTraceIndexInfoOutputs(p);
+ whereTraceIndexInfoOutputs(p, pTab);
if( rc!=SQLITE_OK && rc!=SQLITE_CONSTRAINT ){
if( rc==SQLITE_NOMEM ){
@@ -161264,7 +164180,8 @@ static int whereRangeScanEst(
** sample, then assume they are 4x more selective. This brings
** the estimated selectivity more in line with what it would be
** if estimated without the use of STAT4 tables. */
- if( iLwrIdx==iUprIdx ) nNew -= 20; assert( 20==sqlite3LogEst(4) );
+ if( iLwrIdx==iUprIdx ){ nNew -= 20; }
+ assert( 20==sqlite3LogEst(4) );
}else{
nNew = 10; assert( 10==sqlite3LogEst(2) );
}
@@ -161488,17 +164405,34 @@ SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC){
#ifdef WHERETRACE_ENABLED
/*
** Print a WhereLoop object for debugging purposes
-*/
-SQLITE_PRIVATE void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){
- WhereInfo *pWInfo = pWC->pWInfo;
- int nb = 1+(pWInfo->pTabList->nSrc+3)/4;
- SrcItem *pItem = pWInfo->pTabList->a + p->iTab;
- Table *pTab = pItem->pTab;
- Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1;
- sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
- p->iTab, nb, p->maskSelf, nb, p->prereq & mAll);
- sqlite3DebugPrintf(" %12s",
- pItem->zAlias ? pItem->zAlias : pTab->zName);
+**
+** Format example:
+**
+** .--- Position in WHERE clause rSetup, rRun, nOut ---.
+** | |
+** | .--- selfMask nTerm ------. |
+** | | | |
+** | | .-- prereq Idx wsFlags----. | |
+** | | | Name | | |
+** | | | __|__ nEq ---. ___|__ | __|__
+** | / \ / \ / \ | / \ / \ / \
+** 1.002.001 t2.t2xy 2 f 010241 N 2 cost 0,56,31
+*/
+SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){
+ if( pWC ){
+ WhereInfo *pWInfo = pWC->pWInfo;
+ int nb = 1+(pWInfo->pTabList->nSrc+3)/4;
+ SrcItem *pItem = pWInfo->pTabList->a + p->iTab;
+ Table *pTab = pItem->pTab;
+ Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1;
+ sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
+ p->iTab, nb, p->maskSelf, nb, p->prereq & mAll);
+ sqlite3DebugPrintf(" %12s",
+ pItem->zAlias ? pItem->zAlias : pTab->zName);
+ }else{
+ sqlite3DebugPrintf("%c%2d.%03llx.%03llx %c%d",
+ p->cId, p->iTab, p->maskSelf, p->prereq & 0xfff, p->cId, p->iTab);
+ }
if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
const char *zName;
if( p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0 ){
@@ -161535,6 +164469,15 @@ SQLITE_PRIVATE void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){
}
}
}
+SQLITE_PRIVATE void sqlite3ShowWhereLoop(const WhereLoop *p){
+ if( p ) sqlite3WhereLoopPrint(p, 0);
+}
+SQLITE_PRIVATE void sqlite3ShowWhereLoopList(const WhereLoop *p){
+ while( p ){
+ sqlite3ShowWhereLoop(p);
+ p = p->pNextLoop;
+ }
+}
#endif
/*
@@ -161647,46 +164590,60 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
}
/*
-** Return TRUE if all of the following are true:
+** Return TRUE if X is a proper subset of Y but is of equal or less cost.
+** In other words, return true if all constraints of X are also part of Y
+** and Y has additional constraints that might speed the search that X lacks
+** but the cost of running X is not more than the cost of running Y.
+**
+** In other words, return true if the cost relationwship between X and Y
+** is inverted and needs to be adjusted.
+**
+** Case 1:
**
-** (1) X has the same or lower cost, or returns the same or fewer rows,
-** than Y.
-** (2) X uses fewer WHERE clause terms than Y
-** (3) Every WHERE clause term used by X is also used by Y
-** (4) X skips at least as many columns as Y
-** (5) If X is a covering index, than Y is too
+** (1a) X and Y use the same index.
+** (1b) X has fewer == terms than Y
+** (1c) Neither X nor Y use skip-scan
+** (1d) X does not have a a greater cost than Y
**
-** Conditions (2) and (3) mean that X is a "proper subset" of Y.
-** If X is a proper subset of Y then Y is a better choice and ought
-** to have a lower cost. This routine returns TRUE when that cost
-** relationship is inverted and needs to be adjusted. Constraint (4)
-** was added because if X uses skip-scan less than Y it still might
-** deserve a lower cost even if it is a proper subset of Y. Constraint (5)
-** was added because a covering index probably deserves to have a lower cost
-** than a non-covering index even if it is a proper subset.
+** Case 2:
+**
+** (2a) X has the same or lower cost, or returns the same or fewer rows,
+** than Y.
+** (2b) X uses fewer WHERE clause terms than Y
+** (2c) Every WHERE clause term used by X is also used by Y
+** (2d) X skips at least as many columns as Y
+** (2e) If X is a covering index, than Y is too
*/
static int whereLoopCheaperProperSubset(
const WhereLoop *pX, /* First WhereLoop to compare */
const WhereLoop *pY /* Compare against this WhereLoop */
){
int i, j;
+ if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0; /* (1d) and (2a) */
+ assert( (pX->wsFlags & WHERE_VIRTUALTABLE)==0 );
+ assert( (pY->wsFlags & WHERE_VIRTUALTABLE)==0 );
+ if( pX->u.btree.nEq < pY->u.btree.nEq /* (1b) */
+ && pX->u.btree.pIndex==pY->u.btree.pIndex /* (1a) */
+ && pX->nSkip==0 && pY->nSkip==0 /* (1c) */
+ ){
+ return 1; /* Case 1 is true */
+ }
if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){
- return 0; /* X is not a subset of Y */
+ return 0; /* (2b) */
}
- if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0;
- if( pY->nSkip > pX->nSkip ) return 0;
+ if( pY->nSkip > pX->nSkip ) return 0; /* (2d) */
for(i=pX->nLTerm-1; i>=0; i--){
if( pX->aLTerm[i]==0 ) continue;
for(j=pY->nLTerm-1; j>=0; j--){
if( pY->aLTerm[j]==pX->aLTerm[i] ) break;
}
- if( j<0 ) return 0; /* X not a subset of Y since term X[i] not used by Y */
+ if( j<0 ) return 0; /* (2c) */
}
if( (pX->wsFlags&WHERE_IDX_ONLY)!=0
&& (pY->wsFlags&WHERE_IDX_ONLY)==0 ){
- return 0; /* Constraint (5) */
+ return 0; /* (2e) */
}
- return 1; /* All conditions meet */
+ return 1; /* Case 2 is true */
}
/*
@@ -162176,7 +165133,12 @@ static int whereLoopAddBtreeIndex(
assert( pNew->u.btree.nBtm==0 );
opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
}
- if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
+ if( pProbe->bUnordered || pProbe->bLowQual ){
+ if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
+ if( pProbe->bLowQual && pSrc->fg.isIndexedBy==0 ){
+ opMask &= ~(WO_EQ|WO_IN|WO_IS);
+ }
+ }
assert( pNew->u.btree.nEq<pProbe->nColumn );
assert( pNew->u.btree.nEq<pProbe->nKeyCol
@@ -162442,10 +165404,13 @@ static int whereLoopAddBtreeIndex(
}
}
- /* Set rCostIdx to the cost of visiting selected rows in index. Add
- ** it to pNew->rRun, which is currently set to the cost of the index
- ** seek only. Then, if this is a non-covering index, add the cost of
- ** visiting the rows in the main table. */
+ /* Set rCostIdx to the estimated cost of visiting selected rows in the
+ ** index. The estimate is the sum of two values:
+ ** 1. The cost of doing one search-by-key to find the first matching
+ ** entry
+ ** 2. Stepping forward in the index pNew->nOut times to find all
+ ** additional matching entries.
+ */
assert( pSrc->pTab->szTabRow>0 );
if( pProbe->idxType==SQLITE_IDXTYPE_IPK ){
/* The pProbe->szIdxRow is low for an IPK table since the interior
@@ -162456,7 +165421,15 @@ static int whereLoopAddBtreeIndex(
}else{
rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow;
}
- pNew->rRun = sqlite3LogEstAdd(rLogSize, rCostIdx);
+ rCostIdx = sqlite3LogEstAdd(rLogSize, rCostIdx);
+
+ /* Estimate the cost of running the loop. If all data is coming
+ ** from the index, then this is just the cost of doing the index
+ ** lookup and scan. But if some data is coming out of the main table,
+ ** we also have to add in the cost of doing pNew->nOut searches to
+ ** locate the row in the main table that corresponds to the index entry.
+ */
+ pNew->rRun = rCostIdx;
if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK|WHERE_EXPRIDX))==0 ){
pNew->rRun = sqlite3LogEstAdd(pNew->rRun, pNew->nOut + 16);
}
@@ -162562,7 +165535,9 @@ static int indexMightHelpWithOrderBy(
for(ii=0; ii<pOB->nExpr; ii++){
Expr *pExpr = sqlite3ExprSkipCollateAndLikely(pOB->a[ii].pExpr);
if( NEVER(pExpr==0) ) continue;
- if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){
+ if( (pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN)
+ && pExpr->iTable==iCursor
+ ){
if( pExpr->iColumn<0 ) return 1;
for(jj=0; jj<pIndex->nKeyCol; jj++){
if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1;
@@ -162757,6 +165732,100 @@ static SQLITE_NOINLINE u32 whereIsCoveringIndex(
}
/*
+** This is an sqlite3ParserAddCleanup() callback that is invoked to
+** free the Parse->pIdxEpr list when the Parse object is destroyed.
+*/
+static void whereIndexedExprCleanup(sqlite3 *db, void *pObject){
+ IndexedExpr **pp = (IndexedExpr**)pObject;
+ while( *pp!=0 ){
+ IndexedExpr *p = *pp;
+ *pp = p->pIENext;
+ sqlite3ExprDelete(db, p->pExpr);
+ sqlite3DbFreeNN(db, p);
+ }
+}
+
+/*
+** This function is called for a partial index - one with a WHERE clause - in
+** two scenarios. In both cases, it determines whether or not the WHERE
+** clause on the index implies that a column of the table may be safely
+** replaced by a constant expression. For example, in the following
+** SELECT:
+**
+** CREATE INDEX i1 ON t1(b, c) WHERE a=<expr>;
+** SELECT a, b, c FROM t1 WHERE a=<expr> AND b=?;
+**
+** The "a" in the select-list may be replaced by <expr>, iff:
+**
+** (a) <expr> is a constant expression, and
+** (b) The (a=<expr>) comparison uses the BINARY collation sequence, and
+** (c) Column "a" has an affinity other than NONE or BLOB.
+**
+** If argument pItem is NULL, then pMask must not be NULL. In this case this
+** function is being called as part of determining whether or not pIdx
+** is a covering index. This function clears any bits in (*pMask)
+** corresponding to columns that may be replaced by constants as described
+** above.
+**
+** Otherwise, if pItem is not NULL, then this function is being called
+** as part of coding a loop that uses index pIdx. In this case, add entries
+** to the Parse.pIdxPartExpr list for each column that can be replaced
+** by a constant.
+*/
+static void wherePartIdxExpr(
+ Parse *pParse, /* Parse context */
+ Index *pIdx, /* Partial index being processed */
+ Expr *pPart, /* WHERE clause being processed */
+ Bitmask *pMask, /* Mask to clear bits in */
+ int iIdxCur, /* Cursor number for index */
+ SrcItem *pItem /* The FROM clause entry for the table */
+){
+ assert( pItem==0 || (pItem->fg.jointype & JT_RIGHT)==0 );
+ assert( (pItem==0 || pMask==0) && (pMask!=0 || pItem!=0) );
+
+ if( pPart->op==TK_AND ){
+ wherePartIdxExpr(pParse, pIdx, pPart->pRight, pMask, iIdxCur, pItem);
+ pPart = pPart->pLeft;
+ }
+
+ if( (pPart->op==TK_EQ || pPart->op==TK_IS) ){
+ Expr *pLeft = pPart->pLeft;
+ Expr *pRight = pPart->pRight;
+ u8 aff;
+
+ if( pLeft->op!=TK_COLUMN ) return;
+ if( !sqlite3ExprIsConstant(0, pRight) ) return;
+ if( !sqlite3IsBinary(sqlite3ExprCompareCollSeq(pParse, pPart)) ) return;
+ if( pLeft->iColumn<0 ) return;
+ aff = pIdx->pTable->aCol[pLeft->iColumn].affinity;
+ if( aff>=SQLITE_AFF_TEXT ){
+ if( pItem ){
+ sqlite3 *db = pParse->db;
+ IndexedExpr *p = (IndexedExpr*)sqlite3DbMallocRaw(db, sizeof(*p));
+ if( p ){
+ int bNullRow = (pItem->fg.jointype&(JT_LEFT|JT_LTORJ))!=0;
+ p->pExpr = sqlite3ExprDup(db, pRight, 0);
+ p->iDataCur = pItem->iCursor;
+ p->iIdxCur = iIdxCur;
+ p->iIdxCol = pLeft->iColumn;
+ p->bMaybeNullRow = bNullRow;
+ p->pIENext = pParse->pIdxPartExpr;
+ p->aff = aff;
+ pParse->pIdxPartExpr = p;
+ if( p->pIENext==0 ){
+ void *pArg = (void*)&pParse->pIdxPartExpr;
+ sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pArg);
+ }
+ }
+ }else if( pLeft->iColumn<(BMS-1) ){
+ *pMask &= ~((Bitmask)1 << pLeft->iColumn);
+ }
+ }
+ }
+}
+
+
+/*
** Add all WhereLoop objects for a single table of the join where the table
** is identified by pBuilder->pNew->iTab. That table is guaranteed to be
** a b-tree table, not a virtual table.
@@ -162959,9 +166028,6 @@ static int whereLoopAddBtree(
#else
pNew->rRun = rSize + 16;
#endif
- if( IsView(pTab) || (pTab->tabFlags & TF_Ephemeral)!=0 ){
- pNew->wsFlags |= WHERE_VIEWSCAN;
- }
ApplyCostMultiplier(pNew->rRun, pTab->costMult);
whereLoopOutputAdjust(pWC, pNew, rSize);
rc = whereLoopInsert(pBuilder, pNew);
@@ -162974,6 +166040,11 @@ static int whereLoopAddBtree(
pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED;
}else{
m = pSrc->colUsed & pProbe->colNotIdxed;
+ if( pProbe->pPartIdxWhere ){
+ wherePartIdxExpr(
+ pWInfo->pParse, pProbe, pProbe->pPartIdxWhere, &m, 0, 0
+ );
+ }
pNew->wsFlags = WHERE_INDEXED;
if( m==TOPBIT || (pProbe->bHasExpr && !pProbe->bHasVCol && m!=0) ){
u32 isCov = whereIsCoveringIndex(pWInfo, pProbe, pSrc->iCursor);
@@ -163072,7 +166143,7 @@ static int whereLoopAddBtree(
** unique index is used (making the index functionally non-unique)
** then the sqlite_stat1 data becomes important for scoring the
** plan */
- pTab->tabFlags |= TF_StatsUsed;
+ pTab->tabFlags |= TF_MaybeReanalyze;
}
#ifdef SQLITE_ENABLE_STAT4
sqlite3Stat4ProbeFree(pBuilder->pRec);
@@ -163095,6 +166166,21 @@ static int isLimitTerm(WhereTerm *pTerm){
}
/*
+** Return true if the first nCons constraints in the pUsage array are
+** marked as in-use (have argvIndex>0). False otherwise.
+*/
+static int allConstraintsUsed(
+ struct sqlite3_index_constraint_usage *aUsage,
+ int nCons
+){
+ int ii;
+ for(ii=0; ii<nCons; ii++){
+ if( aUsage[ii].argvIndex<=0 ) return 0;
+ }
+ return 1;
+}
+
+/*
** Argument pIdxInfo is already populated with all constraints that may
** be used by the virtual table identified by pBuilder->pNew->iTab. This
** function marks a subset of those constraints usable, invokes the
@@ -163234,13 +166320,20 @@ static int whereLoopAddVirtualOne(
*pbIn = 1; assert( (mExclude & WO_IN)==0 );
}
+ /* Unless pbRetryLimit is non-NULL, there should be no LIMIT/OFFSET
+ ** terms. And if there are any, they should follow all other terms. */
assert( pbRetryLimit || !isLimitTerm(pTerm) );
- if( isLimitTerm(pTerm) && *pbIn ){
+ assert( !isLimitTerm(pTerm) || i>=nConstraint-2 );
+ assert( !isLimitTerm(pTerm) || i==nConstraint-1 || isLimitTerm(pTerm+1) );
+
+ if( isLimitTerm(pTerm) && (*pbIn || !allConstraintsUsed(pUsage, i)) ){
/* If there is an IN(...) term handled as an == (separate call to
** xFilter for each value on the RHS of the IN) and a LIMIT or
- ** OFFSET term handled as well, the plan is unusable. Set output
- ** variable *pbRetryLimit to true to tell the caller to retry with
- ** LIMIT and OFFSET disabled. */
+ ** OFFSET term handled as well, the plan is unusable. Similarly,
+ ** if there is a LIMIT/OFFSET and there are other unused terms,
+ ** the plan cannot be used. In these cases set variable *pbRetryLimit
+ ** to true to tell the caller to retry with LIMIT and OFFSET
+ ** disabled. */
if( pIdxInfo->needToFreeIdxStr ){
sqlite3_free(pIdxInfo->idxStr);
pIdxInfo->idxStr = 0;
@@ -163356,7 +166449,7 @@ SQLITE_API int sqlite3_vtab_rhs_value(
sqlite3_value *pVal = 0;
int rc = SQLITE_OK;
if( iCons<0 || iCons>=pIdxInfo->nConstraint ){
- rc = SQLITE_MISUSE; /* EV: R-30545-25046 */
+ rc = SQLITE_MISUSE_BKPT; /* EV: R-30545-25046 */
}else{
if( pH->aRhs[iCons]==0 ){
WhereTerm *pTerm = &pH->pWC->a[pIdxInfo->aConstraint[iCons].iTermOffset];
@@ -164097,7 +167190,7 @@ static i8 wherePathSatisfiesOrderBy(
if( MASKBIT(i) & obSat ) continue;
p = pOrderBy->a[i].pExpr;
mTerm = sqlite3WhereExprUsage(&pWInfo->sMaskSet,p);
- if( mTerm==0 && !sqlite3ExprIsConstant(p) ) continue;
+ if( mTerm==0 && !sqlite3ExprIsConstant(0,p) ) continue;
if( (mTerm&~orderDistinctMask)==0 ){
obSat |= MASKBIT(i);
}
@@ -164380,14 +167473,6 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
rUnsorted -= 2; /* TUNING: Slight bias in favor of no-sort plans */
}
- /* TUNING: A full-scan of a VIEW or subquery in the outer loop
- ** is not so bad. */
- if( iLoop==0 && (pWLoop->wsFlags & WHERE_VIEWSCAN)!=0 && nLoop>1 ){
- rCost += -10;
- nOut += -30;
- WHERETRACE(0x80,("VIEWSCAN cost reduction for %c\n",pWLoop->cId));
- }
-
/* Check to see if pWLoop should be added to the set of
** mxChoice best-so-far paths.
**
@@ -164574,10 +167659,9 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){
pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
}
- if( pWInfo->pSelect->pOrderBy
- && pWInfo->nOBSat > pWInfo->pSelect->pOrderBy->nExpr ){
- pWInfo->nOBSat = pWInfo->pSelect->pOrderBy->nExpr;
- }
+ /* vvv--- See check-in [12ad822d9b827777] on 2023-03-16 ---vvv */
+ assert( pWInfo->pSelect->pOrderBy==0
+ || pWInfo->nOBSat <= pWInfo->pSelect->pOrderBy->nExpr );
}else{
pWInfo->revMask = pFrom->revLoop;
if( pWInfo->nOBSat<=0 ){
@@ -164620,7 +167704,6 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
}
}
-
pWInfo->nRowOut = pFrom->nRow;
/* Free temporary memory and return success */
@@ -164629,6 +167712,83 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
}
/*
+** This routine implements a heuristic designed to improve query planning.
+** This routine is called in between the first and second call to
+** wherePathSolver(). Hence the name "Interstage" "Heuristic".
+**
+** The first call to wherePathSolver() (hereafter just "solver()") computes
+** the best path without regard to the order of the outputs. The second call
+** to the solver() builds upon the first call to try to find an alternative
+** path that satisfies the ORDER BY clause.
+**
+** This routine looks at the results of the first solver() run, and for
+** every FROM clause term in the resulting query plan that uses an equality
+** constraint against an index, disable other WhereLoops for that same
+** FROM clause term that would try to do a full-table scan. This prevents
+** an index search from being converted into a full-table scan in order to
+** satisfy an ORDER BY clause, since even though we might get slightly better
+** performance using the full-scan without sorting if the output size
+** estimates are very precise, we might also get severe performance
+** degradation using the full-scan if the output size estimate is too large.
+** It is better to err on the side of caution.
+**
+** Except, if the first solver() call generated a full-table scan in an outer
+** loop then stop this analysis at the first full-scan, since the second
+** solver() run might try to swap that full-scan for another in order to
+** get the output into the correct order. In other words, we allow a
+** rewrite like this:
+**
+** First Solver() Second Solver()
+** |-- SCAN t1 |-- SCAN t2
+** |-- SEARCH t2 `-- SEARCH t1
+** `-- SORT USING B-TREE
+**
+** The purpose of this routine is to disallow rewrites such as:
+**
+** First Solver() Second Solver()
+** |-- SEARCH t1 |-- SCAN t2 <--- bad!
+** |-- SEARCH t2 `-- SEARCH t1
+** `-- SORT USING B-TREE
+**
+** See test cases in test/whereN.test for the real-world query that
+** originally provoked this heuristic.
+*/
+static SQLITE_NOINLINE void whereInterstageHeuristic(WhereInfo *pWInfo){
+ int i;
+#ifdef WHERETRACE_ENABLED
+ int once = 0;
+#endif
+ for(i=0; i<pWInfo->nLevel; i++){
+ WhereLoop *p = pWInfo->a[i].pWLoop;
+ if( p==0 ) break;
+ if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 ) continue;
+ if( (p->wsFlags & (WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_IN))!=0 ){
+ u8 iTab = p->iTab;
+ WhereLoop *pLoop;
+ for(pLoop=pWInfo->pLoops; pLoop; pLoop=pLoop->pNextLoop){
+ if( pLoop->iTab!=iTab ) continue;
+ if( (pLoop->wsFlags & (WHERE_CONSTRAINT|WHERE_AUTO_INDEX))!=0 ){
+ /* Auto-index and index-constrained loops allowed to remain */
+ continue;
+ }
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace & 0x80 ){
+ if( once==0 ){
+ sqlite3DebugPrintf("Loops disabled by interstage heuristic:\n");
+ once = 1;
+ }
+ sqlite3WhereLoopPrint(pLoop, &pWInfo->sWC);
+ }
+#endif /* WHERETRACE_ENABLED */
+ pLoop->prereq = ALLBITS; /* Prevent 2nd solver() from using this one */
+ }
+ }else{
+ break;
+ }
+ }
+}
+
+/*
** Most queries use only a single table (they are not joins) and have
** simple == constraints against indexed fields. This routine attempts
** to plan those simple cases using much less ceremony than the
@@ -164916,7 +168076,7 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
SrcItem *pItem = &pWInfo->pTabList->a[pLoop->iTab];
Table *pTab = pItem->pTab;
if( (pTab->tabFlags & TF_HasStat1)==0 ) break;
- pTab->tabFlags |= TF_StatsUsed;
+ pTab->tabFlags |= TF_MaybeReanalyze;
if( i>=1
&& (pLoop->wsFlags & reqFlags)==reqFlags
/* vvvvvv--- Always the case if WHERE_COLUMN_EQ is defined */
@@ -164938,17 +168098,55 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
}
/*
-** This is an sqlite3ParserAddCleanup() callback that is invoked to
-** free the Parse->pIdxEpr list when the Parse object is destroyed.
+** Expression Node callback for sqlite3ExprCanReturnSubtype().
+**
+** Only a function call is able to return a subtype. So if the node
+** is not a function call, return WRC_Prune immediately.
+**
+** A function call is able to return a subtype if it has the
+** SQLITE_RESULT_SUBTYPE property.
+**
+** Assume that every function is able to pass-through a subtype from
+** one of its argument (using sqlite3_result_value()). Most functions
+** are not this way, but we don't have a mechanism to distinguish those
+** that are from those that are not, so assume they all work this way.
+** That means that if one of its arguments is another function and that
+** other function is able to return a subtype, then this function is
+** able to return a subtype.
*/
-static void whereIndexedExprCleanup(sqlite3 *db, void *pObject){
- Parse *pParse = (Parse*)pObject;
- while( pParse->pIdxEpr!=0 ){
- IndexedExpr *p = pParse->pIdxEpr;
- pParse->pIdxEpr = p->pIENext;
- sqlite3ExprDelete(db, p->pExpr);
- sqlite3DbFreeNN(db, p);
+static int exprNodeCanReturnSubtype(Walker *pWalker, Expr *pExpr){
+ int n;
+ FuncDef *pDef;
+ sqlite3 *db;
+ if( pExpr->op!=TK_FUNCTION ){
+ return WRC_Prune;
}
+ assert( ExprUseXList(pExpr) );
+ db = pWalker->pParse->db;
+ n = pExpr->x.pList ? pExpr->x.pList->nExpr : 0;
+ pDef = sqlite3FindFunction(db, pExpr->u.zToken, n, ENC(db), 0);
+ if( pDef==0 || (pDef->funcFlags & SQLITE_RESULT_SUBTYPE)!=0 ){
+ pWalker->eCode = 1;
+ return WRC_Prune;
+ }
+ return WRC_Continue;
+}
+
+/*
+** Return TRUE if expression pExpr is able to return a subtype.
+**
+** A TRUE return does not guarantee that a subtype will be returned.
+** It only indicates that a subtype return is possible. False positives
+** are acceptable as they only disable an optimization. False negatives,
+** on the other hand, can lead to incorrect answers.
+*/
+static int sqlite3ExprCanReturnSubtype(Parse *pParse, Expr *pExpr){
+ Walker w;
+ memset(&w, 0, sizeof(w));
+ w.pParse = pParse;
+ w.xExprCallback = exprNodeCanReturnSubtype;
+ sqlite3WalkExpr(&w, pExpr);
+ return w.eCode;
}
/*
@@ -164976,20 +168174,20 @@ static SQLITE_NOINLINE void whereAddIndexedExpr(
for(i=0; i<pIdx->nColumn; i++){
Expr *pExpr;
int j = pIdx->aiColumn[i];
- int bMaybeNullRow;
if( j==XN_EXPR ){
pExpr = pIdx->aColExpr->a[i].pExpr;
- testcase( pTabItem->fg.jointype & JT_LEFT );
- testcase( pTabItem->fg.jointype & JT_RIGHT );
- testcase( pTabItem->fg.jointype & JT_LTORJ );
- bMaybeNullRow = (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0;
}else if( j>=0 && (pTab->aCol[j].colFlags & COLFLAG_VIRTUAL)!=0 ){
pExpr = sqlite3ColumnExpr(pTab, &pTab->aCol[j]);
- bMaybeNullRow = 0;
}else{
continue;
}
- if( sqlite3ExprIsConstant(pExpr) ) continue;
+ if( sqlite3ExprIsConstant(0,pExpr) ) continue;
+ if( pExpr->op==TK_FUNCTION && sqlite3ExprCanReturnSubtype(pParse,pExpr) ){
+ /* Functions that might set a subtype should not be replaced by the
+ ** value taken from an expression index since the index omits the
+ ** subtype. https://sqlite.org/forum/forumpost/68d284c86b082c3e */
+ continue;
+ }
p = sqlite3DbMallocRaw(pParse->db, sizeof(IndexedExpr));
if( p==0 ) break;
p->pIENext = pParse->pIdxEpr;
@@ -165003,7 +168201,7 @@ static SQLITE_NOINLINE void whereAddIndexedExpr(
p->iDataCur = pTabItem->iCursor;
p->iIdxCur = iIdxCur;
p->iIdxCol = i;
- p->bMaybeNullRow = bMaybeNullRow;
+ p->bMaybeNullRow = (pTabItem->fg.jointype & (JT_LEFT|JT_LTORJ|JT_RIGHT))!=0;
if( sqlite3IndexAffinityStr(pParse->db, pIdx) ){
p->aff = pIdx->zColAff[i];
}
@@ -165012,7 +168210,8 @@ static SQLITE_NOINLINE void whereAddIndexedExpr(
#endif
pParse->pIdxEpr = p;
if( p->pIENext==0 ){
- sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pParse);
+ void *pArg = (void*)&pParse->pIdxEpr;
+ sqlite3ParserAddCleanup(pParse, whereIndexedExprCleanup, pArg);
}
}
}
@@ -165167,7 +168366,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
/* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */
testcase( pOrderBy && pOrderBy->nExpr==BMS-1 );
- if( pOrderBy && pOrderBy->nExpr>=BMS ) pOrderBy = 0;
+ if( pOrderBy && pOrderBy->nExpr>=BMS ){
+ pOrderBy = 0;
+ wctrlFlags &= ~WHERE_WANT_DISTINCT;
+ }
/* The number of tables in the FROM clause is limited by the number of
** bits in a Bitmask
@@ -165192,7 +168394,10 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
** field (type Bitmask) it must be aligned on an 8-byte boundary on
** some architectures. Hence the ROUND8() below.
*/
- nByteWInfo = ROUND8P(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel));
+ nByteWInfo = ROUND8P(sizeof(WhereInfo));
+ if( nTabList>1 ){
+ nByteWInfo = ROUND8P(nByteWInfo + (nTabList-1)*sizeof(WhereLevel));
+ }
pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop));
if( db->mallocFailed ){
sqlite3DbFree(db, pWInfo);
@@ -165246,7 +168451,11 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
){
pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
}
- ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW"));
+ if( ALWAYS(pWInfo->pSelect)
+ && (pWInfo->pSelect->selFlags & SF_MultiValue)==0
+ ){
+ ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW"));
+ }
}else{
/* Assign a bit from the bitmask to every term in the FROM clause.
**
@@ -165399,9 +168608,20 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
wherePathSolver(pWInfo, 0);
if( db->mallocFailed ) goto whereBeginError;
if( pWInfo->pOrderBy ){
+ whereInterstageHeuristic(pWInfo);
wherePathSolver(pWInfo, pWInfo->nRowOut+1);
if( db->mallocFailed ) goto whereBeginError;
}
+
+ /* TUNING: Assume that a DISTINCT clause on a subquery reduces
+ ** the output size by a factor of 8 (LogEst -30).
+ */
+ if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)!=0 ){
+ WHERETRACE(0x0080,("nRowOut reduced from %d to %d due to DISTINCT\n",
+ pWInfo->nRowOut, pWInfo->nRowOut-30));
+ pWInfo->nRowOut -= 30;
+ }
+
}
assert( pWInfo->pTabList!=0 );
if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){
@@ -165614,6 +168834,11 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
if( pIx->bHasExpr && OptimizationEnabled(db, SQLITE_IndexedExpr) ){
whereAddIndexedExpr(pParse, pIx, iIndexCur, pTabItem);
}
+ if( pIx->pPartIdxWhere && (pTabItem->fg.jointype & JT_RIGHT)==0 ){
+ wherePartIdxExpr(
+ pParse, pIx, pIx->pPartIdxWhere, 0, iIndexCur, pTabItem
+ );
+ }
}
pLevel->iIdxCur = iIndexCur;
assert( pIx!=0 );
@@ -165739,6 +168964,11 @@ whereBeginError:
pParse->nQueryLoop = pWInfo->savedNQueryLoop;
whereInfoFree(db, pWInfo);
}
+#ifdef WHERETRACE_ENABLED
+ /* Prevent harmless compiler warnings about debugging routines
+ ** being declared but never used */
+ sqlite3ShowWhereLoopList(0);
+#endif /* WHERETRACE_ENABLED */
return 0;
}
@@ -165928,7 +169158,15 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v);
assert( (ws & WHERE_IDX_ONLY)==0 || (ws & WHERE_INDEXED)!=0 );
if( (ws & WHERE_IDX_ONLY)==0 ){
- assert( pLevel->iTabCur==pTabList->a[pLevel->iFrom].iCursor );
+ SrcItem *pSrc = &pTabList->a[pLevel->iFrom];
+ assert( pLevel->iTabCur==pSrc->iCursor );
+ if( pSrc->fg.viaCoroutine ){
+ int m, n;
+ n = pSrc->regResult;
+ assert( pSrc->pTab!=0 );
+ m = pSrc->pTab->nCol;
+ sqlite3VdbeAddOp3(v, OP_Null, 0, n, n+m-1);
+ }
sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iTabCur);
}
if( (ws & WHERE_INDEXED)
@@ -165978,6 +169216,7 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
*/
if( pTabItem->fg.viaCoroutine ){
testcase( pParse->db->mallocFailed );
+ assert( pTabItem->regResult>=0 );
translateColumnToCopy(pParse, pLevel->addrBody, pLevel->iTabCur,
pTabItem->regResult, 0);
continue;
@@ -167156,7 +170395,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
assert( ExprUseXList(pWin->pOwner) );
assert( pWin->pWFunc!=0 );
pArgs = pWin->pOwner->x.pList;
- if( pWin->pWFunc->funcFlags & SQLITE_FUNC_SUBTYPE ){
+ if( pWin->pWFunc->funcFlags & SQLITE_SUBTYPE ){
selectWindowRewriteEList(pParse, pMWin, pSrc, pArgs, pTab, &pSublist);
pWin->iArgCol = (pSublist ? pSublist->nExpr : 0);
pWin->bExprArgs = 1;
@@ -167282,7 +170521,7 @@ SQLITE_PRIVATE void sqlite3WindowListDelete(sqlite3 *db, Window *p){
** variable values in the expression tree.
*/
static Expr *sqlite3WindowOffsetExpr(Parse *pParse, Expr *pExpr){
- if( 0==sqlite3ExprIsConstant(pExpr) ){
+ if( 0==sqlite3ExprIsConstant(0,pExpr) ){
if( IN_RENAME_OBJECT ) sqlite3RenameExprUnmap(pParse, pExpr);
sqlite3ExprDelete(pParse->db, pExpr);
pExpr = sqlite3ExprAlloc(pParse->db, TK_NULL, 0, 0);
@@ -167430,8 +170669,9 @@ SQLITE_PRIVATE void sqlite3WindowAttach(Parse *pParse, Expr *p, Window *pWin){
if( p ){
assert( p->op==TK_FUNCTION );
assert( pWin );
+ assert( ExprIsFullSize(p) );
p->y.pWin = pWin;
- ExprSetProperty(p, EP_WinFunc);
+ ExprSetProperty(p, EP_WinFunc|EP_FullSize);
pWin->pOwner = p;
if( (p->flags & EP_Distinct) && pWin->eFrmType!=TK_FILTER ){
sqlite3ErrorMsg(pParse,
@@ -169373,6 +172613,14 @@ static void updateDeleteLimitError(
return pSelect;
}
+ /* Memory allocator for parser stack resizing. This is a thin wrapper around
+ ** sqlite3_realloc() that includes a call to sqlite3FaultSim() to facilitate
+ ** testing.
+ */
+ static void *parserStackRealloc(void *pOld, sqlite3_uint64 newSize){
+ return sqlite3FaultSim(700) ? 0 : sqlite3_realloc(pOld, newSize);
+ }
+
/* Construct a new Expr object from a single token */
static Expr *tokenExpr(Parse *pParse, int op, Token t){
@@ -169622,8 +172870,8 @@ static void updateDeleteLimitError(
#define TK_TRUEFALSE 170
#define TK_ISNOT 171
#define TK_FUNCTION 172
-#define TK_UMINUS 173
-#define TK_UPLUS 174
+#define TK_UPLUS 173
+#define TK_UMINUS 174
#define TK_TRUTH 175
#define TK_REGISTER 176
#define TK_VECTOR 177
@@ -169632,8 +172880,9 @@ static void updateDeleteLimitError(
#define TK_ASTERISK 180
#define TK_SPAN 181
#define TK_ERROR 182
-#define TK_SPACE 183
-#define TK_ILLEGAL 184
+#define TK_QNUMBER 183
+#define TK_SPACE 184
+#define TK_ILLEGAL 185
#endif
/**************** End token definitions ***************************************/
@@ -169674,6 +172923,9 @@ static void updateDeleteLimitError(
** sqlite3ParserARG_STORE Code to store %extra_argument into yypParser
** sqlite3ParserARG_FETCH Code to extract %extra_argument from yypParser
** sqlite3ParserCTX_* As sqlite3ParserARG_ except for %extra_context
+** YYREALLOC Name of the realloc() function to use
+** YYFREE Name of the free() function to use
+** YYDYNSTACK True if stack space should be extended on heap
** YYERRORSYMBOL is the code number of the error symbol. If not
** defined, then do no error processing.
** YYNSTATE the combined number of states.
@@ -169687,37 +172939,39 @@ static void updateDeleteLimitError(
** YY_NO_ACTION The yy_action[] code for no-op
** YY_MIN_REDUCE Minimum value for reduce actions
** YY_MAX_REDUCE Maximum value for reduce actions
+** YY_MIN_DSTRCTR Minimum symbol value that has a destructor
+** YY_MAX_DSTRCTR Maximum symbol value that has a destructor
*/
#ifndef INTERFACE
# define INTERFACE 1
#endif
/************* Begin control #defines *****************************************/
#define YYCODETYPE unsigned short int
-#define YYNOCODE 319
+#define YYNOCODE 322
#define YYACTIONTYPE unsigned short int
#define YYWILDCARD 101
#define sqlite3ParserTOKENTYPE Token
typedef union {
int yyinit;
sqlite3ParserTOKENTYPE yy0;
- TriggerStep* yy33;
- Window* yy41;
- Select* yy47;
- SrcList* yy131;
- struct TrigEvent yy180;
- struct {int value; int mask;} yy231;
- IdList* yy254;
- u32 yy285;
- ExprList* yy322;
- Cte* yy385;
- int yy394;
- Upsert* yy444;
- u8 yy516;
- With* yy521;
- const char* yy522;
- Expr* yy528;
- OnOrUsing yy561;
- struct FrameBound yy595;
+ ExprList* yy14;
+ With* yy59;
+ Cte* yy67;
+ Upsert* yy122;
+ IdList* yy132;
+ int yy144;
+ const char* yy168;
+ SrcList* yy203;
+ Window* yy211;
+ OnOrUsing yy269;
+ struct TrigEvent yy286;
+ struct {int value; int mask;} yy383;
+ u32 yy391;
+ TriggerStep* yy427;
+ Expr* yy454;
+ u8 yy462;
+ struct FrameBound yy509;
+ Select* yy555;
} YYMINORTYPE;
#ifndef YYSTACKDEPTH
#define YYSTACKDEPTH 100
@@ -169727,24 +172981,29 @@ typedef union {
#define sqlite3ParserARG_PARAM
#define sqlite3ParserARG_FETCH
#define sqlite3ParserARG_STORE
+#define YYREALLOC parserStackRealloc
+#define YYFREE sqlite3_free
+#define YYDYNSTACK 1
#define sqlite3ParserCTX_SDECL Parse *pParse;
#define sqlite3ParserCTX_PDECL ,Parse *pParse
#define sqlite3ParserCTX_PARAM ,pParse
#define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse;
#define sqlite3ParserCTX_STORE yypParser->pParse=pParse;
#define YYFALLBACK 1
-#define YYNSTATE 575
-#define YYNRULE 403
-#define YYNRULE_WITH_ACTION 338
-#define YYNTOKEN 185
-#define YY_MAX_SHIFT 574
-#define YY_MIN_SHIFTREDUCE 833
-#define YY_MAX_SHIFTREDUCE 1235
-#define YY_ERROR_ACTION 1236
-#define YY_ACCEPT_ACTION 1237
-#define YY_NO_ACTION 1238
-#define YY_MIN_REDUCE 1239
-#define YY_MAX_REDUCE 1641
+#define YYNSTATE 583
+#define YYNRULE 409
+#define YYNRULE_WITH_ACTION 344
+#define YYNTOKEN 186
+#define YY_MAX_SHIFT 582
+#define YY_MIN_SHIFTREDUCE 845
+#define YY_MAX_SHIFTREDUCE 1253
+#define YY_ERROR_ACTION 1254
+#define YY_ACCEPT_ACTION 1255
+#define YY_NO_ACTION 1256
+#define YY_MIN_REDUCE 1257
+#define YY_MAX_REDUCE 1665
+#define YY_MIN_DSTRCTR 205
+#define YY_MAX_DSTRCTR 319
/************* End control #defines *******************************************/
#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))
@@ -169760,6 +173019,22 @@ typedef union {
# define yytestcase(X)
#endif
+/* Macro to determine if stack space has the ability to grow using
+** heap memory.
+*/
+#if YYSTACKDEPTH<=0 || YYDYNSTACK
+# define YYGROWABLESTACK 1
+#else
+# define YYGROWABLESTACK 0
+#endif
+
+/* Guarantee a minimum number of initial stack slots.
+*/
+#if YYSTACKDEPTH<=0
+# undef YYSTACKDEPTH
+# define YYSTACKDEPTH 2 /* Need a minimum stack size */
+#endif
+
/* Next are the tables used to determine what action to take based on the
** current state and lookahead token. These tables are used to implement
@@ -169811,618 +173086,630 @@ typedef union {
** yy_default[] Default action for each state.
**
*********** Begin parsing tables **********************************************/
-#define YY_ACTTAB_COUNT (2096)
+#define YY_ACTTAB_COUNT (2142)
static const YYACTIONTYPE yy_action[] = {
- /* 0 */ 568, 208, 568, 118, 115, 229, 568, 118, 115, 229,
- /* 10 */ 568, 1310, 377, 1289, 408, 562, 562, 562, 568, 409,
- /* 20 */ 378, 1310, 1272, 41, 41, 41, 41, 208, 1520, 71,
- /* 30 */ 71, 969, 419, 41, 41, 491, 303, 279, 303, 970,
- /* 40 */ 397, 71, 71, 125, 126, 80, 1210, 1210, 1047, 1050,
- /* 50 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 476, 409,
- /* 60 */ 1237, 1, 1, 574, 2, 1241, 550, 118, 115, 229,
- /* 70 */ 317, 480, 146, 480, 524, 118, 115, 229, 529, 1323,
- /* 80 */ 417, 523, 142, 125, 126, 80, 1210, 1210, 1047, 1050,
- /* 90 */ 1037, 1037, 123, 123, 124, 124, 124, 124, 118, 115,
- /* 100 */ 229, 327, 122, 122, 122, 122, 121, 121, 120, 120,
- /* 110 */ 120, 119, 116, 444, 284, 284, 284, 284, 442, 442,
- /* 120 */ 442, 1559, 376, 1561, 1186, 375, 1157, 565, 1157, 565,
- /* 130 */ 409, 1559, 537, 259, 226, 444, 101, 145, 449, 316,
- /* 140 */ 559, 240, 122, 122, 122, 122, 121, 121, 120, 120,
- /* 150 */ 120, 119, 116, 444, 125, 126, 80, 1210, 1210, 1047,
- /* 160 */ 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, 142,
- /* 170 */ 294, 1186, 339, 448, 120, 120, 120, 119, 116, 444,
- /* 180 */ 127, 1186, 1187, 1186, 148, 441, 440, 568, 119, 116,
- /* 190 */ 444, 124, 124, 124, 124, 117, 122, 122, 122, 122,
- /* 200 */ 121, 121, 120, 120, 120, 119, 116, 444, 454, 113,
- /* 210 */ 13, 13, 546, 122, 122, 122, 122, 121, 121, 120,
- /* 220 */ 120, 120, 119, 116, 444, 422, 316, 559, 1186, 1187,
- /* 230 */ 1186, 149, 1218, 409, 1218, 124, 124, 124, 124, 122,
- /* 240 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116,
- /* 250 */ 444, 465, 342, 1034, 1034, 1048, 1051, 125, 126, 80,
- /* 260 */ 1210, 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124,
- /* 270 */ 124, 124, 1275, 522, 222, 1186, 568, 409, 224, 514,
- /* 280 */ 175, 82, 83, 122, 122, 122, 122, 121, 121, 120,
- /* 290 */ 120, 120, 119, 116, 444, 1005, 16, 16, 1186, 133,
- /* 300 */ 133, 125, 126, 80, 1210, 1210, 1047, 1050, 1037, 1037,
- /* 310 */ 123, 123, 124, 124, 124, 124, 122, 122, 122, 122,
- /* 320 */ 121, 121, 120, 120, 120, 119, 116, 444, 1038, 546,
- /* 330 */ 1186, 373, 1186, 1187, 1186, 252, 1429, 399, 504, 501,
- /* 340 */ 500, 111, 560, 566, 4, 924, 924, 433, 499, 340,
- /* 350 */ 460, 328, 360, 394, 1231, 1186, 1187, 1186, 563, 568,
- /* 360 */ 122, 122, 122, 122, 121, 121, 120, 120, 120, 119,
- /* 370 */ 116, 444, 284, 284, 369, 1572, 1598, 441, 440, 154,
- /* 380 */ 409, 445, 71, 71, 1282, 565, 1215, 1186, 1187, 1186,
- /* 390 */ 85, 1217, 271, 557, 543, 515, 515, 568, 98, 1216,
- /* 400 */ 6, 1274, 472, 142, 125, 126, 80, 1210, 1210, 1047,
- /* 410 */ 1050, 1037, 1037, 123, 123, 124, 124, 124, 124, 550,
- /* 420 */ 13, 13, 1024, 507, 1218, 1186, 1218, 549, 109, 109,
- /* 430 */ 222, 568, 1232, 175, 568, 427, 110, 197, 445, 569,
- /* 440 */ 445, 430, 1546, 1014, 325, 551, 1186, 270, 287, 368,
- /* 450 */ 510, 363, 509, 257, 71, 71, 543, 71, 71, 359,
- /* 460 */ 316, 559, 1604, 122, 122, 122, 122, 121, 121, 120,
- /* 470 */ 120, 120, 119, 116, 444, 1014, 1014, 1016, 1017, 27,
- /* 480 */ 284, 284, 1186, 1187, 1186, 1152, 568, 1603, 409, 899,
- /* 490 */ 190, 550, 356, 565, 550, 935, 533, 517, 1152, 516,
- /* 500 */ 413, 1152, 552, 1186, 1187, 1186, 568, 544, 544, 51,
- /* 510 */ 51, 214, 125, 126, 80, 1210, 1210, 1047, 1050, 1037,
- /* 520 */ 1037, 123, 123, 124, 124, 124, 124, 1186, 474, 135,
- /* 530 */ 135, 409, 284, 284, 1484, 505, 121, 121, 120, 120,
- /* 540 */ 120, 119, 116, 444, 1005, 565, 518, 217, 541, 541,
- /* 550 */ 316, 559, 142, 6, 532, 125, 126, 80, 1210, 1210,
- /* 560 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124,
- /* 570 */ 1548, 122, 122, 122, 122, 121, 121, 120, 120, 120,
- /* 580 */ 119, 116, 444, 485, 1186, 1187, 1186, 482, 281, 1263,
- /* 590 */ 955, 252, 1186, 373, 504, 501, 500, 1186, 340, 570,
- /* 600 */ 1186, 570, 409, 292, 499, 955, 874, 191, 480, 316,
- /* 610 */ 559, 384, 290, 380, 122, 122, 122, 122, 121, 121,
- /* 620 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1210,
- /* 630 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
- /* 640 */ 124, 409, 394, 1132, 1186, 867, 100, 284, 284, 1186,
- /* 650 */ 1187, 1186, 373, 1089, 1186, 1187, 1186, 1186, 1187, 1186,
- /* 660 */ 565, 455, 32, 373, 233, 125, 126, 80, 1210, 1210,
- /* 670 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124,
- /* 680 */ 1428, 957, 568, 228, 956, 122, 122, 122, 122, 121,
- /* 690 */ 121, 120, 120, 120, 119, 116, 444, 1152, 228, 1186,
- /* 700 */ 157, 1186, 1187, 1186, 1547, 13, 13, 301, 955, 1226,
- /* 710 */ 1152, 153, 409, 1152, 373, 1575, 1170, 5, 369, 1572,
- /* 720 */ 429, 1232, 3, 955, 122, 122, 122, 122, 121, 121,
- /* 730 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1210,
- /* 740 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
- /* 750 */ 124, 409, 208, 567, 1186, 1025, 1186, 1187, 1186, 1186,
- /* 760 */ 388, 850, 155, 1546, 286, 402, 1094, 1094, 488, 568,
- /* 770 */ 465, 342, 1315, 1315, 1546, 125, 126, 80, 1210, 1210,
- /* 780 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124,
- /* 790 */ 129, 568, 13, 13, 374, 122, 122, 122, 122, 121,
- /* 800 */ 121, 120, 120, 120, 119, 116, 444, 302, 568, 453,
- /* 810 */ 528, 1186, 1187, 1186, 13, 13, 1186, 1187, 1186, 1293,
- /* 820 */ 463, 1263, 409, 1313, 1313, 1546, 1010, 453, 452, 200,
- /* 830 */ 299, 71, 71, 1261, 122, 122, 122, 122, 121, 121,
- /* 840 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1210,
- /* 850 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
- /* 860 */ 124, 409, 227, 1069, 1152, 284, 284, 419, 312, 278,
- /* 870 */ 278, 285, 285, 1415, 406, 405, 382, 1152, 565, 568,
- /* 880 */ 1152, 1189, 565, 1592, 565, 125, 126, 80, 1210, 1210,
- /* 890 */ 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124, 124,
- /* 900 */ 453, 1476, 13, 13, 1530, 122, 122, 122, 122, 121,
- /* 910 */ 121, 120, 120, 120, 119, 116, 444, 201, 568, 354,
- /* 920 */ 1578, 574, 2, 1241, 838, 839, 840, 1554, 317, 1205,
- /* 930 */ 146, 6, 409, 255, 254, 253, 206, 1323, 9, 1189,
- /* 940 */ 262, 71, 71, 424, 122, 122, 122, 122, 121, 121,
- /* 950 */ 120, 120, 120, 119, 116, 444, 125, 126, 80, 1210,
- /* 960 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
- /* 970 */ 124, 568, 284, 284, 568, 1206, 409, 573, 313, 1241,
- /* 980 */ 349, 1292, 352, 419, 317, 565, 146, 491, 525, 1635,
- /* 990 */ 395, 371, 491, 1323, 70, 70, 1291, 71, 71, 240,
- /* 1000 */ 1321, 104, 80, 1210, 1210, 1047, 1050, 1037, 1037, 123,
- /* 1010 */ 123, 124, 124, 124, 124, 122, 122, 122, 122, 121,
- /* 1020 */ 121, 120, 120, 120, 119, 116, 444, 1110, 284, 284,
- /* 1030 */ 428, 448, 1519, 1206, 439, 284, 284, 1483, 1348, 311,
- /* 1040 */ 474, 565, 1111, 969, 491, 491, 217, 1259, 565, 1532,
- /* 1050 */ 568, 970, 207, 568, 1024, 240, 383, 1112, 519, 122,
- /* 1060 */ 122, 122, 122, 121, 121, 120, 120, 120, 119, 116,
- /* 1070 */ 444, 1015, 107, 71, 71, 1014, 13, 13, 910, 568,
- /* 1080 */ 1489, 568, 284, 284, 97, 526, 491, 448, 911, 1322,
- /* 1090 */ 1318, 545, 409, 284, 284, 565, 151, 209, 1489, 1491,
- /* 1100 */ 262, 450, 55, 55, 56, 56, 565, 1014, 1014, 1016,
- /* 1110 */ 443, 332, 409, 527, 12, 295, 125, 126, 80, 1210,
- /* 1120 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
- /* 1130 */ 124, 347, 409, 862, 1528, 1206, 125, 126, 80, 1210,
- /* 1140 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
- /* 1150 */ 124, 1133, 1633, 474, 1633, 371, 125, 114, 80, 1210,
- /* 1160 */ 1210, 1047, 1050, 1037, 1037, 123, 123, 124, 124, 124,
- /* 1170 */ 124, 1489, 329, 474, 331, 122, 122, 122, 122, 121,
- /* 1180 */ 121, 120, 120, 120, 119, 116, 444, 203, 1415, 568,
- /* 1190 */ 1290, 862, 464, 1206, 436, 122, 122, 122, 122, 121,
- /* 1200 */ 121, 120, 120, 120, 119, 116, 444, 553, 1133, 1634,
- /* 1210 */ 539, 1634, 15, 15, 890, 122, 122, 122, 122, 121,
- /* 1220 */ 121, 120, 120, 120, 119, 116, 444, 568, 298, 538,
- /* 1230 */ 1131, 1415, 1552, 1553, 1327, 409, 6, 6, 1163, 1264,
- /* 1240 */ 415, 320, 284, 284, 1415, 508, 565, 525, 300, 457,
- /* 1250 */ 43, 43, 568, 891, 12, 565, 330, 478, 425, 407,
- /* 1260 */ 126, 80, 1210, 1210, 1047, 1050, 1037, 1037, 123, 123,
- /* 1270 */ 124, 124, 124, 124, 568, 57, 57, 288, 1186, 1415,
- /* 1280 */ 496, 458, 392, 392, 391, 273, 389, 1131, 1551, 847,
- /* 1290 */ 1163, 407, 6, 568, 321, 1152, 470, 44, 44, 1550,
- /* 1300 */ 1110, 426, 234, 6, 323, 256, 540, 256, 1152, 431,
- /* 1310 */ 568, 1152, 322, 17, 487, 1111, 58, 58, 122, 122,
- /* 1320 */ 122, 122, 121, 121, 120, 120, 120, 119, 116, 444,
- /* 1330 */ 1112, 216, 481, 59, 59, 1186, 1187, 1186, 111, 560,
- /* 1340 */ 324, 4, 236, 456, 526, 568, 237, 456, 568, 437,
- /* 1350 */ 168, 556, 420, 141, 479, 563, 568, 293, 568, 1091,
- /* 1360 */ 568, 293, 568, 1091, 531, 568, 870, 8, 60, 60,
- /* 1370 */ 235, 61, 61, 568, 414, 568, 414, 568, 445, 62,
- /* 1380 */ 62, 45, 45, 46, 46, 47, 47, 199, 49, 49,
- /* 1390 */ 557, 568, 359, 568, 100, 486, 50, 50, 63, 63,
- /* 1400 */ 64, 64, 561, 415, 535, 410, 568, 1024, 568, 534,
- /* 1410 */ 316, 559, 316, 559, 65, 65, 14, 14, 568, 1024,
- /* 1420 */ 568, 512, 930, 870, 1015, 109, 109, 929, 1014, 66,
- /* 1430 */ 66, 131, 131, 110, 451, 445, 569, 445, 416, 177,
- /* 1440 */ 1014, 132, 132, 67, 67, 568, 467, 568, 930, 471,
- /* 1450 */ 1360, 283, 226, 929, 315, 1359, 407, 568, 459, 407,
- /* 1460 */ 1014, 1014, 1016, 239, 407, 86, 213, 1346, 52, 52,
- /* 1470 */ 68, 68, 1014, 1014, 1016, 1017, 27, 1577, 1174, 447,
- /* 1480 */ 69, 69, 288, 97, 108, 1535, 106, 392, 392, 391,
- /* 1490 */ 273, 389, 568, 877, 847, 881, 568, 111, 560, 466,
- /* 1500 */ 4, 568, 152, 30, 38, 568, 1128, 234, 396, 323,
- /* 1510 */ 111, 560, 527, 4, 563, 53, 53, 322, 568, 163,
- /* 1520 */ 163, 568, 337, 468, 164, 164, 333, 563, 76, 76,
- /* 1530 */ 568, 289, 1508, 568, 31, 1507, 568, 445, 338, 483,
- /* 1540 */ 100, 54, 54, 344, 72, 72, 296, 236, 1076, 557,
- /* 1550 */ 445, 877, 1356, 134, 134, 168, 73, 73, 141, 161,
- /* 1560 */ 161, 1566, 557, 535, 568, 319, 568, 348, 536, 1007,
- /* 1570 */ 473, 261, 261, 889, 888, 235, 535, 568, 1024, 568,
- /* 1580 */ 475, 534, 261, 367, 109, 109, 521, 136, 136, 130,
- /* 1590 */ 130, 1024, 110, 366, 445, 569, 445, 109, 109, 1014,
- /* 1600 */ 162, 162, 156, 156, 568, 110, 1076, 445, 569, 445,
- /* 1610 */ 410, 351, 1014, 568, 353, 316, 559, 568, 343, 568,
- /* 1620 */ 100, 497, 357, 258, 100, 896, 897, 140, 140, 355,
- /* 1630 */ 1306, 1014, 1014, 1016, 1017, 27, 139, 139, 362, 451,
- /* 1640 */ 137, 137, 138, 138, 1014, 1014, 1016, 1017, 27, 1174,
- /* 1650 */ 447, 568, 372, 288, 111, 560, 1018, 4, 392, 392,
- /* 1660 */ 391, 273, 389, 568, 1137, 847, 568, 1072, 568, 258,
- /* 1670 */ 492, 563, 568, 211, 75, 75, 555, 960, 234, 261,
- /* 1680 */ 323, 111, 560, 927, 4, 113, 77, 77, 322, 74,
- /* 1690 */ 74, 42, 42, 1369, 445, 48, 48, 1414, 563, 972,
- /* 1700 */ 973, 1088, 1087, 1088, 1087, 860, 557, 150, 928, 1342,
- /* 1710 */ 113, 1354, 554, 1419, 1018, 1271, 1262, 1250, 236, 1249,
- /* 1720 */ 1251, 445, 1585, 1339, 308, 276, 168, 309, 11, 141,
- /* 1730 */ 393, 310, 232, 557, 1401, 1024, 335, 291, 1396, 219,
- /* 1740 */ 336, 109, 109, 934, 297, 1406, 235, 341, 477, 110,
- /* 1750 */ 502, 445, 569, 445, 1389, 1405, 1014, 400, 1289, 365,
- /* 1760 */ 223, 1480, 1024, 1479, 1351, 1352, 1350, 1349, 109, 109,
- /* 1770 */ 204, 1588, 1226, 558, 265, 218, 110, 205, 445, 569,
- /* 1780 */ 445, 410, 387, 1014, 1527, 179, 316, 559, 1014, 1014,
- /* 1790 */ 1016, 1017, 27, 230, 1525, 1223, 79, 560, 85, 4,
- /* 1800 */ 418, 215, 548, 81, 84, 188, 1402, 173, 181, 461,
- /* 1810 */ 451, 35, 462, 563, 183, 1014, 1014, 1016, 1017, 27,
- /* 1820 */ 184, 1485, 185, 186, 495, 242, 98, 398, 1408, 36,
- /* 1830 */ 1407, 484, 91, 469, 401, 1410, 445, 192, 1474, 246,
- /* 1840 */ 1496, 490, 346, 277, 248, 196, 493, 511, 557, 350,
- /* 1850 */ 1252, 249, 250, 403, 1309, 1308, 111, 560, 432, 4,
- /* 1860 */ 1307, 1300, 93, 1602, 881, 1601, 224, 404, 434, 520,
- /* 1870 */ 263, 435, 1571, 563, 1279, 1278, 364, 1024, 306, 1277,
- /* 1880 */ 264, 1600, 1557, 109, 109, 370, 1299, 307, 1556, 438,
- /* 1890 */ 128, 110, 1374, 445, 569, 445, 445, 546, 1014, 10,
- /* 1900 */ 1461, 105, 381, 1373, 34, 571, 99, 1332, 557, 314,
- /* 1910 */ 1180, 530, 272, 274, 379, 210, 1331, 547, 385, 386,
- /* 1920 */ 275, 572, 1247, 1242, 411, 412, 1512, 165, 178, 1513,
- /* 1930 */ 1014, 1014, 1016, 1017, 27, 1511, 1510, 1024, 78, 147,
- /* 1940 */ 166, 220, 221, 109, 109, 834, 304, 167, 446, 212,
- /* 1950 */ 318, 110, 231, 445, 569, 445, 144, 1086, 1014, 1084,
- /* 1960 */ 326, 180, 169, 1205, 182, 334, 238, 913, 241, 1100,
- /* 1970 */ 187, 170, 171, 421, 87, 88, 423, 189, 89, 90,
- /* 1980 */ 172, 1103, 243, 1099, 244, 158, 18, 245, 345, 247,
- /* 1990 */ 1014, 1014, 1016, 1017, 27, 261, 1092, 193, 1220, 489,
- /* 2000 */ 194, 37, 366, 849, 494, 251, 195, 506, 92, 19,
- /* 2010 */ 498, 358, 20, 503, 879, 361, 94, 892, 305, 159,
- /* 2020 */ 513, 39, 95, 1168, 160, 1053, 964, 1139, 96, 174,
- /* 2030 */ 1138, 225, 280, 282, 198, 958, 113, 1158, 1154, 260,
- /* 2040 */ 21, 22, 23, 1156, 1162, 1161, 1143, 24, 33, 25,
- /* 2050 */ 202, 542, 26, 100, 1067, 102, 1054, 103, 7, 1052,
- /* 2060 */ 1056, 1109, 1057, 1108, 266, 267, 28, 40, 390, 1019,
- /* 2070 */ 861, 112, 29, 564, 1176, 1175, 268, 176, 143, 923,
- /* 2080 */ 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238, 1238,
- /* 2090 */ 1238, 1238, 1238, 1238, 269, 1593,
+ /* 0 */ 576, 128, 125, 232, 1622, 549, 576, 1290, 1281, 576,
+ /* 10 */ 328, 576, 1300, 212, 576, 128, 125, 232, 578, 412,
+ /* 20 */ 578, 391, 1542, 51, 51, 523, 405, 1293, 529, 51,
+ /* 30 */ 51, 983, 51, 51, 81, 81, 1107, 61, 61, 984,
+ /* 40 */ 1107, 1292, 380, 135, 136, 90, 1228, 1228, 1063, 1066,
+ /* 50 */ 1053, 1053, 133, 133, 134, 134, 134, 134, 1577, 412,
+ /* 60 */ 287, 287, 7, 287, 287, 422, 1050, 1050, 1064, 1067,
+ /* 70 */ 289, 556, 492, 573, 524, 561, 573, 497, 561, 482,
+ /* 80 */ 530, 262, 229, 135, 136, 90, 1228, 1228, 1063, 1066,
+ /* 90 */ 1053, 1053, 133, 133, 134, 134, 134, 134, 128, 125,
+ /* 100 */ 232, 1506, 132, 132, 132, 132, 131, 131, 130, 130,
+ /* 110 */ 130, 129, 126, 450, 1204, 1255, 1, 1, 582, 2,
+ /* 120 */ 1259, 1571, 420, 1582, 379, 320, 1174, 153, 1174, 1584,
+ /* 130 */ 412, 378, 1582, 543, 1341, 330, 111, 570, 570, 570,
+ /* 140 */ 293, 1054, 132, 132, 132, 132, 131, 131, 130, 130,
+ /* 150 */ 130, 129, 126, 450, 135, 136, 90, 1228, 1228, 1063,
+ /* 160 */ 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, 287,
+ /* 170 */ 287, 1204, 1205, 1204, 255, 287, 287, 510, 507, 506,
+ /* 180 */ 137, 455, 573, 212, 561, 447, 446, 505, 573, 1616,
+ /* 190 */ 561, 134, 134, 134, 134, 127, 400, 243, 132, 132,
+ /* 200 */ 132, 132, 131, 131, 130, 130, 130, 129, 126, 450,
+ /* 210 */ 282, 471, 345, 132, 132, 132, 132, 131, 131, 130,
+ /* 220 */ 130, 130, 129, 126, 450, 574, 155, 936, 936, 454,
+ /* 230 */ 227, 521, 1236, 412, 1236, 134, 134, 134, 134, 132,
+ /* 240 */ 132, 132, 132, 131, 131, 130, 130, 130, 129, 126,
+ /* 250 */ 450, 130, 130, 130, 129, 126, 450, 135, 136, 90,
+ /* 260 */ 1228, 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134,
+ /* 270 */ 134, 134, 128, 125, 232, 450, 576, 412, 397, 1249,
+ /* 280 */ 180, 92, 93, 132, 132, 132, 132, 131, 131, 130,
+ /* 290 */ 130, 130, 129, 126, 450, 381, 387, 1204, 383, 81,
+ /* 300 */ 81, 135, 136, 90, 1228, 1228, 1063, 1066, 1053, 1053,
+ /* 310 */ 133, 133, 134, 134, 134, 134, 132, 132, 132, 132,
+ /* 320 */ 131, 131, 130, 130, 130, 129, 126, 450, 131, 131,
+ /* 330 */ 130, 130, 130, 129, 126, 450, 556, 1204, 302, 319,
+ /* 340 */ 567, 121, 568, 480, 4, 555, 1149, 1657, 1628, 1657,
+ /* 350 */ 45, 128, 125, 232, 1204, 1205, 1204, 1250, 571, 1169,
+ /* 360 */ 132, 132, 132, 132, 131, 131, 130, 130, 130, 129,
+ /* 370 */ 126, 450, 1169, 287, 287, 1169, 1019, 576, 422, 1019,
+ /* 380 */ 412, 451, 1602, 582, 2, 1259, 573, 44, 561, 95,
+ /* 390 */ 320, 110, 153, 565, 1204, 1205, 1204, 522, 522, 1341,
+ /* 400 */ 81, 81, 7, 44, 135, 136, 90, 1228, 1228, 1063,
+ /* 410 */ 1066, 1053, 1053, 133, 133, 134, 134, 134, 134, 295,
+ /* 420 */ 1149, 1658, 1040, 1658, 1204, 1147, 319, 567, 119, 119,
+ /* 430 */ 343, 466, 331, 343, 287, 287, 120, 556, 451, 577,
+ /* 440 */ 451, 1169, 1169, 1028, 319, 567, 438, 573, 210, 561,
+ /* 450 */ 1339, 1451, 546, 531, 1169, 1169, 1598, 1169, 1169, 416,
+ /* 460 */ 319, 567, 243, 132, 132, 132, 132, 131, 131, 130,
+ /* 470 */ 130, 130, 129, 126, 450, 1028, 1028, 1030, 1031, 35,
+ /* 480 */ 44, 1204, 1205, 1204, 472, 287, 287, 1328, 412, 1307,
+ /* 490 */ 372, 1595, 359, 225, 454, 1204, 195, 1328, 573, 1147,
+ /* 500 */ 561, 1333, 1333, 274, 576, 1188, 576, 340, 46, 196,
+ /* 510 */ 537, 217, 135, 136, 90, 1228, 1228, 1063, 1066, 1053,
+ /* 520 */ 1053, 133, 133, 134, 134, 134, 134, 19, 19, 19,
+ /* 530 */ 19, 412, 581, 1204, 1259, 511, 1204, 319, 567, 320,
+ /* 540 */ 944, 153, 425, 491, 430, 943, 1204, 488, 1341, 1450,
+ /* 550 */ 532, 1277, 1204, 1205, 1204, 135, 136, 90, 1228, 1228,
+ /* 560 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134,
+ /* 570 */ 575, 132, 132, 132, 132, 131, 131, 130, 130, 130,
+ /* 580 */ 129, 126, 450, 287, 287, 528, 287, 287, 372, 1595,
+ /* 590 */ 1204, 1205, 1204, 1204, 1205, 1204, 573, 486, 561, 573,
+ /* 600 */ 889, 561, 412, 1204, 1205, 1204, 886, 40, 22, 22,
+ /* 610 */ 220, 243, 525, 1449, 132, 132, 132, 132, 131, 131,
+ /* 620 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228,
+ /* 630 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134,
+ /* 640 */ 134, 412, 180, 454, 1204, 879, 255, 287, 287, 510,
+ /* 650 */ 507, 506, 372, 1595, 1568, 1331, 1331, 576, 889, 505,
+ /* 660 */ 573, 44, 561, 559, 1207, 135, 136, 90, 1228, 1228,
+ /* 670 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134,
+ /* 680 */ 81, 81, 422, 576, 377, 132, 132, 132, 132, 131,
+ /* 690 */ 131, 130, 130, 130, 129, 126, 450, 297, 287, 287,
+ /* 700 */ 460, 1204, 1205, 1204, 1204, 534, 19, 19, 448, 448,
+ /* 710 */ 448, 573, 412, 561, 230, 436, 1187, 535, 319, 567,
+ /* 720 */ 363, 432, 1207, 1435, 132, 132, 132, 132, 131, 131,
+ /* 730 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228,
+ /* 740 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134,
+ /* 750 */ 134, 412, 211, 949, 1169, 1041, 1110, 1110, 494, 547,
+ /* 760 */ 547, 1204, 1205, 1204, 7, 539, 1570, 1169, 376, 576,
+ /* 770 */ 1169, 5, 1204, 486, 3, 135, 136, 90, 1228, 1228,
+ /* 780 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134,
+ /* 790 */ 576, 513, 19, 19, 427, 132, 132, 132, 132, 131,
+ /* 800 */ 131, 130, 130, 130, 129, 126, 450, 305, 1204, 433,
+ /* 810 */ 225, 1204, 385, 19, 19, 273, 290, 371, 516, 366,
+ /* 820 */ 515, 260, 412, 538, 1568, 549, 1024, 362, 437, 1204,
+ /* 830 */ 1205, 1204, 902, 1552, 132, 132, 132, 132, 131, 131,
+ /* 840 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228,
+ /* 850 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134,
+ /* 860 */ 134, 412, 1435, 514, 1281, 1204, 1205, 1204, 1204, 1205,
+ /* 870 */ 1204, 903, 48, 342, 1568, 1568, 1279, 1627, 1568, 911,
+ /* 880 */ 576, 129, 126, 450, 110, 135, 136, 90, 1228, 1228,
+ /* 890 */ 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134, 134,
+ /* 900 */ 265, 576, 459, 19, 19, 132, 132, 132, 132, 131,
+ /* 910 */ 131, 130, 130, 130, 129, 126, 450, 1345, 204, 576,
+ /* 920 */ 459, 458, 50, 47, 19, 19, 49, 434, 1105, 573,
+ /* 930 */ 497, 561, 412, 428, 108, 1224, 1569, 1554, 376, 205,
+ /* 940 */ 550, 550, 81, 81, 132, 132, 132, 132, 131, 131,
+ /* 950 */ 130, 130, 130, 129, 126, 450, 135, 136, 90, 1228,
+ /* 960 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134,
+ /* 970 */ 134, 480, 576, 1204, 576, 1541, 412, 1435, 969, 315,
+ /* 980 */ 1659, 398, 284, 497, 969, 893, 1569, 1569, 376, 376,
+ /* 990 */ 1569, 461, 376, 1224, 459, 80, 80, 81, 81, 497,
+ /* 1000 */ 374, 114, 90, 1228, 1228, 1063, 1066, 1053, 1053, 133,
+ /* 1010 */ 133, 134, 134, 134, 134, 132, 132, 132, 132, 131,
+ /* 1020 */ 131, 130, 130, 130, 129, 126, 450, 1204, 1505, 576,
+ /* 1030 */ 1204, 1205, 1204, 1366, 316, 486, 281, 281, 497, 431,
+ /* 1040 */ 557, 288, 288, 402, 1340, 471, 345, 298, 429, 573,
+ /* 1050 */ 576, 561, 81, 81, 573, 374, 561, 971, 386, 132,
+ /* 1060 */ 132, 132, 132, 131, 131, 130, 130, 130, 129, 126,
+ /* 1070 */ 450, 231, 117, 81, 81, 287, 287, 231, 287, 287,
+ /* 1080 */ 576, 1511, 576, 1336, 1204, 1205, 1204, 139, 573, 556,
+ /* 1090 */ 561, 573, 412, 561, 441, 456, 969, 213, 558, 1511,
+ /* 1100 */ 1513, 1550, 969, 143, 143, 145, 145, 1368, 314, 478,
+ /* 1110 */ 444, 970, 412, 850, 851, 852, 135, 136, 90, 1228,
+ /* 1120 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134,
+ /* 1130 */ 134, 357, 412, 397, 1148, 304, 135, 136, 90, 1228,
+ /* 1140 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134,
+ /* 1150 */ 134, 1575, 323, 6, 862, 7, 135, 124, 90, 1228,
+ /* 1160 */ 1228, 1063, 1066, 1053, 1053, 133, 133, 134, 134, 134,
+ /* 1170 */ 134, 409, 408, 1511, 212, 132, 132, 132, 132, 131,
+ /* 1180 */ 131, 130, 130, 130, 129, 126, 450, 411, 118, 1204,
+ /* 1190 */ 116, 10, 352, 265, 355, 132, 132, 132, 132, 131,
+ /* 1200 */ 131, 130, 130, 130, 129, 126, 450, 576, 324, 306,
+ /* 1210 */ 576, 306, 1250, 469, 158, 132, 132, 132, 132, 131,
+ /* 1220 */ 131, 130, 130, 130, 129, 126, 450, 207, 1224, 1126,
+ /* 1230 */ 65, 65, 470, 66, 66, 412, 447, 446, 882, 531,
+ /* 1240 */ 335, 258, 257, 256, 1127, 1233, 1204, 1205, 1204, 327,
+ /* 1250 */ 1235, 874, 159, 576, 16, 480, 1085, 1040, 1234, 1128,
+ /* 1260 */ 136, 90, 1228, 1228, 1063, 1066, 1053, 1053, 133, 133,
+ /* 1270 */ 134, 134, 134, 134, 1029, 576, 81, 81, 1028, 1040,
+ /* 1280 */ 922, 576, 463, 1236, 576, 1236, 1224, 502, 107, 1435,
+ /* 1290 */ 923, 6, 576, 410, 1498, 882, 1029, 480, 21, 21,
+ /* 1300 */ 1028, 332, 1380, 334, 53, 53, 497, 81, 81, 874,
+ /* 1310 */ 1028, 1028, 1030, 445, 259, 19, 19, 533, 132, 132,
+ /* 1320 */ 132, 132, 131, 131, 130, 130, 130, 129, 126, 450,
+ /* 1330 */ 551, 301, 1028, 1028, 1030, 107, 532, 545, 121, 568,
+ /* 1340 */ 1188, 4, 1126, 1576, 449, 576, 462, 7, 1282, 418,
+ /* 1350 */ 462, 350, 1435, 576, 518, 571, 544, 1127, 121, 568,
+ /* 1360 */ 442, 4, 1188, 464, 533, 1180, 1223, 9, 67, 67,
+ /* 1370 */ 487, 576, 1128, 303, 410, 571, 54, 54, 451, 576,
+ /* 1380 */ 123, 944, 576, 417, 576, 333, 943, 1379, 576, 236,
+ /* 1390 */ 565, 576, 1574, 564, 68, 68, 7, 576, 451, 362,
+ /* 1400 */ 419, 182, 69, 69, 541, 70, 70, 71, 71, 540,
+ /* 1410 */ 565, 72, 72, 484, 55, 55, 473, 1180, 296, 1040,
+ /* 1420 */ 56, 56, 296, 493, 541, 119, 119, 410, 1573, 542,
+ /* 1430 */ 569, 418, 7, 120, 1244, 451, 577, 451, 465, 1040,
+ /* 1440 */ 1028, 576, 1557, 552, 476, 119, 119, 527, 259, 121,
+ /* 1450 */ 568, 240, 4, 120, 576, 451, 577, 451, 576, 477,
+ /* 1460 */ 1028, 576, 156, 576, 57, 57, 571, 576, 286, 229,
+ /* 1470 */ 410, 336, 1028, 1028, 1030, 1031, 35, 59, 59, 219,
+ /* 1480 */ 983, 60, 60, 220, 73, 73, 74, 74, 984, 451,
+ /* 1490 */ 75, 75, 1028, 1028, 1030, 1031, 35, 96, 216, 291,
+ /* 1500 */ 552, 565, 1188, 318, 395, 395, 394, 276, 392, 576,
+ /* 1510 */ 485, 859, 474, 1311, 410, 541, 576, 417, 1530, 1144,
+ /* 1520 */ 540, 399, 1188, 292, 237, 1153, 326, 38, 23, 576,
+ /* 1530 */ 1040, 576, 20, 20, 325, 299, 119, 119, 164, 76,
+ /* 1540 */ 76, 1529, 121, 568, 120, 4, 451, 577, 451, 203,
+ /* 1550 */ 576, 1028, 141, 141, 142, 142, 576, 322, 39, 571,
+ /* 1560 */ 341, 1021, 110, 264, 239, 901, 900, 423, 242, 908,
+ /* 1570 */ 909, 370, 173, 77, 77, 43, 479, 1310, 264, 62,
+ /* 1580 */ 62, 369, 451, 1028, 1028, 1030, 1031, 35, 1601, 1192,
+ /* 1590 */ 453, 1092, 238, 291, 565, 163, 1309, 110, 395, 395,
+ /* 1600 */ 394, 276, 392, 986, 987, 859, 481, 346, 264, 110,
+ /* 1610 */ 1032, 489, 576, 1188, 503, 1088, 261, 261, 237, 576,
+ /* 1620 */ 326, 121, 568, 1040, 4, 347, 1376, 413, 325, 119,
+ /* 1630 */ 119, 948, 319, 567, 351, 78, 78, 120, 571, 451,
+ /* 1640 */ 577, 451, 79, 79, 1028, 354, 356, 576, 360, 1092,
+ /* 1650 */ 110, 576, 974, 942, 264, 123, 457, 358, 239, 576,
+ /* 1660 */ 519, 451, 939, 1104, 123, 1104, 173, 576, 1032, 43,
+ /* 1670 */ 63, 63, 1324, 565, 168, 168, 1028, 1028, 1030, 1031,
+ /* 1680 */ 35, 576, 169, 169, 1308, 872, 238, 157, 1589, 576,
+ /* 1690 */ 86, 86, 365, 89, 568, 375, 4, 1103, 941, 1103,
+ /* 1700 */ 123, 576, 1040, 1389, 64, 64, 1188, 1434, 119, 119,
+ /* 1710 */ 571, 576, 82, 82, 563, 576, 120, 165, 451, 577,
+ /* 1720 */ 451, 413, 1362, 1028, 144, 144, 319, 567, 576, 1374,
+ /* 1730 */ 562, 498, 279, 451, 83, 83, 1439, 576, 166, 166,
+ /* 1740 */ 576, 1289, 554, 576, 1280, 565, 576, 12, 576, 1268,
+ /* 1750 */ 457, 146, 146, 1267, 576, 1028, 1028, 1030, 1031, 35,
+ /* 1760 */ 140, 140, 1269, 167, 167, 1609, 160, 160, 1359, 150,
+ /* 1770 */ 150, 149, 149, 311, 1040, 576, 312, 147, 147, 313,
+ /* 1780 */ 119, 119, 222, 235, 576, 1188, 396, 576, 120, 576,
+ /* 1790 */ 451, 577, 451, 1192, 453, 1028, 508, 291, 148, 148,
+ /* 1800 */ 1421, 1612, 395, 395, 394, 276, 392, 85, 85, 859,
+ /* 1810 */ 87, 87, 84, 84, 553, 576, 294, 576, 1426, 338,
+ /* 1820 */ 339, 1425, 237, 300, 326, 1416, 1409, 1028, 1028, 1030,
+ /* 1830 */ 1031, 35, 325, 344, 403, 483, 226, 1307, 52, 52,
+ /* 1840 */ 58, 58, 368, 1371, 1502, 566, 1501, 121, 568, 221,
+ /* 1850 */ 4, 208, 268, 209, 390, 1244, 1549, 1188, 1372, 1370,
+ /* 1860 */ 1369, 1547, 239, 184, 571, 233, 421, 1241, 95, 218,
+ /* 1870 */ 173, 1507, 193, 43, 91, 94, 178, 186, 467, 188,
+ /* 1880 */ 468, 1422, 13, 189, 190, 191, 501, 451, 245, 108,
+ /* 1890 */ 238, 401, 1428, 1427, 1430, 475, 404, 1496, 197, 565,
+ /* 1900 */ 14, 490, 249, 101, 1518, 496, 349, 280, 251, 201,
+ /* 1910 */ 353, 499, 252, 406, 1270, 253, 517, 1327, 1326, 435,
+ /* 1920 */ 1325, 1318, 103, 893, 1296, 413, 227, 407, 1040, 1626,
+ /* 1930 */ 319, 567, 1625, 1297, 119, 119, 439, 367, 1317, 1295,
+ /* 1940 */ 1624, 526, 120, 440, 451, 577, 451, 1594, 309, 1028,
+ /* 1950 */ 310, 373, 266, 267, 457, 1580, 1579, 443, 138, 1394,
+ /* 1960 */ 552, 1393, 11, 1483, 384, 115, 317, 1350, 109, 536,
+ /* 1970 */ 42, 579, 382, 214, 1349, 388, 1198, 389, 275, 277,
+ /* 1980 */ 278, 1028, 1028, 1030, 1031, 35, 580, 1265, 414, 1260,
+ /* 1990 */ 170, 415, 183, 1534, 1535, 1533, 171, 154, 307, 1532,
+ /* 2000 */ 846, 223, 224, 88, 452, 215, 172, 321, 234, 1102,
+ /* 2010 */ 152, 1188, 1100, 329, 185, 174, 1223, 925, 187, 241,
+ /* 2020 */ 337, 244, 1116, 192, 175, 176, 424, 426, 97, 194,
+ /* 2030 */ 98, 99, 100, 177, 1119, 1115, 246, 247, 161, 24,
+ /* 2040 */ 248, 348, 1238, 264, 1108, 250, 495, 199, 198, 15,
+ /* 2050 */ 861, 500, 369, 254, 504, 509, 512, 200, 102, 25,
+ /* 2060 */ 179, 361, 26, 364, 104, 891, 308, 162, 105, 904,
+ /* 2070 */ 520, 106, 1185, 1069, 1155, 17, 228, 27, 1154, 283,
+ /* 2080 */ 285, 263, 978, 202, 972, 123, 28, 1175, 29, 30,
+ /* 2090 */ 1179, 1171, 31, 1173, 1160, 41, 32, 206, 548, 33,
+ /* 2100 */ 110, 1178, 1083, 8, 112, 1070, 113, 1068, 1072, 34,
+ /* 2110 */ 1073, 560, 1125, 269, 1124, 270, 36, 18, 1194, 1033,
+ /* 2120 */ 873, 151, 122, 37, 393, 271, 272, 572, 181, 1193,
+ /* 2130 */ 1256, 1256, 1256, 935, 1256, 1256, 1256, 1256, 1256, 1256,
+ /* 2140 */ 1256, 1617,
};
static const YYCODETYPE yy_lookahead[] = {
- /* 0 */ 193, 193, 193, 274, 275, 276, 193, 274, 275, 276,
- /* 10 */ 193, 223, 219, 225, 206, 210, 211, 212, 193, 19,
- /* 20 */ 219, 233, 216, 216, 217, 216, 217, 193, 295, 216,
- /* 30 */ 217, 31, 193, 216, 217, 193, 228, 213, 230, 39,
- /* 40 */ 206, 216, 217, 43, 44, 45, 46, 47, 48, 49,
- /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 193, 19,
- /* 60 */ 185, 186, 187, 188, 189, 190, 253, 274, 275, 276,
- /* 70 */ 195, 193, 197, 193, 261, 274, 275, 276, 253, 204,
- /* 80 */ 238, 204, 81, 43, 44, 45, 46, 47, 48, 49,
- /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 274, 275,
- /* 100 */ 276, 262, 102, 103, 104, 105, 106, 107, 108, 109,
- /* 110 */ 110, 111, 112, 113, 239, 240, 239, 240, 210, 211,
- /* 120 */ 212, 314, 315, 314, 59, 316, 86, 252, 88, 252,
- /* 130 */ 19, 314, 315, 256, 257, 113, 25, 72, 296, 138,
- /* 140 */ 139, 266, 102, 103, 104, 105, 106, 107, 108, 109,
+ /* 0 */ 194, 276, 277, 278, 216, 194, 194, 217, 194, 194,
+ /* 10 */ 194, 194, 224, 194, 194, 276, 277, 278, 204, 19,
+ /* 20 */ 206, 202, 297, 217, 218, 205, 207, 217, 205, 217,
+ /* 30 */ 218, 31, 217, 218, 217, 218, 29, 217, 218, 39,
+ /* 40 */ 33, 217, 220, 43, 44, 45, 46, 47, 48, 49,
+ /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 312, 19,
+ /* 60 */ 240, 241, 316, 240, 241, 194, 46, 47, 48, 49,
+ /* 70 */ 22, 254, 65, 253, 254, 255, 253, 194, 255, 194,
+ /* 80 */ 263, 258, 259, 43, 44, 45, 46, 47, 48, 49,
+ /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 276, 277,
+ /* 100 */ 278, 285, 102, 103, 104, 105, 106, 107, 108, 109,
+ /* 110 */ 110, 111, 112, 113, 59, 186, 187, 188, 189, 190,
+ /* 120 */ 191, 310, 239, 317, 318, 196, 86, 198, 88, 317,
+ /* 130 */ 19, 319, 317, 318, 205, 264, 25, 211, 212, 213,
+ /* 140 */ 205, 121, 102, 103, 104, 105, 106, 107, 108, 109,
/* 150 */ 110, 111, 112, 113, 43, 44, 45, 46, 47, 48,
- /* 160 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 81,
- /* 170 */ 292, 59, 292, 298, 108, 109, 110, 111, 112, 113,
- /* 180 */ 69, 116, 117, 118, 72, 106, 107, 193, 111, 112,
- /* 190 */ 113, 54, 55, 56, 57, 58, 102, 103, 104, 105,
- /* 200 */ 106, 107, 108, 109, 110, 111, 112, 113, 120, 25,
- /* 210 */ 216, 217, 145, 102, 103, 104, 105, 106, 107, 108,
- /* 220 */ 109, 110, 111, 112, 113, 231, 138, 139, 116, 117,
- /* 230 */ 118, 164, 153, 19, 155, 54, 55, 56, 57, 102,
+ /* 160 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 240,
+ /* 170 */ 241, 116, 117, 118, 119, 240, 241, 122, 123, 124,
+ /* 180 */ 69, 298, 253, 194, 255, 106, 107, 132, 253, 141,
+ /* 190 */ 255, 54, 55, 56, 57, 58, 207, 268, 102, 103,
+ /* 200 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
+ /* 210 */ 214, 128, 129, 102, 103, 104, 105, 106, 107, 108,
+ /* 220 */ 109, 110, 111, 112, 113, 134, 25, 136, 137, 300,
+ /* 230 */ 165, 166, 153, 19, 155, 54, 55, 56, 57, 102,
/* 240 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
- /* 250 */ 113, 128, 129, 46, 47, 48, 49, 43, 44, 45,
+ /* 250 */ 113, 108, 109, 110, 111, 112, 113, 43, 44, 45,
/* 260 */ 46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
- /* 270 */ 56, 57, 216, 193, 25, 59, 193, 19, 165, 166,
- /* 280 */ 193, 67, 24, 102, 103, 104, 105, 106, 107, 108,
- /* 290 */ 109, 110, 111, 112, 113, 73, 216, 217, 59, 216,
- /* 300 */ 217, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ /* 270 */ 56, 57, 276, 277, 278, 113, 194, 19, 22, 23,
+ /* 280 */ 194, 67, 24, 102, 103, 104, 105, 106, 107, 108,
+ /* 290 */ 109, 110, 111, 112, 113, 220, 250, 59, 252, 217,
+ /* 300 */ 218, 43, 44, 45, 46, 47, 48, 49, 50, 51,
/* 310 */ 52, 53, 54, 55, 56, 57, 102, 103, 104, 105,
- /* 320 */ 106, 107, 108, 109, 110, 111, 112, 113, 121, 145,
- /* 330 */ 59, 193, 116, 117, 118, 119, 273, 204, 122, 123,
- /* 340 */ 124, 19, 20, 134, 22, 136, 137, 19, 132, 127,
- /* 350 */ 128, 129, 24, 22, 23, 116, 117, 118, 36, 193,
+ /* 320 */ 106, 107, 108, 109, 110, 111, 112, 113, 106, 107,
+ /* 330 */ 108, 109, 110, 111, 112, 113, 254, 59, 205, 138,
+ /* 340 */ 139, 19, 20, 194, 22, 263, 22, 23, 231, 25,
+ /* 350 */ 72, 276, 277, 278, 116, 117, 118, 101, 36, 76,
/* 360 */ 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
- /* 370 */ 112, 113, 239, 240, 311, 312, 215, 106, 107, 241,
- /* 380 */ 19, 59, 216, 217, 223, 252, 115, 116, 117, 118,
- /* 390 */ 151, 120, 26, 71, 193, 308, 309, 193, 149, 128,
- /* 400 */ 313, 216, 269, 81, 43, 44, 45, 46, 47, 48,
- /* 410 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 253,
- /* 420 */ 216, 217, 100, 95, 153, 59, 155, 261, 106, 107,
- /* 430 */ 25, 193, 101, 193, 193, 231, 114, 25, 116, 117,
- /* 440 */ 118, 113, 304, 121, 193, 204, 59, 119, 120, 121,
- /* 450 */ 122, 123, 124, 125, 216, 217, 193, 216, 217, 131,
- /* 460 */ 138, 139, 230, 102, 103, 104, 105, 106, 107, 108,
+ /* 370 */ 112, 113, 89, 240, 241, 92, 73, 194, 194, 73,
+ /* 380 */ 19, 59, 188, 189, 190, 191, 253, 81, 255, 151,
+ /* 390 */ 196, 25, 198, 71, 116, 117, 118, 311, 312, 205,
+ /* 400 */ 217, 218, 316, 81, 43, 44, 45, 46, 47, 48,
+ /* 410 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 270,
+ /* 420 */ 22, 23, 100, 25, 59, 101, 138, 139, 106, 107,
+ /* 430 */ 127, 128, 129, 127, 240, 241, 114, 254, 116, 117,
+ /* 440 */ 118, 76, 76, 121, 138, 139, 263, 253, 264, 255,
+ /* 450 */ 205, 275, 87, 19, 89, 89, 194, 92, 92, 199,
+ /* 460 */ 138, 139, 268, 102, 103, 104, 105, 106, 107, 108,
/* 470 */ 109, 110, 111, 112, 113, 153, 154, 155, 156, 157,
- /* 480 */ 239, 240, 116, 117, 118, 76, 193, 23, 19, 25,
- /* 490 */ 22, 253, 23, 252, 253, 108, 87, 204, 89, 261,
- /* 500 */ 198, 92, 261, 116, 117, 118, 193, 306, 307, 216,
- /* 510 */ 217, 150, 43, 44, 45, 46, 47, 48, 49, 50,
- /* 520 */ 51, 52, 53, 54, 55, 56, 57, 59, 193, 216,
- /* 530 */ 217, 19, 239, 240, 283, 23, 106, 107, 108, 109,
- /* 540 */ 110, 111, 112, 113, 73, 252, 253, 142, 308, 309,
- /* 550 */ 138, 139, 81, 313, 145, 43, 44, 45, 46, 47,
+ /* 480 */ 81, 116, 117, 118, 129, 240, 241, 224, 19, 226,
+ /* 490 */ 314, 315, 23, 25, 300, 59, 22, 234, 253, 101,
+ /* 500 */ 255, 236, 237, 26, 194, 183, 194, 152, 72, 22,
+ /* 510 */ 145, 150, 43, 44, 45, 46, 47, 48, 49, 50,
+ /* 520 */ 51, 52, 53, 54, 55, 56, 57, 217, 218, 217,
+ /* 530 */ 218, 19, 189, 59, 191, 23, 59, 138, 139, 196,
+ /* 540 */ 135, 198, 232, 283, 232, 140, 59, 287, 205, 275,
+ /* 550 */ 116, 205, 116, 117, 118, 43, 44, 45, 46, 47,
/* 560 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
- /* 570 */ 307, 102, 103, 104, 105, 106, 107, 108, 109, 110,
- /* 580 */ 111, 112, 113, 281, 116, 117, 118, 285, 23, 193,
- /* 590 */ 25, 119, 59, 193, 122, 123, 124, 59, 127, 203,
- /* 600 */ 59, 205, 19, 268, 132, 25, 23, 22, 193, 138,
- /* 610 */ 139, 249, 204, 251, 102, 103, 104, 105, 106, 107,
+ /* 570 */ 194, 102, 103, 104, 105, 106, 107, 108, 109, 110,
+ /* 580 */ 111, 112, 113, 240, 241, 194, 240, 241, 314, 315,
+ /* 590 */ 116, 117, 118, 116, 117, 118, 253, 194, 255, 253,
+ /* 600 */ 59, 255, 19, 116, 117, 118, 23, 22, 217, 218,
+ /* 610 */ 142, 268, 205, 275, 102, 103, 104, 105, 106, 107,
/* 620 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46,
/* 630 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 640 */ 57, 19, 22, 23, 59, 23, 25, 239, 240, 116,
- /* 650 */ 117, 118, 193, 11, 116, 117, 118, 116, 117, 118,
- /* 660 */ 252, 269, 22, 193, 15, 43, 44, 45, 46, 47,
+ /* 640 */ 57, 19, 194, 300, 59, 23, 119, 240, 241, 122,
+ /* 650 */ 123, 124, 314, 315, 194, 236, 237, 194, 117, 132,
+ /* 660 */ 253, 81, 255, 205, 59, 43, 44, 45, 46, 47,
/* 670 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
- /* 680 */ 273, 143, 193, 118, 143, 102, 103, 104, 105, 106,
- /* 690 */ 107, 108, 109, 110, 111, 112, 113, 76, 118, 59,
- /* 700 */ 241, 116, 117, 118, 304, 216, 217, 292, 143, 60,
- /* 710 */ 89, 241, 19, 92, 193, 193, 23, 22, 311, 312,
- /* 720 */ 231, 101, 22, 143, 102, 103, 104, 105, 106, 107,
+ /* 680 */ 217, 218, 194, 194, 194, 102, 103, 104, 105, 106,
+ /* 690 */ 107, 108, 109, 110, 111, 112, 113, 294, 240, 241,
+ /* 700 */ 120, 116, 117, 118, 59, 194, 217, 218, 211, 212,
+ /* 710 */ 213, 253, 19, 255, 194, 19, 23, 254, 138, 139,
+ /* 720 */ 24, 232, 117, 194, 102, 103, 104, 105, 106, 107,
/* 730 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46,
/* 740 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 750 */ 57, 19, 193, 193, 59, 23, 116, 117, 118, 59,
- /* 760 */ 201, 21, 241, 304, 22, 206, 127, 128, 129, 193,
- /* 770 */ 128, 129, 235, 236, 304, 43, 44, 45, 46, 47,
+ /* 750 */ 57, 19, 264, 108, 76, 23, 127, 128, 129, 311,
+ /* 760 */ 312, 116, 117, 118, 316, 87, 306, 89, 308, 194,
+ /* 770 */ 92, 22, 59, 194, 22, 43, 44, 45, 46, 47,
/* 780 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
- /* 790 */ 22, 193, 216, 217, 193, 102, 103, 104, 105, 106,
- /* 800 */ 107, 108, 109, 110, 111, 112, 113, 231, 193, 193,
- /* 810 */ 193, 116, 117, 118, 216, 217, 116, 117, 118, 226,
- /* 820 */ 80, 193, 19, 235, 236, 304, 23, 211, 212, 231,
- /* 830 */ 204, 216, 217, 205, 102, 103, 104, 105, 106, 107,
+ /* 790 */ 194, 95, 217, 218, 265, 102, 103, 104, 105, 106,
+ /* 800 */ 107, 108, 109, 110, 111, 112, 113, 232, 59, 113,
+ /* 810 */ 25, 59, 194, 217, 218, 119, 120, 121, 122, 123,
+ /* 820 */ 124, 125, 19, 145, 194, 194, 23, 131, 232, 116,
+ /* 830 */ 117, 118, 35, 194, 102, 103, 104, 105, 106, 107,
/* 840 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46,
/* 850 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 860 */ 57, 19, 193, 123, 76, 239, 240, 193, 253, 239,
- /* 870 */ 240, 239, 240, 193, 106, 107, 193, 89, 252, 193,
- /* 880 */ 92, 59, 252, 141, 252, 43, 44, 45, 46, 47,
+ /* 860 */ 57, 19, 194, 66, 194, 116, 117, 118, 116, 117,
+ /* 870 */ 118, 74, 242, 294, 194, 194, 206, 23, 194, 25,
+ /* 880 */ 194, 111, 112, 113, 25, 43, 44, 45, 46, 47,
/* 890 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
- /* 900 */ 284, 161, 216, 217, 193, 102, 103, 104, 105, 106,
- /* 910 */ 107, 108, 109, 110, 111, 112, 113, 231, 193, 16,
- /* 920 */ 187, 188, 189, 190, 7, 8, 9, 309, 195, 25,
- /* 930 */ 197, 313, 19, 127, 128, 129, 262, 204, 22, 117,
- /* 940 */ 24, 216, 217, 263, 102, 103, 104, 105, 106, 107,
+ /* 900 */ 24, 194, 194, 217, 218, 102, 103, 104, 105, 106,
+ /* 910 */ 107, 108, 109, 110, 111, 112, 113, 241, 232, 194,
+ /* 920 */ 212, 213, 242, 242, 217, 218, 242, 130, 11, 253,
+ /* 930 */ 194, 255, 19, 265, 149, 59, 306, 194, 308, 232,
+ /* 940 */ 309, 310, 217, 218, 102, 103, 104, 105, 106, 107,
/* 950 */ 108, 109, 110, 111, 112, 113, 43, 44, 45, 46,
/* 960 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 970 */ 57, 193, 239, 240, 193, 59, 19, 188, 253, 190,
- /* 980 */ 77, 226, 79, 193, 195, 252, 197, 193, 19, 301,
- /* 990 */ 302, 193, 193, 204, 216, 217, 226, 216, 217, 266,
- /* 1000 */ 204, 159, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 970 */ 57, 194, 194, 59, 194, 239, 19, 194, 25, 254,
+ /* 980 */ 303, 304, 23, 194, 25, 126, 306, 306, 308, 308,
+ /* 990 */ 306, 271, 308, 117, 286, 217, 218, 217, 218, 194,
+ /* 1000 */ 194, 159, 45, 46, 47, 48, 49, 50, 51, 52,
/* 1010 */ 53, 54, 55, 56, 57, 102, 103, 104, 105, 106,
- /* 1020 */ 107, 108, 109, 110, 111, 112, 113, 12, 239, 240,
- /* 1030 */ 232, 298, 238, 117, 253, 239, 240, 238, 259, 260,
- /* 1040 */ 193, 252, 27, 31, 193, 193, 142, 204, 252, 193,
- /* 1050 */ 193, 39, 262, 193, 100, 266, 278, 42, 204, 102,
+ /* 1020 */ 107, 108, 109, 110, 111, 112, 113, 59, 239, 194,
+ /* 1030 */ 116, 117, 118, 260, 254, 194, 240, 241, 194, 233,
+ /* 1040 */ 205, 240, 241, 205, 239, 128, 129, 270, 265, 253,
+ /* 1050 */ 194, 255, 217, 218, 253, 194, 255, 143, 280, 102,
/* 1060 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
- /* 1070 */ 113, 117, 159, 216, 217, 121, 216, 217, 63, 193,
- /* 1080 */ 193, 193, 239, 240, 115, 116, 193, 298, 73, 238,
- /* 1090 */ 238, 231, 19, 239, 240, 252, 22, 24, 211, 212,
- /* 1100 */ 24, 193, 216, 217, 216, 217, 252, 153, 154, 155,
- /* 1110 */ 253, 16, 19, 144, 213, 268, 43, 44, 45, 46,
+ /* 1070 */ 113, 118, 159, 217, 218, 240, 241, 118, 240, 241,
+ /* 1080 */ 194, 194, 194, 239, 116, 117, 118, 22, 253, 254,
+ /* 1090 */ 255, 253, 19, 255, 233, 194, 143, 24, 263, 212,
+ /* 1100 */ 213, 194, 143, 217, 218, 217, 218, 261, 262, 271,
+ /* 1110 */ 254, 143, 19, 7, 8, 9, 43, 44, 45, 46,
/* 1120 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 1130 */ 57, 238, 19, 59, 193, 59, 43, 44, 45, 46,
+ /* 1130 */ 57, 16, 19, 22, 23, 294, 43, 44, 45, 46,
/* 1140 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 1150 */ 57, 22, 23, 193, 25, 193, 43, 44, 45, 46,
+ /* 1150 */ 57, 312, 194, 214, 21, 316, 43, 44, 45, 46,
/* 1160 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 1170 */ 57, 284, 77, 193, 79, 102, 103, 104, 105, 106,
- /* 1180 */ 107, 108, 109, 110, 111, 112, 113, 286, 193, 193,
- /* 1190 */ 193, 117, 291, 117, 232, 102, 103, 104, 105, 106,
- /* 1200 */ 107, 108, 109, 110, 111, 112, 113, 204, 22, 23,
- /* 1210 */ 66, 25, 216, 217, 35, 102, 103, 104, 105, 106,
- /* 1220 */ 107, 108, 109, 110, 111, 112, 113, 193, 268, 85,
- /* 1230 */ 101, 193, 309, 309, 240, 19, 313, 313, 94, 208,
- /* 1240 */ 209, 193, 239, 240, 193, 66, 252, 19, 268, 244,
- /* 1250 */ 216, 217, 193, 74, 213, 252, 161, 19, 263, 254,
+ /* 1170 */ 57, 106, 107, 286, 194, 102, 103, 104, 105, 106,
+ /* 1180 */ 107, 108, 109, 110, 111, 112, 113, 207, 158, 59,
+ /* 1190 */ 160, 22, 77, 24, 79, 102, 103, 104, 105, 106,
+ /* 1200 */ 107, 108, 109, 110, 111, 112, 113, 194, 194, 229,
+ /* 1210 */ 194, 231, 101, 80, 22, 102, 103, 104, 105, 106,
+ /* 1220 */ 107, 108, 109, 110, 111, 112, 113, 288, 59, 12,
+ /* 1230 */ 217, 218, 293, 217, 218, 19, 106, 107, 59, 19,
+ /* 1240 */ 16, 127, 128, 129, 27, 115, 116, 117, 118, 194,
+ /* 1250 */ 120, 59, 22, 194, 24, 194, 123, 100, 128, 42,
/* 1260 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
- /* 1270 */ 54, 55, 56, 57, 193, 216, 217, 5, 59, 193,
- /* 1280 */ 19, 244, 10, 11, 12, 13, 14, 101, 309, 17,
- /* 1290 */ 146, 254, 313, 193, 193, 76, 115, 216, 217, 309,
- /* 1300 */ 12, 263, 30, 313, 32, 46, 87, 46, 89, 130,
- /* 1310 */ 193, 92, 40, 22, 263, 27, 216, 217, 102, 103,
+ /* 1270 */ 54, 55, 56, 57, 117, 194, 217, 218, 121, 100,
+ /* 1280 */ 63, 194, 245, 153, 194, 155, 117, 19, 115, 194,
+ /* 1290 */ 73, 214, 194, 256, 161, 116, 117, 194, 217, 218,
+ /* 1300 */ 121, 77, 194, 79, 217, 218, 194, 217, 218, 117,
+ /* 1310 */ 153, 154, 155, 254, 46, 217, 218, 144, 102, 103,
/* 1320 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
- /* 1330 */ 42, 150, 291, 216, 217, 116, 117, 118, 19, 20,
- /* 1340 */ 193, 22, 70, 260, 116, 193, 24, 264, 193, 263,
- /* 1350 */ 78, 63, 61, 81, 116, 36, 193, 260, 193, 29,
- /* 1360 */ 193, 264, 193, 33, 145, 193, 59, 48, 216, 217,
- /* 1370 */ 98, 216, 217, 193, 115, 193, 115, 193, 59, 216,
- /* 1380 */ 217, 216, 217, 216, 217, 216, 217, 255, 216, 217,
- /* 1390 */ 71, 193, 131, 193, 25, 65, 216, 217, 216, 217,
- /* 1400 */ 216, 217, 208, 209, 85, 133, 193, 100, 193, 90,
- /* 1410 */ 138, 139, 138, 139, 216, 217, 216, 217, 193, 100,
- /* 1420 */ 193, 108, 135, 116, 117, 106, 107, 140, 121, 216,
- /* 1430 */ 217, 216, 217, 114, 162, 116, 117, 118, 299, 300,
- /* 1440 */ 121, 216, 217, 216, 217, 193, 244, 193, 135, 244,
- /* 1450 */ 193, 256, 257, 140, 244, 193, 254, 193, 193, 254,
- /* 1460 */ 153, 154, 155, 141, 254, 149, 150, 258, 216, 217,
- /* 1470 */ 216, 217, 153, 154, 155, 156, 157, 0, 1, 2,
- /* 1480 */ 216, 217, 5, 115, 158, 193, 160, 10, 11, 12,
- /* 1490 */ 13, 14, 193, 59, 17, 126, 193, 19, 20, 129,
- /* 1500 */ 22, 193, 22, 22, 24, 193, 23, 30, 25, 32,
- /* 1510 */ 19, 20, 144, 22, 36, 216, 217, 40, 193, 216,
- /* 1520 */ 217, 193, 152, 129, 216, 217, 193, 36, 216, 217,
- /* 1530 */ 193, 99, 193, 193, 53, 193, 193, 59, 23, 193,
- /* 1540 */ 25, 216, 217, 193, 216, 217, 152, 70, 59, 71,
- /* 1550 */ 59, 117, 193, 216, 217, 78, 216, 217, 81, 216,
- /* 1560 */ 217, 318, 71, 85, 193, 133, 193, 193, 90, 23,
- /* 1570 */ 23, 25, 25, 120, 121, 98, 85, 193, 100, 193,
- /* 1580 */ 23, 90, 25, 121, 106, 107, 19, 216, 217, 216,
- /* 1590 */ 217, 100, 114, 131, 116, 117, 118, 106, 107, 121,
- /* 1600 */ 216, 217, 216, 217, 193, 114, 117, 116, 117, 118,
- /* 1610 */ 133, 193, 121, 193, 193, 138, 139, 193, 23, 193,
- /* 1620 */ 25, 23, 23, 25, 25, 7, 8, 216, 217, 193,
- /* 1630 */ 193, 153, 154, 155, 156, 157, 216, 217, 193, 162,
- /* 1640 */ 216, 217, 216, 217, 153, 154, 155, 156, 157, 1,
- /* 1650 */ 2, 193, 193, 5, 19, 20, 59, 22, 10, 11,
- /* 1660 */ 12, 13, 14, 193, 97, 17, 193, 23, 193, 25,
- /* 1670 */ 288, 36, 193, 242, 216, 217, 236, 23, 30, 25,
- /* 1680 */ 32, 19, 20, 23, 22, 25, 216, 217, 40, 216,
- /* 1690 */ 217, 216, 217, 193, 59, 216, 217, 193, 36, 83,
- /* 1700 */ 84, 153, 153, 155, 155, 23, 71, 25, 23, 193,
- /* 1710 */ 25, 193, 193, 193, 117, 193, 193, 193, 70, 193,
- /* 1720 */ 193, 59, 193, 255, 255, 287, 78, 255, 243, 81,
- /* 1730 */ 191, 255, 297, 71, 271, 100, 293, 245, 267, 214,
- /* 1740 */ 246, 106, 107, 108, 246, 271, 98, 245, 293, 114,
- /* 1750 */ 220, 116, 117, 118, 267, 271, 121, 271, 225, 219,
- /* 1760 */ 229, 219, 100, 219, 259, 259, 259, 259, 106, 107,
- /* 1770 */ 249, 196, 60, 280, 141, 243, 114, 249, 116, 117,
- /* 1780 */ 118, 133, 245, 121, 200, 297, 138, 139, 153, 154,
- /* 1790 */ 155, 156, 157, 297, 200, 38, 19, 20, 151, 22,
- /* 1800 */ 200, 150, 140, 294, 294, 22, 272, 43, 234, 18,
- /* 1810 */ 162, 270, 200, 36, 237, 153, 154, 155, 156, 157,
- /* 1820 */ 237, 283, 237, 237, 18, 199, 149, 246, 272, 270,
- /* 1830 */ 272, 200, 158, 246, 246, 234, 59, 234, 246, 199,
- /* 1840 */ 290, 62, 289, 200, 199, 22, 221, 115, 71, 200,
- /* 1850 */ 200, 199, 199, 221, 218, 218, 19, 20, 64, 22,
- /* 1860 */ 218, 227, 22, 224, 126, 224, 165, 221, 24, 305,
- /* 1870 */ 200, 113, 312, 36, 218, 220, 218, 100, 282, 218,
- /* 1880 */ 91, 218, 317, 106, 107, 221, 227, 282, 317, 82,
- /* 1890 */ 148, 114, 265, 116, 117, 118, 59, 145, 121, 22,
- /* 1900 */ 277, 158, 200, 265, 25, 202, 147, 250, 71, 279,
- /* 1910 */ 13, 146, 194, 194, 249, 248, 250, 140, 247, 246,
- /* 1920 */ 6, 192, 192, 192, 303, 303, 213, 207, 300, 213,
- /* 1930 */ 153, 154, 155, 156, 157, 213, 213, 100, 213, 222,
- /* 1940 */ 207, 214, 214, 106, 107, 4, 222, 207, 3, 22,
- /* 1950 */ 163, 114, 15, 116, 117, 118, 16, 23, 121, 23,
- /* 1960 */ 139, 151, 130, 25, 142, 16, 24, 20, 144, 1,
- /* 1970 */ 142, 130, 130, 61, 53, 53, 37, 151, 53, 53,
- /* 1980 */ 130, 116, 34, 1, 141, 5, 22, 115, 161, 141,
- /* 1990 */ 153, 154, 155, 156, 157, 25, 68, 68, 75, 41,
- /* 2000 */ 115, 24, 131, 20, 19, 125, 22, 96, 22, 22,
- /* 2010 */ 67, 23, 22, 67, 59, 24, 22, 28, 67, 23,
- /* 2020 */ 22, 22, 149, 23, 23, 23, 116, 23, 25, 37,
- /* 2030 */ 97, 141, 23, 23, 22, 143, 25, 75, 88, 34,
- /* 2040 */ 34, 34, 34, 86, 75, 93, 23, 34, 22, 34,
- /* 2050 */ 25, 24, 34, 25, 23, 142, 23, 142, 44, 23,
- /* 2060 */ 23, 23, 11, 23, 25, 22, 22, 22, 15, 23,
- /* 2070 */ 23, 22, 22, 25, 1, 1, 141, 25, 23, 135,
- /* 2080 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2090 */ 319, 319, 319, 319, 141, 141, 319, 319, 319, 319,
- /* 2100 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2110 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2120 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2130 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2140 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2150 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2160 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2170 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2180 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2190 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2200 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2210 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2220 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2230 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2240 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2250 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2260 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2270 */ 319, 319, 319, 319, 319, 319, 319, 319, 319, 319,
- /* 2280 */ 319,
+ /* 1330 */ 232, 270, 153, 154, 155, 115, 116, 66, 19, 20,
+ /* 1340 */ 183, 22, 12, 312, 254, 194, 262, 316, 209, 210,
+ /* 1350 */ 266, 239, 194, 194, 108, 36, 85, 27, 19, 20,
+ /* 1360 */ 265, 22, 183, 245, 144, 94, 25, 48, 217, 218,
+ /* 1370 */ 293, 194, 42, 270, 256, 36, 217, 218, 59, 194,
+ /* 1380 */ 25, 135, 194, 115, 194, 161, 140, 194, 194, 15,
+ /* 1390 */ 71, 194, 312, 63, 217, 218, 316, 194, 59, 131,
+ /* 1400 */ 301, 302, 217, 218, 85, 217, 218, 217, 218, 90,
+ /* 1410 */ 71, 217, 218, 19, 217, 218, 245, 146, 262, 100,
+ /* 1420 */ 217, 218, 266, 265, 85, 106, 107, 256, 312, 90,
+ /* 1430 */ 209, 210, 316, 114, 60, 116, 117, 118, 194, 100,
+ /* 1440 */ 121, 194, 194, 145, 115, 106, 107, 19, 46, 19,
+ /* 1450 */ 20, 24, 22, 114, 194, 116, 117, 118, 194, 245,
+ /* 1460 */ 121, 194, 164, 194, 217, 218, 36, 194, 258, 259,
+ /* 1470 */ 256, 194, 153, 154, 155, 156, 157, 217, 218, 150,
+ /* 1480 */ 31, 217, 218, 142, 217, 218, 217, 218, 39, 59,
+ /* 1490 */ 217, 218, 153, 154, 155, 156, 157, 149, 150, 5,
+ /* 1500 */ 145, 71, 183, 245, 10, 11, 12, 13, 14, 194,
+ /* 1510 */ 116, 17, 129, 227, 256, 85, 194, 115, 194, 23,
+ /* 1520 */ 90, 25, 183, 99, 30, 97, 32, 22, 22, 194,
+ /* 1530 */ 100, 194, 217, 218, 40, 152, 106, 107, 23, 217,
+ /* 1540 */ 218, 194, 19, 20, 114, 22, 116, 117, 118, 257,
+ /* 1550 */ 194, 121, 217, 218, 217, 218, 194, 133, 53, 36,
+ /* 1560 */ 23, 23, 25, 25, 70, 120, 121, 61, 141, 7,
+ /* 1570 */ 8, 121, 78, 217, 218, 81, 23, 227, 25, 217,
+ /* 1580 */ 218, 131, 59, 153, 154, 155, 156, 157, 0, 1,
+ /* 1590 */ 2, 59, 98, 5, 71, 23, 227, 25, 10, 11,
+ /* 1600 */ 12, 13, 14, 83, 84, 17, 23, 23, 25, 25,
+ /* 1610 */ 59, 194, 194, 183, 23, 23, 25, 25, 30, 194,
+ /* 1620 */ 32, 19, 20, 100, 22, 194, 194, 133, 40, 106,
+ /* 1630 */ 107, 108, 138, 139, 194, 217, 218, 114, 36, 116,
+ /* 1640 */ 117, 118, 217, 218, 121, 194, 194, 194, 23, 117,
+ /* 1650 */ 25, 194, 23, 23, 25, 25, 162, 194, 70, 194,
+ /* 1660 */ 145, 59, 23, 153, 25, 155, 78, 194, 117, 81,
+ /* 1670 */ 217, 218, 194, 71, 217, 218, 153, 154, 155, 156,
+ /* 1680 */ 157, 194, 217, 218, 194, 23, 98, 25, 321, 194,
+ /* 1690 */ 217, 218, 194, 19, 20, 194, 22, 153, 23, 155,
+ /* 1700 */ 25, 194, 100, 194, 217, 218, 183, 194, 106, 107,
+ /* 1710 */ 36, 194, 217, 218, 237, 194, 114, 243, 116, 117,
+ /* 1720 */ 118, 133, 194, 121, 217, 218, 138, 139, 194, 194,
+ /* 1730 */ 194, 290, 289, 59, 217, 218, 194, 194, 217, 218,
+ /* 1740 */ 194, 194, 140, 194, 194, 71, 194, 244, 194, 194,
+ /* 1750 */ 162, 217, 218, 194, 194, 153, 154, 155, 156, 157,
+ /* 1760 */ 217, 218, 194, 217, 218, 194, 217, 218, 257, 217,
+ /* 1770 */ 218, 217, 218, 257, 100, 194, 257, 217, 218, 257,
+ /* 1780 */ 106, 107, 215, 299, 194, 183, 192, 194, 114, 194,
+ /* 1790 */ 116, 117, 118, 1, 2, 121, 221, 5, 217, 218,
+ /* 1800 */ 273, 197, 10, 11, 12, 13, 14, 217, 218, 17,
+ /* 1810 */ 217, 218, 217, 218, 140, 194, 246, 194, 273, 295,
+ /* 1820 */ 247, 273, 30, 247, 32, 269, 269, 153, 154, 155,
+ /* 1830 */ 156, 157, 40, 246, 273, 295, 230, 226, 217, 218,
+ /* 1840 */ 217, 218, 220, 261, 220, 282, 220, 19, 20, 244,
+ /* 1850 */ 22, 250, 141, 250, 246, 60, 201, 183, 261, 261,
+ /* 1860 */ 261, 201, 70, 299, 36, 299, 201, 38, 151, 150,
+ /* 1870 */ 78, 285, 22, 81, 296, 296, 43, 235, 18, 238,
+ /* 1880 */ 201, 274, 272, 238, 238, 238, 18, 59, 200, 149,
+ /* 1890 */ 98, 247, 274, 274, 235, 247, 247, 247, 235, 71,
+ /* 1900 */ 272, 201, 200, 158, 292, 62, 291, 201, 200, 22,
+ /* 1910 */ 201, 222, 200, 222, 201, 200, 115, 219, 219, 64,
+ /* 1920 */ 219, 228, 22, 126, 221, 133, 165, 222, 100, 225,
+ /* 1930 */ 138, 139, 225, 219, 106, 107, 24, 219, 228, 219,
+ /* 1940 */ 219, 307, 114, 113, 116, 117, 118, 315, 284, 121,
+ /* 1950 */ 284, 222, 201, 91, 162, 320, 320, 82, 148, 267,
+ /* 1960 */ 145, 267, 22, 279, 201, 158, 281, 251, 147, 146,
+ /* 1970 */ 25, 203, 250, 249, 251, 248, 13, 247, 195, 195,
+ /* 1980 */ 6, 153, 154, 155, 156, 157, 193, 193, 305, 193,
+ /* 1990 */ 208, 305, 302, 214, 214, 214, 208, 223, 223, 214,
+ /* 2000 */ 4, 215, 215, 214, 3, 22, 208, 163, 15, 23,
+ /* 2010 */ 16, 183, 23, 139, 151, 130, 25, 20, 142, 24,
+ /* 2020 */ 16, 144, 1, 142, 130, 130, 61, 37, 53, 151,
+ /* 2030 */ 53, 53, 53, 130, 116, 1, 34, 141, 5, 22,
+ /* 2040 */ 115, 161, 75, 25, 68, 141, 41, 115, 68, 24,
+ /* 2050 */ 20, 19, 131, 125, 67, 67, 96, 22, 22, 22,
+ /* 2060 */ 37, 23, 22, 24, 22, 59, 67, 23, 149, 28,
+ /* 2070 */ 22, 25, 23, 23, 23, 22, 141, 34, 97, 23,
+ /* 2080 */ 23, 34, 116, 22, 143, 25, 34, 75, 34, 34,
+ /* 2090 */ 75, 88, 34, 86, 23, 22, 34, 25, 24, 34,
+ /* 2100 */ 25, 93, 23, 44, 142, 23, 142, 23, 23, 22,
+ /* 2110 */ 11, 25, 23, 25, 23, 22, 22, 22, 1, 23,
+ /* 2120 */ 23, 23, 22, 22, 15, 141, 141, 25, 25, 1,
+ /* 2130 */ 322, 322, 322, 135, 322, 322, 322, 322, 322, 322,
+ /* 2140 */ 322, 141, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2150 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2160 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2170 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2180 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2190 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2200 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2210 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2220 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2230 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2240 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2250 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2260 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2270 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2280 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2290 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2300 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2310 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
+ /* 2320 */ 322, 322, 322, 322, 322, 322, 322, 322,
};
-#define YY_SHIFT_COUNT (574)
+#define YY_SHIFT_COUNT (582)
#define YY_SHIFT_MIN (0)
-#define YY_SHIFT_MAX (2074)
+#define YY_SHIFT_MAX (2128)
static const unsigned short int yy_shift_ofst[] = {
- /* 0 */ 1648, 1477, 1272, 322, 322, 1, 1319, 1478, 1491, 1837,
- /* 10 */ 1837, 1837, 471, 0, 0, 214, 1093, 1837, 1837, 1837,
- /* 20 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
- /* 30 */ 271, 271, 1219, 1219, 216, 88, 1, 1, 1, 1,
- /* 40 */ 1, 40, 111, 258, 361, 469, 512, 583, 622, 693,
- /* 50 */ 732, 803, 842, 913, 1073, 1093, 1093, 1093, 1093, 1093,
- /* 60 */ 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093,
- /* 70 */ 1093, 1093, 1093, 1113, 1093, 1216, 957, 957, 1635, 1662,
- /* 80 */ 1777, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
- /* 90 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
- /* 100 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
- /* 110 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
- /* 120 */ 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837, 1837,
- /* 130 */ 137, 181, 181, 181, 181, 181, 181, 181, 94, 430,
- /* 140 */ 66, 65, 112, 366, 533, 533, 740, 1261, 533, 533,
- /* 150 */ 79, 79, 533, 412, 412, 412, 77, 412, 123, 113,
- /* 160 */ 113, 22, 22, 2096, 2096, 328, 328, 328, 239, 468,
- /* 170 */ 468, 468, 468, 1015, 1015, 409, 366, 1129, 1186, 533,
- /* 180 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533,
- /* 190 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 969,
- /* 200 */ 621, 621, 533, 642, 788, 788, 1228, 1228, 822, 822,
- /* 210 */ 67, 1274, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 1307,
- /* 220 */ 954, 954, 585, 472, 640, 387, 695, 538, 541, 700,
- /* 230 */ 533, 533, 533, 533, 533, 533, 533, 533, 533, 533,
- /* 240 */ 222, 533, 533, 533, 533, 533, 533, 533, 533, 533,
- /* 250 */ 533, 533, 533, 1179, 1179, 1179, 533, 533, 533, 565,
- /* 260 */ 533, 533, 533, 916, 1144, 533, 533, 1288, 533, 533,
- /* 270 */ 533, 533, 533, 533, 533, 533, 639, 1330, 209, 1076,
- /* 280 */ 1076, 1076, 1076, 580, 209, 209, 1313, 768, 917, 649,
- /* 290 */ 1181, 1316, 405, 1316, 1238, 249, 1181, 1181, 249, 1181,
- /* 300 */ 405, 1238, 1369, 464, 1259, 1012, 1012, 1012, 1368, 1368,
- /* 310 */ 1368, 1368, 184, 184, 1326, 904, 1287, 1480, 1712, 1712,
- /* 320 */ 1633, 1633, 1757, 1757, 1633, 1647, 1651, 1783, 1764, 1791,
- /* 330 */ 1791, 1791, 1791, 1633, 1806, 1677, 1651, 1651, 1677, 1783,
- /* 340 */ 1764, 1677, 1764, 1677, 1633, 1806, 1674, 1779, 1633, 1806,
- /* 350 */ 1823, 1633, 1806, 1633, 1806, 1823, 1732, 1732, 1732, 1794,
- /* 360 */ 1840, 1840, 1823, 1732, 1738, 1732, 1794, 1732, 1732, 1701,
- /* 370 */ 1844, 1758, 1758, 1823, 1633, 1789, 1789, 1807, 1807, 1742,
- /* 380 */ 1752, 1877, 1633, 1743, 1742, 1759, 1765, 1677, 1879, 1897,
- /* 390 */ 1897, 1914, 1914, 1914, 2096, 2096, 2096, 2096, 2096, 2096,
- /* 400 */ 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 2096, 207,
- /* 410 */ 1095, 331, 620, 903, 806, 1074, 1483, 1432, 1481, 1322,
- /* 420 */ 1370, 1394, 1515, 1291, 1546, 1547, 1557, 1595, 1598, 1599,
- /* 430 */ 1434, 1453, 1618, 1462, 1567, 1489, 1644, 1654, 1616, 1660,
- /* 440 */ 1548, 1549, 1682, 1685, 1597, 742, 1941, 1945, 1927, 1787,
- /* 450 */ 1937, 1940, 1934, 1936, 1821, 1810, 1832, 1938, 1938, 1942,
- /* 460 */ 1822, 1947, 1824, 1949, 1968, 1828, 1841, 1938, 1842, 1912,
- /* 470 */ 1939, 1938, 1826, 1921, 1922, 1925, 1926, 1850, 1865, 1948,
- /* 480 */ 1843, 1982, 1980, 1964, 1872, 1827, 1928, 1970, 1929, 1923,
- /* 490 */ 1958, 1848, 1885, 1977, 1983, 1985, 1871, 1880, 1984, 1943,
- /* 500 */ 1986, 1987, 1988, 1990, 1946, 1955, 1991, 1911, 1989, 1994,
- /* 510 */ 1951, 1992, 1996, 1873, 1998, 2000, 2001, 2002, 2003, 2004,
- /* 520 */ 1999, 1933, 1890, 2009, 2010, 1910, 2005, 2012, 1892, 2011,
- /* 530 */ 2006, 2007, 2008, 2013, 1950, 1962, 1957, 2014, 1969, 1952,
- /* 540 */ 2015, 2023, 2026, 2027, 2025, 2028, 2018, 1913, 1915, 2031,
- /* 550 */ 2011, 2033, 2036, 2037, 2038, 2039, 2040, 2043, 2051, 2044,
- /* 560 */ 2045, 2046, 2047, 2049, 2050, 2048, 1944, 1935, 1953, 1954,
- /* 570 */ 2052, 2055, 2053, 2073, 2074,
+ /* 0 */ 1792, 1588, 1494, 322, 322, 399, 306, 1319, 1339, 1430,
+ /* 10 */ 1828, 1828, 1828, 580, 399, 399, 399, 399, 399, 0,
+ /* 20 */ 0, 214, 1093, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
+ /* 30 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1130, 1130,
+ /* 40 */ 365, 365, 55, 278, 436, 713, 713, 201, 201, 201,
+ /* 50 */ 201, 40, 111, 258, 361, 469, 512, 583, 622, 693,
+ /* 60 */ 732, 803, 842, 913, 1073, 1093, 1093, 1093, 1093, 1093,
+ /* 70 */ 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093, 1093,
+ /* 80 */ 1093, 1093, 1093, 1113, 1093, 1216, 957, 957, 1523, 1602,
+ /* 90 */ 1674, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
+ /* 100 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
+ /* 110 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
+ /* 120 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
+ /* 130 */ 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828, 1828,
+ /* 140 */ 137, 181, 181, 181, 181, 181, 181, 181, 96, 222,
+ /* 150 */ 143, 477, 713, 1133, 1268, 713, 713, 79, 79, 713,
+ /* 160 */ 770, 83, 65, 65, 65, 288, 162, 162, 2142, 2142,
+ /* 170 */ 696, 696, 696, 238, 474, 474, 474, 474, 1217, 1217,
+ /* 180 */ 678, 477, 324, 398, 713, 713, 713, 713, 713, 713,
+ /* 190 */ 713, 713, 713, 713, 713, 713, 713, 713, 713, 713,
+ /* 200 */ 713, 713, 713, 1220, 366, 366, 713, 917, 283, 283,
+ /* 210 */ 434, 434, 605, 605, 1298, 2142, 2142, 2142, 2142, 2142,
+ /* 220 */ 2142, 2142, 1179, 1157, 1157, 487, 527, 585, 645, 749,
+ /* 230 */ 914, 968, 752, 713, 713, 713, 713, 713, 713, 713,
+ /* 240 */ 713, 713, 713, 303, 713, 713, 713, 713, 713, 713,
+ /* 250 */ 713, 713, 713, 713, 713, 713, 797, 797, 797, 713,
+ /* 260 */ 713, 713, 959, 713, 713, 713, 1169, 1271, 713, 713,
+ /* 270 */ 1330, 713, 713, 713, 713, 713, 713, 713, 713, 629,
+ /* 280 */ 7, 91, 876, 876, 876, 876, 953, 91, 91, 1246,
+ /* 290 */ 1065, 1106, 1374, 1329, 1348, 468, 1348, 1394, 785, 1329,
+ /* 300 */ 1329, 785, 1329, 468, 1394, 859, 854, 1402, 1449, 1449,
+ /* 310 */ 1449, 1173, 1173, 1173, 1173, 1355, 1355, 1030, 1341, 405,
+ /* 320 */ 1230, 1795, 1795, 1711, 1711, 1829, 1829, 1711, 1717, 1719,
+ /* 330 */ 1850, 1833, 1860, 1860, 1860, 1860, 1711, 1868, 1740, 1719,
+ /* 340 */ 1719, 1740, 1850, 1833, 1740, 1833, 1740, 1711, 1868, 1745,
+ /* 350 */ 1843, 1711, 1868, 1887, 1711, 1868, 1711, 1868, 1887, 1801,
+ /* 360 */ 1801, 1801, 1855, 1900, 1900, 1887, 1801, 1797, 1801, 1855,
+ /* 370 */ 1801, 1801, 1761, 1912, 1830, 1830, 1887, 1711, 1862, 1862,
+ /* 380 */ 1875, 1875, 1810, 1815, 1940, 1711, 1807, 1810, 1821, 1823,
+ /* 390 */ 1740, 1945, 1963, 1963, 1974, 1974, 1974, 2142, 2142, 2142,
+ /* 400 */ 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142, 2142,
+ /* 410 */ 2142, 2142, 20, 1224, 256, 1111, 1115, 1114, 1192, 1496,
+ /* 420 */ 1424, 1505, 1427, 355, 1383, 1537, 1506, 1538, 1553, 1583,
+ /* 430 */ 1584, 1591, 1625, 541, 1445, 1562, 1450, 1572, 1515, 1428,
+ /* 440 */ 1532, 1592, 1629, 1520, 1630, 1639, 1510, 1544, 1662, 1675,
+ /* 450 */ 1551, 48, 1996, 2001, 1983, 1844, 1993, 1994, 1986, 1989,
+ /* 460 */ 1874, 1863, 1885, 1991, 1991, 1995, 1876, 1997, 1877, 2004,
+ /* 470 */ 2021, 1881, 1894, 1991, 1895, 1965, 1990, 1991, 1878, 1975,
+ /* 480 */ 1977, 1978, 1979, 1903, 1918, 2002, 1896, 2034, 2033, 2017,
+ /* 490 */ 1925, 1880, 1976, 2018, 1980, 1967, 2005, 1904, 1932, 2025,
+ /* 500 */ 2030, 2032, 1921, 1928, 2035, 1987, 2036, 2037, 2038, 2040,
+ /* 510 */ 1988, 2006, 2039, 1960, 2041, 2042, 1999, 2023, 2044, 2043,
+ /* 520 */ 1919, 2048, 2049, 2050, 2046, 2051, 2053, 1981, 1935, 2056,
+ /* 530 */ 2057, 1966, 2047, 2061, 1941, 2060, 2052, 2054, 2055, 2058,
+ /* 540 */ 2003, 2012, 2007, 2059, 2015, 2008, 2062, 2071, 2073, 2074,
+ /* 550 */ 2072, 2075, 2065, 1962, 1964, 2079, 2060, 2082, 2084, 2085,
+ /* 560 */ 2087, 2086, 2089, 2088, 2091, 2093, 2099, 2094, 2095, 2096,
+ /* 570 */ 2097, 2100, 2101, 2102, 1998, 1984, 1985, 2000, 2103, 2098,
+ /* 580 */ 2109, 2117, 2128,
};
-#define YY_REDUCE_COUNT (408)
-#define YY_REDUCE_MIN (-271)
-#define YY_REDUCE_MAX (1740)
+#define YY_REDUCE_COUNT (411)
+#define YY_REDUCE_MIN (-275)
+#define YY_REDUCE_MAX (1798)
static const short yy_reduce_ofst[] = {
- /* 0 */ -125, 733, 789, 241, 293, -123, -193, -191, -183, -187,
- /* 10 */ 166, 238, 133, -207, -199, -267, -176, -6, 204, 489,
- /* 20 */ 576, -175, 598, 686, 615, 725, 860, 778, 781, 857,
- /* 30 */ 616, 887, 87, 240, -192, 408, 626, 796, 843, 854,
- /* 40 */ 1003, -271, -271, -271, -271, -271, -271, -271, -271, -271,
- /* 50 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271,
- /* 60 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271,
- /* 70 */ -271, -271, -271, -271, -271, -271, -271, -271, 80, 83,
- /* 80 */ 313, 886, 888, 996, 1034, 1059, 1081, 1100, 1117, 1152,
- /* 90 */ 1155, 1163, 1165, 1167, 1169, 1172, 1180, 1182, 1184, 1198,
- /* 100 */ 1200, 1213, 1215, 1225, 1227, 1252, 1254, 1264, 1299, 1303,
- /* 110 */ 1308, 1312, 1325, 1328, 1337, 1340, 1343, 1371, 1373, 1384,
- /* 120 */ 1386, 1411, 1420, 1424, 1426, 1458, 1470, 1473, 1475, 1479,
- /* 130 */ -271, -271, -271, -271, -271, -271, -271, -271, -271, -271,
- /* 140 */ -271, 138, 459, 396, -158, 470, 302, -212, 521, 201,
- /* 150 */ -195, -92, 559, 630, 632, 630, -271, 632, 901, 63,
- /* 160 */ 407, -271, -271, -271, -271, 161, 161, 161, 251, 335,
- /* 170 */ 847, 960, 980, 537, 588, 618, 628, 688, 688, -166,
- /* 180 */ -161, 674, 790, 794, 799, 851, 852, -122, 680, -120,
- /* 190 */ 995, 1038, 415, 1051, 893, 798, 962, 400, 1086, 779,
- /* 200 */ 923, 924, 263, 1041, 979, 990, 1083, 1097, 1031, 1194,
- /* 210 */ 362, 994, 1139, 1005, 1037, 1202, 1205, 1195, 1210, -194,
- /* 220 */ 56, 185, -135, 232, 522, 560, 601, 617, 669, 683,
- /* 230 */ 711, 856, 908, 941, 1048, 1101, 1147, 1257, 1262, 1265,
- /* 240 */ 392, 1292, 1333, 1339, 1342, 1346, 1350, 1359, 1374, 1418,
- /* 250 */ 1421, 1436, 1437, 593, 755, 770, 997, 1445, 1459, 1209,
- /* 260 */ 1500, 1504, 1516, 1132, 1243, 1518, 1519, 1440, 1520, 560,
- /* 270 */ 1522, 1523, 1524, 1526, 1527, 1529, 1382, 1438, 1431, 1468,
- /* 280 */ 1469, 1472, 1476, 1209, 1431, 1431, 1485, 1525, 1539, 1435,
- /* 290 */ 1463, 1471, 1492, 1487, 1443, 1494, 1474, 1484, 1498, 1486,
- /* 300 */ 1502, 1455, 1530, 1531, 1533, 1540, 1542, 1544, 1505, 1506,
- /* 310 */ 1507, 1508, 1521, 1528, 1493, 1537, 1532, 1575, 1488, 1496,
- /* 320 */ 1584, 1594, 1509, 1510, 1600, 1538, 1534, 1541, 1574, 1577,
- /* 330 */ 1583, 1585, 1586, 1612, 1626, 1581, 1556, 1558, 1587, 1559,
- /* 340 */ 1601, 1588, 1603, 1592, 1631, 1640, 1550, 1553, 1643, 1645,
- /* 350 */ 1625, 1649, 1652, 1650, 1653, 1632, 1636, 1637, 1642, 1634,
- /* 360 */ 1639, 1641, 1646, 1656, 1655, 1658, 1659, 1661, 1663, 1560,
- /* 370 */ 1564, 1596, 1605, 1664, 1670, 1565, 1571, 1627, 1638, 1657,
- /* 380 */ 1665, 1623, 1702, 1630, 1666, 1667, 1671, 1673, 1703, 1718,
- /* 390 */ 1719, 1729, 1730, 1731, 1621, 1622, 1628, 1720, 1713, 1716,
- /* 400 */ 1722, 1723, 1733, 1717, 1724, 1727, 1728, 1725, 1740,
+ /* 0 */ -71, 194, 343, 835, -180, -177, 838, -194, -188, -185,
+ /* 10 */ -183, 82, 183, -65, 133, 245, 346, 407, 458, -178,
+ /* 20 */ 75, -275, -4, 310, 312, 489, 575, 596, 463, 686,
+ /* 30 */ 707, 725, 780, 1098, 856, 778, 1059, 1090, 708, 887,
+ /* 40 */ 86, 448, 980, 630, 680, 681, 684, 796, 801, 796,
+ /* 50 */ 801, -261, -261, -261, -261, -261, -261, -261, -261, -261,
+ /* 60 */ -261, -261, -261, -261, -261, -261, -261, -261, -261, -261,
+ /* 70 */ -261, -261, -261, -261, -261, -261, -261, -261, -261, -261,
+ /* 80 */ -261, -261, -261, -261, -261, -261, -261, -261, 391, 886,
+ /* 90 */ 888, 1013, 1016, 1081, 1087, 1151, 1159, 1177, 1185, 1188,
+ /* 100 */ 1190, 1194, 1197, 1203, 1247, 1260, 1264, 1267, 1269, 1273,
+ /* 110 */ 1315, 1322, 1335, 1337, 1356, 1362, 1418, 1425, 1453, 1457,
+ /* 120 */ 1465, 1473, 1487, 1495, 1507, 1517, 1521, 1534, 1543, 1546,
+ /* 130 */ 1549, 1552, 1554, 1560, 1581, 1590, 1593, 1595, 1621, 1623,
+ /* 140 */ -261, -261, -261, -261, -261, -261, -261, -261, -261, -261,
+ /* 150 */ -261, -186, -117, 260, 263, 460, 631, -74, 497, -181,
+ /* 160 */ -261, 939, 176, 274, 338, 676, -261, -261, -261, -261,
+ /* 170 */ -212, -212, -212, -184, 149, 777, 1061, 1103, 265, 419,
+ /* 180 */ -254, 670, 677, 677, -11, -129, 184, 488, 736, 789,
+ /* 190 */ 805, 844, 403, 529, 579, 668, 783, 841, 1158, 1112,
+ /* 200 */ 806, 861, 1095, 846, 839, 1031, -189, 1077, 1080, 1116,
+ /* 210 */ 1084, 1156, 1139, 1221, 46, 1099, 1037, 1118, 1171, 1214,
+ /* 220 */ 1210, 1258, -210, -190, -176, -115, 117, 262, 376, 490,
+ /* 230 */ 511, 520, 618, 639, 743, 901, 907, 958, 1014, 1055,
+ /* 240 */ 1108, 1193, 1244, 720, 1248, 1277, 1324, 1347, 1417, 1431,
+ /* 250 */ 1432, 1440, 1451, 1452, 1463, 1478, 1286, 1350, 1369, 1490,
+ /* 260 */ 1498, 1501, 773, 1509, 1513, 1528, 1292, 1367, 1535, 1536,
+ /* 270 */ 1477, 1542, 376, 1547, 1550, 1555, 1559, 1568, 1571, 1441,
+ /* 280 */ 1443, 1474, 1511, 1516, 1519, 1522, 773, 1474, 1474, 1503,
+ /* 290 */ 1567, 1594, 1484, 1527, 1556, 1570, 1557, 1524, 1573, 1545,
+ /* 300 */ 1548, 1576, 1561, 1587, 1540, 1575, 1606, 1611, 1622, 1624,
+ /* 310 */ 1626, 1582, 1597, 1598, 1599, 1601, 1603, 1563, 1608, 1605,
+ /* 320 */ 1604, 1564, 1566, 1655, 1660, 1578, 1579, 1665, 1586, 1607,
+ /* 330 */ 1610, 1642, 1641, 1645, 1646, 1647, 1679, 1688, 1644, 1618,
+ /* 340 */ 1619, 1648, 1628, 1659, 1649, 1663, 1650, 1700, 1702, 1612,
+ /* 350 */ 1615, 1706, 1708, 1689, 1709, 1712, 1713, 1715, 1691, 1698,
+ /* 360 */ 1699, 1701, 1693, 1704, 1707, 1705, 1714, 1703, 1718, 1710,
+ /* 370 */ 1720, 1721, 1632, 1634, 1664, 1666, 1729, 1751, 1635, 1636,
+ /* 380 */ 1692, 1694, 1716, 1722, 1684, 1763, 1685, 1723, 1724, 1727,
+ /* 390 */ 1730, 1768, 1783, 1784, 1793, 1794, 1796, 1683, 1686, 1690,
+ /* 400 */ 1782, 1779, 1780, 1781, 1785, 1788, 1774, 1775, 1786, 1787,
+ /* 410 */ 1789, 1798,
};
static const YYACTIONTYPE yy_default[] = {
- /* 0 */ 1639, 1639, 1639, 1469, 1236, 1347, 1236, 1236, 1236, 1469,
- /* 10 */ 1469, 1469, 1236, 1377, 1377, 1522, 1269, 1236, 1236, 1236,
- /* 20 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1468, 1236, 1236,
- /* 30 */ 1236, 1236, 1555, 1555, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 40 */ 1236, 1236, 1386, 1236, 1393, 1236, 1236, 1236, 1236, 1236,
- /* 50 */ 1470, 1471, 1236, 1236, 1236, 1521, 1523, 1486, 1400, 1399,
- /* 60 */ 1398, 1397, 1504, 1365, 1391, 1384, 1388, 1465, 1466, 1464,
- /* 70 */ 1617, 1471, 1470, 1236, 1387, 1433, 1449, 1432, 1236, 1236,
- /* 80 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 90 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 100 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 110 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 120 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 130 */ 1441, 1448, 1447, 1446, 1455, 1445, 1442, 1435, 1434, 1436,
- /* 140 */ 1437, 1236, 1236, 1260, 1236, 1236, 1257, 1311, 1236, 1236,
- /* 150 */ 1236, 1236, 1236, 1541, 1540, 1236, 1438, 1236, 1269, 1427,
- /* 160 */ 1426, 1452, 1439, 1451, 1450, 1529, 1591, 1590, 1487, 1236,
- /* 170 */ 1236, 1236, 1236, 1236, 1236, 1555, 1236, 1236, 1236, 1236,
- /* 180 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 190 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1367,
- /* 200 */ 1555, 1555, 1236, 1269, 1555, 1555, 1368, 1368, 1265, 1265,
- /* 210 */ 1371, 1236, 1536, 1338, 1338, 1338, 1338, 1347, 1338, 1236,
- /* 220 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 230 */ 1236, 1236, 1236, 1236, 1526, 1524, 1236, 1236, 1236, 1236,
- /* 240 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 250 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 260 */ 1236, 1236, 1236, 1343, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 270 */ 1236, 1236, 1236, 1236, 1236, 1584, 1236, 1499, 1325, 1343,
- /* 280 */ 1343, 1343, 1343, 1345, 1326, 1324, 1337, 1270, 1243, 1631,
- /* 290 */ 1403, 1392, 1344, 1392, 1628, 1390, 1403, 1403, 1390, 1403,
- /* 300 */ 1344, 1628, 1286, 1606, 1281, 1377, 1377, 1377, 1367, 1367,
- /* 310 */ 1367, 1367, 1371, 1371, 1467, 1344, 1337, 1236, 1631, 1631,
- /* 320 */ 1353, 1353, 1630, 1630, 1353, 1487, 1614, 1412, 1314, 1320,
- /* 330 */ 1320, 1320, 1320, 1353, 1254, 1390, 1614, 1614, 1390, 1412,
- /* 340 */ 1314, 1390, 1314, 1390, 1353, 1254, 1503, 1625, 1353, 1254,
- /* 350 */ 1477, 1353, 1254, 1353, 1254, 1477, 1312, 1312, 1312, 1301,
- /* 360 */ 1236, 1236, 1477, 1312, 1286, 1312, 1301, 1312, 1312, 1573,
- /* 370 */ 1236, 1481, 1481, 1477, 1353, 1565, 1565, 1380, 1380, 1385,
- /* 380 */ 1371, 1472, 1353, 1236, 1385, 1383, 1381, 1390, 1304, 1587,
- /* 390 */ 1587, 1583, 1583, 1583, 1636, 1636, 1536, 1599, 1269, 1269,
- /* 400 */ 1269, 1269, 1599, 1288, 1288, 1270, 1270, 1269, 1599, 1236,
- /* 410 */ 1236, 1236, 1236, 1236, 1236, 1594, 1236, 1531, 1488, 1357,
- /* 420 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 430 */ 1236, 1236, 1236, 1236, 1542, 1236, 1236, 1236, 1236, 1236,
- /* 440 */ 1236, 1236, 1236, 1236, 1236, 1417, 1236, 1239, 1533, 1236,
- /* 450 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1394, 1395, 1358,
- /* 460 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1409, 1236, 1236,
- /* 470 */ 1236, 1404, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 480 */ 1627, 1236, 1236, 1236, 1236, 1236, 1236, 1502, 1501, 1236,
- /* 490 */ 1236, 1355, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 500 */ 1236, 1236, 1236, 1236, 1236, 1284, 1236, 1236, 1236, 1236,
- /* 510 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 520 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1382,
- /* 530 */ 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 540 */ 1236, 1236, 1236, 1236, 1570, 1372, 1236, 1236, 1236, 1236,
- /* 550 */ 1618, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236, 1236,
- /* 560 */ 1236, 1236, 1236, 1236, 1236, 1610, 1328, 1418, 1236, 1421,
- /* 570 */ 1258, 1236, 1248, 1236, 1236,
+ /* 0 */ 1663, 1663, 1663, 1491, 1254, 1367, 1254, 1254, 1254, 1254,
+ /* 10 */ 1491, 1491, 1491, 1254, 1254, 1254, 1254, 1254, 1254, 1397,
+ /* 20 */ 1397, 1544, 1287, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 30 */ 1254, 1254, 1254, 1254, 1254, 1490, 1254, 1254, 1254, 1254,
+ /* 40 */ 1578, 1578, 1254, 1254, 1254, 1254, 1254, 1563, 1562, 1254,
+ /* 50 */ 1254, 1254, 1406, 1254, 1413, 1254, 1254, 1254, 1254, 1254,
+ /* 60 */ 1492, 1493, 1254, 1254, 1254, 1543, 1545, 1508, 1420, 1419,
+ /* 70 */ 1418, 1417, 1526, 1385, 1411, 1404, 1408, 1487, 1488, 1486,
+ /* 80 */ 1641, 1493, 1492, 1254, 1407, 1455, 1471, 1454, 1254, 1254,
+ /* 90 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 100 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 110 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 120 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 130 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 140 */ 1463, 1470, 1469, 1468, 1477, 1467, 1464, 1457, 1456, 1458,
+ /* 150 */ 1459, 1278, 1254, 1275, 1329, 1254, 1254, 1254, 1254, 1254,
+ /* 160 */ 1460, 1287, 1448, 1447, 1446, 1254, 1474, 1461, 1473, 1472,
+ /* 170 */ 1551, 1615, 1614, 1509, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 180 */ 1578, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 190 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 200 */ 1254, 1254, 1254, 1387, 1578, 1578, 1254, 1287, 1578, 1578,
+ /* 210 */ 1388, 1388, 1283, 1283, 1391, 1558, 1358, 1358, 1358, 1358,
+ /* 220 */ 1367, 1358, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 230 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1548, 1546, 1254,
+ /* 240 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 250 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 260 */ 1254, 1254, 1254, 1254, 1254, 1254, 1363, 1254, 1254, 1254,
+ /* 270 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1608, 1254,
+ /* 280 */ 1521, 1343, 1363, 1363, 1363, 1363, 1365, 1344, 1342, 1357,
+ /* 290 */ 1288, 1261, 1655, 1423, 1412, 1364, 1412, 1652, 1410, 1423,
+ /* 300 */ 1423, 1410, 1423, 1364, 1652, 1304, 1630, 1299, 1397, 1397,
+ /* 310 */ 1397, 1387, 1387, 1387, 1387, 1391, 1391, 1489, 1364, 1357,
+ /* 320 */ 1254, 1655, 1655, 1373, 1373, 1654, 1654, 1373, 1509, 1638,
+ /* 330 */ 1432, 1332, 1338, 1338, 1338, 1338, 1373, 1272, 1410, 1638,
+ /* 340 */ 1638, 1410, 1432, 1332, 1410, 1332, 1410, 1373, 1272, 1525,
+ /* 350 */ 1649, 1373, 1272, 1499, 1373, 1272, 1373, 1272, 1499, 1330,
+ /* 360 */ 1330, 1330, 1319, 1254, 1254, 1499, 1330, 1304, 1330, 1319,
+ /* 370 */ 1330, 1330, 1596, 1254, 1503, 1503, 1499, 1373, 1588, 1588,
+ /* 380 */ 1400, 1400, 1405, 1391, 1494, 1373, 1254, 1405, 1403, 1401,
+ /* 390 */ 1410, 1322, 1611, 1611, 1607, 1607, 1607, 1660, 1660, 1558,
+ /* 400 */ 1623, 1287, 1287, 1287, 1287, 1623, 1306, 1306, 1288, 1288,
+ /* 410 */ 1287, 1623, 1254, 1254, 1254, 1254, 1254, 1254, 1618, 1254,
+ /* 420 */ 1553, 1510, 1377, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 430 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1564,
+ /* 440 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 450 */ 1254, 1437, 1254, 1257, 1555, 1254, 1254, 1254, 1254, 1254,
+ /* 460 */ 1254, 1254, 1254, 1414, 1415, 1378, 1254, 1254, 1254, 1254,
+ /* 470 */ 1254, 1254, 1254, 1429, 1254, 1254, 1254, 1424, 1254, 1254,
+ /* 480 */ 1254, 1254, 1254, 1254, 1254, 1254, 1651, 1254, 1254, 1254,
+ /* 490 */ 1254, 1254, 1254, 1524, 1523, 1254, 1254, 1375, 1254, 1254,
+ /* 500 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 510 */ 1254, 1302, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 520 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 530 */ 1254, 1254, 1254, 1254, 1254, 1402, 1254, 1254, 1254, 1254,
+ /* 540 */ 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 550 */ 1593, 1392, 1254, 1254, 1254, 1254, 1642, 1254, 1254, 1254,
+ /* 560 */ 1254, 1352, 1254, 1254, 1254, 1254, 1254, 1254, 1254, 1254,
+ /* 570 */ 1254, 1254, 1254, 1634, 1346, 1438, 1254, 1441, 1276, 1254,
+ /* 580 */ 1266, 1254, 1254,
};
/********** End of lemon-generated parsing tables *****************************/
@@ -170615,8 +173902,8 @@ static const YYCODETYPE yyFallback[] = {
0, /* TRUEFALSE => nothing */
0, /* ISNOT => nothing */
0, /* FUNCTION => nothing */
- 0, /* UMINUS => nothing */
0, /* UPLUS => nothing */
+ 0, /* UMINUS => nothing */
0, /* TRUTH => nothing */
0, /* REGISTER => nothing */
0, /* VECTOR => nothing */
@@ -170625,6 +173912,7 @@ static const YYCODETYPE yyFallback[] = {
0, /* ASTERISK => nothing */
0, /* SPAN => nothing */
0, /* ERROR => nothing */
+ 0, /* QNUMBER => nothing */
0, /* SPACE => nothing */
0, /* ILLEGAL => nothing */
};
@@ -170667,14 +173955,9 @@ struct yyParser {
#endif
sqlite3ParserARG_SDECL /* A place to hold %extra_argument */
sqlite3ParserCTX_SDECL /* A place to hold %extra_context */
-#if YYSTACKDEPTH<=0
- int yystksz; /* Current side of the stack */
- yyStackEntry *yystack; /* The parser's stack */
- yyStackEntry yystk0; /* First stack entry */
-#else
- yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */
- yyStackEntry *yystackEnd; /* Last entry in the stack */
-#endif
+ yyStackEntry *yystackEnd; /* Last entry in the stack */
+ yyStackEntry *yystack; /* The parser stack */
+ yyStackEntry yystk0[YYSTACKDEPTH]; /* Initial stack space */
};
typedef struct yyParser yyParser;
@@ -170888,8 +174171,8 @@ static const char *const yyTokenName[] = {
/* 170 */ "TRUEFALSE",
/* 171 */ "ISNOT",
/* 172 */ "FUNCTION",
- /* 173 */ "UMINUS",
- /* 174 */ "UPLUS",
+ /* 173 */ "UPLUS",
+ /* 174 */ "UMINUS",
/* 175 */ "TRUTH",
/* 176 */ "REGISTER",
/* 177 */ "VECTOR",
@@ -170898,142 +174181,145 @@ static const char *const yyTokenName[] = {
/* 180 */ "ASTERISK",
/* 181 */ "SPAN",
/* 182 */ "ERROR",
- /* 183 */ "SPACE",
- /* 184 */ "ILLEGAL",
- /* 185 */ "input",
- /* 186 */ "cmdlist",
- /* 187 */ "ecmd",
- /* 188 */ "cmdx",
- /* 189 */ "explain",
- /* 190 */ "cmd",
- /* 191 */ "transtype",
- /* 192 */ "trans_opt",
- /* 193 */ "nm",
- /* 194 */ "savepoint_opt",
- /* 195 */ "create_table",
- /* 196 */ "create_table_args",
- /* 197 */ "createkw",
- /* 198 */ "temp",
- /* 199 */ "ifnotexists",
- /* 200 */ "dbnm",
- /* 201 */ "columnlist",
- /* 202 */ "conslist_opt",
- /* 203 */ "table_option_set",
- /* 204 */ "select",
- /* 205 */ "table_option",
- /* 206 */ "columnname",
- /* 207 */ "carglist",
- /* 208 */ "typetoken",
- /* 209 */ "typename",
- /* 210 */ "signed",
- /* 211 */ "plus_num",
- /* 212 */ "minus_num",
- /* 213 */ "scanpt",
- /* 214 */ "scantok",
- /* 215 */ "ccons",
- /* 216 */ "term",
- /* 217 */ "expr",
- /* 218 */ "onconf",
- /* 219 */ "sortorder",
- /* 220 */ "autoinc",
- /* 221 */ "eidlist_opt",
- /* 222 */ "refargs",
- /* 223 */ "defer_subclause",
- /* 224 */ "generated",
- /* 225 */ "refarg",
- /* 226 */ "refact",
- /* 227 */ "init_deferred_pred_opt",
- /* 228 */ "conslist",
- /* 229 */ "tconscomma",
- /* 230 */ "tcons",
- /* 231 */ "sortlist",
- /* 232 */ "eidlist",
- /* 233 */ "defer_subclause_opt",
- /* 234 */ "orconf",
- /* 235 */ "resolvetype",
- /* 236 */ "raisetype",
- /* 237 */ "ifexists",
- /* 238 */ "fullname",
- /* 239 */ "selectnowith",
- /* 240 */ "oneselect",
- /* 241 */ "wqlist",
- /* 242 */ "multiselect_op",
- /* 243 */ "distinct",
- /* 244 */ "selcollist",
- /* 245 */ "from",
- /* 246 */ "where_opt",
- /* 247 */ "groupby_opt",
- /* 248 */ "having_opt",
- /* 249 */ "orderby_opt",
- /* 250 */ "limit_opt",
- /* 251 */ "window_clause",
- /* 252 */ "values",
- /* 253 */ "nexprlist",
- /* 254 */ "sclp",
- /* 255 */ "as",
- /* 256 */ "seltablist",
- /* 257 */ "stl_prefix",
- /* 258 */ "joinop",
- /* 259 */ "on_using",
- /* 260 */ "indexed_by",
- /* 261 */ "exprlist",
- /* 262 */ "xfullname",
- /* 263 */ "idlist",
- /* 264 */ "indexed_opt",
- /* 265 */ "nulls",
- /* 266 */ "with",
- /* 267 */ "where_opt_ret",
- /* 268 */ "setlist",
- /* 269 */ "insert_cmd",
- /* 270 */ "idlist_opt",
- /* 271 */ "upsert",
- /* 272 */ "returning",
- /* 273 */ "filter_over",
- /* 274 */ "likeop",
- /* 275 */ "between_op",
- /* 276 */ "in_op",
- /* 277 */ "paren_exprlist",
- /* 278 */ "case_operand",
- /* 279 */ "case_exprlist",
- /* 280 */ "case_else",
- /* 281 */ "uniqueflag",
- /* 282 */ "collate",
- /* 283 */ "vinto",
- /* 284 */ "nmnum",
- /* 285 */ "trigger_decl",
- /* 286 */ "trigger_cmd_list",
- /* 287 */ "trigger_time",
- /* 288 */ "trigger_event",
- /* 289 */ "foreach_clause",
- /* 290 */ "when_clause",
- /* 291 */ "trigger_cmd",
- /* 292 */ "trnm",
- /* 293 */ "tridxby",
- /* 294 */ "database_kw_opt",
- /* 295 */ "key_opt",
- /* 296 */ "add_column_fullname",
- /* 297 */ "kwcolumn_opt",
- /* 298 */ "create_vtab",
- /* 299 */ "vtabarglist",
- /* 300 */ "vtabarg",
- /* 301 */ "vtabargtoken",
- /* 302 */ "lp",
- /* 303 */ "anylist",
- /* 304 */ "wqitem",
- /* 305 */ "wqas",
- /* 306 */ "windowdefn_list",
- /* 307 */ "windowdefn",
- /* 308 */ "window",
- /* 309 */ "frame_opt",
- /* 310 */ "part_opt",
- /* 311 */ "filter_clause",
- /* 312 */ "over_clause",
- /* 313 */ "range_or_rows",
- /* 314 */ "frame_bound",
- /* 315 */ "frame_bound_s",
- /* 316 */ "frame_bound_e",
- /* 317 */ "frame_exclude_opt",
- /* 318 */ "frame_exclude",
+ /* 183 */ "QNUMBER",
+ /* 184 */ "SPACE",
+ /* 185 */ "ILLEGAL",
+ /* 186 */ "input",
+ /* 187 */ "cmdlist",
+ /* 188 */ "ecmd",
+ /* 189 */ "cmdx",
+ /* 190 */ "explain",
+ /* 191 */ "cmd",
+ /* 192 */ "transtype",
+ /* 193 */ "trans_opt",
+ /* 194 */ "nm",
+ /* 195 */ "savepoint_opt",
+ /* 196 */ "create_table",
+ /* 197 */ "create_table_args",
+ /* 198 */ "createkw",
+ /* 199 */ "temp",
+ /* 200 */ "ifnotexists",
+ /* 201 */ "dbnm",
+ /* 202 */ "columnlist",
+ /* 203 */ "conslist_opt",
+ /* 204 */ "table_option_set",
+ /* 205 */ "select",
+ /* 206 */ "table_option",
+ /* 207 */ "columnname",
+ /* 208 */ "carglist",
+ /* 209 */ "typetoken",
+ /* 210 */ "typename",
+ /* 211 */ "signed",
+ /* 212 */ "plus_num",
+ /* 213 */ "minus_num",
+ /* 214 */ "scanpt",
+ /* 215 */ "scantok",
+ /* 216 */ "ccons",
+ /* 217 */ "term",
+ /* 218 */ "expr",
+ /* 219 */ "onconf",
+ /* 220 */ "sortorder",
+ /* 221 */ "autoinc",
+ /* 222 */ "eidlist_opt",
+ /* 223 */ "refargs",
+ /* 224 */ "defer_subclause",
+ /* 225 */ "generated",
+ /* 226 */ "refarg",
+ /* 227 */ "refact",
+ /* 228 */ "init_deferred_pred_opt",
+ /* 229 */ "conslist",
+ /* 230 */ "tconscomma",
+ /* 231 */ "tcons",
+ /* 232 */ "sortlist",
+ /* 233 */ "eidlist",
+ /* 234 */ "defer_subclause_opt",
+ /* 235 */ "orconf",
+ /* 236 */ "resolvetype",
+ /* 237 */ "raisetype",
+ /* 238 */ "ifexists",
+ /* 239 */ "fullname",
+ /* 240 */ "selectnowith",
+ /* 241 */ "oneselect",
+ /* 242 */ "wqlist",
+ /* 243 */ "multiselect_op",
+ /* 244 */ "distinct",
+ /* 245 */ "selcollist",
+ /* 246 */ "from",
+ /* 247 */ "where_opt",
+ /* 248 */ "groupby_opt",
+ /* 249 */ "having_opt",
+ /* 250 */ "orderby_opt",
+ /* 251 */ "limit_opt",
+ /* 252 */ "window_clause",
+ /* 253 */ "values",
+ /* 254 */ "nexprlist",
+ /* 255 */ "mvalues",
+ /* 256 */ "sclp",
+ /* 257 */ "as",
+ /* 258 */ "seltablist",
+ /* 259 */ "stl_prefix",
+ /* 260 */ "joinop",
+ /* 261 */ "on_using",
+ /* 262 */ "indexed_by",
+ /* 263 */ "exprlist",
+ /* 264 */ "xfullname",
+ /* 265 */ "idlist",
+ /* 266 */ "indexed_opt",
+ /* 267 */ "nulls",
+ /* 268 */ "with",
+ /* 269 */ "where_opt_ret",
+ /* 270 */ "setlist",
+ /* 271 */ "insert_cmd",
+ /* 272 */ "idlist_opt",
+ /* 273 */ "upsert",
+ /* 274 */ "returning",
+ /* 275 */ "filter_over",
+ /* 276 */ "likeop",
+ /* 277 */ "between_op",
+ /* 278 */ "in_op",
+ /* 279 */ "paren_exprlist",
+ /* 280 */ "case_operand",
+ /* 281 */ "case_exprlist",
+ /* 282 */ "case_else",
+ /* 283 */ "uniqueflag",
+ /* 284 */ "collate",
+ /* 285 */ "vinto",
+ /* 286 */ "nmnum",
+ /* 287 */ "trigger_decl",
+ /* 288 */ "trigger_cmd_list",
+ /* 289 */ "trigger_time",
+ /* 290 */ "trigger_event",
+ /* 291 */ "foreach_clause",
+ /* 292 */ "when_clause",
+ /* 293 */ "trigger_cmd",
+ /* 294 */ "trnm",
+ /* 295 */ "tridxby",
+ /* 296 */ "database_kw_opt",
+ /* 297 */ "key_opt",
+ /* 298 */ "add_column_fullname",
+ /* 299 */ "kwcolumn_opt",
+ /* 300 */ "create_vtab",
+ /* 301 */ "vtabarglist",
+ /* 302 */ "vtabarg",
+ /* 303 */ "vtabargtoken",
+ /* 304 */ "lp",
+ /* 305 */ "anylist",
+ /* 306 */ "wqitem",
+ /* 307 */ "wqas",
+ /* 308 */ "withnm",
+ /* 309 */ "windowdefn_list",
+ /* 310 */ "windowdefn",
+ /* 311 */ "window",
+ /* 312 */ "frame_opt",
+ /* 313 */ "part_opt",
+ /* 314 */ "filter_clause",
+ /* 315 */ "over_clause",
+ /* 316 */ "range_or_rows",
+ /* 317 */ "frame_bound",
+ /* 318 */ "frame_bound_s",
+ /* 319 */ "frame_bound_e",
+ /* 320 */ "frame_exclude_opt",
+ /* 321 */ "frame_exclude",
};
#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
@@ -171136,349 +174422,363 @@ static const char *const yyRuleName[] = {
/* 92 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt",
/* 93 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt",
/* 94 */ "values ::= VALUES LP nexprlist RP",
- /* 95 */ "values ::= values COMMA LP nexprlist RP",
- /* 96 */ "distinct ::= DISTINCT",
- /* 97 */ "distinct ::= ALL",
- /* 98 */ "distinct ::=",
- /* 99 */ "sclp ::=",
- /* 100 */ "selcollist ::= sclp scanpt expr scanpt as",
- /* 101 */ "selcollist ::= sclp scanpt STAR",
- /* 102 */ "selcollist ::= sclp scanpt nm DOT STAR",
- /* 103 */ "as ::= AS nm",
- /* 104 */ "as ::=",
- /* 105 */ "from ::=",
- /* 106 */ "from ::= FROM seltablist",
- /* 107 */ "stl_prefix ::= seltablist joinop",
- /* 108 */ "stl_prefix ::=",
- /* 109 */ "seltablist ::= stl_prefix nm dbnm as on_using",
- /* 110 */ "seltablist ::= stl_prefix nm dbnm as indexed_by on_using",
- /* 111 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using",
- /* 112 */ "seltablist ::= stl_prefix LP select RP as on_using",
- /* 113 */ "seltablist ::= stl_prefix LP seltablist RP as on_using",
- /* 114 */ "dbnm ::=",
- /* 115 */ "dbnm ::= DOT nm",
- /* 116 */ "fullname ::= nm",
- /* 117 */ "fullname ::= nm DOT nm",
- /* 118 */ "xfullname ::= nm",
- /* 119 */ "xfullname ::= nm DOT nm",
- /* 120 */ "xfullname ::= nm DOT nm AS nm",
- /* 121 */ "xfullname ::= nm AS nm",
- /* 122 */ "joinop ::= COMMA|JOIN",
- /* 123 */ "joinop ::= JOIN_KW JOIN",
- /* 124 */ "joinop ::= JOIN_KW nm JOIN",
- /* 125 */ "joinop ::= JOIN_KW nm nm JOIN",
- /* 126 */ "on_using ::= ON expr",
- /* 127 */ "on_using ::= USING LP idlist RP",
- /* 128 */ "on_using ::=",
- /* 129 */ "indexed_opt ::=",
- /* 130 */ "indexed_by ::= INDEXED BY nm",
- /* 131 */ "indexed_by ::= NOT INDEXED",
- /* 132 */ "orderby_opt ::=",
- /* 133 */ "orderby_opt ::= ORDER BY sortlist",
- /* 134 */ "sortlist ::= sortlist COMMA expr sortorder nulls",
- /* 135 */ "sortlist ::= expr sortorder nulls",
- /* 136 */ "sortorder ::= ASC",
- /* 137 */ "sortorder ::= DESC",
- /* 138 */ "sortorder ::=",
- /* 139 */ "nulls ::= NULLS FIRST",
- /* 140 */ "nulls ::= NULLS LAST",
- /* 141 */ "nulls ::=",
- /* 142 */ "groupby_opt ::=",
- /* 143 */ "groupby_opt ::= GROUP BY nexprlist",
- /* 144 */ "having_opt ::=",
- /* 145 */ "having_opt ::= HAVING expr",
- /* 146 */ "limit_opt ::=",
- /* 147 */ "limit_opt ::= LIMIT expr",
- /* 148 */ "limit_opt ::= LIMIT expr OFFSET expr",
- /* 149 */ "limit_opt ::= LIMIT expr COMMA expr",
- /* 150 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret",
- /* 151 */ "where_opt ::=",
- /* 152 */ "where_opt ::= WHERE expr",
- /* 153 */ "where_opt_ret ::=",
- /* 154 */ "where_opt_ret ::= WHERE expr",
- /* 155 */ "where_opt_ret ::= RETURNING selcollist",
- /* 156 */ "where_opt_ret ::= WHERE expr RETURNING selcollist",
- /* 157 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret",
- /* 158 */ "setlist ::= setlist COMMA nm EQ expr",
- /* 159 */ "setlist ::= setlist COMMA LP idlist RP EQ expr",
- /* 160 */ "setlist ::= nm EQ expr",
- /* 161 */ "setlist ::= LP idlist RP EQ expr",
- /* 162 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert",
- /* 163 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning",
- /* 164 */ "upsert ::=",
- /* 165 */ "upsert ::= RETURNING selcollist",
- /* 166 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert",
- /* 167 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert",
- /* 168 */ "upsert ::= ON CONFLICT DO NOTHING returning",
- /* 169 */ "upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning",
- /* 170 */ "returning ::= RETURNING selcollist",
- /* 171 */ "insert_cmd ::= INSERT orconf",
- /* 172 */ "insert_cmd ::= REPLACE",
- /* 173 */ "idlist_opt ::=",
- /* 174 */ "idlist_opt ::= LP idlist RP",
- /* 175 */ "idlist ::= idlist COMMA nm",
- /* 176 */ "idlist ::= nm",
- /* 177 */ "expr ::= LP expr RP",
- /* 178 */ "expr ::= ID|INDEXED|JOIN_KW",
- /* 179 */ "expr ::= nm DOT nm",
- /* 180 */ "expr ::= nm DOT nm DOT nm",
- /* 181 */ "term ::= NULL|FLOAT|BLOB",
- /* 182 */ "term ::= STRING",
- /* 183 */ "term ::= INTEGER",
- /* 184 */ "expr ::= VARIABLE",
- /* 185 */ "expr ::= expr COLLATE ID|STRING",
- /* 186 */ "expr ::= CAST LP expr AS typetoken RP",
- /* 187 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP",
- /* 188 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP",
- /* 189 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over",
- /* 190 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over",
- /* 191 */ "term ::= CTIME_KW",
- /* 192 */ "expr ::= LP nexprlist COMMA expr RP",
- /* 193 */ "expr ::= expr AND expr",
- /* 194 */ "expr ::= expr OR expr",
- /* 195 */ "expr ::= expr LT|GT|GE|LE expr",
- /* 196 */ "expr ::= expr EQ|NE expr",
- /* 197 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
- /* 198 */ "expr ::= expr PLUS|MINUS expr",
- /* 199 */ "expr ::= expr STAR|SLASH|REM expr",
- /* 200 */ "expr ::= expr CONCAT expr",
- /* 201 */ "likeop ::= NOT LIKE_KW|MATCH",
- /* 202 */ "expr ::= expr likeop expr",
- /* 203 */ "expr ::= expr likeop expr ESCAPE expr",
- /* 204 */ "expr ::= expr ISNULL|NOTNULL",
- /* 205 */ "expr ::= expr NOT NULL",
- /* 206 */ "expr ::= expr IS expr",
- /* 207 */ "expr ::= expr IS NOT expr",
- /* 208 */ "expr ::= expr IS NOT DISTINCT FROM expr",
- /* 209 */ "expr ::= expr IS DISTINCT FROM expr",
- /* 210 */ "expr ::= NOT expr",
- /* 211 */ "expr ::= BITNOT expr",
- /* 212 */ "expr ::= PLUS|MINUS expr",
- /* 213 */ "expr ::= expr PTR expr",
- /* 214 */ "between_op ::= BETWEEN",
- /* 215 */ "between_op ::= NOT BETWEEN",
- /* 216 */ "expr ::= expr between_op expr AND expr",
- /* 217 */ "in_op ::= IN",
- /* 218 */ "in_op ::= NOT IN",
- /* 219 */ "expr ::= expr in_op LP exprlist RP",
- /* 220 */ "expr ::= LP select RP",
- /* 221 */ "expr ::= expr in_op LP select RP",
- /* 222 */ "expr ::= expr in_op nm dbnm paren_exprlist",
- /* 223 */ "expr ::= EXISTS LP select RP",
- /* 224 */ "expr ::= CASE case_operand case_exprlist case_else END",
- /* 225 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
- /* 226 */ "case_exprlist ::= WHEN expr THEN expr",
- /* 227 */ "case_else ::= ELSE expr",
- /* 228 */ "case_else ::=",
- /* 229 */ "case_operand ::=",
- /* 230 */ "exprlist ::=",
- /* 231 */ "nexprlist ::= nexprlist COMMA expr",
- /* 232 */ "nexprlist ::= expr",
- /* 233 */ "paren_exprlist ::=",
- /* 234 */ "paren_exprlist ::= LP exprlist RP",
- /* 235 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
- /* 236 */ "uniqueflag ::= UNIQUE",
- /* 237 */ "uniqueflag ::=",
- /* 238 */ "eidlist_opt ::=",
- /* 239 */ "eidlist_opt ::= LP eidlist RP",
- /* 240 */ "eidlist ::= eidlist COMMA nm collate sortorder",
- /* 241 */ "eidlist ::= nm collate sortorder",
- /* 242 */ "collate ::=",
- /* 243 */ "collate ::= COLLATE ID|STRING",
- /* 244 */ "cmd ::= DROP INDEX ifexists fullname",
- /* 245 */ "cmd ::= VACUUM vinto",
- /* 246 */ "cmd ::= VACUUM nm vinto",
- /* 247 */ "vinto ::= INTO expr",
- /* 248 */ "vinto ::=",
- /* 249 */ "cmd ::= PRAGMA nm dbnm",
- /* 250 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
- /* 251 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
- /* 252 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
- /* 253 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
- /* 254 */ "plus_num ::= PLUS INTEGER|FLOAT",
- /* 255 */ "minus_num ::= MINUS INTEGER|FLOAT",
- /* 256 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
- /* 257 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
- /* 258 */ "trigger_time ::= BEFORE|AFTER",
- /* 259 */ "trigger_time ::= INSTEAD OF",
- /* 260 */ "trigger_time ::=",
- /* 261 */ "trigger_event ::= DELETE|INSERT",
- /* 262 */ "trigger_event ::= UPDATE",
- /* 263 */ "trigger_event ::= UPDATE OF idlist",
- /* 264 */ "when_clause ::=",
- /* 265 */ "when_clause ::= WHEN expr",
- /* 266 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
- /* 267 */ "trigger_cmd_list ::= trigger_cmd SEMI",
- /* 268 */ "trnm ::= nm DOT nm",
- /* 269 */ "tridxby ::= INDEXED BY nm",
- /* 270 */ "tridxby ::= NOT INDEXED",
- /* 271 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt",
- /* 272 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt",
- /* 273 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt",
- /* 274 */ "trigger_cmd ::= scanpt select scanpt",
- /* 275 */ "expr ::= RAISE LP IGNORE RP",
- /* 276 */ "expr ::= RAISE LP raisetype COMMA nm RP",
- /* 277 */ "raisetype ::= ROLLBACK",
- /* 278 */ "raisetype ::= ABORT",
- /* 279 */ "raisetype ::= FAIL",
- /* 280 */ "cmd ::= DROP TRIGGER ifexists fullname",
- /* 281 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
- /* 282 */ "cmd ::= DETACH database_kw_opt expr",
- /* 283 */ "key_opt ::=",
- /* 284 */ "key_opt ::= KEY expr",
- /* 285 */ "cmd ::= REINDEX",
- /* 286 */ "cmd ::= REINDEX nm dbnm",
- /* 287 */ "cmd ::= ANALYZE",
- /* 288 */ "cmd ::= ANALYZE nm dbnm",
- /* 289 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
- /* 290 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
- /* 291 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm",
- /* 292 */ "add_column_fullname ::= fullname",
- /* 293 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm",
- /* 294 */ "cmd ::= create_vtab",
- /* 295 */ "cmd ::= create_vtab LP vtabarglist RP",
- /* 296 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
- /* 297 */ "vtabarg ::=",
- /* 298 */ "vtabargtoken ::= ANY",
- /* 299 */ "vtabargtoken ::= lp anylist RP",
- /* 300 */ "lp ::= LP",
- /* 301 */ "with ::= WITH wqlist",
- /* 302 */ "with ::= WITH RECURSIVE wqlist",
- /* 303 */ "wqas ::= AS",
- /* 304 */ "wqas ::= AS MATERIALIZED",
- /* 305 */ "wqas ::= AS NOT MATERIALIZED",
- /* 306 */ "wqitem ::= nm eidlist_opt wqas LP select RP",
- /* 307 */ "wqlist ::= wqitem",
- /* 308 */ "wqlist ::= wqlist COMMA wqitem",
- /* 309 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn",
- /* 310 */ "windowdefn ::= nm AS LP window RP",
- /* 311 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt",
- /* 312 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt",
- /* 313 */ "window ::= ORDER BY sortlist frame_opt",
- /* 314 */ "window ::= nm ORDER BY sortlist frame_opt",
- /* 315 */ "window ::= nm frame_opt",
- /* 316 */ "frame_opt ::=",
- /* 317 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt",
- /* 318 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt",
- /* 319 */ "range_or_rows ::= RANGE|ROWS|GROUPS",
- /* 320 */ "frame_bound_s ::= frame_bound",
- /* 321 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
- /* 322 */ "frame_bound_e ::= frame_bound",
- /* 323 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
- /* 324 */ "frame_bound ::= expr PRECEDING|FOLLOWING",
- /* 325 */ "frame_bound ::= CURRENT ROW",
- /* 326 */ "frame_exclude_opt ::=",
- /* 327 */ "frame_exclude_opt ::= EXCLUDE frame_exclude",
- /* 328 */ "frame_exclude ::= NO OTHERS",
- /* 329 */ "frame_exclude ::= CURRENT ROW",
- /* 330 */ "frame_exclude ::= GROUP|TIES",
- /* 331 */ "window_clause ::= WINDOW windowdefn_list",
- /* 332 */ "filter_over ::= filter_clause over_clause",
- /* 333 */ "filter_over ::= over_clause",
- /* 334 */ "filter_over ::= filter_clause",
- /* 335 */ "over_clause ::= OVER LP window RP",
- /* 336 */ "over_clause ::= OVER nm",
- /* 337 */ "filter_clause ::= FILTER LP WHERE expr RP",
- /* 338 */ "input ::= cmdlist",
- /* 339 */ "cmdlist ::= cmdlist ecmd",
- /* 340 */ "cmdlist ::= ecmd",
- /* 341 */ "ecmd ::= SEMI",
- /* 342 */ "ecmd ::= cmdx SEMI",
- /* 343 */ "ecmd ::= explain cmdx SEMI",
- /* 344 */ "trans_opt ::=",
- /* 345 */ "trans_opt ::= TRANSACTION",
- /* 346 */ "trans_opt ::= TRANSACTION nm",
- /* 347 */ "savepoint_opt ::= SAVEPOINT",
- /* 348 */ "savepoint_opt ::=",
- /* 349 */ "cmd ::= create_table create_table_args",
- /* 350 */ "table_option_set ::= table_option",
- /* 351 */ "columnlist ::= columnlist COMMA columnname carglist",
- /* 352 */ "columnlist ::= columnname carglist",
- /* 353 */ "nm ::= ID|INDEXED|JOIN_KW",
- /* 354 */ "nm ::= STRING",
- /* 355 */ "typetoken ::= typename",
- /* 356 */ "typename ::= ID|STRING",
- /* 357 */ "signed ::= plus_num",
- /* 358 */ "signed ::= minus_num",
- /* 359 */ "carglist ::= carglist ccons",
- /* 360 */ "carglist ::=",
- /* 361 */ "ccons ::= NULL onconf",
- /* 362 */ "ccons ::= GENERATED ALWAYS AS generated",
- /* 363 */ "ccons ::= AS generated",
- /* 364 */ "conslist_opt ::= COMMA conslist",
- /* 365 */ "conslist ::= conslist tconscomma tcons",
- /* 366 */ "conslist ::= tcons",
- /* 367 */ "tconscomma ::=",
- /* 368 */ "defer_subclause_opt ::= defer_subclause",
- /* 369 */ "resolvetype ::= raisetype",
- /* 370 */ "selectnowith ::= oneselect",
- /* 371 */ "oneselect ::= values",
- /* 372 */ "sclp ::= selcollist COMMA",
- /* 373 */ "as ::= ID|STRING",
- /* 374 */ "indexed_opt ::= indexed_by",
- /* 375 */ "returning ::=",
- /* 376 */ "expr ::= term",
- /* 377 */ "likeop ::= LIKE_KW|MATCH",
- /* 378 */ "case_operand ::= expr",
- /* 379 */ "exprlist ::= nexprlist",
- /* 380 */ "nmnum ::= plus_num",
- /* 381 */ "nmnum ::= nm",
- /* 382 */ "nmnum ::= ON",
- /* 383 */ "nmnum ::= DELETE",
- /* 384 */ "nmnum ::= DEFAULT",
- /* 385 */ "plus_num ::= INTEGER|FLOAT",
- /* 386 */ "foreach_clause ::=",
- /* 387 */ "foreach_clause ::= FOR EACH ROW",
- /* 388 */ "trnm ::= nm",
- /* 389 */ "tridxby ::=",
- /* 390 */ "database_kw_opt ::= DATABASE",
- /* 391 */ "database_kw_opt ::=",
- /* 392 */ "kwcolumn_opt ::=",
- /* 393 */ "kwcolumn_opt ::= COLUMNKW",
- /* 394 */ "vtabarglist ::= vtabarg",
- /* 395 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
- /* 396 */ "vtabarg ::= vtabarg vtabargtoken",
- /* 397 */ "anylist ::=",
- /* 398 */ "anylist ::= anylist LP anylist RP",
- /* 399 */ "anylist ::= anylist ANY",
- /* 400 */ "with ::=",
- /* 401 */ "windowdefn_list ::= windowdefn",
- /* 402 */ "window ::= frame_opt",
+ /* 95 */ "oneselect ::= mvalues",
+ /* 96 */ "mvalues ::= values COMMA LP nexprlist RP",
+ /* 97 */ "mvalues ::= mvalues COMMA LP nexprlist RP",
+ /* 98 */ "distinct ::= DISTINCT",
+ /* 99 */ "distinct ::= ALL",
+ /* 100 */ "distinct ::=",
+ /* 101 */ "sclp ::=",
+ /* 102 */ "selcollist ::= sclp scanpt expr scanpt as",
+ /* 103 */ "selcollist ::= sclp scanpt STAR",
+ /* 104 */ "selcollist ::= sclp scanpt nm DOT STAR",
+ /* 105 */ "as ::= AS nm",
+ /* 106 */ "as ::=",
+ /* 107 */ "from ::=",
+ /* 108 */ "from ::= FROM seltablist",
+ /* 109 */ "stl_prefix ::= seltablist joinop",
+ /* 110 */ "stl_prefix ::=",
+ /* 111 */ "seltablist ::= stl_prefix nm dbnm as on_using",
+ /* 112 */ "seltablist ::= stl_prefix nm dbnm as indexed_by on_using",
+ /* 113 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using",
+ /* 114 */ "seltablist ::= stl_prefix LP select RP as on_using",
+ /* 115 */ "seltablist ::= stl_prefix LP seltablist RP as on_using",
+ /* 116 */ "dbnm ::=",
+ /* 117 */ "dbnm ::= DOT nm",
+ /* 118 */ "fullname ::= nm",
+ /* 119 */ "fullname ::= nm DOT nm",
+ /* 120 */ "xfullname ::= nm",
+ /* 121 */ "xfullname ::= nm DOT nm",
+ /* 122 */ "xfullname ::= nm DOT nm AS nm",
+ /* 123 */ "xfullname ::= nm AS nm",
+ /* 124 */ "joinop ::= COMMA|JOIN",
+ /* 125 */ "joinop ::= JOIN_KW JOIN",
+ /* 126 */ "joinop ::= JOIN_KW nm JOIN",
+ /* 127 */ "joinop ::= JOIN_KW nm nm JOIN",
+ /* 128 */ "on_using ::= ON expr",
+ /* 129 */ "on_using ::= USING LP idlist RP",
+ /* 130 */ "on_using ::=",
+ /* 131 */ "indexed_opt ::=",
+ /* 132 */ "indexed_by ::= INDEXED BY nm",
+ /* 133 */ "indexed_by ::= NOT INDEXED",
+ /* 134 */ "orderby_opt ::=",
+ /* 135 */ "orderby_opt ::= ORDER BY sortlist",
+ /* 136 */ "sortlist ::= sortlist COMMA expr sortorder nulls",
+ /* 137 */ "sortlist ::= expr sortorder nulls",
+ /* 138 */ "sortorder ::= ASC",
+ /* 139 */ "sortorder ::= DESC",
+ /* 140 */ "sortorder ::=",
+ /* 141 */ "nulls ::= NULLS FIRST",
+ /* 142 */ "nulls ::= NULLS LAST",
+ /* 143 */ "nulls ::=",
+ /* 144 */ "groupby_opt ::=",
+ /* 145 */ "groupby_opt ::= GROUP BY nexprlist",
+ /* 146 */ "having_opt ::=",
+ /* 147 */ "having_opt ::= HAVING expr",
+ /* 148 */ "limit_opt ::=",
+ /* 149 */ "limit_opt ::= LIMIT expr",
+ /* 150 */ "limit_opt ::= LIMIT expr OFFSET expr",
+ /* 151 */ "limit_opt ::= LIMIT expr COMMA expr",
+ /* 152 */ "cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret",
+ /* 153 */ "where_opt ::=",
+ /* 154 */ "where_opt ::= WHERE expr",
+ /* 155 */ "where_opt_ret ::=",
+ /* 156 */ "where_opt_ret ::= WHERE expr",
+ /* 157 */ "where_opt_ret ::= RETURNING selcollist",
+ /* 158 */ "where_opt_ret ::= WHERE expr RETURNING selcollist",
+ /* 159 */ "cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret",
+ /* 160 */ "setlist ::= setlist COMMA nm EQ expr",
+ /* 161 */ "setlist ::= setlist COMMA LP idlist RP EQ expr",
+ /* 162 */ "setlist ::= nm EQ expr",
+ /* 163 */ "setlist ::= LP idlist RP EQ expr",
+ /* 164 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert",
+ /* 165 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning",
+ /* 166 */ "upsert ::=",
+ /* 167 */ "upsert ::= RETURNING selcollist",
+ /* 168 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert",
+ /* 169 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert",
+ /* 170 */ "upsert ::= ON CONFLICT DO NOTHING returning",
+ /* 171 */ "upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning",
+ /* 172 */ "returning ::= RETURNING selcollist",
+ /* 173 */ "insert_cmd ::= INSERT orconf",
+ /* 174 */ "insert_cmd ::= REPLACE",
+ /* 175 */ "idlist_opt ::=",
+ /* 176 */ "idlist_opt ::= LP idlist RP",
+ /* 177 */ "idlist ::= idlist COMMA nm",
+ /* 178 */ "idlist ::= nm",
+ /* 179 */ "expr ::= LP expr RP",
+ /* 180 */ "expr ::= ID|INDEXED|JOIN_KW",
+ /* 181 */ "expr ::= nm DOT nm",
+ /* 182 */ "expr ::= nm DOT nm DOT nm",
+ /* 183 */ "term ::= NULL|FLOAT|BLOB",
+ /* 184 */ "term ::= STRING",
+ /* 185 */ "term ::= INTEGER",
+ /* 186 */ "expr ::= VARIABLE",
+ /* 187 */ "expr ::= expr COLLATE ID|STRING",
+ /* 188 */ "expr ::= CAST LP expr AS typetoken RP",
+ /* 189 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP",
+ /* 190 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP",
+ /* 191 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP",
+ /* 192 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over",
+ /* 193 */ "expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over",
+ /* 194 */ "expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over",
+ /* 195 */ "term ::= CTIME_KW",
+ /* 196 */ "expr ::= LP nexprlist COMMA expr RP",
+ /* 197 */ "expr ::= expr AND expr",
+ /* 198 */ "expr ::= expr OR expr",
+ /* 199 */ "expr ::= expr LT|GT|GE|LE expr",
+ /* 200 */ "expr ::= expr EQ|NE expr",
+ /* 201 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
+ /* 202 */ "expr ::= expr PLUS|MINUS expr",
+ /* 203 */ "expr ::= expr STAR|SLASH|REM expr",
+ /* 204 */ "expr ::= expr CONCAT expr",
+ /* 205 */ "likeop ::= NOT LIKE_KW|MATCH",
+ /* 206 */ "expr ::= expr likeop expr",
+ /* 207 */ "expr ::= expr likeop expr ESCAPE expr",
+ /* 208 */ "expr ::= expr ISNULL|NOTNULL",
+ /* 209 */ "expr ::= expr NOT NULL",
+ /* 210 */ "expr ::= expr IS expr",
+ /* 211 */ "expr ::= expr IS NOT expr",
+ /* 212 */ "expr ::= expr IS NOT DISTINCT FROM expr",
+ /* 213 */ "expr ::= expr IS DISTINCT FROM expr",
+ /* 214 */ "expr ::= NOT expr",
+ /* 215 */ "expr ::= BITNOT expr",
+ /* 216 */ "expr ::= PLUS|MINUS expr",
+ /* 217 */ "expr ::= expr PTR expr",
+ /* 218 */ "between_op ::= BETWEEN",
+ /* 219 */ "between_op ::= NOT BETWEEN",
+ /* 220 */ "expr ::= expr between_op expr AND expr",
+ /* 221 */ "in_op ::= IN",
+ /* 222 */ "in_op ::= NOT IN",
+ /* 223 */ "expr ::= expr in_op LP exprlist RP",
+ /* 224 */ "expr ::= LP select RP",
+ /* 225 */ "expr ::= expr in_op LP select RP",
+ /* 226 */ "expr ::= expr in_op nm dbnm paren_exprlist",
+ /* 227 */ "expr ::= EXISTS LP select RP",
+ /* 228 */ "expr ::= CASE case_operand case_exprlist case_else END",
+ /* 229 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
+ /* 230 */ "case_exprlist ::= WHEN expr THEN expr",
+ /* 231 */ "case_else ::= ELSE expr",
+ /* 232 */ "case_else ::=",
+ /* 233 */ "case_operand ::=",
+ /* 234 */ "exprlist ::=",
+ /* 235 */ "nexprlist ::= nexprlist COMMA expr",
+ /* 236 */ "nexprlist ::= expr",
+ /* 237 */ "paren_exprlist ::=",
+ /* 238 */ "paren_exprlist ::= LP exprlist RP",
+ /* 239 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
+ /* 240 */ "uniqueflag ::= UNIQUE",
+ /* 241 */ "uniqueflag ::=",
+ /* 242 */ "eidlist_opt ::=",
+ /* 243 */ "eidlist_opt ::= LP eidlist RP",
+ /* 244 */ "eidlist ::= eidlist COMMA nm collate sortorder",
+ /* 245 */ "eidlist ::= nm collate sortorder",
+ /* 246 */ "collate ::=",
+ /* 247 */ "collate ::= COLLATE ID|STRING",
+ /* 248 */ "cmd ::= DROP INDEX ifexists fullname",
+ /* 249 */ "cmd ::= VACUUM vinto",
+ /* 250 */ "cmd ::= VACUUM nm vinto",
+ /* 251 */ "vinto ::= INTO expr",
+ /* 252 */ "vinto ::=",
+ /* 253 */ "cmd ::= PRAGMA nm dbnm",
+ /* 254 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
+ /* 255 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
+ /* 256 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
+ /* 257 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
+ /* 258 */ "plus_num ::= PLUS INTEGER|FLOAT",
+ /* 259 */ "minus_num ::= MINUS INTEGER|FLOAT",
+ /* 260 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
+ /* 261 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
+ /* 262 */ "trigger_time ::= BEFORE|AFTER",
+ /* 263 */ "trigger_time ::= INSTEAD OF",
+ /* 264 */ "trigger_time ::=",
+ /* 265 */ "trigger_event ::= DELETE|INSERT",
+ /* 266 */ "trigger_event ::= UPDATE",
+ /* 267 */ "trigger_event ::= UPDATE OF idlist",
+ /* 268 */ "when_clause ::=",
+ /* 269 */ "when_clause ::= WHEN expr",
+ /* 270 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
+ /* 271 */ "trigger_cmd_list ::= trigger_cmd SEMI",
+ /* 272 */ "trnm ::= nm DOT nm",
+ /* 273 */ "tridxby ::= INDEXED BY nm",
+ /* 274 */ "tridxby ::= NOT INDEXED",
+ /* 275 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt",
+ /* 276 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt",
+ /* 277 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt",
+ /* 278 */ "trigger_cmd ::= scanpt select scanpt",
+ /* 279 */ "expr ::= RAISE LP IGNORE RP",
+ /* 280 */ "expr ::= RAISE LP raisetype COMMA nm RP",
+ /* 281 */ "raisetype ::= ROLLBACK",
+ /* 282 */ "raisetype ::= ABORT",
+ /* 283 */ "raisetype ::= FAIL",
+ /* 284 */ "cmd ::= DROP TRIGGER ifexists fullname",
+ /* 285 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
+ /* 286 */ "cmd ::= DETACH database_kw_opt expr",
+ /* 287 */ "key_opt ::=",
+ /* 288 */ "key_opt ::= KEY expr",
+ /* 289 */ "cmd ::= REINDEX",
+ /* 290 */ "cmd ::= REINDEX nm dbnm",
+ /* 291 */ "cmd ::= ANALYZE",
+ /* 292 */ "cmd ::= ANALYZE nm dbnm",
+ /* 293 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
+ /* 294 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
+ /* 295 */ "cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm",
+ /* 296 */ "add_column_fullname ::= fullname",
+ /* 297 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm",
+ /* 298 */ "cmd ::= create_vtab",
+ /* 299 */ "cmd ::= create_vtab LP vtabarglist RP",
+ /* 300 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
+ /* 301 */ "vtabarg ::=",
+ /* 302 */ "vtabargtoken ::= ANY",
+ /* 303 */ "vtabargtoken ::= lp anylist RP",
+ /* 304 */ "lp ::= LP",
+ /* 305 */ "with ::= WITH wqlist",
+ /* 306 */ "with ::= WITH RECURSIVE wqlist",
+ /* 307 */ "wqas ::= AS",
+ /* 308 */ "wqas ::= AS MATERIALIZED",
+ /* 309 */ "wqas ::= AS NOT MATERIALIZED",
+ /* 310 */ "wqitem ::= withnm eidlist_opt wqas LP select RP",
+ /* 311 */ "withnm ::= nm",
+ /* 312 */ "wqlist ::= wqitem",
+ /* 313 */ "wqlist ::= wqlist COMMA wqitem",
+ /* 314 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn",
+ /* 315 */ "windowdefn ::= nm AS LP window RP",
+ /* 316 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt",
+ /* 317 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt",
+ /* 318 */ "window ::= ORDER BY sortlist frame_opt",
+ /* 319 */ "window ::= nm ORDER BY sortlist frame_opt",
+ /* 320 */ "window ::= nm frame_opt",
+ /* 321 */ "frame_opt ::=",
+ /* 322 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt",
+ /* 323 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt",
+ /* 324 */ "range_or_rows ::= RANGE|ROWS|GROUPS",
+ /* 325 */ "frame_bound_s ::= frame_bound",
+ /* 326 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
+ /* 327 */ "frame_bound_e ::= frame_bound",
+ /* 328 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
+ /* 329 */ "frame_bound ::= expr PRECEDING|FOLLOWING",
+ /* 330 */ "frame_bound ::= CURRENT ROW",
+ /* 331 */ "frame_exclude_opt ::=",
+ /* 332 */ "frame_exclude_opt ::= EXCLUDE frame_exclude",
+ /* 333 */ "frame_exclude ::= NO OTHERS",
+ /* 334 */ "frame_exclude ::= CURRENT ROW",
+ /* 335 */ "frame_exclude ::= GROUP|TIES",
+ /* 336 */ "window_clause ::= WINDOW windowdefn_list",
+ /* 337 */ "filter_over ::= filter_clause over_clause",
+ /* 338 */ "filter_over ::= over_clause",
+ /* 339 */ "filter_over ::= filter_clause",
+ /* 340 */ "over_clause ::= OVER LP window RP",
+ /* 341 */ "over_clause ::= OVER nm",
+ /* 342 */ "filter_clause ::= FILTER LP WHERE expr RP",
+ /* 343 */ "term ::= QNUMBER",
+ /* 344 */ "input ::= cmdlist",
+ /* 345 */ "cmdlist ::= cmdlist ecmd",
+ /* 346 */ "cmdlist ::= ecmd",
+ /* 347 */ "ecmd ::= SEMI",
+ /* 348 */ "ecmd ::= cmdx SEMI",
+ /* 349 */ "ecmd ::= explain cmdx SEMI",
+ /* 350 */ "trans_opt ::=",
+ /* 351 */ "trans_opt ::= TRANSACTION",
+ /* 352 */ "trans_opt ::= TRANSACTION nm",
+ /* 353 */ "savepoint_opt ::= SAVEPOINT",
+ /* 354 */ "savepoint_opt ::=",
+ /* 355 */ "cmd ::= create_table create_table_args",
+ /* 356 */ "table_option_set ::= table_option",
+ /* 357 */ "columnlist ::= columnlist COMMA columnname carglist",
+ /* 358 */ "columnlist ::= columnname carglist",
+ /* 359 */ "nm ::= ID|INDEXED|JOIN_KW",
+ /* 360 */ "nm ::= STRING",
+ /* 361 */ "typetoken ::= typename",
+ /* 362 */ "typename ::= ID|STRING",
+ /* 363 */ "signed ::= plus_num",
+ /* 364 */ "signed ::= minus_num",
+ /* 365 */ "carglist ::= carglist ccons",
+ /* 366 */ "carglist ::=",
+ /* 367 */ "ccons ::= NULL onconf",
+ /* 368 */ "ccons ::= GENERATED ALWAYS AS generated",
+ /* 369 */ "ccons ::= AS generated",
+ /* 370 */ "conslist_opt ::= COMMA conslist",
+ /* 371 */ "conslist ::= conslist tconscomma tcons",
+ /* 372 */ "conslist ::= tcons",
+ /* 373 */ "tconscomma ::=",
+ /* 374 */ "defer_subclause_opt ::= defer_subclause",
+ /* 375 */ "resolvetype ::= raisetype",
+ /* 376 */ "selectnowith ::= oneselect",
+ /* 377 */ "oneselect ::= values",
+ /* 378 */ "sclp ::= selcollist COMMA",
+ /* 379 */ "as ::= ID|STRING",
+ /* 380 */ "indexed_opt ::= indexed_by",
+ /* 381 */ "returning ::=",
+ /* 382 */ "expr ::= term",
+ /* 383 */ "likeop ::= LIKE_KW|MATCH",
+ /* 384 */ "case_operand ::= expr",
+ /* 385 */ "exprlist ::= nexprlist",
+ /* 386 */ "nmnum ::= plus_num",
+ /* 387 */ "nmnum ::= nm",
+ /* 388 */ "nmnum ::= ON",
+ /* 389 */ "nmnum ::= DELETE",
+ /* 390 */ "nmnum ::= DEFAULT",
+ /* 391 */ "plus_num ::= INTEGER|FLOAT",
+ /* 392 */ "foreach_clause ::=",
+ /* 393 */ "foreach_clause ::= FOR EACH ROW",
+ /* 394 */ "trnm ::= nm",
+ /* 395 */ "tridxby ::=",
+ /* 396 */ "database_kw_opt ::= DATABASE",
+ /* 397 */ "database_kw_opt ::=",
+ /* 398 */ "kwcolumn_opt ::=",
+ /* 399 */ "kwcolumn_opt ::= COLUMNKW",
+ /* 400 */ "vtabarglist ::= vtabarg",
+ /* 401 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
+ /* 402 */ "vtabarg ::= vtabarg vtabargtoken",
+ /* 403 */ "anylist ::=",
+ /* 404 */ "anylist ::= anylist LP anylist RP",
+ /* 405 */ "anylist ::= anylist ANY",
+ /* 406 */ "with ::=",
+ /* 407 */ "windowdefn_list ::= windowdefn",
+ /* 408 */ "window ::= frame_opt",
};
#endif /* NDEBUG */
-#if YYSTACKDEPTH<=0
+#if YYGROWABLESTACK
/*
** Try to increase the size of the parser stack. Return the number
** of errors. Return 0 on success.
*/
static int yyGrowStack(yyParser *p){
+ int oldSize = 1 + (int)(p->yystackEnd - p->yystack);
int newSize;
int idx;
yyStackEntry *pNew;
- newSize = p->yystksz*2 + 100;
- idx = p->yytos ? (int)(p->yytos - p->yystack) : 0;
- if( p->yystack==&p->yystk0 ){
- pNew = malloc(newSize*sizeof(pNew[0]));
- if( pNew ) pNew[0] = p->yystk0;
+ newSize = oldSize*2 + 100;
+ idx = (int)(p->yytos - p->yystack);
+ if( p->yystack==p->yystk0 ){
+ pNew = YYREALLOC(0, newSize*sizeof(pNew[0]));
+ if( pNew==0 ) return 1;
+ memcpy(pNew, p->yystack, oldSize*sizeof(pNew[0]));
}else{
- pNew = realloc(p->yystack, newSize*sizeof(pNew[0]));
+ pNew = YYREALLOC(p->yystack, newSize*sizeof(pNew[0]));
+ if( pNew==0 ) return 1;
}
- if( pNew ){
- p->yystack = pNew;
- p->yytos = &p->yystack[idx];
+ p->yystack = pNew;
+ p->yytos = &p->yystack[idx];
#ifndef NDEBUG
- if( yyTraceFILE ){
- fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n",
- yyTracePrompt, p->yystksz, newSize);
- }
-#endif
- p->yystksz = newSize;
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n",
+ yyTracePrompt, oldSize, newSize);
}
- return pNew==0;
+#endif
+ p->yystackEnd = &p->yystack[newSize-1];
+ return 0;
}
+#endif /* YYGROWABLESTACK */
+
+#if !YYGROWABLESTACK
+/* For builds that do no have a growable stack, yyGrowStack always
+** returns an error.
+*/
+# define yyGrowStack(X) 1
#endif
/* Datatype of the argument to the memory allocated passed as the
@@ -171498,24 +174798,14 @@ SQLITE_PRIVATE void sqlite3ParserInit(void *yypRawParser sqlite3ParserCTX_PDECL)
#ifdef YYTRACKMAXSTACKDEPTH
yypParser->yyhwm = 0;
#endif
-#if YYSTACKDEPTH<=0
- yypParser->yytos = NULL;
- yypParser->yystack = NULL;
- yypParser->yystksz = 0;
- if( yyGrowStack(yypParser) ){
- yypParser->yystack = &yypParser->yystk0;
- yypParser->yystksz = 1;
- }
-#endif
+ yypParser->yystack = yypParser->yystk0;
+ yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1];
#ifndef YYNOERRORRECOVERY
yypParser->yyerrcnt = -1;
#endif
yypParser->yytos = yypParser->yystack;
yypParser->yystack[0].stateno = 0;
yypParser->yystack[0].major = 0;
-#if YYSTACKDEPTH>0
- yypParser->yystackEnd = &yypParser->yystack[YYSTACKDEPTH-1];
-#endif
}
#ifndef sqlite3Parser_ENGINEALWAYSONSTACK
@@ -171569,97 +174859,98 @@ static void yy_destructor(
** inside the C code.
*/
/********* Begin destructor definitions ***************************************/
- case 204: /* select */
- case 239: /* selectnowith */
- case 240: /* oneselect */
- case 252: /* values */
+ case 205: /* select */
+ case 240: /* selectnowith */
+ case 241: /* oneselect */
+ case 253: /* values */
+ case 255: /* mvalues */
{
-sqlite3SelectDelete(pParse->db, (yypminor->yy47));
-}
- break;
- case 216: /* term */
- case 217: /* expr */
- case 246: /* where_opt */
- case 248: /* having_opt */
- case 267: /* where_opt_ret */
- case 278: /* case_operand */
- case 280: /* case_else */
- case 283: /* vinto */
- case 290: /* when_clause */
- case 295: /* key_opt */
- case 311: /* filter_clause */
+sqlite3SelectDelete(pParse->db, (yypminor->yy555));
+}
+ break;
+ case 217: /* term */
+ case 218: /* expr */
+ case 247: /* where_opt */
+ case 249: /* having_opt */
+ case 269: /* where_opt_ret */
+ case 280: /* case_operand */
+ case 282: /* case_else */
+ case 285: /* vinto */
+ case 292: /* when_clause */
+ case 297: /* key_opt */
+ case 314: /* filter_clause */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy528));
-}
- break;
- case 221: /* eidlist_opt */
- case 231: /* sortlist */
- case 232: /* eidlist */
- case 244: /* selcollist */
- case 247: /* groupby_opt */
- case 249: /* orderby_opt */
- case 253: /* nexprlist */
- case 254: /* sclp */
- case 261: /* exprlist */
- case 268: /* setlist */
- case 277: /* paren_exprlist */
- case 279: /* case_exprlist */
- case 310: /* part_opt */
+sqlite3ExprDelete(pParse->db, (yypminor->yy454));
+}
+ break;
+ case 222: /* eidlist_opt */
+ case 232: /* sortlist */
+ case 233: /* eidlist */
+ case 245: /* selcollist */
+ case 248: /* groupby_opt */
+ case 250: /* orderby_opt */
+ case 254: /* nexprlist */
+ case 256: /* sclp */
+ case 263: /* exprlist */
+ case 270: /* setlist */
+ case 279: /* paren_exprlist */
+ case 281: /* case_exprlist */
+ case 313: /* part_opt */
{
-sqlite3ExprListDelete(pParse->db, (yypminor->yy322));
+sqlite3ExprListDelete(pParse->db, (yypminor->yy14));
}
break;
- case 238: /* fullname */
- case 245: /* from */
- case 256: /* seltablist */
- case 257: /* stl_prefix */
- case 262: /* xfullname */
+ case 239: /* fullname */
+ case 246: /* from */
+ case 258: /* seltablist */
+ case 259: /* stl_prefix */
+ case 264: /* xfullname */
{
-sqlite3SrcListDelete(pParse->db, (yypminor->yy131));
+sqlite3SrcListDelete(pParse->db, (yypminor->yy203));
}
break;
- case 241: /* wqlist */
+ case 242: /* wqlist */
{
-sqlite3WithDelete(pParse->db, (yypminor->yy521));
+sqlite3WithDelete(pParse->db, (yypminor->yy59));
}
break;
- case 251: /* window_clause */
- case 306: /* windowdefn_list */
+ case 252: /* window_clause */
+ case 309: /* windowdefn_list */
{
-sqlite3WindowListDelete(pParse->db, (yypminor->yy41));
+sqlite3WindowListDelete(pParse->db, (yypminor->yy211));
}
break;
- case 263: /* idlist */
- case 270: /* idlist_opt */
+ case 265: /* idlist */
+ case 272: /* idlist_opt */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy254));
+sqlite3IdListDelete(pParse->db, (yypminor->yy132));
}
break;
- case 273: /* filter_over */
- case 307: /* windowdefn */
- case 308: /* window */
- case 309: /* frame_opt */
- case 312: /* over_clause */
+ case 275: /* filter_over */
+ case 310: /* windowdefn */
+ case 311: /* window */
+ case 312: /* frame_opt */
+ case 315: /* over_clause */
{
-sqlite3WindowDelete(pParse->db, (yypminor->yy41));
+sqlite3WindowDelete(pParse->db, (yypminor->yy211));
}
break;
- case 286: /* trigger_cmd_list */
- case 291: /* trigger_cmd */
+ case 288: /* trigger_cmd_list */
+ case 293: /* trigger_cmd */
{
-sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy33));
+sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy427));
}
break;
- case 288: /* trigger_event */
+ case 290: /* trigger_event */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy180).b);
+sqlite3IdListDelete(pParse->db, (yypminor->yy286).b);
}
break;
- case 314: /* frame_bound */
- case 315: /* frame_bound_s */
- case 316: /* frame_bound_e */
+ case 317: /* frame_bound */
+ case 318: /* frame_bound_s */
+ case 319: /* frame_bound_e */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy595).pExpr);
+sqlite3ExprDelete(pParse->db, (yypminor->yy509).pExpr);
}
break;
/********* End destructor definitions *****************************************/
@@ -171693,9 +174984,26 @@ static void yy_pop_parser_stack(yyParser *pParser){
*/
SQLITE_PRIVATE void sqlite3ParserFinalize(void *p){
yyParser *pParser = (yyParser*)p;
- while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser);
-#if YYSTACKDEPTH<=0
- if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack);
+
+ /* In-lined version of calling yy_pop_parser_stack() for each
+ ** element left in the stack */
+ yyStackEntry *yytos = pParser->yytos;
+ while( yytos>pParser->yystack ){
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ fprintf(yyTraceFILE,"%sPopping %s\n",
+ yyTracePrompt,
+ yyTokenName[yytos->major]);
+ }
+#endif
+ if( yytos->major>=YY_MIN_DSTRCTR ){
+ yy_destructor(pParser, yytos->major, &yytos->minor);
+ }
+ yytos--;
+ }
+
+#if YYGROWABLESTACK
+ if( pParser->yystack!=pParser->yystk0 ) YYFREE(pParser->yystack);
#endif
}
@@ -171878,7 +175186,7 @@ static void yyStackOverflow(yyParser *yypParser){
** stack every overflows */
/******** Begin %stack_overflow code ******************************************/
- sqlite3ErrorMsg(pParse, "parser stack overflow");
+ sqlite3OomFault(pParse->db);
/******** End %stack_overflow code ********************************************/
sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument var */
sqlite3ParserCTX_STORE
@@ -171922,25 +175230,19 @@ static void yy_shift(
assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) );
}
#endif
-#if YYSTACKDEPTH>0
- if( yypParser->yytos>yypParser->yystackEnd ){
- yypParser->yytos--;
- yyStackOverflow(yypParser);
- return;
- }
-#else
- if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){
+ yytos = yypParser->yytos;
+ if( yytos>yypParser->yystackEnd ){
if( yyGrowStack(yypParser) ){
yypParser->yytos--;
yyStackOverflow(yypParser);
return;
}
+ yytos = yypParser->yytos;
+ assert( yytos <= yypParser->yystackEnd );
}
-#endif
if( yyNewState > YY_MAX_SHIFT ){
yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
}
- yytos = yypParser->yytos;
yytos->stateno = yyNewState;
yytos->major = yyMajor;
yytos->minor.yy0 = yyMinor;
@@ -171950,409 +175252,415 @@ static void yy_shift(
/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
** of that rule */
static const YYCODETYPE yyRuleInfoLhs[] = {
- 189, /* (0) explain ::= EXPLAIN */
- 189, /* (1) explain ::= EXPLAIN QUERY PLAN */
- 188, /* (2) cmdx ::= cmd */
- 190, /* (3) cmd ::= BEGIN transtype trans_opt */
- 191, /* (4) transtype ::= */
- 191, /* (5) transtype ::= DEFERRED */
- 191, /* (6) transtype ::= IMMEDIATE */
- 191, /* (7) transtype ::= EXCLUSIVE */
- 190, /* (8) cmd ::= COMMIT|END trans_opt */
- 190, /* (9) cmd ::= ROLLBACK trans_opt */
- 190, /* (10) cmd ::= SAVEPOINT nm */
- 190, /* (11) cmd ::= RELEASE savepoint_opt nm */
- 190, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
- 195, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
- 197, /* (14) createkw ::= CREATE */
- 199, /* (15) ifnotexists ::= */
- 199, /* (16) ifnotexists ::= IF NOT EXISTS */
- 198, /* (17) temp ::= TEMP */
- 198, /* (18) temp ::= */
- 196, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */
- 196, /* (20) create_table_args ::= AS select */
- 203, /* (21) table_option_set ::= */
- 203, /* (22) table_option_set ::= table_option_set COMMA table_option */
- 205, /* (23) table_option ::= WITHOUT nm */
- 205, /* (24) table_option ::= nm */
- 206, /* (25) columnname ::= nm typetoken */
- 208, /* (26) typetoken ::= */
- 208, /* (27) typetoken ::= typename LP signed RP */
- 208, /* (28) typetoken ::= typename LP signed COMMA signed RP */
- 209, /* (29) typename ::= typename ID|STRING */
- 213, /* (30) scanpt ::= */
- 214, /* (31) scantok ::= */
- 215, /* (32) ccons ::= CONSTRAINT nm */
- 215, /* (33) ccons ::= DEFAULT scantok term */
- 215, /* (34) ccons ::= DEFAULT LP expr RP */
- 215, /* (35) ccons ::= DEFAULT PLUS scantok term */
- 215, /* (36) ccons ::= DEFAULT MINUS scantok term */
- 215, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */
- 215, /* (38) ccons ::= NOT NULL onconf */
- 215, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */
- 215, /* (40) ccons ::= UNIQUE onconf */
- 215, /* (41) ccons ::= CHECK LP expr RP */
- 215, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */
- 215, /* (43) ccons ::= defer_subclause */
- 215, /* (44) ccons ::= COLLATE ID|STRING */
- 224, /* (45) generated ::= LP expr RP */
- 224, /* (46) generated ::= LP expr RP ID */
- 220, /* (47) autoinc ::= */
- 220, /* (48) autoinc ::= AUTOINCR */
- 222, /* (49) refargs ::= */
- 222, /* (50) refargs ::= refargs refarg */
- 225, /* (51) refarg ::= MATCH nm */
- 225, /* (52) refarg ::= ON INSERT refact */
- 225, /* (53) refarg ::= ON DELETE refact */
- 225, /* (54) refarg ::= ON UPDATE refact */
- 226, /* (55) refact ::= SET NULL */
- 226, /* (56) refact ::= SET DEFAULT */
- 226, /* (57) refact ::= CASCADE */
- 226, /* (58) refact ::= RESTRICT */
- 226, /* (59) refact ::= NO ACTION */
- 223, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
- 223, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
- 227, /* (62) init_deferred_pred_opt ::= */
- 227, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */
- 227, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
- 202, /* (65) conslist_opt ::= */
- 229, /* (66) tconscomma ::= COMMA */
- 230, /* (67) tcons ::= CONSTRAINT nm */
- 230, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
- 230, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */
- 230, /* (70) tcons ::= CHECK LP expr RP onconf */
- 230, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
- 233, /* (72) defer_subclause_opt ::= */
- 218, /* (73) onconf ::= */
- 218, /* (74) onconf ::= ON CONFLICT resolvetype */
- 234, /* (75) orconf ::= */
- 234, /* (76) orconf ::= OR resolvetype */
- 235, /* (77) resolvetype ::= IGNORE */
- 235, /* (78) resolvetype ::= REPLACE */
- 190, /* (79) cmd ::= DROP TABLE ifexists fullname */
- 237, /* (80) ifexists ::= IF EXISTS */
- 237, /* (81) ifexists ::= */
- 190, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
- 190, /* (83) cmd ::= DROP VIEW ifexists fullname */
- 190, /* (84) cmd ::= select */
- 204, /* (85) select ::= WITH wqlist selectnowith */
- 204, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */
- 204, /* (87) select ::= selectnowith */
- 239, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */
- 242, /* (89) multiselect_op ::= UNION */
- 242, /* (90) multiselect_op ::= UNION ALL */
- 242, /* (91) multiselect_op ::= EXCEPT|INTERSECT */
- 240, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
- 240, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
- 252, /* (94) values ::= VALUES LP nexprlist RP */
- 252, /* (95) values ::= values COMMA LP nexprlist RP */
- 243, /* (96) distinct ::= DISTINCT */
- 243, /* (97) distinct ::= ALL */
- 243, /* (98) distinct ::= */
- 254, /* (99) sclp ::= */
- 244, /* (100) selcollist ::= sclp scanpt expr scanpt as */
- 244, /* (101) selcollist ::= sclp scanpt STAR */
- 244, /* (102) selcollist ::= sclp scanpt nm DOT STAR */
- 255, /* (103) as ::= AS nm */
- 255, /* (104) as ::= */
- 245, /* (105) from ::= */
- 245, /* (106) from ::= FROM seltablist */
- 257, /* (107) stl_prefix ::= seltablist joinop */
- 257, /* (108) stl_prefix ::= */
- 256, /* (109) seltablist ::= stl_prefix nm dbnm as on_using */
- 256, /* (110) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
- 256, /* (111) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
- 256, /* (112) seltablist ::= stl_prefix LP select RP as on_using */
- 256, /* (113) seltablist ::= stl_prefix LP seltablist RP as on_using */
- 200, /* (114) dbnm ::= */
- 200, /* (115) dbnm ::= DOT nm */
- 238, /* (116) fullname ::= nm */
- 238, /* (117) fullname ::= nm DOT nm */
- 262, /* (118) xfullname ::= nm */
- 262, /* (119) xfullname ::= nm DOT nm */
- 262, /* (120) xfullname ::= nm DOT nm AS nm */
- 262, /* (121) xfullname ::= nm AS nm */
- 258, /* (122) joinop ::= COMMA|JOIN */
- 258, /* (123) joinop ::= JOIN_KW JOIN */
- 258, /* (124) joinop ::= JOIN_KW nm JOIN */
- 258, /* (125) joinop ::= JOIN_KW nm nm JOIN */
- 259, /* (126) on_using ::= ON expr */
- 259, /* (127) on_using ::= USING LP idlist RP */
- 259, /* (128) on_using ::= */
- 264, /* (129) indexed_opt ::= */
- 260, /* (130) indexed_by ::= INDEXED BY nm */
- 260, /* (131) indexed_by ::= NOT INDEXED */
- 249, /* (132) orderby_opt ::= */
- 249, /* (133) orderby_opt ::= ORDER BY sortlist */
- 231, /* (134) sortlist ::= sortlist COMMA expr sortorder nulls */
- 231, /* (135) sortlist ::= expr sortorder nulls */
- 219, /* (136) sortorder ::= ASC */
- 219, /* (137) sortorder ::= DESC */
- 219, /* (138) sortorder ::= */
- 265, /* (139) nulls ::= NULLS FIRST */
- 265, /* (140) nulls ::= NULLS LAST */
- 265, /* (141) nulls ::= */
- 247, /* (142) groupby_opt ::= */
- 247, /* (143) groupby_opt ::= GROUP BY nexprlist */
- 248, /* (144) having_opt ::= */
- 248, /* (145) having_opt ::= HAVING expr */
- 250, /* (146) limit_opt ::= */
- 250, /* (147) limit_opt ::= LIMIT expr */
- 250, /* (148) limit_opt ::= LIMIT expr OFFSET expr */
- 250, /* (149) limit_opt ::= LIMIT expr COMMA expr */
- 190, /* (150) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
- 246, /* (151) where_opt ::= */
- 246, /* (152) where_opt ::= WHERE expr */
- 267, /* (153) where_opt_ret ::= */
- 267, /* (154) where_opt_ret ::= WHERE expr */
- 267, /* (155) where_opt_ret ::= RETURNING selcollist */
- 267, /* (156) where_opt_ret ::= WHERE expr RETURNING selcollist */
- 190, /* (157) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
- 268, /* (158) setlist ::= setlist COMMA nm EQ expr */
- 268, /* (159) setlist ::= setlist COMMA LP idlist RP EQ expr */
- 268, /* (160) setlist ::= nm EQ expr */
- 268, /* (161) setlist ::= LP idlist RP EQ expr */
- 190, /* (162) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
- 190, /* (163) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
- 271, /* (164) upsert ::= */
- 271, /* (165) upsert ::= RETURNING selcollist */
- 271, /* (166) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
- 271, /* (167) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
- 271, /* (168) upsert ::= ON CONFLICT DO NOTHING returning */
- 271, /* (169) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
- 272, /* (170) returning ::= RETURNING selcollist */
- 269, /* (171) insert_cmd ::= INSERT orconf */
- 269, /* (172) insert_cmd ::= REPLACE */
- 270, /* (173) idlist_opt ::= */
- 270, /* (174) idlist_opt ::= LP idlist RP */
- 263, /* (175) idlist ::= idlist COMMA nm */
- 263, /* (176) idlist ::= nm */
- 217, /* (177) expr ::= LP expr RP */
- 217, /* (178) expr ::= ID|INDEXED|JOIN_KW */
- 217, /* (179) expr ::= nm DOT nm */
- 217, /* (180) expr ::= nm DOT nm DOT nm */
- 216, /* (181) term ::= NULL|FLOAT|BLOB */
- 216, /* (182) term ::= STRING */
- 216, /* (183) term ::= INTEGER */
- 217, /* (184) expr ::= VARIABLE */
- 217, /* (185) expr ::= expr COLLATE ID|STRING */
- 217, /* (186) expr ::= CAST LP expr AS typetoken RP */
- 217, /* (187) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
- 217, /* (188) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
- 217, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
- 217, /* (190) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
- 216, /* (191) term ::= CTIME_KW */
- 217, /* (192) expr ::= LP nexprlist COMMA expr RP */
- 217, /* (193) expr ::= expr AND expr */
- 217, /* (194) expr ::= expr OR expr */
- 217, /* (195) expr ::= expr LT|GT|GE|LE expr */
- 217, /* (196) expr ::= expr EQ|NE expr */
- 217, /* (197) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
- 217, /* (198) expr ::= expr PLUS|MINUS expr */
- 217, /* (199) expr ::= expr STAR|SLASH|REM expr */
- 217, /* (200) expr ::= expr CONCAT expr */
- 274, /* (201) likeop ::= NOT LIKE_KW|MATCH */
- 217, /* (202) expr ::= expr likeop expr */
- 217, /* (203) expr ::= expr likeop expr ESCAPE expr */
- 217, /* (204) expr ::= expr ISNULL|NOTNULL */
- 217, /* (205) expr ::= expr NOT NULL */
- 217, /* (206) expr ::= expr IS expr */
- 217, /* (207) expr ::= expr IS NOT expr */
- 217, /* (208) expr ::= expr IS NOT DISTINCT FROM expr */
- 217, /* (209) expr ::= expr IS DISTINCT FROM expr */
- 217, /* (210) expr ::= NOT expr */
- 217, /* (211) expr ::= BITNOT expr */
- 217, /* (212) expr ::= PLUS|MINUS expr */
- 217, /* (213) expr ::= expr PTR expr */
- 275, /* (214) between_op ::= BETWEEN */
- 275, /* (215) between_op ::= NOT BETWEEN */
- 217, /* (216) expr ::= expr between_op expr AND expr */
- 276, /* (217) in_op ::= IN */
- 276, /* (218) in_op ::= NOT IN */
- 217, /* (219) expr ::= expr in_op LP exprlist RP */
- 217, /* (220) expr ::= LP select RP */
- 217, /* (221) expr ::= expr in_op LP select RP */
- 217, /* (222) expr ::= expr in_op nm dbnm paren_exprlist */
- 217, /* (223) expr ::= EXISTS LP select RP */
- 217, /* (224) expr ::= CASE case_operand case_exprlist case_else END */
- 279, /* (225) case_exprlist ::= case_exprlist WHEN expr THEN expr */
- 279, /* (226) case_exprlist ::= WHEN expr THEN expr */
- 280, /* (227) case_else ::= ELSE expr */
- 280, /* (228) case_else ::= */
- 278, /* (229) case_operand ::= */
- 261, /* (230) exprlist ::= */
- 253, /* (231) nexprlist ::= nexprlist COMMA expr */
- 253, /* (232) nexprlist ::= expr */
- 277, /* (233) paren_exprlist ::= */
- 277, /* (234) paren_exprlist ::= LP exprlist RP */
- 190, /* (235) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
- 281, /* (236) uniqueflag ::= UNIQUE */
- 281, /* (237) uniqueflag ::= */
- 221, /* (238) eidlist_opt ::= */
- 221, /* (239) eidlist_opt ::= LP eidlist RP */
- 232, /* (240) eidlist ::= eidlist COMMA nm collate sortorder */
- 232, /* (241) eidlist ::= nm collate sortorder */
- 282, /* (242) collate ::= */
- 282, /* (243) collate ::= COLLATE ID|STRING */
- 190, /* (244) cmd ::= DROP INDEX ifexists fullname */
- 190, /* (245) cmd ::= VACUUM vinto */
- 190, /* (246) cmd ::= VACUUM nm vinto */
- 283, /* (247) vinto ::= INTO expr */
- 283, /* (248) vinto ::= */
- 190, /* (249) cmd ::= PRAGMA nm dbnm */
- 190, /* (250) cmd ::= PRAGMA nm dbnm EQ nmnum */
- 190, /* (251) cmd ::= PRAGMA nm dbnm LP nmnum RP */
- 190, /* (252) cmd ::= PRAGMA nm dbnm EQ minus_num */
- 190, /* (253) cmd ::= PRAGMA nm dbnm LP minus_num RP */
- 211, /* (254) plus_num ::= PLUS INTEGER|FLOAT */
- 212, /* (255) minus_num ::= MINUS INTEGER|FLOAT */
- 190, /* (256) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
- 285, /* (257) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
- 287, /* (258) trigger_time ::= BEFORE|AFTER */
- 287, /* (259) trigger_time ::= INSTEAD OF */
- 287, /* (260) trigger_time ::= */
- 288, /* (261) trigger_event ::= DELETE|INSERT */
- 288, /* (262) trigger_event ::= UPDATE */
- 288, /* (263) trigger_event ::= UPDATE OF idlist */
- 290, /* (264) when_clause ::= */
- 290, /* (265) when_clause ::= WHEN expr */
- 286, /* (266) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
- 286, /* (267) trigger_cmd_list ::= trigger_cmd SEMI */
- 292, /* (268) trnm ::= nm DOT nm */
- 293, /* (269) tridxby ::= INDEXED BY nm */
- 293, /* (270) tridxby ::= NOT INDEXED */
- 291, /* (271) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
- 291, /* (272) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
- 291, /* (273) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
- 291, /* (274) trigger_cmd ::= scanpt select scanpt */
- 217, /* (275) expr ::= RAISE LP IGNORE RP */
- 217, /* (276) expr ::= RAISE LP raisetype COMMA nm RP */
- 236, /* (277) raisetype ::= ROLLBACK */
- 236, /* (278) raisetype ::= ABORT */
- 236, /* (279) raisetype ::= FAIL */
- 190, /* (280) cmd ::= DROP TRIGGER ifexists fullname */
- 190, /* (281) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
- 190, /* (282) cmd ::= DETACH database_kw_opt expr */
- 295, /* (283) key_opt ::= */
- 295, /* (284) key_opt ::= KEY expr */
- 190, /* (285) cmd ::= REINDEX */
- 190, /* (286) cmd ::= REINDEX nm dbnm */
- 190, /* (287) cmd ::= ANALYZE */
- 190, /* (288) cmd ::= ANALYZE nm dbnm */
- 190, /* (289) cmd ::= ALTER TABLE fullname RENAME TO nm */
- 190, /* (290) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
- 190, /* (291) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
- 296, /* (292) add_column_fullname ::= fullname */
- 190, /* (293) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
- 190, /* (294) cmd ::= create_vtab */
- 190, /* (295) cmd ::= create_vtab LP vtabarglist RP */
- 298, /* (296) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
- 300, /* (297) vtabarg ::= */
- 301, /* (298) vtabargtoken ::= ANY */
- 301, /* (299) vtabargtoken ::= lp anylist RP */
- 302, /* (300) lp ::= LP */
- 266, /* (301) with ::= WITH wqlist */
- 266, /* (302) with ::= WITH RECURSIVE wqlist */
- 305, /* (303) wqas ::= AS */
- 305, /* (304) wqas ::= AS MATERIALIZED */
- 305, /* (305) wqas ::= AS NOT MATERIALIZED */
- 304, /* (306) wqitem ::= nm eidlist_opt wqas LP select RP */
- 241, /* (307) wqlist ::= wqitem */
- 241, /* (308) wqlist ::= wqlist COMMA wqitem */
- 306, /* (309) windowdefn_list ::= windowdefn_list COMMA windowdefn */
- 307, /* (310) windowdefn ::= nm AS LP window RP */
- 308, /* (311) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
- 308, /* (312) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
- 308, /* (313) window ::= ORDER BY sortlist frame_opt */
- 308, /* (314) window ::= nm ORDER BY sortlist frame_opt */
- 308, /* (315) window ::= nm frame_opt */
- 309, /* (316) frame_opt ::= */
- 309, /* (317) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
- 309, /* (318) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
- 313, /* (319) range_or_rows ::= RANGE|ROWS|GROUPS */
- 315, /* (320) frame_bound_s ::= frame_bound */
- 315, /* (321) frame_bound_s ::= UNBOUNDED PRECEDING */
- 316, /* (322) frame_bound_e ::= frame_bound */
- 316, /* (323) frame_bound_e ::= UNBOUNDED FOLLOWING */
- 314, /* (324) frame_bound ::= expr PRECEDING|FOLLOWING */
- 314, /* (325) frame_bound ::= CURRENT ROW */
- 317, /* (326) frame_exclude_opt ::= */
- 317, /* (327) frame_exclude_opt ::= EXCLUDE frame_exclude */
- 318, /* (328) frame_exclude ::= NO OTHERS */
- 318, /* (329) frame_exclude ::= CURRENT ROW */
- 318, /* (330) frame_exclude ::= GROUP|TIES */
- 251, /* (331) window_clause ::= WINDOW windowdefn_list */
- 273, /* (332) filter_over ::= filter_clause over_clause */
- 273, /* (333) filter_over ::= over_clause */
- 273, /* (334) filter_over ::= filter_clause */
- 312, /* (335) over_clause ::= OVER LP window RP */
- 312, /* (336) over_clause ::= OVER nm */
- 311, /* (337) filter_clause ::= FILTER LP WHERE expr RP */
- 185, /* (338) input ::= cmdlist */
- 186, /* (339) cmdlist ::= cmdlist ecmd */
- 186, /* (340) cmdlist ::= ecmd */
- 187, /* (341) ecmd ::= SEMI */
- 187, /* (342) ecmd ::= cmdx SEMI */
- 187, /* (343) ecmd ::= explain cmdx SEMI */
- 192, /* (344) trans_opt ::= */
- 192, /* (345) trans_opt ::= TRANSACTION */
- 192, /* (346) trans_opt ::= TRANSACTION nm */
- 194, /* (347) savepoint_opt ::= SAVEPOINT */
- 194, /* (348) savepoint_opt ::= */
- 190, /* (349) cmd ::= create_table create_table_args */
- 203, /* (350) table_option_set ::= table_option */
- 201, /* (351) columnlist ::= columnlist COMMA columnname carglist */
- 201, /* (352) columnlist ::= columnname carglist */
- 193, /* (353) nm ::= ID|INDEXED|JOIN_KW */
- 193, /* (354) nm ::= STRING */
- 208, /* (355) typetoken ::= typename */
- 209, /* (356) typename ::= ID|STRING */
- 210, /* (357) signed ::= plus_num */
- 210, /* (358) signed ::= minus_num */
- 207, /* (359) carglist ::= carglist ccons */
- 207, /* (360) carglist ::= */
- 215, /* (361) ccons ::= NULL onconf */
- 215, /* (362) ccons ::= GENERATED ALWAYS AS generated */
- 215, /* (363) ccons ::= AS generated */
- 202, /* (364) conslist_opt ::= COMMA conslist */
- 228, /* (365) conslist ::= conslist tconscomma tcons */
- 228, /* (366) conslist ::= tcons */
- 229, /* (367) tconscomma ::= */
- 233, /* (368) defer_subclause_opt ::= defer_subclause */
- 235, /* (369) resolvetype ::= raisetype */
- 239, /* (370) selectnowith ::= oneselect */
- 240, /* (371) oneselect ::= values */
- 254, /* (372) sclp ::= selcollist COMMA */
- 255, /* (373) as ::= ID|STRING */
- 264, /* (374) indexed_opt ::= indexed_by */
- 272, /* (375) returning ::= */
- 217, /* (376) expr ::= term */
- 274, /* (377) likeop ::= LIKE_KW|MATCH */
- 278, /* (378) case_operand ::= expr */
- 261, /* (379) exprlist ::= nexprlist */
- 284, /* (380) nmnum ::= plus_num */
- 284, /* (381) nmnum ::= nm */
- 284, /* (382) nmnum ::= ON */
- 284, /* (383) nmnum ::= DELETE */
- 284, /* (384) nmnum ::= DEFAULT */
- 211, /* (385) plus_num ::= INTEGER|FLOAT */
- 289, /* (386) foreach_clause ::= */
- 289, /* (387) foreach_clause ::= FOR EACH ROW */
- 292, /* (388) trnm ::= nm */
- 293, /* (389) tridxby ::= */
- 294, /* (390) database_kw_opt ::= DATABASE */
- 294, /* (391) database_kw_opt ::= */
- 297, /* (392) kwcolumn_opt ::= */
- 297, /* (393) kwcolumn_opt ::= COLUMNKW */
- 299, /* (394) vtabarglist ::= vtabarg */
- 299, /* (395) vtabarglist ::= vtabarglist COMMA vtabarg */
- 300, /* (396) vtabarg ::= vtabarg vtabargtoken */
- 303, /* (397) anylist ::= */
- 303, /* (398) anylist ::= anylist LP anylist RP */
- 303, /* (399) anylist ::= anylist ANY */
- 266, /* (400) with ::= */
- 306, /* (401) windowdefn_list ::= windowdefn */
- 308, /* (402) window ::= frame_opt */
+ 190, /* (0) explain ::= EXPLAIN */
+ 190, /* (1) explain ::= EXPLAIN QUERY PLAN */
+ 189, /* (2) cmdx ::= cmd */
+ 191, /* (3) cmd ::= BEGIN transtype trans_opt */
+ 192, /* (4) transtype ::= */
+ 192, /* (5) transtype ::= DEFERRED */
+ 192, /* (6) transtype ::= IMMEDIATE */
+ 192, /* (7) transtype ::= EXCLUSIVE */
+ 191, /* (8) cmd ::= COMMIT|END trans_opt */
+ 191, /* (9) cmd ::= ROLLBACK trans_opt */
+ 191, /* (10) cmd ::= SAVEPOINT nm */
+ 191, /* (11) cmd ::= RELEASE savepoint_opt nm */
+ 191, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
+ 196, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
+ 198, /* (14) createkw ::= CREATE */
+ 200, /* (15) ifnotexists ::= */
+ 200, /* (16) ifnotexists ::= IF NOT EXISTS */
+ 199, /* (17) temp ::= TEMP */
+ 199, /* (18) temp ::= */
+ 197, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */
+ 197, /* (20) create_table_args ::= AS select */
+ 204, /* (21) table_option_set ::= */
+ 204, /* (22) table_option_set ::= table_option_set COMMA table_option */
+ 206, /* (23) table_option ::= WITHOUT nm */
+ 206, /* (24) table_option ::= nm */
+ 207, /* (25) columnname ::= nm typetoken */
+ 209, /* (26) typetoken ::= */
+ 209, /* (27) typetoken ::= typename LP signed RP */
+ 209, /* (28) typetoken ::= typename LP signed COMMA signed RP */
+ 210, /* (29) typename ::= typename ID|STRING */
+ 214, /* (30) scanpt ::= */
+ 215, /* (31) scantok ::= */
+ 216, /* (32) ccons ::= CONSTRAINT nm */
+ 216, /* (33) ccons ::= DEFAULT scantok term */
+ 216, /* (34) ccons ::= DEFAULT LP expr RP */
+ 216, /* (35) ccons ::= DEFAULT PLUS scantok term */
+ 216, /* (36) ccons ::= DEFAULT MINUS scantok term */
+ 216, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */
+ 216, /* (38) ccons ::= NOT NULL onconf */
+ 216, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */
+ 216, /* (40) ccons ::= UNIQUE onconf */
+ 216, /* (41) ccons ::= CHECK LP expr RP */
+ 216, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */
+ 216, /* (43) ccons ::= defer_subclause */
+ 216, /* (44) ccons ::= COLLATE ID|STRING */
+ 225, /* (45) generated ::= LP expr RP */
+ 225, /* (46) generated ::= LP expr RP ID */
+ 221, /* (47) autoinc ::= */
+ 221, /* (48) autoinc ::= AUTOINCR */
+ 223, /* (49) refargs ::= */
+ 223, /* (50) refargs ::= refargs refarg */
+ 226, /* (51) refarg ::= MATCH nm */
+ 226, /* (52) refarg ::= ON INSERT refact */
+ 226, /* (53) refarg ::= ON DELETE refact */
+ 226, /* (54) refarg ::= ON UPDATE refact */
+ 227, /* (55) refact ::= SET NULL */
+ 227, /* (56) refact ::= SET DEFAULT */
+ 227, /* (57) refact ::= CASCADE */
+ 227, /* (58) refact ::= RESTRICT */
+ 227, /* (59) refact ::= NO ACTION */
+ 224, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
+ 224, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
+ 228, /* (62) init_deferred_pred_opt ::= */
+ 228, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */
+ 228, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
+ 203, /* (65) conslist_opt ::= */
+ 230, /* (66) tconscomma ::= COMMA */
+ 231, /* (67) tcons ::= CONSTRAINT nm */
+ 231, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
+ 231, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */
+ 231, /* (70) tcons ::= CHECK LP expr RP onconf */
+ 231, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
+ 234, /* (72) defer_subclause_opt ::= */
+ 219, /* (73) onconf ::= */
+ 219, /* (74) onconf ::= ON CONFLICT resolvetype */
+ 235, /* (75) orconf ::= */
+ 235, /* (76) orconf ::= OR resolvetype */
+ 236, /* (77) resolvetype ::= IGNORE */
+ 236, /* (78) resolvetype ::= REPLACE */
+ 191, /* (79) cmd ::= DROP TABLE ifexists fullname */
+ 238, /* (80) ifexists ::= IF EXISTS */
+ 238, /* (81) ifexists ::= */
+ 191, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
+ 191, /* (83) cmd ::= DROP VIEW ifexists fullname */
+ 191, /* (84) cmd ::= select */
+ 205, /* (85) select ::= WITH wqlist selectnowith */
+ 205, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */
+ 205, /* (87) select ::= selectnowith */
+ 240, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */
+ 243, /* (89) multiselect_op ::= UNION */
+ 243, /* (90) multiselect_op ::= UNION ALL */
+ 243, /* (91) multiselect_op ::= EXCEPT|INTERSECT */
+ 241, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
+ 241, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
+ 253, /* (94) values ::= VALUES LP nexprlist RP */
+ 241, /* (95) oneselect ::= mvalues */
+ 255, /* (96) mvalues ::= values COMMA LP nexprlist RP */
+ 255, /* (97) mvalues ::= mvalues COMMA LP nexprlist RP */
+ 244, /* (98) distinct ::= DISTINCT */
+ 244, /* (99) distinct ::= ALL */
+ 244, /* (100) distinct ::= */
+ 256, /* (101) sclp ::= */
+ 245, /* (102) selcollist ::= sclp scanpt expr scanpt as */
+ 245, /* (103) selcollist ::= sclp scanpt STAR */
+ 245, /* (104) selcollist ::= sclp scanpt nm DOT STAR */
+ 257, /* (105) as ::= AS nm */
+ 257, /* (106) as ::= */
+ 246, /* (107) from ::= */
+ 246, /* (108) from ::= FROM seltablist */
+ 259, /* (109) stl_prefix ::= seltablist joinop */
+ 259, /* (110) stl_prefix ::= */
+ 258, /* (111) seltablist ::= stl_prefix nm dbnm as on_using */
+ 258, /* (112) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
+ 258, /* (113) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
+ 258, /* (114) seltablist ::= stl_prefix LP select RP as on_using */
+ 258, /* (115) seltablist ::= stl_prefix LP seltablist RP as on_using */
+ 201, /* (116) dbnm ::= */
+ 201, /* (117) dbnm ::= DOT nm */
+ 239, /* (118) fullname ::= nm */
+ 239, /* (119) fullname ::= nm DOT nm */
+ 264, /* (120) xfullname ::= nm */
+ 264, /* (121) xfullname ::= nm DOT nm */
+ 264, /* (122) xfullname ::= nm DOT nm AS nm */
+ 264, /* (123) xfullname ::= nm AS nm */
+ 260, /* (124) joinop ::= COMMA|JOIN */
+ 260, /* (125) joinop ::= JOIN_KW JOIN */
+ 260, /* (126) joinop ::= JOIN_KW nm JOIN */
+ 260, /* (127) joinop ::= JOIN_KW nm nm JOIN */
+ 261, /* (128) on_using ::= ON expr */
+ 261, /* (129) on_using ::= USING LP idlist RP */
+ 261, /* (130) on_using ::= */
+ 266, /* (131) indexed_opt ::= */
+ 262, /* (132) indexed_by ::= INDEXED BY nm */
+ 262, /* (133) indexed_by ::= NOT INDEXED */
+ 250, /* (134) orderby_opt ::= */
+ 250, /* (135) orderby_opt ::= ORDER BY sortlist */
+ 232, /* (136) sortlist ::= sortlist COMMA expr sortorder nulls */
+ 232, /* (137) sortlist ::= expr sortorder nulls */
+ 220, /* (138) sortorder ::= ASC */
+ 220, /* (139) sortorder ::= DESC */
+ 220, /* (140) sortorder ::= */
+ 267, /* (141) nulls ::= NULLS FIRST */
+ 267, /* (142) nulls ::= NULLS LAST */
+ 267, /* (143) nulls ::= */
+ 248, /* (144) groupby_opt ::= */
+ 248, /* (145) groupby_opt ::= GROUP BY nexprlist */
+ 249, /* (146) having_opt ::= */
+ 249, /* (147) having_opt ::= HAVING expr */
+ 251, /* (148) limit_opt ::= */
+ 251, /* (149) limit_opt ::= LIMIT expr */
+ 251, /* (150) limit_opt ::= LIMIT expr OFFSET expr */
+ 251, /* (151) limit_opt ::= LIMIT expr COMMA expr */
+ 191, /* (152) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
+ 247, /* (153) where_opt ::= */
+ 247, /* (154) where_opt ::= WHERE expr */
+ 269, /* (155) where_opt_ret ::= */
+ 269, /* (156) where_opt_ret ::= WHERE expr */
+ 269, /* (157) where_opt_ret ::= RETURNING selcollist */
+ 269, /* (158) where_opt_ret ::= WHERE expr RETURNING selcollist */
+ 191, /* (159) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
+ 270, /* (160) setlist ::= setlist COMMA nm EQ expr */
+ 270, /* (161) setlist ::= setlist COMMA LP idlist RP EQ expr */
+ 270, /* (162) setlist ::= nm EQ expr */
+ 270, /* (163) setlist ::= LP idlist RP EQ expr */
+ 191, /* (164) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
+ 191, /* (165) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
+ 273, /* (166) upsert ::= */
+ 273, /* (167) upsert ::= RETURNING selcollist */
+ 273, /* (168) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
+ 273, /* (169) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
+ 273, /* (170) upsert ::= ON CONFLICT DO NOTHING returning */
+ 273, /* (171) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
+ 274, /* (172) returning ::= RETURNING selcollist */
+ 271, /* (173) insert_cmd ::= INSERT orconf */
+ 271, /* (174) insert_cmd ::= REPLACE */
+ 272, /* (175) idlist_opt ::= */
+ 272, /* (176) idlist_opt ::= LP idlist RP */
+ 265, /* (177) idlist ::= idlist COMMA nm */
+ 265, /* (178) idlist ::= nm */
+ 218, /* (179) expr ::= LP expr RP */
+ 218, /* (180) expr ::= ID|INDEXED|JOIN_KW */
+ 218, /* (181) expr ::= nm DOT nm */
+ 218, /* (182) expr ::= nm DOT nm DOT nm */
+ 217, /* (183) term ::= NULL|FLOAT|BLOB */
+ 217, /* (184) term ::= STRING */
+ 217, /* (185) term ::= INTEGER */
+ 218, /* (186) expr ::= VARIABLE */
+ 218, /* (187) expr ::= expr COLLATE ID|STRING */
+ 218, /* (188) expr ::= CAST LP expr AS typetoken RP */
+ 218, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
+ 218, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
+ 218, /* (191) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
+ 218, /* (192) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
+ 218, /* (193) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
+ 218, /* (194) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
+ 217, /* (195) term ::= CTIME_KW */
+ 218, /* (196) expr ::= LP nexprlist COMMA expr RP */
+ 218, /* (197) expr ::= expr AND expr */
+ 218, /* (198) expr ::= expr OR expr */
+ 218, /* (199) expr ::= expr LT|GT|GE|LE expr */
+ 218, /* (200) expr ::= expr EQ|NE expr */
+ 218, /* (201) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
+ 218, /* (202) expr ::= expr PLUS|MINUS expr */
+ 218, /* (203) expr ::= expr STAR|SLASH|REM expr */
+ 218, /* (204) expr ::= expr CONCAT expr */
+ 276, /* (205) likeop ::= NOT LIKE_KW|MATCH */
+ 218, /* (206) expr ::= expr likeop expr */
+ 218, /* (207) expr ::= expr likeop expr ESCAPE expr */
+ 218, /* (208) expr ::= expr ISNULL|NOTNULL */
+ 218, /* (209) expr ::= expr NOT NULL */
+ 218, /* (210) expr ::= expr IS expr */
+ 218, /* (211) expr ::= expr IS NOT expr */
+ 218, /* (212) expr ::= expr IS NOT DISTINCT FROM expr */
+ 218, /* (213) expr ::= expr IS DISTINCT FROM expr */
+ 218, /* (214) expr ::= NOT expr */
+ 218, /* (215) expr ::= BITNOT expr */
+ 218, /* (216) expr ::= PLUS|MINUS expr */
+ 218, /* (217) expr ::= expr PTR expr */
+ 277, /* (218) between_op ::= BETWEEN */
+ 277, /* (219) between_op ::= NOT BETWEEN */
+ 218, /* (220) expr ::= expr between_op expr AND expr */
+ 278, /* (221) in_op ::= IN */
+ 278, /* (222) in_op ::= NOT IN */
+ 218, /* (223) expr ::= expr in_op LP exprlist RP */
+ 218, /* (224) expr ::= LP select RP */
+ 218, /* (225) expr ::= expr in_op LP select RP */
+ 218, /* (226) expr ::= expr in_op nm dbnm paren_exprlist */
+ 218, /* (227) expr ::= EXISTS LP select RP */
+ 218, /* (228) expr ::= CASE case_operand case_exprlist case_else END */
+ 281, /* (229) case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ 281, /* (230) case_exprlist ::= WHEN expr THEN expr */
+ 282, /* (231) case_else ::= ELSE expr */
+ 282, /* (232) case_else ::= */
+ 280, /* (233) case_operand ::= */
+ 263, /* (234) exprlist ::= */
+ 254, /* (235) nexprlist ::= nexprlist COMMA expr */
+ 254, /* (236) nexprlist ::= expr */
+ 279, /* (237) paren_exprlist ::= */
+ 279, /* (238) paren_exprlist ::= LP exprlist RP */
+ 191, /* (239) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ 283, /* (240) uniqueflag ::= UNIQUE */
+ 283, /* (241) uniqueflag ::= */
+ 222, /* (242) eidlist_opt ::= */
+ 222, /* (243) eidlist_opt ::= LP eidlist RP */
+ 233, /* (244) eidlist ::= eidlist COMMA nm collate sortorder */
+ 233, /* (245) eidlist ::= nm collate sortorder */
+ 284, /* (246) collate ::= */
+ 284, /* (247) collate ::= COLLATE ID|STRING */
+ 191, /* (248) cmd ::= DROP INDEX ifexists fullname */
+ 191, /* (249) cmd ::= VACUUM vinto */
+ 191, /* (250) cmd ::= VACUUM nm vinto */
+ 285, /* (251) vinto ::= INTO expr */
+ 285, /* (252) vinto ::= */
+ 191, /* (253) cmd ::= PRAGMA nm dbnm */
+ 191, /* (254) cmd ::= PRAGMA nm dbnm EQ nmnum */
+ 191, /* (255) cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ 191, /* (256) cmd ::= PRAGMA nm dbnm EQ minus_num */
+ 191, /* (257) cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ 212, /* (258) plus_num ::= PLUS INTEGER|FLOAT */
+ 213, /* (259) minus_num ::= MINUS INTEGER|FLOAT */
+ 191, /* (260) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ 287, /* (261) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ 289, /* (262) trigger_time ::= BEFORE|AFTER */
+ 289, /* (263) trigger_time ::= INSTEAD OF */
+ 289, /* (264) trigger_time ::= */
+ 290, /* (265) trigger_event ::= DELETE|INSERT */
+ 290, /* (266) trigger_event ::= UPDATE */
+ 290, /* (267) trigger_event ::= UPDATE OF idlist */
+ 292, /* (268) when_clause ::= */
+ 292, /* (269) when_clause ::= WHEN expr */
+ 288, /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ 288, /* (271) trigger_cmd_list ::= trigger_cmd SEMI */
+ 294, /* (272) trnm ::= nm DOT nm */
+ 295, /* (273) tridxby ::= INDEXED BY nm */
+ 295, /* (274) tridxby ::= NOT INDEXED */
+ 293, /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
+ 293, /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ 293, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+ 293, /* (278) trigger_cmd ::= scanpt select scanpt */
+ 218, /* (279) expr ::= RAISE LP IGNORE RP */
+ 218, /* (280) expr ::= RAISE LP raisetype COMMA nm RP */
+ 237, /* (281) raisetype ::= ROLLBACK */
+ 237, /* (282) raisetype ::= ABORT */
+ 237, /* (283) raisetype ::= FAIL */
+ 191, /* (284) cmd ::= DROP TRIGGER ifexists fullname */
+ 191, /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ 191, /* (286) cmd ::= DETACH database_kw_opt expr */
+ 297, /* (287) key_opt ::= */
+ 297, /* (288) key_opt ::= KEY expr */
+ 191, /* (289) cmd ::= REINDEX */
+ 191, /* (290) cmd ::= REINDEX nm dbnm */
+ 191, /* (291) cmd ::= ANALYZE */
+ 191, /* (292) cmd ::= ANALYZE nm dbnm */
+ 191, /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */
+ 191, /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ 191, /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
+ 298, /* (296) add_column_fullname ::= fullname */
+ 191, /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ 191, /* (298) cmd ::= create_vtab */
+ 191, /* (299) cmd ::= create_vtab LP vtabarglist RP */
+ 300, /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ 302, /* (301) vtabarg ::= */
+ 303, /* (302) vtabargtoken ::= ANY */
+ 303, /* (303) vtabargtoken ::= lp anylist RP */
+ 304, /* (304) lp ::= LP */
+ 268, /* (305) with ::= WITH wqlist */
+ 268, /* (306) with ::= WITH RECURSIVE wqlist */
+ 307, /* (307) wqas ::= AS */
+ 307, /* (308) wqas ::= AS MATERIALIZED */
+ 307, /* (309) wqas ::= AS NOT MATERIALIZED */
+ 306, /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */
+ 308, /* (311) withnm ::= nm */
+ 242, /* (312) wqlist ::= wqitem */
+ 242, /* (313) wqlist ::= wqlist COMMA wqitem */
+ 309, /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ 310, /* (315) windowdefn ::= nm AS LP window RP */
+ 311, /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ 311, /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ 311, /* (318) window ::= ORDER BY sortlist frame_opt */
+ 311, /* (319) window ::= nm ORDER BY sortlist frame_opt */
+ 311, /* (320) window ::= nm frame_opt */
+ 312, /* (321) frame_opt ::= */
+ 312, /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ 312, /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ 316, /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */
+ 318, /* (325) frame_bound_s ::= frame_bound */
+ 318, /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */
+ 319, /* (327) frame_bound_e ::= frame_bound */
+ 319, /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */
+ 317, /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */
+ 317, /* (330) frame_bound ::= CURRENT ROW */
+ 320, /* (331) frame_exclude_opt ::= */
+ 320, /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */
+ 321, /* (333) frame_exclude ::= NO OTHERS */
+ 321, /* (334) frame_exclude ::= CURRENT ROW */
+ 321, /* (335) frame_exclude ::= GROUP|TIES */
+ 252, /* (336) window_clause ::= WINDOW windowdefn_list */
+ 275, /* (337) filter_over ::= filter_clause over_clause */
+ 275, /* (338) filter_over ::= over_clause */
+ 275, /* (339) filter_over ::= filter_clause */
+ 315, /* (340) over_clause ::= OVER LP window RP */
+ 315, /* (341) over_clause ::= OVER nm */
+ 314, /* (342) filter_clause ::= FILTER LP WHERE expr RP */
+ 217, /* (343) term ::= QNUMBER */
+ 186, /* (344) input ::= cmdlist */
+ 187, /* (345) cmdlist ::= cmdlist ecmd */
+ 187, /* (346) cmdlist ::= ecmd */
+ 188, /* (347) ecmd ::= SEMI */
+ 188, /* (348) ecmd ::= cmdx SEMI */
+ 188, /* (349) ecmd ::= explain cmdx SEMI */
+ 193, /* (350) trans_opt ::= */
+ 193, /* (351) trans_opt ::= TRANSACTION */
+ 193, /* (352) trans_opt ::= TRANSACTION nm */
+ 195, /* (353) savepoint_opt ::= SAVEPOINT */
+ 195, /* (354) savepoint_opt ::= */
+ 191, /* (355) cmd ::= create_table create_table_args */
+ 204, /* (356) table_option_set ::= table_option */
+ 202, /* (357) columnlist ::= columnlist COMMA columnname carglist */
+ 202, /* (358) columnlist ::= columnname carglist */
+ 194, /* (359) nm ::= ID|INDEXED|JOIN_KW */
+ 194, /* (360) nm ::= STRING */
+ 209, /* (361) typetoken ::= typename */
+ 210, /* (362) typename ::= ID|STRING */
+ 211, /* (363) signed ::= plus_num */
+ 211, /* (364) signed ::= minus_num */
+ 208, /* (365) carglist ::= carglist ccons */
+ 208, /* (366) carglist ::= */
+ 216, /* (367) ccons ::= NULL onconf */
+ 216, /* (368) ccons ::= GENERATED ALWAYS AS generated */
+ 216, /* (369) ccons ::= AS generated */
+ 203, /* (370) conslist_opt ::= COMMA conslist */
+ 229, /* (371) conslist ::= conslist tconscomma tcons */
+ 229, /* (372) conslist ::= tcons */
+ 230, /* (373) tconscomma ::= */
+ 234, /* (374) defer_subclause_opt ::= defer_subclause */
+ 236, /* (375) resolvetype ::= raisetype */
+ 240, /* (376) selectnowith ::= oneselect */
+ 241, /* (377) oneselect ::= values */
+ 256, /* (378) sclp ::= selcollist COMMA */
+ 257, /* (379) as ::= ID|STRING */
+ 266, /* (380) indexed_opt ::= indexed_by */
+ 274, /* (381) returning ::= */
+ 218, /* (382) expr ::= term */
+ 276, /* (383) likeop ::= LIKE_KW|MATCH */
+ 280, /* (384) case_operand ::= expr */
+ 263, /* (385) exprlist ::= nexprlist */
+ 286, /* (386) nmnum ::= plus_num */
+ 286, /* (387) nmnum ::= nm */
+ 286, /* (388) nmnum ::= ON */
+ 286, /* (389) nmnum ::= DELETE */
+ 286, /* (390) nmnum ::= DEFAULT */
+ 212, /* (391) plus_num ::= INTEGER|FLOAT */
+ 291, /* (392) foreach_clause ::= */
+ 291, /* (393) foreach_clause ::= FOR EACH ROW */
+ 294, /* (394) trnm ::= nm */
+ 295, /* (395) tridxby ::= */
+ 296, /* (396) database_kw_opt ::= DATABASE */
+ 296, /* (397) database_kw_opt ::= */
+ 299, /* (398) kwcolumn_opt ::= */
+ 299, /* (399) kwcolumn_opt ::= COLUMNKW */
+ 301, /* (400) vtabarglist ::= vtabarg */
+ 301, /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */
+ 302, /* (402) vtabarg ::= vtabarg vtabargtoken */
+ 305, /* (403) anylist ::= */
+ 305, /* (404) anylist ::= anylist LP anylist RP */
+ 305, /* (405) anylist ::= anylist ANY */
+ 268, /* (406) with ::= */
+ 309, /* (407) windowdefn_list ::= windowdefn */
+ 311, /* (408) window ::= frame_opt */
};
/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
@@ -172453,314 +175761,320 @@ static const signed char yyRuleInfoNRhs[] = {
-9, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
-10, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
-4, /* (94) values ::= VALUES LP nexprlist RP */
- -5, /* (95) values ::= values COMMA LP nexprlist RP */
- -1, /* (96) distinct ::= DISTINCT */
- -1, /* (97) distinct ::= ALL */
- 0, /* (98) distinct ::= */
- 0, /* (99) sclp ::= */
- -5, /* (100) selcollist ::= sclp scanpt expr scanpt as */
- -3, /* (101) selcollist ::= sclp scanpt STAR */
- -5, /* (102) selcollist ::= sclp scanpt nm DOT STAR */
- -2, /* (103) as ::= AS nm */
- 0, /* (104) as ::= */
- 0, /* (105) from ::= */
- -2, /* (106) from ::= FROM seltablist */
- -2, /* (107) stl_prefix ::= seltablist joinop */
- 0, /* (108) stl_prefix ::= */
- -5, /* (109) seltablist ::= stl_prefix nm dbnm as on_using */
- -6, /* (110) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
- -8, /* (111) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
- -6, /* (112) seltablist ::= stl_prefix LP select RP as on_using */
- -6, /* (113) seltablist ::= stl_prefix LP seltablist RP as on_using */
- 0, /* (114) dbnm ::= */
- -2, /* (115) dbnm ::= DOT nm */
- -1, /* (116) fullname ::= nm */
- -3, /* (117) fullname ::= nm DOT nm */
- -1, /* (118) xfullname ::= nm */
- -3, /* (119) xfullname ::= nm DOT nm */
- -5, /* (120) xfullname ::= nm DOT nm AS nm */
- -3, /* (121) xfullname ::= nm AS nm */
- -1, /* (122) joinop ::= COMMA|JOIN */
- -2, /* (123) joinop ::= JOIN_KW JOIN */
- -3, /* (124) joinop ::= JOIN_KW nm JOIN */
- -4, /* (125) joinop ::= JOIN_KW nm nm JOIN */
- -2, /* (126) on_using ::= ON expr */
- -4, /* (127) on_using ::= USING LP idlist RP */
- 0, /* (128) on_using ::= */
- 0, /* (129) indexed_opt ::= */
- -3, /* (130) indexed_by ::= INDEXED BY nm */
- -2, /* (131) indexed_by ::= NOT INDEXED */
- 0, /* (132) orderby_opt ::= */
- -3, /* (133) orderby_opt ::= ORDER BY sortlist */
- -5, /* (134) sortlist ::= sortlist COMMA expr sortorder nulls */
- -3, /* (135) sortlist ::= expr sortorder nulls */
- -1, /* (136) sortorder ::= ASC */
- -1, /* (137) sortorder ::= DESC */
- 0, /* (138) sortorder ::= */
- -2, /* (139) nulls ::= NULLS FIRST */
- -2, /* (140) nulls ::= NULLS LAST */
- 0, /* (141) nulls ::= */
- 0, /* (142) groupby_opt ::= */
- -3, /* (143) groupby_opt ::= GROUP BY nexprlist */
- 0, /* (144) having_opt ::= */
- -2, /* (145) having_opt ::= HAVING expr */
- 0, /* (146) limit_opt ::= */
- -2, /* (147) limit_opt ::= LIMIT expr */
- -4, /* (148) limit_opt ::= LIMIT expr OFFSET expr */
- -4, /* (149) limit_opt ::= LIMIT expr COMMA expr */
- -6, /* (150) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
- 0, /* (151) where_opt ::= */
- -2, /* (152) where_opt ::= WHERE expr */
- 0, /* (153) where_opt_ret ::= */
- -2, /* (154) where_opt_ret ::= WHERE expr */
- -2, /* (155) where_opt_ret ::= RETURNING selcollist */
- -4, /* (156) where_opt_ret ::= WHERE expr RETURNING selcollist */
- -9, /* (157) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
- -5, /* (158) setlist ::= setlist COMMA nm EQ expr */
- -7, /* (159) setlist ::= setlist COMMA LP idlist RP EQ expr */
- -3, /* (160) setlist ::= nm EQ expr */
- -5, /* (161) setlist ::= LP idlist RP EQ expr */
- -7, /* (162) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
- -8, /* (163) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
- 0, /* (164) upsert ::= */
- -2, /* (165) upsert ::= RETURNING selcollist */
- -12, /* (166) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
- -9, /* (167) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
- -5, /* (168) upsert ::= ON CONFLICT DO NOTHING returning */
- -8, /* (169) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
- -2, /* (170) returning ::= RETURNING selcollist */
- -2, /* (171) insert_cmd ::= INSERT orconf */
- -1, /* (172) insert_cmd ::= REPLACE */
- 0, /* (173) idlist_opt ::= */
- -3, /* (174) idlist_opt ::= LP idlist RP */
- -3, /* (175) idlist ::= idlist COMMA nm */
- -1, /* (176) idlist ::= nm */
- -3, /* (177) expr ::= LP expr RP */
- -1, /* (178) expr ::= ID|INDEXED|JOIN_KW */
- -3, /* (179) expr ::= nm DOT nm */
- -5, /* (180) expr ::= nm DOT nm DOT nm */
- -1, /* (181) term ::= NULL|FLOAT|BLOB */
- -1, /* (182) term ::= STRING */
- -1, /* (183) term ::= INTEGER */
- -1, /* (184) expr ::= VARIABLE */
- -3, /* (185) expr ::= expr COLLATE ID|STRING */
- -6, /* (186) expr ::= CAST LP expr AS typetoken RP */
- -5, /* (187) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
- -4, /* (188) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
- -6, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
- -5, /* (190) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
- -1, /* (191) term ::= CTIME_KW */
- -5, /* (192) expr ::= LP nexprlist COMMA expr RP */
- -3, /* (193) expr ::= expr AND expr */
- -3, /* (194) expr ::= expr OR expr */
- -3, /* (195) expr ::= expr LT|GT|GE|LE expr */
- -3, /* (196) expr ::= expr EQ|NE expr */
- -3, /* (197) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
- -3, /* (198) expr ::= expr PLUS|MINUS expr */
- -3, /* (199) expr ::= expr STAR|SLASH|REM expr */
- -3, /* (200) expr ::= expr CONCAT expr */
- -2, /* (201) likeop ::= NOT LIKE_KW|MATCH */
- -3, /* (202) expr ::= expr likeop expr */
- -5, /* (203) expr ::= expr likeop expr ESCAPE expr */
- -2, /* (204) expr ::= expr ISNULL|NOTNULL */
- -3, /* (205) expr ::= expr NOT NULL */
- -3, /* (206) expr ::= expr IS expr */
- -4, /* (207) expr ::= expr IS NOT expr */
- -6, /* (208) expr ::= expr IS NOT DISTINCT FROM expr */
- -5, /* (209) expr ::= expr IS DISTINCT FROM expr */
- -2, /* (210) expr ::= NOT expr */
- -2, /* (211) expr ::= BITNOT expr */
- -2, /* (212) expr ::= PLUS|MINUS expr */
- -3, /* (213) expr ::= expr PTR expr */
- -1, /* (214) between_op ::= BETWEEN */
- -2, /* (215) between_op ::= NOT BETWEEN */
- -5, /* (216) expr ::= expr between_op expr AND expr */
- -1, /* (217) in_op ::= IN */
- -2, /* (218) in_op ::= NOT IN */
- -5, /* (219) expr ::= expr in_op LP exprlist RP */
- -3, /* (220) expr ::= LP select RP */
- -5, /* (221) expr ::= expr in_op LP select RP */
- -5, /* (222) expr ::= expr in_op nm dbnm paren_exprlist */
- -4, /* (223) expr ::= EXISTS LP select RP */
- -5, /* (224) expr ::= CASE case_operand case_exprlist case_else END */
- -5, /* (225) case_exprlist ::= case_exprlist WHEN expr THEN expr */
- -4, /* (226) case_exprlist ::= WHEN expr THEN expr */
- -2, /* (227) case_else ::= ELSE expr */
- 0, /* (228) case_else ::= */
- 0, /* (229) case_operand ::= */
- 0, /* (230) exprlist ::= */
- -3, /* (231) nexprlist ::= nexprlist COMMA expr */
- -1, /* (232) nexprlist ::= expr */
- 0, /* (233) paren_exprlist ::= */
- -3, /* (234) paren_exprlist ::= LP exprlist RP */
- -12, /* (235) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
- -1, /* (236) uniqueflag ::= UNIQUE */
- 0, /* (237) uniqueflag ::= */
- 0, /* (238) eidlist_opt ::= */
- -3, /* (239) eidlist_opt ::= LP eidlist RP */
- -5, /* (240) eidlist ::= eidlist COMMA nm collate sortorder */
- -3, /* (241) eidlist ::= nm collate sortorder */
- 0, /* (242) collate ::= */
- -2, /* (243) collate ::= COLLATE ID|STRING */
- -4, /* (244) cmd ::= DROP INDEX ifexists fullname */
- -2, /* (245) cmd ::= VACUUM vinto */
- -3, /* (246) cmd ::= VACUUM nm vinto */
- -2, /* (247) vinto ::= INTO expr */
- 0, /* (248) vinto ::= */
- -3, /* (249) cmd ::= PRAGMA nm dbnm */
- -5, /* (250) cmd ::= PRAGMA nm dbnm EQ nmnum */
- -6, /* (251) cmd ::= PRAGMA nm dbnm LP nmnum RP */
- -5, /* (252) cmd ::= PRAGMA nm dbnm EQ minus_num */
- -6, /* (253) cmd ::= PRAGMA nm dbnm LP minus_num RP */
- -2, /* (254) plus_num ::= PLUS INTEGER|FLOAT */
- -2, /* (255) minus_num ::= MINUS INTEGER|FLOAT */
- -5, /* (256) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
- -11, /* (257) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
- -1, /* (258) trigger_time ::= BEFORE|AFTER */
- -2, /* (259) trigger_time ::= INSTEAD OF */
- 0, /* (260) trigger_time ::= */
- -1, /* (261) trigger_event ::= DELETE|INSERT */
- -1, /* (262) trigger_event ::= UPDATE */
- -3, /* (263) trigger_event ::= UPDATE OF idlist */
- 0, /* (264) when_clause ::= */
- -2, /* (265) when_clause ::= WHEN expr */
- -3, /* (266) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
- -2, /* (267) trigger_cmd_list ::= trigger_cmd SEMI */
- -3, /* (268) trnm ::= nm DOT nm */
- -3, /* (269) tridxby ::= INDEXED BY nm */
- -2, /* (270) tridxby ::= NOT INDEXED */
- -9, /* (271) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
- -8, /* (272) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
- -6, /* (273) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
- -3, /* (274) trigger_cmd ::= scanpt select scanpt */
- -4, /* (275) expr ::= RAISE LP IGNORE RP */
- -6, /* (276) expr ::= RAISE LP raisetype COMMA nm RP */
- -1, /* (277) raisetype ::= ROLLBACK */
- -1, /* (278) raisetype ::= ABORT */
- -1, /* (279) raisetype ::= FAIL */
- -4, /* (280) cmd ::= DROP TRIGGER ifexists fullname */
- -6, /* (281) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
- -3, /* (282) cmd ::= DETACH database_kw_opt expr */
- 0, /* (283) key_opt ::= */
- -2, /* (284) key_opt ::= KEY expr */
- -1, /* (285) cmd ::= REINDEX */
- -3, /* (286) cmd ::= REINDEX nm dbnm */
- -1, /* (287) cmd ::= ANALYZE */
- -3, /* (288) cmd ::= ANALYZE nm dbnm */
- -6, /* (289) cmd ::= ALTER TABLE fullname RENAME TO nm */
- -7, /* (290) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
- -6, /* (291) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
- -1, /* (292) add_column_fullname ::= fullname */
- -8, /* (293) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
- -1, /* (294) cmd ::= create_vtab */
- -4, /* (295) cmd ::= create_vtab LP vtabarglist RP */
- -8, /* (296) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
- 0, /* (297) vtabarg ::= */
- -1, /* (298) vtabargtoken ::= ANY */
- -3, /* (299) vtabargtoken ::= lp anylist RP */
- -1, /* (300) lp ::= LP */
- -2, /* (301) with ::= WITH wqlist */
- -3, /* (302) with ::= WITH RECURSIVE wqlist */
- -1, /* (303) wqas ::= AS */
- -2, /* (304) wqas ::= AS MATERIALIZED */
- -3, /* (305) wqas ::= AS NOT MATERIALIZED */
- -6, /* (306) wqitem ::= nm eidlist_opt wqas LP select RP */
- -1, /* (307) wqlist ::= wqitem */
- -3, /* (308) wqlist ::= wqlist COMMA wqitem */
- -3, /* (309) windowdefn_list ::= windowdefn_list COMMA windowdefn */
- -5, /* (310) windowdefn ::= nm AS LP window RP */
- -5, /* (311) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
- -6, /* (312) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
- -4, /* (313) window ::= ORDER BY sortlist frame_opt */
- -5, /* (314) window ::= nm ORDER BY sortlist frame_opt */
- -2, /* (315) window ::= nm frame_opt */
- 0, /* (316) frame_opt ::= */
- -3, /* (317) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
- -6, /* (318) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
- -1, /* (319) range_or_rows ::= RANGE|ROWS|GROUPS */
- -1, /* (320) frame_bound_s ::= frame_bound */
- -2, /* (321) frame_bound_s ::= UNBOUNDED PRECEDING */
- -1, /* (322) frame_bound_e ::= frame_bound */
- -2, /* (323) frame_bound_e ::= UNBOUNDED FOLLOWING */
- -2, /* (324) frame_bound ::= expr PRECEDING|FOLLOWING */
- -2, /* (325) frame_bound ::= CURRENT ROW */
- 0, /* (326) frame_exclude_opt ::= */
- -2, /* (327) frame_exclude_opt ::= EXCLUDE frame_exclude */
- -2, /* (328) frame_exclude ::= NO OTHERS */
- -2, /* (329) frame_exclude ::= CURRENT ROW */
- -1, /* (330) frame_exclude ::= GROUP|TIES */
- -2, /* (331) window_clause ::= WINDOW windowdefn_list */
- -2, /* (332) filter_over ::= filter_clause over_clause */
- -1, /* (333) filter_over ::= over_clause */
- -1, /* (334) filter_over ::= filter_clause */
- -4, /* (335) over_clause ::= OVER LP window RP */
- -2, /* (336) over_clause ::= OVER nm */
- -5, /* (337) filter_clause ::= FILTER LP WHERE expr RP */
- -1, /* (338) input ::= cmdlist */
- -2, /* (339) cmdlist ::= cmdlist ecmd */
- -1, /* (340) cmdlist ::= ecmd */
- -1, /* (341) ecmd ::= SEMI */
- -2, /* (342) ecmd ::= cmdx SEMI */
- -3, /* (343) ecmd ::= explain cmdx SEMI */
- 0, /* (344) trans_opt ::= */
- -1, /* (345) trans_opt ::= TRANSACTION */
- -2, /* (346) trans_opt ::= TRANSACTION nm */
- -1, /* (347) savepoint_opt ::= SAVEPOINT */
- 0, /* (348) savepoint_opt ::= */
- -2, /* (349) cmd ::= create_table create_table_args */
- -1, /* (350) table_option_set ::= table_option */
- -4, /* (351) columnlist ::= columnlist COMMA columnname carglist */
- -2, /* (352) columnlist ::= columnname carglist */
- -1, /* (353) nm ::= ID|INDEXED|JOIN_KW */
- -1, /* (354) nm ::= STRING */
- -1, /* (355) typetoken ::= typename */
- -1, /* (356) typename ::= ID|STRING */
- -1, /* (357) signed ::= plus_num */
- -1, /* (358) signed ::= minus_num */
- -2, /* (359) carglist ::= carglist ccons */
- 0, /* (360) carglist ::= */
- -2, /* (361) ccons ::= NULL onconf */
- -4, /* (362) ccons ::= GENERATED ALWAYS AS generated */
- -2, /* (363) ccons ::= AS generated */
- -2, /* (364) conslist_opt ::= COMMA conslist */
- -3, /* (365) conslist ::= conslist tconscomma tcons */
- -1, /* (366) conslist ::= tcons */
- 0, /* (367) tconscomma ::= */
- -1, /* (368) defer_subclause_opt ::= defer_subclause */
- -1, /* (369) resolvetype ::= raisetype */
- -1, /* (370) selectnowith ::= oneselect */
- -1, /* (371) oneselect ::= values */
- -2, /* (372) sclp ::= selcollist COMMA */
- -1, /* (373) as ::= ID|STRING */
- -1, /* (374) indexed_opt ::= indexed_by */
- 0, /* (375) returning ::= */
- -1, /* (376) expr ::= term */
- -1, /* (377) likeop ::= LIKE_KW|MATCH */
- -1, /* (378) case_operand ::= expr */
- -1, /* (379) exprlist ::= nexprlist */
- -1, /* (380) nmnum ::= plus_num */
- -1, /* (381) nmnum ::= nm */
- -1, /* (382) nmnum ::= ON */
- -1, /* (383) nmnum ::= DELETE */
- -1, /* (384) nmnum ::= DEFAULT */
- -1, /* (385) plus_num ::= INTEGER|FLOAT */
- 0, /* (386) foreach_clause ::= */
- -3, /* (387) foreach_clause ::= FOR EACH ROW */
- -1, /* (388) trnm ::= nm */
- 0, /* (389) tridxby ::= */
- -1, /* (390) database_kw_opt ::= DATABASE */
- 0, /* (391) database_kw_opt ::= */
- 0, /* (392) kwcolumn_opt ::= */
- -1, /* (393) kwcolumn_opt ::= COLUMNKW */
- -1, /* (394) vtabarglist ::= vtabarg */
- -3, /* (395) vtabarglist ::= vtabarglist COMMA vtabarg */
- -2, /* (396) vtabarg ::= vtabarg vtabargtoken */
- 0, /* (397) anylist ::= */
- -4, /* (398) anylist ::= anylist LP anylist RP */
- -2, /* (399) anylist ::= anylist ANY */
- 0, /* (400) with ::= */
- -1, /* (401) windowdefn_list ::= windowdefn */
- -1, /* (402) window ::= frame_opt */
+ -1, /* (95) oneselect ::= mvalues */
+ -5, /* (96) mvalues ::= values COMMA LP nexprlist RP */
+ -5, /* (97) mvalues ::= mvalues COMMA LP nexprlist RP */
+ -1, /* (98) distinct ::= DISTINCT */
+ -1, /* (99) distinct ::= ALL */
+ 0, /* (100) distinct ::= */
+ 0, /* (101) sclp ::= */
+ -5, /* (102) selcollist ::= sclp scanpt expr scanpt as */
+ -3, /* (103) selcollist ::= sclp scanpt STAR */
+ -5, /* (104) selcollist ::= sclp scanpt nm DOT STAR */
+ -2, /* (105) as ::= AS nm */
+ 0, /* (106) as ::= */
+ 0, /* (107) from ::= */
+ -2, /* (108) from ::= FROM seltablist */
+ -2, /* (109) stl_prefix ::= seltablist joinop */
+ 0, /* (110) stl_prefix ::= */
+ -5, /* (111) seltablist ::= stl_prefix nm dbnm as on_using */
+ -6, /* (112) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
+ -8, /* (113) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
+ -6, /* (114) seltablist ::= stl_prefix LP select RP as on_using */
+ -6, /* (115) seltablist ::= stl_prefix LP seltablist RP as on_using */
+ 0, /* (116) dbnm ::= */
+ -2, /* (117) dbnm ::= DOT nm */
+ -1, /* (118) fullname ::= nm */
+ -3, /* (119) fullname ::= nm DOT nm */
+ -1, /* (120) xfullname ::= nm */
+ -3, /* (121) xfullname ::= nm DOT nm */
+ -5, /* (122) xfullname ::= nm DOT nm AS nm */
+ -3, /* (123) xfullname ::= nm AS nm */
+ -1, /* (124) joinop ::= COMMA|JOIN */
+ -2, /* (125) joinop ::= JOIN_KW JOIN */
+ -3, /* (126) joinop ::= JOIN_KW nm JOIN */
+ -4, /* (127) joinop ::= JOIN_KW nm nm JOIN */
+ -2, /* (128) on_using ::= ON expr */
+ -4, /* (129) on_using ::= USING LP idlist RP */
+ 0, /* (130) on_using ::= */
+ 0, /* (131) indexed_opt ::= */
+ -3, /* (132) indexed_by ::= INDEXED BY nm */
+ -2, /* (133) indexed_by ::= NOT INDEXED */
+ 0, /* (134) orderby_opt ::= */
+ -3, /* (135) orderby_opt ::= ORDER BY sortlist */
+ -5, /* (136) sortlist ::= sortlist COMMA expr sortorder nulls */
+ -3, /* (137) sortlist ::= expr sortorder nulls */
+ -1, /* (138) sortorder ::= ASC */
+ -1, /* (139) sortorder ::= DESC */
+ 0, /* (140) sortorder ::= */
+ -2, /* (141) nulls ::= NULLS FIRST */
+ -2, /* (142) nulls ::= NULLS LAST */
+ 0, /* (143) nulls ::= */
+ 0, /* (144) groupby_opt ::= */
+ -3, /* (145) groupby_opt ::= GROUP BY nexprlist */
+ 0, /* (146) having_opt ::= */
+ -2, /* (147) having_opt ::= HAVING expr */
+ 0, /* (148) limit_opt ::= */
+ -2, /* (149) limit_opt ::= LIMIT expr */
+ -4, /* (150) limit_opt ::= LIMIT expr OFFSET expr */
+ -4, /* (151) limit_opt ::= LIMIT expr COMMA expr */
+ -6, /* (152) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
+ 0, /* (153) where_opt ::= */
+ -2, /* (154) where_opt ::= WHERE expr */
+ 0, /* (155) where_opt_ret ::= */
+ -2, /* (156) where_opt_ret ::= WHERE expr */
+ -2, /* (157) where_opt_ret ::= RETURNING selcollist */
+ -4, /* (158) where_opt_ret ::= WHERE expr RETURNING selcollist */
+ -9, /* (159) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
+ -5, /* (160) setlist ::= setlist COMMA nm EQ expr */
+ -7, /* (161) setlist ::= setlist COMMA LP idlist RP EQ expr */
+ -3, /* (162) setlist ::= nm EQ expr */
+ -5, /* (163) setlist ::= LP idlist RP EQ expr */
+ -7, /* (164) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
+ -8, /* (165) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
+ 0, /* (166) upsert ::= */
+ -2, /* (167) upsert ::= RETURNING selcollist */
+ -12, /* (168) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
+ -9, /* (169) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
+ -5, /* (170) upsert ::= ON CONFLICT DO NOTHING returning */
+ -8, /* (171) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
+ -2, /* (172) returning ::= RETURNING selcollist */
+ -2, /* (173) insert_cmd ::= INSERT orconf */
+ -1, /* (174) insert_cmd ::= REPLACE */
+ 0, /* (175) idlist_opt ::= */
+ -3, /* (176) idlist_opt ::= LP idlist RP */
+ -3, /* (177) idlist ::= idlist COMMA nm */
+ -1, /* (178) idlist ::= nm */
+ -3, /* (179) expr ::= LP expr RP */
+ -1, /* (180) expr ::= ID|INDEXED|JOIN_KW */
+ -3, /* (181) expr ::= nm DOT nm */
+ -5, /* (182) expr ::= nm DOT nm DOT nm */
+ -1, /* (183) term ::= NULL|FLOAT|BLOB */
+ -1, /* (184) term ::= STRING */
+ -1, /* (185) term ::= INTEGER */
+ -1, /* (186) expr ::= VARIABLE */
+ -3, /* (187) expr ::= expr COLLATE ID|STRING */
+ -6, /* (188) expr ::= CAST LP expr AS typetoken RP */
+ -5, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
+ -8, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
+ -4, /* (191) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
+ -6, /* (192) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
+ -9, /* (193) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
+ -5, /* (194) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
+ -1, /* (195) term ::= CTIME_KW */
+ -5, /* (196) expr ::= LP nexprlist COMMA expr RP */
+ -3, /* (197) expr ::= expr AND expr */
+ -3, /* (198) expr ::= expr OR expr */
+ -3, /* (199) expr ::= expr LT|GT|GE|LE expr */
+ -3, /* (200) expr ::= expr EQ|NE expr */
+ -3, /* (201) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
+ -3, /* (202) expr ::= expr PLUS|MINUS expr */
+ -3, /* (203) expr ::= expr STAR|SLASH|REM expr */
+ -3, /* (204) expr ::= expr CONCAT expr */
+ -2, /* (205) likeop ::= NOT LIKE_KW|MATCH */
+ -3, /* (206) expr ::= expr likeop expr */
+ -5, /* (207) expr ::= expr likeop expr ESCAPE expr */
+ -2, /* (208) expr ::= expr ISNULL|NOTNULL */
+ -3, /* (209) expr ::= expr NOT NULL */
+ -3, /* (210) expr ::= expr IS expr */
+ -4, /* (211) expr ::= expr IS NOT expr */
+ -6, /* (212) expr ::= expr IS NOT DISTINCT FROM expr */
+ -5, /* (213) expr ::= expr IS DISTINCT FROM expr */
+ -2, /* (214) expr ::= NOT expr */
+ -2, /* (215) expr ::= BITNOT expr */
+ -2, /* (216) expr ::= PLUS|MINUS expr */
+ -3, /* (217) expr ::= expr PTR expr */
+ -1, /* (218) between_op ::= BETWEEN */
+ -2, /* (219) between_op ::= NOT BETWEEN */
+ -5, /* (220) expr ::= expr between_op expr AND expr */
+ -1, /* (221) in_op ::= IN */
+ -2, /* (222) in_op ::= NOT IN */
+ -5, /* (223) expr ::= expr in_op LP exprlist RP */
+ -3, /* (224) expr ::= LP select RP */
+ -5, /* (225) expr ::= expr in_op LP select RP */
+ -5, /* (226) expr ::= expr in_op nm dbnm paren_exprlist */
+ -4, /* (227) expr ::= EXISTS LP select RP */
+ -5, /* (228) expr ::= CASE case_operand case_exprlist case_else END */
+ -5, /* (229) case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ -4, /* (230) case_exprlist ::= WHEN expr THEN expr */
+ -2, /* (231) case_else ::= ELSE expr */
+ 0, /* (232) case_else ::= */
+ 0, /* (233) case_operand ::= */
+ 0, /* (234) exprlist ::= */
+ -3, /* (235) nexprlist ::= nexprlist COMMA expr */
+ -1, /* (236) nexprlist ::= expr */
+ 0, /* (237) paren_exprlist ::= */
+ -3, /* (238) paren_exprlist ::= LP exprlist RP */
+ -12, /* (239) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ -1, /* (240) uniqueflag ::= UNIQUE */
+ 0, /* (241) uniqueflag ::= */
+ 0, /* (242) eidlist_opt ::= */
+ -3, /* (243) eidlist_opt ::= LP eidlist RP */
+ -5, /* (244) eidlist ::= eidlist COMMA nm collate sortorder */
+ -3, /* (245) eidlist ::= nm collate sortorder */
+ 0, /* (246) collate ::= */
+ -2, /* (247) collate ::= COLLATE ID|STRING */
+ -4, /* (248) cmd ::= DROP INDEX ifexists fullname */
+ -2, /* (249) cmd ::= VACUUM vinto */
+ -3, /* (250) cmd ::= VACUUM nm vinto */
+ -2, /* (251) vinto ::= INTO expr */
+ 0, /* (252) vinto ::= */
+ -3, /* (253) cmd ::= PRAGMA nm dbnm */
+ -5, /* (254) cmd ::= PRAGMA nm dbnm EQ nmnum */
+ -6, /* (255) cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ -5, /* (256) cmd ::= PRAGMA nm dbnm EQ minus_num */
+ -6, /* (257) cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ -2, /* (258) plus_num ::= PLUS INTEGER|FLOAT */
+ -2, /* (259) minus_num ::= MINUS INTEGER|FLOAT */
+ -5, /* (260) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ -11, /* (261) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ -1, /* (262) trigger_time ::= BEFORE|AFTER */
+ -2, /* (263) trigger_time ::= INSTEAD OF */
+ 0, /* (264) trigger_time ::= */
+ -1, /* (265) trigger_event ::= DELETE|INSERT */
+ -1, /* (266) trigger_event ::= UPDATE */
+ -3, /* (267) trigger_event ::= UPDATE OF idlist */
+ 0, /* (268) when_clause ::= */
+ -2, /* (269) when_clause ::= WHEN expr */
+ -3, /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ -2, /* (271) trigger_cmd_list ::= trigger_cmd SEMI */
+ -3, /* (272) trnm ::= nm DOT nm */
+ -3, /* (273) tridxby ::= INDEXED BY nm */
+ -2, /* (274) tridxby ::= NOT INDEXED */
+ -9, /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
+ -8, /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ -6, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+ -3, /* (278) trigger_cmd ::= scanpt select scanpt */
+ -4, /* (279) expr ::= RAISE LP IGNORE RP */
+ -6, /* (280) expr ::= RAISE LP raisetype COMMA nm RP */
+ -1, /* (281) raisetype ::= ROLLBACK */
+ -1, /* (282) raisetype ::= ABORT */
+ -1, /* (283) raisetype ::= FAIL */
+ -4, /* (284) cmd ::= DROP TRIGGER ifexists fullname */
+ -6, /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ -3, /* (286) cmd ::= DETACH database_kw_opt expr */
+ 0, /* (287) key_opt ::= */
+ -2, /* (288) key_opt ::= KEY expr */
+ -1, /* (289) cmd ::= REINDEX */
+ -3, /* (290) cmd ::= REINDEX nm dbnm */
+ -1, /* (291) cmd ::= ANALYZE */
+ -3, /* (292) cmd ::= ANALYZE nm dbnm */
+ -6, /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */
+ -7, /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ -6, /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
+ -1, /* (296) add_column_fullname ::= fullname */
+ -8, /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ -1, /* (298) cmd ::= create_vtab */
+ -4, /* (299) cmd ::= create_vtab LP vtabarglist RP */
+ -8, /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ 0, /* (301) vtabarg ::= */
+ -1, /* (302) vtabargtoken ::= ANY */
+ -3, /* (303) vtabargtoken ::= lp anylist RP */
+ -1, /* (304) lp ::= LP */
+ -2, /* (305) with ::= WITH wqlist */
+ -3, /* (306) with ::= WITH RECURSIVE wqlist */
+ -1, /* (307) wqas ::= AS */
+ -2, /* (308) wqas ::= AS MATERIALIZED */
+ -3, /* (309) wqas ::= AS NOT MATERIALIZED */
+ -6, /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */
+ -1, /* (311) withnm ::= nm */
+ -1, /* (312) wqlist ::= wqitem */
+ -3, /* (313) wqlist ::= wqlist COMMA wqitem */
+ -3, /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ -5, /* (315) windowdefn ::= nm AS LP window RP */
+ -5, /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ -6, /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ -4, /* (318) window ::= ORDER BY sortlist frame_opt */
+ -5, /* (319) window ::= nm ORDER BY sortlist frame_opt */
+ -2, /* (320) window ::= nm frame_opt */
+ 0, /* (321) frame_opt ::= */
+ -3, /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ -6, /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ -1, /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */
+ -1, /* (325) frame_bound_s ::= frame_bound */
+ -2, /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */
+ -1, /* (327) frame_bound_e ::= frame_bound */
+ -2, /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */
+ -2, /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */
+ -2, /* (330) frame_bound ::= CURRENT ROW */
+ 0, /* (331) frame_exclude_opt ::= */
+ -2, /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */
+ -2, /* (333) frame_exclude ::= NO OTHERS */
+ -2, /* (334) frame_exclude ::= CURRENT ROW */
+ -1, /* (335) frame_exclude ::= GROUP|TIES */
+ -2, /* (336) window_clause ::= WINDOW windowdefn_list */
+ -2, /* (337) filter_over ::= filter_clause over_clause */
+ -1, /* (338) filter_over ::= over_clause */
+ -1, /* (339) filter_over ::= filter_clause */
+ -4, /* (340) over_clause ::= OVER LP window RP */
+ -2, /* (341) over_clause ::= OVER nm */
+ -5, /* (342) filter_clause ::= FILTER LP WHERE expr RP */
+ -1, /* (343) term ::= QNUMBER */
+ -1, /* (344) input ::= cmdlist */
+ -2, /* (345) cmdlist ::= cmdlist ecmd */
+ -1, /* (346) cmdlist ::= ecmd */
+ -1, /* (347) ecmd ::= SEMI */
+ -2, /* (348) ecmd ::= cmdx SEMI */
+ -3, /* (349) ecmd ::= explain cmdx SEMI */
+ 0, /* (350) trans_opt ::= */
+ -1, /* (351) trans_opt ::= TRANSACTION */
+ -2, /* (352) trans_opt ::= TRANSACTION nm */
+ -1, /* (353) savepoint_opt ::= SAVEPOINT */
+ 0, /* (354) savepoint_opt ::= */
+ -2, /* (355) cmd ::= create_table create_table_args */
+ -1, /* (356) table_option_set ::= table_option */
+ -4, /* (357) columnlist ::= columnlist COMMA columnname carglist */
+ -2, /* (358) columnlist ::= columnname carglist */
+ -1, /* (359) nm ::= ID|INDEXED|JOIN_KW */
+ -1, /* (360) nm ::= STRING */
+ -1, /* (361) typetoken ::= typename */
+ -1, /* (362) typename ::= ID|STRING */
+ -1, /* (363) signed ::= plus_num */
+ -1, /* (364) signed ::= minus_num */
+ -2, /* (365) carglist ::= carglist ccons */
+ 0, /* (366) carglist ::= */
+ -2, /* (367) ccons ::= NULL onconf */
+ -4, /* (368) ccons ::= GENERATED ALWAYS AS generated */
+ -2, /* (369) ccons ::= AS generated */
+ -2, /* (370) conslist_opt ::= COMMA conslist */
+ -3, /* (371) conslist ::= conslist tconscomma tcons */
+ -1, /* (372) conslist ::= tcons */
+ 0, /* (373) tconscomma ::= */
+ -1, /* (374) defer_subclause_opt ::= defer_subclause */
+ -1, /* (375) resolvetype ::= raisetype */
+ -1, /* (376) selectnowith ::= oneselect */
+ -1, /* (377) oneselect ::= values */
+ -2, /* (378) sclp ::= selcollist COMMA */
+ -1, /* (379) as ::= ID|STRING */
+ -1, /* (380) indexed_opt ::= indexed_by */
+ 0, /* (381) returning ::= */
+ -1, /* (382) expr ::= term */
+ -1, /* (383) likeop ::= LIKE_KW|MATCH */
+ -1, /* (384) case_operand ::= expr */
+ -1, /* (385) exprlist ::= nexprlist */
+ -1, /* (386) nmnum ::= plus_num */
+ -1, /* (387) nmnum ::= nm */
+ -1, /* (388) nmnum ::= ON */
+ -1, /* (389) nmnum ::= DELETE */
+ -1, /* (390) nmnum ::= DEFAULT */
+ -1, /* (391) plus_num ::= INTEGER|FLOAT */
+ 0, /* (392) foreach_clause ::= */
+ -3, /* (393) foreach_clause ::= FOR EACH ROW */
+ -1, /* (394) trnm ::= nm */
+ 0, /* (395) tridxby ::= */
+ -1, /* (396) database_kw_opt ::= DATABASE */
+ 0, /* (397) database_kw_opt ::= */
+ 0, /* (398) kwcolumn_opt ::= */
+ -1, /* (399) kwcolumn_opt ::= COLUMNKW */
+ -1, /* (400) vtabarglist ::= vtabarg */
+ -3, /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */
+ -2, /* (402) vtabarg ::= vtabarg vtabargtoken */
+ 0, /* (403) anylist ::= */
+ -4, /* (404) anylist ::= anylist LP anylist RP */
+ -2, /* (405) anylist ::= anylist ANY */
+ 0, /* (406) with ::= */
+ -1, /* (407) windowdefn_list ::= windowdefn */
+ -1, /* (408) window ::= frame_opt */
};
static void yy_accept(yyParser*); /* Forward Declaration */
@@ -172812,16 +176126,16 @@ static YYACTIONTYPE yy_reduce(
{ sqlite3FinishCoding(pParse); }
break;
case 3: /* cmd ::= BEGIN transtype trans_opt */
-{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy394);}
+{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy144);}
break;
case 4: /* transtype ::= */
-{yymsp[1].minor.yy394 = TK_DEFERRED;}
+{yymsp[1].minor.yy144 = TK_DEFERRED;}
break;
case 5: /* transtype ::= DEFERRED */
case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6);
case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7);
- case 319: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==319);
-{yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/}
+ case 324: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==324);
+{yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-X*/}
break;
case 8: /* cmd ::= COMMIT|END trans_opt */
case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9);
@@ -172844,7 +176158,7 @@ static YYACTIONTYPE yy_reduce(
break;
case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */
{
- sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy394,0,0,yymsp[-2].minor.yy394);
+ sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy144,0,0,yymsp[-2].minor.yy144);
}
break;
case 14: /* createkw ::= CREATE */
@@ -172856,40 +176170,40 @@ static YYACTIONTYPE yy_reduce(
case 62: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==62);
case 72: /* defer_subclause_opt ::= */ yytestcase(yyruleno==72);
case 81: /* ifexists ::= */ yytestcase(yyruleno==81);
- case 98: /* distinct ::= */ yytestcase(yyruleno==98);
- case 242: /* collate ::= */ yytestcase(yyruleno==242);
-{yymsp[1].minor.yy394 = 0;}
+ case 100: /* distinct ::= */ yytestcase(yyruleno==100);
+ case 246: /* collate ::= */ yytestcase(yyruleno==246);
+{yymsp[1].minor.yy144 = 0;}
break;
case 16: /* ifnotexists ::= IF NOT EXISTS */
-{yymsp[-2].minor.yy394 = 1;}
+{yymsp[-2].minor.yy144 = 1;}
break;
case 17: /* temp ::= TEMP */
-{yymsp[0].minor.yy394 = pParse->db->init.busy==0;}
+{yymsp[0].minor.yy144 = pParse->db->init.busy==0;}
break;
case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_option_set */
{
- sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy285,0);
+ sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy391,0);
}
break;
case 20: /* create_table_args ::= AS select */
{
- sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy47);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy47);
+ sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy555);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555);
}
break;
case 21: /* table_option_set ::= */
-{yymsp[1].minor.yy285 = 0;}
+{yymsp[1].minor.yy391 = 0;}
break;
case 22: /* table_option_set ::= table_option_set COMMA table_option */
-{yylhsminor.yy285 = yymsp[-2].minor.yy285|yymsp[0].minor.yy285;}
- yymsp[-2].minor.yy285 = yylhsminor.yy285;
+{yylhsminor.yy391 = yymsp[-2].minor.yy391|yymsp[0].minor.yy391;}
+ yymsp[-2].minor.yy391 = yylhsminor.yy391;
break;
case 23: /* table_option ::= WITHOUT nm */
{
if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){
- yymsp[-1].minor.yy285 = TF_WithoutRowid | TF_NoVisibleRowid;
+ yymsp[-1].minor.yy391 = TF_WithoutRowid | TF_NoVisibleRowid;
}else{
- yymsp[-1].minor.yy285 = 0;
+ yymsp[-1].minor.yy391 = 0;
sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z);
}
}
@@ -172897,20 +176211,20 @@ static YYACTIONTYPE yy_reduce(
case 24: /* table_option ::= nm */
{
if( yymsp[0].minor.yy0.n==6 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"strict",6)==0 ){
- yylhsminor.yy285 = TF_Strict;
+ yylhsminor.yy391 = TF_Strict;
}else{
- yylhsminor.yy285 = 0;
+ yylhsminor.yy391 = 0;
sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z);
}
}
- yymsp[0].minor.yy285 = yylhsminor.yy285;
+ yymsp[0].minor.yy391 = yylhsminor.yy391;
break;
case 25: /* columnname ::= nm typetoken */
{sqlite3AddColumn(pParse,yymsp[-1].minor.yy0,yymsp[0].minor.yy0);}
break;
case 26: /* typetoken ::= */
case 65: /* conslist_opt ::= */ yytestcase(yyruleno==65);
- case 104: /* as ::= */ yytestcase(yyruleno==104);
+ case 106: /* as ::= */ yytestcase(yyruleno==106);
{yymsp[1].minor.yy0.n = 0; yymsp[1].minor.yy0.z = 0;}
break;
case 27: /* typetoken ::= typename LP signed RP */
@@ -172929,7 +176243,7 @@ static YYACTIONTYPE yy_reduce(
case 30: /* scanpt ::= */
{
assert( yyLookahead!=YYNOCODE );
- yymsp[1].minor.yy522 = yyLookaheadToken.z;
+ yymsp[1].minor.yy168 = yyLookaheadToken.z;
}
break;
case 31: /* scantok ::= */
@@ -172943,17 +176257,17 @@ static YYACTIONTYPE yy_reduce(
{pParse->constraintName = yymsp[0].minor.yy0;}
break;
case 33: /* ccons ::= DEFAULT scantok term */
-{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy528,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
+{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy454,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
break;
case 34: /* ccons ::= DEFAULT LP expr RP */
-{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy528,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);}
+{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy454,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);}
break;
case 35: /* ccons ::= DEFAULT PLUS scantok term */
-{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy528,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
+{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy454,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
break;
case 36: /* ccons ::= DEFAULT MINUS scantok term */
{
- Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy528, 0);
+ Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy454, 0);
sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);
}
break;
@@ -172968,151 +176282,151 @@ static YYACTIONTYPE yy_reduce(
}
break;
case 38: /* ccons ::= NOT NULL onconf */
-{sqlite3AddNotNull(pParse, yymsp[0].minor.yy394);}
+{sqlite3AddNotNull(pParse, yymsp[0].minor.yy144);}
break;
case 39: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */
-{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy394,yymsp[0].minor.yy394,yymsp[-2].minor.yy394);}
+{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy144,yymsp[0].minor.yy144,yymsp[-2].minor.yy144);}
break;
case 40: /* ccons ::= UNIQUE onconf */
-{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy394,0,0,0,0,
+{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy144,0,0,0,0,
SQLITE_IDXTYPE_UNIQUE);}
break;
case 41: /* ccons ::= CHECK LP expr RP */
-{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy528,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);}
+{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy454,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);}
break;
case 42: /* ccons ::= REFERENCES nm eidlist_opt refargs */
-{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy322,yymsp[0].minor.yy394);}
+{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy14,yymsp[0].minor.yy144);}
break;
case 43: /* ccons ::= defer_subclause */
-{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy394);}
+{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy144);}
break;
case 44: /* ccons ::= COLLATE ID|STRING */
{sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);}
break;
case 45: /* generated ::= LP expr RP */
-{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy528,0);}
+{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy454,0);}
break;
case 46: /* generated ::= LP expr RP ID */
-{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy528,&yymsp[0].minor.yy0);}
+{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy454,&yymsp[0].minor.yy0);}
break;
case 48: /* autoinc ::= AUTOINCR */
-{yymsp[0].minor.yy394 = 1;}
+{yymsp[0].minor.yy144 = 1;}
break;
case 49: /* refargs ::= */
-{ yymsp[1].minor.yy394 = OE_None*0x0101; /* EV: R-19803-45884 */}
+{ yymsp[1].minor.yy144 = OE_None*0x0101; /* EV: R-19803-45884 */}
break;
case 50: /* refargs ::= refargs refarg */
-{ yymsp[-1].minor.yy394 = (yymsp[-1].minor.yy394 & ~yymsp[0].minor.yy231.mask) | yymsp[0].minor.yy231.value; }
+{ yymsp[-1].minor.yy144 = (yymsp[-1].minor.yy144 & ~yymsp[0].minor.yy383.mask) | yymsp[0].minor.yy383.value; }
break;
case 51: /* refarg ::= MATCH nm */
-{ yymsp[-1].minor.yy231.value = 0; yymsp[-1].minor.yy231.mask = 0x000000; }
+{ yymsp[-1].minor.yy383.value = 0; yymsp[-1].minor.yy383.mask = 0x000000; }
break;
case 52: /* refarg ::= ON INSERT refact */
-{ yymsp[-2].minor.yy231.value = 0; yymsp[-2].minor.yy231.mask = 0x000000; }
+{ yymsp[-2].minor.yy383.value = 0; yymsp[-2].minor.yy383.mask = 0x000000; }
break;
case 53: /* refarg ::= ON DELETE refact */
-{ yymsp[-2].minor.yy231.value = yymsp[0].minor.yy394; yymsp[-2].minor.yy231.mask = 0x0000ff; }
+{ yymsp[-2].minor.yy383.value = yymsp[0].minor.yy144; yymsp[-2].minor.yy383.mask = 0x0000ff; }
break;
case 54: /* refarg ::= ON UPDATE refact */
-{ yymsp[-2].minor.yy231.value = yymsp[0].minor.yy394<<8; yymsp[-2].minor.yy231.mask = 0x00ff00; }
+{ yymsp[-2].minor.yy383.value = yymsp[0].minor.yy144<<8; yymsp[-2].minor.yy383.mask = 0x00ff00; }
break;
case 55: /* refact ::= SET NULL */
-{ yymsp[-1].minor.yy394 = OE_SetNull; /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy144 = OE_SetNull; /* EV: R-33326-45252 */}
break;
case 56: /* refact ::= SET DEFAULT */
-{ yymsp[-1].minor.yy394 = OE_SetDflt; /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy144 = OE_SetDflt; /* EV: R-33326-45252 */}
break;
case 57: /* refact ::= CASCADE */
-{ yymsp[0].minor.yy394 = OE_Cascade; /* EV: R-33326-45252 */}
+{ yymsp[0].minor.yy144 = OE_Cascade; /* EV: R-33326-45252 */}
break;
case 58: /* refact ::= RESTRICT */
-{ yymsp[0].minor.yy394 = OE_Restrict; /* EV: R-33326-45252 */}
+{ yymsp[0].minor.yy144 = OE_Restrict; /* EV: R-33326-45252 */}
break;
case 59: /* refact ::= NO ACTION */
-{ yymsp[-1].minor.yy394 = OE_None; /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy144 = OE_None; /* EV: R-33326-45252 */}
break;
case 60: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
-{yymsp[-2].minor.yy394 = 0;}
+{yymsp[-2].minor.yy144 = 0;}
break;
case 61: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
case 76: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==76);
- case 171: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==171);
-{yymsp[-1].minor.yy394 = yymsp[0].minor.yy394;}
+ case 173: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==173);
+{yymsp[-1].minor.yy144 = yymsp[0].minor.yy144;}
break;
case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80);
- case 215: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==215);
- case 218: /* in_op ::= NOT IN */ yytestcase(yyruleno==218);
- case 243: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==243);
-{yymsp[-1].minor.yy394 = 1;}
+ case 219: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==219);
+ case 222: /* in_op ::= NOT IN */ yytestcase(yyruleno==222);
+ case 247: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==247);
+{yymsp[-1].minor.yy144 = 1;}
break;
case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
-{yymsp[-1].minor.yy394 = 0;}
+{yymsp[-1].minor.yy144 = 0;}
break;
case 66: /* tconscomma ::= COMMA */
{pParse->constraintName.n = 0;}
break;
case 68: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
-{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy322,yymsp[0].minor.yy394,yymsp[-2].minor.yy394,0);}
+{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy14,yymsp[0].minor.yy144,yymsp[-2].minor.yy144,0);}
break;
case 69: /* tcons ::= UNIQUE LP sortlist RP onconf */
-{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy322,yymsp[0].minor.yy394,0,0,0,0,
+{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy14,yymsp[0].minor.yy144,0,0,0,0,
SQLITE_IDXTYPE_UNIQUE);}
break;
case 70: /* tcons ::= CHECK LP expr RP onconf */
-{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy528,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);}
+{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy454,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);}
break;
case 71: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
{
- sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy322, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy322, yymsp[-1].minor.yy394);
- sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy394);
+ sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy14, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[-1].minor.yy144);
+ sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy144);
}
break;
case 73: /* onconf ::= */
case 75: /* orconf ::= */ yytestcase(yyruleno==75);
-{yymsp[1].minor.yy394 = OE_Default;}
+{yymsp[1].minor.yy144 = OE_Default;}
break;
case 74: /* onconf ::= ON CONFLICT resolvetype */
-{yymsp[-2].minor.yy394 = yymsp[0].minor.yy394;}
+{yymsp[-2].minor.yy144 = yymsp[0].minor.yy144;}
break;
case 77: /* resolvetype ::= IGNORE */
-{yymsp[0].minor.yy394 = OE_Ignore;}
+{yymsp[0].minor.yy144 = OE_Ignore;}
break;
case 78: /* resolvetype ::= REPLACE */
- case 172: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==172);
-{yymsp[0].minor.yy394 = OE_Replace;}
+ case 174: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==174);
+{yymsp[0].minor.yy144 = OE_Replace;}
break;
case 79: /* cmd ::= DROP TABLE ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy131, 0, yymsp[-1].minor.yy394);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy203, 0, yymsp[-1].minor.yy144);
}
break;
case 82: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
{
- sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy322, yymsp[0].minor.yy47, yymsp[-7].minor.yy394, yymsp[-5].minor.yy394);
+ sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[0].minor.yy555, yymsp[-7].minor.yy144, yymsp[-5].minor.yy144);
}
break;
case 83: /* cmd ::= DROP VIEW ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy131, 1, yymsp[-1].minor.yy394);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy203, 1, yymsp[-1].minor.yy144);
}
break;
case 84: /* cmd ::= select */
{
SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0};
- sqlite3Select(pParse, yymsp[0].minor.yy47, &dest);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy47);
+ sqlite3Select(pParse, yymsp[0].minor.yy555, &dest);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555);
}
break;
case 85: /* select ::= WITH wqlist selectnowith */
-{yymsp[-2].minor.yy47 = attachWithToSelect(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy521);}
+{yymsp[-2].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);}
break;
case 86: /* select ::= WITH RECURSIVE wqlist selectnowith */
-{yymsp[-3].minor.yy47 = attachWithToSelect(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy521);}
+{yymsp[-3].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);}
break;
case 87: /* select ::= selectnowith */
{
- Select *p = yymsp[0].minor.yy47;
+ Select *p = yymsp[0].minor.yy555;
if( p ){
parserDoubleLinkSelect(pParse, p);
}
@@ -173120,8 +176434,8 @@ static YYACTIONTYPE yy_reduce(
break;
case 88: /* selectnowith ::= selectnowith multiselect_op oneselect */
{
- Select *pRhs = yymsp[0].minor.yy47;
- Select *pLhs = yymsp[-2].minor.yy47;
+ Select *pRhs = yymsp[0].minor.yy555;
+ Select *pLhs = yymsp[-2].minor.yy555;
if( pRhs && pRhs->pPrior ){
SrcList *pFrom;
Token x;
@@ -173131,148 +176445,145 @@ static YYACTIONTYPE yy_reduce(
pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0);
}
if( pRhs ){
- pRhs->op = (u8)yymsp[-1].minor.yy394;
+ pRhs->op = (u8)yymsp[-1].minor.yy144;
pRhs->pPrior = pLhs;
if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue;
pRhs->selFlags &= ~SF_MultiValue;
- if( yymsp[-1].minor.yy394!=TK_ALL ) pParse->hasCompound = 1;
+ if( yymsp[-1].minor.yy144!=TK_ALL ) pParse->hasCompound = 1;
}else{
sqlite3SelectDelete(pParse->db, pLhs);
}
- yymsp[-2].minor.yy47 = pRhs;
+ yymsp[-2].minor.yy555 = pRhs;
}
break;
case 89: /* multiselect_op ::= UNION */
case 91: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==91);
-{yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-OP*/}
+{yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-OP*/}
break;
case 90: /* multiselect_op ::= UNION ALL */
-{yymsp[-1].minor.yy394 = TK_ALL;}
+{yymsp[-1].minor.yy144 = TK_ALL;}
break;
case 92: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
{
- yymsp[-8].minor.yy47 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy322,yymsp[-5].minor.yy131,yymsp[-4].minor.yy528,yymsp[-3].minor.yy322,yymsp[-2].minor.yy528,yymsp[-1].minor.yy322,yymsp[-7].minor.yy394,yymsp[0].minor.yy528);
+ yymsp[-8].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy14,yymsp[-5].minor.yy203,yymsp[-4].minor.yy454,yymsp[-3].minor.yy14,yymsp[-2].minor.yy454,yymsp[-1].minor.yy14,yymsp[-7].minor.yy144,yymsp[0].minor.yy454);
}
break;
case 93: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
{
- yymsp[-9].minor.yy47 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy322,yymsp[-6].minor.yy131,yymsp[-5].minor.yy528,yymsp[-4].minor.yy322,yymsp[-3].minor.yy528,yymsp[-1].minor.yy322,yymsp[-8].minor.yy394,yymsp[0].minor.yy528);
- if( yymsp[-9].minor.yy47 ){
- yymsp[-9].minor.yy47->pWinDefn = yymsp[-2].minor.yy41;
+ yymsp[-9].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy14,yymsp[-6].minor.yy203,yymsp[-5].minor.yy454,yymsp[-4].minor.yy14,yymsp[-3].minor.yy454,yymsp[-1].minor.yy14,yymsp[-8].minor.yy144,yymsp[0].minor.yy454);
+ if( yymsp[-9].minor.yy555 ){
+ yymsp[-9].minor.yy555->pWinDefn = yymsp[-2].minor.yy211;
}else{
- sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy41);
+ sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy211);
}
}
break;
case 94: /* values ::= VALUES LP nexprlist RP */
{
- yymsp[-3].minor.yy47 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy322,0,0,0,0,0,SF_Values,0);
+ yymsp[-3].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy14,0,0,0,0,0,SF_Values,0);
}
break;
- case 95: /* values ::= values COMMA LP nexprlist RP */
+ case 95: /* oneselect ::= mvalues */
{
- Select *pRight, *pLeft = yymsp[-4].minor.yy47;
- pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy322,0,0,0,0,0,SF_Values|SF_MultiValue,0);
- if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue;
- if( pRight ){
- pRight->op = TK_ALL;
- pRight->pPrior = pLeft;
- yymsp[-4].minor.yy47 = pRight;
- }else{
- yymsp[-4].minor.yy47 = pLeft;
- }
+ sqlite3MultiValuesEnd(pParse, yymsp[0].minor.yy555);
}
break;
- case 96: /* distinct ::= DISTINCT */
-{yymsp[0].minor.yy394 = SF_Distinct;}
+ case 96: /* mvalues ::= values COMMA LP nexprlist RP */
+ case 97: /* mvalues ::= mvalues COMMA LP nexprlist RP */ yytestcase(yyruleno==97);
+{
+ yymsp[-4].minor.yy555 = sqlite3MultiValues(pParse, yymsp[-4].minor.yy555, yymsp[-1].minor.yy14);
+}
break;
- case 97: /* distinct ::= ALL */
-{yymsp[0].minor.yy394 = SF_All;}
+ case 98: /* distinct ::= DISTINCT */
+{yymsp[0].minor.yy144 = SF_Distinct;}
break;
- case 99: /* sclp ::= */
- case 132: /* orderby_opt ::= */ yytestcase(yyruleno==132);
- case 142: /* groupby_opt ::= */ yytestcase(yyruleno==142);
- case 230: /* exprlist ::= */ yytestcase(yyruleno==230);
- case 233: /* paren_exprlist ::= */ yytestcase(yyruleno==233);
- case 238: /* eidlist_opt ::= */ yytestcase(yyruleno==238);
-{yymsp[1].minor.yy322 = 0;}
+ case 99: /* distinct ::= ALL */
+{yymsp[0].minor.yy144 = SF_All;}
break;
- case 100: /* selcollist ::= sclp scanpt expr scanpt as */
+ case 101: /* sclp ::= */
+ case 134: /* orderby_opt ::= */ yytestcase(yyruleno==134);
+ case 144: /* groupby_opt ::= */ yytestcase(yyruleno==144);
+ case 234: /* exprlist ::= */ yytestcase(yyruleno==234);
+ case 237: /* paren_exprlist ::= */ yytestcase(yyruleno==237);
+ case 242: /* eidlist_opt ::= */ yytestcase(yyruleno==242);
+{yymsp[1].minor.yy14 = 0;}
+ break;
+ case 102: /* selcollist ::= sclp scanpt expr scanpt as */
{
- yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[-2].minor.yy528);
- if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[0].minor.yy0, 1);
- sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy322,yymsp[-3].minor.yy522,yymsp[-1].minor.yy522);
+ yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[-2].minor.yy454);
+ if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy14, &yymsp[0].minor.yy0, 1);
+ sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy14,yymsp[-3].minor.yy168,yymsp[-1].minor.yy168);
}
break;
- case 101: /* selcollist ::= sclp scanpt STAR */
+ case 103: /* selcollist ::= sclp scanpt STAR */
{
Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0);
sqlite3ExprSetErrorOffset(p, (int)(yymsp[0].minor.yy0.z - pParse->zTail));
- yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy322, p);
+ yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy14, p);
}
break;
- case 102: /* selcollist ::= sclp scanpt nm DOT STAR */
+ case 104: /* selcollist ::= sclp scanpt nm DOT STAR */
{
Expr *pRight, *pLeft, *pDot;
pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0);
sqlite3ExprSetErrorOffset(pRight, (int)(yymsp[0].minor.yy0.z - pParse->zTail));
pLeft = tokenExpr(pParse, TK_ID, yymsp[-2].minor.yy0);
pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
- yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, pDot);
+ yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, pDot);
}
break;
- case 103: /* as ::= AS nm */
- case 115: /* dbnm ::= DOT nm */ yytestcase(yyruleno==115);
- case 254: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==254);
- case 255: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==255);
+ case 105: /* as ::= AS nm */
+ case 117: /* dbnm ::= DOT nm */ yytestcase(yyruleno==117);
+ case 258: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==258);
+ case 259: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==259);
{yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;}
break;
- case 105: /* from ::= */
- case 108: /* stl_prefix ::= */ yytestcase(yyruleno==108);
-{yymsp[1].minor.yy131 = 0;}
+ case 107: /* from ::= */
+ case 110: /* stl_prefix ::= */ yytestcase(yyruleno==110);
+{yymsp[1].minor.yy203 = 0;}
break;
- case 106: /* from ::= FROM seltablist */
+ case 108: /* from ::= FROM seltablist */
{
- yymsp[-1].minor.yy131 = yymsp[0].minor.yy131;
- sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy131);
+ yymsp[-1].minor.yy203 = yymsp[0].minor.yy203;
+ sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy203);
}
break;
- case 107: /* stl_prefix ::= seltablist joinop */
+ case 109: /* stl_prefix ::= seltablist joinop */
{
- if( ALWAYS(yymsp[-1].minor.yy131 && yymsp[-1].minor.yy131->nSrc>0) ) yymsp[-1].minor.yy131->a[yymsp[-1].minor.yy131->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy394;
+ if( ALWAYS(yymsp[-1].minor.yy203 && yymsp[-1].minor.yy203->nSrc>0) ) yymsp[-1].minor.yy203->a[yymsp[-1].minor.yy203->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy144;
}
break;
- case 109: /* seltablist ::= stl_prefix nm dbnm as on_using */
+ case 111: /* seltablist ::= stl_prefix nm dbnm as on_using */
{
- yymsp[-4].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy131,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561);
+ yymsp[-4].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy203,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269);
}
break;
- case 110: /* seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
+ case 112: /* seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
{
- yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy561);
- sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy131, &yymsp[-1].minor.yy0);
+ yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy269);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy203, &yymsp[-1].minor.yy0);
}
break;
- case 111: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
+ case 113: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
{
- yymsp[-7].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy131,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561);
- sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy131, yymsp[-3].minor.yy322);
+ yymsp[-7].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy203,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269);
+ sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy203, yymsp[-3].minor.yy14);
}
break;
- case 112: /* seltablist ::= stl_prefix LP select RP as on_using */
+ case 114: /* seltablist ::= stl_prefix LP select RP as on_using */
{
- yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy47,&yymsp[0].minor.yy561);
+ yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy555,&yymsp[0].minor.yy269);
}
break;
- case 113: /* seltablist ::= stl_prefix LP seltablist RP as on_using */
+ case 115: /* seltablist ::= stl_prefix LP seltablist RP as on_using */
{
- if( yymsp[-5].minor.yy131==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy561.pOn==0 && yymsp[0].minor.yy561.pUsing==0 ){
- yymsp[-5].minor.yy131 = yymsp[-3].minor.yy131;
- }else if( ALWAYS(yymsp[-3].minor.yy131!=0) && yymsp[-3].minor.yy131->nSrc==1 ){
- yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy561);
- if( yymsp[-5].minor.yy131 ){
- SrcItem *pNew = &yymsp[-5].minor.yy131->a[yymsp[-5].minor.yy131->nSrc-1];
- SrcItem *pOld = yymsp[-3].minor.yy131->a;
+ if( yymsp[-5].minor.yy203==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy269.pOn==0 && yymsp[0].minor.yy269.pUsing==0 ){
+ yymsp[-5].minor.yy203 = yymsp[-3].minor.yy203;
+ }else if( ALWAYS(yymsp[-3].minor.yy203!=0) && yymsp[-3].minor.yy203->nSrc==1 ){
+ yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269);
+ if( yymsp[-5].minor.yy203 ){
+ SrcItem *pNew = &yymsp[-5].minor.yy203->a[yymsp[-5].minor.yy203->nSrc-1];
+ SrcItem *pOld = yymsp[-3].minor.yy203->a;
pNew->zName = pOld->zName;
pNew->zDatabase = pOld->zDatabase;
pNew->pSelect = pOld->pSelect;
@@ -173288,153 +176599,153 @@ static YYACTIONTYPE yy_reduce(
pOld->zName = pOld->zDatabase = 0;
pOld->pSelect = 0;
}
- sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy131);
+ sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy203);
}else{
Select *pSubquery;
- sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy131);
- pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy131,0,0,0,0,SF_NestedFrom,0);
- yymsp[-5].minor.yy131 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy131,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy561);
+ sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy203);
+ pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy203,0,0,0,0,SF_NestedFrom,0);
+ yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy269);
}
}
break;
- case 114: /* dbnm ::= */
- case 129: /* indexed_opt ::= */ yytestcase(yyruleno==129);
+ case 116: /* dbnm ::= */
+ case 131: /* indexed_opt ::= */ yytestcase(yyruleno==131);
{yymsp[1].minor.yy0.z=0; yymsp[1].minor.yy0.n=0;}
break;
- case 116: /* fullname ::= nm */
+ case 118: /* fullname ::= nm */
{
- yylhsminor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0);
- if( IN_RENAME_OBJECT && yylhsminor.yy131 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy131->a[0].zName, &yymsp[0].minor.yy0);
+ yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0);
+ if( IN_RENAME_OBJECT && yylhsminor.yy203 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[0].minor.yy0);
}
- yymsp[0].minor.yy131 = yylhsminor.yy131;
+ yymsp[0].minor.yy203 = yylhsminor.yy203;
break;
- case 117: /* fullname ::= nm DOT nm */
+ case 119: /* fullname ::= nm DOT nm */
{
- yylhsminor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
- if( IN_RENAME_OBJECT && yylhsminor.yy131 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy131->a[0].zName, &yymsp[0].minor.yy0);
+ yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
+ if( IN_RENAME_OBJECT && yylhsminor.yy203 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[0].minor.yy0);
}
- yymsp[-2].minor.yy131 = yylhsminor.yy131;
+ yymsp[-2].minor.yy203 = yylhsminor.yy203;
break;
- case 118: /* xfullname ::= nm */
-{yymsp[0].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/}
+ case 120: /* xfullname ::= nm */
+{yymsp[0].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/}
break;
- case 119: /* xfullname ::= nm DOT nm */
-{yymsp[-2].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
+ case 121: /* xfullname ::= nm DOT nm */
+{yymsp[-2].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
- case 120: /* xfullname ::= nm DOT nm AS nm */
+ case 122: /* xfullname ::= nm DOT nm AS nm */
{
- yymsp[-4].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/
- if( yymsp[-4].minor.yy131 ) yymsp[-4].minor.yy131->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
+ yymsp[-4].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/
+ if( yymsp[-4].minor.yy203 ) yymsp[-4].minor.yy203->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
}
break;
- case 121: /* xfullname ::= nm AS nm */
+ case 123: /* xfullname ::= nm AS nm */
{
- yymsp[-2].minor.yy131 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/
- if( yymsp[-2].minor.yy131 ) yymsp[-2].minor.yy131->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
+ yymsp[-2].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/
+ if( yymsp[-2].minor.yy203 ) yymsp[-2].minor.yy203->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
}
break;
- case 122: /* joinop ::= COMMA|JOIN */
-{ yymsp[0].minor.yy394 = JT_INNER; }
+ case 124: /* joinop ::= COMMA|JOIN */
+{ yymsp[0].minor.yy144 = JT_INNER; }
break;
- case 123: /* joinop ::= JOIN_KW JOIN */
-{yymsp[-1].minor.yy394 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
+ case 125: /* joinop ::= JOIN_KW JOIN */
+{yymsp[-1].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
break;
- case 124: /* joinop ::= JOIN_KW nm JOIN */
-{yymsp[-2].minor.yy394 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
+ case 126: /* joinop ::= JOIN_KW nm JOIN */
+{yymsp[-2].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
break;
- case 125: /* joinop ::= JOIN_KW nm nm JOIN */
-{yymsp[-3].minor.yy394 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
+ case 127: /* joinop ::= JOIN_KW nm nm JOIN */
+{yymsp[-3].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
break;
- case 126: /* on_using ::= ON expr */
-{yymsp[-1].minor.yy561.pOn = yymsp[0].minor.yy528; yymsp[-1].minor.yy561.pUsing = 0;}
+ case 128: /* on_using ::= ON expr */
+{yymsp[-1].minor.yy269.pOn = yymsp[0].minor.yy454; yymsp[-1].minor.yy269.pUsing = 0;}
break;
- case 127: /* on_using ::= USING LP idlist RP */
-{yymsp[-3].minor.yy561.pOn = 0; yymsp[-3].minor.yy561.pUsing = yymsp[-1].minor.yy254;}
+ case 129: /* on_using ::= USING LP idlist RP */
+{yymsp[-3].minor.yy269.pOn = 0; yymsp[-3].minor.yy269.pUsing = yymsp[-1].minor.yy132;}
break;
- case 128: /* on_using ::= */
-{yymsp[1].minor.yy561.pOn = 0; yymsp[1].minor.yy561.pUsing = 0;}
+ case 130: /* on_using ::= */
+{yymsp[1].minor.yy269.pOn = 0; yymsp[1].minor.yy269.pUsing = 0;}
break;
- case 130: /* indexed_by ::= INDEXED BY nm */
+ case 132: /* indexed_by ::= INDEXED BY nm */
{yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;}
break;
- case 131: /* indexed_by ::= NOT INDEXED */
+ case 133: /* indexed_by ::= NOT INDEXED */
{yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;}
break;
- case 133: /* orderby_opt ::= ORDER BY sortlist */
- case 143: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==143);
-{yymsp[-2].minor.yy322 = yymsp[0].minor.yy322;}
+ case 135: /* orderby_opt ::= ORDER BY sortlist */
+ case 145: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==145);
+{yymsp[-2].minor.yy14 = yymsp[0].minor.yy14;}
break;
- case 134: /* sortlist ::= sortlist COMMA expr sortorder nulls */
+ case 136: /* sortlist ::= sortlist COMMA expr sortorder nulls */
{
- yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322,yymsp[-2].minor.yy528);
- sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy322,yymsp[-1].minor.yy394,yymsp[0].minor.yy394);
+ yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14,yymsp[-2].minor.yy454);
+ sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy14,yymsp[-1].minor.yy144,yymsp[0].minor.yy144);
}
break;
- case 135: /* sortlist ::= expr sortorder nulls */
+ case 137: /* sortlist ::= expr sortorder nulls */
{
- yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy528); /*A-overwrites-Y*/
- sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy322,yymsp[-1].minor.yy394,yymsp[0].minor.yy394);
+ yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy454); /*A-overwrites-Y*/
+ sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy14,yymsp[-1].minor.yy144,yymsp[0].minor.yy144);
}
break;
- case 136: /* sortorder ::= ASC */
-{yymsp[0].minor.yy394 = SQLITE_SO_ASC;}
+ case 138: /* sortorder ::= ASC */
+{yymsp[0].minor.yy144 = SQLITE_SO_ASC;}
break;
- case 137: /* sortorder ::= DESC */
-{yymsp[0].minor.yy394 = SQLITE_SO_DESC;}
+ case 139: /* sortorder ::= DESC */
+{yymsp[0].minor.yy144 = SQLITE_SO_DESC;}
break;
- case 138: /* sortorder ::= */
- case 141: /* nulls ::= */ yytestcase(yyruleno==141);
-{yymsp[1].minor.yy394 = SQLITE_SO_UNDEFINED;}
+ case 140: /* sortorder ::= */
+ case 143: /* nulls ::= */ yytestcase(yyruleno==143);
+{yymsp[1].minor.yy144 = SQLITE_SO_UNDEFINED;}
break;
- case 139: /* nulls ::= NULLS FIRST */
-{yymsp[-1].minor.yy394 = SQLITE_SO_ASC;}
+ case 141: /* nulls ::= NULLS FIRST */
+{yymsp[-1].minor.yy144 = SQLITE_SO_ASC;}
break;
- case 140: /* nulls ::= NULLS LAST */
-{yymsp[-1].minor.yy394 = SQLITE_SO_DESC;}
+ case 142: /* nulls ::= NULLS LAST */
+{yymsp[-1].minor.yy144 = SQLITE_SO_DESC;}
break;
- case 144: /* having_opt ::= */
- case 146: /* limit_opt ::= */ yytestcase(yyruleno==146);
- case 151: /* where_opt ::= */ yytestcase(yyruleno==151);
- case 153: /* where_opt_ret ::= */ yytestcase(yyruleno==153);
- case 228: /* case_else ::= */ yytestcase(yyruleno==228);
- case 229: /* case_operand ::= */ yytestcase(yyruleno==229);
- case 248: /* vinto ::= */ yytestcase(yyruleno==248);
-{yymsp[1].minor.yy528 = 0;}
+ case 146: /* having_opt ::= */
+ case 148: /* limit_opt ::= */ yytestcase(yyruleno==148);
+ case 153: /* where_opt ::= */ yytestcase(yyruleno==153);
+ case 155: /* where_opt_ret ::= */ yytestcase(yyruleno==155);
+ case 232: /* case_else ::= */ yytestcase(yyruleno==232);
+ case 233: /* case_operand ::= */ yytestcase(yyruleno==233);
+ case 252: /* vinto ::= */ yytestcase(yyruleno==252);
+{yymsp[1].minor.yy454 = 0;}
break;
- case 145: /* having_opt ::= HAVING expr */
- case 152: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==152);
- case 154: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==154);
- case 227: /* case_else ::= ELSE expr */ yytestcase(yyruleno==227);
- case 247: /* vinto ::= INTO expr */ yytestcase(yyruleno==247);
-{yymsp[-1].minor.yy528 = yymsp[0].minor.yy528;}
+ case 147: /* having_opt ::= HAVING expr */
+ case 154: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==154);
+ case 156: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==156);
+ case 231: /* case_else ::= ELSE expr */ yytestcase(yyruleno==231);
+ case 251: /* vinto ::= INTO expr */ yytestcase(yyruleno==251);
+{yymsp[-1].minor.yy454 = yymsp[0].minor.yy454;}
break;
- case 147: /* limit_opt ::= LIMIT expr */
-{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,0);}
+ case 149: /* limit_opt ::= LIMIT expr */
+{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy454,0);}
break;
- case 148: /* limit_opt ::= LIMIT expr OFFSET expr */
-{yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);}
+ case 150: /* limit_opt ::= LIMIT expr OFFSET expr */
+{yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);}
break;
- case 149: /* limit_opt ::= LIMIT expr COMMA expr */
-{yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy528,yymsp[-2].minor.yy528);}
+ case 151: /* limit_opt ::= LIMIT expr COMMA expr */
+{yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy454,yymsp[-2].minor.yy454);}
break;
- case 150: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
+ case 152: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
{
- sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy131, &yymsp[-1].minor.yy0);
- sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy131,yymsp[0].minor.yy528,0,0);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy203, &yymsp[-1].minor.yy0);
+ sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy203,yymsp[0].minor.yy454,0,0);
}
break;
- case 155: /* where_opt_ret ::= RETURNING selcollist */
-{sqlite3AddReturning(pParse,yymsp[0].minor.yy322); yymsp[-1].minor.yy528 = 0;}
+ case 157: /* where_opt_ret ::= RETURNING selcollist */
+{sqlite3AddReturning(pParse,yymsp[0].minor.yy14); yymsp[-1].minor.yy454 = 0;}
break;
- case 156: /* where_opt_ret ::= WHERE expr RETURNING selcollist */
-{sqlite3AddReturning(pParse,yymsp[0].minor.yy322); yymsp[-3].minor.yy528 = yymsp[-2].minor.yy528;}
+ case 158: /* where_opt_ret ::= WHERE expr RETURNING selcollist */
+{sqlite3AddReturning(pParse,yymsp[0].minor.yy14); yymsp[-3].minor.yy454 = yymsp[-2].minor.yy454;}
break;
- case 157: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
+ case 159: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
{
- sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy131, &yymsp[-4].minor.yy0);
- sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy322,"set list");
- if( yymsp[-1].minor.yy131 ){
- SrcList *pFromClause = yymsp[-1].minor.yy131;
+ sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy203, &yymsp[-4].minor.yy0);
+ sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy14,"set list");
+ if( yymsp[-1].minor.yy203 ){
+ SrcList *pFromClause = yymsp[-1].minor.yy203;
if( pFromClause->nSrc>1 ){
Select *pSubquery;
Token as;
@@ -173443,92 +176754,92 @@ static YYACTIONTYPE yy_reduce(
as.z = 0;
pFromClause = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0);
}
- yymsp[-5].minor.yy131 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy131, pFromClause);
+ yymsp[-5].minor.yy203 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy203, pFromClause);
}
- sqlite3Update(pParse,yymsp[-5].minor.yy131,yymsp[-2].minor.yy322,yymsp[0].minor.yy528,yymsp[-6].minor.yy394,0,0,0);
+ sqlite3Update(pParse,yymsp[-5].minor.yy203,yymsp[-2].minor.yy14,yymsp[0].minor.yy454,yymsp[-6].minor.yy144,0,0,0);
}
break;
- case 158: /* setlist ::= setlist COMMA nm EQ expr */
+ case 160: /* setlist ::= setlist COMMA nm EQ expr */
{
- yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[0].minor.yy528);
- sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, 1);
+ yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[0].minor.yy454);
+ sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy14, &yymsp[-2].minor.yy0, 1);
}
break;
- case 159: /* setlist ::= setlist COMMA LP idlist RP EQ expr */
+ case 161: /* setlist ::= setlist COMMA LP idlist RP EQ expr */
{
- yymsp[-6].minor.yy322 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy322, yymsp[-3].minor.yy254, yymsp[0].minor.yy528);
+ yymsp[-6].minor.yy14 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy14, yymsp[-3].minor.yy132, yymsp[0].minor.yy454);
}
break;
- case 160: /* setlist ::= nm EQ expr */
+ case 162: /* setlist ::= nm EQ expr */
{
- yylhsminor.yy322 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy528);
- sqlite3ExprListSetName(pParse, yylhsminor.yy322, &yymsp[-2].minor.yy0, 1);
+ yylhsminor.yy14 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy454);
+ sqlite3ExprListSetName(pParse, yylhsminor.yy14, &yymsp[-2].minor.yy0, 1);
}
- yymsp[-2].minor.yy322 = yylhsminor.yy322;
+ yymsp[-2].minor.yy14 = yylhsminor.yy14;
break;
- case 161: /* setlist ::= LP idlist RP EQ expr */
+ case 163: /* setlist ::= LP idlist RP EQ expr */
{
- yymsp[-4].minor.yy322 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy254, yymsp[0].minor.yy528);
+ yymsp[-4].minor.yy14 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy132, yymsp[0].minor.yy454);
}
break;
- case 162: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
+ case 164: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
{
- sqlite3Insert(pParse, yymsp[-3].minor.yy131, yymsp[-1].minor.yy47, yymsp[-2].minor.yy254, yymsp[-5].minor.yy394, yymsp[0].minor.yy444);
+ sqlite3Insert(pParse, yymsp[-3].minor.yy203, yymsp[-1].minor.yy555, yymsp[-2].minor.yy132, yymsp[-5].minor.yy144, yymsp[0].minor.yy122);
}
break;
- case 163: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
+ case 165: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
{
- sqlite3Insert(pParse, yymsp[-4].minor.yy131, 0, yymsp[-3].minor.yy254, yymsp[-6].minor.yy394, 0);
+ sqlite3Insert(pParse, yymsp[-4].minor.yy203, 0, yymsp[-3].minor.yy132, yymsp[-6].minor.yy144, 0);
}
break;
- case 164: /* upsert ::= */
-{ yymsp[1].minor.yy444 = 0; }
+ case 166: /* upsert ::= */
+{ yymsp[1].minor.yy122 = 0; }
break;
- case 165: /* upsert ::= RETURNING selcollist */
-{ yymsp[-1].minor.yy444 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy322); }
+ case 167: /* upsert ::= RETURNING selcollist */
+{ yymsp[-1].minor.yy122 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy14); }
break;
- case 166: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
-{ yymsp[-11].minor.yy444 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy322,yymsp[-6].minor.yy528,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528,yymsp[0].minor.yy444);}
+ case 168: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
+{ yymsp[-11].minor.yy122 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy14,yymsp[-6].minor.yy454,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454,yymsp[0].minor.yy122);}
break;
- case 167: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
-{ yymsp[-8].minor.yy444 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy322,yymsp[-3].minor.yy528,0,0,yymsp[0].minor.yy444); }
+ case 169: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
+{ yymsp[-8].minor.yy122 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy14,yymsp[-3].minor.yy454,0,0,yymsp[0].minor.yy122); }
break;
- case 168: /* upsert ::= ON CONFLICT DO NOTHING returning */
-{ yymsp[-4].minor.yy444 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); }
+ case 170: /* upsert ::= ON CONFLICT DO NOTHING returning */
+{ yymsp[-4].minor.yy122 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); }
break;
- case 169: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
-{ yymsp[-7].minor.yy444 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528,0);}
+ case 171: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
+{ yymsp[-7].minor.yy122 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454,0);}
break;
- case 170: /* returning ::= RETURNING selcollist */
-{sqlite3AddReturning(pParse,yymsp[0].minor.yy322);}
+ case 172: /* returning ::= RETURNING selcollist */
+{sqlite3AddReturning(pParse,yymsp[0].minor.yy14);}
break;
- case 173: /* idlist_opt ::= */
-{yymsp[1].minor.yy254 = 0;}
+ case 175: /* idlist_opt ::= */
+{yymsp[1].minor.yy132 = 0;}
break;
- case 174: /* idlist_opt ::= LP idlist RP */
-{yymsp[-2].minor.yy254 = yymsp[-1].minor.yy254;}
+ case 176: /* idlist_opt ::= LP idlist RP */
+{yymsp[-2].minor.yy132 = yymsp[-1].minor.yy132;}
break;
- case 175: /* idlist ::= idlist COMMA nm */
-{yymsp[-2].minor.yy254 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy254,&yymsp[0].minor.yy0);}
+ case 177: /* idlist ::= idlist COMMA nm */
+{yymsp[-2].minor.yy132 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy132,&yymsp[0].minor.yy0);}
break;
- case 176: /* idlist ::= nm */
-{yymsp[0].minor.yy254 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
+ case 178: /* idlist ::= nm */
+{yymsp[0].minor.yy132 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
break;
- case 177: /* expr ::= LP expr RP */
-{yymsp[-2].minor.yy528 = yymsp[-1].minor.yy528;}
+ case 179: /* expr ::= LP expr RP */
+{yymsp[-2].minor.yy454 = yymsp[-1].minor.yy454;}
break;
- case 178: /* expr ::= ID|INDEXED|JOIN_KW */
-{yymsp[0].minor.yy528=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
+ case 180: /* expr ::= ID|INDEXED|JOIN_KW */
+{yymsp[0].minor.yy454=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
- case 179: /* expr ::= nm DOT nm */
+ case 181: /* expr ::= nm DOT nm */
{
Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0);
Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0);
- yylhsminor.yy528 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
+ yylhsminor.yy454 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
}
- yymsp[-2].minor.yy528 = yylhsminor.yy528;
+ yymsp[-2].minor.yy454 = yylhsminor.yy454;
break;
- case 180: /* expr ::= nm DOT nm DOT nm */
+ case 182: /* expr ::= nm DOT nm DOT nm */
{
Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-4].minor.yy0);
Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0);
@@ -173537,27 +176848,27 @@ static YYACTIONTYPE yy_reduce(
if( IN_RENAME_OBJECT ){
sqlite3RenameTokenRemap(pParse, 0, temp1);
}
- yylhsminor.yy528 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
+ yylhsminor.yy454 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
}
- yymsp[-4].minor.yy528 = yylhsminor.yy528;
+ yymsp[-4].minor.yy454 = yylhsminor.yy454;
break;
- case 181: /* term ::= NULL|FLOAT|BLOB */
- case 182: /* term ::= STRING */ yytestcase(yyruleno==182);
-{yymsp[0].minor.yy528=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
+ case 183: /* term ::= NULL|FLOAT|BLOB */
+ case 184: /* term ::= STRING */ yytestcase(yyruleno==184);
+{yymsp[0].minor.yy454=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
- case 183: /* term ::= INTEGER */
+ case 185: /* term ::= INTEGER */
{
- yylhsminor.yy528 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
- if( yylhsminor.yy528 ) yylhsminor.yy528->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail);
+ yylhsminor.yy454 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
+ if( yylhsminor.yy454 ) yylhsminor.yy454->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail);
}
- yymsp[0].minor.yy528 = yylhsminor.yy528;
+ yymsp[0].minor.yy454 = yylhsminor.yy454;
break;
- case 184: /* expr ::= VARIABLE */
+ case 186: /* expr ::= VARIABLE */
{
if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){
u32 n = yymsp[0].minor.yy0.n;
- yymsp[0].minor.yy528 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
- sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy528, n);
+ yymsp[0].minor.yy454 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
+ sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy454, n);
}else{
/* When doing a nested parse, one can include terms in an expression
** that look like this: #1 #2 ... These terms refer to registers
@@ -173566,179 +176877,203 @@ static YYACTIONTYPE yy_reduce(
assert( t.n>=2 );
if( pParse->nested==0 ){
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t);
- yymsp[0].minor.yy528 = 0;
+ yymsp[0].minor.yy454 = 0;
}else{
- yymsp[0].minor.yy528 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
- if( yymsp[0].minor.yy528 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy528->iTable);
+ yymsp[0].minor.yy454 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
+ if( yymsp[0].minor.yy454 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy454->iTable);
}
}
}
break;
- case 185: /* expr ::= expr COLLATE ID|STRING */
+ case 187: /* expr ::= expr COLLATE ID|STRING */
{
- yymsp[-2].minor.yy528 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy528, &yymsp[0].minor.yy0, 1);
+ yymsp[-2].minor.yy454 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy454, &yymsp[0].minor.yy0, 1);
}
break;
- case 186: /* expr ::= CAST LP expr AS typetoken RP */
+ case 188: /* expr ::= CAST LP expr AS typetoken RP */
{
- yymsp[-5].minor.yy528 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
- sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy528, yymsp[-3].minor.yy528, 0);
+ yymsp[-5].minor.yy454 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
+ sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy454, yymsp[-3].minor.yy454, 0);
}
break;
- case 187: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
+ case 189: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
{
- yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy394);
+ yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy144);
}
- yymsp[-4].minor.yy528 = yylhsminor.yy528;
+ yymsp[-4].minor.yy454 = yylhsminor.yy454;
break;
- case 188: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
+ case 190: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
{
- yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
+ yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy14, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy144);
+ sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy454, yymsp[-1].minor.yy14);
}
- yymsp[-3].minor.yy528 = yylhsminor.yy528;
+ yymsp[-7].minor.yy454 = yylhsminor.yy454;
break;
- case 189: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
+ case 191: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
{
- yylhsminor.yy528 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy322, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy394);
- sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41);
+ yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
}
- yymsp[-5].minor.yy528 = yylhsminor.yy528;
+ yymsp[-3].minor.yy454 = yylhsminor.yy454;
break;
- case 190: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
+ case 192: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
{
- yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
- sqlite3WindowAttach(pParse, yylhsminor.yy528, yymsp[0].minor.yy41);
+ yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy14, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy144);
+ sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211);
}
- yymsp[-4].minor.yy528 = yylhsminor.yy528;
+ yymsp[-5].minor.yy454 = yylhsminor.yy454;
break;
- case 191: /* term ::= CTIME_KW */
+ case 193: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
{
- yylhsminor.yy528 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
+ yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy14, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy144);
+ sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211);
+ sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy454, yymsp[-2].minor.yy14);
}
- yymsp[0].minor.yy528 = yylhsminor.yy528;
+ yymsp[-8].minor.yy454 = yylhsminor.yy454;
break;
- case 192: /* expr ::= LP nexprlist COMMA expr RP */
+ case 194: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
{
- ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528);
- yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
- if( yymsp[-4].minor.yy528 ){
- yymsp[-4].minor.yy528->x.pList = pList;
+ yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
+ sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211);
+}
+ yymsp[-4].minor.yy454 = yylhsminor.yy454;
+ break;
+ case 195: /* term ::= CTIME_KW */
+{
+ yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
+}
+ yymsp[0].minor.yy454 = yylhsminor.yy454;
+ break;
+ case 196: /* expr ::= LP nexprlist COMMA expr RP */
+{
+ ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy14, yymsp[-1].minor.yy454);
+ yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
+ if( yymsp[-4].minor.yy454 ){
+ yymsp[-4].minor.yy454->x.pList = pList;
if( ALWAYS(pList->nExpr) ){
- yymsp[-4].minor.yy528->flags |= pList->a[0].pExpr->flags & EP_Propagate;
+ yymsp[-4].minor.yy454->flags |= pList->a[0].pExpr->flags & EP_Propagate;
}
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
}
break;
- case 193: /* expr ::= expr AND expr */
-{yymsp[-2].minor.yy528=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);}
+ case 197: /* expr ::= expr AND expr */
+{yymsp[-2].minor.yy454=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);}
break;
- case 194: /* expr ::= expr OR expr */
- case 195: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==195);
- case 196: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==196);
- case 197: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==197);
- case 198: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==198);
- case 199: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==199);
- case 200: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==200);
-{yymsp[-2].minor.yy528=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);}
+ case 198: /* expr ::= expr OR expr */
+ case 199: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==199);
+ case 200: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==200);
+ case 201: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==201);
+ case 202: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==202);
+ case 203: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==203);
+ case 204: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==204);
+{yymsp[-2].minor.yy454=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);}
break;
- case 201: /* likeop ::= NOT LIKE_KW|MATCH */
+ case 205: /* likeop ::= NOT LIKE_KW|MATCH */
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/}
break;
- case 202: /* expr ::= expr likeop expr */
+ case 206: /* expr ::= expr likeop expr */
{
ExprList *pList;
int bNot = yymsp[-1].minor.yy0.n & 0x80000000;
yymsp[-1].minor.yy0.n &= 0x7fffffff;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy528);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy528);
- yymsp[-2].minor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
- if( bNot ) yymsp[-2].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy528, 0);
- if( yymsp[-2].minor.yy528 ) yymsp[-2].minor.yy528->flags |= EP_InfixFunc;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy454);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy454);
+ yymsp[-2].minor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
+ if( bNot ) yymsp[-2].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy454, 0);
+ if( yymsp[-2].minor.yy454 ) yymsp[-2].minor.yy454->flags |= EP_InfixFunc;
}
break;
- case 203: /* expr ::= expr likeop expr ESCAPE expr */
+ case 207: /* expr ::= expr likeop expr ESCAPE expr */
{
ExprList *pList;
int bNot = yymsp[-3].minor.yy0.n & 0x80000000;
yymsp[-3].minor.yy0.n &= 0x7fffffff;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy528);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528);
- yymsp[-4].minor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
- if( bNot ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
- if( yymsp[-4].minor.yy528 ) yymsp[-4].minor.yy528->flags |= EP_InfixFunc;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy454);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy454);
+ yymsp[-4].minor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
+ if( bNot ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
+ if( yymsp[-4].minor.yy454 ) yymsp[-4].minor.yy454->flags |= EP_InfixFunc;
}
break;
- case 204: /* expr ::= expr ISNULL|NOTNULL */
-{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy528,0);}
+ case 208: /* expr ::= expr ISNULL|NOTNULL */
+{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy454,0);}
break;
- case 205: /* expr ::= expr NOT NULL */
-{yymsp[-2].minor.yy528 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy528,0);}
+ case 209: /* expr ::= expr NOT NULL */
+{yymsp[-2].minor.yy454 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy454,0);}
break;
- case 206: /* expr ::= expr IS expr */
+ case 210: /* expr ::= expr IS expr */
{
- yymsp[-2].minor.yy528 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy528,yymsp[0].minor.yy528);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-2].minor.yy528, TK_ISNULL);
+ yymsp[-2].minor.yy454 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-2].minor.yy454, TK_ISNULL);
}
break;
- case 207: /* expr ::= expr IS NOT expr */
+ case 211: /* expr ::= expr IS NOT expr */
{
- yymsp[-3].minor.yy528 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy528,yymsp[0].minor.yy528);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-3].minor.yy528, TK_NOTNULL);
+ yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy454,yymsp[0].minor.yy454);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-3].minor.yy454, TK_NOTNULL);
}
break;
- case 208: /* expr ::= expr IS NOT DISTINCT FROM expr */
+ case 212: /* expr ::= expr IS NOT DISTINCT FROM expr */
{
- yymsp[-5].minor.yy528 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy528,yymsp[0].minor.yy528);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-5].minor.yy528, TK_ISNULL);
+ yymsp[-5].minor.yy454 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy454,yymsp[0].minor.yy454);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-5].minor.yy454, TK_ISNULL);
}
break;
- case 209: /* expr ::= expr IS DISTINCT FROM expr */
+ case 213: /* expr ::= expr IS DISTINCT FROM expr */
{
- yymsp[-4].minor.yy528 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy528,yymsp[0].minor.yy528);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy528, yymsp[-4].minor.yy528, TK_NOTNULL);
+ yymsp[-4].minor.yy454 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy454,yymsp[0].minor.yy454);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-4].minor.yy454, TK_NOTNULL);
}
break;
- case 210: /* expr ::= NOT expr */
- case 211: /* expr ::= BITNOT expr */ yytestcase(yyruleno==211);
-{yymsp[-1].minor.yy528 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy528, 0);/*A-overwrites-B*/}
+ case 214: /* expr ::= NOT expr */
+ case 215: /* expr ::= BITNOT expr */ yytestcase(yyruleno==215);
+{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy454, 0);/*A-overwrites-B*/}
break;
- case 212: /* expr ::= PLUS|MINUS expr */
+ case 216: /* expr ::= PLUS|MINUS expr */
{
- yymsp[-1].minor.yy528 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy528, 0);
- /*A-overwrites-B*/
+ Expr *p = yymsp[0].minor.yy454;
+ u8 op = yymsp[-1].major + (TK_UPLUS-TK_PLUS);
+ assert( TK_UPLUS>TK_PLUS );
+ assert( TK_UMINUS == TK_MINUS + (TK_UPLUS - TK_PLUS) );
+ if( p && p->op==TK_UPLUS ){
+ p->op = op;
+ yymsp[-1].minor.yy454 = p;
+ }else{
+ yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, op, p, 0);
+ /*A-overwrites-B*/
+ }
}
break;
- case 213: /* expr ::= expr PTR expr */
+ case 217: /* expr ::= expr PTR expr */
{
- ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy528);
- pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy528);
- yylhsminor.yy528 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
+ ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy454);
+ pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy454);
+ yylhsminor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
}
- yymsp[-2].minor.yy528 = yylhsminor.yy528;
+ yymsp[-2].minor.yy454 = yylhsminor.yy454;
break;
- case 214: /* between_op ::= BETWEEN */
- case 217: /* in_op ::= IN */ yytestcase(yyruleno==217);
-{yymsp[0].minor.yy394 = 0;}
+ case 218: /* between_op ::= BETWEEN */
+ case 221: /* in_op ::= IN */ yytestcase(yyruleno==221);
+{yymsp[0].minor.yy144 = 0;}
break;
- case 216: /* expr ::= expr between_op expr AND expr */
+ case 220: /* expr ::= expr between_op expr AND expr */
{
- ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy528);
- yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy528, 0);
- if( yymsp[-4].minor.yy528 ){
- yymsp[-4].minor.yy528->x.pList = pList;
+ ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy454);
+ yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy454, 0);
+ if( yymsp[-4].minor.yy454 ){
+ yymsp[-4].minor.yy454->x.pList = pList;
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
- if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
+ if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
}
break;
- case 219: /* expr ::= expr in_op LP exprlist RP */
+ case 223: /* expr ::= expr in_op LP exprlist RP */
{
- if( yymsp[-1].minor.yy322==0 ){
+ if( yymsp[-1].minor.yy14==0 ){
/* Expressions of the form
**
** expr1 IN ()
@@ -173747,208 +177082,208 @@ static YYACTIONTYPE yy_reduce(
** simplify to constants 0 (false) and 1 (true), respectively,
** regardless of the value of expr1.
*/
- sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy528);
- yymsp[-4].minor.yy528 = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy394 ? "true" : "false");
- if( yymsp[-4].minor.yy528 ) sqlite3ExprIdToTrueFalse(yymsp[-4].minor.yy528);
- }else{
- Expr *pRHS = yymsp[-1].minor.yy322->a[0].pExpr;
- if( yymsp[-1].minor.yy322->nExpr==1 && sqlite3ExprIsConstant(pRHS) && yymsp[-4].minor.yy528->op!=TK_VECTOR ){
- yymsp[-1].minor.yy322->a[0].pExpr = 0;
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322);
+ sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy454);
+ yymsp[-4].minor.yy454 = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy144 ? "true" : "false");
+ if( yymsp[-4].minor.yy454 ) sqlite3ExprIdToTrueFalse(yymsp[-4].minor.yy454);
+ }else{
+ Expr *pRHS = yymsp[-1].minor.yy14->a[0].pExpr;
+ if( yymsp[-1].minor.yy14->nExpr==1 && sqlite3ExprIsConstant(pParse,pRHS) && yymsp[-4].minor.yy454->op!=TK_VECTOR ){
+ yymsp[-1].minor.yy14->a[0].pExpr = 0;
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14);
pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0);
- yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy528, pRHS);
- }else if( yymsp[-1].minor.yy322->nExpr==1 && pRHS->op==TK_SELECT ){
- yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pRHS->x.pSelect);
+ yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy454, pRHS);
+ }else if( yymsp[-1].minor.yy14->nExpr==1 && pRHS->op==TK_SELECT ){
+ yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pRHS->x.pSelect);
pRHS->x.pSelect = 0;
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322);
- }else{
- yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0);
- if( yymsp[-4].minor.yy528==0 ){
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322);
- }else if( yymsp[-4].minor.yy528->pLeft->op==TK_VECTOR ){
- int nExpr = yymsp[-4].minor.yy528->pLeft->x.pList->nExpr;
- Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy322);
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14);
+ }else{
+ yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0);
+ if( yymsp[-4].minor.yy454==0 ){
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14);
+ }else if( yymsp[-4].minor.yy454->pLeft->op==TK_VECTOR ){
+ int nExpr = yymsp[-4].minor.yy454->pLeft->x.pList->nExpr;
+ Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy14);
if( pSelectRHS ){
parserDoubleLinkSelect(pParse, pSelectRHS);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pSelectRHS);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pSelectRHS);
}
}else{
- yymsp[-4].minor.yy528->x.pList = yymsp[-1].minor.yy322;
- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy528);
+ yymsp[-4].minor.yy454->x.pList = yymsp[-1].minor.yy14;
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy454);
}
}
- if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
+ if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
}
}
break;
- case 220: /* expr ::= LP select RP */
+ case 224: /* expr ::= LP select RP */
{
- yymsp[-2].minor.yy528 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy528, yymsp[-1].minor.yy47);
+ yymsp[-2].minor.yy454 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy454, yymsp[-1].minor.yy555);
}
break;
- case 221: /* expr ::= expr in_op LP select RP */
+ case 225: /* expr ::= expr in_op LP select RP */
{
- yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, yymsp[-1].minor.yy47);
- if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
+ yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, yymsp[-1].minor.yy555);
+ if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
}
break;
- case 222: /* expr ::= expr in_op nm dbnm paren_exprlist */
+ case 226: /* expr ::= expr in_op nm dbnm paren_exprlist */
{
SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);
Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0);
- if( yymsp[0].minor.yy322 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy322);
- yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy528, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy528, pSelect);
- if( yymsp[-3].minor.yy394 ) yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy528, 0);
+ if( yymsp[0].minor.yy14 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy14);
+ yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pSelect);
+ if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
}
break;
- case 223: /* expr ::= EXISTS LP select RP */
+ case 227: /* expr ::= EXISTS LP select RP */
{
Expr *p;
- p = yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
- sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy47);
+ p = yymsp[-3].minor.yy454 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
+ sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy555);
}
break;
- case 224: /* expr ::= CASE case_operand case_exprlist case_else END */
+ case 228: /* expr ::= CASE case_operand case_exprlist case_else END */
{
- yymsp[-4].minor.yy528 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy528, 0);
- if( yymsp[-4].minor.yy528 ){
- yymsp[-4].minor.yy528->x.pList = yymsp[-1].minor.yy528 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[-1].minor.yy528) : yymsp[-2].minor.yy322;
- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy528);
+ yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy454, 0);
+ if( yymsp[-4].minor.yy454 ){
+ yymsp[-4].minor.yy454->x.pList = yymsp[-1].minor.yy454 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454) : yymsp[-2].minor.yy14;
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy454);
}else{
- sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy322);
- sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528);
+ sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy14);
+ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy454);
}
}
break;
- case 225: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ case 229: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
{
- yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[-2].minor.yy528);
- yymsp[-4].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[0].minor.yy528);
+ yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[-2].minor.yy454);
+ yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[0].minor.yy454);
}
break;
- case 226: /* case_exprlist ::= WHEN expr THEN expr */
+ case 230: /* case_exprlist ::= WHEN expr THEN expr */
{
- yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy528);
- yymsp[-3].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy322, yymsp[0].minor.yy528);
+ yymsp[-3].minor.yy14 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454);
+ yymsp[-3].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy14, yymsp[0].minor.yy454);
}
break;
- case 231: /* nexprlist ::= nexprlist COMMA expr */
-{yymsp[-2].minor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[0].minor.yy528);}
+ case 235: /* nexprlist ::= nexprlist COMMA expr */
+{yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[0].minor.yy454);}
break;
- case 232: /* nexprlist ::= expr */
-{yymsp[0].minor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy528); /*A-overwrites-Y*/}
+ case 236: /* nexprlist ::= expr */
+{yymsp[0].minor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy454); /*A-overwrites-Y*/}
break;
- case 234: /* paren_exprlist ::= LP exprlist RP */
- case 239: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==239);
-{yymsp[-2].minor.yy322 = yymsp[-1].minor.yy322;}
+ case 238: /* paren_exprlist ::= LP exprlist RP */
+ case 243: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==243);
+{yymsp[-2].minor.yy14 = yymsp[-1].minor.yy14;}
break;
- case 235: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ case 239: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
{
sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
- sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy322, yymsp[-10].minor.yy394,
- &yymsp[-11].minor.yy0, yymsp[0].minor.yy528, SQLITE_SO_ASC, yymsp[-8].minor.yy394, SQLITE_IDXTYPE_APPDEF);
+ sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy14, yymsp[-10].minor.yy144,
+ &yymsp[-11].minor.yy0, yymsp[0].minor.yy454, SQLITE_SO_ASC, yymsp[-8].minor.yy144, SQLITE_IDXTYPE_APPDEF);
if( IN_RENAME_OBJECT && pParse->pNewIndex ){
sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0);
}
}
break;
- case 236: /* uniqueflag ::= UNIQUE */
- case 278: /* raisetype ::= ABORT */ yytestcase(yyruleno==278);
-{yymsp[0].minor.yy394 = OE_Abort;}
+ case 240: /* uniqueflag ::= UNIQUE */
+ case 282: /* raisetype ::= ABORT */ yytestcase(yyruleno==282);
+{yymsp[0].minor.yy144 = OE_Abort;}
break;
- case 237: /* uniqueflag ::= */
-{yymsp[1].minor.yy394 = OE_None;}
+ case 241: /* uniqueflag ::= */
+{yymsp[1].minor.yy144 = OE_None;}
break;
- case 240: /* eidlist ::= eidlist COMMA nm collate sortorder */
+ case 244: /* eidlist ::= eidlist COMMA nm collate sortorder */
{
- yymsp[-4].minor.yy322 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy322, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394);
+ yymsp[-4].minor.yy14 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy14, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy144, yymsp[0].minor.yy144);
}
break;
- case 241: /* eidlist ::= nm collate sortorder */
+ case 245: /* eidlist ::= nm collate sortorder */
{
- yymsp[-2].minor.yy322 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy394, yymsp[0].minor.yy394); /*A-overwrites-Y*/
+ yymsp[-2].minor.yy14 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy144, yymsp[0].minor.yy144); /*A-overwrites-Y*/
}
break;
- case 244: /* cmd ::= DROP INDEX ifexists fullname */
-{sqlite3DropIndex(pParse, yymsp[0].minor.yy131, yymsp[-1].minor.yy394);}
+ case 248: /* cmd ::= DROP INDEX ifexists fullname */
+{sqlite3DropIndex(pParse, yymsp[0].minor.yy203, yymsp[-1].minor.yy144);}
break;
- case 245: /* cmd ::= VACUUM vinto */
-{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy528);}
+ case 249: /* cmd ::= VACUUM vinto */
+{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy454);}
break;
- case 246: /* cmd ::= VACUUM nm vinto */
-{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy528);}
+ case 250: /* cmd ::= VACUUM nm vinto */
+{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy454);}
break;
- case 249: /* cmd ::= PRAGMA nm dbnm */
+ case 253: /* cmd ::= PRAGMA nm dbnm */
{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
break;
- case 250: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
+ case 254: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);}
break;
- case 251: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ case 255: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);}
break;
- case 252: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
+ case 256: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);}
break;
- case 253: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ case 257: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);}
break;
- case 256: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ case 260: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
{
Token all;
all.z = yymsp[-3].minor.yy0.z;
all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n;
- sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy33, &all);
+ sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy427, &all);
}
break;
- case 257: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ case 261: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
{
- sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy394, yymsp[-4].minor.yy180.a, yymsp[-4].minor.yy180.b, yymsp[-2].minor.yy131, yymsp[0].minor.yy528, yymsp[-10].minor.yy394, yymsp[-8].minor.yy394);
+ sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy144, yymsp[-4].minor.yy286.a, yymsp[-4].minor.yy286.b, yymsp[-2].minor.yy203, yymsp[0].minor.yy454, yymsp[-10].minor.yy144, yymsp[-8].minor.yy144);
yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/
}
break;
- case 258: /* trigger_time ::= BEFORE|AFTER */
-{ yymsp[0].minor.yy394 = yymsp[0].major; /*A-overwrites-X*/ }
+ case 262: /* trigger_time ::= BEFORE|AFTER */
+{ yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-X*/ }
break;
- case 259: /* trigger_time ::= INSTEAD OF */
-{ yymsp[-1].minor.yy394 = TK_INSTEAD;}
+ case 263: /* trigger_time ::= INSTEAD OF */
+{ yymsp[-1].minor.yy144 = TK_INSTEAD;}
break;
- case 260: /* trigger_time ::= */
-{ yymsp[1].minor.yy394 = TK_BEFORE; }
+ case 264: /* trigger_time ::= */
+{ yymsp[1].minor.yy144 = TK_BEFORE; }
break;
- case 261: /* trigger_event ::= DELETE|INSERT */
- case 262: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==262);
-{yymsp[0].minor.yy180.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy180.b = 0;}
+ case 265: /* trigger_event ::= DELETE|INSERT */
+ case 266: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==266);
+{yymsp[0].minor.yy286.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy286.b = 0;}
break;
- case 263: /* trigger_event ::= UPDATE OF idlist */
-{yymsp[-2].minor.yy180.a = TK_UPDATE; yymsp[-2].minor.yy180.b = yymsp[0].minor.yy254;}
+ case 267: /* trigger_event ::= UPDATE OF idlist */
+{yymsp[-2].minor.yy286.a = TK_UPDATE; yymsp[-2].minor.yy286.b = yymsp[0].minor.yy132;}
break;
- case 264: /* when_clause ::= */
- case 283: /* key_opt ::= */ yytestcase(yyruleno==283);
-{ yymsp[1].minor.yy528 = 0; }
+ case 268: /* when_clause ::= */
+ case 287: /* key_opt ::= */ yytestcase(yyruleno==287);
+{ yymsp[1].minor.yy454 = 0; }
break;
- case 265: /* when_clause ::= WHEN expr */
- case 284: /* key_opt ::= KEY expr */ yytestcase(yyruleno==284);
-{ yymsp[-1].minor.yy528 = yymsp[0].minor.yy528; }
+ case 269: /* when_clause ::= WHEN expr */
+ case 288: /* key_opt ::= KEY expr */ yytestcase(yyruleno==288);
+{ yymsp[-1].minor.yy454 = yymsp[0].minor.yy454; }
break;
- case 266: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ case 270: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
{
- assert( yymsp[-2].minor.yy33!=0 );
- yymsp[-2].minor.yy33->pLast->pNext = yymsp[-1].minor.yy33;
- yymsp[-2].minor.yy33->pLast = yymsp[-1].minor.yy33;
+ assert( yymsp[-2].minor.yy427!=0 );
+ yymsp[-2].minor.yy427->pLast->pNext = yymsp[-1].minor.yy427;
+ yymsp[-2].minor.yy427->pLast = yymsp[-1].minor.yy427;
}
break;
- case 267: /* trigger_cmd_list ::= trigger_cmd SEMI */
+ case 271: /* trigger_cmd_list ::= trigger_cmd SEMI */
{
- assert( yymsp[-1].minor.yy33!=0 );
- yymsp[-1].minor.yy33->pLast = yymsp[-1].minor.yy33;
+ assert( yymsp[-1].minor.yy427!=0 );
+ yymsp[-1].minor.yy427->pLast = yymsp[-1].minor.yy427;
}
break;
- case 268: /* trnm ::= nm DOT nm */
+ case 272: /* trnm ::= nm DOT nm */
{
yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;
sqlite3ErrorMsg(pParse,
@@ -173956,367 +177291,377 @@ static YYACTIONTYPE yy_reduce(
"statements within triggers");
}
break;
- case 269: /* tridxby ::= INDEXED BY nm */
+ case 273: /* tridxby ::= INDEXED BY nm */
{
sqlite3ErrorMsg(pParse,
"the INDEXED BY clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
- case 270: /* tridxby ::= NOT INDEXED */
+ case 274: /* tridxby ::= NOT INDEXED */
{
sqlite3ErrorMsg(pParse,
"the NOT INDEXED clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
- case 271: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
-{yylhsminor.yy33 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy131, yymsp[-3].minor.yy322, yymsp[-1].minor.yy528, yymsp[-7].minor.yy394, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy522);}
- yymsp[-8].minor.yy33 = yylhsminor.yy33;
+ case 275: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
+{yylhsminor.yy427 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy203, yymsp[-3].minor.yy14, yymsp[-1].minor.yy454, yymsp[-7].minor.yy144, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy168);}
+ yymsp[-8].minor.yy427 = yylhsminor.yy427;
break;
- case 272: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ case 276: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
{
- yylhsminor.yy33 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy254,yymsp[-2].minor.yy47,yymsp[-6].minor.yy394,yymsp[-1].minor.yy444,yymsp[-7].minor.yy522,yymsp[0].minor.yy522);/*yylhsminor.yy33-overwrites-yymsp[-6].minor.yy394*/
+ yylhsminor.yy427 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy132,yymsp[-2].minor.yy555,yymsp[-6].minor.yy144,yymsp[-1].minor.yy122,yymsp[-7].minor.yy168,yymsp[0].minor.yy168);/*yylhsminor.yy427-overwrites-yymsp[-6].minor.yy144*/
}
- yymsp[-7].minor.yy33 = yylhsminor.yy33;
+ yymsp[-7].minor.yy427 = yylhsminor.yy427;
break;
- case 273: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
-{yylhsminor.yy33 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy528, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy522);}
- yymsp[-5].minor.yy33 = yylhsminor.yy33;
+ case 277: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+{yylhsminor.yy427 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy454, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy168);}
+ yymsp[-5].minor.yy427 = yylhsminor.yy427;
break;
- case 274: /* trigger_cmd ::= scanpt select scanpt */
-{yylhsminor.yy33 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy47, yymsp[-2].minor.yy522, yymsp[0].minor.yy522); /*yylhsminor.yy33-overwrites-yymsp[-1].minor.yy47*/}
- yymsp[-2].minor.yy33 = yylhsminor.yy33;
+ case 278: /* trigger_cmd ::= scanpt select scanpt */
+{yylhsminor.yy427 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy555, yymsp[-2].minor.yy168, yymsp[0].minor.yy168); /*yylhsminor.yy427-overwrites-yymsp[-1].minor.yy555*/}
+ yymsp[-2].minor.yy427 = yylhsminor.yy427;
break;
- case 275: /* expr ::= RAISE LP IGNORE RP */
+ case 279: /* expr ::= RAISE LP IGNORE RP */
{
- yymsp[-3].minor.yy528 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
- if( yymsp[-3].minor.yy528 ){
- yymsp[-3].minor.yy528->affExpr = OE_Ignore;
+ yymsp[-3].minor.yy454 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
+ if( yymsp[-3].minor.yy454 ){
+ yymsp[-3].minor.yy454->affExpr = OE_Ignore;
}
}
break;
- case 276: /* expr ::= RAISE LP raisetype COMMA nm RP */
+ case 280: /* expr ::= RAISE LP raisetype COMMA nm RP */
{
- yymsp[-5].minor.yy528 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
- if( yymsp[-5].minor.yy528 ) {
- yymsp[-5].minor.yy528->affExpr = (char)yymsp[-3].minor.yy394;
+ yymsp[-5].minor.yy454 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
+ if( yymsp[-5].minor.yy454 ) {
+ yymsp[-5].minor.yy454->affExpr = (char)yymsp[-3].minor.yy144;
}
}
break;
- case 277: /* raisetype ::= ROLLBACK */
-{yymsp[0].minor.yy394 = OE_Rollback;}
+ case 281: /* raisetype ::= ROLLBACK */
+{yymsp[0].minor.yy144 = OE_Rollback;}
break;
- case 279: /* raisetype ::= FAIL */
-{yymsp[0].minor.yy394 = OE_Fail;}
+ case 283: /* raisetype ::= FAIL */
+{yymsp[0].minor.yy144 = OE_Fail;}
break;
- case 280: /* cmd ::= DROP TRIGGER ifexists fullname */
+ case 284: /* cmd ::= DROP TRIGGER ifexists fullname */
{
- sqlite3DropTrigger(pParse,yymsp[0].minor.yy131,yymsp[-1].minor.yy394);
+ sqlite3DropTrigger(pParse,yymsp[0].minor.yy203,yymsp[-1].minor.yy144);
}
break;
- case 281: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ case 285: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
{
- sqlite3Attach(pParse, yymsp[-3].minor.yy528, yymsp[-1].minor.yy528, yymsp[0].minor.yy528);
+ sqlite3Attach(pParse, yymsp[-3].minor.yy454, yymsp[-1].minor.yy454, yymsp[0].minor.yy454);
}
break;
- case 282: /* cmd ::= DETACH database_kw_opt expr */
+ case 286: /* cmd ::= DETACH database_kw_opt expr */
{
- sqlite3Detach(pParse, yymsp[0].minor.yy528);
+ sqlite3Detach(pParse, yymsp[0].minor.yy454);
}
break;
- case 285: /* cmd ::= REINDEX */
+ case 289: /* cmd ::= REINDEX */
{sqlite3Reindex(pParse, 0, 0);}
break;
- case 286: /* cmd ::= REINDEX nm dbnm */
+ case 290: /* cmd ::= REINDEX nm dbnm */
{sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 287: /* cmd ::= ANALYZE */
+ case 291: /* cmd ::= ANALYZE */
{sqlite3Analyze(pParse, 0, 0);}
break;
- case 288: /* cmd ::= ANALYZE nm dbnm */
+ case 292: /* cmd ::= ANALYZE nm dbnm */
{sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 289: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
+ case 293: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
{
- sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy131,&yymsp[0].minor.yy0);
+ sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy203,&yymsp[0].minor.yy0);
}
break;
- case 290: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ case 294: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
{
yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n;
sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0);
}
break;
- case 291: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
+ case 295: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
{
- sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy131, &yymsp[0].minor.yy0);
+ sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy203, &yymsp[0].minor.yy0);
}
break;
- case 292: /* add_column_fullname ::= fullname */
+ case 296: /* add_column_fullname ::= fullname */
{
disableLookaside(pParse);
- sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy131);
+ sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy203);
}
break;
- case 293: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ case 297: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
{
- sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy131, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
+ sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy203, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
}
break;
- case 294: /* cmd ::= create_vtab */
+ case 298: /* cmd ::= create_vtab */
{sqlite3VtabFinishParse(pParse,0);}
break;
- case 295: /* cmd ::= create_vtab LP vtabarglist RP */
+ case 299: /* cmd ::= create_vtab LP vtabarglist RP */
{sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);}
break;
- case 296: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ case 300: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
{
- sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy394);
+ sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy144);
}
break;
- case 297: /* vtabarg ::= */
+ case 301: /* vtabarg ::= */
{sqlite3VtabArgInit(pParse);}
break;
- case 298: /* vtabargtoken ::= ANY */
- case 299: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==299);
- case 300: /* lp ::= LP */ yytestcase(yyruleno==300);
+ case 302: /* vtabargtoken ::= ANY */
+ case 303: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==303);
+ case 304: /* lp ::= LP */ yytestcase(yyruleno==304);
{sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);}
break;
- case 301: /* with ::= WITH wqlist */
- case 302: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==302);
-{ sqlite3WithPush(pParse, yymsp[0].minor.yy521, 1); }
+ case 305: /* with ::= WITH wqlist */
+ case 306: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==306);
+{ sqlite3WithPush(pParse, yymsp[0].minor.yy59, 1); }
break;
- case 303: /* wqas ::= AS */
-{yymsp[0].minor.yy516 = M10d_Any;}
+ case 307: /* wqas ::= AS */
+{yymsp[0].minor.yy462 = M10d_Any;}
break;
- case 304: /* wqas ::= AS MATERIALIZED */
-{yymsp[-1].minor.yy516 = M10d_Yes;}
+ case 308: /* wqas ::= AS MATERIALIZED */
+{yymsp[-1].minor.yy462 = M10d_Yes;}
break;
- case 305: /* wqas ::= AS NOT MATERIALIZED */
-{yymsp[-2].minor.yy516 = M10d_No;}
+ case 309: /* wqas ::= AS NOT MATERIALIZED */
+{yymsp[-2].minor.yy462 = M10d_No;}
break;
- case 306: /* wqitem ::= nm eidlist_opt wqas LP select RP */
+ case 310: /* wqitem ::= withnm eidlist_opt wqas LP select RP */
{
- yymsp[-5].minor.yy385 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy322, yymsp[-1].minor.yy47, yymsp[-3].minor.yy516); /*A-overwrites-X*/
+ yymsp[-5].minor.yy67 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy14, yymsp[-1].minor.yy555, yymsp[-3].minor.yy462); /*A-overwrites-X*/
}
break;
- case 307: /* wqlist ::= wqitem */
+ case 311: /* withnm ::= nm */
+{pParse->bHasWith = 1;}
+ break;
+ case 312: /* wqlist ::= wqitem */
{
- yymsp[0].minor.yy521 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy385); /*A-overwrites-X*/
+ yymsp[0].minor.yy59 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy67); /*A-overwrites-X*/
}
break;
- case 308: /* wqlist ::= wqlist COMMA wqitem */
+ case 313: /* wqlist ::= wqlist COMMA wqitem */
{
- yymsp[-2].minor.yy521 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy521, yymsp[0].minor.yy385);
+ yymsp[-2].minor.yy59 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy59, yymsp[0].minor.yy67);
}
break;
- case 309: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ case 314: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
{
- assert( yymsp[0].minor.yy41!=0 );
- sqlite3WindowChain(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy41);
- yymsp[0].minor.yy41->pNextWin = yymsp[-2].minor.yy41;
- yylhsminor.yy41 = yymsp[0].minor.yy41;
+ assert( yymsp[0].minor.yy211!=0 );
+ sqlite3WindowChain(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy211);
+ yymsp[0].minor.yy211->pNextWin = yymsp[-2].minor.yy211;
+ yylhsminor.yy211 = yymsp[0].minor.yy211;
}
- yymsp[-2].minor.yy41 = yylhsminor.yy41;
+ yymsp[-2].minor.yy211 = yylhsminor.yy211;
break;
- case 310: /* windowdefn ::= nm AS LP window RP */
+ case 315: /* windowdefn ::= nm AS LP window RP */
{
- if( ALWAYS(yymsp[-1].minor.yy41) ){
- yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
+ if( ALWAYS(yymsp[-1].minor.yy211) ){
+ yymsp[-1].minor.yy211->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
}
- yylhsminor.yy41 = yymsp[-1].minor.yy41;
+ yylhsminor.yy211 = yymsp[-1].minor.yy211;
}
- yymsp[-4].minor.yy41 = yylhsminor.yy41;
+ yymsp[-4].minor.yy211 = yylhsminor.yy211;
break;
- case 311: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ case 316: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
{
- yymsp[-4].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, 0);
+ yymsp[-4].minor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy14, yymsp[-1].minor.yy14, 0);
}
break;
- case 312: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ case 317: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
{
- yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, yymsp[-2].minor.yy322, yymsp[-1].minor.yy322, &yymsp[-5].minor.yy0);
+ yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy14, yymsp[-1].minor.yy14, &yymsp[-5].minor.yy0);
}
- yymsp[-5].minor.yy41 = yylhsminor.yy41;
+ yymsp[-5].minor.yy211 = yylhsminor.yy211;
break;
- case 313: /* window ::= ORDER BY sortlist frame_opt */
+ case 318: /* window ::= ORDER BY sortlist frame_opt */
{
- yymsp[-3].minor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, 0);
+ yymsp[-3].minor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, yymsp[-1].minor.yy14, 0);
}
break;
- case 314: /* window ::= nm ORDER BY sortlist frame_opt */
+ case 319: /* window ::= nm ORDER BY sortlist frame_opt */
{
- yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0);
+ yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0);
}
- yymsp[-4].minor.yy41 = yylhsminor.yy41;
+ yymsp[-4].minor.yy211 = yylhsminor.yy211;
break;
- case 315: /* window ::= nm frame_opt */
+ case 320: /* window ::= nm frame_opt */
{
- yylhsminor.yy41 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy41, 0, 0, &yymsp[-1].minor.yy0);
+ yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, 0, &yymsp[-1].minor.yy0);
}
- yymsp[-1].minor.yy41 = yylhsminor.yy41;
+ yymsp[-1].minor.yy211 = yylhsminor.yy211;
break;
- case 316: /* frame_opt ::= */
+ case 321: /* frame_opt ::= */
{
- yymsp[1].minor.yy41 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
+ yymsp[1].minor.yy211 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
}
break;
- case 317: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ case 322: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
{
- yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy394, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy516);
+ yylhsminor.yy211 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy144, yymsp[-1].minor.yy509.eType, yymsp[-1].minor.yy509.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy462);
}
- yymsp[-2].minor.yy41 = yylhsminor.yy41;
+ yymsp[-2].minor.yy211 = yylhsminor.yy211;
break;
- case 318: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ case 323: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
{
- yylhsminor.yy41 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy394, yymsp[-3].minor.yy595.eType, yymsp[-3].minor.yy595.pExpr, yymsp[-1].minor.yy595.eType, yymsp[-1].minor.yy595.pExpr, yymsp[0].minor.yy516);
+ yylhsminor.yy211 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy144, yymsp[-3].minor.yy509.eType, yymsp[-3].minor.yy509.pExpr, yymsp[-1].minor.yy509.eType, yymsp[-1].minor.yy509.pExpr, yymsp[0].minor.yy462);
}
- yymsp[-5].minor.yy41 = yylhsminor.yy41;
+ yymsp[-5].minor.yy211 = yylhsminor.yy211;
break;
- case 320: /* frame_bound_s ::= frame_bound */
- case 322: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==322);
-{yylhsminor.yy595 = yymsp[0].minor.yy595;}
- yymsp[0].minor.yy595 = yylhsminor.yy595;
+ case 325: /* frame_bound_s ::= frame_bound */
+ case 327: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==327);
+{yylhsminor.yy509 = yymsp[0].minor.yy509;}
+ yymsp[0].minor.yy509 = yylhsminor.yy509;
break;
- case 321: /* frame_bound_s ::= UNBOUNDED PRECEDING */
- case 323: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==323);
- case 325: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==325);
-{yylhsminor.yy595.eType = yymsp[-1].major; yylhsminor.yy595.pExpr = 0;}
- yymsp[-1].minor.yy595 = yylhsminor.yy595;
+ case 326: /* frame_bound_s ::= UNBOUNDED PRECEDING */
+ case 328: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==328);
+ case 330: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==330);
+{yylhsminor.yy509.eType = yymsp[-1].major; yylhsminor.yy509.pExpr = 0;}
+ yymsp[-1].minor.yy509 = yylhsminor.yy509;
break;
- case 324: /* frame_bound ::= expr PRECEDING|FOLLOWING */
-{yylhsminor.yy595.eType = yymsp[0].major; yylhsminor.yy595.pExpr = yymsp[-1].minor.yy528;}
- yymsp[-1].minor.yy595 = yylhsminor.yy595;
+ case 329: /* frame_bound ::= expr PRECEDING|FOLLOWING */
+{yylhsminor.yy509.eType = yymsp[0].major; yylhsminor.yy509.pExpr = yymsp[-1].minor.yy454;}
+ yymsp[-1].minor.yy509 = yylhsminor.yy509;
break;
- case 326: /* frame_exclude_opt ::= */
-{yymsp[1].minor.yy516 = 0;}
+ case 331: /* frame_exclude_opt ::= */
+{yymsp[1].minor.yy462 = 0;}
break;
- case 327: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
-{yymsp[-1].minor.yy516 = yymsp[0].minor.yy516;}
+ case 332: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
+{yymsp[-1].minor.yy462 = yymsp[0].minor.yy462;}
break;
- case 328: /* frame_exclude ::= NO OTHERS */
- case 329: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==329);
-{yymsp[-1].minor.yy516 = yymsp[-1].major; /*A-overwrites-X*/}
+ case 333: /* frame_exclude ::= NO OTHERS */
+ case 334: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==334);
+{yymsp[-1].minor.yy462 = yymsp[-1].major; /*A-overwrites-X*/}
break;
- case 330: /* frame_exclude ::= GROUP|TIES */
-{yymsp[0].minor.yy516 = yymsp[0].major; /*A-overwrites-X*/}
+ case 335: /* frame_exclude ::= GROUP|TIES */
+{yymsp[0].minor.yy462 = yymsp[0].major; /*A-overwrites-X*/}
break;
- case 331: /* window_clause ::= WINDOW windowdefn_list */
-{ yymsp[-1].minor.yy41 = yymsp[0].minor.yy41; }
+ case 336: /* window_clause ::= WINDOW windowdefn_list */
+{ yymsp[-1].minor.yy211 = yymsp[0].minor.yy211; }
break;
- case 332: /* filter_over ::= filter_clause over_clause */
+ case 337: /* filter_over ::= filter_clause over_clause */
{
- if( yymsp[0].minor.yy41 ){
- yymsp[0].minor.yy41->pFilter = yymsp[-1].minor.yy528;
+ if( yymsp[0].minor.yy211 ){
+ yymsp[0].minor.yy211->pFilter = yymsp[-1].minor.yy454;
}else{
- sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy528);
+ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy454);
}
- yylhsminor.yy41 = yymsp[0].minor.yy41;
+ yylhsminor.yy211 = yymsp[0].minor.yy211;
}
- yymsp[-1].minor.yy41 = yylhsminor.yy41;
+ yymsp[-1].minor.yy211 = yylhsminor.yy211;
break;
- case 333: /* filter_over ::= over_clause */
+ case 338: /* filter_over ::= over_clause */
{
- yylhsminor.yy41 = yymsp[0].minor.yy41;
+ yylhsminor.yy211 = yymsp[0].minor.yy211;
}
- yymsp[0].minor.yy41 = yylhsminor.yy41;
+ yymsp[0].minor.yy211 = yylhsminor.yy211;
break;
- case 334: /* filter_over ::= filter_clause */
+ case 339: /* filter_over ::= filter_clause */
{
- yylhsminor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
- if( yylhsminor.yy41 ){
- yylhsminor.yy41->eFrmType = TK_FILTER;
- yylhsminor.yy41->pFilter = yymsp[0].minor.yy528;
+ yylhsminor.yy211 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
+ if( yylhsminor.yy211 ){
+ yylhsminor.yy211->eFrmType = TK_FILTER;
+ yylhsminor.yy211->pFilter = yymsp[0].minor.yy454;
}else{
- sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy528);
+ sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy454);
}
}
- yymsp[0].minor.yy41 = yylhsminor.yy41;
+ yymsp[0].minor.yy211 = yylhsminor.yy211;
break;
- case 335: /* over_clause ::= OVER LP window RP */
+ case 340: /* over_clause ::= OVER LP window RP */
{
- yymsp[-3].minor.yy41 = yymsp[-1].minor.yy41;
- assert( yymsp[-3].minor.yy41!=0 );
+ yymsp[-3].minor.yy211 = yymsp[-1].minor.yy211;
+ assert( yymsp[-3].minor.yy211!=0 );
}
break;
- case 336: /* over_clause ::= OVER nm */
+ case 341: /* over_clause ::= OVER nm */
{
- yymsp[-1].minor.yy41 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
- if( yymsp[-1].minor.yy41 ){
- yymsp[-1].minor.yy41->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
+ yymsp[-1].minor.yy211 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
+ if( yymsp[-1].minor.yy211 ){
+ yymsp[-1].minor.yy211->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
}
}
break;
- case 337: /* filter_clause ::= FILTER LP WHERE expr RP */
-{ yymsp[-4].minor.yy528 = yymsp[-1].minor.yy528; }
+ case 342: /* filter_clause ::= FILTER LP WHERE expr RP */
+{ yymsp[-4].minor.yy454 = yymsp[-1].minor.yy454; }
+ break;
+ case 343: /* term ::= QNUMBER */
+{
+ yylhsminor.yy454=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0);
+ sqlite3DequoteNumber(pParse, yylhsminor.yy454);
+}
+ yymsp[0].minor.yy454 = yylhsminor.yy454;
break;
default:
- /* (338) input ::= cmdlist */ yytestcase(yyruleno==338);
- /* (339) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==339);
- /* (340) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=340);
- /* (341) ecmd ::= SEMI */ yytestcase(yyruleno==341);
- /* (342) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==342);
- /* (343) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=343);
- /* (344) trans_opt ::= */ yytestcase(yyruleno==344);
- /* (345) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==345);
- /* (346) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==346);
- /* (347) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==347);
- /* (348) savepoint_opt ::= */ yytestcase(yyruleno==348);
- /* (349) cmd ::= create_table create_table_args */ yytestcase(yyruleno==349);
- /* (350) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=350);
- /* (351) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==351);
- /* (352) columnlist ::= columnname carglist */ yytestcase(yyruleno==352);
- /* (353) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==353);
- /* (354) nm ::= STRING */ yytestcase(yyruleno==354);
- /* (355) typetoken ::= typename */ yytestcase(yyruleno==355);
- /* (356) typename ::= ID|STRING */ yytestcase(yyruleno==356);
- /* (357) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=357);
- /* (358) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=358);
- /* (359) carglist ::= carglist ccons */ yytestcase(yyruleno==359);
- /* (360) carglist ::= */ yytestcase(yyruleno==360);
- /* (361) ccons ::= NULL onconf */ yytestcase(yyruleno==361);
- /* (362) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==362);
- /* (363) ccons ::= AS generated */ yytestcase(yyruleno==363);
- /* (364) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==364);
- /* (365) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==365);
- /* (366) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=366);
- /* (367) tconscomma ::= */ yytestcase(yyruleno==367);
- /* (368) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=368);
- /* (369) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=369);
- /* (370) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=370);
- /* (371) oneselect ::= values */ yytestcase(yyruleno==371);
- /* (372) sclp ::= selcollist COMMA */ yytestcase(yyruleno==372);
- /* (373) as ::= ID|STRING */ yytestcase(yyruleno==373);
- /* (374) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=374);
- /* (375) returning ::= */ yytestcase(yyruleno==375);
- /* (376) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=376);
- /* (377) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==377);
- /* (378) case_operand ::= expr */ yytestcase(yyruleno==378);
- /* (379) exprlist ::= nexprlist */ yytestcase(yyruleno==379);
- /* (380) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=380);
- /* (381) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=381);
- /* (382) nmnum ::= ON */ yytestcase(yyruleno==382);
- /* (383) nmnum ::= DELETE */ yytestcase(yyruleno==383);
- /* (384) nmnum ::= DEFAULT */ yytestcase(yyruleno==384);
- /* (385) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==385);
- /* (386) foreach_clause ::= */ yytestcase(yyruleno==386);
- /* (387) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==387);
- /* (388) trnm ::= nm */ yytestcase(yyruleno==388);
- /* (389) tridxby ::= */ yytestcase(yyruleno==389);
- /* (390) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==390);
- /* (391) database_kw_opt ::= */ yytestcase(yyruleno==391);
- /* (392) kwcolumn_opt ::= */ yytestcase(yyruleno==392);
- /* (393) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==393);
- /* (394) vtabarglist ::= vtabarg */ yytestcase(yyruleno==394);
- /* (395) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==395);
- /* (396) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==396);
- /* (397) anylist ::= */ yytestcase(yyruleno==397);
- /* (398) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==398);
- /* (399) anylist ::= anylist ANY */ yytestcase(yyruleno==399);
- /* (400) with ::= */ yytestcase(yyruleno==400);
- /* (401) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=401);
- /* (402) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=402);
+ /* (344) input ::= cmdlist */ yytestcase(yyruleno==344);
+ /* (345) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==345);
+ /* (346) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=346);
+ /* (347) ecmd ::= SEMI */ yytestcase(yyruleno==347);
+ /* (348) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==348);
+ /* (349) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=349);
+ /* (350) trans_opt ::= */ yytestcase(yyruleno==350);
+ /* (351) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==351);
+ /* (352) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==352);
+ /* (353) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==353);
+ /* (354) savepoint_opt ::= */ yytestcase(yyruleno==354);
+ /* (355) cmd ::= create_table create_table_args */ yytestcase(yyruleno==355);
+ /* (356) table_option_set ::= table_option (OPTIMIZED OUT) */ assert(yyruleno!=356);
+ /* (357) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==357);
+ /* (358) columnlist ::= columnname carglist */ yytestcase(yyruleno==358);
+ /* (359) nm ::= ID|INDEXED|JOIN_KW */ yytestcase(yyruleno==359);
+ /* (360) nm ::= STRING */ yytestcase(yyruleno==360);
+ /* (361) typetoken ::= typename */ yytestcase(yyruleno==361);
+ /* (362) typename ::= ID|STRING */ yytestcase(yyruleno==362);
+ /* (363) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=363);
+ /* (364) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=364);
+ /* (365) carglist ::= carglist ccons */ yytestcase(yyruleno==365);
+ /* (366) carglist ::= */ yytestcase(yyruleno==366);
+ /* (367) ccons ::= NULL onconf */ yytestcase(yyruleno==367);
+ /* (368) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==368);
+ /* (369) ccons ::= AS generated */ yytestcase(yyruleno==369);
+ /* (370) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==370);
+ /* (371) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==371);
+ /* (372) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=372);
+ /* (373) tconscomma ::= */ yytestcase(yyruleno==373);
+ /* (374) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=374);
+ /* (375) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=375);
+ /* (376) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=376);
+ /* (377) oneselect ::= values */ yytestcase(yyruleno==377);
+ /* (378) sclp ::= selcollist COMMA */ yytestcase(yyruleno==378);
+ /* (379) as ::= ID|STRING */ yytestcase(yyruleno==379);
+ /* (380) indexed_opt ::= indexed_by (OPTIMIZED OUT) */ assert(yyruleno!=380);
+ /* (381) returning ::= */ yytestcase(yyruleno==381);
+ /* (382) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=382);
+ /* (383) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==383);
+ /* (384) case_operand ::= expr */ yytestcase(yyruleno==384);
+ /* (385) exprlist ::= nexprlist */ yytestcase(yyruleno==385);
+ /* (386) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=386);
+ /* (387) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=387);
+ /* (388) nmnum ::= ON */ yytestcase(yyruleno==388);
+ /* (389) nmnum ::= DELETE */ yytestcase(yyruleno==389);
+ /* (390) nmnum ::= DEFAULT */ yytestcase(yyruleno==390);
+ /* (391) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==391);
+ /* (392) foreach_clause ::= */ yytestcase(yyruleno==392);
+ /* (393) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==393);
+ /* (394) trnm ::= nm */ yytestcase(yyruleno==394);
+ /* (395) tridxby ::= */ yytestcase(yyruleno==395);
+ /* (396) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==396);
+ /* (397) database_kw_opt ::= */ yytestcase(yyruleno==397);
+ /* (398) kwcolumn_opt ::= */ yytestcase(yyruleno==398);
+ /* (399) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==399);
+ /* (400) vtabarglist ::= vtabarg */ yytestcase(yyruleno==400);
+ /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==401);
+ /* (402) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==402);
+ /* (403) anylist ::= */ yytestcase(yyruleno==403);
+ /* (404) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==404);
+ /* (405) anylist ::= anylist ANY */ yytestcase(yyruleno==405);
+ /* (406) with ::= */ yytestcase(yyruleno==406);
+ /* (407) windowdefn_list ::= windowdefn (OPTIMIZED OUT) */ assert(yyruleno!=407);
+ /* (408) window ::= frame_opt (OPTIMIZED OUT) */ assert(yyruleno!=408);
break;
/********** End reduce actions ************************************************/
};
@@ -174503,19 +177848,12 @@ SQLITE_PRIVATE void sqlite3Parser(
(int)(yypParser->yytos - yypParser->yystack));
}
#endif
-#if YYSTACKDEPTH>0
if( yypParser->yytos>=yypParser->yystackEnd ){
- yyStackOverflow(yypParser);
- break;
- }
-#else
- if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){
if( yyGrowStack(yypParser) ){
yyStackOverflow(yypParser);
break;
}
}
-#endif
}
yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor sqlite3ParserCTX_PARAM);
}else if( yyact <= YY_MAX_SHIFTREDUCE ){
@@ -175586,27 +178924,58 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
*tokenType = TK_INTEGER;
#ifndef SQLITE_OMIT_HEX_INTEGER
if( z[0]=='0' && (z[1]=='x' || z[1]=='X') && sqlite3Isxdigit(z[2]) ){
- for(i=3; sqlite3Isxdigit(z[i]); i++){}
- return i;
- }
+ for(i=3; 1; i++){
+ if( sqlite3Isxdigit(z[i])==0 ){
+ if( z[i]==SQLITE_DIGIT_SEPARATOR ){
+ *tokenType = TK_QNUMBER;
+ }else{
+ break;
+ }
+ }
+ }
+ }else
#endif
- for(i=0; sqlite3Isdigit(z[i]); i++){}
+ {
+ for(i=0; 1; i++){
+ if( sqlite3Isdigit(z[i])==0 ){
+ if( z[i]==SQLITE_DIGIT_SEPARATOR ){
+ *tokenType = TK_QNUMBER;
+ }else{
+ break;
+ }
+ }
+ }
#ifndef SQLITE_OMIT_FLOATING_POINT
- if( z[i]=='.' ){
- i++;
- while( sqlite3Isdigit(z[i]) ){ i++; }
- *tokenType = TK_FLOAT;
- }
- if( (z[i]=='e' || z[i]=='E') &&
- ( sqlite3Isdigit(z[i+1])
- || ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2]))
- )
- ){
- i += 2;
- while( sqlite3Isdigit(z[i]) ){ i++; }
- *tokenType = TK_FLOAT;
- }
+ if( z[i]=='.' ){
+ if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT;
+ for(i++; 1; i++){
+ if( sqlite3Isdigit(z[i])==0 ){
+ if( z[i]==SQLITE_DIGIT_SEPARATOR ){
+ *tokenType = TK_QNUMBER;
+ }else{
+ break;
+ }
+ }
+ }
+ }
+ if( (z[i]=='e' || z[i]=='E') &&
+ ( sqlite3Isdigit(z[i+1])
+ || ((z[i+1]=='+' || z[i+1]=='-') && sqlite3Isdigit(z[i+2]))
+ )
+ ){
+ if( *tokenType==TK_INTEGER ) *tokenType = TK_FLOAT;
+ for(i+=2; 1; i++){
+ if( sqlite3Isdigit(z[i])==0 ){
+ if( z[i]==SQLITE_DIGIT_SEPARATOR ){
+ *tokenType = TK_QNUMBER;
+ }else{
+ break;
+ }
+ }
+ }
+ }
#endif
+ }
while( IdChar(z[i]) ){
*tokenType = TK_ILLEGAL;
i++;
@@ -175771,10 +179140,13 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){
if( tokenType>=TK_WINDOW ){
assert( tokenType==TK_SPACE || tokenType==TK_OVER || tokenType==TK_FILTER
|| tokenType==TK_ILLEGAL || tokenType==TK_WINDOW
+ || tokenType==TK_QNUMBER
);
#else
if( tokenType>=TK_SPACE ){
- assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL );
+ assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL
+ || tokenType==TK_QNUMBER
+ );
#endif /* SQLITE_OMIT_WINDOWFUNC */
if( AtomicLoad(&db->u1.isInterrupted) ){
pParse->rc = SQLITE_INTERRUPT;
@@ -175807,7 +179179,7 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){
assert( n==6 );
tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed);
#endif /* SQLITE_OMIT_WINDOWFUNC */
- }else{
+ }else if( tokenType!=TK_QNUMBER ){
Token x;
x.z = zSql;
x.n = n;
@@ -176440,7 +179812,9 @@ SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*);
#ifdef SQLITE_ENABLE_STMTVTAB
SQLITE_PRIVATE int sqlite3StmtVtabInit(sqlite3*);
#endif
-
+#ifdef SQLITE_EXTRA_AUTOEXT
+int SQLITE_EXTRA_AUTOEXT(sqlite3*);
+#endif
/*
** An array of pointers to extension initializer functions for
** built-in extensions.
@@ -176474,6 +179848,9 @@ static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = {
#ifdef SQLITE_ENABLE_BYTECODE_VTAB
sqlite3VdbeBytecodeVtabInit,
#endif
+#ifdef SQLITE_EXTRA_AUTOEXT
+ SQLITE_EXTRA_AUTOEXT,
+#endif
};
#ifndef SQLITE_AMALGAMATION
@@ -176548,6 +179925,32 @@ SQLITE_API char *sqlite3_temp_directory = 0;
SQLITE_API char *sqlite3_data_directory = 0;
/*
+** Determine whether or not high-precision (long double) floating point
+** math works correctly on CPU currently running.
+*/
+static SQLITE_NOINLINE int hasHighPrecisionDouble(int rc){
+ if( sizeof(LONGDOUBLE_TYPE)<=8 ){
+ /* If the size of "long double" is not more than 8, then
+ ** high-precision math is not possible. */
+ return 0;
+ }else{
+ /* Just because sizeof(long double)>8 does not mean that the underlying
+ ** hardware actually supports high-precision floating point. For example,
+ ** clearing the 0x100 bit in the floating-point control word on Intel
+ ** processors will make long double work like double, even though long
+ ** double takes up more space. The only way to determine if long double
+ ** actually works is to run an experiment. */
+ LONGDOUBLE_TYPE a, b, c;
+ rc++;
+ a = 1.0+rc*0.1;
+ b = 1.0e+18+rc*25.0;
+ c = a+b;
+ return b!=c;
+ }
+}
+
+
+/*
** Initialize SQLite.
**
** This routine must be called to initialize the memory allocation,
@@ -176742,6 +180145,12 @@ SQLITE_API int sqlite3_initialize(void){
}
#endif
+ /* Experimentally determine if high-precision floating point is
+ ** available. */
+#ifndef SQLITE_OMIT_WSD
+ sqlite3Config.bUseLongDouble = hasHighPrecisionDouble(rc);
+#endif
+
return rc;
}
@@ -177121,6 +180530,18 @@ SQLITE_API int sqlite3_config(int op, ...){
}
#endif /* SQLITE_OMIT_DESERIALIZE */
+ case SQLITE_CONFIG_ROWID_IN_VIEW: {
+ int *pVal = va_arg(ap,int*);
+#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
+ if( 0==*pVal ) sqlite3GlobalConfig.mNoVisibleRowid = TF_NoVisibleRowid;
+ if( 1==*pVal ) sqlite3GlobalConfig.mNoVisibleRowid = 0;
+ *pVal = (sqlite3GlobalConfig.mNoVisibleRowid==0);
+#else
+ *pVal = 0;
+#endif
+ break;
+ }
+
default: {
rc = SQLITE_ERROR;
break;
@@ -177312,6 +180733,10 @@ SQLITE_API int sqlite3_db_cacheflush(sqlite3 *db){
SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
va_list ap;
int rc;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
va_start(ap, op);
switch( op ){
@@ -177641,6 +181066,14 @@ static int sqlite3Close(sqlite3 *db, int forceZombie){
}
#endif
+ while( db->pDbData ){
+ DbClientData *p = db->pDbData;
+ db->pDbData = p->pNext;
+ assert( p->pData!=0 );
+ if( p->xDestructor ) p->xDestructor(p->pData);
+ sqlite3_free(p);
+ }
+
/* Convert the connection into a zombie and then close it.
*/
db->eOpenState = SQLITE_STATE_ZOMBIE;
@@ -178258,7 +181691,7 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC );
assert( SQLITE_FUNC_DIRECT==SQLITE_DIRECTONLY );
extraFlags = enc & (SQLITE_DETERMINISTIC|SQLITE_DIRECTONLY|
- SQLITE_SUBTYPE|SQLITE_INNOCUOUS);
+ SQLITE_SUBTYPE|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE);
enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY);
/* The SQLITE_INNOCUOUS flag is the same bit as SQLITE_FUNC_UNSAFE. But
@@ -178715,6 +182148,12 @@ SQLITE_API void *sqlite3_preupdate_hook(
void *pArg /* First callback argument */
){
void *pRet;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( db==0 ){
+ return 0;
+ }
+#endif
sqlite3_mutex_enter(db->mutex);
pRet = db->pPreUpdateArg;
db->xPreUpdateCallback = xCallback;
@@ -178861,7 +182300,7 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
if( eMode<SQLITE_CHECKPOINT_PASSIVE || eMode>SQLITE_CHECKPOINT_TRUNCATE ){
/* EVIDENCE-OF: R-03996-12088 The M parameter must be a valid checkpoint
** mode: */
- return SQLITE_MISUSE;
+ return SQLITE_MISUSE_BKPT;
}
sqlite3_mutex_enter(db->mutex);
@@ -180098,6 +183537,69 @@ SQLITE_API int sqlite3_collation_needed16(
}
#endif /* SQLITE_OMIT_UTF16 */
+/*
+** Find existing client data.
+*/
+SQLITE_API void *sqlite3_get_clientdata(sqlite3 *db, const char *zName){
+ DbClientData *p;
+ sqlite3_mutex_enter(db->mutex);
+ for(p=db->pDbData; p; p=p->pNext){
+ if( strcmp(p->zName, zName)==0 ){
+ void *pResult = p->pData;
+ sqlite3_mutex_leave(db->mutex);
+ return pResult;
+ }
+ }
+ sqlite3_mutex_leave(db->mutex);
+ return 0;
+}
+
+/*
+** Add new client data to a database connection.
+*/
+SQLITE_API int sqlite3_set_clientdata(
+ sqlite3 *db, /* Attach client data to this connection */
+ const char *zName, /* Name of the client data */
+ void *pData, /* The client data itself */
+ void (*xDestructor)(void*) /* Destructor */
+){
+ DbClientData *p, **pp;
+ sqlite3_mutex_enter(db->mutex);
+ pp = &db->pDbData;
+ for(p=db->pDbData; p && strcmp(p->zName,zName); p=p->pNext){
+ pp = &p->pNext;
+ }
+ if( p ){
+ assert( p->pData!=0 );
+ if( p->xDestructor ) p->xDestructor(p->pData);
+ if( pData==0 ){
+ *pp = p->pNext;
+ sqlite3_free(p);
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_OK;
+ }
+ }else if( pData==0 ){
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_OK;
+ }else{
+ size_t n = strlen(zName);
+ p = sqlite3_malloc64( sizeof(DbClientData)+n+1 );
+ if( p==0 ){
+ if( xDestructor ) xDestructor(pData);
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_NOMEM;
+ }
+ memcpy(p->zName, zName, n+1);
+ p->pNext = db->pDbData;
+ db->pDbData = p;
+ }
+ p->pData = pData;
+ p->xDestructor = xDestructor;
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_OK;
+}
+
+
#ifndef SQLITE_OMIT_DEPRECATED
/*
** This function is now an anachronism. It used to be used to recover from a
@@ -180447,6 +183949,28 @@ SQLITE_API int sqlite3_test_control(int op, ...){
}
#endif
+ /* sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b);
+ **
+ ** If b is true, then activate the SQLITE_FkNoAction setting. If b is
+ ** false then clearn that setting. If the SQLITE_FkNoAction setting is
+ ** abled, all foreign key ON DELETE and ON UPDATE actions behave as if
+ ** they were NO ACTION, regardless of how they are defined.
+ **
+ ** NB: One must usually run "PRAGMA writable_schema=RESET" after
+ ** using this test-control, before it will take full effect. failing
+ ** to reset the schema can result in some unexpected behavior.
+ */
+ case SQLITE_TESTCTRL_FK_NO_ACTION: {
+ sqlite3 *db = va_arg(ap, sqlite3*);
+ int b = va_arg(ap, int);
+ if( b ){
+ db->flags |= SQLITE_FkNoAction;
+ }else{
+ db->flags &= ~SQLITE_FkNoAction;
+ }
+ break;
+ }
+
/*
** sqlite3_test_control(BITVEC_TEST, size, program)
**
@@ -180871,11 +184395,11 @@ SQLITE_API int sqlite3_test_control(int op, ...){
** X<0 Make no changes to the bUseLongDouble. Just report value.
** X==0 Disable bUseLongDouble
** X==1 Enable bUseLongDouble
- ** X==2 Set bUseLongDouble to its default value for this platform
+ ** X>=2 Set bUseLongDouble to its default value for this platform
*/
case SQLITE_TESTCTRL_USELONGDOUBLE: {
int b = va_arg(ap, int);
- if( b==2 ) b = sizeof(LONGDOUBLE_TYPE)>8;
+ if( b>=2 ) b = hasHighPrecisionDouble(b);
if( b>=0 ) sqlite3Config.bUseLongDouble = b>0;
rc = sqlite3Config.bUseLongDouble!=0;
break;
@@ -180912,6 +184436,28 @@ SQLITE_API int sqlite3_test_control(int op, ...){
break;
}
#endif
+
+ /* sqlite3_test_control(SQLITE_TESTCTRL_JSON_SELFCHECK, &onOff);
+ **
+ ** Activate or deactivate validation of JSONB that is generated from
+ ** text. Off by default, as the validation is slow. Validation is
+ ** only available if compiled using SQLITE_DEBUG.
+ **
+ ** If onOff is initially 1, then turn it on. If onOff is initially
+ ** off, turn it off. If onOff is initially -1, then change onOff
+ ** to be the current setting.
+ */
+ case SQLITE_TESTCTRL_JSON_SELFCHECK: {
+#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD)
+ int *pOnOff = va_arg(ap, int*);
+ if( *pOnOff<0 ){
+ *pOnOff = sqlite3Config.bJsonSelfcheck;
+ }else{
+ sqlite3Config.bJsonSelfcheck = (u8)((*pOnOff)&0xff);
+ }
+#endif
+ break;
+ }
}
va_end(ap);
#endif /* SQLITE_UNTESTABLE */
@@ -181289,7 +184835,7 @@ SQLITE_API int sqlite3_compileoption_used(const char *zOptName){
int nOpt;
const char **azCompileOpt;
-#if SQLITE_ENABLE_API_ARMOR
+#ifdef SQLITE_ENABLE_API_ARMOR
if( zOptName==0 ){
(void)SQLITE_MISUSE_BKPT;
return 0;
@@ -181484,6 +185030,9 @@ SQLITE_API int sqlite3_unlock_notify(
){
int rc = SQLITE_OK;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
enterMutex();
@@ -182505,6 +186054,7 @@ struct Fts3Table {
int nPgsz; /* Page size for host database */
char *zSegmentsTbl; /* Name of %_segments table */
sqlite3_blob *pSegments; /* Blob handle open on %_segments table */
+ int iSavepoint;
/*
** The following array of hash tables is used to buffer pending index
@@ -182892,6 +186442,8 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int);
SQLITE_PRIVATE int sqlite3Fts3ExprIterate(Fts3Expr*, int (*x)(Fts3Expr*,int,void*), void*);
+SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk);
+
#endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */
#endif /* _FTSINT_H */
@@ -183248,6 +186800,7 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
zLanguageid = (p->zLanguageid ? p->zLanguageid : "__langid");
sqlite3_vtab_config(p->db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
+ sqlite3_vtab_config(p->db, SQLITE_VTAB_INNOCUOUS);
/* Create a list of user columns for the virtual table */
zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]);
@@ -186497,6 +190050,8 @@ static int fts3RenameMethod(
rc = sqlite3Fts3PendingTermsFlush(p);
}
+ p->bIgnoreSavepoint = 1;
+
if( p->zContentTbl==0 ){
fts3DbExec(&rc, db,
"ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';",
@@ -186524,6 +190079,8 @@ static int fts3RenameMethod(
"ALTER TABLE %Q.'%q_segdir' RENAME TO '%q_segdir';",
p->zDb, p->zName, zName
);
+
+ p->bIgnoreSavepoint = 0;
return rc;
}
@@ -186534,12 +190091,28 @@ static int fts3RenameMethod(
*/
static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
int rc = SQLITE_OK;
- UNUSED_PARAMETER(iSavepoint);
- assert( ((Fts3Table *)pVtab)->inTransaction );
- assert( ((Fts3Table *)pVtab)->mxSavepoint <= iSavepoint );
- TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint );
- if( ((Fts3Table *)pVtab)->bIgnoreSavepoint==0 ){
- rc = fts3SyncMethod(pVtab);
+ Fts3Table *pTab = (Fts3Table*)pVtab;
+ assert( pTab->inTransaction );
+ assert( pTab->mxSavepoint<=iSavepoint );
+ TESTONLY( pTab->mxSavepoint = iSavepoint );
+
+ if( pTab->bIgnoreSavepoint==0 ){
+ if( fts3HashCount(&pTab->aIndex[0].hPending)>0 ){
+ char *zSql = sqlite3_mprintf("INSERT INTO %Q.%Q(%Q) VALUES('flush')",
+ pTab->zDb, pTab->zName, pTab->zName
+ );
+ if( zSql ){
+ pTab->bIgnoreSavepoint = 1;
+ rc = sqlite3_exec(pTab->db, zSql, 0, 0, 0);
+ pTab->bIgnoreSavepoint = 0;
+ sqlite3_free(zSql);
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ }
+ if( rc==SQLITE_OK ){
+ pTab->iSavepoint = iSavepoint+1;
+ }
}
return rc;
}
@@ -186550,12 +190123,11 @@ static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
** This is a no-op.
*/
static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
- TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
- UNUSED_PARAMETER(iSavepoint);
- UNUSED_PARAMETER(pVtab);
- assert( p->inTransaction );
- assert( p->mxSavepoint >= iSavepoint );
- TESTONLY( p->mxSavepoint = iSavepoint-1 );
+ Fts3Table *pTab = (Fts3Table*)pVtab;
+ assert( pTab->inTransaction );
+ assert( pTab->mxSavepoint >= iSavepoint );
+ TESTONLY( pTab->mxSavepoint = iSavepoint-1 );
+ pTab->iSavepoint = iSavepoint;
return SQLITE_OK;
}
@@ -186565,11 +190137,13 @@ static int fts3ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
** Discard the contents of the pending terms table.
*/
static int fts3RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
- Fts3Table *p = (Fts3Table*)pVtab;
+ Fts3Table *pTab = (Fts3Table*)pVtab;
UNUSED_PARAMETER(iSavepoint);
- assert( p->inTransaction );
- TESTONLY( p->mxSavepoint = iSavepoint );
- sqlite3Fts3PendingTermsClear(p);
+ assert( pTab->inTransaction );
+ TESTONLY( pTab->mxSavepoint = iSavepoint );
+ if( (iSavepoint+1)<=pTab->iSavepoint ){
+ sqlite3Fts3PendingTermsClear(pTab);
+ }
return SQLITE_OK;
}
@@ -186588,8 +190162,42 @@ static int fts3ShadowName(const char *zName){
return 0;
}
+/*
+** Implementation of the xIntegrity() method on the FTS3/FTS4 virtual
+** table.
+*/
+static int fts3IntegrityMethod(
+ sqlite3_vtab *pVtab, /* The virtual table to be checked */
+ const char *zSchema, /* Name of schema in which pVtab lives */
+ const char *zTabname, /* Name of the pVTab table */
+ int isQuick, /* True if this is a quick_check */
+ char **pzErr /* Write error message here */
+){
+ Fts3Table *p = (Fts3Table*)pVtab;
+ int rc = SQLITE_OK;
+ int bOk = 0;
+
+ UNUSED_PARAMETER(isQuick);
+ rc = sqlite3Fts3IntegrityCheck(p, &bOk);
+ assert( rc!=SQLITE_CORRUPT_VTAB );
+ if( rc==SQLITE_ERROR || (rc&0xFF)==SQLITE_CORRUPT ){
+ *pzErr = sqlite3_mprintf("unable to validate the inverted index for"
+ " FTS%d table %s.%s: %s",
+ p->bFts4 ? 4 : 3, zSchema, zTabname, sqlite3_errstr(rc));
+ if( *pzErr ) rc = SQLITE_OK;
+ }else if( rc==SQLITE_OK && bOk==0 ){
+ *pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s",
+ p->bFts4 ? 4 : 3, zSchema, zTabname);
+ if( *pzErr==0 ) rc = SQLITE_NOMEM;
+ }
+ sqlite3Fts3SegmentsClose(p);
+ return rc;
+}
+
+
+
static const sqlite3_module fts3Module = {
- /* iVersion */ 3,
+ /* iVersion */ 4,
/* xCreate */ fts3CreateMethod,
/* xConnect */ fts3ConnectMethod,
/* xBestIndex */ fts3BestIndexMethod,
@@ -186613,6 +190221,7 @@ static const sqlite3_module fts3Module = {
/* xRelease */ fts3ReleaseMethod,
/* xRollbackTo */ fts3RollbackToMethod,
/* xShadowName */ fts3ShadowName,
+ /* xIntegrity */ fts3IntegrityMethod,
};
/*
@@ -189288,7 +192897,8 @@ SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
int rc; /* Return code */
@@ -192854,7 +196464,8 @@ SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash, void(*xDestr
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
int rc; /* Return code */
@@ -196195,7 +199806,6 @@ SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
rc = fts3SegmentMerge(p, p->iPrevLangid, i, FTS3_SEGCURSOR_PENDING);
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
}
- sqlite3Fts3PendingTermsClear(p);
/* Determine the auto-incr-merge setting if unknown. If enabled,
** estimate the number of leaf blocks of content to be written
@@ -196217,6 +199827,10 @@ SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
rc = sqlite3_reset(pStmt);
}
}
+
+ if( rc==SQLITE_OK ){
+ sqlite3Fts3PendingTermsClear(p);
+ }
return rc;
}
@@ -196848,6 +200462,8 @@ static int fts3AppendToNode(
blobGrowBuffer(pPrev, nTerm, &rc);
if( rc!=SQLITE_OK ) return rc;
+ assert( pPrev!=0 );
+ assert( pPrev->a!=0 );
nPrefix = fts3PrefixCompress(pPrev->a, pPrev->n, zTerm, nTerm);
nSuffix = nTerm - nPrefix;
@@ -196904,9 +200520,13 @@ static int fts3IncrmergeAppend(
nSpace += sqlite3Fts3VarintLen(nDoclist) + nDoclist;
/* If the current block is not empty, and if adding this term/doclist
- ** to the current block would make it larger than Fts3Table.nNodeSize
- ** bytes, write this block out to the database. */
- if( pLeaf->block.n>0 && (pLeaf->block.n + nSpace)>p->nNodeSize ){
+ ** to the current block would make it larger than Fts3Table.nNodeSize bytes,
+ ** and if there is still room for another leaf page, write this block out to
+ ** the database. */
+ if( pLeaf->block.n>0
+ && (pLeaf->block.n + nSpace)>p->nNodeSize
+ && pLeaf->iBlock < (pWriter->iStart + pWriter->nLeafEst)
+ ){
rc = fts3WriteSegment(p, pLeaf->iBlock, pLeaf->block.a, pLeaf->block.n);
pWriter->nWork++;
@@ -197238,7 +200858,7 @@ static int fts3IncrmergeLoad(
rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock,0);
blobGrowBuffer(&pNode->block,
MAX(nBlock, p->nNodeSize)+FTS3_NODE_PADDING, &rc
- );
+ );
if( rc==SQLITE_OK ){
memcpy(pNode->block.a, aBlock, nBlock);
pNode->block.n = nBlock;
@@ -198155,7 +201775,7 @@ static u64 fts3ChecksumIndex(
** If an error occurs (e.g. an OOM or IO error), return an SQLite error
** code. The final value of *pbOk is undefined in this case.
*/
-static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
+SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){
int rc = SQLITE_OK; /* Return code */
u64 cksum1 = 0; /* Checksum based on FTS index contents */
u64 cksum2 = 0; /* Checksum based on %_content contents */
@@ -198233,7 +201853,12 @@ static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
sqlite3_finalize(pStmt);
}
- *pbOk = (cksum1==cksum2);
+ if( rc==SQLITE_CORRUPT_VTAB ){
+ rc = SQLITE_OK;
+ *pbOk = 0;
+ }else{
+ *pbOk = (rc==SQLITE_OK && cksum1==cksum2);
+ }
return rc;
}
@@ -198273,7 +201898,7 @@ static int fts3DoIntegrityCheck(
){
int rc;
int bOk = 0;
- rc = fts3IntegrityCheck(p, &bOk);
+ rc = sqlite3Fts3IntegrityCheck(p, &bOk);
if( rc==SQLITE_OK && bOk==0 ) rc = FTS_CORRUPT_VTAB;
return rc;
}
@@ -198303,8 +201928,11 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
rc = fts3DoIncrmerge(p, &zVal[6]);
}else if( nVal>10 && 0==sqlite3_strnicmp(zVal, "automerge=", 10) ){
rc = fts3DoAutoincrmerge(p, &zVal[10]);
+ }else if( nVal==5 && 0==sqlite3_strnicmp(zVal, "flush", 5) ){
+ rc = sqlite3Fts3PendingTermsFlush(p);
+ }
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
- }else{
+ else{
int v;
if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
v = atoi(&zVal[9]);
@@ -198322,8 +201950,8 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
if( v>=4 && v<=FTS3_MERGE_COUNT && (v&1)==0 ) p->nMergeCount = v;
rc = SQLITE_OK;
}
-#endif
}
+#endif
return rc;
}
@@ -199136,7 +202764,7 @@ static void fts3SnippetDetails(
}
mCover |= mPhrase;
- for(j=0; j<pPhrase->nToken; j++){
+ for(j=0; j<pPhrase->nToken && j<pIter->nSnippet; j++){
mHighlight |= (mPos>>j);
}
@@ -201244,24 +204872,145 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){
**
******************************************************************************
**
-** This SQLite JSON functions.
+** SQLite JSON functions.
**
** This file began as an extension in ext/misc/json1.c in 2015. That
** extension proved so useful that it has now been moved into the core.
**
-** For the time being, all JSON is stored as pure text. (We might add
-** a JSONB type in the future which stores a binary encoding of JSON in
-** a BLOB, but there is no support for JSONB in the current implementation.
-** This implementation parses JSON text at 250 MB/s, so it is hard to see
-** how JSONB might improve on that.)
+** The original design stored all JSON as pure text, canonical RFC-8259.
+** Support for JSON-5 extensions was added with version 3.42.0 (2023-05-16).
+** All generated JSON text still conforms strictly to RFC-8259, but text
+** with JSON-5 extensions is accepted as input.
+**
+** Beginning with version 3.45.0 (circa 2024-01-01), these routines also
+** accept BLOB values that have JSON encoded using a binary representation
+** called "JSONB". The name JSONB comes from PostgreSQL, however the on-disk
+** format SQLite JSONB is completely different and incompatible with
+** PostgreSQL JSONB.
+**
+** Decoding and interpreting JSONB is still O(N) where N is the size of
+** the input, the same as text JSON. However, the constant of proportionality
+** for JSONB is much smaller due to faster parsing. The size of each
+** element in JSONB is encoded in its header, so there is no need to search
+** for delimiters using persnickety syntax rules. JSONB seems to be about
+** 3x faster than text JSON as a result. JSONB is also tends to be slightly
+** smaller than text JSON, by 5% or 10%, but there are corner cases where
+** JSONB can be slightly larger. So you are not far mistaken to say that
+** a JSONB blob is the same size as the equivalent RFC-8259 text.
+**
+**
+** THE JSONB ENCODING:
+**
+** Every JSON element is encoded in JSONB as a header and a payload.
+** The header is between 1 and 9 bytes in size. The payload is zero
+** or more bytes.
+**
+** The lower 4 bits of the first byte of the header determines the
+** element type:
+**
+** 0: NULL
+** 1: TRUE
+** 2: FALSE
+** 3: INT -- RFC-8259 integer literal
+** 4: INT5 -- JSON5 integer literal
+** 5: FLOAT -- RFC-8259 floating point literal
+** 6: FLOAT5 -- JSON5 floating point literal
+** 7: TEXT -- Text literal acceptable to both SQL and JSON
+** 8: TEXTJ -- Text containing RFC-8259 escapes
+** 9: TEXT5 -- Text containing JSON5 and/or RFC-8259 escapes
+** 10: TEXTRAW -- Text containing unescaped syntax characters
+** 11: ARRAY
+** 12: OBJECT
+**
+** The other three possible values (13-15) are reserved for future
+** enhancements.
+**
+** The upper 4 bits of the first byte determine the size of the header
+** and sometimes also the size of the payload. If X is the first byte
+** of the element and if X>>4 is between 0 and 11, then the payload
+** will be that many bytes in size and the header is exactly one byte
+** in size. Other four values for X>>4 (12-15) indicate that the header
+** is more than one byte in size and that the payload size is determined
+** by the remainder of the header, interpreted as a unsigned big-endian
+** integer.
+**
+** Value of X>>4 Size integer Total header size
+** ------------- -------------------- -----------------
+** 12 1 byte (0-255) 2
+** 13 2 byte (0-65535) 3
+** 14 4 byte (0-4294967295) 5
+** 15 8 byte (0-1.8e19) 9
+**
+** The payload size need not be expressed in its minimal form. For example,
+** if the payload size is 10, the size can be expressed in any of 5 different
+** ways: (1) (X>>4)==10, (2) (X>>4)==12 following by on 0x0a byte,
+** (3) (X>>4)==13 followed by 0x00 and 0x0a, (4) (X>>4)==14 followed by
+** 0x00 0x00 0x00 0x0a, or (5) (X>>4)==15 followed by 7 bytes of 0x00 and
+** a single byte of 0x0a. The shorter forms are preferred, of course, but
+** sometimes when generating JSONB, the payload size is not known in advance
+** and it is convenient to reserve sufficient header space to cover the
+** largest possible payload size and then come back later and patch up
+** the size when it becomes known, resulting in a non-minimal encoding.
+**
+** The value (X>>4)==15 is not actually used in the current implementation
+** (as SQLite is currently unable handle BLOBs larger than about 2GB)
+** but is included in the design to allow for future enhancements.
+**
+** The payload follows the header. NULL, TRUE, and FALSE have no payload and
+** their payload size must always be zero. The payload for INT, INT5,
+** FLOAT, FLOAT5, TEXT, TEXTJ, TEXT5, and TEXTROW is text. Note that the
+** "..." or '...' delimiters are omitted from the various text encodings.
+** The payload for ARRAY and OBJECT is a list of additional elements that
+** are the content for the array or object. The payload for an OBJECT
+** must be an even number of elements. The first element of each pair is
+** the label and must be of type TEXT, TEXTJ, TEXT5, or TEXTRAW.
+**
+** A valid JSONB blob consists of a single element, as described above.
+** Usually this will be an ARRAY or OBJECT element which has many more
+** elements as its content. But the overall blob is just a single element.
+**
+** Input validation for JSONB blobs simply checks that the element type
+** code is between 0 and 12 and that the total size of the element
+** (header plus payload) is the same as the size of the BLOB. If those
+** checks are true, the BLOB is assumed to be JSONB and processing continues.
+** Errors are only raised if some other miscoding is discovered during
+** processing.
+**
+** Additional information can be found in the doc/jsonb.md file of the
+** canonical SQLite source tree.
*/
#ifndef SQLITE_OMIT_JSON
/* #include "sqliteInt.h" */
+/* JSONB element types
+*/
+#define JSONB_NULL 0 /* "null" */
+#define JSONB_TRUE 1 /* "true" */
+#define JSONB_FALSE 2 /* "false" */
+#define JSONB_INT 3 /* integer acceptable to JSON and SQL */
+#define JSONB_INT5 4 /* integer in 0x000 notation */
+#define JSONB_FLOAT 5 /* float acceptable to JSON and SQL */
+#define JSONB_FLOAT5 6 /* float with JSON5 extensions */
+#define JSONB_TEXT 7 /* Text compatible with both JSON and SQL */
+#define JSONB_TEXTJ 8 /* Text with JSON escapes */
+#define JSONB_TEXT5 9 /* Text with JSON-5 escape */
+#define JSONB_TEXTRAW 10 /* SQL text that needs escaping for JSON */
+#define JSONB_ARRAY 11 /* An array */
+#define JSONB_OBJECT 12 /* An object */
+
+/* Human-readable names for the JSONB values. The index for each
+** string must correspond to the JSONB_* integer above.
+*/
+static const char * const jsonbType[] = {
+ "null", "true", "false", "integer", "integer",
+ "real", "real", "text", "text", "text",
+ "text", "array", "object", "", "", "", ""
+};
+
/*
** Growing our own isspace() routine this way is twice as fast as
** the library isspace() function, resulting in a 7% overall performance
-** increase for the parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
+** increase for the text-JSON parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
*/
static const char jsonIsSpace[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
@@ -201282,11 +205031,19 @@ static const char jsonIsSpace[] = {
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,
};
-#define fast_isspace(x) (jsonIsSpace[(unsigned char)x])
+#define jsonIsspace(x) (jsonIsSpace[(unsigned char)x])
/*
-** Characters that are special to JSON. Control charaters,
-** '"' and '\\'.
+** The set of all space characters recognized by jsonIsspace().
+** Useful as the second argument to strspn().
+*/
+static const char jsonSpaces[] = "\011\012\015\040";
+
+/*
+** Characters that are special to JSON. Control characters,
+** '"' and '\\' and '\''. Actually, '\'' is not special to
+** canonical JSON, but it is special in JSON-5, so we include
+** it in the set of special characters.
*/
static const char jsonIsOk[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -201308,22 +205065,49 @@ static const char jsonIsOk[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};
-
-#if !defined(SQLITE_DEBUG) && !defined(SQLITE_COVERAGE_TEST)
-# define VVA(X)
-#else
-# define VVA(X) X
-#endif
-
/* Objects */
+typedef struct JsonCache JsonCache;
typedef struct JsonString JsonString;
-typedef struct JsonNode JsonNode;
typedef struct JsonParse JsonParse;
-typedef struct JsonCleanup JsonCleanup;
+
+/*
+** Magic number used for the JSON parse cache in sqlite3_get_auxdata()
+*/
+#define JSON_CACHE_ID (-429938) /* Cache entry */
+#define JSON_CACHE_SIZE 4 /* Max number of cache entries */
+
+/*
+** jsonUnescapeOneChar() returns this invalid code point if it encounters
+** a syntax error.
+*/
+#define JSON_INVALID_CHAR 0x99999
+
+/* A cache mapping JSON text into JSONB blobs.
+**
+** Each cache entry is a JsonParse object with the following restrictions:
+**
+** * The bReadOnly flag must be set
+**
+** * The aBlob[] array must be owned by the JsonParse object. In other
+** words, nBlobAlloc must be non-zero.
+**
+** * eEdit and delta must be zero.
+**
+** * zJson must be an RCStr. In other words bJsonIsRCStr must be true.
+*/
+struct JsonCache {
+ sqlite3 *db; /* Database connection */
+ int nUsed; /* Number of active entries in the cache */
+ JsonParse *a[JSON_CACHE_SIZE]; /* One line for each cache entry */
+};
/* An instance of this object represents a JSON string
** under construction. Really, this is a generic string accumulator
** that can be and is used to create strings other than JSON.
+**
+** If the generated string is longer than will fit into the zSpace[] buffer,
+** then it will be an RCStr string. This aids with caching of large
+** JSON strings.
*/
struct JsonString {
sqlite3_context *pCtx; /* Function context - put error messages here */
@@ -201331,121 +205115,75 @@ struct JsonString {
u64 nAlloc; /* Bytes of storage available in zBuf[] */
u64 nUsed; /* Bytes of zBuf[] currently used */
u8 bStatic; /* True if zBuf is static space */
- u8 bErr; /* True if an error has been encountered */
+ u8 eErr; /* True if an error has been encountered */
char zSpace[100]; /* Initial static space */
};
-/* A deferred cleanup task. A list of JsonCleanup objects might be
-** run when the JsonParse object is destroyed.
-*/
-struct JsonCleanup {
- JsonCleanup *pJCNext; /* Next in a list */
- void (*xOp)(void*); /* Routine to run */
- void *pArg; /* Argument to xOp() */
-};
+/* Allowed values for JsonString.eErr */
+#define JSTRING_OOM 0x01 /* Out of memory */
+#define JSTRING_MALFORMED 0x02 /* Malformed JSONB */
+#define JSTRING_ERR 0x04 /* Error already sent to sqlite3_result */
-/* JSON type values
+/* The "subtype" set for text JSON values passed through using
+** sqlite3_result_subtype() and sqlite3_value_subtype().
*/
-#define JSON_SUBST 0 /* Special edit node. Uses u.iPrev */
-#define JSON_NULL 1
-#define JSON_TRUE 2
-#define JSON_FALSE 3
-#define JSON_INT 4
-#define JSON_REAL 5
-#define JSON_STRING 6
-#define JSON_ARRAY 7
-#define JSON_OBJECT 8
-
-/* The "subtype" set for JSON values */
#define JSON_SUBTYPE 74 /* Ascii for "J" */
/*
-** Names of the various JSON types:
-*/
-static const char * const jsonType[] = {
- "subst",
- "null", "true", "false", "integer", "real", "text", "array", "object"
-};
-
-/* Bit values for the JsonNode.jnFlag field
+** Bit values for the flags passed into various SQL function implementations
+** via the sqlite3_user_data() value.
*/
-#define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */
-#define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */
-#define JNODE_REMOVE 0x04 /* Do not output */
-#define JNODE_REPLACE 0x08 /* Target of a JSON_SUBST node */
-#define JNODE_APPEND 0x10 /* More ARRAY/OBJECT entries at u.iAppend */
-#define JNODE_LABEL 0x20 /* Is a label of an object */
-#define JNODE_JSON5 0x40 /* Node contains JSON5 enhancements */
-
-
-/* A single node of parsed JSON. An array of these nodes describes
-** a parse of JSON + edits.
-**
-** Use the json_parse() SQL function (available when compiled with
-** -DSQLITE_DEBUG) to see a dump of complete JsonParse objects, including
-** a complete listing and decoding of the array of JsonNodes.
-*/
-struct JsonNode {
- u8 eType; /* One of the JSON_ type values */
- u8 jnFlags; /* JNODE flags */
- u8 eU; /* Which union element to use */
- u32 n; /* Bytes of content for INT, REAL or STRING
- ** Number of sub-nodes for ARRAY and OBJECT
- ** Node that SUBST applies to */
- union {
- const char *zJContent; /* 1: Content for INT, REAL, and STRING */
- u32 iAppend; /* 2: More terms for ARRAY and OBJECT */
- u32 iKey; /* 3: Key for ARRAY objects in json_tree() */
- u32 iPrev; /* 4: Previous SUBST node, or 0 */
- } u;
-};
+#define JSON_JSON 0x01 /* Result is always JSON */
+#define JSON_SQL 0x02 /* Result is always SQL */
+#define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */
+#define JSON_ISSET 0x04 /* json_set(), not json_insert() */
+#define JSON_BLOB 0x08 /* Use the BLOB output format */
-/* A parsed and possibly edited JSON string. Lifecycle:
-**
-** 1. JSON comes in and is parsed into an array aNode[]. The original
-** JSON text is stored in zJson.
+/* A parsed JSON value. Lifecycle:
**
-** 2. Zero or more changes are made (via json_remove() or json_replace()
-** or similar) to the aNode[] array.
+** 1. JSON comes in and is parsed into a JSONB value in aBlob. The
+** original text is stored in zJson. This step is skipped if the
+** input is JSONB instead of text JSON.
**
-** 3. A new, edited and mimified JSON string is generated from aNode
-** and stored in zAlt. The JsonParse object always owns zAlt.
+** 2. The aBlob[] array is searched using the JSON path notation, if needed.
**
-** Step 1 always happens. Step 2 and 3 may or may not happen, depending
-** on the operation.
+** 3. Zero or more changes are made to aBlob[] (via json_remove() or
+** json_replace() or json_patch() or similar).
**
-** aNode[].u.zJContent entries typically point into zJson. Hence zJson
-** must remain valid for the lifespan of the parse. For edits,
-** aNode[].u.zJContent might point to malloced space other than zJson.
-** Entries in pClup are responsible for freeing that extra malloced space.
-**
-** When walking the parse tree in aNode[], edits are ignored if useMod is
-** false.
+** 4. New JSON text is generated from the aBlob[] for output. This step
+** is skipped if the function is one of the jsonb_* functions that
+** returns JSONB instead of text JSON.
*/
struct JsonParse {
- u32 nNode; /* Number of slots of aNode[] used */
- u32 nAlloc; /* Number of slots of aNode[] allocated */
- JsonNode *aNode; /* Array of nodes containing the parse */
- char *zJson; /* Original JSON string (before edits) */
- char *zAlt; /* Revised and/or mimified JSON */
- u32 *aUp; /* Index of parent of each node */
- JsonCleanup *pClup;/* Cleanup operations prior to freeing this object */
+ u8 *aBlob; /* JSONB representation of JSON value */
+ u32 nBlob; /* Bytes of aBlob[] actually used */
+ u32 nBlobAlloc; /* Bytes allocated to aBlob[]. 0 if aBlob is external */
+ char *zJson; /* Json text used for parsing */
+ sqlite3 *db; /* The database connection to which this object belongs */
+ int nJson; /* Length of the zJson string in bytes */
+ u32 nJPRef; /* Number of references to this object */
+ u32 iErr; /* Error location in zJson[] */
u16 iDepth; /* Nesting depth */
u8 nErr; /* Number of errors seen */
u8 oom; /* Set to true if out of memory */
u8 bJsonIsRCStr; /* True if zJson is an RCStr */
u8 hasNonstd; /* True if input uses non-standard features like JSON5 */
- u8 useMod; /* Actually use the edits contain inside aNode */
- u8 hasMod; /* aNode contains edits from the original zJson */
- u32 nJPRef; /* Number of references to this object */
- int nJson; /* Length of the zJson string in bytes */
- int nAlt; /* Length of alternative JSON string zAlt, in bytes */
- u32 iErr; /* Error location in zJson[] */
- u32 iSubst; /* Last JSON_SUBST entry in aNode[] */
- u32 iHold; /* Age of this entry in the cache for LRU replacement */
+ u8 bReadOnly; /* Do not modify. */
+ /* Search and edit information. See jsonLookupStep() */
+ u8 eEdit; /* Edit operation to apply */
+ int delta; /* Size change due to the edit */
+ u32 nIns; /* Number of bytes to insert */
+ u32 iLabel; /* Location of label if search landed on an object value */
+ u8 *aIns; /* Content to be inserted */
};
+/* Allowed values for JsonParse.eEdit */
+#define JEDIT_DEL 1 /* Delete if exists */
+#define JEDIT_REPL 2 /* Overwrite if exists */
+#define JEDIT_INS 3 /* Insert if not exists */
+#define JEDIT_SET 4 /* Insert or overwrite */
+
/*
** Maximum nesting depth of JSON for this implementation.
**
@@ -201453,15 +205191,151 @@ struct JsonParse {
** descent parser. A depth of 1000 is far deeper than any sane JSON
** should go. Historical note: This limit was 2000 prior to version 3.42.0
*/
-#define JSON_MAX_DEPTH 1000
+#ifndef SQLITE_JSON_MAX_DEPTH
+# define JSON_MAX_DEPTH 1000
+#else
+# define JSON_MAX_DEPTH SQLITE_JSON_MAX_DEPTH
+#endif
+
+/*
+** Allowed values for the flgs argument to jsonParseFuncArg();
+*/
+#define JSON_EDITABLE 0x01 /* Generate a writable JsonParse object */
+#define JSON_KEEPERROR 0x02 /* Return non-NULL even if there is an error */
+
+/**************************************************************************
+** Forward references
+**************************************************************************/
+static void jsonReturnStringAsBlob(JsonString*);
+static int jsonFuncArgMightBeBinary(sqlite3_value *pJson);
+static u32 jsonTranslateBlobToText(const JsonParse*,u32,JsonString*);
+static void jsonReturnParse(sqlite3_context*,JsonParse*);
+static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32);
+static void jsonParseFree(JsonParse*);
+static u32 jsonbPayloadSize(const JsonParse*, u32, u32*);
+static u32 jsonUnescapeOneChar(const char*, u32, u32*);
+
+/**************************************************************************
+** Utility routines for dealing with JsonCache objects
+**************************************************************************/
+
+/*
+** Free a JsonCache object.
+*/
+static void jsonCacheDelete(JsonCache *p){
+ int i;
+ for(i=0; i<p->nUsed; i++){
+ jsonParseFree(p->a[i]);
+ }
+ sqlite3DbFree(p->db, p);
+}
+static void jsonCacheDeleteGeneric(void *p){
+ jsonCacheDelete((JsonCache*)p);
+}
+
+/*
+** Insert a new entry into the cache. If the cache is full, expel
+** the least recently used entry. Return SQLITE_OK on success or a
+** result code otherwise.
+**
+** Cache entries are stored in age order, oldest first.
+*/
+static int jsonCacheInsert(
+ sqlite3_context *ctx, /* The SQL statement context holding the cache */
+ JsonParse *pParse /* The parse object to be added to the cache */
+){
+ JsonCache *p;
+
+ assert( pParse->zJson!=0 );
+ assert( pParse->bJsonIsRCStr );
+ assert( pParse->delta==0 );
+ p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID);
+ if( p==0 ){
+ sqlite3 *db = sqlite3_context_db_handle(ctx);
+ p = sqlite3DbMallocZero(db, sizeof(*p));
+ if( p==0 ) return SQLITE_NOMEM;
+ p->db = db;
+ sqlite3_set_auxdata(ctx, JSON_CACHE_ID, p, jsonCacheDeleteGeneric);
+ p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID);
+ if( p==0 ) return SQLITE_NOMEM;
+ }
+ if( p->nUsed >= JSON_CACHE_SIZE ){
+ jsonParseFree(p->a[0]);
+ memmove(p->a, &p->a[1], (JSON_CACHE_SIZE-1)*sizeof(p->a[0]));
+ p->nUsed = JSON_CACHE_SIZE-1;
+ }
+ assert( pParse->nBlobAlloc>0 );
+ pParse->eEdit = 0;
+ pParse->nJPRef++;
+ pParse->bReadOnly = 1;
+ p->a[p->nUsed] = pParse;
+ p->nUsed++;
+ return SQLITE_OK;
+}
+
+/*
+** Search for a cached translation the json text supplied by pArg. Return
+** the JsonParse object if found. Return NULL if not found.
+**
+** When a match if found, the matching entry is moved to become the
+** most-recently used entry if it isn't so already.
+**
+** The JsonParse object returned still belongs to the Cache and might
+** be deleted at any moment. If the caller whants the JsonParse to
+** linger, it needs to increment the nPJRef reference counter.
+*/
+static JsonParse *jsonCacheSearch(
+ sqlite3_context *ctx, /* The SQL statement context holding the cache */
+ sqlite3_value *pArg /* Function argument containing SQL text */
+){
+ JsonCache *p;
+ int i;
+ const char *zJson;
+ int nJson;
+
+ if( sqlite3_value_type(pArg)!=SQLITE_TEXT ){
+ return 0;
+ }
+ zJson = (const char*)sqlite3_value_text(pArg);
+ if( zJson==0 ) return 0;
+ nJson = sqlite3_value_bytes(pArg);
+
+ p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID);
+ if( p==0 ){
+ return 0;
+ }
+ for(i=0; i<p->nUsed; i++){
+ if( p->a[i]->zJson==zJson ) break;
+ }
+ if( i>=p->nUsed ){
+ for(i=0; i<p->nUsed; i++){
+ if( p->a[i]->nJson!=nJson ) continue;
+ if( memcmp(p->a[i]->zJson, zJson, nJson)==0 ) break;
+ }
+ }
+ if( i<p->nUsed ){
+ if( i<p->nUsed-1 ){
+ /* Make the matching entry the most recently used entry */
+ JsonParse *tmp = p->a[i];
+ memmove(&p->a[i], &p->a[i+1], (p->nUsed-i-1)*sizeof(tmp));
+ p->a[p->nUsed-1] = tmp;
+ i = p->nUsed - 1;
+ }
+ assert( p->a[i]->delta==0 );
+ return p->a[i];
+ }else{
+ return 0;
+ }
+}
/**************************************************************************
** Utility routines for dealing with JsonString objects
**************************************************************************/
-/* Set the JsonString object to an empty string
+/* Turn uninitialized bulk memory into a valid JsonString object
+** holding a zero-length string.
*/
-static void jsonZero(JsonString *p){
+static void jsonStringZero(JsonString *p){
p->zBuf = p->zSpace;
p->nAlloc = sizeof(p->zSpace);
p->nUsed = 0;
@@ -201470,39 +205344,39 @@ static void jsonZero(JsonString *p){
/* Initialize the JsonString object
*/
-static void jsonInit(JsonString *p, sqlite3_context *pCtx){
+static void jsonStringInit(JsonString *p, sqlite3_context *pCtx){
p->pCtx = pCtx;
- p->bErr = 0;
- jsonZero(p);
+ p->eErr = 0;
+ jsonStringZero(p);
}
/* Free all allocated memory and reset the JsonString object back to its
** initial state.
*/
-static void jsonReset(JsonString *p){
+static void jsonStringReset(JsonString *p){
if( !p->bStatic ) sqlite3RCStrUnref(p->zBuf);
- jsonZero(p);
+ jsonStringZero(p);
}
/* Report an out-of-memory (OOM) condition
*/
-static void jsonOom(JsonString *p){
- p->bErr = 1;
- sqlite3_result_error_nomem(p->pCtx);
- jsonReset(p);
+static void jsonStringOom(JsonString *p){
+ p->eErr |= JSTRING_OOM;
+ if( p->pCtx ) sqlite3_result_error_nomem(p->pCtx);
+ jsonStringReset(p);
}
/* Enlarge pJson->zBuf so that it can hold at least N more bytes.
** Return zero on success. Return non-zero on an OOM error
*/
-static int jsonGrow(JsonString *p, u32 N){
+static int jsonStringGrow(JsonString *p, u32 N){
u64 nTotal = N<p->nAlloc ? p->nAlloc*2 : p->nAlloc+N+10;
char *zNew;
if( p->bStatic ){
- if( p->bErr ) return 1;
+ if( p->eErr ) return 1;
zNew = sqlite3RCStrNew(nTotal);
if( zNew==0 ){
- jsonOom(p);
+ jsonStringOom(p);
return SQLITE_NOMEM;
}
memcpy(zNew, p->zBuf, (size_t)p->nUsed);
@@ -201511,8 +205385,8 @@ static int jsonGrow(JsonString *p, u32 N){
}else{
p->zBuf = sqlite3RCStrResize(p->zBuf, nTotal);
if( p->zBuf==0 ){
- p->bErr = 1;
- jsonZero(p);
+ p->eErr |= JSTRING_OOM;
+ jsonStringZero(p);
return SQLITE_NOMEM;
}
}
@@ -201522,20 +205396,20 @@ static int jsonGrow(JsonString *p, u32 N){
/* Append N bytes from zIn onto the end of the JsonString string.
*/
-static SQLITE_NOINLINE void jsonAppendExpand(
+static SQLITE_NOINLINE void jsonStringExpandAndAppend(
JsonString *p,
const char *zIn,
u32 N
){
assert( N>0 );
- if( jsonGrow(p,N) ) return;
+ if( jsonStringGrow(p,N) ) return;
memcpy(p->zBuf+p->nUsed, zIn, N);
p->nUsed += N;
}
static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){
if( N==0 ) return;
if( N+p->nUsed >= p->nAlloc ){
- jsonAppendExpand(p,zIn,N);
+ jsonStringExpandAndAppend(p,zIn,N);
}else{
memcpy(p->zBuf+p->nUsed, zIn, N);
p->nUsed += N;
@@ -201544,19 +205418,18 @@ static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){
static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){
assert( N>0 );
if( N+p->nUsed >= p->nAlloc ){
- jsonAppendExpand(p,zIn,N);
+ jsonStringExpandAndAppend(p,zIn,N);
}else{
memcpy(p->zBuf+p->nUsed, zIn, N);
p->nUsed += N;
}
}
-
/* Append formatted text (not to exceed N bytes) to the JsonString.
*/
static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){
va_list ap;
- if( (p->nUsed + N >= p->nAlloc) && jsonGrow(p, N) ) return;
+ if( (p->nUsed + N >= p->nAlloc) && jsonStringGrow(p, N) ) return;
va_start(ap, zFormat);
sqlite3_vsnprintf(N, p->zBuf+p->nUsed, zFormat, ap);
va_end(ap);
@@ -201566,7 +205439,7 @@ static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){
/* Append a single character
*/
static SQLITE_NOINLINE void jsonAppendCharExpand(JsonString *p, char c){
- if( jsonGrow(p,1) ) return;
+ if( jsonStringGrow(p,1) ) return;
p->zBuf[p->nUsed++] = c;
}
static void jsonAppendChar(JsonString *p, char c){
@@ -201577,24 +205450,27 @@ static void jsonAppendChar(JsonString *p, char c){
}
}
-/* Try to force the string to be a zero-terminated RCStr string.
+/* Remove a single character from the end of the string
+*/
+static void jsonStringTrimOneChar(JsonString *p){
+ if( p->eErr==0 ){
+ assert( p->nUsed>0 );
+ p->nUsed--;
+ }
+}
+
+
+/* Make sure there is a zero terminator on p->zBuf[]
**
** Return true on success. Return false if an OOM prevents this
** from happening.
*/
-static int jsonForceRCStr(JsonString *p){
+static int jsonStringTerminate(JsonString *p){
jsonAppendChar(p, 0);
- if( p->bErr ) return 0;
- p->nUsed--;
- if( p->bStatic==0 ) return 1;
- p->nAlloc = 0;
- p->nUsed++;
- jsonGrow(p, p->nUsed);
- p->nUsed--;
- return p->bStatic==0;
+ jsonStringTrimOneChar(p);
+ return p->eErr==0;
}
-
/* Append a comma separator to the output buffer, if the previous
** character is not '[' or '{'.
*/
@@ -201606,184 +205482,120 @@ static void jsonAppendSeparator(JsonString *p){
jsonAppendChar(p, ',');
}
-/* Append the N-byte string in zIn to the end of the JsonString string
-** under construction. Enclose the string in "..." and escape
-** any double-quotes or backslash characters contained within the
-** string.
+/* c is a control character. Append the canonical JSON representation
+** of that control character to p.
+**
+** This routine assumes that the output buffer has already been enlarged
+** sufficiently to hold the worst-case encoding plus a nul terminator.
*/
-static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
- u32 i;
- if( zIn==0 || ((N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0) ) return;
- p->zBuf[p->nUsed++] = '"';
- for(i=0; i<N; i++){
- unsigned char c = ((unsigned const char*)zIn)[i];
- if( jsonIsOk[c] ){
- p->zBuf[p->nUsed++] = c;
- }else if( c=='"' || c=='\\' ){
- json_simple_escape:
- if( (p->nUsed+N+3-i > p->nAlloc) && jsonGrow(p,N+3-i)!=0 ) return;
- p->zBuf[p->nUsed++] = '\\';
- p->zBuf[p->nUsed++] = c;
- }else if( c=='\'' ){
- p->zBuf[p->nUsed++] = c;
- }else{
- static const char aSpecial[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
- };
- assert( sizeof(aSpecial)==32 );
- assert( aSpecial['\b']=='b' );
- assert( aSpecial['\f']=='f' );
- assert( aSpecial['\n']=='n' );
- assert( aSpecial['\r']=='r' );
- assert( aSpecial['\t']=='t' );
- assert( c>=0 && c<sizeof(aSpecial) );
- if( aSpecial[c] ){
- c = aSpecial[c];
- goto json_simple_escape;
- }
- if( (p->nUsed+N+7+i > p->nAlloc) && jsonGrow(p,N+7-i)!=0 ) return;
- p->zBuf[p->nUsed++] = '\\';
- p->zBuf[p->nUsed++] = 'u';
- p->zBuf[p->nUsed++] = '0';
- p->zBuf[p->nUsed++] = '0';
- p->zBuf[p->nUsed++] = "0123456789abcdef"[c>>4];
- p->zBuf[p->nUsed++] = "0123456789abcdef"[c&0xf];
- }
+static void jsonAppendControlChar(JsonString *p, u8 c){
+ static const char aSpecial[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ assert( sizeof(aSpecial)==32 );
+ assert( aSpecial['\b']=='b' );
+ assert( aSpecial['\f']=='f' );
+ assert( aSpecial['\n']=='n' );
+ assert( aSpecial['\r']=='r' );
+ assert( aSpecial['\t']=='t' );
+ assert( c>=0 && c<sizeof(aSpecial) );
+ assert( p->nUsed+7 <= p->nAlloc );
+ if( aSpecial[c] ){
+ p->zBuf[p->nUsed] = '\\';
+ p->zBuf[p->nUsed+1] = aSpecial[c];
+ p->nUsed += 2;
+ }else{
+ p->zBuf[p->nUsed] = '\\';
+ p->zBuf[p->nUsed+1] = 'u';
+ p->zBuf[p->nUsed+2] = '0';
+ p->zBuf[p->nUsed+3] = '0';
+ p->zBuf[p->nUsed+4] = "0123456789abcdef"[c>>4];
+ p->zBuf[p->nUsed+5] = "0123456789abcdef"[c&0xf];
+ p->nUsed += 6;
}
- p->zBuf[p->nUsed++] = '"';
- assert( p->nUsed<p->nAlloc );
}
-/*
-** The zIn[0..N] string is a JSON5 string literal. Append to p a translation
-** of the string literal that standard JSON and that omits all JSON5
-** features.
+/* Append the N-byte string in zIn to the end of the JsonString string
+** under construction. Enclose the string in double-quotes ("...") and
+** escape any double-quotes or backslash characters contained within the
+** string.
+**
+** This routine is a high-runner. There is a measurable performance
+** increase associated with unwinding the jsonIsOk[] loop.
*/
-static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){
- u32 i;
- jsonAppendChar(p, '"');
- zIn++;
- N -= 2;
- while( N>0 ){
- for(i=0; i<N && zIn[i]!='\\'; i++){}
- if( i>0 ){
- jsonAppendRawNZ(p, zIn, i);
- zIn += i;
- N -= i;
- if( N==0 ) break;
- }
- assert( zIn[0]=='\\' );
- switch( (u8)zIn[1] ){
- case '\'':
- jsonAppendChar(p, '\'');
- break;
- case 'v':
- jsonAppendRawNZ(p, "\\u0009", 6);
- break;
- case 'x':
- jsonAppendRawNZ(p, "\\u00", 4);
- jsonAppendRawNZ(p, &zIn[2], 2);
- zIn += 2;
- N -= 2;
- break;
- case '0':
- jsonAppendRawNZ(p, "\\u0000", 6);
+static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
+ u32 k;
+ u8 c;
+ const u8 *z = (const u8*)zIn;
+ if( z==0 ) return;
+ if( (N+p->nUsed+2 >= p->nAlloc) && jsonStringGrow(p,N+2)!=0 ) return;
+ p->zBuf[p->nUsed++] = '"';
+ while( 1 /*exit-by-break*/ ){
+ k = 0;
+ /* The following while() is the 4-way unwound equivalent of
+ **
+ ** while( k<N && jsonIsOk[z[k]] ){ k++; }
+ */
+ while( 1 /* Exit by break */ ){
+ if( k+3>=N ){
+ while( k<N && jsonIsOk[z[k]] ){ k++; }
break;
- case '\r':
- if( zIn[2]=='\n' ){
- zIn++;
- N--;
- }
+ }
+ if( !jsonIsOk[z[k]] ){
break;
- case '\n':
+ }
+ if( !jsonIsOk[z[k+1]] ){
+ k += 1;
break;
- case 0xe2:
- assert( N>=4 );
- assert( 0x80==(u8)zIn[2] );
- assert( 0xa8==(u8)zIn[3] || 0xa9==(u8)zIn[3] );
- zIn += 2;
- N -= 2;
+ }
+ if( !jsonIsOk[z[k+2]] ){
+ k += 2;
break;
- default:
- jsonAppendRawNZ(p, zIn, 2);
+ }
+ if( !jsonIsOk[z[k+3]] ){
+ k += 3;
break;
+ }else{
+ k += 4;
+ }
}
- zIn += 2;
- N -= 2;
- }
- jsonAppendChar(p, '"');
-}
-
-/*
-** The zIn[0..N] string is a JSON5 integer literal. Append to p a translation
-** of the string literal that standard JSON and that omits all JSON5
-** features.
-*/
-static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){
- if( zIn[0]=='+' ){
- zIn++;
- N--;
- }else if( zIn[0]=='-' ){
- jsonAppendChar(p, '-');
- zIn++;
- N--;
- }
- if( zIn[0]=='0' && (zIn[1]=='x' || zIn[1]=='X') ){
- sqlite3_int64 i = 0;
- int rc = sqlite3DecOrHexToI64(zIn, &i);
- if( rc<=1 ){
- jsonPrintf(100,p,"%lld",i);
+ if( k>=N ){
+ if( k>0 ){
+ memcpy(&p->zBuf[p->nUsed], z, k);
+ p->nUsed += k;
+ }
+ break;
+ }
+ if( k>0 ){
+ memcpy(&p->zBuf[p->nUsed], z, k);
+ p->nUsed += k;
+ z += k;
+ N -= k;
+ }
+ c = z[0];
+ if( c=='"' || c=='\\' ){
+ if( (p->nUsed+N+3 > p->nAlloc) && jsonStringGrow(p,N+3)!=0 ) return;
+ p->zBuf[p->nUsed++] = '\\';
+ p->zBuf[p->nUsed++] = c;
+ }else if( c=='\'' ){
+ p->zBuf[p->nUsed++] = c;
}else{
- assert( rc==2 );
- jsonAppendRawNZ(p, "9.0e999", 7);
+ if( (p->nUsed+N+7 > p->nAlloc) && jsonStringGrow(p,N+7)!=0 ) return;
+ jsonAppendControlChar(p, c);
}
- return;
- }
- assert( N>0 );
- jsonAppendRawNZ(p, zIn, N);
-}
-
-/*
-** The zIn[0..N] string is a JSON5 real literal. Append to p a translation
-** of the string literal that standard JSON and that omits all JSON5
-** features.
-*/
-static void jsonAppendNormalizedReal(JsonString *p, const char *zIn, u32 N){
- u32 i;
- if( zIn[0]=='+' ){
- zIn++;
- N--;
- }else if( zIn[0]=='-' ){
- jsonAppendChar(p, '-');
- zIn++;
+ z++;
N--;
}
- if( zIn[0]=='.' ){
- jsonAppendChar(p, '0');
- }
- for(i=0; i<N; i++){
- if( zIn[i]=='.' && (i+1==N || !sqlite3Isdigit(zIn[i+1])) ){
- i++;
- jsonAppendRaw(p, zIn, i);
- zIn += i;
- N -= i;
- jsonAppendChar(p, '0');
- break;
- }
- }
- if( N>0 ){
- jsonAppendRawNZ(p, zIn, N);
- }
+ p->zBuf[p->nUsed++] = '"';
+ assert( p->nUsed<p->nAlloc );
}
-
-
/*
-** Append a function parameter value to the JSON string under
-** construction.
+** Append an sqlite3_value (such as a function parameter) to the JSON
+** string under construction in p.
*/
-static void jsonAppendValue(
+static void jsonAppendSqlValue(
JsonString *p, /* Append to this JSON string */
sqlite3_value *pValue /* Value to append */
){
@@ -201813,290 +205625,127 @@ static void jsonAppendValue(
break;
}
default: {
- if( p->bErr==0 ){
+ if( jsonFuncArgMightBeBinary(pValue) ){
+ JsonParse px;
+ memset(&px, 0, sizeof(px));
+ px.aBlob = (u8*)sqlite3_value_blob(pValue);
+ px.nBlob = sqlite3_value_bytes(pValue);
+ jsonTranslateBlobToText(&px, 0, p);
+ }else if( p->eErr==0 ){
sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1);
- p->bErr = 2;
- jsonReset(p);
+ p->eErr = JSTRING_ERR;
+ jsonStringReset(p);
}
break;
}
}
}
-
-/* Make the JSON in p the result of the SQL function.
+/* Make the text in p (which is probably a generated JSON text string)
+** the result of the SQL function.
**
-** The JSON string is reset.
+** The JsonString is reset.
+**
+** If pParse and ctx are both non-NULL, then the SQL string in p is
+** loaded into the zJson field of the pParse object as a RCStr and the
+** pParse is added to the cache.
*/
-static void jsonResult(JsonString *p){
- if( p->bErr==0 ){
- if( p->bStatic ){
+static void jsonReturnString(
+ JsonString *p, /* String to return */
+ JsonParse *pParse, /* JSONB source or NULL */
+ sqlite3_context *ctx /* Where to cache */
+){
+ assert( (pParse!=0)==(ctx!=0) );
+ assert( ctx==0 || ctx==p->pCtx );
+ if( p->eErr==0 ){
+ int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(p->pCtx));
+ if( flags & JSON_BLOB ){
+ jsonReturnStringAsBlob(p);
+ }else if( p->bStatic ){
sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
SQLITE_TRANSIENT, SQLITE_UTF8);
- }else if( jsonForceRCStr(p) ){
- sqlite3RCStrRef(p->zBuf);
- sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
- (void(*)(void*))sqlite3RCStrUnref,
+ }else if( jsonStringTerminate(p) ){
+ if( pParse && pParse->bJsonIsRCStr==0 && pParse->nBlobAlloc>0 ){
+ int rc;
+ pParse->zJson = sqlite3RCStrRef(p->zBuf);
+ pParse->nJson = p->nUsed;
+ pParse->bJsonIsRCStr = 1;
+ rc = jsonCacheInsert(ctx, pParse);
+ if( rc==SQLITE_NOMEM ){
+ sqlite3_result_error_nomem(ctx);
+ jsonStringReset(p);
+ return;
+ }
+ }
+ sqlite3_result_text64(p->pCtx, sqlite3RCStrRef(p->zBuf), p->nUsed,
+ sqlite3RCStrUnref,
SQLITE_UTF8);
+ }else{
+ sqlite3_result_error_nomem(p->pCtx);
}
- }
- if( p->bErr==1 ){
+ }else if( p->eErr & JSTRING_OOM ){
sqlite3_result_error_nomem(p->pCtx);
+ }else if( p->eErr & JSTRING_MALFORMED ){
+ sqlite3_result_error(p->pCtx, "malformed JSON", -1);
}
- jsonReset(p);
+ jsonStringReset(p);
}
/**************************************************************************
-** Utility routines for dealing with JsonNode and JsonParse objects
+** Utility routines for dealing with JsonParse objects
**************************************************************************/
/*
-** Return the number of consecutive JsonNode slots need to represent
-** the parsed JSON at pNode. The minimum answer is 1. For ARRAY and
-** OBJECT types, the number might be larger.
-**
-** Appended elements are not counted. The value returned is the number
-** by which the JsonNode counter should increment in order to go to the
-** next peer value.
-*/
-static u32 jsonNodeSize(JsonNode *pNode){
- return pNode->eType>=JSON_ARRAY ? pNode->n+1 : 1;
-}
-
-/*
** Reclaim all memory allocated by a JsonParse object. But do not
** delete the JsonParse object itself.
*/
static void jsonParseReset(JsonParse *pParse){
- while( pParse->pClup ){
- JsonCleanup *pTask = pParse->pClup;
- pParse->pClup = pTask->pJCNext;
- pTask->xOp(pTask->pArg);
- sqlite3_free(pTask);
- }
assert( pParse->nJPRef<=1 );
- if( pParse->aNode ){
- sqlite3_free(pParse->aNode);
- pParse->aNode = 0;
- }
- pParse->nNode = 0;
- pParse->nAlloc = 0;
- if( pParse->aUp ){
- sqlite3_free(pParse->aUp);
- pParse->aUp = 0;
- }
if( pParse->bJsonIsRCStr ){
sqlite3RCStrUnref(pParse->zJson);
pParse->zJson = 0;
+ pParse->nJson = 0;
pParse->bJsonIsRCStr = 0;
}
- if( pParse->zAlt ){
- sqlite3RCStrUnref(pParse->zAlt);
- pParse->zAlt = 0;
+ if( pParse->nBlobAlloc ){
+ sqlite3DbFree(pParse->db, pParse->aBlob);
+ pParse->aBlob = 0;
+ pParse->nBlob = 0;
+ pParse->nBlobAlloc = 0;
}
}
/*
-** Free a JsonParse object that was obtained from sqlite3_malloc().
-**
-** Note that destroying JsonParse might call sqlite3RCStrUnref() to
-** destroy the zJson value. The RCStr object might recursively invoke
-** JsonParse to destroy this pParse object again. Take care to ensure
-** that this recursive destructor sequence terminates harmlessly.
+** Decrement the reference count on the JsonParse object. When the
+** count reaches zero, free the object.
*/
static void jsonParseFree(JsonParse *pParse){
- if( pParse->nJPRef>1 ){
- pParse->nJPRef--;
- }else{
- jsonParseReset(pParse);
- sqlite3_free(pParse);
- }
-}
-
-/*
-** Add a cleanup task to the JsonParse object.
-**
-** If an OOM occurs, the cleanup operation happens immediately
-** and this function returns SQLITE_NOMEM.
-*/
-static int jsonParseAddCleanup(
- JsonParse *pParse, /* Add the cleanup task to this parser */
- void(*xOp)(void*), /* The cleanup task */
- void *pArg /* Argument to the cleanup */
-){
- JsonCleanup *pTask = sqlite3_malloc64( sizeof(*pTask) );
- if( pTask==0 ){
- pParse->oom = 1;
- xOp(pArg);
- return SQLITE_ERROR;
- }
- pTask->pJCNext = pParse->pClup;
- pParse->pClup = pTask;
- pTask->xOp = xOp;
- pTask->pArg = pArg;
- return SQLITE_OK;
-}
-
-/*
-** Convert the JsonNode pNode into a pure JSON string and
-** append to pOut. Subsubstructure is also included. Return
-** the number of JsonNode objects that are encoded.
-*/
-static void jsonRenderNode(
- JsonParse *pParse, /* the complete parse of the JSON */
- JsonNode *pNode, /* The node to render */
- JsonString *pOut /* Write JSON here */
-){
- assert( pNode!=0 );
- while( (pNode->jnFlags & JNODE_REPLACE)!=0 && pParse->useMod ){
- u32 idx = (u32)(pNode - pParse->aNode);
- u32 i = pParse->iSubst;
- while( 1 /*exit-by-break*/ ){
- assert( i<pParse->nNode );
- assert( pParse->aNode[i].eType==JSON_SUBST );
- assert( pParse->aNode[i].eU==4 );
- assert( pParse->aNode[i].u.iPrev<i );
- if( pParse->aNode[i].n==idx ){
- pNode = &pParse->aNode[i+1];
- break;
- }
- i = pParse->aNode[i].u.iPrev;
- }
- }
- switch( pNode->eType ){
- default: {
- assert( pNode->eType==JSON_NULL );
- jsonAppendRawNZ(pOut, "null", 4);
- break;
- }
- case JSON_TRUE: {
- jsonAppendRawNZ(pOut, "true", 4);
- break;
- }
- case JSON_FALSE: {
- jsonAppendRawNZ(pOut, "false", 5);
- break;
- }
- case JSON_STRING: {
- assert( pNode->eU==1 );
- if( pNode->jnFlags & JNODE_RAW ){
- if( pNode->jnFlags & JNODE_LABEL ){
- jsonAppendChar(pOut, '"');
- jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
- jsonAppendChar(pOut, '"');
- }else{
- jsonAppendString(pOut, pNode->u.zJContent, pNode->n);
- }
- }else if( pNode->jnFlags & JNODE_JSON5 ){
- jsonAppendNormalizedString(pOut, pNode->u.zJContent, pNode->n);
- }else{
- assert( pNode->n>0 );
- jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n);
- }
- break;
- }
- case JSON_REAL: {
- assert( pNode->eU==1 );
- if( pNode->jnFlags & JNODE_JSON5 ){
- jsonAppendNormalizedReal(pOut, pNode->u.zJContent, pNode->n);
- }else{
- assert( pNode->n>0 );
- jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n);
- }
- break;
- }
- case JSON_INT: {
- assert( pNode->eU==1 );
- if( pNode->jnFlags & JNODE_JSON5 ){
- jsonAppendNormalizedInt(pOut, pNode->u.zJContent, pNode->n);
- }else{
- assert( pNode->n>0 );
- jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n);
- }
- break;
- }
- case JSON_ARRAY: {
- u32 j = 1;
- jsonAppendChar(pOut, '[');
- for(;;){
- while( j<=pNode->n ){
- if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){
- jsonAppendSeparator(pOut);
- jsonRenderNode(pParse, &pNode[j], pOut);
- }
- j += jsonNodeSize(&pNode[j]);
- }
- if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
- if( pParse->useMod==0 ) break;
- assert( pNode->eU==2 );
- pNode = &pParse->aNode[pNode->u.iAppend];
- j = 1;
- }
- jsonAppendChar(pOut, ']');
- break;
- }
- case JSON_OBJECT: {
- u32 j = 1;
- jsonAppendChar(pOut, '{');
- for(;;){
- while( j<=pNode->n ){
- if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){
- jsonAppendSeparator(pOut);
- jsonRenderNode(pParse, &pNode[j], pOut);
- jsonAppendChar(pOut, ':');
- jsonRenderNode(pParse, &pNode[j+1], pOut);
- }
- j += 1 + jsonNodeSize(&pNode[j+1]);
- }
- if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
- if( pParse->useMod==0 ) break;
- assert( pNode->eU==2 );
- pNode = &pParse->aNode[pNode->u.iAppend];
- j = 1;
- }
- jsonAppendChar(pOut, '}');
- break;
+ if( pParse ){
+ if( pParse->nJPRef>1 ){
+ pParse->nJPRef--;
+ }else{
+ jsonParseReset(pParse);
+ sqlite3DbFree(pParse->db, pParse);
}
}
}
-/*
-** Return a JsonNode and all its descendants as a JSON string.
-*/
-static void jsonReturnJson(
- JsonParse *pParse, /* The complete JSON */
- JsonNode *pNode, /* Node to return */
- sqlite3_context *pCtx, /* Return value for this function */
- int bGenerateAlt /* Also store the rendered text in zAlt */
-){
- JsonString s;
- if( pParse->oom ){
- sqlite3_result_error_nomem(pCtx);
- return;
- }
- if( pParse->nErr==0 ){
- jsonInit(&s, pCtx);
- jsonRenderNode(pParse, pNode, &s);
- if( bGenerateAlt && pParse->zAlt==0 && jsonForceRCStr(&s) ){
- pParse->zAlt = sqlite3RCStrRef(s.zBuf);
- pParse->nAlt = s.nUsed;
- }
- jsonResult(&s);
- sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
- }
-}
+/**************************************************************************
+** Utility routines for the JSON text parser
+**************************************************************************/
/*
** Translate a single byte of Hex into an integer.
-** This routine only works if h really is a valid hexadecimal
-** character: 0..9a..fA..F
+** This routine only gives a correct answer if h really is a valid hexadecimal
+** character: 0..9a..fA..F. But unlike sqlite3HexToInt(), it does not
+** assert() if the digit is not hex.
*/
static u8 jsonHexToInt(int h){
- assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') );
+#ifdef SQLITE_ASCII
+ h += 9*(1&(h>>6));
+#endif
#ifdef SQLITE_EBCDIC
h += 9*(1&~(h>>4));
-#else
- h += 9*(1&(h>>6));
#endif
return (u8)(h & 0xf);
}
@@ -202106,10 +205755,6 @@ static u8 jsonHexToInt(int h){
*/
static u32 jsonHexToInt4(const char *z){
u32 v;
- assert( sqlite3Isxdigit(z[0]) );
- assert( sqlite3Isxdigit(z[1]) );
- assert( sqlite3Isxdigit(z[2]) );
- assert( sqlite3Isxdigit(z[3]) );
v = (jsonHexToInt(z[0])<<12)
+ (jsonHexToInt(z[1])<<8)
+ (jsonHexToInt(z[2])<<4)
@@ -202118,281 +205763,6 @@ static u32 jsonHexToInt4(const char *z){
}
/*
-** Make the JsonNode the return value of the function.
-*/
-static void jsonReturn(
- JsonParse *pParse, /* Complete JSON parse tree */
- JsonNode *pNode, /* Node to return */
- sqlite3_context *pCtx /* Return value for this function */
-){
- switch( pNode->eType ){
- default: {
- assert( pNode->eType==JSON_NULL );
- sqlite3_result_null(pCtx);
- break;
- }
- case JSON_TRUE: {
- sqlite3_result_int(pCtx, 1);
- break;
- }
- case JSON_FALSE: {
- sqlite3_result_int(pCtx, 0);
- break;
- }
- case JSON_INT: {
- sqlite3_int64 i = 0;
- int rc;
- int bNeg = 0;
- const char *z;
-
- assert( pNode->eU==1 );
- z = pNode->u.zJContent;
- if( z[0]=='-' ){ z++; bNeg = 1; }
- else if( z[0]=='+' ){ z++; }
- rc = sqlite3DecOrHexToI64(z, &i);
- if( rc<=1 ){
- sqlite3_result_int64(pCtx, bNeg ? -i : i);
- }else if( rc==3 && bNeg ){
- sqlite3_result_int64(pCtx, SMALLEST_INT64);
- }else{
- goto to_double;
- }
- break;
- }
- case JSON_REAL: {
- double r;
- const char *z;
- assert( pNode->eU==1 );
- to_double:
- z = pNode->u.zJContent;
- sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
- sqlite3_result_double(pCtx, r);
- break;
- }
- case JSON_STRING: {
- if( pNode->jnFlags & JNODE_RAW ){
- assert( pNode->eU==1 );
- sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n,
- SQLITE_TRANSIENT);
- }else if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){
- /* JSON formatted without any backslash-escapes */
- assert( pNode->eU==1 );
- sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2,
- SQLITE_TRANSIENT);
- }else{
- /* Translate JSON formatted string into raw text */
- u32 i;
- u32 n = pNode->n;
- const char *z;
- char *zOut;
- u32 j;
- u32 nOut = n;
- assert( pNode->eU==1 );
- z = pNode->u.zJContent;
- zOut = sqlite3_malloc( nOut+1 );
- if( zOut==0 ){
- sqlite3_result_error_nomem(pCtx);
- break;
- }
- for(i=1, j=0; i<n-1; i++){
- char c = z[i];
- if( c=='\\' ){
- c = z[++i];
- if( c=='u' ){
- u32 v = jsonHexToInt4(z+i+1);
- i += 4;
- if( v==0 ) break;
- if( v<=0x7f ){
- zOut[j++] = (char)v;
- }else if( v<=0x7ff ){
- zOut[j++] = (char)(0xc0 | (v>>6));
- zOut[j++] = 0x80 | (v&0x3f);
- }else{
- u32 vlo;
- if( (v&0xfc00)==0xd800
- && i<n-6
- && z[i+1]=='\\'
- && z[i+2]=='u'
- && ((vlo = jsonHexToInt4(z+i+3))&0xfc00)==0xdc00
- ){
- /* We have a surrogate pair */
- v = ((v&0x3ff)<<10) + (vlo&0x3ff) + 0x10000;
- i += 6;
- zOut[j++] = 0xf0 | (v>>18);
- zOut[j++] = 0x80 | ((v>>12)&0x3f);
- zOut[j++] = 0x80 | ((v>>6)&0x3f);
- zOut[j++] = 0x80 | (v&0x3f);
- }else{
- zOut[j++] = 0xe0 | (v>>12);
- zOut[j++] = 0x80 | ((v>>6)&0x3f);
- zOut[j++] = 0x80 | (v&0x3f);
- }
- }
- continue;
- }else if( c=='b' ){
- c = '\b';
- }else if( c=='f' ){
- c = '\f';
- }else if( c=='n' ){
- c = '\n';
- }else if( c=='r' ){
- c = '\r';
- }else if( c=='t' ){
- c = '\t';
- }else if( c=='v' ){
- c = '\v';
- }else if( c=='\'' || c=='"' || c=='/' || c=='\\' ){
- /* pass through unchanged */
- }else if( c=='0' ){
- c = 0;
- }else if( c=='x' ){
- c = (jsonHexToInt(z[i+1])<<4) | jsonHexToInt(z[i+2]);
- i += 2;
- }else if( c=='\r' && z[i+1]=='\n' ){
- i++;
- continue;
- }else if( 0xe2==(u8)c ){
- assert( 0x80==(u8)z[i+1] );
- assert( 0xa8==(u8)z[i+2] || 0xa9==(u8)z[i+2] );
- i += 2;
- continue;
- }else{
- continue;
- }
- } /* end if( c=='\\' ) */
- zOut[j++] = c;
- } /* end for() */
- zOut[j] = 0;
- sqlite3_result_text(pCtx, zOut, j, sqlite3_free);
- }
- break;
- }
- case JSON_ARRAY:
- case JSON_OBJECT: {
- jsonReturnJson(pParse, pNode, pCtx, 0);
- break;
- }
- }
-}
-
-/* Forward reference */
-static int jsonParseAddNode(JsonParse*,u32,u32,const char*);
-
-/*
-** A macro to hint to the compiler that a function should not be
-** inlined.
-*/
-#if defined(__GNUC__)
-# define JSON_NOINLINE __attribute__((noinline))
-#elif defined(_MSC_VER) && _MSC_VER>=1310
-# define JSON_NOINLINE __declspec(noinline)
-#else
-# define JSON_NOINLINE
-#endif
-
-
-/*
-** Add a single node to pParse->aNode after first expanding the
-** size of the aNode array. Return the index of the new node.
-**
-** If an OOM error occurs, set pParse->oom and return -1.
-*/
-static JSON_NOINLINE int jsonParseAddNodeExpand(
- JsonParse *pParse, /* Append the node to this object */
- u32 eType, /* Node type */
- u32 n, /* Content size or sub-node count */
- const char *zContent /* Content */
-){
- u32 nNew;
- JsonNode *pNew;
- assert( pParse->nNode>=pParse->nAlloc );
- if( pParse->oom ) return -1;
- nNew = pParse->nAlloc*2 + 10;
- pNew = sqlite3_realloc64(pParse->aNode, sizeof(JsonNode)*nNew);
- if( pNew==0 ){
- pParse->oom = 1;
- return -1;
- }
- pParse->nAlloc = sqlite3_msize(pNew)/sizeof(JsonNode);
- pParse->aNode = pNew;
- assert( pParse->nNode<pParse->nAlloc );
- return jsonParseAddNode(pParse, eType, n, zContent);
-}
-
-/*
-** Create a new JsonNode instance based on the arguments and append that
-** instance to the JsonParse. Return the index in pParse->aNode[] of the
-** new node, or -1 if a memory allocation fails.
-*/
-static int jsonParseAddNode(
- JsonParse *pParse, /* Append the node to this object */
- u32 eType, /* Node type */
- u32 n, /* Content size or sub-node count */
- const char *zContent /* Content */
-){
- JsonNode *p;
- assert( pParse->aNode!=0 || pParse->nNode>=pParse->nAlloc );
- if( pParse->nNode>=pParse->nAlloc ){
- return jsonParseAddNodeExpand(pParse, eType, n, zContent);
- }
- assert( pParse->aNode!=0 );
- p = &pParse->aNode[pParse->nNode];
- assert( p!=0 );
- p->eType = (u8)(eType & 0xff);
- p->jnFlags = (u8)(eType >> 8);
- VVA( p->eU = zContent ? 1 : 0 );
- p->n = n;
- p->u.zJContent = zContent;
- return pParse->nNode++;
-}
-
-/*
-** Add an array of new nodes to the current pParse->aNode array.
-** Return the index of the first node added.
-**
-** If an OOM error occurs, set pParse->oom.
-*/
-static void jsonParseAddNodeArray(
- JsonParse *pParse, /* Append the node to this object */
- JsonNode *aNode, /* Array of nodes to add */
- u32 nNode /* Number of elements in aNew */
-){
- assert( aNode!=0 );
- assert( nNode>=1 );
- if( pParse->nNode + nNode > pParse->nAlloc ){
- u32 nNew = pParse->nNode + nNode;
- JsonNode *aNew = sqlite3_realloc64(pParse->aNode, nNew*sizeof(JsonNode));
- if( aNew==0 ){
- pParse->oom = 1;
- return;
- }
- pParse->nAlloc = sqlite3_msize(aNew)/sizeof(JsonNode);
- pParse->aNode = aNew;
- }
- memcpy(&pParse->aNode[pParse->nNode], aNode, nNode*sizeof(JsonNode));
- pParse->nNode += nNode;
-}
-
-/*
-** Add a new JSON_SUBST node. The node immediately following
-** this new node will be the substitute content for iNode.
-*/
-static int jsonParseAddSubstNode(
- JsonParse *pParse, /* Add the JSON_SUBST here */
- u32 iNode /* References this node */
-){
- int idx = jsonParseAddNode(pParse, JSON_SUBST, iNode, 0);
- if( pParse->oom ) return -1;
- pParse->aNode[iNode].jnFlags |= JNODE_REPLACE;
- pParse->aNode[idx].eU = 4;
- pParse->aNode[idx].u.iPrev = pParse->iSubst;
- pParse->iSubst = idx;
- pParse->hasMod = 1;
- pParse->useMod = 1;
- return idx;
-}
-
-/*
** Return true if z[] begins with 2 (or more) hexadecimal digits
*/
static int jsonIs2Hex(const char *z){
@@ -202545,63 +205915,503 @@ static const struct NanInfName {
char *zMatch;
char *zRepl;
} aNanInfName[] = {
- { 'i', 'I', 3, JSON_REAL, 7, "inf", "9.0e999" },
- { 'i', 'I', 8, JSON_REAL, 7, "infinity", "9.0e999" },
- { 'n', 'N', 3, JSON_NULL, 4, "NaN", "null" },
- { 'q', 'Q', 4, JSON_NULL, 4, "QNaN", "null" },
- { 's', 'S', 4, JSON_NULL, 4, "SNaN", "null" },
+ { 'i', 'I', 3, JSONB_FLOAT, 7, "inf", "9.0e999" },
+ { 'i', 'I', 8, JSONB_FLOAT, 7, "infinity", "9.0e999" },
+ { 'n', 'N', 3, JSONB_NULL, 4, "NaN", "null" },
+ { 'q', 'Q', 4, JSONB_NULL, 4, "QNaN", "null" },
+ { 's', 'S', 4, JSONB_NULL, 4, "SNaN", "null" },
};
+
+/*
+** Report the wrong number of arguments for json_insert(), json_replace()
+** or json_set().
+*/
+static void jsonWrongNumArgs(
+ sqlite3_context *pCtx,
+ const char *zFuncName
+){
+ char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments",
+ zFuncName);
+ sqlite3_result_error(pCtx, zMsg, -1);
+ sqlite3_free(zMsg);
+}
+
+/****************************************************************************
+** Utility routines for dealing with the binary BLOB representation of JSON
+****************************************************************************/
+
+/*
+** Expand pParse->aBlob so that it holds at least N bytes.
+**
+** Return the number of errors.
+*/
+static int jsonBlobExpand(JsonParse *pParse, u32 N){
+ u8 *aNew;
+ u32 t;
+ assert( N>pParse->nBlobAlloc );
+ if( pParse->nBlobAlloc==0 ){
+ t = 100;
+ }else{
+ t = pParse->nBlobAlloc*2;
+ }
+ if( t<N ) t = N+100;
+ aNew = sqlite3DbRealloc(pParse->db, pParse->aBlob, t);
+ if( aNew==0 ){ pParse->oom = 1; return 1; }
+ pParse->aBlob = aNew;
+ pParse->nBlobAlloc = t;
+ return 0;
+}
+
+/*
+** If pParse->aBlob is not previously editable (because it is taken
+** from sqlite3_value_blob(), as indicated by the fact that
+** pParse->nBlobAlloc==0 and pParse->nBlob>0) then make it editable
+** by making a copy into space obtained from malloc.
+**
+** Return true on success. Return false on OOM.
+*/
+static int jsonBlobMakeEditable(JsonParse *pParse, u32 nExtra){
+ u8 *aOld;
+ u32 nSize;
+ assert( !pParse->bReadOnly );
+ if( pParse->oom ) return 0;
+ if( pParse->nBlobAlloc>0 ) return 1;
+ aOld = pParse->aBlob;
+ nSize = pParse->nBlob + nExtra;
+ pParse->aBlob = 0;
+ if( jsonBlobExpand(pParse, nSize) ){
+ return 0;
+ }
+ assert( pParse->nBlobAlloc >= pParse->nBlob + nExtra );
+ memcpy(pParse->aBlob, aOld, pParse->nBlob);
+ return 1;
+}
+
+/* Expand pParse->aBlob and append one bytes.
+*/
+static SQLITE_NOINLINE void jsonBlobExpandAndAppendOneByte(
+ JsonParse *pParse,
+ u8 c
+){
+ jsonBlobExpand(pParse, pParse->nBlob+1);
+ if( pParse->oom==0 ){
+ assert( pParse->nBlob+1<=pParse->nBlobAlloc );
+ pParse->aBlob[pParse->nBlob++] = c;
+ }
+}
+
+/* Append a single character.
+*/
+static void jsonBlobAppendOneByte(JsonParse *pParse, u8 c){
+ if( pParse->nBlob >= pParse->nBlobAlloc ){
+ jsonBlobExpandAndAppendOneByte(pParse, c);
+ }else{
+ pParse->aBlob[pParse->nBlob++] = c;
+ }
+}
+
+/* Slow version of jsonBlobAppendNode() that first resizes the
+** pParse->aBlob structure.
+*/
+static void jsonBlobAppendNode(JsonParse*,u8,u32,const void*);
+static SQLITE_NOINLINE void jsonBlobExpandAndAppendNode(
+ JsonParse *pParse,
+ u8 eType,
+ u32 szPayload,
+ const void *aPayload
+){
+ if( jsonBlobExpand(pParse, pParse->nBlob+szPayload+9) ) return;
+ jsonBlobAppendNode(pParse, eType, szPayload, aPayload);
+}
+
+
+/* Append an node type byte together with the payload size and
+** possibly also the payload.
+**
+** If aPayload is not NULL, then it is a pointer to the payload which
+** is also appended. If aPayload is NULL, the pParse->aBlob[] array
+** is resized (if necessary) so that it is big enough to hold the
+** payload, but the payload is not appended and pParse->nBlob is left
+** pointing to where the first byte of payload will eventually be.
+*/
+static void jsonBlobAppendNode(
+ JsonParse *pParse, /* The JsonParse object under construction */
+ u8 eType, /* Node type. One of JSONB_* */
+ u32 szPayload, /* Number of bytes of payload */
+ const void *aPayload /* The payload. Might be NULL */
+){
+ u8 *a;
+ if( pParse->nBlob+szPayload+9 > pParse->nBlobAlloc ){
+ jsonBlobExpandAndAppendNode(pParse,eType,szPayload,aPayload);
+ return;
+ }
+ assert( pParse->aBlob!=0 );
+ a = &pParse->aBlob[pParse->nBlob];
+ if( szPayload<=11 ){
+ a[0] = eType | (szPayload<<4);
+ pParse->nBlob += 1;
+ }else if( szPayload<=0xff ){
+ a[0] = eType | 0xc0;
+ a[1] = szPayload & 0xff;
+ pParse->nBlob += 2;
+ }else if( szPayload<=0xffff ){
+ a[0] = eType | 0xd0;
+ a[1] = (szPayload >> 8) & 0xff;
+ a[2] = szPayload & 0xff;
+ pParse->nBlob += 3;
+ }else{
+ a[0] = eType | 0xe0;
+ a[1] = (szPayload >> 24) & 0xff;
+ a[2] = (szPayload >> 16) & 0xff;
+ a[3] = (szPayload >> 8) & 0xff;
+ a[4] = szPayload & 0xff;
+ pParse->nBlob += 5;
+ }
+ if( aPayload ){
+ pParse->nBlob += szPayload;
+ memcpy(&pParse->aBlob[pParse->nBlob-szPayload], aPayload, szPayload);
+ }
+}
+
+/* Change the payload size for the node at index i to be szPayload.
+*/
+static int jsonBlobChangePayloadSize(
+ JsonParse *pParse,
+ u32 i,
+ u32 szPayload
+){
+ u8 *a;
+ u8 szType;
+ u8 nExtra;
+ u8 nNeeded;
+ int delta;
+ if( pParse->oom ) return 0;
+ a = &pParse->aBlob[i];
+ szType = a[0]>>4;
+ if( szType<=11 ){
+ nExtra = 0;
+ }else if( szType==12 ){
+ nExtra = 1;
+ }else if( szType==13 ){
+ nExtra = 2;
+ }else{
+ nExtra = 4;
+ }
+ if( szPayload<=11 ){
+ nNeeded = 0;
+ }else if( szPayload<=0xff ){
+ nNeeded = 1;
+ }else if( szPayload<=0xffff ){
+ nNeeded = 2;
+ }else{
+ nNeeded = 4;
+ }
+ delta = nNeeded - nExtra;
+ if( delta ){
+ u32 newSize = pParse->nBlob + delta;
+ if( delta>0 ){
+ if( newSize>pParse->nBlobAlloc && jsonBlobExpand(pParse, newSize) ){
+ return 0; /* OOM error. Error state recorded in pParse->oom. */
+ }
+ a = &pParse->aBlob[i];
+ memmove(&a[1+delta], &a[1], pParse->nBlob - (i+1));
+ }else{
+ memmove(&a[1], &a[1-delta], pParse->nBlob - (i+1-delta));
+ }
+ pParse->nBlob = newSize;
+ }
+ if( nNeeded==0 ){
+ a[0] = (a[0] & 0x0f) | (szPayload<<4);
+ }else if( nNeeded==1 ){
+ a[0] = (a[0] & 0x0f) | 0xc0;
+ a[1] = szPayload & 0xff;
+ }else if( nNeeded==2 ){
+ a[0] = (a[0] & 0x0f) | 0xd0;
+ a[1] = (szPayload >> 8) & 0xff;
+ a[2] = szPayload & 0xff;
+ }else{
+ a[0] = (a[0] & 0x0f) | 0xe0;
+ a[1] = (szPayload >> 24) & 0xff;
+ a[2] = (szPayload >> 16) & 0xff;
+ a[3] = (szPayload >> 8) & 0xff;
+ a[4] = szPayload & 0xff;
+ }
+ return delta;
+}
+
+/*
+** If z[0] is 'u' and is followed by exactly 4 hexadecimal character,
+** then set *pOp to JSONB_TEXTJ and return true. If not, do not make
+** any changes to *pOp and return false.
+*/
+static int jsonIs4HexB(const char *z, int *pOp){
+ if( z[0]!='u' ) return 0;
+ if( !jsonIs4Hex(&z[1]) ) return 0;
+ *pOp = JSONB_TEXTJ;
+ return 1;
+}
+
+/*
+** Check a single element of the JSONB in pParse for validity.
+**
+** The element to be checked starts at offset i and must end at on the
+** last byte before iEnd.
+**
+** Return 0 if everything is correct. Return the 1-based byte offset of the
+** error if a problem is detected. (In other words, if the error is at offset
+** 0, return 1).
+*/
+static u32 jsonbValidityCheck(
+ const JsonParse *pParse, /* Input JSONB. Only aBlob and nBlob are used */
+ u32 i, /* Start of element as pParse->aBlob[i] */
+ u32 iEnd, /* One more than the last byte of the element */
+ u32 iDepth /* Current nesting depth */
+){
+ u32 n, sz, j, k;
+ const u8 *z;
+ u8 x;
+ if( iDepth>JSON_MAX_DEPTH ) return i+1;
+ sz = 0;
+ n = jsonbPayloadSize(pParse, i, &sz);
+ if( NEVER(n==0) ) return i+1; /* Checked by caller */
+ if( NEVER(i+n+sz!=iEnd) ) return i+1; /* Checked by caller */
+ z = pParse->aBlob;
+ x = z[i] & 0x0f;
+ switch( x ){
+ case JSONB_NULL:
+ case JSONB_TRUE:
+ case JSONB_FALSE: {
+ return n+sz==1 ? 0 : i+1;
+ }
+ case JSONB_INT: {
+ if( sz<1 ) return i+1;
+ j = i+n;
+ if( z[j]=='-' ){
+ j++;
+ if( sz<2 ) return i+1;
+ }
+ k = i+n+sz;
+ while( j<k ){
+ if( sqlite3Isdigit(z[j]) ){
+ j++;
+ }else{
+ return j+1;
+ }
+ }
+ return 0;
+ }
+ case JSONB_INT5: {
+ if( sz<3 ) return i+1;
+ j = i+n;
+ if( z[j]=='-' ){
+ if( sz<4 ) return i+1;
+ j++;
+ }
+ if( z[j]!='0' ) return i+1;
+ if( z[j+1]!='x' && z[j+1]!='X' ) return j+2;
+ j += 2;
+ k = i+n+sz;
+ while( j<k ){
+ if( sqlite3Isxdigit(z[j]) ){
+ j++;
+ }else{
+ return j+1;
+ }
+ }
+ return 0;
+ }
+ case JSONB_FLOAT:
+ case JSONB_FLOAT5: {
+ u8 seen = 0; /* 0: initial. 1: '.' seen 2: 'e' seen */
+ if( sz<2 ) return i+1;
+ j = i+n;
+ k = j+sz;
+ if( z[j]=='-' ){
+ j++;
+ if( sz<3 ) return i+1;
+ }
+ if( z[j]=='.' ){
+ if( x==JSONB_FLOAT ) return j+1;
+ if( !sqlite3Isdigit(z[j+1]) ) return j+1;
+ j += 2;
+ seen = 1;
+ }else if( z[j]=='0' && x==JSONB_FLOAT ){
+ if( j+3>k ) return j+1;
+ if( z[j+1]!='.' && z[j+1]!='e' && z[j+1]!='E' ) return j+1;
+ j++;
+ }
+ for(; j<k; j++){
+ if( sqlite3Isdigit(z[j]) ) continue;
+ if( z[j]=='.' ){
+ if( seen>0 ) return j+1;
+ if( x==JSONB_FLOAT && (j==k-1 || !sqlite3Isdigit(z[j+1])) ){
+ return j+1;
+ }
+ seen = 1;
+ continue;
+ }
+ if( z[j]=='e' || z[j]=='E' ){
+ if( seen==2 ) return j+1;
+ if( j==k-1 ) return j+1;
+ if( z[j+1]=='+' || z[j+1]=='-' ){
+ j++;
+ if( j==k-1 ) return j+1;
+ }
+ seen = 2;
+ continue;
+ }
+ return j+1;
+ }
+ if( seen==0 ) return i+1;
+ return 0;
+ }
+ case JSONB_TEXT: {
+ j = i+n;
+ k = j+sz;
+ while( j<k ){
+ if( !jsonIsOk[z[j]] && z[j]!='\'' ) return j+1;
+ j++;
+ }
+ return 0;
+ }
+ case JSONB_TEXTJ:
+ case JSONB_TEXT5: {
+ j = i+n;
+ k = j+sz;
+ while( j<k ){
+ if( !jsonIsOk[z[j]] && z[j]!='\'' ){
+ if( z[j]=='"' ){
+ if( x==JSONB_TEXTJ ) return j+1;
+ }else if( z[j]<=0x1f ){
+ /* Control characters in JSON5 string literals are ok */
+ if( x==JSONB_TEXTJ ) return j+1;
+ }else if( NEVER(z[j]!='\\') || j+1>=k ){
+ return j+1;
+ }else if( strchr("\"\\/bfnrt",z[j+1])!=0 ){
+ j++;
+ }else if( z[j+1]=='u' ){
+ if( j+5>=k ) return j+1;
+ if( !jsonIs4Hex((const char*)&z[j+2]) ) return j+1;
+ j++;
+ }else if( x!=JSONB_TEXT5 ){
+ return j+1;
+ }else{
+ u32 c = 0;
+ u32 szC = jsonUnescapeOneChar((const char*)&z[j], k-j, &c);
+ if( c==JSON_INVALID_CHAR ) return j+1;
+ j += szC - 1;
+ }
+ }
+ j++;
+ }
+ return 0;
+ }
+ case JSONB_TEXTRAW: {
+ return 0;
+ }
+ case JSONB_ARRAY: {
+ u32 sub;
+ j = i+n;
+ k = j+sz;
+ while( j<k ){
+ sz = 0;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return j+1;
+ if( j+n+sz>k ) return j+1;
+ sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1);
+ if( sub ) return sub;
+ j += n + sz;
+ }
+ assert( j==k );
+ return 0;
+ }
+ case JSONB_OBJECT: {
+ u32 cnt = 0;
+ u32 sub;
+ j = i+n;
+ k = j+sz;
+ while( j<k ){
+ sz = 0;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return j+1;
+ if( j+n+sz>k ) return j+1;
+ if( (cnt & 1)==0 ){
+ x = z[j] & 0x0f;
+ if( x<JSONB_TEXT || x>JSONB_TEXTRAW ) return j+1;
+ }
+ sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1);
+ if( sub ) return sub;
+ cnt++;
+ j += n + sz;
+ }
+ assert( j==k );
+ if( (cnt & 1)!=0 ) return j+1;
+ return 0;
+ }
+ default: {
+ return i+1;
+ }
+ }
+}
+
/*
-** Parse a single JSON value which begins at pParse->zJson[i]. Return the
-** index of the first character past the end of the value parsed.
+** Translate a single element of JSON text at pParse->zJson[i] into
+** its equivalent binary JSONB representation. Append the translation into
+** pParse->aBlob[] beginning at pParse->nBlob. The size of
+** pParse->aBlob[] is increased as necessary.
**
-** Special return values:
+** Return the index of the first character past the end of the element parsed,
+** or one of the following special result codes:
**
** 0 End of input
-** -1 Syntax error
-** -2 '}' seen
-** -3 ']' seen
-** -4 ',' seen
-** -5 ':' seen
+** -1 Syntax error or OOM
+** -2 '}' seen \
+** -3 ']' seen \___ For these returns, pParse->iErr is set to
+** -4 ',' seen / the index in zJson[] of the seen character
+** -5 ':' seen /
*/
-static int jsonParseValue(JsonParse *pParse, u32 i){
+static int jsonTranslateTextToBlob(JsonParse *pParse, u32 i){
char c;
u32 j;
- int iThis;
+ u32 iThis, iStart;
int x;
- JsonNode *pNode;
+ u8 t;
const char *z = pParse->zJson;
json_parse_restart:
switch( (u8)z[i] ){
case '{': {
/* Parse object */
- iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
- if( iThis<0 ) return -1;
+ iThis = pParse->nBlob;
+ jsonBlobAppendNode(pParse, JSONB_OBJECT, pParse->nJson-i, 0);
if( ++pParse->iDepth > JSON_MAX_DEPTH ){
pParse->iErr = i;
return -1;
}
+ iStart = pParse->nBlob;
for(j=i+1;;j++){
- u32 nNode = pParse->nNode;
- x = jsonParseValue(pParse, j);
+ u32 iBlob = pParse->nBlob;
+ x = jsonTranslateTextToBlob(pParse, j);
if( x<=0 ){
+ int op;
if( x==(-2) ){
j = pParse->iErr;
- if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1;
+ if( pParse->nBlob!=(u32)iStart ) pParse->hasNonstd = 1;
break;
}
j += json5Whitespace(&z[j]);
+ op = JSONB_TEXT;
if( sqlite3JsonId1(z[j])
- || (z[j]=='\\' && z[j+1]=='u' && jsonIs4Hex(&z[j+2]))
+ || (z[j]=='\\' && jsonIs4HexB(&z[j+1], &op))
){
int k = j+1;
while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0)
- || (z[k]=='\\' && z[k+1]=='u' && jsonIs4Hex(&z[k+2]))
+ || (z[k]=='\\' && jsonIs4HexB(&z[k+1], &op))
){
k++;
}
- jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), k-j, &z[j]);
+ assert( iBlob==pParse->nBlob );
+ jsonBlobAppendNode(pParse, op, k-j, &z[j]);
pParse->hasNonstd = 1;
x = k;
}else{
@@ -202610,24 +206420,24 @@ json_parse_restart:
}
}
if( pParse->oom ) return -1;
- pNode = &pParse->aNode[nNode];
- if( pNode->eType!=JSON_STRING ){
+ t = pParse->aBlob[iBlob] & 0x0f;
+ if( t<JSONB_TEXT || t>JSONB_TEXTRAW ){
pParse->iErr = j;
return -1;
}
- pNode->jnFlags |= JNODE_LABEL;
j = x;
if( z[j]==':' ){
j++;
}else{
- if( fast_isspace(z[j]) ){
- do{ j++; }while( fast_isspace(z[j]) );
+ if( jsonIsspace(z[j]) ){
+ /* strspn() is not helpful here */
+ do{ j++; }while( jsonIsspace(z[j]) );
if( z[j]==':' ){
j++;
goto parse_object_value;
}
}
- x = jsonParseValue(pParse, j);
+ x = jsonTranslateTextToBlob(pParse, j);
if( x!=(-5) ){
if( x!=(-1) ) pParse->iErr = j;
return -1;
@@ -202635,7 +206445,7 @@ json_parse_restart:
j = pParse->iErr+1;
}
parse_object_value:
- x = jsonParseValue(pParse, j);
+ x = jsonTranslateTextToBlob(pParse, j);
if( x<=0 ){
if( x!=(-1) ) pParse->iErr = j;
return -1;
@@ -202646,15 +206456,15 @@ json_parse_restart:
}else if( z[j]=='}' ){
break;
}else{
- if( fast_isspace(z[j]) ){
- do{ j++; }while( fast_isspace(z[j]) );
+ if( jsonIsspace(z[j]) ){
+ j += 1 + (u32)strspn(&z[j+1], jsonSpaces);
if( z[j]==',' ){
continue;
}else if( z[j]=='}' ){
break;
}
}
- x = jsonParseValue(pParse, j);
+ x = jsonTranslateTextToBlob(pParse, j);
if( x==(-4) ){
j = pParse->iErr;
continue;
@@ -202667,25 +206477,27 @@ json_parse_restart:
pParse->iErr = j;
return -1;
}
- pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
+ jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart);
pParse->iDepth--;
return j+1;
}
case '[': {
/* Parse array */
- iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
- if( iThis<0 ) return -1;
+ iThis = pParse->nBlob;
+ assert( i<=(u32)pParse->nJson );
+ jsonBlobAppendNode(pParse, JSONB_ARRAY, pParse->nJson - i, 0);
+ iStart = pParse->nBlob;
+ if( pParse->oom ) return -1;
if( ++pParse->iDepth > JSON_MAX_DEPTH ){
pParse->iErr = i;
return -1;
}
- memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u));
for(j=i+1;;j++){
- x = jsonParseValue(pParse, j);
+ x = jsonTranslateTextToBlob(pParse, j);
if( x<=0 ){
if( x==(-3) ){
j = pParse->iErr;
- if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1;
+ if( pParse->nBlob!=iStart ) pParse->hasNonstd = 1;
break;
}
if( x!=(-1) ) pParse->iErr = j;
@@ -202697,15 +206509,15 @@ json_parse_restart:
}else if( z[j]==']' ){
break;
}else{
- if( fast_isspace(z[j]) ){
- do{ j++; }while( fast_isspace(z[j]) );
+ if( jsonIsspace(z[j]) ){
+ j += 1 + (u32)strspn(&z[j+1], jsonSpaces);
if( z[j]==',' ){
continue;
}else if( z[j]==']' ){
break;
}
}
- x = jsonParseValue(pParse, j);
+ x = jsonTranslateTextToBlob(pParse, j);
if( x==(-4) ){
j = pParse->iErr;
continue;
@@ -202718,23 +206530,33 @@ json_parse_restart:
pParse->iErr = j;
return -1;
}
- pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
+ jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart);
pParse->iDepth--;
return j+1;
}
case '\'': {
- u8 jnFlags;
+ u8 opcode;
char cDelim;
pParse->hasNonstd = 1;
- jnFlags = JNODE_JSON5;
+ opcode = JSONB_TEXT;
goto parse_string;
case '"':
/* Parse string */
- jnFlags = 0;
+ opcode = JSONB_TEXT;
parse_string:
cDelim = z[i];
- for(j=i+1; 1; j++){
- if( jsonIsOk[(unsigned char)z[j]] ) continue;
+ j = i+1;
+ while( 1 /*exit-by-break*/ ){
+ if( jsonIsOk[(u8)z[j]] ){
+ if( !jsonIsOk[(u8)z[j+1]] ){
+ j += 1;
+ }else if( !jsonIsOk[(u8)z[j+2]] ){
+ j += 2;
+ }else{
+ j += 3;
+ continue;
+ }
+ }
c = z[j];
if( c==cDelim ){
break;
@@ -202743,33 +206565,41 @@ json_parse_restart:
if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
|| c=='n' || c=='r' || c=='t'
|| (c=='u' && jsonIs4Hex(&z[j+1])) ){
- jnFlags |= JNODE_ESCAPE;
+ if( opcode==JSONB_TEXT ) opcode = JSONB_TEXTJ;
}else if( c=='\'' || c=='0' || c=='v' || c=='\n'
|| (0xe2==(u8)c && 0x80==(u8)z[j+1]
&& (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]))
|| (c=='x' && jsonIs2Hex(&z[j+1])) ){
- jnFlags |= (JNODE_ESCAPE|JNODE_JSON5);
+ opcode = JSONB_TEXT5;
pParse->hasNonstd = 1;
}else if( c=='\r' ){
if( z[j+1]=='\n' ) j++;
- jnFlags |= (JNODE_ESCAPE|JNODE_JSON5);
+ opcode = JSONB_TEXT5;
pParse->hasNonstd = 1;
}else{
pParse->iErr = j;
return -1;
}
}else if( c<=0x1f ){
- /* Control characters are not allowed in strings */
- pParse->iErr = j;
- return -1;
+ if( c==0 ){
+ pParse->iErr = j;
+ return -1;
+ }
+ /* Control characters are not allowed in canonical JSON string
+ ** literals, but are allowed in JSON5 string literals. */
+ opcode = JSONB_TEXT5;
+ pParse->hasNonstd = 1;
+ }else if( c=='"' ){
+ opcode = JSONB_TEXT5;
}
+ j++;
}
- jsonParseAddNode(pParse, JSON_STRING | (jnFlags<<8), j+1-i, &z[i]);
+ jsonBlobAppendNode(pParse, opcode, j-1-i, &z[i+1]);
return j+1;
}
case 't': {
if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){
- jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
+ jsonBlobAppendOneByte(pParse, JSONB_TRUE);
return i+4;
}
pParse->iErr = i;
@@ -202777,23 +206607,22 @@ json_parse_restart:
}
case 'f': {
if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){
- jsonParseAddNode(pParse, JSON_FALSE, 0, 0);
+ jsonBlobAppendOneByte(pParse, JSONB_FALSE);
return i+5;
}
pParse->iErr = i;
return -1;
}
case '+': {
- u8 seenDP, seenE, jnFlags;
+ u8 seenE;
pParse->hasNonstd = 1;
- jnFlags = JNODE_JSON5;
+ t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */
goto parse_number;
case '.':
if( sqlite3Isdigit(z[i+1]) ){
pParse->hasNonstd = 1;
- jnFlags = JNODE_JSON5;
+ t = 0x03; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */
seenE = 0;
- seenDP = JSON_REAL;
goto parse_number_2;
}
pParse->iErr = i;
@@ -202810,9 +206639,8 @@ json_parse_restart:
case '8':
case '9':
/* Parse number */
- jnFlags = 0;
+ t = 0x00; /* Bit 0x01: JSON5. Bit 0x02: FLOAT */
parse_number:
- seenDP = JSON_INT;
seenE = 0;
assert( '-' < '0' );
assert( '+' < '0' );
@@ -202822,9 +206650,9 @@ json_parse_restart:
if( c<='0' ){
if( c=='0' ){
if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){
- assert( seenDP==JSON_INT );
+ assert( t==0x00 );
pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
+ t = 0x01;
for(j=i+3; sqlite3Isxdigit(z[j]); j++){}
goto parse_number_finish;
}else if( sqlite3Isdigit(z[i+1]) ){
@@ -202841,15 +206669,15 @@ json_parse_restart:
){
pParse->hasNonstd = 1;
if( z[i]=='-' ){
- jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999");
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999");
}else{
- jsonParseAddNode(pParse, JSON_REAL, 7, "9.0e999");
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999");
}
return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4);
}
if( z[i+1]=='.' ){
pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
+ t |= 0x01;
goto parse_number_2;
}
pParse->iErr = i;
@@ -202861,30 +206689,31 @@ json_parse_restart:
return -1;
}else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){
pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
+ t |= 0x01;
for(j=i+4; sqlite3Isxdigit(z[j]); j++){}
goto parse_number_finish;
}
}
}
}
+
parse_number_2:
for(j=i+1;; j++){
c = z[j];
if( sqlite3Isdigit(c) ) continue;
if( c=='.' ){
- if( seenDP==JSON_REAL ){
+ if( (t & 0x02)!=0 ){
pParse->iErr = j;
return -1;
}
- seenDP = JSON_REAL;
+ t |= 0x02;
continue;
}
if( c=='e' || c=='E' ){
if( z[j-1]<'0' ){
if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
+ t |= 0x01;
}else{
pParse->iErr = j;
return -1;
@@ -202894,7 +206723,7 @@ json_parse_restart:
pParse->iErr = j;
return -1;
}
- seenDP = JSON_REAL;
+ t |= 0x02;
seenE = 1;
c = z[j+1];
if( c=='+' || c=='-' ){
@@ -202912,14 +206741,18 @@ json_parse_restart:
if( z[j-1]<'0' ){
if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
pParse->hasNonstd = 1;
- jnFlags |= JNODE_JSON5;
+ t |= 0x01;
}else{
pParse->iErr = j;
return -1;
}
}
parse_number_finish:
- jsonParseAddNode(pParse, seenDP | (jnFlags<<8), j - i, &z[i]);
+ assert( JSONB_INT+0x01==JSONB_INT5 );
+ assert( JSONB_FLOAT+0x01==JSONB_FLOAT5 );
+ assert( JSONB_INT+0x02==JSONB_FLOAT );
+ if( z[i]=='+' ) i++;
+ jsonBlobAppendNode(pParse, JSONB_INT+t, j-i, &z[i]);
return j;
}
case '}': {
@@ -202945,9 +206778,7 @@ json_parse_restart:
case 0x0a:
case 0x0d:
case 0x20: {
- do{
- i++;
- }while( fast_isspace(z[i]) );
+ i += 1 + (u32)strspn(&z[i+1], jsonSpaces);
goto json_parse_restart;
}
case 0x0b:
@@ -202969,10 +206800,11 @@ json_parse_restart:
}
case 'n': {
if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){
- jsonParseAddNode(pParse, JSON_NULL, 0, 0);
+ jsonBlobAppendOneByte(pParse, JSONB_NULL);
return i+4;
}
/* fall-through into the default case that checks for NaN */
+ /* no break */ deliberate_fall_through
}
default: {
u32 k;
@@ -202985,8 +206817,11 @@ json_parse_restart:
continue;
}
if( sqlite3Isalnum(z[i+nn]) ) continue;
- jsonParseAddNode(pParse, aNanInfName[k].eType,
- aNanInfName[k].nRepl, aNanInfName[k].zRepl);
+ if( aNanInfName[k].eType==JSONB_FLOAT ){
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999");
+ }else{
+ jsonBlobAppendOneByte(pParse, JSONB_NULL);
+ }
pParse->hasNonstd = 1;
return i + nn;
}
@@ -202996,6 +206831,7 @@ json_parse_restart:
} /* End switch(z[i]) */
}
+
/*
** Parse a complete JSON string. Return 0 on success or non-zero if there
** are any errors. If an error occurs, free all memory held by pParse,
@@ -203004,20 +206840,26 @@ json_parse_restart:
** pParse must be initialized to an empty parse object prior to calling
** this routine.
*/
-static int jsonParse(
+static int jsonConvertTextToBlob(
JsonParse *pParse, /* Initialize and fill this JsonParse object */
sqlite3_context *pCtx /* Report errors here */
){
int i;
const char *zJson = pParse->zJson;
- i = jsonParseValue(pParse, 0);
+ i = jsonTranslateTextToBlob(pParse, 0);
if( pParse->oom ) i = -1;
if( i>0 ){
+#ifdef SQLITE_DEBUG
assert( pParse->iDepth==0 );
- while( fast_isspace(zJson[i]) ) i++;
+ if( sqlite3Config.bJsonSelfcheck ){
+ assert( jsonbValidityCheck(pParse, 0, pParse->nBlob, 0)==0 );
+ }
+#endif
+ while( jsonIsspace(zJson[i]) ) i++;
if( zJson[i] ){
i += json5Whitespace(&zJson[i]);
if( zJson[i] ){
+ if( pCtx ) sqlite3_result_error(pCtx, "malformed JSON", -1);
jsonParseReset(pParse);
return 1;
}
@@ -203038,248 +206880,832 @@ static int jsonParse(
return 0;
}
+/*
+** The input string pStr is a well-formed JSON text string. Convert
+** this into the JSONB format and make it the return value of the
+** SQL function.
+*/
+static void jsonReturnStringAsBlob(JsonString *pStr){
+ JsonParse px;
+ memset(&px, 0, sizeof(px));
+ jsonStringTerminate(pStr);
+ if( pStr->eErr ){
+ sqlite3_result_error_nomem(pStr->pCtx);
+ return;
+ }
+ px.zJson = pStr->zBuf;
+ px.nJson = pStr->nUsed;
+ px.db = sqlite3_context_db_handle(pStr->pCtx);
+ (void)jsonTranslateTextToBlob(&px, 0);
+ if( px.oom ){
+ sqlite3DbFree(px.db, px.aBlob);
+ sqlite3_result_error_nomem(pStr->pCtx);
+ }else{
+ assert( px.nBlobAlloc>0 );
+ assert( !px.bReadOnly );
+ sqlite3_result_blob(pStr->pCtx, px.aBlob, px.nBlob, SQLITE_DYNAMIC);
+ }
+}
+
+/* The byte at index i is a node type-code. This routine
+** determines the payload size for that node and writes that
+** payload size in to *pSz. It returns the offset from i to the
+** beginning of the payload. Return 0 on error.
+*/
+static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){
+ u8 x;
+ u32 sz;
+ u32 n;
+ if( NEVER(i>pParse->nBlob) ){
+ *pSz = 0;
+ return 0;
+ }
+ x = pParse->aBlob[i]>>4;
+ if( x<=11 ){
+ sz = x;
+ n = 1;
+ }else if( x==12 ){
+ if( i+1>=pParse->nBlob ){
+ *pSz = 0;
+ return 0;
+ }
+ sz = pParse->aBlob[i+1];
+ n = 2;
+ }else if( x==13 ){
+ if( i+2>=pParse->nBlob ){
+ *pSz = 0;
+ return 0;
+ }
+ sz = (pParse->aBlob[i+1]<<8) + pParse->aBlob[i+2];
+ n = 3;
+ }else if( x==14 ){
+ if( i+4>=pParse->nBlob ){
+ *pSz = 0;
+ return 0;
+ }
+ sz = ((u32)pParse->aBlob[i+1]<<24) + (pParse->aBlob[i+2]<<16) +
+ (pParse->aBlob[i+3]<<8) + pParse->aBlob[i+4];
+ n = 5;
+ }else{
+ if( i+8>=pParse->nBlob
+ || pParse->aBlob[i+1]!=0
+ || pParse->aBlob[i+2]!=0
+ || pParse->aBlob[i+3]!=0
+ || pParse->aBlob[i+4]!=0
+ ){
+ *pSz = 0;
+ return 0;
+ }
+ sz = (pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) +
+ (pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8];
+ n = 9;
+ }
+ if( (i64)i+sz+n > pParse->nBlob
+ && (i64)i+sz+n > pParse->nBlob-pParse->delta
+ ){
+ sz = 0;
+ n = 0;
+ }
+ *pSz = sz;
+ return n;
+}
+
-/* Mark node i of pParse as being a child of iParent. Call recursively
-** to fill in all the descendants of node i.
+/*
+** Translate the binary JSONB representation of JSON beginning at
+** pParse->aBlob[i] into a JSON text string. Append the JSON
+** text onto the end of pOut. Return the index in pParse->aBlob[]
+** of the first byte past the end of the element that is translated.
+**
+** If an error is detected in the BLOB input, the pOut->eErr flag
+** might get set to JSTRING_MALFORMED. But not all BLOB input errors
+** are detected. So a malformed JSONB input might either result
+** in an error, or in incorrect JSON.
+**
+** The pOut->eErr JSTRING_OOM flag is set on a OOM.
*/
-static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){
- JsonNode *pNode = &pParse->aNode[i];
- u32 j;
- pParse->aUp[i] = iParent;
- switch( pNode->eType ){
- case JSON_ARRAY: {
- for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){
- jsonParseFillInParentage(pParse, i+j, i);
+static u32 jsonTranslateBlobToText(
+ const JsonParse *pParse, /* the complete parse of the JSON */
+ u32 i, /* Start rendering at this index */
+ JsonString *pOut /* Write JSON here */
+){
+ u32 sz, n, j, iEnd;
+
+ n = jsonbPayloadSize(pParse, i, &sz);
+ if( n==0 ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ return pParse->nBlob+1;
+ }
+ switch( pParse->aBlob[i] & 0x0f ){
+ case JSONB_NULL: {
+ jsonAppendRawNZ(pOut, "null", 4);
+ return i+1;
+ }
+ case JSONB_TRUE: {
+ jsonAppendRawNZ(pOut, "true", 4);
+ return i+1;
+ }
+ case JSONB_FALSE: {
+ jsonAppendRawNZ(pOut, "false", 5);
+ return i+1;
+ }
+ case JSONB_INT:
+ case JSONB_FLOAT: {
+ if( sz==0 ) goto malformed_jsonb;
+ jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz);
+ break;
+ }
+ case JSONB_INT5: { /* Integer literal in hexadecimal notation */
+ u32 k = 2;
+ sqlite3_uint64 u = 0;
+ const char *zIn = (const char*)&pParse->aBlob[i+n];
+ int bOverflow = 0;
+ if( sz==0 ) goto malformed_jsonb;
+ if( zIn[0]=='-' ){
+ jsonAppendChar(pOut, '-');
+ k++;
+ }else if( zIn[0]=='+' ){
+ k++;
}
+ for(; k<sz; k++){
+ if( !sqlite3Isxdigit(zIn[k]) ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ break;
+ }else if( (u>>60)!=0 ){
+ bOverflow = 1;
+ }else{
+ u = u*16 + sqlite3HexToInt(zIn[k]);
+ }
+ }
+ jsonPrintf(100,pOut,bOverflow?"9.0e999":"%llu", u);
break;
}
- case JSON_OBJECT: {
- for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){
- pParse->aUp[i+j] = i;
- jsonParseFillInParentage(pParse, i+j+1, i);
+ case JSONB_FLOAT5: { /* Float literal missing digits beside "." */
+ u32 k = 0;
+ const char *zIn = (const char*)&pParse->aBlob[i+n];
+ if( sz==0 ) goto malformed_jsonb;
+ if( zIn[0]=='-' ){
+ jsonAppendChar(pOut, '-');
+ k++;
+ }
+ if( zIn[k]=='.' ){
+ jsonAppendChar(pOut, '0');
+ }
+ for(; k<sz; k++){
+ jsonAppendChar(pOut, zIn[k]);
+ if( zIn[k]=='.' && (k+1==sz || !sqlite3Isdigit(zIn[k+1])) ){
+ jsonAppendChar(pOut, '0');
+ }
+ }
+ break;
+ }
+ case JSONB_TEXT:
+ case JSONB_TEXTJ: {
+ jsonAppendChar(pOut, '"');
+ jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz);
+ jsonAppendChar(pOut, '"');
+ break;
+ }
+ case JSONB_TEXT5: {
+ const char *zIn;
+ u32 k;
+ u32 sz2 = sz;
+ zIn = (const char*)&pParse->aBlob[i+n];
+ jsonAppendChar(pOut, '"');
+ while( sz2>0 ){
+ for(k=0; k<sz2 && (jsonIsOk[(u8)zIn[k]] || zIn[k]=='\''); k++){}
+ if( k>0 ){
+ jsonAppendRawNZ(pOut, zIn, k);
+ if( k>=sz2 ){
+ break;
+ }
+ zIn += k;
+ sz2 -= k;
+ }
+ if( zIn[0]=='"' ){
+ jsonAppendRawNZ(pOut, "\\\"", 2);
+ zIn++;
+ sz2--;
+ continue;
+ }
+ if( zIn[0]<=0x1f ){
+ if( pOut->nUsed+7>pOut->nAlloc && jsonStringGrow(pOut,7) ) break;
+ jsonAppendControlChar(pOut, zIn[0]);
+ zIn++;
+ sz2--;
+ continue;
+ }
+ assert( zIn[0]=='\\' );
+ assert( sz2>=1 );
+ if( sz2<2 ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ break;
+ }
+ switch( (u8)zIn[1] ){
+ case '\'':
+ jsonAppendChar(pOut, '\'');
+ break;
+ case 'v':
+ jsonAppendRawNZ(pOut, "\\u0009", 6);
+ break;
+ case 'x':
+ if( sz2<4 ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ sz2 = 2;
+ break;
+ }
+ jsonAppendRawNZ(pOut, "\\u00", 4);
+ jsonAppendRawNZ(pOut, &zIn[2], 2);
+ zIn += 2;
+ sz2 -= 2;
+ break;
+ case '0':
+ jsonAppendRawNZ(pOut, "\\u0000", 6);
+ break;
+ case '\r':
+ if( sz2>2 && zIn[2]=='\n' ){
+ zIn++;
+ sz2--;
+ }
+ break;
+ case '\n':
+ break;
+ case 0xe2:
+ /* '\' followed by either U+2028 or U+2029 is ignored as
+ ** whitespace. Not that in UTF8, U+2028 is 0xe2 0x80 0x29.
+ ** U+2029 is the same except for the last byte */
+ if( sz2<4
+ || 0x80!=(u8)zIn[2]
+ || (0xa8!=(u8)zIn[3] && 0xa9!=(u8)zIn[3])
+ ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ sz2 = 2;
+ break;
+ }
+ zIn += 2;
+ sz2 -= 2;
+ break;
+ default:
+ jsonAppendRawNZ(pOut, zIn, 2);
+ break;
+ }
+ assert( sz2>=2 );
+ zIn += 2;
+ sz2 -= 2;
}
+ jsonAppendChar(pOut, '"');
+ break;
+ }
+ case JSONB_TEXTRAW: {
+ jsonAppendString(pOut, (const char*)&pParse->aBlob[i+n], sz);
break;
}
+ case JSONB_ARRAY: {
+ jsonAppendChar(pOut, '[');
+ j = i+n;
+ iEnd = j+sz;
+ while( j<iEnd && pOut->eErr==0 ){
+ j = jsonTranslateBlobToText(pParse, j, pOut);
+ jsonAppendChar(pOut, ',');
+ }
+ if( j>iEnd ) pOut->eErr |= JSTRING_MALFORMED;
+ if( sz>0 ) jsonStringTrimOneChar(pOut);
+ jsonAppendChar(pOut, ']');
+ break;
+ }
+ case JSONB_OBJECT: {
+ int x = 0;
+ jsonAppendChar(pOut, '{');
+ j = i+n;
+ iEnd = j+sz;
+ while( j<iEnd && pOut->eErr==0 ){
+ j = jsonTranslateBlobToText(pParse, j, pOut);
+ jsonAppendChar(pOut, (x++ & 1) ? ',' : ':');
+ }
+ if( (x & 1)!=0 || j>iEnd ) pOut->eErr |= JSTRING_MALFORMED;
+ if( sz>0 ) jsonStringTrimOneChar(pOut);
+ jsonAppendChar(pOut, '}');
+ break;
+ }
+
default: {
+ malformed_jsonb:
+ pOut->eErr |= JSTRING_MALFORMED;
break;
}
}
+ return i+n+sz;
+}
+
+/* Context for recursion of json_pretty()
+*/
+typedef struct JsonPretty JsonPretty;
+struct JsonPretty {
+ JsonParse *pParse; /* The BLOB being rendered */
+ JsonString *pOut; /* Generate pretty output into this string */
+ const char *zIndent; /* Use this text for indentation */
+ u32 szIndent; /* Bytes in zIndent[] */
+ u32 nIndent; /* Current level of indentation */
+};
+
+/* Append indentation to the pretty JSON under construction */
+static void jsonPrettyIndent(JsonPretty *pPretty){
+ u32 jj;
+ for(jj=0; jj<pPretty->nIndent; jj++){
+ jsonAppendRaw(pPretty->pOut, pPretty->zIndent, pPretty->szIndent);
+ }
}
/*
-** Compute the parentage of all nodes in a completed parse.
+** Translate the binary JSONB representation of JSON beginning at
+** pParse->aBlob[i] into a JSON text string. Append the JSON
+** text onto the end of pOut. Return the index in pParse->aBlob[]
+** of the first byte past the end of the element that is translated.
+**
+** This is a variant of jsonTranslateBlobToText() that "pretty-prints"
+** the output. Extra whitespace is inserted to make the JSON easier
+** for humans to read.
+**
+** If an error is detected in the BLOB input, the pOut->eErr flag
+** might get set to JSTRING_MALFORMED. But not all BLOB input errors
+** are detected. So a malformed JSONB input might either result
+** in an error, or in incorrect JSON.
+**
+** The pOut->eErr JSTRING_OOM flag is set on a OOM.
*/
-static int jsonParseFindParents(JsonParse *pParse){
- u32 *aUp;
- assert( pParse->aUp==0 );
- aUp = pParse->aUp = sqlite3_malloc64( sizeof(u32)*pParse->nNode );
- if( aUp==0 ){
- pParse->oom = 1;
- return SQLITE_NOMEM;
+static u32 jsonTranslateBlobToPrettyText(
+ JsonPretty *pPretty, /* Pretty-printing context */
+ u32 i /* Start rendering at this index */
+){
+ u32 sz, n, j, iEnd;
+ const JsonParse *pParse = pPretty->pParse;
+ JsonString *pOut = pPretty->pOut;
+ n = jsonbPayloadSize(pParse, i, &sz);
+ if( n==0 ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ return pParse->nBlob+1;
}
- jsonParseFillInParentage(pParse, 0, 0);
- return SQLITE_OK;
+ switch( pParse->aBlob[i] & 0x0f ){
+ case JSONB_ARRAY: {
+ j = i+n;
+ iEnd = j+sz;
+ jsonAppendChar(pOut, '[');
+ if( j<iEnd ){
+ jsonAppendChar(pOut, '\n');
+ pPretty->nIndent++;
+ while( pOut->eErr==0 ){
+ jsonPrettyIndent(pPretty);
+ j = jsonTranslateBlobToPrettyText(pPretty, j);
+ if( j>=iEnd ) break;
+ jsonAppendRawNZ(pOut, ",\n", 2);
+ }
+ jsonAppendChar(pOut, '\n');
+ pPretty->nIndent--;
+ jsonPrettyIndent(pPretty);
+ }
+ jsonAppendChar(pOut, ']');
+ i = iEnd;
+ break;
+ }
+ case JSONB_OBJECT: {
+ j = i+n;
+ iEnd = j+sz;
+ jsonAppendChar(pOut, '{');
+ if( j<iEnd ){
+ jsonAppendChar(pOut, '\n');
+ pPretty->nIndent++;
+ while( pOut->eErr==0 ){
+ jsonPrettyIndent(pPretty);
+ j = jsonTranslateBlobToText(pParse, j, pOut);
+ if( j>iEnd ){
+ pOut->eErr |= JSTRING_MALFORMED;
+ break;
+ }
+ jsonAppendRawNZ(pOut, ": ", 2);
+ j = jsonTranslateBlobToPrettyText(pPretty, j);
+ if( j>=iEnd ) break;
+ jsonAppendRawNZ(pOut, ",\n", 2);
+ }
+ jsonAppendChar(pOut, '\n');
+ pPretty->nIndent--;
+ jsonPrettyIndent(pPretty);
+ }
+ jsonAppendChar(pOut, '}');
+ i = iEnd;
+ break;
+ }
+ default: {
+ i = jsonTranslateBlobToText(pParse, i, pOut);
+ break;
+ }
+ }
+ return i;
+}
+
+
+/* Return true if the input pJson
+**
+** For performance reasons, this routine does not do a detailed check of the
+** input BLOB to ensure that it is well-formed. Hence, false positives are
+** possible. False negatives should never occur, however.
+*/
+static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){
+ u32 sz, n;
+ const u8 *aBlob;
+ int nBlob;
+ JsonParse s;
+ if( sqlite3_value_type(pJson)!=SQLITE_BLOB ) return 0;
+ aBlob = sqlite3_value_blob(pJson);
+ nBlob = sqlite3_value_bytes(pJson);
+ if( nBlob<1 ) return 0;
+ if( NEVER(aBlob==0) || (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0;
+ memset(&s, 0, sizeof(s));
+ s.aBlob = (u8*)aBlob;
+ s.nBlob = nBlob;
+ n = jsonbPayloadSize(&s, 0, &sz);
+ if( n==0 ) return 0;
+ if( sz+n!=(u32)nBlob ) return 0;
+ if( (aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0 ) return 0;
+ return sz+n==(u32)nBlob;
}
/*
-** Magic number used for the JSON parse cache in sqlite3_get_auxdata()
+** Given that a JSONB_ARRAY object starts at offset i, return
+** the number of entries in that array.
*/
-#define JSON_CACHE_ID (-429938) /* First cache entry */
-#define JSON_CACHE_SZ 4 /* Max number of cache entries */
+static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){
+ u32 n, sz, i, iEnd;
+ u32 k = 0;
+ n = jsonbPayloadSize(pParse, iRoot, &sz);
+ iEnd = iRoot+n+sz;
+ for(i=iRoot+n; n>0 && i<iEnd; i+=sz+n, k++){
+ n = jsonbPayloadSize(pParse, i, &sz);
+ }
+ return k;
+}
/*
-** Obtain a complete parse of the JSON found in the pJson argument
-**
-** Use the sqlite3_get_auxdata() cache to find a preexisting parse
-** if it is available. If the cache is not available or if it
-** is no longer valid, parse the JSON again and return the new parse.
-** Also register the new parse so that it will be available for
-** future sqlite3_get_auxdata() calls.
-**
-** If an error occurs and pErrCtx!=0 then report the error on pErrCtx
-** and return NULL.
-**
-** The returned pointer (if it is not NULL) is owned by the cache in
-** most cases, not the caller. The caller does NOT need to invoke
-** jsonParseFree(), in most cases.
+** Edit the payload size of the element at iRoot by the amount in
+** pParse->delta.
+*/
+static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){
+ u32 sz = 0;
+ u32 nBlob;
+ assert( pParse->delta!=0 );
+ assert( pParse->nBlobAlloc >= pParse->nBlob );
+ nBlob = pParse->nBlob;
+ pParse->nBlob = pParse->nBlobAlloc;
+ (void)jsonbPayloadSize(pParse, iRoot, &sz);
+ pParse->nBlob = nBlob;
+ sz += pParse->delta;
+ pParse->delta += jsonBlobChangePayloadSize(pParse, iRoot, sz);
+}
+
+/*
+** Modify the JSONB blob at pParse->aBlob by removing nDel bytes of
+** content beginning at iDel, and replacing them with nIns bytes of
+** content given by aIns.
**
-** Except, if an error occurs and pErrCtx==0 then return the JsonParse
-** object with JsonParse.nErr non-zero and the caller will own the JsonParse
-** object. In that case, it will be the responsibility of the caller to
-** invoke jsonParseFree(). To summarize:
+** nDel may be zero, in which case no bytes are removed. But iDel is
+** still important as new bytes will be insert beginning at iDel.
**
-** pErrCtx!=0 || p->nErr==0 ==> Return value p is owned by the
-** cache. Call does not need to
-** free it.
+** aIns may be zero, in which case space is created to hold nIns bytes
+** beginning at iDel, but that space is uninitialized.
**
-** pErrCtx==0 && p->nErr!=0 ==> Return value is owned by the caller
-** and so the caller must free it.
+** Set pParse->oom if an OOM occurs.
*/
-static JsonParse *jsonParseCached(
- sqlite3_context *pCtx, /* Context to use for cache search */
- sqlite3_value *pJson, /* Function param containing JSON text */
- sqlite3_context *pErrCtx, /* Write parse errors here if not NULL */
- int bUnedited /* No prior edits allowed */
+static void jsonBlobEdit(
+ JsonParse *pParse, /* The JSONB to be modified is in pParse->aBlob */
+ u32 iDel, /* First byte to be removed */
+ u32 nDel, /* Number of bytes to remove */
+ const u8 *aIns, /* Content to insert */
+ u32 nIns /* Bytes of content to insert */
){
- char *zJson = (char*)sqlite3_value_text(pJson);
- int nJson = sqlite3_value_bytes(pJson);
- JsonParse *p;
- JsonParse *pMatch = 0;
- int iKey;
- int iMinKey = 0;
- u32 iMinHold = 0xffffffff;
- u32 iMaxHold = 0;
- int bJsonRCStr;
+ i64 d = (i64)nIns - (i64)nDel;
+ if( d!=0 ){
+ if( pParse->nBlob + d > pParse->nBlobAlloc ){
+ jsonBlobExpand(pParse, pParse->nBlob+d);
+ if( pParse->oom ) return;
+ }
+ memmove(&pParse->aBlob[iDel+nIns],
+ &pParse->aBlob[iDel+nDel],
+ pParse->nBlob - (iDel+nDel));
+ pParse->nBlob += d;
+ pParse->delta += d;
+ }
+ if( nIns && aIns ) memcpy(&pParse->aBlob[iDel], aIns, nIns);
+}
- if( zJson==0 ) return 0;
- for(iKey=0; iKey<JSON_CACHE_SZ; iKey++){
- p = (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iKey);
- if( p==0 ){
- iMinKey = iKey;
- break;
+/*
+** Return the number of escaped newlines to be ignored.
+** An escaped newline is a one of the following byte sequences:
+**
+** 0x5c 0x0a
+** 0x5c 0x0d
+** 0x5c 0x0d 0x0a
+** 0x5c 0xe2 0x80 0xa8
+** 0x5c 0xe2 0x80 0xa9
+*/
+static u32 jsonBytesToBypass(const char *z, u32 n){
+ u32 i = 0;
+ while( i+1<n ){
+ if( z[i]!='\\' ) return i;
+ if( z[i+1]=='\n' ){
+ i += 2;
+ continue;
}
- if( pMatch==0
- && p->nJson==nJson
- && (p->hasMod==0 || bUnedited==0)
- && (p->zJson==zJson || memcmp(p->zJson,zJson,nJson)==0)
- ){
- p->nErr = 0;
- p->useMod = 0;
- pMatch = p;
- }else
- if( pMatch==0
- && p->zAlt!=0
- && bUnedited==0
- && p->nAlt==nJson
- && memcmp(p->zAlt, zJson, nJson)==0
- ){
- p->nErr = 0;
- p->useMod = 1;
- pMatch = p;
- }else if( p->iHold<iMinHold ){
- iMinHold = p->iHold;
- iMinKey = iKey;
+ if( z[i+1]=='\r' ){
+ if( i+2<n && z[i+2]=='\n' ){
+ i += 3;
+ }else{
+ i += 2;
+ }
+ continue;
}
- if( p->iHold>iMaxHold ){
- iMaxHold = p->iHold;
+ if( 0xe2==(u8)z[i+1]
+ && i+3<n
+ && 0x80==(u8)z[i+2]
+ && (0xa8==(u8)z[i+3] || 0xa9==(u8)z[i+3])
+ ){
+ i += 4;
+ continue;
}
+ break;
}
- if( pMatch ){
- /* The input JSON text was found in the cache. Use the preexisting
- ** parse of this JSON */
- pMatch->nErr = 0;
- pMatch->iHold = iMaxHold+1;
- assert( pMatch->nJPRef>0 ); /* pMatch is owned by the cache */
- return pMatch;
- }
+ return i;
+}
- /* The input JSON was not found anywhere in the cache. We will need
- ** to parse it ourselves and generate a new JsonParse object.
- */
- bJsonRCStr = sqlite3ValueIsOfClass(pJson,(void(*)(void*))sqlite3RCStrUnref);
- p = sqlite3_malloc64( sizeof(*p) + (bJsonRCStr ? 0 : nJson+1) );
- if( p==0 ){
- sqlite3_result_error_nomem(pCtx);
- return 0;
+/*
+** Input z[0..n] defines JSON escape sequence including the leading '\\'.
+** Decode that escape sequence into a single character. Write that
+** character into *piOut. Return the number of bytes in the escape sequence.
+**
+** If there is a syntax error of some kind (for example too few characters
+** after the '\\' to complete the encoding) then *piOut is set to
+** JSON_INVALID_CHAR.
+*/
+static u32 jsonUnescapeOneChar(const char *z, u32 n, u32 *piOut){
+ assert( n>0 );
+ assert( z[0]=='\\' );
+ if( n<2 ){
+ *piOut = JSON_INVALID_CHAR;
+ return n;
}
- memset(p, 0, sizeof(*p));
- if( bJsonRCStr ){
- p->zJson = sqlite3RCStrRef(zJson);
- p->bJsonIsRCStr = 1;
- }else{
- p->zJson = (char*)&p[1];
- memcpy(p->zJson, zJson, nJson+1);
+ switch( (u8)z[1] ){
+ case 'u': {
+ u32 v, vlo;
+ if( n<6 ){
+ *piOut = JSON_INVALID_CHAR;
+ return n;
+ }
+ v = jsonHexToInt4(&z[2]);
+ if( (v & 0xfc00)==0xd800
+ && n>=12
+ && z[6]=='\\'
+ && z[7]=='u'
+ && ((vlo = jsonHexToInt4(&z[8]))&0xfc00)==0xdc00
+ ){
+ *piOut = ((v&0x3ff)<<10) + (vlo&0x3ff) + 0x10000;
+ return 12;
+ }else{
+ *piOut = v;
+ return 6;
+ }
+ }
+ case 'b': { *piOut = '\b'; return 2; }
+ case 'f': { *piOut = '\f'; return 2; }
+ case 'n': { *piOut = '\n'; return 2; }
+ case 'r': { *piOut = '\r'; return 2; }
+ case 't': { *piOut = '\t'; return 2; }
+ case 'v': { *piOut = '\v'; return 2; }
+ case '0': { *piOut = 0; return 2; }
+ case '\'':
+ case '"':
+ case '/':
+ case '\\':{ *piOut = z[1]; return 2; }
+ case 'x': {
+ if( n<4 ){
+ *piOut = JSON_INVALID_CHAR;
+ return n;
+ }
+ *piOut = (jsonHexToInt(z[2])<<4) | jsonHexToInt(z[3]);
+ return 4;
+ }
+ case 0xe2:
+ case '\r':
+ case '\n': {
+ u32 nSkip = jsonBytesToBypass(z, n);
+ if( nSkip==0 ){
+ *piOut = JSON_INVALID_CHAR;
+ return n;
+ }else if( nSkip==n ){
+ *piOut = 0;
+ return n;
+ }else if( z[nSkip]=='\\' ){
+ return nSkip + jsonUnescapeOneChar(&z[nSkip], n-nSkip, piOut);
+ }else{
+ int sz = sqlite3Utf8ReadLimited((u8*)&z[nSkip], n-nSkip, piOut);
+ return nSkip + sz;
+ }
+ }
+ default: {
+ *piOut = JSON_INVALID_CHAR;
+ return 2;
+ }
}
- p->nJPRef = 1;
- if( jsonParse(p, pErrCtx) ){
- if( pErrCtx==0 ){
- p->nErr = 1;
- assert( p->nJPRef==1 ); /* Caller will own the new JsonParse object p */
- return p;
+}
+
+
+/*
+** Compare two object labels. Return 1 if they are equal and
+** 0 if they differ.
+**
+** In this version, we know that one or the other or both of the
+** two comparands contains an escape sequence.
+*/
+static SQLITE_NOINLINE int jsonLabelCompareEscaped(
+ const char *zLeft, /* The left label */
+ u32 nLeft, /* Size of the left label in bytes */
+ int rawLeft, /* True if zLeft contains no escapes */
+ const char *zRight, /* The right label */
+ u32 nRight, /* Size of the right label in bytes */
+ int rawRight /* True if zRight is escape-free */
+){
+ u32 cLeft, cRight;
+ assert( rawLeft==0 || rawRight==0 );
+ while( 1 /*exit-by-return*/ ){
+ if( nLeft==0 ){
+ cLeft = 0;
+ }else if( rawLeft || zLeft[0]!='\\' ){
+ cLeft = ((u8*)zLeft)[0];
+ if( cLeft>=0xc0 ){
+ int sz = sqlite3Utf8ReadLimited((u8*)zLeft, nLeft, &cLeft);
+ zLeft += sz;
+ nLeft -= sz;
+ }else{
+ zLeft++;
+ nLeft--;
+ }
+ }else{
+ u32 n = jsonUnescapeOneChar(zLeft, nLeft, &cLeft);
+ zLeft += n;
+ assert( n<=nLeft );
+ nLeft -= n;
+ }
+ if( nRight==0 ){
+ cRight = 0;
+ }else if( rawRight || zRight[0]!='\\' ){
+ cRight = ((u8*)zRight)[0];
+ if( cRight>=0xc0 ){
+ int sz = sqlite3Utf8ReadLimited((u8*)zRight, nRight, &cRight);
+ zRight += sz;
+ nRight -= sz;
+ }else{
+ zRight++;
+ nRight--;
+ }
+ }else{
+ u32 n = jsonUnescapeOneChar(zRight, nRight, &cRight);
+ zRight += n;
+ assert( n<=nRight );
+ nRight -= n;
}
- jsonParseFree(p);
- return 0;
+ if( cLeft!=cRight ) return 0;
+ if( cLeft==0 ) return 1;
}
- p->nJson = nJson;
- p->iHold = iMaxHold+1;
- /* Transfer ownership of the new JsonParse to the cache */
- sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p,
- (void(*)(void*))jsonParseFree);
- return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey);
}
/*
-** Compare the OBJECT label at pNode against zKey,nKey. Return true on
-** a match.
+** Compare two object labels. Return 1 if they are equal and
+** 0 if they differ. Return -1 if an OOM occurs.
*/
-static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){
- assert( pNode->eU==1 );
- if( pNode->jnFlags & JNODE_RAW ){
- if( pNode->n!=nKey ) return 0;
- return strncmp(pNode->u.zJContent, zKey, nKey)==0;
+static int jsonLabelCompare(
+ const char *zLeft, /* The left label */
+ u32 nLeft, /* Size of the left label in bytes */
+ int rawLeft, /* True if zLeft contains no escapes */
+ const char *zRight, /* The right label */
+ u32 nRight, /* Size of the right label in bytes */
+ int rawRight /* True if zRight is escape-free */
+){
+ if( rawLeft && rawRight ){
+ /* Simpliest case: Neither label contains escapes. A simple
+ ** memcmp() is sufficient. */
+ if( nLeft!=nRight ) return 0;
+ return memcmp(zLeft, zRight, nLeft)==0;
}else{
- if( pNode->n!=nKey+2 ) return 0;
- return strncmp(pNode->u.zJContent+1, zKey, nKey)==0;
+ return jsonLabelCompareEscaped(zLeft, nLeft, rawLeft,
+ zRight, nRight, rawRight);
}
}
-static int jsonSameLabel(const JsonNode *p1, const JsonNode *p2){
- if( p1->jnFlags & JNODE_RAW ){
- return jsonLabelCompare(p2, p1->u.zJContent, p1->n);
- }else if( p2->jnFlags & JNODE_RAW ){
- return jsonLabelCompare(p1, p2->u.zJContent, p2->n);
+
+/*
+** Error returns from jsonLookupStep()
+*/
+#define JSON_LOOKUP_ERROR 0xffffffff
+#define JSON_LOOKUP_NOTFOUND 0xfffffffe
+#define JSON_LOOKUP_PATHERROR 0xfffffffd
+#define JSON_LOOKUP_ISERROR(x) ((x)>=JSON_LOOKUP_PATHERROR)
+
+/* Forward declaration */
+static u32 jsonLookupStep(JsonParse*,u32,const char*,u32);
+
+
+/* This helper routine for jsonLookupStep() populates pIns with
+** binary data that is to be inserted into pParse.
+**
+** In the common case, pIns just points to pParse->aIns and pParse->nIns.
+** But if the zPath of the original edit operation includes path elements
+** that go deeper, additional substructure must be created.
+**
+** For example:
+**
+** json_insert('{}', '$.a.b.c', 123);
+**
+** The search stops at '$.a' But additional substructure must be
+** created for the ".b.c" part of the patch so that the final result
+** is: {"a":{"b":{"c"::123}}}. This routine populates pIns with
+** the binary equivalent of {"b":{"c":123}} so that it can be inserted.
+**
+** The caller is responsible for resetting pIns when it has finished
+** using the substructure.
+*/
+static u32 jsonCreateEditSubstructure(
+ JsonParse *pParse, /* The original JSONB that is being edited */
+ JsonParse *pIns, /* Populate this with the blob data to insert */
+ const char *zTail /* Tail of the path that determins substructure */
+){
+ static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT };
+ int rc;
+ memset(pIns, 0, sizeof(*pIns));
+ pIns->db = pParse->db;
+ if( zTail[0]==0 ){
+ /* No substructure. Just insert what is given in pParse. */
+ pIns->aBlob = pParse->aIns;
+ pIns->nBlob = pParse->nIns;
+ rc = 0;
}else{
- return p1->n==p2->n && strncmp(p1->u.zJContent,p2->u.zJContent,p1->n)==0;
+ /* Construct the binary substructure */
+ pIns->nBlob = 1;
+ pIns->aBlob = (u8*)&emptyObject[zTail[0]=='.'];
+ pIns->eEdit = pParse->eEdit;
+ pIns->nIns = pParse->nIns;
+ pIns->aIns = pParse->aIns;
+ rc = jsonLookupStep(pIns, 0, zTail, 0);
+ pParse->oom |= pIns->oom;
}
+ return rc; /* Error code only */
}
-/* forward declaration */
-static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**);
-
/*
-** Search along zPath to find the node specified. Return a pointer
-** to that node, or NULL if zPath is malformed or if there is no such
-** node.
+** Search along zPath to find the Json element specified. Return an
+** index into pParse->aBlob[] for the start of that element's value.
+**
+** If the value found by this routine is the value half of label/value pair
+** within an object, then set pPath->iLabel to the start of the corresponding
+** label, before returning.
+**
+** Return one of the JSON_LOOKUP error codes if problems are seen.
**
-** If pApnd!=0, then try to append new nodes to complete zPath if it is
-** possible to do so and if no existing node corresponds to zPath. If
-** new nodes are appended *pApnd is set to 1.
+** This routine will also modify the blob. If pParse->eEdit is one of
+** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, or JEDIT_SET, then changes might be
+** made to the selected value. If an edit is performed, then the return
+** value does not necessarily point to the select element. If an edit
+** is performed, the return value is only useful for detecting error
+** conditions.
*/
-static JsonNode *jsonLookupStep(
+static u32 jsonLookupStep(
JsonParse *pParse, /* The JSON to search */
- u32 iRoot, /* Begin the search at this node */
+ u32 iRoot, /* Begin the search at this element of aBlob[] */
const char *zPath, /* The path to search */
- int *pApnd, /* Append nodes to complete path if not NULL */
- const char **pzErr /* Make *pzErr point to any syntax error in zPath */
+ u32 iLabel /* Label if iRoot is a value of in an object */
){
- u32 i, j, nKey;
+ u32 i, j, k, nKey, sz, n, iEnd, rc;
const char *zKey;
- JsonNode *pRoot;
- if( pParse->oom ) return 0;
- pRoot = &pParse->aNode[iRoot];
- if( pRoot->jnFlags & (JNODE_REPLACE|JNODE_REMOVE) && pParse->useMod ){
- while( (pRoot->jnFlags & JNODE_REPLACE)!=0 ){
- u32 idx = (u32)(pRoot - pParse->aNode);
- i = pParse->iSubst;
- while( 1 /*exit-by-break*/ ){
- assert( i<pParse->nNode );
- assert( pParse->aNode[i].eType==JSON_SUBST );
- assert( pParse->aNode[i].eU==4 );
- assert( pParse->aNode[i].u.iPrev<i );
- if( pParse->aNode[i].n==idx ){
- pRoot = &pParse->aNode[i+1];
- iRoot = i+1;
- break;
- }
- i = pParse->aNode[i].u.iPrev;
+ u8 x;
+
+ if( zPath[0]==0 ){
+ if( pParse->eEdit && jsonBlobMakeEditable(pParse, pParse->nIns) ){
+ n = jsonbPayloadSize(pParse, iRoot, &sz);
+ sz += n;
+ if( pParse->eEdit==JEDIT_DEL ){
+ if( iLabel>0 ){
+ sz += iRoot - iLabel;
+ iRoot = iLabel;
+ }
+ jsonBlobEdit(pParse, iRoot, sz, 0, 0);
+ }else if( pParse->eEdit==JEDIT_INS ){
+ /* Already exists, so json_insert() is a no-op */
+ }else{
+ /* json_set() or json_replace() */
+ jsonBlobEdit(pParse, iRoot, sz, pParse->aIns, pParse->nIns);
}
}
- if( pRoot->jnFlags & JNODE_REMOVE ){
- return 0;
- }
+ pParse->iLabel = iLabel;
+ return iRoot;
}
- if( zPath[0]==0 ) return pRoot;
if( zPath[0]=='.' ){
- if( pRoot->eType!=JSON_OBJECT ) return 0;
+ int rawKey = 1;
+ x = pParse->aBlob[iRoot];
zPath++;
if( zPath[0]=='"' ){
zKey = zPath + 1;
@@ -203288,250 +207714,705 @@ static JsonNode *jsonLookupStep(
if( zPath[i] ){
i++;
}else{
- *pzErr = zPath;
- return 0;
+ return JSON_LOOKUP_PATHERROR;
}
testcase( nKey==0 );
+ rawKey = memchr(zKey, '\\', nKey)==0;
}else{
zKey = zPath;
for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
nKey = i;
if( nKey==0 ){
- *pzErr = zPath;
- return 0;
- }
- }
- j = 1;
- for(;;){
- while( j<=pRoot->n ){
- if( jsonLabelCompare(pRoot+j, zKey, nKey) ){
- return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr);
- }
- j++;
- j += jsonNodeSize(&pRoot[j]);
+ return JSON_LOOKUP_PATHERROR;
+ }
+ }
+ if( (x & 0x0f)!=JSONB_OBJECT ) return JSON_LOOKUP_NOTFOUND;
+ n = jsonbPayloadSize(pParse, iRoot, &sz);
+ j = iRoot + n; /* j is the index of a label */
+ iEnd = j+sz;
+ while( j<iEnd ){
+ int rawLabel;
+ const char *zLabel;
+ x = pParse->aBlob[j] & 0x0f;
+ if( x<JSONB_TEXT || x>JSONB_TEXTRAW ) return JSON_LOOKUP_ERROR;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return JSON_LOOKUP_ERROR;
+ k = j+n; /* k is the index of the label text */
+ if( k+sz>=iEnd ) return JSON_LOOKUP_ERROR;
+ zLabel = (const char*)&pParse->aBlob[k];
+ rawLabel = x==JSONB_TEXT || x==JSONB_TEXTRAW;
+ if( jsonLabelCompare(zKey, nKey, rawKey, zLabel, sz, rawLabel) ){
+ u32 v = k+sz; /* v is the index of the value */
+ if( ((pParse->aBlob[v])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR;
+ n = jsonbPayloadSize(pParse, v, &sz);
+ if( n==0 || v+n+sz>iEnd ) return JSON_LOOKUP_ERROR;
+ assert( j>0 );
+ rc = jsonLookupStep(pParse, v, &zPath[i], j);
+ if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
+ return rc;
}
- if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
- if( pParse->useMod==0 ) break;
- assert( pRoot->eU==2 );
- iRoot = pRoot->u.iAppend;
- pRoot = &pParse->aNode[iRoot];
- j = 1;
- }
- if( pApnd ){
- u32 iStart, iLabel;
- JsonNode *pNode;
- assert( pParse->useMod );
- iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);
- iLabel = jsonParseAddNode(pParse, JSON_STRING, nKey, zKey);
- zPath += i;
- pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
- if( pParse->oom ) return 0;
- if( pNode ){
- pRoot = &pParse->aNode[iRoot];
- assert( pRoot->eU==0 );
- pRoot->u.iAppend = iStart;
- pRoot->jnFlags |= JNODE_APPEND;
- VVA( pRoot->eU = 2 );
- pParse->aNode[iLabel].jnFlags |= JNODE_RAW;
- }
- return pNode;
+ j = k+sz;
+ if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return JSON_LOOKUP_ERROR;
+ j += n+sz;
+ }
+ if( j>iEnd ) return JSON_LOOKUP_ERROR;
+ if( pParse->eEdit>=JEDIT_INS ){
+ u32 nIns; /* Total bytes to insert (label+value) */
+ JsonParse v; /* BLOB encoding of the value to be inserted */
+ JsonParse ix; /* Header of the label to be inserted */
+ testcase( pParse->eEdit==JEDIT_INS );
+ testcase( pParse->eEdit==JEDIT_SET );
+ memset(&ix, 0, sizeof(ix));
+ ix.db = pParse->db;
+ jsonBlobAppendNode(&ix, rawKey?JSONB_TEXTRAW:JSONB_TEXT5, nKey, 0);
+ pParse->oom |= ix.oom;
+ rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i]);
+ if( !JSON_LOOKUP_ISERROR(rc)
+ && jsonBlobMakeEditable(pParse, ix.nBlob+nKey+v.nBlob)
+ ){
+ assert( !pParse->oom );
+ nIns = ix.nBlob + nKey + v.nBlob;
+ jsonBlobEdit(pParse, j, 0, 0, nIns);
+ if( !pParse->oom ){
+ assert( pParse->aBlob!=0 ); /* Because pParse->oom!=0 */
+ assert( ix.aBlob!=0 ); /* Because pPasre->oom!=0 */
+ memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob);
+ k = j + ix.nBlob;
+ memcpy(&pParse->aBlob[k], zKey, nKey);
+ k += nKey;
+ memcpy(&pParse->aBlob[k], v.aBlob, v.nBlob);
+ if( ALWAYS(pParse->delta) ) jsonAfterEditSizeAdjust(pParse, iRoot);
+ }
+ }
+ jsonParseReset(&v);
+ jsonParseReset(&ix);
+ return rc;
}
}else if( zPath[0]=='[' ){
- i = 0;
- j = 1;
- while( sqlite3Isdigit(zPath[j]) ){
- i = i*10 + zPath[j] - '0';
- j++;
+ x = pParse->aBlob[iRoot] & 0x0f;
+ if( x!=JSONB_ARRAY ) return JSON_LOOKUP_NOTFOUND;
+ n = jsonbPayloadSize(pParse, iRoot, &sz);
+ k = 0;
+ i = 1;
+ while( sqlite3Isdigit(zPath[i]) ){
+ k = k*10 + zPath[i] - '0';
+ i++;
}
- if( j<2 || zPath[j]!=']' ){
+ if( i<2 || zPath[i]!=']' ){
if( zPath[1]=='#' ){
- JsonNode *pBase = pRoot;
- int iBase = iRoot;
- if( pRoot->eType!=JSON_ARRAY ) return 0;
- for(;;){
- while( j<=pBase->n ){
- if( (pBase[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i++;
- j += jsonNodeSize(&pBase[j]);
- }
- if( (pBase->jnFlags & JNODE_APPEND)==0 ) break;
- if( pParse->useMod==0 ) break;
- assert( pBase->eU==2 );
- iBase = pBase->u.iAppend;
- pBase = &pParse->aNode[iBase];
- j = 1;
- }
- j = 2;
+ k = jsonbArrayCount(pParse, iRoot);
+ i = 2;
if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){
- unsigned int x = 0;
- j = 3;
+ unsigned int nn = 0;
+ i = 3;
do{
- x = x*10 + zPath[j] - '0';
- j++;
- }while( sqlite3Isdigit(zPath[j]) );
- if( x>i ) return 0;
- i -= x;
+ nn = nn*10 + zPath[i] - '0';
+ i++;
+ }while( sqlite3Isdigit(zPath[i]) );
+ if( nn>k ) return JSON_LOOKUP_NOTFOUND;
+ k -= nn;
}
- if( zPath[j]!=']' ){
- *pzErr = zPath;
- return 0;
+ if( zPath[i]!=']' ){
+ return JSON_LOOKUP_PATHERROR;
}
}else{
- *pzErr = zPath;
- return 0;
+ return JSON_LOOKUP_PATHERROR;
}
}
- if( pRoot->eType!=JSON_ARRAY ) return 0;
- zPath += j + 1;
- j = 1;
- for(;;){
- while( j<=pRoot->n
- && (i>0 || ((pRoot[j].jnFlags & JNODE_REMOVE)!=0 && pParse->useMod))
+ j = iRoot+n;
+ iEnd = j+sz;
+ while( j<iEnd ){
+ if( k==0 ){
+ rc = jsonLookupStep(pParse, j, &zPath[i+1], 0);
+ if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
+ return rc;
+ }
+ k--;
+ n = jsonbPayloadSize(pParse, j, &sz);
+ if( n==0 ) return JSON_LOOKUP_ERROR;
+ j += n+sz;
+ }
+ if( j>iEnd ) return JSON_LOOKUP_ERROR;
+ if( k>0 ) return JSON_LOOKUP_NOTFOUND;
+ if( pParse->eEdit>=JEDIT_INS ){
+ JsonParse v;
+ testcase( pParse->eEdit==JEDIT_INS );
+ testcase( pParse->eEdit==JEDIT_SET );
+ rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i+1]);
+ if( !JSON_LOOKUP_ISERROR(rc)
+ && jsonBlobMakeEditable(pParse, v.nBlob)
){
- if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i--;
- j += jsonNodeSize(&pRoot[j]);
- }
- if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
- if( pParse->useMod==0 ) break;
- assert( pRoot->eU==2 );
- iRoot = pRoot->u.iAppend;
- pRoot = &pParse->aNode[iRoot];
- j = 1;
- }
- if( j<=pRoot->n ){
- return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr);
- }
- if( i==0 && pApnd ){
- u32 iStart;
- JsonNode *pNode;
- assert( pParse->useMod );
- iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0);
- pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
- if( pParse->oom ) return 0;
- if( pNode ){
- pRoot = &pParse->aNode[iRoot];
- assert( pRoot->eU==0 );
- pRoot->u.iAppend = iStart;
- pRoot->jnFlags |= JNODE_APPEND;
- VVA( pRoot->eU = 2 );
+ assert( !pParse->oom );
+ jsonBlobEdit(pParse, j, 0, v.aBlob, v.nBlob);
}
- return pNode;
+ jsonParseReset(&v);
+ if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
+ return rc;
}
}else{
- *pzErr = zPath;
+ return JSON_LOOKUP_PATHERROR;
}
- return 0;
+ return JSON_LOOKUP_NOTFOUND;
}
/*
-** Append content to pParse that will complete zPath. Return a pointer
-** to the inserted node, or return NULL if the append fails.
+** Convert a JSON BLOB into text and make that text the return value
+** of an SQL function.
*/
-static JsonNode *jsonLookupAppend(
- JsonParse *pParse, /* Append content to the JSON parse */
- const char *zPath, /* Description of content to append */
- int *pApnd, /* Set this flag to 1 */
- const char **pzErr /* Make this point to any syntax error */
+static void jsonReturnTextJsonFromBlob(
+ sqlite3_context *ctx,
+ const u8 *aBlob,
+ u32 nBlob
){
- *pApnd = 1;
- if( zPath[0]==0 ){
- jsonParseAddNode(pParse, JSON_NULL, 0, 0);
- return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1];
- }
- if( zPath[0]=='.' ){
- jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
- }else if( strncmp(zPath,"[0]",3)==0 ){
- jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
- }else{
- return 0;
- }
- if( pParse->oom ) return 0;
- return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr);
+ JsonParse x;
+ JsonString s;
+
+ if( NEVER(aBlob==0) ) return;
+ memset(&x, 0, sizeof(x));
+ x.aBlob = (u8*)aBlob;
+ x.nBlob = nBlob;
+ jsonStringInit(&s, ctx);
+ jsonTranslateBlobToText(&x, 0, &s);
+ jsonReturnString(&s, 0, 0);
}
+
/*
-** Return the text of a syntax error message on a JSON path. Space is
-** obtained from sqlite3_malloc().
+** Return the value of the BLOB node at index i.
+**
+** If the value is a primitive, return it as an SQL value.
+** If the value is an array or object, return it as either
+** JSON text or the BLOB encoding, depending on the JSON_B flag
+** on the userdata.
*/
-static char *jsonPathSyntaxError(const char *zErr){
- return sqlite3_mprintf("JSON path error near '%q'", zErr);
+static void jsonReturnFromBlob(
+ JsonParse *pParse, /* Complete JSON parse tree */
+ u32 i, /* Index of the node */
+ sqlite3_context *pCtx, /* Return value for this function */
+ int textOnly /* return text JSON. Disregard user-data */
+){
+ u32 n, sz;
+ int rc;
+ sqlite3 *db = sqlite3_context_db_handle(pCtx);
+
+ n = jsonbPayloadSize(pParse, i, &sz);
+ if( n==0 ){
+ sqlite3_result_error(pCtx, "malformed JSON", -1);
+ return;
+ }
+ switch( pParse->aBlob[i] & 0x0f ){
+ case JSONB_NULL: {
+ if( sz ) goto returnfromblob_malformed;
+ sqlite3_result_null(pCtx);
+ break;
+ }
+ case JSONB_TRUE: {
+ if( sz ) goto returnfromblob_malformed;
+ sqlite3_result_int(pCtx, 1);
+ break;
+ }
+ case JSONB_FALSE: {
+ if( sz ) goto returnfromblob_malformed;
+ sqlite3_result_int(pCtx, 0);
+ break;
+ }
+ case JSONB_INT5:
+ case JSONB_INT: {
+ sqlite3_int64 iRes = 0;
+ char *z;
+ int bNeg = 0;
+ char x;
+ if( sz==0 ) goto returnfromblob_malformed;
+ x = (char)pParse->aBlob[i+n];
+ if( x=='-' ){
+ if( sz<2 ) goto returnfromblob_malformed;
+ n++;
+ sz--;
+ bNeg = 1;
+ }
+ z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
+ if( z==0 ) goto returnfromblob_oom;
+ rc = sqlite3DecOrHexToI64(z, &iRes);
+ sqlite3DbFree(db, z);
+ if( rc==0 ){
+ sqlite3_result_int64(pCtx, bNeg ? -iRes : iRes);
+ }else if( rc==3 && bNeg ){
+ sqlite3_result_int64(pCtx, SMALLEST_INT64);
+ }else if( rc==1 ){
+ goto returnfromblob_malformed;
+ }else{
+ if( bNeg ){ n--; sz++; }
+ goto to_double;
+ }
+ break;
+ }
+ case JSONB_FLOAT5:
+ case JSONB_FLOAT: {
+ double r;
+ char *z;
+ if( sz==0 ) goto returnfromblob_malformed;
+ to_double:
+ z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
+ if( z==0 ) goto returnfromblob_oom;
+ rc = sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
+ sqlite3DbFree(db, z);
+ if( rc<=0 ) goto returnfromblob_malformed;
+ sqlite3_result_double(pCtx, r);
+ break;
+ }
+ case JSONB_TEXTRAW:
+ case JSONB_TEXT: {
+ sqlite3_result_text(pCtx, (char*)&pParse->aBlob[i+n], sz,
+ SQLITE_TRANSIENT);
+ break;
+ }
+ case JSONB_TEXT5:
+ case JSONB_TEXTJ: {
+ /* Translate JSON formatted string into raw text */
+ u32 iIn, iOut;
+ const char *z;
+ char *zOut;
+ u32 nOut = sz;
+ z = (const char*)&pParse->aBlob[i+n];
+ zOut = sqlite3DbMallocRaw(db, nOut+1);
+ if( zOut==0 ) goto returnfromblob_oom;
+ for(iIn=iOut=0; iIn<sz; iIn++){
+ char c = z[iIn];
+ if( c=='\\' ){
+ u32 v;
+ u32 szEscape = jsonUnescapeOneChar(&z[iIn], sz-iIn, &v);
+ if( v<=0x7f ){
+ zOut[iOut++] = (char)v;
+ }else if( v<=0x7ff ){
+ assert( szEscape>=2 );
+ zOut[iOut++] = (char)(0xc0 | (v>>6));
+ zOut[iOut++] = 0x80 | (v&0x3f);
+ }else if( v<0x10000 ){
+ assert( szEscape>=3 );
+ zOut[iOut++] = 0xe0 | (v>>12);
+ zOut[iOut++] = 0x80 | ((v>>6)&0x3f);
+ zOut[iOut++] = 0x80 | (v&0x3f);
+ }else if( v==JSON_INVALID_CHAR ){
+ /* Silently ignore illegal unicode */
+ }else{
+ assert( szEscape>=4 );
+ zOut[iOut++] = 0xf0 | (v>>18);
+ zOut[iOut++] = 0x80 | ((v>>12)&0x3f);
+ zOut[iOut++] = 0x80 | ((v>>6)&0x3f);
+ zOut[iOut++] = 0x80 | (v&0x3f);
+ }
+ iIn += szEscape - 1;
+ }else{
+ zOut[iOut++] = c;
+ }
+ } /* end for() */
+ assert( iOut<=nOut );
+ zOut[iOut] = 0;
+ sqlite3_result_text(pCtx, zOut, iOut, SQLITE_DYNAMIC);
+ break;
+ }
+ case JSONB_ARRAY:
+ case JSONB_OBJECT: {
+ int flags = textOnly ? 0 : SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx));
+ if( flags & JSON_BLOB ){
+ sqlite3_result_blob(pCtx, &pParse->aBlob[i], sz+n, SQLITE_TRANSIENT);
+ }else{
+ jsonReturnTextJsonFromBlob(pCtx, &pParse->aBlob[i], sz+n);
+ }
+ break;
+ }
+ default: {
+ goto returnfromblob_malformed;
+ }
+ }
+ return;
+
+returnfromblob_oom:
+ sqlite3_result_error_nomem(pCtx);
+ return;
+
+returnfromblob_malformed:
+ sqlite3_result_error(pCtx, "malformed JSON", -1);
+ return;
}
/*
-** Do a node lookup using zPath. Return a pointer to the node on success.
-** Return NULL if not found or if there is an error.
+** pArg is a function argument that might be an SQL value or a JSON
+** value. Figure out what it is and encode it as a JSONB blob.
+** Return the results in pParse.
**
-** On an error, write an error message into pCtx and increment the
-** pParse->nErr counter.
+** pParse is uninitialized upon entry. This routine will handle the
+** initialization of pParse. The result will be contained in
+** pParse->aBlob and pParse->nBlob. pParse->aBlob might be dynamically
+** allocated (if pParse->nBlobAlloc is greater than zero) in which case
+** the caller is responsible for freeing the space allocated to pParse->aBlob
+** when it has finished with it. Or pParse->aBlob might be a static string
+** or a value obtained from sqlite3_value_blob(pArg).
**
-** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if
-** nodes are appended.
+** If the argument is a BLOB that is clearly not a JSONB, then this
+** function might set an error message in ctx and return non-zero.
+** It might also set an error message and return non-zero on an OOM error.
*/
-static JsonNode *jsonLookup(
- JsonParse *pParse, /* The JSON to search */
- const char *zPath, /* The path to search */
- int *pApnd, /* Append nodes to complete path if not NULL */
- sqlite3_context *pCtx /* Report errors here, if not NULL */
-){
- const char *zErr = 0;
- JsonNode *pNode = 0;
- char *zMsg;
-
- if( zPath==0 ) return 0;
- if( zPath[0]!='$' ){
- zErr = zPath;
- goto lookup_err;
+static int jsonFunctionArgToBlob(
+ sqlite3_context *ctx,
+ sqlite3_value *pArg,
+ JsonParse *pParse
+){
+ int eType = sqlite3_value_type(pArg);
+ static u8 aNull[] = { 0x00 };
+ memset(pParse, 0, sizeof(pParse[0]));
+ pParse->db = sqlite3_context_db_handle(ctx);
+ switch( eType ){
+ default: {
+ pParse->aBlob = aNull;
+ pParse->nBlob = 1;
+ return 0;
+ }
+ case SQLITE_BLOB: {
+ if( jsonFuncArgMightBeBinary(pArg) ){
+ pParse->aBlob = (u8*)sqlite3_value_blob(pArg);
+ pParse->nBlob = sqlite3_value_bytes(pArg);
+ }else{
+ sqlite3_result_error(ctx, "JSON cannot hold BLOB values", -1);
+ return 1;
+ }
+ break;
+ }
+ case SQLITE_TEXT: {
+ const char *zJson = (const char*)sqlite3_value_text(pArg);
+ int nJson = sqlite3_value_bytes(pArg);
+ if( zJson==0 ) return 1;
+ if( sqlite3_value_subtype(pArg)==JSON_SUBTYPE ){
+ pParse->zJson = (char*)zJson;
+ pParse->nJson = nJson;
+ if( jsonConvertTextToBlob(pParse, ctx) ){
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ sqlite3DbFree(pParse->db, pParse->aBlob);
+ memset(pParse, 0, sizeof(pParse[0]));
+ return 1;
+ }
+ }else{
+ jsonBlobAppendNode(pParse, JSONB_TEXTRAW, nJson, zJson);
+ }
+ break;
+ }
+ case SQLITE_FLOAT: {
+ double r = sqlite3_value_double(pArg);
+ if( NEVER(sqlite3IsNaN(r)) ){
+ jsonBlobAppendNode(pParse, JSONB_NULL, 0, 0);
+ }else{
+ int n = sqlite3_value_bytes(pArg);
+ const char *z = (const char*)sqlite3_value_text(pArg);
+ if( z==0 ) return 1;
+ if( z[0]=='I' ){
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999");
+ }else if( z[0]=='-' && z[1]=='I' ){
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999");
+ }else{
+ jsonBlobAppendNode(pParse, JSONB_FLOAT, n, z);
+ }
+ }
+ break;
+ }
+ case SQLITE_INTEGER: {
+ int n = sqlite3_value_bytes(pArg);
+ const char *z = (const char*)sqlite3_value_text(pArg);
+ if( z==0 ) return 1;
+ jsonBlobAppendNode(pParse, JSONB_INT, n, z);
+ break;
+ }
+ }
+ if( pParse->oom ){
+ sqlite3_result_error_nomem(ctx);
+ return 1;
+ }else{
+ return 0;
}
- zPath++;
- pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr);
- if( zErr==0 ) return pNode;
+}
-lookup_err:
- pParse->nErr++;
- assert( zErr!=0 && pCtx!=0 );
- zMsg = jsonPathSyntaxError(zErr);
+/*
+** Generate a bad path error.
+**
+** If ctx is not NULL then push the error message into ctx and return NULL.
+** If ctx is NULL, then return the text of the error message.
+*/
+static char *jsonBadPathError(
+ sqlite3_context *ctx, /* The function call containing the error */
+ const char *zPath /* The path with the problem */
+){
+ char *zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath);
+ if( ctx==0 ) return zMsg;
if( zMsg ){
- sqlite3_result_error(pCtx, zMsg, -1);
+ sqlite3_result_error(ctx, zMsg, -1);
sqlite3_free(zMsg);
}else{
- sqlite3_result_error_nomem(pCtx);
+ sqlite3_result_error_nomem(ctx);
}
return 0;
}
+/* argv[0] is a BLOB that seems likely to be a JSONB. Subsequent
+** arguments come in parse where each pair contains a JSON path and
+** content to insert or set at that patch. Do the updates
+** and return the result.
+**
+** The specific operation is determined by eEdit, which can be one
+** of JEDIT_INS, JEDIT_REPL, or JEDIT_SET.
+*/
+static void jsonInsertIntoBlob(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv,
+ int eEdit /* JEDIT_INS, JEDIT_REPL, or JEDIT_SET */
+){
+ int i;
+ u32 rc = 0;
+ const char *zPath = 0;
+ int flgs;
+ JsonParse *p;
+ JsonParse ax;
+
+ assert( (argc&1)==1 );
+ flgs = argc==1 ? 0 : JSON_EDITABLE;
+ p = jsonParseFuncArg(ctx, argv[0], flgs);
+ if( p==0 ) return;
+ for(i=1; i<argc-1; i+=2){
+ if( sqlite3_value_type(argv[i])==SQLITE_NULL ) continue;
+ zPath = (const char*)sqlite3_value_text(argv[i]);
+ if( zPath==0 ){
+ sqlite3_result_error_nomem(ctx);
+ jsonParseFree(p);
+ return;
+ }
+ if( zPath[0]!='$' ) goto jsonInsertIntoBlob_patherror;
+ if( jsonFunctionArgToBlob(ctx, argv[i+1], &ax) ){
+ jsonParseReset(&ax);
+ jsonParseFree(p);
+ return;
+ }
+ if( zPath[1]==0 ){
+ if( eEdit==JEDIT_REPL || eEdit==JEDIT_SET ){
+ jsonBlobEdit(p, 0, p->nBlob, ax.aBlob, ax.nBlob);
+ }
+ rc = 0;
+ }else{
+ p->eEdit = eEdit;
+ p->nIns = ax.nBlob;
+ p->aIns = ax.aBlob;
+ p->delta = 0;
+ rc = jsonLookupStep(p, 0, zPath+1, 0);
+ }
+ jsonParseReset(&ax);
+ if( rc==JSON_LOOKUP_NOTFOUND ) continue;
+ if( JSON_LOOKUP_ISERROR(rc) ) goto jsonInsertIntoBlob_patherror;
+ }
+ jsonReturnParse(ctx, p);
+ jsonParseFree(p);
+ return;
+
+jsonInsertIntoBlob_patherror:
+ jsonParseFree(p);
+ if( rc==JSON_LOOKUP_ERROR ){
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ }else{
+ jsonBadPathError(ctx, zPath);
+ }
+ return;
+}
/*
-** Report the wrong number of arguments for json_insert(), json_replace()
-** or json_set().
+** If pArg is a blob that seems like a JSONB blob, then initialize
+** p to point to that JSONB and return TRUE. If pArg does not seem like
+** a JSONB blob, then return FALSE;
+**
+** This routine is only called if it is already known that pArg is a
+** blob. The only open question is whether or not the blob appears
+** to be a JSONB blob.
*/
-static void jsonWrongNumArgs(
- sqlite3_context *pCtx,
- const char *zFuncName
-){
- char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments",
- zFuncName);
- sqlite3_result_error(pCtx, zMsg, -1);
- sqlite3_free(zMsg);
+static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){
+ u32 n, sz = 0;
+ p->aBlob = (u8*)sqlite3_value_blob(pArg);
+ p->nBlob = (u32)sqlite3_value_bytes(pArg);
+ if( p->nBlob==0 ){
+ p->aBlob = 0;
+ return 0;
+ }
+ if( NEVER(p->aBlob==0) ){
+ return 0;
+ }
+ if( (p->aBlob[0] & 0x0f)<=JSONB_OBJECT
+ && (n = jsonbPayloadSize(p, 0, &sz))>0
+ && sz+n==p->nBlob
+ && ((p->aBlob[0] & 0x0f)>JSONB_FALSE || sz==0)
+ ){
+ return 1;
+ }
+ p->aBlob = 0;
+ p->nBlob = 0;
+ return 0;
}
/*
-** Mark all NULL entries in the Object passed in as JNODE_REMOVE.
+** Generate a JsonParse object, containing valid JSONB in aBlob and nBlob,
+** from the SQL function argument pArg. Return a pointer to the new
+** JsonParse object.
+**
+** Ownership of the new JsonParse object is passed to the caller. The
+** caller should invoke jsonParseFree() on the return value when it
+** has finished using it.
+**
+** If any errors are detected, an appropriate error messages is set
+** using sqlite3_result_error() or the equivalent and this routine
+** returns NULL. This routine also returns NULL if the pArg argument
+** is an SQL NULL value, but no error message is set in that case. This
+** is so that SQL functions that are given NULL arguments will return
+** a NULL value.
*/
-static void jsonRemoveAllNulls(JsonNode *pNode){
- int i, n;
- assert( pNode->eType==JSON_OBJECT );
- n = pNode->n;
- for(i=2; i<=n; i += jsonNodeSize(&pNode[i])+1){
- switch( pNode[i].eType ){
- case JSON_NULL:
- pNode[i].jnFlags |= JNODE_REMOVE;
- break;
- case JSON_OBJECT:
- jsonRemoveAllNulls(&pNode[i]);
- break;
+static JsonParse *jsonParseFuncArg(
+ sqlite3_context *ctx,
+ sqlite3_value *pArg,
+ u32 flgs
+){
+ int eType; /* Datatype of pArg */
+ JsonParse *p = 0; /* Value to be returned */
+ JsonParse *pFromCache = 0; /* Value taken from cache */
+ sqlite3 *db; /* The database connection */
+
+ assert( ctx!=0 );
+ eType = sqlite3_value_type(pArg);
+ if( eType==SQLITE_NULL ){
+ return 0;
+ }
+ pFromCache = jsonCacheSearch(ctx, pArg);
+ if( pFromCache ){
+ pFromCache->nJPRef++;
+ if( (flgs & JSON_EDITABLE)==0 ){
+ return pFromCache;
+ }
+ }
+ db = sqlite3_context_db_handle(ctx);
+rebuild_from_cache:
+ p = sqlite3DbMallocZero(db, sizeof(*p));
+ if( p==0 ) goto json_pfa_oom;
+ memset(p, 0, sizeof(*p));
+ p->db = db;
+ p->nJPRef = 1;
+ if( pFromCache!=0 ){
+ u32 nBlob = pFromCache->nBlob;
+ p->aBlob = sqlite3DbMallocRaw(db, nBlob);
+ if( p->aBlob==0 ) goto json_pfa_oom;
+ memcpy(p->aBlob, pFromCache->aBlob, nBlob);
+ p->nBlobAlloc = p->nBlob = nBlob;
+ p->hasNonstd = pFromCache->hasNonstd;
+ jsonParseFree(pFromCache);
+ return p;
+ }
+ if( eType==SQLITE_BLOB ){
+ if( jsonArgIsJsonb(pArg,p) ){
+ if( (flgs & JSON_EDITABLE)!=0 && jsonBlobMakeEditable(p, 0)==0 ){
+ goto json_pfa_oom;
+ }
+ return p;
+ }
+ /* If the blob is not valid JSONB, fall through into trying to cast
+ ** the blob into text which is then interpreted as JSON. (tag-20240123-a)
+ **
+ ** This goes against all historical documentation about how the SQLite
+ ** JSON functions were suppose to work. From the beginning, blob was
+ ** reserved for expansion and a blob value should have raised an error.
+ ** But it did not, due to a bug. And many applications came to depend
+ ** upon this buggy behavior, espeically when using the CLI and reading
+ ** JSON text using readfile(), which returns a blob. For this reason
+ ** we will continue to support the bug moving forward.
+ ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d
+ */
+ }
+ p->zJson = (char*)sqlite3_value_text(pArg);
+ p->nJson = sqlite3_value_bytes(pArg);
+ if( db->mallocFailed ) goto json_pfa_oom;
+ if( p->nJson==0 ) goto json_pfa_malformed;
+ assert( p->zJson!=0 );
+ if( jsonConvertTextToBlob(p, (flgs & JSON_KEEPERROR) ? 0 : ctx) ){
+ if( flgs & JSON_KEEPERROR ){
+ p->nErr = 1;
+ return p;
+ }else{
+ jsonParseFree(p);
+ return 0;
+ }
+ }else{
+ int isRCStr = sqlite3ValueIsOfClass(pArg, sqlite3RCStrUnref);
+ int rc;
+ if( !isRCStr ){
+ char *zNew = sqlite3RCStrNew( p->nJson );
+ if( zNew==0 ) goto json_pfa_oom;
+ memcpy(zNew, p->zJson, p->nJson);
+ p->zJson = zNew;
+ p->zJson[p->nJson] = 0;
+ }else{
+ sqlite3RCStrRef(p->zJson);
+ }
+ p->bJsonIsRCStr = 1;
+ rc = jsonCacheInsert(ctx, p);
+ if( rc==SQLITE_NOMEM ) goto json_pfa_oom;
+ if( flgs & JSON_EDITABLE ){
+ pFromCache = p;
+ p = 0;
+ goto rebuild_from_cache;
}
}
+ return p;
+
+json_pfa_malformed:
+ if( flgs & JSON_KEEPERROR ){
+ p->nErr = 1;
+ return p;
+ }else{
+ jsonParseFree(p);
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ return 0;
+ }
+
+json_pfa_oom:
+ jsonParseFree(pFromCache);
+ jsonParseFree(p);
+ sqlite3_result_error_nomem(ctx);
+ return 0;
}
+/*
+** Make the return value of a JSON function either the raw JSONB blob
+** or make it JSON text, depending on whether the JSON_BLOB flag is
+** set on the function.
+*/
+static void jsonReturnParse(
+ sqlite3_context *ctx,
+ JsonParse *p
+){
+ int flgs;
+ if( p->oom ){
+ sqlite3_result_error_nomem(ctx);
+ return;
+ }
+ flgs = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ if( flgs & JSON_BLOB ){
+ if( p->nBlobAlloc>0 && !p->bReadOnly ){
+ sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_DYNAMIC);
+ p->nBlobAlloc = 0;
+ }else{
+ sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_TRANSIENT);
+ }
+ }else{
+ JsonString s;
+ jsonStringInit(&s, ctx);
+ p->delta = 0;
+ jsonTranslateBlobToText(p, 0, &s);
+ jsonReturnString(&s, p, ctx);
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+ }
+}
/****************************************************************************
** SQL functions used for testing and debugging
@@ -203539,63 +208420,124 @@ static void jsonRemoveAllNulls(JsonNode *pNode){
#if SQLITE_DEBUG
/*
-** Print N node entries.
-*/
-static void jsonDebugPrintNodeEntries(
- JsonNode *aNode, /* First node entry to print */
- int N /* Number of node entries to print */
-){
- int i;
- for(i=0; i<N; i++){
- const char *zType;
- if( aNode[i].jnFlags & JNODE_LABEL ){
- zType = "label";
- }else{
- zType = jsonType[aNode[i].eType];
+** Decode JSONB bytes in aBlob[] starting at iStart through but not
+** including iEnd. Indent the
+** content by nIndent spaces.
+*/
+static void jsonDebugPrintBlob(
+ JsonParse *pParse, /* JSON content */
+ u32 iStart, /* Start rendering here */
+ u32 iEnd, /* Do not render this byte or any byte after this one */
+ int nIndent, /* Indent by this many spaces */
+ sqlite3_str *pOut /* Generate output into this sqlite3_str object */
+){
+ while( iStart<iEnd ){
+ u32 i, n, nn, sz = 0;
+ int showContent = 1;
+ u8 x = pParse->aBlob[iStart] & 0x0f;
+ u32 savedNBlob = pParse->nBlob;
+ sqlite3_str_appendf(pOut, "%5d:%*s", iStart, nIndent, "");
+ if( pParse->nBlobAlloc>pParse->nBlob ){
+ pParse->nBlob = pParse->nBlobAlloc;
+ }
+ nn = n = jsonbPayloadSize(pParse, iStart, &sz);
+ if( nn==0 ) nn = 1;
+ if( sz>0 && x<JSONB_ARRAY ){
+ nn += sz;
+ }
+ for(i=0; i<nn; i++){
+ sqlite3_str_appendf(pOut, " %02x", pParse->aBlob[iStart+i]);
}
- printf("node %4u: %-7s n=%-5d", i, zType, aNode[i].n);
- if( (aNode[i].jnFlags & ~JNODE_LABEL)!=0 ){
- u8 f = aNode[i].jnFlags;
- if( f & JNODE_RAW ) printf(" RAW");
- if( f & JNODE_ESCAPE ) printf(" ESCAPE");
- if( f & JNODE_REMOVE ) printf(" REMOVE");
- if( f & JNODE_REPLACE ) printf(" REPLACE");
- if( f & JNODE_APPEND ) printf(" APPEND");
- if( f & JNODE_JSON5 ) printf(" JSON5");
+ if( n==0 ){
+ sqlite3_str_appendf(pOut, " ERROR invalid node size\n");
+ iStart = n==0 ? iStart+1 : iEnd;
+ continue;
+ }
+ pParse->nBlob = savedNBlob;
+ if( iStart+n+sz>iEnd ){
+ iEnd = iStart+n+sz;
+ if( iEnd>pParse->nBlob ){
+ if( pParse->nBlobAlloc>0 && iEnd>pParse->nBlobAlloc ){
+ iEnd = pParse->nBlobAlloc;
+ }else{
+ iEnd = pParse->nBlob;
+ }
+ }
+ }
+ sqlite3_str_appendall(pOut," <-- ");
+ switch( x ){
+ case JSONB_NULL: sqlite3_str_appendall(pOut,"null"); break;
+ case JSONB_TRUE: sqlite3_str_appendall(pOut,"true"); break;
+ case JSONB_FALSE: sqlite3_str_appendall(pOut,"false"); break;
+ case JSONB_INT: sqlite3_str_appendall(pOut,"int"); break;
+ case JSONB_INT5: sqlite3_str_appendall(pOut,"int5"); break;
+ case JSONB_FLOAT: sqlite3_str_appendall(pOut,"float"); break;
+ case JSONB_FLOAT5: sqlite3_str_appendall(pOut,"float5"); break;
+ case JSONB_TEXT: sqlite3_str_appendall(pOut,"text"); break;
+ case JSONB_TEXTJ: sqlite3_str_appendall(pOut,"textj"); break;
+ case JSONB_TEXT5: sqlite3_str_appendall(pOut,"text5"); break;
+ case JSONB_TEXTRAW: sqlite3_str_appendall(pOut,"textraw"); break;
+ case JSONB_ARRAY: {
+ sqlite3_str_appendf(pOut,"array, %u bytes\n", sz);
+ jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut);
+ showContent = 0;
+ break;
+ }
+ case JSONB_OBJECT: {
+ sqlite3_str_appendf(pOut, "object, %u bytes\n", sz);
+ jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut);
+ showContent = 0;
+ break;
+ }
+ default: {
+ sqlite3_str_appendall(pOut, "ERROR: unknown node type\n");
+ showContent = 0;
+ break;
+ }
}
- switch( aNode[i].eU ){
- case 1: printf(" zJContent=[%.*s]\n",
- aNode[i].n, aNode[i].u.zJContent); break;
- case 2: printf(" iAppend=%u\n", aNode[i].u.iAppend); break;
- case 3: printf(" iKey=%u\n", aNode[i].u.iKey); break;
- case 4: printf(" iPrev=%u\n", aNode[i].u.iPrev); break;
- default: printf("\n");
+ if( showContent ){
+ if( sz==0 && x<=JSONB_FALSE ){
+ sqlite3_str_append(pOut, "\n", 1);
+ }else{
+ u32 j;
+ sqlite3_str_appendall(pOut, ": \"");
+ for(j=iStart+n; j<iStart+n+sz; j++){
+ u8 c = pParse->aBlob[j];
+ if( c<0x20 || c>=0x7f ) c = '.';
+ sqlite3_str_append(pOut, (char*)&c, 1);
+ }
+ sqlite3_str_append(pOut, "\"\n", 2);
+ }
}
+ iStart += n + sz;
}
}
-#endif /* SQLITE_DEBUG */
-
-
-#if 0 /* 1 for debugging. 0 normally. Requires -DSQLITE_DEBUG too */
-static void jsonDebugPrintParse(JsonParse *p){
- jsonDebugPrintNodeEntries(p->aNode, p->nNode);
-}
-static void jsonDebugPrintNode(JsonNode *pNode){
- jsonDebugPrintNodeEntries(pNode, jsonNodeSize(pNode));
+static void jsonShowParse(JsonParse *pParse){
+ sqlite3_str out;
+ char zBuf[1000];
+ if( pParse==0 ){
+ printf("NULL pointer\n");
+ return;
+ }else{
+ printf("nBlobAlloc = %u\n", pParse->nBlobAlloc);
+ printf("nBlob = %u\n", pParse->nBlob);
+ printf("delta = %d\n", pParse->delta);
+ if( pParse->nBlob==0 ) return;
+ printf("content (bytes 0..%u):\n", pParse->nBlob-1);
+ }
+ sqlite3StrAccumInit(&out, 0, zBuf, sizeof(zBuf), 1000000);
+ jsonDebugPrintBlob(pParse, 0, pParse->nBlob, 0, &out);
+ printf("%s", sqlite3_str_value(&out));
+ sqlite3_str_reset(&out);
}
-#else
- /* The usual case */
-# define jsonDebugPrintNode(X)
-# define jsonDebugPrintParse(X)
-#endif
+#endif /* SQLITE_DEBUG */
#ifdef SQLITE_DEBUG
/*
** SQL function: json_parse(JSON)
**
-** Parse JSON using jsonParseCached(). Then print a dump of that
-** parse on standard output. Return the mimified JSON result, just
-** like the json() function.
+** Parse JSON using jsonParseFuncArg(). Return text that is a
+** human-readable dump of the binary JSONB for the input parameter.
*/
static void jsonParseFunc(
sqlite3_context *ctx,
@@ -203603,38 +208545,20 @@ static void jsonParseFunc(
sqlite3_value **argv
){
JsonParse *p; /* The parse */
+ sqlite3_str out;
- assert( argc==1 );
- p = jsonParseCached(ctx, argv[0], ctx, 0);
+ assert( argc>=1 );
+ sqlite3StrAccumInit(&out, 0, 0, 0, 1000000);
+ p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
- printf("nNode = %u\n", p->nNode);
- printf("nAlloc = %u\n", p->nAlloc);
- printf("nJson = %d\n", p->nJson);
- printf("nAlt = %d\n", p->nAlt);
- printf("nErr = %u\n", p->nErr);
- printf("oom = %u\n", p->oom);
- printf("hasNonstd = %u\n", p->hasNonstd);
- printf("useMod = %u\n", p->useMod);
- printf("hasMod = %u\n", p->hasMod);
- printf("nJPRef = %u\n", p->nJPRef);
- printf("iSubst = %u\n", p->iSubst);
- printf("iHold = %u\n", p->iHold);
- jsonDebugPrintNodeEntries(p->aNode, p->nNode);
- jsonReturnJson(p, p->aNode, ctx, 1);
-}
-
-/*
-** The json_test1(JSON) function return true (1) if the input is JSON
-** text generated by another json function. It returns (0) if the input
-** is not known to be JSON.
-*/
-static void jsonTest1Func(
- sqlite3_context *ctx,
- int argc,
- sqlite3_value **argv
-){
- UNUSED_PARAMETER(argc);
- sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE);
+ if( argc==1 ){
+ jsonDebugPrintBlob(p, 0, p->nBlob, 0, &out);
+ sqlite3_result_text64(ctx,out.zText,out.nChar,SQLITE_TRANSIENT,SQLITE_UTF8);
+ }else{
+ jsonShowParse(p);
+ }
+ jsonParseFree(p);
+ sqlite3_str_reset(&out);
}
#endif /* SQLITE_DEBUG */
@@ -203643,7 +208567,7 @@ static void jsonTest1Func(
****************************************************************************/
/*
-** Implementation of the json_QUOTE(VALUE) function. Return a JSON value
+** Implementation of the json_quote(VALUE) function. Return a JSON value
** corresponding to the SQL value input. Mostly this means putting
** double-quotes around strings and returning the unquoted string "null"
** when given a NULL input.
@@ -203656,9 +208580,9 @@ static void jsonQuoteFunc(
JsonString jx;
UNUSED_PARAMETER(argc);
- jsonInit(&jx, ctx);
- jsonAppendValue(&jx, argv[0]);
- jsonResult(&jx);
+ jsonStringInit(&jx, ctx);
+ jsonAppendSqlValue(&jx, argv[0]);
+ jsonReturnString(&jx, 0, 0);
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
@@ -203675,18 +208599,17 @@ static void jsonArrayFunc(
int i;
JsonString jx;
- jsonInit(&jx, ctx);
+ jsonStringInit(&jx, ctx);
jsonAppendChar(&jx, '[');
for(i=0; i<argc; i++){
jsonAppendSeparator(&jx);
- jsonAppendValue(&jx, argv[i]);
+ jsonAppendSqlValue(&jx, argv[i]);
}
jsonAppendChar(&jx, ']');
- jsonResult(&jx);
+ jsonReturnString(&jx, 0, 0);
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
-
/*
** json_array_length(JSON)
** json_array_length(JSON, PATH)
@@ -203700,46 +208623,46 @@ static void jsonArrayLengthFunc(
sqlite3_value **argv
){
JsonParse *p; /* The parse */
- sqlite3_int64 n = 0;
+ sqlite3_int64 cnt = 0;
u32 i;
- JsonNode *pNode;
+ u8 eErr = 0;
- p = jsonParseCached(ctx, argv[0], ctx, 0);
+ p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
- assert( p->nNode );
if( argc==2 ){
const char *zPath = (const char*)sqlite3_value_text(argv[1]);
- pNode = jsonLookup(p, zPath, 0, ctx);
- }else{
- pNode = p->aNode;
- }
- if( pNode==0 ){
- return;
- }
- if( pNode->eType==JSON_ARRAY ){
- while( 1 /*exit-by-break*/ ){
- i = 1;
- while( i<=pNode->n ){
- if( (pNode[i].jnFlags & JNODE_REMOVE)==0 ) n++;
- i += jsonNodeSize(&pNode[i]);
+ if( zPath==0 ){
+ jsonParseFree(p);
+ return;
+ }
+ i = jsonLookupStep(p, 0, zPath[0]=='$' ? zPath+1 : "@", 0);
+ if( JSON_LOOKUP_ISERROR(i) ){
+ if( i==JSON_LOOKUP_NOTFOUND ){
+ /* no-op */
+ }else if( i==JSON_LOOKUP_PATHERROR ){
+ jsonBadPathError(ctx, zPath);
+ }else{
+ sqlite3_result_error(ctx, "malformed JSON", -1);
}
- if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
- if( p->useMod==0 ) break;
- assert( pNode->eU==2 );
- pNode = &p->aNode[pNode->u.iAppend];
+ eErr = 1;
+ i = 0;
}
+ }else{
+ i = 0;
}
- sqlite3_result_int64(ctx, n);
+ if( (p->aBlob[i] & 0x0f)==JSONB_ARRAY ){
+ cnt = jsonbArrayCount(p, i);
+ }
+ if( !eErr ) sqlite3_result_int64(ctx, cnt);
+ jsonParseFree(p);
}
-/*
-** Bit values for the flags passed into jsonExtractFunc() or
-** jsonSetFunc() via the user-data value.
-*/
-#define JSON_JSON 0x01 /* Result is always JSON */
-#define JSON_SQL 0x02 /* Result is always SQL */
-#define JSON_ABPATH 0x03 /* Allow abbreviated JSON path specs */
-#define JSON_ISSET 0x04 /* json_set(), not json_insert() */
+/* True if the string is all alphanumerics and underscores */
+static int jsonAllAlphanum(const char *z, int n){
+ int i;
+ for(i=0; i<n && (sqlite3Isalnum(z[i]) || z[i]=='_'); i++){}
+ return i==n;
+}
/*
** json_extract(JSON, PATH, ...)
@@ -203766,152 +208689,307 @@ static void jsonExtractFunc(
int argc,
sqlite3_value **argv
){
- JsonParse *p; /* The parse */
- JsonNode *pNode;
- const char *zPath;
- int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
- JsonString jx;
+ JsonParse *p = 0; /* The parse */
+ int flags; /* Flags associated with the function */
+ int i; /* Loop counter */
+ JsonString jx; /* String for array result */
if( argc<2 ) return;
- p = jsonParseCached(ctx, argv[0], ctx, 0);
+ p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
- if( argc==2 ){
+ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ jsonStringInit(&jx, ctx);
+ if( argc>2 ){
+ jsonAppendChar(&jx, '[');
+ }
+ for(i=1; i<argc; i++){
/* With a single PATH argument */
- zPath = (const char*)sqlite3_value_text(argv[1]);
- if( zPath==0 ) return;
- if( flags & JSON_ABPATH ){
- if( zPath[0]!='$' || (zPath[1]!='.' && zPath[1]!='[' && zPath[1]!=0) ){
- /* The -> and ->> operators accept abbreviated PATH arguments. This
- ** is mostly for compatibility with PostgreSQL, but also for
- ** convenience.
- **
- ** NUMBER ==> $[NUMBER] // PG compatible
- ** LABEL ==> $.LABEL // PG compatible
- ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience
- */
- jsonInit(&jx, ctx);
- if( sqlite3Isdigit(zPath[0]) ){
- jsonAppendRawNZ(&jx, "$[", 2);
- jsonAppendRaw(&jx, zPath, (int)strlen(zPath));
- jsonAppendRawNZ(&jx, "]", 2);
- }else{
- jsonAppendRawNZ(&jx, "$.", 1 + (zPath[0]!='['));
- jsonAppendRaw(&jx, zPath, (int)strlen(zPath));
- jsonAppendChar(&jx, 0);
- }
- pNode = jx.bErr ? 0 : jsonLookup(p, jx.zBuf, 0, ctx);
- jsonReset(&jx);
+ const char *zPath = (const char*)sqlite3_value_text(argv[i]);
+ int nPath;
+ u32 j;
+ if( zPath==0 ) goto json_extract_error;
+ nPath = sqlite3Strlen30(zPath);
+ if( zPath[0]=='$' ){
+ j = jsonLookupStep(p, 0, zPath+1, 0);
+ }else if( (flags & JSON_ABPATH) ){
+ /* The -> and ->> operators accept abbreviated PATH arguments. This
+ ** is mostly for compatibility with PostgreSQL, but also for
+ ** convenience.
+ **
+ ** NUMBER ==> $[NUMBER] // PG compatible
+ ** LABEL ==> $.LABEL // PG compatible
+ ** [NUMBER] ==> $[NUMBER] // Not PG. Purely for convenience
+ */
+ jsonStringInit(&jx, ctx);
+ if( sqlite3_value_type(argv[i])==SQLITE_INTEGER ){
+ jsonAppendRawNZ(&jx, "[", 1);
+ jsonAppendRaw(&jx, zPath, nPath);
+ jsonAppendRawNZ(&jx, "]", 2);
+ }else if( jsonAllAlphanum(zPath, nPath) ){
+ jsonAppendRawNZ(&jx, ".", 1);
+ jsonAppendRaw(&jx, zPath, nPath);
+ }else if( zPath[0]=='[' && nPath>=3 && zPath[nPath-1]==']' ){
+ jsonAppendRaw(&jx, zPath, nPath);
}else{
- pNode = jsonLookup(p, zPath, 0, ctx);
+ jsonAppendRawNZ(&jx, ".\"", 2);
+ jsonAppendRaw(&jx, zPath, nPath);
+ jsonAppendRawNZ(&jx, "\"", 1);
}
- if( pNode ){
+ jsonStringTerminate(&jx);
+ j = jsonLookupStep(p, 0, jx.zBuf, 0);
+ jsonStringReset(&jx);
+ }else{
+ jsonBadPathError(ctx, zPath);
+ goto json_extract_error;
+ }
+ if( j<p->nBlob ){
+ if( argc==2 ){
if( flags & JSON_JSON ){
- jsonReturnJson(p, pNode, ctx, 0);
+ jsonStringInit(&jx, ctx);
+ jsonTranslateBlobToText(p, j, &jx);
+ jsonReturnString(&jx, 0, 0);
+ jsonStringReset(&jx);
+ assert( (flags & JSON_BLOB)==0 );
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}else{
- jsonReturn(p, pNode, ctx);
- sqlite3_result_subtype(ctx, 0);
+ jsonReturnFromBlob(p, j, ctx, 0);
+ if( (flags & (JSON_SQL|JSON_BLOB))==0
+ && (p->aBlob[j]&0x0f)>=JSONB_ARRAY
+ ){
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+ }
}
+ }else{
+ jsonAppendSeparator(&jx);
+ jsonTranslateBlobToText(p, j, &jx);
}
- }else{
- pNode = jsonLookup(p, zPath, 0, ctx);
- if( p->nErr==0 && pNode ) jsonReturn(p, pNode, ctx);
- }
- }else{
- /* Two or more PATH arguments results in a JSON array with each
- ** element of the array being the value selected by one of the PATHs */
- int i;
- jsonInit(&jx, ctx);
- jsonAppendChar(&jx, '[');
- for(i=1; i<argc; i++){
- zPath = (const char*)sqlite3_value_text(argv[i]);
- pNode = jsonLookup(p, zPath, 0, ctx);
- if( p->nErr ) break;
- jsonAppendSeparator(&jx);
- if( pNode ){
- jsonRenderNode(p, pNode, &jx);
+ }else if( j==JSON_LOOKUP_NOTFOUND ){
+ if( argc==2 ){
+ goto json_extract_error; /* Return NULL if not found */
}else{
+ jsonAppendSeparator(&jx);
jsonAppendRawNZ(&jx, "null", 4);
}
+ }else if( j==JSON_LOOKUP_ERROR ){
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ goto json_extract_error;
+ }else{
+ jsonBadPathError(ctx, zPath);
+ goto json_extract_error;
}
- if( i==argc ){
- jsonAppendChar(&jx, ']');
- jsonResult(&jx);
+ }
+ if( argc>2 ){
+ jsonAppendChar(&jx, ']');
+ jsonReturnString(&jx, 0, 0);
+ if( (flags & JSON_BLOB)==0 ){
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
- jsonReset(&jx);
}
+json_extract_error:
+ jsonStringReset(&jx);
+ jsonParseFree(p);
+ return;
}
-/* This is the RFC 7396 MergePatch algorithm.
-*/
-static JsonNode *jsonMergePatch(
- JsonParse *pParse, /* The JSON parser that contains the TARGET */
- u32 iTarget, /* Node of the TARGET in pParse */
- JsonNode *pPatch /* The PATCH */
-){
- u32 i, j;
- u32 iRoot;
- JsonNode *pTarget;
- if( pPatch->eType!=JSON_OBJECT ){
- return pPatch;
- }
- assert( iTarget<pParse->nNode );
- pTarget = &pParse->aNode[iTarget];
- assert( (pPatch->jnFlags & JNODE_APPEND)==0 );
- if( pTarget->eType!=JSON_OBJECT ){
- jsonRemoveAllNulls(pPatch);
- return pPatch;
- }
- iRoot = iTarget;
- for(i=1; i<pPatch->n; i += jsonNodeSize(&pPatch[i+1])+1){
- u32 nKey;
- const char *zKey;
- assert( pPatch[i].eType==JSON_STRING );
- assert( pPatch[i].jnFlags & JNODE_LABEL );
- assert( pPatch[i].eU==1 );
- nKey = pPatch[i].n;
- zKey = pPatch[i].u.zJContent;
- for(j=1; j<pTarget->n; j += jsonNodeSize(&pTarget[j+1])+1 ){
- assert( pTarget[j].eType==JSON_STRING );
- assert( pTarget[j].jnFlags & JNODE_LABEL );
- if( jsonSameLabel(&pPatch[i], &pTarget[j]) ){
- if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_REPLACE) ) break;
- if( pPatch[i+1].eType==JSON_NULL ){
- pTarget[j+1].jnFlags |= JNODE_REMOVE;
- }else{
- JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]);
- if( pNew==0 ) return 0;
- if( pNew!=&pParse->aNode[iTarget+j+1] ){
- jsonParseAddSubstNode(pParse, iTarget+j+1);
- jsonParseAddNodeArray(pParse, pNew, jsonNodeSize(pNew));
- }
- pTarget = &pParse->aNode[iTarget];
- }
- break;
+/*
+** Return codes for jsonMergePatch()
+*/
+#define JSON_MERGE_OK 0 /* Success */
+#define JSON_MERGE_BADTARGET 1 /* Malformed TARGET blob */
+#define JSON_MERGE_BADPATCH 2 /* Malformed PATCH blob */
+#define JSON_MERGE_OOM 3 /* Out-of-memory condition */
+
+/*
+** RFC-7396 MergePatch for two JSONB blobs.
+**
+** pTarget is the target. pPatch is the patch. The target is updated
+** in place. The patch is read-only.
+**
+** The original RFC-7396 algorithm is this:
+**
+** define MergePatch(Target, Patch):
+** if Patch is an Object:
+** if Target is not an Object:
+** Target = {} # Ignore the contents and set it to an empty Object
+** for each Name/Value pair in Patch:
+** if Value is null:
+** if Name exists in Target:
+** remove the Name/Value pair from Target
+** else:
+** Target[Name] = MergePatch(Target[Name], Value)
+** return Target
+** else:
+** return Patch
+**
+** Here is an equivalent algorithm restructured to show the actual
+** implementation:
+**
+** 01 define MergePatch(Target, Patch):
+** 02 if Patch is not an Object:
+** 03 return Patch
+** 04 else: // if Patch is an Object
+** 05 if Target is not an Object:
+** 06 Target = {}
+** 07 for each Name/Value pair in Patch:
+** 08 if Name exists in Target:
+** 09 if Value is null:
+** 10 remove the Name/Value pair from Target
+** 11 else
+** 12 Target[name] = MergePatch(Target[Name], Value)
+** 13 else if Value is not NULL:
+** 14 if Value is not an Object:
+** 15 Target[name] = Value
+** 16 else:
+** 17 Target[name] = MergePatch('{}',value)
+** 18 return Target
+** |
+** ^---- Line numbers referenced in comments in the implementation
+*/
+static int jsonMergePatch(
+ JsonParse *pTarget, /* The JSON parser that contains the TARGET */
+ u32 iTarget, /* Index of TARGET in pTarget->aBlob[] */
+ const JsonParse *pPatch, /* The PATCH */
+ u32 iPatch /* Index of PATCH in pPatch->aBlob[] */
+){
+ u8 x; /* Type of a single node */
+ u32 n, sz=0; /* Return values from jsonbPayloadSize() */
+ u32 iTCursor; /* Cursor position while scanning the target object */
+ u32 iTStart; /* First label in the target object */
+ u32 iTEndBE; /* Original first byte past end of target, before edit */
+ u32 iTEnd; /* Current first byte past end of target */
+ u8 eTLabel; /* Node type of the target label */
+ u32 iTLabel = 0; /* Index of the label */
+ u32 nTLabel = 0; /* Header size in bytes for the target label */
+ u32 szTLabel = 0; /* Size of the target label payload */
+ u32 iTValue = 0; /* Index of the target value */
+ u32 nTValue = 0; /* Header size of the target value */
+ u32 szTValue = 0; /* Payload size for the target value */
+
+ u32 iPCursor; /* Cursor position while scanning the patch */
+ u32 iPEnd; /* First byte past the end of the patch */
+ u8 ePLabel; /* Node type of the patch label */
+ u32 iPLabel; /* Start of patch label */
+ u32 nPLabel; /* Size of header on the patch label */
+ u32 szPLabel; /* Payload size of the patch label */
+ u32 iPValue; /* Start of patch value */
+ u32 nPValue; /* Header size for the patch value */
+ u32 szPValue; /* Payload size of the patch value */
+
+ assert( iTarget>=0 && iTarget<pTarget->nBlob );
+ assert( iPatch>=0 && iPatch<pPatch->nBlob );
+ x = pPatch->aBlob[iPatch] & 0x0f;
+ if( x!=JSONB_OBJECT ){ /* Algorithm line 02 */
+ u32 szPatch; /* Total size of the patch, header+payload */
+ u32 szTarget; /* Total size of the target, header+payload */
+ n = jsonbPayloadSize(pPatch, iPatch, &sz);
+ szPatch = n+sz;
+ sz = 0;
+ n = jsonbPayloadSize(pTarget, iTarget, &sz);
+ szTarget = n+sz;
+ jsonBlobEdit(pTarget, iTarget, szTarget, pPatch->aBlob+iPatch, szPatch);
+ return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK; /* Line 03 */
+ }
+ x = pTarget->aBlob[iTarget] & 0x0f;
+ if( x!=JSONB_OBJECT ){ /* Algorithm line 05 */
+ n = jsonbPayloadSize(pTarget, iTarget, &sz);
+ jsonBlobEdit(pTarget, iTarget+n, sz, 0, 0);
+ x = pTarget->aBlob[iTarget];
+ pTarget->aBlob[iTarget] = (x & 0xf0) | JSONB_OBJECT;
+ }
+ n = jsonbPayloadSize(pPatch, iPatch, &sz);
+ if( NEVER(n==0) ) return JSON_MERGE_BADPATCH;
+ iPCursor = iPatch+n;
+ iPEnd = iPCursor+sz;
+ n = jsonbPayloadSize(pTarget, iTarget, &sz);
+ if( NEVER(n==0) ) return JSON_MERGE_BADTARGET;
+ iTStart = iTarget+n;
+ iTEndBE = iTStart+sz;
+
+ while( iPCursor<iPEnd ){ /* Algorithm line 07 */
+ iPLabel = iPCursor;
+ ePLabel = pPatch->aBlob[iPCursor] & 0x0f;
+ if( ePLabel<JSONB_TEXT || ePLabel>JSONB_TEXTRAW ){
+ return JSON_MERGE_BADPATCH;
+ }
+ nPLabel = jsonbPayloadSize(pPatch, iPCursor, &szPLabel);
+ if( nPLabel==0 ) return JSON_MERGE_BADPATCH;
+ iPValue = iPCursor + nPLabel + szPLabel;
+ if( iPValue>=iPEnd ) return JSON_MERGE_BADPATCH;
+ nPValue = jsonbPayloadSize(pPatch, iPValue, &szPValue);
+ if( nPValue==0 ) return JSON_MERGE_BADPATCH;
+ iPCursor = iPValue + nPValue + szPValue;
+ if( iPCursor>iPEnd ) return JSON_MERGE_BADPATCH;
+
+ iTCursor = iTStart;
+ iTEnd = iTEndBE + pTarget->delta;
+ while( iTCursor<iTEnd ){
+ int isEqual; /* true if the patch and target labels match */
+ iTLabel = iTCursor;
+ eTLabel = pTarget->aBlob[iTCursor] & 0x0f;
+ if( eTLabel<JSONB_TEXT || eTLabel>JSONB_TEXTRAW ){
+ return JSON_MERGE_BADTARGET;
+ }
+ nTLabel = jsonbPayloadSize(pTarget, iTCursor, &szTLabel);
+ if( nTLabel==0 ) return JSON_MERGE_BADTARGET;
+ iTValue = iTLabel + nTLabel + szTLabel;
+ if( iTValue>=iTEnd ) return JSON_MERGE_BADTARGET;
+ nTValue = jsonbPayloadSize(pTarget, iTValue, &szTValue);
+ if( nTValue==0 ) return JSON_MERGE_BADTARGET;
+ if( iTValue + nTValue + szTValue > iTEnd ) return JSON_MERGE_BADTARGET;
+ isEqual = jsonLabelCompare(
+ (const char*)&pPatch->aBlob[iPLabel+nPLabel],
+ szPLabel,
+ (ePLabel==JSONB_TEXT || ePLabel==JSONB_TEXTRAW),
+ (const char*)&pTarget->aBlob[iTLabel+nTLabel],
+ szTLabel,
+ (eTLabel==JSONB_TEXT || eTLabel==JSONB_TEXTRAW));
+ if( isEqual ) break;
+ iTCursor = iTValue + nTValue + szTValue;
+ }
+ x = pPatch->aBlob[iPValue] & 0x0f;
+ if( iTCursor<iTEnd ){
+ /* A match was found. Algorithm line 08 */
+ if( x==0 ){
+ /* Patch value is NULL. Algorithm line 09 */
+ jsonBlobEdit(pTarget, iTLabel, nTLabel+szTLabel+nTValue+szTValue, 0,0);
+ /* vvvvvv----- No OOM on a delete-only edit */
+ if( NEVER(pTarget->oom) ) return JSON_MERGE_OOM;
+ }else{
+ /* Algorithm line 12 */
+ int rc, savedDelta = pTarget->delta;
+ pTarget->delta = 0;
+ rc = jsonMergePatch(pTarget, iTValue, pPatch, iPValue);
+ if( rc ) return rc;
+ pTarget->delta += savedDelta;
+ }
+ }else if( x>0 ){ /* Algorithm line 13 */
+ /* No match and patch value is not NULL */
+ u32 szNew = szPLabel+nPLabel;
+ if( (pPatch->aBlob[iPValue] & 0x0f)!=JSONB_OBJECT ){ /* Line 14 */
+ jsonBlobEdit(pTarget, iTEnd, 0, 0, szPValue+nPValue+szNew);
+ if( pTarget->oom ) return JSON_MERGE_OOM;
+ memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew);
+ memcpy(&pTarget->aBlob[iTEnd+szNew],
+ &pPatch->aBlob[iPValue], szPValue+nPValue);
+ }else{
+ int rc, savedDelta;
+ jsonBlobEdit(pTarget, iTEnd, 0, 0, szNew+1);
+ if( pTarget->oom ) return JSON_MERGE_OOM;
+ memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew);
+ pTarget->aBlob[iTEnd+szNew] = 0x00;
+ savedDelta = pTarget->delta;
+ pTarget->delta = 0;
+ rc = jsonMergePatch(pTarget, iTEnd+szNew,pPatch,iPValue);
+ if( rc ) return rc;
+ pTarget->delta += savedDelta;
}
}
- if( j>=pTarget->n && pPatch[i+1].eType!=JSON_NULL ){
- int iStart;
- JsonNode *pApnd;
- u32 nApnd;
- iStart = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
- jsonParseAddNode(pParse, JSON_STRING, nKey, zKey);
- pApnd = &pPatch[i+1];
- if( pApnd->eType==JSON_OBJECT ) jsonRemoveAllNulls(pApnd);
- nApnd = jsonNodeSize(pApnd);
- jsonParseAddNodeArray(pParse, pApnd, jsonNodeSize(pApnd));
- if( pParse->oom ) return 0;
- pParse->aNode[iStart].n = 1+nApnd;
- pParse->aNode[iRoot].jnFlags |= JNODE_APPEND;
- pParse->aNode[iRoot].u.iAppend = iStart;
- VVA( pParse->aNode[iRoot].eU = 2 );
- iRoot = iStart;
- pTarget = &pParse->aNode[iTarget];
- }
}
- return pTarget;
+ if( pTarget->delta ) jsonAfterEditSizeAdjust(pTarget, iTarget);
+ return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK;
}
+
/*
** Implementation of the json_mergepatch(JSON1,JSON2) function. Return a JSON
** object that is the result of running the RFC 7396 MergePatch() algorithm
@@ -203922,28 +209000,27 @@ static void jsonPatchFunc(
int argc,
sqlite3_value **argv
){
- JsonParse *pX; /* The JSON that is being patched */
- JsonParse *pY; /* The patch */
- JsonNode *pResult; /* The result of the merge */
+ JsonParse *pTarget; /* The TARGET */
+ JsonParse *pPatch; /* The PATCH */
+ int rc; /* Result code */
UNUSED_PARAMETER(argc);
- pX = jsonParseCached(ctx, argv[0], ctx, 1);
- if( pX==0 ) return;
- assert( pX->hasMod==0 );
- pX->hasMod = 1;
- pY = jsonParseCached(ctx, argv[1], ctx, 1);
- if( pY==0 ) return;
- pX->useMod = 1;
- pY->useMod = 1;
- pResult = jsonMergePatch(pX, 0, pY->aNode);
- assert( pResult!=0 || pX->oom );
- if( pResult && pX->oom==0 ){
- jsonDebugPrintParse(pX);
- jsonDebugPrintNode(pResult);
- jsonReturnJson(pX, pResult, ctx, 0);
- }else{
- sqlite3_result_error_nomem(ctx);
+ assert( argc==2 );
+ pTarget = jsonParseFuncArg(ctx, argv[0], JSON_EDITABLE);
+ if( pTarget==0 ) return;
+ pPatch = jsonParseFuncArg(ctx, argv[1], 0);
+ if( pPatch ){
+ rc = jsonMergePatch(pTarget, 0, pPatch, 0);
+ if( rc==JSON_MERGE_OK ){
+ jsonReturnParse(ctx, pTarget);
+ }else if( rc==JSON_MERGE_OOM ){
+ sqlite3_result_error_nomem(ctx);
+ }else{
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ }
+ jsonParseFree(pPatch);
}
+ jsonParseFree(pTarget);
}
@@ -203967,23 +209044,23 @@ static void jsonObjectFunc(
"of arguments", -1);
return;
}
- jsonInit(&jx, ctx);
+ jsonStringInit(&jx, ctx);
jsonAppendChar(&jx, '{');
for(i=0; i<argc; i+=2){
if( sqlite3_value_type(argv[i])!=SQLITE_TEXT ){
sqlite3_result_error(ctx, "json_object() labels must be TEXT", -1);
- jsonReset(&jx);
+ jsonStringReset(&jx);
return;
}
jsonAppendSeparator(&jx);
z = (const char*)sqlite3_value_text(argv[i]);
- n = (u32)sqlite3_value_bytes(argv[i]);
+ n = sqlite3_value_bytes(argv[i]);
jsonAppendString(&jx, z, n);
jsonAppendChar(&jx, ':');
- jsonAppendValue(&jx, argv[i+1]);
+ jsonAppendSqlValue(&jx, argv[i+1]);
}
jsonAppendChar(&jx, '}');
- jsonResult(&jx);
+ jsonReturnString(&jx, 0, 0);
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
@@ -203999,118 +209076,50 @@ static void jsonRemoveFunc(
int argc,
sqlite3_value **argv
){
- JsonParse *pParse; /* The parse */
- JsonNode *pNode;
- const char *zPath;
- u32 i;
+ JsonParse *p; /* The parse */
+ const char *zPath = 0; /* Path of element to be removed */
+ int i; /* Loop counter */
+ u32 rc; /* Subroutine return code */
if( argc<1 ) return;
- pParse = jsonParseCached(ctx, argv[0], ctx, argc>1);
- if( pParse==0 ) return;
- for(i=1; i<(u32)argc; i++){
+ p = jsonParseFuncArg(ctx, argv[0], argc>1 ? JSON_EDITABLE : 0);
+ if( p==0 ) return;
+ for(i=1; i<argc; i++){
zPath = (const char*)sqlite3_value_text(argv[i]);
- if( zPath==0 ) goto remove_done;
- pNode = jsonLookup(pParse, zPath, 0, ctx);
- if( pParse->nErr ) goto remove_done;
- if( pNode ){
- pNode->jnFlags |= JNODE_REMOVE;
- pParse->hasMod = 1;
- pParse->useMod = 1;
+ if( zPath==0 ){
+ goto json_remove_done;
+ }
+ if( zPath[0]!='$' ){
+ goto json_remove_patherror;
+ }
+ if( zPath[1]==0 ){
+ /* json_remove(j,'$') returns NULL */
+ goto json_remove_done;
+ }
+ p->eEdit = JEDIT_DEL;
+ p->delta = 0;
+ rc = jsonLookupStep(p, 0, zPath+1, 0);
+ if( JSON_LOOKUP_ISERROR(rc) ){
+ if( rc==JSON_LOOKUP_NOTFOUND ){
+ continue; /* No-op */
+ }else if( rc==JSON_LOOKUP_PATHERROR ){
+ jsonBadPathError(ctx, zPath);
+ }else{
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ }
+ goto json_remove_done;
}
}
- if( (pParse->aNode[0].jnFlags & JNODE_REMOVE)==0 ){
- jsonReturnJson(pParse, pParse->aNode, ctx, 1);
- }
-remove_done:
- jsonDebugPrintParse(p);
-}
+ jsonReturnParse(ctx, p);
+ jsonParseFree(p);
+ return;
-/*
-** Substitute the value at iNode with the pValue parameter.
-*/
-static void jsonReplaceNode(
- sqlite3_context *pCtx,
- JsonParse *p,
- int iNode,
- sqlite3_value *pValue
-){
- int idx = jsonParseAddSubstNode(p, iNode);
- if( idx<=0 ){
- assert( p->oom );
- return;
- }
- switch( sqlite3_value_type(pValue) ){
- case SQLITE_NULL: {
- jsonParseAddNode(p, JSON_NULL, 0, 0);
- break;
- }
- case SQLITE_FLOAT: {
- char *z = sqlite3_mprintf("%!0.15g", sqlite3_value_double(pValue));
- int n;
- if( z==0 ){
- p->oom = 1;
- break;
- }
- n = sqlite3Strlen30(z);
- jsonParseAddNode(p, JSON_REAL, n, z);
- jsonParseAddCleanup(p, sqlite3_free, z);
- break;
- }
- case SQLITE_INTEGER: {
- char *z = sqlite3_mprintf("%lld", sqlite3_value_int64(pValue));
- int n;
- if( z==0 ){
- p->oom = 1;
- break;
- }
- n = sqlite3Strlen30(z);
- jsonParseAddNode(p, JSON_INT, n, z);
- jsonParseAddCleanup(p, sqlite3_free, z);
+json_remove_patherror:
+ jsonBadPathError(ctx, zPath);
- break;
- }
- case SQLITE_TEXT: {
- const char *z = (const char*)sqlite3_value_text(pValue);
- u32 n = (u32)sqlite3_value_bytes(pValue);
- if( z==0 ){
- p->oom = 1;
- break;
- }
- if( sqlite3_value_subtype(pValue)!=JSON_SUBTYPE ){
- char *zCopy = sqlite3DbStrDup(0, z);
- int k;
- if( zCopy ){
- jsonParseAddCleanup(p, sqlite3_free, zCopy);
- }else{
- p->oom = 1;
- sqlite3_result_error_nomem(pCtx);
- }
- k = jsonParseAddNode(p, JSON_STRING, n, zCopy);
- assert( k>0 || p->oom );
- if( p->oom==0 ) p->aNode[k].jnFlags |= JNODE_RAW;
- }else{
- JsonParse *pPatch = jsonParseCached(pCtx, pValue, pCtx, 1);
- if( pPatch==0 ){
- p->oom = 1;
- break;
- }
- jsonParseAddNodeArray(p, pPatch->aNode, pPatch->nNode);
- /* The nodes copied out of pPatch and into p likely contain
- ** u.zJContent pointers into pPatch->zJson. So preserve the
- ** content of pPatch until p is destroyed. */
- assert( pPatch->nJPRef>=1 );
- pPatch->nJPRef++;
- jsonParseAddCleanup(p, (void(*)(void*))jsonParseFree, pPatch);
- }
- break;
- }
- default: {
- jsonParseAddNode(p, JSON_NULL, 0, 0);
- sqlite3_result_error(pCtx, "JSON cannot hold BLOB values", -1);
- p->nErr++;
- break;
- }
- }
+json_remove_done:
+ jsonParseFree(p);
+ return;
}
/*
@@ -204124,30 +209133,12 @@ static void jsonReplaceFunc(
int argc,
sqlite3_value **argv
){
- JsonParse *pParse; /* The parse */
- JsonNode *pNode;
- const char *zPath;
- u32 i;
-
if( argc<1 ) return;
if( (argc&1)==0 ) {
jsonWrongNumArgs(ctx, "replace");
return;
}
- pParse = jsonParseCached(ctx, argv[0], ctx, argc>1);
- if( pParse==0 ) return;
- for(i=1; i<(u32)argc; i+=2){
- zPath = (const char*)sqlite3_value_text(argv[i]);
- pParse->useMod = 1;
- pNode = jsonLookup(pParse, zPath, 0, ctx);
- if( pParse->nErr ) goto replace_err;
- if( pNode ){
- jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]);
- }
- }
- jsonReturnJson(pParse, pParse->aNode, ctx, 1);
-replace_err:
- jsonDebugPrintParse(pParse);
+ jsonInsertIntoBlob(ctx, argc, argv, JEDIT_REPL);
}
@@ -204168,39 +209159,16 @@ static void jsonSetFunc(
int argc,
sqlite3_value **argv
){
- JsonParse *pParse; /* The parse */
- JsonNode *pNode;
- const char *zPath;
- u32 i;
- int bApnd;
- int bIsSet = sqlite3_user_data(ctx)!=0;
+
+ int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ int bIsSet = (flags&JSON_ISSET)!=0;
if( argc<1 ) return;
if( (argc&1)==0 ) {
jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert");
return;
}
- pParse = jsonParseCached(ctx, argv[0], ctx, argc>1);
- if( pParse==0 ) return;
- for(i=1; i<(u32)argc; i+=2){
- zPath = (const char*)sqlite3_value_text(argv[i]);
- bApnd = 0;
- pParse->useMod = 1;
- pNode = jsonLookup(pParse, zPath, &bApnd, ctx);
- if( pParse->oom ){
- sqlite3_result_error_nomem(ctx);
- goto jsonSetDone;
- }else if( pParse->nErr ){
- goto jsonSetDone;
- }else if( pNode && (bApnd || bIsSet) ){
- jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]);
- }
- }
- jsonDebugPrintParse(pParse);
- jsonReturnJson(pParse, pParse->aNode, ctx, 1);
-
-jsonSetDone:
- /* no cleanup required */;
+ jsonInsertIntoBlob(ctx, argc, argv, bIsSet ? JEDIT_SET : JEDIT_INS);
}
/*
@@ -204216,27 +209184,127 @@ static void jsonTypeFunc(
sqlite3_value **argv
){
JsonParse *p; /* The parse */
- const char *zPath;
- JsonNode *pNode;
+ const char *zPath = 0;
+ u32 i;
- p = jsonParseCached(ctx, argv[0], ctx, 0);
+ p = jsonParseFuncArg(ctx, argv[0], 0);
if( p==0 ) return;
if( argc==2 ){
zPath = (const char*)sqlite3_value_text(argv[1]);
- pNode = jsonLookup(p, zPath, 0, ctx);
+ if( zPath==0 ) goto json_type_done;
+ if( zPath[0]!='$' ){
+ jsonBadPathError(ctx, zPath);
+ goto json_type_done;
+ }
+ i = jsonLookupStep(p, 0, zPath+1, 0);
+ if( JSON_LOOKUP_ISERROR(i) ){
+ if( i==JSON_LOOKUP_NOTFOUND ){
+ /* no-op */
+ }else if( i==JSON_LOOKUP_PATHERROR ){
+ jsonBadPathError(ctx, zPath);
+ }else{
+ sqlite3_result_error(ctx, "malformed JSON", -1);
+ }
+ goto json_type_done;
+ }
}else{
- pNode = p->aNode;
+ i = 0;
}
- if( pNode ){
- sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
+ sqlite3_result_text(ctx, jsonbType[p->aBlob[i]&0x0f], -1, SQLITE_STATIC);
+json_type_done:
+ jsonParseFree(p);
+}
+
+/*
+** json_pretty(JSON)
+** json_pretty(JSON, INDENT)
+**
+** Return text that is a pretty-printed rendering of the input JSON.
+** If the argument is not valid JSON, return NULL.
+**
+** The INDENT argument is text that is used for indentation. If omitted,
+** it defaults to four spaces (the same as PostgreSQL).
+*/
+static void jsonPrettyFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonString s; /* The output string */
+ JsonPretty x; /* Pretty printing context */
+
+ memset(&x, 0, sizeof(x));
+ x.pParse = jsonParseFuncArg(ctx, argv[0], 0);
+ if( x.pParse==0 ) return;
+ x.pOut = &s;
+ jsonStringInit(&s, ctx);
+ if( argc==1 || (x.zIndent = (const char*)sqlite3_value_text(argv[1]))==0 ){
+ x.zIndent = " ";
+ x.szIndent = 4;
+ }else{
+ x.szIndent = (u32)strlen(x.zIndent);
}
+ jsonTranslateBlobToPrettyText(&x, 0);
+ jsonReturnString(&s, 0, 0);
+ jsonParseFree(x.pParse);
}
/*
** json_valid(JSON)
-**
-** Return 1 if JSON is a well-formed canonical JSON string according
-** to RFC-7159. Return 0 otherwise.
+** json_valid(JSON, FLAGS)
+**
+** Check the JSON argument to see if it is well-formed. The FLAGS argument
+** encodes the various constraints on what is meant by "well-formed":
+**
+** 0x01 Canonical RFC-8259 JSON text
+** 0x02 JSON text with optional JSON-5 extensions
+** 0x04 Superficially appears to be JSONB
+** 0x08 Strictly well-formed JSONB
+**
+** If the FLAGS argument is omitted, it defaults to 1. Useful values for
+** FLAGS include:
+**
+** 1 Strict canonical JSON text
+** 2 JSON text perhaps with JSON-5 extensions
+** 4 Superficially appears to be JSONB
+** 5 Canonical JSON text or superficial JSONB
+** 6 JSON-5 text or superficial JSONB
+** 8 Strict JSONB
+** 9 Canonical JSON text or strict JSONB
+** 10 JSON-5 text or strict JSONB
+**
+** Other flag combinations are redundant. For example, every canonical
+** JSON text is also well-formed JSON-5 text, so FLAG values 2 and 3
+** are the same. Similarly, any input that passes a strict JSONB validation
+** will also pass the superficial validation so 12 through 15 are the same
+** as 8 through 11 respectively.
+**
+** This routine runs in linear time to validate text and when doing strict
+** JSONB validation. Superficial JSONB validation is constant time,
+** assuming the BLOB is already in memory. The performance advantage
+** of superficial JSONB validation is why that option is provided.
+** Application developers can choose to do fast superficial validation or
+** slower strict validation, according to their specific needs.
+**
+** Only the lower four bits of the FLAGS argument are currently used.
+** Higher bits are reserved for future expansion. To facilitate
+** compatibility, the current implementation raises an error if any bit
+** in FLAGS is set other than the lower four bits.
+**
+** The original circa 2015 implementation of the JSON routines in
+** SQLite only supported canonical RFC-8259 JSON text and the json_valid()
+** function only accepted one argument. That is why the default value
+** for the FLAGS argument is 1, since FLAGS=1 causes this routine to only
+** recognize canonical RFC-8259 JSON text as valid. The extra FLAGS
+** argument was added when the JSON routines were extended to support
+** JSON5-like extensions and binary JSONB stored in BLOBs.
+**
+** Return Values:
+**
+** * Raise an error if FLAGS is outside the range of 1 to 15.
+** * Return NULL if the input is NULL
+** * Return 1 if the input is well-formed.
+** * Return 0 if the input is not well-formed.
*/
static void jsonValidFunc(
sqlite3_context *ctx,
@@ -204244,79 +209312,128 @@ static void jsonValidFunc(
sqlite3_value **argv
){
JsonParse *p; /* The parse */
- UNUSED_PARAMETER(argc);
- if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
+ u8 flags = 1;
+ u8 res = 0;
+ if( argc==2 ){
+ i64 f = sqlite3_value_int64(argv[1]);
+ if( f<1 || f>15 ){
+ sqlite3_result_error(ctx, "FLAGS parameter to json_valid() must be"
+ " between 1 and 15", -1);
+ return;
+ }
+ flags = f & 0x0f;
+ }
+ switch( sqlite3_value_type(argv[0]) ){
+ case SQLITE_NULL: {
#ifdef SQLITE_LEGACY_JSON_VALID
- /* Incorrect legacy behavior was to return FALSE for a NULL input */
- sqlite3_result_int(ctx, 0);
+ /* Incorrect legacy behavior was to return FALSE for a NULL input */
+ sqlite3_result_int(ctx, 0);
#endif
- return;
- }
- p = jsonParseCached(ctx, argv[0], 0, 0);
- if( p==0 || p->oom ){
- sqlite3_result_error_nomem(ctx);
- sqlite3_free(p);
- }else{
- sqlite3_result_int(ctx, p->nErr==0 && (p->hasNonstd==0 || p->useMod));
- if( p->nErr ) jsonParseFree(p);
+ return;
+ }
+ case SQLITE_BLOB: {
+ if( jsonFuncArgMightBeBinary(argv[0]) ){
+ if( flags & 0x04 ){
+ /* Superficial checking only - accomplished by the
+ ** jsonFuncArgMightBeBinary() call above. */
+ res = 1;
+ }else if( flags & 0x08 ){
+ /* Strict checking. Check by translating BLOB->TEXT->BLOB. If
+ ** no errors occur, call that a "strict check". */
+ JsonParse px;
+ u32 iErr;
+ memset(&px, 0, sizeof(px));
+ px.aBlob = (u8*)sqlite3_value_blob(argv[0]);
+ px.nBlob = sqlite3_value_bytes(argv[0]);
+ iErr = jsonbValidityCheck(&px, 0, px.nBlob, 1);
+ res = iErr==0;
+ }
+ break;
+ }
+ /* Fall through into interpreting the input as text. See note
+ ** above at tag-20240123-a. */
+ /* no break */ deliberate_fall_through
+ }
+ default: {
+ JsonParse px;
+ if( (flags & 0x3)==0 ) break;
+ memset(&px, 0, sizeof(px));
+
+ p = jsonParseFuncArg(ctx, argv[0], JSON_KEEPERROR);
+ if( p ){
+ if( p->oom ){
+ sqlite3_result_error_nomem(ctx);
+ }else if( p->nErr ){
+ /* no-op */
+ }else if( (flags & 0x02)!=0 || p->hasNonstd==0 ){
+ res = 1;
+ }
+ jsonParseFree(p);
+ }else{
+ sqlite3_result_error_nomem(ctx);
+ }
+ break;
+ }
}
+ sqlite3_result_int(ctx, res);
}
/*
** json_error_position(JSON)
**
-** If the argument is not an interpretable JSON string, then return the 1-based
-** character position at which the parser first recognized that the input
-** was in error. The left-most character is 1. If the string is valid
-** JSON, then return 0.
-**
-** Note that json_valid() is only true for strictly conforming canonical JSON.
-** But this routine returns zero if the input contains extension. Thus:
-**
-** (1) If the input X is strictly conforming canonical JSON:
-**
-** json_valid(X) returns true
-** json_error_position(X) returns 0
-**
-** (2) If the input X is JSON but it includes extension (such as JSON5) that
-** are not part of RFC-8259:
+** If the argument is NULL, return NULL
**
-** json_valid(X) returns false
-** json_error_position(X) return 0
+** If the argument is BLOB, do a full validity check and return non-zero
+** if the check fails. The return value is the approximate 1-based offset
+** to the byte of the element that contains the first error.
**
-** (3) If the input X cannot be interpreted as JSON even taking extensions
-** into account:
-**
-** json_valid(X) return false
-** json_error_position(X) returns 1 or more
+** Otherwise interpret the argument is TEXT (even if it is numeric) and
+** return the 1-based character position for where the parser first recognized
+** that the input was not valid JSON, or return 0 if the input text looks
+** ok. JSON-5 extensions are accepted.
*/
static void jsonErrorFunc(
sqlite3_context *ctx,
int argc,
sqlite3_value **argv
){
- JsonParse *p; /* The parse */
+ i64 iErrPos = 0; /* Error position to be returned */
+ JsonParse s;
+
+ assert( argc==1 );
UNUSED_PARAMETER(argc);
- if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
- p = jsonParseCached(ctx, argv[0], 0, 0);
- if( p==0 || p->oom ){
+ memset(&s, 0, sizeof(s));
+ s.db = sqlite3_context_db_handle(ctx);
+ if( jsonFuncArgMightBeBinary(argv[0]) ){
+ s.aBlob = (u8*)sqlite3_value_blob(argv[0]);
+ s.nBlob = sqlite3_value_bytes(argv[0]);
+ iErrPos = (i64)jsonbValidityCheck(&s, 0, s.nBlob, 1);
+ }else{
+ s.zJson = (char*)sqlite3_value_text(argv[0]);
+ if( s.zJson==0 ) return; /* NULL input or OOM */
+ s.nJson = sqlite3_value_bytes(argv[0]);
+ if( jsonConvertTextToBlob(&s,0) ){
+ if( s.oom ){
+ iErrPos = -1;
+ }else{
+ /* Convert byte-offset s.iErr into a character offset */
+ u32 k;
+ assert( s.zJson!=0 ); /* Because s.oom is false */
+ for(k=0; k<s.iErr && ALWAYS(s.zJson[k]); k++){
+ if( (s.zJson[k] & 0xc0)!=0x80 ) iErrPos++;
+ }
+ iErrPos++;
+ }
+ }
+ }
+ jsonParseReset(&s);
+ if( iErrPos<0 ){
sqlite3_result_error_nomem(ctx);
- sqlite3_free(p);
- }else if( p->nErr==0 ){
- sqlite3_result_int(ctx, 0);
}else{
- int n = 1;
- u32 i;
- const char *z = (const char*)sqlite3_value_text(argv[0]);
- for(i=0; i<p->iErr && ALWAYS(z[i]); i++){
- if( (z[i]&0xc0)!=0x80 ) n++;
- }
- sqlite3_result_int(ctx, n);
- jsonParseFree(p);
+ sqlite3_result_int64(ctx, iErrPos);
}
}
-
/****************************************************************************
** Aggregate SQL function implementations
****************************************************************************/
@@ -204335,32 +209452,42 @@ static void jsonArrayStep(
pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
if( pStr ){
if( pStr->zBuf==0 ){
- jsonInit(pStr, ctx);
+ jsonStringInit(pStr, ctx);
jsonAppendChar(pStr, '[');
}else if( pStr->nUsed>1 ){
jsonAppendChar(pStr, ',');
}
pStr->pCtx = ctx;
- jsonAppendValue(pStr, argv[0]);
+ jsonAppendSqlValue(pStr, argv[0]);
}
}
static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){
JsonString *pStr;
pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
if( pStr ){
+ int flags;
pStr->pCtx = ctx;
jsonAppendChar(pStr, ']');
- if( pStr->bErr ){
- if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
- assert( pStr->bStatic );
+ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ if( pStr->eErr ){
+ jsonReturnString(pStr, 0, 0);
+ return;
+ }else if( flags & JSON_BLOB ){
+ jsonReturnStringAsBlob(pStr);
+ if( isFinal ){
+ if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf);
+ }else{
+ jsonStringTrimOneChar(pStr);
+ }
+ return;
}else if( isFinal ){
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
pStr->bStatic ? SQLITE_TRANSIENT :
- (void(*)(void*))sqlite3RCStrUnref);
+ sqlite3RCStrUnref);
pStr->bStatic = 1;
}else{
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
- pStr->nUsed--;
+ jsonStringTrimOneChar(pStr);
}
}else{
sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
@@ -204441,35 +209568,46 @@ static void jsonObjectStep(
pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
if( pStr ){
if( pStr->zBuf==0 ){
- jsonInit(pStr, ctx);
+ jsonStringInit(pStr, ctx);
jsonAppendChar(pStr, '{');
}else if( pStr->nUsed>1 ){
jsonAppendChar(pStr, ',');
}
pStr->pCtx = ctx;
z = (const char*)sqlite3_value_text(argv[0]);
- n = (u32)sqlite3_value_bytes(argv[0]);
+ n = sqlite3Strlen30(z);
jsonAppendString(pStr, z, n);
jsonAppendChar(pStr, ':');
- jsonAppendValue(pStr, argv[1]);
+ jsonAppendSqlValue(pStr, argv[1]);
}
}
static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){
JsonString *pStr;
pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
if( pStr ){
+ int flags;
jsonAppendChar(pStr, '}');
- if( pStr->bErr ){
- if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
- assert( pStr->bStatic );
+ pStr->pCtx = ctx;
+ flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
+ if( pStr->eErr ){
+ jsonReturnString(pStr, 0, 0);
+ return;
+ }else if( flags & JSON_BLOB ){
+ jsonReturnStringAsBlob(pStr);
+ if( isFinal ){
+ if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf);
+ }else{
+ jsonStringTrimOneChar(pStr);
+ }
+ return;
}else if( isFinal ){
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
pStr->bStatic ? SQLITE_TRANSIENT :
- (void(*)(void*))sqlite3RCStrUnref);
+ sqlite3RCStrUnref);
pStr->bStatic = 1;
}else{
sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
- pStr->nUsed--;
+ jsonStringTrimOneChar(pStr);
}
}else{
sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
@@ -204489,19 +209627,37 @@ static void jsonObjectFinal(sqlite3_context *ctx){
/****************************************************************************
** The json_each virtual table
****************************************************************************/
+typedef struct JsonParent JsonParent;
+struct JsonParent {
+ u32 iHead; /* Start of object or array */
+ u32 iValue; /* Start of the value */
+ u32 iEnd; /* First byte past the end */
+ u32 nPath; /* Length of path */
+ i64 iKey; /* Key for JSONB_ARRAY */
+};
+
typedef struct JsonEachCursor JsonEachCursor;
struct JsonEachCursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
u32 iRowid; /* The rowid */
- u32 iBegin; /* The first node of the scan */
- u32 i; /* Index in sParse.aNode[] of current row */
+ u32 i; /* Index in sParse.aBlob[] of current row */
u32 iEnd; /* EOF when i equals or exceeds this value */
- u8 eType; /* Type of top-level element */
+ u32 nRoot; /* Size of the root path in bytes */
+ u8 eType; /* Type of the container for element i */
u8 bRecursive; /* True for json_tree(). False for json_each() */
- char *zJson; /* Input JSON */
- char *zRoot; /* Path by which to filter zJson */
+ u32 nParent; /* Current nesting depth */
+ u32 nParentAlloc; /* Space allocated for aParent[] */
+ JsonParent *aParent; /* Parent elements of i */
+ sqlite3 *db; /* Database connection */
+ JsonString path; /* Current path */
JsonParse sParse; /* Parse of the input JSON */
};
+typedef struct JsonEachConnection JsonEachConnection;
+struct JsonEachConnection {
+ sqlite3_vtab base; /* Base class - must be first */
+ sqlite3 *db; /* Database connection */
+};
+
/* Constructor for the json_each virtual table */
static int jsonEachConnect(
@@ -204511,7 +209667,7 @@ static int jsonEachConnect(
sqlite3_vtab **ppVtab,
char **pzErr
){
- sqlite3_vtab *pNew;
+ JsonEachConnection *pNew;
int rc;
/* Column numbers */
@@ -204537,28 +209693,32 @@ static int jsonEachConnect(
"CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path,"
"json HIDDEN,root HIDDEN)");
if( rc==SQLITE_OK ){
- pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
+ pNew = (JsonEachConnection*)sqlite3DbMallocZero(db, sizeof(*pNew));
+ *ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
- memset(pNew, 0, sizeof(*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
+ pNew->db = db;
}
return rc;
}
/* destructor for json_each virtual table */
static int jsonEachDisconnect(sqlite3_vtab *pVtab){
- sqlite3_free(pVtab);
+ JsonEachConnection *p = (JsonEachConnection*)pVtab;
+ sqlite3DbFree(p->db, pVtab);
return SQLITE_OK;
}
/* constructor for a JsonEachCursor object for json_each(). */
static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
+ JsonEachConnection *pVtab = (JsonEachConnection*)p;
JsonEachCursor *pCur;
UNUSED_PARAMETER(p);
- pCur = sqlite3_malloc( sizeof(*pCur) );
+ pCur = sqlite3DbMallocZero(pVtab->db, sizeof(*pCur));
if( pCur==0 ) return SQLITE_NOMEM;
- memset(pCur, 0, sizeof(*pCur));
+ pCur->db = pVtab->db;
+ jsonStringZero(&pCur->path);
*ppCursor = &pCur->base;
return SQLITE_OK;
}
@@ -204576,21 +209736,24 @@ static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
/* Reset a JsonEachCursor back to its original state. Free any memory
** held. */
static void jsonEachCursorReset(JsonEachCursor *p){
- sqlite3_free(p->zRoot);
jsonParseReset(&p->sParse);
+ jsonStringReset(&p->path);
+ sqlite3DbFree(p->db, p->aParent);
p->iRowid = 0;
p->i = 0;
+ p->aParent = 0;
+ p->nParent = 0;
+ p->nParentAlloc = 0;
p->iEnd = 0;
p->eType = 0;
- p->zJson = 0;
- p->zRoot = 0;
}
/* Destructor for a jsonEachCursor object */
static int jsonEachClose(sqlite3_vtab_cursor *cur){
JsonEachCursor *p = (JsonEachCursor*)cur;
jsonEachCursorReset(p);
- sqlite3_free(cur);
+
+ sqlite3DbFree(p->db, cur);
return SQLITE_OK;
}
@@ -204601,200 +209764,233 @@ static int jsonEachEof(sqlite3_vtab_cursor *cur){
return p->i >= p->iEnd;
}
-/* Advance the cursor to the next element for json_tree() */
-static int jsonEachNext(sqlite3_vtab_cursor *cur){
- JsonEachCursor *p = (JsonEachCursor*)cur;
- if( p->bRecursive ){
- if( p->sParse.aNode[p->i].jnFlags & JNODE_LABEL ) p->i++;
- p->i++;
- p->iRowid++;
- if( p->i<p->iEnd ){
- u32 iUp = p->sParse.aUp[p->i];
- JsonNode *pUp = &p->sParse.aNode[iUp];
- p->eType = pUp->eType;
- if( pUp->eType==JSON_ARRAY ){
- assert( pUp->eU==0 || pUp->eU==3 );
- testcase( pUp->eU==3 );
- VVA( pUp->eU = 3 );
- if( iUp==p->i-1 ){
- pUp->u.iKey = 0;
- }else{
- pUp->u.iKey++;
+/*
+** If the cursor is currently pointing at the label of a object entry,
+** then return the index of the value. For all other cases, return the
+** current pointer position, which is the value.
+*/
+static int jsonSkipLabel(JsonEachCursor *p){
+ if( p->eType==JSONB_OBJECT ){
+ u32 sz = 0;
+ u32 n = jsonbPayloadSize(&p->sParse, p->i, &sz);
+ return p->i + n + sz;
+ }else{
+ return p->i;
+ }
+}
+
+/*
+** Append the path name for the current element.
+*/
+static void jsonAppendPathName(JsonEachCursor *p){
+ assert( p->nParent>0 );
+ assert( p->eType==JSONB_ARRAY || p->eType==JSONB_OBJECT );
+ if( p->eType==JSONB_ARRAY ){
+ jsonPrintf(30, &p->path, "[%lld]", p->aParent[p->nParent-1].iKey);
+ }else{
+ u32 n, sz = 0, k, i;
+ const char *z;
+ int needQuote = 0;
+ n = jsonbPayloadSize(&p->sParse, p->i, &sz);
+ k = p->i + n;
+ z = (const char*)&p->sParse.aBlob[k];
+ if( sz==0 || !sqlite3Isalpha(z[0]) ){
+ needQuote = 1;
+ }else{
+ for(i=0; i<sz; i++){
+ if( !sqlite3Isalnum(z[i]) ){
+ needQuote = 1;
+ break;
}
}
}
- }else{
- switch( p->eType ){
- case JSON_ARRAY: {
- p->i += jsonNodeSize(&p->sParse.aNode[p->i]);
- p->iRowid++;
- break;
- }
- case JSON_OBJECT: {
- p->i += 1 + jsonNodeSize(&p->sParse.aNode[p->i+1]);
- p->iRowid++;
- break;
- }
- default: {
- p->i = p->iEnd;
- break;
- }
+ if( needQuote ){
+ jsonPrintf(sz+4,&p->path,".\"%.*s\"", sz, z);
+ }else{
+ jsonPrintf(sz+2,&p->path,".%.*s", sz, z);
}
}
- return SQLITE_OK;
}
-/* Append an object label to the JSON Path being constructed
-** in pStr.
-*/
-static void jsonAppendObjectPathElement(
- JsonString *pStr,
- JsonNode *pNode
-){
- int jj, nn;
- const char *z;
- assert( pNode->eType==JSON_STRING );
- assert( pNode->jnFlags & JNODE_LABEL );
- assert( pNode->eU==1 );
- z = pNode->u.zJContent;
- nn = pNode->n;
- if( (pNode->jnFlags & JNODE_RAW)==0 ){
- assert( nn>=2 );
- assert( z[0]=='"' || z[0]=='\'' );
- assert( z[nn-1]=='"' || z[0]=='\'' );
- if( nn>2 && sqlite3Isalpha(z[1]) ){
- for(jj=2; jj<nn-1 && sqlite3Isalnum(z[jj]); jj++){}
- if( jj==nn-1 ){
- z++;
- nn -= 2;
+/* Advance the cursor to the next element for json_tree() */
+static int jsonEachNext(sqlite3_vtab_cursor *cur){
+ JsonEachCursor *p = (JsonEachCursor*)cur;
+ int rc = SQLITE_OK;
+ if( p->bRecursive ){
+ u8 x;
+ u8 levelChange = 0;
+ u32 n, sz = 0;
+ u32 i = jsonSkipLabel(p);
+ x = p->sParse.aBlob[i] & 0x0f;
+ n = jsonbPayloadSize(&p->sParse, i, &sz);
+ if( x==JSONB_OBJECT || x==JSONB_ARRAY ){
+ JsonParent *pParent;
+ if( p->nParent>=p->nParentAlloc ){
+ JsonParent *pNew;
+ u64 nNew;
+ nNew = p->nParentAlloc*2 + 3;
+ pNew = sqlite3DbRealloc(p->db, p->aParent, sizeof(JsonParent)*nNew);
+ if( pNew==0 ) return SQLITE_NOMEM;
+ p->nParentAlloc = (u32)nNew;
+ p->aParent = pNew;
+ }
+ levelChange = 1;
+ pParent = &p->aParent[p->nParent];
+ pParent->iHead = p->i;
+ pParent->iValue = i;
+ pParent->iEnd = i + n + sz;
+ pParent->iKey = -1;
+ pParent->nPath = (u32)p->path.nUsed;
+ if( p->eType && p->nParent ){
+ jsonAppendPathName(p);
+ if( p->path.eErr ) rc = SQLITE_NOMEM;
+ }
+ p->nParent++;
+ p->i = i + n;
+ }else{
+ p->i = i + n + sz;
+ }
+ while( p->nParent>0 && p->i >= p->aParent[p->nParent-1].iEnd ){
+ p->nParent--;
+ p->path.nUsed = p->aParent[p->nParent].nPath;
+ levelChange = 1;
+ }
+ if( levelChange ){
+ if( p->nParent>0 ){
+ JsonParent *pParent = &p->aParent[p->nParent-1];
+ u32 iVal = pParent->iValue;
+ p->eType = p->sParse.aBlob[iVal] & 0x0f;
+ }else{
+ p->eType = 0;
}
}
+ }else{
+ u32 n, sz = 0;
+ u32 i = jsonSkipLabel(p);
+ n = jsonbPayloadSize(&p->sParse, i, &sz);
+ p->i = i + n + sz;
+ }
+ if( p->eType==JSONB_ARRAY && p->nParent ){
+ p->aParent[p->nParent-1].iKey++;
}
- jsonPrintf(nn+2, pStr, ".%.*s", nn, z);
+ p->iRowid++;
+ return rc;
}
-/* Append the name of the path for element i to pStr
+/* Length of the path for rowid==0 in bRecursive mode.
*/
-static void jsonEachComputePath(
- JsonEachCursor *p, /* The cursor */
- JsonString *pStr, /* Write the path here */
- u32 i /* Path to this element */
-){
- JsonNode *pNode, *pUp;
- u32 iUp;
- if( i==0 ){
- jsonAppendChar(pStr, '$');
- return;
- }
- iUp = p->sParse.aUp[i];
- jsonEachComputePath(p, pStr, iUp);
- pNode = &p->sParse.aNode[i];
- pUp = &p->sParse.aNode[iUp];
- if( pUp->eType==JSON_ARRAY ){
- assert( pUp->eU==3 || (pUp->eU==0 && pUp->u.iKey==0) );
- testcase( pUp->eU==0 );
- jsonPrintf(30, pStr, "[%d]", pUp->u.iKey);
- }else{
- assert( pUp->eType==JSON_OBJECT );
- if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--;
- jsonAppendObjectPathElement(pStr, pNode);
+static int jsonEachPathLength(JsonEachCursor *p){
+ u32 n = p->path.nUsed;
+ char *z = p->path.zBuf;
+ if( p->iRowid==0 && p->bRecursive && n>=2 ){
+ while( n>1 ){
+ n--;
+ if( z[n]=='[' || z[n]=='.' ){
+ u32 x, sz = 0;
+ char cSaved = z[n];
+ z[n] = 0;
+ assert( p->sParse.eEdit==0 );
+ x = jsonLookupStep(&p->sParse, 0, z+1, 0);
+ z[n] = cSaved;
+ if( JSON_LOOKUP_ISERROR(x) ) continue;
+ if( x + jsonbPayloadSize(&p->sParse, x, &sz) == p->i ) break;
+ }
+ }
}
+ return n;
}
/* Return the value of a column */
static int jsonEachColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
- int i /* Which column to return */
+ int iColumn /* Which column to return */
){
JsonEachCursor *p = (JsonEachCursor*)cur;
- JsonNode *pThis = &p->sParse.aNode[p->i];
- switch( i ){
+ switch( iColumn ){
case JEACH_KEY: {
- if( p->i==0 ) break;
- if( p->eType==JSON_OBJECT ){
- jsonReturn(&p->sParse, pThis, ctx);
- }else if( p->eType==JSON_ARRAY ){
- u32 iKey;
- if( p->bRecursive ){
- if( p->iRowid==0 ) break;
- assert( p->sParse.aNode[p->sParse.aUp[p->i]].eU==3 );
- iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey;
+ if( p->nParent==0 ){
+ u32 n, j;
+ if( p->nRoot==1 ) break;
+ j = jsonEachPathLength(p);
+ n = p->nRoot - j;
+ if( n==0 ){
+ break;
+ }else if( p->path.zBuf[j]=='[' ){
+ i64 x;
+ sqlite3Atoi64(&p->path.zBuf[j+1], &x, n-1, SQLITE_UTF8);
+ sqlite3_result_int64(ctx, x);
+ }else if( p->path.zBuf[j+1]=='"' ){
+ sqlite3_result_text(ctx, &p->path.zBuf[j+2], n-3, SQLITE_TRANSIENT);
}else{
- iKey = p->iRowid;
+ sqlite3_result_text(ctx, &p->path.zBuf[j+1], n-1, SQLITE_TRANSIENT);
}
- sqlite3_result_int64(ctx, (sqlite3_int64)iKey);
+ break;
+ }
+ if( p->eType==JSONB_OBJECT ){
+ jsonReturnFromBlob(&p->sParse, p->i, ctx, 1);
+ }else{
+ assert( p->eType==JSONB_ARRAY );
+ sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iKey);
}
break;
}
case JEACH_VALUE: {
- if( pThis->jnFlags & JNODE_LABEL ) pThis++;
- jsonReturn(&p->sParse, pThis, ctx);
+ u32 i = jsonSkipLabel(p);
+ jsonReturnFromBlob(&p->sParse, i, ctx, 1);
+ if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY ){
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+ }
break;
}
case JEACH_TYPE: {
- if( pThis->jnFlags & JNODE_LABEL ) pThis++;
- sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC);
+ u32 i = jsonSkipLabel(p);
+ u8 eType = p->sParse.aBlob[i] & 0x0f;
+ sqlite3_result_text(ctx, jsonbType[eType], -1, SQLITE_STATIC);
break;
}
case JEACH_ATOM: {
- if( pThis->jnFlags & JNODE_LABEL ) pThis++;
- if( pThis->eType>=JSON_ARRAY ) break;
- jsonReturn(&p->sParse, pThis, ctx);
+ u32 i = jsonSkipLabel(p);
+ if( (p->sParse.aBlob[i] & 0x0f)<JSONB_ARRAY ){
+ jsonReturnFromBlob(&p->sParse, i, ctx, 1);
+ }
break;
}
case JEACH_ID: {
- sqlite3_result_int64(ctx,
- (sqlite3_int64)p->i + ((pThis->jnFlags & JNODE_LABEL)!=0));
+ sqlite3_result_int64(ctx, (sqlite3_int64)p->i);
break;
}
case JEACH_PARENT: {
- if( p->i>p->iBegin && p->bRecursive ){
- sqlite3_result_int64(ctx, (sqlite3_int64)p->sParse.aUp[p->i]);
+ if( p->nParent>0 && p->bRecursive ){
+ sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iHead);
}
break;
}
case JEACH_FULLKEY: {
- JsonString x;
- jsonInit(&x, ctx);
- if( p->bRecursive ){
- jsonEachComputePath(p, &x, p->i);
- }else{
- if( p->zRoot ){
- jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot));
- }else{
- jsonAppendChar(&x, '$');
- }
- if( p->eType==JSON_ARRAY ){
- jsonPrintf(30, &x, "[%d]", p->iRowid);
- }else if( p->eType==JSON_OBJECT ){
- jsonAppendObjectPathElement(&x, pThis);
- }
- }
- jsonResult(&x);
+ u64 nBase = p->path.nUsed;
+ if( p->nParent ) jsonAppendPathName(p);
+ sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed,
+ SQLITE_TRANSIENT, SQLITE_UTF8);
+ p->path.nUsed = nBase;
break;
}
case JEACH_PATH: {
- if( p->bRecursive ){
- JsonString x;
- jsonInit(&x, ctx);
- jsonEachComputePath(p, &x, p->sParse.aUp[p->i]);
- jsonResult(&x);
- break;
- }
- /* For json_each() path and root are the same so fall through
- ** into the root case */
- /* no break */ deliberate_fall_through
+ u32 n = jsonEachPathLength(p);
+ sqlite3_result_text64(ctx, p->path.zBuf, n,
+ SQLITE_TRANSIENT, SQLITE_UTF8);
+ break;
}
default: {
- const char *zRoot = p->zRoot;
- if( zRoot==0 ) zRoot = "$";
- sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC);
+ sqlite3_result_text(ctx, p->path.zBuf, p->nRoot, SQLITE_STATIC);
break;
}
case JEACH_JSON: {
- assert( i==JEACH_JSON );
- sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
+ if( p->sParse.zJson==0 ){
+ sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob,
+ SQLITE_TRANSIENT);
+ }else{
+ sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_TRANSIENT);
+ }
break;
}
}
@@ -204885,86 +210081,97 @@ static int jsonEachFilter(
int argc, sqlite3_value **argv
){
JsonEachCursor *p = (JsonEachCursor*)cur;
- const char *z;
const char *zRoot = 0;
- sqlite3_int64 n;
+ u32 i, n, sz;
UNUSED_PARAMETER(idxStr);
UNUSED_PARAMETER(argc);
jsonEachCursorReset(p);
if( idxNum==0 ) return SQLITE_OK;
- z = (const char*)sqlite3_value_text(argv[0]);
- if( z==0 ) return SQLITE_OK;
memset(&p->sParse, 0, sizeof(p->sParse));
p->sParse.nJPRef = 1;
- if( sqlite3ValueIsOfClass(argv[0], (void(*)(void*))sqlite3RCStrUnref) ){
- p->sParse.zJson = sqlite3RCStrRef((char*)z);
- }else{
- n = sqlite3_value_bytes(argv[0]);
- p->sParse.zJson = sqlite3RCStrNew( n+1 );
- if( p->sParse.zJson==0 ) return SQLITE_NOMEM;
- memcpy(p->sParse.zJson, z, (size_t)n+1);
- }
- p->sParse.bJsonIsRCStr = 1;
- p->zJson = p->sParse.zJson;
- if( jsonParse(&p->sParse, 0) ){
- int rc = SQLITE_NOMEM;
- if( p->sParse.oom==0 ){
- sqlite3_free(cur->pVtab->zErrMsg);
- cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON");
- if( cur->pVtab->zErrMsg ) rc = SQLITE_ERROR;
+ p->sParse.db = p->db;
+ if( jsonFuncArgMightBeBinary(argv[0]) ){
+ p->sParse.nBlob = sqlite3_value_bytes(argv[0]);
+ p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]);
+ }else{
+ p->sParse.zJson = (char*)sqlite3_value_text(argv[0]);
+ p->sParse.nJson = sqlite3_value_bytes(argv[0]);
+ if( p->sParse.zJson==0 ){
+ p->i = p->iEnd = 0;
+ return SQLITE_OK;
}
- jsonEachCursorReset(p);
- return rc;
- }else if( p->bRecursive && jsonParseFindParents(&p->sParse) ){
- jsonEachCursorReset(p);
- return SQLITE_NOMEM;
- }else{
- JsonNode *pNode = 0;
- if( idxNum==3 ){
- const char *zErr = 0;
- zRoot = (const char*)sqlite3_value_text(argv[1]);
- if( zRoot==0 ) return SQLITE_OK;
- n = sqlite3_value_bytes(argv[1]);
- p->zRoot = sqlite3_malloc64( n+1 );
- if( p->zRoot==0 ) return SQLITE_NOMEM;
- memcpy(p->zRoot, zRoot, (size_t)n+1);
- if( zRoot[0]!='$' ){
- zErr = zRoot;
- }else{
- pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr);
+ if( jsonConvertTextToBlob(&p->sParse, 0) ){
+ if( p->sParse.oom ){
+ return SQLITE_NOMEM;
}
- if( zErr ){
+ goto json_each_malformed_input;
+ }
+ }
+ if( idxNum==3 ){
+ zRoot = (const char*)sqlite3_value_text(argv[1]);
+ if( zRoot==0 ) return SQLITE_OK;
+ if( zRoot[0]!='$' ){
+ sqlite3_free(cur->pVtab->zErrMsg);
+ cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot);
+ jsonEachCursorReset(p);
+ return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
+ }
+ p->nRoot = sqlite3Strlen30(zRoot);
+ if( zRoot[1]==0 ){
+ i = p->i = 0;
+ p->eType = 0;
+ }else{
+ i = jsonLookupStep(&p->sParse, 0, zRoot+1, 0);
+ if( JSON_LOOKUP_ISERROR(i) ){
+ if( i==JSON_LOOKUP_NOTFOUND ){
+ p->i = 0;
+ p->eType = 0;
+ p->iEnd = 0;
+ return SQLITE_OK;
+ }
sqlite3_free(cur->pVtab->zErrMsg);
- cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr);
+ cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot);
jsonEachCursorReset(p);
return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
- }else if( pNode==0 ){
- return SQLITE_OK;
}
- }else{
- pNode = p->sParse.aNode;
- }
- p->iBegin = p->i = (int)(pNode - p->sParse.aNode);
- p->eType = pNode->eType;
- if( p->eType>=JSON_ARRAY ){
- assert( pNode->eU==0 );
- VVA( pNode->eU = 3 );
- pNode->u.iKey = 0;
- p->iEnd = p->i + pNode->n + 1;
- if( p->bRecursive ){
- p->eType = p->sParse.aNode[p->sParse.aUp[p->i]].eType;
- if( p->i>0 && (p->sParse.aNode[p->i-1].jnFlags & JNODE_LABEL)!=0 ){
- p->i--;
- }
+ if( p->sParse.iLabel ){
+ p->i = p->sParse.iLabel;
+ p->eType = JSONB_OBJECT;
}else{
- p->i++;
- }
- }else{
- p->iEnd = p->i+1;
- }
+ p->i = i;
+ p->eType = JSONB_ARRAY;
+ }
+ }
+ jsonAppendRaw(&p->path, zRoot, p->nRoot);
+ }else{
+ i = p->i = 0;
+ p->eType = 0;
+ p->nRoot = 1;
+ jsonAppendRaw(&p->path, "$", 1);
+ }
+ p->nParent = 0;
+ n = jsonbPayloadSize(&p->sParse, i, &sz);
+ p->iEnd = i+n+sz;
+ if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY && !p->bRecursive ){
+ p->i = i + n;
+ p->eType = p->sParse.aBlob[i] & 0x0f;
+ p->aParent = sqlite3DbMallocZero(p->db, sizeof(JsonParent));
+ if( p->aParent==0 ) return SQLITE_NOMEM;
+ p->nParent = 1;
+ p->nParentAlloc = 1;
+ p->aParent[0].iKey = 0;
+ p->aParent[0].iEnd = p->iEnd;
+ p->aParent[0].iHead = p->i;
+ p->aParent[0].iValue = i;
}
return SQLITE_OK;
+
+json_each_malformed_input:
+ sqlite3_free(cur->pVtab->zErrMsg);
+ cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON");
+ jsonEachCursorReset(p);
+ return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
}
/* The methods of the json_each virtual table */
@@ -204992,7 +210199,8 @@ static sqlite3_module jsonEachModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
/* The methods of the json_tree virtual table. */
@@ -205020,7 +210228,8 @@ static sqlite3_module jsonTreeModule = {
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#endif /* !defined(SQLITE_OMIT_JSON) */
@@ -205031,34 +210240,59 @@ static sqlite3_module jsonTreeModule = {
SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){
#ifndef SQLITE_OMIT_JSON
static FuncDef aJsonFunc[] = {
- JFUNCTION(json, 1, 0, jsonRemoveFunc),
- JFUNCTION(json_array, -1, 0, jsonArrayFunc),
- JFUNCTION(json_array_length, 1, 0, jsonArrayLengthFunc),
- JFUNCTION(json_array_length, 2, 0, jsonArrayLengthFunc),
- JFUNCTION(json_error_position,1, 0, jsonErrorFunc),
- JFUNCTION(json_extract, -1, 0, jsonExtractFunc),
- JFUNCTION(->, 2, JSON_JSON, jsonExtractFunc),
- JFUNCTION(->>, 2, JSON_SQL, jsonExtractFunc),
- JFUNCTION(json_insert, -1, 0, jsonSetFunc),
- JFUNCTION(json_object, -1, 0, jsonObjectFunc),
- JFUNCTION(json_patch, 2, 0, jsonPatchFunc),
- JFUNCTION(json_quote, 1, 0, jsonQuoteFunc),
- JFUNCTION(json_remove, -1, 0, jsonRemoveFunc),
- JFUNCTION(json_replace, -1, 0, jsonReplaceFunc),
- JFUNCTION(json_set, -1, JSON_ISSET, jsonSetFunc),
- JFUNCTION(json_type, 1, 0, jsonTypeFunc),
- JFUNCTION(json_type, 2, 0, jsonTypeFunc),
- JFUNCTION(json_valid, 1, 0, jsonValidFunc),
+ /* sqlite3_result_subtype() ----, ,--- sqlite3_value_subtype() */
+ /* | | */
+ /* Uses cache ------, | | ,---- Returns JSONB */
+ /* | | | | */
+ /* Number of arguments ---, | | | | ,--- Flags */
+ /* | | | | | | */
+ JFUNCTION(json, 1,1,1, 0,0,0, jsonRemoveFunc),
+ JFUNCTION(jsonb, 1,1,0, 0,1,0, jsonRemoveFunc),
+ JFUNCTION(json_array, -1,0,1, 1,0,0, jsonArrayFunc),
+ JFUNCTION(jsonb_array, -1,0,1, 1,1,0, jsonArrayFunc),
+ JFUNCTION(json_array_length, 1,1,0, 0,0,0, jsonArrayLengthFunc),
+ JFUNCTION(json_array_length, 2,1,0, 0,0,0, jsonArrayLengthFunc),
+ JFUNCTION(json_error_position,1,1,0, 0,0,0, jsonErrorFunc),
+ JFUNCTION(json_extract, -1,1,1, 0,0,0, jsonExtractFunc),
+ JFUNCTION(jsonb_extract, -1,1,0, 0,1,0, jsonExtractFunc),
+ JFUNCTION(->, 2,1,1, 0,0,JSON_JSON, jsonExtractFunc),
+ JFUNCTION(->>, 2,1,0, 0,0,JSON_SQL, jsonExtractFunc),
+ JFUNCTION(json_insert, -1,1,1, 1,0,0, jsonSetFunc),
+ JFUNCTION(jsonb_insert, -1,1,0, 1,1,0, jsonSetFunc),
+ JFUNCTION(json_object, -1,0,1, 1,0,0, jsonObjectFunc),
+ JFUNCTION(jsonb_object, -1,0,1, 1,1,0, jsonObjectFunc),
+ JFUNCTION(json_patch, 2,1,1, 0,0,0, jsonPatchFunc),
+ JFUNCTION(jsonb_patch, 2,1,0, 0,1,0, jsonPatchFunc),
+ JFUNCTION(json_pretty, 1,1,0, 0,0,0, jsonPrettyFunc),
+ JFUNCTION(json_pretty, 2,1,0, 0,0,0, jsonPrettyFunc),
+ JFUNCTION(json_quote, 1,0,1, 1,0,0, jsonQuoteFunc),
+ JFUNCTION(json_remove, -1,1,1, 0,0,0, jsonRemoveFunc),
+ JFUNCTION(jsonb_remove, -1,1,0, 0,1,0, jsonRemoveFunc),
+ JFUNCTION(json_replace, -1,1,1, 1,0,0, jsonReplaceFunc),
+ JFUNCTION(jsonb_replace, -1,1,0, 1,1,0, jsonReplaceFunc),
+ JFUNCTION(json_set, -1,1,1, 1,0,JSON_ISSET, jsonSetFunc),
+ JFUNCTION(jsonb_set, -1,1,0, 1,1,JSON_ISSET, jsonSetFunc),
+ JFUNCTION(json_type, 1,1,0, 0,0,0, jsonTypeFunc),
+ JFUNCTION(json_type, 2,1,0, 0,0,0, jsonTypeFunc),
+ JFUNCTION(json_valid, 1,1,0, 0,0,0, jsonValidFunc),
+ JFUNCTION(json_valid, 2,1,0, 0,0,0, jsonValidFunc),
#if SQLITE_DEBUG
- JFUNCTION(json_parse, 1, 0, jsonParseFunc),
- JFUNCTION(json_test1, 1, 0, jsonTest1Func),
+ JFUNCTION(json_parse, 1,1,0, 0,0,0, jsonParseFunc),
#endif
WAGGREGATE(json_group_array, 1, 0, 0,
jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
- SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|
+ SQLITE_DETERMINISTIC),
+ WAGGREGATE(jsonb_group_array, 1, JSON_BLOB, 0,
+ jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
WAGGREGATE(json_group_object, 2, 0, 0,
jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse,
- SQLITE_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC)
+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
+ WAGGREGATE(jsonb_group_object,2, JSON_BLOB, 0,
+ jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse,
+ SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|
+ SQLITE_DETERMINISTIC)
};
sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc));
#endif
@@ -205255,6 +210489,7 @@ struct Rtree {
int iDepth; /* Current depth of the r-tree structure */
char *zDb; /* Name of database containing r-tree table */
char *zName; /* Name of r-tree table */
+ char *zNodeName; /* Name of the %_node table */
u32 nBusy; /* Current number of users of this structure */
i64 nRowEst; /* Estimated number of rows in this table */
u32 nCursor; /* Number of open cursors */
@@ -205267,7 +210502,6 @@ struct Rtree {
** headed by the node (leaf nodes have RtreeNode.iNode==0).
*/
RtreeNode *pDeleted;
- int iReinsertHeight; /* Height of sub-trees Reinsert() has run on */
/* Blob I/O on xxx_node */
sqlite3_blob *pNodeBlob;
@@ -205564,15 +210798,20 @@ struct RtreeMatchArg {
** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined
** at run-time.
*/
-#ifndef SQLITE_BYTEORDER
-# if defined(i386) || defined(__i386__) || defined(_M_IX86) || \
+#ifndef SQLITE_BYTEORDER /* Replicate changes at tag-20230904a */
+# if defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
+# define SQLITE_BYTEORDER 4321
+# elif defined(__BYTE_ORDER__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
+# define SQLITE_BYTEORDER 1234
+# elif defined(__BIG_ENDIAN__) && __BIG_ENDIAN__==1
+# define SQLITE_BYTEORDER 4321
+# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \
defined(__ARMEL__) || defined(__AARCH64EL__) || defined(_M_ARM64)
-# define SQLITE_BYTEORDER 1234
-# elif defined(sparc) || defined(__ppc__) || \
- defined(__ARMEB__) || defined(__AARCH64EB__)
-# define SQLITE_BYTEORDER 4321
+# define SQLITE_BYTEORDER 1234
+# elif defined(sparc) || defined(__ARMEB__) || defined(__AARCH64EB__)
+# define SQLITE_BYTEORDER 4321
# else
# define SQLITE_BYTEORDER 0
# endif
@@ -205778,11 +211017,9 @@ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){
** Clear the Rtree.pNodeBlob object
*/
static void nodeBlobReset(Rtree *pRtree){
- if( pRtree->pNodeBlob && pRtree->inWrTrans==0 && pRtree->nCursor==0 ){
- sqlite3_blob *pBlob = pRtree->pNodeBlob;
- pRtree->pNodeBlob = 0;
- sqlite3_blob_close(pBlob);
- }
+ sqlite3_blob *pBlob = pRtree->pNodeBlob;
+ pRtree->pNodeBlob = 0;
+ sqlite3_blob_close(pBlob);
}
/*
@@ -205801,7 +211038,7 @@ static int nodeAcquire(
** increase its reference count and return it.
*/
if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){
- if( pParent && pParent!=pNode->pParent ){
+ if( pParent && ALWAYS(pParent!=pNode->pParent) ){
RTREE_IS_CORRUPT(pRtree);
return SQLITE_CORRUPT_VTAB;
}
@@ -205821,14 +211058,11 @@ static int nodeAcquire(
}
}
if( pRtree->pNodeBlob==0 ){
- char *zTab = sqlite3_mprintf("%s_node", pRtree->zName);
- if( zTab==0 ) return SQLITE_NOMEM;
- rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, zTab, "data", iNode, 0,
+ rc = sqlite3_blob_open(pRtree->db, pRtree->zDb, pRtree->zNodeName,
+ "data", iNode, 0,
&pRtree->pNodeBlob);
- sqlite3_free(zTab);
}
if( rc ){
- nodeBlobReset(pRtree);
*ppNode = 0;
/* If unable to open an sqlite3_blob on the desired row, that can only
** be because the shadow tables hold erroneous data. */
@@ -205888,6 +211122,7 @@ static int nodeAcquire(
}
*ppNode = pNode;
}else{
+ nodeBlobReset(pRtree);
if( pNode ){
pRtree->nNodeRef--;
sqlite3_free(pNode);
@@ -206032,6 +211267,7 @@ static void nodeGetCoord(
int iCoord, /* Which coordinate to extract */
RtreeCoord *pCoord /* OUT: Space to write result to */
){
+ assert( iCell<NCELL(pNode) );
readCoord(&pNode->zData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord);
}
@@ -206221,7 +211457,9 @@ static int rtreeClose(sqlite3_vtab_cursor *cur){
sqlite3_finalize(pCsr->pReadAux);
sqlite3_free(pCsr);
pRtree->nCursor--;
- nodeBlobReset(pRtree);
+ if( pRtree->nCursor==0 && pRtree->inWrTrans==0 ){
+ nodeBlobReset(pRtree);
+ }
return SQLITE_OK;
}
@@ -206806,7 +212044,11 @@ static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
int rc = SQLITE_OK;
RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
if( rc==SQLITE_OK && ALWAYS(p) ){
- *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell);
+ if( p->iCell>=NCELL(pNode) ){
+ rc = SQLITE_ABORT;
+ }else{
+ *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell);
+ }
}
return rc;
}
@@ -206824,6 +212066,7 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
if( rc ) return rc;
if( NEVER(p==0) ) return SQLITE_OK;
+ if( p->iCell>=NCELL(pNode) ) return SQLITE_ABORT;
if( i==0 ){
sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell));
}else if( i<=pRtree->nDim2 ){
@@ -206921,6 +212164,8 @@ static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
return SQLITE_OK;
}
+SQLITE_PRIVATE int sqlite3IntFloatCompare(i64,double);
+
/*
** Rtree virtual table module xFilter method.
*/
@@ -206950,7 +212195,8 @@ static int rtreeFilter(
i64 iNode = 0;
int eType = sqlite3_value_numeric_type(argv[0]);
if( eType==SQLITE_INTEGER
- || (eType==SQLITE_FLOAT && sqlite3_value_double(argv[0])==iRowid)
+ || (eType==SQLITE_FLOAT
+ && 0==sqlite3IntFloatCompare(iRowid,sqlite3_value_double(argv[0])))
){
rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
}else{
@@ -207166,8 +212412,12 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
pIdxInfo->idxNum = 2;
pIdxInfo->needToFreeIdxStr = 1;
- if( iIdx>0 && 0==(pIdxInfo->idxStr = sqlite3_mprintf("%s", zIdxStr)) ){
- return SQLITE_NOMEM;
+ if( iIdx>0 ){
+ pIdxInfo->idxStr = sqlite3_malloc( iIdx+1 );
+ if( pIdxInfo->idxStr==0 ){
+ return SQLITE_NOMEM;
+ }
+ memcpy(pIdxInfo->idxStr, zIdxStr, iIdx+1);
}
nRow = pRtree->nRowEst >> (iIdx/2);
@@ -207246,31 +212496,22 @@ static void cellUnion(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){
*/
static int cellContains(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){
int ii;
- int isInt = (pRtree->eCoordType==RTREE_COORD_INT32);
- for(ii=0; ii<pRtree->nDim2; ii+=2){
- RtreeCoord *a1 = &p1->aCoord[ii];
- RtreeCoord *a2 = &p2->aCoord[ii];
- if( (!isInt && (a2[0].f<a1[0].f || a2[1].f>a1[1].f))
- || ( isInt && (a2[0].i<a1[0].i || a2[1].i>a1[1].i))
- ){
- return 0;
+ if( pRtree->eCoordType==RTREE_COORD_INT32 ){
+ for(ii=0; ii<pRtree->nDim2; ii+=2){
+ RtreeCoord *a1 = &p1->aCoord[ii];
+ RtreeCoord *a2 = &p2->aCoord[ii];
+ if( a2[0].i<a1[0].i || a2[1].i>a1[1].i ) return 0;
+ }
+ }else{
+ for(ii=0; ii<pRtree->nDim2; ii+=2){
+ RtreeCoord *a1 = &p1->aCoord[ii];
+ RtreeCoord *a2 = &p2->aCoord[ii];
+ if( a2[0].f<a1[0].f || a2[1].f>a1[1].f ) return 0;
}
}
return 1;
}
-/*
-** Return the amount cell p would grow by if it were unioned with pCell.
-*/
-static RtreeDValue cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){
- RtreeDValue area;
- RtreeCell cell;
- memcpy(&cell, p, sizeof(RtreeCell));
- area = cellArea(pRtree, &cell);
- cellUnion(pRtree, &cell, pCell);
- return (cellArea(pRtree, &cell)-area);
-}
-
static RtreeDValue cellOverlap(
Rtree *pRtree,
RtreeCell *p,
@@ -207317,38 +212558,52 @@ static int ChooseLeaf(
for(ii=0; rc==SQLITE_OK && ii<(pRtree->iDepth-iHeight); ii++){
int iCell;
sqlite3_int64 iBest = 0;
-
+ int bFound = 0;
RtreeDValue fMinGrowth = RTREE_ZERO;
RtreeDValue fMinArea = RTREE_ZERO;
-
int nCell = NCELL(pNode);
- RtreeCell cell;
RtreeNode *pChild = 0;
- RtreeCell *aCell = 0;
-
- /* Select the child node which will be enlarged the least if pCell
- ** is inserted into it. Resolve ties by choosing the entry with
- ** the smallest area.
+ /* First check to see if there is are any cells in pNode that completely
+ ** contains pCell. If two or more cells in pNode completely contain pCell
+ ** then pick the smallest.
*/
for(iCell=0; iCell<nCell; iCell++){
- int bBest = 0;
- RtreeDValue growth;
- RtreeDValue area;
+ RtreeCell cell;
nodeGetCell(pRtree, pNode, iCell, &cell);
- growth = cellGrowth(pRtree, &cell, pCell);
- area = cellArea(pRtree, &cell);
- if( iCell==0||growth<fMinGrowth||(growth==fMinGrowth && area<fMinArea) ){
- bBest = 1;
+ if( cellContains(pRtree, &cell, pCell) ){
+ RtreeDValue area = cellArea(pRtree, &cell);
+ if( bFound==0 || area<fMinArea ){
+ iBest = cell.iRowid;
+ fMinArea = area;
+ bFound = 1;
+ }
}
- if( bBest ){
- fMinGrowth = growth;
- fMinArea = area;
- iBest = cell.iRowid;
+ }
+ if( !bFound ){
+ /* No cells of pNode will completely contain pCell. So pick the
+ ** cell of pNode that grows by the least amount when pCell is added.
+ ** Break ties by selecting the smaller cell.
+ */
+ for(iCell=0; iCell<nCell; iCell++){
+ RtreeCell cell;
+ RtreeDValue growth;
+ RtreeDValue area;
+ nodeGetCell(pRtree, pNode, iCell, &cell);
+ area = cellArea(pRtree, &cell);
+ cellUnion(pRtree, &cell, pCell);
+ growth = cellArea(pRtree, &cell)-area;
+ if( iCell==0
+ || growth<fMinGrowth
+ || (growth==fMinGrowth && area<fMinArea)
+ ){
+ fMinGrowth = growth;
+ fMinArea = area;
+ iBest = cell.iRowid;
+ }
}
}
- sqlite3_free(aCell);
rc = nodeAcquire(pRtree, iBest, pNode, &pChild);
nodeRelease(pRtree, pNode);
pNode = pChild;
@@ -207421,77 +212676,6 @@ static int parentWrite(Rtree *pRtree, sqlite3_int64 iNode, sqlite3_int64 iPar){
static int rtreeInsertCell(Rtree *, RtreeNode *, RtreeCell *, int);
-/*
-** Arguments aIdx, aDistance and aSpare all point to arrays of size
-** nIdx. The aIdx array contains the set of integers from 0 to
-** (nIdx-1) in no particular order. This function sorts the values
-** in aIdx according to the indexed values in aDistance. For
-** example, assuming the inputs:
-**
-** aIdx = { 0, 1, 2, 3 }
-** aDistance = { 5.0, 2.0, 7.0, 6.0 }
-**
-** this function sets the aIdx array to contain:
-**
-** aIdx = { 0, 1, 2, 3 }
-**
-** The aSpare array is used as temporary working space by the
-** sorting algorithm.
-*/
-static void SortByDistance(
- int *aIdx,
- int nIdx,
- RtreeDValue *aDistance,
- int *aSpare
-){
- if( nIdx>1 ){
- int iLeft = 0;
- int iRight = 0;
-
- int nLeft = nIdx/2;
- int nRight = nIdx-nLeft;
- int *aLeft = aIdx;
- int *aRight = &aIdx[nLeft];
-
- SortByDistance(aLeft, nLeft, aDistance, aSpare);
- SortByDistance(aRight, nRight, aDistance, aSpare);
-
- memcpy(aSpare, aLeft, sizeof(int)*nLeft);
- aLeft = aSpare;
-
- while( iLeft<nLeft || iRight<nRight ){
- if( iLeft==nLeft ){
- aIdx[iLeft+iRight] = aRight[iRight];
- iRight++;
- }else if( iRight==nRight ){
- aIdx[iLeft+iRight] = aLeft[iLeft];
- iLeft++;
- }else{
- RtreeDValue fLeft = aDistance[aLeft[iLeft]];
- RtreeDValue fRight = aDistance[aRight[iRight]];
- if( fLeft<fRight ){
- aIdx[iLeft+iRight] = aLeft[iLeft];
- iLeft++;
- }else{
- aIdx[iLeft+iRight] = aRight[iRight];
- iRight++;
- }
- }
- }
-
-#if 0
- /* Check that the sort worked */
- {
- int jj;
- for(jj=1; jj<nIdx; jj++){
- RtreeDValue left = aDistance[aIdx[jj-1]];
- RtreeDValue right = aDistance[aIdx[jj]];
- assert( left<=right );
- }
- }
-#endif
- }
-}
/*
** Arguments aIdx, aCell and aSpare all point to arrays of size
@@ -207976,107 +213160,6 @@ static int deleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell, int iHeight){
return rc;
}
-static int Reinsert(
- Rtree *pRtree,
- RtreeNode *pNode,
- RtreeCell *pCell,
- int iHeight
-){
- int *aOrder;
- int *aSpare;
- RtreeCell *aCell;
- RtreeDValue *aDistance;
- int nCell;
- RtreeDValue aCenterCoord[RTREE_MAX_DIMENSIONS];
- int iDim;
- int ii;
- int rc = SQLITE_OK;
- int n;
-
- memset(aCenterCoord, 0, sizeof(RtreeDValue)*RTREE_MAX_DIMENSIONS);
-
- nCell = NCELL(pNode)+1;
- n = (nCell+1)&(~1);
-
- /* Allocate the buffers used by this operation. The allocation is
- ** relinquished before this function returns.
- */
- aCell = (RtreeCell *)sqlite3_malloc64(n * (
- sizeof(RtreeCell) + /* aCell array */
- sizeof(int) + /* aOrder array */
- sizeof(int) + /* aSpare array */
- sizeof(RtreeDValue) /* aDistance array */
- ));
- if( !aCell ){
- return SQLITE_NOMEM;
- }
- aOrder = (int *)&aCell[n];
- aSpare = (int *)&aOrder[n];
- aDistance = (RtreeDValue *)&aSpare[n];
-
- for(ii=0; ii<nCell; ii++){
- if( ii==(nCell-1) ){
- memcpy(&aCell[ii], pCell, sizeof(RtreeCell));
- }else{
- nodeGetCell(pRtree, pNode, ii, &aCell[ii]);
- }
- aOrder[ii] = ii;
- for(iDim=0; iDim<pRtree->nDim; iDim++){
- aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2]);
- aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2+1]);
- }
- }
- for(iDim=0; iDim<pRtree->nDim; iDim++){
- aCenterCoord[iDim] = (aCenterCoord[iDim]/(nCell*(RtreeDValue)2));
- }
-
- for(ii=0; ii<nCell; ii++){
- aDistance[ii] = RTREE_ZERO;
- for(iDim=0; iDim<pRtree->nDim; iDim++){
- RtreeDValue coord = (DCOORD(aCell[ii].aCoord[iDim*2+1]) -
- DCOORD(aCell[ii].aCoord[iDim*2]));
- aDistance[ii] += (coord-aCenterCoord[iDim])*(coord-aCenterCoord[iDim]);
- }
- }
-
- SortByDistance(aOrder, nCell, aDistance, aSpare);
- nodeZero(pRtree, pNode);
-
- for(ii=0; rc==SQLITE_OK && ii<(nCell-(RTREE_MINCELLS(pRtree)+1)); ii++){
- RtreeCell *p = &aCell[aOrder[ii]];
- nodeInsertCell(pRtree, pNode, p);
- if( p->iRowid==pCell->iRowid ){
- if( iHeight==0 ){
- rc = rowidWrite(pRtree, p->iRowid, pNode->iNode);
- }else{
- rc = parentWrite(pRtree, p->iRowid, pNode->iNode);
- }
- }
- }
- if( rc==SQLITE_OK ){
- rc = fixBoundingBox(pRtree, pNode);
- }
- for(; rc==SQLITE_OK && ii<nCell; ii++){
- /* Find a node to store this cell in. pNode->iNode currently contains
- ** the height of the sub-tree headed by the cell.
- */
- RtreeNode *pInsert;
- RtreeCell *p = &aCell[aOrder[ii]];
- rc = ChooseLeaf(pRtree, p, iHeight, &pInsert);
- if( rc==SQLITE_OK ){
- int rc2;
- rc = rtreeInsertCell(pRtree, pInsert, p, iHeight);
- rc2 = nodeRelease(pRtree, pInsert);
- if( rc==SQLITE_OK ){
- rc = rc2;
- }
- }
- }
-
- sqlite3_free(aCell);
- return rc;
-}
-
/*
** Insert cell pCell into node pNode. Node pNode is the head of a
** subtree iHeight high (leaf nodes have iHeight==0).
@@ -208097,12 +213180,7 @@ static int rtreeInsertCell(
}
}
if( nodeInsertCell(pRtree, pNode, pCell) ){
- if( iHeight<=pRtree->iReinsertHeight || pNode->iNode==1){
- rc = SplitNode(pRtree, pNode, pCell, iHeight);
- }else{
- pRtree->iReinsertHeight = iHeight;
- rc = Reinsert(pRtree, pNode, pCell, iHeight);
- }
+ rc = SplitNode(pRtree, pNode, pCell, iHeight);
}else{
rc = AdjustTree(pRtree, pNode, pCell);
if( ALWAYS(rc==SQLITE_OK) ){
@@ -208445,7 +213523,6 @@ static int rtreeUpdate(
}
if( rc==SQLITE_OK ){
int rc2;
- pRtree->iReinsertHeight = -1;
rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0);
rc2 = nodeRelease(pRtree, pLeaf);
if( rc==SQLITE_OK ){
@@ -208475,7 +213552,7 @@ constraint:
static int rtreeBeginTransaction(sqlite3_vtab *pVtab){
Rtree *pRtree = (Rtree *)pVtab;
assert( pRtree->inWrTrans==0 );
- pRtree->inWrTrans++;
+ pRtree->inWrTrans = 1;
return SQLITE_OK;
}
@@ -208489,6 +213566,9 @@ static int rtreeEndTransaction(sqlite3_vtab *pVtab){
nodeBlobReset(pRtree);
return SQLITE_OK;
}
+static int rtreeRollback(sqlite3_vtab *pVtab){
+ return rtreeEndTransaction(pVtab);
+}
/*
** The xRename method for rtree module virtual tables.
@@ -208586,8 +213666,11 @@ static int rtreeShadowName(const char *zName){
return 0;
}
+/* Forward declaration */
+static int rtreeIntegrity(sqlite3_vtab*, const char*, const char*, int, char**);
+
static sqlite3_module rtreeModule = {
- 3, /* iVersion */
+ 4, /* iVersion */
rtreeCreate, /* xCreate - create a table */
rtreeConnect, /* xConnect - connect to an existing table */
rtreeBestIndex, /* xBestIndex - Determine search strategy */
@@ -208604,13 +213687,14 @@ static sqlite3_module rtreeModule = {
rtreeBeginTransaction, /* xBegin - begin transaction */
rtreeEndTransaction, /* xSync - sync transaction */
rtreeEndTransaction, /* xCommit - commit transaction */
- rtreeEndTransaction, /* xRollback - rollback transaction */
+ rtreeRollback, /* xRollback - rollback transaction */
0, /* xFindFunction - function overloading */
rtreeRename, /* xRename - rename the table */
rtreeSavepoint, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- rtreeShadowName /* xShadowName */
+ rtreeShadowName, /* xShadowName */
+ rtreeIntegrity /* xIntegrity */
};
static int rtreeSqlInit(
@@ -208703,7 +213787,7 @@ static int rtreeSqlInit(
}
sqlite3_free(zSql);
}
- if( pRtree->nAux ){
+ if( pRtree->nAux && rc!=SQLITE_NOMEM ){
pRtree->zReadAuxSql = sqlite3_mprintf(
"SELECT * FROM \"%w\".\"%w_rowid\" WHERE rowid=?1",
zDb, zPrefix);
@@ -208866,22 +213950,27 @@ static int rtreeInit(
}
sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
+ sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
+
/* Allocate the sqlite3_vtab structure */
nDb = (int)strlen(argv[1]);
nName = (int)strlen(argv[2]);
- pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName+2);
+ pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName*2+8);
if( !pRtree ){
return SQLITE_NOMEM;
}
- memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2);
+ memset(pRtree, 0, sizeof(Rtree)+nDb+nName*2+8);
pRtree->nBusy = 1;
pRtree->base.pModule = &rtreeModule;
pRtree->zDb = (char *)&pRtree[1];
pRtree->zName = &pRtree->zDb[nDb+1];
+ pRtree->zNodeName = &pRtree->zName[nName+1];
pRtree->eCoordType = (u8)eCoordType;
memcpy(pRtree->zDb, argv[1], nDb);
memcpy(pRtree->zName, argv[2], nName);
+ memcpy(pRtree->zNodeName, argv[2], nName);
+ memcpy(&pRtree->zNodeName[nName], "_node", 6);
/* Create/Connect to the underlying relational database schema. If
@@ -209378,7 +214467,6 @@ static int rtreeCheckTable(
){
RtreeCheck check; /* Common context for various routines */
sqlite3_stmt *pStmt = 0; /* Used to find column count of rtree table */
- int bEnd = 0; /* True if transaction should be closed */
int nAux = 0; /* Number of extra columns. */
/* Initialize the context object */
@@ -209387,24 +214475,14 @@ static int rtreeCheckTable(
check.zDb = zDb;
check.zTab = zTab;
- /* If there is not already an open transaction, open one now. This is
- ** to ensure that the queries run as part of this integrity-check operate
- ** on a consistent snapshot. */
- if( sqlite3_get_autocommit(db) ){
- check.rc = sqlite3_exec(db, "BEGIN", 0, 0, 0);
- bEnd = 1;
- }
-
/* Find the number of auxiliary columns */
- if( check.rc==SQLITE_OK ){
- pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab);
- if( pStmt ){
- nAux = sqlite3_column_count(pStmt) - 2;
- sqlite3_finalize(pStmt);
- }else
- if( check.rc!=SQLITE_NOMEM ){
- check.rc = SQLITE_OK;
- }
+ pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab);
+ if( pStmt ){
+ nAux = sqlite3_column_count(pStmt) - 2;
+ sqlite3_finalize(pStmt);
+ }else
+ if( check.rc!=SQLITE_NOMEM ){
+ check.rc = SQLITE_OK;
}
/* Find number of dimensions in the rtree table. */
@@ -209435,16 +214513,36 @@ static int rtreeCheckTable(
sqlite3_finalize(check.aCheckMapping[0]);
sqlite3_finalize(check.aCheckMapping[1]);
- /* If one was opened, close the transaction */
- if( bEnd ){
- int rc = sqlite3_exec(db, "END", 0, 0, 0);
- if( check.rc==SQLITE_OK ) check.rc = rc;
- }
*pzReport = check.zReport;
return check.rc;
}
/*
+** Implementation of the xIntegrity method for Rtree.
+*/
+static int rtreeIntegrity(
+ sqlite3_vtab *pVtab, /* The virtual table to check */
+ const char *zSchema, /* Schema in which the virtual table lives */
+ const char *zName, /* Name of the virtual table */
+ int isQuick, /* True for a quick_check */
+ char **pzErr /* Write results here */
+){
+ Rtree *pRtree = (Rtree*)pVtab;
+ int rc;
+ assert( pzErr!=0 && *pzErr==0 );
+ UNUSED_PARAMETER(zSchema);
+ UNUSED_PARAMETER(zName);
+ UNUSED_PARAMETER(isQuick);
+ rc = rtreeCheckTable(pRtree->db, pRtree->zDb, pRtree->zName, pzErr);
+ if( rc==SQLITE_OK && *pzErr ){
+ *pzErr = sqlite3_mprintf("In RTree %s.%s:\n%z",
+ pRtree->zDb, pRtree->zName, *pzErr);
+ if( (*pzErr)==0 ) rc = SQLITE_NOMEM;
+ }
+ return rc;
+}
+
+/*
** Usage:
**
** rtreecheck(<rtree-table>);
@@ -210765,24 +215863,28 @@ static int geopolyInit(
(void)pAux;
sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
+ sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
/* Allocate the sqlite3_vtab structure */
nDb = strlen(argv[1]);
nName = strlen(argv[2]);
- pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName+2);
+ pRtree = (Rtree *)sqlite3_malloc64(sizeof(Rtree)+nDb+nName*2+8);
if( !pRtree ){
return SQLITE_NOMEM;
}
- memset(pRtree, 0, sizeof(Rtree)+nDb+nName+2);
+ memset(pRtree, 0, sizeof(Rtree)+nDb+nName*2+8);
pRtree->nBusy = 1;
pRtree->base.pModule = &rtreeModule;
pRtree->zDb = (char *)&pRtree[1];
pRtree->zName = &pRtree->zDb[nDb+1];
+ pRtree->zNodeName = &pRtree->zName[nName+1];
pRtree->eCoordType = RTREE_COORD_REAL32;
pRtree->nDim = 2;
pRtree->nDim2 = 4;
memcpy(pRtree->zDb, argv[1], nDb);
memcpy(pRtree->zName, argv[2], nName);
+ memcpy(pRtree->zNodeName, argv[2], nName);
+ memcpy(&pRtree->zNodeName[nName], "_node", 6);
/* Create/Connect to the underlying relational database schema. If
@@ -211196,7 +216298,6 @@ static int geopolyUpdate(
}
if( rc==SQLITE_OK ){
int rc2;
- pRtree->iReinsertHeight = -1;
rc = rtreeInsertCell(pRtree, pLeaf, &cell, 0);
rc2 = nodeRelease(pRtree, pLeaf);
if( rc==SQLITE_OK ){
@@ -211293,7 +216394,8 @@ static sqlite3_module geopolyModule = {
rtreeSavepoint, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- rtreeShadowName /* xShadowName */
+ rtreeShadowName, /* xShadowName */
+ rtreeIntegrity /* xIntegrity */
};
static int sqlite3_geopoly_init(sqlite3 *db){
@@ -212004,7 +217106,7 @@ static void icuLoadCollation(
UCollator *pUCollator; /* ICU library collation object */
int rc; /* Return code from sqlite3_create_collation_x() */
- assert(nArg==2);
+ assert(nArg==2 || nArg==3);
(void)nArg; /* Unused parameter */
zLocale = (const char *)sqlite3_value_text(apArg[0]);
zName = (const char *)sqlite3_value_text(apArg[1]);
@@ -212019,7 +217121,39 @@ static void icuLoadCollation(
return;
}
assert(p);
-
+ if(nArg==3){
+ const char *zOption = (const char*)sqlite3_value_text(apArg[2]);
+ static const struct {
+ const char *zName;
+ UColAttributeValue val;
+ } aStrength[] = {
+ { "PRIMARY", UCOL_PRIMARY },
+ { "SECONDARY", UCOL_SECONDARY },
+ { "TERTIARY", UCOL_TERTIARY },
+ { "DEFAULT", UCOL_DEFAULT_STRENGTH },
+ { "QUARTERNARY", UCOL_QUATERNARY },
+ { "IDENTICAL", UCOL_IDENTICAL },
+ };
+ unsigned int i;
+ for(i=0; i<sizeof(aStrength)/sizeof(aStrength[0]); i++){
+ if( sqlite3_stricmp(zOption,aStrength[i].zName)==0 ){
+ ucol_setStrength(pUCollator, aStrength[i].val);
+ break;
+ }
+ }
+ if( i>=sizeof(aStrength)/sizeof(aStrength[0]) ){
+ sqlite3_str *pStr = sqlite3_str_new(sqlite3_context_db_handle(p));
+ sqlite3_str_appendf(pStr,
+ "unknown collation strength \"%s\" - should be one of:",
+ zOption);
+ for(i=0; i<sizeof(aStrength)/sizeof(aStrength[0]); i++){
+ sqlite3_str_appendf(pStr, " %s", aStrength[i].zName);
+ }
+ sqlite3_result_error(p, sqlite3_str_value(pStr), -1);
+ sqlite3_free(sqlite3_str_finish(pStr));
+ return;
+ }
+ }
rc = sqlite3_create_collation_v2(db, zName, SQLITE_UTF16, (void *)pUCollator,
icuCollationColl, icuCollationDel
);
@@ -212042,6 +217176,7 @@ SQLITE_PRIVATE int sqlite3IcuInit(sqlite3 *db){
void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
} scalars[] = {
{"icu_load_collation",2,SQLITE_UTF8|SQLITE_DIRECTONLY,1, icuLoadCollation},
+ {"icu_load_collation",3,SQLITE_UTF8|SQLITE_DIRECTONLY,1, icuLoadCollation},
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_ICU)
{"regexp", 2, SQLITE_ANY|SQLITEICU_EXTRAFLAGS, 0, icuRegexpFunc},
{"lower", 1, SQLITE_UTF16|SQLITEICU_EXTRAFLAGS, 0, icuCaseFunc16},
@@ -213192,6 +218327,7 @@ typedef unsigned int u32;
typedef unsigned short u16;
typedef unsigned char u8;
typedef sqlite3_int64 i64;
+typedef sqlite3_uint64 u64;
#endif
/*
@@ -213878,6 +219014,7 @@ static int rbuObjIterNext(sqlite3rbu *p, RbuObjIter *pIter){
if( rc!=SQLITE_ROW ){
rc = resetAndCollectError(pIter->pTblIter, &p->zErrmsg);
pIter->zTbl = 0;
+ pIter->zDataTbl = 0;
}else{
pIter->zTbl = (const char*)sqlite3_column_text(pIter->pTblIter, 0);
pIter->zDataTbl = (const char*)sqlite3_column_text(pIter->pTblIter,1);
@@ -215972,7 +221109,7 @@ static i64 rbuShmChecksum(sqlite3rbu *p){
u32 volatile *ptr;
p->rc = pDb->pMethods->xShmMap(pDb, 0, 32*1024, 0, (void volatile**)&ptr);
if( p->rc==SQLITE_OK ){
- iRet = ((i64)ptr[10] << 32) + ptr[11];
+ iRet = (i64)(((u64)ptr[10] << 32) + ptr[11]);
}
}
return iRet;
@@ -219307,7 +224444,8 @@ SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
return sqlite3_create_module(db, "dbstat", &dbstat_module, 0);
}
@@ -219744,7 +224882,8 @@ SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0);
}
@@ -219875,6 +225014,18 @@ struct sqlite3_changeset_iter {
** The data associated with each hash-table entry is a structure containing
** a subset of the initial values that the modified row contained at the
** start of the session. Or no initial values if the row was inserted.
+**
+** pDfltStmt:
+** This is only used by the sqlite3changegroup_xxx() APIs, not by
+** regular sqlite3_session objects. It is a SELECT statement that
+** selects the default value for each table column. For example,
+** if the table is
+**
+** CREATE TABLE xx(a DEFAULT 1, b, c DEFAULT 'abc')
+**
+** then this variable is the compiled version of:
+**
+** SELECT 1, NULL, 'abc'
*/
struct SessionTable {
SessionTable *pNext;
@@ -219883,10 +225034,12 @@ struct SessionTable {
int bStat1; /* True if this is sqlite_stat1 */
int bRowid; /* True if this table uses rowid for PK */
const char **azCol; /* Column names */
+ const char **azDflt; /* Default value expressions */
u8 *abPK; /* Array of primary key flags */
int nEntry; /* Total number of entries in hash table */
int nChange; /* Size of apChange[] array */
SessionChange **apChange; /* Hash table buckets */
+ sqlite3_stmt *pDfltStmt;
};
/*
@@ -220055,6 +225208,7 @@ struct SessionTable {
struct SessionChange {
u8 op; /* One of UPDATE, DELETE, INSERT */
u8 bIndirect; /* True if this change is "indirect" */
+ u16 nRecordField; /* Number of fields in aRecord[] */
int nMaxSize; /* Max size of eventual changeset record */
int nRecord; /* Number of bytes in buffer aRecord[] */
u8 *aRecord; /* Buffer containing old.* record */
@@ -220080,7 +225234,7 @@ static int sessionVarintLen(int iVal){
** Read a varint value from aBuf[] into *piVal. Return the number of
** bytes read.
*/
-static int sessionVarintGet(u8 *aBuf, int *piVal){
+static int sessionVarintGet(const u8 *aBuf, int *piVal){
return getVarint32(aBuf, *piVal);
}
@@ -220343,9 +225497,11 @@ static int sessionPreupdateHash(
** Return the number of bytes of space occupied by the value (including
** the type byte).
*/
-static int sessionSerialLen(u8 *a){
- int e = *a;
+static int sessionSerialLen(const u8 *a){
+ int e;
int n;
+ assert( a!=0 );
+ e = *a;
if( e==0 || e==0xFF ) return 1;
if( e==SQLITE_NULL ) return 1;
if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9;
@@ -220750,13 +225906,14 @@ static int sessionGrowHash(
**
** For example, if the table is declared as:
**
-** CREATE TABLE tbl1(w, x, y, z, PRIMARY KEY(w, z));
+** CREATE TABLE tbl1(w, x DEFAULT 'abc', y, z, PRIMARY KEY(w, z));
**
-** Then the four output variables are populated as follows:
+** Then the five output variables are populated as follows:
**
** *pnCol = 4
** *pzTab = "tbl1"
** *pazCol = {"w", "x", "y", "z"}
+** *pazDflt = {NULL, 'abc', NULL, NULL}
** *pabPK = {1, 0, 0, 1}
**
** All returned buffers are part of the same single allocation, which must
@@ -220770,6 +225927,7 @@ static int sessionTableInfo(
int *pnCol, /* OUT: number of columns */
const char **pzTab, /* OUT: Copy of zThis */
const char ***pazCol, /* OUT: Array of column names for table */
+ const char ***pazDflt, /* OUT: Array of default value expressions */
u8 **pabPK, /* OUT: Array of booleans - true for PK col */
int *pbRowid /* OUT: True if only PK is a rowid */
){
@@ -220782,11 +225940,18 @@ static int sessionTableInfo(
int i;
u8 *pAlloc = 0;
char **azCol = 0;
+ char **azDflt = 0;
u8 *abPK = 0;
int bRowid = 0; /* Set to true to use rowid as PK */
assert( pazCol && pabPK );
+ *pazCol = 0;
+ *pabPK = 0;
+ *pnCol = 0;
+ if( pzTab ) *pzTab = 0;
+ if( pazDflt ) *pazDflt = 0;
+
nThis = sqlite3Strlen30(zThis);
if( nThis==12 && 0==sqlite3_stricmp("sqlite_stat1", zThis) ){
rc = sqlite3_table_column_metadata(db, zDb, zThis, 0, 0, 0, 0, 0, 0);
@@ -220800,39 +225965,28 @@ static int sessionTableInfo(
}else if( rc==SQLITE_ERROR ){
zPragma = sqlite3_mprintf("");
}else{
- *pazCol = 0;
- *pabPK = 0;
- *pnCol = 0;
- if( pzTab ) *pzTab = 0;
return rc;
}
}else{
zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis);
}
if( !zPragma ){
- *pazCol = 0;
- *pabPK = 0;
- *pnCol = 0;
- if( pzTab ) *pzTab = 0;
return SQLITE_NOMEM;
}
rc = sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0);
sqlite3_free(zPragma);
if( rc!=SQLITE_OK ){
- *pazCol = 0;
- *pabPK = 0;
- *pnCol = 0;
- if( pzTab ) *pzTab = 0;
return rc;
}
nByte = nThis + 1;
bRowid = (pbRowid!=0);
while( SQLITE_ROW==sqlite3_step(pStmt) ){
- nByte += sqlite3_column_bytes(pStmt, 1);
+ nByte += sqlite3_column_bytes(pStmt, 1); /* name */
+ nByte += sqlite3_column_bytes(pStmt, 4); /* dflt_value */
nDbCol++;
- if( sqlite3_column_int(pStmt, 5) ) bRowid = 0;
+ if( sqlite3_column_int(pStmt, 5) ) bRowid = 0; /* pk */
}
if( nDbCol==0 ) bRowid = 0;
nDbCol += bRowid;
@@ -220840,15 +225994,18 @@ static int sessionTableInfo(
rc = sqlite3_reset(pStmt);
if( rc==SQLITE_OK ){
- nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
+ nByte += nDbCol * (sizeof(const char *)*2 + sizeof(u8) + 1 + 1);
pAlloc = sessionMalloc64(pSession, nByte);
if( pAlloc==0 ){
rc = SQLITE_NOMEM;
+ }else{
+ memset(pAlloc, 0, nByte);
}
}
if( rc==SQLITE_OK ){
azCol = (char **)pAlloc;
- pAlloc = (u8 *)&azCol[nDbCol];
+ azDflt = (char**)&azCol[nDbCol];
+ pAlloc = (u8 *)&azDflt[nDbCol];
abPK = (u8 *)pAlloc;
pAlloc = &abPK[nDbCol];
if( pzTab ){
@@ -220868,11 +226025,21 @@ static int sessionTableInfo(
}
while( SQLITE_ROW==sqlite3_step(pStmt) ){
int nName = sqlite3_column_bytes(pStmt, 1);
+ int nDflt = sqlite3_column_bytes(pStmt, 4);
const unsigned char *zName = sqlite3_column_text(pStmt, 1);
+ const unsigned char *zDflt = sqlite3_column_text(pStmt, 4);
+
if( zName==0 ) break;
memcpy(pAlloc, zName, nName+1);
azCol[i] = (char *)pAlloc;
pAlloc += nName+1;
+ if( zDflt ){
+ memcpy(pAlloc, zDflt, nDflt+1);
+ azDflt[i] = (char *)pAlloc;
+ pAlloc += nDflt+1;
+ }else{
+ azDflt[i] = 0;
+ }
abPK[i] = sqlite3_column_int(pStmt, 5);
i++;
}
@@ -220883,14 +226050,11 @@ static int sessionTableInfo(
** free any allocation made. An error code will be returned in this case.
*/
if( rc==SQLITE_OK ){
- *pazCol = (const char **)azCol;
+ *pazCol = (const char**)azCol;
+ if( pazDflt ) *pazDflt = (const char**)azDflt;
*pabPK = abPK;
*pnCol = nDbCol;
}else{
- *pazCol = 0;
- *pabPK = 0;
- *pnCol = 0;
- if( pzTab ) *pzTab = 0;
sessionFree(pSession, azCol);
}
if( pbRowid ) *pbRowid = bRowid;
@@ -220899,10 +226063,9 @@ static int sessionTableInfo(
}
/*
-** This function is only called from within a pre-update handler for a
-** write to table pTab, part of session pSession. If this is the first
-** write to this table, initalize the SessionTable.nCol, azCol[] and
-** abPK[] arrays accordingly.
+** This function is called to initialize the SessionTable.nCol, azCol[]
+** abPK[] and azDflt[] members of SessionTable object pTab. If these
+** fields are already initilialized, this function is a no-op.
**
** If an error occurs, an error code is stored in sqlite3_session.rc and
** non-zero returned. Or, if no error occurs but the table has no primary
@@ -220910,15 +226073,22 @@ static int sessionTableInfo(
** indicate that updates on this table should be ignored. SessionTable.abPK
** is set to NULL in this case.
*/
-static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
+static int sessionInitTable(
+ sqlite3_session *pSession, /* Optional session handle */
+ SessionTable *pTab, /* Table object to initialize */
+ sqlite3 *db, /* Database handle to read schema from */
+ const char *zDb /* Name of db - "main", "temp" etc. */
+){
+ int rc = SQLITE_OK;
+
if( pTab->nCol==0 ){
u8 *abPK;
assert( pTab->azCol==0 || pTab->abPK==0 );
- pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb,
- pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK,
- (pSession->bImplicitPK ? &pTab->bRowid : 0)
+ rc = sessionTableInfo(pSession, db, zDb,
+ pTab->zName, &pTab->nCol, 0, &pTab->azCol, &pTab->azDflt, &abPK,
+ ((pSession==0 || pSession->bImplicitPK) ? &pTab->bRowid : 0)
);
- if( pSession->rc==SQLITE_OK ){
+ if( rc==SQLITE_OK ){
int i;
for(i=0; i<pTab->nCol; i++){
if( abPK[i] ){
@@ -220930,14 +226100,321 @@ static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
pTab->bStat1 = 1;
}
- if( pSession->bEnableSize ){
+ if( pSession && pSession->bEnableSize ){
pSession->nMaxChangesetSize += (
1 + sessionVarintLen(pTab->nCol) + pTab->nCol + strlen(pTab->zName)+1
);
}
}
}
- return (pSession->rc || pTab->abPK==0);
+
+ if( pSession ){
+ pSession->rc = rc;
+ return (rc || pTab->abPK==0);
+ }
+ return rc;
+}
+
+/*
+** Re-initialize table object pTab.
+*/
+static int sessionReinitTable(sqlite3_session *pSession, SessionTable *pTab){
+ int nCol = 0;
+ const char **azCol = 0;
+ const char **azDflt = 0;
+ u8 *abPK = 0;
+ int bRowid = 0;
+
+ assert( pSession->rc==SQLITE_OK );
+
+ pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb,
+ pTab->zName, &nCol, 0, &azCol, &azDflt, &abPK,
+ (pSession->bImplicitPK ? &bRowid : 0)
+ );
+ if( pSession->rc==SQLITE_OK ){
+ if( pTab->nCol>nCol || pTab->bRowid!=bRowid ){
+ pSession->rc = SQLITE_SCHEMA;
+ }else{
+ int ii;
+ int nOldCol = pTab->nCol;
+ for(ii=0; ii<nCol; ii++){
+ if( ii<pTab->nCol ){
+ if( pTab->abPK[ii]!=abPK[ii] ){
+ pSession->rc = SQLITE_SCHEMA;
+ }
+ }else if( abPK[ii] ){
+ pSession->rc = SQLITE_SCHEMA;
+ }
+ }
+
+ if( pSession->rc==SQLITE_OK ){
+ const char **a = pTab->azCol;
+ pTab->azCol = azCol;
+ pTab->nCol = nCol;
+ pTab->azDflt = azDflt;
+ pTab->abPK = abPK;
+ azCol = a;
+ }
+ if( pSession->bEnableSize ){
+ pSession->nMaxChangesetSize += (nCol - nOldCol);
+ pSession->nMaxChangesetSize += sessionVarintLen(nCol);
+ pSession->nMaxChangesetSize -= sessionVarintLen(nOldCol);
+ }
+ }
+ }
+
+ sqlite3_free((char*)azCol);
+ return pSession->rc;
+}
+
+/*
+** Session-change object (*pp) contains an old.* record with fewer than
+** nCol fields. This function updates it with the default values for
+** the missing fields.
+*/
+static void sessionUpdateOneChange(
+ sqlite3_session *pSession, /* For memory accounting */
+ int *pRc, /* IN/OUT: Error code */
+ SessionChange **pp, /* IN/OUT: Change object to update */
+ int nCol, /* Number of columns now in table */
+ sqlite3_stmt *pDflt /* SELECT <default-values...> */
+){
+ SessionChange *pOld = *pp;
+
+ while( pOld->nRecordField<nCol ){
+ SessionChange *pNew = 0;
+ int nByte = 0;
+ int nIncr = 0;
+ int iField = pOld->nRecordField;
+ int eType = sqlite3_column_type(pDflt, iField);
+ switch( eType ){
+ case SQLITE_NULL:
+ nIncr = 1;
+ break;
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT:
+ nIncr = 9;
+ break;
+ default: {
+ int n = sqlite3_column_bytes(pDflt, iField);
+ nIncr = 1 + sessionVarintLen(n) + n;
+ assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
+ break;
+ }
+ }
+
+ nByte = nIncr + (sizeof(SessionChange) + pOld->nRecord);
+ pNew = sessionMalloc64(pSession, nByte);
+ if( pNew==0 ){
+ *pRc = SQLITE_NOMEM;
+ return;
+ }else{
+ memcpy(pNew, pOld, sizeof(SessionChange));
+ pNew->aRecord = (u8*)&pNew[1];
+ memcpy(pNew->aRecord, pOld->aRecord, pOld->nRecord);
+ pNew->aRecord[pNew->nRecord++] = (u8)eType;
+ switch( eType ){
+ case SQLITE_INTEGER: {
+ i64 iVal = sqlite3_column_int64(pDflt, iField);
+ sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal);
+ pNew->nRecord += 8;
+ break;
+ }
+
+ case SQLITE_FLOAT: {
+ double rVal = sqlite3_column_double(pDflt, iField);
+ i64 iVal = 0;
+ memcpy(&iVal, &rVal, sizeof(rVal));
+ sessionPutI64(&pNew->aRecord[pNew->nRecord], iVal);
+ pNew->nRecord += 8;
+ break;
+ }
+
+ case SQLITE_TEXT: {
+ int n = sqlite3_column_bytes(pDflt, iField);
+ const char *z = (const char*)sqlite3_column_text(pDflt, iField);
+ pNew->nRecord += sessionVarintPut(&pNew->aRecord[pNew->nRecord], n);
+ memcpy(&pNew->aRecord[pNew->nRecord], z, n);
+ pNew->nRecord += n;
+ break;
+ }
+
+ case SQLITE_BLOB: {
+ int n = sqlite3_column_bytes(pDflt, iField);
+ const u8 *z = (const u8*)sqlite3_column_blob(pDflt, iField);
+ pNew->nRecord += sessionVarintPut(&pNew->aRecord[pNew->nRecord], n);
+ memcpy(&pNew->aRecord[pNew->nRecord], z, n);
+ pNew->nRecord += n;
+ break;
+ }
+
+ default:
+ assert( eType==SQLITE_NULL );
+ break;
+ }
+
+ sessionFree(pSession, pOld);
+ *pp = pOld = pNew;
+ pNew->nRecordField++;
+ pNew->nMaxSize += nIncr;
+ if( pSession ){
+ pSession->nMaxChangesetSize += nIncr;
+ }
+ }
+ }
+}
+
+/*
+** Ensure that there is room in the buffer to append nByte bytes of data.
+** If not, use sqlite3_realloc() to grow the buffer so that there is.
+**
+** If successful, return zero. Otherwise, if an OOM condition is encountered,
+** set *pRc to SQLITE_NOMEM and return non-zero.
+*/
+static int sessionBufferGrow(SessionBuffer *p, i64 nByte, int *pRc){
+#define SESSION_MAX_BUFFER_SZ (0x7FFFFF00 - 1)
+ i64 nReq = p->nBuf + nByte;
+ if( *pRc==SQLITE_OK && nReq>p->nAlloc ){
+ u8 *aNew;
+ i64 nNew = p->nAlloc ? p->nAlloc : 128;
+
+ do {
+ nNew = nNew*2;
+ }while( nNew<nReq );
+
+ /* The value of SESSION_MAX_BUFFER_SZ is copied from the implementation
+ ** of sqlite3_realloc64(). Allocations greater than this size in bytes
+ ** always fail. It is used here to ensure that this routine can always
+ ** allocate up to this limit - instead of up to the largest power of
+ ** two smaller than the limit. */
+ if( nNew>SESSION_MAX_BUFFER_SZ ){
+ nNew = SESSION_MAX_BUFFER_SZ;
+ if( nNew<nReq ){
+ *pRc = SQLITE_NOMEM;
+ return 1;
+ }
+ }
+
+ aNew = (u8 *)sqlite3_realloc64(p->aBuf, nNew);
+ if( 0==aNew ){
+ *pRc = SQLITE_NOMEM;
+ }else{
+ p->aBuf = aNew;
+ p->nAlloc = nNew;
+ }
+ }
+ return (*pRc!=SQLITE_OK);
+}
+
+
+/*
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwise, append a string to the buffer. All bytes in the string
+** up to (but not including) the nul-terminator are written to the buffer.
+**
+** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
+** returning.
+*/
+static void sessionAppendStr(
+ SessionBuffer *p,
+ const char *zStr,
+ int *pRc
+){
+ int nStr = sqlite3Strlen30(zStr);
+ if( 0==sessionBufferGrow(p, nStr+1, pRc) ){
+ memcpy(&p->aBuf[p->nBuf], zStr, nStr);
+ p->nBuf += nStr;
+ p->aBuf[p->nBuf] = 0x00;
+ }
+}
+
+/*
+** Format a string using printf() style formatting and then append it to the
+** buffer using sessionAppendString().
+*/
+static void sessionAppendPrintf(
+ SessionBuffer *p, /* Buffer to append to */
+ int *pRc,
+ const char *zFmt,
+ ...
+){
+ if( *pRc==SQLITE_OK ){
+ char *zApp = 0;
+ va_list ap;
+ va_start(ap, zFmt);
+ zApp = sqlite3_vmprintf(zFmt, ap);
+ if( zApp==0 ){
+ *pRc = SQLITE_NOMEM;
+ }else{
+ sessionAppendStr(p, zApp, pRc);
+ }
+ va_end(ap);
+ sqlite3_free(zApp);
+ }
+}
+
+/*
+** Prepare a statement against database handle db that SELECTs a single
+** row containing the default values for each column in table pTab. For
+** example, if pTab is declared as:
+**
+** CREATE TABLE pTab(a PRIMARY KEY, b DEFAULT 123, c DEFAULT 'abcd');
+**
+** Then this function prepares and returns the SQL statement:
+**
+** SELECT NULL, 123, 'abcd';
+*/
+static int sessionPrepareDfltStmt(
+ sqlite3 *db, /* Database handle */
+ SessionTable *pTab, /* Table to prepare statement for */
+ sqlite3_stmt **ppStmt /* OUT: Statement handle */
+){
+ SessionBuffer sql = {0,0,0};
+ int rc = SQLITE_OK;
+ const char *zSep = " ";
+ int ii = 0;
+
+ *ppStmt = 0;
+ sessionAppendPrintf(&sql, &rc, "SELECT");
+ for(ii=0; ii<pTab->nCol; ii++){
+ const char *zDflt = pTab->azDflt[ii] ? pTab->azDflt[ii] : "NULL";
+ sessionAppendPrintf(&sql, &rc, "%s%s", zSep, zDflt);
+ zSep = ", ";
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_prepare_v2(db, (const char*)sql.aBuf, -1, ppStmt, 0);
+ }
+ sqlite3_free(sql.aBuf);
+
+ return rc;
+}
+
+/*
+** Table pTab has one or more existing change-records with old.* records
+** with fewer than pTab->nCol columns. This function updates all such
+** change-records with the default values for the missing columns.
+*/
+static int sessionUpdateChanges(sqlite3_session *pSession, SessionTable *pTab){
+ sqlite3_stmt *pStmt = 0;
+ int rc = pSession->rc;
+
+ rc = sessionPrepareDfltStmt(pSession->db, pTab, &pStmt);
+ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ int ii = 0;
+ SessionChange **pp = 0;
+ for(ii=0; ii<pTab->nChange; ii++){
+ for(pp=&pTab->apChange[ii]; *pp; pp=&((*pp)->pNext)){
+ if( (*pp)->nRecordField!=pTab->nCol ){
+ sessionUpdateOneChange(pSession, &rc, pp, pTab->nCol, pStmt);
+ }
+ }
+ }
+ }
+
+ pSession->rc = rc;
+ rc = sqlite3_finalize(pStmt);
+ if( pSession->rc==SQLITE_OK ) pSession->rc = rc;
+ return pSession->rc;
}
/*
@@ -221100,16 +226577,22 @@ static void sessionPreupdateOneChange(
int iHash;
int bNull = 0;
int rc = SQLITE_OK;
+ int nExpect = 0;
SessionStat1Ctx stat1 = {{0,0,0,0,0},0};
if( pSession->rc ) return;
/* Load table details if required */
- if( sessionInitTable(pSession, pTab) ) return;
+ if( sessionInitTable(pSession, pTab, pSession->db, pSession->zDb) ) return;
/* Check the number of columns in this xPreUpdate call matches the
** number of columns in the table. */
- if( (pTab->nCol-pTab->bRowid)!=pSession->hook.xCount(pSession->hook.pCtx) ){
+ nExpect = pSession->hook.xCount(pSession->hook.pCtx);
+ if( (pTab->nCol-pTab->bRowid)<nExpect ){
+ if( sessionReinitTable(pSession, pTab) ) return;
+ if( sessionUpdateChanges(pSession, pTab) ) return;
+ }
+ if( (pTab->nCol-pTab->bRowid)!=nExpect ){
pSession->rc = SQLITE_SCHEMA;
return;
}
@@ -221186,7 +226669,7 @@ static void sessionPreupdateOneChange(
}
/* Allocate the change object */
- pC = (SessionChange *)sessionMalloc64(pSession, nByte);
+ pC = (SessionChange*)sessionMalloc64(pSession, nByte);
if( !pC ){
rc = SQLITE_NOMEM;
goto error_out;
@@ -221219,6 +226702,7 @@ static void sessionPreupdateOneChange(
if( pSession->bIndirect || pSession->hook.xDepth(pSession->hook.pCtx) ){
pC->bIndirect = 1;
}
+ pC->nRecordField = pTab->nCol;
pC->nRecord = nByte;
pC->op = op;
pC->pNext = pTab->apChange[iHash];
@@ -221598,7 +227082,7 @@ SQLITE_API int sqlite3session_diff(
/* Locate and if necessary initialize the target table object */
rc = sessionFindTable(pSession, zTbl, &pTo);
if( pTo==0 ) goto diff_out;
- if( sessionInitTable(pSession, pTo) ){
+ if( sessionInitTable(pSession, pTo, pSession->db, pSession->zDb) ){
rc = pSession->rc;
goto diff_out;
}
@@ -221611,7 +227095,7 @@ SQLITE_API int sqlite3session_diff(
int bRowid = 0;
u8 *abPK;
const char **azCol = 0;
- rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, &abPK,
+ rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, 0, &abPK,
pSession->bImplicitPK ? &bRowid : 0
);
if( rc==SQLITE_OK ){
@@ -221726,6 +227210,7 @@ static void sessionDeleteTable(sqlite3_session *pSession, SessionTable *pList){
sessionFree(pSession, p);
}
}
+ sqlite3_finalize(pTab->pDfltStmt);
sessionFree(pSession, (char*)pTab->azCol); /* cast works around VC++ bug */
sessionFree(pSession, pTab->apChange);
sessionFree(pSession, pTab);
@@ -221758,9 +227243,7 @@ SQLITE_API void sqlite3session_delete(sqlite3_session *pSession){
** associated hash-tables. */
sessionDeleteTable(pSession, pSession->pTable);
- /* Assert that all allocations have been freed and then free the
- ** session object itself. */
- assert( pSession->nMalloc==0 );
+ /* Free the session object. */
sqlite3_free(pSession);
}
@@ -221832,48 +227315,6 @@ SQLITE_API int sqlite3session_attach(
}
/*
-** Ensure that there is room in the buffer to append nByte bytes of data.
-** If not, use sqlite3_realloc() to grow the buffer so that there is.
-**
-** If successful, return zero. Otherwise, if an OOM condition is encountered,
-** set *pRc to SQLITE_NOMEM and return non-zero.
-*/
-static int sessionBufferGrow(SessionBuffer *p, i64 nByte, int *pRc){
-#define SESSION_MAX_BUFFER_SZ (0x7FFFFF00 - 1)
- i64 nReq = p->nBuf + nByte;
- if( *pRc==SQLITE_OK && nReq>p->nAlloc ){
- u8 *aNew;
- i64 nNew = p->nAlloc ? p->nAlloc : 128;
-
- do {
- nNew = nNew*2;
- }while( nNew<nReq );
-
- /* The value of SESSION_MAX_BUFFER_SZ is copied from the implementation
- ** of sqlite3_realloc64(). Allocations greater than this size in bytes
- ** always fail. It is used here to ensure that this routine can always
- ** allocate up to this limit - instead of up to the largest power of
- ** two smaller than the limit. */
- if( nNew>SESSION_MAX_BUFFER_SZ ){
- nNew = SESSION_MAX_BUFFER_SZ;
- if( nNew<nReq ){
- *pRc = SQLITE_NOMEM;
- return 1;
- }
- }
-
- aNew = (u8 *)sqlite3_realloc64(p->aBuf, nNew);
- if( 0==aNew ){
- *pRc = SQLITE_NOMEM;
- }else{
- p->aBuf = aNew;
- p->nAlloc = nNew;
- }
- }
- return (*pRc!=SQLITE_OK);
-}
-
-/*
** Append the value passed as the second argument to the buffer passed
** as the first.
**
@@ -221943,27 +227384,6 @@ static void sessionAppendBlob(
/*
** This function is a no-op if *pRc is other than SQLITE_OK when it is
-** called. Otherwise, append a string to the buffer. All bytes in the string
-** up to (but not including) the nul-terminator are written to the buffer.
-**
-** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
-** returning.
-*/
-static void sessionAppendStr(
- SessionBuffer *p,
- const char *zStr,
- int *pRc
-){
- int nStr = sqlite3Strlen30(zStr);
- if( 0==sessionBufferGrow(p, nStr+1, pRc) ){
- memcpy(&p->aBuf[p->nBuf], zStr, nStr);
- p->nBuf += nStr;
- p->aBuf[p->nBuf] = 0x00;
- }
-}
-
-/*
-** This function is a no-op if *pRc is other than SQLITE_OK when it is
** called. Otherwise, append the string representation of integer iVal
** to the buffer. No nul-terminator is written.
**
@@ -221980,27 +227400,6 @@ static void sessionAppendInteger(
sessionAppendStr(p, aBuf, pRc);
}
-static void sessionAppendPrintf(
- SessionBuffer *p, /* Buffer to append to */
- int *pRc,
- const char *zFmt,
- ...
-){
- if( *pRc==SQLITE_OK ){
- char *zApp = 0;
- va_list ap;
- va_start(ap, zFmt);
- zApp = sqlite3_vmprintf(zFmt, ap);
- if( zApp==0 ){
- *pRc = SQLITE_NOMEM;
- }else{
- sessionAppendStr(p, zApp, pRc);
- }
- va_end(ap);
- sqlite3_free(zApp);
- }
-}
-
/*
** This function is a no-op if *pRc is other than SQLITE_OK when it is
** called. Otherwise, append the string zStr enclosed in quotes (") and
@@ -222491,26 +227890,16 @@ static int sessionGenerateChangeset(
for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
if( pTab->nEntry ){
const char *zName = pTab->zName;
- int nCol = 0; /* Number of columns in table */
- u8 *abPK = 0; /* Primary key array */
- const char **azCol = 0; /* Table columns */
int i; /* Used to iterate through hash buckets */
sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */
int nRewind = buf.nBuf; /* Initial size of write buffer */
int nNoop; /* Size of buffer after writing tbl header */
- int bRowid = 0;
+ int nOldCol = pTab->nCol;
/* Check the table schema is still Ok. */
- rc = sessionTableInfo(
- 0, db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK,
- (pSession->bImplicitPK ? &bRowid : 0)
- );
- if( rc==SQLITE_OK && (
- pTab->nCol!=nCol
- || pTab->bRowid!=bRowid
- || memcmp(abPK, pTab->abPK, nCol)
- )){
- rc = SQLITE_SCHEMA;
+ rc = sessionReinitTable(pSession, pTab);
+ if( rc==SQLITE_OK && pTab->nCol!=nOldCol ){
+ rc = sessionUpdateChanges(pSession, pTab);
}
/* Write a table header */
@@ -222518,8 +227907,8 @@ static int sessionGenerateChangeset(
/* Build and compile a statement to execute: */
if( rc==SQLITE_OK ){
- rc = sessionSelectStmt(
- db, 0, pSession->zDb, zName, bRowid, nCol, azCol, abPK, &pSel
+ rc = sessionSelectStmt(db, 0, pSession->zDb,
+ zName, pTab->bRowid, pTab->nCol, pTab->azCol, pTab->abPK, &pSel
);
}
@@ -222528,22 +227917,22 @@ static int sessionGenerateChangeset(
SessionChange *p; /* Used to iterate through changes */
for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){
- rc = sessionSelectBind(pSel, nCol, abPK, p);
+ rc = sessionSelectBind(pSel, pTab->nCol, pTab->abPK, p);
if( rc!=SQLITE_OK ) continue;
if( sqlite3_step(pSel)==SQLITE_ROW ){
if( p->op==SQLITE_INSERT ){
int iCol;
sessionAppendByte(&buf, SQLITE_INSERT, &rc);
sessionAppendByte(&buf, p->bIndirect, &rc);
- for(iCol=0; iCol<nCol; iCol++){
+ for(iCol=0; iCol<pTab->nCol; iCol++){
sessionAppendCol(&buf, pSel, iCol, &rc);
}
}else{
- assert( abPK!=0 ); /* Because sessionSelectStmt() returned ok */
- rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, abPK);
+ assert( pTab->abPK!=0 );
+ rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, pTab->abPK);
}
}else if( p->op!=SQLITE_INSERT ){
- rc = sessionAppendDelete(&buf, bPatchset, p, nCol, abPK);
+ rc = sessionAppendDelete(&buf, bPatchset, p, pTab->nCol,pTab->abPK);
}
if( rc==SQLITE_OK ){
rc = sqlite3_reset(pSel);
@@ -222568,7 +227957,6 @@ static int sessionGenerateChangeset(
if( buf.nBuf==nNoop ){
buf.nBuf = nRewind;
}
- sqlite3_free((char*)azCol); /* cast works around VC++ bug */
}
}
@@ -223192,14 +228580,14 @@ static int sessionChangesetNextOne(
p->rc = sessionInputBuffer(&p->in, 2);
if( p->rc!=SQLITE_OK ) return p->rc;
+ sessionDiscardData(&p->in);
+ p->in.iCurrent = p->in.iNext;
+
/* If the iterator is already at the end of the changeset, return DONE. */
if( p->in.iNext>=p->in.nData ){
return SQLITE_DONE;
}
- sessionDiscardData(&p->in);
- p->in.iCurrent = p->in.iNext;
-
op = p->in.aData[p->in.iNext++];
while( op=='T' || op=='P' ){
if( pbNew ) *pbNew = 1;
@@ -224697,7 +230085,7 @@ static int sessionChangesetApply(
sqlite3changeset_pk(pIter, &abPK, 0);
rc = sessionTableInfo(0, db, "main", zNew,
- &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK, &sApply.bRowid
+ &sApply.nCol, &zTab, &sApply.azCol, 0, &sApply.abPK, &sApply.bRowid
);
if( rc!=SQLITE_OK ) break;
for(i=0; i<sApply.nCol; i++){
@@ -224829,11 +230217,24 @@ SQLITE_API int sqlite3changeset_apply_v2(
sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
int bInv = !!(flags & SQLITE_CHANGESETAPPLY_INVERT);
int rc = sessionChangesetStart(&pIter, 0, 0, nChangeset, pChangeset, bInv, 1);
+ u64 savedFlag = db->flags & SQLITE_FkNoAction;
+
+ if( flags & SQLITE_CHANGESETAPPLY_FKNOACTION ){
+ db->flags |= ((u64)SQLITE_FkNoAction);
+ db->aDb[0].pSchema->schema_cookie -= 32;
+ }
+
if( rc==SQLITE_OK ){
rc = sessionChangesetApply(
db, pIter, xFilter, xConflict, pCtx, ppRebase, pnRebase, flags
);
}
+
+ if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){
+ assert( db->flags & SQLITE_FkNoAction );
+ db->flags &= ~((u64)SQLITE_FkNoAction);
+ db->aDb[0].pSchema->schema_cookie -= 32;
+ }
return rc;
}
@@ -224921,6 +230322,10 @@ struct sqlite3_changegroup {
int rc; /* Error code */
int bPatch; /* True to accumulate patchsets */
SessionTable *pList; /* List of tables in current patch */
+ SessionBuffer rec;
+
+ sqlite3 *db; /* Configured by changegroup_schema() */
+ char *zDb; /* Configured by changegroup_schema() */
};
/*
@@ -224941,6 +230346,7 @@ static int sessionChangeMerge(
){
SessionChange *pNew = 0;
int rc = SQLITE_OK;
+ assert( aRec!=0 );
if( !pExist ){
pNew = (SessionChange *)sqlite3_malloc64(sizeof(SessionChange) + nRec);
@@ -225107,84 +230513,236 @@ static int sessionChangeMerge(
}
/*
-** Add all changes in the changeset traversed by the iterator passed as
-** the first argument to the changegroup hash tables.
+** Check if a changeset entry with nCol columns and the PK array passed
+** as the final argument to this function is compatible with SessionTable
+** pTab. If so, return 1. Otherwise, if they are incompatible in some way,
+** return 0.
*/
-static int sessionChangesetToHash(
- sqlite3_changeset_iter *pIter, /* Iterator to read from */
- sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */
- int bRebase /* True if hash table is for rebasing */
+static int sessionChangesetCheckCompat(
+ SessionTable *pTab,
+ int nCol,
+ u8 *abPK
){
- u8 *aRec;
- int nRec;
- int rc = SQLITE_OK;
- SessionTable *pTab = 0;
-
- while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec, 0) ){
- const char *zNew;
- int nCol;
- int op;
- int iHash;
- int bIndirect;
- SessionChange *pChange;
- SessionChange *pExist = 0;
- SessionChange **pp;
-
- if( pGrp->pList==0 ){
- pGrp->bPatch = pIter->bPatchset;
- }else if( pIter->bPatchset!=pGrp->bPatch ){
- rc = SQLITE_ERROR;
- break;
+ if( pTab->azCol && nCol<pTab->nCol ){
+ int ii;
+ for(ii=0; ii<pTab->nCol; ii++){
+ u8 bPK = (ii < nCol) ? abPK[ii] : 0;
+ if( pTab->abPK[ii]!=bPK ) return 0;
}
+ return 1;
+ }
+ return (pTab->nCol==nCol && 0==memcmp(abPK, pTab->abPK, nCol));
+}
- sqlite3changeset_op(pIter, &zNew, &nCol, &op, &bIndirect);
- if( !pTab || sqlite3_stricmp(zNew, pTab->zName) ){
- /* Search the list for a matching table */
- int nNew = (int)strlen(zNew);
- u8 *abPK;
+static int sessionChangesetExtendRecord(
+ sqlite3_changegroup *pGrp,
+ SessionTable *pTab,
+ int nCol,
+ int op,
+ const u8 *aRec,
+ int nRec,
+ SessionBuffer *pOut
+){
+ int rc = SQLITE_OK;
+ int ii = 0;
- sqlite3changeset_pk(pIter, &abPK, 0);
- for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){
- if( 0==sqlite3_strnicmp(pTab->zName, zNew, nNew+1) ) break;
- }
- if( !pTab ){
- SessionTable **ppTab;
+ assert( pTab->azCol );
+ assert( nCol<pTab->nCol );
- pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nNew+1);
- if( !pTab ){
- rc = SQLITE_NOMEM;
+ pOut->nBuf = 0;
+ if( op==SQLITE_INSERT || (op==SQLITE_DELETE && pGrp->bPatch==0) ){
+ /* Append the missing default column values to the record. */
+ sessionAppendBlob(pOut, aRec, nRec, &rc);
+ if( rc==SQLITE_OK && pTab->pDfltStmt==0 ){
+ rc = sessionPrepareDfltStmt(pGrp->db, pTab, &pTab->pDfltStmt);
+ }
+ for(ii=nCol; rc==SQLITE_OK && ii<pTab->nCol; ii++){
+ int eType = sqlite3_column_type(pTab->pDfltStmt, ii);
+ sessionAppendByte(pOut, eType, &rc);
+ switch( eType ){
+ case SQLITE_FLOAT:
+ case SQLITE_INTEGER: {
+ i64 iVal;
+ if( eType==SQLITE_INTEGER ){
+ iVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
+ }else{
+ double rVal = sqlite3_column_int64(pTab->pDfltStmt, ii);
+ memcpy(&iVal, &rVal, sizeof(i64));
+ }
+ if( SQLITE_OK==sessionBufferGrow(pOut, 8, &rc) ){
+ sessionPutI64(&pOut->aBuf[pOut->nBuf], iVal);
+ }
break;
}
- memset(pTab, 0, sizeof(SessionTable));
- pTab->nCol = nCol;
- pTab->abPK = (u8*)&pTab[1];
- memcpy(pTab->abPK, abPK, nCol);
- pTab->zName = (char*)&pTab->abPK[nCol];
- memcpy(pTab->zName, zNew, nNew+1);
-
- /* The new object must be linked on to the end of the list, not
- ** simply added to the start of it. This is to ensure that the
- ** tables within the output of sqlite3changegroup_output() are in
- ** the right order. */
- for(ppTab=&pGrp->pList; *ppTab; ppTab=&(*ppTab)->pNext);
- *ppTab = pTab;
- }else if( pTab->nCol!=nCol || memcmp(pTab->abPK, abPK, nCol) ){
- rc = SQLITE_SCHEMA;
- break;
+
+ case SQLITE_BLOB:
+ case SQLITE_TEXT: {
+ int n = sqlite3_column_bytes(pTab->pDfltStmt, ii);
+ sessionAppendVarint(pOut, n, &rc);
+ if( eType==SQLITE_TEXT ){
+ const u8 *z = (const u8*)sqlite3_column_text(pTab->pDfltStmt, ii);
+ sessionAppendBlob(pOut, z, n, &rc);
+ }else{
+ const u8 *z = (const u8*)sqlite3_column_blob(pTab->pDfltStmt, ii);
+ sessionAppendBlob(pOut, z, n, &rc);
+ }
+ break;
+ }
+
+ default:
+ assert( eType==SQLITE_NULL );
+ break;
+ }
+ }
+ }else if( op==SQLITE_UPDATE ){
+ /* Append missing "undefined" entries to the old.* record. And, if this
+ ** is an UPDATE, to the new.* record as well. */
+ int iOff = 0;
+ if( pGrp->bPatch==0 ){
+ for(ii=0; ii<nCol; ii++){
+ iOff += sessionSerialLen(&aRec[iOff]);
+ }
+ sessionAppendBlob(pOut, aRec, iOff, &rc);
+ for(ii=0; ii<(pTab->nCol-nCol); ii++){
+ sessionAppendByte(pOut, 0x00, &rc);
}
}
- if( sessionGrowHash(0, pIter->bPatchset, pTab) ){
- rc = SQLITE_NOMEM;
- break;
+ sessionAppendBlob(pOut, &aRec[iOff], nRec-iOff, &rc);
+ for(ii=0; ii<(pTab->nCol-nCol); ii++){
+ sessionAppendByte(pOut, 0x00, &rc);
}
+ }else{
+ assert( op==SQLITE_DELETE && pGrp->bPatch );
+ sessionAppendBlob(pOut, aRec, nRec, &rc);
+ }
+
+ return rc;
+}
+
+/*
+** Locate or create a SessionTable object that may be used to add the
+** change currently pointed to by iterator pIter to changegroup pGrp.
+** If successful, set output variable (*ppTab) to point to the table
+** object and return SQLITE_OK. Otherwise, if some error occurs, return
+** an SQLite error code and leave (*ppTab) set to NULL.
+*/
+static int sessionChangesetFindTable(
+ sqlite3_changegroup *pGrp,
+ const char *zTab,
+ sqlite3_changeset_iter *pIter,
+ SessionTable **ppTab
+){
+ int rc = SQLITE_OK;
+ SessionTable *pTab = 0;
+ int nTab = (int)strlen(zTab);
+ u8 *abPK = 0;
+ int nCol = 0;
+
+ *ppTab = 0;
+ sqlite3changeset_pk(pIter, &abPK, &nCol);
+
+ /* Search the list for an existing table */
+ for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){
+ if( 0==sqlite3_strnicmp(pTab->zName, zTab, nTab+1) ) break;
+ }
+
+ /* If one was not found above, create a new table now */
+ if( !pTab ){
+ SessionTable **ppNew;
+
+ pTab = sqlite3_malloc64(sizeof(SessionTable) + nCol + nTab+1);
+ if( !pTab ){
+ return SQLITE_NOMEM;
+ }
+ memset(pTab, 0, sizeof(SessionTable));
+ pTab->nCol = nCol;
+ pTab->abPK = (u8*)&pTab[1];
+ memcpy(pTab->abPK, abPK, nCol);
+ pTab->zName = (char*)&pTab->abPK[nCol];
+ memcpy(pTab->zName, zTab, nTab+1);
+
+ if( pGrp->db ){
+ pTab->nCol = 0;
+ rc = sessionInitTable(0, pTab, pGrp->db, pGrp->zDb);
+ if( rc ){
+ assert( pTab->azCol==0 );
+ sqlite3_free(pTab);
+ return rc;
+ }
+ }
+
+ /* The new object must be linked on to the end of the list, not
+ ** simply added to the start of it. This is to ensure that the
+ ** tables within the output of sqlite3changegroup_output() are in
+ ** the right order. */
+ for(ppNew=&pGrp->pList; *ppNew; ppNew=&(*ppNew)->pNext);
+ *ppNew = pTab;
+ }
+
+ /* Check that the table is compatible. */
+ if( !sessionChangesetCheckCompat(pTab, nCol, abPK) ){
+ rc = SQLITE_SCHEMA;
+ }
+
+ *ppTab = pTab;
+ return rc;
+}
+
+/*
+** Add the change currently indicated by iterator pIter to the hash table
+** belonging to changegroup pGrp.
+*/
+static int sessionOneChangeToHash(
+ sqlite3_changegroup *pGrp,
+ sqlite3_changeset_iter *pIter,
+ int bRebase
+){
+ int rc = SQLITE_OK;
+ int nCol = 0;
+ int op = 0;
+ int iHash = 0;
+ int bIndirect = 0;
+ SessionChange *pChange = 0;
+ SessionChange *pExist = 0;
+ SessionChange **pp = 0;
+ SessionTable *pTab = 0;
+ u8 *aRec = &pIter->in.aData[pIter->in.iCurrent + 2];
+ int nRec = (pIter->in.iNext - pIter->in.iCurrent) - 2;
+
+ /* Ensure that only changesets, or only patchsets, but not a mixture
+ ** of both, are being combined. It is an error to try to combine a
+ ** changeset and a patchset. */
+ if( pGrp->pList==0 ){
+ pGrp->bPatch = pIter->bPatchset;
+ }else if( pIter->bPatchset!=pGrp->bPatch ){
+ rc = SQLITE_ERROR;
+ }
+
+ if( rc==SQLITE_OK ){
+ const char *zTab = 0;
+ sqlite3changeset_op(pIter, &zTab, &nCol, &op, &bIndirect);
+ rc = sessionChangesetFindTable(pGrp, zTab, pIter, &pTab);
+ }
+
+ if( rc==SQLITE_OK && nCol<pTab->nCol ){
+ SessionBuffer *pBuf = &pGrp->rec;
+ rc = sessionChangesetExtendRecord(pGrp, pTab, nCol, op, aRec, nRec, pBuf);
+ aRec = pBuf->aBuf;
+ nRec = pBuf->nBuf;
+ assert( pGrp->db );
+ }
+
+ if( rc==SQLITE_OK && sessionGrowHash(0, pIter->bPatchset, pTab) ){
+ rc = SQLITE_NOMEM;
+ }
+
+ if( rc==SQLITE_OK ){
+ /* Search for existing entry. If found, remove it from the hash table.
+ ** Code below may link it back in. */
iHash = sessionChangeHash(
pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange
);
-
- /* Search for existing entry. If found, remove it from the hash table.
- ** Code below may link it back in.
- */
for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){
int bPkOnly1 = 0;
int bPkOnly2 = 0;
@@ -225199,16 +230757,39 @@ static int sessionChangesetToHash(
break;
}
}
+ }
+ if( rc==SQLITE_OK ){
rc = sessionChangeMerge(pTab, bRebase,
pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange
);
- if( rc ) break;
- if( pChange ){
- pChange->pNext = pTab->apChange[iHash];
- pTab->apChange[iHash] = pChange;
- pTab->nEntry++;
- }
+ }
+ if( rc==SQLITE_OK && pChange ){
+ pChange->pNext = pTab->apChange[iHash];
+ pTab->apChange[iHash] = pChange;
+ pTab->nEntry++;
+ }
+
+ if( rc==SQLITE_OK ) rc = pIter->rc;
+ return rc;
+}
+
+/*
+** Add all changes in the changeset traversed by the iterator passed as
+** the first argument to the changegroup hash tables.
+*/
+static int sessionChangesetToHash(
+ sqlite3_changeset_iter *pIter, /* Iterator to read from */
+ sqlite3_changegroup *pGrp, /* Changegroup object to add changeset to */
+ int bRebase /* True if hash table is for rebasing */
+){
+ u8 *aRec;
+ int nRec;
+ int rc = SQLITE_OK;
+
+ while( SQLITE_ROW==(sessionChangesetNext(pIter, &aRec, &nRec, 0)) ){
+ rc = sessionOneChangeToHash(pGrp, pIter, bRebase);
+ if( rc!=SQLITE_OK ) break;
}
if( rc==SQLITE_OK ) rc = pIter->rc;
@@ -225298,6 +230879,31 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp){
}
/*
+** Provide a database schema to the changegroup object.
+*/
+SQLITE_API int sqlite3changegroup_schema(
+ sqlite3_changegroup *pGrp,
+ sqlite3 *db,
+ const char *zDb
+){
+ int rc = SQLITE_OK;
+
+ if( pGrp->pList || pGrp->db ){
+ /* Cannot add a schema after one or more calls to sqlite3changegroup_add(),
+ ** or after sqlite3changegroup_schema() has already been called. */
+ rc = SQLITE_MISUSE;
+ }else{
+ pGrp->zDb = sqlite3_mprintf("%s", zDb);
+ if( pGrp->zDb==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ pGrp->db = db;
+ }
+ }
+ return rc;
+}
+
+/*
** Add the changeset currently stored in buffer pData, size nData bytes,
** to changeset-group p.
*/
@@ -225314,6 +230920,23 @@ SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup *pGrp, int nData, void
}
/*
+** Add a single change to a changeset-group.
+*/
+SQLITE_API int sqlite3changegroup_add_change(
+ sqlite3_changegroup *pGrp,
+ sqlite3_changeset_iter *pIter
+){
+ if( pIter->in.iCurrent==pIter->in.iNext
+ || pIter->rc!=SQLITE_OK
+ || pIter->bInvert
+ ){
+ /* Iterator does not point to any valid entry or is an INVERT iterator. */
+ return SQLITE_ERROR;
+ }
+ return sessionOneChangeToHash(pGrp, pIter, 0);
+}
+
+/*
** Obtain a buffer containing a changeset representing the concatenation
** of all changesets added to the group so far.
*/
@@ -225360,7 +230983,9 @@ SQLITE_API int sqlite3changegroup_output_strm(
*/
SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){
if( pGrp ){
+ sqlite3_free(pGrp->zDb);
sessionDeleteTable(0, pGrp->pList);
+ sqlite3_free(pGrp->rec.aBuf);
sqlite3_free(pGrp);
}
}
@@ -225762,6 +231387,7 @@ SQLITE_API int sqlite3rebaser_rebase_strm(
SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p){
if( p ){
sessionDeleteTable(0, p->grp.pList);
+ sqlite3_free(p->grp.rec.aBuf);
sqlite3_free(p);
}
}
@@ -225859,8 +231485,8 @@ struct Fts5PhraseIter {
** EXTENSION API FUNCTIONS
**
** xUserData(pFts):
-** Return a copy of the context pointer the extension function was
-** registered with.
+** Return a copy of the pUserData pointer passed to the xCreateFunction()
+** API when the extension function was registered.
**
** xColumnTotalSize(pFts, iCol, pnToken):
** If parameter iCol is less than zero, set output variable *pnToken
@@ -225892,8 +231518,11 @@ struct Fts5PhraseIter {
** created with the "columnsize=0" option.
**
** xColumnText:
-** This function attempts to retrieve the text of column iCol of the
-** current document. If successful, (*pz) is set to point to a buffer
+** If parameter iCol is less than zero, or greater than or equal to the
+** number of columns in the table, SQLITE_RANGE is returned.
+**
+** Otherwise, this function attempts to retrieve the text of column iCol of
+** the current document. If successful, (*pz) is set to point to a buffer
** containing the text in utf-8 encoding, (*pn) is set to the size in bytes
** (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
** if an error occurs, an SQLite error code is returned and the final values
@@ -225903,8 +231532,10 @@ struct Fts5PhraseIter {
** Returns the number of phrases in the current query expression.
**
** xPhraseSize:
-** Returns the number of tokens in phrase iPhrase of the query. Phrases
-** are numbered starting from zero.
+** If parameter iCol is less than zero, or greater than or equal to the
+** number of phrases in the current query, as returned by xPhraseCount,
+** 0 is returned. Otherwise, this function returns the number of tokens in
+** phrase iPhrase of the query. Phrases are numbered starting from zero.
**
** xInstCount:
** Set *pnInst to the total number of occurrences of all phrases within
@@ -225920,12 +231551,13 @@ struct Fts5PhraseIter {
** Query for the details of phrase match iIdx within the current row.
** Phrase matches are numbered starting from zero, so the iIdx argument
** should be greater than or equal to zero and smaller than the value
-** output by xInstCount().
+** output by xInstCount(). If iIdx is less than zero or greater than
+** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned.
**
-** Usually, output parameter *piPhrase is set to the phrase number, *piCol
+** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol
** to the column in which it occurs and *piOff the token offset of the
-** first token of the phrase. Returns SQLITE_OK if successful, or an error
-** code (i.e. SQLITE_NOMEM) if an error occurs.
+** first token of the phrase. SQLITE_OK is returned if successful, or an
+** error code (i.e. SQLITE_NOMEM) if an error occurs.
**
** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option.
@@ -225951,6 +231583,10 @@ struct Fts5PhraseIter {
** Invoking Api.xUserData() returns a copy of the pointer passed as
** the third argument to pUserData.
**
+** If parameter iPhrase is less than zero, or greater than or equal to
+** the number of phrases in the query, as returned by xPhraseCount(),
+** this function returns SQLITE_RANGE.
+**
** If the callback function returns any value other than SQLITE_OK, the
** query is abandoned and the xQueryPhrase function returns immediately.
** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
@@ -226065,9 +231701,42 @@ struct Fts5PhraseIter {
**
** xPhraseNextColumn()
** See xPhraseFirstColumn above.
+**
+** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken)
+** This is used to access token iToken of phrase iPhrase of the current
+** query. Before returning, output parameter *ppToken is set to point
+** to a buffer containing the requested token, and *pnToken to the
+** size of this buffer in bytes.
+**
+** If iPhrase or iToken are less than zero, or if iPhrase is greater than
+** or equal to the number of phrases in the query as reported by
+** xPhraseCount(), or if iToken is equal to or greater than the number of
+** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken
+ are both zeroed.
+**
+** The output text is not a copy of the query text that specified the
+** token. It is the output of the tokenizer module. For tokendata=1
+** tables, this includes any embedded 0x00 and trailing data.
+**
+** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken)
+** This is used to access token iToken of phrase hit iIdx within the
+** current row. If iIdx is less than zero or greater than or equal to the
+** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
+** output variable (*ppToken) is set to point to a buffer containing the
+** matching document token, and (*pnToken) to the size of that buffer in
+** bytes. This API is not available if the specified token matches a
+** prefix query term. In that case both output variables are always set
+** to 0.
+**
+** The output text is not a copy of the document text that was tokenized.
+** It is the output of the tokenizer module. For tokendata=1 tables, this
+** includes any embedded 0x00 and trailing data.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option.
*/
struct Fts5ExtensionApi {
- int iVersion; /* Currently always set to 2 */
+ int iVersion; /* Currently always set to 3 */
void *(*xUserData)(Fts5Context*);
@@ -226102,6 +231771,13 @@ struct Fts5ExtensionApi {
int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
+
+ /* Below this point are iVersion>=3 only */
+ int (*xQueryToken)(Fts5Context*,
+ int iPhrase, int iToken,
+ const char **ppToken, int *pnToken
+ );
+ int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
};
/*
@@ -226576,6 +232252,7 @@ struct Fts5Config {
char *zContent; /* content table */
char *zContentRowid; /* "content_rowid=" option value */
int bColumnsize; /* "columnsize=" option value (dflt==1) */
+ int bTokendata; /* "tokendata=" option value (dflt==0) */
int eDetail; /* FTS5_DETAIL_XXX value */
char *zContentExprlist;
Fts5Tokenizer *pTok;
@@ -226764,17 +232441,19 @@ struct Fts5IndexIter {
/*
** Values used as part of the flags argument passed to IndexQuery().
*/
-#define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */
-#define FTS5INDEX_QUERY_DESC 0x0002 /* Docs in descending rowid order */
-#define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */
-#define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */
+#define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */
+#define FTS5INDEX_QUERY_DESC 0x0002 /* Docs in descending rowid order */
+#define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */
+#define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */
/* The following are used internally by the fts5_index.c module. They are
** defined here only to make it easier to avoid clashes with the flags
** above. */
-#define FTS5INDEX_QUERY_SKIPEMPTY 0x0010
-#define FTS5INDEX_QUERY_NOOUTPUT 0x0020
-#define FTS5INDEX_QUERY_SKIPHASH 0x0040
+#define FTS5INDEX_QUERY_SKIPEMPTY 0x0010
+#define FTS5INDEX_QUERY_NOOUTPUT 0x0020
+#define FTS5INDEX_QUERY_SKIPHASH 0x0040
+#define FTS5INDEX_QUERY_NOTOKENDATA 0x0080
+#define FTS5INDEX_QUERY_SCANONETERM 0x0100
/*
** Create/destroy an Fts5Index object.
@@ -226843,6 +232522,10 @@ static void *sqlite3Fts5StructureRef(Fts5Index*);
static void sqlite3Fts5StructureRelease(void*);
static int sqlite3Fts5StructureTest(Fts5Index*, void*);
+/*
+** Used by xInstToken():
+*/
+static int sqlite3Fts5IterToken(Fts5IndexIter*, i64, int, int, const char**, int*);
/*
** Insert or remove data to or from the index. Each time a document is
@@ -226920,6 +232603,13 @@ static int sqlite3Fts5IndexLoadConfig(Fts5Index *p);
static int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin);
static int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid);
+static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter*);
+
+/* Used to populate hash tables for xInstToken in detail=none/column mode. */
+static int sqlite3Fts5IndexIterWriteTokendata(
+ Fts5IndexIter*, const char*, int, i64 iRowid, int iCol, int iOff
+);
+
/*
** End of interface to code in fts5_index.c.
**************************************************************************/
@@ -227025,6 +232715,7 @@ static void sqlite3Fts5HashScanNext(Fts5Hash*);
static int sqlite3Fts5HashScanEof(Fts5Hash*);
static void sqlite3Fts5HashScanEntry(Fts5Hash *,
const char **pzTerm, /* OUT: term (nul-terminated) */
+ int *pnTerm, /* OUT: Size of term in bytes */
const u8 **ppDoclist, /* OUT: pointer to doclist */
int *pnDoclist /* OUT: size of doclist in bytes */
);
@@ -227151,6 +232842,10 @@ static int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**);
static int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *);
+static int sqlite3Fts5ExprQueryToken(Fts5Expr*, int, int, const char**, int*);
+static int sqlite3Fts5ExprInstToken(Fts5Expr*, i64, int, int, int, int, const char**, int*);
+static void sqlite3Fts5ExprClearTokens(Fts5Expr*);
+
/*******************************************
** The fts5_expr.c API above this point is used by the other hand-written
** C code in this module. The interfaces below this point are called by
@@ -227387,6 +233082,9 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*);
** sqlite3Fts5ParserARG_STORE Code to store %extra_argument into fts5yypParser
** sqlite3Fts5ParserARG_FETCH Code to extract %extra_argument from fts5yypParser
** sqlite3Fts5ParserCTX_* As sqlite3Fts5ParserARG_ except for %extra_context
+** fts5YYREALLOC Name of the realloc() function to use
+** fts5YYFREE Name of the free() function to use
+** fts5YYDYNSTACK True if stack space should be extended on heap
** fts5YYERRORSYMBOL is the code number of the error symbol. If not
** defined, then do no error processing.
** fts5YYNSTATE the combined number of states.
@@ -227400,6 +233098,8 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*);
** fts5YY_NO_ACTION The fts5yy_action[] code for no-op
** fts5YY_MIN_REDUCE Minimum value for reduce actions
** fts5YY_MAX_REDUCE Maximum value for reduce actions
+** fts5YY_MIN_DSTRCTR Minimum symbol value that has a destructor
+** fts5YY_MAX_DSTRCTR Maximum symbol value that has a destructor
*/
#ifndef INTERFACE
# define INTERFACE 1
@@ -227426,6 +233126,9 @@ typedef union {
#define sqlite3Fts5ParserARG_PARAM ,pParse
#define sqlite3Fts5ParserARG_FETCH Fts5Parse *pParse=fts5yypParser->pParse;
#define sqlite3Fts5ParserARG_STORE fts5yypParser->pParse=pParse;
+#define fts5YYREALLOC realloc
+#define fts5YYFREE free
+#define fts5YYDYNSTACK 0
#define sqlite3Fts5ParserCTX_SDECL
#define sqlite3Fts5ParserCTX_PDECL
#define sqlite3Fts5ParserCTX_PARAM
@@ -227443,6 +233146,8 @@ typedef union {
#define fts5YY_NO_ACTION 82
#define fts5YY_MIN_REDUCE 83
#define fts5YY_MAX_REDUCE 110
+#define fts5YY_MIN_DSTRCTR 16
+#define fts5YY_MAX_DSTRCTR 24
/************* End control #defines *******************************************/
#define fts5YY_NLOOKAHEAD ((int)(sizeof(fts5yy_lookahead)/sizeof(fts5yy_lookahead[0])))
@@ -227458,6 +233163,22 @@ typedef union {
# define fts5yytestcase(X)
#endif
+/* Macro to determine if stack space has the ability to grow using
+** heap memory.
+*/
+#if fts5YYSTACKDEPTH<=0 || fts5YYDYNSTACK
+# define fts5YYGROWABLESTACK 1
+#else
+# define fts5YYGROWABLESTACK 0
+#endif
+
+/* Guarantee a minimum number of initial stack slots.
+*/
+#if fts5YYSTACKDEPTH<=0
+# undef fts5YYSTACKDEPTH
+# define fts5YYSTACKDEPTH 2 /* Need a minimum stack size */
+#endif
+
/* Next are the tables used to determine what action to take based on the
** current state and lookahead token. These tables are used to implement
@@ -227618,14 +233339,9 @@ struct fts5yyParser {
#endif
sqlite3Fts5ParserARG_SDECL /* A place to hold %extra_argument */
sqlite3Fts5ParserCTX_SDECL /* A place to hold %extra_context */
-#if fts5YYSTACKDEPTH<=0
- int fts5yystksz; /* Current side of the stack */
- fts5yyStackEntry *fts5yystack; /* The parser's stack */
- fts5yyStackEntry fts5yystk0; /* First stack entry */
-#else
- fts5yyStackEntry fts5yystack[fts5YYSTACKDEPTH]; /* The parser's stack */
- fts5yyStackEntry *fts5yystackEnd; /* Last entry in the stack */
-#endif
+ fts5yyStackEntry *fts5yystackEnd; /* Last entry in the stack */
+ fts5yyStackEntry *fts5yystack; /* The parser stack */
+ fts5yyStackEntry fts5yystk0[fts5YYSTACKDEPTH]; /* Initial stack space */
};
typedef struct fts5yyParser fts5yyParser;
@@ -227732,37 +233448,45 @@ static const char *const fts5yyRuleName[] = {
#endif /* NDEBUG */
-#if fts5YYSTACKDEPTH<=0
+#if fts5YYGROWABLESTACK
/*
** Try to increase the size of the parser stack. Return the number
** of errors. Return 0 on success.
*/
static int fts5yyGrowStack(fts5yyParser *p){
+ int oldSize = 1 + (int)(p->fts5yystackEnd - p->fts5yystack);
int newSize;
int idx;
fts5yyStackEntry *pNew;
- newSize = p->fts5yystksz*2 + 100;
- idx = p->fts5yytos ? (int)(p->fts5yytos - p->fts5yystack) : 0;
- if( p->fts5yystack==&p->fts5yystk0 ){
- pNew = malloc(newSize*sizeof(pNew[0]));
- if( pNew ) pNew[0] = p->fts5yystk0;
+ newSize = oldSize*2 + 100;
+ idx = (int)(p->fts5yytos - p->fts5yystack);
+ if( p->fts5yystack==p->fts5yystk0 ){
+ pNew = fts5YYREALLOC(0, newSize*sizeof(pNew[0]));
+ if( pNew==0 ) return 1;
+ memcpy(pNew, p->fts5yystack, oldSize*sizeof(pNew[0]));
}else{
- pNew = realloc(p->fts5yystack, newSize*sizeof(pNew[0]));
+ pNew = fts5YYREALLOC(p->fts5yystack, newSize*sizeof(pNew[0]));
+ if( pNew==0 ) return 1;
}
- if( pNew ){
- p->fts5yystack = pNew;
- p->fts5yytos = &p->fts5yystack[idx];
+ p->fts5yystack = pNew;
+ p->fts5yytos = &p->fts5yystack[idx];
#ifndef NDEBUG
- if( fts5yyTraceFILE ){
- fprintf(fts5yyTraceFILE,"%sStack grows from %d to %d entries.\n",
- fts5yyTracePrompt, p->fts5yystksz, newSize);
- }
-#endif
- p->fts5yystksz = newSize;
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sStack grows from %d to %d entries.\n",
+ fts5yyTracePrompt, oldSize, newSize);
}
- return pNew==0;
+#endif
+ p->fts5yystackEnd = &p->fts5yystack[newSize-1];
+ return 0;
}
+#endif /* fts5YYGROWABLESTACK */
+
+#if !fts5YYGROWABLESTACK
+/* For builds that do no have a growable stack, fts5yyGrowStack always
+** returns an error.
+*/
+# define fts5yyGrowStack(X) 1
#endif
/* Datatype of the argument to the memory allocated passed as the
@@ -227782,24 +233506,14 @@ static void sqlite3Fts5ParserInit(void *fts5yypRawParser sqlite3Fts5ParserCTX_PD
#ifdef fts5YYTRACKMAXSTACKDEPTH
fts5yypParser->fts5yyhwm = 0;
#endif
-#if fts5YYSTACKDEPTH<=0
- fts5yypParser->fts5yytos = NULL;
- fts5yypParser->fts5yystack = NULL;
- fts5yypParser->fts5yystksz = 0;
- if( fts5yyGrowStack(fts5yypParser) ){
- fts5yypParser->fts5yystack = &fts5yypParser->fts5yystk0;
- fts5yypParser->fts5yystksz = 1;
- }
-#endif
+ fts5yypParser->fts5yystack = fts5yypParser->fts5yystk0;
+ fts5yypParser->fts5yystackEnd = &fts5yypParser->fts5yystack[fts5YYSTACKDEPTH-1];
#ifndef fts5YYNOERRORRECOVERY
fts5yypParser->fts5yyerrcnt = -1;
#endif
fts5yypParser->fts5yytos = fts5yypParser->fts5yystack;
fts5yypParser->fts5yystack[0].stateno = 0;
fts5yypParser->fts5yystack[0].major = 0;
-#if fts5YYSTACKDEPTH>0
- fts5yypParser->fts5yystackEnd = &fts5yypParser->fts5yystack[fts5YYSTACKDEPTH-1];
-#endif
}
#ifndef sqlite3Fts5Parser_ENGINEALWAYSONSTACK
@@ -227913,9 +233627,26 @@ static void fts5yy_pop_parser_stack(fts5yyParser *pParser){
*/
static void sqlite3Fts5ParserFinalize(void *p){
fts5yyParser *pParser = (fts5yyParser*)p;
- while( pParser->fts5yytos>pParser->fts5yystack ) fts5yy_pop_parser_stack(pParser);
-#if fts5YYSTACKDEPTH<=0
- if( pParser->fts5yystack!=&pParser->fts5yystk0 ) free(pParser->fts5yystack);
+
+ /* In-lined version of calling fts5yy_pop_parser_stack() for each
+ ** element left in the stack */
+ fts5yyStackEntry *fts5yytos = pParser->fts5yytos;
+ while( fts5yytos>pParser->fts5yystack ){
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sPopping %s\n",
+ fts5yyTracePrompt,
+ fts5yyTokenName[fts5yytos->major]);
+ }
+#endif
+ if( fts5yytos->major>=fts5YY_MIN_DSTRCTR ){
+ fts5yy_destructor(pParser, fts5yytos->major, &fts5yytos->minor);
+ }
+ fts5yytos--;
+ }
+
+#if fts5YYGROWABLESTACK
+ if( pParser->fts5yystack!=pParser->fts5yystk0 ) fts5YYFREE(pParser->fts5yystack);
#endif
}
@@ -228142,25 +233873,19 @@ static void fts5yy_shift(
assert( fts5yypParser->fts5yyhwm == (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack) );
}
#endif
-#if fts5YYSTACKDEPTH>0
- if( fts5yypParser->fts5yytos>fts5yypParser->fts5yystackEnd ){
- fts5yypParser->fts5yytos--;
- fts5yyStackOverflow(fts5yypParser);
- return;
- }
-#else
- if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz] ){
+ fts5yytos = fts5yypParser->fts5yytos;
+ if( fts5yytos>fts5yypParser->fts5yystackEnd ){
if( fts5yyGrowStack(fts5yypParser) ){
fts5yypParser->fts5yytos--;
fts5yyStackOverflow(fts5yypParser);
return;
}
+ fts5yytos = fts5yypParser->fts5yytos;
+ assert( fts5yytos <= fts5yypParser->fts5yystackEnd );
}
-#endif
if( fts5yyNewState > fts5YY_MAX_SHIFT ){
fts5yyNewState += fts5YY_MIN_REDUCE - fts5YY_MIN_SHIFTREDUCE;
}
- fts5yytos = fts5yypParser->fts5yytos;
fts5yytos->stateno = fts5yyNewState;
fts5yytos->major = fts5yyMajor;
fts5yytos->minor.fts5yy0 = fts5yyMinor;
@@ -228597,19 +234322,12 @@ static void sqlite3Fts5Parser(
(int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack));
}
#endif
-#if fts5YYSTACKDEPTH>0
if( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystackEnd ){
- fts5yyStackOverflow(fts5yypParser);
- break;
- }
-#else
- if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz-1] ){
if( fts5yyGrowStack(fts5yypParser) ){
fts5yyStackOverflow(fts5yypParser);
break;
}
}
-#endif
}
fts5yyact = fts5yy_reduce(fts5yypParser,fts5yyruleno,fts5yymajor,fts5yyminor sqlite3Fts5ParserCTX_PARAM);
}else if( fts5yyact <= fts5YY_MAX_SHIFTREDUCE ){
@@ -228865,15 +234583,19 @@ static int fts5CInstIterInit(
*/
typedef struct HighlightContext HighlightContext;
struct HighlightContext {
- CInstIter iter; /* Coalesced Instance Iterator */
- int iPos; /* Current token offset in zIn[] */
+ /* Constant parameters to fts5HighlightCb() */
int iRangeStart; /* First token to include */
int iRangeEnd; /* If non-zero, last token to include */
const char *zOpen; /* Opening highlight */
const char *zClose; /* Closing highlight */
const char *zIn; /* Input text */
int nIn; /* Size of input text in bytes */
- int iOff; /* Current offset within zIn[] */
+
+ /* Variables modified by fts5HighlightCb() */
+ CInstIter iter; /* Coalesced Instance Iterator */
+ int iPos; /* Current token offset in zIn[] */
+ int iOff; /* Have copied up to this offset in zIn[] */
+ int bOpen; /* True if highlight is open */
char *zOut; /* Output value */
};
@@ -228906,8 +234628,8 @@ static int fts5HighlightCb(
int tflags, /* Mask of FTS5_TOKEN_* flags */
const char *pToken, /* Buffer containing token */
int nToken, /* Size of token in bytes */
- int iStartOff, /* Start offset of token */
- int iEndOff /* End offset of token */
+ int iStartOff, /* Start byte offset of token */
+ int iEndOff /* End byte offset of token */
){
HighlightContext *p = (HighlightContext*)pContext;
int rc = SQLITE_OK;
@@ -228923,30 +234645,55 @@ static int fts5HighlightCb(
if( p->iRangeStart && iPos==p->iRangeStart ) p->iOff = iStartOff;
}
- if( iPos==p->iter.iStart ){
+ /* If the parenthesis is open, and this token is not part of the current
+ ** phrase, and the starting byte offset of this token is past the point
+ ** that has currently been copied into the output buffer, close the
+ ** parenthesis. */
+ if( p->bOpen
+ && (iPos<=p->iter.iStart || p->iter.iStart<0)
+ && iStartOff>p->iOff
+ ){
+ fts5HighlightAppend(&rc, p, p->zClose, -1);
+ p->bOpen = 0;
+ }
+
+ /* If this is the start of a new phrase, and the highlight is not open:
+ **
+ ** * copy text from the input up to the start of the phrase, and
+ ** * open the highlight.
+ */
+ if( iPos==p->iter.iStart && p->bOpen==0 ){
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iStartOff - p->iOff);
fts5HighlightAppend(&rc, p, p->zOpen, -1);
p->iOff = iStartOff;
+ p->bOpen = 1;
}
if( iPos==p->iter.iEnd ){
- if( p->iRangeEnd>=0 && p->iter.iStart<p->iRangeStart ){
+ if( p->bOpen==0 ){
+ assert( p->iRangeEnd>=0 );
fts5HighlightAppend(&rc, p, p->zOpen, -1);
+ p->bOpen = 1;
}
fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
- fts5HighlightAppend(&rc, p, p->zClose, -1);
p->iOff = iEndOff;
+
if( rc==SQLITE_OK ){
rc = fts5CInstIterNext(&p->iter);
}
}
- if( p->iRangeEnd>=0 && iPos==p->iRangeEnd ){
- fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
- p->iOff = iEndOff;
- if( iPos>=p->iter.iStart && iPos<p->iter.iEnd ){
+ if( iPos==p->iRangeEnd ){
+ if( p->bOpen ){
+ if( p->iter.iStart>=0 && iPos>=p->iter.iStart ){
+ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
+ p->iOff = iEndOff;
+ }
fts5HighlightAppend(&rc, p, p->zClose, -1);
+ p->bOpen = 0;
}
+ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
+ p->iOff = iEndOff;
}
return rc;
@@ -228978,8 +234725,10 @@ static void fts5HighlightFunction(
ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
ctx.iRangeEnd = -1;
rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn);
-
- if( ctx.zIn ){
+ if( rc==SQLITE_RANGE ){
+ sqlite3_result_text(pCtx, "", -1, SQLITE_STATIC);
+ rc = SQLITE_OK;
+ }else if( ctx.zIn ){
if( rc==SQLITE_OK ){
rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter);
}
@@ -228987,6 +234736,9 @@ static void fts5HighlightFunction(
if( rc==SQLITE_OK ){
rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
}
+ if( ctx.bOpen ){
+ fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1);
+ }
fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff);
if( rc==SQLITE_OK ){
@@ -229265,6 +235017,9 @@ static void fts5SnippetFunction(
if( rc==SQLITE_OK ){
rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
}
+ if( ctx.bOpen ){
+ fts5HighlightAppend(&rc, &ctx, ctx.zClose, -1);
+ }
if( ctx.iRangeEnd>=(nColSize-1) ){
fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff);
}else{
@@ -229540,6 +235295,7 @@ static void sqlite3Fts5BufferAppendBlob(
){
if( nData ){
if( fts5BufferGrow(pRc, pBuf, nData) ) return;
+ assert( pBuf->p!=0 );
memcpy(&pBuf->p[pBuf->n], pData, nData);
pBuf->n += nData;
}
@@ -229641,6 +235397,7 @@ static int sqlite3Fts5PoslistNext64(
i64 *piOff /* IN/OUT: Current offset */
){
int i = *pi;
+ assert( a!=0 || i==0 );
if( i>=n ){
/* EOF */
*piOff = -1;
@@ -229648,6 +235405,7 @@ static int sqlite3Fts5PoslistNext64(
}else{
i64 iOff = *piOff;
u32 iVal;
+ assert( a!=0 );
fts5FastGetVarint32(a, i, iVal);
if( iVal<=1 ){
if( iVal==0 ){
@@ -230279,6 +236037,16 @@ static int fts5ConfigParseSpecial(
return rc;
}
+ if( sqlite3_strnicmp("tokendata", zCmd, nCmd)==0 ){
+ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
+ *pzErr = sqlite3_mprintf("malformed tokendata=... directive");
+ rc = SQLITE_ERROR;
+ }else{
+ pConfig->bTokendata = (zArg[0]=='1');
+ }
+ return rc;
+ }
+
*pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd);
return SQLITE_ERROR;
}
@@ -231012,7 +236780,9 @@ struct Fts5ExprNode {
struct Fts5ExprTerm {
u8 bPrefix; /* True for a prefix term */
u8 bFirst; /* True if token must be first in column */
- char *zTerm; /* nul-terminated term */
+ char *pTerm; /* Term data */
+ int nQueryTerm; /* Effective size of term in bytes */
+ int nFullTerm; /* Size of term in bytes incl. tokendata */
Fts5IndexIter *pIter; /* Iterator for this term */
Fts5ExprTerm *pSynonym; /* Pointer to first in list of synonyms */
};
@@ -231879,7 +237649,7 @@ static int fts5ExprNearInitAll(
p->pIter = 0;
}
rc = sqlite3Fts5IndexQuery(
- pExpr->pIndex, p->zTerm, (int)strlen(p->zTerm),
+ pExpr->pIndex, p->pTerm, p->nQueryTerm,
(pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) |
(pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0),
pNear->pColset,
@@ -232516,7 +238286,7 @@ static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){
Fts5ExprTerm *pSyn;
Fts5ExprTerm *pNext;
Fts5ExprTerm *pTerm = &pPhrase->aTerm[i];
- sqlite3_free(pTerm->zTerm);
+ sqlite3_free(pTerm->pTerm);
sqlite3Fts5IterClose(pTerm->pIter);
for(pSyn=pTerm->pSynonym; pSyn; pSyn=pNext){
pNext = pSyn->pSynonym;
@@ -232614,6 +238384,7 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset(
typedef struct TokenCtx TokenCtx;
struct TokenCtx {
Fts5ExprPhrase *pPhrase;
+ Fts5Config *pConfig;
int rc;
};
@@ -232647,8 +238418,12 @@ static int fts5ParseTokenize(
rc = SQLITE_NOMEM;
}else{
memset(pSyn, 0, (size_t)nByte);
- pSyn->zTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer);
- memcpy(pSyn->zTerm, pToken, nToken);
+ pSyn->pTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer);
+ pSyn->nFullTerm = pSyn->nQueryTerm = nToken;
+ if( pCtx->pConfig->bTokendata ){
+ pSyn->nQueryTerm = (int)strlen(pSyn->pTerm);
+ }
+ memcpy(pSyn->pTerm, pToken, nToken);
pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym;
pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn;
}
@@ -232673,7 +238448,11 @@ static int fts5ParseTokenize(
if( rc==SQLITE_OK ){
pTerm = &pPhrase->aTerm[pPhrase->nTerm++];
memset(pTerm, 0, sizeof(Fts5ExprTerm));
- pTerm->zTerm = sqlite3Fts5Strndup(&rc, pToken, nToken);
+ pTerm->pTerm = sqlite3Fts5Strndup(&rc, pToken, nToken);
+ pTerm->nFullTerm = pTerm->nQueryTerm = nToken;
+ if( pCtx->pConfig->bTokendata && rc==SQLITE_OK ){
+ pTerm->nQueryTerm = (int)strlen(pTerm->pTerm);
+ }
}
}
@@ -232740,6 +238519,7 @@ static Fts5ExprPhrase *sqlite3Fts5ParseTerm(
memset(&sCtx, 0, sizeof(TokenCtx));
sCtx.pPhrase = pAppend;
+ sCtx.pConfig = pConfig;
rc = fts5ParseStringFromToken(pToken, &z);
if( rc==SQLITE_OK ){
@@ -232787,12 +238567,15 @@ static int sqlite3Fts5ExprClonePhrase(
Fts5Expr **ppNew
){
int rc = SQLITE_OK; /* Return code */
- Fts5ExprPhrase *pOrig; /* The phrase extracted from pExpr */
+ Fts5ExprPhrase *pOrig = 0; /* The phrase extracted from pExpr */
Fts5Expr *pNew = 0; /* Expression to return via *ppNew */
- TokenCtx sCtx = {0,0}; /* Context object for fts5ParseTokenize */
-
- pOrig = pExpr->apExprPhrase[iPhrase];
- pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr));
+ TokenCtx sCtx = {0,0,0}; /* Context object for fts5ParseTokenize */
+ if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){
+ rc = SQLITE_RANGE;
+ }else{
+ pOrig = pExpr->apExprPhrase[iPhrase];
+ pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr));
+ }
if( rc==SQLITE_OK ){
pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc,
sizeof(Fts5ExprPhrase*));
@@ -232805,7 +238588,7 @@ static int sqlite3Fts5ExprClonePhrase(
pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc,
sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*));
}
- if( rc==SQLITE_OK ){
+ if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){
Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
if( pColsetOrig ){
sqlite3_int64 nByte;
@@ -232819,26 +238602,27 @@ static int sqlite3Fts5ExprClonePhrase(
}
}
- if( pOrig->nTerm ){
- int i; /* Used to iterate through phrase terms */
- for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){
- int tflags = 0;
- Fts5ExprTerm *p;
- for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){
- const char *zTerm = p->zTerm;
- rc = fts5ParseTokenize((void*)&sCtx, tflags, zTerm, (int)strlen(zTerm),
- 0, 0);
- tflags = FTS5_TOKEN_COLOCATED;
- }
- if( rc==SQLITE_OK ){
- sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
- sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst;
+ if( rc==SQLITE_OK ){
+ if( pOrig->nTerm ){
+ int i; /* Used to iterate through phrase terms */
+ sCtx.pConfig = pExpr->pConfig;
+ for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){
+ int tflags = 0;
+ Fts5ExprTerm *p;
+ for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){
+ rc = fts5ParseTokenize((void*)&sCtx,tflags,p->pTerm,p->nFullTerm,0,0);
+ tflags = FTS5_TOKEN_COLOCATED;
+ }
+ if( rc==SQLITE_OK ){
+ sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
+ sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst;
+ }
}
+ }else{
+ /* This happens when parsing a token or quoted phrase that contains
+ ** no token characters at all. (e.g ... MATCH '""'). */
+ sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));
}
- }else{
- /* This happens when parsing a token or quoted phrase that contains
- ** no token characters at all. (e.g ... MATCH '""'). */
- sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));
}
if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){
@@ -233208,11 +238992,13 @@ static Fts5ExprNode *fts5ParsePhraseToAnd(
if( parseGrowPhraseArray(pParse) ){
fts5ExprPhraseFree(pPhrase);
}else{
+ Fts5ExprTerm *p = &pNear->apPhrase[0]->aTerm[ii];
+ Fts5ExprTerm *pTo = &pPhrase->aTerm[0];
pParse->apPhrase[pParse->nPhrase++] = pPhrase;
pPhrase->nTerm = 1;
- pPhrase->aTerm[0].zTerm = sqlite3Fts5Strndup(
- &pParse->rc, pNear->apPhrase[0]->aTerm[ii].zTerm, -1
- );
+ pTo->pTerm = sqlite3Fts5Strndup(&pParse->rc, p->pTerm, p->nFullTerm);
+ pTo->nQueryTerm = p->nQueryTerm;
+ pTo->nFullTerm = p->nFullTerm;
pRet->apChild[ii] = sqlite3Fts5ParseNode(pParse, FTS5_STRING,
0, 0, sqlite3Fts5ParseNearset(pParse, 0, pPhrase)
);
@@ -233397,16 +239183,17 @@ static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){
/* Determine the maximum amount of space required. */
for(p=pTerm; p; p=p->pSynonym){
- nByte += (int)strlen(pTerm->zTerm) * 2 + 3 + 2;
+ nByte += pTerm->nQueryTerm * 2 + 3 + 2;
}
zQuoted = sqlite3_malloc64(nByte);
if( zQuoted ){
int i = 0;
for(p=pTerm; p; p=p->pSynonym){
- char *zIn = p->zTerm;
+ char *zIn = p->pTerm;
+ char *zEnd = &zIn[p->nQueryTerm];
zQuoted[i++] = '"';
- while( *zIn ){
+ while( zIn<zEnd ){
if( *zIn=='"' ) zQuoted[i++] = '"';
zQuoted[i++] = *zIn++;
}
@@ -233484,8 +239271,10 @@ static char *fts5ExprPrintTcl(
zRet = fts5PrintfAppend(zRet, " {");
for(iTerm=0; zRet && iTerm<pPhrase->nTerm; iTerm++){
- char *zTerm = pPhrase->aTerm[iTerm].zTerm;
- zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" ", zTerm);
+ Fts5ExprTerm *p = &pPhrase->aTerm[iTerm];
+ zRet = fts5PrintfAppend(zRet, "%s%.*s", iTerm==0?"":" ",
+ p->nQueryTerm, p->pTerm
+ );
if( pPhrase->aTerm[iTerm].bPrefix ){
zRet = fts5PrintfAppend(zRet, "*");
}
@@ -233886,6 +239675,17 @@ static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){
return 0;
}
+/*
+** pToken is a buffer nToken bytes in size that may or may not contain
+** an embedded 0x00 byte. If it does, return the number of bytes in
+** the buffer before the 0x00. If it does not, return nToken.
+*/
+static int fts5QueryTerm(const char *pToken, int nToken){
+ int ii;
+ for(ii=0; ii<nToken && pToken[ii]; ii++){}
+ return ii;
+}
+
static int fts5ExprPopulatePoslistsCb(
void *pCtx, /* Copy of 2nd argument to xTokenize() */
int tflags, /* Mask of FTS5_TOKEN_* flags */
@@ -233897,22 +239697,33 @@ static int fts5ExprPopulatePoslistsCb(
Fts5ExprCtx *p = (Fts5ExprCtx*)pCtx;
Fts5Expr *pExpr = p->pExpr;
int i;
+ int nQuery = nToken;
+ i64 iRowid = pExpr->pRoot->iRowid;
UNUSED_PARAM2(iUnused1, iUnused2);
- if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE;
+ if( nQuery>FTS5_MAX_TOKEN_SIZE ) nQuery = FTS5_MAX_TOKEN_SIZE;
+ if( pExpr->pConfig->bTokendata ){
+ nQuery = fts5QueryTerm(pToken, nQuery);
+ }
if( (tflags & FTS5_TOKEN_COLOCATED)==0 ) p->iOff++;
for(i=0; i<pExpr->nPhrase; i++){
- Fts5ExprTerm *pTerm;
+ Fts5ExprTerm *pT;
if( p->aPopulator[i].bOk==0 ) continue;
- for(pTerm=&pExpr->apExprPhrase[i]->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){
- int nTerm = (int)strlen(pTerm->zTerm);
- if( (nTerm==nToken || (nTerm<nToken && pTerm->bPrefix))
- && memcmp(pTerm->zTerm, pToken, nTerm)==0
+ for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){
+ if( (pT->nQueryTerm==nQuery || (pT->nQueryTerm<nQuery && pT->bPrefix))
+ && memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0
){
int rc = sqlite3Fts5PoslistWriterAppend(
&pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff
);
+ if( rc==SQLITE_OK && pExpr->pConfig->bTokendata && !pT->bPrefix ){
+ int iCol = p->iOff>>32;
+ int iTokOff = p->iOff & 0x7FFFFFFF;
+ rc = sqlite3Fts5IndexIterWriteTokendata(
+ pT->pIter, pToken, nToken, iRowid, iCol, iTokOff
+ );
+ }
if( rc ) return rc;
break;
}
@@ -234049,6 +239860,83 @@ static int sqlite3Fts5ExprPhraseCollist(
}
/*
+** Does the work of the fts5_api.xQueryToken() API method.
+*/
+static int sqlite3Fts5ExprQueryToken(
+ Fts5Expr *pExpr,
+ int iPhrase,
+ int iToken,
+ const char **ppOut,
+ int *pnOut
+){
+ Fts5ExprPhrase *pPhrase = 0;
+
+ if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){
+ return SQLITE_RANGE;
+ }
+ pPhrase = pExpr->apExprPhrase[iPhrase];
+ if( iToken<0 || iToken>=pPhrase->nTerm ){
+ return SQLITE_RANGE;
+ }
+
+ *ppOut = pPhrase->aTerm[iToken].pTerm;
+ *pnOut = pPhrase->aTerm[iToken].nFullTerm;
+ return SQLITE_OK;
+}
+
+/*
+** Does the work of the fts5_api.xInstToken() API method.
+*/
+static int sqlite3Fts5ExprInstToken(
+ Fts5Expr *pExpr,
+ i64 iRowid,
+ int iPhrase,
+ int iCol,
+ int iOff,
+ int iToken,
+ const char **ppOut,
+ int *pnOut
+){
+ Fts5ExprPhrase *pPhrase = 0;
+ Fts5ExprTerm *pTerm = 0;
+ int rc = SQLITE_OK;
+
+ if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){
+ return SQLITE_RANGE;
+ }
+ pPhrase = pExpr->apExprPhrase[iPhrase];
+ if( iToken<0 || iToken>=pPhrase->nTerm ){
+ return SQLITE_RANGE;
+ }
+ pTerm = &pPhrase->aTerm[iToken];
+ if( pTerm->bPrefix==0 ){
+ if( pExpr->pConfig->bTokendata ){
+ rc = sqlite3Fts5IterToken(
+ pTerm->pIter, iRowid, iCol, iOff+iToken, ppOut, pnOut
+ );
+ }else{
+ *ppOut = pTerm->pTerm;
+ *pnOut = pTerm->nFullTerm;
+ }
+ }
+ return rc;
+}
+
+/*
+** Clear the token mappings for all Fts5IndexIter objects mannaged by
+** the expression passed as the only argument.
+*/
+static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){
+ int ii;
+ for(ii=0; ii<pExpr->nPhrase; ii++){
+ Fts5ExprTerm *pT;
+ for(pT=&pExpr->apExprPhrase[ii]->aTerm[0]; pT; pT=pT->pSynonym){
+ sqlite3Fts5IndexIterClearTokendata(pT->pIter);
+ }
+ }
+}
+
+/*
** 2014 August 11
**
** The author disclaims copyright to this source code. In place of
@@ -234086,10 +239974,15 @@ struct Fts5Hash {
/*
** Each entry in the hash table is represented by an object of the
-** following type. Each object, its key (a nul-terminated string) and
-** its current data are stored in a single memory allocation. The
-** key immediately follows the object in memory. The position list
-** data immediately follows the key data in memory.
+** following type. Each object, its key, and its current data are stored
+** in a single memory allocation. The key immediately follows the object
+** in memory. The position list data immediately follows the key data
+** in memory.
+**
+** The key is Fts5HashEntry.nKey bytes in size. It consists of a single
+** byte identifying the index (either the main term index or a prefix-index),
+** followed by the term data. For example: "0token". There is no
+** nul-terminator - in this case nKey=6.
**
** The data that follows the key is in a similar, but not identical format
** to the doclist data stored in the database. It is:
@@ -234224,8 +240117,7 @@ static int fts5HashResize(Fts5Hash *pHash){
unsigned int iHash;
Fts5HashEntry *p = apOld[i];
apOld[i] = p->pHashNext;
- iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p),
- (int)strlen(fts5EntryKey(p)));
+ iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p), p->nKey);
p->pHashNext = apNew[iHash];
apNew[iHash] = p;
}
@@ -234309,7 +240201,7 @@ static int sqlite3Fts5HashWrite(
for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
char *zKey = fts5EntryKey(p);
if( zKey[0]==bByte
- && p->nKey==nToken
+ && p->nKey==nToken+1
&& memcmp(&zKey[1], pToken, nToken)==0
){
break;
@@ -234339,9 +240231,9 @@ static int sqlite3Fts5HashWrite(
zKey[0] = bByte;
memcpy(&zKey[1], pToken, nToken);
assert( iHash==fts5HashKey(pHash->nSlot, (u8*)zKey, nToken+1) );
- p->nKey = nToken;
+ p->nKey = nToken+1;
zKey[nToken+1] = '\0';
- p->nData = nToken+1 + 1 + sizeof(Fts5HashEntry);
+ p->nData = nToken+1 + sizeof(Fts5HashEntry);
p->pHashNext = pHash->aSlot[iHash];
pHash->aSlot[iHash] = p;
pHash->nEntry++;
@@ -234458,12 +240350,17 @@ static Fts5HashEntry *fts5HashEntryMerge(
*ppOut = p1;
p1 = 0;
}else{
- int i = 0;
char *zKey1 = fts5EntryKey(p1);
char *zKey2 = fts5EntryKey(p2);
- while( zKey1[i]==zKey2[i] ) i++;
+ int nMin = MIN(p1->nKey, p2->nKey);
- if( ((u8)zKey1[i])>((u8)zKey2[i]) ){
+ int cmp = memcmp(zKey1, zKey2, nMin);
+ if( cmp==0 ){
+ cmp = p1->nKey - p2->nKey;
+ }
+ assert( cmp!=0 );
+
+ if( cmp>0 ){
/* p2 is smaller */
*ppOut = p2;
ppOut = &p2->pScanNext;
@@ -234482,10 +240379,8 @@ static Fts5HashEntry *fts5HashEntryMerge(
}
/*
-** Extract all tokens from hash table iHash and link them into a list
-** in sorted order. The hash table is cleared before returning. It is
-** the responsibility of the caller to free the elements of the returned
-** list.
+** Link all tokens from hash table iHash into a list in sorted order. The
+** tokens are not removed from the hash table.
*/
static int fts5HashEntrySort(
Fts5Hash *pHash,
@@ -234507,7 +240402,7 @@ static int fts5HashEntrySort(
Fts5HashEntry *pIter;
for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){
if( pTerm==0
- || (pIter->nKey+1>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm))
+ || (pIter->nKey>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm))
){
Fts5HashEntry *pEntry = pIter;
pEntry->pScanNext = 0;
@@ -234546,12 +240441,11 @@ static int sqlite3Fts5HashQuery(
for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
zKey = fts5EntryKey(p);
- assert( p->nKey+1==(int)strlen(zKey) );
- if( nTerm==p->nKey+1 && memcmp(zKey, pTerm, nTerm)==0 ) break;
+ if( nTerm==p->nKey && memcmp(zKey, pTerm, nTerm)==0 ) break;
}
if( p ){
- int nHashPre = sizeof(Fts5HashEntry) + nTerm + 1;
+ int nHashPre = sizeof(Fts5HashEntry) + nTerm;
int nList = p->nData - nHashPre;
u8 *pRet = (u8*)(*ppOut = sqlite3_malloc64(nPre + nList + 10));
if( pRet ){
@@ -234612,19 +240506,22 @@ static int sqlite3Fts5HashScanEof(Fts5Hash *p){
static void sqlite3Fts5HashScanEntry(
Fts5Hash *pHash,
const char **pzTerm, /* OUT: term (nul-terminated) */
+ int *pnTerm, /* OUT: Size of term in bytes */
const u8 **ppDoclist, /* OUT: pointer to doclist */
int *pnDoclist /* OUT: size of doclist in bytes */
){
Fts5HashEntry *p;
if( (p = pHash->pScan) ){
char *zKey = fts5EntryKey(p);
- int nTerm = (int)strlen(zKey);
+ int nTerm = p->nKey;
fts5HashAddPoslistSize(pHash, p, 0);
*pzTerm = zKey;
- *ppDoclist = (const u8*)&zKey[nTerm+1];
- *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1);
+ *pnTerm = nTerm;
+ *ppDoclist = (const u8*)&zKey[nTerm];
+ *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm);
}else{
*pzTerm = 0;
+ *pnTerm = 0;
*ppDoclist = 0;
*pnDoclist = 0;
}
@@ -234955,6 +240852,9 @@ typedef struct Fts5SegWriter Fts5SegWriter;
typedef struct Fts5Structure Fts5Structure;
typedef struct Fts5StructureLevel Fts5StructureLevel;
typedef struct Fts5StructureSegment Fts5StructureSegment;
+typedef struct Fts5TokenDataIter Fts5TokenDataIter;
+typedef struct Fts5TokenDataMap Fts5TokenDataMap;
+typedef struct Fts5TombstoneArray Fts5TombstoneArray;
struct Fts5Data {
u8 *p; /* Pointer to buffer containing record */
@@ -234989,6 +240889,7 @@ struct Fts5Index {
/* Error state. */
int rc; /* Current error code */
+ int flushRc;
/* State used by the fts5DataXXX() functions. */
sqlite3_blob *pReader; /* RO incr-blob open on %_data table */
@@ -234997,6 +240898,7 @@ struct Fts5Index {
sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */
sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=?" */
sqlite3_stmt *pIdxSelect;
+ sqlite3_stmt *pIdxNextSelect;
int nRead; /* Total number of blocks read */
sqlite3_stmt *pDeleteFromIdx;
@@ -235150,8 +241052,7 @@ struct Fts5SegIter {
Fts5Data *pLeaf; /* Current leaf data */
Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */
i64 iLeafOffset; /* Byte offset within current leaf */
- Fts5Data **apTombstone; /* Array of tombstone pages */
- int nTombstone;
+ Fts5TombstoneArray *pTombArray; /* Array of tombstone pages */
/* Next method */
void (*xNext)(Fts5Index*, Fts5SegIter*, int*);
@@ -235179,6 +241080,15 @@ struct Fts5SegIter {
};
/*
+** Array of tombstone pages. Reference counted.
+*/
+struct Fts5TombstoneArray {
+ int nRef; /* Number of pointers to this object */
+ int nTombstone;
+ Fts5Data *apTombstone[1]; /* Array of tombstone pages */
+};
+
+/*
** Argument is a pointer to an Fts5Data structure that contains a
** leaf page.
*/
@@ -235222,9 +241132,16 @@ struct Fts5SegIter {
** poslist:
** Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered.
** There is no way to tell if this is populated or not.
+**
+** pColset:
+** If not NULL, points to an object containing a set of column indices.
+** Only matches that occur in one of these columns will be returned.
+** The Fts5Iter does not own the Fts5Colset object, and so it is not
+** freed when the iterator is closed - it is owned by the upper layer.
*/
struct Fts5Iter {
Fts5IndexIter base; /* Base class containing output vars */
+ Fts5TokenDataIter *pTokenDataIter;
Fts5Index *pIndex; /* Index that owns this iterator */
Fts5Buffer poslist; /* Buffer containing current poslist */
@@ -235242,7 +241159,6 @@ struct Fts5Iter {
Fts5SegIter aSeg[1]; /* Array of segment iterators */
};
-
/*
** An instance of the following type is used to iterate through the contents
** of a doclist-index record.
@@ -236160,9 +242076,9 @@ static int fts5DlidxLvlNext(Fts5DlidxLvl *pLvl){
}
if( iOff<pData->nn ){
- i64 iVal;
+ u64 iVal;
pLvl->iLeafPgno += (iOff - pLvl->iOff) + 1;
- iOff += fts5GetVarint(&pData->p[iOff], (u64*)&iVal);
+ iOff += fts5GetVarint(&pData->p[iOff], &iVal);
pLvl->iRowid += iVal;
pLvl->iOff = iOff;
}else{
@@ -236541,18 +242457,20 @@ static void fts5SegIterSetNext(Fts5Index *p, Fts5SegIter *pIter){
}
/*
-** Allocate a tombstone hash page array (pIter->apTombstone) for the
-** iterator passed as the second argument. If an OOM error occurs, leave
-** an error in the Fts5Index object.
+** Allocate a tombstone hash page array object (pIter->pTombArray) for
+** the iterator passed as the second argument. If an OOM error occurs,
+** leave an error in the Fts5Index object.
*/
static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){
const int nTomb = pIter->pSeg->nPgTombstone;
if( nTomb>0 ){
- Fts5Data **apTomb = 0;
- apTomb = (Fts5Data**)sqlite3Fts5MallocZero(&p->rc, sizeof(Fts5Data)*nTomb);
- if( apTomb ){
- pIter->apTombstone = apTomb;
- pIter->nTombstone = nTomb;
+ int nByte = nTomb * sizeof(Fts5Data*) + sizeof(Fts5TombstoneArray);
+ Fts5TombstoneArray *pNew;
+ pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte);
+ if( pNew ){
+ pNew->nTombstone = nTomb;
+ pNew->nRef = 1;
+ pIter->pTombArray = pNew;
}
}
}
@@ -236809,15 +242727,16 @@ static void fts5SegIterNext_None(
}else{
const u8 *pList = 0;
const char *zTerm = 0;
+ int nTerm = 0;
int nList;
sqlite3Fts5HashScanNext(p->pHash);
- sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList);
+ sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList);
if( pList==0 ) goto next_none_eof;
pIter->pLeaf->p = (u8*)pList;
pIter->pLeaf->nn = nList;
pIter->pLeaf->szLeaf = nList;
pIter->iEndofDoclist = nList;
- sqlite3Fts5BufferSet(&p->rc,&pIter->term, (int)strlen(zTerm), (u8*)zTerm);
+ sqlite3Fts5BufferSet(&p->rc,&pIter->term, nTerm, (u8*)zTerm);
pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
}
@@ -236883,11 +242802,12 @@ static void fts5SegIterNext(
}else if( pIter->pSeg==0 ){
const u8 *pList = 0;
const char *zTerm = 0;
+ int nTerm = 0;
int nList = 0;
assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm );
if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){
sqlite3Fts5HashScanNext(p->pHash);
- sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList);
+ sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList);
}
if( pList==0 ){
fts5DataRelease(pIter->pLeaf);
@@ -236897,8 +242817,7 @@ static void fts5SegIterNext(
pIter->pLeaf->nn = nList;
pIter->pLeaf->szLeaf = nList;
pIter->iEndofDoclist = nList+1;
- sqlite3Fts5BufferSet(&p->rc, &pIter->term, (int)strlen(zTerm),
- (u8*)zTerm);
+ sqlite3Fts5BufferSet(&p->rc, &pIter->term, nTerm, (u8*)zTerm);
pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
*pbNewTerm = 1;
}
@@ -237284,7 +243203,7 @@ static void fts5SegIterSeekInit(
fts5LeafSeek(p, bGe, pIter, pTerm, nTerm);
}
- if( p->rc==SQLITE_OK && bGe==0 ){
+ if( p->rc==SQLITE_OK && (bGe==0 || (flags & FTS5INDEX_QUERY_SCANONETERM)) ){
pIter->flags |= FTS5_SEGITER_ONETERM;
if( pIter->pLeaf ){
if( flags & FTS5INDEX_QUERY_DESC ){
@@ -237300,7 +243219,9 @@ static void fts5SegIterSeekInit(
}
fts5SegIterSetNext(p, pIter);
- fts5SegIterAllocTombstone(p, pIter);
+ if( 0==(flags & FTS5INDEX_QUERY_SCANONETERM) ){
+ fts5SegIterAllocTombstone(p, pIter);
+ }
/* Either:
**
@@ -237317,6 +243238,79 @@ static void fts5SegIterSeekInit(
);
}
+
+/*
+** SQL used by fts5SegIterNextInit() to find the page to open.
+*/
+static sqlite3_stmt *fts5IdxNextStmt(Fts5Index *p){
+ if( p->pIdxNextSelect==0 ){
+ Fts5Config *pConfig = p->pConfig;
+ fts5IndexPrepareStmt(p, &p->pIdxNextSelect, sqlite3_mprintf(
+ "SELECT pgno FROM '%q'.'%q_idx' WHERE "
+ "segid=? AND term>? ORDER BY term ASC LIMIT 1",
+ pConfig->zDb, pConfig->zName
+ ));
+
+ }
+ return p->pIdxNextSelect;
+}
+
+/*
+** This is similar to fts5SegIterSeekInit(), except that it initializes
+** the segment iterator to point to the first term following the page
+** with pToken/nToken on it.
+*/
+static void fts5SegIterNextInit(
+ Fts5Index *p,
+ const char *pTerm, int nTerm,
+ Fts5StructureSegment *pSeg, /* Description of segment */
+ Fts5SegIter *pIter /* Object to populate */
+){
+ int iPg = -1; /* Page of segment to open */
+ int bDlidx = 0;
+ sqlite3_stmt *pSel = 0; /* SELECT to find iPg */
+
+ pSel = fts5IdxNextStmt(p);
+ if( pSel ){
+ assert( p->rc==SQLITE_OK );
+ sqlite3_bind_int(pSel, 1, pSeg->iSegid);
+ sqlite3_bind_blob(pSel, 2, pTerm, nTerm, SQLITE_STATIC);
+
+ if( sqlite3_step(pSel)==SQLITE_ROW ){
+ i64 val = sqlite3_column_int64(pSel, 0);
+ iPg = (int)(val>>1);
+ bDlidx = (val & 0x0001);
+ }
+ p->rc = sqlite3_reset(pSel);
+ sqlite3_bind_null(pSel, 2);
+ if( p->rc ) return;
+ }
+
+ memset(pIter, 0, sizeof(*pIter));
+ pIter->pSeg = pSeg;
+ pIter->flags |= FTS5_SEGITER_ONETERM;
+ if( iPg>=0 ){
+ pIter->iLeafPgno = iPg - 1;
+ fts5SegIterNextPage(p, pIter);
+ fts5SegIterSetNext(p, pIter);
+ }
+ if( pIter->pLeaf ){
+ const u8 *a = pIter->pLeaf->p;
+ int iTermOff = 0;
+
+ pIter->iPgidxOff = pIter->pLeaf->szLeaf;
+ pIter->iPgidxOff += fts5GetVarint32(&a[pIter->iPgidxOff], iTermOff);
+ pIter->iLeafOffset = iTermOff;
+ fts5SegIterLoadTerm(p, pIter, 0);
+ fts5SegIterLoadNPos(p, pIter);
+ if( bDlidx ) fts5SegIterLoadDlidx(p, pIter);
+
+ assert( p->rc!=SQLITE_OK ||
+ fts5BufferCompareBlob(&pIter->term, (const u8*)pTerm, nTerm)>0
+ );
+ }
+}
+
/*
** Initialize the object pIter to point to term pTerm/nTerm within the
** in-memory hash table. If there is no such term in the hash-table, the
@@ -237343,14 +243337,21 @@ static void fts5SegIterHashInit(
const u8 *pList = 0;
p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm);
- sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &pList, &nList);
- n = (z ? (int)strlen((const char*)z) : 0);
+ sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &n, &pList, &nList);
if( pList ){
pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data));
if( pLeaf ){
pLeaf->p = (u8*)pList;
}
}
+
+ /* The call to sqlite3Fts5HashScanInit() causes the hash table to
+ ** fill the size field of all existing position lists. This means they
+ ** can no longer be appended to. Since the only scenario in which they
+ ** can be appended to is if the previous operation on this table was
+ ** a DELETE, by clearing the Fts5Index.bDelete flag we can avoid this
+ ** possibility altogether. */
+ p->bDelete = 0;
}else{
p->rc = sqlite3Fts5HashQuery(p->pHash, sizeof(Fts5Data),
(const char*)pTerm, nTerm, (void**)&pLeaf, &nList
@@ -237396,13 +243397,30 @@ static void fts5IndexFreeArray(Fts5Data **ap, int n){
}
/*
+** Decrement the ref-count of the object passed as the only argument. If it
+** reaches 0, free it and its contents.
+*/
+static void fts5TombstoneArrayDelete(Fts5TombstoneArray *p){
+ if( p ){
+ p->nRef--;
+ if( p->nRef<=0 ){
+ int ii;
+ for(ii=0; ii<p->nTombstone; ii++){
+ fts5DataRelease(p->apTombstone[ii]);
+ }
+ sqlite3_free(p);
+ }
+ }
+}
+
+/*
** Zero the iterator passed as the only argument.
*/
static void fts5SegIterClear(Fts5SegIter *pIter){
fts5BufferFree(&pIter->term);
fts5DataRelease(pIter->pLeaf);
fts5DataRelease(pIter->pNextLeaf);
- fts5IndexFreeArray(pIter->apTombstone, pIter->nTombstone);
+ fts5TombstoneArrayDelete(pIter->pTombArray);
fts5DlidxIterFree(pIter->pDlidx);
sqlite3_free(pIter->aRowidOffset);
memset(pIter, 0, sizeof(Fts5SegIter));
@@ -237536,7 +243554,6 @@ static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){
assert_nc( i2!=0 );
pRes->bTermEq = 1;
if( p1->iRowid==p2->iRowid ){
- p1->bDel = p2->bDel;
return i2;
}
res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : +1;
@@ -237648,7 +243665,6 @@ static void fts5SegIterNextFrom(
}while( p->rc==SQLITE_OK );
}
-
/*
** Free the iterator object passed as the second argument.
*/
@@ -237793,24 +243809,25 @@ static int fts5IndexTombstoneQuery(
static int fts5MultiIterIsDeleted(Fts5Iter *pIter){
int iFirst = pIter->aFirst[1].iFirst;
Fts5SegIter *pSeg = &pIter->aSeg[iFirst];
+ Fts5TombstoneArray *pArray = pSeg->pTombArray;
- if( pSeg->pLeaf && pSeg->nTombstone ){
+ if( pSeg->pLeaf && pArray ){
/* Figure out which page the rowid might be present on. */
- int iPg = ((u64)pSeg->iRowid) % pSeg->nTombstone;
+ int iPg = ((u64)pSeg->iRowid) % pArray->nTombstone;
assert( iPg>=0 );
/* If tombstone hash page iPg has not yet been loaded from the
** database, load it now. */
- if( pSeg->apTombstone[iPg]==0 ){
- pSeg->apTombstone[iPg] = fts5DataRead(pIter->pIndex,
+ if( pArray->apTombstone[iPg]==0 ){
+ pArray->apTombstone[iPg] = fts5DataRead(pIter->pIndex,
FTS5_TOMBSTONE_ROWID(pSeg->pSeg->iSegid, iPg)
);
- if( pSeg->apTombstone[iPg]==0 ) return 0;
+ if( pArray->apTombstone[iPg]==0 ) return 0;
}
return fts5IndexTombstoneQuery(
- pSeg->apTombstone[iPg],
- pSeg->nTombstone,
+ pArray->apTombstone[iPg],
+ pArray->nTombstone,
pSeg->iRowid
);
}
@@ -237904,7 +243921,7 @@ static Fts5Iter *fts5MultiIterAlloc(
int nSeg
){
Fts5Iter *pNew;
- int nSlot; /* Power of two >= nSeg */
+ i64 nSlot; /* Power of two >= nSeg */
for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2);
pNew = fts5IdxMalloc(p,
@@ -238349,6 +244366,32 @@ static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){
}
}
+/*
+** All the component segment-iterators of pIter have been set up. This
+** functions finishes setup for iterator pIter itself.
+*/
+static void fts5MultiIterFinishSetup(Fts5Index *p, Fts5Iter *pIter){
+ int iIter;
+ for(iIter=pIter->nSeg-1; iIter>0; iIter--){
+ int iEq;
+ if( (iEq = fts5MultiIterDoCompare(pIter, iIter)) ){
+ Fts5SegIter *pSeg = &pIter->aSeg[iEq];
+ if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0);
+ fts5MultiIterAdvanced(p, pIter, iEq, iIter);
+ }
+ }
+ fts5MultiIterSetEof(pIter);
+ fts5AssertMultiIterSetup(p, pIter);
+
+ if( (pIter->bSkipEmpty && fts5MultiIterIsEmpty(p, pIter))
+ || fts5MultiIterIsDeleted(pIter)
+ ){
+ fts5MultiIterNext(p, pIter, 0, 0);
+ }else if( pIter->base.bEof==0 ){
+ Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst];
+ pIter->xSetOutputs(pIter, pSeg);
+ }
+}
/*
** Allocate a new Fts5Iter object.
@@ -238430,31 +244473,12 @@ static void fts5MultiIterNew(
assert( iIter==nSeg );
}
- /* If the above was successful, each component iterators now points
+ /* If the above was successful, each component iterator now points
** to the first entry in its segment. In this case initialize the
** aFirst[] array. Or, if an error has occurred, free the iterator
** object and set the output variable to NULL. */
if( p->rc==SQLITE_OK ){
- for(iIter=pNew->nSeg-1; iIter>0; iIter--){
- int iEq;
- if( (iEq = fts5MultiIterDoCompare(pNew, iIter)) ){
- Fts5SegIter *pSeg = &pNew->aSeg[iEq];
- if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0);
- fts5MultiIterAdvanced(p, pNew, iEq, iIter);
- }
- }
- fts5MultiIterSetEof(pNew);
- fts5AssertMultiIterSetup(p, pNew);
-
- if( (pNew->bSkipEmpty && fts5MultiIterIsEmpty(p, pNew))
- || fts5MultiIterIsDeleted(pNew)
- ){
- fts5MultiIterNext(p, pNew, 0, 0);
- }else if( pNew->base.bEof==0 ){
- Fts5SegIter *pSeg = &pNew->aSeg[pNew->aFirst[1].iFirst];
- pNew->xSetOutputs(pNew, pSeg);
- }
-
+ fts5MultiIterFinishSetup(p, pNew);
}else{
fts5MultiIterFree(pNew);
*ppOut = 0;
@@ -238479,7 +244503,6 @@ static void fts5MultiIterNew2(
pNew = fts5MultiIterAlloc(p, 2);
if( pNew ){
Fts5SegIter *pIter = &pNew->aSeg[1];
-
pIter->flags = FTS5_SEGITER_ONETERM;
if( pData->szLeaf>0 ){
pIter->pLeaf = pData;
@@ -238627,6 +244650,7 @@ static void fts5IndexDiscardData(Fts5Index *p){
sqlite3Fts5HashClear(p->pHash);
p->nPendingData = 0;
p->nPendingRow = 0;
+ p->flushRc = SQLITE_OK;
}
p->nContentlessDelete = 0;
}
@@ -238842,7 +244866,7 @@ static void fts5WriteDlidxAppend(
}
if( pDlidx->bPrevValid ){
- iVal = iRowid - pDlidx->iPrev;
+ iVal = (u64)iRowid - (u64)pDlidx->iPrev;
}else{
i64 iPgno = (i==0 ? pWriter->writer.pgno : pDlidx[-1].pgno);
assert( pDlidx->buf.n==0 );
@@ -239029,7 +245053,7 @@ static void fts5WriteAppendPoslistData(
const u8 *a = aData;
int n = nData;
- assert( p->pConfig->pgsz>0 );
+ assert( p->pConfig->pgsz>0 || p->rc!=SQLITE_OK );
while( p->rc==SQLITE_OK
&& (pPage->buf.n + pPage->pgidx.n + n)>=p->pConfig->pgsz
){
@@ -239680,7 +245704,6 @@ static void fts5DoSecureDelete(
int iPgIdx = pSeg->pLeaf->szLeaf;
u64 iDelta = 0;
- u64 iNextDelta = 0;
int iNextOff = 0;
int iOff = 0;
int nIdx = 0;
@@ -239688,8 +245711,6 @@ static void fts5DoSecureDelete(
int bLastInDoclist = 0;
int iIdx = 0;
int iStart = 0;
- int iKeyOff = 0;
- int iPrevKeyOff = 0;
int iDelKeyOff = 0; /* Offset of deleted key, if any */
nIdx = nPg-iPgIdx;
@@ -239714,10 +245735,21 @@ static void fts5DoSecureDelete(
** This block sets the following variables:
**
** iStart:
+ ** The offset of the first byte of the rowid or delta-rowid
+ ** value for the doclist entry being removed.
+ **
** iDelta:
+ ** The value of the rowid or delta-rowid value for the doclist
+ ** entry being removed.
+ **
+ ** iNextOff:
+ ** The offset of the next entry following the position list
+ ** for the one being removed. If the position list for this
+ ** entry overflows onto the next leaf page, this value will be
+ ** greater than pLeaf->szLeaf.
*/
{
- int iSOP;
+ int iSOP; /* Start-Of-Position-list */
if( pSeg->iLeafPgno==pSeg->iTermLeafPgno ){
iStart = pSeg->iTermLeafOffset;
}else{
@@ -239753,47 +245785,81 @@ static void fts5DoSecureDelete(
}
iOff = iStart;
+
+ /* If the position-list for the entry being removed flows over past
+ ** the end of this page, delete the portion of the position-list on the
+ ** next page and beyond.
+ **
+ ** Set variable bLastInDoclist to true if this entry happens
+ ** to be the last rowid in the doclist for its term. */
if( iNextOff>=iPgIdx ){
int pgno = pSeg->iLeafPgno+1;
fts5SecureDeleteOverflow(p, pSeg->pSeg, pgno, &bLastInDoclist);
iNextOff = iPgIdx;
- }else{
- /* Set bLastInDoclist to true if the entry being removed is the last
- ** in its doclist. */
- for(iIdx=0, iKeyOff=0; iIdx<nIdx; /* no-op */){
- u32 iVal = 0;
- iIdx += fts5GetVarint32(&aIdx[iIdx], iVal);
- iKeyOff += iVal;
- if( iKeyOff==iNextOff ){
- bLastInDoclist = 1;
+ }
+
+ if( pSeg->bDel==0 ){
+ if( iNextOff!=iPgIdx ){
+ /* Loop through the page-footer. If iNextOff (offset of the
+ ** entry following the one we are removing) is equal to the
+ ** offset of a key on this page, then the entry is the last
+ ** in its doclist. */
+ int iKeyOff = 0;
+ for(iIdx=0; iIdx<nIdx; /* no-op */){
+ u32 iVal = 0;
+ iIdx += fts5GetVarint32(&aIdx[iIdx], iVal);
+ iKeyOff += iVal;
+ if( iKeyOff==iNextOff ){
+ bLastInDoclist = 1;
+ }
}
}
- }
- if( fts5GetU16(&aPg[0])==iStart && (bLastInDoclist||iNextOff==iPgIdx) ){
- fts5PutU16(&aPg[0], 0);
+ /* If this is (a) the first rowid on a page and (b) is not followed by
+ ** another position list on the same page, set the "first-rowid" field
+ ** of the header to 0. */
+ if( fts5GetU16(&aPg[0])==iStart && (bLastInDoclist || iNextOff==iPgIdx) ){
+ fts5PutU16(&aPg[0], 0);
+ }
}
- if( bLastInDoclist==0 ){
+ if( pSeg->bDel ){
+ iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta);
+ aPg[iOff++] = 0x01;
+ }else if( bLastInDoclist==0 ){
if( iNextOff!=iPgIdx ){
+ u64 iNextDelta = 0;
iNextOff += fts5GetVarint(&aPg[iNextOff], &iNextDelta);
iOff += sqlite3Fts5PutVarint(&aPg[iOff], iDelta + iNextDelta);
}
}else if(
- iStart==pSeg->iTermLeafOffset && pSeg->iLeafPgno==pSeg->iTermLeafPgno
+ pSeg->iLeafPgno==pSeg->iTermLeafPgno
+ && iStart==pSeg->iTermLeafOffset
){
/* The entry being removed was the only position list in its
** doclist. Therefore the term needs to be removed as well. */
int iKey = 0;
- for(iIdx=0, iKeyOff=0; iIdx<nIdx; iKey++){
+ int iKeyOff = 0;
+
+ /* Set iKeyOff to the offset of the term that will be removed - the
+ ** last offset in the footer that is not greater than iStart. */
+ for(iIdx=0; iIdx<nIdx; iKey++){
u32 iVal = 0;
iIdx += fts5GetVarint32(&aIdx[iIdx], iVal);
if( (iKeyOff+iVal)>(u32)iStart ) break;
iKeyOff += iVal;
}
+ assert_nc( iKey>=1 );
+ /* Set iDelKeyOff to the value of the footer entry to remove from
+ ** the page. */
iDelKeyOff = iOff = iKeyOff;
+
if( iNextOff!=iPgIdx ){
+ /* This is the only position-list associated with the term, and there
+ ** is another term following it on this page. So the subsequent term
+ ** needs to be moved to replace the term associated with the entry
+ ** being removed. */
int nPrefix = 0;
int nSuffix = 0;
int nPrefix2 = 0;
@@ -239872,6 +245938,15 @@ static void fts5DoSecureDelete(
}
}
+ /* Assuming no error has occurred, this block does final edits to the
+ ** leaf page before writing it back to disk. Input variables are:
+ **
+ ** nPg: Total initial size of leaf page.
+ ** iPgIdx: Initial offset of page footer.
+ **
+ ** iOff: Offset to move data to
+ ** iNextOff: Offset to move data from
+ */
if( p->rc==SQLITE_OK ){
const int nMove = nPg - iNextOff; /* Number of bytes to move */
int nShift = iNextOff - iOff; /* Distance to move them */
@@ -239914,10 +245989,10 @@ static void fts5FlushSecureDelete(
Fts5Index *p,
Fts5Structure *pStruct,
const char *zTerm,
+ int nTerm,
i64 iRowid
){
const int f = FTS5INDEX_QUERY_SKIPHASH;
- int nTerm = (int)strlen(zTerm);
Fts5Iter *pIter = 0; /* Used to find term instance */
fts5MultiIterNew(p, pStruct, f, 0, (const u8*)zTerm, nTerm, -1, 0, &pIter);
@@ -239991,8 +246066,7 @@ static void fts5FlushOneHash(Fts5Index *p){
int nDoclist; /* Size of doclist in bytes */
/* Get the term and doclist for this entry. */
- sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist);
- nTerm = (int)strlen(zTerm);
+ sqlite3Fts5HashScanEntry(pHash, &zTerm, &nTerm, &pDoclist, &nDoclist);
if( bSecureDelete==0 ){
fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm);
if( p->rc!=SQLITE_OK ) break;
@@ -240022,7 +246096,7 @@ static void fts5FlushOneHash(Fts5Index *p){
if( bSecureDelete ){
if( eDetail==FTS5_DETAIL_NONE ){
if( iOff<nDoclist && pDoclist[iOff]==0x00 ){
- fts5FlushSecureDelete(p, pStruct, zTerm, iRowid);
+ fts5FlushSecureDelete(p, pStruct, zTerm, nTerm, iRowid);
iOff++;
if( iOff<nDoclist && pDoclist[iOff]==0x00 ){
iOff++;
@@ -240032,7 +246106,7 @@ static void fts5FlushOneHash(Fts5Index *p){
}
}
}else if( (pDoclist[iOff] & 0x01) ){
- fts5FlushSecureDelete(p, pStruct, zTerm, iRowid);
+ fts5FlushSecureDelete(p, pStruct, zTerm, nTerm, iRowid);
if( p->rc!=SQLITE_OK || pDoclist[iOff]==0x01 ){
iOff++;
continue;
@@ -240072,10 +246146,16 @@ static void fts5FlushOneHash(Fts5Index *p){
fts5WriteFlushLeaf(p, &writer);
}
}else{
- int bDummy;
- int nPos;
- int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy);
- nCopy += nPos;
+ int bDel = 0;
+ int nPos = 0;
+ int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDel);
+ if( bDel && bSecureDelete ){
+ fts5BufferAppendVarint(&p->rc, pBuf, nPos*2);
+ iOff += nCopy;
+ nCopy = nPos;
+ }else{
+ nCopy += nPos;
+ }
if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){
/* The entire poslist will fit on the current leaf. So copy
** it in one go. */
@@ -240113,7 +246193,6 @@ static void fts5FlushOneHash(Fts5Index *p){
assert( pBuf->n<=pBuf->nSpace );
if( p->rc==SQLITE_OK ) sqlite3Fts5HashScanNext(pHash);
}
- sqlite3Fts5HashClear(pHash);
fts5WriteFinish(p, &writer, &pgnoLast);
assert( p->rc!=SQLITE_OK || bSecureDelete || pgnoLast>0 );
@@ -240146,7 +246225,6 @@ static void fts5FlushOneHash(Fts5Index *p){
fts5IndexCrisismerge(p, &pStruct);
fts5StructureWrite(p, pStruct);
fts5StructureRelease(pStruct);
- p->nContentlessDelete = 0;
}
/*
@@ -240154,11 +246232,21 @@ static void fts5FlushOneHash(Fts5Index *p){
*/
static void fts5IndexFlush(Fts5Index *p){
/* Unless it is empty, flush the hash table to disk */
+ if( p->flushRc ){
+ p->rc = p->flushRc;
+ return;
+ }
if( p->nPendingData || p->nContentlessDelete ){
assert( p->pHash );
fts5FlushOneHash(p);
- p->nPendingData = 0;
- p->nPendingRow = 0;
+ if( p->rc==SQLITE_OK ){
+ sqlite3Fts5HashClear(p->pHash);
+ p->nPendingData = 0;
+ p->nPendingRow = 0;
+ p->nContentlessDelete = 0;
+ }else if( p->nPendingData || p->nContentlessDelete ){
+ p->flushRc = p->rc;
+ }
}
}
@@ -240236,8 +246324,9 @@ static int sqlite3Fts5IndexOptimize(Fts5Index *p){
assert( p->rc==SQLITE_OK );
fts5IndexFlush(p);
- assert( p->nContentlessDelete==0 );
+ assert( p->rc!=SQLITE_OK || p->nContentlessDelete==0 );
pStruct = fts5StructureRead(p);
+ assert( p->rc!=SQLITE_OK || pStruct!=0 );
fts5StructureInvalidate(p);
if( pStruct ){
@@ -240643,7 +246732,7 @@ static void fts5SetupPrefixIter(
u8 *pToken, /* Buffer containing prefix to match */
int nToken, /* Size of buffer pToken in bytes */
Fts5Colset *pColset, /* Restrict matches to these columns */
- Fts5Iter **ppIter /* OUT: New iterator */
+ Fts5Iter **ppIter /* OUT: New iterator */
){
Fts5Structure *pStruct;
Fts5Buffer *aBuf;
@@ -240664,8 +246753,9 @@ static void fts5SetupPrefixIter(
aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf);
pStruct = fts5StructureRead(p);
+ assert( p->rc!=SQLITE_OK || (aBuf && pStruct) );
- if( aBuf && pStruct ){
+ if( p->rc==SQLITE_OK ){
const int flags = FTS5INDEX_QUERY_SCAN
| FTS5INDEX_QUERY_SKIPEMPTY
| FTS5INDEX_QUERY_NOOUTPUT;
@@ -240677,6 +246767,12 @@ static void fts5SetupPrefixIter(
int bNewTerm = 1;
memset(&doclist, 0, sizeof(doclist));
+
+ /* If iIdx is non-zero, then it is the number of a prefix-index for
+ ** prefixes 1 character longer than the prefix being queried for. That
+ ** index contains all the doclists required, except for the one
+ ** corresponding to the prefix itself. That one is extracted from the
+ ** main term index here. */
if( iIdx!=0 ){
int dummy = 0;
const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT;
@@ -240700,6 +246796,7 @@ static void fts5SetupPrefixIter(
pToken[0] = FTS5_MAIN_PREFIX + iIdx;
fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
fts5IterSetOutputCb(&p->rc, p1);
+
for( /* no-op */ ;
fts5MultiIterEof(p, p1)==0;
fts5MultiIterNext2(p, p1, &bNewTerm)
@@ -240715,7 +246812,6 @@ static void fts5SetupPrefixIter(
}
if( p1->base.nData==0 ) continue;
-
if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){
for(i=0; p->rc==SQLITE_OK && doclist.n; i++){
int i1 = i*nMerge;
@@ -240754,7 +246850,7 @@ static void fts5SetupPrefixIter(
}
fts5MultiIterFree(p1);
- pData = fts5IdxMalloc(p, sizeof(Fts5Data)+doclist.n+FTS5_DATA_ZERO_PADDING);
+ pData = fts5IdxMalloc(p, sizeof(*pData)+doclist.n+FTS5_DATA_ZERO_PADDING);
if( pData ){
pData->p = (u8*)&pData[1];
pData->nn = pData->szLeaf = doclist.n;
@@ -240897,6 +246993,7 @@ static int sqlite3Fts5IndexClose(Fts5Index *p){
sqlite3_finalize(p->pIdxWriter);
sqlite3_finalize(p->pIdxDeleter);
sqlite3_finalize(p->pIdxSelect);
+ sqlite3_finalize(p->pIdxNextSelect);
sqlite3_finalize(p->pDataVersion);
sqlite3_finalize(p->pDeleteFromIdx);
sqlite3Fts5HashFree(p->pHash);
@@ -240993,6 +247090,457 @@ static int sqlite3Fts5IndexWrite(
}
/*
+** pToken points to a buffer of size nToken bytes containing a search
+** term, including the index number at the start, used on a tokendata=1
+** table. This function returns true if the term in buffer pBuf matches
+** token pToken/nToken.
+*/
+static int fts5IsTokendataPrefix(
+ Fts5Buffer *pBuf,
+ const u8 *pToken,
+ int nToken
+){
+ return (
+ pBuf->n>=nToken
+ && 0==memcmp(pBuf->p, pToken, nToken)
+ && (pBuf->n==nToken || pBuf->p[nToken]==0x00)
+ );
+}
+
+/*
+** Ensure the segment-iterator passed as the only argument points to EOF.
+*/
+static void fts5SegIterSetEOF(Fts5SegIter *pSeg){
+ fts5DataRelease(pSeg->pLeaf);
+ pSeg->pLeaf = 0;
+}
+
+/*
+** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an
+** array of these for each row it visits. Or, for an iterator used by an
+** "ORDER BY rank" query, it accumulates an array of these for the entire
+** query.
+**
+** Each instance in the array indicates the iterator (and therefore term)
+** associated with position iPos of rowid iRowid. This is used by the
+** xInstToken() API.
+*/
+struct Fts5TokenDataMap {
+ i64 iRowid; /* Row this token is located in */
+ i64 iPos; /* Position of token */
+ int iIter; /* Iterator token was read from */
+};
+
+/*
+** An object used to supplement Fts5Iter for tokendata=1 iterators.
+*/
+struct Fts5TokenDataIter {
+ int nIter;
+ int nIterAlloc;
+
+ int nMap;
+ int nMapAlloc;
+ Fts5TokenDataMap *aMap;
+
+ Fts5PoslistReader *aPoslistReader;
+ int *aPoslistToIter;
+ Fts5Iter *apIter[1];
+};
+
+/*
+** This function appends iterator pAppend to Fts5TokenDataIter pIn and
+** returns the result.
+*/
+static Fts5TokenDataIter *fts5AppendTokendataIter(
+ Fts5Index *p, /* Index object (for error code) */
+ Fts5TokenDataIter *pIn, /* Current Fts5TokenDataIter struct */
+ Fts5Iter *pAppend /* Append this iterator */
+){
+ Fts5TokenDataIter *pRet = pIn;
+
+ if( p->rc==SQLITE_OK ){
+ if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){
+ int nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
+ int nByte = nAlloc * sizeof(Fts5Iter*) + sizeof(Fts5TokenDataIter);
+ Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte);
+
+ if( pNew==0 ){
+ p->rc = SQLITE_NOMEM;
+ }else{
+ if( pIn==0 ) memset(pNew, 0, nByte);
+ pRet = pNew;
+ pNew->nIterAlloc = nAlloc;
+ }
+ }
+ }
+ if( p->rc ){
+ sqlite3Fts5IterClose((Fts5IndexIter*)pAppend);
+ }else{
+ pRet->apIter[pRet->nIter++] = pAppend;
+ }
+ assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc );
+
+ return pRet;
+}
+
+/*
+** Delete an Fts5TokenDataIter structure and its contents.
+*/
+static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){
+ if( pSet ){
+ int ii;
+ for(ii=0; ii<pSet->nIter; ii++){
+ fts5MultiIterFree(pSet->apIter[ii]);
+ }
+ sqlite3_free(pSet->aPoslistReader);
+ sqlite3_free(pSet->aMap);
+ sqlite3_free(pSet);
+ }
+}
+
+/*
+** Append a mapping to the token-map belonging to object pT.
+*/
+static void fts5TokendataIterAppendMap(
+ Fts5Index *p,
+ Fts5TokenDataIter *pT,
+ int iIter,
+ i64 iRowid,
+ i64 iPos
+){
+ if( p->rc==SQLITE_OK ){
+ if( pT->nMap==pT->nMapAlloc ){
+ int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
+ int nByte = nNew * sizeof(Fts5TokenDataMap);
+ Fts5TokenDataMap *aNew;
+
+ aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nByte);
+ if( aNew==0 ){
+ p->rc = SQLITE_NOMEM;
+ return;
+ }
+
+ pT->aMap = aNew;
+ pT->nMapAlloc = nNew;
+ }
+
+ pT->aMap[pT->nMap].iRowid = iRowid;
+ pT->aMap[pT->nMap].iPos = iPos;
+ pT->aMap[pT->nMap].iIter = iIter;
+ pT->nMap++;
+ }
+}
+
+/*
+** The iterator passed as the only argument must be a tokendata=1 iterator
+** (pIter->pTokenDataIter!=0). This function sets the iterator output
+** variables (pIter->base.*) according to the contents of the current
+** row.
+*/
+static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){
+ int ii;
+ int nHit = 0;
+ i64 iRowid = SMALLEST_INT64;
+ int iMin = 0;
+
+ Fts5TokenDataIter *pT = pIter->pTokenDataIter;
+
+ pIter->base.nData = 0;
+ pIter->base.pData = 0;
+
+ for(ii=0; ii<pT->nIter; ii++){
+ Fts5Iter *p = pT->apIter[ii];
+ if( p->base.bEof==0 ){
+ if( nHit==0 || p->base.iRowid<iRowid ){
+ iRowid = p->base.iRowid;
+ nHit = 1;
+ pIter->base.pData = p->base.pData;
+ pIter->base.nData = p->base.nData;
+ iMin = ii;
+ }else if( p->base.iRowid==iRowid ){
+ nHit++;
+ }
+ }
+ }
+
+ if( nHit==0 ){
+ pIter->base.bEof = 1;
+ }else{
+ int eDetail = pIter->pIndex->pConfig->eDetail;
+ pIter->base.bEof = 0;
+ pIter->base.iRowid = iRowid;
+
+ if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){
+ fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, iRowid, -1);
+ }else
+ if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){
+ int nReader = 0;
+ int nByte = 0;
+ i64 iPrev = 0;
+
+ /* Allocate array of iterators if they are not already allocated. */
+ if( pT->aPoslistReader==0 ){
+ pT->aPoslistReader = (Fts5PoslistReader*)sqlite3Fts5MallocZero(
+ &pIter->pIndex->rc,
+ pT->nIter * (sizeof(Fts5PoslistReader) + sizeof(int))
+ );
+ if( pT->aPoslistReader==0 ) return;
+ pT->aPoslistToIter = (int*)&pT->aPoslistReader[pT->nIter];
+ }
+
+ /* Populate an iterator for each poslist that will be merged */
+ for(ii=0; ii<pT->nIter; ii++){
+ Fts5Iter *p = pT->apIter[ii];
+ if( iRowid==p->base.iRowid ){
+ pT->aPoslistToIter[nReader] = ii;
+ sqlite3Fts5PoslistReaderInit(
+ p->base.pData, p->base.nData, &pT->aPoslistReader[nReader++]
+ );
+ nByte += p->base.nData;
+ }
+ }
+
+ /* Ensure the output buffer is large enough */
+ if( fts5BufferGrow(&pIter->pIndex->rc, &pIter->poslist, nByte+nHit*10) ){
+ return;
+ }
+
+ /* Ensure the token-mapping is large enough */
+ if( eDetail==FTS5_DETAIL_FULL && pT->nMapAlloc<(pT->nMap + nByte) ){
+ int nNew = (pT->nMapAlloc + nByte) * 2;
+ Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc(
+ pT->aMap, nNew*sizeof(Fts5TokenDataMap)
+ );
+ if( aNew==0 ){
+ pIter->pIndex->rc = SQLITE_NOMEM;
+ return;
+ }
+ pT->aMap = aNew;
+ pT->nMapAlloc = nNew;
+ }
+
+ pIter->poslist.n = 0;
+
+ while( 1 ){
+ i64 iMinPos = LARGEST_INT64;
+
+ /* Find smallest position */
+ iMin = 0;
+ for(ii=0; ii<nReader; ii++){
+ Fts5PoslistReader *pReader = &pT->aPoslistReader[ii];
+ if( pReader->bEof==0 ){
+ if( pReader->iPos<iMinPos ){
+ iMinPos = pReader->iPos;
+ iMin = ii;
+ }
+ }
+ }
+
+ /* If all readers were at EOF, break out of the loop. */
+ if( iMinPos==LARGEST_INT64 ) break;
+
+ sqlite3Fts5PoslistSafeAppend(&pIter->poslist, &iPrev, iMinPos);
+ sqlite3Fts5PoslistReaderNext(&pT->aPoslistReader[iMin]);
+
+ if( eDetail==FTS5_DETAIL_FULL ){
+ pT->aMap[pT->nMap].iPos = iMinPos;
+ pT->aMap[pT->nMap].iIter = pT->aPoslistToIter[iMin];
+ pT->aMap[pT->nMap].iRowid = iRowid;
+ pT->nMap++;
+ }
+ }
+
+ pIter->base.pData = pIter->poslist.p;
+ pIter->base.nData = pIter->poslist.n;
+ }
+ }
+}
+
+/*
+** The iterator passed as the only argument must be a tokendata=1 iterator
+** (pIter->pTokenDataIter!=0). This function advances the iterator. If
+** argument bFrom is false, then the iterator is advanced to the next
+** entry. Or, if bFrom is true, it is advanced to the first entry with
+** a rowid of iFrom or greater.
+*/
+static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){
+ int ii;
+ Fts5TokenDataIter *pT = pIter->pTokenDataIter;
+ Fts5Index *pIndex = pIter->pIndex;
+
+ for(ii=0; ii<pT->nIter; ii++){
+ Fts5Iter *p = pT->apIter[ii];
+ if( p->base.bEof==0
+ && (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowid<iFrom))
+ ){
+ fts5MultiIterNext(pIndex, p, bFrom, iFrom);
+ while( bFrom && p->base.bEof==0
+ && p->base.iRowid<iFrom
+ && pIndex->rc==SQLITE_OK
+ ){
+ fts5MultiIterNext(pIndex, p, 0, 0);
+ }
+ }
+ }
+
+ if( pIndex->rc==SQLITE_OK ){
+ fts5IterSetOutputsTokendata(pIter);
+ }
+}
+
+/*
+** If the segment-iterator passed as the first argument is at EOF, then
+** set pIter->term to a copy of buffer pTerm.
+*/
+static void fts5TokendataSetTermIfEof(Fts5Iter *pIter, Fts5Buffer *pTerm){
+ if( pIter && pIter->aSeg[0].pLeaf==0 ){
+ fts5BufferSet(&pIter->pIndex->rc, &pIter->aSeg[0].term, pTerm->n, pTerm->p);
+ }
+}
+
+/*
+** This function sets up an iterator to use for a non-prefix query on a
+** tokendata=1 table.
+*/
+static Fts5Iter *fts5SetupTokendataIter(
+ Fts5Index *p, /* FTS index to query */
+ const u8 *pToken, /* Buffer containing query term */
+ int nToken, /* Size of buffer pToken in bytes */
+ Fts5Colset *pColset /* Colset to filter on */
+){
+ Fts5Iter *pRet = 0;
+ Fts5TokenDataIter *pSet = 0;
+ Fts5Structure *pStruct = 0;
+ const int flags = FTS5INDEX_QUERY_SCANONETERM | FTS5INDEX_QUERY_SCAN;
+
+ Fts5Buffer bSeek = {0, 0, 0};
+ Fts5Buffer *pSmall = 0;
+
+ fts5IndexFlush(p);
+ pStruct = fts5StructureRead(p);
+
+ while( p->rc==SQLITE_OK ){
+ Fts5Iter *pPrev = pSet ? pSet->apIter[pSet->nIter-1] : 0;
+ Fts5Iter *pNew = 0;
+ Fts5SegIter *pNewIter = 0;
+ Fts5SegIter *pPrevIter = 0;
+
+ int iLvl, iSeg, ii;
+
+ pNew = fts5MultiIterAlloc(p, pStruct->nSegment);
+ if( pSmall ){
+ fts5BufferSet(&p->rc, &bSeek, pSmall->n, pSmall->p);
+ fts5BufferAppendBlob(&p->rc, &bSeek, 1, (const u8*)"\0");
+ }else{
+ fts5BufferSet(&p->rc, &bSeek, nToken, pToken);
+ }
+ if( p->rc ){
+ sqlite3Fts5IterClose((Fts5IndexIter*)pNew);
+ break;
+ }
+
+ pNewIter = &pNew->aSeg[0];
+ pPrevIter = (pPrev ? &pPrev->aSeg[0] : 0);
+ for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
+ for(iSeg=pStruct->aLevel[iLvl].nSeg-1; iSeg>=0; iSeg--){
+ Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg];
+ int bDone = 0;
+
+ if( pPrevIter ){
+ if( fts5BufferCompare(pSmall, &pPrevIter->term) ){
+ memcpy(pNewIter, pPrevIter, sizeof(Fts5SegIter));
+ memset(pPrevIter, 0, sizeof(Fts5SegIter));
+ bDone = 1;
+ }else if( pPrevIter->iEndofDoclist>pPrevIter->pLeaf->szLeaf ){
+ fts5SegIterNextInit(p,(const char*)bSeek.p,bSeek.n-1,pSeg,pNewIter);
+ bDone = 1;
+ }
+ }
+
+ if( bDone==0 ){
+ fts5SegIterSeekInit(p, bSeek.p, bSeek.n, flags, pSeg, pNewIter);
+ }
+
+ if( pPrevIter ){
+ if( pPrevIter->pTombArray ){
+ pNewIter->pTombArray = pPrevIter->pTombArray;
+ pNewIter->pTombArray->nRef++;
+ }
+ }else{
+ fts5SegIterAllocTombstone(p, pNewIter);
+ }
+
+ pNewIter++;
+ if( pPrevIter ) pPrevIter++;
+ if( p->rc ) break;
+ }
+ }
+ fts5TokendataSetTermIfEof(pPrev, pSmall);
+
+ pNew->bSkipEmpty = 1;
+ pNew->pColset = pColset;
+ fts5IterSetOutputCb(&p->rc, pNew);
+
+ /* Loop through all segments in the new iterator. Find the smallest
+ ** term that any segment-iterator points to. Iterator pNew will be
+ ** used for this term. Also, set any iterator that points to a term that
+ ** does not match pToken/nToken to point to EOF */
+ pSmall = 0;
+ for(ii=0; ii<pNew->nSeg; ii++){
+ Fts5SegIter *pII = &pNew->aSeg[ii];
+ if( 0==fts5IsTokendataPrefix(&pII->term, pToken, nToken) ){
+ fts5SegIterSetEOF(pII);
+ }
+ if( pII->pLeaf && (!pSmall || fts5BufferCompare(pSmall, &pII->term)>0) ){
+ pSmall = &pII->term;
+ }
+ }
+
+ /* If pSmall is still NULL at this point, then the new iterator does
+ ** not point to any terms that match the query. So delete it and break
+ ** out of the loop - all required iterators have been collected. */
+ if( pSmall==0 ){
+ sqlite3Fts5IterClose((Fts5IndexIter*)pNew);
+ break;
+ }
+
+ /* Append this iterator to the set and continue. */
+ pSet = fts5AppendTokendataIter(p, pSet, pNew);
+ }
+
+ if( p->rc==SQLITE_OK && pSet ){
+ int ii;
+ for(ii=0; ii<pSet->nIter; ii++){
+ Fts5Iter *pIter = pSet->apIter[ii];
+ int iSeg;
+ for(iSeg=0; iSeg<pIter->nSeg; iSeg++){
+ pIter->aSeg[iSeg].flags |= FTS5_SEGITER_ONETERM;
+ }
+ fts5MultiIterFinishSetup(p, pIter);
+ }
+ }
+
+ if( p->rc==SQLITE_OK ){
+ pRet = fts5MultiIterAlloc(p, 0);
+ }
+ if( pRet ){
+ pRet->pTokenDataIter = pSet;
+ if( pSet ){
+ fts5IterSetOutputsTokendata(pRet);
+ }else{
+ pRet->base.bEof = 1;
+ }
+ }else{
+ fts5TokendataIterDelete(pSet);
+ }
+
+ fts5StructureRelease(pStruct);
+ fts5BufferFree(&bSeek);
+ return pRet;
+}
+
+
+/*
** Open a new iterator to iterate though all rowid that match the
** specified token or token prefix.
*/
@@ -241013,8 +247561,13 @@ static int sqlite3Fts5IndexQuery(
if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){
int iIdx = 0; /* Index to search */
int iPrefixIdx = 0; /* +1 prefix index */
+ int bTokendata = pConfig->bTokendata;
if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken);
+ if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){
+ bTokendata = 0;
+ }
+
/* Figure out which index to search and set iIdx accordingly. If this
** is a prefix query for which there is no prefix index, set iIdx to
** greater than pConfig->nPrefix to indicate that the query will be
@@ -241040,7 +247593,10 @@ static int sqlite3Fts5IndexQuery(
}
}
- if( iIdx<=pConfig->nPrefix ){
+ if( bTokendata && iIdx==0 ){
+ buf.p[0] = '0';
+ pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset);
+ }else if( iIdx<=pConfig->nPrefix ){
/* Straight index lookup */
Fts5Structure *pStruct = fts5StructureRead(p);
buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx);
@@ -241087,7 +247643,11 @@ static int sqlite3Fts5IndexQuery(
static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
assert( pIter->pIndex->rc==SQLITE_OK );
- fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);
+ if( pIter->pTokenDataIter ){
+ fts5TokendataIterNext(pIter, 0, 0);
+ }else{
+ fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);
+ }
return fts5IndexReturn(pIter->pIndex);
}
@@ -241120,7 +247680,11 @@ static int sqlite3Fts5IterNextScan(Fts5IndexIter *pIndexIter){
*/
static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
- fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch);
+ if( pIter->pTokenDataIter ){
+ fts5TokendataIterNext(pIter, 1, iMatch);
+ }else{
+ fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch);
+ }
return fts5IndexReturn(pIter->pIndex);
}
@@ -241136,12 +247700,106 @@ static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){
}
/*
+** This is used by xInstToken() to access the token at offset iOff, column
+** iCol of row iRowid. The token is returned via output variables *ppOut
+** and *pnOut. The iterator passed as the first argument must be a tokendata=1
+** iterator (pIter->pTokenDataIter!=0).
+*/
+static int sqlite3Fts5IterToken(
+ Fts5IndexIter *pIndexIter,
+ i64 iRowid,
+ int iCol,
+ int iOff,
+ const char **ppOut, int *pnOut
+){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
+ Fts5TokenDataIter *pT = pIter->pTokenDataIter;
+ Fts5TokenDataMap *aMap = pT->aMap;
+ i64 iPos = (((i64)iCol)<<32) + iOff;
+
+ int i1 = 0;
+ int i2 = pT->nMap;
+ int iTest = 0;
+
+ while( i2>i1 ){
+ iTest = (i1 + i2) / 2;
+
+ if( aMap[iTest].iRowid<iRowid ){
+ i1 = iTest+1;
+ }else if( aMap[iTest].iRowid>iRowid ){
+ i2 = iTest;
+ }else{
+ if( aMap[iTest].iPos<iPos ){
+ if( aMap[iTest].iPos<0 ){
+ break;
+ }
+ i1 = iTest+1;
+ }else if( aMap[iTest].iPos>iPos ){
+ i2 = iTest;
+ }else{
+ break;
+ }
+ }
+ }
+
+ if( i2>i1 ){
+ Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter];
+ *ppOut = (const char*)pMap->aSeg[0].term.p+1;
+ *pnOut = pMap->aSeg[0].term.n-1;
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** Clear any existing entries from the token-map associated with the
+** iterator passed as the only argument.
+*/
+static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
+ if( pIter && pIter->pTokenDataIter ){
+ pIter->pTokenDataIter->nMap = 0;
+ }
+}
+
+/*
+** Set a token-mapping for the iterator passed as the first argument. This
+** is used in detail=column or detail=none mode when a token is requested
+** using the xInstToken() API. In this case the caller tokenizers the
+** current row and configures the token-mapping via multiple calls to this
+** function.
+*/
+static int sqlite3Fts5IndexIterWriteTokendata(
+ Fts5IndexIter *pIndexIter,
+ const char *pToken, int nToken,
+ i64 iRowid, int iCol, int iOff
+){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
+ Fts5TokenDataIter *pT = pIter->pTokenDataIter;
+ Fts5Index *p = pIter->pIndex;
+ int ii;
+
+ assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL );
+ assert( pIter->pTokenDataIter );
+
+ for(ii=0; ii<pT->nIter; ii++){
+ Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term;
+ if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break;
+ }
+ if( ii<pT->nIter ){
+ fts5TokendataIterAppendMap(p, pT, ii, iRowid, (((i64)iCol)<<32) + iOff);
+ }
+ return fts5IndexReturn(p);
+}
+
+/*
** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery().
*/
static void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){
if( pIndexIter ){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
Fts5Index *pIndex = pIter->pIndex;
+ fts5TokendataIterDelete(pIter->pTokenDataIter);
fts5MultiIterFree(pIter);
sqlite3Fts5IndexCloseReader(pIndex);
}
@@ -241649,7 +248307,9 @@ static int fts5QueryCksum(
int eDetail = p->pConfig->eDetail;
u64 cksum = *pCksum;
Fts5IndexIter *pIter = 0;
- int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIter);
+ int rc = sqlite3Fts5IndexQuery(
+ p, z, n, (flags | FTS5INDEX_QUERY_NOTOKENDATA), 0, &pIter
+ );
while( rc==SQLITE_OK && ALWAYS(pIter!=0) && 0==sqlite3Fts5IterEof(pIter) ){
i64 rowid = pIter->iRowid;
@@ -241816,7 +248476,7 @@ static void fts5IndexIntegrityCheckEmpty(
}
static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
- int iTermOff = 0;
+ i64 iTermOff = 0;
int ii;
Fts5Buffer buf1 = {0,0,0};
@@ -241825,7 +248485,7 @@ static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
ii = pLeaf->szLeaf;
while( ii<pLeaf->nn && p->rc==SQLITE_OK ){
int res;
- int iOff;
+ i64 iOff;
int nIncr;
ii += fts5GetVarint32(&pLeaf->p[ii], nIncr);
@@ -242348,6 +249008,24 @@ static void fts5DecodeRowidList(
#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
+static void fts5BufferAppendTerm(int *pRc, Fts5Buffer *pBuf, Fts5Buffer *pTerm){
+ int ii;
+ fts5BufferGrow(pRc, pBuf, pTerm->n*2 + 1);
+ if( *pRc==SQLITE_OK ){
+ for(ii=0; ii<pTerm->n; ii++){
+ if( pTerm->p[ii]==0x00 ){
+ pBuf->p[pBuf->n++] = '\\';
+ pBuf->p[pBuf->n++] = '0';
+ }else{
+ pBuf->p[pBuf->n++] = pTerm->p[ii];
+ }
+ }
+ pBuf->p[pBuf->n] = 0x00;
+ }
+}
+#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
+
+#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
/*
** The implementation of user-defined scalar function fts5_decode().
*/
@@ -242454,9 +249132,8 @@ static void fts5DecodeFunction(
iOff += fts5GetVarint32(&a[iOff], nAppend);
term.n = nKeep;
fts5BufferAppendBlob(&rc, &term, nAppend, &a[iOff]);
- sqlite3Fts5BufferAppendPrintf(
- &rc, &s, " term=%.*s", term.n, (const char*)term.p
- );
+ sqlite3Fts5BufferAppendPrintf(&rc, &s, " term=");
+ fts5BufferAppendTerm(&rc, &s, &term);
iOff += nAppend;
/* Figure out where the doclist for this term ends */
@@ -242564,9 +249241,8 @@ static void fts5DecodeFunction(
fts5BufferAppendBlob(&rc, &term, nByte, &a[iOff]);
iOff += nByte;
- sqlite3Fts5BufferAppendPrintf(
- &rc, &s, " term=%.*s", term.n, (const char*)term.p
- );
+ sqlite3Fts5BufferAppendPrintf(&rc, &s, " term=");
+ fts5BufferAppendTerm(&rc, &s, &term);
iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], iEnd-iOff);
}
@@ -242900,7 +249576,8 @@ static int sqlite3Fts5IndexInit(sqlite3 *db){
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
- 0 /* xShadowName */
+ 0, /* xShadowName */
+ 0 /* xIntegrity */
};
rc = sqlite3_create_module(db, "fts5_structure", &fts5structure_module, 0);
}
@@ -243039,6 +249716,8 @@ struct Fts5FullTable {
Fts5Storage *pStorage; /* Document store */
Fts5Global *pGlobal; /* Global (connection wide) data */
Fts5Cursor *pSortCsr; /* Sort data from this cursor */
+ int iSavepoint; /* Successful xSavepoint()+1 */
+
#ifdef SQLITE_DEBUG
struct Fts5TransactionState ts;
#endif
@@ -243327,6 +250006,13 @@ static int fts5InitVtab(
pConfig->pzErrmsg = 0;
}
+ if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ rc = sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, (int)1);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
+ }
+
if( rc!=SQLITE_OK ){
fts5FreeVtab(pTab);
pTab = 0;
@@ -243569,12 +250255,15 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
}
idxStr[iIdxStr] = '\0';
- /* Set idxFlags flags for the ORDER BY clause */
+ /* Set idxFlags flags for the ORDER BY clause
+ **
+ ** Note that tokendata=1 tables cannot currently handle "ORDER BY rowid DESC".
+ */
if( pInfo->nOrderBy==1 ){
int iSort = pInfo->aOrderBy[0].iColumn;
if( iSort==(pConfig->nCol+1) && bSeenMatch ){
idxFlags |= FTS5_BI_ORDER_RANK;
- }else if( iSort==-1 ){
+ }else if( iSort==-1 && (!pInfo->aOrderBy[0].desc || !pConfig->bTokendata) ){
idxFlags |= FTS5_BI_ORDER_ROWID;
}
if( BitFlagTest(idxFlags, FTS5_BI_ORDER_RANK|FTS5_BI_ORDER_ROWID) ){
@@ -243826,6 +250515,16 @@ static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){
);
assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) );
+ /* If this cursor uses FTS5_PLAN_MATCH and this is a tokendata=1 table,
+ ** clear any token mappings accumulated at the fts5_index.c level. In
+ ** other cases, specifically FTS5_PLAN_SOURCE and FTS5_PLAN_SORTED_MATCH,
+ ** we need to retain the mappings for the entire query. */
+ if( pCsr->ePlan==FTS5_PLAN_MATCH
+ && ((Fts5Table*)pCursor->pVtab)->pConfig->bTokendata
+ ){
+ sqlite3Fts5ExprClearTokens(pCsr->pExpr);
+ }
+
if( pCsr->ePlan<3 ){
int bSkip = 0;
if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc;
@@ -244251,6 +250950,9 @@ static int fts5FilterMethod(
pCsr->iFirstRowid = fts5GetRowidLimit(pRowidGe, SMALLEST_INT64);
}
+ rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
+ if( rc!=SQLITE_OK ) goto filter_out;
+
if( pTab->pSortCsr ){
/* If pSortCsr is non-NULL, then this call is being made as part of
** processing for a "... MATCH <expr> ORDER BY rank" query (ePlan is
@@ -244273,6 +250975,7 @@ static int fts5FilterMethod(
pCsr->pExpr = pTab->pSortCsr->pExpr;
rc = fts5CursorFirst(pTab, pCsr, bDesc);
}else if( pCsr->pExpr ){
+ assert( rc==SQLITE_OK );
rc = fts5CursorParseRank(pConfig, pCsr, pRank);
if( rc==SQLITE_OK ){
if( bOrderByRank ){
@@ -244444,6 +251147,7 @@ static int fts5SpecialInsert(
Fts5Config *pConfig = pTab->p.pConfig;
int rc = SQLITE_OK;
int bError = 0;
+ int bLoadConfig = 0;
if( 0==sqlite3_stricmp("delete-all", zCmd) ){
if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
@@ -244455,6 +251159,7 @@ static int fts5SpecialInsert(
}else{
rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage);
}
+ bLoadConfig = 1;
}else if( 0==sqlite3_stricmp("rebuild", zCmd) ){
if( pConfig->eContent==FTS5_CONTENT_NONE ){
fts5SetVtabError(pTab,
@@ -244464,6 +251169,7 @@ static int fts5SpecialInsert(
}else{
rc = sqlite3Fts5StorageRebuild(pTab->pStorage);
}
+ bLoadConfig = 1;
}else if( 0==sqlite3_stricmp("optimize", zCmd) ){
rc = sqlite3Fts5StorageOptimize(pTab->pStorage);
}else if( 0==sqlite3_stricmp("merge", zCmd) ){
@@ -244476,8 +251182,13 @@ static int fts5SpecialInsert(
}else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){
pConfig->bPrefixIndex = sqlite3_value_int(pVal);
#endif
+ }else if( 0==sqlite3_stricmp("flush", zCmd) ){
+ rc = sqlite3Fts5FlushToDisk(&pTab->p);
}else{
- rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
+ rc = sqlite3Fts5FlushToDisk(&pTab->p);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
+ }
if( rc==SQLITE_OK ){
rc = sqlite3Fts5ConfigSetValue(pTab->p.pConfig, zCmd, pVal, &bError);
}
@@ -244489,6 +251200,12 @@ static int fts5SpecialInsert(
}
}
}
+
+ if( rc==SQLITE_OK && bLoadConfig ){
+ pTab->p.pConfig->iCookie--;
+ rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
+ }
+
return rc;
}
@@ -244607,7 +251324,7 @@ static int fts5UpdateMethod(
assert( nArg!=1 || eType0==SQLITE_INTEGER );
/* Filter out attempts to run UPDATE or DELETE on contentless tables.
- ** This is not suported. Except - DELETE is supported if the CREATE
+ ** This is not suported. Except - they are both supported if the CREATE
** VIRTUAL TABLE statement contained "contentless_delete=1". */
if( eType0==SQLITE_INTEGER
&& pConfig->eContent==FTS5_CONTENT_NONE
@@ -244636,7 +251353,8 @@ static int fts5UpdateMethod(
}
else if( eType0!=SQLITE_INTEGER ){
- /* If this is a REPLACE, first remove the current entry (if any) */
+ /* An INSERT statement. If the conflict-mode is REPLACE, first remove
+ ** the current entry (if any). */
if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){
i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0);
@@ -244795,7 +251513,10 @@ static int fts5ApiColumnText(
){
int rc = SQLITE_OK;
Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
- if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab))
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ if( iCol<0 || iCol>=pTab->pConfig->nCol ){
+ rc = SQLITE_RANGE;
+ }else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab))
|| pCsr->ePlan==FTS5_PLAN_SPECIAL
){
*pz = 0;
@@ -244820,8 +251541,9 @@ static int fts5CsrPoslist(
int rc = SQLITE_OK;
int bLive = (pCsr->pSorter==0);
- if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){
-
+ if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){
+ rc = SQLITE_RANGE;
+ }else if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){
if( pConfig->eDetail!=FTS5_DETAIL_FULL ){
Fts5PoslistPopulator *aPopulator;
int i;
@@ -244845,15 +251567,21 @@ static int fts5CsrPoslist(
CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST);
}
- if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){
- Fts5Sorter *pSorter = pCsr->pSorter;
- int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]);
- *pn = pSorter->aIdx[iPhrase] - i1;
- *pa = &pSorter->aPoslist[i1];
+ if( rc==SQLITE_OK ){
+ if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){
+ Fts5Sorter *pSorter = pCsr->pSorter;
+ int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]);
+ *pn = pSorter->aIdx[iPhrase] - i1;
+ *pa = &pSorter->aPoslist[i1];
+ }else{
+ *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa);
+ }
}else{
- *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa);
+ *pa = 0;
+ *pn = 0;
}
+
return rc;
}
@@ -244960,12 +251688,6 @@ static int fts5ApiInst(
){
if( iIdx<0 || iIdx>=pCsr->nInstCount ){
rc = SQLITE_RANGE;
-#if 0
- }else if( fts5IsOffsetless((Fts5Table*)pCsr->base.pVtab) ){
- *piPhrase = pCsr->aInst[iIdx*3];
- *piCol = pCsr->aInst[iIdx*3 + 2];
- *piOff = -1;
-#endif
}else{
*piPhrase = pCsr->aInst[iIdx*3];
*piCol = pCsr->aInst[iIdx*3 + 1];
@@ -245220,13 +251942,56 @@ static int fts5ApiPhraseFirstColumn(
return rc;
}
+/*
+** xQueryToken() API implemenetation.
+*/
+static int fts5ApiQueryToken(
+ Fts5Context* pCtx,
+ int iPhrase,
+ int iToken,
+ const char **ppOut,
+ int *pnOut
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ return sqlite3Fts5ExprQueryToken(pCsr->pExpr, iPhrase, iToken, ppOut, pnOut);
+}
+
+/*
+** xInstToken() API implemenetation.
+*/
+static int fts5ApiInstToken(
+ Fts5Context *pCtx,
+ int iIdx,
+ int iToken,
+ const char **ppOut, int *pnOut
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ int rc = SQLITE_OK;
+ if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
+ || SQLITE_OK==(rc = fts5CacheInstArray(pCsr))
+ ){
+ if( iIdx<0 || iIdx>=pCsr->nInstCount ){
+ rc = SQLITE_RANGE;
+ }else{
+ int iPhrase = pCsr->aInst[iIdx*3];
+ int iCol = pCsr->aInst[iIdx*3 + 1];
+ int iOff = pCsr->aInst[iIdx*3 + 2];
+ i64 iRowid = fts5CursorRowid(pCsr);
+ rc = sqlite3Fts5ExprInstToken(
+ pCsr->pExpr, iRowid, iPhrase, iCol, iOff, iToken, ppOut, pnOut
+ );
+ }
+ }
+ return rc;
+}
+
static int fts5ApiQueryPhrase(Fts5Context*, int, void*,
int(*)(const Fts5ExtensionApi*, Fts5Context*, void*)
);
static const Fts5ExtensionApi sFts5Api = {
- 2, /* iVersion */
+ 3, /* iVersion */
fts5ApiUserData,
fts5ApiColumnCount,
fts5ApiRowCount,
@@ -245246,6 +252011,8 @@ static const Fts5ExtensionApi sFts5Api = {
fts5ApiPhraseNext,
fts5ApiPhraseFirstColumn,
fts5ApiPhraseNextColumn,
+ fts5ApiQueryToken,
+ fts5ApiInstToken
};
/*
@@ -245510,8 +252277,10 @@ static int fts5RenameMethod(
sqlite3_vtab *pVtab, /* Virtual table handle */
const char *zName /* New name of table */
){
+ int rc;
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
- return sqlite3Fts5StorageRename(pTab->pStorage, zName);
+ rc = sqlite3Fts5StorageRename(pTab->pStorage, zName);
+ return rc;
}
static int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
@@ -245525,9 +252294,15 @@ static int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
** Flush the contents of the pending-terms table to disk.
*/
static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
- UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
- fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_SAVEPOINT, iSavepoint);
- return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
+ Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
+ int rc = SQLITE_OK;
+
+ fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint);
+ rc = sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
+ if( rc==SQLITE_OK ){
+ pTab->iSavepoint = iSavepoint+1;
+ }
+ return rc;
}
/*
@@ -245536,9 +252311,16 @@ static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
** This is a no-op.
*/
static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
- UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
- fts5CheckTransactionState((Fts5FullTable*)pVtab, FTS5_RELEASE, iSavepoint);
- return sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);
+ Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
+ int rc = SQLITE_OK;
+ fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint);
+ if( (iSavepoint+1)<pTab->iSavepoint ){
+ rc = sqlite3Fts5FlushToDisk(&pTab->p);
+ if( rc==SQLITE_OK ){
+ pTab->iSavepoint = iSavepoint;
+ }
+ }
+ return rc;
}
/*
@@ -245548,11 +252330,14 @@ static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
*/
static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
- UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
+ int rc = SQLITE_OK;
fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
fts5TripCursors(pTab);
- pTab->p.pConfig->pgsz = 0;
- return sqlite3Fts5StorageRollback(pTab->pStorage);
+ if( (iSavepoint+1)<=pTab->iSavepoint ){
+ pTab->p.pConfig->pgsz = 0;
+ rc = sqlite3Fts5StorageRollback(pTab->pStorage);
+ }
+ return rc;
}
/*
@@ -245754,7 +252539,7 @@ static void fts5SourceIdFunc(
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
- sqlite3_result_text(pCtx, "fts5: 2023-09-11 12:01:27 2d3a40c05c49e1a49264912b1a05bc2143ac0e7c3df588276ce80a4cbc9bd1b0", -1, SQLITE_TRANSIENT);
+ sqlite3_result_text(pCtx, "fts5: 2024-05-23 13:25:27 96c92aba00c8375bc32fafcdf12429c58bd8aabfcadab6683e35bbb9cdebf19e", -1, SQLITE_TRANSIENT);
}
/*
@@ -245772,9 +252557,41 @@ static int fts5ShadowName(const char *zName){
return 0;
}
+/*
+** Run an integrity check on the FTS5 data structures. Return a string
+** if anything is found amiss. Return a NULL pointer if everything is
+** OK.
+*/
+static int fts5IntegrityMethod(
+ sqlite3_vtab *pVtab, /* the FTS5 virtual table to check */
+ const char *zSchema, /* Name of schema in which this table lives */
+ const char *zTabname, /* Name of the table itself */
+ int isQuick, /* True if this is a quick-check */
+ char **pzErr /* Write error message here */
+){
+ Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
+ int rc;
+
+ assert( pzErr!=0 && *pzErr==0 );
+ UNUSED_PARAM(isQuick);
+ rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, 0);
+ if( (rc&0xff)==SQLITE_CORRUPT ){
+ *pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s",
+ zSchema, zTabname);
+ rc = (*pzErr) ? SQLITE_OK : SQLITE_NOMEM;
+ }else if( rc!=SQLITE_OK ){
+ *pzErr = sqlite3_mprintf("unable to validate the inverted index for"
+ " FTS5 table %s.%s: %s",
+ zSchema, zTabname, sqlite3_errstr(rc));
+ }
+ sqlite3Fts5IndexCloseReader(pTab->p.pIndex);
+
+ return rc;
+}
+
static int fts5Init(sqlite3 *db){
static const sqlite3_module fts5Mod = {
- /* iVersion */ 3,
+ /* iVersion */ 4,
/* xCreate */ fts5CreateMethod,
/* xConnect */ fts5ConnectMethod,
/* xBestIndex */ fts5BestIndexMethod,
@@ -245797,7 +252614,8 @@ static int fts5Init(sqlite3 *db){
/* xSavepoint */ fts5SavepointMethod,
/* xRelease */ fts5ReleaseMethod,
/* xRollbackTo */ fts5RollbackToMethod,
- /* xShadowName */ fts5ShadowName
+ /* xShadowName */ fts5ShadowName,
+ /* xIntegrity */ fts5IntegrityMethod
};
int rc;
@@ -246563,7 +253381,7 @@ static int sqlite3Fts5StorageRebuild(Fts5Storage *p){
}
if( rc==SQLITE_OK ){
- rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
+ rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, pConfig->pzErrmsg);
}
while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pScan) ){
@@ -247074,7 +253892,9 @@ static int sqlite3Fts5StorageSync(Fts5Storage *p){
i64 iLastRowid = sqlite3_last_insert_rowid(p->pConfig->db);
if( p->bTotalsValid ){
rc = fts5StorageSaveTotals(p);
- p->bTotalsValid = 0;
+ if( rc==SQLITE_OK ){
+ p->bTotalsValid = 0;
+ }
}
if( rc==SQLITE_OK ){
rc = sqlite3Fts5IndexSync(p->pIndex);
@@ -247348,6 +254168,12 @@ static const unsigned char sqlite3Utf8Trans1[] = {
#endif /* ifndef SQLITE_AMALGAMATION */
+#define FTS5_SKIP_UTF8(zIn) { \
+ if( ((unsigned char)(*(zIn++)))>=0xc0 ){ \
+ while( (((unsigned char)*zIn) & 0xc0)==0x80 ){ zIn++; } \
+ } \
+}
+
typedef struct Unicode61Tokenizer Unicode61Tokenizer;
struct Unicode61Tokenizer {
unsigned char aTokenChar[128]; /* ASCII range token characters */
@@ -248383,6 +255209,7 @@ static int fts5PorterTokenize(
typedef struct TrigramTokenizer TrigramTokenizer;
struct TrigramTokenizer {
int bFold; /* True to fold to lower-case */
+ int iFoldParam; /* Parameter to pass to Fts5UnicodeFold() */
};
/*
@@ -248409,6 +255236,7 @@ static int fts5TriCreate(
}else{
int i;
pNew->bFold = 1;
+ pNew->iFoldParam = 0;
for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
const char *zArg = azArg[i+1];
if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){
@@ -248417,10 +255245,21 @@ static int fts5TriCreate(
}else{
pNew->bFold = (zArg[0]=='0');
}
+ }else if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){
+ if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){
+ rc = SQLITE_ERROR;
+ }else{
+ pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0;
+ }
}else{
rc = SQLITE_ERROR;
}
}
+
+ if( pNew->iFoldParam!=0 && pNew->bFold==0 ){
+ rc = SQLITE_ERROR;
+ }
+
if( rc!=SQLITE_OK ){
fts5TriDelete((Fts5Tokenizer*)pNew);
pNew = 0;
@@ -248443,40 +255282,62 @@ static int fts5TriTokenize(
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
int rc = SQLITE_OK;
char aBuf[32];
+ char *zOut = aBuf;
+ int ii;
const unsigned char *zIn = (const unsigned char*)pText;
const unsigned char *zEof = &zIn[nText];
u32 iCode;
+ int aStart[3]; /* Input offset of each character in aBuf[] */
UNUSED_PARAM(unusedFlags);
- while( 1 ){
- char *zOut = aBuf;
- int iStart = zIn - (const unsigned char*)pText;
- const unsigned char *zNext;
-
- READ_UTF8(zIn, zEof, iCode);
- if( iCode==0 ) break;
- zNext = zIn;
- if( zIn<zEof ){
- if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
- WRITE_UTF8(zOut, iCode);
+
+ /* Populate aBuf[] with the characters for the first trigram. */
+ for(ii=0; ii<3; ii++){
+ do {
+ aStart[ii] = zIn - (const unsigned char*)pText;
READ_UTF8(zIn, zEof, iCode);
- if( iCode==0 ) break;
- }else{
- break;
- }
- if( zIn<zEof ){
- if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
- WRITE_UTF8(zOut, iCode);
+ if( iCode==0 ) return SQLITE_OK;
+ if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
+ }while( iCode==0 );
+ WRITE_UTF8(zOut, iCode);
+ }
+
+ /* At the start of each iteration of this loop:
+ **
+ ** aBuf: Contains 3 characters. The 3 characters of the next trigram.
+ ** zOut: Points to the byte following the last character in aBuf.
+ ** aStart[3]: Contains the byte offset in the input text corresponding
+ ** to the start of each of the three characters in the buffer.
+ */
+ assert( zIn<=zEof );
+ while( 1 ){
+ int iNext; /* Start of character following current tri */
+ const char *z1;
+
+ /* Read characters from the input up until the first non-diacritic */
+ do {
+ iNext = zIn - (const unsigned char*)pText;
READ_UTF8(zIn, zEof, iCode);
if( iCode==0 ) break;
- if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
- WRITE_UTF8(zOut, iCode);
- }else{
- break;
- }
- rc = xToken(pCtx, 0, aBuf, zOut-aBuf, iStart, iStart + zOut-aBuf);
- if( rc!=SQLITE_OK ) break;
- zIn = zNext;
+ if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
+ }while( iCode==0 );
+
+ /* Pass the current trigram back to fts5 */
+ rc = xToken(pCtx, 0, aBuf, zOut-aBuf, aStart[0], iNext);
+ if( iCode==0 || rc!=SQLITE_OK ) break;
+
+ /* Remove the first character from buffer aBuf[]. Append the character
+ ** with codepoint iCode. */
+ z1 = aBuf;
+ FTS5_SKIP_UTF8(z1);
+ memmove(aBuf, z1, zOut - z1);
+ zOut -= (z1 - aBuf);
+ WRITE_UTF8(zOut, iCode);
+
+ /* Update the aStart[] array */
+ aStart[0] = aStart[1];
+ aStart[1] = aStart[2];
+ aStart[2] = iNext;
}
return rc;
@@ -248499,7 +255360,9 @@ static int sqlite3Fts5TokenizerPattern(
){
if( xCreate==fts5TriCreate ){
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
- return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB;
+ if( p->iFoldParam==0 ){
+ return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB;
+ }
}
return FTS5_PATTERN_NONE;
}
@@ -250288,7 +257151,7 @@ static int fts5VocabFilterMethod(
if( pEq ){
zTerm = (const char *)sqlite3_value_text(pEq);
nTerm = sqlite3_value_bytes(pEq);
- f = 0;
+ f = FTS5INDEX_QUERY_NOTOKENDATA;
}else{
if( pGe ){
zTerm = (const char *)sqlite3_value_text(pGe);
@@ -250442,7 +257305,8 @@ static int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
- /* xShadowName */ 0
+ /* xShadowName */ 0,
+ /* xIntegrity */ 0
};
void *p = (void*)pGlobal;
@@ -250771,6 +257635,7 @@ static sqlite3_module stmtModule = {
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadowName */
+ 0 /* xIntegrity */
};
#endif /* SQLITE_OMIT_VIRTUALTABLE */
diff --git a/src/libs/3rdparty/sqlite/sqlite3.h b/src/libs/3rdparty/sqlite/sqlite3.h
index b9d0692988..57df8dcf20 100644
--- a/src/libs/3rdparty/sqlite/sqlite3.h
+++ b/src/libs/3rdparty/sqlite/sqlite3.h
@@ -146,9 +146,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.43.1"
-#define SQLITE_VERSION_NUMBER 3043001
-#define SQLITE_SOURCE_ID "2023-09-11 12:01:27 2d3a40c05c49e1a49264912b1a05bc2143ac0e7c3df588276ce80a4cbc9bd1b0"
+#define SQLITE_VERSION "3.46.0"
+#define SQLITE_VERSION_NUMBER 3046000
+#define SQLITE_SOURCE_ID "2024-05-23 13:25:27 96c92aba00c8375bc32fafcdf12429c58bd8aabfcadab6683e35bbb9cdebf19e"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -420,6 +420,8 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running.
** <li> The application must not modify the SQL statement text passed into
** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running.
+** <li> The application must not dereference the arrays or string pointers
+** passed as the 3rd and 4th callback parameters after it returns.
** </ul>
*/
SQLITE_API int sqlite3_exec(
@@ -762,11 +764,11 @@ struct sqlite3_file {
** </ul>
** xLock() upgrades the database file lock. In other words, xLock() moves the
** database file lock in the direction NONE toward EXCLUSIVE. The argument to
-** xLock() is always on of SHARED, RESERVED, PENDING, or EXCLUSIVE, never
+** xLock() is always one of SHARED, RESERVED, PENDING, or EXCLUSIVE, never
** SQLITE_LOCK_NONE. If the database file lock is already at or above the
** requested lock, then the call to xLock() is a no-op.
** xUnlock() downgrades the database file lock to either SHARED or NONE.
-* If the lock is already at or below the requested lock state, then the call
+** If the lock is already at or below the requested lock state, then the call
** to xUnlock() is a no-op.
** The xCheckReservedLock() method checks whether any database connection,
** either in this process or in some other process, is holding a RESERVED,
@@ -2127,7 +2129,7 @@ struct sqlite3_mem_methods {
** is stored in each sorted record and the required column values loaded
** from the database as records are returned in sorted order. The default
** value for this option is to never use this optimization. Specifying a
-** negative value for this option restores the default behaviour.
+** negative value for this option restores the default behavior.
** This option is only available if SQLite is compiled with the
** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option.
**
@@ -2141,6 +2143,22 @@ struct sqlite3_mem_methods {
** configuration setting is never used, then the default maximum is determined
** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that
** compile-time option is not set, then the default maximum is 1073741824.
+**
+** [[SQLITE_CONFIG_ROWID_IN_VIEW]]
+** <dt>SQLITE_CONFIG_ROWID_IN_VIEW
+** <dd>The SQLITE_CONFIG_ROWID_IN_VIEW option enables or disables the ability
+** for VIEWs to have a ROWID. The capability can only be enabled if SQLite is
+** compiled with -DSQLITE_ALLOW_ROWID_IN_VIEW, in which case the capability
+** defaults to on. This configuration option queries the current setting or
+** changes the setting to off or on. The argument is a pointer to an integer.
+** If that integer initially holds a value of 1, then the ability for VIEWs to
+** have ROWIDs is activated. If the integer initially holds zero, then the
+** ability is deactivated. Any other initial value for the integer leaves the
+** setting unchanged. After changes, if any, the integer is written with
+** a 1 or 0, if the ability for VIEWs to have ROWIDs is on or off. If SQLite
+** is compiled without -DSQLITE_ALLOW_ROWID_IN_VIEW (which is the usual and
+** recommended case) then the integer is always filled with zero, regardless
+** if its initial value.
** </dl>
*/
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@@ -2172,6 +2190,7 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */
#define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */
#define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */
+#define SQLITE_CONFIG_ROWID_IN_VIEW 30 /* int* */
/*
** CAPI3REF: Database Connection Configuration Options
@@ -2302,7 +2321,7 @@ struct sqlite3_mem_methods {
** database handle, SQLite checks if this will mean that there are now no
** connections at all to the database. If so, it performs a checkpoint
** operation before closing the connection. This option may be used to
-** override this behaviour. The first parameter passed to this operation
+** override this behavior. The first parameter passed to this operation
** is an integer - positive to disable checkpoints-on-close, or zero (the
** default) to enable them, and negative to leave the setting unchanged.
** The second parameter is a pointer to an integer
@@ -3286,8 +3305,8 @@ SQLITE_API int sqlite3_set_authorizer(
#define SQLITE_RECURSIVE 33 /* NULL NULL */
/*
-** CAPI3REF: Tracing And Profiling Functions
-** METHOD: sqlite3
+** CAPI3REF: Deprecated Tracing And Profiling Functions
+** DEPRECATED
**
** These routines are deprecated. Use the [sqlite3_trace_v2()] interface
** instead of the routines described here.
@@ -3954,14 +3973,17 @@ SQLITE_API void sqlite3_free_filename(sqlite3_filename);
** </ul>
**
** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
-** text that describes the error, as either UTF-8 or UTF-16 respectively.
+** text that describes the error, as either UTF-8 or UTF-16 respectively,
+** or NULL if no error message is available.
+** (See how SQLite handles [invalid UTF] for exceptions to this rule.)
** ^(Memory to hold the error message string is managed internally.
** The application does not need to worry about freeing the result.
** However, the error string might be overwritten or deallocated by
** subsequent calls to other SQLite interface functions.)^
**
-** ^The sqlite3_errstr() interface returns the English-language text
-** that describes the [result code], as UTF-8.
+** ^The sqlite3_errstr(E) interface returns the English-language text
+** that describes the [result code] E, as UTF-8, or NULL if E is not an
+** result code for which a text error message is available.
** ^(Memory to hold the error message string is managed internally
** and must not be freed by the application)^.
**
@@ -5325,6 +5347,7 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt);
*/
SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
+
/*
** CAPI3REF: Create Or Redefine SQL Functions
** KEYWORDS: {function creation routines}
@@ -5571,13 +5594,27 @@ SQLITE_API int sqlite3_create_window_function(
** </dd>
**
** [[SQLITE_SUBTYPE]] <dt>SQLITE_SUBTYPE</dt><dd>
-** The SQLITE_SUBTYPE flag indicates to SQLite that a function may call
+** The SQLITE_SUBTYPE flag indicates to SQLite that a function might call
** [sqlite3_value_subtype()] to inspect the sub-types of its arguments.
-** Specifying this flag makes no difference for scalar or aggregate user
-** functions. However, if it is not specified for a user-defined window
-** function, then any sub-types belonging to arguments passed to the window
-** function may be discarded before the window function is called (i.e.
-** sqlite3_value_subtype() will always return 0).
+** This flag instructs SQLite to omit some corner-case optimizations that
+** might disrupt the operation of the [sqlite3_value_subtype()] function,
+** causing it to return zero rather than the correct subtype().
+** SQL functions that invokes [sqlite3_value_subtype()] should have this
+** property. If the SQLITE_SUBTYPE property is omitted, then the return
+** value from [sqlite3_value_subtype()] might sometimes be zero even though
+** a non-zero subtype was specified by the function argument expression.
+**
+** [[SQLITE_RESULT_SUBTYPE]] <dt>SQLITE_RESULT_SUBTYPE</dt><dd>
+** The SQLITE_RESULT_SUBTYPE flag indicates to SQLite that a function might call
+** [sqlite3_result_subtype()] to cause a sub-type to be associated with its
+** result.
+** Every function that invokes [sqlite3_result_subtype()] should have this
+** property. If it does not, then the call to [sqlite3_result_subtype()]
+** might become a no-op if the function is used as term in an
+** [expression index]. On the other hand, SQL functions that never invoke
+** [sqlite3_result_subtype()] should avoid setting this property, as the
+** purpose of this property is to disable certain optimizations that are
+** incompatible with subtypes.
** </dd>
** </dl>
*/
@@ -5585,6 +5622,7 @@ SQLITE_API int sqlite3_create_window_function(
#define SQLITE_DIRECTONLY 0x000080000
#define SQLITE_SUBTYPE 0x000100000
#define SQLITE_INNOCUOUS 0x000200000
+#define SQLITE_RESULT_SUBTYPE 0x001000000
/*
** CAPI3REF: Deprecated Functions
@@ -5781,6 +5819,12 @@ SQLITE_API int sqlite3_value_encoding(sqlite3_value*);
** information can be used to pass a limited amount of context from
** one SQL function to another. Use the [sqlite3_result_subtype()]
** routine to set the subtype for the return value of an SQL function.
+**
+** Every [application-defined SQL function] that invoke this interface
+** should include the [SQLITE_SUBTYPE] property in the text
+** encoding argument when the function is [sqlite3_create_function|registered].
+** If the [SQLITE_SUBTYPE] property is omitted, then sqlite3_value_subtype()
+** might return zero instead of the upstream subtype in some corner cases.
*/
SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
@@ -5879,48 +5923,56 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
** METHOD: sqlite3_context
**
** These functions may be used by (non-aggregate) SQL functions to
-** associate metadata with argument values. If the same value is passed to
-** multiple invocations of the same SQL function during query execution, under
-** some circumstances the associated metadata may be preserved. An example
-** of where this might be useful is in a regular-expression matching
-** function. The compiled version of the regular expression can be stored as
-** metadata associated with the pattern string.
+** associate auxiliary data with argument values. If the same argument
+** value is passed to multiple invocations of the same SQL function during
+** query execution, under some circumstances the associated auxiliary data
+** might be preserved. An example of where this might be useful is in a
+** regular-expression matching function. The compiled version of the regular
+** expression can be stored as auxiliary data associated with the pattern string.
** Then as long as the pattern string remains the same,
** the compiled regular expression can be reused on multiple
** invocations of the same function.
**
-** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the metadata
+** ^The sqlite3_get_auxdata(C,N) interface returns a pointer to the auxiliary data
** associated by the sqlite3_set_auxdata(C,N,P,X) function with the Nth argument
** value to the application-defined function. ^N is zero for the left-most
-** function argument. ^If there is no metadata
+** function argument. ^If there is no auxiliary data
** associated with the function argument, the sqlite3_get_auxdata(C,N) interface
** returns a NULL pointer.
**
-** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th
-** argument of the application-defined function. ^Subsequent
+** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as auxiliary data for the
+** N-th argument of the application-defined function. ^Subsequent
** calls to sqlite3_get_auxdata(C,N) return P from the most recent
-** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or
-** NULL if the metadata has been discarded.
+** sqlite3_set_auxdata(C,N,P,X) call if the auxiliary data is still valid or
+** NULL if the auxiliary data has been discarded.
** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL,
** SQLite will invoke the destructor function X with parameter P exactly
-** once, when the metadata is discarded.
-** SQLite is free to discard the metadata at any time, including: <ul>
+** once, when the auxiliary data is discarded.
+** SQLite is free to discard the auxiliary data at any time, including: <ul>
** <li> ^(when the corresponding function parameter changes)^, or
** <li> ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the
** SQL statement)^, or
** <li> ^(when sqlite3_set_auxdata() is invoked again on the same
** parameter)^, or
** <li> ^(during the original sqlite3_set_auxdata() call when a memory
-** allocation error occurs.)^ </ul>
+** allocation error occurs.)^
+** <li> ^(during the original sqlite3_set_auxdata() call if the function
+** is evaluated during query planning instead of during query execution,
+** as sometimes happens with [SQLITE_ENABLE_STAT4].)^ </ul>
**
-** Note the last bullet in particular. The destructor X in
+** Note the last two bullets in particular. The destructor X in
** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the
** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata()
** should be called near the end of the function implementation and the
** function implementation should not make any use of P after
-** sqlite3_set_auxdata() has been called.
-**
-** ^(In practice, metadata is preserved between function calls for
+** sqlite3_set_auxdata() has been called. Furthermore, a call to
+** sqlite3_get_auxdata() that occurs immediately after a corresponding call
+** to sqlite3_set_auxdata() might still return NULL if an out-of-memory
+** condition occurred during the sqlite3_set_auxdata() call or if the
+** function is being evaluated during query planning rather than during
+** query execution.
+**
+** ^(In practice, auxiliary data is preserved between function calls for
** function parameters that are compile-time constants, including literal
** values and [parameters] and expressions composed from the same.)^
**
@@ -5930,10 +5982,67 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
**
** These routines must be called from the same thread in which
** the SQL function is running.
+**
+** See also: [sqlite3_get_clientdata()] and [sqlite3_set_clientdata()].
*/
SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N);
SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*));
+/*
+** CAPI3REF: Database Connection Client Data
+** METHOD: sqlite3
+**
+** These functions are used to associate one or more named pointers
+** with a [database connection].
+** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P
+** to be attached to [database connection] D using name N. Subsequent
+** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P
+** or a NULL pointer if there were no prior calls to
+** sqlite3_set_clientdata() with the same values of D and N.
+** Names are compared using strcmp() and are thus case sensitive.
+**
+** If P and X are both non-NULL, then the destructor X is invoked with
+** argument P on the first of the following occurrences:
+** <ul>
+** <li> An out-of-memory error occurs during the call to
+** sqlite3_set_clientdata() which attempts to register pointer P.
+** <li> A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made
+** with the same D and N parameters.
+** <li> The database connection closes. SQLite does not make any guarantees
+** about the order in which destructors are called, only that all
+** destructors will be called exactly once at some point during the
+** database connection closing process.
+** </ul>
+**
+** SQLite does not do anything with client data other than invoke
+** destructors on the client data at the appropriate time. The intended
+** use for client data is to provide a mechanism for wrapper libraries
+** to store additional information about an SQLite database connection.
+**
+** There is no limit (other than available memory) on the number of different
+** client data pointers (with different names) that can be attached to a
+** single database connection. However, the implementation is optimized
+** for the case of having only one or two different client data names.
+** Applications and wrapper libraries are discouraged from using more than
+** one client data name each.
+**
+** There is no way to enumerate the client data pointers
+** associated with a database connection. The N parameter can be thought
+** of as a secret key such that only code that knows the secret key is able
+** to access the associated data.
+**
+** Security Warning: These interfaces should not be exposed in scripting
+** languages or in other circumstances where it might be possible for an
+** an attacker to invoke them. Any agent that can invoke these interfaces
+** can probably also take control of the process.
+**
+** Database connection client data is only available for SQLite
+** version 3.44.0 ([dateof:3.44.0]) and later.
+**
+** See also: [sqlite3_set_auxdata()] and [sqlite3_get_auxdata()].
+*/
+SQLITE_API void *sqlite3_get_clientdata(sqlite3*,const char*);
+SQLITE_API int sqlite3_set_clientdata(sqlite3*, const char*, void*, void(*)(void*));
/*
** CAPI3REF: Constants Defining Special Destructor Behavior
@@ -6135,6 +6244,20 @@ SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
** higher order bits are discarded.
** The number of subtype bytes preserved by SQLite might increase
** in future releases of SQLite.
+**
+** Every [application-defined SQL function] that invokes this interface
+** should include the [SQLITE_RESULT_SUBTYPE] property in its
+** text encoding argument when the SQL function is
+** [sqlite3_create_function|registered]. If the [SQLITE_RESULT_SUBTYPE]
+** property is omitted from the function that invokes sqlite3_result_subtype(),
+** then in some cases the sqlite3_result_subtype() might fail to set
+** the result subtype.
+**
+** If SQLite is compiled with -DSQLITE_STRICT_SUBTYPE=1, then any
+** SQL function that invokes the sqlite3_result_subtype() interface
+** and that does not have the SQLITE_RESULT_SUBTYPE property will raise
+** an error. Future versions of SQLite might enable -DSQLITE_STRICT_SUBTYPE=1
+** by default.
*/
SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
@@ -6566,7 +6689,7 @@ SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName);
SQLITE_API int sqlite3_txn_state(sqlite3*,const char *zSchema);
/*
-** CAPI3REF: Allowed return values from [sqlite3_txn_state()]
+** CAPI3REF: Allowed return values from sqlite3_txn_state()
** KEYWORDS: {transaction state}
**
** These constants define the current transaction state of a database file.
@@ -6698,7 +6821,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
** ^Each call to the sqlite3_autovacuum_pages() interface overrides all
** previous invocations for that database connection. ^If the callback
** argument (C) to sqlite3_autovacuum_pages(D,C,P,X) is a NULL pointer,
-** then the autovacuum steps callback is cancelled. The return value
+** then the autovacuum steps callback is canceled. The return value
** from sqlite3_autovacuum_pages() is normally SQLITE_OK, but might
** be some other error code if something goes wrong. The current
** implementation will only return SQLITE_OK or SQLITE_MISUSE, but other
@@ -6764,6 +6887,12 @@ SQLITE_API int sqlite3_autovacuum_pages(
** The exceptions defined in this paragraph might change in a future
** release of SQLite.
**
+** Whether the update hook is invoked before or after the
+** corresponding change is currently unspecified and may differ
+** depending on the type of change. Do not rely on the order of the
+** hook call with regards to the final result of the operation which
+** triggers the hook.
+**
** The update hook implementation must not do anything that will modify
** the database connection that invoked the update hook. Any actions
** to modify the database connection must be deferred until after the
@@ -7217,6 +7346,10 @@ struct sqlite3_module {
/* The methods above are in versions 1 and 2 of the sqlite_module object.
** Those below are for version 3 and greater. */
int (*xShadowName)(const char*);
+ /* The methods above are in versions 1 through 3 of the sqlite_module object.
+ ** Those below are for version 4 and greater. */
+ int (*xIntegrity)(sqlite3_vtab *pVTab, const char *zSchema,
+ const char *zTabName, int mFlags, char **pzErr);
};
/*
@@ -7704,7 +7837,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
** code is returned and the transaction rolled back.
**
** Calling this function with an argument that is not a NULL pointer or an
-** open blob handle results in undefined behaviour. ^Calling this routine
+** open blob handle results in undefined behavior. ^Calling this routine
** with a null pointer (such as would be returned by a failed call to
** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function
** is passed a valid open blob handle, the values returned by the
@@ -7931,9 +8064,11 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
**
** ^(Some systems (for example, Windows 95) do not support the operation
** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try()
-** will always return SQLITE_BUSY. The SQLite core only ever uses
-** sqlite3_mutex_try() as an optimization so this is acceptable
-** behavior.)^
+** will always return SQLITE_BUSY. In most cases the SQLite core only uses
+** sqlite3_mutex_try() as an optimization, so this is acceptable
+** behavior. The exceptions are unix builds that set the
+** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working
+** sqlite3_mutex_try() is required.)^
**
** ^The sqlite3_mutex_leave() routine exits a mutex that was
** previously entered by the same thread. The behavior
@@ -8184,6 +8319,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_PRNG_SAVE 5
#define SQLITE_TESTCTRL_PRNG_RESTORE 6
#define SQLITE_TESTCTRL_PRNG_RESET 7 /* NOT USED */
+#define SQLITE_TESTCTRL_FK_NO_ACTION 7
#define SQLITE_TESTCTRL_BITVEC_TEST 8
#define SQLITE_TESTCTRL_FAULT_INSTALL 9
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
@@ -8191,6 +8327,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_ASSERT 12
#define SQLITE_TESTCTRL_ALWAYS 13
#define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */
+#define SQLITE_TESTCTRL_JSON_SELFCHECK 14
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
#define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
@@ -8226,7 +8363,7 @@ SQLITE_API int sqlite3_test_control(int op, ...);
** The sqlite3_keyword_count() interface returns the number of distinct
** keywords understood by SQLite.
**
-** The sqlite3_keyword_name(N,Z,L) interface finds the N-th keyword and
+** The sqlite3_keyword_name(N,Z,L) interface finds the 0-based N-th keyword and
** makes *Z point to that keyword expressed as UTF8 and writes the number
** of bytes in the keyword into *L. The string that *Z points to is not
** zero-terminated. The sqlite3_keyword_name(N,Z,L) routine returns
@@ -9245,8 +9382,8 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);
** blocked connection already has a registered unlock-notify callback,
** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is
** called with a NULL pointer as its second argument, then any existing
-** unlock-notify callback is cancelled. ^The blocked connections
-** unlock-notify callback may also be cancelled by closing the blocked
+** unlock-notify callback is canceled. ^The blocked connections
+** unlock-notify callback may also be canceled by closing the blocked
** connection using [sqlite3_close()].
**
** The unlock-notify callback is not reentrant. If an application invokes
@@ -9805,24 +9942,45 @@ SQLITE_API const char *sqlite3_vtab_collation(sqlite3_index_info*,int);
** <li value="2"><p>
** ^(If the sqlite3_vtab_distinct() interface returns 2, that means
** that the query planner does not need the rows returned in any particular
-** order, as long as rows with the same values in all "aOrderBy" columns
-** are adjacent.)^ ^(Furthermore, only a single row for each particular
-** combination of values in the columns identified by the "aOrderBy" field
-** needs to be returned.)^ ^It is always ok for two or more rows with the same
-** values in all "aOrderBy" columns to be returned, as long as all such rows
-** are adjacent. ^The virtual table may, if it chooses, omit extra rows
-** that have the same value for all columns identified by "aOrderBy".
-** ^However omitting the extra rows is optional.
+** order, as long as rows with the same values in all columns identified
+** by "aOrderBy" are adjacent.)^ ^(Furthermore, when two or more rows
+** contain the same values for all columns identified by "colUsed", all but
+** one such row may optionally be omitted from the result.)^
+** The virtual table is not required to omit rows that are duplicates
+** over the "colUsed" columns, but if the virtual table can do that without
+** too much extra effort, it could potentially help the query to run faster.
** This mode is used for a DISTINCT query.
** <li value="3"><p>
-** ^(If the sqlite3_vtab_distinct() interface returns 3, that means
-** that the query planner needs only distinct rows but it does need the
-** rows to be sorted.)^ ^The virtual table implementation is free to omit
-** rows that are identical in all aOrderBy columns, if it wants to, but
-** it is not required to omit any rows. This mode is used for queries
+** ^(If the sqlite3_vtab_distinct() interface returns 3, that means the
+** virtual table must return rows in the order defined by "aOrderBy" as
+** if the sqlite3_vtab_distinct() interface had returned 0. However if
+** two or more rows in the result have the same values for all columns
+** identified by "colUsed", then all but one such row may optionally be
+** omitted.)^ Like when the return value is 2, the virtual table
+** is not required to omit rows that are duplicates over the "colUsed"
+** columns, but if the virtual table can do that without
+** too much extra effort, it could potentially help the query to run faster.
+** This mode is used for queries
** that have both DISTINCT and ORDER BY clauses.
** </ol>
**
+** <p>The following table summarizes the conditions under which the
+** virtual table is allowed to set the "orderByConsumed" flag based on
+** the value returned by sqlite3_vtab_distinct(). This table is a
+** restatement of the previous four paragraphs:
+**
+** <table border=1 cellspacing=0 cellpadding=10 width="90%">
+** <tr>
+** <td valign="top">sqlite3_vtab_distinct() return value
+** <td valign="top">Rows are returned in aOrderBy order
+** <td valign="top">Rows with the same value in all aOrderBy columns are adjacent
+** <td valign="top">Duplicates over all colUsed columns may be omitted
+** <tr><td>0<td>yes<td>yes<td>no
+** <tr><td>1<td>no<td>yes<td>no
+** <tr><td>2<td>no<td>yes<td>yes
+** <tr><td>3<td>yes<td>yes<td>yes
+** </table>
+**
** ^For the purposes of comparing virtual table output values to see if the
** values are same value for sorting purposes, two NULL values are considered
** to be the same. In other words, the comparison operator is "IS"
@@ -10549,6 +10707,13 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c
** SQLITE_SERIALIZE_NOCOPY bit is set but no contiguous copy
** of the database exists.
**
+** After the call, if the SQLITE_SERIALIZE_NOCOPY bit had been set,
+** the returned buffer content will remain accessible and unchanged
+** until either the next write operation on the connection or when
+** the connection is closed, and applications must not modify the
+** buffer. If the bit had been clear, the returned buffer will not
+** be accessed by SQLite after the call.
+**
** A call to sqlite3_serialize(D,S,P,F) might return NULL even if the
** SQLITE_SERIALIZE_NOCOPY bit is omitted from argument F if a memory
** allocation error occurs.
@@ -10597,6 +10762,9 @@ SQLITE_API unsigned char *sqlite3_serialize(
** SQLite will try to increase the buffer size using sqlite3_realloc64()
** if writes on the database cause it to grow larger than M bytes.
**
+** Applications must not modify the buffer P or invalidate it before
+** the database connection D is closed.
+**
** The sqlite3_deserialize() interface will fail with SQLITE_BUSY if the
** database is currently in a read transaction or is involved in a backup
** operation.
@@ -10605,6 +10773,13 @@ SQLITE_API unsigned char *sqlite3_serialize(
** S argument to sqlite3_deserialize(D,S,P,N,M,F) is "temp" then the
** function returns SQLITE_ERROR.
**
+** The deserialized database should not be in [WAL mode]. If the database
+** is in WAL mode, then any attempt to use the database file will result
+** in an [SQLITE_CANTOPEN] error. The application can set the
+** [file format version numbers] (bytes 18 and 19) of the input database P
+** to 0x01 prior to invoking sqlite3_deserialize(D,S,P,N,M,F) to force the
+** database file into rollback mode and work around this limitation.
+**
** If sqlite3_deserialize(D,S,P,N,M,F) fails for any reason and if the
** SQLITE_DESERIALIZE_FREEONCLOSE bit is set in argument F, then
** [sqlite3_free()] is invoked on argument P prior to returning.
@@ -11678,6 +11853,18 @@ SQLITE_API int sqlite3changeset_concat(
/*
+** CAPI3REF: Upgrade the Schema of a Changeset/Patchset
+*/
+SQLITE_API int sqlite3changeset_upgrade(
+ sqlite3 *db,
+ const char *zDb,
+ int nIn, const void *pIn, /* Input changeset */
+ int *pnOut, void **ppOut /* OUT: Inverse of input */
+);
+
+
+
+/*
** CAPI3REF: Changegroup Handle
**
** A changegroup is an object used to combine two or more
@@ -11724,6 +11911,38 @@ typedef struct sqlite3_changegroup sqlite3_changegroup;
SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp);
/*
+** CAPI3REF: Add a Schema to a Changegroup
+** METHOD: sqlite3_changegroup_schema
+**
+** This method may be used to optionally enforce the rule that the changesets
+** added to the changegroup handle must match the schema of database zDb
+** ("main", "temp", or the name of an attached database). If
+** sqlite3changegroup_add() is called to add a changeset that is not compatible
+** with the configured schema, SQLITE_SCHEMA is returned and the changegroup
+** object is left in an undefined state.
+**
+** A changeset schema is considered compatible with the database schema in
+** the same way as for sqlite3changeset_apply(). Specifically, for each
+** table in the changeset, there exists a database table with:
+**
+** <ul>
+** <li> The name identified by the changeset, and
+** <li> at least as many columns as recorded in the changeset, and
+** <li> the primary key columns in the same position as recorded in
+** the changeset.
+** </ul>
+**
+** The output of the changegroup object always has the same schema as the
+** database nominated using this function. In cases where changesets passed
+** to sqlite3changegroup_add() have fewer columns than the corresponding table
+** in the database schema, these are filled in using the default column
+** values from the database schema. This makes it possible to combined
+** changesets that have different numbers of columns for a single table
+** within a changegroup, provided that they are otherwise compatible.
+*/
+SQLITE_API int sqlite3changegroup_schema(sqlite3_changegroup*, sqlite3*, const char *zDb);
+
+/*
** CAPI3REF: Add A Changeset To A Changegroup
** METHOD: sqlite3_changegroup
**
@@ -11791,17 +12010,46 @@ SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp);
** If the new changeset contains changes to a table that is already present
** in the changegroup, then the number of columns and the position of the
** primary key columns for the table must be consistent. If this is not the
-** case, this function fails with SQLITE_SCHEMA. If the input changeset
-** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is
-** returned. Or, if an out-of-memory condition occurs during processing, this
-** function returns SQLITE_NOMEM. In all cases, if an error occurs the state
-** of the final contents of the changegroup is undefined.
+** case, this function fails with SQLITE_SCHEMA. Except, if the changegroup
+** object has been configured with a database schema using the
+** sqlite3changegroup_schema() API, then it is possible to combine changesets
+** with different numbers of columns for a single table, provided that
+** they are otherwise compatible.
+**
+** If the input changeset appears to be corrupt and the corruption is
+** detected, SQLITE_CORRUPT is returned. Or, if an out-of-memory condition
+** occurs during processing, this function returns SQLITE_NOMEM.
**
-** If no error occurs, SQLITE_OK is returned.
+** In all cases, if an error occurs the state of the final contents of the
+** changegroup is undefined. If no error occurs, SQLITE_OK is returned.
*/
SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
/*
+** CAPI3REF: Add A Single Change To A Changegroup
+** METHOD: sqlite3_changegroup
+**
+** This function adds the single change currently indicated by the iterator
+** passed as the second argument to the changegroup object. The rules for
+** adding the change are just as described for [sqlite3changegroup_add()].
+**
+** If the change is successfully added to the changegroup, SQLITE_OK is
+** returned. Otherwise, an SQLite error code is returned.
+**
+** The iterator must point to a valid entry when this function is called.
+** If it does not, SQLITE_ERROR is returned and no change is added to the
+** changegroup. Additionally, the iterator must not have been opened with
+** the SQLITE_CHANGESETAPPLY_INVERT flag. In this case SQLITE_ERROR is also
+** returned.
+*/
+SQLITE_API int sqlite3changegroup_add_change(
+ sqlite3_changegroup*,
+ sqlite3_changeset_iter*
+);
+
+
+
+/*
** CAPI3REF: Obtain A Composite Changeset From A Changegroup
** METHOD: sqlite3_changegroup
**
@@ -12062,10 +12310,17 @@ SQLITE_API int sqlite3changeset_apply_v2(
** <li>an insert change if all fields of the conflicting row match
** the row being inserted.
** </ul>
+**
+** <dt>SQLITE_CHANGESETAPPLY_FKNOACTION <dd>
+** If this flag it set, then all foreign key constraints in the target
+** database behave as if they were declared with "ON UPDATE NO ACTION ON
+** DELETE NO ACTION", even if they are actually CASCADE, RESTRICT, SET NULL
+** or SET DEFAULT.
*/
#define SQLITE_CHANGESETAPPLY_NOSAVEPOINT 0x0001
#define SQLITE_CHANGESETAPPLY_INVERT 0x0002
#define SQLITE_CHANGESETAPPLY_IGNORENOOP 0x0004
+#define SQLITE_CHANGESETAPPLY_FKNOACTION 0x0008
/*
** CAPI3REF: Constants Passed To The Conflict Handler
@@ -12598,8 +12853,8 @@ struct Fts5PhraseIter {
** EXTENSION API FUNCTIONS
**
** xUserData(pFts):
-** Return a copy of the context pointer the extension function was
-** registered with.
+** Return a copy of the pUserData pointer passed to the xCreateFunction()
+** API when the extension function was registered.
**
** xColumnTotalSize(pFts, iCol, pnToken):
** If parameter iCol is less than zero, set output variable *pnToken
@@ -12631,8 +12886,11 @@ struct Fts5PhraseIter {
** created with the "columnsize=0" option.
**
** xColumnText:
-** This function attempts to retrieve the text of column iCol of the
-** current document. If successful, (*pz) is set to point to a buffer
+** If parameter iCol is less than zero, or greater than or equal to the
+** number of columns in the table, SQLITE_RANGE is returned.
+**
+** Otherwise, this function attempts to retrieve the text of column iCol of
+** the current document. If successful, (*pz) is set to point to a buffer
** containing the text in utf-8 encoding, (*pn) is set to the size in bytes
** (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
** if an error occurs, an SQLite error code is returned and the final values
@@ -12642,8 +12900,10 @@ struct Fts5PhraseIter {
** Returns the number of phrases in the current query expression.
**
** xPhraseSize:
-** Returns the number of tokens in phrase iPhrase of the query. Phrases
-** are numbered starting from zero.
+** If parameter iCol is less than zero, or greater than or equal to the
+** number of phrases in the current query, as returned by xPhraseCount,
+** 0 is returned. Otherwise, this function returns the number of tokens in
+** phrase iPhrase of the query. Phrases are numbered starting from zero.
**
** xInstCount:
** Set *pnInst to the total number of occurrences of all phrases within
@@ -12659,12 +12919,13 @@ struct Fts5PhraseIter {
** Query for the details of phrase match iIdx within the current row.
** Phrase matches are numbered starting from zero, so the iIdx argument
** should be greater than or equal to zero and smaller than the value
-** output by xInstCount().
+** output by xInstCount(). If iIdx is less than zero or greater than
+** or equal to the value returned by xInstCount(), SQLITE_RANGE is returned.
**
-** Usually, output parameter *piPhrase is set to the phrase number, *piCol
+** Otherwise, output parameter *piPhrase is set to the phrase number, *piCol
** to the column in which it occurs and *piOff the token offset of the
-** first token of the phrase. Returns SQLITE_OK if successful, or an error
-** code (i.e. SQLITE_NOMEM) if an error occurs.
+** first token of the phrase. SQLITE_OK is returned if successful, or an
+** error code (i.e. SQLITE_NOMEM) if an error occurs.
**
** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option.
@@ -12690,6 +12951,10 @@ struct Fts5PhraseIter {
** Invoking Api.xUserData() returns a copy of the pointer passed as
** the third argument to pUserData.
**
+** If parameter iPhrase is less than zero, or greater than or equal to
+** the number of phrases in the query, as returned by xPhraseCount(),
+** this function returns SQLITE_RANGE.
+**
** If the callback function returns any value other than SQLITE_OK, the
** query is abandoned and the xQueryPhrase function returns immediately.
** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
@@ -12804,9 +13069,42 @@ struct Fts5PhraseIter {
**
** xPhraseNextColumn()
** See xPhraseFirstColumn above.
+**
+** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken)
+** This is used to access token iToken of phrase iPhrase of the current
+** query. Before returning, output parameter *ppToken is set to point
+** to a buffer containing the requested token, and *pnToken to the
+** size of this buffer in bytes.
+**
+** If iPhrase or iToken are less than zero, or if iPhrase is greater than
+** or equal to the number of phrases in the query as reported by
+** xPhraseCount(), or if iToken is equal to or greater than the number of
+** tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken
+ are both zeroed.
+**
+** The output text is not a copy of the query text that specified the
+** token. It is the output of the tokenizer module. For tokendata=1
+** tables, this includes any embedded 0x00 and trailing data.
+**
+** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken)
+** This is used to access token iToken of phrase hit iIdx within the
+** current row. If iIdx is less than zero or greater than or equal to the
+** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
+** output variable (*ppToken) is set to point to a buffer containing the
+** matching document token, and (*pnToken) to the size of that buffer in
+** bytes. This API is not available if the specified token matches a
+** prefix query term. In that case both output variables are always set
+** to 0.
+**
+** The output text is not a copy of the document text that was tokenized.
+** It is the output of the tokenizer module. For tokendata=1 tables, this
+** includes any embedded 0x00 and trailing data.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option.
*/
struct Fts5ExtensionApi {
- int iVersion; /* Currently always set to 2 */
+ int iVersion; /* Currently always set to 3 */
void *(*xUserData)(Fts5Context*);
@@ -12841,6 +13139,13 @@ struct Fts5ExtensionApi {
int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
+
+ /* Below this point are iVersion>=3 only */
+ int (*xQueryToken)(Fts5Context*,
+ int iPhrase, int iToken,
+ const char **ppToken, int *pnToken
+ );
+ int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
};
/*
diff --git a/src/libs/3rdparty/sqlite/sqlite3ext.h b/src/libs/3rdparty/sqlite/sqlite3ext.h
index 7116380992..ae0949baf7 100644
--- a/src/libs/3rdparty/sqlite/sqlite3ext.h
+++ b/src/libs/3rdparty/sqlite/sqlite3ext.h
@@ -363,6 +363,9 @@ struct sqlite3_api_routines {
int (*is_interrupted)(sqlite3*);
/* Version 3.43.0 and later */
int (*stmt_explain)(sqlite3_stmt*,int);
+ /* Version 3.44.0 and later */
+ void *(*get_clientdata)(sqlite3*,const char*);
+ int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
};
/*
@@ -693,6 +696,9 @@ typedef int (*sqlite3_loadext_entry)(
#define sqlite3_is_interrupted sqlite3_api->is_interrupted
/* Version 3.43.0 and later */
#define sqlite3_stmt_explain sqlite3_api->stmt_explain
+/* Version 3.44.0 and later */
+#define sqlite3_get_clientdata sqlite3_api->get_clientdata
+#define sqlite3_set_clientdata sqlite3_api->set_clientdata
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
diff --git a/src/libs/advanceddockingsystem/dockmanager.cpp b/src/libs/advanceddockingsystem/dockmanager.cpp
index b718302f4c..60586ff4f4 100644
--- a/src/libs/advanceddockingsystem/dockmanager.cpp
+++ b/src/libs/advanceddockingsystem/dockmanager.cpp
@@ -1531,62 +1531,31 @@ QByteArray DockManager::loadFile(const FilePath &filePath)
QString DockManager::readDisplayName(const FilePath &filePath)
{
- auto data = loadFile(filePath);
-
- if (data.isEmpty())
- return {};
-
- auto tmp = data.startsWith("<?xml") ? data : qUncompress(data);
-
- DockingStateReader reader(tmp);
- if (!reader.readNextStartElement())
- return {};
-
- if (reader.name() != QLatin1String("QtAdvancedDockingSystem"))
- return {};
-
- return reader.attributes().value(workspaceDisplayNameAttribute.toString()).toString();
+ return readAttribute(filePath, workspaceDisplayNameAttribute);
}
bool DockManager::writeDisplayName(const FilePath &filePath, const QString &displayName)
{
- const expected_str<QByteArray> content = filePath.fileContents();
-
- QTC_ASSERT_EXPECTED(content, return false);
-
- QDomDocument doc;
- QString error_msg;
- int error_line, error_col;
- if (!doc.setContent(*content, &error_msg, &error_line, &error_col)) {
- qWarning() << QString("XML error on line %1, col %2: %3")
- .arg(error_line)
- .arg(error_col)
- .arg(error_msg);
- return false;
- }
-
- QDomElement docElem = doc.documentElement();
- docElem.setAttribute(workspaceDisplayNameAttribute.toString(), displayName);
+ return writeAttribute(filePath, workspaceDisplayNameAttribute, displayName);
+}
- const expected_str<void> result = write(filePath, doc.toByteArray(workspaceXmlFormattingIndent));
- if (!result) {
- qWarning() << "Could not write display name" << displayName << "to" << filePath << ":"
- << result.error();
- return false;
- }
+QString DockManager::readMcusEnabled(const FilePath &filePath)
+{
+ return readAttribute(filePath, workspaceMcusEnabledAttribute);
+}
- return true;
+bool DockManager::writeMcusEnabled(const FilePath &filePath, const QString &mcusEnabled)
+{
+ return writeAttribute(filePath, workspaceMcusEnabledAttribute, mcusEnabled);
}
-QString DockManager::readMcusEnabled(const FilePath &filePath)
+QString DockManager::readAttribute(const FilePath &filePath, QStringView key)
{
auto data = loadFile(filePath);
-
if (data.isEmpty())
return {};
auto tmp = data.startsWith("<?xml") ? data : qUncompress(data);
-
DockingStateReader reader(tmp);
if (!reader.readNextStartElement())
return {};
@@ -1594,13 +1563,12 @@ QString DockManager::readMcusEnabled(const FilePath &filePath)
if (reader.name() != QLatin1String("QtAdvancedDockingSystem"))
return {};
- return reader.attributes().value(workspaceMcusEnabledAttribute.toString()).toString();
+ return reader.attributes().value(key.toString()).toString();
}
-bool DockManager::writeMcusEnabled(const FilePath &filePath, const QString &mcusEnabled)
+bool DockManager::writeAttribute(const FilePath &filePath, QStringView key, const QString &value)
{
const expected_str<QByteArray> content = filePath.fileContents();
-
QTC_ASSERT_EXPECTED(content, return false);
QDomDocument doc;
@@ -1608,23 +1576,19 @@ bool DockManager::writeMcusEnabled(const FilePath &filePath, const QString &mcus
int error_line, error_col;
if (!doc.setContent(*content, &error_msg, &error_line, &error_col)) {
qWarning() << QString("XML error on line %1, col %2: %3")
- .arg(error_line)
- .arg(error_col)
- .arg(error_msg);
+ .arg(error_line).arg(error_col).arg(error_msg);
return false;
}
QDomElement docElem = doc.documentElement();
- docElem.setAttribute(workspaceMcusEnabledAttribute.toString(), mcusEnabled);
+ docElem.setAttribute(key.toString(), value);
const expected_str<void> result = write(filePath, doc.toByteArray(workspaceXmlFormattingIndent));
- if (!result) {
- qWarning() << "Could not write mcusEnabled" << mcusEnabled << "to" << filePath << ":"
- << result.error();
- return false;
- }
+ if (result)
+ return true;
- return true;
+ qWarning() << "Could not write" << key << value << "to" << filePath << ":" << result.error();
+ return false;
}
expected_str<void> DockManager::write(const FilePath &filePath, const QByteArray &data)
diff --git a/src/libs/advanceddockingsystem/dockmanager.h b/src/libs/advanceddockingsystem/dockmanager.h
index 23aa21a578..b092eedef0 100644
--- a/src/libs/advanceddockingsystem/dockmanager.h
+++ b/src/libs/advanceddockingsystem/dockmanager.h
@@ -458,7 +458,7 @@ public:
/**
* This function sets the tool button style for the given dock widget state. It is possible to
- * switch the tool button style depending on the state. If a dock widget is floating, then here
+ * switch the tool button style depending on the state. If a dock widget is floating, then here
* are more space and it is possible to select a style that requires more space like
* Qt::ToolButtonTextUnderIcon. For the docked state Qt::ToolButtonIconOnly might be better.
*/
@@ -783,6 +783,9 @@ signals:
void lockWorkspaceChanged();
private:
+ static QString readAttribute(const Utils::FilePath &filePath, QStringView key);
+ static bool writeAttribute(const Utils::FilePath &filePath, QStringView key,
+ const QString &value);
static Utils::expected_str<void> write(const Utils::FilePath &filePath, const QByteArray &data);
Utils::expected_str<QByteArray> loadWorkspace(const Workspace &workspace) const;
diff --git a/src/libs/advanceddockingsystem/workspaceview.cpp b/src/libs/advanceddockingsystem/workspaceview.cpp
index 26983c2bcd..116ad95670 100644
--- a/src/libs/advanceddockingsystem/workspaceview.cpp
+++ b/src/libs/advanceddockingsystem/workspaceview.cpp
@@ -289,7 +289,7 @@ void WorkspaceView::keyPressEvent(QKeyEvent *event)
void WorkspaceView::dropEvent(QDropEvent *event)
{
- const QModelIndex dropIndex = indexAt(event->pos());
+ const QModelIndex dropIndex = indexAt(event->position().toPoint());
const DropIndicatorPosition dropIndicator = dropIndicatorPosition();
const auto droppedWorkspaces = selectedWorkspaces();
diff --git a/src/libs/cplusplus/CppDocument.cpp b/src/libs/cplusplus/CppDocument.cpp
index cdc036f177..0b0d366925 100644
--- a/src/libs/cplusplus/CppDocument.cpp
+++ b/src/libs/cplusplus/CppDocument.cpp
@@ -356,6 +356,15 @@ void Document::addUndefinedMacroUse(const QByteArray &name,
_undefinedMacroUses.append(use);
}
+int Document::pragmaOnceLine() const
+{
+ for (const Pragma &p : _pragmas) {
+ if (p.tokens.size() == 1 && p.tokens.first() == "once")
+ return p.line;
+ }
+ return -1;
+}
+
/*!
\class Document::MacroUse
\brief The MacroUse class represents the usage of a macro in a
diff --git a/src/libs/cplusplus/CppDocument.h b/src/libs/cplusplus/CppDocument.h
index 8834c7538a..ca32807309 100644
--- a/src/libs/cplusplus/CppDocument.h
+++ b/src/libs/cplusplus/CppDocument.h
@@ -11,15 +11,14 @@
#include <utils/filepath.h>
-#include <QSharedPointer>
+#include <QAtomicInt>
+#include <QByteArrayList>
#include <QDateTime>
-#include <QHash>
#include <QFuture>
-#include <QAtomicInt>
+#include <QHash>
+#include <QSharedPointer>
namespace CPlusPlus {
-
-class Macro;
class MacroArgumentReference;
class LookupContext;
@@ -55,6 +54,9 @@ public:
void addUndefinedMacroUse(const QByteArray &name,
int bytesOffset, int utf16charsOffset);
+ void appendPragma(const Pragma &pragma) { _pragmas << pragma; }
+ int pragmaOnceLine() const;
+
Control *control() const { return _control; }
Control *swapControl(Control *newControl);
TranslationUnit *translationUnit() const { return _translationUnit; }
@@ -353,6 +355,8 @@ private:
/// the macro name of the include guard, if there is one.
QByteArray _includeGuardMacroName;
+ QList<Pragma> _pragmas;
+
QByteArray m_fingerprint;
QByteArray _source;
diff --git a/src/libs/cplusplus/FastPreprocessor.cpp b/src/libs/cplusplus/FastPreprocessor.cpp
index 8dcc8509b1..88acd386e4 100644
--- a/src/libs/cplusplus/FastPreprocessor.cpp
+++ b/src/libs/cplusplus/FastPreprocessor.cpp
@@ -85,6 +85,12 @@ void FastPreprocessor::macroAdded(const Macro &macro)
_currentDoc->appendMacro(macro);
}
+void FastPreprocessor::pragmaAdded(const Pragma &pragma)
+{
+ Q_ASSERT(_currentDoc);
+ _currentDoc->appendPragma(pragma);
+}
+
static const Macro revision(const Snapshot &s, const Macro &m)
{
if (Document::Ptr d = s.document(m.filePath())) {
diff --git a/src/libs/cplusplus/FastPreprocessor.h b/src/libs/cplusplus/FastPreprocessor.h
index 641a0164cf..cf1ac86cdf 100644
--- a/src/libs/cplusplus/FastPreprocessor.h
+++ b/src/libs/cplusplus/FastPreprocessor.h
@@ -40,7 +40,7 @@ public:
const Utils::FilePaths &initialIncludes = {});
virtual void macroAdded(const Macro &);
-
+ virtual void pragmaAdded(const Pragma &pragma);
virtual void passedMacroDefinitionCheck(int, int, int, const Macro &);
virtual void failedMacroDefinitionCheck(int, int, const ByteArrayRef &);
diff --git a/src/libs/cplusplus/Macro.h b/src/libs/cplusplus/Macro.h
index 26a33ea849..d5288dadf1 100644
--- a/src/libs/cplusplus/Macro.h
+++ b/src/libs/cplusplus/Macro.h
@@ -157,4 +157,11 @@ private:
};
};
+class CPLUSPLUS_EXPORT Pragma
+{
+public:
+ QByteArrayList tokens;
+ int line;
+};
+
} // CPlusPlus
diff --git a/src/libs/cplusplus/PreprocessorClient.h b/src/libs/cplusplus/PreprocessorClient.h
index 2d0df7faf9..2c7ad0a42c 100644
--- a/src/libs/cplusplus/PreprocessorClient.h
+++ b/src/libs/cplusplus/PreprocessorClient.h
@@ -14,6 +14,7 @@ namespace CPlusPlus {
class ByteArrayRef;
class Macro;
+class Pragma;
class CPLUSPLUS_EXPORT MacroArgumentReference
{
@@ -61,6 +62,7 @@ public:
virtual ~Client() = 0;
virtual void macroAdded(const Macro &macro) = 0;
+ virtual void pragmaAdded(const Pragma &pragma) = 0;
virtual void passedMacroDefinitionCheck(int bytesOffset, int utf16charsOffset,
int line, const Macro &macro) = 0;
diff --git a/src/libs/cplusplus/ResolveExpression.cpp b/src/libs/cplusplus/ResolveExpression.cpp
index 4fc1cd2d56..e779c447bf 100644
--- a/src/libs/cplusplus/ResolveExpression.cpp
+++ b/src/libs/cplusplus/ResolveExpression.cpp
@@ -1062,6 +1062,8 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList<LookupItem> &bas
}
typedefsResolver.resolve(&ty, &scope, r.binding());
+ if (auto ref = ty->asReferenceType()) // deref if needed
+ ty = ref->elementType();
if (Q_UNLIKELY(debug))
qDebug() << "- after typedef resolving:" << oo.prettyType(ty);
@@ -1082,6 +1084,9 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList<LookupItem> &bas
if (ClassOrNamespace *binding = findClass(type, scope))
return binding;
+ if (ClassOrNamespace *binding = findClass(type, r.scope())) // local classes and structs
+ return binding;
+
} else {
ClassOrNamespace *binding
= findClassForTemplateParameterInExpressionScope(r.binding(),
@@ -1176,6 +1181,9 @@ ClassOrNamespace *ResolveExpression::baseExpression(const QList<LookupItem> &bas
if (ClassOrNamespace *binding = findClass(ty, scope, enclosingBinding))
return binding;
+
+ if (ClassOrNamespace *binding = findClass(ty, r.scope())) // local classes and structs
+ return binding;
}
}
diff --git a/src/libs/cplusplus/pp-engine.cpp b/src/libs/cplusplus/pp-engine.cpp
index 74ca1ceb51..a1343e5471 100644
--- a/src/libs/cplusplus/pp-engine.cpp
+++ b/src/libs/cplusplus/pp-engine.cpp
@@ -1626,10 +1626,10 @@ void Preprocessor::handlePreprocessorDirective(PPToken *tk)
static const QByteArray ppInclude("include");
static const QByteArray ppIncludeNext("include_next");
static const QByteArray ppImport("import");
+ static const QByteArray ppPragma("pragma");
//### TODO:
// line
// error
- // pragma
if (tk->is(T_IDENTIFIER)) {
const ByteArrayRef directive = tk->asByteArrayRef();
@@ -1640,6 +1640,8 @@ void Preprocessor::handlePreprocessorDirective(PPToken *tk)
handleIfDefDirective(true, tk);
} else if (directive == ppEndIf) {
handleEndIfDirective(tk, poundToken);
+ } else if (directive == ppPragma) {
+ handlePragmaDirective(tk);
} else {
m_state.updateIncludeGuardState(State::IncludeGuardStateHint_OtherToken);
@@ -1866,6 +1868,23 @@ void Preprocessor::handleDefineDirective(PPToken *tk)
m_client->macroAdded(macro);
}
+void Preprocessor::handlePragmaDirective(PPToken *tk)
+{
+ Pragma pragma;
+ pragma.line = tk->lineno;
+ lex(tk); // consume "pragma" token
+
+ while (isContinuationToken(*tk)) {
+ if (!consumeComments(tk))
+ return;
+ pragma.tokens << tk->asByteArrayRef().toByteArray();
+ lex(tk);
+ }
+
+ if (m_client)
+ m_client->pragmaAdded(pragma);
+}
+
QByteArray Preprocessor::expand(PPToken *tk, PPToken *lastConditionToken)
{
unsigned line = tk->lineno;
diff --git a/src/libs/cplusplus/pp-engine.h b/src/libs/cplusplus/pp-engine.h
index 2163380dea..537f505b95 100644
--- a/src/libs/cplusplus/pp-engine.h
+++ b/src/libs/cplusplus/pp-engine.h
@@ -210,6 +210,7 @@ private:
void handlePreprocessorDirective(PPToken *tk);
void handleIncludeDirective(PPToken *tk, bool includeNext);
void handleDefineDirective(PPToken *tk);
+ void handlePragmaDirective(PPToken *tk);
QByteArray expand(PPToken *tk, PPToken *lastConditionToken = nullptr);
const Internal::PPToken evalExpression(PPToken *tk, Value &result);
void handleIfDirective(PPToken *tk);
diff --git a/src/libs/extensionsystem/CMakeLists.txt b/src/libs/extensionsystem/CMakeLists.txt
index fb836631d9..a1768577b0 100644
--- a/src/libs/extensionsystem/CMakeLists.txt
+++ b/src/libs/extensionsystem/CMakeLists.txt
@@ -11,7 +11,7 @@ add_qtc_library(ExtensionSystem
pluginerroroverview.cpp pluginerroroverview.h
pluginerrorview.cpp pluginerrorview.h
pluginmanager.cpp pluginmanager.h pluginmanager_p.h
- pluginspec.cpp pluginspec.h pluginspec_p.h
+ pluginspec.cpp pluginspec.h
pluginview.cpp pluginview.h
EXPLICIT_MOC
pluginmanager.h
@@ -20,10 +20,16 @@ add_qtc_library(ExtensionSystem
find_package(Qt6 COMPONENTS Test QUIET)
+# If ExtensionSystem was compiled with QtTest, it should provide the test options
+# and the test related API, regardless if Qt Creator was compiled with tests or not,
+# and regardless if an external plugin is compiled with tests or not.
+# ExtensionSystem may not require QtTest in public headers though.
+# API with EXTENSIONSYSTEM_TEST_EXPORT (like CppPluginSpec) should only be exported
+# if Qt Creator is compiled with tests.
extend_qtc_library(ExtensionSystem
CONDITION TARGET Qt::Test
DEPENDS Qt::Test
- DEFINES WITH_TESTS
+ PUBLIC_DEFINES EXTENSIONSYSTEM_WITH_TESTOPTION
)
extend_qtc_library(ExtensionSystem
diff --git a/src/libs/extensionsystem/extensionsystem.qbs b/src/libs/extensionsystem/extensionsystem.qbs
index 8748c27ba6..c34e97e754 100644
--- a/src/libs/extensionsystem/extensionsystem.qbs
+++ b/src/libs/extensionsystem/extensionsystem.qbs
@@ -2,6 +2,7 @@ QtcLibrary {
name: "ExtensionSystem"
cpp.defines: base.concat(["EXTENSIONSYSTEM_LIBRARY", "IDE_TEST_DIR=\".\""])
+ .concat(qtc.withPluginTests ? ["EXTENSIONSYSTEM_WITH_TESTOPTION"] : [])
Depends { name: "Qt"; submodules: ["core", "widgets"] }
Depends { name: "Qt.testlib"; condition: qtc.withPluginTests }
@@ -29,12 +30,13 @@ QtcLibrary {
"pluginmanager_p.h",
"pluginspec.cpp",
"pluginspec.h",
- "pluginspec_p.h",
"pluginview.cpp",
"pluginview.h",
]
Export {
Depends { name: "Qt.core" }
+ Depends { name: "qtc" }
+ cpp.defines: qtc.withPluginTests ? ["EXTENSIONSYSTEM_WITH_TESTOPTION"] : []
}
}
diff --git a/src/libs/extensionsystem/invoker.h b/src/libs/extensionsystem/invoker.h
index 8cd27eb175..ada8aa7cf5 100644
--- a/src/libs/extensionsystem/invoker.h
+++ b/src/libs/extensionsystem/invoker.h
@@ -38,7 +38,7 @@ private:
InvokerBase(const InvokerBase &); // Unimplemented.
template <class T> const char *typeName()
{
- return QMetaType::typeName(qMetaTypeId<T>());
+ return QMetaType(qMetaTypeId<T>()).name();
}
QObject *target;
QGenericArgument arg[10];
diff --git a/src/libs/extensionsystem/optionsparser.cpp b/src/libs/extensionsystem/optionsparser.cpp
index 205442488e..21dfba16ec 100644
--- a/src/libs/extensionsystem/optionsparser.cpp
+++ b/src/libs/extensionsystem/optionsparser.cpp
@@ -6,7 +6,6 @@
#include "extensionsystemtr.h"
#include "pluginmanager.h"
#include "pluginmanager_p.h"
-#include "pluginspec_p.h"
#include <utils/algorithm.h>
@@ -65,7 +64,7 @@ bool OptionsParser::parse()
continue;
if (checkForNoCrashcheckOption())
continue;
-#ifdef WITH_TESTS
+#ifdef EXTENSIONSYSTEM_WITH_TESTOPTION
if (checkForTestOptions())
continue;
if (checkForScenarioOption())
@@ -119,7 +118,7 @@ bool OptionsParser::checkForTestOptions()
} else {
m_pmPrivate->testSpecs.emplace_back(spec, args);
}
- } else {
+ } else {
if (m_errorString)
*m_errorString = Tr::tr("The plugin \"%1\" does not exist.").arg(pluginName);
m_hasError = true;
@@ -177,7 +176,7 @@ bool OptionsParser::checkForLoadOption()
if (nextToken(RequiredToken)) {
if (m_currentArg == QLatin1String("all")) {
for (PluginSpec *spec : std::as_const(m_pmPrivate->pluginSpecs))
- spec->d->setForceEnabled(true);
+ spec->setForceEnabled(true);
m_isDependencyRefreshNeeded = true;
} else {
PluginSpec *spec = m_pmPrivate->pluginByName(m_currentArg);
@@ -186,7 +185,7 @@ bool OptionsParser::checkForLoadOption()
*m_errorString = Tr::tr("The plugin \"%1\" does not exist.").arg(m_currentArg);
m_hasError = true;
} else {
- spec->d->setForceEnabled(true);
+ spec->setForceEnabled(true);
m_isDependencyRefreshNeeded = true;
}
}
@@ -202,7 +201,7 @@ bool OptionsParser::checkForNoLoadOption()
if (nextToken(RequiredToken)) {
if (m_currentArg == QLatin1String("all")) {
for (PluginSpec *spec : std::as_const(m_pmPrivate->pluginSpecs))
- spec->d->setForceDisabled(true);
+ spec->setForceDisabled(true);
m_isDependencyRefreshNeeded = true;
} else {
PluginSpec *spec = m_pmPrivate->pluginByName(m_currentArg);
@@ -211,10 +210,10 @@ bool OptionsParser::checkForNoLoadOption()
*m_errorString = Tr::tr("The plugin \"%1\" does not exist.").arg(m_currentArg);
m_hasError = true;
} else {
- spec->d->setForceDisabled(true);
+ spec->setForceDisabled(true);
// recursively disable all plugins that require this plugin
for (PluginSpec *dependantSpec : PluginManager::pluginsRequiringPlugin(spec))
- dependantSpec->d->setForceDisabled(true);
+ dependantSpec->setForceDisabled(true);
m_isDependencyRefreshNeeded = true;
}
}
@@ -292,10 +291,10 @@ bool OptionsParser::checkForUnknownOption()
void OptionsParser::forceDisableAllPluginsExceptTestedAndForceEnabled()
{
for (const PluginManagerPrivate::TestSpec &testSpec : m_pmPrivate->testSpecs)
- testSpec.pluginSpec->d->setForceEnabled(true);
+ testSpec.pluginSpec->setForceEnabled(true);
for (PluginSpec *spec : std::as_const(m_pmPrivate->pluginSpecs)) {
if (!spec->isForceEnabled() && !spec->isRequired())
- spec->d->setForceDisabled(true);
+ spec->setForceDisabled(true);
}
}
diff --git a/src/libs/extensionsystem/plugindetailsview.cpp b/src/libs/extensionsystem/plugindetailsview.cpp
index 73fa2b1229..35e646bad7 100644
--- a/src/libs/extensionsystem/plugindetailsview.cpp
+++ b/src/libs/extensionsystem/plugindetailsview.cpp
@@ -8,6 +8,7 @@
#include "pluginspec.h"
#include <utils/algorithm.h>
+#include <utils/infolabel.h>
#include <utils/layoutbuilder.h>
#include <QCoreApplication>
@@ -54,6 +55,7 @@ public:
, copyright(createContentsLabel())
, license(createTextEdit())
, dependencies(new QListWidget(q))
+ , softLoadable(new Utils::InfoLabel)
{
using namespace Layouting;
@@ -70,7 +72,8 @@ public:
Tr::tr("Description:"), description, br,
Tr::tr("Copyright:"), copyright, br,
Tr::tr("License:"), license, br,
- Tr::tr("Dependencies:"), dependencies,
+ Tr::tr("Dependencies:"), dependencies, br,
+ Tr::tr("Loadable without restart:"), softLoadable, br,
noMargin
}.attachTo(q);
// clang-format on
@@ -90,6 +93,7 @@ public:
QLabel *copyright = nullptr;
QTextEdit *license = nullptr;
QListWidget *dependencies = nullptr;
+ Utils::InfoLabel *softLoadable = nullptr;
private:
QLabel *createContentsLabel() {
@@ -142,7 +146,7 @@ void PluginDetailsView::update(PluginSpec *spec)
d->vendor->setText(spec->vendor());
d->component->setText(spec->category().isEmpty() ? Tr::tr("None") : spec->category());
d->url->setText(QString::fromLatin1("<a href=\"%1\">%1</a>").arg(spec->url()));
- d->location->setText(QDir::toNativeSeparators(spec->filePath()));
+ d->location->setText(spec->filePath().toUserOutput());
const QString pattern = spec->platformSpecification().pattern();
const QString platform = pattern.isEmpty() ? Tr::tr("All") : pattern;
const QString platformString = Tr::tr("%1 (current: \"%2\")")
@@ -157,6 +161,8 @@ void PluginDetailsView::update(PluginSpec *spec)
d->license->setText(spec->license());
d->dependencies->addItems(Utils::transform<QList>(spec->dependencies(),
&PluginDependency::toString));
+ d->softLoadable->setType(spec->isSoftLoadable() ? Utils::InfoLabel::Ok
+ : Utils::InfoLabel::NotOk);
}
void PluginDetailsView::showModal(QWidget *parent, PluginSpec *spec)
diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp
index fcb3062faa..4fbaba01e5 100644
--- a/src/libs/extensionsystem/pluginmanager.cpp
+++ b/src/libs/extensionsystem/pluginmanager.cpp
@@ -8,7 +8,6 @@
#include "optionsparser.h"
#include "pluginmanager_p.h"
#include "pluginspec.h"
-#include "pluginspec_p.h"
#include <nanotrace/nanotrace.h>
@@ -18,8 +17,8 @@
#include <utils/futuresynchronizer.h>
#include <utils/hostosinfo.h>
#include <utils/mimeutils.h>
-#include <utils/process.h>
#include <utils/qtcassert.h>
+#include <utils/qtcprocess.h>
#include <utils/qtcsettings.h>
#include <utils/threadutils.h>
@@ -35,6 +34,7 @@
#include <QLibraryInfo>
#include <QMessageBox>
#include <QMetaProperty>
+#include <QPluginLoader>
#include <QPushButton>
#include <QScopeGuard>
#include <QSysInfo>
@@ -42,7 +42,7 @@
#include <QTimer>
#include <QWriteLocker>
-#ifdef WITH_TESTS
+#ifdef EXTENSIONSYSTEM_WITH_TESTOPTION
#include <utils/hostosinfo.h>
#include <QTest>
#include <QThread>
@@ -50,6 +50,7 @@
#include <functional>
#include <memory>
+#include <type_traits>
Q_LOGGING_CATEGORY(pluginLog, "qtc.extensionsystem", QtWarningMsg)
@@ -327,6 +328,11 @@ void PluginManager::loadPluginsAtRuntime(const QSet<PluginSpec *> &plugins)
d->loadPluginsAtRuntime(plugins);
}
+void PluginManager::addPlugins(const PluginSpecs &specs)
+{
+ d->addPlugins(specs);
+}
+
/*!
Returns \c true if any plugin has errors even though it is enabled.
Most useful to call after loadPlugins().
@@ -428,17 +434,12 @@ QString PluginManager::systemInformation()
return result;
}
-FutureSynchronizer *PluginManager::futureSynchronizer()
-{
- return d->m_futureSynchronizer.get();
-}
-
/*!
The list of paths were the plugin manager searches for plugins.
\sa setPluginPaths()
*/
-QStringList PluginManager::pluginPaths()
+FilePaths PluginManager::pluginPaths()
{
return d->pluginPaths;
}
@@ -450,7 +451,7 @@ QStringList PluginManager::pluginPaths()
\sa pluginPaths()
\sa loadPlugins()
*/
-void PluginManager::setPluginPaths(const QStringList &paths)
+void PluginManager::setPluginPaths(const FilePaths &paths)
{
d->setPluginPaths(paths);
}
@@ -551,12 +552,12 @@ QStringList PluginManager::argumentsForRestart()
\sa setPluginPaths()
*/
-const QVector<PluginSpec *> PluginManager::plugins()
+const PluginSpecs PluginManager::plugins()
{
return d->pluginSpecs;
}
-QHash<QString, QVector<PluginSpec *>> PluginManager::pluginCollections()
+QHash<QString, PluginSpecs> PluginManager::pluginCollections()
{
return d->pluginCategories;
}
@@ -636,11 +637,13 @@ void PluginManager::remoteArguments(const QString &serializedArgument, QObject *
for (const PluginSpec *ps : plugins()) {
if (ps->state() == PluginSpec::Running) {
const QStringList pluginOptions = subList(serializedArguments, QLatin1Char(':') + ps->name());
- QObject *socketParent = ps->plugin()->remoteCommand(pluginOptions, workingDirectory,
- arguments);
- if (socketParent && socket) {
- socket->setParent(socketParent);
- socket = nullptr;
+ if (IPlugin *plugin = ps->plugin()) {
+ QObject *socketParent
+ = plugin->remoteCommand(pluginOptions, workingDirectory, arguments);
+ if (socketParent && socket) {
+ socket->setParent(socketParent);
+ socket = nullptr;
+ }
}
}
}
@@ -740,7 +743,7 @@ void PluginManager::formatOptions(QTextStream &str, int optionIndentation, int d
QLatin1String("Disable startup check for previously crashed instance"),
optionIndentation,
descriptionIndentation);
-#ifdef WITH_TESTS
+#ifdef EXTENSIONSYSTEM_WITH_TESTOPTION
formatOption(str, QString::fromLatin1(OptionsParser::TEST_OPTION)
+ QLatin1String(" <plugin>[,testfunction[:testdata]]..."), QString(),
QLatin1String("Run plugin's tests (by default a separate settings path is used)"),
@@ -793,7 +796,7 @@ bool PluginManager::testRunRequested()
return !d->testSpecs.empty();
}
-#ifdef WITH_TESTS
+#ifdef EXTENSIONSYSTEM_WITH_TESTOPTION
// Called in plugin initialization, the scenario function will be called later, from main
bool PluginManager::registerScenario(const QString &scenarioId, std::function<bool()> scenarioStarter)
{
@@ -896,8 +899,10 @@ PluginManager::ProcessData PluginManager::creatorProcessData()
/*!
Returns a list of plugins in load order.
*/
-QVector<PluginSpec *> PluginManager::loadQueue()
+PluginSpecs PluginManager::loadQueue()
{
+ // Ensure order preservation
+ static_assert(std::is_same<PluginSpecs, QList<class PluginSpec *> >::value);
return d->loadQueue();
}
@@ -906,14 +911,6 @@ QVector<PluginSpec *> PluginManager::loadQueue()
/*!
\internal
*/
-PluginSpec *PluginManagerPrivate::createSpec()
-{
- return new PluginSpec();
-}
-
-/*!
- \internal
-*/
void PluginManagerPrivate::setSettings(QtcSettings *s)
{
if (settings)
@@ -935,14 +932,6 @@ void PluginManagerPrivate::setGlobalSettings(QtcSettings *s)
globalSettings->setParent(this);
}
-/*!
- \internal
-*/
-PluginSpecPrivate *PluginManagerPrivate::privateSpec(PluginSpec *spec)
-{
- return spec->d;
-}
-
void PluginManagerPrivate::startDelayedInitialize()
{
Utils::setMimeStartupPhase(MimeStartupPhase::PluginsDelayedInitializing);
@@ -954,8 +943,8 @@ void PluginManagerPrivate::startDelayedInitialize()
delayedInitializeQueue.pop();
NANOTRACE_SCOPE(specName, specName + "::delayedInitialized");
profilingReport(">delayedInitialize", spec);
- bool delay = spec->d->delayedInitialize();
- profilingReport("<delayedInitialize", spec, &spec->d->performanceData.delayedInitialize);
+ bool delay = spec->delayedInitialize();
+ profilingReport("<delayedInitialize", spec, &spec->performanceData().delayedInitialize);
if (delay) // give UI a bit of breathing space, but prevent user interaction
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
}
@@ -967,17 +956,18 @@ void PluginManagerPrivate::startDelayedInitialize()
}
NANOTRACE_SHUTDOWN();
emit q->initializationDone();
-#ifdef WITH_TESTS
- if (PluginManager::testRunRequested())
- startTests();
- else if (PluginManager::isScenarioRequested()) {
- if (PluginManager::runScenario()) {
- const QString info = QString("Successfully started scenario \"%1\"...").arg(d->m_requestedScenario);
- qInfo("%s", qPrintable(info));
- } else {
- QMetaObject::invokeMethod(this, [] { emit m_instance->scenarioFinished(1); });
- }
+#ifdef EXTENSIONSYSTEM_WITH_TESTOPTION
+ if (PluginManager::testRunRequested())
+ startTests();
+ else if (PluginManager::isScenarioRequested()) {
+ if (PluginManager::runScenario()) {
+ const QString info
+ = QString("Successfully started scenario \"%1\"...").arg(d->m_requestedScenario);
+ qInfo("%s", qPrintable(info));
+ } else {
+ QMetaObject::invokeMethod(this, [] { emit m_instance->scenarioFinished(1); });
}
+ }
#endif
}
@@ -986,10 +976,7 @@ void PluginManagerPrivate::startDelayedInitialize()
*/
PluginManagerPrivate::PluginManagerPrivate(PluginManager *pluginManager) :
q(pluginManager)
-{
- m_futureSynchronizer.reset(new FutureSynchronizer);
-}
-
+{}
/*!
\internal
@@ -1042,7 +1029,7 @@ void PluginManagerPrivate::stopAll()
m_isShuttingDown = true;
delayedInitializeTimer.stop();
- const QVector<PluginSpec *> queue = loadQueue();
+ const PluginSpecs queue = loadQueue();
for (PluginSpec *spec : queue)
loadPlugin(spec, PluginSpec::Stopped);
}
@@ -1052,7 +1039,11 @@ void PluginManagerPrivate::stopAll()
*/
void PluginManagerPrivate::deleteAll()
{
- m_futureSynchronizer.reset(); // Synchronize all futures from all plugins
+ // Guard against someone playing with the setting
+ QTC_ASSERT(
+ Utils::futureSynchronizer()->isCancelOnWait(),
+ Utils::futureSynchronizer()->cancelAllFutures());
+ Utils::futureSynchronizer()->waitForFinished(); // Synchronize all futures from all plugins
Utils::reverseForeach(loadQueue(), [this](PluginSpec *spec) {
loadPlugin(spec, PluginSpec::Deleted);
});
@@ -1068,10 +1059,8 @@ void PluginManagerPrivate::checkForDuplicatePlugins()
if (spec->isEffectivelyEnabled() && other->isEffectivelyEnabled()) {
const QString error = Tr::tr(
"Multiple versions of the same plugin have been found.");
- spec->d->hasError = true;
- spec->d->errorString = error;
- other->d->hasError = true;
- other->d->errorString = error;
+ spec->setError(error);
+ other->setError(error);
}
} else {
seen.insert(spec->name(), spec);
@@ -1081,14 +1070,15 @@ void PluginManagerPrivate::checkForDuplicatePlugins()
static QHash<IPlugin *, QList<TestCreator>> g_testCreators;
-void PluginManagerPrivate::addTestCreator(IPlugin *plugin, const TestCreator &testCreator)
+void PluginManagerPrivate::addTestCreator(
+ [[maybe_unused]] IPlugin *plugin, [[maybe_unused]] const TestCreator &testCreator)
{
-#ifdef WITH_TESTS
+#ifdef EXTENSIONSYSTEM_WITH_TESTOPTION
g_testCreators[plugin].append(testCreator);
#endif
}
-#ifdef WITH_TESTS
+#ifdef EXTENSIONSYSTEM_WITH_TESTOPTION
using TestPlan = QHash<QObject *, QStringList>; // Object -> selected test functions
@@ -1380,7 +1370,7 @@ void PluginManagerPrivate::loadPlugins()
if (m_profilingVerbosity > 0)
qDebug("Profiling started");
- const QVector<PluginSpec *> queue = loadQueue();
+ const PluginSpecs queue = loadQueue();
Utils::setMimeStartupPhase(MimeStartupPhase::PluginsLoading);
{
NANOTRACE_SCOPE("ExtensionSystem", "Load");
@@ -1403,7 +1393,7 @@ void PluginManagerPrivate::loadPlugins()
delayedInitializeQueue.push(spec);
} else {
// Plugin initialization failed, so cleanup after it
- spec->d->kill();
+ spec->kill();
}
});
}
@@ -1421,10 +1411,23 @@ void PluginManagerPrivate::loadPlugins()
void PluginManagerPrivate::loadPluginsAtRuntime(const QSet<PluginSpec *> &plugins)
{
QTC_CHECK(allOf(plugins, [](PluginSpec *spec) { return spec->isSoftLoadable(); }));
- // load the plugins ordered by dependency
+
+ // load the plugins and their dependencies (if possible) ordered by dependency
const QList<PluginSpec *> queue = filtered(loadQueue(), [&plugins](PluginSpec *spec) {
- return plugins.contains(spec);
+ // Is the current plugin already running, or not soft loadable?
+ if (spec->state() == PluginSpec::State::Running || !spec->isSoftLoadable())
+ return false;
+
+ // Is the current plugin in the list of plugins to load?
+ if (plugins.contains(spec))
+ return true;
+
+ // Is the current plugin a dependency of any of the plugins we want to load?
+ return plugins.contains(spec) || Utils::anyOf(plugins, [spec](PluginSpec *other) {
+ return other->requiresAny({spec});
+ });
});
+
std::queue<PluginSpec *> localDelayedInitializeQueue;
for (PluginSpec *spec : queue)
loadPlugin(spec, PluginSpec::Loaded);
@@ -1434,12 +1437,12 @@ void PluginManagerPrivate::loadPluginsAtRuntime(const QSet<PluginSpec *> &plugin
[this](PluginSpec *spec) { loadPlugin(spec, PluginSpec::Running); });
Utils::reverseForeach(queue, [](PluginSpec *spec) {
if (spec->state() == PluginSpec::Running) {
- const bool delay = spec->d->delayedInitialize();
+ const bool delay = spec->delayedInitialize();
if (delay)
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
} else {
// Plugin initialization failed, so cleanup after it
- spec->d->kill();
+ spec->kill();
}
});
emit q->pluginsChanged();
@@ -1456,7 +1459,7 @@ void PluginManagerPrivate::shutdown()
shutdownEventLoop->exec();
}
deleteAll();
-#ifdef WITH_TESTS
+#ifdef EXTENSIONSYSTEM_WITH_TESTOPTION
if (PluginManager::isScenarioRunning("TestModelManagerInterface")) {
qDebug() << "Point 2: Expect the next call to Point 3 triggers a crash";
QThread::sleep(5);
@@ -1473,11 +1476,11 @@ void PluginManagerPrivate::shutdown()
/*!
\internal
*/
-const QVector<PluginSpec *> PluginManagerPrivate::loadQueue()
+const PluginSpecs PluginManagerPrivate::loadQueue()
{
- QVector<PluginSpec *> queue;
+ PluginSpecs queue;
for (PluginSpec *spec : std::as_const(pluginSpecs)) {
- QVector<PluginSpec *> circularityCheckQueue;
+ PluginSpecs circularityCheckQueue;
loadQueue(spec, queue, circularityCheckQueue);
}
return queue;
@@ -1487,24 +1490,24 @@ const QVector<PluginSpec *> PluginManagerPrivate::loadQueue()
\internal
*/
bool PluginManagerPrivate::loadQueue(PluginSpec *spec,
- QVector<PluginSpec *> &queue,
- QVector<PluginSpec *> &circularityCheckQueue)
+ PluginSpecs &queue,
+ PluginSpecs &circularityCheckQueue)
{
if (queue.contains(spec))
return true;
// check for circular dependencies
if (circularityCheckQueue.contains(spec)) {
- spec->d->hasError = true;
- spec->d->errorString = Tr::tr("Circular dependency detected:");
- spec->d->errorString += QLatin1Char('\n');
+ QString errorString = Tr::tr("Circular dependency detected:");
+ errorString += QLatin1Char('\n');
int index = circularityCheckQueue.indexOf(spec);
for (int i = index; i < circularityCheckQueue.size(); ++i) {
const PluginSpec *depSpec = circularityCheckQueue.at(i);
- spec->d->errorString.append(Tr::tr("%1 (%2) depends on")
- .arg(depSpec->name(), depSpec->version()));
- spec->d->errorString += QLatin1Char('\n');
+ errorString.append(
+ Tr::tr("%1 (%2) depends on").arg(depSpec->name(), depSpec->version()));
+ errorString += QLatin1Char('\n');
}
- spec->d->errorString.append(Tr::tr("%1 (%2)").arg(spec->name(), spec->version()));
+ errorString.append(Tr::tr("%1 (%2)").arg(spec->name(), spec->version()));
+ spec->setError(errorString);
return false;
}
circularityCheckQueue.append(spec);
@@ -1523,10 +1526,9 @@ bool PluginManagerPrivate::loadQueue(PluginSpec *spec,
continue;
PluginSpec *depSpec = it.value();
if (!loadQueue(depSpec, queue, circularityCheckQueue)) {
- spec->d->hasError = true;
- spec->d->errorString =
+ spec->setError(
Tr::tr("Cannot load plugin because dependency failed to load: %1 (%2)\nReason: %3")
- .arg(depSpec->name(), depSpec->version(), depSpec->errorString());
+ .arg(depSpec->name(), depSpec->version(), depSpec->errorString()));
return false;
}
}
@@ -1619,9 +1621,9 @@ void PluginManagerPrivate::checkForProblematicPlugins()
dialog.addButton(Tr::tr("Continue"), QMessageBox::RejectRole);
dialog.exec();
if (dialog.clickedButton() == disableButton) {
- spec->d->setForceDisabled(true);
+ spec->setForceDisabled(true);
for (PluginSpec *other : dependents)
- other->d->setForceDisabled(true);
+ other->setForceDisabled(true);
enableDependenciesIndirectly();
}
}
@@ -1664,15 +1666,15 @@ void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destSt
case PluginSpec::Running: {
NANOTRACE_SCOPE(specName, specName + "::extensionsInitialized");
profilingReport(">initializeExtensions", spec);
- spec->d->initializeExtensions();
+ spec->initializeExtensions();
profilingReport("<initializeExtensions",
spec,
- &spec->d->performanceData.extensionsInitialized);
+ &spec->performanceData().extensionsInitialized);
return;
}
case PluginSpec::Deleted:
profilingReport(">delete", spec);
- spec->d->kill();
+ spec->kill();
profilingReport("<delete", spec);
return;
default:
@@ -1686,10 +1688,10 @@ void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destSt
continue;
PluginSpec *depSpec = it.value();
if (depSpec->state() != destState) {
- spec->d->hasError = true;
- spec->d->errorString =
- Tr::tr("Cannot load plugin because dependency failed to load: %1(%2)\nReason: %3")
- .arg(depSpec->name(), depSpec->version(), depSpec->errorString());
+ spec->setError(
+ Tr::tr(
+ "Cannot load plugin because dependency failed to load: %1(%2)\nReason: %3")
+ .arg(depSpec->name(), depSpec->version(), depSpec->errorString()));
return;
}
}
@@ -1698,20 +1700,20 @@ void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destSt
case PluginSpec::Loaded: {
NANOTRACE_SCOPE(specName, specName + "::load");
profilingReport(">loadLibrary", spec);
- spec->d->loadLibrary();
- profilingReport("<loadLibrary", spec, &spec->d->performanceData.load);
+ spec->loadLibrary();
+ profilingReport("<loadLibrary", spec, &spec->performanceData().load);
break;
}
case PluginSpec::Initialized: {
NANOTRACE_SCOPE(specName, specName + "::initialize");
profilingReport(">initializePlugin", spec);
- spec->d->initializePlugin();
- profilingReport("<initializePlugin", spec, &spec->d->performanceData.initialize);
+ spec->initializePlugin();
+ profilingReport("<initializePlugin", spec, &spec->performanceData().initialize);
break;
}
case PluginSpec::Stopped:
profilingReport(">stop", spec);
- if (spec->d->stop() == IPlugin::AsynchronousShutdown) {
+ if (spec->stop() == IPlugin::AsynchronousShutdown) {
asynchronousPlugins << spec;
connect(spec->plugin(), &IPlugin::asynchronousShutdownFinished, this, [this, spec] {
asynchronousPlugins.remove(spec);
@@ -1729,7 +1731,7 @@ void PluginManagerPrivate::loadPlugin(PluginSpec *spec, PluginSpec::State destSt
/*!
\internal
*/
-void PluginManagerPrivate::setPluginPaths(const QStringList &paths)
+void PluginManagerPrivate::setPluginPaths(const FilePaths &paths)
{
qCDebug(pluginLog) << "Plugin search paths:" << paths;
qCDebug(pluginLog) << "Required IID:" << pluginIID;
@@ -1738,60 +1740,40 @@ void PluginManagerPrivate::setPluginPaths(const QStringList &paths)
readPluginPaths();
}
-static const QStringList pluginFiles(const QStringList &pluginPaths)
+static const FilePaths pluginFiles(const FilePaths &pluginPaths)
{
- QStringList pluginFiles;
- QStringList searchPaths = pluginPaths;
+ FilePaths pluginFiles;
+ FilePaths searchPaths = pluginPaths;
while (!searchPaths.isEmpty()) {
- const QDir dir(searchPaths.takeFirst());
- const QFileInfoList files = dir.entryInfoList(QDir::Files | QDir::NoSymLinks);
- const QStringList absoluteFilePaths = Utils::transform(files, &QFileInfo::absoluteFilePath);
- pluginFiles += Utils::filtered(absoluteFilePaths, [](const QString &path) { return QLibrary::isLibrary(path); });
- const QFileInfoList dirs = dir.entryInfoList(QDir::Dirs|QDir::NoDotAndDotDot);
- searchPaths += Utils::transform(dirs, &QFileInfo::absoluteFilePath);
+ const FilePath dir = searchPaths.takeFirst().absoluteFilePath();
+ const FilePaths files = dir.dirEntries(QDir::Files | QDir::NoSymLinks);
+ pluginFiles += Utils::filtered(files, [](const FilePath &path) {
+ return QLibrary::isLibrary(path.toFSPathString());
+ });
+ const FilePaths dirs = dir.dirEntries(QDir::Dirs | QDir::NoDotAndDotDot);
+ searchPaths += dirs;
}
return pluginFiles;
}
-/*!
- \internal
-*/
-void PluginManagerPrivate::readPluginPaths()
+void PluginManagerPrivate::addPlugins(const PluginSpecs &specs)
{
- qDeleteAll(pluginSpecs);
- pluginSpecs.clear();
- pluginCategories.clear();
-
- // default
- pluginCategories.insert(QString(), QVector<PluginSpec *>());
-
- // from the file system
- for (const QString &pluginFile : pluginFiles(pluginPaths)) {
- PluginSpec *spec = PluginSpec::read(pluginFile);
- if (spec) // Qt Creator plugin
- pluginSpecs.append(spec);
- }
- // static
- for (const QStaticPlugin &plugin : QPluginLoader::staticPlugins()) {
- PluginSpec *spec = PluginSpec::read(plugin);
- if (spec) // Qt Creator plugin
- pluginSpecs.append(spec);
- }
+ pluginSpecs += specs;
- for (PluginSpec *spec : pluginSpecs) {
+ for (PluginSpec *spec : specs) {
// defaultDisabledPlugins and defaultEnabledPlugins from install settings
// is used to override the defaults read from the plugin spec
if (spec->isEnabledByDefault() && defaultDisabledPlugins.contains(spec->name())) {
- spec->d->setEnabledByDefault(false);
- spec->d->setEnabledBySettings(false);
+ spec->setEnabledByDefault(false);
+ spec->setEnabledBySettings(false);
} else if (!spec->isEnabledByDefault() && defaultEnabledPlugins.contains(spec->name())) {
- spec->d->setEnabledByDefault(true);
- spec->d->setEnabledBySettings(true);
+ spec->setEnabledByDefault(true);
+ spec->setEnabledBySettings(true);
}
if (!spec->isEnabledByDefault() && forceEnabledPlugins.contains(spec->name()))
- spec->d->setEnabledBySettings(true);
+ spec->setEnabledBySettings(true);
if (spec->isEnabledByDefault() && disabledPlugins.contains(spec->name()))
- spec->d->setEnabledBySettings(false);
+ spec->setEnabledBySettings(false);
pluginCategories[spec->category()].append(spec);
}
@@ -1803,21 +1785,50 @@ void PluginManagerPrivate::readPluginPaths()
emit q->pluginsChanged();
}
+/*!
+ \internal
+*/
+void PluginManagerPrivate::readPluginPaths()
+{
+ PluginSpecs newSpecs;
+
+ // from the file system
+ for (const FilePath &pluginFile : pluginFiles(pluginPaths)) {
+ expected_str<PluginSpec *> spec = readCppPluginSpec(pluginFile);
+ if (!spec) {
+ qCInfo(pluginLog).noquote() << QString("Ignoring plugin \"%1\" because: %2")
+ .arg(pluginFile.toUserOutput())
+ .arg(spec.error());
+ continue;
+ }
+ newSpecs.append(*spec);
+ }
+
+ // static
+ for (const QStaticPlugin &plugin : QPluginLoader::staticPlugins()) {
+ expected_str<PluginSpec *> spec = readCppPluginSpec(plugin);
+ QTC_ASSERT_EXPECTED(spec, continue);
+ newSpecs.append(*spec);
+ }
+
+ addPlugins(newSpecs);
+}
+
void PluginManagerPrivate::resolveDependencies()
{
for (PluginSpec *spec : std::as_const(pluginSpecs))
- spec->d->resolveDependencies(pluginSpecs);
+ spec->resolveDependencies(pluginSpecs);
}
void PluginManagerPrivate::enableDependenciesIndirectly()
{
for (PluginSpec *spec : std::as_const(pluginSpecs))
- spec->d->enabledIndirectly = false;
+ spec->setEnabledIndirectly(false);
// cannot use reverse loadQueue here, because test dependencies can introduce circles
- QVector<PluginSpec *> queue = Utils::filtered(pluginSpecs, &PluginSpec::isEffectivelyEnabled);
+ PluginSpecs queue = Utils::filtered(pluginSpecs, &PluginSpec::isEffectivelyEnabled);
while (!queue.isEmpty()) {
PluginSpec *spec = queue.takeFirst();
- queue += spec->d->enableDependenciesIndirectly(containsTestSpec(spec));
+ queue += spec->enableDependenciesIndirectly(containsTestSpec(spec));
}
}
@@ -1888,11 +1899,11 @@ void PluginManagerPrivate::profilingReport(const char *what, const PluginSpec *s
QString PluginManagerPrivate::profilingSummary(qint64 *totalOut) const
{
QString summary;
- const QVector<PluginSpec *> specs = Utils::sorted(pluginSpecs,
- [](PluginSpec *s1, PluginSpec *s2) {
- return s1->performanceData().total()
- < s2->performanceData().total();
- });
+ const PluginSpecs specs = Utils::sorted(pluginSpecs,
+ [](PluginSpec *s1, PluginSpec *s2) {
+ return s1->performanceData().total()
+ < s2->performanceData().total();
+ });
const qint64 total
= std::accumulate(specs.constBegin(), specs.constEnd(), 0, [](qint64 t, PluginSpec *s) {
return t + s->performanceData().total();
diff --git a/src/libs/extensionsystem/pluginmanager.h b/src/libs/extensionsystem/pluginmanager.h
index a38f3c3fe7..68bba7a83c 100644
--- a/src/libs/extensionsystem/pluginmanager.h
+++ b/src/libs/extensionsystem/pluginmanager.h
@@ -6,6 +6,7 @@
#include "extensionsystem_global.h"
#include <aggregation/aggregate.h>
+#include <utils/filepath.h>
#include <utils/qtcsettings.h>
#include <QObject>
@@ -15,8 +16,6 @@ QT_BEGIN_NAMESPACE
class QTextStream;
QT_END_NAMESPACE
-namespace Utils { class FutureSynchronizer; }
-
namespace ExtensionSystem {
class IPlugin;
class PluginSpec;
@@ -69,8 +68,8 @@ public:
static QVector<PluginSpec *> loadQueue();
static void loadPlugins();
static void loadPluginsAtRuntime(const QSet<PluginSpec *> &plugins);
- static QStringList pluginPaths();
- static void setPluginPaths(const QStringList &paths);
+ static Utils::FilePaths pluginPaths();
+ static void setPluginPaths(const Utils::FilePaths &paths);
static QString pluginIID();
static void setPluginIID(const QString &iid);
static const QVector<PluginSpec *> plugins();
@@ -82,6 +81,8 @@ public:
static void checkForProblematicPlugins();
static PluginSpec *specForPlugin(IPlugin *plugin);
+ static void addPlugins(const QVector<PluginSpec *> &specs);
+
// Settings
static void setSettings(Utils::QtcSettings *settings);
static Utils::QtcSettings *settings();
@@ -104,7 +105,7 @@ public:
static bool testRunRequested();
-#ifdef WITH_TESTS
+#ifdef EXTENSIONSYSTEM_WITH_TESTOPTION
static bool registerScenario(const QString &scenarioId, std::function<bool()> scenarioStarter);
static bool isScenarioRequested();
static bool runScenario();
@@ -136,8 +137,6 @@ public:
static QString systemInformation();
- static Utils::FutureSynchronizer *futureSynchronizer();
-
signals:
void objectAdded(QObject *obj);
void aboutToRemoveObject(QObject *obj);
diff --git a/src/libs/extensionsystem/pluginmanager_p.h b/src/libs/extensionsystem/pluginmanager_p.h
index 27efbf4c8f..ad9d4f81ee 100644
--- a/src/libs/extensionsystem/pluginmanager_p.h
+++ b/src/libs/extensionsystem/pluginmanager_p.h
@@ -36,8 +36,6 @@ class PluginManager;
namespace Internal {
-class PluginSpecPrivate;
-
class EXTENSIONSYSTEM_TEST_EXPORT PluginManagerPrivate : public QObject
{
public:
@@ -52,8 +50,10 @@ public:
void checkForProblematicPlugins();
void loadPlugins();
void loadPluginsAtRuntime(const QSet<PluginSpec *> &plugins);
+ void addPlugins(const QVector<PluginSpec *> &specs);
+
void shutdown();
- void setPluginPaths(const QStringList &paths);
+ void setPluginPaths(const Utils::FilePaths &paths);
const QVector<ExtensionSystem::PluginSpec *> loadQueue();
void loadPlugin(PluginSpec *spec, PluginSpec::State destState);
void resolveDependencies();
@@ -91,7 +91,7 @@ public:
QHash<QString, QVector<PluginSpec *>> pluginCategories;
QVector<PluginSpec *> pluginSpecs;
std::vector<TestSpec> testSpecs;
- QStringList pluginPaths;
+ Utils::FilePaths pluginPaths;
QString pluginIID;
QVector<QObject *> allObjects; // ### make this a QVector<QPointer<QObject> > > ?
QStringList defaultDisabledPlugins; // Plugins/Ignored from install settings
@@ -119,10 +119,6 @@ public:
PluginSpec *pluginForOption(const QString &option, bool *requiresArgument) const;
PluginSpec *pluginByName(const QString &name) const;
- // used by tests
- static PluginSpec *createSpec();
- static PluginSpecPrivate *privateSpec(PluginSpec *spec);
-
static void addTestCreator(IPlugin *plugin, const std::function<QObject *()> &testCreator);
mutable QReadWriteLock m_lock;
@@ -140,7 +136,6 @@ public:
QWaitCondition m_scenarioWaitCondition;
PluginManager::ProcessData m_creatorProcessData;
- std::unique_ptr<Utils::FutureSynchronizer> m_futureSynchronizer;
private:
PluginManager *q;
@@ -155,7 +150,7 @@ private:
void deleteAll();
void checkForDuplicatePlugins();
-#ifdef WITH_TESTS
+#ifdef EXTENSIONSYSTEM_WITH_TESTOPTION
void startTests();
#endif
};
diff --git a/src/libs/extensionsystem/pluginspec.cpp b/src/libs/extensionsystem/pluginspec.cpp
index 49f9c680fe..d251576049 100644
--- a/src/libs/extensionsystem/pluginspec.cpp
+++ b/src/libs/extensionsystem/pluginspec.cpp
@@ -6,7 +6,6 @@
#include "extensionsystemtr.h"
#include "iplugin.h"
#include "pluginmanager.h"
-#include "pluginspec_p.h"
#include <utils/algorithm.h>
#include <utils/hostosinfo.h>
@@ -24,7 +23,10 @@
#include <QJsonValue>
#include <QPluginLoader>
+Q_LOGGING_CATEGORY(pluginSpecLog, "qtc.extensionsystem.plugin", QtWarningMsg)
+
using namespace ExtensionSystem::Internal;
+using namespace Utils;
namespace ExtensionSystem {
@@ -168,22 +170,78 @@ QString PluginDependency::toString() const
return name + " (" + version + typeString(type) + ")";
}
+namespace Internal {
+class PluginSpecImplPrivate
+{
+public:
+ std::optional<QPluginLoader> loader;
+ std::optional<QStaticPlugin> staticPlugin;
+
+ IPlugin *plugin = nullptr;
+};
+
+class PluginSpecPrivate
+{
+public:
+ ExtensionSystem::PerformanceData performanceData;
+
+ QString name;
+ QString version;
+ QString compatVersion;
+ QString vendor;
+ QString category;
+ QString description;
+ QString longDescription;
+ QString url;
+ QString license;
+ QString revision;
+ QString copyright;
+ QStringList arguments;
+ QRegularExpression platformSpecification;
+ QVector<ExtensionSystem::PluginDependency> dependencies;
+
+ PluginSpec::PluginArgumentDescriptions argumentDescriptions;
+ FilePath location;
+ FilePath filePath;
+
+ bool experimental{false};
+ bool deprecated{false};
+ bool required{false};
+
+ bool enabledByDefault{false};
+ bool enabledBySettings{true};
+ bool enabledIndirectly{false};
+ bool forceEnabled{false};
+ bool forceDisabled{false};
+ bool softLoadable{false};
+
+ std::optional<QString> errorString;
+
+ PluginSpec::State state;
+ QHash<PluginDependency, PluginSpec *> dependencySpecs;
+
+ QJsonObject metaData;
+
+ Utils::expected_str<void> readMetaData(const QJsonObject &metaData);
+ Utils::expected_str<void> reportError(const QString &error)
+ {
+ errorString = error;
+ return {};
+ };
+};
+} // namespace Internal
+
/*!
\internal
*/
-PluginSpec::PluginSpec()
- : d(new PluginSpecPrivate(this))
-{
-}
+CppPluginSpec::CppPluginSpec()
+ : d(new PluginSpecImplPrivate)
+{}
/*!
\internal
*/
-PluginSpec::~PluginSpec()
-{
- delete d;
- d = nullptr;
-}
+CppPluginSpec::~CppPluginSpec() = default;
/*!
Returns the plugin name. This is valid after the PluginSpec::Read state is
@@ -278,10 +336,7 @@ QString PluginSpec::category() const
QString PluginSpec::revision() const
{
- const QJsonValue revision = metaData().value("Revision");
- if (revision.isString())
- return revision.toString();
- return QString();
+ return d->revision;
}
/*!
@@ -419,7 +474,7 @@ QJsonObject PluginSpec::metaData() const
return d->metaData;
}
-const PerformanceData &PluginSpec::performanceData() const
+PerformanceData &PluginSpec::performanceData() const
{
return d->performanceData;
}
@@ -436,7 +491,7 @@ PluginSpec::PluginArgumentDescriptions PluginSpec::argumentDescriptions() const
/*!
Returns the absolute path to the directory containing the plugin.
*/
-QString PluginSpec::location() const
+FilePath PluginSpec::location() const
{
return d->location;
}
@@ -444,7 +499,7 @@ QString PluginSpec::location() const
/*!
Returns the absolute path to the plugin.
*/
-QString PluginSpec::filePath() const
+FilePath PluginSpec::filePath() const
{
return d->filePath;
}
@@ -491,7 +546,7 @@ PluginSpec::State PluginSpec::state() const
*/
bool PluginSpec::hasError() const
{
- return d->hasError;
+ return d->errorString.has_value();
}
/*!
@@ -500,18 +555,21 @@ bool PluginSpec::hasError() const
*/
QString PluginSpec::errorString() const
{
- return d->errorString;
+ return d->errorString.value_or(QString());
}
/*!
- Returns whether this plugin can be used to fill in a dependency of the given
- \a pluginName and \a version.
+ Returns whether the plugin \a spec can be used to fill the \a dependency of this plugin.
- \sa PluginSpec::dependencies()
+ \sa PluginSpec::dependencies()
*/
-bool PluginSpec::provides(const QString &pluginName, const QString &version) const
+bool PluginSpec::provides(PluginSpec *spec, const PluginDependency &dependency) const
{
- return d->provides(pluginName, version);
+ if (QString::compare(dependency.name, spec->name(), Qt::CaseInsensitive) != 0)
+ return false;
+
+ return (versionCompare(spec->version(), dependency.version) >= 0)
+ && (versionCompare(spec->compatVersion(), dependency.version) <= 0);
}
/*!
@@ -519,7 +577,7 @@ bool PluginSpec::provides(const QString &pluginName, const QString &version) con
already been successfully loaded. That is, the PluginSpec::Loaded state
is reached.
*/
-IPlugin *PluginSpec::plugin() const
+IPlugin *CppPluginSpec::plugin() const
{
return d->plugin;
}
@@ -548,6 +606,11 @@ bool PluginSpec::requiresAny(const QSet<PluginSpec *> &plugins) const
return false;
}
+void PluginSpec::setEnabledByDefault(bool value)
+{
+ d->enabledByDefault = value;
+}
+
/*!
Sets whether the plugin should be loaded at startup to \a value.
@@ -555,27 +618,44 @@ bool PluginSpec::requiresAny(const QSet<PluginSpec *> &plugins) const
*/
void PluginSpec::setEnabledBySettings(bool value)
{
- d->setEnabledBySettings(value);
+ d->enabledBySettings = value;
}
-
-PluginSpec *PluginSpec::read(const QString &filePath)
+void PluginSpec::setEnabledIndirectly(bool value)
{
- auto spec = new PluginSpec;
- if (!spec->d->read(filePath)) { // not a Qt Creator plugin
- delete spec;
- return nullptr;
- }
- return spec;
+ d->enabledIndirectly = value;
+}
+void PluginSpec::setForceDisabled(bool value)
+{
+ if (value)
+ d->forceEnabled = false;
+ d->forceDisabled = value;
+}
+void PluginSpec::setForceEnabled(bool value)
+{
+ if (value)
+ d->forceDisabled = false;
+ d->forceEnabled = value;
}
-PluginSpec *PluginSpec::read(const QStaticPlugin &plugin)
+// returns the plugins that it actually indirectly enabled
+PluginSpecs PluginSpec::enableDependenciesIndirectly(bool enableTestDependencies)
{
- auto spec = new PluginSpec;
- if (!spec->d->read(plugin)) { // not a Qt Creator plugin
- delete spec;
- return nullptr;
+ if (!isEffectivelyEnabled()) // plugin not enabled, nothing to do
+ return {};
+
+ PluginSpecs enabled;
+ for (auto it = d->dependencySpecs.cbegin(), end = d->dependencySpecs.cend(); it != end; ++it) {
+ if (it.key().type != PluginDependency::Required
+ && (!enableTestDependencies || it.key().type != PluginDependency::Test))
+ continue;
+
+ PluginSpec *dependencySpec = it.value();
+ if (!dependencySpec->isEffectivelyEnabled()) {
+ dependencySpec->setEnabledIndirectly(true);
+ enabled << dependencySpec;
+ }
}
- return spec;
+ return enabled;
}
//==========PluginSpecPrivate==================
@@ -610,112 +690,46 @@ namespace {
const char ARGUMENT_PARAMETER[] = "Parameter";
const char ARGUMENT_DESCRIPTION[] = "Description";
}
-/*!
- \internal
-*/
-PluginSpecPrivate::PluginSpecPrivate(PluginSpec *spec)
- : q(spec)
-{}
-
-void PluginSpecPrivate::reset()
-{
- name.clear();
- version.clear();
- compatVersion.clear();
- vendor.clear();
- copyright.clear();
- license.clear();
- description.clear();
- longDescription.clear();
- url.clear();
- category.clear();
- location.clear();
- filePath.clear();
- state = PluginSpec::Invalid;
- hasError = false;
- errorString.clear();
- dependencies.clear();
- metaData = QJsonObject();
- loader.reset();
- staticPlugin.reset();
-}
/*!
\internal
Returns false if the file does not represent a Qt Creator plugin.
*/
-bool PluginSpecPrivate::read(const QString &fileName)
-{
- qCDebug(pluginLog) << "\nReading meta data of" << fileName;
- reset();
- QFileInfo fileInfo(fileName);
- location = fileInfo.absolutePath();
- filePath = fileInfo.absoluteFilePath();
- loader.emplace();
- if (Utils::HostOsInfo::isMacHost())
- loader->setLoadHints(QLibrary::ExportExternalSymbolsHint);
- loader->setFileName(filePath);
- if (loader->fileName().isEmpty()) {
- qCDebug(pluginLog) << "Cannot open file";
- return false;
- }
+expected_str<PluginSpec *> readCppPluginSpec(const FilePath &fileName)
+{
+ auto spec = new CppPluginSpec;
- if (!readMetaData(loader->metaData()))
- return false;
+ const FilePath absPath = fileName.absoluteFilePath();
- state = PluginSpec::Read;
- return true;
-}
-
-bool PluginSpecPrivate::read(const QStaticPlugin &plugin)
-{
- qCDebug(pluginLog) << "\nReading meta data of static plugin";
- reset();
- staticPlugin = plugin;
- if (!readMetaData(plugin.metaData()))
- return false;
+ spec->setLocation(absPath.parentDir());
+ spec->setFilePath(absPath);
+ spec->d->loader.emplace();
- state = PluginSpec::Read;
- return true;
-}
+ if (Utils::HostOsInfo::isMacHost())
+ spec->d->loader->setLoadHints(QLibrary::ExportExternalSymbolsHint);
-void PluginSpecPrivate::setEnabledBySettings(bool value)
-{
- enabledBySettings = value;
-}
+ spec->d->loader->setFileName(absPath.toFSPathString());
+ if (spec->d->loader->fileName().isEmpty())
+ return make_unexpected(::ExtensionSystem::Tr::tr("Cannot open file"));
-void PluginSpecPrivate::setEnabledByDefault(bool value)
-{
- enabledByDefault = value;
-}
+ expected_str<void> r = spec->readMetaData(spec->d->loader->metaData());
+ if (!r)
+ return make_unexpected(r.error());
-void PluginSpecPrivate::setForceEnabled(bool value)
-{
- forceEnabled = value;
- if (value)
- forceDisabled = false;
+ return spec;
}
-void PluginSpecPrivate::setForceDisabled(bool value)
+expected_str<PluginSpec *> readCppPluginSpec(const QStaticPlugin &plugin)
{
- if (value)
- forceEnabled = false;
- forceDisabled = value;
-}
+ auto spec = new CppPluginSpec;
-void PluginSpecPrivate::setSoftLoadable(bool value)
-{
- softLoadable = value;
-}
+ qCDebug(pluginLog) << "\nReading meta data of static plugin";
+ spec->d->staticPlugin = plugin;
+ expected_str<void> r = spec->readMetaData(plugin.metaData());
+ if (!r)
+ return make_unexpected(r.error());
-/*!
- \internal
-*/
-bool PluginSpecPrivate::reportError(const QString &err)
-{
- errorString = err;
- hasError = true;
- return true;
+ return spec;
}
static inline QString msgValueMissing(const char *key)
@@ -725,58 +739,67 @@ static inline QString msgValueMissing(const char *key)
static inline QString msgValueIsNotAString(const char *key)
{
- return Tr::tr("Value for key \"%1\" is not a string")
- .arg(QLatin1String(key));
+ return Tr::tr("Value for key \"%1\" is not a string").arg(QLatin1String(key));
}
static inline QString msgValueIsNotABool(const char *key)
{
- return Tr::tr("Value for key \"%1\" is not a bool")
- .arg(QLatin1String(key));
+ return Tr::tr("Value for key \"%1\" is not a bool").arg(QLatin1String(key));
}
static inline QString msgValueIsNotAObjectArray(const char *key)
{
- return Tr::tr("Value for key \"%1\" is not an array of objects")
- .arg(QLatin1String(key));
+ return Tr::tr("Value for key \"%1\" is not an array of objects").arg(QLatin1String(key));
}
static inline QString msgValueIsNotAMultilineString(const char *key)
{
return Tr::tr("Value for key \"%1\" is not a string and not an array of strings")
- .arg(QLatin1String(key));
+ .arg(QLatin1String(key));
}
static inline QString msgInvalidFormat(const char *key, const QString &content)
{
- return Tr::tr("Value \"%2\" for key \"%1\" has invalid format")
- .arg(QLatin1String(key), content);
+ return Tr::tr("Value \"%2\" for key \"%1\" has invalid format").arg(QLatin1String(key), content);
+}
+
+Utils::expected_str<void> PluginSpec::readMetaData(const QJsonObject &metaData)
+{
+ return d->readMetaData(metaData);
+}
+Utils::expected_str<void> PluginSpec::reportError(const QString &error)
+{
+ return d->reportError(error);
}
/*!
\internal
*/
-bool PluginSpecPrivate::readMetaData(const QJsonObject &pluginMetaData)
+expected_str<void> CppPluginSpec::readMetaData(const QJsonObject &pluginMetaData)
{
- qCDebug(pluginLog) << "MetaData:" << QJsonDocument(pluginMetaData).toJson();
+ qCDebug(pluginLog).noquote() << "MetaData:" << QJsonDocument(pluginMetaData).toJson();
QJsonValue value;
value = pluginMetaData.value(QLatin1String("IID"));
- if (!value.isString()) {
- qCDebug(pluginLog) << "Not a plugin (no string IID found)";
- return false;
- }
- if (value.toString() != PluginManager::pluginIID()) {
- qCDebug(pluginLog) << "Plugin ignored (IID does not match)";
- return false;
- }
+ if (!value.isString())
+ return make_unexpected(::ExtensionSystem::Tr::tr("No IID found"));
+
+ if (value.toString() != PluginManager::pluginIID())
+ return make_unexpected(::ExtensionSystem::Tr::tr("Expected IID \"%1\", but found \"%2\"")
+ .arg(PluginManager::pluginIID())
+ .arg(value.toString()));
value = pluginMetaData.value(QLatin1String(PLUGIN_METADATA));
- if (!value.isObject()) {
+ if (!value.isObject())
return reportError(::ExtensionSystem::Tr::tr("Plugin meta data not found"));
- }
- metaData = value.toObject();
- value = metaData.value(QLatin1String(PLUGIN_NAME));
+ return PluginSpec::readMetaData(value.toObject());
+}
+
+Utils::expected_str<void> PluginSpecPrivate::readMetaData(const QJsonObject &data)
+{
+ metaData = data;
+
+ QJsonValue value = metaData.value(QLatin1String(PLUGIN_NAME));
if (value.isUndefined())
return reportError(msgValueMissing(PLUGIN_NAME));
if (!value.isString())
@@ -789,14 +812,14 @@ bool PluginSpecPrivate::readMetaData(const QJsonObject &pluginMetaData)
if (!value.isString())
return reportError(msgValueIsNotAString(PLUGIN_VERSION));
version = value.toString();
- if (!isValidVersion(version))
+ if (!PluginSpec::isValidVersion(version))
return reportError(msgInvalidFormat(PLUGIN_VERSION, version));
value = metaData.value(QLatin1String(PLUGIN_COMPATVERSION));
if (!value.isUndefined() && !value.isString())
return reportError(msgValueIsNotAString(PLUGIN_COMPATVERSION));
compatVersion = value.toString(version);
- if (!value.isUndefined() && !isValidVersion(compatVersion))
+ if (!value.isUndefined() && !PluginSpec::isValidVersion(compatVersion))
return reportError(msgInvalidFormat(PLUGIN_COMPATVERSION, compatVersion));
value = metaData.value(QLatin1String(PLUGIN_REQUIRED));
@@ -820,11 +843,9 @@ bool PluginSpecPrivate::readMetaData(const QJsonObject &pluginMetaData)
value = metaData.value(QLatin1String(PLUGIN_DISABLED_BY_DEFAULT));
if (!value.isUndefined() && !value.isBool())
return reportError(msgValueIsNotABool(PLUGIN_DISABLED_BY_DEFAULT));
- enabledByDefault = !value.toBool(false);
+ enabledByDefault = !value.toBool(experimental || deprecated);
qCDebug(pluginLog) << "enabledByDefault =" << enabledByDefault;
- if (experimental || deprecated)
- enabledByDefault = false;
enabledBySettings = enabledByDefault;
value = metaData.value(QLatin1String(PLUGIN_SOFTLOADABLE));
@@ -844,11 +865,11 @@ bool PluginSpecPrivate::readMetaData(const QJsonObject &pluginMetaData)
copyright = value.toString();
value = metaData.value(QLatin1String(DESCRIPTION));
- if (!value.isUndefined() && !Utils::readMultiLineString(value, &description))
+ if (!value.isUndefined() && !readMultiLineString(value, &description))
return reportError(msgValueIsNotAString(DESCRIPTION));
value = metaData.value(QLatin1String(LONGDESCRIPTION));
- if (!value.isUndefined() && !Utils::readMultiLineString(value, &longDescription))
+ if (!value.isUndefined() && !readMultiLineString(value, &longDescription))
return reportError(msgValueIsNotAString(LONGDESCRIPTION));
value = metaData.value(QLatin1String(URL));
@@ -862,9 +883,14 @@ bool PluginSpecPrivate::readMetaData(const QJsonObject &pluginMetaData)
category = value.toString();
value = metaData.value(QLatin1String(LICENSE));
- if (!value.isUndefined() && !Utils::readMultiLineString(value, &license))
+ if (!value.isUndefined() && !readMultiLineString(value, &license))
return reportError(msgValueIsNotAMultilineString(LICENSE));
+ value = metaData.value("Revision");
+ if (!value.isUndefined() && !value.isString())
+ return reportError(msgValueIsNotAString("Revision"));
+ revision = value.toString();
+
value = metaData.value(QLatin1String(PLATFORM));
if (!value.isUndefined() && !value.isString())
return reportError(msgValueIsNotAString(PLATFORM));
@@ -906,7 +932,7 @@ bool PluginSpecPrivate::readMetaData(const QJsonObject &pluginMetaData)
.arg(msgValueIsNotAString(DEPENDENCY_VERSION)));
}
dep.version = value.toString();
- if (!isValidVersion(dep.version)) {
+ if (!PluginSpec::isValidVersion(dep.version)) {
return reportError(
::ExtensionSystem::Tr::tr("Dependency: %1")
.arg(msgInvalidFormat(DEPENDENCY_VERSION, dep.version)));
@@ -987,23 +1013,15 @@ bool PluginSpecPrivate::readMetaData(const QJsonObject &pluginMetaData)
}
}
- return true;
-}
+ state = PluginSpec::Read;
-/*!
- \internal
-*/
-bool PluginSpecPrivate::provides(const QString &pluginName, const QString &pluginVersion) const
-{
- if (QString::compare(pluginName, name, Qt::CaseInsensitive) != 0)
- return false;
- return (versionCompare(version, pluginVersion) >= 0) && (versionCompare(compatVersion, pluginVersion) <= 0);
+ return {};
}
/*!
\internal
*/
-const QRegularExpression &PluginSpecPrivate::versionRegExp()
+static const QRegularExpression &versionRegExp()
{
static const QRegularExpression reg("^([0-9]+)(?:[.]([0-9]+))?(?:[.]([0-9]+))?(?:_([0-9]+))?$");
return reg;
@@ -1011,7 +1029,7 @@ const QRegularExpression &PluginSpecPrivate::versionRegExp()
/*!
\internal
*/
-bool PluginSpecPrivate::isValidVersion(const QString &version)
+bool PluginSpec::isValidVersion(const QString &version)
{
return versionRegExp().match(version).hasMatch();
}
@@ -1019,7 +1037,7 @@ bool PluginSpecPrivate::isValidVersion(const QString &version)
/*!
\internal
*/
-int PluginSpecPrivate::versionCompare(const QString &version1, const QString &version2)
+int PluginSpec::versionCompare(const QString &version1, const QString &version2)
{
const QRegularExpressionMatch match1 = versionRegExp().match(version1);
const QRegularExpressionMatch match2 = versionRegExp().match(version2);
@@ -1041,196 +1059,206 @@ int PluginSpecPrivate::versionCompare(const QString &version1, const QString &ve
/*!
\internal
*/
-bool PluginSpecPrivate::resolveDependencies(const QVector<PluginSpec *> &specs)
+bool PluginSpec::resolveDependencies(const PluginSpecs &specs)
{
- if (hasError)
+ if (hasError())
return false;
- if (state == PluginSpec::Resolved)
- state = PluginSpec::Read; // Go back, so we just re-resolve the dependencies.
- if (state != PluginSpec::Read) {
- errorString = ::ExtensionSystem::Tr::tr("Resolving dependencies failed because state != Read");
- hasError = true;
+ if (state() > PluginSpec::Resolved)
+ return true; // We are resolved already.
+ if (state() == PluginSpec::Resolved)
+ setState(PluginSpec::Read); // Go back, so we just re-resolve the dependencies.
+ if (state() != PluginSpec::Read) {
+ setError(::ExtensionSystem::Tr::tr("Resolving dependencies failed because state != Read"));
return false;
}
+
QHash<PluginDependency, PluginSpec *> resolvedDependencies;
- for (const PluginDependency &dependency : std::as_const(dependencies)) {
- PluginSpec * const found = Utils::findOrDefault(specs, [&dependency](PluginSpec *spec) {
- return spec->provides(dependency.name, dependency.version);
+ for (const PluginDependency &dependency : d->dependencies) {
+ PluginSpec *const found = findOrDefault(specs, [this, &dependency](PluginSpec *spec) {
+ return provides(spec, dependency);
});
if (!found) {
if (dependency.type == PluginDependency::Required) {
- hasError = true;
- if (!errorString.isEmpty())
- errorString.append(QLatin1Char('\n'));
- errorString.append(::ExtensionSystem::Tr::tr("Could not resolve dependency '%1(%2)'")
- .arg(dependency.name, dependency.version));
+ const QString error = ::ExtensionSystem::Tr::tr(
+ "Could not resolve dependency '%1(%2)'")
+ .arg(dependency.name, dependency.version);
+ if (hasError())
+ setError(errorString() + '\n' + error);
+ else
+ setError(error);
}
continue;
}
resolvedDependencies.insert(dependency, found);
}
- if (hasError)
+ if (hasError())
return false;
- dependencySpecs = resolvedDependencies;
+ d->dependencySpecs = resolvedDependencies;
- state = PluginSpec::Resolved;
+ d->state = PluginSpec::Resolved;
return true;
}
-// returns the plugins that it actually indirectly enabled
-QVector<PluginSpec *> PluginSpecPrivate::enableDependenciesIndirectly(bool enableTestDependencies)
+PluginSpec::PluginSpec()
+ : d(new PluginSpecPrivate())
+{}
+
+PluginSpec::~PluginSpec() = default;
+
+void PluginSpec::setState(State state)
{
- if (!q->isEffectivelyEnabled()) // plugin not enabled, nothing to do
- return {};
- QVector<PluginSpec *> enabled;
- for (auto it = dependencySpecs.cbegin(), end = dependencySpecs.cend(); it != end; ++it) {
- if (it.key().type != PluginDependency::Required
- && (!enableTestDependencies || it.key().type != PluginDependency::Test))
- continue;
- PluginSpec *dependencySpec = it.value();
- if (!dependencySpec->isEffectivelyEnabled()) {
- dependencySpec->d->enabledIndirectly = true;
- enabled << dependencySpec;
- }
- }
- return enabled;
+ d->state = state;
+}
+
+void PluginSpec::setLocation(const FilePath &location)
+{
+ d->location = location;
+}
+
+void PluginSpec::setFilePath(const FilePath &filePath)
+{
+ d->filePath = filePath;
+}
+
+void PluginSpec::setError(const QString &errorString)
+{
+ qCWarning(pluginSpecLog).noquote() << "[" << name() << "]"
+ << "Plugin error:" << errorString;
+ d->errorString = errorString;
}
/*!
\internal
*/
-bool PluginSpecPrivate::loadLibrary()
+bool CppPluginSpec::loadLibrary()
{
- if (hasError)
+ if (hasError())
return false;
- if (state != PluginSpec::Resolved) {
- if (state == PluginSpec::Loaded)
+
+ if (state() != PluginSpec::Resolved) {
+ if (state() == PluginSpec::Loaded)
return true;
- errorString =
- ::ExtensionSystem::Tr::tr("Loading the library failed because state != Resolved");
- hasError = true;
+ setError(::ExtensionSystem::Tr::tr("Loading the library failed because state != Resolved"));
return false;
}
- if (loader && !loader->load()) {
- hasError = true;
- errorString = QDir::toNativeSeparators(filePath) + QString::fromLatin1(": ")
- + loader->errorString();
+ if (d->loader && !d->loader->load()) {
+ setError(filePath().toUserOutput() + QString::fromLatin1(": ") + d->loader->errorString());
return false;
}
- auto *pluginObject = loader ? qobject_cast<IPlugin *>(loader->instance())
- : qobject_cast<IPlugin *>(staticPlugin->instance());
+ auto *pluginObject = d->loader ? qobject_cast<IPlugin *>(d->loader->instance())
+ : qobject_cast<IPlugin *>(d->staticPlugin->instance());
if (!pluginObject) {
- hasError = true;
- errorString =
- ::ExtensionSystem::Tr::tr("Plugin is not valid (does not derive from IPlugin)");
- if (loader)
- loader->unload();
+ setError(::ExtensionSystem::Tr::tr("Plugin is not valid (does not derive from IPlugin)"));
+ if (d->loader)
+ d->loader->unload();
return false;
}
- state = PluginSpec::Loaded;
- plugin = pluginObject;
+ setState(PluginSpec::Loaded);
+ d->plugin = pluginObject;
return true;
}
/*!
\internal
*/
-bool PluginSpecPrivate::initializePlugin()
+bool CppPluginSpec::initializePlugin()
{
- if (hasError)
+ if (hasError())
return false;
- if (state != PluginSpec::Loaded) {
- if (state == PluginSpec::Initialized)
+
+ if (state() != PluginSpec::Loaded) {
+ if (state() == PluginSpec::Initialized)
return true;
- errorString = ::ExtensionSystem::Tr::tr(
- "Initializing the plugin failed because state != Loaded");
- hasError = true;
+ setError(
+ ::ExtensionSystem::Tr::tr("Initializing the plugin failed because state != Loaded"));
return false;
}
- if (!plugin) {
- errorString = ::ExtensionSystem::Tr::tr(
- "Internal error: have no plugin instance to initialize");
- hasError = true;
+ if (!d->plugin) {
+ setError(
+ ::ExtensionSystem::Tr::tr("Internal error: have no plugin instance to initialize"));
return false;
}
QString err;
- if (!plugin->initialize(arguments, &err)) {
- errorString = ::ExtensionSystem::Tr::tr("Plugin initialization failed: %1").arg(err);
- hasError = true;
+ if (!d->plugin->initialize(arguments(), &err)) {
+ setError(::ExtensionSystem::Tr::tr("Plugin initialization failed: %1").arg(err));
return false;
}
- state = PluginSpec::Initialized;
+ setState(PluginSpec::Initialized);
return true;
}
/*!
\internal
*/
-bool PluginSpecPrivate::initializeExtensions()
+bool CppPluginSpec::initializeExtensions()
{
- if (hasError)
+ if (hasError())
return false;
- if (state != PluginSpec::Initialized) {
- if (state == PluginSpec::Running)
+
+ if (state() != PluginSpec::Initialized) {
+ if (state() == PluginSpec::Running)
return true;
- errorString = ::ExtensionSystem::Tr::tr(
- "Cannot perform extensionsInitialized because state != Initialized");
- hasError = true;
+ setError(::ExtensionSystem::Tr::tr(
+ "Cannot perform extensionsInitialized because state != Initialized"));
return false;
}
- if (!plugin) {
- errorString = ::ExtensionSystem::Tr::tr(
- "Internal error: have no plugin instance to perform extensionsInitialized");
- hasError = true;
+ if (!d->plugin) {
+ setError(::ExtensionSystem::Tr::tr(
+ "Internal error: have no plugin instance to perform extensionsInitialized"));
return false;
}
- plugin->extensionsInitialized();
- state = PluginSpec::Running;
+ d->plugin->extensionsInitialized();
+ setState(PluginSpec::Running);
return true;
}
/*!
\internal
*/
-bool PluginSpecPrivate::delayedInitialize()
+bool CppPluginSpec::delayedInitialize()
{
- if (hasError)
- return false;
- if (state != PluginSpec::Running)
+ if (hasError())
+ return true;
+
+ if (state() != PluginSpec::Running)
return false;
- if (!plugin) {
- errorString = ::ExtensionSystem::Tr::tr(
- "Internal error: have no plugin instance to perform delayedInitialize");
- hasError = true;
+ if (!d->plugin) {
+ setError(::ExtensionSystem::Tr::tr(
+ "Internal error: have no plugin instance to perform delayedInitialize"));
return false;
}
- const bool res = plugin->delayedInitialize();
+ const bool res = d->plugin->delayedInitialize();
return res;
}
/*!
\internal
*/
-IPlugin::ShutdownFlag PluginSpecPrivate::stop()
+IPlugin::ShutdownFlag CppPluginSpec::stop()
{
- if (!plugin)
+ if (hasError())
+ return IPlugin::ShutdownFlag::SynchronousShutdown;
+
+ if (!d->plugin)
return IPlugin::SynchronousShutdown;
- state = PluginSpec::Stopped;
- return plugin->aboutToShutdown();
+ setState(PluginSpec::Stopped);
+ return d->plugin->aboutToShutdown();
}
/*!
\internal
*/
-void PluginSpecPrivate::kill()
+void CppPluginSpec::kill()
{
- if (!plugin)
+ if (hasError())
return;
- delete plugin;
- plugin = nullptr;
- state = PluginSpec::Deleted;
-}
-} // ExtensionSystem
+ if (!d->plugin)
+ return;
+ delete d->plugin;
+ d->plugin = nullptr;
+ setState(PluginSpec::Deleted);
+}
+} // namespace ExtensionSystem
diff --git a/src/libs/extensionsystem/pluginspec.h b/src/libs/extensionsystem/pluginspec.h
index 8db6bb4411..e9a771d4a9 100644
--- a/src/libs/extensionsystem/pluginspec.h
+++ b/src/libs/extensionsystem/pluginspec.h
@@ -5,6 +5,11 @@
#include "extensionsystem_global.h"
+#include "iplugin.h"
+
+#include <utils/expected.h>
+#include <utils/filepath.h>
+
#include <QHash>
#include <QStaticPlugin>
#include <QString>
@@ -14,30 +19,31 @@ QT_BEGIN_NAMESPACE
class QRegularExpression;
QT_END_NAMESPACE
+class tst_PluginSpec;
+
namespace ExtensionSystem {
namespace Internal {
class OptionsParser;
-class PluginSpecPrivate;
+class PluginSpecImplPrivate;
class PluginManagerPrivate;
+class PluginSpecPrivate;
} // Internal
-class IPlugin;
class PluginView;
struct EXTENSIONSYSTEM_EXPORT PluginDependency
{
- enum Type {
- Required,
- Optional,
- Test
- };
+ enum Type { Required, Optional, Test };
PluginDependency() : type(Required) {}
-
- friend size_t qHash(const PluginDependency &value);
+ PluginDependency(const QString &name, const QString &version, Type type = Required)
+ : name(name)
+ , version(version)
+ , type(type)
+ {}
QString name;
QString version;
@@ -46,6 +52,8 @@ struct EXTENSIONSYSTEM_EXPORT PluginDependency
QString toString() const;
};
+size_t EXTENSIONSYSTEM_EXPORT qHash(const PluginDependency &value);
+
struct EXTENSIONSYSTEM_EXPORT PluginArgumentDescription
{
QString name;
@@ -71,79 +79,137 @@ struct EXTENSIONSYSTEM_EXPORT PerformanceData
}
};
+using PluginSpecs = QList<class PluginSpec *>;
+
class EXTENSIONSYSTEM_EXPORT PluginSpec
{
-public:
- enum State { Invalid, Read, Resolved, Loaded, Initialized, Running, Stopped, Deleted};
+ friend class ::tst_PluginSpec;
+ friend class Internal::PluginManagerPrivate;
+ friend class Internal::OptionsParser;
- ~PluginSpec();
-
- // information from the xml file, valid after 'Read' state is reached
- QString name() const;
- QString version() const;
- QString compatVersion() const;
- QString vendor() const;
- QString copyright() const;
- QString license() const;
- QString description() const;
- QString longDescription() const;
- QString url() const;
- QString category() const;
- QString revision() const;
- QRegularExpression platformSpecification() const;
- bool isAvailableForHostPlatform() const;
- bool isRequired() const;
- bool isExperimental() const;
- bool isDeprecated() const;
- bool isEnabledByDefault() const;
- bool isEnabledBySettings() const;
- bool isEffectivelyEnabled() const;
- bool isEnabledIndirectly() const;
- bool isForceEnabled() const;
- bool isForceDisabled() const;
- bool isSoftLoadable() const;
- QVector<PluginDependency> dependencies() const;
- QJsonObject metaData() const;
- const PerformanceData &performanceData() const;
+public:
+ PluginSpec();
+ virtual ~PluginSpec();
using PluginArgumentDescriptions = QVector<PluginArgumentDescription>;
- PluginArgumentDescriptions argumentDescriptions() const;
+ enum State { Invalid, Read, Resolved, Loaded, Initialized, Running, Stopped, Deleted};
- // other information, valid after 'Read' state is reached
- QString location() const;
- QString filePath() const;
+ // information read from the plugin, valid after 'Read' state is reached
+ virtual QString name() const;
+ virtual QString version() const;
+ virtual QString compatVersion() const;
+ virtual QString vendor() const;
+ virtual QString copyright() const;
+ virtual QString license() const;
+ virtual QString description() const;
+ virtual QString longDescription() const;
+ virtual QString url() const;
+ virtual QString category() const;
+ virtual QString revision() const;
+ virtual QRegularExpression platformSpecification() const;
+
+ virtual bool isAvailableForHostPlatform() const;
+ virtual bool isRequired() const;
+ virtual bool isExperimental() const;
+ virtual bool isDeprecated() const;
+ virtual bool isEnabledByDefault() const;
+ virtual bool isEnabledBySettings() const;
+ virtual bool isEffectivelyEnabled() const;
+ virtual bool isEnabledIndirectly() const;
+ virtual bool isForceEnabled() const;
+ virtual bool isForceDisabled() const;
+ virtual bool isSoftLoadable() const;
+
+ virtual QVector<PluginDependency> dependencies() const;
+ virtual QJsonObject metaData() const;
+ virtual PerformanceData &performanceData() const;
+ virtual PluginArgumentDescriptions argumentDescriptions() const;
+ virtual Utils::FilePath location() const;
+ virtual Utils::FilePath filePath() const;
+ virtual QStringList arguments() const;
+ virtual void setArguments(const QStringList &arguments);
+ virtual void addArgument(const QString &argument);
+ virtual QHash<PluginDependency, PluginSpec *> dependencySpecs() const;
+
+ virtual bool provides(PluginSpec *spec, const PluginDependency &dependency) const;
+ virtual bool requiresAny(const QSet<PluginSpec *> &plugins) const;
+ virtual PluginSpecs enableDependenciesIndirectly(bool enableTestDependencies);
+ virtual bool resolveDependencies(const PluginSpecs &pluginSpecs);
+
+ virtual IPlugin *plugin() const = 0;
+ virtual State state() const;
+ virtual bool hasError() const;
+ virtual QString errorString() const;
+
+ static bool isValidVersion(const QString &version);
+ static int versionCompare(const QString &version1, const QString &version2);
+
+ virtual void setEnabledBySettings(bool value);
+
+protected:
+ virtual void setEnabledByDefault(bool value);
+ virtual void setEnabledIndirectly(bool value);
+ virtual void setForceDisabled(bool value);
+ virtual void setForceEnabled(bool value);
+
+ virtual bool loadLibrary() = 0;
+ virtual bool initializePlugin() = 0;
+ virtual bool initializeExtensions() = 0;
+ virtual bool delayedInitialize() = 0;
+ virtual IPlugin::ShutdownFlag stop() = 0;
+ virtual void kill() = 0;
+
+ virtual void setError(const QString &errorString);
+
+protected:
+ virtual void setState(State state);
+
+ virtual void setLocation(const Utils::FilePath &location);
+ virtual void setFilePath(const Utils::FilePath &filePath);
+ virtual Utils::expected_str<void> readMetaData(const QJsonObject &metaData);
+ Utils::expected_str<void> reportError(const QString &error);
- QStringList arguments() const;
- void setArguments(const QStringList &arguments);
- void addArgument(const QString &argument);
+private:
+ std::unique_ptr<Internal::PluginSpecPrivate> d;
+};
- bool provides(const QString &pluginName, const QString &version) const;
+EXTENSIONSYSTEM_EXPORT Utils::expected_str<PluginSpec *> readCppPluginSpec(
+ const Utils::FilePath &filePath);
+EXTENSIONSYSTEM_EXPORT Utils::expected_str<PluginSpec *> readCppPluginSpec(
+ const QStaticPlugin &plugin);
- // dependency specs, valid after 'Resolved' state is reached
- QHash<PluginDependency, PluginSpec *> dependencySpecs() const;
- bool requiresAny(const QSet<PluginSpec *> &plugins) const;
+class EXTENSIONSYSTEM_TEST_EXPORT CppPluginSpec : public PluginSpec
+{
+ friend EXTENSIONSYSTEM_EXPORT Utils::expected_str<PluginSpec *> readCppPluginSpec(
+ const Utils::FilePath &filePath);
+ friend EXTENSIONSYSTEM_EXPORT Utils::expected_str<PluginSpec *> readCppPluginSpec(
+ const QStaticPlugin &plugin);
+
+public:
+ ~CppPluginSpec() override;
// linked plugin instance, valid after 'Loaded' state is reached
- IPlugin *plugin() const;
+ IPlugin *plugin() const override;
- // state
- State state() const;
- bool hasError() const;
- QString errorString() const;
+ bool loadLibrary() override;
+ bool initializePlugin() override;
+ bool initializeExtensions() override;
+ bool delayedInitialize() override;
+ IPlugin::ShutdownFlag stop() override;
+ void kill() override;
- void setEnabledBySettings(bool value);
+ Utils::expected_str<void> readMetaData(const QJsonObject &pluginMetaData) override;
- static PluginSpec *read(const QString &filePath);
- static PluginSpec *read(const QStaticPlugin &plugin);
+protected:
+ CppPluginSpec();
private:
- PluginSpec();
-
- Internal::PluginSpecPrivate *d;
+ std::unique_ptr<Internal::PluginSpecImplPrivate> d;
friend class PluginView;
friend class Internal::OptionsParser;
friend class Internal::PluginManagerPrivate;
- friend class Internal::PluginSpecPrivate;
+ friend class Internal::PluginSpecImplPrivate;
+ friend class ::tst_PluginSpec;
};
} // namespace ExtensionSystem
diff --git a/src/libs/extensionsystem/pluginspec_p.h b/src/libs/extensionsystem/pluginspec_p.h
deleted file mode 100644
index 61832213ca..0000000000
--- a/src/libs/extensionsystem/pluginspec_p.h
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "pluginspec.h"
-#include "iplugin.h"
-
-#include <QJsonObject>
-#include <QObject>
-#include <QPluginLoader>
-#include <QRegularExpression>
-#include <QStringList>
-#include <QVector>
-#include <QXmlStreamReader>
-
-#include <optional>
-
-namespace ExtensionSystem {
-
-class IPlugin;
-
-namespace Internal {
-
-class EXTENSIONSYSTEM_TEST_EXPORT PluginSpecPrivate : public QObject
-{
- Q_OBJECT
-
-public:
- PluginSpecPrivate(PluginSpec *spec);
-
- void reset();
- bool read(const QString &fileName);
- bool read(const QStaticPlugin &plugin);
- bool provides(const QString &pluginName, const QString &version) const;
- bool resolveDependencies(const QVector<PluginSpec *> &specs);
- bool loadLibrary();
- bool initializePlugin();
- bool initializeExtensions();
- bool delayedInitialize();
- IPlugin::ShutdownFlag stop();
- void kill();
-
- void setEnabledBySettings(bool value);
- void setEnabledByDefault(bool value);
- void setForceEnabled(bool value);
- void setForceDisabled(bool value);
- void setSoftLoadable(bool value);
-
- std::optional<QPluginLoader> loader;
- std::optional<QStaticPlugin> staticPlugin;
-
- QString name;
- QString version;
- QString compatVersion;
- bool required = false;
- bool experimental = false;
- bool enabledByDefault = true;
- bool deprecated = false;
- QString vendor;
- QString copyright;
- QString license;
- QString description;
- QString longDescription;
- QString url;
- QString category;
- QRegularExpression platformSpecification;
- QVector<PluginDependency> dependencies;
- QJsonObject metaData;
- bool enabledBySettings = true;
- bool enabledIndirectly = false;
- bool forceEnabled = false;
- bool forceDisabled = false;
- bool softLoadable = false;
-
- QString location;
- QString filePath;
- QStringList arguments;
-
- QHash<PluginDependency, PluginSpec *> dependencySpecs;
- PluginSpec::PluginArgumentDescriptions argumentDescriptions;
- IPlugin *plugin = nullptr;
-
- QList<TestCreator> registeredPluginTests;
-
- PluginSpec::State state = PluginSpec::Invalid;
- bool hasError = false;
- QString errorString;
-
- PerformanceData performanceData;
-
- static bool isValidVersion(const QString &version);
- static int versionCompare(const QString &version1, const QString &version2);
-
- QVector<PluginSpec *> enableDependenciesIndirectly(bool enableTestDependencies = false);
-
- bool readMetaData(const QJsonObject &pluginMetaData);
-
-private:
- PluginSpec *q;
-
- bool reportError(const QString &err);
- static const QRegularExpression &versionRegExp();
-};
-
-} // namespace Internal
-} // namespace ExtensionSystem
diff --git a/src/libs/extensionsystem/pluginview.cpp b/src/libs/extensionsystem/pluginview.cpp
index 40738be487..0c6aeb2dff 100644
--- a/src/libs/extensionsystem/pluginview.cpp
+++ b/src/libs/extensionsystem/pluginview.cpp
@@ -5,7 +5,7 @@
#include "extensionsystemtr.h"
#include "pluginmanager.h"
-#include "pluginspec_p.h"
+#include "pluginspec.h"
#include <utils/algorithm.h>
#include <utils/categorysortfiltermodel.h>
@@ -50,8 +50,9 @@
*/
/*!
- \fn void ExtensionSystem::PluginView::pluginSettingsChanged(ExtensionSystem::PluginSpec *spec)
- The settings for the plugin list entry corresponding to \a spec changed.
+ \fn void ExtensionSystem::PluginView::pluginsChanged(const QSet<ExtensionSystem::PluginSpec *> &spec, bool enabled)
+ The value of \a enabled for the plugin list entry corresponding to \a spec
+ changed.
*/
using namespace Utils;
@@ -119,7 +120,7 @@ public:
toolTip = Tr::tr("Path: %1\nPlugin is disabled by command line argument.");
else
toolTip = Tr::tr("Path: %1");
- return toolTip.arg(QDir::toNativeSeparators(m_spec->filePath()));
+ return toolTip.arg(m_spec->filePath().toUserOutput());
}
if (role == Qt::DecorationRole) {
bool ok = !m_spec->hasError();
@@ -198,7 +199,7 @@ public:
class CollectionItem : public TreeItem
{
public:
- CollectionItem(const QString &name, const QVector<PluginSpec *> &plugins, PluginView *view)
+ CollectionItem(const QString &name, const PluginSpecs &plugins, PluginView *view)
: m_name(name)
, m_plugins(plugins)
, m_view(view)
@@ -238,7 +239,7 @@ public:
bool setData(int column, const QVariant &data, int role) override
{
if (column == LoadedColumn && role == Qt::CheckStateRole) {
- const QVector<PluginSpec *> affectedPlugins
+ const PluginSpecs affectedPlugins
= Utils::filtered(m_plugins, [](PluginSpec *spec) { return !spec->isRequired(); });
if (m_view->setPluginsEnabled(toSet(affectedPlugins), data.toBool())) {
update();
@@ -258,7 +259,7 @@ public:
public:
QString m_name;
- const QVector<PluginSpec *> m_plugins;
+ const PluginSpecs m_plugins;
PluginView *m_view; // Not owned.
};
@@ -346,7 +347,7 @@ void PluginView::updatePlugins()
// Model.
m_model->clear();
- const QHash<QString, QVector<PluginSpec *>> pluginCollections
+ const QHash<QString, PluginSpecs> pluginCollections
= PluginManager::pluginCollections();
std::vector<CollectionItem *> collections;
const auto end = pluginCollections.cend();
@@ -416,8 +417,8 @@ bool PluginView::setPluginsEnabled(const QSet<PluginSpec *> &plugins, bool enabl
});
QTC_ASSERT(item, continue);
if (m_affectedPlugins.find(spec) == m_affectedPlugins.end())
- m_affectedPlugins[spec] = spec->d->enabledBySettings;
- spec->d->setEnabledBySettings(enable);
+ m_affectedPlugins[spec] = spec->isEnabledBySettings();
+ spec->setEnabledBySettings(enable);
item->updateColumn(LoadedColumn);
item->parent()->updateColumn(LoadedColumn);
}
@@ -428,7 +429,7 @@ bool PluginView::setPluginsEnabled(const QSet<PluginSpec *> &plugins, bool enabl
void PluginView::cancelChanges()
{
for (auto element : m_affectedPlugins)
- element.first->d->setEnabledBySettings(element.second);
+ element.first->setEnabledBySettings(element.second);
}
} // namespace ExtensionSystem
diff --git a/src/libs/languageserverprotocol/CMakeLists.txt b/src/libs/languageserverprotocol/CMakeLists.txt
index a3031413a4..0261046016 100644
--- a/src/libs/languageserverprotocol/CMakeLists.txt
+++ b/src/libs/languageserverprotocol/CMakeLists.txt
@@ -22,5 +22,6 @@ add_qtc_library(LanguageServerProtocol
servercapabilities.cpp servercapabilities.h
shutdownmessages.cpp shutdownmessages.h
textsynchronization.cpp textsynchronization.h
+ typehierarchy.cpp typehierarchy.h
workspace.cpp workspace.h
)
diff --git a/src/libs/languageserverprotocol/callhierarchy.cpp b/src/libs/languageserverprotocol/callhierarchy.cpp
index 4b19d71fa8..49b789c28a 100644
--- a/src/libs/languageserverprotocol/callhierarchy.cpp
+++ b/src/libs/languageserverprotocol/callhierarchy.cpp
@@ -25,4 +25,9 @@ CallHierarchyOutgoingCallsRequest::CallHierarchyOutgoingCallsRequest(
: Request(methodName, params)
{}
+std::optional<QList<SymbolTag>> CallHierarchyItem::symbolTags() const
+{
+ return Internal::getSymbolTags(*this);
+}
+
} // namespace LanguageServerProtocol
diff --git a/src/libs/languageserverprotocol/callhierarchy.h b/src/libs/languageserverprotocol/callhierarchy.h
index 1cc022d6d1..27e241b8ba 100644
--- a/src/libs/languageserverprotocol/callhierarchy.h
+++ b/src/libs/languageserverprotocol/callhierarchy.h
@@ -18,6 +18,8 @@ public:
SymbolKind symbolKind() const { return SymbolKind(typedValue<int>(kindKey)); }
void setSymbolKind(const SymbolKind &symbolKind) { insert(kindKey, int(symbolKind)); }
+ std::optional<QList<SymbolTag>> symbolTags() const;
+
Range range() const { return typedValue<Range>(rangeKey); }
void setRange(const Range &range) { insert(rangeKey, range); }
diff --git a/src/libs/languageserverprotocol/clientcapabilities.cpp b/src/libs/languageserverprotocol/clientcapabilities.cpp
index acae7ad554..60ea4523fd 100644
--- a/src/libs/languageserverprotocol/clientcapabilities.cpp
+++ b/src/libs/languageserverprotocol/clientcapabilities.cpp
@@ -19,6 +19,20 @@ void SymbolCapabilities::SymbolKindCapabilities::setValueSet(const QList<SymbolK
insert(valueSetKey, enumArrayToJsonArray<SymbolKind>(valueSet));
}
+std::optional<QList<SymbolTag> > SymbolCapabilities::SymbolTagCapabilities::valueSet() const
+{
+ if (std::optional<QList<int>> array = optionalArray<int>(valueSetKey)) {
+ return std::make_optional(
+ Utils::transform(*array, [](int value) { return static_cast<SymbolTag>(value); }));
+ }
+ return std::nullopt;
+}
+
+void SymbolCapabilities::SymbolTagCapabilities::setValueSet(const QList<SymbolTag> &valueSet)
+{
+ insert(valueSetKey, enumArrayToJsonArray<SymbolTag>(valueSet));
+}
+
WorkspaceClientCapabilities::WorkspaceClientCapabilities()
{
setWorkspaceFolders(true);
diff --git a/src/libs/languageserverprotocol/clientcapabilities.h b/src/libs/languageserverprotocol/clientcapabilities.h
index 367e1b3fe4..91c9954cd7 100644
--- a/src/libs/languageserverprotocol/clientcapabilities.h
+++ b/src/libs/languageserverprotocol/clientcapabilities.h
@@ -131,12 +131,31 @@ public:
void clearValueSet() { remove(valueSetKey); }
};
+ class LANGUAGESERVERPROTOCOL_EXPORT SymbolTagCapabilities : public JsonObject
+ {
+ public:
+ using JsonObject::JsonObject;
+
+ /*
+ * The client supports tags on `SymbolInformation` and `WorkspaceSymbol`.
+ * Clients supporting tags have to handle unknown tags gracefully.
+ */
+ std::optional<QList<SymbolTag>> valueSet() const;
+ void setValueSet(const QList<SymbolTag> &valueSet);
+ void clearValueSet() { remove(valueSetKey); }
+ };
+
// Specific capabilities for the `SymbolKind` in the `workspace/symbol` request.
std::optional<SymbolKindCapabilities> symbolKind() const
{ return optionalValue<SymbolKindCapabilities>(symbolKindKey); }
void setSymbolKind(const SymbolKindCapabilities &symbolKind) { insert(symbolKindKey, symbolKind); }
void clearSymbolKind() { remove(symbolKindKey); }
+ std::optional<SymbolTagCapabilities> symbolTag() const
+ { return optionalValue<SymbolTagCapabilities>(tagSupportKey); }
+ void setSymbolTag(const SymbolTagCapabilities &symbolTag) { insert(tagSupportKey, symbolTag); }
+ void clearSymbolTag() { remove(tagSupportKey); }
+
std::optional<bool> hierarchicalDocumentSymbolSupport() const
{ return optionalValue<bool>(hierarchicalDocumentSymbolSupportKey); }
void setHierarchicalDocumentSymbolSupport(bool hierarchicalDocumentSymbolSupport)
@@ -510,6 +529,12 @@ public:
void setCallHierarchy(const DynamicRegistrationCapabilities &callHierarchy)
{ insert(callHierarchyKey, callHierarchy); }
void clearCallHierarchy() { remove(callHierarchyKey); }
+
+ std::optional<DynamicRegistrationCapabilities> typeHierarchy() const
+ { return optionalValue<DynamicRegistrationCapabilities>(typeHierarchyKey); }
+ void setTypeHierarchy(const DynamicRegistrationCapabilities &typeHierarchy)
+ { insert(typeHierarchyKey, typeHierarchy); }
+ void clearTypeHierarchy() { remove(typeHierarchyKey); }
};
class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensWorkspaceClientCapabilities : public JsonObject
diff --git a/src/libs/languageserverprotocol/jsonkeys.h b/src/libs/languageserverprotocol/jsonkeys.h
index 236d28bfda..37894f166e 100644
--- a/src/libs/languageserverprotocol/jsonkeys.h
+++ b/src/libs/languageserverprotocol/jsonkeys.h
@@ -193,10 +193,12 @@ constexpr Key startKey{"start"};
constexpr Key supportedKey{"supported"};
constexpr Key symbolKey{"symbol"};
constexpr Key symbolKindKey{"symbolKind"};
+constexpr Key symbolTagKey{"symbolTag"};
constexpr Key syncKindKey{"syncKind"};
constexpr Key synchronizationKey{"synchronization"};
constexpr Key tabSizeKey{"tabSize"};
constexpr Key tagsKey{"tags"};
+constexpr Key tagSupportKey{"tagSupport"};
constexpr Key targetKey{"target"};
constexpr Key textDocumentKey{"textDocument"};
constexpr Key textDocumentSyncKey{"textDocumentSync"};
@@ -216,6 +218,8 @@ constexpr Key trimTrailingWhitespaceKey{"trimTrailingWhitespace"};
constexpr Key typeDefinitionKey{"typeDefinition"};
constexpr Key typeDefinitionProviderKey{"typeDefinitionProvider"};
constexpr Key typeKey{"type"};
+constexpr Key typeHierarchyKey{"typeHierarchy"};
+constexpr Key typeHierarchyProviderKey{"typeHierarchyProvider"};
constexpr Key unregistrationsKey{"unregistrations"};
constexpr Key uriKey{"uri"};
constexpr Key valueKey{"value"};
diff --git a/src/libs/languageserverprotocol/jsonobject.h b/src/libs/languageserverprotocol/jsonobject.h
index 178e74e5ad..ce2ad50a40 100644
--- a/src/libs/languageserverprotocol/jsonobject.h
+++ b/src/libs/languageserverprotocol/jsonobject.h
@@ -10,26 +10,11 @@
#include <QDebug>
#include <QJsonObject>
-#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
#include <QLatin1StringView>
-#else
-#include <QLatin1String>
-#endif
namespace LanguageServerProtocol {
-#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
using Key = QLatin1StringView;
-#else
-class Key : public QLatin1String
-{
-public:
- using QLatin1String::QLatin1String;
- constexpr inline explicit Key(const char *s) noexcept
- : QLatin1String(s, std::char_traits<char>::length(s))
- {}
-};
-#endif
class LANGUAGESERVERPROTOCOL_EXPORT JsonObject
{
@@ -108,7 +93,8 @@ private:
template<typename T, typename V>
JsonObject::iterator JsonObject::insertVariant(const Key key, const V &variant)
{
- return std::holds_alternative<T>(variant) ? insert(key, std::get<T>(variant)) : end();
+ const auto v = std::get_if<T>(&variant);
+ return v ? insert(key, *v) : end();
}
template<typename T1, typename T2, typename... Args, typename V>
diff --git a/src/libs/languageserverprotocol/jsonrpcmessages.h b/src/libs/languageserverprotocol/jsonrpcmessages.h
index 680bff9578..a794f7f819 100644
--- a/src/libs/languageserverprotocol/jsonrpcmessages.h
+++ b/src/libs/languageserverprotocol/jsonrpcmessages.h
@@ -68,19 +68,19 @@ public:
private:
friend size_t qHash(const MessageId &id)
{
- if (std::holds_alternative<int>(id))
- return QT_PREPEND_NAMESPACE(qHash(std::get<int>(id)));
- if (std::holds_alternative<QString>(id))
- return QT_PREPEND_NAMESPACE(qHash(std::get<QString>(id)));
+ if (const int *iid = std::get_if<int>(&id))
+ return QT_PREPEND_NAMESPACE(qHash(*iid));
+ if (const QString *sid = std::get_if<QString>(&id))
+ return QT_PREPEND_NAMESPACE(qHash(*sid));
return QT_PREPEND_NAMESPACE(qHash(0));
}
friend QDebug operator<<(QDebug stream, const MessageId &id)
{
- if (std::holds_alternative<int>(id))
- stream << std::get<int>(id);
+ if (const int *iid = std::get_if<int>(&id))
+ stream << *iid;
else
- stream << std::get<QString>(id);
+ stream << *std::get_if<QString>(&id);
return stream;
}
};
diff --git a/src/libs/languageserverprotocol/languagefeatures.cpp b/src/libs/languageserverprotocol/languagefeatures.cpp
index dcf076bd03..0c76a3b1c0 100644
--- a/src/libs/languageserverprotocol/languagefeatures.cpp
+++ b/src/libs/languageserverprotocol/languagefeatures.cpp
@@ -261,6 +261,12 @@ DocumentHighlightsResult::DocumentHighlightsResult(const QJsonValue &value)
}
}
+template<>
+MarkedString fromJsonValue<MarkedString>(const QJsonValue &value)
+{
+ return MarkedString(value);
+}
+
MarkedString::MarkedString(const QJsonValue &value)
{
if (value.isObject())
@@ -303,8 +309,8 @@ HoverContent::HoverContent(const QJsonValue &value)
bool HoverContent::isValid() const
{
- if (std::holds_alternative<MarkedString>(*this))
- return std::get<MarkedString>(*this).isValid();
+ if (const auto s = std::get_if<MarkedString>(this))
+ return s->isValid();
return true;
}
diff --git a/src/libs/languageserverprotocol/languagefeatures.h b/src/libs/languageserverprotocol/languagefeatures.h
index e5ca9df31d..6a60ec3612 100644
--- a/src/libs/languageserverprotocol/languagefeatures.h
+++ b/src/libs/languageserverprotocol/languagefeatures.h
@@ -52,6 +52,9 @@ public:
operator QJsonValue() const;
};
+template<>
+LANGUAGESERVERPROTOCOL_EXPORT MarkedString fromJsonValue<MarkedString>(const QJsonValue &value);
+
class LANGUAGESERVERPROTOCOL_EXPORT HoverContent
: public std::variant<MarkedString, QList<MarkedString>, MarkupContent>
{
diff --git a/src/libs/languageserverprotocol/languageserverprotocol.qbs b/src/libs/languageserverprotocol/languageserverprotocol.qbs
index 0368ceafee..2bf717c293 100644
--- a/src/libs/languageserverprotocol/languageserverprotocol.qbs
+++ b/src/libs/languageserverprotocol/languageserverprotocol.qbs
@@ -44,6 +44,8 @@ QtcLibrary {
"shutdownmessages.h",
"textsynchronization.cpp",
"textsynchronization.h",
+ "typehierarchy.cpp",
+ "typehierarchy.h",
"workspace.cpp",
"workspace.h",
]
diff --git a/src/libs/languageserverprotocol/lsptypes.cpp b/src/libs/languageserverprotocol/lsptypes.cpp
index ddd9b0e66c..1bc8707572 100644
--- a/src/libs/languageserverprotocol/lsptypes.cpp
+++ b/src/libs/languageserverprotocol/lsptypes.cpp
@@ -6,6 +6,7 @@
#include "languageserverprotocoltr.h"
#include "lsputils.h"
+#include <utils/algorithm.h>
#include <utils/textutils.h>
#include <QFile>
@@ -100,10 +101,10 @@ MarkupOrString::MarkupOrString(const QJsonValue &val)
QJsonValue MarkupOrString::toJson() const
{
- if (std::holds_alternative<QString>(*this))
- return std::get<QString>(*this);
- if (std::holds_alternative<MarkupContent>(*this))
- return QJsonValue(std::get<MarkupContent>(*this));
+ if (const auto s = std::get_if<QString>(this))
+ return *s;
+ if (const auto c = std::get_if<MarkupContent>(this))
+ return QJsonValue(*c);
return {};
}
@@ -487,4 +488,27 @@ bool DeleteFileOperation::isValid() const
return contains(uriKey) && value(kindKey) == "delete";
}
+namespace Internal {
+std::optional<QList<SymbolTag>> getSymbolTags(const JsonObject &o)
+{
+ const QJsonObject &obj = o;
+ const QJsonValue &val = obj.value(tagsKey);
+ if (val.isUndefined() || !val.isArray())
+ return std::nullopt;
+
+ return Utils::transform<QList<SymbolTag>>(val.toArray(), [](const QJsonValue &v) {
+ return static_cast<SymbolTag>(v.toInt());
+ });
+}
+} // namespace Internal
+
+std::optional<QList<SymbolTag>> SymbolInformation::symbolTags() const
+{
+ return Internal::getSymbolTags(*this);
+}
+
+std::optional<QList<SymbolTag>> DocumentSymbol::symbolTags() const
+{
+ return Internal::getSymbolTags(*this);
+}
} // namespace LanguageServerProtocol
diff --git a/src/libs/languageserverprotocol/lsptypes.h b/src/libs/languageserverprotocol/lsptypes.h
index ae19f36145..6e25fcb49d 100644
--- a/src/libs/languageserverprotocol/lsptypes.h
+++ b/src/libs/languageserverprotocol/lsptypes.h
@@ -574,6 +574,13 @@ public:
{ return contains(uriKey) && contains(nameKey); }
};
+enum class SymbolTag {
+ Deprecated = 1,
+};
+namespace Internal {
+std::optional<QList<SymbolTag>> getSymbolTags(const JsonObject &o);
+} // namespace Internal
+
class LANGUAGESERVERPROTOCOL_EXPORT SymbolInformation : public JsonObject
{
public:
@@ -585,6 +592,8 @@ public:
int kind() const { return typedValue<int>(kindKey); }
void setKind(int kind) { insert(kindKey, kind); }
+ std::optional<QList<SymbolTag>> symbolTags() const;
+
std::optional<bool> deprecated() const { return optionalValue<bool>(deprecatedKey); }
void setDeprecated(bool deprecated) { insert(deprecatedKey, deprecated); }
void clearDeprecated() { remove(deprecatedKey); }
@@ -616,6 +625,8 @@ public:
int kind() const { return typedValue<int>(kindKey); }
void setKind(int kind) { insert(kindKey, kind); }
+ std::optional<QList<SymbolTag>> symbolTags() const;
+
std::optional<bool> deprecated() const { return optionalValue<bool>(deprecatedKey); }
void setDeprecated(bool deprecated) { insert(deprecatedKey, deprecated); }
void clearDeprecated() { remove(deprecatedKey); }
@@ -693,4 +704,4 @@ enum Kind {
};
} // namespace CompletionItemKind
-} // namespace LanguageClient
+} // namespace LanguageServerProtocol
diff --git a/src/libs/languageserverprotocol/lsputils.h b/src/libs/languageserverprotocol/lsputils.h
index 0c815bafd8..a5178f813f 100644
--- a/src/libs/languageserverprotocol/lsputils.h
+++ b/src/libs/languageserverprotocol/lsputils.h
@@ -68,7 +68,7 @@ public:
if (value.isArray()) {
QList<T> values;
values.reserve(value.toArray().count());
- for (auto arrayValue : value.toArray())
+ for (const auto &arrayValue : value.toArray())
values << fromJsonValue<T>(arrayValue);
*this = values;
} else {
@@ -89,15 +89,16 @@ public:
QList<T> toListOrEmpty() const
{
- if (std::holds_alternative<QList<T>>(*this))
- return std::get<QList<T>>(*this);
+ if (const auto l = std::get_if<QList<T>>(this))
+ return *l;
return {};
}
QList<T> toList() const
{
- QTC_ASSERT(std::holds_alternative<QList<T>>(*this), return {});
- return std::get<QList<T>>(*this);
+ const auto l = std::get_if<QList<T>>(this);
+ QTC_ASSERT(l, return {});
+ return *l;
}
bool isNull() const { return std::holds_alternative<std::nullptr_t>(*this); }
};
@@ -127,15 +128,17 @@ public:
T value(const T &defaultValue = T()) const
{
- QTC_ASSERT(std::holds_alternative<T>(*this), return defaultValue);
- return std::get<T>(*this);
+ const auto t = std::get_if<T>(this);
+ QTC_ASSERT(t, return defaultValue);
+ return *t;
}
template<typename Type>
LanguageClientValue<Type> transform()
{
- QTC_ASSERT(!std::holds_alternative<T>(*this), return LanguageClientValue<Type>());
- return Type(std::get<T>(*this));
+ const auto t = std::get_if<T>(this);
+ QTC_ASSERT(t, return LanguageClientValue<Type>());
+ return Type(*t);
}
bool isNull() const { return std::holds_alternative<std::nullptr_t>(*this); }
diff --git a/src/libs/languageserverprotocol/progresssupport.cpp b/src/libs/languageserverprotocol/progresssupport.cpp
index 2ae4af1b98..ce110782dc 100644
--- a/src/libs/languageserverprotocol/progresssupport.cpp
+++ b/src/libs/languageserverprotocol/progresssupport.cpp
@@ -21,9 +21,9 @@ ProgressToken::ProgressToken(const QJsonValue &value)
ProgressToken::operator QJsonValue() const
{
- if (std::holds_alternative<QString>(*this))
- return QJsonValue(std::get<QString>(*this));
- return QJsonValue(std::get<int>(*this));
+ if (const auto s = std::get_if<QString>(this))
+ return QJsonValue(*s);
+ return QJsonValue(*std::get_if<int>(this));
}
ProgressParams::ProgressType ProgressParams::value() const
diff --git a/src/libs/languageserverprotocol/servercapabilities.cpp b/src/libs/languageserverprotocol/servercapabilities.cpp
index 885198d12e..b109d08687 100644
--- a/src/libs/languageserverprotocol/servercapabilities.cpp
+++ b/src/libs/languageserverprotocol/servercapabilities.cpp
@@ -189,13 +189,35 @@ void ServerCapabilities::setCallHierarchyProvider(
const std::variant<bool, WorkDoneProgressOptions> &callHierarchyProvider)
{
QJsonValue val;
- if (std::holds_alternative<bool>(callHierarchyProvider))
- val = std::get<bool>(callHierarchyProvider);
- else if (std::holds_alternative<WorkDoneProgressOptions>(callHierarchyProvider))
- val = QJsonObject(std::get<WorkDoneProgressOptions>(callHierarchyProvider));
+ if (const auto boolvalue = std::get_if<bool>(&callHierarchyProvider))
+ val = *boolvalue;
+ else if (const auto options = std::get_if<WorkDoneProgressOptions>(&callHierarchyProvider))
+ val = QJsonObject(*options);
insert(callHierarchyProviderKey, val);
}
+std::optional<std::variant<bool, WorkDoneProgressOptions>> ServerCapabilities::typeHierarchyProvider()
+ const
+{
+ const QJsonValue &provider = value(typeHierarchyProviderKey);
+ if (provider.isBool())
+ return provider.toBool();
+ else if (provider.isObject())
+ return WorkDoneProgressOptions(provider.toObject());
+ return std::nullopt;
+}
+
+void ServerCapabilities::setTypeHierarchyProvider(
+ const std::variant<bool, WorkDoneProgressOptions> &typeHierarchyProvider)
+{
+ QJsonValue val;
+ if (const auto boolvalue = std::get_if<bool>(&typeHierarchyProvider))
+ val = *boolvalue;
+ else if (const auto options = std::get_if<WorkDoneProgressOptions>(&typeHierarchyProvider))
+ val = QJsonObject(*options);
+ insert(typeHierarchyProviderKey, val);
+}
+
std::optional<std::variant<bool, WorkDoneProgressOptions>>
ServerCapabilities::workspaceSymbolProvider() const
{
diff --git a/src/libs/languageserverprotocol/servercapabilities.h b/src/libs/languageserverprotocol/servercapabilities.h
index c3b50dd590..32598d2afa 100644
--- a/src/libs/languageserverprotocol/servercapabilities.h
+++ b/src/libs/languageserverprotocol/servercapabilities.h
@@ -330,6 +330,10 @@ public:
void setCallHierarchyProvider(const std::variant<bool, WorkDoneProgressOptions> &callHierarchyProvider);
void clearCallHierarchyProvider() { remove(callHierarchyProviderKey); }
+ std::optional<std::variant<bool, WorkDoneProgressOptions>> typeHierarchyProvider() const;
+ void setTypeHierarchyProvider(const std::variant<bool, WorkDoneProgressOptions> &typeHierarchyProvider);
+ void clearTypeHierarchyProvider() { remove(typeHierarchyProviderKey); }
+
// The server provides workspace symbol support.
std::optional<std::variant<bool, WorkDoneProgressOptions>> workspaceSymbolProvider() const;
void setWorkspaceSymbolProvider(std::variant<bool, WorkDoneProgressOptions> workspaceSymbolProvider);
diff --git a/src/libs/languageserverprotocol/typehierarchy.cpp b/src/libs/languageserverprotocol/typehierarchy.cpp
new file mode 100644
index 0000000000..1734e25a6b
--- /dev/null
+++ b/src/libs/languageserverprotocol/typehierarchy.cpp
@@ -0,0 +1,28 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "typehierarchy.h"
+
+namespace LanguageServerProtocol {
+
+bool TypeHierarchyItem::isValid() const
+{
+ return contains(nameKey) && contains(kindKey) && contains(uriKey) && contains(rangeKey)
+ && contains(selectionRangeKey);
+}
+std::optional<QList<SymbolTag>> TypeHierarchyItem::symbolTags() const
+{
+ return Internal::getSymbolTags(*this);
+}
+
+PrepareTypeHierarchyRequest::PrepareTypeHierarchyRequest(const TextDocumentPositionParams &params)
+ : Request(methodName, params)
+{}
+TypeHierarchySupertypesRequest::TypeHierarchySupertypesRequest(const TypeHierarchyParams &params)
+ : Request(methodName, params)
+{}
+TypeHierarchySubtypesRequest::TypeHierarchySubtypesRequest(const TypeHierarchyParams &params)
+ : Request(methodName, params)
+{}
+
+} // namespace LanguageServerProtocol
diff --git a/src/libs/languageserverprotocol/typehierarchy.h b/src/libs/languageserverprotocol/typehierarchy.h
new file mode 100644
index 0000000000..d5e88c8cfd
--- /dev/null
+++ b/src/libs/languageserverprotocol/typehierarchy.h
@@ -0,0 +1,85 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "jsonrpcmessages.h"
+
+namespace LanguageServerProtocol {
+
+class LANGUAGESERVERPROTOCOL_EXPORT TypeHierarchyItem : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ QString name() const { return typedValue<QString>(nameKey); }
+ void setName(const QString &name) { insert(nameKey, name); }
+
+ SymbolKind symbolKind() const { return SymbolKind(typedValue<int>(kindKey)); }
+ void setSymbolKind(const SymbolKind &symbolKind) { insert(kindKey, int(symbolKind)); }
+
+ std::optional<QList<SymbolTag>> symbolTags() const;
+
+ std::optional<QString> detail() const { return optionalValue<QString>(detailKey); }
+ void setDetail(const QString &detail) { insert(detailKey, detail); }
+ void clearDetail() { remove(detailKey); }
+
+ DocumentUri uri() const { return DocumentUri::fromProtocol(typedValue<QString>(uriKey)); }
+ void setUri(const DocumentUri &uri) { insert(uriKey, uri); }
+
+ Range range() const { return typedValue<Range>(rangeKey); }
+ void setRange(const Range &range) { insert(rangeKey, range); }
+
+ Range selectionRange() const { return typedValue<Range>(selectionRangeKey); }
+ void setSelectionRange(Range selectionRange) { insert(selectionRangeKey, selectionRange); }
+
+ /*
+ * A data entry field that is preserved between a type hierarchy prepare and
+ * supertypes or subtypes requests. It could also be used to identify the
+ * type hierarchy in the server, helping improve the performance on
+ * resolving supertypes and subtypes.
+ */
+ std::optional<QJsonValue> data() const { return optionalValue<QJsonValue>(dataKey); }
+
+ bool isValid() const override;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT PrepareTypeHierarchyRequest : public Request<
+ LanguageClientArray<TypeHierarchyItem>, std::nullptr_t, TextDocumentPositionParams>
+{
+public:
+ explicit PrepareTypeHierarchyRequest(const TextDocumentPositionParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/prepareTypeHierarchy";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT TypeHierarchyParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ TypeHierarchyItem item() const { return typedValue<TypeHierarchyItem>(itemKey); }
+ void setItem(const TypeHierarchyItem &item) { insert(itemKey, item); }
+
+ bool isValid() const override { return contains(itemKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT TypeHierarchySupertypesRequest : public Request<
+ LanguageClientArray<TypeHierarchyItem>, std::nullptr_t, TypeHierarchyParams>
+{
+public:
+ explicit TypeHierarchySupertypesRequest(const TypeHierarchyParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "typeHierarchy/supertypes";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT TypeHierarchySubtypesRequest
+ : public Request<LanguageClientArray<TypeHierarchyItem>, std::nullptr_t, TypeHierarchyParams>
+{
+public:
+ explicit TypeHierarchySubtypesRequest(const TypeHierarchyParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "typeHierarchy/subtypes";
+};
+
+} // namespace LanguageServerProtocol
diff --git a/src/libs/languageserverprotocol/workspace.cpp b/src/libs/languageserverprotocol/workspace.cpp
index 2d49be2d5f..0d37b80934 100644
--- a/src/libs/languageserverprotocol/workspace.cpp
+++ b/src/libs/languageserverprotocol/workspace.cpp
@@ -64,10 +64,11 @@ ExecuteCommandParams::ExecuteCommandParams(const Command &command)
LanguageServerProtocol::WorkSpaceFolderResult::operator const QJsonValue() const
{
- if (!std::holds_alternative<QList<WorkSpaceFolder>>(*this))
+ const auto folders = std::get_if<QList<WorkSpaceFolder>>(this);
+ if (!folders)
return QJsonValue::Null;
QJsonArray array;
- for (const auto &folder : std::get<QList<WorkSpaceFolder>>(*this))
+ for (const auto &folder : *folders)
array.append(QJsonValue(folder));
return array;
}
diff --git a/src/libs/languageutils/componentversion.cpp b/src/libs/languageutils/componentversion.cpp
index 6c599e300b..edfa2c552b 100644
--- a/src/libs/languageutils/componentversion.cpp
+++ b/src/libs/languageutils/componentversion.cpp
@@ -6,31 +6,19 @@
#include <QString>
#include <QCryptographicHash>
-#include <limits>
-
namespace LanguageUtils {
-const int ComponentVersion::NoVersion = -1;
-const int ComponentVersion::MaxVersion = std::numeric_limits<int>::max();
-
-ComponentVersion::ComponentVersion()
- : _major(NoVersion), _minor(NoVersion)
-{
-}
-
-ComponentVersion::ComponentVersion(int major, int minor)
- : _major(major), _minor(minor)
-{
-}
+// QTC_TEMP
-ComponentVersion::ComponentVersion(const QString &versionString)
- : _major(NoVersion), _minor(NoVersion)
+ComponentVersion::ComponentVersion(QStringView versionString)
+ : _major(NoVersion)
+ , _minor(NoVersion)
{
- int dotIdx = versionString.indexOf(QLatin1Char('.'));
+ auto dotIdx = versionString.indexOf(QLatin1Char('.'));
if (dotIdx == -1)
return;
bool ok = false;
- int maybeMajor = versionString.left(dotIdx).toInt(&ok);
+ auto maybeMajor = versionString.left(dotIdx).toInt(&ok);
if (!ok)
return;
int maybeMinor = versionString.mid(dotIdx + 1).toInt(&ok);
@@ -40,15 +28,6 @@ ComponentVersion::ComponentVersion(const QString &versionString)
_minor = maybeMinor;
}
-ComponentVersion::~ComponentVersion()
-{
-}
-
-bool ComponentVersion::isValid() const
-{
- return _major >= 0 && _minor >= 0;
-}
-
QString ComponentVersion::toString() const
{
QByteArray temp;
@@ -61,40 +40,8 @@ QString ComponentVersion::toString() const
void ComponentVersion::addToHash(QCryptographicHash &hash) const
{
- hash.addData(reinterpret_cast<const char *>(&_major), sizeof(_major));
- hash.addData(reinterpret_cast<const char *>(&_minor), sizeof(_minor));
-}
-
-bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs)
-{
- return lhs.majorVersion() < rhs.majorVersion()
- || (lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() < rhs.minorVersion());
-}
-
-bool operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs)
-{
- return lhs.majorVersion() < rhs.majorVersion()
- || (lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() <= rhs.minorVersion());
-}
-
-bool operator>(const ComponentVersion &lhs, const ComponentVersion &rhs)
-{
- return rhs < lhs;
-}
-
-bool operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs)
-{
- return rhs <= lhs;
-}
-
-bool operator==(const ComponentVersion &lhs, const ComponentVersion &rhs)
-{
- return lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() == rhs.minorVersion();
-}
-
-bool operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs)
-{
- return !(lhs == rhs);
+ hash.addData(QByteArrayView(reinterpret_cast<const char *>(&_major), sizeof(_major)));
+ hash.addData(QByteArrayView(reinterpret_cast<const char *>(&_minor), sizeof(_minor)));
}
} // namespace LanguageUtils
diff --git a/src/libs/languageutils/componentversion.h b/src/libs/languageutils/componentversion.h
index 4b39e8402a..2b8311a6cb 100644
--- a/src/libs/languageutils/componentversion.h
+++ b/src/libs/languageutils/componentversion.h
@@ -9,6 +9,10 @@ QT_BEGIN_NAMESPACE
class QCryptographicHash;
QT_END_NAMESPACE
+#include <QStringView>
+
+#include <limits>
+
namespace LanguageUtils {
class LANGUAGEUTILS_EXPORT ComponentVersion
@@ -17,25 +21,56 @@ class LANGUAGEUTILS_EXPORT ComponentVersion
int _minor;
public:
- static const int NoVersion;
- static const int MaxVersion;
+ static constexpr int NoVersion = -1;
+ static constexpr int MaxVersion = std::numeric_limits<int>::max();
+
+ ComponentVersion()
+ : _major(NoVersion)
+ , _minor(NoVersion)
+ {}
- ComponentVersion();
- ComponentVersion(int major, int minor);
- explicit ComponentVersion(const QString &versionString);
- ~ComponentVersion();
+ ComponentVersion(int major, int minor)
+ : _major(major)
+ , _minor(minor)
+ {}
+
+ explicit ComponentVersion(QStringView versionString);
+ ~ComponentVersion() = default;
int majorVersion() const { return _major; }
int minorVersion() const { return _minor; }
- friend bool LANGUAGEUTILS_EXPORT operator<(const ComponentVersion &lhs, const ComponentVersion &rhs);
- friend bool LANGUAGEUTILS_EXPORT operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs);
- friend bool LANGUAGEUTILS_EXPORT operator>(const ComponentVersion &lhs, const ComponentVersion &rhs);
- friend bool LANGUAGEUTILS_EXPORT operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs);
- friend bool LANGUAGEUTILS_EXPORT operator==(const ComponentVersion &lhs, const ComponentVersion &rhs);
- friend bool LANGUAGEUTILS_EXPORT operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs);
+ friend bool operator<(const ComponentVersion &lhs, const ComponentVersion &rhs)
+ {
+ return std::tie(lhs._major, lhs._minor) < std::tie(rhs._major, rhs._minor);
+ }
+
+ friend bool operator<=(const ComponentVersion &lhs, const ComponentVersion &rhs)
+ {
+ return std::tie(lhs._major, lhs._minor) <= std::tie(rhs._major, rhs._minor);
+ }
+
+ friend bool operator>(const ComponentVersion &lhs, const ComponentVersion &rhs)
+ {
+ return rhs < lhs;
+ }
+
+ friend bool operator>=(const ComponentVersion &lhs, const ComponentVersion &rhs)
+ {
+ return rhs <= lhs;
+ }
+
+ friend bool operator==(const ComponentVersion &lhs, const ComponentVersion &rhs)
+ {
+ return lhs.majorVersion() == rhs.majorVersion() && lhs.minorVersion() == rhs.minorVersion();
+ }
+
+ friend bool operator!=(const ComponentVersion &lhs, const ComponentVersion &rhs)
+ {
+ return !(lhs == rhs);
+ }
- bool isValid() const;
+ bool isValid() const { return _major >= 0 && _minor >= 0; }
QString toString() const;
void addToHash(QCryptographicHash &hash) const;
};
diff --git a/src/libs/languageutils/fakemetaobject.cpp b/src/libs/languageutils/fakemetaobject.cpp
index 4c553cdc7b..da7877c1b6 100644
--- a/src/libs/languageutils/fakemetaobject.cpp
+++ b/src/libs/languageutils/fakemetaobject.cpp
@@ -6,6 +6,21 @@
using namespace LanguageUtils;
+static QByteArrayView asData(const void *mem, size_t len)
+{
+ return QByteArrayView(reinterpret_cast<const char *>(mem), len);
+}
+
+static void addData(QCryptographicHash &hash, int x)
+{
+ hash.addData(asData(&x, sizeof(int)));
+}
+
+static void addData(QCryptographicHash &hash, const QString &x)
+{
+ hash.addData(asData(x.constData(), x.size() * sizeof(QChar)));
+}
+
FakeMetaEnum::FakeMetaEnum()
{}
@@ -39,15 +54,12 @@ bool FakeMetaEnum::hasKey(const QString &key) const
void FakeMetaEnum::addToHash(QCryptographicHash &hash) const
{
- int len = m_name.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- hash.addData(reinterpret_cast<const char *>(m_name.constData()), len * sizeof(QChar));
- len = m_keys.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ addData(hash, m_name.size());
+ addData(hash, m_name);
+ addData(hash, m_keys.size());
for (const QString &key : std::as_const(m_keys)) {
- len = key.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar));
+ addData(hash, key.size());
+ addData(hash, key);
}
}
@@ -121,29 +133,23 @@ void FakeMetaMethod::setRevision(int r)
void FakeMetaMethod::addToHash(QCryptographicHash &hash) const
{
- int len = m_name.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- hash.addData(reinterpret_cast<const char *>(m_name.constData()), len * sizeof(QChar));
- hash.addData(reinterpret_cast<const char *>(&m_methodAccess), sizeof(m_methodAccess));
- hash.addData(reinterpret_cast<const char *>(&m_methodTy), sizeof(m_methodTy));
- hash.addData(reinterpret_cast<const char *>(&m_revision), sizeof(m_revision));
- len = m_paramNames.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ addData(hash, m_name.size());
+ addData(hash, m_name);
+ addData(hash, m_methodAccess);
+ addData(hash, m_methodTy);
+ addData(hash, m_revision);
+ addData(hash, m_paramNames.size());
for (const QString &pName : std::as_const(m_paramNames)) {
- len = pName.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- hash.addData(reinterpret_cast<const char *>(pName.constData()), len * sizeof(QChar));
+ addData(hash, pName.size());
+ addData(hash, pName);
}
- len = m_paramTypes.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ addData(hash, m_paramTypes.size());
for (const QString &pType : std::as_const(m_paramTypes)) {
- len = pType.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- hash.addData(reinterpret_cast<const char *>(pType.constData()), len * sizeof(QChar));
+ addData(hash, pType.size());
+ addData(hash, pType);
}
- len = m_returnType.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- hash.addData(reinterpret_cast<const char *>(m_returnType.constData()), len * sizeof(QChar));
+ addData(hash, m_returnType.size());
+ addData(hash, m_returnType);
}
QString FakeMetaMethod::describe(int baseIndent) const
@@ -213,17 +219,15 @@ int FakeMetaProperty::revision() const
void FakeMetaProperty::addToHash(QCryptographicHash &hash) const
{
- int len = m_propertyName.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- hash.addData(reinterpret_cast<const char *>(m_propertyName.constData()), len * sizeof(QChar));
- hash.addData(reinterpret_cast<const char *>(&m_revision), sizeof(m_revision));
+ addData(hash, m_propertyName.size());
+ addData(hash, m_propertyName);
+ addData(hash, m_revision);
int flags = (m_isList ? (1 << 0) : 0)
+ (m_isPointer ? (1 << 1) : 0)
+ (m_isWritable ? (1 << 2) : 0);
- hash.addData(reinterpret_cast<const char *>(&flags), sizeof(flags));
- len = m_type.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- hash.addData(reinterpret_cast<const char *>(m_type.constData()), len * sizeof(QChar));
+ addData(hash, flags);
+ addData(hash, m_type.size());
+ addData(hash, m_type);
}
QString FakeMetaProperty::describe(int baseIndent) const
@@ -361,34 +365,34 @@ QByteArray FakeMetaObject::calculateFingerprint() const
{
QCryptographicHash hash(QCryptographicHash::Sha1);
int len = m_className.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- hash.addData(reinterpret_cast<const char *>(m_className.constData()), len * sizeof(QChar));
+ addData(hash, len);
+ addData(hash, m_className);
len = m_attachedTypeName.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- hash.addData(reinterpret_cast<const char *>(m_attachedTypeName.constData()), len * sizeof(QChar));
+ addData(hash, len);
+ addData(hash, m_attachedTypeName);
len = m_defaultPropertyName.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- hash.addData(reinterpret_cast<const char *>(m_defaultPropertyName.constData()), len * sizeof(QChar));
+ addData(hash, len);
+ addData(hash, m_defaultPropertyName);
len = m_enumNameToIndex.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ addData(hash, len);
{
QStringList keys(m_enumNameToIndex.keys());
keys.sort();
for (const QString &key : std::as_const(keys)) {
len = key.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar));
+ addData(hash, len);
+ addData(hash, key);
int value = m_enumNameToIndex.value(key);
- hash.addData(reinterpret_cast<const char *>(&value), sizeof(value)); // avoid? this adds order dependency to fingerprint...
+ addData(hash, value);
m_enums.at(value).addToHash(hash);
}
}
len = m_exports.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ addData(hash, len);
for (const Export &e : std::as_const(m_exports))
e.addToHash(hash); // normalize order?
len = m_exports.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
+ addData(hash, len);
for (const FakeMetaMethod &m : std::as_const(m_methods))
m.addToHash(hash); // normalize order?
{
@@ -396,16 +400,16 @@ QByteArray FakeMetaObject::calculateFingerprint() const
keys.sort();
for (const QString &key : std::as_const(keys)) {
len = key.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- hash.addData(reinterpret_cast<const char *>(key.constData()), len * sizeof(QChar));
+ addData(hash, len);
+ addData(hash, key);
int value = m_propNameToIdx.value(key);
- hash.addData(reinterpret_cast<const char *>(&value), sizeof(value)); // avoid? this adds order dependency to fingerprint...
+ addData(hash, value);
m_props.at(value).addToHash(hash);
}
}
len = m_superName.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- hash.addData(reinterpret_cast<const char *>(m_superName.constData()), len * sizeof(QChar));
+ addData(hash, len);
+ addData(hash, m_superName);
QByteArray res = hash.result();
res.append('F');
@@ -540,14 +544,12 @@ bool FakeMetaObject::Export::isValid() const
void FakeMetaObject::Export::addToHash(QCryptographicHash &hash) const
{
- int len = package.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- hash.addData(reinterpret_cast<const char *>(package.constData()), len * sizeof(QChar));
- len = type.size();
- hash.addData(reinterpret_cast<const char *>(&len), sizeof(len));
- hash.addData(reinterpret_cast<const char *>(type.constData()), len * sizeof(QChar));
+ addData(hash, package.size());
+ addData(hash, package);
+ addData(hash, type.size());
+ addData(hash, type);
version.addToHash(hash);
- hash.addData(reinterpret_cast<const char *>(&metaObjectRevision), sizeof(metaObjectRevision));
+ addData(hash, metaObjectRevision);
}
QString FakeMetaObject::Export::describe(int baseIndent) const
diff --git a/src/libs/libs.qbs b/src/libs/libs.qbs
index a6426d4ae4..aaf42e181c 100644
--- a/src/libs/libs.qbs
+++ b/src/libs/libs.qbs
@@ -26,7 +26,9 @@ Project {
"utils/utils.qbs",
"3rdparty/libptyqt/ptyqt.qbs",
"3rdparty/libvterm/vterm.qbs",
+ "3rdparty/lua/lua.qbs",
"3rdparty/qtkeychain/qtkeychain.qbs",
+ "3rdparty/sol2/sol2.qbs",
"3rdparty/syntax-highlighting/syntax-highlighting.qbs",
"3rdparty/winpty/winpty.qbs",
"3rdparty/yaml-cpp/yaml-cpp.qbs",
diff --git a/src/libs/modelinglib/CMakeLists.txt b/src/libs/modelinglib/CMakeLists.txt
index 27801f479a..e966b39459 100644
--- a/src/libs/modelinglib/CMakeLists.txt
+++ b/src/libs/modelinglib/CMakeLists.txt
@@ -126,12 +126,15 @@ add_qtc_library(Modeling
qmt/model/mrelation.cpp qmt/model/mrelation.h
qmt/model/msourceexpansion.cpp qmt/model/msourceexpansion.h
qmt/model/mvisitor.h
+ qmt/model_ui/modeltreefilterdata.h qmt/model_ui/modeltreefilterdata.cpp
qmt/model_ui/modeltreeviewinterface.h
qmt/model_ui/sortedtreemodel.cpp qmt/model_ui/sortedtreemodel.h
qmt/model_ui/stereotypescontroller.cpp qmt/model_ui/stereotypescontroller.h
qmt/model_ui/treemodel.cpp qmt/model_ui/treemodel.h
qmt/model_ui/treemodelmanager.cpp qmt/model_ui/treemodelmanager.h
+ qmt/model_widgets_ui/addrelatedelementsdialog.h qmt/model_widgets_ui/addrelatedelementsdialog.cpp
qmt/model_widgets_ui/classmembersedit.cpp qmt/model_widgets_ui/classmembersedit.h
+ qmt/model_widgets_ui/modeltreefilter.h qmt/model_widgets_ui/modeltreefilter.cpp
qmt/model_widgets_ui/modeltreeview.cpp qmt/model_widgets_ui/modeltreeview.h
qmt/model_widgets_ui/palettebox.cpp qmt/model_widgets_ui/palettebox.h
qmt/model_widgets_ui/propertiesview.cpp qmt/model_widgets_ui/propertiesview.h
@@ -158,6 +161,7 @@ add_qtc_library(Modeling
qmt/style/defaultstyle.h
qmt/style/objectvisuals.cpp qmt/style/objectvisuals.h
qmt/style/relationstarterstyle.cpp qmt/style/relationstarterstyle.h
+ qmt/style/relationvisuals.cpp qmt/style/relationvisuals.h
qmt/style/stylecontroller.cpp qmt/style/stylecontroller.h
qmt/style/style.cpp
qmt/style/styledobject.cpp qmt/style/styledobject.h
diff --git a/src/libs/modelinglib/modelinglib.qbs b/src/libs/modelinglib/modelinglib.qbs
index 90fc1d0d98..9929bf33c0 100644
--- a/src/libs/modelinglib/modelinglib.qbs
+++ b/src/libs/modelinglib/modelinglib.qbs
@@ -238,6 +238,8 @@ QtcLibrary {
"model_controller/mselection.h",
"model_controller/mvoidvisitor.cpp",
"model_controller/mvoidvisitor.h",
+ "model_ui/modeltreefilterdata.cpp",
+ "model_ui/modeltreefilterdata.h",
"model_ui/modeltreeviewinterface.h",
"model_ui/sortedtreemodel.cpp",
"model_ui/sortedtreemodel.h",
@@ -247,8 +249,12 @@ QtcLibrary {
"model_ui/treemodel.h",
"model_ui/treemodelmanager.cpp",
"model_ui/treemodelmanager.h",
+ "model_widgets_ui/addrelatedelementsdialog.h",
+ "model_widgets_ui/addrelatedelementsdialog.cpp",
"model_widgets_ui/classmembersedit.cpp",
"model_widgets_ui/classmembersedit.h",
+ "model_widgets_ui/modeltreefilter.cpp",
+ "model_widgets_ui/modeltreefilter.h",
"model_widgets_ui/modeltreeview.cpp",
"model_widgets_ui/modeltreeview.h",
"model_widgets_ui/palettebox.cpp",
@@ -295,6 +301,8 @@ QtcLibrary {
"style/objectvisuals.h",
"style/relationstarterstyle.cpp",
"style/relationstarterstyle.h",
+ "style/relationvisuals.cpp",
+ "style/relationvisuals.h",
"style/style.cpp",
"style/style.h",
"style/stylecontroller.cpp",
diff --git a/src/libs/modelinglib/qmt/config/configcontroller.cpp b/src/libs/modelinglib/qmt/config/configcontroller.cpp
index 4bb9b806c4..c584bce1c2 100644
--- a/src/libs/modelinglib/qmt/config/configcontroller.cpp
+++ b/src/libs/modelinglib/qmt/config/configcontroller.cpp
@@ -9,12 +9,11 @@
#include "qmt/stereotype/stereotypecontroller.h"
-#include <QDir>
-#include <QFileInfo>
-#include <QFile>
-
#include <QDebug>
+using Utils::FilePath;
+using Utils::FilePaths;
+
namespace qmt {
class ConfigController::ConfigControllerPrivate
@@ -39,7 +38,7 @@ void ConfigController::setStereotypeController(StereotypeController *stereotypeC
d->m_stereotypeController = stereotypeController;
}
-void ConfigController::readStereotypeDefinitions(const QString &path)
+void ConfigController::readStereotypeDefinitions(const FilePath &path)
{
if (path.isEmpty()) {
// TODO add error handling
@@ -54,25 +53,19 @@ void ConfigController::readStereotypeDefinitions(const QString &path)
connect(&parser, &StereotypeDefinitionParser::toolbarParsed,
this, &ConfigController::onToolbarParsed);
- QStringList fileNames;
- QDir dir;
- QFileInfo fileInfo(path);
- if (fileInfo.isFile()) {
- dir.setPath(fileInfo.path());
- fileNames.append(fileInfo.fileName());
- } else if (fileInfo.isDir()) {
- dir.setPath(path);
- dir.setNameFilters(QStringList("*.def"));
- fileNames = dir.entryList(QDir::Files);
+ FilePaths paths;
+ if (path.isFile()) {
+ paths.append(path);
+ } else if (path.isDir()) {
+ paths = path.dirEntries({ { "*.def" } });
} else {
// TODO add error handling
return;
}
- for (const QString &fileName : std::as_const(fileNames)) {
- QFile file(QFileInfo(dir, fileName).absoluteFilePath());
- if (file.open(QIODevice::ReadOnly)) {
- QString text = QString::fromUtf8(file.readAll());
- file.close();
+ for (const FilePath &filePath : std::as_const(paths)) {
+ auto data = filePath.fileContents();
+ if (data.has_value()) {
+ QString text = QString::fromUtf8(data.value());
StringTextSource source;
source.setSourceId(1);
source.setText(text);
diff --git a/src/libs/modelinglib/qmt/config/configcontroller.h b/src/libs/modelinglib/qmt/config/configcontroller.h
index 28e6d65afc..7d8af82f6e 100644
--- a/src/libs/modelinglib/qmt/config/configcontroller.h
+++ b/src/libs/modelinglib/qmt/config/configcontroller.h
@@ -5,6 +5,8 @@
#include "qmt/infrastructure/qmt_global.h"
+#include <utils/filepath.h>
+
#include <QObject>
namespace qmt {
@@ -24,7 +26,7 @@ public:
void setStereotypeController(StereotypeController *stereotypeController);
- void readStereotypeDefinitions(const QString &path);
+ void readStereotypeDefinitions(const Utils::FilePath &path);
private:
void onStereotypeIconParsed(const StereotypeIcon &stereotypeIcon);
diff --git a/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.cpp b/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.cpp
index b3caebb164..d81f71c500 100644
--- a/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.cpp
+++ b/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.cpp
@@ -28,12 +28,15 @@ static const int KEYWORD_WIDTH = 6;
static const int KEYWORD_HEIGHT = 7;
static const int KEYWORD_MINWIDTH = 8;
static const int KEYWORD_MINHEIGHT = 9;
-static const int KEYWORD_LOCK_SIZE = 10;
-static const int KEYWORD_DISPLAY = 11;
-static const int KEYWORD_TEXTALIGN = 12;
-static const int KEYWORD_BASECOLOR = 13;
-static const int KEYWORD_SHAPE = 14;
-static const int KEYWORD_OUTLINE = 15;
+static const int KEYWORD_ICONWIDTH = 10;
+static const int KEYWORD_ICONHEIGHT = 11;
+static const int KEYWORD_LOCK_SIZE = 12;
+static const int KEYWORD_DISPLAY = 13;
+static const int KEYWORD_TEXTALIGN = 14;
+static const int KEYWORD_BASECOLOR = 15;
+static const int KEYWORD_SHAPE = 16;
+static const int KEYWORD_OUTLINE = 17;
+static const int KEYWORD_Z = 18;
// Shape items
static const int KEYWORD_CIRCLE = 30;
@@ -102,8 +105,18 @@ static const int KEYWORD_DASH = 136;
static const int KEYWORD_DASHDOT = 137;
static const int KEYWORD_DASHDOTDOT = 138;
static const int KEYWORD_COLOR = 139;
-
-// Operatoren
+static const int KEYWORD_EMPHASIZED = 140;
+
+// Shape Value Units and Origin
+static const int KEYWORD_ABS = 150;
+static const int KEYWORD_FIX = 151;
+static const int KEYWORD_SCALE = 152;
+static const int KEYWORD_FRACT = 153;
+static const int KEYWORD_ORIGIN = 154;
+static const int KEYWORD_CENTER = 155;
+static const int KEYWORD_SIZE = 156;
+
+// Operators
static const int OPERATOR_SEMICOLON = 1;
static const int OPERATOR_BRACE_OPEN = 2;
static const int OPERATOR_BRACE_CLOSE = 3;
@@ -111,6 +124,7 @@ static const int OPERATOR_COLON = 4;
static const int OPERATOR_COMMA = 5;
static const int OPERATOR_PERIOD = 6;
static const int OPERATOR_MINUS = 7;
+static const int OPERATOR_PERCENTAGE = 8;
template <typename T, typename U>
QHash<T, U> operator<<(QHash<T, U> hash, QPair<T, U> pair) {
@@ -213,12 +227,15 @@ void StereotypeDefinitionParser::parse(ITextSource *source)
{"height", KEYWORD_HEIGHT},
{"minwidth", KEYWORD_MINWIDTH},
{"minheight", KEYWORD_MINHEIGHT},
+ {"iconwidth", KEYWORD_ICONWIDTH},
+ {"iconheight", KEYWORD_ICONHEIGHT},
{"locksize", KEYWORD_LOCK_SIZE},
{"display", KEYWORD_DISPLAY},
{"textalignment", KEYWORD_TEXTALIGN},
{"basecolor", KEYWORD_BASECOLOR},
{"shape", KEYWORD_SHAPE},
{"outline", KEYWORD_OUTLINE},
+ {"z", KEYWORD_Z},
{"circle", KEYWORD_CIRCLE},
{"ellipse", KEYWORD_ELLIPSE},
{"line", KEYWORD_LINE},
@@ -276,7 +293,15 @@ void StereotypeDefinitionParser::parse(ITextSource *source)
{"dash", KEYWORD_DASH},
{"dashdot", KEYWORD_DASHDOT},
{"dashdotdot", KEYWORD_DASHDOTDOT},
- {"color", KEYWORD_COLOR}});
+ {"color", KEYWORD_COLOR},
+ {"emphasized", KEYWORD_EMPHASIZED},
+ {"abs", KEYWORD_ABS},
+ {"fix", KEYWORD_FIX},
+ {"scale", KEYWORD_SCALE},
+ {"fract", KEYWORD_FRACT},
+ {"origin", KEYWORD_ORIGIN},
+ {"center", KEYWORD_CENTER},
+ {"size", KEYWORD_SIZE}});
textScanner.setOperators({{";", OPERATOR_SEMICOLON},
{"{", OPERATOR_BRACE_OPEN},
@@ -284,7 +309,8 @@ void StereotypeDefinitionParser::parse(ITextSource *source)
{":", OPERATOR_COLON},
{",", OPERATOR_COMMA},
{".", OPERATOR_PERIOD},
- {"-", OPERATOR_MINUS}});
+ {"-", OPERATOR_MINUS},
+ {"%", OPERATOR_PERCENTAGE}});
textScanner.setSource(source);
@@ -364,6 +390,23 @@ void StereotypeDefinitionParser::parseIcon()
case KEYWORD_MINHEIGHT:
stereotypeIcon.setMinHeight(parseFloatProperty());
break;
+ case KEYWORD_ICONWIDTH:
+ stereotypeIcon.setIconWith(parseFloatProperty());
+ break;
+ case KEYWORD_ICONHEIGHT:
+ stereotypeIcon.setIconHeight(parseFloatProperty());
+ break;
+ case KEYWORD_Z:
+ {
+ const static QHash<QString, StereotypeIcon::DepthLayer> zNames = QHash<QString, StereotypeIcon::DepthLayer>()
+ << qMakePair(QString("behinditems"), StereotypeIcon::DepthBehindItems)
+ << qMakePair(QString("amongitems"), StereotypeIcon::DepthAmongItems)
+ << qMakePair(QString("beforeitems"), StereotypeIcon::DepthBeforeItems);
+ parseEnum<StereotypeIcon::DepthLayer>(
+ parseIdentifierProperty(), zNames, token.sourcePos(),
+ [&](StereotypeIcon::DepthLayer layer) { stereotypeIcon.setDepthLayer(layer); });
+ break;
+ }
case KEYWORD_LOCK_SIZE:
{
const static QHash<QString, StereotypeIcon::SizeLock> lockNames
@@ -577,7 +620,7 @@ QHash<int, StereotypeDefinitionParser::IconCommandParameter> StereotypeDefinitio
throw StereotypeDefinitionParserError("Property given twice.", token.sourcePos());
IconCommandParameter parameter = parameters.value(token.subtype());
if (parameter.type() == IconCommandParameter::ShapeValue)
- parameter.setShapeValue(ShapeValueF(parseFloatProperty(), parameter.unit(), parameter.origin()));
+ parameter.setShapeValue(parseShapeValueProperty(parameter.unit(), parameter.origin()));
else if (parameter.type() == IconCommandParameter::Boolean)
parameter.setBoolean(parseBoolProperty());
else
@@ -650,8 +693,6 @@ void StereotypeDefinitionParser::parseRelation(CustomRelation::Element element)
}
case KEYWORD_COLOR:
{
- if (element != CustomRelation::Element::Relation)
- throwUnknownPropertyError(token);
Value expression = parseProperty();
if (expression.type() == Color) {
relation.setColorType(CustomRelation::ColorType::Custom);
@@ -663,6 +704,10 @@ void StereotypeDefinitionParser::parseRelation(CustomRelation::Element element)
relation.setColorType(CustomRelation::ColorType::EndA);
} else if (colorName == "b") {
relation.setColorType(CustomRelation::ColorType::EndB);
+ } else if (colorName == "warning") {
+ relation.setColorType(CustomRelation::ColorType::Warning);
+ } else if (colorName == "error") {
+ relation.setColorType(CustomRelation::ColorType::Error);
} else if (QColor::isValidColor(colorName)) {
relation.setColorType(CustomRelation::ColorType::Custom);
relation.setColor(QColor(colorName));
@@ -674,6 +719,9 @@ void StereotypeDefinitionParser::parseRelation(CustomRelation::Element element)
}
break;
}
+ case KEYWORD_EMPHASIZED:
+ relation.setEmphasized(parseBoolProperty());
+ break;
case KEYWORD_END:
parseRelationEnd(&relation);
break;
@@ -976,6 +1024,65 @@ StereotypeDefinitionParser::Value StereotypeDefinitionParser::parseProperty()
return parseExpression();
}
+ShapeValueF StereotypeDefinitionParser::parseShapeValueProperty(ShapeValueF::Unit unit, ShapeValueF::Origin origin)
+{
+ expectColon();
+ qreal value = parseFloatExpression();
+ // unit
+ Token token = d->m_scanner->read();
+ if (token.type() == Token::TokenOperator) {
+ switch (token.subtype()) {
+ case OPERATOR_PERCENTAGE:
+ unit = ShapeValueF::UnitPercentage;
+ break;
+ default:
+ d->m_scanner->unread(token);
+ break;
+ }
+ } else if (token.type() == Token::TokenKeyword) {
+ switch (token.subtype()) {
+ case KEYWORD_ABS:
+ unit = ShapeValueF::UnitAbsolute;
+ break;
+ case KEYWORD_FIX:
+ unit = ShapeValueF::UnitRelative;
+ break;
+ case KEYWORD_SCALE:
+ unit = ShapeValueF::UnitScaled;
+ break;
+ case KEYWORD_FRACT:
+ unit = ShapeValueF::UnitPercentage;
+ break;
+ default:
+ d->m_scanner->unread(token);
+ break;
+ }
+ } else {
+ d->m_scanner->unread(token);
+ }
+ // origin
+ token = d->m_scanner->read();
+ if (token.type() == Token::TokenKeyword) {
+ switch (token.subtype()) {
+ case KEYWORD_ORIGIN:
+ origin = ShapeValueF::OriginTopOrLeft;
+ break;
+ case KEYWORD_CENTER:
+ origin = ShapeValueF::OriginCenter;
+ break;
+ case KEYWORD_SIZE:
+ origin = ShapeValueF::OriginBottomOrRight;
+ break;
+ default:
+ d->m_scanner->unread(token);
+ break;
+ }
+ } else {
+ d->m_scanner->unread(token);
+ }
+ return ShapeValueF(value, unit, origin);
+}
+
QString StereotypeDefinitionParser::parseStringExpression()
{
Token token = d->m_scanner->read();
@@ -1050,11 +1157,8 @@ QColor StereotypeDefinitionParser::parseColorExpression()
Token token = d->m_scanner->read();
if (token.type() == Token::TokenIdentifier || token.type() == Token::TokenColor) {
QString value = token.text().toLower();
- QColor color;
- if (QColor::isValidColor(value)) {
- color.setNamedColor(value);
- return color;
- }
+ if (QColor::isValidColorName(value))
+ return QColor::fromString(value);
}
throw StereotypeDefinitionParserError("Expected color name.", token.sourcePos());
}
@@ -1084,9 +1188,8 @@ StereotypeDefinitionParser::Value StereotypeDefinitionParser::parseExpression()
return Value(Float, QVariant(value));
} else if (token.type() == Token::TokenColor) {
QString value = token.text().toLower();
- QColor color;
- if (QColor::isValidColor(value)) {
- color.setNamedColor(value);
+ if (QColor::isValidColorName(value)) {
+ const QColor color = QColor::fromString(value);
return Value(Color, QVariant(color));
} else {
throw StereotypeDefinitionParserError("Invalid color.", token.sourcePos());
diff --git a/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.h b/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.h
index e32126c730..97b14211ca 100644
--- a/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.h
+++ b/src/libs/modelinglib/qmt/config/stereotypedefinitionparser.h
@@ -6,6 +6,7 @@
#include <QObject>
#include "qmt/infrastructure/exceptions.h"
#include "qmt/stereotype/customrelation.h"
+#include "qmt/stereotype/shapevalue.h"
#include "qmt/stereotype/toolbar.h"
#include "sourcepos.h"
@@ -20,7 +21,6 @@ class ITextSource;
class Token;
class StereotypeIcon;
class Toolbar;
-class ShapeValueF;
class QMT_EXPORT StereotypeDefinitionParserError : public Exception
{
@@ -97,6 +97,7 @@ private:
bool parseBoolProperty();
QColor parseColorProperty();
Value parseProperty();
+ ShapeValueF parseShapeValueProperty(ShapeValueF::Unit unit, ShapeValueF::Origin origin);
QString parseStringExpression();
qreal parseFloatExpression();
diff --git a/src/libs/modelinglib/qmt/controller/namecontroller.cpp b/src/libs/modelinglib/qmt/controller/namecontroller.cpp
index d910be4b12..2f9506d3fe 100644
--- a/src/libs/modelinglib/qmt/controller/namecontroller.cpp
+++ b/src/libs/modelinglib/qmt/controller/namecontroller.cpp
@@ -3,9 +3,10 @@
#include "namecontroller.h"
-#include <QFileInfo>
#include <QDebug>
+using Utils::FilePath;
+
namespace qmt {
NameController::NameController(QObject *parent)
@@ -17,10 +18,9 @@ NameController::~NameController()
{
}
-QString NameController::convertFileNameToElementName(const QString &fileName)
+QString NameController::convertFileNameToElementName(const FilePath &fileName)
{
- QFileInfo fileInfo(fileName);
- QString baseName = fileInfo.baseName().trimmed();
+ QString baseName = fileName.baseName().trimmed();
QString elementName;
bool makeTitlecase = true;
bool insertSpace = false;
@@ -65,13 +65,19 @@ QString NameController::convertElementNameToBaseFileName(const QString &elementN
return baseFileName;
}
-QString NameController::calcRelativePath(const QString &absoluteFileName, const QString &anchorPath)
+FilePath NameController::calcRelativePath(const FilePath &absoluteFileName,
+ const FilePath &anchorPath)
{
+ // TODO try using Utils::FilePath::relativePath
+ QString absoluteFilePath = absoluteFileName.path();
+ QString anchorPathString = anchorPath.path();
int secondLastSlashIndex = -1;
int slashIndex = -1;
int i = 0;
- while (i < absoluteFileName.size() && i < anchorPath.size() && absoluteFileName.at(i) == anchorPath.at(i)) {
- if (absoluteFileName.at(i) == QLatin1Char('/')) {
+ while (i < absoluteFilePath.size() && i < anchorPathString.size()
+ && absoluteFilePath.at(i) == anchorPathString.at(i))
+ {
+ if (absoluteFilePath.at(i) == QLatin1Char('/')) {
secondLastSlashIndex = slashIndex;
slashIndex = i;
}
@@ -81,22 +87,22 @@ QString NameController::calcRelativePath(const QString &absoluteFileName, const
QString relativePath;
if (slashIndex < 0) {
- relativePath = absoluteFileName;
- } else if (i >= absoluteFileName.size()) {
+ relativePath = absoluteFilePath;
+ } else if (i >= absoluteFilePath.size()) {
// absoluteFileName is a substring of anchor path.
if (slashIndex == i - 1) {
if (secondLastSlashIndex < 0)
- relativePath = absoluteFileName;
+ relativePath = absoluteFilePath;
else
- relativePath = absoluteFileName.mid(secondLastSlashIndex + 1);
+ relativePath = absoluteFilePath.mid(secondLastSlashIndex + 1);
} else {
- relativePath = absoluteFileName.mid(slashIndex + 1);
+ relativePath = absoluteFilePath.mid(slashIndex + 1);
}
} else {
- relativePath = absoluteFileName.mid(slashIndex + 1);
+ relativePath = absoluteFilePath.mid(slashIndex + 1);
}
- return relativePath;
+ return FilePath::fromString(relativePath);
}
QString NameController::calcElementNameSearchId(const QString &elementName)
@@ -109,16 +115,17 @@ QString NameController::calcElementNameSearchId(const QString &elementName)
return searchId;
}
-QStringList NameController::buildElementsPath(const QString &filePath, bool ignoreLastFilePathPart)
+QStringList NameController::buildElementsPath(const FilePath &filePath,
+ bool ignoreLastFilePathPart)
{
QList<QString> relativeElements;
- QStringList split = filePath.split("/");
+ QStringList split = filePath.toString().split("/");
QStringList::const_iterator splitEnd = split.constEnd();
if (ignoreLastFilePathPart || split.last().isEmpty())
--splitEnd;
for (auto it = split.constBegin(); it != splitEnd; ++it) {
- QString packageName = qmt::NameController::convertFileNameToElementName(*it);
+ QString packageName = qmt::NameController::convertFileNameToElementName(FilePath::fromString(*it));
relativeElements.append(packageName);
}
return relativeElements;
diff --git a/src/libs/modelinglib/qmt/controller/namecontroller.h b/src/libs/modelinglib/qmt/controller/namecontroller.h
index 03e3be30bb..bb1ff0c3e1 100644
--- a/src/libs/modelinglib/qmt/controller/namecontroller.h
+++ b/src/libs/modelinglib/qmt/controller/namecontroller.h
@@ -6,6 +6,8 @@
#include <QObject>
#include "qmt/infrastructure/qmt_global.h"
+#include <utils/filepath.h>
+
#include <QString>
#include <QStringList>
@@ -18,12 +20,12 @@ private:
~NameController() override;
public:
- static QString convertFileNameToElementName(const QString &fileName);
+ static QString convertFileNameToElementName(const Utils::FilePath &fileName);
static QString convertElementNameToBaseFileName(const QString &elementName);
- // TODO use Utils::FilePath instead
- static QString calcRelativePath(const QString &absoluteFileName, const QString &anchorPath);
+ static Utils::FilePath calcRelativePath(const Utils::FilePath &absoluteFileName,
+ const Utils::FilePath &anchorPath);
static QString calcElementNameSearchId(const QString &elementName);
- static QStringList buildElementsPath(const QString &filePath, bool ignoreLastFilePathPart);
+ static QStringList buildElementsPath(const Utils::FilePath &filePath, bool ignoreLastFilePathPart);
static bool parseClassName(const QString &fullClassName, QString *umlNamespace,
QString *className, QStringList *templateParameters);
};
diff --git a/src/libs/modelinglib/qmt/diagram/dobject.cpp b/src/libs/modelinglib/qmt/diagram/dobject.cpp
index 4e0e5cb642..543e6af30f 100644
--- a/src/libs/modelinglib/qmt/diagram/dobject.cpp
+++ b/src/libs/modelinglib/qmt/diagram/dobject.cpp
@@ -6,11 +6,11 @@
#include "dvisitor.h"
#include "dconstvisitor.h"
+using Utils::FilePath;
+
namespace qmt {
-DObject::DObject()
-{
-}
+DObject::DObject() {}
DObject::DObject(const DObject &rhs)
: DElement(rhs),
@@ -25,7 +25,10 @@ DObject::DObject(const DObject &rhs)
m_visualSecondaryRole(rhs.m_visualSecondaryRole),
m_stereotypeDisplay(rhs.m_stereotypeDisplay),
m_isAutoSized(rhs.m_isAutoSized),
- m_isVisualEmphasized(rhs.m_isVisualEmphasized)
+ m_isVisualEmphasized(rhs.m_isVisualEmphasized),
+ m_hasLinkedFile(rhs.m_hasLinkedFile),
+ m_imagePath(rhs.m_imagePath),
+ m_image(rhs.m_image)
{
}
@@ -49,6 +52,9 @@ DObject &DObject::operator =(const DObject &rhs)
m_stereotypeDisplay = rhs.m_stereotypeDisplay;
m_isAutoSized = rhs.m_isAutoSized;
m_isVisualEmphasized = rhs.m_isVisualEmphasized;
+ m_hasLinkedFile = rhs.m_hasLinkedFile;
+ m_imagePath = rhs.m_imagePath;
+ m_image = rhs.m_image;
}
return *this;
}
@@ -113,6 +119,26 @@ void DObject::setVisualEmphasized(bool visualEmphasized)
m_isVisualEmphasized = visualEmphasized;
}
+void DObject::setLinkedFile(bool linkedFile)
+{
+ m_hasLinkedFile = linkedFile;
+}
+
+bool DObject::hasImage() const
+{
+ return !m_image.isNull();
+}
+
+void DObject::setImagePath(const FilePath &path)
+{
+ m_imagePath = path;
+}
+
+void DObject::setImage(const QImage &image)
+{
+ m_image = image;
+}
+
void DObject::accept(DVisitor *visitor)
{
visitor->visitDObject(this);
diff --git a/src/libs/modelinglib/qmt/diagram/dobject.h b/src/libs/modelinglib/qmt/diagram/dobject.h
index c8cf2b0e4d..7364f15749 100644
--- a/src/libs/modelinglib/qmt/diagram/dobject.h
+++ b/src/libs/modelinglib/qmt/diagram/dobject.h
@@ -7,6 +7,9 @@
#include "qmt/infrastructure/uid.h"
+#include <utils/filepath.h>
+
+#include <QImage>
#include <QList>
#include <QPointF>
#include <QRectF>
@@ -78,6 +81,13 @@ public:
void setAutoSized(bool autoSized);
bool isVisualEmphasized() const { return m_isVisualEmphasized; }
void setVisualEmphasized(bool visualEmphasized);
+ bool hasLinkedFile() const { return m_hasLinkedFile; }
+ void setLinkedFile(bool linkedFile);
+ Utils::FilePath imagePath() const { return m_imagePath; }
+ void setImagePath(const Utils::FilePath &path);
+ bool hasImage() const;
+ QImage image() const { return m_image; }
+ void setImage(const QImage &image);
void accept(DVisitor *visitor) override;
void accept(DConstVisitor *visitor) const override;
@@ -95,6 +105,9 @@ private:
StereotypeDisplay m_stereotypeDisplay = StereotypeSmart;
bool m_isAutoSized = true;
bool m_isVisualEmphasized = false;
+ bool m_hasLinkedFile = false;
+ Utils::FilePath m_imagePath;
+ QImage m_image;
};
} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/diagram/drelation.cpp b/src/libs/modelinglib/qmt/diagram/drelation.cpp
index a21f61c707..890bd275e4 100644
--- a/src/libs/modelinglib/qmt/diagram/drelation.cpp
+++ b/src/libs/modelinglib/qmt/diagram/drelation.cpp
@@ -65,6 +65,31 @@ void DRelation::setIntermediatePoints(const QList<DRelation::IntermediatePoint>
m_intermediatePoints = intermediatePoints;
}
+void DRelation::setVisualPrimaryRole(VisualPrimaryRole visualPrimaryRole)
+{
+ m_visualPrimaryRole = visualPrimaryRole;
+}
+
+void DRelation::setVisualSecondaryRole(VisualSecondaryRole visualSecondaryRole)
+{
+ m_visualSecondaryRole = visualSecondaryRole;
+}
+
+void DRelation::setVisualEmphasized(bool visualEmphasized)
+{
+ m_isVisualEmphasized = visualEmphasized;
+}
+
+void DRelation::setColor(const QColor &color)
+{
+ m_color = color;
+}
+
+void DRelation::setThickness(qreal thickness)
+{
+ m_thickness = thickness;
+}
+
void DRelation::accept(DVisitor *visitor)
{
visitor->visitDRelation(this);
diff --git a/src/libs/modelinglib/qmt/diagram/drelation.h b/src/libs/modelinglib/qmt/diagram/drelation.h
index 34bdaf1986..ed5624ad9f 100644
--- a/src/libs/modelinglib/qmt/diagram/drelation.h
+++ b/src/libs/modelinglib/qmt/diagram/drelation.h
@@ -5,6 +5,7 @@
#include "delement.h"
+#include <QColor>
#include <QList>
#include <QPointF>
@@ -15,6 +16,22 @@ class DObject;
class QMT_EXPORT DRelation : public DElement
{
public:
+ enum VisualPrimaryRole {
+ PrimaryRoleNormal,
+ PrimaryRoleCustom1,
+ PrimaryRoleCustom2,
+ PrimaryRoleCustom3,
+ PrimaryRoleCustom4,
+ PrimaryRoleCustom5
+ };
+
+ enum VisualSecondaryRole {
+ SecondaryRoleNone,
+ SecondaryRoleWarning,
+ SecondaryRoleError,
+ SecondaryRoleSoften
+ };
+
class IntermediatePoint
{
public:
@@ -42,6 +59,16 @@ public:
void setName(const QString &name);
const QList<IntermediatePoint> intermediatePoints() const { return m_intermediatePoints; }
void setIntermediatePoints(const QList<IntermediatePoint> &intermediatePoints);
+ VisualPrimaryRole visualPrimaryRole() const { return m_visualPrimaryRole; }
+ void setVisualPrimaryRole(VisualPrimaryRole visualPrimaryRole);
+ VisualSecondaryRole visualSecondaryRole() const { return m_visualSecondaryRole; }
+ void setVisualSecondaryRole(VisualSecondaryRole visualSecondaryRole);
+ bool isVisualEmphasized() const { return m_isVisualEmphasized; }
+ void setVisualEmphasized(bool visualEmphasized);
+ QColor color() const { return m_color; }
+ void setColor(const QColor &color);
+ qreal thickness() const { return m_thickness; }
+ void setThickness(qreal thickness);
void accept(DVisitor *visitor) override;
void accept(DConstVisitor *visitor) const override;
@@ -53,6 +80,11 @@ private:
Uid m_endBUid;
QString m_name;
QList<IntermediatePoint> m_intermediatePoints;
+ VisualPrimaryRole m_visualPrimaryRole = PrimaryRoleNormal;
+ VisualSecondaryRole m_visualSecondaryRole = SecondaryRoleNone;
+ bool m_isVisualEmphasized = false;
+ QColor m_color;
+ qreal m_thickness = 0;
};
bool operator==(const DRelation::IntermediatePoint &lhs, const DRelation::IntermediatePoint &rhs);
diff --git a/src/libs/modelinglib/qmt/diagram_controller/dflatassignmentvisitor.cpp b/src/libs/modelinglib/qmt/diagram_controller/dflatassignmentvisitor.cpp
index 5e88175812..5e4c072588 100644
--- a/src/libs/modelinglib/qmt/diagram_controller/dflatassignmentvisitor.cpp
+++ b/src/libs/modelinglib/qmt/diagram_controller/dflatassignmentvisitor.cpp
@@ -49,7 +49,10 @@ void DFlatAssignmentVisitor::visitDObject(const DObject *object)
target->setVisualPrimaryRole(object->visualPrimaryRole());
target->setVisualSecondaryRole(object->visualSecondaryRole());
target->setVisualEmphasized(object->isVisualEmphasized());
+ target->setLinkedFile(object->hasLinkedFile());
target->setStereotypeDisplay(object->stereotypeDisplay());
+ target->setImagePath(object->imagePath());
+ target->setImage(object->image());
}
void DFlatAssignmentVisitor::visitDPackage(const DPackage *package)
@@ -100,6 +103,8 @@ void DFlatAssignmentVisitor::visitDRelation(const DRelation *relation)
QMT_ASSERT(target, return);
target->setStereotypes(relation->stereotypes());
target->setIntermediatePoints(relation->intermediatePoints());
+ target->setVisualPrimaryRole(relation->visualPrimaryRole());
+ target->setThickness(relation->thickness());
}
void DFlatAssignmentVisitor::visitDInheritance(const DInheritance *inheritance)
diff --git a/src/libs/modelinglib/qmt/diagram_controller/dupdatevisitor.cpp b/src/libs/modelinglib/qmt/diagram_controller/dupdatevisitor.cpp
index 91a95438ad..2dd079e1bc 100644
--- a/src/libs/modelinglib/qmt/diagram_controller/dupdatevisitor.cpp
+++ b/src/libs/modelinglib/qmt/diagram_controller/dupdatevisitor.cpp
@@ -65,13 +65,16 @@ void DUpdateVisitor::visitMObject(const MObject *object)
}
if (isUpdating(object->name() != dobject->name()))
dobject->setName(object->name());
+ bool hasLinkedFile = !object->linkedFileName().isEmpty();
+ if (isUpdating(hasLinkedFile != dobject->hasLinkedFile()))
+ dobject->setLinkedFile(hasLinkedFile);
// TODO unlikely that this is called for all objects if hierarchy is modified
// PERFORM remove loop
int depth = 1;
const MObject *owner = object->owner();
while (owner) {
owner = owner->owner();
- depth += 1;
+ depth += 3;
}
if (isUpdating(depth != dobject->depth()))
dobject->setDepth(depth);
diff --git a/src/libs/modelinglib/qmt/diagram_scene/capabilities/windable.h b/src/libs/modelinglib/qmt/diagram_scene/capabilities/windable.h
index a21f1caea3..c31249e5c1 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/capabilities/windable.h
+++ b/src/libs/modelinglib/qmt/diagram_scene/capabilities/windable.h
@@ -20,7 +20,7 @@ public:
virtual void insertHandle(int beforeIndex, const QPointF &pos, double rasterWidth, double rasterHeight) = 0;
virtual void deleteHandle(int index) = 0;
virtual void setHandlePos(int index, const QPointF &pos) = 0;
- virtual void dropHandle(int index, double rasterWidth, double rasterHeight) = 0;
+ virtual void dropHandle(int index, bool extraSnap, double rasterWidth, double rasterHeight) = 0;
};
} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/diagram_scene/diagramscenemodel.cpp b/src/libs/modelinglib/qmt/diagram_scene/diagramscenemodel.cpp
index 13167f691d..9366a39503 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/diagramscenemodel.cpp
+++ b/src/libs/modelinglib/qmt/diagram_scene/diagramscenemodel.cpp
@@ -209,15 +209,26 @@ DSelection DiagramSceneModel::selectedElements() const
return selection;
}
-DElement *DiagramSceneModel::findTopmostElement(const QPointF &scenePos) const
+QGraphicsItem *DiagramSceneModel::findTopmostItem(const QPointF &scenePos) const
{
// fetch affected items from scene in correct drawing order to find topmost element
const QList<QGraphicsItem *> items = m_graphicsScene->items(scenePos);
+ QGraphicsItem *candidate = NULL;
for (QGraphicsItem *item : items) {
- if (m_graphicsItems.contains(item))
- return m_itemToElementMap.value(item);
+ if (m_graphicsItems.contains(item)) {
+ if (!candidate || item->zValue() > candidate->zValue())
+ candidate = item;
+ }
}
- return nullptr;
+ return candidate;
+}
+
+DElement *DiagramSceneModel::findTopmostElement(const QPointF &scenePos) const
+{
+ QGraphicsItem *item = findTopmostItem(scenePos);
+ if (!item)
+ return nullptr;
+ return m_itemToElementMap.value(item);
}
DObject *DiagramSceneModel::findTopmostObject(const QPointF &scenePos) const
@@ -230,16 +241,13 @@ DObject *DiagramSceneModel::findTopmostObject(const QPointF &scenePos) const
ObjectItem *DiagramSceneModel::findTopmostObjectItem(const QPointF &scenePos) const
{
- // fetch affected items from scene in correct drawing order to find topmost element
- const QList<QGraphicsItem *> items = m_graphicsScene->items(scenePos);
- for (QGraphicsItem *item : std::as_const(items)) {
- if (m_graphicsItems.contains(item)) {
- DObject *object = dynamic_cast<DObject *>(m_itemToElementMap.value(item));
- if (object)
- return dynamic_cast<ObjectItem *>(item);
- }
- }
- return nullptr;
+ QGraphicsItem *item = findTopmostItem(scenePos);
+ if (!item)
+ return nullptr;
+ DObject *object = dynamic_cast<DObject *>(m_itemToElementMap.value(item));
+ if (!object)
+ return nullptr;
+ return dynamic_cast<ObjectItem *>(item);
}
QGraphicsItem *DiagramSceneModel::graphicsItem(DElement *element) const
@@ -353,9 +361,8 @@ void DiagramSceneModel::selectAllElements()
void DiagramSceneModel::selectElement(DElement *element)
{
QGraphicsItem *selectItem = m_elementToItemMap.value(element);
- // We have to create a copy since "setSelected" may modify m_selectedItems
- const QSet<QGraphicsItem *> copy = m_selectedItems;
- for (QGraphicsItem *item : copy) {
+ auto selectedItems = m_selectedItems;
+ for (QGraphicsItem *item : std::as_const(selectedItems)) {
if (item != selectItem)
item->setSelected(false);
}
@@ -365,9 +372,17 @@ void DiagramSceneModel::selectElement(DElement *element)
void DiagramSceneModel::editElement(DElement *element)
{
- auto editable = dynamic_cast<IEditable *>(m_elementToItemMap.value(element));
- if (editable && editable->isEditable())
+ QGraphicsItem *item = m_elementToItemMap.value(element);
+ auto editable = dynamic_cast<IEditable *>(item);
+ if (editable && editable->isEditable()) {
+ auto selectable = dynamic_cast<ISelectable *>(item);
+ if (selectable && item != m_focusItem) {
+ unsetFocusItem();
+ selectable->setFocusSelected(true);
+ m_focusItem = item;
+ }
editable->edit();
+ }
}
void DiagramSceneModel::copyToClipboard()
@@ -509,9 +524,8 @@ void DiagramSceneModel::selectItem(QGraphicsItem *item, bool multiSelect)
{
if (!multiSelect) {
if (!item->isSelected()) {
- // We have to create a copy since "setSelected" may modify m_selectedItems
- const QSet<QGraphicsItem *> copy = m_selectedItems;
- for (QGraphicsItem *selectedItem : copy) {
+ auto selectedItems = m_selectedItems;
+ for (QGraphicsItem *selectedItem : std::as_const(selectedItems)) {
if (selectedItem != item)
selectedItem->setSelected(false);
}
@@ -626,6 +640,7 @@ void DiagramSceneModel::keyReleaseEvent(QKeyEvent *event)
void DiagramSceneModel::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
+ m_mousePressed = true;
updateFocusItem(Utils::toSet(m_graphicsScene->selectedItems()));
m_latchController->mousePressEventLatching(event);
mousePressEventReparenting(event);
@@ -660,8 +675,10 @@ void DiagramSceneModel::mouseMoveEventReparenting(QGraphicsSceneMouseEvent *even
void DiagramSceneModel::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
+ m_mousePressed = false;
m_latchController->mouseReleaseEventLatching(event);
mouseReleaseEventReparenting(event);
+ recalcSceneRectSize();
}
void DiagramSceneModel::mouseReleaseEventReparenting(QGraphicsSceneMouseEvent *event)
@@ -853,7 +870,7 @@ void DiagramSceneModel::onSelectionChanged()
QSet<QGraphicsItem *> newSecondarySelectedItems;
// select all contained objects secondarily
- for (QGraphicsItem *selectedItem : std::as_const(m_secondarySelectedItems)) {
+ for (QGraphicsItem *selectedItem : std::as_const(m_selectedItems)) {
const QList<QGraphicsItem *> items = collectCollidingObjectItems(selectedItem, CollidingInnerItems);
for (QGraphicsItem *item : items) {
if (!item->isSelected() && dynamic_cast<ISelectable *>(item)
@@ -910,7 +927,8 @@ void DiagramSceneModel::onSelectionChanged()
}
}
- for (QGraphicsItem *item : std::as_const(m_secondarySelectedItems)) {
+ auto secondarySelectedItems = m_secondarySelectedItems;
+ for (QGraphicsItem *item : std::as_const(secondarySelectedItems)) {
if (!newSecondarySelectedItems.contains(item)) {
auto selectable = dynamic_cast<ISelectable *>(item);
QMT_ASSERT(selectable, return);
@@ -1041,8 +1059,19 @@ void DiagramSceneModel::recalcSceneRectSize()
if (!dynamic_cast<SwimlaneItem *>(item))
sceneRect |= item->mapRectToScene(item->boundingRect());
}
- m_sceneRect = sceneRect;
- emit sceneRectChanged(sceneRect);
+ if (m_mousePressed) {
+ if (sceneRect.left() < m_sceneRect.left())
+ m_sceneRect.setLeft(sceneRect.left());
+ if (sceneRect.right() > m_sceneRect.right())
+ m_sceneRect.setRight(sceneRect.right());
+ if (sceneRect.top() < m_sceneRect.top())
+ m_sceneRect.setTop(sceneRect.top());
+ if (sceneRect.bottom() > m_sceneRect.bottom())
+ m_sceneRect.setBottom(sceneRect.bottom());
+ } else {
+ m_sceneRect = sceneRect;
+ }
+ emit sceneRectChanged(m_sceneRect);
}
QGraphicsItem *DiagramSceneModel::createGraphicsItem(DElement *element)
diff --git a/src/libs/modelinglib/qmt/diagram_scene/diagramscenemodel.h b/src/libs/modelinglib/qmt/diagram_scene/diagramscenemodel.h
index 0021faf459..b976f421f9 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/diagramscenemodel.h
+++ b/src/libs/modelinglib/qmt/diagram_scene/diagramscenemodel.h
@@ -79,6 +79,7 @@ public:
bool hasSelection() const;
bool hasMultiObjectsSelection() const;
DSelection selectedElements() const;
+ QGraphicsItem *findTopmostItem(const QPointF &scenePos) const;
DElement *findTopmostElement(const QPointF &scenePos) const;
DObject *findTopmostObject(const QPointF &scenePos) const;
ObjectItem *findTopmostObjectItem(const QPointF &scenePos) const;
@@ -169,6 +170,7 @@ private:
QGraphicsItem *m_focusItem = nullptr;
QRectF m_sceneRect;
QList<Uid> m_relationEndsUid;
+ bool m_mousePressed = false;
};
} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/classitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/classitem.cpp
index 14ad25b2c1..23a9c524a2 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/items/classitem.cpp
+++ b/src/libs/modelinglib/qmt/diagram_scene/items/classitem.cpp
@@ -85,29 +85,16 @@ void ClassItem::update()
m_methodsText.clear();
}
- // custom icon
- if (stereotypeIconDisplay() == StereotypeIcon::DisplayIcon) {
- if (!m_customIcon)
- m_customIcon = new CustomIconItem(diagramSceneModel(), this);
- m_customIcon->setStereotypeIconId(stereotypeIconId());
- m_customIcon->setBaseSize(stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT));
- m_customIcon->setBrush(style->fillBrush());
- m_customIcon->setPen(style->outerLinePen());
- m_customIcon->setZValue(SHAPE_ZVALUE);
- } else if (m_customIcon) {
- m_customIcon->scene()->removeItem(m_customIcon);
- delete m_customIcon;
- m_customIcon = nullptr;
- }
+ updateCustomIcon(style);
// shape
- if (!m_customIcon) {
+ if (!customIconItem()) {
if (!m_shape)
m_shape = new QGraphicsRectItem(this);
m_shape->setBrush(style->fillBrush());
m_shape->setPen(style->outerLinePen());
m_shape->setZValue(SHAPE_ZVALUE);
- } else if (m_shape){
+ } else if (m_shape) {
m_shape->scene()->removeItem(m_shape);
delete m_shape;
m_shape = nullptr;
@@ -259,7 +246,7 @@ void ClassItem::update()
m_templateParameterBox = nullptr;
}
- updateSelectionMarker(m_customIcon);
+ updateSelectionMarker(customIconItem());
updateRelationStarter();
updateAlignmentButtons();
updateGeometry();
@@ -267,8 +254,8 @@ void ClassItem::update()
bool ClassItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const
{
- if (m_customIcon) {
- QList<QPolygonF> polygons = m_customIcon->outline();
+ if (customIconItem()) {
+ QList<QPolygonF> polygons = customIconItem()->outline();
for (int i = 0; i < polygons.size(); ++i)
polygons[i].translate(object()->pos() + object()->rect().topLeft());
if (shapeIcon().textAlignment() == qmt::StereotypeIcon::TextalignBelow) {
@@ -508,7 +495,7 @@ DClass::TemplateDisplay ClassItem::templateDisplay() const
DClass::TemplateDisplay templateDisplay = diagramClass->templateDisplay();
if (templateDisplay == DClass::TemplateSmart) {
- if (m_customIcon)
+ if (customIconItem())
templateDisplay = DClass::TemplateName;
else
templateDisplay = DClass::TemplateBox;
@@ -521,8 +508,8 @@ QSizeF ClassItem::calcMinimumGeometry() const
double width = 0.0;
double height = 0.0;
- if (m_customIcon) {
- QSizeF sz = stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(),
+ if (customIconItem()) {
+ QSizeF sz = customIconItemMinimumSize(customIconItem(),
CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT);
if (shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignTop
&& shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignCenter)
@@ -589,12 +576,7 @@ void ClassItem::updateGeometry()
height = geometry.height();
if (object()->isAutoSized()) {
- if (!m_customIcon) {
- if (width < MINIMUM_AUTO_WIDTH)
- width = MINIMUM_AUTO_WIDTH;
- if (height < MINIMUM_AUTO_HEIGHT)
- height = MINIMUM_AUTO_HEIGHT;
- }
+ correctAutoSize(customIconItem(), width, height, MINIMUM_AUTO_WIDTH, MINIMUM_AUTO_HEIGHT);
} else {
QRectF rect = object()->rect();
if (rect.width() > width)
@@ -619,15 +601,15 @@ void ClassItem::updateGeometry()
// a backup for the graphics item used for manual resized and persistency.
object()->setRect(rect);
- if (m_customIcon) {
- m_customIcon->setPos(left, top);
- m_customIcon->setActualSize(QSizeF(width, height));
+ if (customIconItem()) {
+ customIconItem()->setPos(left, top);
+ customIconItem()->setActualSize(QSizeF(width, height));
}
if (m_shape)
m_shape->setRect(rect);
- if (m_customIcon) {
+ if (customIconItem()) {
switch (shapeIcon().textAlignment()) {
case qmt::StereotypeIcon::TextalignBelow:
y += height + BODY_VERT_BORDER;
@@ -679,7 +661,7 @@ void ClassItem::updateGeometry()
y += nameItem()->boundingRect().height();
}
if (m_contextLabel) {
- if (m_customIcon)
+ if (customIconItem())
m_contextLabel->resetMaxWidth();
else
m_contextLabel->setMaxWidth(width - 2 * BODY_HORIZ_BORDER);
@@ -692,7 +674,7 @@ void ClassItem::updateGeometry()
y += 8.0;
}
if (m_attributes) {
- if (m_customIcon)
+ if (customIconItem())
m_attributes->setPos(-m_attributes->boundingRect().width() / 2.0, y);
else
m_attributes->setPos(left + BODY_HORIZ_BORDER, y);
@@ -704,7 +686,7 @@ void ClassItem::updateGeometry()
y += 8.0;
}
if (m_methods) {
- if (m_customIcon)
+ if (customIconItem())
m_methods->setPos(-m_methods->boundingRect().width() / 2.0, y);
else
m_methods->setPos(left + BODY_HORIZ_BORDER, y);
@@ -765,21 +747,22 @@ void ClassItem::updateMembers(const Style *style)
break;
}
+ bool needBr = false;
if (text && !text->isEmpty())
- *text += "<br/>";
+ needBr = true;
- bool addNewline = false;
- bool addSpace = false;
if (currentVisibility)
*currentVisibility = member.visibility();
if (currentGroup && member.group() != *currentGroup) {
- *text += QString("[%1]").arg(member.group());
- addNewline = true;
+ needBr = false;
+ *text += QString("<p style=\"padding:0;margin-top:6;margin-bottom:0;\"><b>[%1]</b></p>").arg(member.group());
*currentGroup = member.group();
}
- if (addNewline)
+
+ if (needBr)
*text += "<br/>";
+ bool addSpace = false;
bool haveSignal = false;
bool haveSlot = false;
if (member.visibility() != MClassMember::VisibilityUndefined) {
diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/classitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/classitem.h
index 83ce0a9a5a..cc92ab1ff5 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/items/classitem.h
+++ b/src/libs/modelinglib/qmt/diagram_scene/items/classitem.h
@@ -53,7 +53,6 @@ private:
void updateGeometry();
void updateMembers(const Style *style);
- CustomIconItem *m_customIcon = nullptr;
QGraphicsRectItem *m_shape = nullptr;
QGraphicsSimpleTextItem *m_baseClasses = nullptr;
QGraphicsSimpleTextItem *m_namespace = nullptr;
diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.cpp
index 6a54a001e5..e7fffcbf9a 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.cpp
+++ b/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.cpp
@@ -53,24 +53,11 @@ void ComponentItem::update()
const Style *style = adaptedStyle(stereotypeIconId());
- // custom icon
- if (stereotypeIconDisplay() == StereotypeIcon::DisplayIcon) {
- if (!m_customIcon)
- m_customIcon = new CustomIconItem(diagramSceneModel(), this);
- m_customIcon->setStereotypeIconId(stereotypeIconId());
- m_customIcon->setBaseSize(stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT));
- m_customIcon->setBrush(style->fillBrush());
- m_customIcon->setPen(style->outerLinePen());
- m_customIcon->setZValue(SHAPE_ZVALUE);
- } else if (m_customIcon) {
- m_customIcon->scene()->removeItem(m_customIcon);
- delete m_customIcon;
- m_customIcon = nullptr;
- }
+ updateCustomIcon(style);
// shape
bool deleteRects = false;
- if (!m_customIcon) {
+ if (!customIconItem()) {
if (!m_shape)
m_shape = new QGraphicsRectItem(this);
m_shape->setBrush(style->fillBrush());
@@ -130,7 +117,7 @@ void ComponentItem::update()
m_contextLabel = nullptr;
}
- updateSelectionMarker(m_customIcon);
+ updateSelectionMarker(customIconItem());
updateRelationStarter();
updateAlignmentButtons();
updateGeometry();
@@ -138,8 +125,8 @@ void ComponentItem::update()
bool ComponentItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const
{
- if (m_customIcon) {
- QList<QPolygonF> polygons = m_customIcon->outline();
+ if (customIconItem()) {
+ QList<QPolygonF> polygons = customIconItem()->outline();
for (int i = 0; i < polygons.size(); ++i)
polygons[i].translate(object()->pos() + object()->rect().topLeft());
if (shapeIcon().textAlignment() == qmt::StereotypeIcon::TextalignBelow) {
@@ -207,14 +194,16 @@ QSizeF ComponentItem::calcMinimumGeometry() const
{
double width = 0.0;
double height = 0.0;
+ double customMinHeight = 0.0;
- if (m_customIcon) {
- QSizeF sz = stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(),
+ if (customIconItem()) {
+ QSizeF sz = customIconItemMinimumSize(customIconItem(),
CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT);
if (shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignTop
&& shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignCenter)
return sz;
width = sz.width();
+ customMinHeight = sz.height();
}
height += BODY_VERT_BORDER;
@@ -234,7 +223,7 @@ QSizeF ComponentItem::calcMinimumGeometry() const
height += m_contextLabel->height();
height += BODY_VERT_BORDER;
- if (!hasPlainShape()) {
+ if (!customIconItem() && !hasPlainShape()) {
width = RECT_WIDTH * 0.5 + BODY_HORIZ_BORDER + width + BODY_HORIZ_BORDER + RECT_WIDTH * 0.5;
double minHeight = UPPER_RECT_Y + RECT_HEIGHT + RECT_Y_DISTANCE + RECT_HEIGHT + LOWER_RECT_MIN_Y;
if (height < minHeight)
@@ -243,6 +232,9 @@ QSizeF ComponentItem::calcMinimumGeometry() const
width = BODY_HORIZ_BORDER + width + BODY_HORIZ_BORDER;
}
+ if (height < customMinHeight)
+ height = customMinHeight;
+
return GeometryUtilities::ensureMinimumRasterSize(QSizeF(width, height), 2 * RASTER_WIDTH, 2 * RASTER_HEIGHT);
}
@@ -259,7 +251,7 @@ void ComponentItem::updateGeometry()
height = geometry.height();
if (object()->isAutoSized()) {
- // nothing
+ correctAutoSize(customIconItem(), width, height, 0, 0);
} else {
QRectF rect = object()->rect();
if (rect.width() > width)
@@ -283,9 +275,9 @@ void ComponentItem::updateGeometry()
// a backup for the graphics item used for manual resized and persistency.
object()->setRect(rect);
- if (m_customIcon) {
- m_customIcon->setPos(left, top);
- m_customIcon->setActualSize(QSizeF(width, height));
+ if (customIconItem()) {
+ customIconItem()->setPos(left, top);
+ customIconItem()->setActualSize(QSizeF(width, height));
}
if (m_shape)
@@ -303,7 +295,7 @@ void ComponentItem::updateGeometry()
m_lowerRect->setPos(left - RECT_WIDTH * 0.5, top + UPPER_RECT_Y + RECT_HEIGHT + RECT_Y_DISTANCE);
}
- if (m_customIcon) {
+ if (customIconItem()) {
switch (shapeIcon().textAlignment()) {
case qmt::StereotypeIcon::TextalignBelow:
y += height + BODY_VERT_BORDER;
@@ -345,7 +337,7 @@ void ComponentItem::updateGeometry()
y += nameItem()->boundingRect().height();
}
if (m_contextLabel) {
- if (m_customIcon) {
+ if (customIconItem()) {
m_contextLabel->resetMaxWidth();
} else {
double maxContextWidth = width - 2 * BODY_HORIZ_BORDER - (hasPlainShape() ? 0 : RECT_WIDTH);
diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.h
index f00fbf30eb..f4910f53ce 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.h
+++ b/src/libs/modelinglib/qmt/diagram_scene/items/componentitem.h
@@ -41,7 +41,6 @@ private:
QSizeF calcMinimumGeometry() const;
void updateGeometry();
- CustomIconItem *m_customIcon = nullptr;
QGraphicsRectItem *m_shape = nullptr;
QGraphicsRectItem *m_upperRect = nullptr;
QGraphicsRectItem *m_lowerRect = nullptr;
diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.cpp
index c37c38b3bb..951d6f3b1a 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.cpp
+++ b/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.cpp
@@ -47,23 +47,10 @@ void DiagramItem::update()
const Style *style = adaptedStyle(stereotypeIconId());
- // custom icon
- if (stereotypeIconDisplay() == StereotypeIcon::DisplayIcon) {
- if (!m_customIcon)
- m_customIcon = new CustomIconItem(diagramSceneModel(), this);
- m_customIcon->setStereotypeIconId(stereotypeIconId());
- m_customIcon->setBaseSize(stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT));
- m_customIcon->setBrush(style->fillBrush());
- m_customIcon->setPen(style->outerLinePen());
- m_customIcon->setZValue(SHAPE_ZVALUE);
- } else if (m_customIcon) {
- m_customIcon->scene()->removeItem(m_customIcon);
- delete m_customIcon;
- m_customIcon = nullptr;
- }
+ updateCustomIcon(style);
// shape
- if (!m_customIcon) {
+ if (!customIconItem()) {
if (!m_body)
m_body = new QGraphicsPolygonItem(this);
m_body->setBrush(style->fillBrush());
@@ -93,15 +80,15 @@ void DiagramItem::update()
// diagram name
updateNameItem(style);
- updateSelectionMarker(m_customIcon);
+ updateSelectionMarker(customIconItem());
updateAlignmentButtons();
updateGeometry();
}
bool DiagramItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const
{
- if (m_customIcon) {
- QList<QPolygonF> polygons = m_customIcon->outline();
+ if (customIconItem()) {
+ QList<QPolygonF> polygons = customIconItem()->outline();
for (int i = 0; i < polygons.size(); ++i)
polygons[i].translate(object()->pos() + object()->rect().topLeft());
if (shapeIcon().textAlignment() == qmt::StereotypeIcon::TextalignBelow) {
@@ -131,8 +118,8 @@ QSizeF DiagramItem::calcMinimumGeometry() const
double width = MINIMUM_WIDTH;
double height = 0.0;
- if (m_customIcon) {
- QSizeF sz = stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(),
+ if (customIconItem()) {
+ QSizeF sz = customIconItemMinimumSize(customIconItem(),
CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT);
if (shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignTop
&& shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignCenter)
@@ -175,12 +162,7 @@ void DiagramItem::updateGeometry()
height = geometry.height();
if (object()->isAutoSized()) {
- if (!m_customIcon) {
- if (width < MINIMUM_AUTO_WIDTH)
- width = MINIMUM_AUTO_WIDTH;
- if (height < MINIMUM_AUTO_HEIGHT)
- height = MINIMUM_AUTO_HEIGHT;
- }
+ correctAutoSize(customIconItem(), width, height, MINIMUM_AUTO_WIDTH, MINIMUM_AUTO_HEIGHT);
} else {
QRectF rect = object()->rect();
if (rect.width() > width)
@@ -203,9 +185,9 @@ void DiagramItem::updateGeometry()
// a backup for the graphics item used for manual resized and persistency.
object()->setRect(rect);
- if (m_customIcon) {
- m_customIcon->setPos(left, top);
- m_customIcon->setActualSize(QSizeF(width, height));
+ if (customIconItem()) {
+ customIconItem()->setPos(left, top);
+ customIconItem()->setActualSize(QSizeF(width, height));
}
if (m_body) {
@@ -227,7 +209,7 @@ void DiagramItem::updateGeometry()
m_fold->setPolygon(foldPolygon);
}
- if (m_customIcon) {
+ if (customIconItem()) {
switch (shapeIcon().textAlignment()) {
case qmt::StereotypeIcon::TextalignBelow:
y += height + BODY_VERT_BORDER;
diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.h
index 81012b04a1..88d9f9a7a8 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.h
+++ b/src/libs/modelinglib/qmt/diagram_scene/items/diagramitem.h
@@ -29,7 +29,6 @@ private:
QSizeF calcMinimumGeometry() const;
void updateGeometry();
- CustomIconItem *m_customIcon = nullptr;
QGraphicsPolygonItem *m_body = nullptr;
QGraphicsPolygonItem *m_fold = nullptr;
};
diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.cpp
index 97a1b337fc..9855889b08 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.cpp
+++ b/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.cpp
@@ -52,22 +52,10 @@ void ItemItem::update()
const Style *style = adaptedStyle(shapeIconId());
- if (!shapeIconId().isEmpty()) {
- if (!m_customIcon)
- m_customIcon = new CustomIconItem(diagramSceneModel(), this);
- m_customIcon->setStereotypeIconId(shapeIconId());
- m_customIcon->setBaseSize(stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT));
- m_customIcon->setBrush(style->fillBrush());
- m_customIcon->setPen(style->outerLinePen());
- m_customIcon->setZValue(SHAPE_ZVALUE);
- } else if (m_customIcon) {
- m_customIcon->scene()->removeItem(m_customIcon);
- delete m_customIcon;
- m_customIcon = nullptr;
- }
+ updateCustomIcon(style);
// shape
- if (!m_customIcon) {
+ if (!customIconItem()) {
if (!m_shape)
m_shape = new QGraphicsRectItem(this);
m_shape->setBrush(style->fillBrush());
@@ -100,7 +88,7 @@ void ItemItem::update()
m_contextLabel = nullptr;
}
- updateSelectionMarker(m_customIcon);
+ updateSelectionMarker(customIconItem());
updateRelationStarter();
updateAlignmentButtons();
updateGeometry();
@@ -108,8 +96,8 @@ void ItemItem::update()
bool ItemItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const
{
- if (m_customIcon) {
- QList<QPolygonF> polygons = m_customIcon->outline();
+ if (customIconItem()) {
+ QList<QPolygonF> polygons = customIconItem()->outline();
for (int i = 0; i < polygons.size(); ++i)
polygons[i].translate(object()->pos() + object()->rect().topLeft());
if (shapeIcon().textAlignment() == qmt::StereotypeIcon::TextalignBelow) {
@@ -152,14 +140,16 @@ QSizeF ItemItem::calcMinimumGeometry() const
{
double width = 0.0;
double height = 0.0;
+ double customMinHeight = 0.0;
- if (m_customIcon) {
- QSizeF sz = stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(),
+ if (customIconItem()) {
+ QSizeF sz = customIconItemMinimumSize(customIconItem(),
CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT);
if (shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignTop
&& shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignCenter)
return sz;
width = sz.width();
+ customMinHeight = sz.height();
}
height += BODY_VERT_BORDER;
@@ -181,6 +171,9 @@ QSizeF ItemItem::calcMinimumGeometry() const
width = BODY_HORIZ_BORDER + width + BODY_HORIZ_BORDER;
+ if (height < customMinHeight)
+ height = customMinHeight;
+
return GeometryUtilities::ensureMinimumRasterSize(QSizeF(width, height), 2 * RASTER_WIDTH, 2 * RASTER_HEIGHT);
}
@@ -197,7 +190,7 @@ void ItemItem::updateGeometry()
height = geometry.height();
if (object()->isAutoSized()) {
- // nothing
+ correctAutoSize(customIconItem(), width, height, 0, 0);
} else {
QRectF rect = object()->rect();
if (rect.width() > width)
@@ -221,15 +214,15 @@ void ItemItem::updateGeometry()
// a backup for the graphics item used for manual resized and persistency.
object()->setRect(rect);
- if (m_customIcon) {
- m_customIcon->setPos(left, top);
- m_customIcon->setActualSize(QSizeF(width, height));
+ if (customIconItem()) {
+ customIconItem()->setPos(left, top);
+ customIconItem()->setActualSize(QSizeF(width, height));
}
if (m_shape)
m_shape->setRect(rect);
- if (m_customIcon) {
+ if (customIconItem()) {
switch (shapeIcon().textAlignment()) {
case qmt::StereotypeIcon::TextalignBelow:
y += height + BODY_VERT_BORDER;
@@ -271,7 +264,7 @@ void ItemItem::updateGeometry()
y += nameItem()->boundingRect().height();
}
if (m_contextLabel) {
- if (m_customIcon) {
+ if (customIconItem()) {
m_contextLabel->resetMaxWidth();
} else {
double maxContextWidth = width - 2 * BODY_HORIZ_BORDER;
diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.h
index 327b3799ac..de091a2bc7 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.h
+++ b/src/libs/modelinglib/qmt/diagram_scene/items/itemitem.h
@@ -39,7 +39,6 @@ private:
QSizeF calcMinimumGeometry() const;
void updateGeometry();
- CustomIconItem *m_customIcon = nullptr;
QGraphicsRectItem *m_shape = nullptr;
ContextLabelItem *m_contextLabel = nullptr;
};
diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.cpp
index 0fe32080cf..d4f6bd386b 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.cpp
+++ b/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.cpp
@@ -185,6 +185,8 @@ void ObjectItem::setFocusSelected(bool focusSelected)
if (m_isFocusSelected != focusSelected) {
m_isFocusSelected = focusSelected;
update();
+ if (m_nameItem)
+ m_nameItem->setEditable(focusSelected);
}
}
@@ -353,9 +355,9 @@ void ObjectItem::relationDrawn(const QString &id, ObjectItem *targetItem, const
elementType = targetItem->stereotypeIconId();
else if (!targetItem->shapeIconId().isEmpty())
elementType = targetItem->shapeIconId();
- else
- elementType = targetItem->elementType();
- if (!endItems.contains(elementType)) {
+ if ((!elementType.isEmpty() && !endItems.contains(elementType))
+ && !endItems.contains(targetItem->elementType()))
+ {
return;
}
// create relation
@@ -498,6 +500,7 @@ void ObjectItem::updateStereotypes(const QString &stereotypeIconId, StereotypeIc
m_stereotypeIcon = new CustomIconItem(m_diagramSceneModel, this);
m_stereotypeIcon->setStereotypeIconId(stereotypeIconId);
m_stereotypeIcon->setBaseSize(QSizeF(m_stereotypeIcon->shapeWidth(), m_stereotypeIcon->shapeHeight()));
+ m_stereotypeIcon->setActualSize(QSizeF(m_stereotypeIcon->shapeWidth(), m_stereotypeIcon->shapeHeight()));
m_stereotypeIcon->setBrush(style->fillBrush());
m_stereotypeIcon->setPen(style->innerLinePen());
} else if (m_stereotypeIcon) {
@@ -518,45 +521,92 @@ void ObjectItem::updateStereotypes(const QString &stereotypeIconId, StereotypeIc
}
}
-QSizeF ObjectItem::stereotypeIconMinimumSize(const StereotypeIcon &stereotypeIcon,
+QSizeF ObjectItem::customIconItemMinimumSize(const CustomIconItem *customIconItem,
qreal minimumWidth, qreal minimumHeight) const
{
Q_UNUSED(minimumWidth)
qreal width = 0.0;
qreal height = 0.0;
- if (stereotypeIcon.hasMinWidth() && !stereotypeIcon.hasMinHeight()) {
- width = stereotypeIcon.minWidth();
- if (stereotypeIcon.sizeLock() == StereotypeIcon::LockHeight || stereotypeIcon.sizeLock() == StereotypeIcon::LockSize)
- height = stereotypeIcon.minHeight();
- else
- height = width * stereotypeIcon.height() / stereotypeIcon.width();
- } else if (!stereotypeIcon.hasMinWidth() && stereotypeIcon.hasMinHeight()) {
- height = stereotypeIcon.minHeight();
- if (stereotypeIcon.sizeLock() == StereotypeIcon::LockWidth || stereotypeIcon.sizeLock() == StereotypeIcon::LockSize)
- width = stereotypeIcon.minWidth();
- else
- width = height * stereotypeIcon.width() / stereotypeIcon.height();
- } else if (stereotypeIcon.hasMinWidth() && stereotypeIcon.hasMinHeight()) {
- if (stereotypeIcon.sizeLock() == StereotypeIcon::LockRatio) {
+ if (customIconItem->hasImage()) {
+ const QImage &image = customIconItem->image();
+ width = minimumWidth;
+ height = image.height() * width / image.width();
+ if (height < minimumHeight) {
+ height = minimumHeight;
+ width = image.width() * height / image.height();
+ }
+ } else {
+ const StereotypeIcon &stereotypeIcon = customIconItem->stereotypeIcon();
+ if (stereotypeIcon.hasMinWidth() && !stereotypeIcon.hasMinHeight()) {
width = stereotypeIcon.minWidth();
- height = width * stereotypeIcon.height() / stereotypeIcon.width();
- if (height < stereotypeIcon.minHeight()) {
+ if (stereotypeIcon.sizeLock() == StereotypeIcon::LockHeight || stereotypeIcon.sizeLock() == StereotypeIcon::LockSize)
height = stereotypeIcon.minHeight();
+ else
+ height = width * stereotypeIcon.height() / stereotypeIcon.width();
+ } else if (!stereotypeIcon.hasMinWidth() && stereotypeIcon.hasMinHeight()) {
+ height = stereotypeIcon.minHeight();
+ if (stereotypeIcon.sizeLock() == StereotypeIcon::LockWidth || stereotypeIcon.sizeLock() == StereotypeIcon::LockSize)
+ width = stereotypeIcon.minWidth();
+ else
width = height * stereotypeIcon.width() / stereotypeIcon.height();
- QMT_CHECK(width <= stereotypeIcon.minWidth());
+ } else if (stereotypeIcon.hasMinWidth() && stereotypeIcon.hasMinHeight()) {
+ if (stereotypeIcon.sizeLock() == StereotypeIcon::LockRatio) {
+ width = stereotypeIcon.minWidth();
+ height = width * stereotypeIcon.height() / stereotypeIcon.width();
+ if (height < stereotypeIcon.minHeight()) {
+ height = stereotypeIcon.minHeight();
+ width = height * stereotypeIcon.width() / stereotypeIcon.height();
+ QMT_CHECK(width <= stereotypeIcon.minWidth());
+ }
+ } else {
+ width = stereotypeIcon.minWidth();
+ height = stereotypeIcon.minHeight();
}
} else {
- width = stereotypeIcon.minWidth();
- height = stereotypeIcon.minHeight();
+ height = minimumHeight;
+ width = height * stereotypeIcon.width() / stereotypeIcon.height();
}
- } else {
- height = minimumHeight;
- width = height * stereotypeIcon.width() / stereotypeIcon.height();
}
return QSizeF(width, height);
}
+void ObjectItem::correctAutoSize(const CustomIconItem* customIconItem, qreal &width, qreal &height, qreal minimumWidth, qreal minimumHeight) const
+{
+ if (customIconItem) {
+ if (customIconItem->hasImage()) {
+ width = customIconItem->image().width();
+ height = customIconItem->image().height();
+ }
+ } else {
+ if (width < minimumWidth)
+ width = minimumWidth;
+ if (height < minimumHeight)
+ height = minimumHeight;
+ }
+}
+
+void ObjectItem::updateCustomIcon(const Style *style)
+{
+ if (object()->hasImage()) {
+ if (!m_customIcon)
+ m_customIcon = new CustomIconItem(diagramSceneModel(), this);
+ m_customIcon->setImage(object()->image());
+ m_customIcon->setZValue(SHAPE_ZVALUE);
+ } else if (!shapeIconId().isEmpty()) {
+ if (!m_customIcon)
+ m_customIcon = new CustomIconItem(diagramSceneModel(), this);
+ m_customIcon->setStereotypeIconId(shapeIconId());
+ m_customIcon->setBrush(style->fillBrush());
+ m_customIcon->setPen(style->outerLinePen());
+ m_customIcon->setZValue(SHAPE_ZVALUE);
+ } else if (m_customIcon) {
+ m_customIcon->scene()->removeItem(m_customIcon);
+ delete m_customIcon;
+ m_customIcon = nullptr;
+ }
+}
+
bool ObjectItem::suppressTextDisplay() const
{
return m_shapeIcon.textAlignment() == StereotypeIcon::TextalignNone;
@@ -564,6 +614,7 @@ bool ObjectItem::suppressTextDisplay() const
void ObjectItem::updateNameItem(const Style *style)
{
+ QString display_name = buildDisplayName();
if (!suppressTextDisplay()) {
if (!m_nameItem) {
m_nameItem = new EditableTextItem(this);
@@ -582,16 +633,18 @@ void ObjectItem::updateNameItem(const Style *style)
QObject::connect(m_nameItem, &EditableTextItem::returnKeyPressed, m_nameItem,
[this] { this->m_nameItem->clearFocus(); });
}
- if (style->headerFont() != m_nameItem->font())
- m_nameItem->setFont(style->headerFont());
+ QFont font = style->headerFont();
+ if (object()->hasLinkedFile())
+ font.setUnderline(true);
+ if (font != m_nameItem->font())
+ m_nameItem->setFont(font);
if (style->textBrush().color() != m_nameItem->defaultTextColor())
m_nameItem->setDefaultTextColor(style->textBrush().color());
if (!m_nameItem->hasFocus()) {
- QString name = buildDisplayName();
- if (name != m_nameItem->toPlainText())
- m_nameItem->setPlainText(name);
+ if (display_name != m_nameItem->toPlainText())
+ m_nameItem->setPlainText(display_name);
}
- } else if (m_nameItem ){
+ } else if (m_nameItem) {
m_nameItem->scene()->removeItem(m_nameItem);
delete m_nameItem;
m_nameItem = nullptr;
@@ -621,30 +674,45 @@ void ObjectItem::setObjectName(const QString &objectName)
void ObjectItem::updateDepth()
{
- setZValue(m_object->depth());
+ int depth_delta = 0;
+ switch (m_shapeIcon.depthLayer()) {
+ case StereotypeIcon::DepthBehindItems:
+ depth_delta = -1;
+ break;
+ case StereotypeIcon::DepthAmongItems:
+ depth_delta = 0;
+ break;
+ case StereotypeIcon::DepthBeforeItems:
+ depth_delta = 1;
+ break;
+ }
+ setZValue(m_object->depth() + depth_delta);
}
-void ObjectItem::updateSelectionMarker(CustomIconItem *customIconItem)
+void ObjectItem::updateSelectionMarker(const CustomIconItem *customIconItem)
{
if (customIconItem) {
- StereotypeIcon stereotypeIcon = customIconItem->stereotypeIcon();
ResizeFlags resizeFlags = ResizeUnlocked;
- switch (stereotypeIcon.sizeLock()) {
- case StereotypeIcon::LockNone:
- resizeFlags = ResizeUnlocked;
- break;
- case StereotypeIcon::LockWidth:
- resizeFlags = ResizeLockedWidth;
- break;
- case StereotypeIcon::LockHeight:
- resizeFlags = ResizeLockedHeight;
- break;
- case StereotypeIcon::LockSize:
- resizeFlags = ResizeLockedSize;
- break;
- case StereotypeIcon::LockRatio:
+ if (customIconItem->hasImage()) {
resizeFlags = ResizeLockedRatio;
- break;
+ } else {
+ switch (customIconItem->stereotypeIcon().sizeLock()) {
+ case StereotypeIcon::LockNone:
+ resizeFlags = ResizeUnlocked;
+ break;
+ case StereotypeIcon::LockWidth:
+ resizeFlags = ResizeLockedWidth;
+ break;
+ case StereotypeIcon::LockHeight:
+ resizeFlags = ResizeLockedHeight;
+ break;
+ case StereotypeIcon::LockSize:
+ resizeFlags = ResizeLockedSize;
+ break;
+ case StereotypeIcon::LockRatio:
+ resizeFlags = ResizeLockedRatio;
+ break;
+ }
}
updateSelectionMarker(resizeFlags);
} else {
@@ -990,6 +1058,10 @@ void ObjectItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
addSeparator = true;
if (element_tasks->extendContextMenu(object(), diagramSceneModel()->diagram(), &menu))
addSeparator = true;
+ if (element_tasks->hasLinkedFile(m_object, m_diagramSceneModel->diagram())) {
+ menu.addAction(new ContextMenuAction(Tr::tr("Open Linked File"), "openLinkedFile", &menu));
+ addSeparator = true;
+ }
if (addSeparator)
menu.addSeparator();
menu.addAction(new ContextMenuAction(Tr::tr("Remove"), "remove",
@@ -1000,12 +1072,12 @@ void ObjectItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
QMenu alignMenu;
alignMenu.setTitle(Tr::tr("Align Objects"));
alignMenu.addAction(new ContextMenuAction(Tr::tr("Align Left"), "alignLeft", &alignMenu));
- alignMenu.addAction(new ContextMenuAction(Tr::tr("Center Vertically"), "centerVertically",
+ alignMenu.addAction(new ContextMenuAction(Tr::tr("Center Horizontally"), "centerHorizontally",
&alignMenu));
alignMenu.addAction(new ContextMenuAction(Tr::tr("Align Right"), "alignRight", &alignMenu));
alignMenu.addSeparator();
alignMenu.addAction(new ContextMenuAction(Tr::tr("Align Top"), "alignTop", &alignMenu));
- alignMenu.addAction(new ContextMenuAction(Tr::tr("Center Horizontally"), "centerHorizontally",
+ alignMenu.addAction(new ContextMenuAction(Tr::tr("Center Vertically"), "centerVertically",
&alignMenu));
alignMenu.addAction(new ContextMenuAction(Tr::tr("Align Bottom"), "alignBottom", &alignMenu));
alignMenu.addSeparator();
@@ -1022,7 +1094,6 @@ void ObjectItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
layoutMenu.addAction(new ContextMenuAction(Tr::tr("Equal Vertical Space"), "sameVBorderDistance", &alignMenu));
layoutMenu.setEnabled(m_diagramSceneModel->hasMultiObjectsSelection());
menu.addMenu(&layoutMenu);
- menu.addAction(new ContextMenuAction(Tr::tr("Add Related Elements"), "addRelatedElements", &menu));
QAction *selectedAction = menu.exec(event->screenPos());
if (selectedAction) {
@@ -1035,6 +1106,8 @@ void ObjectItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
m_diagramSceneModel->diagramSceneController()->elementTasks()->openDiagram(m_object, m_diagramSceneModel->diagram());
} else if (action->id() == "createDiagram") {
m_diagramSceneModel->diagramSceneController()->elementTasks()->createAndOpenDiagram(m_object, m_diagramSceneModel->diagram());
+ } else if (action->id() == "openLinkedFile") {
+ m_diagramSceneModel->diagramSceneController()->elementTasks()->openLinkedFile(m_object, m_diagramSceneModel->diagram());
} else if (action->id() == "remove") {
DSelection selection = m_diagramSceneModel->selectedElements();
if (selection.isEmpty())
@@ -1073,11 +1146,6 @@ void ObjectItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
align(IAlignable::AlignHeight, "height");
} else if (action->id() == "sameSize") {
align(IAlignable::AlignSize, "size");
- } else if (action->id() == "addRelatedElements") {
- DSelection selection = m_diagramSceneModel->selectedElements();
- if (selection.isEmpty())
- selection.append(m_object->uid(), m_diagramSceneModel->diagram()->uid());
- m_diagramSceneModel->diagramSceneController()->addRelatedElements(selection, m_diagramSceneModel->diagram());
}
}
}
diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.h
index e7acd5705c..254ef215ca 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.h
+++ b/src/libs/modelinglib/qmt/diagram_scene/items/objectitem.h
@@ -120,8 +120,12 @@ protected:
StereotypeIcon::Display stereotypeDisplay, const Style *style);
StereotypesItem *stereotypesItem() const { return m_stereotypesItem; }
CustomIconItem *stereotypeIconItem() const { return m_stereotypeIcon; }
- QSizeF stereotypeIconMinimumSize(const StereotypeIcon &stereotypeIcon, qreal minimumWidth,
+ CustomIconItem *customIconItem() const { return m_customIcon; }
+ QSizeF customIconItemMinimumSize(const CustomIconItem* customIconItem, qreal minimumWidth,
qreal minimumHeight) const;
+ void correctAutoSize(const CustomIconItem *customIconItem, qreal& width, qreal& height,
+ qreal minimumWidth, qreal minimumHeight) const;
+ void updateCustomIcon(const Style* style);
bool suppressTextDisplay() const;
void updateNameItem(const Style *style);
EditableTextItem *nameItem() const { return m_nameItem; }
@@ -130,7 +134,7 @@ protected:
void setObjectName(const QString &objectName);
void updateDepth();
- void updateSelectionMarker(CustomIconItem *customIconItem);
+ void updateSelectionMarker(const CustomIconItem* customIconItem);
void updateSelectionMarker(ResizeFlags resizeFlags);
void updateSelectionMarkerGeometry(const QRectF &objectRect);
void updateRelationStarter();
@@ -169,6 +173,7 @@ private:
StereotypeIcon::Display m_stereotypeIconDisplay = StereotypeIcon::DisplayLabel;
StereotypesItem *m_stereotypesItem = nullptr;
CustomIconItem *m_stereotypeIcon = nullptr;
+ CustomIconItem *m_customIcon = nullptr;
EditableTextItem *m_nameItem = nullptr;
RectangularSelectionItem *m_selectionMarker = nullptr;
RelationStarter *m_relationStarter = nullptr;
diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.cpp
index b473d17ec8..03e6d8d0f5 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.cpp
+++ b/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.cpp
@@ -64,23 +64,10 @@ void PackageItem::update()
const Style *style = adaptedStyle(stereotypeIconId());
- // custom icon
- if (stereotypeIconDisplay() == StereotypeIcon::DisplayIcon) {
- if (!m_customIcon)
- m_customIcon = new CustomIconItem(diagramSceneModel(), this);
- m_customIcon->setStereotypeIconId(stereotypeIconId());
- m_customIcon->setBaseSize(stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(), CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT));
- m_customIcon->setBrush(style->fillBrush());
- m_customIcon->setPen(style->outerLinePen());
- m_customIcon->setZValue(SHAPE_ZVALUE);
- } else if (m_customIcon) {
- m_customIcon->scene()->removeItem(m_customIcon);
- delete m_customIcon;
- m_customIcon = nullptr;
- }
+ updateCustomIcon(style);
// shape
- if (!m_customIcon) {
+ if (!customIconItem()) {
if (!m_shape)
m_shape = new QGraphicsPolygonItem(this);
m_shape->setBrush(style->fillBrush());
@@ -111,7 +98,7 @@ void PackageItem::update()
m_contextLabel = nullptr;
}
- updateSelectionMarker(m_customIcon);
+ updateSelectionMarker(customIconItem());
updateRelationStarter();
updateAlignmentButtons();
updateGeometry();
@@ -119,8 +106,8 @@ void PackageItem::update()
bool PackageItem::intersectShapeWithLine(const QLineF &line, QPointF *intersectionPoint, QLineF *intersectionLine) const
{
- if (m_customIcon) {
- QList<QPolygonF> polygons = m_customIcon->outline();
+ if (customIconItem()) {
+ QList<QPolygonF> polygons = customIconItem()->outline();
for (int i = 0; i < polygons.size(); ++i)
polygons[i].translate(object()->pos() + object()->rect().topLeft());
if (shapeIcon().textAlignment() == qmt::StereotypeIcon::TextalignBelow) {
@@ -169,8 +156,8 @@ PackageItem::ShapeGeometry PackageItem::calcMinimumGeometry() const
double width = 0.0;
double height = 0.0;
- if (m_customIcon) {
- QSizeF sz = stereotypeIconMinimumSize(m_customIcon->stereotypeIcon(),
+ if (customIconItem()) {
+ QSizeF sz = customIconItemMinimumSize(customIconItem(),
CUSTOM_ICON_MINIMUM_AUTO_WIDTH, CUSTOM_ICON_MINIMUM_AUTO_HEIGHT);
if (shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignTop
&& shapeIcon().textAlignment() != qmt::StereotypeIcon::TextalignCenter)
@@ -244,12 +231,7 @@ void PackageItem::updateGeometry()
// calc width and height
if (object()->isAutoSized()) {
- if (!m_customIcon) {
- if (width < MINIMUM_AUTO_WIDTH)
- width = MINIMUM_AUTO_WIDTH;
- if (height < MINIMUM_AUTO_HEIGHT)
- height = MINIMUM_AUTO_HEIGHT;
- }
+ correctAutoSize(customIconItem(), width, height, MINIMUM_AUTO_WIDTH, MINIMUM_AUTO_HEIGHT);
} else {
QRectF rect = object()->rect();
if (rect.width() > width)
@@ -273,9 +255,9 @@ void PackageItem::updateGeometry()
// a backup for the graphics item used for manual resized and persistency.
object()->setRect(rect);
- if (m_customIcon) {
- m_customIcon->setPos(left, top);
- m_customIcon->setActualSize(QSizeF(width, height));
+ if (customIconItem()) {
+ customIconItem()->setPos(left, top);
+ customIconItem()->setActualSize(QSizeF(width, height));
switch (shapeIcon().textAlignment()) {
case qmt::StereotypeIcon::TextalignBelow:
diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.h
index 19c2fe54ab..f87af2fa35 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.h
+++ b/src/libs/modelinglib/qmt/diagram_scene/items/packageitem.h
@@ -40,7 +40,6 @@ private:
ShapeGeometry calcMinimumGeometry() const;
void updateGeometry();
- CustomIconItem *m_customIcon = nullptr;
QGraphicsPolygonItem *m_shape = nullptr;
ContextLabelItem *m_contextLabel = nullptr;
};
diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/relationitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/relationitem.cpp
index a2f444f59e..ca9dd8c3a2 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/items/relationitem.cpp
+++ b/src/libs/modelinglib/qmt/diagram_scene/items/relationitem.cpp
@@ -380,7 +380,12 @@ void RelationItem::setHandlePos(int index, const QPointF &pos)
}
}
-void RelationItem::dropHandle(int index, double rasterWidth, double rasterHeight)
+static inline bool between(double v, double mid, double range)
+{
+ return v >= mid-range && v <= mid+range;
+}
+
+void RelationItem::dropHandle(int index, bool extraSnap, double rasterWidth, double rasterHeight)
{
if (index == 0) {
m_grabbedEndA = false;
@@ -398,9 +403,40 @@ void RelationItem::dropHandle(int index, double rasterWidth, double rasterHeight
QMT_ASSERT(index >= 0 && index < intermediatePoints.size(), return);
QPointF pos = intermediatePoints.at(index).pos();
- double x = qRound(pos.x() / rasterWidth) * rasterWidth;
- double y = qRound(pos.y() / rasterHeight) * rasterHeight;
- intermediatePoints[index].setPos(QPointF(x, y));
+ static constexpr int ANGLE = 20.0;
+ bool roundX = true;
+ bool roundY = true;
+ if (extraSnap) {
+ if (index >= 1) {
+ double angle = GeometryUtilities::calcAngle(QLineF(intermediatePoints.at(index).pos(),
+ intermediatePoints.at(index - 1).pos()));
+ if (between(qAbs(angle), 90, ANGLE)) {
+ pos.setX(intermediatePoints.at(index - 1).pos().x());
+ roundX = false;
+ }
+ if (between(angle, 0, ANGLE) || between(qAbs(angle), 180, ANGLE)) {
+ pos.setY(intermediatePoints.at(index - 1).pos().y());
+ roundY = false;
+ }
+ }
+ if (index < intermediatePoints.size() - 1) {
+ double angle = GeometryUtilities::calcAngle(QLineF(intermediatePoints.at(index).pos(),
+ intermediatePoints.at(index + 1).pos()));
+ if (between(qAbs(angle), 90, ANGLE)) {
+ pos.setX(intermediatePoints.at(index + 1).pos().x());
+ roundX = false;
+ }
+ if (between(angle, 0, ANGLE) || between(qAbs(angle), 180, ANGLE)) {
+ pos.setY(intermediatePoints.at(index + 1).pos().y());
+ roundY = false;
+ }
+ }
+ }
+ if (roundX)
+ pos.setX(qRound(pos.x() / rasterWidth) * rasterWidth);
+ if (roundY)
+ pos.setY(qRound(pos.y() / rasterHeight) * rasterHeight);
+ intermediatePoints[index].setPos(pos);
m_diagramSceneModel->diagramController()->startUpdateElement(m_relation, m_diagramSceneModel->diagram(), DiagramController::UpdateMinor);
m_relation->setIntermediatePoints(intermediatePoints);
@@ -507,9 +543,11 @@ void RelationItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
const Style *RelationItem::adaptedStyle()
{
- DObject *endAObject = m_diagramSceneModel->diagramController()->findElement<DObject>(m_relation->endAUid(), m_diagramSceneModel->diagram());
- DObject *endBObject = m_diagramSceneModel->diagramController()->findElement<DObject>(m_relation->endBUid(), m_diagramSceneModel->diagram());
- StyledRelation styledRelation(m_relation, endAObject, endBObject);
+ const DObject *endAObject = m_diagramSceneModel->diagramController()->findElement<DObject>(m_relation->endAUid(), m_diagramSceneModel->diagram());
+ const DObject *endBObject = m_diagramSceneModel->diagramController()->findElement<DObject>(m_relation->endBUid(), m_diagramSceneModel->diagram());
+ const CustomRelation customRelation = m_diagramSceneModel->stereotypeController()
+ ->findCustomRelationByStereotype(m_relation->stereotypes().value(0));
+ StyledRelation styledRelation(m_relation, endAObject, endBObject, &customRelation);
return m_diagramSceneModel->styleController()->adaptRelationStyle(styledRelation);
}
diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/relationitem.h b/src/libs/modelinglib/qmt/diagram_scene/items/relationitem.h
index a43b8b2749..0980d232e4 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/items/relationitem.h
+++ b/src/libs/modelinglib/qmt/diagram_scene/items/relationitem.h
@@ -56,7 +56,7 @@ public:
void insertHandle(int beforeIndex, const QPointF &pos, double rasterWidth, double rasterHeight) override;
void deleteHandle(int index) override;
void setHandlePos(int index, const QPointF &pos) override;
- void dropHandle(int index, double rasterWidth, double rasterHeight) override;
+ void dropHandle(int index, bool extraSnap, double rasterWidth, double rasterHeight) override;
virtual void update();
diff --git a/src/libs/modelinglib/qmt/diagram_scene/items/stereotypedisplayvisitor.cpp b/src/libs/modelinglib/qmt/diagram_scene/items/stereotypedisplayvisitor.cpp
index ad668f6bee..9313327c87 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/items/stereotypedisplayvisitor.cpp
+++ b/src/libs/modelinglib/qmt/diagram_scene/items/stereotypedisplayvisitor.cpp
@@ -57,7 +57,9 @@ void StereotypeDisplayVisitor::visitDObject(const DObject *object)
DObject::StereotypeDisplay stereotypeDisplay = object->stereotypeDisplay();
m_stereotypeIconId = m_stereotypeController->findStereotypeIconId(m_stereotypeIconElement, object->stereotypes());
- if (m_stereotypeIconId.isEmpty() && stereotypeDisplay == DObject::StereotypeIcon) {
+ if (object->hasImage() && stereotypeDisplay == DObject::StereotypeSmart) {
+ stereotypeDisplay = DObject::StereotypeLabel;
+ } else if (m_stereotypeIconId.isEmpty() && stereotypeDisplay == DObject::StereotypeIcon) {
stereotypeDisplay = DObject::StereotypeLabel;
} else if (!m_stereotypeIconId.isEmpty() && stereotypeDisplay == DObject::StereotypeSmart) {
StereotypeIcon stereotypeIcon = m_stereotypeController->findStereotypeIcon(m_stereotypeIconId);
diff --git a/src/libs/modelinglib/qmt/diagram_scene/parts/arrowitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/parts/arrowitem.cpp
index 3a14eb10d9..632da76f05 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/parts/arrowitem.cpp
+++ b/src/libs/modelinglib/qmt/diagram_scene/parts/arrowitem.cpp
@@ -384,27 +384,29 @@ void ArrowItem::updateShaft(const Style *style)
QMT_ASSERT(m_shaftItem, return);
QPen pen(style->linePen());
+ pen.setCapStyle(Qt::FlatCap);
+
+ auto scale = [&pen](qreal v) {
+ return v / ((pen.widthF() - 1.0) / 2.0 + 1.0);
+ };
+
switch (m_shaft) {
case ShaftSolid:
break;
case ShaftDashed:
- pen.setDashPattern(QVector<qreal>()
- << (4.0 / pen.widthF()) << (4.0 / pen.widthF()));
+ pen.setDashPattern(QVector<qreal>() << scale(5.0) << scale(3.0));
break;
case ShaftDot:
- pen.setDashPattern(QVector<qreal>()
- << (2.0 / pen.widthF()) << (2.0 / pen.widthF()));
+ pen.setDashPattern(QVector<qreal>() << scale(3.0) << scale(3.0));
break;
case ShaftDashDot:
- pen.setDashPattern(QVector<qreal>()
- << (4.0 / pen.widthF()) << (2.0 / pen.widthF())
- << (2.0 / pen.widthF()) << (2.0 / pen.widthF()));
+ pen.setDashPattern(QVector<qreal>() << scale(5.0) << scale(3.0)
+ << scale(3.0) << scale(3.0));
break;
case ShaftDashDotDot:
- pen.setDashPattern(QVector<qreal>()
- << (4.0 / pen.widthF()) << (2.0 / pen.widthF())
- << (2.0 / pen.widthF()) << (2.0 / pen.widthF())
- << (2.0 / pen.widthF()) << (2.0 / pen.widthF()));
+ pen.setDashPattern(QVector<qreal>() << scale(5.0) << scale(3.0)
+ << scale(3.0) << scale(3.0)
+ << scale(3.0) << scale(3.0));
break;
}
m_shaftItem->setPen(pen);
diff --git a/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.cpp
index 066f9b7875..36e4c4f789 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.cpp
+++ b/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.cpp
@@ -57,55 +57,97 @@ void CustomIconItem::setPen(const QPen &pen)
m_pen = pen;
}
+void CustomIconItem::setImage(const QImage &image)
+{
+ m_image = image;
+}
+
double CustomIconItem::shapeWidth() const
{
- return m_stereotypeIcon.width();
+ if (!m_image.isNull())
+ return m_image.width();
+ return m_stereotypeIcon.hasIconWidth() ? m_stereotypeIcon.iconWidth()
+ : m_stereotypeIcon.width();
}
double CustomIconItem::shapeHeight() const
{
- return m_stereotypeIcon.height();
+ if (!m_image.isNull())
+ return m_image.height();
+ return m_stereotypeIcon.hasIconHeight() ? m_stereotypeIcon.iconHeight()
+ : m_stereotypeIcon.height();
}
QRectF CustomIconItem::boundingRect() const
{
- ShapeSizeVisitor visitor(QPointF(0.0, 0.0), QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), m_baseSize, m_actualSize);
+ if (!m_image.isNull())
+ return QRectF(QPointF(0.0, 0.0), m_actualSize) | childrenBoundingRect();
+ ShapeSizeVisitor visitor(QPointF(0.0, 0.0),
+ QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()),
+ m_baseSize,
+ m_actualSize);
m_stereotypeIcon.iconShape().visitShapes(&visitor);
return visitor.boundingRect() | childrenBoundingRect();
}
-void CustomIconItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+void CustomIconItem::paint(QPainter *painter,
+ const QStyleOptionGraphicsItem *option,
+ QWidget *widget)
{
Q_UNUSED(option)
Q_UNUSED(widget)
painter->save();
- painter->setBrush(m_brush);
- painter->setPen(m_pen);
+ if (!m_image.isNull()) {
+ painter->drawImage(QRectF(QPointF(0.0, 0.0), m_actualSize), m_image);
+ } else {
+ painter->setBrush(m_brush);
+ painter->setPen(m_pen);
#ifdef DEBUG_OUTLINE
- ShapePolygonVisitor visitor(QPointF(0.0, 0.0), QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), m_baseSize, m_actualSize);
- IconShape shape = m_stereotypeIcon.outlineShape();
- if (shape.isEmpty())
- shape = m_stereotypeIcon.iconShape();
- shape.visitShapes(&visitor);
- painter->drawPath(visitor.path());
- ShapePaintVisitor visitor1(painter, QPointF(0.0, 0.0), QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), m_baseSize, m_actualSize);
- m_stereotypeIcon.iconShape().visitShapes(&visitor1);
+ ShapePolygonVisitor visitor(QPointF(0.0, 0.0),
+ QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()),
+ m_baseSize,
+ m_actualSize);
+ IconShape shape = m_stereotypeIcon.outlineShape();
+ if (shape.isEmpty())
+ shape = m_stereotypeIcon.iconShape();
+ shape.visitShapes(&visitor);
+ painter->drawPath(visitor.path());
+ ShapePaintVisitor visitor1(painter,
+ QPointF(0.0, 0.0),
+ QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()),
+ m_baseSize,
+ m_actualSize);
+ m_stereotypeIcon.iconShape().visitShapes(&visitor1);
#else
- ShapePaintVisitor visitor(painter, QPointF(0.0, 0.0), QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), m_baseSize, m_actualSize);
- m_stereotypeIcon.iconShape().visitShapes(&visitor);
+ ShapePaintVisitor visitor(painter,
+ QPointF(0.0, 0.0),
+ QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()),
+ m_baseSize,
+ m_actualSize);
+ m_stereotypeIcon.iconShape().visitShapes(&visitor);
#endif
+ }
painter->restore();
}
QList<QPolygonF> CustomIconItem::outline() const
{
- ShapePolygonVisitor visitor(QPointF(0.0, 0.0), QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()), m_baseSize, m_actualSize);
- IconShape shape = m_stereotypeIcon.outlineShape();
- if (shape.isEmpty())
- shape = m_stereotypeIcon.iconShape();
- shape.visitShapes(&visitor);
- return visitor.toPolygons();
+ if (!m_image.isNull()) {
+ QList<QPolygonF> polygons;
+ polygons.append(QPolygonF(QRectF(QPointF(0.0, 0.0), m_actualSize)));
+ return polygons;
+ } else {
+ ShapePolygonVisitor visitor(QPointF(0.0, 0.0),
+ QSizeF(m_stereotypeIcon.width(), m_stereotypeIcon.height()),
+ m_baseSize,
+ m_actualSize);
+ IconShape shape = m_stereotypeIcon.outlineShape();
+ if (shape.isEmpty())
+ shape = m_stereotypeIcon.iconShape();
+ shape.visitShapes(&visitor);
+ return visitor.toPolygons();
+ }
}
} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.h b/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.h
index 7f85e87516..f61db552ac 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.h
+++ b/src/libs/modelinglib/qmt/diagram_scene/parts/customiconitem.h
@@ -9,6 +9,7 @@
#include "qmt/stereotype/stereotypeicon.h"
#include <QBrush>
+#include <QImage>
#include <QPen>
namespace qmt {
@@ -26,6 +27,9 @@ public:
void setActualSize(const QSizeF &actualSize);
void setBrush(const QBrush &brush);
void setPen(const QPen &pen);
+ QImage image() const { return m_image; }
+ bool hasImage() const { return !m_image.isNull(); }
+ void setImage(const QImage &image);
StereotypeIcon stereotypeIcon() const { return m_stereotypeIcon; }
double shapeWidth() const;
double shapeHeight() const;
@@ -43,6 +47,7 @@ private:
QSizeF m_actualSize;
QBrush m_brush;
QPen m_pen;
+ QImage m_image;
};
} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/diagram_scene/parts/editabletextitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/parts/editabletextitem.cpp
index 60275cb5ec..185321d84c 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/parts/editabletextitem.cpp
+++ b/src/libs/modelinglib/qmt/diagram_scene/parts/editabletextitem.cpp
@@ -13,13 +13,18 @@ namespace qmt {
EditableTextItem::EditableTextItem(QGraphicsItem *parent)
: QGraphicsTextItem(parent)
{
- setTextInteractionFlags(Qt::TextEditorInteraction);
+ setTextInteractionFlags(Qt::NoTextInteraction);
}
EditableTextItem::~EditableTextItem()
{
}
+void EditableTextItem::setEditable(bool editable)
+{
+ setTextInteractionFlags(editable ? Qt::TextEditorInteraction : Qt::NoTextInteraction);
+}
+
void EditableTextItem::setShowFocus(bool showFocus)
{
m_showFocus = showFocus;
@@ -72,13 +77,6 @@ void EditableTextItem::keyReleaseEvent(QKeyEvent *event)
QGraphicsTextItem::keyReleaseEvent(event);
}
-void EditableTextItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
-{
- scene()->clearSelection();
- parentItem()->setSelected(true);
- QGraphicsTextItem::mousePressEvent(event);
-}
-
void EditableTextItem::focusOutEvent(QFocusEvent *event)
{
QGraphicsTextItem::focusOutEvent(event);
diff --git a/src/libs/modelinglib/qmt/diagram_scene/parts/editabletextitem.h b/src/libs/modelinglib/qmt/diagram_scene/parts/editabletextitem.h
index 7c361b72fd..b18bf85cfc 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/parts/editabletextitem.h
+++ b/src/libs/modelinglib/qmt/diagram_scene/parts/editabletextitem.h
@@ -19,6 +19,7 @@ signals:
void returnKeyPressed();
public:
+ void setEditable(bool editable);
void setShowFocus(bool showFocus);
void setFilterReturnKey(bool filterReturnKey);
void setFilterTabKey(bool filterTabKey);
@@ -30,7 +31,6 @@ public:
protected:
void keyPressEvent(QKeyEvent *event) override;
void keyReleaseEvent(QKeyEvent *event) override;
- void mousePressEvent(QGraphicsSceneMouseEvent *event) override;
void focusOutEvent(QFocusEvent *event) override;
private:
diff --git a/src/libs/modelinglib/qmt/diagram_scene/parts/pathselectionitem.cpp b/src/libs/modelinglib/qmt/diagram_scene/parts/pathselectionitem.cpp
index 8206a7fe9a..231b78a3a0 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/parts/pathselectionitem.cpp
+++ b/src/libs/modelinglib/qmt/diagram_scene/parts/pathselectionitem.cpp
@@ -79,7 +79,10 @@ protected:
{
m_lastPos = event->scenePos();
QPointF delta = m_lastPos - m_startPos;
- m_owner->moveHandle(m_pointIndex, delta, Release, m_qualifier);
+ HandleQualifier qualifier = m_qualifier;
+ if (qualifier == None && (event->modifiers() & Qt::ShiftModifier) != 0)
+ qualifier = SnapHandle;
+ m_owner->moveHandle(m_pointIndex, delta, Release, qualifier);
clearFocus();
}
@@ -267,6 +270,7 @@ void PathSelectionItem::moveHandle(int pointIndex, const QPointF &deltaMove, Han
{
switch (handleQualifier) {
case None:
+ case SnapHandle:
{
if (handleStatus == Press) {
m_focusHandleItem = m_handles.at(pointIndex);
@@ -275,13 +279,13 @@ void PathSelectionItem::moveHandle(int pointIndex, const QPointF &deltaMove, Han
QPointF newPos = m_originalHandlePos + deltaMove;
m_windable->setHandlePos(pointIndex, newPos);
if (handleStatus == Release) {
- m_windable->dropHandle(pointIndex, RASTER_WIDTH, RASTER_HEIGHT);
+ m_windable->dropHandle(pointIndex, handleQualifier == SnapHandle, RASTER_WIDTH, RASTER_HEIGHT);
m_focusHandleItem = nullptr;
}
break;
}
case DeleteHandle:
- if (handleStatus == Press)
+ if (handleStatus == Release)
m_windable->deleteHandle(pointIndex);
break;
}
diff --git a/src/libs/modelinglib/qmt/diagram_scene/parts/pathselectionitem.h b/src/libs/modelinglib/qmt/diagram_scene/parts/pathselectionitem.h
index 0c4891948a..06a2d36c49 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/parts/pathselectionitem.h
+++ b/src/libs/modelinglib/qmt/diagram_scene/parts/pathselectionitem.h
@@ -25,7 +25,8 @@ class PathSelectionItem : public QGraphicsItem
enum HandleQualifier {
None,
- DeleteHandle
+ DeleteHandle,
+ SnapHandle
};
public:
diff --git a/src/libs/modelinglib/qmt/diagram_scene/parts/relationstarter.cpp b/src/libs/modelinglib/qmt/diagram_scene/parts/relationstarter.cpp
index 26b8ebc1c4..49bca85162 100644
--- a/src/libs/modelinglib/qmt/diagram_scene/parts/relationstarter.cpp
+++ b/src/libs/modelinglib/qmt/diagram_scene/parts/relationstarter.cpp
@@ -121,11 +121,13 @@ void RelationStarter::keyPressEvent(QKeyEvent *event)
m_currentPreviewArrowIntermediatePoints.append(p);
// Do not update the preview arrow here because last two points are now identical which looks wired
}
+ event->accept();
} else if (event->key() == Qt::Key_Control) {
if (!m_currentPreviewArrowIntermediatePoints.isEmpty()) {
m_currentPreviewArrowIntermediatePoints.removeLast();
updateCurrentPreviewArrow(m_currentPreviewArrow->lastLineSegment().p1());
}
+ event->accept();
}
}
diff --git a/src/libs/modelinglib/qmt/diagram_widgets_ui/diagramview.cpp b/src/libs/modelinglib/qmt/diagram_widgets_ui/diagramview.cpp
index 5634c4eb7f..555dc812e6 100644
--- a/src/libs/modelinglib/qmt/diagram_widgets_ui/diagramview.cpp
+++ b/src/libs/modelinglib/qmt/diagram_widgets_ui/diagramview.cpp
@@ -14,9 +14,14 @@
#include <QDragLeaveEvent>
#include <QDropEvent>
#include <QMimeData>
+#include <QScrollBar>
namespace qmt {
+namespace {
+const qreal ADJUSTMENT = 80;
+};
+
DiagramView::DiagramView(QWidget *parent)
: QGraphicsView(parent)
{
@@ -25,6 +30,9 @@ DiagramView::DiagramView(QWidget *parent)
setDragMode(QGraphicsView::RubberBandDrag);
setFrameShape(QFrame::NoFrame);
setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
+ m_panTimer.setInterval(200);
+ m_panTimer.setSingleShot(false);
+ connect(&m_panTimer, &QTimer::timeout, this, &DiagramView::onPanTimeout);
}
DiagramView::~DiagramView()
@@ -104,7 +112,7 @@ void DiagramView::dropEvent(QDropEvent *event)
if (diagramSceneController->isAddingAllowed(Uid(QUuid(key)),
m_diagramSceneModel->diagram())) {
diagramSceneController->addExistingModelElement(Uid(QUuid(key)),
- mapToScene(event->pos()),
+ mapToScene(event->position().toPoint()),
m_diagramSceneModel->diagram());
}
}
@@ -118,10 +126,10 @@ void DiagramView::dropEvent(QDropEvent *event)
QString stereotype;
dataStream >> newElementId >> name >> stereotype;
if (!newElementId.isEmpty()) {
- QPointF pos = mapToScene(event->pos());
+ QPointF pos = mapToScene(event->position().toPoint());
diagramSceneController->dropNewElement(
newElementId, name, stereotype, m_diagramSceneModel->findTopmostElement(pos),
- pos, m_diagramSceneModel->diagram(), event->pos(), size());
+ pos, m_diagramSceneModel->diagram(), event->position().toPoint(), size());
}
}
event->accept();
@@ -130,12 +138,41 @@ void DiagramView::dropEvent(QDropEvent *event)
}
}
+void DiagramView::mousePressEvent(QMouseEvent *event)
+{
+ m_panTimer.start();
+ QGraphicsView::mousePressEvent(event);
+}
+
+void DiagramView::mouseReleaseEvent(QMouseEvent *event)
+{
+ m_panTimer.stop();
+ QGraphicsView::mouseReleaseEvent(event);
+}
+
+void DiagramView::mouseMoveEvent(QMouseEvent *event)
+{
+ QGraphicsView::mouseMoveEvent(event);
+ m_lastMouse = event->pos();
+}
+
void DiagramView::onSceneRectChanged(const QRectF &sceneRect)
{
// add some adjustment to all 4 sides
- static const qreal ADJUSTMENT = 80;
QRectF rect = sceneRect.adjusted(-ADJUSTMENT, -ADJUSTMENT, ADJUSTMENT, ADJUSTMENT);
setSceneRect(rect);
}
+void DiagramView::onPanTimeout()
+{
+ if (m_lastMouse.x() < ADJUSTMENT)
+ horizontalScrollBar()->triggerAction(QScrollBar::SliderSingleStepSub);
+ else if (m_lastMouse.x() > viewport()->size().width() - ADJUSTMENT)
+ horizontalScrollBar()->triggerAction(QScrollBar::SliderSingleStepAdd);
+ if (m_lastMouse.y() < ADJUSTMENT)
+ verticalScrollBar()->triggerAction(QScrollBar::SliderSingleStepSub);
+ else if (m_lastMouse.y() > viewport()->size().height() - ADJUSTMENT)
+ verticalScrollBar()->triggerAction(QScrollBar::SliderSingleStepAdd);
+}
+
} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/diagram_widgets_ui/diagramview.h b/src/libs/modelinglib/qmt/diagram_widgets_ui/diagramview.h
index 41386f09bd..4eee60ff44 100644
--- a/src/libs/modelinglib/qmt/diagram_widgets_ui/diagramview.h
+++ b/src/libs/modelinglib/qmt/diagram_widgets_ui/diagramview.h
@@ -7,6 +7,7 @@
#include "qmt/infrastructure/qmt_global.h"
#include <QPointer>
+#include <QTimer>
namespace qmt {
@@ -27,11 +28,17 @@ protected:
void dragLeaveEvent(QDragLeaveEvent *event) override;
void dragMoveEvent(QDragMoveEvent *event) override;
void dropEvent(QDropEvent *event) override;
+ void mousePressEvent(QMouseEvent *event) override;
+ void mouseReleaseEvent(QMouseEvent *event) override;
+ void mouseMoveEvent(QMouseEvent *event) override;
private:
void onSceneRectChanged(const QRectF &sceneRect);
+ void onPanTimeout();
QPointer<DiagramSceneModel> m_diagramSceneModel;
+ QPointF m_lastMouse;
+ QTimer m_panTimer;
};
} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/document_controller/documentcontroller.cpp b/src/libs/modelinglib/qmt/document_controller/documentcontroller.cpp
index 68184d2fb5..696fc30558 100644
--- a/src/libs/modelinglib/qmt/document_controller/documentcontroller.cpp
+++ b/src/libs/modelinglib/qmt/document_controller/documentcontroller.cpp
@@ -32,7 +32,7 @@
#include "../../modelinglibtr.h"
-#include <QFileInfo>
+using Utils::FilePath;
namespace qmt {
@@ -233,7 +233,7 @@ MDiagram *DocumentController::findOrCreateRootDiagram()
return rootDiagram;
}
-void DocumentController::createNewProject(const QString &fileName)
+void DocumentController::createNewProject(const FilePath &fileName)
{
m_diagramsManager->removeAllDiagrams();
m_treeModel->setModelController(nullptr);
@@ -246,7 +246,7 @@ void DocumentController::createNewProject(const QString &fileName)
m_modelController->setRootPackage(m_projectController->project()->rootPackage());
}
-void DocumentController::loadProject(const QString &fileName)
+void DocumentController::loadProject(const FilePath &fileName)
{
m_diagramsManager->removeAllDiagrams();
m_treeModel->setModelController(nullptr);
diff --git a/src/libs/modelinglib/qmt/document_controller/documentcontroller.h b/src/libs/modelinglib/qmt/document_controller/documentcontroller.h
index 8053d189e0..9c5188e3f7 100644
--- a/src/libs/modelinglib/qmt/document_controller/documentcontroller.h
+++ b/src/libs/modelinglib/qmt/document_controller/documentcontroller.h
@@ -7,6 +7,8 @@
#include "qmt/infrastructure/qmt_global.h"
#include "qmt/model_controller/modelcontroller.h"
+#include <utils/filepath.h>
+
namespace qmt {
class ProjectController;
@@ -77,8 +79,8 @@ public:
MDiagram *findRootDiagram();
MDiagram *findOrCreateRootDiagram();
- void createNewProject(const QString &fileName);
- void loadProject(const QString &fileName);
+ void createNewProject(const Utils::FilePath &fileName);
+ void loadProject(const Utils::FilePath &fileName);
private:
ProjectController *m_projectController = nullptr;
diff --git a/src/libs/modelinglib/qmt/infrastructure/ioexceptions.cpp b/src/libs/modelinglib/qmt/infrastructure/ioexceptions.cpp
index 4af8df9236..bb11a4d566 100644
--- a/src/libs/modelinglib/qmt/infrastructure/ioexceptions.cpp
+++ b/src/libs/modelinglib/qmt/infrastructure/ioexceptions.cpp
@@ -7,6 +7,8 @@
#include <QObject>
+using Utils::FilePath;
+
namespace qmt {
IOException::IOException(const QString &errorMsg)
@@ -14,39 +16,39 @@ IOException::IOException(const QString &errorMsg)
{
}
-FileIOException::FileIOException(const QString &errorMsg, const QString &fileName, int lineNumber)
+FileIOException::FileIOException(const QString &errorMsg, const FilePath &fileName, int lineNumber)
: IOException(errorMsg),
m_fileName(fileName),
m_lineNumber(lineNumber)
{
}
-FileNotFoundException::FileNotFoundException(const QString &fileName)
+FileNotFoundException::FileNotFoundException(const FilePath &fileName)
: FileIOException(Tr::tr("File not found."), fileName)
{
}
-FileCreationException::FileCreationException(const QString &fileName)
+FileCreationException::FileCreationException(const FilePath &fileName)
: FileIOException(Tr::tr("Unable to create file."), fileName)
{
}
-FileWriteError::FileWriteError(const QString &fileName, int lineNumber)
+FileWriteError::FileWriteError(const FilePath &fileName, int lineNumber)
: FileIOException(Tr::tr("Writing to file failed."), fileName, lineNumber)
{
}
-FileReadError::FileReadError(const QString &fileName, int lineNumber)
+FileReadError::FileReadError(const FilePath &fileName, int lineNumber)
: FileIOException(Tr::tr("Reading from file failed."), fileName, lineNumber)
{
}
-IllegalXmlFile::IllegalXmlFile(const QString &fileName, int lineNumber)
+IllegalXmlFile::IllegalXmlFile(const FilePath &fileName, int lineNumber)
: FileIOException(Tr::tr("Illegal XML file."), fileName, lineNumber)
{
}
-UnknownFileVersion::UnknownFileVersion(int version, const QString &fileName, int lineNumber)
+UnknownFileVersion::UnknownFileVersion(int version, const FilePath &fileName, int lineNumber)
: FileIOException(Tr::tr("Unable to handle file version %1.")
.arg(version), fileName, lineNumber)
{
diff --git a/src/libs/modelinglib/qmt/infrastructure/ioexceptions.h b/src/libs/modelinglib/qmt/infrastructure/ioexceptions.h
index aece890529..7dc91ff4ba 100644
--- a/src/libs/modelinglib/qmt/infrastructure/ioexceptions.h
+++ b/src/libs/modelinglib/qmt/infrastructure/ioexceptions.h
@@ -5,6 +5,8 @@
#include "exceptions.h"
+#include <utils/filepath.h>
+
namespace qmt {
class IOException : public Exception
@@ -16,51 +18,51 @@ public:
class FileIOException : public IOException
{
public:
- explicit FileIOException(const QString &errorMsg, const QString &fileName = QString(),
+ explicit FileIOException(const QString &errorMsg, const Utils::FilePath &fileName = {},
int lineNumber = -1);
- QString fileName() const { return m_fileName; }
+ Utils::FilePath fileName() const { return m_fileName; }
int lineNumber() const { return m_lineNumber; }
private:
- QString m_fileName;
+ Utils::FilePath m_fileName;
int m_lineNumber = -1;
};
class FileNotFoundException : public FileIOException
{
public:
- explicit FileNotFoundException(const QString &fileName);
+ explicit FileNotFoundException(const Utils::FilePath &fileName);
};
class FileCreationException : public FileIOException
{
public:
- explicit FileCreationException(const QString &fileName);
+ explicit FileCreationException(const Utils::FilePath &fileName);
};
class FileWriteError : public FileIOException
{
public:
- explicit FileWriteError(const QString &fileName, int lineNumber = -1);
+ explicit FileWriteError(const Utils::FilePath &fileName, int lineNumber = -1);
};
class FileReadError : public FileIOException
{
public:
- explicit FileReadError(const QString &fileName, int lineNumber = -1);
+ explicit FileReadError(const Utils::FilePath &fileName, int lineNumber = -1);
};
class IllegalXmlFile : public FileIOException
{
public:
- explicit IllegalXmlFile(const QString &fileName, int lineNumber = -1);
+ explicit IllegalXmlFile(const Utils::FilePath &fileName, int lineNumber = -1);
};
class UnknownFileVersion : public FileIOException
{
public:
- UnknownFileVersion(int version, const QString &fileName, int lineNumber = -1);
+ UnknownFileVersion(int version, const Utils::FilePath &fileName, int lineNumber = -1);
};
} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/model/mdiagram.h b/src/libs/modelinglib/qmt/model/mdiagram.h
index 8f327daabc..bebe17cb50 100644
--- a/src/libs/modelinglib/qmt/model/mdiagram.h
+++ b/src/libs/modelinglib/qmt/model/mdiagram.h
@@ -21,7 +21,7 @@ public:
MDiagram &operator=(const MDiagram &rhs);
- const QList<DElement *> &diagramElements() const { return m_elements; }
+ const QList<DElement *> diagramElements() const { return m_elements; }
DElement *findDiagramElement(const Uid &key) const;
DElement *findDelegate(const Uid &modelUid) const;
void setDiagramElements(const QList<DElement *> &elements);
diff --git a/src/libs/modelinglib/qmt/model/mobject.cpp b/src/libs/modelinglib/qmt/model/mobject.cpp
index 734be13567..f5b4e2b36f 100644
--- a/src/libs/modelinglib/qmt/model/mobject.cpp
+++ b/src/libs/modelinglib/qmt/model/mobject.cpp
@@ -8,6 +8,8 @@
#include "mvisitor.h"
#include "mconstvisitor.h"
+using Utils::FilePath;
+
namespace qmt {
MObject::MObject()
@@ -20,6 +22,7 @@ MObject::MObject()
MObject::MObject(const MObject &rhs)
: MElement(rhs),
m_name(rhs.m_name),
+ m_linkedfilename(rhs.m_linkedfilename),
m_children(true),
m_relations(true)
{
@@ -34,6 +37,7 @@ MObject &MObject::operator =(const MObject &rhs)
if (this != &rhs) {
MElement::operator=(rhs);
m_name = rhs.m_name;
+ m_linkedfilename = rhs.m_linkedfilename;
// no deep copy; list of children remains unchanged
}
return *this;
@@ -44,6 +48,11 @@ void MObject::setName(const QString &name)
m_name = name;
}
+void MObject::setLinkedFileName(const FilePath &linkedfilename)
+{
+ m_linkedfilename = linkedfilename;
+}
+
void MObject::setChildren(const Handles<MObject> &children)
{
m_children = children;
diff --git a/src/libs/modelinglib/qmt/model/mobject.h b/src/libs/modelinglib/qmt/model/mobject.h
index 1dc95887d9..29d10da4ba 100644
--- a/src/libs/modelinglib/qmt/model/mobject.h
+++ b/src/libs/modelinglib/qmt/model/mobject.h
@@ -6,6 +6,8 @@
#include "melement.h"
#include "qmt/infrastructure/handles.h"
+#include <utils/filepath.h>
+
#include <QString>
namespace qmt {
@@ -23,6 +25,8 @@ public:
QString name() const { return m_name; }
void setName(const QString &name);
+ Utils::FilePath linkedFileName() const { return m_linkedfilename; }
+ void setLinkedFileName(const Utils::FilePath &linkedfilename);
const Handles<MObject> &children() const { return m_children; }
void setChildren(const Handles<MObject> &children);
@@ -48,6 +52,7 @@ public:
private:
QString m_name;
+ Utils::FilePath m_linkedfilename;
Handles<MObject> m_children;
Handles<MRelation> m_relations;
};
diff --git a/src/libs/modelinglib/qmt/model_controller/mflatassignmentvisitor.cpp b/src/libs/modelinglib/qmt/model_controller/mflatassignmentvisitor.cpp
index deb458e4bc..66cfb4576f 100644
--- a/src/libs/modelinglib/qmt/model_controller/mflatassignmentvisitor.cpp
+++ b/src/libs/modelinglib/qmt/model_controller/mflatassignmentvisitor.cpp
@@ -35,6 +35,7 @@ void MFlatAssignmentVisitor::visitMObject(const MObject *object)
auto targetObject = dynamic_cast<MObject *>(m_target);
QMT_ASSERT(targetObject, return);
targetObject->setName(object->name());
+ targetObject->setLinkedFileName(object->linkedFileName());
}
void MFlatAssignmentVisitor::visitMPackage(const MPackage *package)
diff --git a/src/libs/modelinglib/qmt/model_ui/modeltreefilterdata.cpp b/src/libs/modelinglib/qmt/model_ui/modeltreefilterdata.cpp
new file mode 100644
index 0000000000..ba411690f5
--- /dev/null
+++ b/src/libs/modelinglib/qmt/model_ui/modeltreefilterdata.cpp
@@ -0,0 +1,43 @@
+// Copyright (C) 2018 Jochen Becher
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "modeltreefilterdata.h"
+
+namespace qmt {
+
+void ModelTreeViewData::setShowRelations(bool newShowRelations)
+{
+ m_showRelations = newShowRelations;
+}
+
+void ModelTreeViewData::setShowDiagramElements(bool newShowDiagramElements)
+{
+ m_showDiagramElements = newShowDiagramElements;
+}
+
+void ModelTreeFilterData::setType(Type newType)
+{
+ m_type = newType;
+}
+
+void ModelTreeFilterData::setCustomId(const QString &newCustomId)
+{
+ m_customId = newCustomId;
+}
+
+void ModelTreeFilterData::setStereotypes(const QStringList &newStereotypes)
+{
+ m_stereotypes = newStereotypes;
+}
+
+void ModelTreeFilterData::setName(const QString &newName)
+{
+ m_name = newName;
+}
+
+void ModelTreeFilterData::setDirection(Direction newDirection)
+{
+ m_direction = newDirection;
+}
+
+} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/model_ui/modeltreefilterdata.h b/src/libs/modelinglib/qmt/model_ui/modeltreefilterdata.h
new file mode 100644
index 0000000000..3af018fefd
--- /dev/null
+++ b/src/libs/modelinglib/qmt/model_ui/modeltreefilterdata.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2018 Jochen Becher
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QString>
+#include <QStringList>
+
+namespace qmt {
+
+class ModelTreeViewData
+{
+public:
+
+ bool showRelations() const { return m_showRelations; }
+ void setShowRelations(bool newShowRelations);
+
+ bool showDiagramElements() const { return m_showDiagramElements; }
+ void setShowDiagramElements(bool newShowDiagramElements);
+
+private:
+ bool m_showRelations = true;
+ bool m_showDiagramElements = false;
+};
+
+class ModelTreeFilterData
+{
+public:
+ enum class Type {
+ Any,
+ Package,
+ Component,
+ Class,
+ Diagram,
+ Item,
+ Dependency,
+ Association,
+ Inheritance,
+ Connection
+ };
+
+ enum class Direction {
+ Any,
+ Outgoing,
+ Incoming,
+ Bidirectional
+ };
+
+ Type type() const { return m_type; }
+ void setType(Type newType);
+
+ QString customId() const { return m_customId; }
+ void setCustomId(const QString &newCustomId);
+
+ QStringList stereotypes() const { return m_stereotypes; }
+ void setStereotypes(const QStringList &newStereotypes);
+
+ QString name() const { return m_name; }
+ void setName(const QString &newName);
+
+ Direction direction() const { return m_direction; }
+ void setDirection(Direction newDirection);
+
+private:
+ Type m_type = Type::Any;
+ QString m_customId;
+ QStringList m_stereotypes;
+ QString m_name;
+ Direction m_direction = Direction::Any;
+};
+
+} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.cpp b/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.cpp
index 972c2f9c00..e69e1635a7 100644
--- a/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.cpp
+++ b/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.cpp
@@ -3,14 +3,203 @@
#include "sortedtreemodel.h"
+#include "qmt/model/massociation.h"
+#include "qmt/model/mcanvasdiagram.h"
+#include "qmt/model/mclass.h"
+#include "qmt/model/mcomponent.h"
+#include "qmt/model/mconnection.h"
+#include "qmt/model/mdependency.h"
+#include "qmt/model/minheritance.h"
+#include "qmt/model/mitem.h"
+#include "qmt/model/mpackage.h"
+#include "qmt/model/mconstvisitor.h"
#include "treemodel.h"
namespace qmt {
+namespace {
+
+class Filter : public MConstVisitor {
+public:
+
+ void setModelTreeViewData(const ModelTreeViewData *viewData)
+ {
+ m_viewData = viewData;
+ }
+
+ void setModelTreeFilterData(const ModelTreeFilterData *filterData)
+ {
+ m_filterData = filterData;
+ }
+
+ bool keep() const { return m_keep; }
+
+ void visitMElement(const MElement *element) override
+ {
+ if (!m_filterData->stereotypes().isEmpty()) {
+ const QStringList stereotypes = element->stereotypes();
+ bool containsElementStereotype = std::any_of(
+ stereotypes.constBegin(), stereotypes.constEnd(),
+ [&](const QString &s) { return m_filterData->stereotypes().contains(s); });
+ if (!containsElementStereotype) {
+ m_keep = false;
+ return;
+ }
+ }
+ }
+
+ void visitMObject(const MObject *object) override
+ {
+ if (!m_filterData->name().isEmpty() && m_filterData->name() != object->name())
+ m_keep = false;
+ else
+ visitMElement(object);
+ }
+
+ void visitMPackage(const MPackage *package) override
+ {
+ if (m_filterData->type() != ModelTreeFilterData::Type::Any
+ && m_filterData->type() != ModelTreeFilterData::Type::Package)
+ {
+ m_keep = false;
+ } else {
+ visitMObject(package);
+ }
+ }
+
+ void visitMClass(const MClass *klass) override
+ {
+ if (m_filterData->type() != ModelTreeFilterData::Type::Any
+ && m_filterData->type() != ModelTreeFilterData::Type::Class)
+ {
+ m_keep = false;
+ } else {
+ visitMObject(klass);
+ }
+ }
+
+ void visitMComponent(const MComponent *component) override
+ {
+ if (m_filterData->type() != ModelTreeFilterData::Type::Any
+ && m_filterData->type() != ModelTreeFilterData::Type::Component)
+ {
+ m_keep = false;
+ } else {
+ visitMObject(component);
+ }
+ }
+
+ void visitMDiagram(const MDiagram *diagram) override
+ {
+ if (m_filterData->type() != ModelTreeFilterData::Type::Any
+ && m_filterData->type() != ModelTreeFilterData::Type::Diagram)
+ {
+ m_keep = false;
+ } else {
+ visitMObject(diagram);
+ }
+ }
+
+ void visitMCanvasDiagram(const MCanvasDiagram *diagram) override
+ {
+ visitMDiagram(diagram);
+ }
+
+ void visitMItem(const MItem *item) override
+ {
+ if (m_filterData->type() != ModelTreeFilterData::Type::Any
+ && m_filterData->type() != ModelTreeFilterData::Type::Item)
+ {
+ m_keep = false;
+ } else {
+ visitMObject(item);
+ }
+ }
+
+ void visitMRelation(const MRelation *relation) override
+ {
+ if (!m_viewData->showRelations())
+ m_keep = false;
+ else if (!m_filterData->name().isEmpty() && m_filterData->name() != relation->name())
+ m_keep = false;
+ else
+ visitMElement(relation);
+ }
+
+ void visitMDependency(const MDependency *dependency) override
+ {
+ if (m_filterData->type() != ModelTreeFilterData::Type::Any
+ && m_filterData->type() != ModelTreeFilterData::Type::Dependency)
+ {
+ m_keep = false;
+ } else {
+ switch (m_filterData->direction()) {
+ case ModelTreeFilterData::Direction::Any:
+ break;
+ case ModelTreeFilterData::Direction::Outgoing:
+ if (dependency->direction() != MDependency::AToB)
+ m_keep = false;
+ break;
+ case ModelTreeFilterData::Direction::Incoming:
+ if (dependency->direction() != MDependency::BToA)
+ m_keep = false;
+ break;
+ case ModelTreeFilterData::Direction::Bidirectional:
+ if (dependency->direction() != MDependency::Bidirectional)
+ m_keep = false;
+ break;
+ }
+ if (m_keep)
+ visitMRelation(dependency);
+ }
+ }
+
+ void visitMInheritance(const MInheritance *inheritance) override
+ {
+ if (m_filterData->type() != ModelTreeFilterData::Type::Any
+ && m_filterData->type() != ModelTreeFilterData::Type::Inheritance)
+ {
+ m_keep = false;
+ } else {
+ visitMRelation(inheritance);
+ }
+ }
+
+ void visitMAssociation(const MAssociation *association) override
+ {
+ if (m_filterData->type() != ModelTreeFilterData::Type::Any
+ && m_filterData->type() != ModelTreeFilterData::Type::Association)
+ {
+ m_keep = false;
+ } else {
+ visitMRelation(association);
+ }
+ }
+
+ void visitMConnection(const MConnection *connection) override
+ {
+ if (m_filterData->type() != ModelTreeFilterData::Type::Any
+ && m_filterData->type() != ModelTreeFilterData::Type::Connection)
+ {
+ m_keep = false;
+ } else {
+ visitMRelation(connection);
+ }
+ }
+
+private:
+ const ModelTreeViewData *m_viewData = nullptr;
+ const ModelTreeFilterData *m_filterData = nullptr;
+ bool m_keep = true;
+};
+
+}
+
SortedTreeModel::SortedTreeModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
setDynamicSortFilter(false);
+ setRecursiveFilteringEnabled(true);
setSortCaseSensitivity(Qt::CaseInsensitive);
m_delayedSortTimer.setSingleShot(true);
@@ -33,6 +222,37 @@ void SortedTreeModel::setTreeModel(TreeModel *treeModel)
startDelayedSortTimer();
}
+void SortedTreeModel::setModelTreeViewData(const ModelTreeViewData &viewData)
+{
+ m_modelTreeViewData = viewData;
+ beginResetModel();
+ endResetModel();
+ startDelayedSortTimer();
+}
+
+void SortedTreeModel::setModelTreeFilterData(const ModelTreeFilterData &filterData)
+{
+ m_modelTreeViewFilterData = filterData;
+ beginResetModel();
+ endResetModel();
+ startDelayedSortTimer();
+}
+
+bool SortedTreeModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
+{
+ QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
+ MElement *element = m_treeModel->element(index);
+ if (element) {
+ Filter filter;
+ filter.setModelTreeViewData(&m_modelTreeViewData);
+ filter.setModelTreeFilterData(&m_modelTreeViewFilterData);
+ element->accept(&filter);
+ if (!filter.keep())
+ return false;
+ }
+ return true;
+}
+
bool SortedTreeModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
TreeModel::ItemType leftItemType = TreeModel::ItemType(sourceModel()->data(left, TreeModel::RoleItemType).toInt());
diff --git a/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.h b/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.h
index 0c01cf7de7..797a321132 100644
--- a/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.h
+++ b/src/libs/modelinglib/qmt/model_ui/sortedtreemodel.h
@@ -4,6 +4,7 @@
#pragma once
#include "qmt/infrastructure/qmt_global.h"
+#include "qmt/model_ui/modeltreefilterdata.h"
#include <QSortFilterProxyModel>
#include <QTimer>
@@ -21,7 +22,11 @@ public:
TreeModel *treeModel() const { return m_treeModel; }
void setTreeModel(TreeModel *treeModel);
+ void setModelTreeViewData(const ModelTreeViewData &viewData);
+ void setModelTreeFilterData(const ModelTreeFilterData &filterData);
+
protected:
+ bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
private:
@@ -32,6 +37,8 @@ private:
void startDelayedSortTimer();
TreeModel *m_treeModel = nullptr;
+ ModelTreeViewData m_modelTreeViewData;
+ ModelTreeFilterData m_modelTreeViewFilterData;
QTimer m_delayedSortTimer;
};
diff --git a/src/libs/modelinglib/qmt/model_ui/treemodel.cpp b/src/libs/modelinglib/qmt/model_ui/treemodel.cpp
index 3e8d5a59b6..23d589a654 100644
--- a/src/libs/modelinglib/qmt/model_ui/treemodel.cpp
+++ b/src/libs/modelinglib/qmt/model_ui/treemodel.cpp
@@ -30,6 +30,8 @@
#include <QStandardItem>
+using Utils::FilePath;
+
namespace qmt {
class TreeModel::ModelItem : public QStandardItem
@@ -807,7 +809,7 @@ QString TreeModel::createRelationLabel(const MRelation *relation)
}
QIcon TreeModel::createIcon(StereotypeIcon::Element stereotypeIconElement, StyleEngine::ElementType styleElementType,
- const QStringList &stereotypes, const QString &defaultIconPath)
+ const QStringList &stereotypes, const FilePath &defaultIconPath)
{
const Style *style = m_styleController->adaptStyle(styleElementType);
return m_stereotypeController->createIcon(stereotypeIconElement, stereotypes, defaultIconPath, style,
diff --git a/src/libs/modelinglib/qmt/model_ui/treemodel.h b/src/libs/modelinglib/qmt/model_ui/treemodel.h
index add325a94a..5ea3596348 100644
--- a/src/libs/modelinglib/qmt/model_ui/treemodel.h
+++ b/src/libs/modelinglib/qmt/model_ui/treemodel.h
@@ -9,6 +9,8 @@
#include <qmt/stereotype/stereotypeicon.h>
#include <qmt/style/styleengine.h>
+#include <utils/filepath.h>
+
#include <QScopedPointer>
#include <QHash>
@@ -93,8 +95,9 @@ private:
QString createObjectLabel(const MObject *object);
QString createRelationLabel(const MRelation *relation);
QIcon createIcon(StereotypeIcon::Element stereotypeIconElement,
- StyleEngine::ElementType styleElementType, const QStringList &stereotypes,
- const QString &defaultIconPath);
+ StyleEngine::ElementType styleElementType,
+ const QStringList &stereotypes,
+ const Utils::FilePath &defaultIconPath);
enum Busy {
NotBusy,
diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/addrelatedelementsdialog.cpp b/src/libs/modelinglib/qmt/model_widgets_ui/addrelatedelementsdialog.cpp
new file mode 100644
index 0000000000..40499e2839
--- /dev/null
+++ b/src/libs/modelinglib/qmt/model_widgets_ui/addrelatedelementsdialog.cpp
@@ -0,0 +1,426 @@
+// Copyright (C) 2018 Jochen Becher
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "addrelatedelementsdialog.h"
+
+#include <qmt/tasks/diagramscenecontroller.h>
+#include <qmt/diagram_controller/dselection.h>
+#include <qmt/model/mdiagram.h>
+#include <qmt/model/mrelation.h>
+#include <qmt/model/mdependency.h>
+#include <qmt/model/minheritance.h>
+#include <qmt/model/massociation.h>
+#include <qmt/model/mconnection.h>
+#include <qmt/model_controller/modelcontroller.h>
+#include <qmt/model_controller/mvoidvisitor.h>
+
+#include "../../modelinglibtr.h"
+
+#include <utils/layoutbuilder.h>
+
+#include <QComboBox>
+#include <QDialogButtonBox>
+#include <QLabel>
+#include <QStringListModel>
+
+namespace qmt {
+
+namespace {
+
+enum class RelationType {
+ Any,
+ Dependency,
+ Association,
+ Inheritance,
+ Connection
+};
+
+enum class RelationDirection {
+ Any,
+ Outgoing,
+ Incoming,
+ Bidirectional
+};
+
+enum class ElementType {
+ Any,
+ Package,
+ Component,
+ Class,
+ Diagram,
+ Item,
+};
+
+class Filter : public qmt::MVoidConstVisitor {
+public:
+
+ void setRelationType(RelationType newRelationType)
+ {
+ m_relationType = newRelationType;
+ }
+
+ void setRelationTypeId(const QString &newRelationTypeId)
+ {
+ m_relationTypeId = newRelationTypeId;
+ }
+
+ void setRelationDirection(RelationDirection newRelationDirection)
+ {
+ m_relationDirection = newRelationDirection;
+ }
+
+ void setRelationStereotypes(const QStringList &newRelationStereotypes)
+ {
+ m_relationStereotypes = newRelationStereotypes;
+ }
+
+ void setElementType(ElementType newElementType)
+ {
+ m_elementType = newElementType;
+ }
+
+ void setElementStereotypes(const QStringList &newElementStereotypes)
+ {
+ m_elementStereotypes = newElementStereotypes;
+ }
+
+ void setObject(const qmt::DObject *dobject, const qmt::MObject *mobject)
+ {
+ m_dobject = dobject;
+ m_mobject = mobject;
+ }
+
+ void setRelation(const qmt::MRelation *relation)
+ {
+ m_relation = relation;
+ }
+
+ bool keep() const { return m_keep; }
+
+ void reset()
+ {
+ m_dobject = nullptr;
+ m_mobject = nullptr;
+ m_relation = nullptr;
+ m_keep = true;
+ }
+
+ // MConstVisitor interface
+ void visitMObject(const qmt::MObject *object) override
+ {
+ if (!m_elementStereotypes.isEmpty()) {
+ const QStringList stereotypes = object->stereotypes();
+ bool containsElementStereotype = std::any_of(
+ stereotypes.constBegin(), stereotypes.constEnd(),
+ [&](const QString &s) { return m_elementStereotypes.contains(s); });
+ if (!containsElementStereotype) {
+ m_keep = false;
+ return;
+ }
+ }
+ }
+
+ void visitMPackage(const qmt::MPackage *package) override
+ {
+ if (m_elementType == ElementType::Any || m_elementType == ElementType::Package)
+ qmt::MVoidConstVisitor::visitMPackage(package);
+ else
+ m_keep = false;
+ }
+
+ void visitMClass(const qmt::MClass *klass) override
+ {
+ if (m_elementType == ElementType::Any || m_elementType == ElementType::Class)
+ qmt::MVoidConstVisitor::visitMClass(klass);
+ else
+ m_keep = false;
+ }
+
+ void visitMComponent(const qmt::MComponent *component) override
+ {
+ if (m_elementType == ElementType::Any || m_elementType == ElementType::Component)
+ qmt::MVoidConstVisitor::visitMComponent(component);
+ else
+ m_keep = false;
+ }
+
+ void visitMDiagram(const qmt::MDiagram *diagram) override
+ {
+ if (m_elementType == ElementType::Any || m_elementType == ElementType::Diagram)
+ qmt::MVoidConstVisitor::visitMDiagram(diagram);
+ else
+ m_keep = false;
+ }
+
+ void visitMItem(const qmt::MItem *item) override
+ {
+ if (m_elementType == ElementType::Any || m_elementType == ElementType::Item)
+ qmt::MVoidConstVisitor::visitMItem(item);
+ else
+ m_keep = false;
+ }
+
+ void visitMRelation(const qmt::MRelation *relation) override
+ {
+ if (!m_relationStereotypes.isEmpty()) {
+ const QStringList relationStereotypes = relation->stereotypes();
+ bool containsRelationStereotype = std::any_of(
+ relationStereotypes.constBegin(), relationStereotypes.constEnd(),
+ [&](const QString &s) { return m_relationStereotypes.contains(s); });
+ if (!containsRelationStereotype) {
+ m_keep = false;
+ return;
+ }
+ }
+ }
+
+ void visitMDependency(const qmt::MDependency *dependency) override
+ {
+ if (m_relationDirection != RelationDirection::Any) {
+ bool keep = false;
+ if (m_relationDirection == RelationDirection::Outgoing) {
+ if (dependency->direction() == qmt::MDependency::AToB && dependency->endAUid() == m_mobject->uid())
+ keep = true;
+ else if (dependency->direction() == qmt::MDependency::BToA && dependency->endBUid() == m_mobject->uid())
+ keep = true;
+ } else if (m_relationDirection == RelationDirection::Incoming) {
+ if (dependency->direction() == qmt::MDependency::AToB && dependency->endBUid() == m_mobject->uid())
+ keep = true;
+ else if (dependency->direction() == qmt::MDependency::BToA && dependency->endAUid() == m_mobject->uid())
+ keep = true;
+ } else if (m_relationDirection == RelationDirection::Bidirectional) {
+ if (dependency->direction() == qmt::MDependency::Bidirectional)
+ keep = true;
+ }
+ m_keep = keep;
+ if (!keep)
+ return;
+ }
+ if (m_relationType == RelationType::Any || m_relationType == RelationType::Dependency)
+ qmt::MVoidConstVisitor::visitMDependency(dependency);
+ else
+ m_keep = false;
+ }
+
+ bool testDirection(const qmt::MRelation *relation)
+ {
+ if (m_relationDirection != RelationDirection::Any) {
+ bool keep = false;
+ if (m_relationDirection == RelationDirection::Outgoing) {
+ if (relation->endAUid() == m_mobject->uid())
+ keep = true;
+ } else if (m_relationDirection == RelationDirection::Incoming) {
+ if (relation->endBUid() == m_mobject->uid())
+ keep = true;
+ }
+ m_keep = keep;
+ if (!keep)
+ return false;
+ }
+ return true;
+ }
+
+ void visitMInheritance(const qmt::MInheritance *inheritance) override
+ {
+ if (!testDirection(inheritance))
+ return;
+ if (m_relationType == RelationType::Any || m_relationType == RelationType::Inheritance)
+ qmt::MVoidConstVisitor::visitMInheritance(inheritance);
+ else
+ m_keep = false;
+ }
+
+ void visitMAssociation(const qmt::MAssociation *association) override
+ {
+ if (!testDirection(association))
+ return;
+ if (m_relationType == RelationType::Any || m_relationType == RelationType::Association)
+ qmt::MVoidConstVisitor::visitMAssociation(association);
+ else
+ m_keep = false;
+ }
+
+ void visitMConnection(const qmt::MConnection *connection) override
+ {
+ if (!testDirection(connection))
+ return;
+ if (m_relationType == RelationType::Any || m_relationType == RelationType::Connection)
+ qmt::MVoidConstVisitor::visitMConnection(connection);
+ else
+ m_keep = false;
+ }
+
+private:
+ RelationType m_relationType = RelationType::Any;
+ QString m_relationTypeId;
+ RelationDirection m_relationDirection = RelationDirection::Any;
+ QStringList m_relationStereotypes;
+ ElementType m_elementType = ElementType::Any;
+ QStringList m_elementStereotypes;
+ const qmt::DObject *m_dobject = nullptr;
+ const qmt::MObject *m_mobject = nullptr;
+ const qmt::MRelation *m_relation = nullptr;
+ bool m_keep = true;
+};
+} // namespace
+
+class AddRelatedElementsDialog::Private {
+public:
+ qmt::DiagramSceneController *m_diagramSceneController = nullptr;
+ qmt::DSelection m_selection;
+ qmt::Uid m_diagramUid;
+ QStringListModel m_relationTypeModel;
+ QStringListModel m_relationDirectionModel;
+ QStringListModel m_relationStereotypesModel;
+ QStringListModel m_elementTypeModel;
+ QStringListModel m_elementStereotypesModel;
+ Filter m_filter;
+
+ QComboBox *RelationTypeCombobox;
+ QComboBox *DirectionCombobox;
+ QComboBox *StereotypesCombobox;
+ QComboBox *ElementStereotypesCombobox;
+ QComboBox *ElementTypeComboBox;
+ QLabel *NumberOfMatchingElementsValue;
+ QDialogButtonBox *buttonBox;
+};
+
+AddRelatedElementsDialog::AddRelatedElementsDialog(QWidget *parent) :
+ QDialog(parent),
+ d(new Private)
+{
+ setMinimumWidth(500);
+
+ d->RelationTypeCombobox = new QComboBox;
+ d->DirectionCombobox = new QComboBox;
+ d->StereotypesCombobox = new QComboBox;
+ d->StereotypesCombobox->setEditable(true);
+ d->ElementTypeComboBox = new QComboBox;
+ d->ElementStereotypesCombobox = new QComboBox;
+ d->ElementStereotypesCombobox->setEditable(true);
+ d->NumberOfMatchingElementsValue = new QLabel;
+ d->buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
+
+ using namespace Layouting;
+ Column {
+ Group {
+ title(Tr::tr("Relation Attributes")),
+ Form {
+ Tr::tr("Type"), d->RelationTypeCombobox, br,
+ Tr::tr("Direction"), d->DirectionCombobox, br,
+ Tr::tr("Stereotypes"), d->StereotypesCombobox, br,
+ },
+ },
+ Group {
+ title(Tr::tr("Other Element Attributes")),
+ Form {
+ Tr::tr("Type"), d->ElementTypeComboBox, br,
+ Tr::tr("Stereotypes"), d->ElementStereotypesCombobox, br,
+ },
+ },
+ Row {
+ Tr::tr("Number of matching elements: "), d->NumberOfMatchingElementsValue, st,
+ },
+ st,
+ d->buttonBox,
+ }.attachTo(this);
+
+ connect(d->RelationTypeCombobox, &QComboBox::currentIndexChanged, this, &AddRelatedElementsDialog::updateNumberOfElements);
+ connect(d->DirectionCombobox, &QComboBox::currentIndexChanged, this, &AddRelatedElementsDialog::updateNumberOfElements);
+ connect(d->StereotypesCombobox, &QComboBox::currentTextChanged, this, &AddRelatedElementsDialog::updateNumberOfElements);
+ connect(d->ElementTypeComboBox, &QComboBox::currentIndexChanged, this, &AddRelatedElementsDialog::updateNumberOfElements);
+ connect(d->ElementStereotypesCombobox, &QComboBox::currentTextChanged, this, &AddRelatedElementsDialog::updateNumberOfElements);
+ connect(d->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(d->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+ connect(this, &QDialog::accepted, this, &AddRelatedElementsDialog::onAccepted);
+}
+
+AddRelatedElementsDialog::~AddRelatedElementsDialog()
+{
+ delete d;
+}
+
+void AddRelatedElementsDialog::setDiagramSceneController(qmt::DiagramSceneController *diagramSceneController)
+{
+ d->m_diagramSceneController = diagramSceneController;
+}
+
+void AddRelatedElementsDialog::setElements(const qmt::DSelection &selection, qmt::MDiagram *diagram)
+{
+ d->m_selection = selection;
+ d->m_diagramUid = diagram->uid();
+ QStringList relationTypes = {"Any", "Dependency", "Association", "Inheritance"};
+ d->m_relationTypeModel.setStringList(relationTypes);
+ d->RelationTypeCombobox->setModel(&d->m_relationTypeModel);
+ QStringList relationDirections = {"Any", "Outgoing (->)", "Incoming (<-)", "Bidirectional (<->)"};
+ d->m_relationDirectionModel.setStringList(relationDirections);
+ d->DirectionCombobox->setModel(&d->m_relationDirectionModel);
+ QStringList relationStereotypes = { };
+ d->m_relationStereotypesModel.setStringList(relationStereotypes);
+ d->StereotypesCombobox->setModel(&d->m_relationStereotypesModel);
+ QStringList elementTypes = {"Any", "Package", "Component", "Class", "Diagram", "Item"};
+ d->m_elementTypeModel.setStringList(elementTypes);
+ d->ElementTypeComboBox->setModel(&d->m_elementTypeModel);
+ QStringList elementStereotypes = { };
+ d->m_elementStereotypesModel.setStringList(elementStereotypes);
+ d->ElementStereotypesCombobox->setModel(&d->m_elementStereotypesModel);
+ updateNumberOfElements();
+}
+
+void AddRelatedElementsDialog::onAccepted()
+{
+ qmt::MDiagram *diagram = d->m_diagramSceneController->modelController()->findElement<qmt::MDiagram>(d->m_diagramUid);
+ if (diagram) {
+ updateFilter();
+ d->m_diagramSceneController->addRelatedElements(
+ d->m_selection, diagram,
+ [this](qmt::DObject *dobject, qmt::MObject *mobject, qmt::MRelation *relation) -> bool
+ {
+ return this->filter(dobject, mobject, relation);
+ });
+ }
+}
+
+void AddRelatedElementsDialog::updateFilter()
+{
+ d->m_filter.setRelationType((RelationType) d->RelationTypeCombobox->currentIndex());
+ d->m_filter.setRelationDirection((RelationDirection) d->DirectionCombobox->currentIndex());
+ d->m_filter.setRelationStereotypes(d->StereotypesCombobox->currentText().split(',', Qt::SkipEmptyParts));
+ d->m_filter.setElementType((ElementType) d->ElementTypeComboBox->currentIndex());
+ d->m_filter.setElementStereotypes(d->ElementStereotypesCombobox->currentText().split(',', Qt::SkipEmptyParts));
+}
+
+bool AddRelatedElementsDialog::filter(qmt::DObject *dobject, qmt::MObject *mobject, qmt::MRelation *relation)
+{
+ d->m_filter.reset();
+ d->m_filter.setObject(dobject, mobject);
+ d->m_filter.setRelation(relation);
+ relation->accept(&d->m_filter);
+ if (!d->m_filter.keep())
+ return false;
+ qmt::MObject *targetObject = nullptr;
+ if (relation->endAUid() != mobject->uid())
+ targetObject = d->m_diagramSceneController->modelController()->findObject(relation->endAUid());
+ else if (relation->endBUid() != mobject->uid())
+ targetObject = d->m_diagramSceneController->modelController()->findObject(relation->endBUid());
+ if (!targetObject)
+ return false;
+ targetObject->accept(&d->m_filter);
+ return d->m_filter.keep();
+}
+
+void AddRelatedElementsDialog::updateNumberOfElements()
+{
+ qmt::MDiagram *diagram = d->m_diagramSceneController->modelController()->findElement<qmt::MDiagram>(d->m_diagramUid);
+ if (diagram) {
+ updateFilter();
+ d->NumberOfMatchingElementsValue->setText(QString::number(d->m_diagramSceneController->countRelatedElements(
+ d->m_selection, diagram,
+ [this](qmt::DObject *dobject, qmt::MObject *mobject, qmt::MRelation *relation) -> bool
+ {
+ return this->filter(dobject, mobject, relation);
+ })));
+ }
+}
+
+} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/addrelatedelementsdialog.h b/src/libs/modelinglib/qmt/model_widgets_ui/addrelatedelementsdialog.h
new file mode 100644
index 0000000000..e0fefa1c6c
--- /dev/null
+++ b/src/libs/modelinglib/qmt/model_widgets_ui/addrelatedelementsdialog.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2018 Jochen Becher
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "qmt/infrastructure/qmt_global.h"
+
+#include <QDialog>
+
+namespace qmt {
+
+class DSelection;
+class DObject;
+class MObject;
+class MDiagram;
+class MRelation;
+class DiagramSceneController;
+}
+
+namespace qmt {
+
+class QMT_EXPORT AddRelatedElementsDialog : public QDialog
+{
+ Q_OBJECT
+ class Private;
+
+public:
+ explicit AddRelatedElementsDialog(QWidget *parent = nullptr);
+ ~AddRelatedElementsDialog();
+
+ void setDiagramSceneController(qmt::DiagramSceneController *diagramSceneController);
+ void setElements(const qmt::DSelection &selection, qmt::MDiagram *diagram);
+
+private:
+ void onAccepted();
+ void updateFilter();
+ bool filter(qmt::DObject *dobject, qmt::MObject *mobject, qmt::MRelation *relation);
+ void updateNumberOfElements();
+
+ Private *d = nullptr;
+};
+
+} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.cpp b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.cpp
new file mode 100644
index 0000000000..f02b466830
--- /dev/null
+++ b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.cpp
@@ -0,0 +1,173 @@
+// Copyright (C) 2018 Jochen Becher
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "modeltreefilter.h"
+
+#include "../../modelinglibtr.h"
+
+#include <utils/layoutbuilder.h>
+
+#include <QCheckBox>
+#include <QComboBox>
+#include <QFrame>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QStringListModel>
+
+namespace qmt {
+
+class ModelTreeFilter::Private {
+public:
+ QStringListModel m_typeModel;
+ QStringListModel m_stereotypesModel;
+ QStringListModel m_directionModel;
+
+ QPushButton *resetViewButton;
+ QCheckBox *relationsCheckBox;
+ QCheckBox *diagramElementsCheckBox;
+ QPushButton *clearFilterButton;
+ QComboBox *typeComboBox;
+ QComboBox *stereotypesComboBox;
+ QLineEdit *nameLineEdit;
+ QComboBox *directionComboBox;
+};
+
+ModelTreeFilter::ModelTreeFilter(QWidget *parent) :
+ QWidget(parent),
+ d(new Private)
+{
+ d->resetViewButton = new QPushButton(Tr::tr("Reset"));
+ d->relationsCheckBox = new QCheckBox(Tr::tr("Relations"));
+ d->diagramElementsCheckBox = new QCheckBox(Tr::tr("Diagram Elements"));
+ d->diagramElementsCheckBox->setMaximumHeight(0);
+ d->clearFilterButton = new QPushButton(Tr::tr("Clear"));
+ d->typeComboBox = new QComboBox;
+ d->stereotypesComboBox = new QComboBox;
+ d->stereotypesComboBox->setEditable(true);
+ d->nameLineEdit = new QLineEdit;
+ d->directionComboBox = new QComboBox;
+
+ const auto boldLabel = [] (const QString &title) -> QLabel * {
+ auto label = new QLabel(title);
+ QFont boldFont = label->font();
+ boldFont.setWeight(QFont::Bold);
+ label->setFont(boldFont);
+ return label;
+ };
+
+ const auto line = [] () -> QFrame * {
+ auto line = new QFrame;
+ line->setFrameShadow(QFrame::Plain);
+ line->setFrameShape(QFrame::HLine);
+ return line;
+ };
+
+ const int margin = 9;
+
+ using namespace Layouting;
+ Column {
+ Column {
+ Row {
+ boldLabel(Tr::tr("View")), st, d->resetViewButton
+ },
+ d->relationsCheckBox,
+ d->diagramElementsCheckBox,
+ customMargins(margin, 0, margin, 0),
+ },
+ Space(10),
+ line(),
+ Column {
+ Row {
+ boldLabel(Tr::tr("Filter")), st, d->clearFilterButton
+ },
+ Space(10),
+ Form {
+ Tr::tr("Type:"), d->typeComboBox, br,
+ Tr::tr("Stereotypes:"), d->stereotypesComboBox, br,
+ Tr::tr("Name:"), d->nameLineEdit, br,
+ Tr::tr("Direction:"), d->directionComboBox, br,
+ },
+ customMargins(margin, 0, margin, 0),
+ },
+ st,
+ line(),
+ customMargins(0, margin, 0, 0),
+ }.attachTo(this);
+
+ connect(d->resetViewButton, &QPushButton::clicked, this, &ModelTreeFilter::resetView);
+ connect(d->relationsCheckBox, &QCheckBox::clicked, this, &ModelTreeFilter::onViewChanged);
+ connect(d->diagramElementsCheckBox, &QCheckBox::clicked, this, &ModelTreeFilter::onViewChanged);
+ connect(d->clearFilterButton, &QPushButton::clicked, this, &ModelTreeFilter::clearFilter);
+ connect(d->typeComboBox, &QComboBox::currentIndexChanged, this, [this](int index) {
+ d->directionComboBox->setDisabled(index != (int) ModelTreeFilterData::Type::Dependency);
+ });
+ connect(d->typeComboBox, &QComboBox::currentIndexChanged, this, &ModelTreeFilter::onFilterChanged);
+ connect(d->stereotypesComboBox, &QComboBox::currentTextChanged, this, &ModelTreeFilter::onFilterChanged);
+ connect(d->nameLineEdit, &QLineEdit::textChanged, this, &ModelTreeFilter::onFilterChanged);
+ connect(d->directionComboBox, &QComboBox::currentTextChanged, this, &ModelTreeFilter::onFilterChanged);
+ setupFilter();
+ resetView();
+ clearFilter();
+}
+
+ModelTreeFilter::~ModelTreeFilter()
+{
+ delete d;
+}
+
+void ModelTreeFilter::setupFilter()
+{
+ QStringList types = {"Any", "Package", "Component", "Class", "Diagram", "Item",
+ "Dependency", "Association", "Inheritance", "Connection"};
+ d->m_typeModel.setStringList(types);
+ d->typeComboBox->setModel(&d->m_typeModel);
+
+ QStringList stereotypes = { };
+ d->m_stereotypesModel.setStringList(stereotypes);
+ d->stereotypesComboBox->setModel(&d->m_stereotypesModel);
+
+ QStringList directions = {"Any", "Outgoing (->)", "Incoming (<-)", "Bidirectional (<->)"};
+ d->m_directionModel.setStringList(directions);
+ d->directionComboBox->setModel(&d->m_directionModel);
+}
+
+void ModelTreeFilter::resetView()
+{
+ d->relationsCheckBox->setChecked(true);
+ d->diagramElementsCheckBox->setChecked(false);
+ onViewChanged();
+}
+
+void ModelTreeFilter::clearFilter()
+{
+ d->typeComboBox->setCurrentIndex(0);
+ d->stereotypesComboBox->clearEditText();
+ d->nameLineEdit->clear();
+ d->directionComboBox->setCurrentIndex(0);
+ onFilterChanged();
+}
+
+void ModelTreeFilter::onViewChanged()
+{
+ ModelTreeViewData modelTreeViewData;
+ modelTreeViewData.setShowRelations(d->relationsCheckBox->isChecked());
+ modelTreeViewData.setShowDiagramElements(d->diagramElementsCheckBox->isChecked());
+ emit viewDataChanged(modelTreeViewData);
+}
+
+void ModelTreeFilter::onFilterChanged()
+{
+ ModelTreeFilterData modelTreeFilterData;
+ modelTreeFilterData.setType((ModelTreeFilterData::Type) d->typeComboBox->currentIndex());
+ modelTreeFilterData.setCustomId(QString());
+ modelTreeFilterData.setStereotypes(d->stereotypesComboBox->currentText().split(',', Qt::SkipEmptyParts));
+ modelTreeFilterData.setName(d->nameLineEdit->text().trimmed());
+ if (modelTreeFilterData.type() == ModelTreeFilterData::Type::Dependency)
+ modelTreeFilterData.setDirection((ModelTreeFilterData::Direction) d->directionComboBox->currentIndex());
+ else
+ modelTreeFilterData.setDirection(ModelTreeFilterData::Direction::Any);
+ emit filterDataChanged(modelTreeFilterData);
+}
+
+} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.h b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.h
new file mode 100644
index 0000000000..1de7237c6e
--- /dev/null
+++ b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreefilter.h
@@ -0,0 +1,39 @@
+// Copyright (C) 2018 Jochen Becher
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "qmt/infrastructure/qmt_global.h"
+#include "qmt/model_ui/modeltreefilterdata.h"
+
+#include <QWidget>
+
+namespace qmt {
+
+class QMT_EXPORT ModelTreeFilter : public QWidget
+{
+ Q_OBJECT
+ class Private;
+
+public:
+ explicit ModelTreeFilter(QWidget *parent = nullptr);
+ ~ModelTreeFilter();
+
+signals:
+ void viewDataChanged(const ModelTreeViewData &modelTreeViewData);
+ void filterDataChanged(const ModelTreeFilterData &modelTreeFilterData);
+
+private:
+
+ void setupFilter();
+ void resetView();
+ void clearFilter();
+ void onViewChanged();
+ void onFilterChanged();
+
+private:
+ Private *d = nullptr;
+};
+
+
+} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/modeltreeview.cpp b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreeview.cpp
index 68c22ce6df..e2006a37b5 100644
--- a/src/libs/modelinglib/qmt/model_widgets_ui/modeltreeview.cpp
+++ b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreeview.cpp
@@ -71,6 +71,46 @@ void ModelTreeView::setElementTasks(IElementTasks *elementTasks)
m_elementTasks = elementTasks;
}
+static void StoreStatus(QTreeView *view, QSortFilterProxyModel *model, const QModelIndex &parent, QVector<QModelIndex> &expanded_items)
+{
+ for (int index = 0; index < model->rowCount(parent); ++index) {
+ auto proxy_index = model->index(index, 0, parent);
+ if (view->isExpanded(proxy_index)) {
+ auto source_index = model->mapToSource(proxy_index);
+ expanded_items.append(source_index);
+ }
+ StoreStatus(view, model, proxy_index, expanded_items);
+ }
+}
+
+static void ApplyStatus(QTreeView *view, QSortFilterProxyModel *model, QVector<QModelIndex> &expanded_items)
+{
+ for (auto source_index : expanded_items) {
+ auto proxy_index = model->mapFromSource(source_index);
+ view->setExpanded(proxy_index, true);
+ }
+}
+
+void ModelTreeView::setModelTreeViewData(const ModelTreeViewData &viewData)
+{
+ if (m_sortedTreeModel) {
+ QVector<QModelIndex> expanded_items;
+ StoreStatus(this, m_sortedTreeModel, QModelIndex(), expanded_items);
+ m_sortedTreeModel->setModelTreeViewData(viewData);
+ ApplyStatus(this, m_sortedTreeModel, expanded_items);
+ }
+}
+
+void ModelTreeView::setModelTreeFilterData(const ModelTreeFilterData &filterData)
+{
+ if (m_sortedTreeModel) {
+ QVector<QModelIndex> expanded_items;
+ StoreStatus(this, m_sortedTreeModel, QModelIndex(), expanded_items);
+ m_sortedTreeModel->setModelTreeFilterData(filterData);
+ ApplyStatus(this, m_sortedTreeModel, expanded_items);
+ }
+}
+
QModelIndex ModelTreeView::mapToSourceModelIndex(const QModelIndex &index) const
{
return m_sortedTreeModel->mapToSource(index);
@@ -144,7 +184,7 @@ void ModelTreeView::dragMoveEvent(QDragMoveEvent *event)
{
QTreeView::dragMoveEvent(event);
bool accept = false;
- QModelIndex dropIndex = indexAt(event->pos());
+ QModelIndex dropIndex = indexAt(event->position().toPoint());
QModelIndex dropSourceModelIndex = m_sortedTreeModel->mapToSource(dropIndex);
if (dropSourceModelIndex.isValid()) {
TreeModel *treeModel = m_sortedTreeModel->treeModel();
@@ -175,7 +215,7 @@ void ModelTreeView::dropEvent(QDropEvent *event)
bool accept = false;
event->setDropAction(Qt::MoveAction);
if (event->mimeData()->hasFormat("text/model-elements")) {
- QModelIndex dropIndex = indexAt(event->pos());
+ QModelIndex dropIndex = indexAt(event->position().toPoint());
QModelIndex dropSourceModelIndex = m_sortedTreeModel->mapToSource(dropIndex);
if (dropSourceModelIndex.isValid()) {
TreeModel *treeModel = m_sortedTreeModel->treeModel();
@@ -237,6 +277,10 @@ void ModelTreeView::contextMenuEvent(QContextMenuEvent *event)
menu.addAction(new ContextMenuAction(Tr::tr("Open Diagram"), "openDiagram", &menu));
addSeparator = true;
}
+ if (m_elementTasks->hasLinkedFile(melement)) {
+ menu.addAction(new ContextMenuAction(Tr::tr("Open Linked File"), "openLinkedFile", &menu));
+ addSeparator = true;
+ }
if (melement->owner()) {
if (addSeparator)
menu.addSeparator();
@@ -253,6 +297,8 @@ void ModelTreeView::contextMenuEvent(QContextMenuEvent *event)
m_elementTasks->openClassDefinition(melement);
} else if (action->id() == "openDiagram") {
m_elementTasks->openDiagram(melement);
+ } else if (action->id() == "openLinkedFile") {
+ m_elementTasks->openLinkedFile(melement);
} else if (action->id() == "delete") {
MSelection selection;
selection.append(melement->uid(), melement->owner()->uid());
diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/modeltreeview.h b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreeview.h
index 0c07cb0372..e5537644a3 100644
--- a/src/libs/modelinglib/qmt/model_widgets_ui/modeltreeview.h
+++ b/src/libs/modelinglib/qmt/model_widgets_ui/modeltreeview.h
@@ -5,6 +5,7 @@
#include "qmt/infrastructure/qmt_global.h"
#include "qmt/model_ui/modeltreeviewinterface.h"
+#include "qmt/model_ui/modeltreefilterdata.h"
#include <QElapsedTimer>
#include <QTreeView>
@@ -32,6 +33,9 @@ public:
void setTreeModel(SortedTreeModel *model);
void setElementTasks(IElementTasks *elementTasks);
+ void setModelTreeViewData(const ModelTreeViewData &viewData);
+ void setModelTreeFilterData(const ModelTreeFilterData &filterData);
+
QModelIndex mapToSourceModelIndex(const QModelIndex &index) const;
void selectFromSourceModelIndex(const QModelIndex &index);
diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.cpp b/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.cpp
index 969313ca33..11df24188d 100644
--- a/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.cpp
+++ b/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.cpp
@@ -43,6 +43,7 @@
#include "qmt/diagram_scene/items/stereotypedisplayvisitor.h"
#include "qmt/stereotype/stereotypecontroller.h"
#include "qmt/stereotype/customrelation.h"
+#include "qmt/style/relationvisuals.h"
#include "qmt/style/stylecontroller.h"
#include "qmt/style/style.h"
#include "qmt/style/objectvisuals.h"
@@ -237,6 +238,61 @@ static DClass::TemplateDisplay translateIndexToTemplateDisplay(int index)
return map[index];
}
+static int translateRelationVisualPrimaryRoleToIndex(DRelation::VisualPrimaryRole visualRole)
+{
+ switch (visualRole) {
+ case DRelation::PrimaryRoleNormal:
+ return 0;
+ case DRelation::PrimaryRoleCustom1:
+ return 1;
+ case DRelation::PrimaryRoleCustom2:
+ return 2;
+ case DRelation::PrimaryRoleCustom3:
+ return 3;
+ case DRelation::PrimaryRoleCustom4:
+ return 4;
+ case DRelation::PrimaryRoleCustom5:
+ return 5;
+ }
+ return 0;
+}
+
+static DRelation::VisualPrimaryRole translateIndexToRelationVisualPrimaryRole(int index)
+{
+ static const DRelation::VisualPrimaryRole map[] = {
+ DRelation::PrimaryRoleNormal,
+ DRelation::PrimaryRoleCustom1, DRelation::PrimaryRoleCustom2, DRelation::PrimaryRoleCustom3,
+ DRelation::PrimaryRoleCustom4, DRelation::PrimaryRoleCustom5
+ };
+ QMT_ASSERT(index >= 0 && index <= 5, return DRelation::PrimaryRoleNormal);
+ return map[index];
+}
+
+static int translateRelationVisualSecondaryRoleToIndex(DRelation::VisualSecondaryRole visualRole)
+{
+ switch (visualRole) {
+ case DRelation::SecondaryRoleNone:
+ return 0;
+ case DRelation::SecondaryRoleWarning:
+ return 1;
+ case DRelation::SecondaryRoleError:
+ return 2;
+ case DRelation::SecondaryRoleSoften:
+ return 3;
+ }
+ return 0;
+}
+
+static DRelation::VisualSecondaryRole translateIndexToRelationVisualSecondaryRole(int index)
+{
+ static const DRelation::VisualSecondaryRole map[] = {
+ DRelation::SecondaryRoleNone,
+ DRelation::SecondaryRoleWarning, DRelation::SecondaryRoleError, DRelation::SecondaryRoleSoften
+ };
+ QMT_ASSERT(index >= 0 && index <= 5, return DRelation::SecondaryRoleNone);
+ return map[index];
+}
+
static int translateAnnotationVisualRoleToIndex(DAnnotation::VisualRole visualRole)
{
switch (visualRole) {
@@ -398,6 +454,7 @@ void PropertiesView::MView::visitMPackage(const MPackage *package)
else
setTitle<MPackage>(m_modelElements, Tr::tr("Package"), Tr::tr("Packages"));
visitMObject(package);
+ visitMObjectBehind(package);
}
void PropertiesView::MView::visitMClass(const MClass *klass)
@@ -472,12 +529,14 @@ void PropertiesView::MView::visitMClass(const MClass *klass)
}
if (m_classMembersEdit->isEnabled() != isSingleSelection)
m_classMembersEdit->setEnabled(isSingleSelection);
+ visitMObjectBehind(klass);
}
void PropertiesView::MView::visitMComponent(const MComponent *component)
{
setTitle<MComponent>(m_modelElements, Tr::tr("Component"), Tr::tr("Components"));
visitMObject(component);
+ visitMObjectBehind(component);
}
void PropertiesView::MView::visitMDiagram(const MDiagram *diagram)
@@ -497,6 +556,7 @@ void PropertiesView::MView::visitMCanvasDiagram(const MCanvasDiagram *diagram)
{
setTitle<MCanvasDiagram>(m_modelElements, Tr::tr("Canvas Diagram"), Tr::tr("Canvas Diagrams"));
visitMDiagram(diagram);
+ visitMDiagramBehind(diagram);
}
void PropertiesView::MView::visitMItem(const MItem *item)
@@ -521,6 +581,7 @@ void PropertiesView::MView::visitMItem(const MItem *item)
if (m_itemVarietyEdit->isEnabled() != isSingleSelection)
m_itemVarietyEdit->setEnabled(isSingleSelection);
}
+ visitMObjectBehind(item);
}
void PropertiesView::MView::visitMRelation(const MRelation *relation)
@@ -850,6 +911,7 @@ void PropertiesView::MView::visitDElement(const DElement *element)
void PropertiesView::MView::visitDObject(const DObject *object)
{
visitDElement(object);
+ visitDObjectBefore(object);
#ifdef SHOW_DEBUG_PROPERTIES
if (!m_posRectLabel) {
m_posRectLabel = new QLabel(m_topWidget);
@@ -1057,6 +1119,52 @@ void PropertiesView::MView::visitDItem(const DItem *item)
void PropertiesView::MView::visitDRelation(const DRelation *relation)
{
visitDElement(relation);
+ if (!m_relationVisualPrimaryRoleSelector) {
+ m_relationVisualPrimaryRoleSelector = new PaletteBox(m_topWidget);
+ setRelationPrimaryRolePalette(m_styleElementType, DRelation::PrimaryRoleNormal);
+ setRelationPrimaryRolePalette(m_styleElementType, DRelation::PrimaryRoleCustom1);
+ setRelationPrimaryRolePalette(m_styleElementType, DRelation::PrimaryRoleCustom2);
+ setRelationPrimaryRolePalette(m_styleElementType, DRelation::PrimaryRoleCustom3);
+ setRelationPrimaryRolePalette(m_styleElementType, DRelation::PrimaryRoleCustom4);
+ setRelationPrimaryRolePalette(m_styleElementType, DRelation::PrimaryRoleCustom5);
+ addRow(Tr::tr("Color:"), m_relationVisualPrimaryRoleSelector, "color");
+ connect(m_relationVisualPrimaryRoleSelector, &PaletteBox::activated,
+ this, &PropertiesView::MView::onRelationVisualPrimaryRoleChanged);
+ }
+ if (!m_relationVisualPrimaryRoleSelector->hasFocus()) {
+ DRelation::VisualPrimaryRole visualPrimaryRole;
+ if (haveSameValue(m_diagramElements, &DRelation::visualPrimaryRole, &visualPrimaryRole))
+ m_relationVisualPrimaryRoleSelector->setCurrentIndex(translateRelationVisualPrimaryRoleToIndex(visualPrimaryRole));
+ else
+ m_relationVisualPrimaryRoleSelector->setCurrentIndex(-1);
+ }
+ if (!m_relationVisualSecondaryRoleSelector) {
+ m_relationVisualSecondaryRoleSelector = new QComboBox(m_topWidget);
+ m_relationVisualSecondaryRoleSelector->addItems({ Tr::tr("Normal"), Tr::tr("Warning"), Tr::tr("Error"), Tr::tr("Soften") });
+ addRow(Tr::tr("Role:"), m_relationVisualSecondaryRoleSelector, "role");
+ connect(m_relationVisualSecondaryRoleSelector, QOverload<int>::of(&QComboBox::activated),
+ this, &PropertiesView::MView::onRelationVisualSecondaryRoleChanged);
+ }
+ if (!m_relationVisualSecondaryRoleSelector->hasFocus()) {
+ DRelation::VisualSecondaryRole visualSecondaryRole;
+ if (haveSameValue(m_diagramElements, &DRelation::visualSecondaryRole, &visualSecondaryRole))
+ m_relationVisualSecondaryRoleSelector->setCurrentIndex(translateRelationVisualSecondaryRoleToIndex(visualSecondaryRole));
+ else
+ m_relationVisualSecondaryRoleSelector->setCurrentIndex(-1);
+ }
+ if (!m_relationVisualEmphasizedCheckbox) {
+ m_relationVisualEmphasizedCheckbox = new QCheckBox(Tr::tr("Emphasized"), m_topWidget);
+ addRow(QString(), m_relationVisualEmphasizedCheckbox, "emphasized");
+ connect(m_relationVisualEmphasizedCheckbox, &QAbstractButton::clicked,
+ this, &PropertiesView::MView::onRelationVisualEmphasizedChanged);
+ }
+ if (!m_relationVisualEmphasizedCheckbox->hasFocus()) {
+ bool emphasized;
+ if (haveSameValue(m_diagramElements, &DRelation::isVisualEmphasized, &emphasized))
+ m_relationVisualEmphasizedCheckbox->setChecked(emphasized);
+ else
+ m_relationVisualEmphasizedCheckbox->setChecked(false);
+ }
#ifdef SHOW_DEBUG_PROPERTIES
if (!m_pointsLabel) {
m_pointsLabel = new QLabel(m_topWidget);
@@ -1145,6 +1253,26 @@ void PropertiesView::MView::visitDSwimlane(const DSwimlane *swimlane)
visitDElement(swimlane);
}
+void PropertiesView::MView::visitMElementBehind(const MElement *element)
+{
+ Q_UNUSED(element)
+}
+
+void PropertiesView::MView::visitMObjectBehind(const MObject *object)
+{
+ visitMElementBehind(object);
+}
+
+void PropertiesView::MView::visitMDiagramBehind(const MDiagram *diagram)
+{
+ visitMObjectBehind(diagram);
+}
+
+void PropertiesView::MView::visitDObjectBefore(const DObject *object)
+{
+ Q_UNUSED(object);
+}
+
void PropertiesView::MView::onStereotypesChanged(const QString &stereotypes)
{
QList<QString> set = m_stereotypesController->fromString(stereotypes);
@@ -1392,7 +1520,42 @@ void PropertiesView::MView::onAnnotationVisualRoleChanged(int visualRoleIndex)
{
DAnnotation::VisualRole visualRole = translateIndexToAnnotationVisualRole((visualRoleIndex));
assignModelElement<DAnnotation, DAnnotation::VisualRole>(
- m_diagramElements, SelectionMulti, visualRole, &DAnnotation::visualRole, &DAnnotation::setVisualRole);
+ m_diagramElements, SelectionMulti, visualRole, &DAnnotation::visualRole, &DAnnotation::setVisualRole);
+}
+
+
+void PropertiesView::MView::onRelationVisualPrimaryRoleChanged(int visualRoleIndex)
+{
+ DRelation::VisualPrimaryRole visualRole = translateIndexToRelationVisualPrimaryRole(visualRoleIndex);
+ assignModelElement<DRelation, DRelation::VisualPrimaryRole>(
+ m_diagramElements, SelectionMulti, visualRole,
+ &DRelation::visualPrimaryRole, &DRelation::setVisualPrimaryRole);
+}
+
+void PropertiesView::MView::onRelationVisualSecondaryRoleChanged(int visualRoleIndex)
+{
+ DRelation::VisualSecondaryRole visualRole = translateIndexToRelationVisualSecondaryRole(visualRoleIndex);
+ assignModelElement<DRelation, DRelation::VisualSecondaryRole>(
+ m_diagramElements, SelectionMulti, visualRole,
+ &DRelation::visualSecondaryRole, &DRelation::setVisualSecondaryRole);
+}
+
+void PropertiesView::MView::onRelationVisualEmphasizedChanged(bool visualEmphasized)
+{
+ assignModelElement<DRelation, bool>(m_diagramElements, SelectionMulti, visualEmphasized,
+ &DRelation::isVisualEmphasized, &DRelation::setVisualEmphasized);
+}
+
+void PropertiesView::MView::onRelationColorChanged(const QColor &color)
+{
+ assignModelElement<DRelation, QColor>(m_diagramElements, SelectionMulti, color,
+ &DRelation::color, &DRelation::setColor);
+}
+
+void PropertiesView::MView::onRelationThicknessChanged(qreal thickness)
+{
+ assignModelElement<DRelation, qreal>(m_diagramElements, SelectionMulti, thickness,
+ &DRelation::thickness, &DRelation::setThickness);
}
void PropertiesView::MView::prepare()
@@ -1553,7 +1716,8 @@ void PropertiesView::MView::setPrimaryRolePalette(StyleEngine::ElementType eleme
DObject::VisualPrimaryRole visualPrimaryRole, const QColor &baseColor)
{
int index = translateVisualPrimaryRoleToIndex(visualPrimaryRole);
- const Style *style = m_propertiesView->styleController()->adaptObjectStyle(elementType, ObjectVisuals(visualPrimaryRole, DObject::SecondaryRoleNone, false, baseColor, 0));
+ const Style *style = m_propertiesView->styleController()->adaptObjectStyle(
+ elementType, ObjectVisuals(visualPrimaryRole, DObject::SecondaryRoleNone, false, baseColor, 0));
m_visualPrimaryRoleSelector->setBrush(index, style->fillBrush());
m_visualPrimaryRoleSelector->setLinePen(index, style->linePen());
}
@@ -1595,112 +1759,15 @@ QString PropertiesView::MView::formatTemplateParameters(const QList<QString> &te
return templateParamters;
}
-template<class T, class V>
-QList<T *> PropertiesView::MView::filter(const QList<V *> &elements)
-{
- QList<T *> filtered;
- for (V *element : elements) {
- auto t = dynamic_cast<T *>(element);
- if (t)
- filtered.append(t);
- }
- return filtered;
-}
-
-template<class T, class V, class BASE>
-bool PropertiesView::MView::haveSameValue(const QList<BASE *> &baseElements, V (T::*getter)() const, V *value)
+void PropertiesView::MView::setRelationPrimaryRolePalette(StyleEngine::ElementType elementType,
+ DRelation::VisualPrimaryRole visualPrimaryRole)
{
- const QList<T *> elements = filter<T>(baseElements);
- QMT_CHECK(!elements.isEmpty());
- V candidate = V(); // avoid warning of reading uninitialized variable
- bool haveCandidate = false;
- for (T *element : elements) {
- if (!haveCandidate) {
- candidate = ((*element).*getter)();
- haveCandidate = true;
- } else {
- if (candidate != ((*element).*getter)())
- return false;
- }
- }
- QMT_CHECK(haveCandidate);
- if (!haveCandidate)
- return false;
- if (value)
- *value = candidate;
- return true;
-}
-
-template<class T, class V, class BASE>
-void PropertiesView::MView::assignModelElement(const QList<BASE *> &baseElements, SelectionType selectionType,
- const V &value, V (T::*getter)() const, void (T::*setter)(const V &))
-{
- const QList<T *> elements = filter<T>(baseElements);
- if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) {
- for (T *element : elements) {
- if (value != ((*element).*getter)()) {
- m_propertiesView->beginUpdate(element);
- ((*element).*setter)(value);
- m_propertiesView->endUpdate(element, false);
- }
- }
- }
-}
-
-template<class T, class V, class BASE>
-void PropertiesView::MView::assignModelElement(const QList<BASE *> &baseElements, SelectionType selectionType,
- const V &value, V (T::*getter)() const, void (T::*setter)(V))
-{
- const QList<T *> elements = filter<T>(baseElements);
- if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) {
- for (T *element : elements) {
- if (value != ((*element).*getter)()) {
- m_propertiesView->beginUpdate(element);
- ((*element).*setter)(value);
- m_propertiesView->endUpdate(element, false);
- }
- }
- }
-}
-
-template<class T, class E, class V, class BASE>
-void PropertiesView::MView::assignEmbeddedModelElement(const QList<BASE *> &baseElements, SelectionType selectionType,
- const V &value, E (T::*getter)() const,
- void (T::*setter)(const E &),
- V (E::*vGetter)() const, void (E::*vSetter)(const V &))
-{
- const QList<T *> elements = filter<T>(baseElements);
- if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) {
- for (T *element : elements) {
- E embedded = ((*element).*getter)();
- if (value != (embedded.*vGetter)()) {
- m_propertiesView->beginUpdate(element);
- (embedded.*vSetter)(value);
- ((*element).*setter)(embedded);
- m_propertiesView->endUpdate(element, false);
- }
- }
- }
-}
-
-template<class T, class E, class V, class BASE>
-void PropertiesView::MView::assignEmbeddedModelElement(const QList<BASE *> &baseElements, SelectionType selectionType,
- const V &value, E (T::*getter)() const,
- void (T::*setter)(const E &),
- V (E::*vGetter)() const, void (E::*vSetter)(V))
-{
- const QList<T *> elements = filter<T>(baseElements);
- if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) {
- for (T *element : elements) {
- E embedded = ((*element).*getter)();
- if (value != (embedded.*vGetter)()) {
- m_propertiesView->beginUpdate(element);
- (embedded.*vSetter)(value);
- ((*element).*setter)(embedded);
- m_propertiesView->endUpdate(element, false);
- }
- }
- }
+ int index = translateRelationVisualPrimaryRoleToIndex(visualPrimaryRole);
+ const Style *style = m_propertiesView->styleController()->adaptRelationStyle(
+ elementType, RelationVisuals(DObject::PrimaryRoleNormal, visualPrimaryRole,
+ DRelation::SecondaryRoleNone, false));
+ m_relationVisualPrimaryRoleSelector->setBrush(index, style->fillBrush());
+ m_relationVisualPrimaryRoleSelector->setLinePen(index, style->linePen());
}
} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.h b/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.h
index 4762803a20..8fa9f0d137 100644
--- a/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.h
+++ b/src/libs/modelinglib/qmt/model_widgets_ui/propertiesviewmview.h
@@ -8,6 +8,8 @@
#include "qmt/model/mconstvisitor.h"
#include "qmt/diagram/dconstvisitor.h"
#include "qmt/diagram/dobject.h"
+#include "qmt/diagram/drelation.h"
+#include "qmt/infrastructure/qmtassert.h"
#include "qmt/stereotype/stereotypeicon.h"
#include "qmt/style/styleengine.h"
@@ -76,6 +78,11 @@ public:
void edit();
protected:
+ virtual void visitMElementBehind(const MElement *element);
+ virtual void visitMObjectBehind(const MObject *object);
+ virtual void visitMDiagramBehind(const MDiagram *diagram);
+ virtual void visitDObjectBefore(const DObject *object);
+
void onStereotypesChanged(const QString &stereotypes);
void onObjectNameChanged(const QString &name);
void onNamespaceChanged(const QString &umlNamespace);
@@ -110,6 +117,11 @@ protected:
void onPlainShapeChanged(bool plainShape);
void onItemShapeChanged(const QString &shape);
void onAutoWidthChanged(bool autoWidthed);
+ void onRelationVisualPrimaryRoleChanged(int visualRoleIndex);
+ void onRelationVisualSecondaryRoleChanged(int visualRoleIndex);
+ void onRelationVisualEmphasizedChanged(bool visualEmphasized);
+ void onRelationColorChanged(const QColor &color);
+ void onRelationThicknessChanged(qreal thickness);
void onAnnotationVisualRoleChanged(int visualRoleIndex);
void prepare();
@@ -139,6 +151,9 @@ protected:
QList<QString> splitTemplateParameters(const QString &templateParameters);
QString formatTemplateParameters(const QList<QString> &templateParametersList);
+ void setRelationPrimaryRolePalette(StyleEngine::ElementType elementType,
+ DRelation::VisualPrimaryRole visualPrimaryRole);
+
enum SelectionType {
SelectionSingle,
SelectionMulti
@@ -149,6 +164,9 @@ protected:
template<class T, class V, class BASE>
bool haveSameValue(const QList<BASE *> &baseElements, V (T::*getter)() const, V *value);
template<class T, class V, class BASE>
+ bool isValueChanged(const QList<BASE *> &baseElements, SelectionType selectionType,
+ const V &value, V (T::*getter)() const);
+ template<class T, class V, class BASE>
void assignModelElement(const QList<BASE *> &baseElements, SelectionType selectionType,
const V &value, V (T::*getter)() const, void (T::*setter)(const V &));
template<class T, class V, class BASE>
@@ -232,7 +250,132 @@ protected:
QCheckBox *m_annotationAutoWidthCheckbox = nullptr;
QComboBox *m_annotationVisualRoleSelector = nullptr;
// DRelation
+ PaletteBox *m_relationVisualPrimaryRoleSelector = nullptr;
+ QComboBox *m_relationVisualSecondaryRoleSelector = nullptr;
+ QCheckBox *m_relationVisualEmphasizedCheckbox = nullptr;
QLabel *m_pointsLabel = nullptr;
};
+template<class T, class V>
+QList<T *> PropertiesView::MView::filter(const QList<V *> &elements)
+{
+ QList<T *> filtered;
+ for (auto *element : elements) {
+ auto t = dynamic_cast<T *>(element);
+ if (t)
+ filtered.append(t);
+ }
+ return filtered;
+}
+
+template<class T, class V, class BASE>
+inline bool PropertiesView::MView::haveSameValue(const QList<BASE *> &baseElements, V (T::*getter)() const, V *value)
+{
+ QList<T *> elements = filter<T>(baseElements);
+ QMT_CHECK(!elements.isEmpty());
+ V candidate = V(); // avoid warning of reading uninitialized variable
+ bool haveCandidate = false;
+ for (const auto *element : elements) {
+ if (!haveCandidate) {
+ candidate = ((*element).*getter)();
+ haveCandidate = true;
+ } else {
+ if (candidate != ((*element).*getter)())
+ return false;
+ }
+ }
+ QMT_CHECK(haveCandidate);
+ if (!haveCandidate)
+ return false;
+ if (value)
+ *value = candidate;
+ return true;
+}
+
+template<class T, class V, class BASE>
+inline bool PropertiesView::MView::isValueChanged(const QList<BASE *> &baseElements, SelectionType selectionType,
+ const V &value, V (T::*getter)() const)
+{
+ QList<T *> elements = filter<T>(baseElements);
+ if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) {
+ for (const auto *element : elements) {
+ if (value != ((*element).*getter)())
+ return true;
+ }
+ }
+ return false;
+}
+
+template<class T, class V, class BASE>
+inline void PropertiesView::MView::assignModelElement(const QList<BASE *> &baseElements, SelectionType selectionType,
+ const V &value, V (T::*getter)() const, void (T::*setter)(const V &))
+{
+ QList<T *> elements = filter<T>(baseElements);
+ if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) {
+ for (auto *element : elements) {
+ if (value != ((*element).*getter)()) {
+ m_propertiesView->beginUpdate(element);
+ ((*element).*setter)(value);
+ m_propertiesView->endUpdate(element, false);
+ }
+ }
+ }
+}
+
+template<class T, class V, class BASE>
+inline void PropertiesView::MView::assignModelElement(const QList<BASE *> &baseElements, SelectionType selectionType,
+ const V &value, V (T::*getter)() const, void (T::*setter)(V))
+{
+ QList<T *> elements = filter<T>(baseElements);
+ if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) {
+ for (auto *element : elements) {
+ if (value != ((*element).*getter)()) {
+ m_propertiesView->beginUpdate(element);
+ ((*element).*setter)(value);
+ m_propertiesView->endUpdate(element, false);
+ }
+ }
+ }
+}
+
+template<class T, class E, class V, class BASE>
+inline void PropertiesView::MView::assignEmbeddedModelElement(const QList<BASE *> &baseElements, SelectionType selectionType,
+ const V &value, E (T::*getter)() const,
+ void (T::*setter)(const E &),
+ V (E::*vGetter)() const, void (E::*vSetter)(const V &))
+{
+ QList<T *> elements = filter<T>(baseElements);
+ if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) {
+ for (auto *element : elements) {
+ E embedded = ((*element).*getter)();
+ if (value != (embedded.*vGetter)()) {
+ m_propertiesView->beginUpdate(element);
+ (embedded.*vSetter)(value);
+ ((*element).*setter)(embedded);
+ m_propertiesView->endUpdate(element, false);
+ }
+ }
+ }
+}
+
+template<class T, class E, class V, class BASE>
+inline void PropertiesView::MView::assignEmbeddedModelElement(const QList<BASE *> &baseElements, SelectionType selectionType,
+ const V &value, E (T::*getter)() const,
+ void (T::*setter)(const E &),
+ V (E::*vGetter)() const, void (E::*vSetter)(V))
+{
+ QList<T *> elements = filter<T>(baseElements);
+ if ((selectionType == SelectionSingle && elements.size() == 1) || selectionType == SelectionMulti) {
+ for (auto *element : elements) {
+ E embedded = ((*element).*getter)();
+ if (value != (embedded.*vGetter)()) {
+ m_propertiesView->beginUpdate(element);
+ (embedded.*vSetter)(value);
+ ((*element).*setter)(embedded);
+ m_propertiesView->endUpdate(element, false);
+ }
+ }
+ }
+}
+
} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/project/project.cpp b/src/libs/modelinglib/qmt/project/project.cpp
index 098db684ef..2e08adc6df 100644
--- a/src/libs/modelinglib/qmt/project/project.cpp
+++ b/src/libs/modelinglib/qmt/project/project.cpp
@@ -3,6 +3,8 @@
#include "project.h"
+using Utils::FilePath;
+
namespace qmt {
Project::Project()
@@ -23,7 +25,7 @@ bool Project::hasFileName() const
return !m_fileName.isEmpty();
}
-void Project::setFileName(const QString &fileName)
+void Project::setFileName(const FilePath &fileName)
{
m_fileName = fileName;
}
@@ -33,7 +35,7 @@ void Project::setRootPackage(MPackage *rootPackage)
m_rootPackage = rootPackage;
}
-void Project::setConfigPath(const QString &configPath)
+void Project::setConfigPath(const FilePath &configPath)
{
m_configPath = configPath;
}
diff --git a/src/libs/modelinglib/qmt/project/project.h b/src/libs/modelinglib/qmt/project/project.h
index fab6bb4228..556471e9ed 100644
--- a/src/libs/modelinglib/qmt/project/project.h
+++ b/src/libs/modelinglib/qmt/project/project.h
@@ -5,6 +5,8 @@
#include "qmt/infrastructure/uid.h"
+#include <utils/filepath.h>
+
#include <QString>
namespace qmt {
@@ -20,18 +22,18 @@ public:
Uid uid() const { return m_uid; }
void setUid(const Uid &uid);
bool hasFileName() const;
- QString fileName() const { return m_fileName; }
- void setFileName(const QString &fileName);
+ Utils::FilePath fileName() const { return m_fileName; }
+ void setFileName(const Utils::FilePath &fileName);
MPackage *rootPackage() const { return m_rootPackage; }
void setRootPackage(MPackage *rootPackage);
- QString configPath() const { return m_configPath; }
- void setConfigPath(const QString &configPath);
+ Utils::FilePath configPath() const { return m_configPath; }
+ void setConfigPath(const Utils::FilePath &configPath);
private:
Uid m_uid;
- QString m_fileName;
+ Utils::FilePath m_fileName;
MPackage *m_rootPackage = nullptr;
- QString m_configPath;
+ Utils::FilePath m_configPath;
};
} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/project_controller/projectcontroller.cpp b/src/libs/modelinglib/qmt/project_controller/projectcontroller.cpp
index f7a7e6d2c9..47eb01138e 100644
--- a/src/libs/modelinglib/qmt/project_controller/projectcontroller.cpp
+++ b/src/libs/modelinglib/qmt/project_controller/projectcontroller.cpp
@@ -10,6 +10,8 @@
#include "../../modelinglibtr.h"
+using Utils::FilePath;
+
namespace qmt {
NoFileNameException::NoFileNameException()
@@ -31,7 +33,7 @@ ProjectController::~ProjectController()
{
}
-void ProjectController::newProject(const QString &fileName)
+void ProjectController::newProject(const FilePath &fileName)
{
m_project.reset(new Project());
auto rootPackage = new MPackage();
@@ -43,7 +45,7 @@ void ProjectController::newProject(const QString &fileName)
emit changed();
}
-void ProjectController::setFileName(const QString &fileName)
+void ProjectController::setFileName(const FilePath &fileName)
{
if (fileName != m_project->fileName()) {
m_project->setFileName(fileName);
@@ -82,7 +84,7 @@ void ProjectController::save()
emit changed();
}
-void ProjectController::saveAs(const QString &fileName)
+void ProjectController::saveAs(const FilePath &fileName)
{
setFileName(fileName);
save();
diff --git a/src/libs/modelinglib/qmt/project_controller/projectcontroller.h b/src/libs/modelinglib/qmt/project_controller/projectcontroller.h
index 1992c72448..09cf65074a 100644
--- a/src/libs/modelinglib/qmt/project_controller/projectcontroller.h
+++ b/src/libs/modelinglib/qmt/project_controller/projectcontroller.h
@@ -6,6 +6,8 @@
#include "qmt/infrastructure/exceptions.h"
#include "qmt/infrastructure/qmt_global.h"
+#include <utils/filepath.h>
+
#include <QObject>
#include <QString>
@@ -35,19 +37,19 @@ public:
signals:
void changed();
- void fileNameChanged(const QString &fileName);
+ void fileNameChanged(const Utils::FilePath &fileName);
public:
Project *project() const { return m_project.data(); }
bool isModified() const { return m_isModified; }
- void newProject(const QString &fileName);
- void setFileName(const QString &fileName);
+ void newProject(const Utils::FilePath &fileName);
+ void setFileName(const Utils::FilePath &fileName);
void setModified();
void load();
void save();
- void saveAs(const QString &fileName);
+ void saveAs(const Utils::FilePath &fileName);
private:
QScopedPointer<Project> m_project;
diff --git a/src/libs/modelinglib/qmt/serializer/diagramserializer.cpp b/src/libs/modelinglib/qmt/serializer/diagramserializer.cpp
index 5170d4fdc7..2d68862c32 100644
--- a/src/libs/modelinglib/qmt/serializer/diagramserializer.cpp
+++ b/src/libs/modelinglib/qmt/serializer/diagramserializer.cpp
@@ -29,10 +29,35 @@
#include "qark/qxmlinarchive.h"
#include "qark/serialize.h"
+#include <QBuffer>
+
using namespace qmt;
namespace qark {
+template<class Archive>
+inline void save(Archive &archive, const QImage &image)
+{
+ QByteArray a;
+ QBuffer buffer(&a);
+ buffer.open(QIODevice::WriteOnly);
+ image.save(&buffer, "PNG");
+ // TODO add write(const QByteArray &)
+ archive.write(QString::fromLatin1(a.toBase64()));
+}
+
+template<class Archive>
+inline void load(Archive &archive, QImage &image)
+{
+ QString s;
+ // TODO add read(QByteArray &)
+ archive.read(&s);
+ QByteArray a = QByteArray::fromBase64(s.toLatin1());
+ QBuffer buffer(&a);
+ buffer.open(QIODevice::ReadOnly);
+ image.load(&buffer, "PNG");
+}
+
// DElement
QARK_REGISTER_TYPE_NAME(DElement, "DElement")
@@ -102,7 +127,10 @@ inline void Access<Archive, DObject>::serialize(Archive &archive, DObject &objec
|| attr("visual-role", object, &visualRole, &setVisualRole)
|| attr("visual-role2", object, &DObject::visualSecondaryRole, &DObject::setVisualSecondaryRole)
|| attr("visual-emphasized", object, &DObject::isVisualEmphasized, &DObject::setVisualEmphasized)
+ || attr("linkedfile", object, &DObject::hasLinkedFile, &DObject::setLinkedFile)
|| attr("stereotype-display", object, &DObject::stereotypeDisplay, &DObject::setStereotypeDisplay)
+ || attr("image-path", object, &DObject::imagePath, &DObject::setImagePath)
+ || attr("image", object, &DObject::image, &DObject::setImage)
// depth is not persistent
|| end;
}
@@ -220,6 +248,9 @@ inline void Access<Archive, DRelation>::serialize(Archive &archive, DRelation &r
|| attr("b", relation, &DRelation::endBUid, &DRelation::setEndBUid)
|| attr("name", relation, &DRelation::name, &DRelation::setName)
|| attr("points", relation, &DRelation::intermediatePoints, &DRelation::setIntermediatePoints)
+ || attr("visualPrimaryRole", relation, &DRelation::visualPrimaryRole, &DRelation::setVisualPrimaryRole)
+ || attr("visualSecondaryRole", relation, &DRelation::visualSecondaryRole, &DRelation::setVisualSecondaryRole)
+ || attr("thickness", relation, &DRelation::thickness, &DRelation::setThickness)
|| end;
}
diff --git a/src/libs/modelinglib/qmt/serializer/diagramserializer.h b/src/libs/modelinglib/qmt/serializer/diagramserializer.h
index a571ff94ef..aa5d1adb52 100644
--- a/src/libs/modelinglib/qmt/serializer/diagramserializer.h
+++ b/src/libs/modelinglib/qmt/serializer/diagramserializer.h
@@ -6,6 +6,7 @@
#include "qmt/diagram/dannotation.h"
#include "qmt/diagram/dobject.h"
#include "qmt/diagram/dclass.h"
+#include "qmt/diagram/drelation.h"
#include "qark/serialize_enum.h"
@@ -16,5 +17,7 @@ QARK_SERIALIZE_ENUM(qmt::DObject::VisualSecondaryRole)
QARK_SERIALIZE_ENUM(qmt::DObject::StereotypeDisplay)
QARK_SERIALIZE_ENUM(qmt::DClass::TemplateDisplay)
QARK_SERIALIZE_ENUM(qmt::DAnnotation::VisualRole)
+QARK_SERIALIZE_ENUM(qmt::DRelation::VisualPrimaryRole)
+QARK_SERIALIZE_ENUM(qmt::DRelation::VisualSecondaryRole)
} // namespace qark
diff --git a/src/libs/modelinglib/qmt/serializer/infrastructureserializer.h b/src/libs/modelinglib/qmt/serializer/infrastructureserializer.h
index e67e5af4eb..59103b3fa9 100644
--- a/src/libs/modelinglib/qmt/serializer/infrastructureserializer.h
+++ b/src/libs/modelinglib/qmt/serializer/infrastructureserializer.h
@@ -3,6 +3,8 @@
#pragma once
+#include <utils/filepath.h>
+
#include "qmt/infrastructure/handle.h"
#include "qmt/infrastructure/handles.h"
#include "qmt/infrastructure/uid.h"
@@ -51,4 +53,20 @@ inline void serialize(Archive &archive, qmt::Handles<T> &handles)
|| end;
}
+// Utils::FilePath
+
+template<class Archive>
+inline void save(Archive &archive, const Utils::FilePath &filePath)
+{
+ archive.write(filePath.toFSPathString());
+}
+
+template<class Archive>
+inline void load(Archive &archive, Utils::FilePath &filePath)
+{
+ QString s;
+ archive.read(&s);
+ filePath = Utils::FilePath::fromUserInput(s);
+}
+
} // namespace qark
diff --git a/src/libs/modelinglib/qmt/serializer/modelserializer.cpp b/src/libs/modelinglib/qmt/serializer/modelserializer.cpp
index 115eba08d0..935a825ab2 100644
--- a/src/libs/modelinglib/qmt/serializer/modelserializer.cpp
+++ b/src/libs/modelinglib/qmt/serializer/modelserializer.cpp
@@ -95,6 +95,7 @@ inline void Access<Archive, MObject>::serialize(Archive &archive, MObject &objec
archive || tag(object)
|| base<MElement>(object)
|| attr("name", object, &MObject::name, &MObject::setName)
+ || attr("linkedfilename", object, &MObject::linkedFileName, &MObject::setLinkedFileName)
|| attr("children", object, &MObject::children, &MObject::setChildren)
|| attr("relations", object, &MObject::relations, &MObject::setRelations)
|| end;
diff --git a/src/libs/modelinglib/qmt/serializer/projectserializer.cpp b/src/libs/modelinglib/qmt/serializer/projectserializer.cpp
index 77f231b358..832d47309b 100644
--- a/src/libs/modelinglib/qmt/serializer/projectserializer.cpp
+++ b/src/libs/modelinglib/qmt/serializer/projectserializer.cpp
@@ -20,6 +20,8 @@
#include <QFile>
+using Utils::FilePath;
+
namespace qark {
using namespace qmt;
@@ -48,11 +50,11 @@ ProjectSerializer::~ProjectSerializer()
{
}
-void ProjectSerializer::save(const QString &fileName, const Project *project)
+void ProjectSerializer::save(const FilePath &fileName, const Project *project)
{
QMT_ASSERT(project, return);
- QFile file(fileName);
+ QFile file(fileName.toFSPathString());
if (!file.open(QIODevice::WriteOnly))
throw FileCreationException(fileName);
@@ -84,11 +86,11 @@ QByteArray ProjectSerializer::save(const Project *project)
return buffer;
}
-void ProjectSerializer::load(const QString &fileName, Project *project)
+void ProjectSerializer::load(const FilePath &fileName, Project *project)
{
QMT_ASSERT(project, return);
- QFile file(fileName);
+ QFile file(fileName.toFSPathString());
if (!file.open(QIODevice::ReadOnly))
throw FileNotFoundException(fileName);
diff --git a/src/libs/modelinglib/qmt/serializer/projectserializer.h b/src/libs/modelinglib/qmt/serializer/projectserializer.h
index c6dd74bcd8..bafa407071 100644
--- a/src/libs/modelinglib/qmt/serializer/projectserializer.h
+++ b/src/libs/modelinglib/qmt/serializer/projectserializer.h
@@ -5,6 +5,8 @@
#include "qmt/infrastructure/qmt_global.h"
+#include <utils/filepath.h>
+
#include <QString>
QT_BEGIN_NAMESPACE
@@ -21,9 +23,9 @@ public:
ProjectSerializer();
~ProjectSerializer();
- void save(const QString &fileName, const Project *project);
+ void save(const Utils::FilePath &fileName, const Project *project);
QByteArray save(const Project *project);
- void load(const QString &fileName, Project *project);
+ void load(const Utils::FilePath &fileName, Project *project);
private:
void write(QXmlStreamWriter *writer, const Project *project);
diff --git a/src/libs/modelinglib/qmt/stereotype/customrelation.cpp b/src/libs/modelinglib/qmt/stereotype/customrelation.cpp
index fa9c80a1a0..cc80da1b80 100644
--- a/src/libs/modelinglib/qmt/stereotype/customrelation.cpp
+++ b/src/libs/modelinglib/qmt/stereotype/customrelation.cpp
@@ -114,4 +114,9 @@ void CustomRelation::setColor(const QColor &color)
m_color = color;
}
+void CustomRelation::setEmphasized(bool emphasized)
+{
+ m_emphasized = emphasized;
+}
+
} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/stereotype/customrelation.h b/src/libs/modelinglib/qmt/stereotype/customrelation.h
index 7125865ade..dd32c542c0 100644
--- a/src/libs/modelinglib/qmt/stereotype/customrelation.h
+++ b/src/libs/modelinglib/qmt/stereotype/customrelation.h
@@ -55,7 +55,10 @@ public:
enum class ColorType {
EndA,
EndB,
- Custom
+ Custom,
+ Warning,
+ Error,
+ Soften,
};
class End {
@@ -113,6 +116,8 @@ public:
void setColorType(ColorType colorType);
QColor color() const { return m_color; }
void setColor(const QColor &color);
+ bool emphasized() const { return m_emphasized; }
+ void setEmphasized(bool emphasized);
friend auto qHash(CustomRelation::Relationship relationship) {
return ::qHash(static_cast<int>(relationship));
@@ -139,6 +144,7 @@ private:
ShaftPattern m_shaftPattern = ShaftPattern::Solid;
ColorType m_colorType = ColorType::EndA;
QColor m_color;
+ bool m_emphasized = false;
};
} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/stereotype/shapevalue.cpp b/src/libs/modelinglib/qmt/stereotype/shapevalue.cpp
index 9e1ee46c15..cf8ea5ef65 100644
--- a/src/libs/modelinglib/qmt/stereotype/shapevalue.cpp
+++ b/src/libs/modelinglib/qmt/stereotype/shapevalue.cpp
@@ -29,7 +29,7 @@ qreal ShapeValueF::mapScaledTo(qreal scaledOrigin, qreal originalSize, qreal bas
v = originalSize != 0 ? (m_value * actualSize / originalSize) : m_value;
break;
case UnitPercentage:
- v = m_value * actualSize;
+ v = m_value * actualSize / 100.0;
break;
}
switch (m_origin) {
diff --git a/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.cpp b/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.cpp
index 030c9e12a5..2f679df117 100644
--- a/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.cpp
+++ b/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.cpp
@@ -10,6 +10,8 @@
#include "qmt/infrastructure/qmtassert.h"
#include "qmt/style/style.h"
+
+#include <utils/filepath.h>
#include <utils/algorithm.h>
#include <QHash>
@@ -19,12 +21,14 @@
#include <algorithm>
+using Utils::FilePath;
+
namespace qmt {
namespace {
struct IconKey {
- IconKey(StereotypeIcon::Element element, const QList<QString> &stereotypes, const QString &defaultIconPath,
+ IconKey(StereotypeIcon::Element element, const QList<QString> &stereotypes, const FilePath &defaultIconPath,
const Uid &styleUid, const QSize &size, const QMarginsF &margins, qreal lineWidth)
: m_element(element),
m_stereotypes(stereotypes),
@@ -53,7 +57,7 @@ struct IconKey {
const StereotypeIcon::Element m_element;
const QList<QString> m_stereotypes;
- const QString m_defaultIconPath;
+ const FilePath m_defaultIconPath;
const Uid m_styleUid;
const QSize m_size;
const QMarginsF m_margins;
@@ -68,6 +72,7 @@ public:
QHash<QPair<StereotypeIcon::Element, QString>, QString> m_stereotypeToIconIdMap;
QHash<QString, StereotypeIcon> m_iconIdToStereotypeIconsMap;
QHash<QString, CustomRelation> m_relationIdToCustomRelationMap;
+ QHash<QString, CustomRelation> m_stereotypeToCustomRelationMap;
QList<Toolbar> m_toolbars;
QList<Toolbar> m_elementToolbars;
QHash<IconKey, QIcon> m_iconMap;
@@ -150,8 +155,13 @@ CustomRelation StereotypeController::findCustomRelation(const QString &customRel
return d->m_relationIdToCustomRelationMap.value(customRelationId);
}
+CustomRelation StereotypeController::findCustomRelationByStereotype(const QString &steoreotype) const
+{
+ return d->m_stereotypeToCustomRelationMap.value(steoreotype);
+}
+
QIcon StereotypeController::createIcon(StereotypeIcon::Element element, const QList<QString> &stereotypes,
- const QString &defaultIconPath, const Style *style, const QSize &size,
+ const FilePath &defaultIconPath, const Style *style, const QSize &size,
const QMarginsF &margins, qreal lineWidth)
{
IconKey key(element, stereotypes, defaultIconPath, style->uid(), size, margins, lineWidth);
@@ -225,7 +235,7 @@ QIcon StereotypeController::createIcon(StereotypeIcon::Element element, const QL
icon = QIcon(pixmap);
}
if (icon.isNull() && !defaultIconPath.isEmpty())
- icon = QIcon(defaultIconPath);
+ icon = QIcon(defaultIconPath.toFSPathString());
d->m_iconMap.insert(key, icon);
return icon;
@@ -251,6 +261,9 @@ void StereotypeController::addStereotypeIcon(const StereotypeIcon &stereotypeIco
void StereotypeController::addCustomRelation(const CustomRelation &customRelation)
{
d->m_relationIdToCustomRelationMap.insert(customRelation.id(), customRelation);
+ QString stereotype = Utils::toList(customRelation.stereotypes()).value(0);
+ if (!stereotype.isEmpty())
+ d->m_stereotypeToCustomRelationMap.insert(stereotype, customRelation);
}
void StereotypeController::addToolbar(const Toolbar &toolbar)
diff --git a/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.h b/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.h
index 676f9a275a..49acc5f937 100644
--- a/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.h
+++ b/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.h
@@ -5,6 +5,8 @@
#include "stereotypeicon.h"
+#include <utils/filepath.h>
+
#include <QMarginsF>
#include <QObject>
@@ -33,9 +35,14 @@ public:
const QList<QString> &stereotypes) const;
StereotypeIcon findStereotypeIcon(const QString &stereotypeIconId) const;
CustomRelation findCustomRelation(const QString &customRelationId) const;
- QIcon createIcon(StereotypeIcon::Element element, const QList<QString> &stereotypes,
- const QString &defaultIconPath, const Style *style,
- const QSize &size, const QMarginsF &margins, qreal lineWidth);
+ CustomRelation findCustomRelationByStereotype(const QString &steoreotype) const;
+ QIcon createIcon(StereotypeIcon::Element element,
+ const QList<QString> &stereotypes,
+ const Utils::FilePath &defaultIconPath,
+ const Style *style,
+ const QSize &size,
+ const QMarginsF &margins,
+ qreal lineWidth);
void addStereotypeIcon(const StereotypeIcon &stereotypeIcon);
void addCustomRelation(const CustomRelation &customRelation);
diff --git a/src/libs/modelinglib/qmt/stereotype/stereotypeicon.cpp b/src/libs/modelinglib/qmt/stereotype/stereotypeicon.cpp
index 8bcf5df170..19e466c509 100644
--- a/src/libs/modelinglib/qmt/stereotype/stereotypeicon.cpp
+++ b/src/libs/modelinglib/qmt/stereotype/stereotypeicon.cpp
@@ -62,6 +62,21 @@ void StereotypeIcon::setMinHeight(qreal minHeight)
m_minHeight = minHeight;
}
+void StereotypeIcon::setIconWith(qreal iconWidth)
+{
+ m_iconWidth = iconWidth;
+}
+
+void StereotypeIcon::setIconHeight(qreal iconHeight)
+{
+ m_iconHeight = iconHeight;
+}
+
+void StereotypeIcon::setDepthLayer(DepthLayer depthLayer)
+{
+ m_depthLayer = depthLayer;
+}
+
void StereotypeIcon::setSizeLock(StereotypeIcon::SizeLock sizeLock)
{
m_sizeLock = sizeLock;
diff --git a/src/libs/modelinglib/qmt/stereotype/stereotypeicon.h b/src/libs/modelinglib/qmt/stereotype/stereotypeicon.h
index be45245da5..fcb5f1a15c 100644
--- a/src/libs/modelinglib/qmt/stereotype/stereotypeicon.h
+++ b/src/libs/modelinglib/qmt/stereotype/stereotypeicon.h
@@ -31,6 +31,12 @@ public:
DisplaySmart
};
+ enum DepthLayer {
+ DepthBehindItems,
+ DepthAmongItems,
+ DepthBeforeItems,
+ };
+
enum SizeLock {
LockNone,
LockWidth,
@@ -62,12 +68,20 @@ public:
void setWidth(qreal width);
qreal height() const { return m_height; }
void setHeight(qreal height);
- bool hasMinWidth() const { return m_minWidth > 0; }
+ bool hasMinWidth() const { return m_minWidth > 0.0; }
qreal minWidth() const { return m_minWidth; }
void setMinWidth(qreal minWidth);
- bool hasMinHeight() const { return m_minHeight > 0; }
+ bool hasMinHeight() const { return m_minHeight > 0.0; }
qreal minHeight() const { return m_minHeight; }
void setMinHeight(qreal minHeight);
+ bool hasIconWidth() const { return m_iconWidth > 0.0; }
+ qreal iconWidth() const { return m_iconWidth; }
+ void setIconWith(qreal iconWidth);
+ bool hasIconHeight() const { return m_iconHeight > 0.0; }
+ qreal iconHeight() const { return m_iconHeight; }
+ void setIconHeight(qreal iconHeight);
+ DepthLayer depthLayer() const { return m_depthLayer; }
+ void setDepthLayer(DepthLayer depthLayer);
SizeLock sizeLock() const { return m_sizeLock; }
void setSizeLock(SizeLock sizeLock);
Display display() const { return m_display; }
@@ -90,8 +104,11 @@ private:
QString m_name;
qreal m_width = 100.0;
qreal m_height = 100.0;
- qreal m_minWidth = -1;
- qreal m_minHeight = -1;
+ qreal m_minWidth = -1.0;
+ qreal m_minHeight = -1.0;
+ qreal m_iconWidth = -1.0;
+ qreal m_iconHeight = -1.0;
+ DepthLayer m_depthLayer = DepthAmongItems;
SizeLock m_sizeLock = LockNone;
Display m_display = DisplaySmart;
TextAlignment m_textAlignment = TextalignBelow;
diff --git a/src/libs/modelinglib/qmt/style/defaultstyleengine.cpp b/src/libs/modelinglib/qmt/style/defaultstyleengine.cpp
index 74e91837dc..bce1804892 100644
--- a/src/libs/modelinglib/qmt/style/defaultstyleengine.cpp
+++ b/src/libs/modelinglib/qmt/style/defaultstyleengine.cpp
@@ -5,6 +5,7 @@
#include "defaultstyle.h"
#include "objectvisuals.h"
+#include "relationvisuals.h"
#include "styledobject.h"
#include "styledrelation.h"
@@ -14,6 +15,7 @@
#include "qmt/diagram/ditem.h"
#include "qmt/diagram/dannotation.h"
#include "qmt/infrastructure/qmtassert.h"
+#include "qmt/stereotype/customrelation.h"
#include <utils/algorithm.h>
@@ -73,25 +75,29 @@ bool operator==(const ObjectStyleKey &lhs, const ObjectStyleKey &rhs)
class RelationStyleKey
{
public:
- RelationStyleKey(StyleEngine::ElementType elementType = StyleEngine::TypeOther,
- DObject::VisualPrimaryRole visualPrimaryRole = DObject::PrimaryRoleNormal)
+ RelationStyleKey(StyleEngine::ElementType elementType, const RelationVisuals &relationVisuals,
+ bool withObject = false)
: m_elementType(elementType),
- m_visualPrimaryRole(visualPrimaryRole)
+ m_relationVisuals(relationVisuals),
+ m_withObject(withObject)
{
}
StyleEngine::ElementType m_elementType = StyleEngine::TypeOther;
- DObject::VisualPrimaryRole m_visualPrimaryRole = DObject::PrimaryRoleNormal;
+ RelationVisuals m_relationVisuals;
+ bool m_withObject = false;
};
size_t qHash(const RelationStyleKey &styleKey)
{
- return ::qHash(styleKey.m_elementType) ^ ::qHash(styleKey.m_visualPrimaryRole);
+ return ::qHash(styleKey.m_elementType) ^ qHash(styleKey.m_relationVisuals);
}
bool operator==(const RelationStyleKey &lhs, const RelationStyleKey &rhs)
{
- return lhs.m_elementType == rhs.m_elementType && lhs.m_visualPrimaryRole == rhs.m_visualPrimaryRole;
+ return lhs.m_elementType == rhs.m_elementType
+ && lhs.m_relationVisuals == rhs.m_relationVisuals
+ && lhs.m_withObject == rhs.m_withObject;
}
class AnnotationStyleKey
@@ -253,34 +259,6 @@ const Style *DefaultStyleEngine::applyObjectStyle(const Style *baseStyle, StyleE
return derivedStyle;
}
-static bool areStackingRoles(DObject::VisualPrimaryRole rhsPrimaryRole,
- DObject::VisualSecondaryRole rhsSecondaryRole,
- DObject::VisualPrimaryRole lhsPrimaryRole,
- DObject::VisualSecondaryRole lhsSecondaryRols)
-{
- switch (rhsSecondaryRole) {
- case DObject::SecondaryRoleNone:
- case DObject::SecondaryRoleLighter:
- case DObject::SecondaryRoleDarker:
- case DObject::SecondaryRoleFlat:
- switch (lhsSecondaryRols) {
- case DObject::SecondaryRoleNone:
- case DObject::SecondaryRoleLighter:
- case DObject::SecondaryRoleDarker:
- case DObject::SecondaryRoleFlat:
- return lhsPrimaryRole == rhsPrimaryRole;
- case DObject::SecondaryRoleSoften:
- case DObject::SecondaryRoleOutline:
- return false;
- }
- break;
- case DObject::SecondaryRoleSoften:
- case DObject::SecondaryRoleOutline:
- return false;
- }
- return true;
-}
-
const Style *DefaultStyleEngine::applyObjectStyle(const Style *baseStyle, const StyledObject &styledObject,
const Parameters *parameters)
{
@@ -325,10 +303,12 @@ const Style *DefaultStyleEngine::applyObjectStyle(const Style *baseStyle, const
}
int depth = 0;
if (!depths.isEmpty()) {
- for (auto it = depths.cbegin(); it != depths.cend(); ++it) {
- if (it->m_elementType == elementType
- && areStackingRoles(it->m_visualPrimaryRole, it->m_visualSecondaryRole,
- styledVisualPrimaryRole, styledVisualSecondaryRole)) {
+ const QList<int> keys = Utils::sorted(depths.keys());
+ for (int d : keys) {
+ DepthProperties properties = depths.value(d);
+ if (properties.m_elementType == elementType
+ && areStackingRoles(properties.m_visualPrimaryRole, properties.m_visualSecondaryRole,
+ styledVisualPrimaryRole, styledVisualSecondaryRole)) {
++depth;
} else {
depth = 0;
@@ -345,13 +325,100 @@ const Style *DefaultStyleEngine::applyObjectStyle(const Style *baseStyle, const
parameters);
}
+const Style *DefaultStyleEngine::applyRelationStyle(const Style *baseStyle, ElementType elementType, const RelationVisuals &relationVisuals, const Parameters *parameters)
+{
+ Q_UNUSED(parameters);
+
+ RelationStyleKey key(elementType, relationVisuals);
+ const Style *derivedStyle = m_relationStyleMap.value(key);
+ if (!derivedStyle) {
+ auto style = new Style(baseStyle->type());
+ static QColor customColors[] = {
+ QColor(0xEE, 0x8E, 0x99).darker(110), // ROLE_CUSTOM1,
+ QColor(0x80, 0xAF, 0x47).lighter(130), // ROLE_CUSTOM2,
+ QColor(0xFF, 0xA1, 0x5B).lighter(100), // ROLE_CUSTOM3,
+ QColor(0x55, 0xC4, 0xCF).lighter(120), // ROLE_CUSTOM4,
+ QColor(0xFF, 0xE1, 0x4B) // ROLE_CUSTOM5,
+ };
+
+ int index = static_cast<int>(relationVisuals.visualPrimaryRole()) - static_cast<int>(DRelation::PrimaryRoleCustom1);
+ QColor lineColor = index >= 0 && index <= 4 ? customColors[index] : Qt::black;
+ switch (relationVisuals.visualSecondaryRole()) {
+ case DRelation::SecondaryRoleNone:
+ break;
+ case DRelation::SecondaryRoleWarning:
+ lineColor = Qt::yellow;
+ break;
+ case DRelation::SecondaryRoleError:
+ lineColor = Qt::red;
+ break;
+ case DRelation::SecondaryRoleSoften:
+ lineColor = Qt::gray;
+ break;
+ }
+
+ QColor fillColor = lineColor == Qt::black ? Qt::darkGray : lineColor.lighter(150);
+ QPen linePen = baseStyle->linePen();
+ linePen.setWidth(1);
+ linePen.setColor(lineColor);
+ style->setLinePen(linePen);
+ QBrush textBrush = baseStyle->textBrush();
+ textBrush.setColor(Qt::black);
+ style->setTextBrush(textBrush);
+ QBrush brush = baseStyle->fillBrush();
+ brush.setColor(fillColor);
+ brush.setStyle(Qt::SolidPattern);
+ style->setFillBrush(brush);
+ style->setNormalFont(baseStyle->normalFont());
+ style->setSmallFont(baseStyle->smallFont());
+ style->setHeaderFont(baseStyle->headerFont());
+ m_relationStyleMap.insert(key, style);
+ derivedStyle = style;
+ }
+ return derivedStyle;
+}
+
const Style *DefaultStyleEngine::applyRelationStyle(const Style *baseStyle, const StyledRelation &styledRelation,
const Parameters *parameters)
{
Q_UNUSED(parameters)
ElementType elementType = objectType(styledRelation.endA());
- RelationStyleKey key(elementType, styledRelation.endA() ? styledRelation.endA()->visualPrimaryRole() : DObject::PrimaryRoleNormal);
+ RelationVisuals relationVisuals;
+ if (styledRelation.customRelation()) {
+ switch (styledRelation.customRelation()->colorType()) {
+ case CustomRelation::ColorType::EndA:
+ // TODO implement
+ break;
+ case CustomRelation::ColorType::EndB:
+ // TODO implement
+ break;
+ case CustomRelation::ColorType::Custom:
+ // TODO implement
+ break;
+ case CustomRelation::ColorType::Warning:
+ relationVisuals.setVisualSecondaryRole(DRelation::VisualSecondaryRole::SecondaryRoleWarning);
+ break;
+ case CustomRelation::ColorType::Error:
+ relationVisuals.setVisualSecondaryRole(DRelation::VisualSecondaryRole::SecondaryRoleError);
+ break;
+ case CustomRelation::ColorType::Soften:
+ relationVisuals.setVisualSecondaryRole(DRelation::VisualSecondaryRole::SecondaryRoleSoften);
+ break;
+ }
+ relationVisuals.setEmphasized(styledRelation.customRelation()->emphasized());
+ }
+ if (styledRelation.endA())
+ relationVisuals.setVisualObjectPrimaryRole(styledRelation.endA()->visualPrimaryRole());
+ if (styledRelation.relation()) {
+ if (styledRelation.relation()->visualPrimaryRole() != DRelation::VisualPrimaryRole::PrimaryRoleNormal)
+ relationVisuals.setVisualPrimaryRole(styledRelation.relation()->visualPrimaryRole());
+ if (styledRelation.relation()->visualSecondaryRole() != DRelation::VisualSecondaryRole::SecondaryRoleNone)
+ relationVisuals.setVisualSecondaryRole(styledRelation.relation()->visualSecondaryRole());
+ if (styledRelation.relation()->isVisualEmphasized())
+ relationVisuals.setEmphasized(styledRelation.relation()->isVisualEmphasized());
+ }
+ RelationStyleKey key(elementType, relationVisuals, true);
const Style *derivedStyle = m_relationStyleMap.value(key);
if (!derivedStyle) {
auto style = new Style(baseStyle->type());
@@ -365,12 +432,36 @@ const Style *DefaultStyleEngine::applyRelationStyle(const Style *baseStyle, cons
QColor lineColor = DefaultStyleEngine::lineColor(objectType(object), objectVisuals);
QColor fillColor = lineColor;
+ static QColor customColors[] = {
+ QColor(0xEE, 0x8E, 0x99).darker(110), // ROLE_CUSTOM1,
+ QColor(0x80, 0xAF, 0x47).lighter(130), // ROLE_CUSTOM2,
+ QColor(0xFF, 0xA1, 0x5B).lighter(100), // ROLE_CUSTOM3,
+ QColor(0x55, 0xC4, 0xCF).lighter(120), // ROLE_CUSTOM4,
+ QColor(0xFF, 0xE1, 0x4B) // ROLE_CUSTOM5,
+ };
+
+ int index = static_cast<int>(relationVisuals.visualPrimaryRole()) - static_cast<int>(DRelation::PrimaryRoleCustom1);
+ lineColor = (index >= 0 && index <= 4) ? customColors[index] : lineColor;
+ switch (relationVisuals.visualSecondaryRole()) {
+ case DRelation::SecondaryRoleNone:
+ break;
+ case DRelation::SecondaryRoleWarning:
+ lineColor = QColor(0xffc800);
+ break;
+ case DRelation::SecondaryRoleError:
+ lineColor = Qt::red;
+ break;
+ case DRelation::SecondaryRoleSoften:
+ lineColor = Qt::gray;
+ break;
+ }
+
QPen linePen = baseStyle->linePen();
- linePen.setWidth(1);
+ linePen.setWidth(relationVisuals.isEmphasized() ? 3 : 1);
linePen.setColor(lineColor);
style->setLinePen(linePen);
QBrush textBrush = baseStyle->textBrush();
- textBrush.setColor(QColor("black"));
+ textBrush.setColor(Qt::black);
style->setTextBrush(textBrush);
QBrush brush = baseStyle->fillBrush();
brush.setColor(fillColor);
@@ -497,6 +588,34 @@ DefaultStyleEngine::ElementType DefaultStyleEngine::objectType(const DObject *ob
return elementType;
}
+bool DefaultStyleEngine::areStackingRoles(DObject::VisualPrimaryRole rhsPrimaryRole,
+ DObject::VisualSecondaryRole rhsSecondaryRole,
+ DObject::VisualPrimaryRole lhsPrimaryRole,
+ DObject::VisualSecondaryRole lhsSecondaryRols)
+{
+ switch (rhsSecondaryRole) {
+ case DObject::SecondaryRoleNone:
+ case DObject::SecondaryRoleLighter:
+ case DObject::SecondaryRoleDarker:
+ case DObject::SecondaryRoleFlat:
+ switch (lhsSecondaryRols) {
+ case DObject::SecondaryRoleNone:
+ case DObject::SecondaryRoleLighter:
+ case DObject::SecondaryRoleDarker:
+ case DObject::SecondaryRoleFlat:
+ return lhsPrimaryRole == rhsPrimaryRole;
+ case DObject::SecondaryRoleSoften:
+ case DObject::SecondaryRoleOutline:
+ return false;
+ }
+ break;
+ case DObject::SecondaryRoleSoften:
+ case DObject::SecondaryRoleOutline:
+ return false;
+ }
+ return true;
+}
+
QColor DefaultStyleEngine::baseColor(ElementType elementType, ObjectVisuals objectVisuals)
{
if (objectVisuals.visualSecondaryRole() == DObject::SecondaryRoleOutline)
diff --git a/src/libs/modelinglib/qmt/style/defaultstyleengine.h b/src/libs/modelinglib/qmt/style/defaultstyleengine.h
index 67cdf9ed85..b5ebe557e0 100644
--- a/src/libs/modelinglib/qmt/style/defaultstyleengine.h
+++ b/src/libs/modelinglib/qmt/style/defaultstyleengine.h
@@ -37,6 +37,9 @@ public:
const Parameters *parameters) override;
const Style *applyObjectStyle(const Style *baseStyle, const StyledObject &styledObject,
const Parameters *parameters) override;
+ const Style *applyRelationStyle(const Style *baseStyle, ElementType elementType,
+ const RelationVisuals &relationVisuals,
+ const Parameters *parameters) override;
const Style *applyRelationStyle(const Style *baseStyle, const StyledRelation &styledRelation,
const Parameters *parameters) override;
const Style *applyAnnotationStyle(const Style *baseStyle, const DAnnotation *annotation,
@@ -54,6 +57,11 @@ private:
ElementType objectType(const DObject *object);
+ bool areStackingRoles(DObject::VisualPrimaryRole rhsPrimaryRole,
+ DObject::VisualSecondaryRole rhsSecondaryRole,
+ DObject::VisualPrimaryRole lhsPrimaryRole,
+ DObject::VisualSecondaryRole lhsSecondaryRols);
+
QColor baseColor(ElementType elementType, ObjectVisuals objectVisuals);
QColor lineColor(ElementType elementType, const ObjectVisuals &objectVisuals);
QColor fillColor(ElementType elementType, const ObjectVisuals &objectVisuals);
diff --git a/src/libs/modelinglib/qmt/style/relationvisuals.cpp b/src/libs/modelinglib/qmt/style/relationvisuals.cpp
new file mode 100644
index 0000000000..d297047ae1
--- /dev/null
+++ b/src/libs/modelinglib/qmt/style/relationvisuals.cpp
@@ -0,0 +1,58 @@
+// Copyright (C) 2024 Jochen Becher
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "relationvisuals.h"
+
+namespace qmt {
+
+RelationVisuals::RelationVisuals() {}
+
+RelationVisuals::RelationVisuals(DObject::VisualPrimaryRole visualObjectPrimaryRole,
+ DRelation::VisualPrimaryRole visualPrimaryRole,
+ DRelation::VisualSecondaryRole visualSecondaryRole,
+ bool emphasized)
+ : m_visualObjectPrimaryRole(visualObjectPrimaryRole)
+ , m_visualPrimaryRole(visualPrimaryRole)
+ , m_visualSecondaryRole(visualSecondaryRole)
+ , m_isEmphasized(emphasized)
+{}
+
+RelationVisuals::~RelationVisuals() {}
+
+void RelationVisuals::setVisualPrimaryRole(DRelation::VisualPrimaryRole VisualPrimaryRole)
+{
+ m_visualPrimaryRole = VisualPrimaryRole;
+}
+
+void RelationVisuals::setVisualObjectPrimaryRole(DObject::VisualPrimaryRole visualPrimaryRole)
+{
+ m_visualObjectPrimaryRole = visualPrimaryRole;
+}
+
+void RelationVisuals::setVisualSecondaryRole(DRelation::VisualSecondaryRole visualSecondaryRole)
+{
+ m_visualSecondaryRole = visualSecondaryRole;
+}
+
+void RelationVisuals::setEmphasized(bool emphasized)
+{
+ m_isEmphasized = emphasized;
+}
+
+bool operator==(const RelationVisuals &lhs, const RelationVisuals &rhs)
+{
+ return lhs.visualObjectPrimaryRole() == rhs.visualObjectPrimaryRole()
+ && lhs.visualPrimaryRole() == rhs.visualPrimaryRole()
+ && lhs.visualSecondaryRole() == rhs.visualSecondaryRole()
+ && lhs.isEmphasized() == rhs.isEmphasized();
+}
+
+size_t qHash(const RelationVisuals &relationVisuals)
+{
+ return ::qHash(static_cast<int>(relationVisuals.visualObjectPrimaryRole()))
+ ^ ::qHash(static_cast<int>(relationVisuals.visualPrimaryRole()))
+ ^ ::qHash(static_cast<int>(relationVisuals.visualSecondaryRole()))
+ ^ ::qHash(relationVisuals.isEmphasized());
+}
+
+} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/style/relationvisuals.h b/src/libs/modelinglib/qmt/style/relationvisuals.h
new file mode 100644
index 0000000000..be87a7d291
--- /dev/null
+++ b/src/libs/modelinglib/qmt/style/relationvisuals.h
@@ -0,0 +1,42 @@
+// Copyright (C) 2024 Jochen Becher
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "qmt/diagram/drelation.h"
+#include "qmt/diagram/dobject.h"
+
+#include <QColor>
+
+namespace qmt {
+
+class QMT_EXPORT RelationVisuals
+{
+public:
+ RelationVisuals();
+ RelationVisuals(DObject::VisualPrimaryRole visualObjectPrimaryRole,
+ DRelation::VisualPrimaryRole visualPrimaryRole,
+ DRelation::VisualSecondaryRole visualSecondaryRole,
+ bool emphasized);
+ ~RelationVisuals();
+
+ DObject::VisualPrimaryRole visualObjectPrimaryRole() const { return m_visualObjectPrimaryRole; }
+ void setVisualObjectPrimaryRole(DObject::VisualPrimaryRole visualPrimaryRole);
+ DRelation::VisualPrimaryRole visualPrimaryRole() const { return m_visualPrimaryRole; }
+ void setVisualPrimaryRole(DRelation::VisualPrimaryRole visualPrimaryRole);
+ DRelation::VisualSecondaryRole visualSecondaryRole() const { return m_visualSecondaryRole; }
+ void setVisualSecondaryRole(DRelation::VisualSecondaryRole visualSecondaryRole);
+ bool isEmphasized() const { return m_isEmphasized; }
+ void setEmphasized(bool emphasized);
+
+private:
+ DObject::VisualPrimaryRole m_visualObjectPrimaryRole = DObject::PrimaryRoleNormal;
+ DRelation::VisualPrimaryRole m_visualPrimaryRole = DRelation::PrimaryRoleNormal;
+ DRelation::VisualSecondaryRole m_visualSecondaryRole = DRelation::SecondaryRoleNone;
+ bool m_isEmphasized = false;
+};
+
+bool operator==(const RelationVisuals &lhs, const RelationVisuals &rhs);
+size_t qHash(const RelationVisuals &relationVisuals);
+
+} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/style/stylecontroller.cpp b/src/libs/modelinglib/qmt/style/stylecontroller.cpp
index 5cd50c1738..bbfc28622e 100644
--- a/src/libs/modelinglib/qmt/style/stylecontroller.cpp
+++ b/src/libs/modelinglib/qmt/style/stylecontroller.cpp
@@ -63,6 +63,12 @@ const Style *StyleController::adaptObjectStyle(const StyledObject &object)
return m_defaultStyleEngine->applyObjectStyle(m_defaultStyle.data(), object, &parameters);
}
+const Style *StyleController::adaptRelationStyle(StyleEngine::ElementType elementType, const RelationVisuals &relationVisuals)
+{
+ Parameters parameters(this);
+ return m_defaultStyleEngine->applyRelationStyle(m_defaultStyle.data(), elementType, relationVisuals, &parameters);
+}
+
const Style *StyleController::adaptRelationStyle(const StyledRelation &relation)
{
Parameters parameters(this);
diff --git a/src/libs/modelinglib/qmt/style/stylecontroller.h b/src/libs/modelinglib/qmt/style/stylecontroller.h
index 2627bb2caa..afa566460f 100644
--- a/src/libs/modelinglib/qmt/style/stylecontroller.h
+++ b/src/libs/modelinglib/qmt/style/stylecontroller.h
@@ -4,7 +4,6 @@
#pragma once
#include "styleengine.h"
-#include "qmt/diagram/dobject.h"
#include <QObject>
#include <QScopedPointer>
@@ -14,6 +13,7 @@ namespace qmt {
class Style;
class ObjectVisuals;
class StyledObject;
+class RelationVisuals;
class StyledRelation;
class DAnnotation;
class DBoundary;
@@ -33,6 +33,8 @@ public:
const Style *adaptObjectStyle(StyleEngine::ElementType elementType,
const ObjectVisuals &objectVisuals);
const Style *adaptObjectStyle(const StyledObject &object);
+ const Style *adaptRelationStyle(StyleEngine::ElementType elementType,
+ const RelationVisuals &relationVisuals);
const Style *adaptRelationStyle(const StyledRelation &relation);
const Style *adaptAnnotationStyle(const DAnnotation *annotation);
const Style *adaptBoundaryStyle(const DBoundary *boundary);
diff --git a/src/libs/modelinglib/qmt/style/styledrelation.cpp b/src/libs/modelinglib/qmt/style/styledrelation.cpp
index 3c78abf96d..144e5f22ac 100644
--- a/src/libs/modelinglib/qmt/style/styledrelation.cpp
+++ b/src/libs/modelinglib/qmt/style/styledrelation.cpp
@@ -5,10 +5,12 @@
namespace qmt {
-StyledRelation::StyledRelation(const DRelation *relation, const DObject *endA, const DObject *endB)
+StyledRelation::StyledRelation(const DRelation *relation, const DObject *endA, const DObject *endB,
+ const CustomRelation *customRelation)
: m_relation(relation),
m_endA(endA),
- m_endB(endB)
+ m_endB(endB),
+ m_customRelation(customRelation)
{
}
diff --git a/src/libs/modelinglib/qmt/style/styledrelation.h b/src/libs/modelinglib/qmt/style/styledrelation.h
index 422947908e..27e8735463 100644
--- a/src/libs/modelinglib/qmt/style/styledrelation.h
+++ b/src/libs/modelinglib/qmt/style/styledrelation.h
@@ -9,21 +9,25 @@ namespace qmt {
class DRelation;
class DObject;
+class CustomRelation;
class QMT_EXPORT StyledRelation
{
public:
- StyledRelation(const DRelation *relation, const DObject *endA, const DObject *endB);
+ StyledRelation(const DRelation *relation, const DObject *endA, const DObject *endB,
+ const CustomRelation *customRelation);
~StyledRelation();
const DRelation *relation() const { return m_relation; }
const DObject *endA() const { return m_endA; }
const DObject *endB() const { return m_endB; }
+ const CustomRelation *customRelation() const { return m_customRelation; }
private:
const DRelation *m_relation = nullptr;
const DObject *m_endA = nullptr;
const DObject *m_endB = nullptr;
+ const CustomRelation *m_customRelation = nullptr;
};
} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/style/styleengine.h b/src/libs/modelinglib/qmt/style/styleengine.h
index e78f4303d9..463a62a194 100644
--- a/src/libs/modelinglib/qmt/style/styleengine.h
+++ b/src/libs/modelinglib/qmt/style/styleengine.h
@@ -14,6 +14,7 @@ namespace qmt {
class Style;
class ObjectVisuals;
class StyledObject;
+class RelationVisuals;
class StyledRelation;
class DAnnotation;
@@ -51,6 +52,9 @@ public:
const Parameters *parameters) = 0;
virtual const Style *applyObjectStyle(const Style *baseStyle, const StyledObject &,
const Parameters *) = 0;
+ virtual const Style *applyRelationStyle(const Style *baseStyle, ElementType elementType,
+ const RelationVisuals &relationVisuals,
+ const Parameters *) = 0;
virtual const Style *applyRelationStyle(const Style *baseStyle, const StyledRelation &,
const Parameters *) = 0;
virtual const Style *applyAnnotationStyle(const Style *baseStyle, const DAnnotation *,
diff --git a/src/libs/modelinglib/qmt/tasks/diagramscenecontroller.cpp b/src/libs/modelinglib/qmt/tasks/diagramscenecontroller.cpp
index 00aacf9c82..23f6ddc297 100644
--- a/src/libs/modelinglib/qmt/tasks/diagramscenecontroller.cpp
+++ b/src/libs/modelinglib/qmt/tasks/diagramscenecontroller.cpp
@@ -42,8 +42,6 @@
#include "../../modelinglibtr.h"
#include <QMenu>
-#include <QFileInfo>
-#include <QDir>
#include <QQueue>
#include <QPair>
@@ -421,7 +419,44 @@ void DiagramSceneController::dropNewModelElement(MObject *modelObject, MPackage
emit newElementCreated(element, diagram);
}
-void DiagramSceneController::addRelatedElements(const DSelection &selection, MDiagram *diagram)
+int DiagramSceneController::countRelatedElements(const DSelection &selection, MDiagram *diagram, std::function<bool (qmt::DObject *, qmt::MObject *, qmt::MRelation *)> filter)
+{
+ int counter = 0;
+ const QList<DSelection::Index> indices = selection.indices();
+ for (const DSelection::Index &index : indices) {
+ DElement *delement = m_diagramController->findElement(index.elementKey(), diagram);
+ QMT_ASSERT(delement, return 0);
+ DObject *dobject = dynamic_cast<DObject *>(delement);
+ if (dobject && dobject->modelUid().isValid()) {
+ MObject *mobject = m_modelController->findElement<MObject>(delement->modelUid());
+ if (mobject) {
+ const QList<MRelation *> relations = m_modelController->findRelationsOfObject(mobject);
+ QList<MRelation *> filteredRelations;
+ const QList<MRelation *> *relationsList = nullptr;
+ if (filter) {
+ for (MRelation *relation : relations) {
+ if (filter(dobject, mobject, relation))
+ filteredRelations.append(relation);
+ }
+ relationsList = &filteredRelations;
+ } else {
+ relationsList = &relations;
+ }
+ for (MRelation *relation : *relationsList) {
+ if (relation->endAUid() != mobject->uid())
+ ++counter;
+ else if (relation->endBUid() != mobject->uid())
+ ++counter;
+ }
+ }
+ }
+ }
+ return counter;
+}
+
+void DiagramSceneController::addRelatedElements(
+ const DSelection &selection, MDiagram *diagram,
+ std::function<bool (qmt::DObject *dobject, qmt::MObject *mobject, qmt::MRelation *relation)> filter)
{
m_diagramController->undoController()->beginMergeSequence(Tr::tr("Add Related Element"));
const QList<DSelection::Index> indices = selection.indices();
@@ -435,8 +470,19 @@ void DiagramSceneController::addRelatedElements(const DSelection &selection, MDi
qreal dAngle = 360.0 / 11.5;
qreal dRadius = 100.0;
const QList<MRelation *> relations = m_modelController->findRelationsOfObject(mobject);
+ QList<MRelation *> filteredRelations;
+ const QList<MRelation *> *relationsList = nullptr;
+ if (filter) {
+ for (MRelation *relation : relations) {
+ if (filter(dobject, mobject, relation))
+ filteredRelations.append(relation);
+ }
+ relationsList = &filteredRelations;
+ } else {
+ relationsList = &relations;
+ }
int count = 0;
- for (MRelation *relation : relations) {
+ for (MRelation *relation : *relationsList) {
if (relation->endAUid() != mobject->uid() || relation->endBUid() != mobject->uid())
++count;
}
@@ -446,7 +492,7 @@ void DiagramSceneController::addRelatedElements(const DSelection &selection, MDi
}
qreal radius = 200.0;
qreal angle = 0.0;
- for (MRelation *relation : relations) {
+ for (MRelation *relation : *relationsList) {
QPointF pos(dobject->pos());
pos += QPointF(radius * sin(angle / 180 * M_PI), -radius * cos(angle / 180 * M_PI));
bool added = false;
diff --git a/src/libs/modelinglib/qmt/tasks/diagramscenecontroller.h b/src/libs/modelinglib/qmt/tasks/diagramscenecontroller.h
index a062fa10c4..14e704a192 100644
--- a/src/libs/modelinglib/qmt/tasks/diagramscenecontroller.h
+++ b/src/libs/modelinglib/qmt/tasks/diagramscenecontroller.h
@@ -86,7 +86,12 @@ public:
DElement *topMostElementAtPos, const QPointF &pos, MDiagram *diagram, const QPoint &viewPos, const QSize &viewSize);
void dropNewModelElement(MObject *modelObject, MPackage *parentPackage, const QPointF &pos,
MDiagram *diagram);
- void addRelatedElements(const DSelection &selection, MDiagram *diagram);
+ int countRelatedElements(
+ const DSelection &selection, MDiagram *diagram,
+ std::function<bool (qmt::DObject *dobject, qmt::MObject *mobject, qmt::MRelation *relation)> filter);
+ void addRelatedElements(
+ const DSelection &selection, MDiagram *diagram,
+ std::function<bool (qmt::DObject *dobject, qmt::MObject *mobject, qmt::MRelation *relation)> filter);
MPackage *findSuitableParentPackage(DElement *topmostDiagramElement, MDiagram *diagram);
MDiagram *findDiagramBySearchId(MPackage *package, const QString &diagramName);
diff --git a/src/libs/modelinglib/qmt/tasks/ielementtasks.h b/src/libs/modelinglib/qmt/tasks/ielementtasks.h
index a40277d874..8c22632762 100644
--- a/src/libs/modelinglib/qmt/tasks/ielementtasks.h
+++ b/src/libs/modelinglib/qmt/tasks/ielementtasks.h
@@ -59,8 +59,13 @@ public:
virtual void createAndOpenDiagram(const MElement *) = 0;
virtual void createAndOpenDiagram(const DElement *, const MDiagram *) = 0;
+ virtual bool hasLinkedFile(const MElement *) const = 0;
+ virtual bool hasLinkedFile(const DElement *, const MDiagram *) const = 0;
+ virtual void openLinkedFile(const MElement *) = 0;
+ virtual void openLinkedFile(const DElement *, const MDiagram *) = 0;
+
virtual bool extendContextMenu(const DElement *, const MDiagram *, QMenu *) = 0;
- virtual bool handleContextMenuAction(const DElement *, const MDiagram *, const QString &) = 0;
+ virtual bool handleContextMenuAction(DElement *, MDiagram *, const QString &) = 0;
};
} // namespace qmt
diff --git a/src/libs/modelinglib/qmt/tasks/voidelementtasks.cpp b/src/libs/modelinglib/qmt/tasks/voidelementtasks.cpp
index 2bd7fbe049..fdc2e24bd4 100644
--- a/src/libs/modelinglib/qmt/tasks/voidelementtasks.cpp
+++ b/src/libs/modelinglib/qmt/tasks/voidelementtasks.cpp
@@ -147,12 +147,30 @@ void VoidElementTasks::createAndOpenDiagram(const DElement *, const MDiagram *)
{
}
+bool VoidElementTasks::hasLinkedFile(const MElement *) const
+{
+ return false;
+}
+
+bool VoidElementTasks::hasLinkedFile(const DElement *, const MDiagram *) const
+{
+ return false;
+}
+
+void VoidElementTasks::openLinkedFile(const MElement *)
+{
+}
+
+void VoidElementTasks::openLinkedFile(const DElement *, const MDiagram *)
+{
+}
+
bool VoidElementTasks::extendContextMenu(const DElement *, const MDiagram *, QMenu *)
{
return false;
}
-bool VoidElementTasks::handleContextMenuAction(const DElement *, const MDiagram *, const QString &)
+bool VoidElementTasks::handleContextMenuAction(DElement *, MDiagram *, const QString &)
{
return false;
}
diff --git a/src/libs/modelinglib/qmt/tasks/voidelementtasks.h b/src/libs/modelinglib/qmt/tasks/voidelementtasks.h
index 3e3d483d9a..9e8753b71e 100644
--- a/src/libs/modelinglib/qmt/tasks/voidelementtasks.h
+++ b/src/libs/modelinglib/qmt/tasks/voidelementtasks.h
@@ -51,8 +51,13 @@ public:
void createAndOpenDiagram(const MElement *) override;
void createAndOpenDiagram(const DElement *, const MDiagram *) override;
+ bool hasLinkedFile(const qmt::MElement *) const override;
+ bool hasLinkedFile(const qmt::DElement *, const qmt::MDiagram *) const override;
+ void openLinkedFile(const qmt::MElement *) override;
+ void openLinkedFile(const qmt::DElement *, const qmt::MDiagram *) override;
+
bool extendContextMenu(const DElement *, const MDiagram *, QMenu *) override;
- bool handleContextMenuAction(const DElement *, const MDiagram *, const QString &) override;
+ bool handleContextMenuAction(DElement *, MDiagram *, const QString &) override;
};
} // namespace qmt
diff --git a/src/libs/modelinglib/qtserialization/inc/qark/serialize_container.h b/src/libs/modelinglib/qtserialization/inc/qark/serialize_container.h
index c95f2c3f96..109b77d991 100644
--- a/src/libs/modelinglib/qtserialization/inc/qark/serialize_container.h
+++ b/src/libs/modelinglib/qtserialization/inc/qark/serialize_container.h
@@ -4,6 +4,7 @@
#pragma once
#include "parameters.h"
+#include "tag.h"
#include <QList>
#include <QHash>
diff --git a/src/libs/nanotrace/CMakeLists.txt b/src/libs/nanotrace/CMakeLists.txt
index 8652a81798..50693644ea 100644
--- a/src/libs/nanotrace/CMakeLists.txt
+++ b/src/libs/nanotrace/CMakeLists.txt
@@ -3,21 +3,21 @@ add_qtc_library(Nanotrace
SOURCES
nanotraceglobals.h
nanotrace.cpp nanotrace.h
+ nanotracehr.cpp nanotracehr.h
+ staticstring.h
PUBLIC_DEPENDS Qt::Core Qt::Gui
PROPERTIES
CXX_VISIBILITY_PRESET default
VISIBILITY_INLINES_HIDDEN OFF
)
+if(TARGET Nanotrace)
+ target_compile_options(Nanotrace PUBLIC $<$<COMPILE_LANG_AND_ID:CXX,MSVC>:/wd5030>)
+endif()
+
option(DESIGNSTUDIO_USE_NANOTRACE
"Enables collecting performance data with nanotrace for Design Studio" OFF)
extend_qtc_library(Nanotrace
CONDITION DESIGN_STUDIO_USE_NANOTRACE
PUBLIC_DEFINES NANOTRACE_DESIGNSTUDIO_ENABLED
)
-
-option(NANOTRACEHR_ENABLED "Enables collecting high resolution performance data" OFF)
-extend_qtc_library(Nanotrace
- SOURCES
- nanotracehr.cpp nanotracehr.h
-)
diff --git a/src/libs/nanotrace/nanotraceglobals.h b/src/libs/nanotrace/nanotraceglobals.h
index 64af31aab9..145b8ad003 100644
--- a/src/libs/nanotrace/nanotraceglobals.h
+++ b/src/libs/nanotrace/nanotraceglobals.h
@@ -7,10 +7,24 @@
#if defined(NANOTRACE_LIBRARY)
#define NANOTRACE_EXPORT Q_DECL_EXPORT
+# ifdef Q_OS_WINDOWS
+# define NANOTRACE_EXPORT_EXTERN_TEMPLATE
+# else
+# define NANOTRACE_EXPORT_EXTERN_TEMPLATE Q_DECL_EXPORT
+# endif
+# ifdef Q_OS_WINDOWS
+# define NANOTRACE_EXPORT_TEMPLATE Q_DECL_EXPORT
+# else
+# define NANOTRACE_EXPORT_TEMPLATE
+# endif
#elif defined(NANOTRACE_STATIC_LIBRARY)
-#define NANOTRACE_EXPORT
+# define NANOTRACE_EXPORT
+# define NANOTRACE_EXPORT_EXTERN_TEMPLATE
+# define NANOTRACE_EXPORT_TEMPLATE
#else
-#define NANOTRACE_EXPORT Q_DECL_IMPORT
+# define NANOTRACE_EXPORT Q_DECL_IMPORT
+# define NANOTRACE_EXPORT_EXTERN_TEMPLATE
+# define NANOTRACE_EXPORT_TEMPLATE
#endif
#define NANOTRACESHARED_EXPORT NANOTRACE_EXPORT
diff --git a/src/libs/nanotrace/nanotracehr.cpp b/src/libs/nanotrace/nanotracehr.cpp
index ada5bf65f5..0006227434 100644
--- a/src/libs/nanotrace/nanotracehr.cpp
+++ b/src/libs/nanotrace/nanotracehr.cpp
@@ -45,6 +45,18 @@ unsigned int getUnsignedIntegerHash(std::thread::id id)
return static_cast<unsigned int>(std::hash<std::thread::id>{}(id) & 0xFFFFFFFF);
}
+template<std::size_t capacity>
+constexpr bool isArgumentValid(const StaticString<capacity> &string)
+{
+ return string.isValid() && string.size();
+}
+
+template<typename String>
+constexpr bool isArgumentValid(const String &string)
+{
+ return string.size();
+}
+
template<typename TraceEvent>
void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, std::thread::id threadId)
{
@@ -67,23 +79,24 @@ void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, st
out << R"(,"flow_in":true)";
}
- if (event.arguments.size())
+ if (isArgumentValid(event.arguments)) {
out << R"(,"args":)" << event.arguments;
+ }
out << "}";
}
-void writeMetaEvent(TraceFile<Tracing::IsEnabled> *file, std::string_view key, std::string_view value)
+void writeMetaEvent(TraceFile<Tracing::IsEnabled> &file, std::string_view key, std::string_view value)
{
- std::lock_guard lock{file->fileMutex};
- auto &out = file->out;
+ std::lock_guard lock{file.fileMutex};
+ auto &out = file.out;
if (out.is_open()) {
- file->out << R"({"name":")" << key << R"(","ph":"M", "pid":)"
- << getUnsignedIntegerHash(QCoreApplication::applicationPid()) << R"(,"tid":)"
- << getUnsignedIntegerHash(std::this_thread::get_id()) << R"(,"args":{"name":")"
- << value << R"("}})"
- << ",\n";
+ file.out << R"({"name":")" << key << R"(","ph":"M", "pid":)"
+ << getUnsignedIntegerHash(QCoreApplication::applicationPid()) << R"(,"tid":)"
+ << getUnsignedIntegerHash(std::this_thread::get_id()) << R"(,"args":{"name":")"
+ << value << R"("}})"
+ << ",\n";
}
}
@@ -103,7 +116,6 @@ std::string getThreadName()
} // namespace
-namespace Internal {
template<typename String>
void convertToString(String &string, const QImage &image)
{
@@ -132,14 +144,11 @@ void convertToString(String &string, const QImage &image)
return "alpha premultiplied"sv;
}))));
- Internal::convertToString(string, dict);
+ convertToString(string, dict);
}
-template NANOTRACE_EXPORT void convertToString(std::string &string, const QImage &image);
template NANOTRACE_EXPORT void convertToString(ArgumentsString &string, const QImage &image);
-} // namespace Internal
-
template<typename TraceEvent>
void flushEvents(const Utils::span<TraceEvent> events,
std::thread::id threadId,
@@ -148,8 +157,8 @@ void flushEvents(const Utils::span<TraceEvent> events,
if (events.empty())
return;
- std::lock_guard lock{eventQueue.file->fileMutex};
- auto &out = eventQueue.file->out;
+ std::lock_guard lock{eventQueue.file.fileMutex};
+ auto &out = eventQueue.file.out;
if (out.is_open()) {
auto processId = QCoreApplication::applicationPid();
@@ -200,17 +209,17 @@ void finalizeFile(EnabledTraceFile &file)
template<typename TraceEvent>
void flushInThread(EnabledEventQueue<TraceEvent> &eventQueue)
{
- if (eventQueue.file->processing.valid())
- eventQueue.file->processing.wait();
+ if (eventQueue.file.processing.valid())
+ eventQueue.file.processing.wait();
auto flush = [&](const Utils::span<TraceEvent> &events, std::thread::id threadId) {
flushEvents(events, threadId, eventQueue);
};
- eventQueue.file->processing = std::async(std::launch::async,
- flush,
- eventQueue.currentEvents.subspan(0, eventQueue.eventsIndex),
- eventQueue.threadId);
+ eventQueue.file.processing = std::async(std::launch::async,
+ flush,
+ eventQueue.currentEvents.subspan(0, eventQueue.eventsIndex),
+ eventQueue.threadId);
eventQueue.currentEvents = eventQueue.currentEvents.data() == eventQueue.eventsOne.data()
? eventQueue.eventsTwo
: eventQueue.eventsOne;
@@ -222,28 +231,12 @@ template NANOTRACE_EXPORT void flushInThread(EnabledEventQueue<StringTraceEvent>
template NANOTRACE_EXPORT void flushInThread(
EnabledEventQueue<StringViewWithStringArgumentsTraceEvent> &eventQueue);
-namespace {
-TraceFile<tracingStatus()> globalTraceFile{"global.json"};
-thread_local EventQueueData<StringTraceEvent, 1000, tracingStatus()> globalEventQueueData{
- globalTraceFile};
-thread_local EventQueue s_globalEventQueue = globalEventQueueData.createEventQueue();
-} // namespace
-
-EventQueue<StringTraceEvent, tracingStatus()> &globalEventQueue()
-{
- return s_globalEventQueue;
-}
-
template<typename TraceEvent>
-EventQueue<TraceEvent, Tracing::IsEnabled>::EventQueue(EnabledTraceFile *file,
- TraceEventsSpan eventsOne,
- TraceEventsSpan eventsTwo)
+EventQueue<TraceEvent, Tracing::IsEnabled>::EventQueue(EnabledTraceFile &file)
: file{file}
- , eventsOne{eventsOne}
- , eventsTwo{eventsTwo}
- , currentEvents{eventsOne}
, threadId{std::this_thread::get_id()}
{
+ setEventsSpans(*eventArrayOne.get(), *eventArrayTwo.get());
Internal::EventQueueTracker<TraceEvent>::get().addQueue(this);
if (auto thread = QThread::currentThread()) {
auto name = getThreadName();
@@ -262,6 +255,15 @@ EventQueue<TraceEvent, Tracing::IsEnabled>::~EventQueue()
}
template<typename TraceEvent>
+void EventQueue<TraceEvent, Tracing::IsEnabled>::setEventsSpans(TraceEventsSpan eventsSpanOne,
+ TraceEventsSpan eventsSpanTwo)
+{
+ eventsOne = eventsSpanOne;
+ eventsTwo = eventsSpanTwo;
+ currentEvents = eventsSpanOne;
+}
+
+template<typename TraceEvent>
void EventQueue<TraceEvent, Tracing::IsEnabled>::flush()
{
std::lock_guard lock{mutex};
@@ -271,8 +273,9 @@ void EventQueue<TraceEvent, Tracing::IsEnabled>::flush()
}
}
-template class EventQueue<StringViewTraceEvent, Tracing::IsEnabled>;
-template class EventQueue<StringTraceEvent, Tracing::IsEnabled>;
-template class EventQueue<StringViewWithStringArgumentsTraceEvent, Tracing::IsEnabled>;
+template class NANOTRACE_EXPORT_TEMPLATE EventQueue<StringViewTraceEvent, Tracing::IsEnabled>;
+template class NANOTRACE_EXPORT_TEMPLATE EventQueue<StringTraceEvent, Tracing::IsEnabled>;
+template class NANOTRACE_EXPORT_TEMPLATE
+ EventQueue<StringViewWithStringArgumentsTraceEvent, Tracing::IsEnabled>;
} // namespace NanotraceHR
diff --git a/src/libs/nanotrace/nanotracehr.h b/src/libs/nanotrace/nanotracehr.h
index 62ec7dc03c..15f8109fe9 100644
--- a/src/libs/nanotrace/nanotracehr.h
+++ b/src/libs/nanotrace/nanotracehr.h
@@ -5,15 +5,23 @@
#include "nanotraceglobals.h"
+#include "staticstring.h"
+
#include <utils/smallstring.h>
#include <utils/span.h>
+#include <utils/utility.h>
#include <QByteArrayView>
+#include <QList>
+#include <QMap>
#include <QStringView>
+#include <QVarLengthArray>
+#include <QVariant>
#include <array>
#include <atomic>
#include <chrono>
+#include <exception>
#include <fstream>
#include <future>
#include <mutex>
@@ -32,15 +40,6 @@ static_assert(std::is_same_v<Clock::duration, std::chrono::nanoseconds>,
enum class Tracing { IsDisabled, IsEnabled };
-constexpr Tracing tracingStatus()
-{
-#ifdef NANOTRACEHR_ENABLED
- return Tracing::IsEnabled;
-#else
- return Tracing::IsDisabled;
-#endif
-}
-
#if __cplusplus >= 202002L && __has_cpp_attribute(msvc::no_unique_address)
# define NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
#elif __cplusplus >= 202002L && __has_cpp_attribute(no_unique_address) >= 201803L
@@ -49,7 +48,7 @@ constexpr Tracing tracingStatus()
# define NO_UNIQUE_ADDRESS
#endif
-using ArgumentsString = Utils::BasicSmallString<510>;
+using ArgumentsString = StaticString<3700>;
namespace Literals {
struct TracerLiteral
@@ -60,6 +59,8 @@ struct TracerLiteral
operator std::string() const { return std::string{text}; }
+ operator Utils::SmallString() const { return text; }
+
private:
constexpr TracerLiteral(std::string_view text)
: text{text}
@@ -86,44 +87,41 @@ struct IsDictonary
inline constexpr IsDictonary isDictonary;
-namespace Internal {
-
template<typename String>
void convertToString(String &string, std::string_view text)
{
- string.append(R"(")");
+ string.append('\"');
string.append(text);
- string.append(R"(")");
+ string.append('\"');
}
template<typename String>
void convertToString(String &string, const QImage &image);
-extern template NANOTRACE_EXPORT void convertToString(std::string &string, const QImage &image);
extern template NANOTRACE_EXPORT void convertToString(ArgumentsString &string, const QImage &image);
template<typename String, std::size_t size>
void convertToString(String &string, const char (&text)[size])
{
- string.append(R"(")");
- string.append(text);
- string.append(R"(")");
+ string.append('\"');
+ string.append(std::string_view{text, size - 1});
+ string.append('\"');
}
template<typename String>
void convertToString(String &string, QStringView text)
{
- string.append(R"(")");
+ string.append('\"');
string.append(Utils::PathString{text});
- string.append(R"(")");
+ string.append('\"');
}
template<typename String>
void convertToString(String &string, const QByteArray &text)
{
- string.append(R"(")");
+ string.append('\"');
string.append(std::string_view(text.data(), Utils::usize(text)));
- string.append(R"(")");
+ string.append('\"');
}
template<typename String>
@@ -135,38 +133,44 @@ void convertToString(String &string, bool isTrue)
string.append("false");
}
-template<typename String, typename Callable, typename = std::enable_if_t<std::is_invocable_v<Callable>>>
+template<typename String, typename Callable, typename std::enable_if_t<std::is_invocable_v<Callable>, bool> = true>
void convertToString(String &string, Callable &&callable)
{
convertToString(string, callable());
}
-template<typename String>
-void convertToString(String &string, int number)
+template<typename String, typename Number, typename std::enable_if_t<std::is_arithmetic_v<Number>, bool> = true>
+void convertToString(String &string, Number number)
{
- string.append(Utils::SmallString::number(number));
+ string.append(number);
}
-template<typename String>
-void convertToString(String &string, long long number)
+template<typename String, typename Enumeration, typename std::enable_if_t<std::is_enum_v<Enumeration>, bool> = true>
+void convertToString(String &string, Enumeration enumeration)
{
- string.append(Utils::SmallString::number(number));
+ string.append(Utils::to_underlying(enumeration));
}
template<typename String>
-void convertToString(String &string, double number)
+void convertToString(String &string, const QString &text)
{
- string.append(Utils::SmallString::number(number));
+ convertToString(string, QStringView{text});
}
-template<typename String, typename... Arguments>
-void convertToString(String &string, const std::tuple<const IsDictonary &, Arguments...> &dictonary);
-
-template<typename String, typename... Arguments>
-void convertToString(String &string, const std::tuple<const IsArray &, Arguments...> &list);
+template<typename String>
+void convertToString(String &string, const QVariant &value)
+{
+ convertToString(string, value.toString());
+}
-template<typename String, template<typename...> typename Container, typename... Arguments>
-void convertToString(String &string, const Container<Arguments...> &container);
+template<typename String, typename Type>
+void convertToString(String &string, const std::optional<Type> &value)
+{
+ if (value)
+ convertToString(string, *value);
+ else
+ convertToString(string, "empty optional");
+}
template<typename String, typename Value>
void convertArrayEntryToString(String &string, const Value &value)
@@ -178,15 +182,15 @@ void convertArrayEntryToString(String &string, const Value &value)
template<typename String, typename... Entries>
void convertArrayToString(String &string, const IsArray &, Entries &...entries)
{
- string.append(R"([)");
+ string.append('[');
(convertArrayEntryToString(string, entries), ...);
if (sizeof...(entries))
string.pop_back();
- string.append("]");
+ string.append(']');
}
template<typename String, typename... Arguments>
-void convertToString(String &string, const std::tuple<const IsArray &, Arguments...> &list)
+void convertToString(String &string, const std::tuple<IsArray, Arguments...> &list)
{
std::apply([&](auto &&...entries) { convertArrayToString(string, entries...); }, list);
}
@@ -196,59 +200,107 @@ void convertDictonaryEntryToString(String &string, const std::tuple<Key, Value>
{
const auto &[key, value] = argument;
convertToString(string, key);
- string.append(":");
+ string.append(':');
convertToString(string, value);
- string.append(",");
+ string.append(',');
}
template<typename String, typename... Entries>
void convertDictonaryToString(String &string, const IsDictonary &, Entries &...entries)
{
- string.append(R"({)");
+ string.append('{');
(convertDictonaryEntryToString(string, entries), ...);
if (sizeof...(entries))
string.pop_back();
- string.append("}");
+ string.append('}');
}
template<typename String, typename... Arguments>
-void convertToString(String &string, const std::tuple<const IsDictonary &, Arguments...> &dictonary)
+void convertToString(String &string, const std::tuple<IsDictonary, Arguments...> &dictonary)
{
std::apply([&](auto &&...entries) { convertDictonaryToString(string, entries...); }, dictonary);
}
-template<typename String, template<typename...> typename Container, typename... Arguments>
-void convertToString(String &string, const Container<Arguments...> &container)
+template<typename T>
+struct is_container : std::false_type
+{};
+
+template<typename... Arguments>
+struct is_container<std::vector<Arguments...>> : std::true_type
+{};
+
+template<typename... Arguments>
+struct is_container<QList<Arguments...>> : std::true_type
+{};
+
+template<typename T, qsizetype Prealloc>
+struct is_container<QVarLengthArray<T, Prealloc>> : std::true_type
+{};
+
+template<typename String, typename Container, typename std::enable_if_t<is_container<Container>::value, bool> = true>
+void convertToString(String &string, const Container &values)
{
- string.append("[");
- for (const auto &entry : container) {
- convertToString(string, entry);
- string.append(",");
+ string.append('[');
+
+ for (const auto &value : values) {
+ convertToString(string, value);
+ string.append(',');
}
- if (container.size())
+ if (values.size())
string.pop_back();
- string.append("]");
+ string.append(']');
}
-template<typename String, typename... Arguments>
-String toArguments(Arguments &&...arguments)
+template<typename T>
+struct is_map : std::false_type
+{};
+
+template<typename... Arguments>
+struct is_map<QtPrivate::QKeyValueRange<Arguments...>> : std::true_type
+{};
+
+template<typename... Arguments>
+struct is_map<std::map<Arguments...>> : std::true_type
+{};
+
+template<typename String, typename Map, typename std::enable_if_t<is_map<Map>::value, bool> = true>
+void convertToString(String &string, const Map &map)
{
- if constexpr (tracingStatus() == Tracing::IsEnabled) {
- String text;
- constexpr auto argumentCount = sizeof...(Arguments);
- text.append("{");
- (convertDictonaryEntryToString(text, arguments), ...);
- if (argumentCount)
- text.pop_back();
-
- text.append("}");
-
- return text;
- } else {
- return {};
+ string.append('{');
+
+ for (const auto &[key, value] : map) {
+ convertToString(string, key);
+ string.append(':');
+ convertToString(string, value);
+ string.append(',');
}
+
+ if (map.begin() != map.end())
+ string.pop_back();
+
+ string.append('}');
+}
+
+template<typename String, typename Key, typename Value>
+void convertToString(String &string, const QMap<Key, Value> &dictonary)
+{
+ convertToString(string, dictonary.asKeyValueRange());
+}
+
+namespace Internal {
+
+template<typename String, typename... Arguments>
+void toArguments(String &text, Arguments &&...arguments)
+{
+ constexpr auto argumentCount = sizeof...(Arguments);
+ text.append('{');
+ (convertDictonaryEntryToString(text, arguments), ...);
+ if (argumentCount)
+ text.pop_back();
+
+ text.append('}');
}
inline std::string_view toArguments(std::string_view arguments)
@@ -257,48 +309,58 @@ inline std::string_view toArguments(std::string_view arguments)
}
template<typename String>
-void appendArguments(String &eventArguments)
+void setArguments(String &eventArguments)
{
- eventArguments = {};
+ if constexpr (std::is_same_v<String, std::string_view>)
+ eventArguments = {};
+ else
+ eventArguments.clear();
}
template<typename String>
-void appendArguments(String &eventArguments, TracerLiteral arguments)
+void setArguments(String &eventArguments, TracerLiteral arguments)
{
eventArguments = arguments;
}
template<typename String, typename... Arguments>
-[[maybe_unused]] void appendArguments(String &eventArguments, Arguments &&...arguments)
+[[maybe_unused]] void setArguments(String &eventArguments, Arguments &&...arguments)
{
static_assert(
!std::is_same_v<String, std::string_view>,
R"(The arguments type of the tracing event queue is a string view. You can only provide trace token arguments as TracerLiteral (""_t).)");
- eventArguments = Internal::toArguments<String>(std::forward<Arguments>(arguments)...);
+ if constexpr (std::is_same_v<String, std::string_view>)
+ eventArguments = {};
+ else
+ eventArguments.clear();
+ Internal::toArguments(eventArguments, std::forward<Arguments>(arguments)...);
}
} // namespace Internal
template<typename Key, typename Value>
-auto keyValue(Key &&key, Value &&value)
+auto keyValue(const Key &key, Value &&value)
{
- return std::forward_as_tuple(std::forward<Key>(key), std::forward<Value>(value));
+ if constexpr (std::is_lvalue_reference_v<Value>)
+ return std::tuple<const Key &, const Value &>(key, value);
+ else
+ return std::tuple<const Key &, std::decay_t<Value>>(key, value);
}
template<typename... Entries>
auto dictonary(Entries &&...entries)
{
- return std::forward_as_tuple(isDictonary, std::forward<Entries>(entries)...);
+ return std::make_tuple(isDictonary, std::forward<Entries>(entries)...);
}
template<typename... Entries>
auto array(Entries &&...entries)
{
- return std::forward_as_tuple(isArray, std::forward<Entries>(entries)...);
+ return std::make_tuple(isArray, std::forward<Entries>(entries)...);
}
-enum class IsFlow : std::size_t { No = 0, Out = 1 << 0, In = 1 << 1, InOut = In | Out };
+enum class IsFlow : char { No = 0, Out = 1 << 0, In = 1 << 1, InOut = In | Out };
inline bool operator&(IsFlow first, IsFlow second)
{
@@ -307,7 +369,7 @@ inline bool operator&(IsFlow first, IsFlow second)
}
template<typename String, typename ArgumentsString>
-struct TraceEvent
+struct alignas(4096) TraceEvent
{
using StringType = String;
using ArgumentType = std::conditional_t<std::is_same_v<String, std::string_view>, TracerLiteral, String>;
@@ -322,18 +384,18 @@ struct TraceEvent
String name;
String category;
- ArgumentsString arguments;
TimePoint time;
Duration duration;
std::size_t id = 0;
- std::size_t bindId : 62;
- IsFlow flow : 2;
+ std::size_t bindId = 0;
+ IsFlow flow = IsFlow::No;
char type = ' ';
+ ArgumentsString arguments;
};
using StringViewTraceEvent = TraceEvent<std::string_view, std::string_view>;
using StringViewWithStringArgumentsTraceEvent = TraceEvent<std::string_view, ArgumentsString>;
-using StringTraceEvent = TraceEvent<std::string, std::string>;
+using StringTraceEvent = TraceEvent<Utils::SmallString, ArgumentsString>;
enum class IsEnabled { No, Yes };
@@ -411,6 +473,8 @@ class EventQueue
{
public:
using IsActive = std::false_type;
+
+ template<typename TraceFile> EventQueue(TraceFile &) {}
};
namespace Internal {
@@ -421,7 +485,13 @@ class EventQueueTracker
using Queue = EventQueue<TraceEvent, Tracing::IsEnabled>;
public:
- EventQueueTracker() = default;
+ EventQueueTracker()
+ {
+ terminateHandler = std::get_terminate();
+
+ std::set_terminate([]() { EventQueueTracker::get().terminate(); });
+ }
+
EventQueueTracker(const EventQueueTracker &) = delete;
EventQueueTracker(EventQueueTracker &&) = delete;
EventQueueTracker &operator=(const EventQueueTracker &) = delete;
@@ -455,8 +525,25 @@ public:
}
private:
+ void terminate()
+ {
+ flushAll();
+ if (terminateHandler)
+ terminateHandler();
+ }
+
+ void flushAll()
+ {
+ std::lock_guard lock{mutex};
+
+ for (auto queue : queues)
+ queue->flush();
+ }
+
+private:
std::mutex mutex;
std::vector<Queue *> queues;
+ std::terminate_handler terminateHandler = nullptr;
};
} // namespace Internal
@@ -464,14 +551,17 @@ template<typename TraceEvent>
class EventQueue<TraceEvent, Tracing::IsEnabled>
{
using TraceEventsSpan = Utils::span<TraceEvent>;
+ using TraceEvents = std::array<TraceEvent, 1000>;
public:
using IsActive = std::true_type;
- EventQueue(EnabledTraceFile *file, TraceEventsSpan eventsOne, TraceEventsSpan eventsTwo);
+ EventQueue(EnabledTraceFile &file);
~EventQueue();
+ void setEventsSpans(TraceEventsSpan eventsOne, TraceEventsSpan eventsTwo);
+
void flush();
EventQueue(const EventQueue &) = delete;
@@ -479,7 +569,9 @@ public:
EventQueue &operator=(const EventQueue &) = delete;
EventQueue &operator=(EventQueue &&) = delete;
- EnabledTraceFile *file = nullptr;
+ EnabledTraceFile &file;
+ std::unique_ptr<TraceEvents> eventArrayOne = std::make_unique<TraceEvents>();
+ std::unique_ptr<TraceEvents> eventArrayTwo = std::make_unique<TraceEvents>();
TraceEventsSpan eventsOne;
TraceEventsSpan eventsTwo;
TraceEventsSpan currentEvents;
@@ -496,44 +588,10 @@ using StringViewWithStringArgumentsEventQueue = EventQueue<StringViewWithStringA
template<Tracing isEnabled>
using StringEventQueue = EventQueue<StringTraceEvent, isEnabled>;
-extern template class NANOTRACE_EXPORT EventQueue<StringViewTraceEvent, Tracing::IsEnabled>;
-extern template class NANOTRACE_EXPORT EventQueue<StringTraceEvent, Tracing::IsEnabled>;
-extern template class NANOTRACE_EXPORT EventQueue<StringViewWithStringArgumentsTraceEvent, Tracing::IsEnabled>;
-
-template<typename TraceEvent, std::size_t eventCount, Tracing isEnabled>
-class EventQueueData
-{
-public:
- using IsActive = std::true_type;
-
- EventQueueData(TraceFile<Tracing::IsDisabled> &) {}
-
- EventQueue<TraceEvent, Tracing::IsDisabled> createEventQueue() { return {}; }
-};
-
-template<typename TraceEvent, std::size_t eventCount>
-class EventQueueData<TraceEvent, eventCount, Tracing::IsEnabled>
-{
- using TraceEvents = std::array<TraceEvent, eventCount>;
-
-public:
- using IsActive = std::true_type;
-
- EventQueueData(EnabledTraceFile &file)
- : file{file}
- {}
-
- EventQueue<TraceEvent, Tracing::IsEnabled> createEventQueue()
- {
- return {&file, eventsOne, eventsTwo};
- }
-
- EnabledTraceFile &file;
- TraceEvents eventsOne;
- TraceEvents eventsTwo;
-};
-
-NANOTRACE_EXPORT EventQueue<StringTraceEvent, tracingStatus()> &globalEventQueue();
+extern template class NANOTRACE_EXPORT_EXTERN_TEMPLATE EventQueue<StringViewTraceEvent, Tracing::IsEnabled>;
+extern template class NANOTRACE_EXPORT_EXTERN_TEMPLATE EventQueue<StringTraceEvent, Tracing::IsEnabled>;
+extern template class NANOTRACE_EXPORT_EXTERN_TEMPLATE
+ EventQueue<StringViewWithStringArgumentsTraceEvent, Tracing::IsEnabled>;
template<typename TraceEvent>
TraceEvent &getTraceEvent(EnabledEventQueue<TraceEvent> &eventQueue)
@@ -574,6 +632,7 @@ public:
BasicEnabledToken &operator=(const BasicEnabledToken &) = default;
BasicEnabledToken(BasicEnabledToken &&other) noexcept = default;
BasicEnabledToken &operator=(BasicEnabledToken &&other) noexcept = default;
+
~BasicEnabledToken() {}
constexpr explicit operator bool() const { return false; }
@@ -672,7 +731,7 @@ public:
std::size_t bindId = 0;
if (m_id) {
- auto category = m_category();
+ auto &category = m_category();
bindId = category.createBindId();
category.begin('b', m_id, name, bindId, IsFlow::Out, std::forward<Arguments>(arguments)...);
}
@@ -727,101 +786,6 @@ using StringViewWithStringArgumentsCategory = Category<StringViewWithStringArgum
using DisabledToken = Token<StringViewCategory<Tracing::IsDisabled>, Tracing::IsDisabled>;
template<typename Category, Tracing isEnabled>
-class ObjectToken : public BasicDisabledToken
-{
-public:
- using ArgumentType = typename Category::ArgumentType;
-
- ObjectToken() = default;
- ObjectToken(const ObjectToken &) = delete;
- ObjectToken &operator=(const ObjectToken &) = delete;
- ObjectToken(ObjectToken &&other) noexcept = default;
- ObjectToken &operator=(ObjectToken &&other) noexcept = default;
- ~ObjectToken() = default;
-
- template<typename... Arguments>
- void change(ArgumentType, Arguments &&...)
- {}
-};
-
-template<typename Category>
-class ObjectToken<Category, Tracing::IsEnabled> : public BasicEnabledToken
-{
- using CategoryFunctionPointer = typename Category::CategoryFunctionPointer;
-
- ObjectToken(std::string_view name, std::size_t id, CategoryFunctionPointer category)
- : m_name{name}
- , m_id{id}
- , m_category{category}
- {}
-
-public:
- using StringType = typename Category::StringType;
- using ArgumentType = typename Category::ArgumentType;
-
- friend Category;
-
- ObjectToken(const ObjectToken &other)
- : m_name{other.m_name}
- , m_category{other.m_category}
- {
- if (other.m_id)
- m_id = m_category().beginObject(m_name).m_id;
- }
-
- ObjectToken &operator=(const ObjectToken &other)
- {
- if (this != &other) {
- ~ObjectToken();
- if (other.m_id) {
- m_category = other.m_category;
- m_name = other.m_name;
- m_id = other.m_category->beginObject(other.m_name).m_id;
- }
- }
- }
-
- ObjectToken(ObjectToken &&other) noexcept
- : m_name{std::move(other.m_name)}
- , m_id{std::exchange(other.m_id, 0)}
- , m_category{std::exchange(other.m_category, nullptr)}
- {}
-
- ObjectToken &operator=(ObjectToken &&other) noexcept
- {
- if (&other != this) {
- m_name = std::move(other.m_name);
- m_id = std::exchange(other.m_id, 0);
- m_category = std::exchange(other.m_category, nullptr);
- }
-
- return *this;
- }
-
- ~ObjectToken()
- {
- if (m_id)
- m_category().end('e', m_id, std::move(m_name));
-
- m_id = 0;
- }
-
- template<typename... Arguments>
- void change(ArgumentType name, Arguments &&...arguments)
- {
- if (m_id) {
- m_category().tick(
- 'n', m_id, std::move(name), 0, IsFlow::No, std::forward<Arguments>(arguments)...);
- }
- }
-
-private:
- StringType m_name;
- std::size_t m_id = 0;
- CategoryFunctionPointer m_category = nullptr;
-};
-
-template<typename Category, Tracing isEnabled>
class AsynchronousToken : public BasicDisabledToken
{
public:
@@ -847,6 +811,12 @@ public:
}
template<typename... Arguments>
+ [[nodiscard]] AsynchronousToken begin(const FlowTokenType &, ArgumentType, Arguments &&...)
+ {
+ return {};
+ }
+
+ template<typename... Arguments>
[[nodiscard]] std::pair<AsynchronousToken, FlowTokenType> beginWithFlow(ArgumentType,
Arguments &&...)
{
@@ -858,6 +828,10 @@ public:
{}
template<typename... Arguments>
+ void tick(const FlowTokenType &, ArgumentType, Arguments &&...)
+ {}
+
+ template<typename... Arguments>
FlowTokenType tickWithFlow(ArgumentType, Arguments &&...)
{
return {};
@@ -866,8 +840,9 @@ public:
template<typename... Arguments>
void end(Arguments &&...)
{}
-};
+ static constexpr bool categoryIsActive() { return Category::isActive(); }
+};
using DisabledAsynchronousToken = AsynchronousToken<StringViewCategory<Tracing::IsDisabled>,
Tracing::IsDisabled>;
@@ -938,19 +913,35 @@ public:
}
template<typename... Arguments>
+ [[nodiscard]] AsynchronousToken begin(const FlowTokenType &flowToken,
+ ArgumentType name,
+ Arguments &&...arguments)
+ {
+ if (m_id)
+ m_category().begin('b',
+ m_id,
+ name,
+ flowToken.bindId(),
+ IsFlow::In,
+ std::forward<Arguments>(arguments)...);
+
+ return AsynchronousToken{std::move(name), m_id, m_category};
+ }
+
+ template<typename... Arguments>
[[nodiscard]] std::pair<AsynchronousToken, FlowTokenType> beginWithFlow(ArgumentType name,
Arguments &&...arguments)
{
std::size_t bindId = 0;
if (m_id) {
- auto category = m_category();
+ auto &category = m_category();
bindId = category.createBindId();
category.begin('b', m_id, name, bindId, IsFlow::Out, std::forward<Arguments>(arguments)...);
}
return {std::piecewise_construct,
- std::forward_as_tuple(std::move(name), m_id, m_category),
+ std::forward_as_tuple(PrivateTag{}, std::move(name), m_id, m_category),
std::forward_as_tuple(PrivateTag{}, std::move(name), bindId, m_category)};
}
@@ -964,17 +955,30 @@ public:
}
template<typename... Arguments>
+ void tick(const FlowTokenType &flowToken, ArgumentType name, Arguments &&...arguments)
+ {
+ if (m_id) {
+ m_category().tick('n',
+ m_id,
+ std::move(name),
+ flowToken.bindId(),
+ IsFlow::In,
+ std::forward<Arguments>(arguments)...);
+ }
+ }
+
+ template<typename... Arguments>
FlowTokenType tickWithFlow(ArgumentType name, Arguments &&...arguments)
{
std::size_t bindId = 0;
if (m_id) {
- auto category = m_category();
+ auto &category = m_category();
bindId = category.createBindId();
category.tick('n', m_id, name, bindId, IsFlow::Out, std::forward<Arguments>(arguments)...);
}
- return {std::move(name), bindId, m_category};
+ return {PrivateTag{}, std::move(name), bindId, m_category};
}
template<typename... Arguments>
@@ -986,6 +990,8 @@ public:
m_id = 0;
}
+ static constexpr bool categoryIsActive() { return Category::isActive(); }
+
private:
StringType m_name;
std::size_t m_id = 0;
@@ -1087,9 +1093,9 @@ public:
std::size_t id = 0;
if (m_bindId) {
- auto category = m_category();
+ auto &category = m_category();
id = category.createId();
- category->begin('b', id, name, m_bindId, IsFlow::In, std::forward<Arguments>(arguments)...);
+ category.begin('b', id, name, m_bindId, IsFlow::In, std::forward<Arguments>(arguments)...);
}
return {std::move(name), id, m_category};
@@ -1103,10 +1109,10 @@ public:
std::size_t bindId = 0;
if (m_bindId) {
- auto category = m_category();
+ auto &category = m_category();
id = category.createId();
bindId = category.createBindId();
- category->begin('b', id, name, bindId, IsFlow::InOut, std::forward<Arguments>(arguments)...);
+ category.begin('b', id, name, bindId, IsFlow::InOut, std::forward<Arguments>(arguments)...);
}
return {std::piecewise_construct,
@@ -1153,6 +1159,8 @@ public:
m_category().tick('i', 0, name, m_bindId, IsFlow::In, std::forward<Arguments>(arguments)...);
}
+ std::size_t bindId() const { return m_bindId; }
+
private:
StringType m_name;
std::size_t m_bindId = 0;
@@ -1167,7 +1175,6 @@ public:
using ArgumentType = typename TraceEvent::ArgumentType;
using ArgumentsStringType = typename TraceEvent::ArgumentsStringType;
using AsynchronousTokenType = AsynchronousToken<Category, Tracing::IsDisabled>;
- using ObjectTokenType = ObjectToken<Category, Tracing::IsDisabled>;
using FlowTokenType = FlowToken<Category, Tracing::IsDisabled>;
using TracerType = Tracer<Category, std::false_type>;
using TokenType = Token<Category, Tracing::IsDisabled>;
@@ -1190,12 +1197,6 @@ public:
{}
template<typename... Arguments>
- [[nodiscard]] ObjectTokenType beginObject(ArgumentType, Arguments &&...)
- {
- return {};
- }
-
- template<typename... Arguments>
[[nodiscard]] TracerType beginDuration(ArgumentType, Arguments &&...)
{
return {};
@@ -1208,6 +1209,10 @@ public:
return std::pair<TracerType, FlowTokenType>();
}
+ template<typename... Arguments>
+ void threadEvent(ArgumentType, Arguments &&...)
+ {}
+
static constexpr bool isActive() { return false; }
};
@@ -1223,14 +1228,12 @@ public:
using ArgumentsStringType = typename TraceEvent::ArgumentsStringType;
using StringType = typename TraceEvent::StringType;
using AsynchronousTokenType = AsynchronousToken<Category, Tracing::IsEnabled>;
- using ObjectTokenType = ObjectToken<Category, Tracing::IsEnabled>;
using FlowTokenType = FlowToken<Category, Tracing::IsEnabled>;
using TracerType = Tracer<Category, std::true_type>;
using TokenType = Token<Category, Tracing::IsEnabled>;
using CategoryFunctionPointer = Category &(*) ();
friend AsynchronousTokenType;
- friend ObjectTokenType;
friend TokenType;
friend FlowTokenType;
friend TracerType;
@@ -1247,6 +1250,9 @@ public:
m_bindIdCounter = m_globalBindIdCounter += 1ULL << 32;
}
+ Category(const Category &) = delete;
+ Category &operator=(const Category &) = delete;
+
template<typename... Arguments>
[[nodiscard]] AsynchronousTokenType beginAsynchronous(ArgumentType traceName,
Arguments &&...arguments)
@@ -1273,16 +1279,6 @@ public:
}
template<typename... Arguments>
- [[nodiscard]] ObjectTokenType beginObject(ArgumentType traceName, Arguments &&...arguments)
- {
- std::size_t id = createId();
-
- begin('b', id, std::move(traceName), 0, IsFlow::No, std::forward<Arguments>(arguments)...);
-
- return {traceName, id, m_self};
- }
-
- template<typename... Arguments>
[[nodiscard]] TracerType beginDuration(ArgumentType traceName, Arguments &&...arguments)
{
return {traceName, m_self, std::forward<Arguments>(arguments)...};
@@ -1304,6 +1300,24 @@ public:
std::forward_as_tuple(PrivateTag{}, traceName, bindId, m_self)};
}
+ template<typename... Arguments>
+ void threadEvent(ArgumentType traceName, Arguments &&...arguments)
+ {
+ if (isEnabled == IsEnabled::No)
+ return;
+
+ auto &traceEvent = getTraceEvent(m_eventQueue);
+
+ traceEvent.time = Clock::now();
+ traceEvent.name = std::move(traceName);
+ traceEvent.category = traceName;
+ traceEvent.type = 'i';
+ traceEvent.id = 0;
+ traceEvent.bindId = 0;
+ traceEvent.flow = IsFlow::No;
+ Internal::setArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
+ }
+
EnabledEventQueue<TraceEvent> &eventQueue() const { return m_eventQueue; }
std::string_view name() const { return m_name; }
@@ -1337,7 +1351,7 @@ private:
traceEvent.id = id;
traceEvent.bindId = bindId;
traceEvent.flow = flow;
- Internal::appendArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
+ Internal::setArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
traceEvent.time = Clock::now();
}
@@ -1363,7 +1377,7 @@ private:
traceEvent.id = id;
traceEvent.bindId = bindId;
traceEvent.flow = flow;
- Internal::appendArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
+ Internal::setArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
}
template<typename... Arguments>
@@ -1383,9 +1397,11 @@ private:
traceEvent.id = id;
traceEvent.bindId = 0;
traceEvent.flow = IsFlow::No;
- Internal::appendArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
+ Internal::setArguments(traceEvent.arguments, std::forward<Arguments>(arguments)...);
}
+ CategoryFunctionPointer self() { return m_self; }
+
private:
StringType m_name;
EnabledEventQueue<TraceEvent> &m_eventQueue;
@@ -1470,11 +1486,8 @@ class Tracer<Category, std::true_type>
, flow{flow}
, m_category{category}
{
- if (category().isEnabled == IsEnabled::Yes) {
- Internal::appendArguments<ArgumentsStringType>(m_arguments,
- std::forward<Arguments>(arguments)...);
- m_start = Clock::now();
- }
+ if (category().isEnabled == IsEnabled::Yes)
+ sendBeginTrace(std::forward<Arguments>(arguments)...);
}
public:
@@ -1493,13 +1506,15 @@ public:
: m_name{name}
, m_category{category}
{
- if (category().isEnabled == IsEnabled::Yes) {
- Internal::appendArguments<ArgumentsStringType>(m_arguments,
- std::forward<Arguments>(arguments)...);
- m_start = Clock::now();
- }
+ if (category().isEnabled == IsEnabled::Yes)
+ sendBeginTrace(std::forward<Arguments>(arguments)...);
}
+ template<typename... Arguments>
+ [[nodiscard]] Tracer(ArgumentType name, Category &category, Arguments &&...arguments)
+ : Tracer(std::move(name), category.self(), std::forward<Arguments>(arguments)...)
+ {}
+
Tracer(const Tracer &) = delete;
Tracer &operator=(const Tracer &) = delete;
Tracer(Tracer &&other) noexcept = delete;
@@ -1507,7 +1522,7 @@ public:
TokenType createToken() { return {0, m_category}; }
- ~Tracer() { sendTrace(); }
+ ~Tracer() { sendEndTrace(); }
template<typename... Arguments>
Tracer beginDuration(ArgumentType name, Arguments &&...arguments)
@@ -1524,44 +1539,52 @@ public:
template<typename... Arguments>
void end(Arguments &&...arguments)
{
- sendTrace(std::forward<Arguments>(arguments)...);
+ sendEndTrace(std::forward<Arguments>(arguments)...);
m_name = {};
}
private:
template<typename... Arguments>
- void sendTrace(Arguments &&...arguments)
+ void sendBeginTrace(Arguments &&...arguments)
+ {
+ auto &category = m_category();
+ if (category.isEnabled == IsEnabled::Yes) {
+ auto &traceEvent = getTraceEvent(category.eventQueue());
+ traceEvent.name = m_name;
+ traceEvent.category = category.name();
+ traceEvent.bindId = m_bindId;
+ traceEvent.flow = flow;
+ traceEvent.type = 'B';
+ Internal::setArguments<ArgumentsStringType>(traceEvent.arguments,
+ std::forward<Arguments>(arguments)...);
+ traceEvent.time = Clock::now();
+ }
+ }
+
+ template<typename... Arguments>
+ void sendEndTrace(Arguments &&...arguments)
{
if (m_name.size()) {
- auto category = m_category();
+ auto &category = m_category();
if (category.isEnabled == IsEnabled::Yes) {
- auto duration = Clock::now() - m_start;
+ auto end = Clock::now();
auto &traceEvent = getTraceEvent(category.eventQueue());
- traceEvent.name = m_name;
+ traceEvent.name = std::move(m_name);
traceEvent.category = category.name();
- traceEvent.time = m_start;
- traceEvent.duration = duration;
+ traceEvent.time = end;
traceEvent.bindId = m_bindId;
traceEvent.flow = flow;
- traceEvent.type = 'X';
- if (sizeof...(arguments)) {
- m_arguments.clear();
- Internal::appendArguments<ArgumentsStringType>(traceEvent.arguments,
- std::forward<Arguments>(
- arguments)...);
- } else {
- traceEvent.arguments = m_arguments;
- }
+ traceEvent.type = 'E';
+ Internal::setArguments<ArgumentsStringType>(traceEvent.arguments,
+ std::forward<Arguments>(arguments)...);
}
}
}
private:
- TimePoint m_start;
StringType m_name;
- ArgumentsStringType m_arguments;
- std::size_t m_bindId;
- IsFlow flow;
+ std::size_t m_bindId = 0;
+ IsFlow flow = IsFlow::No;
CategoryFunctionPointer m_category;
};
@@ -1569,51 +1592,4 @@ template<typename Category, typename... Arguments>
Tracer(typename Category::ArgumentType name, Category &category, Arguments &&...)
-> Tracer<Category, typename Category::IsActive>;
-#ifdef NANOTRACEHR_ENABLED
-class GlobalTracer
-{
-public:
- template<typename... Arguments>
- [[nodiscard]] GlobalTracer(std::string name, std::string category, Arguments &&...arguments)
- : m_name{std::move(name)}
- , m_category{std::move(category)}
- {
- if (globalEventQueue().isEnabled == IsEnabled::Yes) {
- Internal::appendArguments(m_arguments, std::forward<Arguments>(arguments)...);
- m_start = Clock::now();
- }
- }
-
- ~GlobalTracer()
- {
- if (globalEventQueue().isEnabled == IsEnabled::Yes) {
- auto duration = Clock::now() - m_start;
- auto &traceEvent = getTraceEvent(globalEventQueue());
- traceEvent.name = std::move(m_name);
- traceEvent.category = std::move(m_category);
- traceEvent.arguments = std::move(m_arguments);
- traceEvent.time = std::move(m_start);
- traceEvent.duration = std::move(duration);
- traceEvent.type = 'X';
- }
- }
-
-private:
- TimePoint m_start;
- std::string m_name;
- std::string m_category;
- std::string m_arguments;
-};
-#else
-class GlobalTracer
-{
-public:
- GlobalTracer(std::string_view, std::string_view, std::string_view) {}
-
- GlobalTracer(std::string_view, std::string_view) {}
-
- ~GlobalTracer() {}
-};
-#endif
-
} // namespace NanotraceHR
diff --git a/src/libs/nanotrace/staticstring.h b/src/libs/nanotrace/staticstring.h
new file mode 100644
index 0000000000..d787bd2fe3
--- /dev/null
+++ b/src/libs/nanotrace/staticstring.h
@@ -0,0 +1,116 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include <utils/smallstringview.h>
+
+#if !(defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L))
+# include <QLocale>
+#endif
+
+#include <array>
+#include <charconv>
+#include <limits>
+
+namespace NanotraceHR {
+
+template<std::size_t Capacity>
+class StaticString
+{
+public:
+ StaticString() = default;
+ StaticString(const StaticString &) = delete;
+ StaticString &operator=(const StaticString &) = delete;
+
+ char *data() { return m_data.data(); }
+
+ const char *data() const { return m_data.data(); }
+
+ void append(Utils::SmallStringView string) noexcept
+ {
+ auto newSize = m_size + string.size();
+
+ if (newSize <= Capacity) {
+ std::char_traits<char>::copy(std::next(data(), static_cast<std::ptrdiff_t>(m_size)),
+ string.data(),
+ string.size());
+ m_size = newSize;
+ } else {
+ m_size = Capacity + 1;
+ }
+ }
+
+ void append(char character) noexcept
+ {
+ auto newSize = m_size + 1;
+
+ if (newSize <= Capacity) {
+ auto current = std::next(data(), static_cast<std::ptrdiff_t>(m_size));
+ *current = character;
+
+ m_size = newSize;
+ } else {
+ m_size = Capacity + 1;
+ }
+ }
+
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ void append(Type number)
+ {
+#if !(defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L))
+ if constexpr (std::is_floating_point_v<Type>) {
+ QLocale locale{QLocale::Language::C};
+ append(locale.toString(number).toStdString());
+ return;
+ }
+#endif
+ // 2 bytes for the sign and because digits10 returns the floor
+ char buffer[std::numeric_limits<Type>::digits10 + 2];
+ auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
+ auto endOfConversionString = result.ptr;
+
+ append({buffer, endOfConversionString});
+ }
+
+ void pop_back() { --m_size; }
+
+ StaticString &operator+=(Utils::SmallStringView string) noexcept
+ {
+ append(string);
+
+ return *this;
+ }
+
+ StaticString &operator+=(char character) noexcept
+ {
+ append(character);
+
+ return *this;
+ }
+
+ template<typename Type, typename = std::enable_if_t<std::is_arithmetic_v<Type>>>
+ StaticString &operator+=(Type number) noexcept
+ {
+ append(number);
+
+ return *this;
+ }
+
+ bool isValid() const { return m_size <= Capacity; }
+
+ std::size_t size() const { return m_size; }
+
+ friend std::ostream &operator<<(std::ostream &out, const StaticString &text)
+ {
+ return out << std::string_view{text.data(), text.size()};
+ }
+
+ void clear() { m_size = 0; }
+
+private:
+ std::array<char, Capacity> m_data;
+ std::size_t m_size = 0;
+};
+
+} // namespace NanotraceHR
diff --git a/src/libs/qmljs/CMakeLists.txt b/src/libs/qmljs/CMakeLists.txt
index 4ca15cf7cc..a4966a8b4a 100644
--- a/src/libs/qmljs/CMakeLists.txt
+++ b/src/libs/qmljs/CMakeLists.txt
@@ -33,7 +33,6 @@ add_qtc_library(QmlJS
qmljsfindexportedcpptypes.cpp qmljsfindexportedcpptypes.h
qmljsicons.cpp qmljsicons.h
qmljsimportdependencies.cpp qmljsimportdependencies.h
- qmljsindenter.cpp qmljsindenter.h
qmljsinterpreter.cpp qmljsinterpreter.h
qmljslineinfo.cpp qmljslineinfo.h
qmljslink.cpp qmljslink.h
diff --git a/src/libs/qmljs/jsoncheck.cpp b/src/libs/qmljs/jsoncheck.cpp
index 1d7c04e50c..3cb44111f9 100644
--- a/src/libs/qmljs/jsoncheck.cpp
+++ b/src/libs/qmljs/jsoncheck.cpp
@@ -447,7 +447,7 @@ JsonValue *JsonValue::build(const QVariant &variant, JsonMemoryPool *pool)
{
switch (variant.typeId()) {
- case QVariant::List: {
+ case QMetaType::QVariantList: {
auto newValue = new (pool) JsonArrayValue;
const QList<QVariant> list = variant.toList();
for (const QVariant &element : list)
@@ -455,7 +455,7 @@ JsonValue *JsonValue::build(const QVariant &variant, JsonMemoryPool *pool)
return newValue;
}
- case QVariant::Map: {
+ case QMetaType::QVariantMap: {
auto newValue = new (pool) JsonObjectValue;
const QVariantMap variantMap = variant.toMap();
for (QVariantMap::const_iterator it = variantMap.begin(); it != variantMap.end(); ++it)
@@ -463,19 +463,19 @@ JsonValue *JsonValue::build(const QVariant &variant, JsonMemoryPool *pool)
return newValue;
}
- case QVariant::String:
+ case QMetaType::QString:
return new (pool) JsonStringValue(variant.toString());
- case QVariant::Int:
+ case QMetaType::Int:
return new (pool) JsonIntValue(variant.toInt());
- case QVariant::Double:
+ case QMetaType::Double:
return new (pool) JsonDoubleValue(variant.toDouble());
- case QVariant::Bool:
+ case QMetaType::Bool:
return new (pool) JsonBooleanValue(variant.toBool());
- case QVariant::Invalid:
+ case QMetaType::UnknownType:
return new (pool) JsonNullValue;
default:
diff --git a/src/libs/qmljs/qmljs.qbs b/src/libs/qmljs/qmljs.qbs
index 18434c83ba..d5cc21faf1 100644
--- a/src/libs/qmljs/qmljs.qbs
+++ b/src/libs/qmljs/qmljs.qbs
@@ -28,7 +28,6 @@ QtcLibrary {
"qmljsfindexportedcpptypes.cpp", "qmljsfindexportedcpptypes.h",
"qmljsicons.cpp", "qmljsicons.h",
"qmljsimportdependencies.cpp", "qmljsimportdependencies.h",
- "qmljsindenter.cpp", "qmljsindenter.h",
"qmljsinterpreter.cpp", "qmljsinterpreter.h",
"qmljsdialect.cpp", "qmljsdialect.h",
"qmljslineinfo.cpp", "qmljslineinfo.h",
diff --git a/src/libs/qmljs/qmljsbind.cpp b/src/libs/qmljs/qmljsbind.cpp
index 6ccddcbe63..163680998f 100644
--- a/src/libs/qmljs/qmljsbind.cpp
+++ b/src/libs/qmljs/qmljsbind.cpp
@@ -333,6 +333,23 @@ bool Bind::visit(UiInlineComponent *ast)
return true;
}
+bool Bind::visit(UiEnumDeclaration *ast)
+{
+ if (_currentObjectValue) {
+ UiEnumValue *value = new UiEnumValue(ast, &_valueOwner, _currentObjectValue->originId());
+ _qmlObjects.insert(ast, value);
+ _currentObjectValue->setMember(ast->name, value);
+ // add enum value's keys as member to its parent object
+ for (auto it = ast->members; it; it = it->next) {
+ const QString name = it->member.toString();
+ _currentObjectValue->setMember(name, _valueOwner.intValue());
+ _currentObjectValue->setPropertyInfo(
+ name, PropertyInfo(PropertyInfo::Readable | PropertyInfo::ValueType));
+ }
+ }
+ return true;
+}
+
bool Bind::visit(AST::TemplateLiteral *ast)
{
Node::accept(ast->expression, this);
diff --git a/src/libs/qmljs/qmljsbind.h b/src/libs/qmljs/qmljsbind.h
index 08a60c17cc..d7096abdb2 100644
--- a/src/libs/qmljs/qmljsbind.h
+++ b/src/libs/qmljs/qmljsbind.h
@@ -57,6 +57,7 @@ protected:
bool visit(AST::UiScriptBinding *ast) override;
bool visit(AST::UiArrayBinding *ast) override;
bool visit(AST::UiInlineComponent *ast) override;
+ bool visit(AST::UiEnumDeclaration *ast) override;
// QML/JS
bool visit(AST::TemplateLiteral *ast) override;
diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp
index 7ceff0e78d..8cf85f11ed 100644
--- a/src/libs/qmljs/qmljscheck.cpp
+++ b/src/libs/qmljs/qmljscheck.cpp
@@ -846,6 +846,32 @@ bool Check::visit(UiObjectInitializer *)
return true;
}
+bool Check::visit(AST::UiEnumDeclaration *ast)
+{
+ const Value *localLookup = _scopeChain.lookup(ast->name.toString());
+ Utils::FilePath fp;
+ int line, column;
+ if (localLookup->getSourceLocation(&fp, &line, &column)) {
+ // if it's not "us" we get shadowed by another enum declaration
+ if (int(ast->identifierToken.startLine) != line || int(ast->identifierToken.startColumn) != column)
+ addMessage(ErrDuplicateId, SourceLocation(0, 0, line, column));
+ }
+ return true;
+}
+
+bool Check::visit(AST::UiEnumMemberList *ast)
+{
+ QStringList names;
+ for (auto it = ast; it; it = it->next) {
+ if (!it->member.first().isUpper())
+ addMessage(ErrInvalidEnumValue, it->memberToken); // better a different message?
+ if (names.contains(it->member)) // duplicate enum value
+ addMessage(ErrInvalidEnumValue, it->memberToken); // better a different message?
+ names.append(it->member.toString());
+ }
+ return true;
+}
+
bool Check::visit(AST::TemplateLiteral *ast)
{
Node::accept(ast->expression, this);
diff --git a/src/libs/qmljs/qmljscheck.h b/src/libs/qmljs/qmljscheck.h
index 1fb96d251b..d7fc6cc425 100644
--- a/src/libs/qmljs/qmljscheck.h
+++ b/src/libs/qmljs/qmljscheck.h
@@ -57,6 +57,8 @@ protected:
bool visit(AST::FunctionDeclaration *ast) override;
bool visit(AST::FunctionExpression *ast) override;
bool visit(AST::UiObjectInitializer *) override;
+ bool visit(AST::UiEnumDeclaration *ast) override;
+ bool visit(AST::UiEnumMemberList *ast) override;
bool visit(AST::TemplateLiteral *ast) override;
bool visit(AST::BinaryExpression *ast) override;
diff --git a/src/libs/qmljs/qmljsicons.cpp b/src/libs/qmljs/qmljsicons.cpp
index 0086843553..a0522ca726 100644
--- a/src/libs/qmljs/qmljsicons.cpp
+++ b/src/libs/qmljs/qmljsicons.cpp
@@ -120,3 +120,8 @@ QIcon Icons::functionDeclarationIcon()
{
return Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::FuncPublic);
}
+
+QIcon Icons::enumMemberIcon()
+{
+ return Utils::CodeModelIcon::iconForType(Utils::CodeModelIcon::Enum);
+}
diff --git a/src/libs/qmljs/qmljsicons.h b/src/libs/qmljs/qmljsicons.h
index 4466d07a18..1565ae5355 100644
--- a/src/libs/qmljs/qmljsicons.h
+++ b/src/libs/qmljs/qmljsicons.h
@@ -28,6 +28,7 @@ public:
static QIcon scriptBindingIcon();
static QIcon publicMemberIcon();
static QIcon functionDeclarationIcon();
+ static QIcon enumMemberIcon();
private:
Icons();
diff --git a/src/libs/qmljs/qmljsimportdependencies.cpp b/src/libs/qmljs/qmljsimportdependencies.cpp
index f1c4181a83..8e725fa521 100644
--- a/src/libs/qmljs/qmljsimportdependencies.cpp
+++ b/src/libs/qmljs/qmljsimportdependencies.cpp
@@ -185,14 +185,14 @@ ImportKey::ImportKey(ImportType::Enum type, const QString &path, int majorVersio
void ImportKey::addToHash(QCryptographicHash &hash) const
{
- hash.addData(reinterpret_cast<const char *>(&type), sizeof(type));
- hash.addData(reinterpret_cast<const char *>(&majorVersion), sizeof(majorVersion));
- hash.addData(reinterpret_cast<const char *>(&minorVersion), sizeof(minorVersion));
+ hash.addData(QByteArrayView(reinterpret_cast<const char *>(&type), sizeof(type)));
+ hash.addData(QByteArrayView(reinterpret_cast<const char *>(&majorVersion), sizeof(majorVersion)));
+ hash.addData(QByteArrayView(reinterpret_cast<const char *>(&minorVersion), sizeof(minorVersion)));
for (const QString &s : splitPath) {
- hash.addData("/", 1);
- hash.addData(reinterpret_cast<const char *>(s.constData()), sizeof(QChar) * s.size());
+ hash.addData("/");
+ hash.addData(QByteArrayView(reinterpret_cast<const char *>(s.constData()), sizeof(QChar) * s.size()));
}
- hash.addData("/", 1);
+ hash.addData("/");
}
ImportKey ImportKey::flatKey() const {
@@ -547,11 +547,11 @@ QByteArray DependencyInfo::calculateFingerprint(const ImportDependencies &deps)
QStringList coreImports = Utils::toList(allCoreImports);
coreImports.sort();
for (const QString &importId : std::as_const(coreImports)) {
- hash.addData(reinterpret_cast<const char*>(importId.constData()), importId.size() * sizeof(QChar));
+ hash.addData(QByteArrayView(reinterpret_cast<const char*>(importId.constData()), importId.size() * sizeof(QChar)));
QByteArray coreImportFingerprint = deps.coreImport(importId).fingerprint;
hash.addData(coreImportFingerprint);
}
- hash.addData("/", 1);
+ hash.addData("/");
QList<ImportKey> imports = Utils::toList(allImports);
std::sort(imports.begin(), imports.end());
for (const ImportKey &k : std::as_const(imports))
diff --git a/src/libs/qmljs/qmljsindenter.cpp b/src/libs/qmljs/qmljsindenter.cpp
deleted file mode 100644
index 8055847d90..0000000000
--- a/src/libs/qmljs/qmljsindenter.cpp
+++ /dev/null
@@ -1,603 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-/*
- This file is a self-contained interactive indenter for Qt Script.
-
- The general problem of indenting a program is ill posed. On
- the one hand, an indenter has to analyze programs written in a
- free-form formal language that is best described in terms of
- tokens, not characters, not lines. On the other hand, indentation
- applies to lines and white space characters matter, and otherwise
- the programs to indent are formally invalid in general, as they
- are begin edited.
-
- The approach taken here works line by line. We receive a program
- consisting of N lines or more, and we want to compute the
- indentation appropriate for the Nth line. Lines beyond the Nth
- lines are of no concern to us, so for simplicity we pretend the
- program has exactly N lines and we call the Nth line the "bottom
- line". Typically, we have to indent the bottom line when it's
- still empty, so we concentrate our analysis on the N - 1 lines
- that precede.
-
- By inspecting the (N - 1)-th line, the (N - 2)-th line, ...
- backwards, we determine the kind of the bottom line and indent it
- accordingly.
-
- * The bottom line is a comment line. See
- bottomLineStartsInCComment() and
- indentWhenBottomLineStartsInCComment().
- * The bottom line is a continuation line. See isContinuationLine()
- and indentForContinuationLine().
- * The bottom line is a standalone line. See
- indentForStandaloneLine().
-
- Certain tokens that influence the indentation, notably braces,
- are looked for in the lines. This is done by simple string
- comparison, without a real tokenizer. Confusing constructs such
- as comments and string literals are removed beforehand.
-*/
-
-#include <qmljs/qmljsindenter.h>
-#include <qmljs/qmljsscanner.h>
-
-#include <QTextBlock>
-
-using namespace QmlJS;
-
-/*
- Saves and restores the state of the global linizer. This enables
- backtracking.
-
- Identical to the defines in qmljslineinfo.cpp
-*/
-#define YY_SAVE() LinizerState savedState = yyLinizerState
-#define YY_RESTORE() yyLinizerState = savedState
-
-
-QmlJSIndenter::QmlJSIndenter()
- : caseOrDefault(QRegularExpression(QLatin1String(
- "^\\s*(?:"
- "case\\b[^:]+|"
- "default)"
- "\\s*:.*$")))
-
-{
-
- /*
- The indenter supports a few parameters:
-
- * ppHardwareTabSize is the size of a '\t' in your favorite editor.
- * ppIndentSize is the size of an indentation, or software tab
- size.
- * ppContinuationIndentSize is the extra indent for a continuation
- line, when there is nothing to align against on the previous
- line.
- * ppCommentOffset is the indentation within a C-style comment,
- when it cannot be picked up.
- */
-
- ppHardwareTabSize = 8;
- ppIndentSize = 4;
- ppContinuationIndentSize = 8;
- ppCommentOffset = 2;
-}
-
-QmlJSIndenter::~QmlJSIndenter()
-{
-}
-
-void QmlJSIndenter::setTabSize(int size)
-{
- ppHardwareTabSize = size;
-}
-
-void QmlJSIndenter::setIndentSize(int size)
-{
- ppIndentSize = size;
- ppContinuationIndentSize = 2 * size;
-}
-
-/*
- Returns true if string t is made only of white space; otherwise
- returns false.
-*/
-bool QmlJSIndenter::isOnlyWhiteSpace(const QString &t) const
-{
- return firstNonWhiteSpace(t).isNull();
-}
-
-/*
- Assuming string t is a line, returns the column number of a given
- index. Column numbers and index are identical for strings that don't
- contain '\t's.
-*/
-int QmlJSIndenter::columnForIndex(const QString &t, int index) const
-{
- int col = 0;
- if (index > t.length())
- index = t.length();
-
- for (int i = 0; i < index; i++) {
- if (t.at(i) == QLatin1Char('\t'))
- col = ((col / ppHardwareTabSize) + 1) * ppHardwareTabSize;
- else
- col++;
- }
- return col;
-}
-
-/*
- Returns the indentation size of string t.
-*/
-int QmlJSIndenter::indentOfLine(const QString &t) const
-{
- return columnForIndex(t, t.indexOf(firstNonWhiteSpace(t)));
-}
-
-/*
- Replaces t[k] by ch, unless t[k] is '\t'. Tab characters are better
- left alone since they break the "index equals column" rule. No
- provisions are taken against '\n' or '\r', which shouldn't occur in
- t anyway.
-*/
-void QmlJSIndenter::eraseChar(QString &t, int k, QChar ch) const
-{
- if (t.at(k) != QLatin1Char('\t'))
- t[k] = ch;
-}
-
-/*
- Returns '(' if the last parenthesis is opening, ')' if it is
- closing, and QChar() if there are no parentheses in t.
-*/
-QChar QmlJSIndenter::lastParen() const
-{
- for (int index = yyLinizerState.tokens.size() - 1; index != -1; --index) {
- const Token &token = yyLinizerState.tokens.at(index);
-
- if (token.is(Token::LeftParenthesis))
- return QLatin1Char('(');
-
- else if (token.is(Token::RightParenthesis))
- return QLatin1Char(')');
- }
-
- return QChar();
-}
-
-/*
- Returns true if typedIn the same as okayCh or is null; otherwise
- returns false.
-*/
-bool QmlJSIndenter::okay(QChar typedIn, QChar okayCh) const
-{
- return typedIn == QChar() || typedIn == okayCh;
-}
-
-/*
- Returns the recommended indent for the bottom line of yyProgram
- assuming that it starts in a C-style comment, a condition that is
- tested elsewhere.
-
- Essentially, we're trying to align against some text on the
- previous line.
-*/
-int QmlJSIndenter::indentWhenBottomLineStartsInMultiLineComment()
-{
- QTextBlock block = yyProgram.lastBlock().previous();
- QString blockText;
-
- for (; block.isValid(); block = block.previous()) {
- blockText = block.text();
-
- if (! isOnlyWhiteSpace(blockText))
- break;
- }
-
- return indentOfLine(blockText);
-}
-
-/*
- Returns the recommended indent for the bottom line of yyProgram,
- assuming it's a continuation line.
-
- We're trying to align the continuation line against some parenthesis
- or other bracked left opened on a previous line, or some interesting
- operator such as '='.
-*/
-int QmlJSIndenter::indentForContinuationLine()
-{
- int braceDepth = 0;
- int delimDepth = 0;
-
- bool leftBraceFollowed = *yyLeftBraceFollows;
-
- for (int i = 0; i < SmallRoof; i++) {
- int hook = -1;
-
- int j = yyLine->length();
- while (j > 0 && hook < 0) {
- j--;
- QChar ch = yyLine->at(j);
-
- switch (ch.unicode()) {
- case ')':
- delimDepth++;
- break;
- case ']':
- braceDepth++;
- break;
- case '}':
- braceDepth++;
- break;
- case '(':
- delimDepth--;
- /*
- An unclosed delimiter is a good place to align at,
- at least for some styles (including Qt's).
- */
- if (delimDepth == -1)
- hook = j;
- break;
-
- case '[':
- braceDepth--;
- /*
- An unclosed delimiter is a good place to align at,
- at least for some styles (including Qt's).
- */
- if (braceDepth == -1)
- hook = j;
- break;
- case '{':
- braceDepth--;
- /*
- A left brace followed by other stuff on the same
- line is typically for an enum or an initializer.
- Such a brace must be treated just like the other
- delimiters.
- */
- if (braceDepth == -1) {
- if (j < yyLine->length() - 1)
- hook = j;
- else
- return 0; // shouldn't happen
- }
- break;
- case '=':
- /*
- An equal sign is a very natural alignment hook
- because it's usually the operator with the lowest
- precedence in statements it appears in. Case in
- point:
-
- int x = 1 +
- 2;
-
- However, we have to beware of constructs such as
- default arguments and explicit enum constant
- values:
-
- void foo(int x = 0,
- int y = 0);
-
- And not
-
- void foo(int x = 0,
- int y = 0);
-
- These constructs are caracterized by a ',' at the
- end of the unfinished lines or by unbalanced
- parentheses.
- */
- Q_ASSERT(j - 1 >= 0);
-
- if (QString::fromLatin1("!=<>").indexOf(yyLine->at(j - 1)) == -1 &&
- j + 1 < yyLine->length() && yyLine->at(j + 1) != QLatin1Char('=')) {
- if (braceDepth == 0 && delimDepth == 0 &&
- j < yyLine->length() - 1 &&
- !yyLine->endsWith(QLatin1Char(',')) &&
- (yyLine->contains(QLatin1Char('(')) == yyLine->contains(QLatin1Char(')'))))
- hook = j;
- }
- }
- }
-
- if (hook >= 0) {
- /*
- Yes, we have a delimiter or an operator to align
- against! We don't really align against it, but rather
- against the following token, if any. In this example,
- the following token is "11":
-
- int x = (11 +
- 2);
-
- If there is no such token, we use a continuation indent:
-
- static QRegExp foo(QString(
- "foo foo foo foo foo foo foo foo foo"));
- */
- hook++;
- while (hook < yyLine->length()) {
- if (!yyLine->at(hook).isSpace())
- return columnForIndex(*yyLine, hook);
- hook++;
- }
- return indentOfLine(*yyLine) + ppContinuationIndentSize;
- }
-
- if (braceDepth != 0)
- break;
-
- /*
- The line's delimiters are balanced. It looks like a
- continuation line or something.
- */
- if (delimDepth == 0) {
- if (leftBraceFollowed) {
- /*
- We have
-
- int main()
- {
-
- or
-
- Bar::Bar()
- : Foo(x)
- {
-
- The "{" should be flush left.
- */
- if (!isContinuationLine())
- return indentOfLine(*yyLine);
- } else if (isContinuationLine() || yyLine->endsWith(QLatin1Char(','))) {
- /*
- We have
-
- x = a +
- b +
- c;
-
- or
-
- int t[] = {
- 1, 2, 3,
- 4, 5, 6
-
- The "c;" should fall right under the "b +", and the
- "4, 5, 6" right under the "1, 2, 3,".
- */
- return indentOfLine(*yyLine);
- } else {
- /*
- We have
-
- stream << 1 +
- 2;
-
- We could, but we don't, try to analyze which
- operator has precedence over which and so on, to
- obtain the excellent result
-
- stream << 1 +
- 2;
-
- We do have a special trick above for the assignment
- operator above, though.
- */
- return indentOfLine(*yyLine) + ppContinuationIndentSize;
- }
- }
-
- if (!readLine())
- break;
- }
- return 0;
-}
-
-/*
- Returns the recommended indent for the bottom line of yyProgram if
- that line is standalone (or should be indented likewise).
-
- Indenting a standalone line is tricky, mostly because of braceless
- control statements. Grossly, we are looking backwards for a special
- line, a "hook line", that we can use as a starting point to indent,
- and then modify the indentation level according to the braces met
- along the way to that hook.
-
- Let's consider a few examples. In all cases, we want to indent the
- bottom line.
-
- Example 1:
-
- x = 1;
- y = 2;
-
- The hook line is "x = 1;". We met 0 opening braces and 0 closing
- braces. Therefore, "y = 2;" inherits the indent of "x = 1;".
-
- Example 2:
-
- if (x) {
- y;
-
- The hook line is "if (x) {". No matter what precedes it, "y;" has
- to be indented one level deeper than the hook line, since we met one
- opening brace along the way.
-
- Example 3:
-
- if (a)
- while (b) {
- c;
- }
- d;
-
- To indent "d;" correctly, we have to go as far as the "if (a)".
- Compare with
-
- if (a) {
- while (b) {
- c;
- }
- d;
-
- Still, we're striving to go back as little as possible to
- accommodate people with irregular indentation schemes. A hook line
- near at hand is much more reliable than a remote one.
-*/
-int QmlJSIndenter::indentForStandaloneLine()
-{
- for (int i = 0; i < SmallRoof; i++) {
- if (!*yyLeftBraceFollows) {
- YY_SAVE();
-
- if (matchBracelessControlStatement()) {
- /*
- The situation is this, and we want to indent "z;":
-
- if (x &&
- y)
- z;
-
- yyLine is "if (x &&".
- */
- return indentOfLine(*yyLine) + ppIndentSize;
- }
- YY_RESTORE();
- }
-
- if (yyLine->endsWith(QLatin1Char(';')) || yyLine->contains(QLatin1Char('{'))) {
- /*
- The situation is possibly this, and we want to indent
- "z;":
-
- while (x)
- y;
- z;
-
- We return the indent of "while (x)". In place of "y;",
- any arbitrarily complex compound statement can appear.
- */
-
- if (*yyBraceDepth > 0) {
- do {
- if (!readLine())
- break;
- } while (*yyBraceDepth > 0);
- }
-
- LinizerState hookState;
-
- while (isContinuationLine())
- readLine();
- hookState = yyLinizerState;
-
- readLine();
- if (*yyBraceDepth <= 0) {
- do {
- if (!matchBracelessControlStatement())
- break;
- hookState = yyLinizerState;
- } while (readLine());
- }
-
- yyLinizerState = hookState;
-
- while (isContinuationLine())
- readLine();
-
- int indentChange = - *yyBraceDepth;
- if (caseOrDefault.match(*yyLine).hasMatch())
- ++indentChange;
-
- /*
- Never trust lines containing only '{' or '}', as some
- people (Richard M. Stallman) format them weirdly.
- */
- if (yyLine->trimmed().length() > 1)
- return indentOfLine(*yyLine) + indentChange * ppIndentSize;
- }
-
- if (!readLine())
- return -*yyBraceDepth * ppIndentSize;
- }
- return 0;
-}
-
-/*
- Returns the recommended indent for the bottom line of program.
- Unless null, typedIn stores the character of yyProgram that
- triggered reindentation.
-
- This function works better if typedIn is set properly; it is
- slightly more conservative if typedIn is completely wild, and
- slighly more liberal if typedIn is always null. The user might be
- annoyed by the liberal behavior.
-*/
-int QmlJSIndenter::indentForBottomLine(QTextBlock begin, QTextBlock end, QChar typedIn)
-{
- if (begin == end)
- return 0;
-
- const QTextBlock last = end.previous();
-
- initialize(begin, last);
-
- QString bottomLine = last.text();
- QChar firstCh = firstNonWhiteSpace(bottomLine);
- int indent = 0;
-
- if (bottomLineStartsInMultilineComment()) {
- /*
- The bottom line starts in a C-style comment. Indent it
- smartly, unless the user has already played around with it,
- in which case it's better to leave her stuff alone.
- */
- if (isOnlyWhiteSpace(bottomLine))
- indent = indentWhenBottomLineStartsInMultiLineComment();
- else
- indent = indentOfLine(bottomLine);
- } else {
- if (isUnfinishedLine())
- indent = indentForContinuationLine();
- else
- indent = indentForStandaloneLine();
-
- if ((okay(typedIn, QLatin1Char('}')) && firstCh == QLatin1Char('}'))
- || (okay(typedIn, QLatin1Char(']')) && firstCh == QLatin1Char(']'))) {
- /*
- A closing brace is one level more to the left than the
- code it follows.
- */
- indent -= ppIndentSize;
- } else if (okay(typedIn, QLatin1Char(':'))) {
- if (caseOrDefault.match(bottomLine).hasMatch()) {
- /*
- Move a case label (or the ':' in front of a
- constructor initialization list) one level to the
- left, but only if the user did not play around with
- it yet. Some users have exotic tastes in the
- matter, and most users probably are not patient
- enough to wait for the final ':' to format their
- code properly.
-
- We don't attempt the same for goto labels, as the
- user is probably the middle of "foo::bar". (Who
- uses goto, anyway?)
- */
- if (indentOfLine(bottomLine) <= indent)
- indent -= ppIndentSize;
- else
- indent = indentOfLine(bottomLine);
- }
- }
- }
-
- return qMax(0, indent);
-}
-
diff --git a/src/libs/qmljs/qmljsindenter.h b/src/libs/qmljs/qmljsindenter.h
deleted file mode 100644
index 55141c1b3b..0000000000
--- a/src/libs/qmljs/qmljsindenter.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include <qmljs/qmljs_global.h>
-#include <qmljs/qmljslineinfo.h>
-
-#include <QRegularExpression>
-
-QT_FORWARD_DECLARE_CLASS(QTextBlock)
-
-namespace QmlJS {
-
-class QMLJS_EXPORT QmlJSIndenter : public LineInfo
-{
- Q_DISABLE_COPY(QmlJSIndenter)
-
-public:
- QmlJSIndenter();
- ~QmlJSIndenter();
-
- void setTabSize(int size);
- void setIndentSize(int size);
-
- int indentForBottomLine(QTextBlock firstBlock, QTextBlock lastBlock, QChar typedIn);
-
-private:
- bool isOnlyWhiteSpace(const QString &t) const;
- int columnForIndex(const QString &t, int index) const;
- int indentOfLine(const QString &t) const;
-
- void eraseChar(QString &t, int k, QChar ch) const;
- QChar lastParen() const;
- bool okay(QChar typedIn, QChar okayCh) const;
-
- int indentWhenBottomLineStartsInMultiLineComment();
- int indentForContinuationLine();
- int indentForStandaloneLine();
-
-private:
- int ppHardwareTabSize;
- int ppIndentSize;
- int ppContinuationIndentSize;
- int ppCommentOffset;
-
-private:
- QRegularExpression caseOrDefault;
-};
-
-} // namespace QmlJS
diff --git a/src/libs/qmljs/qmljsinterpreter.cpp b/src/libs/qmljs/qmljsinterpreter.cpp
index 5d687d5cd7..09bc6aecdb 100644
--- a/src/libs/qmljs/qmljsinterpreter.cpp
+++ b/src/libs/qmljs/qmljsinterpreter.cpp
@@ -635,6 +635,52 @@ const CppComponentValue *QmlEnumValue::owner() const
return m_owner;
}
+UiEnumValue::UiEnumValue(AST::UiEnumDeclaration *ast,
+ ValueOwner *valueOwner, const QString &originId)
+ : ObjectValue(valueOwner, originId)
+ , m_name(ast->name.toString())
+{
+ setClassName("enum");
+ m_path = Utils::FilePath::fromUserInput(originId);
+ m_line = ast->identifierToken.startLine;
+ m_column = ast->identifierToken.startColumn;
+
+ for (auto it = ast->members; it; it = it->next) {
+ const QString name = it->member.toString();
+ setMember(name, valueOwner->intValue());
+ setPropertyInfo(name, PropertyInfo(PropertyInfo::Readable|PropertyInfo::ValueType));
+ m_keys.append(name);
+ m_values.append(it->value);
+ }
+}
+
+UiEnumValue::~UiEnumValue()
+{
+}
+
+const UiEnumValue *UiEnumValue::asUiEnumValue() const
+{
+ return this;
+}
+
+bool UiEnumValue::getSourceLocation(Utils::FilePath *path, int *line, int *column) const
+{
+ *path = m_path;
+ *line = m_line;
+ *column = m_column;
+ return true;
+}
+
+QString UiEnumValue::name() const
+{
+ return m_name;
+}
+
+QStringList UiEnumValue::keys() const
+{
+ return m_keys;
+}
+
////////////////////////////////////////////////////////////////////////////////
// ValueVisitor
////////////////////////////////////////////////////////////////////////////////
@@ -791,6 +837,11 @@ const QmlEnumValue *Value::asQmlEnumValue() const
return nullptr;
}
+const UiEnumValue *Value::asUiEnumValue() const
+{
+ return nullptr;
+}
+
const QmlPrototypeReference *Value::asQmlPrototypeReference() const
{
return nullptr;
diff --git a/src/libs/qmljs/qmljsinterpreter.h b/src/libs/qmljs/qmljsinterpreter.h
index 8651196aef..aeb3c9e438 100644
--- a/src/libs/qmljs/qmljsinterpreter.h
+++ b/src/libs/qmljs/qmljsinterpreter.h
@@ -44,6 +44,7 @@ class NumberValue;
class ModuleApiInfo;
class ObjectValue;
class QmlEnumValue;
+class UiEnumValue;
class QmlPrototypeReference;
class RealValue;
class Reference;
@@ -115,6 +116,7 @@ public:
virtual const CppComponentValue *asCppComponentValue() const;
virtual const ASTObjectValue *asAstObjectValue() const;
virtual const QmlEnumValue *asQmlEnumValue() const;
+ virtual const UiEnumValue *asUiEnumValue() const;
virtual const QmlPrototypeReference *asQmlPrototypeReference() const;
virtual const ASTPropertyReference *asAstPropertyReference() const;
virtual const ASTVariableReference *asAstVariableReference() const;
@@ -246,6 +248,12 @@ template <> Q_INLINE_TEMPLATE const QmlEnumValue *value_cast(const Value *v)
else return nullptr;
}
+template <> Q_INLINE_TEMPLATE const UiEnumValue *value_cast(const Value *v)
+{
+ if (v) return v->asUiEnumValue();
+ else return nullptr;
+}
+
template <> Q_INLINE_TEMPLATE const QmlPrototypeReference *value_cast(const Value *v)
{
if (v) return v->asQmlPrototypeReference();
@@ -559,6 +567,27 @@ private:
int m_enumIndex;
};
+class QMLJS_EXPORT UiEnumValue : public ObjectValue
+{
+public:
+ UiEnumValue(AST::UiEnumDeclaration *uiEnum, ValueOwner *valueOwner,
+ const QString &originId);
+ ~UiEnumValue();
+
+ const UiEnumValue *asUiEnumValue() const override;
+ bool getSourceLocation(Utils::FilePath *path, int *line, int *column) const override;
+ QString name() const;
+ QStringList keys() const;
+
+private:
+ const QString m_name;
+ Utils::FilePath m_path;
+ int m_line;
+ int m_column;
+ QStringList m_keys;
+ QList<int> m_values;
+};
+
// A ObjectValue based on a FakeMetaObject.
// May only have other CppComponentValue as ancestors.
diff --git a/src/libs/qmljs/qmljsplugindumper.cpp b/src/libs/qmljs/qmljsplugindumper.cpp
index 36f2cd802d..653fa8a701 100644
--- a/src/libs/qmljs/qmljsplugindumper.cpp
+++ b/src/libs/qmljs/qmljsplugindumper.cpp
@@ -13,7 +13,7 @@
#include <utils/filesystemwatcher.h>
#include <utils/fileutils.h>
#include <utils/hostosinfo.h>
-#include <utils/process.h>
+#include <utils/qtcprocess.h>
#include <QDir>
#include <QDirIterator>
diff --git a/src/libs/qmljs/qmljsstaticanalysismessage.cpp b/src/libs/qmljs/qmljsstaticanalysismessage.cpp
index 5b15e69428..a58bf43765 100644
--- a/src/libs/qmljs/qmljsstaticanalysismessage.cpp
+++ b/src/libs/qmljs/qmljsstaticanalysismessage.cpp
@@ -153,7 +153,7 @@ StaticAnalysisMessages::StaticAnalysisMessages()
Tr::tr("Invalid property type \"%1\"."), 1);
newMsg(WarnEqualityTypeCoercion, Error,
Tr::tr("== and != perform type coercion, use === or !== to avoid it."));
- newMsg(WarnExpectedNewWithUppercaseFunction, Error,
+ newMsg(WarnExpectedNewWithUppercaseFunction, Warning,
Tr::tr("Calls of functions that start with an uppercase letter should use 'new'."));
newMsg(WarnNewWithLowercaseFunction, Error,
Tr::tr("Use 'new' only with functions that start with an uppercase letter."));
@@ -189,8 +189,9 @@ StaticAnalysisMessages::StaticAnalysisMessages()
Tr::tr("Maximum string value length is %1."), 1);
newMsg(ErrInvalidArrayValueLength, Error,
Tr::tr("%1 elements expected in array value."), 1);
- newMsg(WarnImperativeCodeNotEditableInVisualDesigner, Warning,
- Tr::tr("Imperative code is not supported in Qt Design Studio."));
+ newMsg(WarnImperativeCodeNotEditableInVisualDesigner,
+ Warning,
+ Tr::tr("JavaScript can break the visual tooling in Qt Design Studio."));
newMsg(WarnUnsupportedTypeInVisualDesigner, Warning,
Tr::tr("This type (%1) is not supported in Qt Design Studio."), 1);
newMsg(WarnReferenceToParentItemNotSupportedByVisualDesigner, Warning,
diff --git a/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h b/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h
index 1e8d82e632..b0012f3eb2 100644
--- a/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h
+++ b/src/libs/qmlpuppetcommunication/commands/puppettocreatorcommand.h
@@ -18,6 +18,7 @@ public:
ActiveSceneChanged,
ActiveSplitChanged,
RenderModelNodePreviewImage,
+ Import3DPreviewImage,
Import3DSupport,
NodeAtPos,
BakeLightsProgress,
diff --git a/src/libs/qmlpuppetcommunication/commands/view3dactioncommand.cpp b/src/libs/qmlpuppetcommunication/commands/view3dactioncommand.cpp
index eac0961393..a522499ffa 100644
--- a/src/libs/qmlpuppetcommunication/commands/view3dactioncommand.cpp
+++ b/src/libs/qmlpuppetcommunication/commands/view3dactioncommand.cpp
@@ -3,6 +3,8 @@
#include "view3dactioncommand.h"
+#include <utils/utility.h>
+
#include <QDebug>
#include <QDataStream>
@@ -64,16 +66,9 @@ QDebug operator<<(QDebug debug, const View3DActionCommand &command)
<< command.m_value << ")\n";
}
-template<typename Enumeration>
-constexpr std::underlying_type_t<Enumeration> to_underlying(Enumeration enumeration) noexcept
-{
- static_assert(std::is_enum_v<Enumeration>, "to_underlying expect an enumeration");
- return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
-}
-
QDebug operator<<(QDebug debug, View3DActionType type)
{
- return debug.nospace() << to_underlying(type);
+ return debug.nospace() << Utils::to_underlying(type);
}
} // namespace QmlDesigner
diff --git a/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp b/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp
index 545d4d927d..7747a9d118 100644
--- a/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp
+++ b/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp
@@ -15,7 +15,7 @@
#define QTC_ASSERT_STRING(cond) qDebug("SOFT ASSERT: \"" cond"\" in file " __FILE__ ", line " QTC_ASSERT_STRINGIFY(__LINE__))
#define QTC_ASSERT(cond, action) if (cond) {} else { QTC_ASSERT_STRING(#cond); action; } do {} while (0)
-static Q_LOGGING_CATEGORY(imageContainerDebug, "qtc.imagecontainer.debug", QtDebugMsg)
+static Q_LOGGING_CATEGORY(imageContainerDebug, "qtc.imagecontainer")
namespace QmlDesigner {
@@ -194,9 +194,10 @@ static void readSharedMemory(qint32 key, ImageContainer &container)
QImage image = QImage(imageWidth, imageHeight, QImage::Format(imageFormat));
image.setDevicePixelRatio(pixelRatio);
- if (image.isNull())
- qCInfo(imageContainerDebug()) << Q_FUNC_INFO << "Not able to create image:" << imageWidth << imageHeight << imageFormat;
- else
+ if (image.isNull()) {
+ if (imageWidth || imageHeight || imageFormat)
+ qCWarning(imageContainerDebug) << Q_FUNC_INFO << "Not able to create image:" << imageWidth << imageHeight << imageFormat;
+ } else
std::memcpy(image.bits(), reinterpret_cast<const qint32*>(sharedMemory.constData()) + 6, byteCount);
container.setImage(image);
diff --git a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h
index d5a715f1d8..d248e5e6fd 100644
--- a/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h
+++ b/src/libs/qmlpuppetcommunication/interfaces/nodeinstanceglobal.h
@@ -36,6 +36,7 @@ enum class View3DActionType {
OrientationToggle,
EditLightToggle,
ShowGrid,
+ ShowLookAt,
ShowSelectionBox,
ShowIconGizmo,
ShowCameraFrustum,
@@ -46,10 +47,18 @@ enum class View3DActionType {
ParticlesSeek,
SyncEnvBackground,
GetNodeAtPos,
+ GetNodeAtMainScenePos,
SetBakeLightsView3D,
SplitViewToggle,
MaterialOverride,
- ShowWireframe
+ ShowWireframe,
+ FlyModeToggle,
+ EditCameraRotation,
+ EditCameraMove,
+ EditCameraStopAllMoves,
+ SetLastSceneEnvData,
+ Import3dUpdatePreviewImage,
+ Import3dRotatePreviewModel
};
constexpr bool isNanotraceEnabled()
diff --git a/src/libs/qmlpuppetcommunication/types/enumeration.h b/src/libs/qmlpuppetcommunication/types/enumeration.h
index 57bfe6c0ef..5fe2294621 100644
--- a/src/libs/qmlpuppetcommunication/types/enumeration.h
+++ b/src/libs/qmlpuppetcommunication/types/enumeration.h
@@ -43,17 +43,20 @@ public:
EnumerationNameView scope() const
{
- auto found = std::find(m_enumerationName.begin(), m_enumerationName.end(), '.');
- return {m_enumerationName.begin(), found};
+ auto found = std::find(m_enumerationName.rbegin(), m_enumerationName.rend(), '.');
+ if (found != m_enumerationName.rend())
+ return {m_enumerationName.begin(), std::prev(found.base())};
+
+ return {m_enumerationName.end(), m_enumerationName.end()};
}
EnumerationNameView toScope() const { return scope().toByteArray(); }
EnumerationNameView name() const
{
- auto found = std::find(m_enumerationName.begin(), m_enumerationName.end(), '.');
- if (found != m_enumerationName.end())
- return {std::next(found), m_enumerationName.end()};
+ auto found = std::find(m_enumerationName.rbegin(), m_enumerationName.rend(), '.');
+ if (found != m_enumerationName.rend())
+ return {found.base(), m_enumerationName.end()};
return {m_enumerationName.end(), m_enumerationName.end()};
}
diff --git a/src/libs/qtcreatorcdbext/CMakeLists.txt b/src/libs/qtcreatorcdbext/CMakeLists.txt
index 4359d79d54..47eca2cd16 100644
--- a/src/libs/qtcreatorcdbext/CMakeLists.txt
+++ b/src/libs/qtcreatorcdbext/CMakeLists.txt
@@ -4,7 +4,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../../cmake")
project(qtcreatorcdbext)
-set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
diff --git a/src/libs/qtcreatorcdbext/pytype.cpp b/src/libs/qtcreatorcdbext/pytype.cpp
index 9345c1d91a..479895bbcd 100644
--- a/src/libs/qtcreatorcdbext/pytype.cpp
+++ b/src/libs/qtcreatorcdbext/pytype.cpp
@@ -49,10 +49,24 @@ static bool isType(const std::string &typeName, const std::vector<std::string> &
static bool isIntegralType(const std::string &typeName)
{
- static const std::vector<std::string> integralTypes({"bool",
- "char", "unsigned char", "char16_t", "char32_t", "wchar_t",
- "short", "unsigned short", "int", "unsigned int",
- "long", "unsigned long", "int64", "unsigned int64", "__int64", "unsigned __int64"});
+ static const std::vector<std::string> integralTypes(
+ {"bool",
+ "char",
+ "unsigned char",
+ "char16_t",
+ "char32_t",
+ "wchar_t",
+ "short",
+ "unsigned short",
+ "int",
+ "unsigned int",
+ "long",
+ "unsigned long",
+ "int64",
+ "unsigned int64",
+ "__int64",
+ "unsigned __int64",
+ "HRESULT"});
return isType(typeName, integralTypes);
}
diff --git a/src/libs/qtcreatorcdbext/pyvalue.cpp b/src/libs/qtcreatorcdbext/pyvalue.cpp
index f3813abf85..671f4d5350 100644
--- a/src/libs/qtcreatorcdbext/pyvalue.cpp
+++ b/src/libs/qtcreatorcdbext/pyvalue.cpp
@@ -13,8 +13,23 @@ constexpr bool debuggingValueEnabled() { return debugPyValue || debugPyCdbextMod
static std::map<CIDebugSymbolGroup *, std::list<PyValue *>> valuesForSymbolGroup;
+void dumpSymbolGroup(CIDebugSymbolGroup *symbolGroup)
+{
+ if (!debuggingValueEnabled())
+ return;
+
+ ULONG count;
+ if (FAILED(symbolGroup->GetNumberSymbols(&count)))
+ return;
+ DebugPrint() << "Symbol group " << symbolGroup << " has " << count << " symbols";
+ for (ULONG i = 0; i < count; ++i)
+ DebugPrint() << " " << i << ": " << PyValue(i, symbolGroup).name();
+}
+
void PyValue::indicesMoved(CIDebugSymbolGroup *symbolGroup, ULONG start, ULONG delta)
{
+ if (debuggingValueEnabled())
+ DebugPrint() << "PyValue::indicesMoved " << symbolGroup << " start " << start << " delta " << delta << "\n";
if (delta == 0)
return;
ULONG count;
@@ -26,6 +41,7 @@ void PyValue::indicesMoved(CIDebugSymbolGroup *symbolGroup, ULONG start, ULONG d
if (val->m_index >= start && val->m_index + delta < count)
val->m_index += delta;
}
+ dumpSymbolGroup(symbolGroup);
}
PyValue::PyValue(unsigned long index, CIDebugSymbolGroup *symbolGroup)
@@ -146,6 +162,7 @@ bool PyValue::expand()
return false;
if (params.Flags & DEBUG_SYMBOL_EXPANDED)
return true;
+ dumpSymbolGroup(m_symbolGroup);
if (FAILED(m_symbolGroup->ExpandSymbol(m_index, TRUE)))
return false;
if (FAILED(m_symbolGroup->GetSymbolParameters(m_index, 1, &params)))
diff --git a/src/libs/solutions/spinner/spinner.cpp b/src/libs/solutions/spinner/spinner.cpp
index b56684720b..479467ae58 100644
--- a/src/libs/solutions/spinner/spinner.cpp
+++ b/src/libs/solutions/spinner/spinner.cpp
@@ -72,6 +72,8 @@ public:
void setUpdateCallback(const UpdateCallback &cb) { m_callback = cb; }
+ void setColor(const QColor &color);
+
QSize size() const { return m_pixmap.size() / m_pixmap.devicePixelRatio(); }
void paint(QPainter &painter, const QRect &rect) const;
void startAnimation() { m_timer.start(); }
@@ -87,6 +89,7 @@ private:
QTimer m_timer;
mutable QPixmap m_pixmap;
UpdateCallback m_callback;
+ QColor m_color;
};
static QString imageFileNameForSpinnerSize(SpinnerSize size)
@@ -102,12 +105,12 @@ static QString imageFileNameForSpinnerSize(SpinnerSize size)
return {};
}
-static QPixmap themedPixmapForSpinnerSize(SpinnerSize size, qreal dpr)
+static QPixmap themedPixmapForSpinnerSize(SpinnerSize size, const QColor &color, qreal dpr)
{
QImage mask(qt_findAtNxFile(imageFileNameForSpinnerSize(size), dpr));
mask.invertPixels();
QImage themedImage(mask.size(), QImage::Format_ARGB32);
- themedImage.fill(qApp->palette().text().color());
+ themedImage.fill(color);
themedImage.setAlphaChannel(mask);
QPixmap themedPixmap = QPixmap::fromImage(themedImage);
themedPixmap.setDevicePixelRatio(mask.devicePixelRatio());
@@ -115,6 +118,7 @@ static QPixmap themedPixmapForSpinnerSize(SpinnerSize size, qreal dpr)
}
SpinnerPainter::SpinnerPainter(SpinnerSize size)
+ : m_color(qApp->palette().text().color())
{
m_timer.setSingleShot(false);
QObject::connect(&m_timer, &QTimer::timeout, &m_timer, [this] {
@@ -130,14 +134,20 @@ void SpinnerPainter::setSize(SpinnerSize size)
m_size = size;
m_rotationStep = size == SpinnerSize::Small ? 45 : 30;
m_timer.setInterval(size == SpinnerSize::Small ? 100 : 80);
- m_pixmap = themedPixmapForSpinnerSize(size, qApp->devicePixelRatio());
+ m_pixmap = themedPixmapForSpinnerSize(size, m_color, qApp->devicePixelRatio());
+}
+
+void SpinnerPainter::setColor(const QColor &color)
+{
+ m_color = color;
+ m_pixmap = themedPixmapForSpinnerSize(m_size, m_color, qApp->devicePixelRatio());
}
void SpinnerPainter::paint(QPainter &painter, const QRect &rect) const
{
const qreal dpr = painter.device()->devicePixelRatioF();
if (!qFuzzyCompare(m_pixmap.devicePixelRatio(), dpr))
- m_pixmap = themedPixmapForSpinnerSize(m_size, dpr);
+ m_pixmap = themedPixmapForSpinnerSize(m_size, m_color, dpr);
painter.save();
painter.setRenderHint(QPainter::SmoothPixmapTransform);
QPoint translate(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2);
@@ -173,6 +183,11 @@ public:
}
QSize sizeHint() const final { return m_paint.size(); }
+ void setColor(const QColor &color)
+ {
+ m_paint.setColor(color);
+ }
+
protected:
void showEvent(QShowEvent *) final { m_paint.startAnimation(); }
void hideEvent(QHideEvent *) final { m_paint.stopAnimation(); }
@@ -232,6 +247,14 @@ void Spinner::setSize(SpinnerSize size)
}
/*!
+ Sets the color of the spinner to \a color.
+*/
+void Spinner::setColor(const QColor &color)
+{
+ m_widget->setColor(color);
+}
+
+/*!
Shows the animated spinner as an overlay for the parent widget
set previously in the constructor.
*/
diff --git a/src/libs/solutions/spinner/spinner.h b/src/libs/solutions/spinner/spinner.h
index 8ab7cb84d9..8dd166a864 100644
--- a/src/libs/solutions/spinner/spinner.h
+++ b/src/libs/solutions/spinner/spinner.h
@@ -27,6 +27,7 @@ class SPINNER_EXPORT Spinner : public QObject
public:
explicit Spinner(SpinnerSize size, QWidget *parent = nullptr);
void setSize(SpinnerSize size);
+ void setColor(const QColor &color);
void show();
void hide();
bool isVisible() const;
diff --git a/src/libs/solutions/tasking/barrier.cpp b/src/libs/solutions/tasking/barrier.cpp
index e160dbac76..126d4f3b8a 100644
--- a/src/libs/solutions/tasking/barrier.cpp
+++ b/src/libs/solutions/tasking/barrier.cpp
@@ -1,36 +1,36 @@
-// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2024 Jarek Kobus
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "barrier.h"
+QT_BEGIN_NAMESPACE
+
namespace Tasking {
// That's cut down qtcassert.{c,h} to avoid the dependency.
-#define QTC_STRINGIFY_HELPER(x) #x
-#define QTC_STRINGIFY(x) QTC_STRINGIFY_HELPER(x)
-#define QTC_STRING(cond) qDebug("SOFT ASSERT: \"%s\" in %s: %s", cond, __FILE__, QTC_STRINGIFY(__LINE__))
-#define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_STRING(#cond); action; } do {} while (0)
-#define QTC_CHECK(cond) if (cond) {} else { QTC_STRING(#cond); } do {} while (0)
+#define QT_STRING(cond) qDebug("SOFT ASSERT: \"%s\" in %s: %s", cond, __FILE__, QT_STRINGIFY(__LINE__))
+#define QT_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QT_STRING(#cond); action; } do {} while (0)
void Barrier::setLimit(int value)
{
- QTC_ASSERT(!isRunning(), return);
- QTC_ASSERT(value > 0, return);
+ QT_ASSERT(!isRunning(), return);
+ QT_ASSERT(value > 0, return);
m_limit = value;
}
void Barrier::start()
{
- QTC_ASSERT(!isRunning(), return);
+ QT_ASSERT(!isRunning(), return);
m_current = 0;
- m_result = {};
+ m_result.reset();
}
void Barrier::advance()
{
// Calling advance on finished is OK
- QTC_ASSERT(isRunning() || m_result, return);
+ QT_ASSERT(isRunning() || m_result, return);
if (!isRunning()) // no-op
return;
++m_current;
@@ -41,7 +41,7 @@ void Barrier::advance()
void Barrier::stopWithResult(DoneResult result)
{
// Calling stopWithResult on finished is OK when the same success is passed
- QTC_ASSERT(isRunning() || (m_result && *m_result == result), return);
+ QT_ASSERT(isRunning() || (m_result && *m_result == result), return);
if (!isRunning()) // no-op
return;
m_current = -1;
@@ -50,3 +50,5 @@ void Barrier::stopWithResult(DoneResult result)
}
} // namespace Tasking
+
+QT_END_NAMESPACE
diff --git a/src/libs/solutions/tasking/barrier.h b/src/libs/solutions/tasking/barrier.h
index a9ed9949f0..a484844d38 100644
--- a/src/libs/solutions/tasking/barrier.h
+++ b/src/libs/solutions/tasking/barrier.h
@@ -1,12 +1,16 @@
-// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2024 Jarek Kobus
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#pragma once
+#ifndef TASKING_BARRIER_H
+#define TASKING_BARRIER_H
#include "tasking_global.h"
#include "tasktree.h"
+QT_BEGIN_NAMESPACE
+
namespace Tasking {
class TASKING_EXPORT Barrier final : public QObject
@@ -25,7 +29,7 @@ public:
int current() const { return m_current; }
std::optional<DoneResult> result() const { return m_result; }
-signals:
+Q_SIGNALS:
void done(DoneResult success);
private:
@@ -91,3 +95,7 @@ GroupItem waitForBarrierTask(const MultiBarrier<Limit> &sharedBarrier)
}
} // namespace Tasking
+
+QT_END_NAMESPACE
+
+#endif // TASKING_BARRIER_H
diff --git a/src/libs/solutions/tasking/concurrentcall.h b/src/libs/solutions/tasking/concurrentcall.h
index 6bd4460131..ae1fcd17cd 100644
--- a/src/libs/solutions/tasking/concurrentcall.h
+++ b/src/libs/solutions/tasking/concurrentcall.h
@@ -1,11 +1,15 @@
-// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2024 Jarek Kobus
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#pragma once
+#ifndef TASKING_CONCURRENTCALL_H
+#define TASKING_CONCURRENTCALL_H
#include "tasktree.h"
-#include <QtConcurrent>
+#include <QtConcurrent/QtConcurrent>
+
+QT_BEGIN_NAMESPACE
namespace Tasking {
@@ -76,7 +80,7 @@ public:
}
}
- void start() {
+ void start() final {
if (!this->task()->m_startHandler) {
emit this->done(DoneResult::Error); // TODO: Add runtime assert
return;
@@ -98,3 +102,7 @@ template <typename T>
using ConcurrentCallTask = CustomTask<ConcurrentCallTaskAdapter<T>>;
} // namespace Tasking
+
+QT_END_NAMESPACE
+
+#endif // TASKING_CONCURRENTCALL_H
diff --git a/src/libs/solutions/tasking/networkquery.cpp b/src/libs/solutions/tasking/networkquery.cpp
index 335f79afd9..30789ec925 100644
--- a/src/libs/solutions/tasking/networkquery.cpp
+++ b/src/libs/solutions/tasking/networkquery.cpp
@@ -1,9 +1,12 @@
-// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2024 Jarek Kobus
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "networkquery.h"
-#include <QNetworkAccessManager>
+#include <QtNetwork/QNetworkAccessManager>
+
+QT_BEGIN_NAMESPACE
namespace Tasking {
@@ -51,3 +54,5 @@ NetworkQuery::~NetworkQuery()
}
} // namespace Tasking
+
+QT_END_NAMESPACE
diff --git a/src/libs/solutions/tasking/networkquery.h b/src/libs/solutions/tasking/networkquery.h
index dd099dc7a8..6d28848671 100644
--- a/src/libs/solutions/tasking/networkquery.h
+++ b/src/libs/solutions/tasking/networkquery.h
@@ -1,20 +1,21 @@
-// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2024 Jarek Kobus
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#pragma once
+#ifndef TASKING_NETWORKQUERY_H
+#define TASKING_NETWORKQUERY_H
#include "tasking_global.h"
#include "tasktree.h"
-#include <QNetworkReply>
-#include <QNetworkRequest>
+#include <QtNetwork/QNetworkReply>
+#include <QtNetwork/QNetworkRequest>
#include <memory>
QT_BEGIN_NAMESPACE
class QNetworkAccessManager;
-QT_END_NAMESPACE
namespace Tasking {
@@ -37,7 +38,7 @@ public:
QNetworkReply *reply() const { return m_reply.get(); }
void start();
-signals:
+Q_SIGNALS:
void started();
void done(DoneResult result);
@@ -59,3 +60,7 @@ public:
using NetworkQueryTask = CustomTask<NetworkQueryTaskAdapter>;
} // namespace Tasking
+
+QT_END_NAMESPACE
+
+#endif // TASKING_NETWORKQUERY_H
diff --git a/src/libs/solutions/tasking/qprocesstask.cpp b/src/libs/solutions/tasking/qprocesstask.cpp
index 7d63e65847..b0a61986a3 100644
--- a/src/libs/solutions/tasking/qprocesstask.cpp
+++ b/src/libs/solutions/tasking/qprocesstask.cpp
@@ -1,14 +1,19 @@
-// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2024 Jarek Kobus
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "qprocesstask.h"
-#include <QCoreApplication>
-#include <QDebug>
-#include <QMutex>
-#include <QThread>
-#include <QTimer>
-#include <QWaitCondition>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QMutex>
+#include <QtCore/QThread>
+#include <QtCore/QTimer>
+#include <QtCore/QWaitCondition>
+
+QT_BEGIN_NAMESPACE
+
+#if QT_CONFIG(process)
namespace Tasking {
@@ -60,7 +65,7 @@ public:
terminate();
}
-signals:
+Q_SIGNALS:
void finished();
private:
@@ -262,4 +267,13 @@ void QProcessDeleter::operator()(QProcess *process)
} // namespace Tasking
+#endif // QT_CONFIG(process)
+
+QT_END_NAMESPACE
+
+#if QT_CONFIG(process)
+
#include "qprocesstask.moc"
+
+#endif // QT_CONFIG(process)
+
diff --git a/src/libs/solutions/tasking/qprocesstask.h b/src/libs/solutions/tasking/qprocesstask.h
index cc0b83698b..3b06e624fb 100644
--- a/src/libs/solutions/tasking/qprocesstask.h
+++ b/src/libs/solutions/tasking/qprocesstask.h
@@ -1,13 +1,19 @@
-// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2024 Jarek Kobus
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#pragma once
+#ifndef TASKING_QPROCESSTASK_H
+#define TASKING_QPROCESSTASK_H
#include "tasking_global.h"
#include "tasktree.h"
-#include <QProcess>
+#include <QtCore/QProcess>
+
+QT_BEGIN_NAMESPACE
+
+#if QT_CONFIG(process)
namespace Tasking {
@@ -45,17 +51,17 @@ public:
class TASKING_EXPORT QProcessAdapter : public TaskAdapter<QProcess, QProcessDeleter>
{
private:
- void start() override {
+ void start() final {
connect(task(), &QProcess::finished, this, [this] {
const bool success = task()->exitStatus() == QProcess::NormalExit
&& task()->error() == QProcess::UnknownError
&& task()->exitCode() == 0;
- emit done(toDoneResult(success));
+ Q_EMIT done(toDoneResult(success));
});
connect(task(), &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
if (error != QProcess::FailedToStart)
return;
- emit done(DoneResult::Error);
+ Q_EMIT done(DoneResult::Error);
});
task()->start();
}
@@ -64,3 +70,9 @@ private:
using QProcessTask = CustomTask<QProcessAdapter>;
} // namespace Tasking
+
+#endif // QT_CONFIG(process)
+
+QT_END_NAMESPACE
+
+#endif // TASKING_QPROCESSTASK_H
diff --git a/src/libs/solutions/tasking/tasking_global.h b/src/libs/solutions/tasking/tasking_global.h
index d7e76fa9e6..024d7e3303 100644
--- a/src/libs/solutions/tasking/tasking_global.h
+++ b/src/libs/solutions/tasking/tasking_global.h
@@ -1,9 +1,12 @@
-// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#pragma once
+#ifndef TASKING_GLOBAL_H
+#define TASKING_GLOBAL_H
-#include <qglobal.h>
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
#if defined(TASKING_LIBRARY)
# define TASKING_EXPORT Q_DECL_EXPORT
@@ -12,3 +15,7 @@
#else
# define TASKING_EXPORT Q_DECL_IMPORT
#endif
+
+QT_END_NAMESPACE
+
+#endif // TASKING_GLOBAL_H
diff --git a/src/libs/solutions/tasking/tasktree.cpp b/src/libs/solutions/tasking/tasktree.cpp
index 7d37bf9758..5c1f65e503 100644
--- a/src/libs/solutions/tasking/tasktree.cpp
+++ b/src/libs/solutions/tasking/tasktree.cpp
@@ -1,22 +1,27 @@
-// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2024 Jarek Kobus
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "tasktree.h"
-#include <QDebug>
-#include <QEventLoop>
-#include <QFutureWatcher>
-#include <QHash>
-#include <QMetaEnum>
-#include <QMutex>
-#include <QPromise>
-#include <QPointer>
-#include <QSet>
-#include <QTime>
-#include <QTimer>
+#include "barrier.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QEventLoop>
+#include <QtCore/QFutureWatcher>
+#include <QtCore/QHash>
+#include <QtCore/QMetaEnum>
+#include <QtCore/QMutex>
+#include <QtCore/QPointer>
+#include <QtCore/QPromise>
+#include <QtCore/QSet>
+#include <QtCore/QTime>
+#include <QtCore/QTimer>
using namespace std::chrono;
+QT_BEGIN_NAMESPACE
+
namespace Tasking {
// That's cut down qtcassert.{c,h} to avoid the dependency.
@@ -882,6 +887,20 @@ private:
*/
/*!
+ \variable parallelIdealThreadCountLimit
+ A convenient global group's element describing the parallel execution mode with a limited
+ number of tasks running simultanously. The limit is equal to the ideal number of threads
+ excluding the calling thread.
+
+ This is a shortcut to:
+ \code
+ parallelLimit(qMax(QThread::idealThreadCount() - 1, 1))
+ \endcode
+
+ \sa parallel, parallelLimit()
+*/
+
+/*!
\variable stopOnError
A convenient global group's element describing the StopOnError workflow policy.
@@ -1161,6 +1180,7 @@ const GroupItem nullItem = GroupItem({});
const GroupItem sequential = parallelLimit(1);
const GroupItem parallel = parallelLimit(0);
+const GroupItem parallelIdealThreadCountLimit = parallelLimit(qMax(QThread::idealThreadCount() - 1, 1));
const GroupItem stopOnError = workflowPolicy(WorkflowPolicy::StopOnError);
const GroupItem continueOnError = workflowPolicy(WorkflowPolicy::ContinueOnError);
@@ -1271,6 +1291,12 @@ const void *Loop::valuePtr() const
using StoragePtr = void *;
+static QString s_activeStorageWarning =
+ "The referenced storage is not reachable in the running tree. "
+ "A nullptr will be returned which might lead to a crash in the calling code. "
+ "It is possible that no storage was added to the tree, "
+ "or the storage is not reachable from where it is referenced.";
+
class StorageThreadData
{
Q_DISABLE_COPY_MOVE(StorageThreadData)
@@ -1279,7 +1305,7 @@ public:
StorageThreadData() = default;
void pushStorage(StoragePtr storagePtr)
{
- m_activeStorageStack.push_back(storagePtr);
+ m_activeStorageStack.push_back({storagePtr, activeTaskTree()});
}
void popStorage()
{
@@ -1288,16 +1314,16 @@ public:
}
StoragePtr activeStorage() const
{
- QT_ASSERT(m_activeStorageStack.size(), qWarning(
- "The referenced storage is not reachable in the running tree. "
- "A nullptr will be returned which might lead to a crash in the calling code. "
- "It is possible that no storage was added to the tree, "
- "or the storage is not reachable from where it is referenced."); return nullptr);
- return m_activeStorageStack.last();
+ QT_ASSERT(m_activeStorageStack.size(),
+ qWarning().noquote() << s_activeStorageWarning; return nullptr);
+ const QPair<StoragePtr, TaskTree *> &top = m_activeStorageStack.last();
+ QT_ASSERT(top.second == activeTaskTree(),
+ qWarning().noquote() << s_activeStorageWarning; return nullptr);
+ return top.first;
}
private:
- QList<StoragePtr> m_activeStorageStack;
+ QList<QPair<StoragePtr, TaskTree *>> m_activeStorageStack;
};
class StorageData
@@ -1430,6 +1456,11 @@ ExecutableItem ExecutableItem::withTimeout(milliseconds timeout,
static QString currentTime() { return QTime::currentTime().toString(Qt::ISODateWithMs); }
+static QString logHeader(const QString &logName)
+{
+ return QString::fromLatin1("TASK TREE LOG [%1] \"%2\"").arg(currentTime(), logName);
+};
+
/*!
Attaches a custom debug printout to a copy of \c this ExecutableItem,
issued on task startup and after the task is finished, and returns the coupled item.
@@ -1443,9 +1474,6 @@ static QString currentTime() { return QTime::currentTime().toString(Qt::ISODateW
*/
ExecutableItem ExecutableItem::withLog(const QString &logName) const
{
- const auto header = [logName] {
- return QString("TASK TREE LOG [%1] \"%2\"").arg(currentTime(), logName);
- };
struct LogStorage
{
time_point<system_clock, nanoseconds> start;
@@ -1454,25 +1482,43 @@ ExecutableItem ExecutableItem::withLog(const QString &logName) const
const Storage<LogStorage> storage;
return Group {
storage,
- onGroupSetup([storage, header] {
+ onGroupSetup([storage, logName] {
storage->start = system_clock::now();
storage->asyncCount = activeTaskTree()->asyncCount();
- qDebug().noquote() << header() << "started.";
+ qDebug().noquote().nospace() << logHeader(logName) << " started.";
}),
*this,
- onGroupDone([storage, header](DoneWith result) {
+ onGroupDone([storage, logName](DoneWith result) {
const auto elapsed = duration_cast<milliseconds>(system_clock::now() - storage->start);
const int asyncCountDiff = activeTaskTree()->asyncCount() - storage->asyncCount;
QT_CHECK(asyncCountDiff >= 0);
const QMetaEnum doneWithEnum = QMetaEnum::fromType<DoneWith>();
- const QString syncType = asyncCountDiff ? QString("asynchronously")
- : QString("synchronously");
- qDebug().noquote().nospace() << header() << " finished " << syncType << " with "
- << doneWithEnum.valueToKey(int(result)) << " within " << elapsed.count() << "ms.";
+ const QString syncType = asyncCountDiff ? QString::fromLatin1("asynchronously")
+ : QString::fromLatin1("synchronously");
+ qDebug().noquote().nospace() << logHeader(logName) << " finished " << syncType
+ << " with " << doneWithEnum.valueToKey(int(result))
+ << " within " << elapsed.count() << "ms.";
})
};
}
+ExecutableItem ExecutableItem::withCancelImpl(
+ const std::function<void(QObject *, const std::function<void()> &)> &connectWrapper) const
+{
+ const auto onSetup = [connectWrapper](Barrier &barrier) {
+ connectWrapper(&barrier, [barrierPtr = &barrier] { barrierPtr->advance(); });
+ };
+ return Group {
+ parallel,
+ stopOnSuccessOrError,
+ Group {
+ finishAllAndError,
+ BarrierTask(onSetup)
+ },
+ *this
+ };
+}
+
class TaskTreePrivate;
class TaskNode;
class RuntimeContainer;
@@ -3346,7 +3392,7 @@ TimeoutTaskAdapter::~TimeoutTaskAdapter()
void TimeoutTaskAdapter::start()
{
m_timerId = scheduleTimeout(*task(), this, [this] {
- m_timerId = {};
+ m_timerId.reset();
emit done(DoneResult::Success);
});
}
@@ -3381,3 +3427,5 @@ void TimeoutTaskAdapter::start()
*/
} // namespace Tasking
+
+QT_END_NAMESPACE
diff --git a/src/libs/solutions/tasking/tasktree.h b/src/libs/solutions/tasking/tasktree.h
index 8ea255303b..caedb78c2c 100644
--- a/src/libs/solutions/tasking/tasktree.h
+++ b/src/libs/solutions/tasking/tasktree.h
@@ -1,19 +1,20 @@
-// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2024 Jarek Kobus
+// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#pragma once
+#ifndef TASKING_TASKTREE_H
+#define TASKING_TASKTREE_H
#include "tasking_global.h"
-#include <QObject>
-#include <QList>
+#include <QtCore/QList>
+#include <QtCore/QObject>
#include <memory>
QT_BEGIN_NAMESPACE
template <class T>
class QFuture;
-QT_END_NAMESPACE
namespace Tasking {
@@ -41,7 +42,7 @@ enum class WorkflowPolicy
FinishAllAndSuccess, // 4 - Reports success after all children finished.
FinishAllAndError // 5 - Reports error after all children finished.
};
-Q_ENUM_NS(WorkflowPolicy);
+Q_ENUM_NS(WorkflowPolicy)
enum class SetupResult
{
@@ -49,14 +50,14 @@ enum class SetupResult
StopWithSuccess,
StopWithError
};
-Q_ENUM_NS(SetupResult);
+Q_ENUM_NS(SetupResult)
enum class DoneResult
{
Success,
Error
};
-Q_ENUM_NS(DoneResult);
+Q_ENUM_NS(DoneResult)
enum class DoneWith
{
@@ -64,7 +65,7 @@ enum class DoneWith
Error,
Cancel
};
-Q_ENUM_NS(DoneWith);
+Q_ENUM_NS(DoneWith)
enum class CallDoneIf
{
@@ -72,7 +73,7 @@ enum class CallDoneIf
Success,
Error
};
-Q_ENUM_NS(CallDoneIf);
+Q_ENUM_NS(CallDoneIf)
TASKING_EXPORT DoneResult toDoneResult(bool success);
@@ -84,7 +85,7 @@ class TASKING_EXPORT TaskInterface : public QObject
{
Q_OBJECT
-signals:
+Q_SIGNALS:
void done(DoneResult result);
private:
@@ -187,7 +188,7 @@ public:
}
private:
- static StorageConstructor ctor() { return [] { return new StorageStruct; }; }
+ static StorageConstructor ctor() { return [] { return new StorageStruct(); }; }
static StorageDestructor dtor() {
return [](void *storage) { delete static_cast<StorageStruct *>(storage); };
}
@@ -290,10 +291,25 @@ public:
ExecutableItem withTimeout(std::chrono::milliseconds timeout,
const std::function<void()> &handler = {}) const;
ExecutableItem withLog(const QString &logName) const;
+ template <typename SenderSignalPairGetter>
+ ExecutableItem withCancel(SenderSignalPairGetter &&getter) const
+ {
+ const auto connectWrapper = [getter](QObject *guard, const std::function<void()> &trigger) {
+ const auto senderSignalPair = getter();
+ QObject::connect(senderSignalPair.first, senderSignalPair.second, guard, [trigger] {
+ trigger();
+ }, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection));
+ };
+ return withCancelImpl(connectWrapper);
+ }
protected:
ExecutableItem() = default;
ExecutableItem(const TaskHandler &handler) : GroupItem(handler) {}
+
+private:
+ ExecutableItem withCancelImpl(
+ const std::function<void(QObject *, const std::function<void()> &)> &connectWrapper) const;
};
class TASKING_EXPORT Group : public ExecutableItem
@@ -330,7 +346,7 @@ private:
std::invoke(handler);
return SetupResult::Continue;
};
- };
+ }
template <typename Handler>
static GroupDoneHandler wrapGroupDone(Handler &&handler)
{
@@ -353,7 +369,7 @@ private:
std::invoke(handler);
return result == DoneWith::Success ? DoneResult::Success : DoneResult::Error;
};
- };
+ }
};
template <typename Handler>
@@ -375,6 +391,7 @@ TASKING_EXPORT extern const GroupItem nullItem;
TASKING_EXPORT extern const GroupItem sequential;
TASKING_EXPORT extern const GroupItem parallel;
+TASKING_EXPORT extern const GroupItem parallelIdealThreadCountLimit;
TASKING_EXPORT extern const GroupItem stopOnError;
TASKING_EXPORT extern const GroupItem continueOnError;
@@ -417,7 +434,7 @@ private:
std::invoke(handler);
return SetupResult::StopWithSuccess;
};
- };
+ }
};
template <typename Task, typename Deleter = std::default_delete<Task>>
@@ -474,7 +491,7 @@ private:
std::invoke(handler, *adapter.task());
return SetupResult::Continue;
};
- };
+ }
template <typename Handler>
static InterfaceDoneHandler wrapDone(Handler &&handler) {
@@ -513,7 +530,7 @@ private:
std::invoke(handler);
return result == DoneWith::Success ? DoneResult::Success : DoneResult::Error;
};
- };
+ }
};
class TASKING_EXPORT TaskTree final : public QObject
@@ -563,7 +580,7 @@ public:
wrapHandler<const StorageStruct>(std::forward<Handler>(handler)));
}
-signals:
+Q_SIGNALS:
void started();
void done(DoneWith result);
void asyncCountChanged(int count);
@@ -608,3 +625,7 @@ using TaskTreeTask = CustomTask<TaskTreeTaskAdapter>;
using TimeoutTask = CustomTask<TimeoutTaskAdapter>;
} // namespace Tasking
+
+QT_END_NAMESPACE
+
+#endif // TASKING_TASKTREE_H
diff --git a/src/libs/solutions/tasking/tasktreerunner.cpp b/src/libs/solutions/tasking/tasktreerunner.cpp
index 0f7e96db08..d0daa5daae 100644
--- a/src/libs/solutions/tasking/tasktreerunner.cpp
+++ b/src/libs/solutions/tasking/tasktreerunner.cpp
@@ -1,3 +1,4 @@
+// Copyright (C) 2024 Jarek Kobus
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
@@ -5,6 +6,8 @@
#include "tasktree.h"
+QT_BEGIN_NAMESPACE
+
namespace Tasking {
TaskTreeRunner::~TaskTreeRunner() = default;
@@ -38,3 +41,5 @@ void TaskTreeRunner::reset()
}
} // namespace Tasking
+
+QT_END_NAMESPACE
diff --git a/src/libs/solutions/tasking/tasktreerunner.h b/src/libs/solutions/tasking/tasktreerunner.h
index 92d9f9c0b4..ba41c0913f 100644
--- a/src/libs/solutions/tasking/tasktreerunner.h
+++ b/src/libs/solutions/tasking/tasktreerunner.h
@@ -1,12 +1,16 @@
+// Copyright (C) 2024 Jarek Kobus
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#pragma once
+#ifndef TASKING_TASKTREERUNNER_H
+#define TASKING_TASKTREERUNNER_H
#include "tasking_global.h"
#include "tasktree.h"
-#include <QObject>
+#include <QtCore/QObject>
+
+QT_BEGIN_NAMESPACE
namespace Tasking {
@@ -33,7 +37,7 @@ public:
// No done() signal is emitted.
void reset();
-signals:
+Q_SIGNALS:
void aboutToStart(TaskTree *taskTree);
void done(DoneWith result);
@@ -42,3 +46,7 @@ private:
};
} // namespace Tasking
+
+QT_END_NAMESPACE
+
+#endif // TASKING_TASKTREERUNNER_H
diff --git a/src/libs/sqlite/CMakeLists.txt b/src/libs/sqlite/CMakeLists.txt
index 2c7e1ebbf3..f86f31871a 100644
--- a/src/libs/sqlite/CMakeLists.txt
+++ b/src/libs/sqlite/CMakeLists.txt
@@ -31,7 +31,7 @@ endif()
add_qtc_library(Sqlite
PROPERTIES AUTOMOC OFF AUTOUIC OFF
- DEPENDS Qt::Core Threads::Threads ${CMAKE_DL_LIBS} SqliteInternal
+ DEPENDS Qt::Core Threads::Threads ${CMAKE_DL_LIBS} SqliteInternal Nanotrace
INCLUDES
../3rdparty/sqlite
PUBLIC_INCLUDES
@@ -58,7 +58,7 @@ add_qtc_library(Sqlite
sqlitesessionchangeset.cpp sqlitesessionchangeset.h
sqlitesessions.cpp sqlitesessions.h
sqlitetable.h
- sqlitetransaction.h
+ sqlitetracing.cpp sqlitetracing.h
sqlitetransaction.h
sqlitevalue.h
sqlitewritestatement.h
diff --git a/src/libs/sqlite/sqlitebasestatement.cpp b/src/libs/sqlite/sqlitebasestatement.cpp
index 406d49d248..8557bf6ad2 100644
--- a/src/libs/sqlite/sqlitebasestatement.cpp
+++ b/src/libs/sqlite/sqlitebasestatement.cpp
@@ -26,33 +26,7 @@ extern "C" int sqlite3_carray_bind(
namespace Sqlite {
-namespace {
-using TraceFile = NanotraceHR::TraceFile<sqliteTracingStatus()>;
-
-TraceFile traceFile{"sqlite.json"};
-
-thread_local NanotraceHR::EventQueueData<NanotraceHR::StringViewTraceEvent, 10000, sqliteTracingStatus()>
- eventQueueData(traceFile);
-thread_local NanotraceHR::EventQueue eventQueue = eventQueueData.createEventQueue();
-
-NanotraceHR::StringViewCategory<sqliteTracingStatus()> &sqliteLowLevelCategory();
-
-thread_local NanotraceHR::StringViewCategory<sqliteTracingStatus()> sqliteLowLevelCategory_{
- "sqlite low level"_t, eventQueue, sqliteLowLevelCategory};
-
-NanotraceHR::StringViewCategory<sqliteTracingStatus()> &sqliteLowLevelCategory()
-{
- return sqliteLowLevelCategory_;
-}
-
-thread_local NanotraceHR::StringViewCategory<sqliteTracingStatus()> sqliteHighLevelCategory_{
- "sqlite high level"_t, eventQueue, sqliteHighLevelCategory};
-} // namespace
-
-NanotraceHR::StringViewCategory<sqliteTracingStatus()> &sqliteHighLevelCategory()
-{
- return sqliteHighLevelCategory_;
-}
+using NanotraceHR::keyValue;
BaseStatement::BaseStatement(Utils::SmallStringView sqlStatement, Database &database)
: m_database(database)
@@ -108,14 +82,18 @@ void BaseStatement::waitForUnlockNotify() const
void BaseStatement::reset() const noexcept
{
- NanotraceHR::Tracer tracer{"reset"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"reset"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle())};
sqlite3_reset(m_compiledStatement.get());
}
bool BaseStatement::next() const
{
- NanotraceHR::Tracer tracer{"next"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"next"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle())};
int resultCode;
do {
@@ -142,7 +120,10 @@ void BaseStatement::step() const
void BaseStatement::bindNull(int index)
{
- NanotraceHR::Tracer tracer{"bind null"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind null"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index)};
int resultCode = sqlite3_bind_null(m_compiledStatement.get(), index);
if (resultCode != SQLITE_OK)
@@ -156,7 +137,11 @@ void BaseStatement::bind(int index, NullValue)
void BaseStatement::bind(int index, int value)
{
- NanotraceHR::Tracer tracer{"bind int"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind int"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("value", value)};
int resultCode = sqlite3_bind_int(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK)
@@ -165,7 +150,11 @@ void BaseStatement::bind(int index, int value)
void BaseStatement::bind(int index, long long value)
{
- NanotraceHR::Tracer tracer{"bind long long"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind long long"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("value", value)};
int resultCode = sqlite3_bind_int64(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK)
@@ -174,7 +163,11 @@ void BaseStatement::bind(int index, long long value)
void BaseStatement::bind(int index, double value)
{
- NanotraceHR::Tracer tracer{"bind double"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind double"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("value", value)};
int resultCode = sqlite3_bind_double(m_compiledStatement.get(), index, value);
if (resultCode != SQLITE_OK)
@@ -183,7 +176,11 @@ void BaseStatement::bind(int index, double value)
void BaseStatement::bind(int index, void *pointer)
{
- NanotraceHR::Tracer tracer{"bind pointer"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind pointer"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(pointer))};
int resultCode = sqlite3_bind_pointer(m_compiledStatement.get(), index, pointer, "carray", nullptr);
if (resultCode != SQLITE_OK)
@@ -192,7 +189,12 @@ void BaseStatement::bind(int index, void *pointer)
void BaseStatement::bind(int index, Utils::span<const int> values)
{
- NanotraceHR::Tracer tracer{"bind int span"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind int span"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(values.data())),
+ keyValue("size", values.size())};
int resultCode = sqlite3_carray_bind(m_compiledStatement.get(),
index,
@@ -206,7 +208,12 @@ void BaseStatement::bind(int index, Utils::span<const int> values)
void BaseStatement::bind(int index, Utils::span<const long long> values)
{
- NanotraceHR::Tracer tracer{"bind long long span"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind long long span"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(values.data())),
+ keyValue("size", values.size())};
int resultCode = sqlite3_carray_bind(m_compiledStatement.get(),
index,
@@ -220,7 +227,12 @@ void BaseStatement::bind(int index, Utils::span<const long long> values)
void BaseStatement::bind(int index, Utils::span<const double> values)
{
- NanotraceHR::Tracer tracer{"bind double span"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind double span"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(values.data())),
+ keyValue("size", values.size())};
int resultCode = sqlite3_carray_bind(m_compiledStatement.get(),
index,
@@ -234,7 +246,12 @@ void BaseStatement::bind(int index, Utils::span<const double> values)
void BaseStatement::bind(int index, Utils::span<const char *> values)
{
- NanotraceHR::Tracer tracer{"bind const char* span"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind const char* span"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(values.data())),
+ keyValue("size", values.size())};
int resultCode = sqlite3_carray_bind(m_compiledStatement.get(),
index,
@@ -248,7 +265,11 @@ void BaseStatement::bind(int index, Utils::span<const char *> values)
void BaseStatement::bind(int index, Utils::SmallStringView text)
{
- NanotraceHR::Tracer tracer{"bind string"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind string"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("text", text)};
int resultCode = sqlite3_bind_text(m_compiledStatement.get(),
index,
@@ -261,7 +282,12 @@ void BaseStatement::bind(int index, Utils::SmallStringView text)
void BaseStatement::bind(int index, BlobView blobView)
{
- NanotraceHR::Tracer tracer{"bind blob"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"bind blob"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("index", index),
+ keyValue("pointer", reinterpret_cast<std::uintptr_t>(blobView.data())),
+ keyValue("size", blobView.size())};
int resultCode = SQLITE_OK;
@@ -281,7 +307,11 @@ void BaseStatement::bind(int index, BlobView blobView)
void BaseStatement::bind(int index, const Value &value)
{
- NanotraceHR::Tracer tracer{"bind value"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{
+ "bind value"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ };
switch (value.type()) {
case ValueType::Integer:
@@ -304,7 +334,11 @@ void BaseStatement::bind(int index, const Value &value)
void BaseStatement::bind(int index, ValueView value)
{
- NanotraceHR::Tracer tracer{"bind value"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{
+ "bind value"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ };
switch (value.type()) {
case ValueType::Integer:
@@ -327,7 +361,9 @@ void BaseStatement::bind(int index, ValueView value)
void BaseStatement::prepare(Utils::SmallStringView sqlStatement)
{
- NanotraceHR::Tracer tracer{"prepare"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"prepare"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sql statement", sqlStatement)};
if (!m_database.isLocked())
throw DatabaseIsNotLocked{};
@@ -343,14 +379,18 @@ void BaseStatement::prepare(Utils::SmallStringView sqlStatement)
nullptr);
m_compiledStatement.reset(sqliteStatement);
- if (resultCode == SQLITE_LOCKED)
+ if (resultCode == SQLITE_LOCKED) {
+ tracer.tick("wait for unlock"_t);
waitForUnlockNotify();
+ }
} while (resultCode == SQLITE_LOCKED);
if (resultCode != SQLITE_OK)
Sqlite::throwError(resultCode, sqliteDatabaseHandle());
+
+ tracer.end(keyValue("sqlite statement", handle()));
}
sqlite3 *BaseStatement::sqliteDatabaseHandle() const
@@ -428,7 +468,10 @@ StringType convertToTextForColumn(sqlite3_stmt *sqlStatment, int column)
Type BaseStatement::fetchType(int column) const
{
- NanotraceHR::Tracer tracer{"fetch type"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch type"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
auto dataType = sqlite3_column_type(m_compiledStatement.get(), column);
@@ -450,9 +493,16 @@ Type BaseStatement::fetchType(int column) const
int BaseStatement::fetchIntValue(int column) const
{
- NanotraceHR::Tracer tracer{"fetch int"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch int"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto value = sqlite3_column_int(m_compiledStatement.get(), column);
- return sqlite3_column_int(m_compiledStatement.get(), column);
+ tracer.end(keyValue("value", value));
+
+ return value;
}
template<>
@@ -474,9 +524,16 @@ long BaseStatement::fetchValue<long>(int column) const
long long BaseStatement::fetchLongLongValue(int column) const
{
- NanotraceHR::Tracer tracer{"fetch long long"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch long long"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto value = sqlite3_column_int64(m_compiledStatement.get(), column);
- return sqlite3_column_int64(m_compiledStatement.get(), column);
+ tracer.end(keyValue("value", value));
+
+ return value;
}
template<>
@@ -487,14 +544,24 @@ long long BaseStatement::fetchValue<long long>(int column) const
double BaseStatement::fetchDoubleValue(int column) const
{
- NanotraceHR::Tracer tracer{"fetch double"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch double"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto value = sqlite3_column_double(m_compiledStatement.get(), column);
+
+ tracer.end(keyValue("value", value));
- return sqlite3_column_double(m_compiledStatement.get(), column);
+ return value;
}
BlobView BaseStatement::fetchBlobValue(int column) const
{
- NanotraceHR::Tracer tracer{"fetch blob"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{"fetch blob"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
return convertToBlobForColumn(m_compiledStatement.get(), column);
}
@@ -508,7 +575,16 @@ double BaseStatement::fetchValue<double>(int column) const
template<typename StringType>
StringType BaseStatement::fetchValue(int column) const
{
- return convertToTextForColumn<StringType>(m_compiledStatement.get(), column);
+ NanotraceHR::Tracer tracer{"fetch string value"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto text = convertToTextForColumn<StringType>(m_compiledStatement.get(), column);
+
+ tracer.end(keyValue("text", text));
+
+ return text;
}
template SQLITE_EXPORT Utils::SmallStringView BaseStatement::fetchValue<Utils::SmallStringView>(
@@ -520,11 +596,25 @@ template SQLITE_EXPORT Utils::PathString BaseStatement::fetchValue<Utils::PathSt
Utils::SmallStringView BaseStatement::fetchSmallStringViewValue(int column) const
{
- return fetchValue<Utils::SmallStringView>(column);
+ NanotraceHR::Tracer tracer{"fetch string view"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
+ auto text = fetchValue<Utils::SmallStringView>(column);
+
+ tracer.end(keyValue("text", text));
+
+ return text;
}
ValueView BaseStatement::fetchValueView(int column) const
{
+ NanotraceHR::Tracer tracer{"fetch value view"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", handle()),
+ keyValue("column", column)};
+
int dataType = sqlite3_column_type(m_compiledStatement.get(), column);
switch (dataType) {
case SQLITE_NULL:
@@ -544,7 +634,11 @@ ValueView BaseStatement::fetchValueView(int column) const
void BaseStatement::Deleter::operator()(sqlite3_stmt *statement)
{
- NanotraceHR::Tracer tracer{"finalize"_t, sqliteLowLevelCategory()};
+ NanotraceHR::Tracer tracer{
+ "finalize"_t,
+ sqliteLowLevelCategory(),
+ keyValue("sqlite statement", reinterpret_cast<std::uintptr_t>(statement)),
+ };
sqlite3_finalize(statement);
}
diff --git a/src/libs/sqlite/sqlitebasestatement.h b/src/libs/sqlite/sqlitebasestatement.h
index d2306c67a5..64acba0a27 100644
--- a/src/libs/sqlite/sqlitebasestatement.h
+++ b/src/libs/sqlite/sqlitebasestatement.h
@@ -9,6 +9,7 @@
#include "sqliteblob.h"
#include "sqliteexception.h"
#include "sqliteids.h"
+#include "sqlitetracing.h"
#include "sqlitetransaction.h"
#include "sqlitevalue.h"
@@ -30,31 +31,11 @@ using std::int64_t;
namespace Sqlite {
-using namespace NanotraceHR::Literals;
-
class Database;
class DatabaseBackend;
enum class Type : char { Invalid, Integer, Float, Text, Blob, Null };
-template<typename Enumeration>
-constexpr static std::underlying_type_t<Enumeration> to_underlying(Enumeration enumeration) noexcept
-{
- static_assert(std::is_enum_v<Enumeration>, "to_underlying expect an enumeration");
- return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
-}
-
-constexpr NanotraceHR::Tracing sqliteTracingStatus()
-{
-#ifdef ENABLE_SQLITE_TRACING
- return NanotraceHR::tracingStatus();
-#else
- return NanotraceHR::Tracing::IsDisabled;
-#endif
-}
-
-SQLITE_EXPORT NanotraceHR::StringViewCategory<sqliteTracingStatus()> &sqliteHighLevelCategory();
-
class SQLITE_EXPORT BaseStatement
{
public:
@@ -99,7 +80,7 @@ public:
template<typename Type, typename = std::enable_if_t<Type::IsBasicId::value>>
void bind(int index, Type id)
{
- if (id)
+ if (!id.isNull())
bind(index, id.internalId());
else
bindNull(index);
@@ -108,7 +89,7 @@ public:
template<typename Enumeration, std::enable_if_t<std::is_enum_v<Enumeration>, bool> = true>
void bind(int index, Enumeration enumeration)
{
- bind(index, to_underlying(enumeration));
+ bind(index, Utils::to_underlying(enumeration));
}
void bind(int index, uint value) { bind(index, static_cast<long long>(value)); }
@@ -136,6 +117,11 @@ public:
protected:
~BaseStatement() = default;
+ std::uintptr_t handle() const
+ {
+ return reinterpret_cast<std::uintptr_t>(m_compiledStatement.get());
+ }
+
private:
struct Deleter
{
@@ -166,7 +152,12 @@ public:
void execute()
{
- NanotraceHR::Tracer tracer{"execute"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{
+ "execute"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle()),
+ };
Resetter resetter{this};
BaseStatement::next();
@@ -175,7 +166,10 @@ public:
template<typename... ValueType>
void bindValues(const ValueType &...values)
{
- NanotraceHR::Tracer tracer{"bind"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"bind"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
static_assert(BindParameterCount == sizeof...(values), "Wrong binding parameter count!");
@@ -186,7 +180,10 @@ public:
template<typename... ValueType>
void write(const ValueType&... values)
{
- NanotraceHR::Tracer tracer{"write"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"write"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
bindValues(values...);
@@ -206,24 +203,37 @@ public:
struct is_container<QVarLengthArray<T, Prealloc>> : std::true_type
{};
+ template<typename T>
+ struct is_small_container : std::false_type
+ {};
+
+ template<typename T, qsizetype Prealloc>
+ struct is_small_container<QVarLengthArray<T, Prealloc>> : std::true_type
+ {};
+
template<typename Container,
std::size_t capacity = 32,
typename = std::enable_if_t<is_container<Container>::value>,
typename... QueryTypes>
auto values(const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"values"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"values"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
Container resultValues;
- resultValues.reserve(std::max(capacity, m_maximumResultCount));
+ using size_tupe = typename Container::size_type;
+ if constexpr (!is_small_container<Container>::value)
+ resultValues.reserve(static_cast<size_tupe>(std::max(capacity, m_maximumResultCount)));
bindValues(queryValues...);
while (BaseStatement::next())
emplaceBackValues(resultValues);
- setMaximumResultCount(resultValues.size());
+ setMaximumResultCount(static_cast<std::size_t>(resultValues.size()));
return resultValues;
}
@@ -241,7 +251,10 @@ public:
template<typename ResultType, typename... QueryTypes>
auto value(const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"value"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"value"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
ResultType resultValue{};
@@ -257,7 +270,10 @@ public:
template<typename ResultType, typename... QueryTypes>
auto optionalValue(const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"optionalValue"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"optionalValue"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
std::optional<ResultType> resultValue;
@@ -273,6 +289,7 @@ public:
template<typename Type>
static auto toValue(Utils::SmallStringView sqlStatement, Database &database)
{
+ using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"toValue"_t, sqliteHighLevelCategory()};
StatementImplementation statement(sqlStatement, database);
@@ -287,7 +304,10 @@ public:
template<typename Callable, typename... QueryTypes>
void readCallback(Callable &&callable, const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"readCallback"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"readCallback"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
@@ -304,7 +324,10 @@ public:
template<typename Container, typename... QueryTypes>
void readTo(Container &container, const QueryTypes &...queryValues)
{
- NanotraceHR::Tracer tracer{"readTo"_t, sqliteHighLevelCategory()};
+ using NanotraceHR::keyValue;
+ NanotraceHR::Tracer tracer{"readTo"_t,
+ sqliteHighLevelCategory(),
+ keyValue("sqlite statement", BaseStatement::handle())};
Resetter resetter{this};
@@ -402,9 +425,11 @@ public:
private:
using TracerCategory = std::decay_t<decltype(sqliteHighLevelCategory())>;
- NanotraceHR::Tracer<TracerCategory, typename TracerCategory::IsActive> tracer{
- "range"_t, sqliteHighLevelCategory()};
StatementImplementation &m_statement;
+ NanotraceHR::Tracer<TracerCategory, typename TracerCategory::IsActive> tracer{
+ "range"_t,
+ sqliteHighLevelCategory(),
+ NanotraceHR::keyValue("sqlite statement", m_statement.handle())};
};
template<typename ResultType>
diff --git a/src/libs/sqlite/sqliteexception.cpp b/src/libs/sqlite/sqliteexception.cpp
index b5f581ad68..bb4a474adb 100644
--- a/src/libs/sqlite/sqliteexception.cpp
+++ b/src/libs/sqlite/sqliteexception.cpp
@@ -3,14 +3,20 @@
#include "sqliteexception.h"
+#include "sqlitetracing.h"
+
#include <utils/smallstringio.h>
+#include <nanotrace/nanotracehr.h>
+
#include <sqlite.h>
#include <QDebug>
namespace Sqlite {
+using NanotraceHR::keyValue;
+
const char *Exception::what() const noexcept
{
return "Sqlite::Exception";
@@ -18,7 +24,10 @@ const char *Exception::what() const noexcept
const char *ExceptionWithMessage::what() const noexcept
{
- return "Sqlite::ExceptionWithMessage";
+ static Utils::SmallString text = Utils::SmallString::join(
+ {"Sqlite::ExceptionWithMessage", m_sqliteErrorMessage});
+
+ return text.data();
}
void ExceptionWithMessage::printWarning() const
@@ -26,6 +35,13 @@ void ExceptionWithMessage::printWarning() const
qWarning() << what() << m_sqliteErrorMessage;
}
+StatementIsBusy::StatementIsBusy(Utils::SmallString &&sqliteErrorMessage)
+ : ExceptionWithMessage{std::move(sqliteErrorMessage)}
+{
+ sqliteHighLevelCategory().threadEvent("StatementIsBusy"_t,
+ keyValue("error message", std::string_view{what()}));
+}
+
const char *StatementIsBusy::what() const noexcept
{
return "Sqlite::StatementIsBusy";
@@ -36,9 +52,19 @@ const char *DatabaseIsBusy::what() const noexcept
return "Sqlite::DatabaseIsBusy";
}
+StatementHasError::StatementHasError(Utils::SmallString &&sqliteErrorMessage)
+ : ExceptionWithMessage{std::move(sqliteErrorMessage)}
+{
+ sqliteHighLevelCategory().threadEvent("StatementHasError"_t,
+ keyValue("error message", std::string_view{what()}));
+}
+
const char *StatementHasError::what() const noexcept
{
- return "Sqlite::StatementHasError";
+ static Utils::SmallString text = Utils::SmallString::join(
+ {"Sqlite::StatementHasError: ", message()});
+
+ return text.data();
}
const char *StatementIsMisused::what() const noexcept
diff --git a/src/libs/sqlite/sqliteexception.h b/src/libs/sqlite/sqliteexception.h
index f0cadfc748..17a0639e19 100644
--- a/src/libs/sqlite/sqliteexception.h
+++ b/src/libs/sqlite/sqliteexception.h
@@ -23,13 +23,15 @@ public:
class SQLITE_EXPORT ExceptionWithMessage : public Exception
{
public:
- ExceptionWithMessage(Utils::SmallString &&sqliteErrorMessage = Utils::SmallString{})
+ ExceptionWithMessage(Utils::SmallString &&sqliteErrorMessage = {})
: m_sqliteErrorMessage(std::move(sqliteErrorMessage))
{}
const char *what() const noexcept override;
void printWarning() const;
+ std::string_view message() const noexcept { return m_sqliteErrorMessage; }
+
private:
Utils::SmallString m_sqliteErrorMessage;
};
@@ -37,7 +39,7 @@ private:
class SQLITE_EXPORT StatementIsBusy : public ExceptionWithMessage
{
public:
- using ExceptionWithMessage::ExceptionWithMessage;
+ StatementIsBusy(Utils::SmallString &&sqliteErrorMessage);
const char *what() const noexcept override;
};
@@ -90,7 +92,8 @@ public:
class SQLITE_EXPORT StatementHasError : public ExceptionWithMessage
{
public:
- using ExceptionWithMessage::ExceptionWithMessage;
+ StatementHasError(Utils::SmallString &&sqliteErrorMessage);
+
const char *what() const noexcept override;
};
diff --git a/src/libs/sqlite/sqliteids.h b/src/libs/sqlite/sqliteids.h
index d64e4d9645..abbb13d7c1 100644
--- a/src/libs/sqlite/sqliteids.h
+++ b/src/libs/sqlite/sqliteids.h
@@ -4,7 +4,9 @@
#pragma once
#include <utils/span.h>
+#include <utils/utility.h>
+#include <nanotrace/nanotracehr.h>
#include <type_traits>
#include <vector>
@@ -26,7 +28,15 @@ public:
return id;
}
- constexpr friend bool compareInvalidAreTrue(BasicId first, BasicId second)
+ template<typename Enumeration>
+ static constexpr BasicId createSpecialState(Enumeration specialState)
+ {
+ BasicId id;
+ id.id = ::Utils::to_underlying(specialState);
+ return id;
+ }
+
+ friend constexpr bool compareInvalidAreTrue(BasicId first, BasicId second)
{
return first.id == second.id;
}
@@ -56,6 +66,14 @@ public:
constexpr bool isValid() const { return id > 0; }
+ constexpr bool isNull() const { return id == 0; }
+
+ template<typename Enumeration>
+ constexpr bool hasSpecialState(Enumeration specialState) const
+ {
+ return id == ::Utils::to_underlying(specialState);
+ }
+
explicit operator bool() const { return isValid(); }
explicit operator std::size_t() const { return static_cast<std::size_t>(id); }
@@ -64,7 +82,16 @@ public:
[[noreturn, deprecated]] InternalIntegerType operator&() const { throw std::exception{}; }
-private:
+ template<typename String>
+ friend void convertToString(String &string, BasicId id)
+ {
+ if (id.isNull())
+ NanotraceHR::convertToString(string, "invalid null");
+ else
+ NanotraceHR::convertToString(string, id.internalId());
+ }
+
+protected:
InternalIntegerType id = 0;
};
@@ -88,4 +115,5 @@ struct hash<Sqlite::BasicId<Type, InternalIntegerType>>
return std::hash<InternalIntegerType>{}(id.internalId());
}
};
+
} // namespace std
diff --git a/src/libs/sqlite/sqliteindex.h b/src/libs/sqlite/sqliteindex.h
index f320fcd599..7c7a8dbb2b 100644
--- a/src/libs/sqlite/sqliteindex.h
+++ b/src/libs/sqlite/sqliteindex.h
@@ -40,6 +40,8 @@ public:
return Utils::SmallString::join({"CREATE ",
m_indexType == IndexType::Unique ? "UNIQUE " : "",
"INDEX IF NOT EXISTS index_",
+ kindName(),
+ "_",
m_tableName,
"_",
m_columnNames.join("_"),
@@ -65,6 +67,23 @@ public:
}
private:
+ std::string_view kindName() const
+ {
+ using namespace std::string_view_literals;
+
+ if (m_indexType == IndexType::Unique && m_condition.hasContent())
+ return "unique_partial"sv;
+
+ if (m_indexType == IndexType::Unique)
+ return "unique"sv;
+
+ if (m_condition.hasContent())
+ return "partial"sv;
+
+ return "normal"sv;
+ }
+
+private:
Utils::SmallString m_tableName;
Utils::SmallStringVector m_columnNames;
IndexType m_indexType;
diff --git a/src/libs/sqlite/sqlitetracing.cpp b/src/libs/sqlite/sqlitetracing.cpp
new file mode 100644
index 0000000000..700546f146
--- /dev/null
+++ b/src/libs/sqlite/sqlitetracing.cpp
@@ -0,0 +1,38 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "sqlitetracing.h"
+
+namespace Sqlite {
+
+TraceFile &traceFile()
+{
+ static TraceFile traceFile{"tracing.json"};
+
+ return traceFile;
+}
+
+namespace {
+
+thread_local NanotraceHR::EventQueue<NanotraceHR::StringViewWithStringArgumentsTraceEvent,
+ sqliteTracingStatus()>
+ eventQueue(traceFile());
+
+} // namespace
+
+NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()> &sqliteLowLevelCategory()
+{
+ thread_local NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()>
+ sqliteLowLevelCategory_{"sqlite low level"_t, eventQueue, sqliteLowLevelCategory};
+ return sqliteLowLevelCategory_;
+}
+
+NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()> &sqliteHighLevelCategory()
+{
+ thread_local NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()>
+ sqliteHighLevelCategory_{"sqlite high level"_t, eventQueue, sqliteHighLevelCategory};
+
+ return sqliteHighLevelCategory_;
+}
+
+} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlitetracing.h b/src/libs/sqlite/sqlitetracing.h
new file mode 100644
index 0000000000..8dadc6de0d
--- /dev/null
+++ b/src/libs/sqlite/sqlitetracing.h
@@ -0,0 +1,29 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "sqliteglobal.h"
+
+#include <nanotrace/nanotracehr.h>
+
+namespace Sqlite {
+using namespace NanotraceHR::Literals;
+
+constexpr NanotraceHR::Tracing sqliteTracingStatus()
+{
+#ifdef ENABLE_SQLITE_TRACING
+ return NanotraceHR::Tracing::IsEnabled;
+#else
+ return NanotraceHR::Tracing::IsDisabled;
+#endif
+}
+
+using TraceFile = NanotraceHR::TraceFile<sqliteTracingStatus()>;
+
+SQLITE_EXPORT TraceFile &traceFile();
+
+NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()> &sqliteLowLevelCategory();
+
+SQLITE_EXPORT NanotraceHR::StringViewWithStringArgumentsCategory<sqliteTracingStatus()> &
+sqliteHighLevelCategory();
+
+} // namespace Sqlite
diff --git a/src/libs/sqlite/sqlitevalue.h b/src/libs/sqlite/sqlitevalue.h
index fe576f3fec..7adadb43b3 100644
--- a/src/libs/sqlite/sqlitevalue.h
+++ b/src/libs/sqlite/sqlitevalue.h
@@ -6,6 +6,7 @@
#include "sqliteblob.h"
#include "sqliteexception.h"
+#include <nanotrace/nanotracehr.h>
#include <utils/smallstring.h>
#include <QVariant>
@@ -33,7 +34,7 @@ public:
explicit ValueBase(NullValue) {}
explicit ValueBase(VariantType &&value)
- : value(value)
+ : value(std::move(value))
{}
explicit ValueBase(const char *value)
@@ -43,6 +44,7 @@ public:
explicit ValueBase(long long value)
: value(value)
{}
+
explicit ValueBase(int value)
: value(static_cast<long long>(value))
{}
@@ -60,11 +62,6 @@ public:
{}
- explicit ValueBase(StringType &&value)
- : value(std::move(value))
-
- {}
-
explicit ValueBase(BlobView value)
: value(value)
@@ -229,14 +226,42 @@ public:
class ValueView : public ValueBase<Utils::SmallStringView, BlobView>
{
public:
+ ValueView() = default;
+
+ explicit ValueView(NullValue) {}
+
explicit ValueView(ValueBase &&base)
: ValueBase(std::move(base))
{}
+ explicit ValueView(Utils::SmallStringView value)
+ : ValueBase(value)
+ {}
+
+ explicit ValueView(BlobView value)
+ : ValueBase(value)
+ {}
+
+ explicit ValueView(long long value)
+ : ValueBase(value)
+ {}
+
+ explicit ValueView(int value)
+ : ValueBase(static_cast<long long>(value))
+ {}
+
+ explicit ValueView(uint value)
+ : ValueBase(static_cast<long long>(value))
+ {}
+
+ explicit ValueView(double value)
+ : ValueBase(value)
+ {}
+
template<typename Type>
static ValueView create(Type &&value_)
{
- return ValueView{ValueBase{value_}};
+ return ValueView(std::forward<Type>(value_));
}
};
@@ -348,18 +373,18 @@ private:
if (value.isNull())
return VariantType{NullValue{}};
- switch (value.userType()) {
- case QVariant::Int:
+ switch (value.typeId()) {
+ case QMetaType::Int:
return VariantType{static_cast<long long>(value.toInt())};
- case QVariant::LongLong:
+ case QMetaType::LongLong:
return VariantType{value.toLongLong()};
- case QVariant::UInt:
+ case QMetaType::UInt:
return VariantType{static_cast<long long>(value.toUInt())};
- case QVariant::Double:
+ case QMetaType::Double:
return VariantType{value.toFloat()};
- case QVariant::String:
+ case QMetaType::QString:
return VariantType{value.toString()};
- case QVariant::ByteArray:
+ case QMetaType::QByteArray:
return VariantType{Blob{value.toByteArray()}};
default:
throw CannotConvert();
@@ -386,4 +411,27 @@ private:
};
using Values = std::vector<Value>;
+
+template<typename String>
+void convertToString(String &string, const Value &value)
+{
+ switch (value.type()) {
+ case ValueType::Null:
+ convertToString(string, "null");
+ break;
+ case ValueType::Integer:
+ convertToString(string, value.toInteger());
+ break;
+ case ValueType::Float:
+ convertToString(string, value.toFloat());
+ break;
+ case ValueType::String:
+ convertToString(string, value.toStringView());
+ break;
+ case ValueType::Blob:
+ convertToString(string, "blob");
+ break;
+ }
+}
+
} // namespace Sqlite
diff --git a/src/libs/tracing/CMakeLists.txt b/src/libs/tracing/CMakeLists.txt
index e89f31f9cc..cd0014f0d2 100644
--- a/src/libs/tracing/CMakeLists.txt
+++ b/src/libs/tracing/CMakeLists.txt
@@ -105,3 +105,7 @@ qt_add_qml_module(Tracing
SOURCES
${TRACING_CPP_SOURCES}
)
+extend_qtc_library(Tracing
+ INCLUDES
+ ../utils/theme
+)
diff --git a/src/libs/tracing/timelineitemsrenderpass.cpp b/src/libs/tracing/timelineitemsrenderpass.cpp
index b93bd83b42..5b50a153e6 100644
--- a/src/libs/tracing/timelineitemsrenderpass.cpp
+++ b/src/libs/tracing/timelineitemsrenderpass.cpp
@@ -464,11 +464,7 @@ bool TimelineItemsMaterialShader::updateUniformData(RenderState &state,
TimelineItemsMaterial::TimelineItemsMaterial() : m_selectedItem(-1)
{
setFlag(QSGMaterial::Blending, false);
-#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
setFlag(QSGMaterial::NoBatching, true);
-#else
- setFlag(QSGMaterial::CustomCompileStep, true);
-#endif // >= Qt 6.3/6.0
}
QVector2D TimelineItemsMaterial::scale() const
diff --git a/src/libs/tracing/timelinenotesrenderpass.cpp b/src/libs/tracing/timelinenotesrenderpass.cpp
index 0f3fa00de3..c0cfa368b5 100644
--- a/src/libs/tracing/timelinenotesrenderpass.cpp
+++ b/src/libs/tracing/timelinenotesrenderpass.cpp
@@ -152,11 +152,7 @@ TimelineNotesRenderPassState::TimelineNotesRenderPassState(int numExpandedRows)
m_nullGeometry(NotesGeometry::point2DWithDistanceFromTop(), 0)
{
m_material.setFlag(QSGMaterial::Blending, true);
-#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
m_material.setFlag(QSGMaterial::NoBatching, true);
-#else
- m_material.setFlag(QSGMaterial::CustomCompileStep, true);
-#endif // >= Qt 6.3
m_expandedRows.reserve(numExpandedRows);
for (int i = 0; i < numExpandedRows; ++i)
m_expandedRows << createNode();
@@ -221,7 +217,7 @@ NotesMaterialShader::NotesMaterialShader()
static QColor notesColor()
{
return Utils::creatorTheme()
- ? Utils::creatorTheme()->color(Utils::Theme::Timeline_HighlightColor)
+ ? Utils::creatorColor(Utils::Theme::Timeline_HighlightColor)
: QColor(255, 165, 0);
}
diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt
index 18c8abda49..ca5e88b66c 100644
--- a/src/libs/utils/CMakeLists.txt
+++ b/src/libs/utils/CMakeLists.txt
@@ -31,6 +31,7 @@ add_qtc_library(Utils
completingtextedit.cpp completingtextedit.h
cpplanguage_details.h
crumblepath.cpp crumblepath.h
+ datafromprocess.h
delegates.cpp delegates.h
detailsbutton.cpp detailsbutton.h
detailswidget.cpp detailswidget.h
@@ -93,6 +94,7 @@ add_qtc_library(Utils
link.cpp link.h
listmodel.h
listutils.h
+ lua.cpp lua.h
macroexpander.cpp macroexpander.h
mathutils.cpp mathutils.h
mimeconstants.h
@@ -122,7 +124,6 @@ add_qtc_library(Utils
port.cpp port.h
portlist.cpp portlist.h
predicates.h
- process.cpp process.h
processenums.h
processhandle.cpp processhandle.h
processhelper.cpp processhelper.h
@@ -135,6 +136,7 @@ add_qtc_library(Utils
qrcparser.cpp qrcparser.h
qtcassert.cpp qtcassert.h
qtcolorbutton.cpp qtcolorbutton.h
+ qtcprocess.cpp qtcprocess.h
qtcsettings.cpp qtcsettings.h
ranges.h
reloadpromptutils.cpp reloadpromptutils.h
@@ -197,6 +199,7 @@ add_qtc_library(Utils
utilstr.h
utilsicons.cpp utilsicons.h
utiltypes.h
+ utility.h
variablechooser.cpp variablechooser.h
winutils.cpp winutils.h
wizard.cpp wizard.h
@@ -269,7 +272,6 @@ extend_qtc_library(Utils
fsengine/fsengine_impl.h
fsengine/diriterator.h
fsengine/fileiteratordevicesappender.h
- fsengine/rootinjectfsengine.h
fsengine/fixedlistfsengine.h
fsengine/fsenginehandler.cpp
fsengine/fsenginehandler.h
diff --git a/src/libs/utils/algorithm.h b/src/libs/utils/algorithm.h
index b781d68102..849316c9a9 100644
--- a/src/libs/utils/algorithm.h
+++ b/src/libs/utils/algorithm.h
@@ -22,7 +22,9 @@
#include <memory>
#include <optional>
+#include <tuple>
#include <type_traits>
+#include <utility>
namespace Utils
{
@@ -985,6 +987,14 @@ C filtered(const C &container, R (S::*predicate)() const)
return out;
}
+template<typename C, typename R, typename S>
+Q_REQUIRED_RESULT C filtered(const C &container, R S::*predicate)
+{
+ C out;
+ std::copy_if(std::begin(container), std::end(container), inserter(out), std::mem_fn(predicate));
+ return out;
+}
+
//////////////////
// filteredCast
/////////////////
@@ -1532,6 +1542,17 @@ void addToHash(QHash<Key, T> *result, const QHash<Key, T> &additionalContents)
result->insert(additionalContents);
}
+template <typename Tuple, std::size_t... I>
+static std::size_t tupleHashHelper(uint seed, const Tuple &tuple, std::index_sequence<I...>)
+{
+ return qHashMulti(seed, (std::get<I>(tuple), ...));
+}
+
+template<typename... T> std::size_t qHash(const std::tuple<T...> &tuple, uint seed = 0)
+{
+ return tupleHashHelper(seed, tuple, std::make_index_sequence<sizeof...(T)>());
+}
+
// Workaround for missing information from QSet::insert()
// Return type could be a pair like for std::set, but we never use the iterator anyway.
template<typename T, typename U> [[nodiscard]] bool insert(QSet<T> &s, const U &v)
diff --git a/src/libs/utils/aspects.cpp b/src/libs/utils/aspects.cpp
index b72fc2060e..e6b8267c4e 100644
--- a/src/libs/utils/aspects.cpp
+++ b/src/libs/utils/aspects.cpp
@@ -10,6 +10,7 @@
#include "guard.h"
#include "iconbutton.h"
#include "layoutbuilder.h"
+#include "macroexpander.h"
#include "passworddialog.h"
#include "pathchooser.h"
#include "pathlisteditor.h"
@@ -27,6 +28,7 @@
#include <QDebug>
#include <QGroupBox>
#include <QLabel>
+#include <QLayout>
#include <QLineEdit>
#include <QListWidget>
#include <QPaintEvent>
@@ -38,6 +40,7 @@
#include <QSpinBox>
#include <QStandardItemModel>
#include <QTextEdit>
+#include <QTreeWidget>
#include <QUndoStack>
using namespace Layouting;
@@ -93,6 +96,7 @@ public:
BaseAspect::DataCloner m_dataCloner;
QList<BaseAspect::DataExtractor> m_dataExtractors;
+ MacroExpander *m_expander = globalMacroExpander();
QUndoStack *m_undoStack = nullptr;
};
@@ -103,16 +107,32 @@ public:
\brief The \c BaseAspect class provides a common base for classes implementing
aspects.
- An aspect is a hunk of data like a property or collection of related
+ An \e aspect is a hunk of data like a property or collection of related
properties of some object, together with a description of its behavior
for common operations like visualizing or persisting.
- Simple aspects are for example a boolean property represented by a QCheckBox
+ Simple aspects are, for example, a boolean property represented by a QCheckBox
in the user interface, or a string property represented by a PathChooser,
- selecting directories in the filesystem.
+ for selecting directories in the filesystem.
- While aspects implementations usually have the ability to visualize and to persist
+ While aspects implementations usually can visualize and persist
their data, or use an ID, neither of these is mandatory.
+
+ The derived classes can implement addToLayout() to create a UI.
+
+ Implement \c guiToBuffer() and \c bufferToGui() to synchronize the UI with
+ the internal data.
+*/
+
+/*!
+ \enum Utils::BaseAspect::Announcement
+
+ Whether to emit a signal when a value changes.
+
+ \value DoEmit
+ Emit a signal.
+ \value BeQuiet
+ Don't emit a signal.
*/
/*!
@@ -159,7 +179,9 @@ QVariant BaseAspect::variantValue() const
/*!
Sets \a value.
- Prefer the typed setValue() of derived classes.
+ If \a howToAnnounce is set to \c DoEmit, emits the \c valueChanged signal.
+
+ Prefer the typed \c setValue() of the derived classes.
*/
void BaseAspect::setVariantValue(const QVariant &value, Announcement howToAnnounce)
{
@@ -185,7 +207,20 @@ QVariant BaseAspect::defaultVariantValue() const
}
/*!
- \fn TypedAspect::setDefaultValue(const ValueType &value)
+ \class Utils::TypedAspect
+ \inheaderfile utils/aspects.h
+ \inmodule QtCreator
+
+ \brief The \c TypedAspect class is a helper class for implementing a simple
+ aspect.
+
+ A typed aspect contains a single piece of data that is of the type
+ \c ValueType.
+*/
+
+
+/*!
+ \fn template <typename ValueType> void Utils::TypedAspect<ValueType>::setDefaultValue(const ValueType &value)
Sets a default \a value and the current value for this aspect.
@@ -248,14 +283,14 @@ QLabel *BaseAspect::createLabel()
return label;
}
-void BaseAspect::addLabeledItem(LayoutItem &parent, QWidget *widget)
+void BaseAspect::addLabeledItem(Layout &parent, QWidget *widget)
{
if (QLabel *l = createLabel()) {
l->setBuddy(widget);
parent.addItem(l);
- parent.addItem(Span(std::max(d->m_spanX - 1, 1), LayoutItem(widget)));
+ parent.addItem(Span(std::max(d->m_spanX - 1, 1), widget));
} else {
- parent.addItem(LayoutItem(widget));
+ parent.addItem(widget);
}
}
@@ -501,21 +536,20 @@ AspectContainer *BaseAspect::container() const
Adds the visual representation of this aspect to the layout with the
specified \a parent using a layout builder.
*/
-void BaseAspect::addToLayout(LayoutItem &)
+void BaseAspect::addToLayout(Layout &)
{
}
-void createItem(Layouting::LayoutItem *item, const BaseAspect &aspect)
+void addToLayout(Layouting::Layout *iface, BaseAspect &aspect)
{
- const_cast<BaseAspect &>(aspect).addToLayout(*item);
+ aspect.addToLayout(*iface);
}
-void createItem(Layouting::LayoutItem *item, const BaseAspect *aspect)
+void addToLayout(Layouting::Layout *item, BaseAspect *aspect)
{
- const_cast<BaseAspect *>(aspect)->addToLayout(*item);
+ aspect->addToLayout(*item);
}
-
/*!
Updates this aspect's value from user-initiated changes in the widget.
@@ -692,6 +726,27 @@ QVariant BaseAspect::fromSettingsValue(const QVariant &val) const
return d->m_fromSettings ? d->m_fromSettings(val) : val;
}
+void BaseAspect::setMacroExpander(MacroExpander *expander)
+{
+ d->m_expander = expander;
+}
+
+MacroExpander *BaseAspect::macroExpander() const
+{
+ return d->m_expander;
+}
+
+void BaseAspect::addMacroExpansion(QWidget *w)
+{
+ if (!d->m_expander)
+ return;
+ const auto chooser = new VariableChooser(w);
+ chooser->addSupportedWidget(w);
+ chooser->addMacroExpanderProvider([this] { return d->m_expander; });
+ if (auto pathChooser = qobject_cast<PathChooser *>(w))
+ pathChooser->setMacroExpander(d->m_expander);
+}
+
namespace Internal {
class BoolAspectPrivate
@@ -827,15 +882,15 @@ public:
aspect->bufferToGui();
}
- void addToLayoutFirst(LayoutItem &parent)
+ void addToLayoutFirst(Layout &parent)
{
if (m_checked && m_checkBoxPlacement == CheckBoxPlacement::Top) {
m_checked->addToLayout(parent);
- parent.addItem(br);
+ parent.flush();
}
}
- void addToLayoutLast(LayoutItem &parent)
+ void addToLayoutLast(Layout &parent)
{
if (m_checked && m_checkBoxPlacement == CheckBoxPlacement::Right)
m_checked->addToLayout(parent);
@@ -855,7 +910,6 @@ public:
Qt::TextElideMode m_elideMode = Qt::ElideNone;
QString m_placeHolderText;
Key m_historyCompleterKey;
- MacroExpanderProvider m_expanderProvider;
StringAspect::ValueAcceptor m_valueAcceptor;
std::optional<FancyLineEdit::ValidationFunction> m_validator;
@@ -900,6 +954,10 @@ public:
class StringListAspectPrivate
{
public:
+ UndoableValue<QStringList> undoable;
+ bool allowAdding{true};
+ bool allowRemoving{true};
+ bool allowEditing{true};
};
class FilePathListAspectPrivate
@@ -939,9 +997,6 @@ public:
Based on QTextEdit, used for user-editable strings that often
do not fit on a line.
- \value PathChooserDisplay
- Based on Utils::PathChooser.
-
\value PasswordLineEditDisplay
Based on QLineEdit, used for password strings
@@ -1086,16 +1141,6 @@ void StringAspect::setAcceptRichText(bool acceptRichText)
emit acceptRichTextChanged(acceptRichText);
}
-void StringAspect::setMacroExpanderProvider(const MacroExpanderProvider &expanderProvider)
-{
- d->m_expanderProvider = expanderProvider;
-}
-
-void StringAspect::setUseGlobalMacroExpander()
-{
- d->m_expanderProvider = &globalMacroExpander;
-}
-
void StringAspect::setUseResetButton()
{
d->m_useResetButton = true;
@@ -1112,18 +1157,10 @@ void StringAspect::setAutoApplyOnEditingFinished(bool applyOnEditingFinished)
d->m_autoApplyOnEditingFinished = applyOnEditingFinished;
}
-void StringAspect::addToLayout(LayoutItem &parent)
+void StringAspect::addToLayout(Layout &parent)
{
d->m_checkerImpl.addToLayoutFirst(parent);
- const auto useMacroExpander = [this](QWidget *w) {
- if (!d->m_expanderProvider)
- return;
- const auto chooser = new VariableChooser(w);
- chooser->addSupportedWidget(w);
- chooser->addMacroExpanderProvider(d->m_expanderProvider);
- };
-
const QString displayedString = d->m_displayFilter ? d->m_displayFilter(volatileValue())
: volatileValue();
@@ -1131,6 +1168,7 @@ void StringAspect::addToLayout(LayoutItem &parent)
case PasswordLineEditDisplay:
case LineEditDisplay: {
auto lineEditDisplay = createSubWidget<FancyLineEdit>();
+ addMacroExpansion(lineEditDisplay);
lineEditDisplay->setPlaceholderText(d->m_placeHolderText);
if (!d->m_historyCompleterKey.isEmpty())
lineEditDisplay->setHistoryCompleter(d->m_historyCompleterKey);
@@ -1164,7 +1202,6 @@ void StringAspect::addToLayout(LayoutItem &parent)
}
addLabeledItem(parent, lineEditDisplay);
- useMacroExpander(lineEditDisplay);
if (d->m_useResetButton) {
auto resetButton = createSubWidget<QPushButton>(Tr::tr("Reset"));
resetButton->setEnabled(lineEditDisplay->text() != defaultValue());
@@ -1222,6 +1259,7 @@ void StringAspect::addToLayout(LayoutItem &parent)
}
case TextEditDisplay: {
auto textEditDisplay = createSubWidget<QTextEdit>();
+ addMacroExpansion(textEditDisplay);
textEditDisplay->setPlaceholderText(d->m_placeHolderText);
textEditDisplay->setUndoRedoEnabled(false);
textEditDisplay->setAcceptRichText(d->m_acceptRichText);
@@ -1240,7 +1278,6 @@ void StringAspect::addToLayout(LayoutItem &parent)
}
addLabeledItem(parent, textEditDisplay);
- useMacroExpander(textEditDisplay);
bufferToGui();
connect(this,
&StringAspect::acceptRichTextChanged,
@@ -1290,8 +1327,10 @@ void StringAspect::addToLayout(LayoutItem &parent)
QString StringAspect::expandedValue() const
{
- if (!m_internal.isEmpty() && d->m_expanderProvider)
- return d->m_expanderProvider()->expand(m_internal);
+ if (!m_internal.isEmpty()) {
+ if (auto expander = macroExpander())
+ return expander->expand(m_internal);
+ }
return m_internal;
}
@@ -1373,10 +1412,10 @@ public:
PathChooser::Kind m_expectedKind = PathChooser::File;
Environment m_environment;
QPointer<PathChooser> m_pathChooserDisplay;
- MacroExpanderProvider m_expanderProvider;
FilePath m_baseFileName;
StringAspect::ValueAcceptor m_valueAcceptor;
std::optional<FancyLineEdit::ValidationFunction> m_validator;
+ std::optional<FilePath> m_effectiveBinary;
std::function<void()> m_openTerminal;
CheckableAspectImplementation m_checkerImpl;
@@ -1397,6 +1436,8 @@ FilePathAspect::FilePathAspect(AspectContainer *container)
addDataExtractor(this, &FilePathAspect::value, &Data::value);
addDataExtractor(this, &FilePathAspect::operator(), &Data::filePath);
+
+ connect(this, &BaseAspect::changed, this, [this] { d->m_effectiveBinary.reset(); });
}
FilePathAspect::~FilePathAspect() = default;
@@ -1416,7 +1457,35 @@ FilePath FilePathAspect::operator()() const
FilePath FilePathAspect::expandedValue() const
{
- return FilePath::fromUserInput(TypedAspect::value());
+ const auto value = TypedAspect::value();
+ if (!value.isEmpty()) {
+ if (auto expander = macroExpander())
+ return FilePath::fromUserInput(expander->expand(value));
+ }
+ return FilePath::fromUserInput(value);
+}
+
+/*!
+ Returns the full path of the set command. Only makes a difference if
+ expected kind is \c Command or \c ExistingCommand and the current
+ file path is an executable provided without its path.
+ Performs a lookup in PATH if necessary.
+ */
+FilePath FilePathAspect::effectiveBinary() const
+{
+ if (d->m_effectiveBinary)
+ return *d->m_effectiveBinary;
+
+ const FilePath current = expandedValue();
+ const PathChooser::Kind kind = d->m_expectedKind;
+ if (kind != PathChooser::ExistingCommand && kind != PathChooser::Command)
+ return current;
+
+ if (current.needsDevice())
+ return current;
+
+ d->m_effectiveBinary.emplace(current.searchInPath());
+ return *d->m_effectiveBinary;
}
QString FilePathAspect::value() const
@@ -1425,7 +1494,9 @@ QString FilePathAspect::value() const
}
/*!
- Sets the value of this file path aspect to \a value.
+ Sets the value of this file path aspect to \a filePath.
+
+ If \a howToAnnounce is set to \c DoEmit, emits the \c valueChanged signal.
\note This does not use any check that the value is actually
a file path.
@@ -1518,21 +1589,14 @@ PathChooser *FilePathAspect::pathChooser() const
return d->m_pathChooserDisplay.data();
}
-void FilePathAspect::addToLayout(Layouting::LayoutItem &parent)
+void FilePathAspect::addToLayout(Layouting::Layout &parent)
{
d->m_checkerImpl.addToLayoutFirst(parent);
- const auto useMacroExpander = [this](QWidget *w) {
- if (!d->m_expanderProvider)
- return;
- const auto chooser = new VariableChooser(w);
- chooser->addSupportedWidget(w);
- chooser->addMacroExpanderProvider(d->m_expanderProvider);
- };
-
const QString displayedString = d->m_displayFilter ? d->m_displayFilter(value()) : value();
d->m_pathChooserDisplay = createSubWidget<PathChooser>();
+ addMacroExpansion(d->m_pathChooserDisplay->lineEdit());
d->m_pathChooserDisplay->setExpectedKind(d->m_expectedKind);
if (!d->m_historyCompleterKey.isEmpty())
d->m_pathChooserDisplay->setHistoryCompleter(d->m_historyCompleterKey);
@@ -1557,7 +1621,6 @@ void FilePathAspect::addToLayout(Layouting::LayoutItem &parent)
d->m_pathChooserDisplay->lineEdit()->setPlaceholderText(d->m_placeHolderText);
d->m_checkerImpl.updateWidgetFromCheckStatus(this, d->m_pathChooserDisplay.data());
addLabeledItem(parent, d->m_pathChooserDisplay);
- useMacroExpander(d->m_pathChooserDisplay->lineEdit());
connect(d->m_pathChooserDisplay, &PathChooser::validChanged, this, &FilePathAspect::validChanged);
bufferToGui();
if (isAutoApply() && d->m_autoApplyOnEditingFinished) {
@@ -1655,9 +1718,12 @@ void FilePathAspect::setAutoApplyOnEditingFinished(bool applyOnEditingFinished)
*/
void FilePathAspect::setExpectedKind(const PathChooser::Kind expectedKind)
{
- d->m_expectedKind = expectedKind;
- if (d->m_pathChooserDisplay)
- d->m_pathChooserDisplay->setExpectedKind(expectedKind);
+ if (d->m_expectedKind != expectedKind) {
+ d->m_expectedKind = expectedKind;
+ d->m_effectiveBinary.reset();
+ if (d->m_pathChooserDisplay)
+ d->m_pathChooserDisplay->setExpectedKind(expectedKind);
+ }
}
void FilePathAspect::setEnvironment(const Environment &env)
@@ -1698,11 +1764,6 @@ void FilePathAspect::setHistoryCompleter(const Key &historyCompleterKey)
d->m_pathChooserDisplay->setHistoryCompleter(historyCompleterKey);
}
-void FilePathAspect::setMacroExpanderProvider(const MacroExpanderProvider &expanderProvider)
-{
- d->m_expanderProvider = expanderProvider;
-}
-
void FilePathAspect::validateInput()
{
if (d->m_pathChooserDisplay)
@@ -1736,7 +1797,7 @@ ColorAspect::ColorAspect(AspectContainer *container)
ColorAspect::~ColorAspect() = default;
-void ColorAspect::addToLayout(Layouting::LayoutItem &parent)
+void ColorAspect::addToLayout(Layouting::Layout &parent)
{
QTC_CHECK(!d->m_colorButton);
d->m_colorButton = createSubWidget<QtColorButton>();
@@ -1913,7 +1974,7 @@ BoolAspect::BoolAspect(AspectContainer *container)
*/
BoolAspect::~BoolAspect() = default;
-void BoolAspect::addToLayoutHelper(Layouting::LayoutItem &parent, QAbstractButton *button)
+void BoolAspect::addToLayoutHelper(Layouting::Layout &parent, QAbstractButton *button)
{
switch (d->m_labelPlacement) {
case LabelPlacement::Compact:
@@ -1922,7 +1983,7 @@ void BoolAspect::addToLayoutHelper(Layouting::LayoutItem &parent, QAbstractButto
break;
case LabelPlacement::AtCheckBox:
button->setText(labelText());
- parent.addItem(empty());
+ parent.addItem(empty);
parent.addItem(button);
break;
case LabelPlacement::InExtraLabel:
@@ -1940,20 +2001,18 @@ void BoolAspect::addToLayoutHelper(Layouting::LayoutItem &parent, QAbstractButto
});
}
-LayoutItem BoolAspect::adoptButton(QAbstractButton *button)
+std::function<void(Layouting::Layout *)> BoolAspect::adoptButton(QAbstractButton *button)
{
- LayoutItem parent;
-
- addToLayoutHelper(parent, button);
-
- bufferToGui();
- return parent;
+ return [this, button](Layouting::Layout *layout) {
+ addToLayoutHelper(*layout, button);
+ bufferToGui();
+ };
}
/*!
\reimp
*/
-void BoolAspect::addToLayout(Layouting::LayoutItem &parent)
+void BoolAspect::addToLayout(Layouting::Layout &parent)
{
QCheckBox *checkBox = createSubWidget<QCheckBox>();
addToLayoutHelper(parent, checkBox);
@@ -2059,7 +2118,7 @@ SelectionAspect::~SelectionAspect() = default;
/*!
\reimp
*/
-void SelectionAspect::addToLayout(Layouting::LayoutItem &parent)
+void SelectionAspect::addToLayout(Layouting::Layout &parent)
{
QTC_CHECK(d->m_buttonGroup == nullptr);
QTC_CHECK(!d->m_comboBox);
@@ -2233,7 +2292,7 @@ MultiSelectionAspect::~MultiSelectionAspect() = default;
/*!
\reimp
*/
-void MultiSelectionAspect::addToLayout(LayoutItem &builder)
+void MultiSelectionAspect::addToLayout(Layout &builder)
{
QTC_CHECK(d->m_listView == nullptr);
if (d->m_allValues.isEmpty())
@@ -2342,7 +2401,7 @@ IntegerAspect::~IntegerAspect() = default;
/*!
\reimp
*/
-void IntegerAspect::addToLayout(Layouting::LayoutItem &parent)
+void IntegerAspect::addToLayout(Layouting::Layout &parent)
{
QTC_CHECK(!d->m_spinBox);
d->m_spinBox = createSubWidget<QSpinBox>();
@@ -2354,7 +2413,7 @@ void IntegerAspect::addToLayout(Layouting::LayoutItem &parent)
if (d->m_maximumValue && d->m_maximumValue)
d->m_spinBox->setRange(int(d->m_minimumValue.value() / d->m_displayScaleFactor),
int(d->m_maximumValue.value() / d->m_displayScaleFactor));
- d->m_spinBox->setValue(int(value() / d->m_displayScaleFactor)); // Must happen after setRange()
+ bufferToGui();
addLabeledItem(parent, d->m_spinBox);
connect(d->m_spinBox.data(), &QSpinBox::valueChanged,
this, &IntegerAspect::handleGuiChanged);
@@ -2444,7 +2503,7 @@ DoubleAspect::~DoubleAspect() = default;
/*!
\reimp
*/
-void DoubleAspect::addToLayout(LayoutItem &builder)
+void DoubleAspect::addToLayout(Layout &builder)
{
QTC_CHECK(!d->m_spinBox);
d->m_spinBox = createSubWidget<QDoubleSpinBox>();
@@ -2511,9 +2570,9 @@ void DoubleAspect::setSingleStep(double step)
*/
TriStateAspect::TriStateAspect(AspectContainer *container,
- const QString &onString,
- const QString &offString,
- const QString &defaultString)
+ const QString &enabledDisplay,
+ const QString &disabledDisplay,
+ const QString &defaultDisplay)
: SelectionAspect(container)
{
setDisplayStyle(DisplayStyle::ComboBox);
@@ -2521,17 +2580,26 @@ TriStateAspect::TriStateAspect(AspectContainer *container,
SelectionAspect::addOption({});
SelectionAspect::addOption({});
SelectionAspect::addOption({});
- setOptionTexts(onString, offString, defaultString);
+ setOptionText(TriState::EnabledValue, enabledDisplay);
+ setOptionText(TriState::DisabledValue, disabledDisplay);
+ setOptionText(TriState::DefaultValue, defaultDisplay);
}
-void TriStateAspect::setOptionTexts(const QString &onString,
- const QString &offString,
- const QString &defaultString)
+static QString defaultTristateDisplay(TriState::Value tristate)
{
- QTC_ASSERT(d->m_options.size() == 3, return);
- d->m_options[0].displayName = onString.isEmpty() ? Tr::tr("Enable") : onString;
- d->m_options[1].displayName = offString.isEmpty() ? Tr::tr("Disable") : offString;
- d->m_options[2].displayName = defaultString.isEmpty() ? Tr::tr("Leave at Default") : defaultString;
+ switch (tristate) {
+ case TriState::EnabledValue: return Tr::tr("Enable");
+ case TriState::DisabledValue: return Tr::tr("Disable");
+ case TriState::DefaultValue: return Tr::tr("Default");
+ }
+ QTC_CHECK(false);
+ return {};
+}
+
+void TriStateAspect::setOptionText(const TriState::Value tristate, const QString &display)
+{
+ d->m_options[tristate].displayName = display.isEmpty()
+ ? defaultTristateDisplay(tristate) : display;
}
TriState TriStateAspect::value() const
@@ -2589,13 +2657,119 @@ StringListAspect::StringListAspect(AspectContainer *container)
*/
StringListAspect::~StringListAspect() = default;
-/*!
- \reimp
-*/
-void StringListAspect::addToLayout(LayoutItem &parent)
+bool StringListAspect::guiToBuffer()
{
- Q_UNUSED(parent)
- // TODO - when needed.
+ const QStringList newValue = d->undoable.get();
+ if (newValue != m_buffer) {
+ m_buffer = newValue;
+ return true;
+ }
+ return false;
+}
+
+void StringListAspect::bufferToGui()
+{
+ d->undoable.setWithoutUndo(m_buffer);
+}
+
+void StringListAspect::addToLayout(Layout &parent)
+{
+ d->undoable.setSilently(value());
+
+ auto editor = new QTreeWidget();
+ editor->setHeaderHidden(true);
+ editor->setRootIsDecorated(false);
+ editor->setEditTriggers(
+ d->allowEditing ? QAbstractItemView::AllEditTriggers : QAbstractItemView::NoEditTriggers);
+
+ QPushButton *add = d->allowAdding ? new QPushButton(Tr::tr("Add")) : nullptr;
+ QPushButton *remove = d->allowRemoving ? new QPushButton(Tr::tr("Remove")) : nullptr;
+
+ auto itemsToStringList = [editor] {
+ QStringList items;
+ const QTreeWidgetItem *rootItem = editor->invisibleRootItem();
+ for (int i = 0, count = rootItem->childCount(); i < count; ++i) {
+ auto expr = rootItem->child(i)->data(0, Qt::DisplayRole).toString();
+ items.append(expr);
+ }
+ return items;
+ };
+
+ auto populate = [editor, this] {
+ editor->clear();
+ for (const QString &entry : d->undoable.get()) {
+ auto item = new QTreeWidgetItem(editor, {entry});
+ item->setData(0, Qt::ToolTipRole, entry);
+ item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
+ }
+ };
+
+ if (add) {
+ connect(add, &QPushButton::clicked, this, [this, populate, editor] {
+ d->undoable.setSilently(d->undoable.get() << "");
+ populate();
+ const QTreeWidgetItem *root = editor->invisibleRootItem();
+ QTreeWidgetItem *lastChild = root->child(root->childCount() - 1);
+ const QModelIndex index = editor->indexFromItem(lastChild, 0);
+ editor->edit(index);
+ });
+ }
+
+ if (remove) {
+ connect(remove, &QPushButton::clicked, this, [this, editor, itemsToStringList] {
+ const QList<QTreeWidgetItem *> selected = editor->selectedItems();
+ QTC_ASSERT(selected.size() == 1, return);
+ editor->invisibleRootItem()->removeChild(selected.first());
+ delete selected.first();
+ d->undoable.set(undoStack(), itemsToStringList());
+ });
+ }
+
+ connect(
+ &d->undoable.m_signal, &UndoSignaller::changed, editor, [this, populate, itemsToStringList] {
+ if (itemsToStringList() != d->undoable.get())
+ populate();
+
+ handleGuiChanged();
+ });
+
+ connect(
+ editor->model(),
+ &QAbstractItemModel::dataChanged,
+ this,
+ [this,
+ itemsToStringList](const QModelIndex &tl, const QModelIndex &br, const QList<int> &roles) {
+ if (!roles.contains(Qt::DisplayRole))
+ return;
+ if (tl != br)
+ return;
+ d->undoable.set(undoStack(), itemsToStringList());
+ });
+
+ populate();
+
+ parent.addItem(
+ // clang-format off
+ Column {
+ createLabel(),
+ Row {
+ editor,
+ If { d->allowAdding || d->allowRemoving, {
+ Column {
+ If { d->allowAdding, {add}, {}},
+ If { d->allowRemoving, {remove}, {}},
+ st,
+ }
+ }, {}},
+ }
+ } // clang-format on
+ );
+
+ registerSubWidget(editor);
+ if (d->allowAdding)
+ registerSubWidget(add);
+ if (d->allowRemoving)
+ registerSubWidget(remove);
}
void StringListAspect::appendValue(const QString &s, bool allowDuplicates)
@@ -2631,6 +2805,32 @@ void StringListAspect::removeValues(const QStringList &values)
setValue(val);
}
+void StringListAspect::setUiAllowAdding(bool allowAdding)
+{
+ d->allowAdding = allowAdding;
+}
+void StringListAspect::setUiAllowRemoving(bool allowRemoving)
+{
+ d->allowRemoving = allowRemoving;
+}
+void StringListAspect::setUiAllowEditing(bool allowEditing)
+{
+ d->allowEditing = allowEditing;
+}
+
+bool StringListAspect::uiAllowAdding() const
+{
+ return d->allowAdding;
+}
+bool StringListAspect::uiAllowRemoving() const
+{
+ return d->allowRemoving;
+}
+bool StringListAspect::uiAllowEditing() const
+{
+ return d->allowEditing;
+}
+
/*!
\class Utils::FilePathListAspect
\inmodule QtCreator
@@ -2668,7 +2868,7 @@ void FilePathListAspect::bufferToGui()
d->undoable.setWithoutUndo(m_buffer);
}
-void FilePathListAspect::addToLayout(LayoutItem &parent)
+void FilePathListAspect::addToLayout(Layout &parent)
{
d->undoable.setSilently(value());
@@ -2762,7 +2962,7 @@ IntegersAspect::~IntegersAspect() = default;
/*!
\reimp
*/
-void IntegersAspect::addToLayout(Layouting::LayoutItem &parent)
+void IntegersAspect::addToLayout(Layouting::Layout &parent)
{
Q_UNUSED(parent)
// TODO - when needed.
@@ -2781,8 +2981,8 @@ void IntegersAspect::addToLayout(Layouting::LayoutItem &parent)
*/
/*!
- Constructs a text display showing the \a message with an icon representing
- type \a type.
+ Constructs a text display with the parent \a container. The display shows
+ \a message and an icon representing the type \a type.
*/
TextDisplay::TextDisplay(AspectContainer *container, const QString &message, InfoLabel::InfoType type)
: BaseAspect(container), d(new Internal::TextDisplayPrivate)
@@ -2799,7 +2999,7 @@ TextDisplay::~TextDisplay() = default;
/*!
\reimp
*/
-void TextDisplay::addToLayout(LayoutItem &parent)
+void TextDisplay::addToLayout(Layout &parent)
{
if (!d->m_label) {
d->m_label = createSubWidget<InfoLabel>(d->m_message, d->m_type);
@@ -2851,7 +3051,7 @@ public:
QList<BaseAspect *> m_items; // Both owned and non-owned.
QList<BaseAspect *> m_ownedItems; // Owned only.
QStringList m_settingsGroup;
- std::function<Layouting::LayoutItem ()> m_layouter;
+ std::function<Layouting::Layout()> m_layouter;
};
AspectContainer::AspectContainer()
@@ -2866,6 +3066,11 @@ AspectContainer::~AspectContainer()
qDeleteAll(d->m_ownedItems);
}
+void AspectContainer::addToLayout(Layouting::Layout &parent)
+{
+ parent.addItem(layouter()());
+}
+
/*!
\internal
*/
@@ -2875,6 +3080,8 @@ void AspectContainer::registerAspect(BaseAspect *aspect, bool takeOwnership)
d->m_items.append(aspect);
if (takeOwnership)
d->m_ownedItems.append(aspect);
+
+ connect(aspect, &BaseAspect::changed, this, [this]() { emit changed(); });
}
void AspectContainer::registerAspects(const AspectContainer &aspects)
@@ -2903,12 +3110,12 @@ AspectContainer::const_iterator AspectContainer::end() const
return d->m_items.cend();
}
-void AspectContainer::setLayouter(const std::function<Layouting::LayoutItem ()> &layouter)
+void AspectContainer::setLayouter(const std::function<Layouting::Layout ()> &layouter)
{
d->m_layouter = layouter;
}
-std::function<LayoutItem ()> AspectContainer::layouter() const
+std::function<Layout ()> AspectContainer::layouter() const
{
return d->m_layouter;
}
@@ -3024,6 +3231,14 @@ void AspectContainer::setUndoStack(QUndoStack *undoStack)
aspect->setUndoStack(undoStack);
}
+void AspectContainer::setMacroExpander(MacroExpander *expander)
+{
+ BaseAspect::setMacroExpander(expander);
+
+ for (BaseAspect *aspect : std::as_const(d->m_items))
+ aspect->setMacroExpander(expander);
+}
+
bool AspectContainer::equals(const AspectContainer &other) const
{
// FIXME: Expensive, but should not really be needed in a fully aspectified world.
@@ -3405,7 +3620,7 @@ private:
int m_index;
};
-void AspectList::addToLayout(Layouting::LayoutItem &parent)
+void AspectList::addToLayout(Layouting::Layout &parent)
{
using namespace Layouting;
@@ -3424,7 +3639,7 @@ void AspectList::addToLayout(Layouting::LayoutItem &parent)
addItem(d->createItem());
});
- Column column{noMargin()};
+ Column column{noMargin};
forEachItem<BaseAspect>([&column, this](const std::shared_ptr<BaseAspect> &item, int idx) {
auto removeBtn = new IconButton;
@@ -3515,7 +3730,7 @@ bool StringSelectionAspect::guiToBuffer()
return oldBuffer != m_buffer;
}
-void StringSelectionAspect::addToLayout(Layouting::LayoutItem &parent)
+void StringSelectionAspect::addToLayout(Layouting::Layout &parent)
{
QTC_ASSERT(m_fillCallback, return);
@@ -3588,4 +3803,4 @@ void StringSelectionAspect::addToLayout(Layouting::LayoutItem &parent)
return addLabeledItem(parent, comboBox);
}
-} // namespace Utils
+} // Utils
diff --git a/src/libs/utils/aspects.h b/src/libs/utils/aspects.h
index 2941cfec61..60a198c1f7 100644
--- a/src/libs/utils/aspects.h
+++ b/src/libs/utils/aspects.h
@@ -7,7 +7,6 @@
#include "guiutils.h"
#include "id.h"
#include "infolabel.h"
-#include "macroexpander.h"
#include "pathchooser.h"
#include "qtcsettings.h"
#include "store.h"
@@ -29,15 +28,14 @@ class QStandardItemModel;
class QItemSelectionModel;
QT_END_NAMESPACE
-namespace Layouting {
-class LayoutItem;
-}
+namespace Layouting { class Layout; }
namespace Utils {
class AspectContainer;
class BoolAspect;
class CheckableDecider;
+class MacroExpander;
namespace Internal {
class AspectContainerPrivate;
@@ -64,6 +62,7 @@ class QTCREATOR_UTILS_EXPORT BaseAspect : public QObject
public:
BaseAspect(AspectContainer *container = nullptr);
+ BaseAspect(const BaseAspect &) = delete;
~BaseAspect() override;
Id id() const;
@@ -125,9 +124,7 @@ public:
virtual void toMap(Store &map) const;
virtual void toActiveMap(Store &map) const { toMap(map); }
virtual void volatileToMap(Store &map) const;
-
- virtual void addToLayout(Layouting::LayoutItem &parent);
-
+ virtual void addToLayout(Layouting::Layout &parent);
virtual void readSettings();
virtual void writeSettings() const;
@@ -205,6 +202,9 @@ public:
// This is expensive. Do not use without good reason
void writeToSettingsImmediatly() const;
+ void setMacroExpander(MacroExpander *expander);
+ MacroExpander *macroExpander() const;
+
signals:
void changed();
void volatileValueChanged();
@@ -222,8 +222,10 @@ protected:
virtual void handleGuiChanged();
+ void addMacroExpansion(QWidget *w);
+
QLabel *createLabel();
- void addLabeledItem(Layouting::LayoutItem &parent, QWidget *widget);
+ void addLabeledItem(Layouting::Layout &parent, QWidget *widget);
void setDataCreatorHelper(const DataCreator &creator) const;
void setDataClonerHelper(const DataCloner &cloner) const;
@@ -276,8 +278,8 @@ private:
friend class Internal::CheckableAspectImplementation;
};
-QTCREATOR_UTILS_EXPORT void createItem(Layouting::LayoutItem *item, const BaseAspect &aspect);
-QTCREATOR_UTILS_EXPORT void createItem(Layouting::LayoutItem *item, const BaseAspect *aspect);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layouting::Layout *layout, BaseAspect *aspect);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layouting::Layout *layout, BaseAspect &aspect);
template<typename ValueType>
class
@@ -439,7 +441,7 @@ public:
BoolAspect(AspectContainer *container = nullptr);
~BoolAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
std::function<void(QObject *)> groupChecker();
Utils::CheckableDecider askAgainCheckableDecider();
@@ -452,10 +454,10 @@ public:
LabelPlacement labelPlacement = LabelPlacement::InExtraLabel);
void setLabelPlacement(LabelPlacement labelPlacement);
- Layouting::LayoutItem adoptButton(QAbstractButton *button);
+ std::function<void(Layouting::Layout *)> adoptButton(QAbstractButton *button);
private:
- void addToLayoutHelper(Layouting::LayoutItem &parent, QAbstractButton *button);
+ void addToLayoutHelper(Layouting::Layout &parent, QAbstractButton *button);
void bufferToGui() override;
bool guiToBuffer() override;
@@ -504,7 +506,7 @@ public:
ColorAspect(AspectContainer *container = nullptr);
~ColorAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
private:
void bufferToGui() override;
@@ -521,7 +523,7 @@ public:
SelectionAspect(AspectContainer *container = nullptr);
~SelectionAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
void finish() override;
QString stringValue() const;
@@ -569,7 +571,7 @@ public:
MultiSelectionAspect(AspectContainer *container = nullptr);
~MultiSelectionAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
enum class DisplayStyle { ListView };
void setDisplayStyle(DisplayStyle style);
@@ -596,7 +598,7 @@ public:
StringAspect(AspectContainer *container = nullptr);
~StringAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
QString operator()() const { return expandedValue(); }
QString expandedValue() const;
@@ -610,8 +612,6 @@ public:
void setPlaceHolderText(const QString &placeHolderText);
void setHistoryCompleter(const Key &historyCompleterKey);
void setAcceptRichText(bool acceptRichText);
- void setMacroExpanderProvider(const MacroExpanderProvider &expanderProvider);
- void setUseGlobalMacroExpander();
void setUseResetButton();
void setValidationFunction(const FancyLineEdit::ValidationFunction &validator);
void setAutoApplyOnEditingFinished(bool applyOnEditingFinished);
@@ -666,6 +666,7 @@ public:
};
FilePath operator()() const;
+ FilePath effectiveBinary() const;
FilePath expandedValue() const;
QString value() const;
void setValue(const FilePath &filePath, Announcement howToAnnounce = DoEmit);
@@ -687,7 +688,6 @@ public:
void setValidationFunction(const FancyLineEdit::ValidationFunction &validator);
void setDisplayFilter(const std::function<QString (const QString &)> &displayFilter);
void setHistoryCompleter(const Key &historyCompleterKey);
- void setMacroExpanderProvider(const MacroExpanderProvider &expanderProvider);
void setShowToolTipOnLabel(bool show);
void setAutoApplyOnEditingFinished(bool applyOnEditingFinished);
@@ -703,7 +703,7 @@ public:
PathChooser *pathChooser() const; // Avoid to use.
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
void fromMap(const Utils::Store &map) override;
void toMap(Utils::Store &map) const override;
@@ -730,7 +730,7 @@ public:
IntegerAspect(AspectContainer *container = nullptr);
~IntegerAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
void setRange(qint64 min, qint64 max);
void setLabel(const QString &label); // FIXME: Use setLabelText
@@ -759,7 +759,7 @@ public:
DoubleAspect(AspectContainer *container = nullptr);
~DoubleAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
void setRange(double min, double max);
void setPrefix(const QString &prefix);
@@ -777,11 +777,11 @@ private:
class QTCREATOR_UTILS_EXPORT TriState
{
+public:
enum Value { EnabledValue, DisabledValue, DefaultValue };
- explicit TriState(Value v) : m_value(v) {}
-public:
TriState() = default;
+ explicit TriState(Value v) : m_value(v) {}
int toInt() const { return int(m_value); }
QVariant toVariant() const { return int(m_value); }
@@ -805,9 +805,9 @@ class QTCREATOR_UTILS_EXPORT TriStateAspect : public SelectionAspect
public:
TriStateAspect(AspectContainer *container = nullptr,
- const QString &onString = {},
- const QString &offString = {},
- const QString &defaultString = {});
+ const QString &enabledDisplay = {},
+ const QString &disabledDisplay = {},
+ const QString &defaultDisplay = {});
TriState operator()() const { return value(); }
TriState value() const;
@@ -816,9 +816,8 @@ public:
TriState defaultValue() const;
void setDefaultValue(TriState setting);
- void setOptionTexts(const QString &onString,
- const QString &offString,
- const QString &defaultString);
+ void setOptionText(const TriState::Value tristate, const QString &display);
+
private:
void addOption(const QString &displayName, const QString &toolTip = {}) = delete;
void addOption(const Option &option) = delete;
@@ -832,13 +831,24 @@ public:
StringListAspect(AspectContainer *container = nullptr);
~StringListAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ bool guiToBuffer() override;
+ void bufferToGui() override;
+
+ void addToLayout(Layouting::Layout &parent) override;
void appendValue(const QString &value, bool allowDuplicates = true);
void removeValue(const QString &value);
void appendValues(const QStringList &values, bool allowDuplicates = true);
void removeValues(const QStringList &values);
+ void setUiAllowAdding(bool allowAdding);
+ void setUiAllowRemoving(bool allowRemoving);
+ void setUiAllowEditing(bool allowEditing);
+
+ bool uiAllowAdding() const;
+ bool uiAllowRemoving() const;
+ bool uiAllowEditing() const;
+
private:
std::unique_ptr<Internal::StringListAspectPrivate> d;
};
@@ -856,7 +866,7 @@ public:
bool guiToBuffer() override;
void bufferToGui() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
void setPlaceHolderText(const QString &placeHolderText);
void appendValue(const FilePath &path, bool allowDuplicates = true);
@@ -876,7 +886,7 @@ public:
IntegersAspect(AspectContainer *container = nullptr);
~IntegersAspect() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
};
class QTCREATOR_UTILS_EXPORT TextDisplay : public BaseAspect
@@ -889,7 +899,7 @@ public:
InfoLabel::InfoType type = InfoLabel::None);
~TextDisplay() override;
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
void setIconType(InfoLabel::InfoType t);
void setText(const QString &message);
@@ -940,6 +950,8 @@ public:
AspectContainer(const AspectContainer &) = delete;
AspectContainer &operator=(const AspectContainer &) = delete;
+ void addToLayout(Layouting::Layout &parent) override;
+
void registerAspect(BaseAspect *aspect, bool takeOwnership = false);
void registerAspects(const AspectContainer &aspects);
@@ -965,6 +977,8 @@ public:
bool isDirty() override;
void setUndoStack(QUndoStack *undoStack) override;
+ void setMacroExpander(MacroExpander *expander);
+
template <typename T> T *aspect() const
{
for (BaseAspect *aspect : aspects())
@@ -990,8 +1004,8 @@ public:
const_iterator begin() const;
const_iterator end() const;
- void setLayouter(const std::function<Layouting::LayoutItem()> &layouter);
- std::function<Layouting::LayoutItem()> layouter() const;
+ void setLayouter(const std::function<Layouting::Layout()> &layouter);
+ std::function<Layouting::Layout()> layouter() const;
signals:
void applied();
@@ -1132,7 +1146,7 @@ public:
QVariant volatileVariantValue() const override { return {}; }
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
private:
std::unique_ptr<Internal::AspectListPrivate> d;
@@ -1144,7 +1158,7 @@ class QTCREATOR_UTILS_EXPORT StringSelectionAspect : public Utils::TypedAspect<Q
public:
StringSelectionAspect(Utils::AspectContainer *container = nullptr);
- void addToLayout(Layouting::LayoutItem &parent) override;
+ void addToLayout(Layouting::Layout &parent) override;
using ResultCallback = std::function<void(QList<QStandardItem *> items)>;
using FillCallback = std::function<void(ResultCallback)>;
diff --git a/src/libs/utils/async.cpp b/src/libs/utils/async.cpp
index 33487db73b..b67e83fc72 100644
--- a/src/libs/utils/async.cpp
+++ b/src/libs/utils/async.cpp
@@ -17,16 +17,6 @@ public:
}
};
-#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
-Q_GLOBAL_STATIC_WITH_ARGS(AsyncThreadPool, s_idle, (QThread::IdlePriority));
-Q_GLOBAL_STATIC_WITH_ARGS(AsyncThreadPool, s_lowest, (QThread::LowestPriority));
-Q_GLOBAL_STATIC_WITH_ARGS(AsyncThreadPool, s_low, (QThread::LowPriority));
-Q_GLOBAL_STATIC_WITH_ARGS(AsyncThreadPool, s_normal, (QThread::NormalPriority));
-Q_GLOBAL_STATIC_WITH_ARGS(AsyncThreadPool, s_high, (QThread::HighPriority));
-Q_GLOBAL_STATIC_WITH_ARGS(AsyncThreadPool, s_highest, (QThread::HighestPriority));
-Q_GLOBAL_STATIC_WITH_ARGS(AsyncThreadPool, s_timeCritical, (QThread::TimeCriticalPriority));
-Q_GLOBAL_STATIC_WITH_ARGS(AsyncThreadPool, s_inherit, (QThread::InheritPriority));
-#else
Q_GLOBAL_STATIC(AsyncThreadPool, s_idle, QThread::IdlePriority);
Q_GLOBAL_STATIC(AsyncThreadPool, s_lowest, QThread::LowestPriority);
Q_GLOBAL_STATIC(AsyncThreadPool, s_low, QThread::LowPriority);
@@ -35,7 +25,6 @@ Q_GLOBAL_STATIC(AsyncThreadPool, s_high, QThread::HighPriority);
Q_GLOBAL_STATIC(AsyncThreadPool, s_highest, QThread::HighestPriority);
Q_GLOBAL_STATIC(AsyncThreadPool, s_timeCritical, QThread::TimeCriticalPriority);
Q_GLOBAL_STATIC(AsyncThreadPool, s_inherit, QThread::InheritPriority);
-#endif
QThreadPool *asyncThreadPool(QThread::Priority priority)
{
diff --git a/src/libs/utils/async.h b/src/libs/utils/async.h
index af6fb3fd85..8c7155bc13 100644
--- a/src/libs/utils/async.h
+++ b/src/libs/utils/async.h
@@ -7,6 +7,7 @@
#include "futuresynchronizer.h"
#include "qtcassert.h"
+#include "threadutils.h"
#include <solutions/tasking/tasktree.h>
@@ -130,7 +131,9 @@ template <typename ResultType>
class Async : public AsyncBase
{
public:
- Async() {
+ Async()
+ : m_synchronizer(isMainThread() ? futureSynchronizer() : nullptr)
+ {
connect(&m_watcher, &QFutureWatcherBase::finished, this, &AsyncBase::done);
connect(&m_watcher, &QFutureWatcherBase::resultReadyAt, this, &AsyncBase::resultReadyAt);
}
diff --git a/src/libs/utils/buildablehelperlibrary.cpp b/src/libs/utils/buildablehelperlibrary.cpp
index 3b54d11412..2be40b6c9b 100644
--- a/src/libs/utils/buildablehelperlibrary.cpp
+++ b/src/libs/utils/buildablehelperlibrary.cpp
@@ -4,7 +4,7 @@
#include "buildablehelperlibrary.h"
#include "environment.h"
#include "hostosinfo.h"
-#include "process.h"
+#include "qtcprocess.h"
#include <QDebug>
#include <QRegularExpression>
diff --git a/src/libs/utils/changeset.cpp b/src/libs/utils/changeset.cpp
index c0c54b1e48..881097c6b7 100644
--- a/src/libs/utils/changeset.cpp
+++ b/src/libs/utils/changeset.cpp
@@ -100,6 +100,41 @@ void ChangeSet::clear()
m_error = false;
}
+ChangeSet ChangeSet::makeReplace(const Range &range, const QString &replacement)
+{
+ ChangeSet c;
+ c.replace(range, replacement);
+ return c;
+}
+
+ChangeSet ChangeSet::makeReplace(int start, int end, const QString &replacement)
+{
+ ChangeSet c;
+ c.replace(start, end, replacement);
+ return c;
+}
+
+ChangeSet ChangeSet::makeRemove(const Range &range)
+{
+ ChangeSet c;
+ c.remove(range);
+ return c;
+}
+
+ChangeSet ChangeSet::makeFlip(int start1, int end1, int start2, int end2)
+{
+ ChangeSet c;
+ c.flip(start1, end1, start2, end2);
+ return c;
+}
+
+ChangeSet ChangeSet::makeInsert(int pos, const QString &text)
+{
+ ChangeSet c;
+ c.insert(pos, text);
+ return c;
+}
+
bool ChangeSet::replace_helper(int pos, int length, const QString &replacement)
{
if (hasOverlap(pos, length))
diff --git a/src/libs/utils/changeset.h b/src/libs/utils/changeset.h
index 6370b20d3c..aee9c99dab 100644
--- a/src/libs/utils/changeset.h
+++ b/src/libs/utils/changeset.h
@@ -76,6 +76,12 @@ public:
void clear();
+ static ChangeSet makeReplace(const Range &range, const QString &replacement);
+ static ChangeSet makeReplace(int start, int end, const QString &replacement);
+ static ChangeSet makeRemove(const Range &range);
+ static ChangeSet makeFlip(int start1, int end1, int start2, int end2);
+ static ChangeSet makeInsert(int pos, const QString &text);
+
bool replace(const Range &range, const QString &replacement);
bool remove(const Range &range);
bool move(const Range &range, int to);
diff --git a/src/libs/utils/checkablemessagebox.cpp b/src/libs/utils/checkablemessagebox.cpp
index 19d3669ada..1d721e04c8 100644
--- a/src/libs/utils/checkablemessagebox.cpp
+++ b/src/libs/utils/checkablemessagebox.cpp
@@ -3,6 +3,7 @@
#include "checkablemessagebox.h"
+#include "guiutils.h"
#include "hostosinfo.h"
#include "qtcassert.h"
#include "qtcsettings.h"
@@ -95,7 +96,7 @@ static QMessageBox::StandardButton exec(
return acceptButton;
}
- QMessageBox msgBox(parent);
+ QMessageBox msgBox(dialogParent(parent));
prepare(icon, title, text, decider, buttons, defaultButton, buttonTextOverrides, msg, msgBox);
msgBox.exec();
@@ -131,7 +132,7 @@ static void show(QWidget *parent,
return;
}
- QMessageBox *msgBox = new QMessageBox(parent);
+ QMessageBox *msgBox = new QMessageBox(dialogParent(parent));
prepare(icon, title, text, decider, buttons, defaultButton, buttonTextOverrides, msg, *msgBox);
std::optional<QPointer<QObject>> guardPtr;
diff --git a/src/libs/utils/clangutils.cpp b/src/libs/utils/clangutils.cpp
index 6cf265820e..86e8e6204e 100644
--- a/src/libs/utils/clangutils.cpp
+++ b/src/libs/utils/clangutils.cpp
@@ -4,7 +4,7 @@
#include "clangutils.h"
#include "filepath.h"
-#include "process.h"
+#include "qtcprocess.h"
#include "utilstr.h"
#include <QVersionNumber>
@@ -65,7 +65,7 @@ bool checkClangdVersion(const FilePath &clangd, QString *error)
QVersionNumber minimumClangdVersion()
{
- return QVersionNumber(14);
+ return QVersionNumber(17);
}
} // namespace Utils
diff --git a/src/libs/utils/codegeneration.cpp b/src/libs/utils/codegeneration.cpp
index 8842023a23..ad3b8bad18 100644
--- a/src/libs/utils/codegeneration.cpp
+++ b/src/libs/utils/codegeneration.cpp
@@ -29,23 +29,6 @@ QTCREATOR_UTILS_EXPORT QString fileNameToCppIdentifier(const QString &s)
return rc;
}
-QTCREATOR_UTILS_EXPORT QString headerGuard(const QString &file)
-{
- return headerGuard(file, QStringList());
-}
-
-QTCREATOR_UTILS_EXPORT QString headerGuard(const QString &file, const QStringList &namespaceList)
-{
- const QChar underscore = QLatin1Char('_');
- QString rc;
- for (int i = 0; i < namespaceList.count(); i++)
- rc += namespaceList.at(i).toUpper() + underscore;
-
- const QFileInfo fi(file);
- rc += fileNameToCppIdentifier(fi.fileName()).toUpper();
- return rc;
-}
-
QTCREATOR_UTILS_EXPORT
void writeIncludeFileDirective(const QString &file, bool globalInclude,
QTextStream &str)
diff --git a/src/libs/utils/codegeneration.h b/src/libs/utils/codegeneration.h
index 9ea0bb28c7..e1c297eb92 100644
--- a/src/libs/utils/codegeneration.h
+++ b/src/libs/utils/codegeneration.h
@@ -17,9 +17,6 @@ namespace Utils {
// or replacing them by an underscore).
QTCREATOR_UTILS_EXPORT QString fileNameToCppIdentifier(const QString &s);
-QTCREATOR_UTILS_EXPORT QString headerGuard(const QString &file);
-QTCREATOR_UTILS_EXPORT QString headerGuard(const QString &file, const QStringList &namespaceList);
-
QTCREATOR_UTILS_EXPORT
void writeIncludeFileDirective(const QString &file,
bool globalInclude,
diff --git a/src/libs/utils/commandline.cpp b/src/libs/utils/commandline.cpp
index 8e6bc60277..b4fa60481e 100644
--- a/src/libs/utils/commandline.cpp
+++ b/src/libs/utils/commandline.cpp
@@ -1425,6 +1425,19 @@ CommandLine::CommandLine(const FilePath &exe, const QStringList &args)
addArgs(args);
}
+CommandLine::CommandLine(const FilePath &exe, std::initializer_list<ArgRef> args)
+ : m_executable(exe)
+{
+ for (const ArgRef &arg : args) {
+ if (const auto ptr = std::get_if<const char *>(&arg.m_arg))
+ addArg(QString::fromUtf8(*ptr));
+ else if (const auto ptr = std::get_if<std::reference_wrapper<const QString>>(&arg.m_arg))
+ addArg(*ptr);
+ else if (const auto ptr = std::get_if<std::reference_wrapper<const QStringList>>(&arg.m_arg))
+ addArgs(*ptr);
+ }
+}
+
CommandLine::CommandLine(const FilePath &exe, const QStringList &args, OsType osType)
: m_executable(exe)
{
diff --git a/src/libs/utils/commandline.h b/src/libs/utils/commandline.h
index 52ff8c5496..3718f80bca 100644
--- a/src/libs/utils/commandline.h
+++ b/src/libs/utils/commandline.h
@@ -11,6 +11,8 @@
#include <QPair>
#include <QStringList>
+#include <variant>
+
namespace Utils {
class AbstractMacroExpander;
@@ -120,8 +122,22 @@ public:
CommandLine();
~CommandLine();
+ struct ArgRef
+ {
+ ArgRef(const char *arg) : m_arg(arg) {}
+ ArgRef(const QString &arg) : m_arg(arg) {}
+ ArgRef(const QStringList &args) : m_arg(args) {}
+
+ private:
+ friend class CommandLine;
+ const std::variant<const char *,
+ std::reference_wrapper<const QString>,
+ std::reference_wrapper<const QStringList>> m_arg;
+ };
+
explicit CommandLine(const FilePath &executable);
CommandLine(const FilePath &exe, const QStringList &args);
+ CommandLine(const FilePath &exe, std::initializer_list<ArgRef> args);
CommandLine(const FilePath &exe, const QStringList &args, OsType osType);
CommandLine(const FilePath &exe, const QString &unparsedArgs, RawType);
diff --git a/src/libs/utils/crumblepath.cpp b/src/libs/utils/crumblepath.cpp
index 779b1c9e55..d434350460 100644
--- a/src/libs/utils/crumblepath.cpp
+++ b/src/libs/utils/crumblepath.cpp
@@ -111,9 +111,9 @@ void CrumblePathButton::paintEvent(QPaintEvent*)
p.drawPixmap(width() - overlapSize, segmentRect.top(), middleSegmentPixmap);
if (option.state & QStyle::State_Enabled)
- option.palette.setColor(QPalette::ButtonText, creatorTheme()->color(Theme::PanelTextColorLight));
+ option.palette.setColor(QPalette::ButtonText, creatorColor(Theme::PanelTextColorLight));
else
- option.palette.setColor(QPalette::Disabled, QPalette::ButtonText, creatorTheme()->color(Theme::IconsDisabledColor));
+ option.palette.setColor(QPalette::Disabled, QPalette::ButtonText, creatorColor(Theme::IconsDisabledColor));
QStylePainter sp(this);
if (option.state & QStyle::State_Sunken)
diff --git a/src/libs/utils/datafromprocess.h b/src/libs/utils/datafromprocess.h
new file mode 100644
index 0000000000..5acb220ba4
--- /dev/null
+++ b/src/libs/utils/datafromprocess.h
@@ -0,0 +1,148 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "algorithm.h"
+#include "commandline.h"
+#include "environment.h"
+#include "filepath.h"
+#include "qtcprocess.h"
+
+#include <QDateTime>
+#include <QHash>
+#include <QMutex>
+#include <QMutexLocker>
+
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <optional>
+#include <utility>
+
+namespace Utils {
+
+// Use this facility for cached retrieval of data from a tool that always returns the same
+// output for the same parameters and is side effect free.
+// A prime example is version info via a --version switch.
+template<typename Data> class DataFromProcess
+{
+public:
+ class Parameters
+ {
+ public:
+ using OutputParser = std::function<std::optional<Data>(const QString &)>;
+ using ErrorHandler = std::function<void(const Process &)>;
+ using Callback = std::function<void(const std::optional<Data> &)>;
+
+ Parameters(const CommandLine &cmdLine, const OutputParser &parser)
+ : commandLine(cmdLine)
+ , parser(parser)
+ {}
+
+ CommandLine commandLine;
+ Environment environment = Environment::systemEnvironment();
+ std::chrono::seconds timeout = std::chrono::seconds(10);
+ OutputParser parser;
+ ErrorHandler errorHandler;
+ Callback callback;
+ QList<ProcessResult> allowedResults{ProcessResult::FinishedWithSuccess};
+ };
+
+ // Use the first variant whenever possible.
+ static void provideData(const Parameters &params);
+ static std::optional<Data> getData(const Parameters &params);
+
+private:
+ using Key = std::tuple<FilePath, QStringList, QString>;
+ using Value = std::pair<std::optional<Data>, QDateTime>;
+
+ static std::optional<Data> getOrProvideData(const Parameters &params);
+ static std::optional<Data> handleProcessFinished(const Parameters &params,
+ const QDateTime &exeTimestamp,
+ const Key &cacheKey,
+ const std::shared_ptr<Process> &process);
+
+ static inline QHash<Key, Value> m_cache;
+ static inline QMutex m_cacheMutex;
+};
+
+template<typename Data>
+inline void DataFromProcess<Data>::provideData(const Parameters &params)
+{
+ QTC_ASSERT(params.callback, return);
+ getOrProvideData(params);
+}
+
+template<typename Data>
+inline std::optional<Data> DataFromProcess<Data>::getData(const Parameters &params)
+{
+ QTC_ASSERT(!params.callback, return {});
+ return getOrProvideData(params);
+}
+
+template<typename Data>
+inline std::optional<Data> DataFromProcess<Data>::getOrProvideData(const Parameters &params)
+{
+ if (params.commandLine.executable().isEmpty()) {
+ if (params.callback)
+ params.callback({});
+ return {};
+ }
+
+ const auto key = std::make_tuple(params.commandLine.executable(),
+ params.environment.toStringList(),
+ params.commandLine.arguments());
+ const QDateTime exeTimestamp = params.commandLine.executable().lastModified();
+ {
+ QMutexLocker<QMutex> cacheLocker(&m_cacheMutex);
+ const auto it = m_cache.constFind(key);
+ if (it != m_cache.constEnd() && it.value().second == exeTimestamp)
+ return it.value().first;
+ }
+
+ const auto outputRetriever = std::make_shared<Process>();
+ outputRetriever->setCommand(params.commandLine);
+ if (params.callback) {
+ QObject::connect(outputRetriever.get(),
+ &Process::done,
+ [params, exeTimestamp, key, outputRetriever] {
+ handleProcessFinished(params, exeTimestamp, key, outputRetriever);
+ });
+ outputRetriever->start();
+ return {};
+ }
+
+ outputRetriever->runBlocking(params.timeout);
+ return handleProcessFinished(params, exeTimestamp, key, outputRetriever);
+}
+
+template<typename Data>
+inline std::optional<Data> DataFromProcess<Data>::handleProcessFinished(
+ const Parameters &params,
+ const QDateTime &exeTimestamp,
+ const Key &cacheKey,
+ const std::shared_ptr<Process> &process)
+{
+ // Do not store into cache: The next call might succeed.
+ if (process->result() == ProcessResult::Canceled) {
+ if (params.callback)
+ params.callback({});
+ return {};
+ }
+
+ std::optional<Data> data;
+ if (params.allowedResults.contains(process->result()))
+ data = params.parser(process->cleanedStdOut());
+ else if (params.errorHandler)
+ params.errorHandler(*process);
+ QMutexLocker<QMutex> cacheLocker(&m_cacheMutex);
+ m_cache.insert(cacheKey, std::make_pair(data, exeTimestamp));
+ if (params.callback) {
+ params.callback(data);
+ return {};
+ }
+ return data;
+}
+
+} // namespace Utils
diff --git a/src/libs/utils/detailsbutton.cpp b/src/libs/utils/detailsbutton.cpp
index 88c0ca273e..c6e1b6da3f 100644
--- a/src/libs/utils/detailsbutton.cpp
+++ b/src/libs/utils/detailsbutton.cpp
@@ -87,8 +87,8 @@ QColor DetailsButton::outlineColor()
{
return HostOsInfo::isMacHost()
? QGuiApplication::palette().color(QPalette::Mid)
- : StyleHelper::mergedColors(creatorTheme()->color(Theme::TextColorNormal),
- creatorTheme()->color(Theme::BackgroundColorNormal), 15);
+ : StyleHelper::mergedColors(creatorColor(Theme::TextColorNormal),
+ creatorColor(Theme::BackgroundColorNormal), 15);
}
void DetailsButton::paintEvent(QPaintEvent *e)
diff --git a/src/libs/utils/detailswidget.cpp b/src/libs/utils/detailswidget.cpp
index ec7a3d22eb..d278a609ff 100644
--- a/src/libs/utils/detailswidget.cpp
+++ b/src/libs/utils/detailswidget.cpp
@@ -224,7 +224,7 @@ void DetailsWidget::paintEvent(QPaintEvent *paintEvent)
QPainter p(this);
if (creatorTheme()->flag(Theme::FlatProjectsMode) || HostOsInfo::isMacHost()) {
const QColor bgColor = creatorTheme()->flag(Theme::FlatProjectsMode) ?
- creatorTheme()->color(Theme::DetailsWidgetBackgroundColor)
+ creatorColor(Theme::DetailsWidgetBackgroundColor)
: palette().color(QPalette::Window);
p.fillRect(rect(), bgColor);
}
diff --git a/src/libs/utils/devicefileaccess.cpp b/src/libs/utils/devicefileaccess.cpp
index a38a098d38..303ffa6bf5 100644
--- a/src/libs/utils/devicefileaccess.cpp
+++ b/src/libs/utils/devicefileaccess.cpp
@@ -13,7 +13,7 @@
#include "utilstr.h"
#ifndef UTILS_STATIC_LIBRARY
-#include "process.h"
+#include "qtcprocess.h"
#endif
#include <QCoreApplication>
@@ -314,12 +314,10 @@ expected_str<QByteArray> DeviceFileAccess::fileContents(const FilePath &filePath
}
expected_str<qint64> DeviceFileAccess::writeFileContents(const FilePath &filePath,
- const QByteArray &data,
- qint64 offset) const
+ const QByteArray &data) const
{
Q_UNUSED(filePath)
Q_UNUSED(data)
- Q_UNUSED(offset)
QTC_CHECK(false);
return make_unexpected(
Tr::tr("writeFileContents is not implemented for \"%1\".").arg(filePath.toUserOutput()));
@@ -738,8 +736,7 @@ expected_str<QByteArray> DesktopDeviceFileAccess::fileContents(const FilePath &f
}
expected_str<qint64> DesktopDeviceFileAccess::writeFileContents(const FilePath &filePath,
- const QByteArray &data,
- qint64 offset) const
+ const QByteArray &data) const
{
QFile file(filePath.path());
const bool isOpened = file.open(QFile::WriteOnly | QFile::Truncate);
@@ -747,8 +744,6 @@ expected_str<qint64> DesktopDeviceFileAccess::writeFileContents(const FilePath &
return make_unexpected(
Tr::tr("Could not open file \"%1\" for writing.").arg(filePath.toUserOutput()));
- if (offset != 0)
- file.seek(offset);
qint64 res = file.write(data);
if (res != data.size())
return make_unexpected(
@@ -1064,6 +1059,10 @@ expected_str<QByteArray> UnixDeviceFileAccess::fileContents(const FilePath &file
if (disconnected())
return make_unexpected_disconnected();
+ expected_str<FilePath> localSource = filePath.localSource();
+ if (localSource && *localSource != filePath)
+ return localSource->fileContents(limit, offset);
+
QStringList args = {"if=" + filePath.path()};
if (limit > 0 || offset > 0) {
const qint64 gcd = std::gcd(limit, offset);
@@ -1088,17 +1087,16 @@ expected_str<QByteArray> UnixDeviceFileAccess::fileContents(const FilePath &file
}
expected_str<qint64> UnixDeviceFileAccess::writeFileContents(const FilePath &filePath,
- const QByteArray &data,
- qint64 offset) const
+ const QByteArray &data) const
{
if (disconnected())
return make_unexpected_disconnected();
+ expected_str<FilePath> localSource = filePath.localSource();
+ if (localSource && *localSource != filePath)
+ return localSource->writeFileContents(data);
+
QStringList args = {"of=" + filePath.path()};
- if (offset != 0) {
- args.append("bs=1");
- args.append(QString("seek=%1").arg(offset));
- }
RunResult result = runInShell({"dd", args, OsType::OsTypeLinux}, data);
if (result.exitCode != 0) {
diff --git a/src/libs/utils/devicefileaccess.h b/src/libs/utils/devicefileaccess.h
index ba48454127..8a0884ec1d 100644
--- a/src/libs/utils/devicefileaccess.h
+++ b/src/libs/utils/devicefileaccess.h
@@ -69,8 +69,7 @@ protected:
qint64 offset) const;
virtual expected_str<qint64> writeFileContents(const FilePath &filePath,
- const QByteArray &data,
- qint64 offset) const;
+ const QByteArray &data) const;
virtual expected_str<FilePath> createTempFile(const FilePath &filePath);
};
@@ -125,8 +124,7 @@ protected:
qint64 limit,
qint64 offset) const override;
expected_str<qint64> writeFileContents(const FilePath &filePath,
- const QByteArray &data,
- qint64 offset) const override;
+ const QByteArray &data) const override;
expected_str<FilePath> createTempFile(const FilePath &filePath) override;
@@ -178,8 +176,7 @@ protected:
qint64 limit,
qint64 offset) const override;
expected_str<qint64> writeFileContents(const FilePath &filePath,
- const QByteArray &data,
- qint64 offset) const override;
+ const QByteArray &data) const override;
expected_str<FilePath> createTempFile(const FilePath &filePath) override;
diff --git a/src/libs/utils/deviceshell.cpp b/src/libs/utils/deviceshell.cpp
index abccaa1b56..69abc5ce4a 100644
--- a/src/libs/utils/deviceshell.cpp
+++ b/src/libs/utils/deviceshell.cpp
@@ -3,7 +3,7 @@
#include "deviceshell.h"
-#include "process.h"
+#include "qtcprocess.h"
#include "processinterface.h"
#include "qtcassert.h"
#include "utilstr.h"
diff --git a/src/libs/utils/dropsupport.cpp b/src/libs/utils/dropsupport.cpp
index 7416edf9c3..4ed1245643 100644
--- a/src/libs/utils/dropsupport.cpp
+++ b/src/libs/utils/dropsupport.cpp
@@ -108,7 +108,7 @@ bool DropSupport::eventFilter(QObject *obj, QEvent *event)
de->acceptProposedAction();
bool needToScheduleEmit = m_files.isEmpty();
m_files.append(tempFiles);
- m_dropPos = de->pos();
+ m_dropPos = de->position().toPoint();
if (needToScheduleEmit) { // otherwise we already have a timer pending
// Delay the actual drop, to avoid conflict between
// actions that happen when opening files, and actions that the item views do
@@ -122,7 +122,7 @@ bool DropSupport::eventFilter(QObject *obj, QEvent *event)
accepted = true;
bool needToScheduleEmit = m_values.isEmpty();
m_values.append(fileDropMimeData->values());
- m_dropPos = de->pos();
+ m_dropPos = de->position().toPoint();
if (needToScheduleEmit)
QTimer::singleShot(100, this, &DropSupport::emitValuesDropped);
}
diff --git a/src/libs/utils/environment.cpp b/src/libs/utils/environment.cpp
index be43f2a206..190cde2d2d 100644
--- a/src/libs/utils/environment.cpp
+++ b/src/libs/utils/environment.cpp
@@ -433,60 +433,82 @@ const NameValueDictionary &Environment::resolved() const
m_dict = Environment::systemEnvironment().toDictionary();
m_fullDict = true;
break;
- case SetFixedDictionary:
- m_dict = std::get<SetFixedDictionary>(item);
- m_fullDict = true;
+ case SetFixedDictionary: {
+ const auto dict = std::get_if<SetFixedDictionary>(&item);
+ if (QTC_GUARD(dict)) {
+ m_dict = *dict;
+ m_fullDict = true;
+ }
break;
+ }
case SetValue: {
- auto [key, value, enabled] = std::get<SetValue>(item);
- m_dict.set(key, value, enabled);
+ const auto setvalue = std::get_if<SetValue>(&item);
+ if (QTC_GUARD(setvalue)) {
+ auto [key, value, enabled] = *setvalue;
+ m_dict.set(key, value, enabled);
+ }
break;
}
case SetFallbackValue: {
- auto [key, value] = std::get<SetFallbackValue>(item);
- if (m_fullDict) {
- if (m_dict.value(key).isEmpty())
+ const auto fallbackvalue = std::get_if<SetFallbackValue>(&item);
+ if (QTC_GUARD(fallbackvalue)) {
+ auto [key, value] = *fallbackvalue;
+ if (m_fullDict) {
+ if (m_dict.value(key).isEmpty())
+ m_dict.set(key, value, true);
+ } else {
+ QTC_ASSERT(false, qDebug() << "operating on partial dictionary");
m_dict.set(key, value, true);
- } else {
- QTC_ASSERT(false, qDebug() << "operating on partial dictionary");
- m_dict.set(key, value, true);
+ }
}
break;
}
- case UnsetValue:
- m_dict.unset(std::get<UnsetValue>(item));
+ case UnsetValue: {
+ const auto unsetvalue = std::get_if<UnsetValue>(&item);
+ if (QTC_GUARD(unsetvalue))
+ m_dict.unset(*unsetvalue);
break;
+ }
case PrependOrSet: {
- auto [key, value, sep] = std::get<PrependOrSet>(item);
- QTC_ASSERT(!key.contains('='), return m_dict);
- const auto it = m_dict.findKey(key);
- if (it == m_dict.m_values.end()) {
- m_dict.m_values.insert(DictKey(key, m_dict.nameCaseSensitivity()), {value, true});
- } else {
- // Prepend unless it is already there
- const QString toPrepend = value + pathListSeparator(sep);
- if (!it.value().first.startsWith(toPrepend))
- it.value().first.prepend(toPrepend);
+ const auto prependorset = std::get_if<PrependOrSet>(&item);
+ if (QTC_GUARD(prependorset)) {
+ auto [key, value, sep] = *prependorset;
+ QTC_ASSERT(!key.contains('='), return m_dict);
+ const auto it = m_dict.findKey(key);
+ if (it == m_dict.m_values.end()) {
+ m_dict.m_values.insert(DictKey(key, m_dict.nameCaseSensitivity()), {value, true});
+ } else {
+ // Prepend unless it is already there
+ const QString toPrepend = value + pathListSeparator(sep);
+ if (!it.value().first.startsWith(toPrepend))
+ it.value().first.prepend(toPrepend);
+ }
}
break;
}
case AppendOrSet: {
- auto [key, value, sep] = std::get<AppendOrSet>(item);
- QTC_ASSERT(!key.contains('='), return m_dict);
- const auto it = m_dict.findKey(key);
- if (it == m_dict.m_values.end()) {
- m_dict.m_values.insert(DictKey(key, m_dict.nameCaseSensitivity()), {value, true});
- } else {
- // Prepend unless it is already there
- const QString toAppend = pathListSeparator(sep) + value;
- if (!it.value().first.endsWith(toAppend))
- it.value().first.append(toAppend);
+ const auto appendorset = std::get_if<AppendOrSet>(&item);
+ if (QTC_GUARD(appendorset)) {
+ auto [key, value, sep] = *appendorset;
+ QTC_ASSERT(!key.contains('='), return m_dict);
+ const auto it = m_dict.findKey(key);
+ if (it == m_dict.m_values.end()) {
+ m_dict.m_values.insert(DictKey(key, m_dict.nameCaseSensitivity()), {value, true});
+ } else {
+ // Prepend unless it is already there
+ const QString toAppend = pathListSeparator(sep) + value;
+ if (!it.value().first.endsWith(toAppend))
+ it.value().first.append(toAppend);
+ }
}
break;
}
case Modify: {
- EnvironmentItems items = std::get<Modify>(item);
- m_dict.modify(items);
+ const auto modify = std::get_if<Modify>(&item);
+ if (QTC_GUARD(modify)) {
+ EnvironmentItems items = *modify;
+ m_dict.modify(items);
+ }
break;
}
case SetupEnglishOutput:
diff --git a/src/libs/utils/environmentmodel.cpp b/src/libs/utils/environmentmodel.cpp
index 6ac9b35640..e81ee520d8 100644
--- a/src/libs/utils/environmentmodel.cpp
+++ b/src/libs/utils/environmentmodel.cpp
@@ -397,7 +397,8 @@ EnvironmentItems EnvironmentModel::userChanges() const
void EnvironmentModel::setUserChanges(const EnvironmentItems &items)
{
EnvironmentItems filtered = Utils::filtered(items, [](const EnvironmentItem &i) {
- return i.name != "export " && !i.name.contains('=');
+ return i.operation == EnvironmentItem::Comment
+ || (i.name != "export " && !i.name.contains('='));
});
// We assume nobody is reordering the items here.
if (filtered == d->m_items)
diff --git a/src/libs/utils/execmenu.cpp b/src/libs/utils/execmenu.cpp
index 13d7915295..4e463bdd6f 100644
--- a/src/libs/utils/execmenu.cpp
+++ b/src/libs/utils/execmenu.cpp
@@ -44,7 +44,8 @@ QAction *execMenuAtWidget(QMenu *menu, QWidget *widget)
}
/*!
- Adds tool tips to the menu that show the actions tool tip when hovering over an entry.
+ Adds tool tips to the \a menu that show the action's tool tip when hovering
+ over an entry.
*/
void addToolTipsToMenu(QMenu *menu)
{
diff --git a/src/libs/utils/expected.h b/src/libs/utils/expected.h
index 33231c1246..0e018eb8ab 100644
--- a/src/libs/utils/expected.h
+++ b/src/libs/utils/expected.h
@@ -30,3 +30,12 @@ using expected_str = tl::expected<T, QString>;
} \
do { \
} while (0)
+
+#define QTC_CHECK_EXPECTED(expected) \
+ if (Q_LIKELY(expected)) { \
+ } else { \
+ ::Utils::writeAssertLocation( \
+ QString("%1:%2: %3").arg(__FILE__).arg(__LINE__).arg(expected.error()).toUtf8().data()); \
+ } \
+ do { \
+ } while (0)
diff --git a/src/libs/utils/externalterminalprocessimpl.cpp b/src/libs/utils/externalterminalprocessimpl.cpp
index ca178d7bf9..693905bddc 100644
--- a/src/libs/utils/externalterminalprocessimpl.cpp
+++ b/src/libs/utils/externalterminalprocessimpl.cpp
@@ -4,7 +4,7 @@
#include "externalterminalprocessimpl.h"
#include "algorithm.h"
-#include "process.h"
+#include "qtcprocess.h"
#include "terminalcommand.h"
#include "utilstr.h"
@@ -181,7 +181,7 @@ expected_str<qint64> ProcessStubCreator::startStubProcess(const ProcessSetupData
process->setProcessMode(ProcessMode::Writer);
} else {
QString extraArgsFromOptions = terminal.executeArgs;
- CommandLine cmdLine = {terminal.command, {}};
+ CommandLine cmdLine{terminal.command};
if (!extraArgsFromOptions.isEmpty())
cmdLine.addArgs(extraArgsFromOptions, CommandLine::Raw);
cmdLine.addCommandLineAsArgs(setupData.m_commandLine, CommandLine::Raw);
@@ -190,8 +190,6 @@ expected_str<qint64> ProcessStubCreator::startStubProcess(const ProcessSetupData
process->setEnvironment(
setupData.m_environment.appliedToEnvironment(Environment::systemEnvironment()));
- process->setEnvironment(setupData.m_environment);
-
process->start();
process->waitForStarted();
if (process->error() != QProcess::UnknownError) {
diff --git a/src/libs/utils/fancylineedit.cpp b/src/libs/utils/fancylineedit.cpp
index 44a9423608..69d7209c3a 100644
--- a/src/libs/utils/fancylineedit.cpp
+++ b/src/libs/utils/fancylineedit.cpp
@@ -5,6 +5,7 @@
#include "camelcasecursor.h"
#include "execmenu.h"
+#include "futuresynchronizer.h"
#include "historycompleter.h"
#include "hostosinfo.h"
#include "icon.h"
@@ -98,6 +99,7 @@ class FancyLineEditPrivate : public QObject
{
public:
explicit FancyLineEditPrivate(FancyLineEdit *parent);
+ ~FancyLineEditPrivate();
bool eventFilter(QObject *obj, QEvent *event) override;
@@ -134,8 +136,8 @@ FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent)
: QObject(parent)
, m_lineEdit(parent)
, m_completionShortcut(completionShortcut()->key(), parent)
- , m_okTextColor(creatorTheme()->color(Theme::TextColorNormal))
- , m_errorTextColor(creatorTheme()->color(Theme::TextColorError))
+ , m_okTextColor(creatorColor(Theme::TextColorNormal))
+ , m_errorTextColor(creatorColor(Theme::TextColorError))
, m_placeholderTextColor(QApplication::palette().color(QPalette::PlaceholderText))
, m_spinner(new SpinnerSolution::Spinner(SpinnerSolution::SpinnerSize::Small, m_lineEdit))
{
@@ -163,6 +165,12 @@ FancyLineEditPrivate::FancyLineEditPrivate(FancyLineEdit *parent)
}
}
+FancyLineEditPrivate::~FancyLineEditPrivate()
+{
+ if (m_validatorWatcher)
+ m_validatorWatcher->cancel();
+}
+
bool FancyLineEditPrivate::eventFilter(QObject *obj, QEvent *event)
{
int buttonIndex = -1;
@@ -446,12 +454,27 @@ void FancyLineEdit::setFiltering(bool on)
}
}
+/*!
+ Set a synchronous or asynchronous validation function \a fn.
+ Asynchronous validation functions can continue to run after destruction of the
+ FancyLineEdit instance. During shutdown asynchronous validation functions can continue
+ to run until before the plugin instances are deleted (at that point the plugin manager
+ waits for them to finish before continuing).
+
+ \sa defaultValidationFunction()
+ */
void FancyLineEdit::setValidationFunction(const FancyLineEdit::ValidationFunction &fn)
{
d->m_validationFunction = fn;
validate();
}
+/*!
+ Returns the default validation function, which synchonously executes the line edit's
+ validator.
+
+ \sa setValidationFunction()
+*/
FancyLineEdit::ValidationFunction FancyLineEdit::defaultValidationFunction()
{
return &FancyLineEdit::validateWithValidator;
@@ -581,6 +604,7 @@ void FancyLineEdit::validate()
AsyncValidationFuture future = validationFunction(text());
d->m_validatorWatcher->setFuture(future);
+ Utils::futureSynchronizer()->addFuture(future);
return;
}
diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp
index 562f3247bd..c4d77a29ef 100644
--- a/src/libs/utils/filepath.cpp
+++ b/src/libs/utils/filepath.cpp
@@ -19,9 +19,11 @@
#include <QFileInfo>
#include <QRegularExpression>
#include <QStringView>
+#include <QTemporaryFile>
#include <QUrl>
+
+#include <QtConcurrent>
#include <QtGlobal>
-#include <QTemporaryFile>
#ifdef Q_OS_WIN
#ifdef QTCREATOR_PCH_H
@@ -291,6 +293,26 @@ QString FilePath::toString() const
return scheme() + "://" + encodedHost() + pathView();
}
+bool FilePath::equals(const FilePath &first, const FilePath &second, Qt::CaseSensitivity cs)
+{
+ if (first.m_hash != 0 && second.m_hash != 0 && first.m_hash != second.m_hash)
+ return false;
+
+ return first.pathView().compare(second.pathView(), cs) == 0
+ && first.host() == second.host()
+ && first.scheme() == second.scheme();
+}
+
+/*!
+ * Returns true if the two file paths compare equal case-sensitively.
+ * This is relevant on semi-case sensitive systems like Windows with NTFS.
+ * @see QTCREATORBUG-30846
+ */
+bool FilePath::equalsCaseSensitive(const FilePath &other) const
+{
+ return equals(*this, other, Qt::CaseSensitive);
+}
+
/*!
Returns a QString for passing on to QString based APIs.
@@ -655,9 +677,9 @@ bool FilePath::ensureReachable(const FilePath &other) const
return false;
}
-expected_str<qint64> FilePath::writeFileContents(const QByteArray &data, qint64 offset) const
+expected_str<qint64> FilePath::writeFileContents(const QByteArray &data) const
{
- return fileAccess()->writeFileContents(*this, data, offset);
+ return fileAccess()->writeFileContents(*this, data);
}
FileStreamHandle FilePath::asyncCopy(const FilePath &target, QObject *context,
@@ -1228,6 +1250,9 @@ FilePathInfo FilePath::filePathInfo() const
*/
bool FilePath::exists() const
{
+ if (isEmpty())
+ return false;
+
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1240,6 +1265,9 @@ bool FilePath::exists() const
*/
bool FilePath::isExecutableFile() const
{
+ if (isEmpty())
+ return false;
+
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1252,6 +1280,9 @@ bool FilePath::isExecutableFile() const
*/
bool FilePath::isWritableDir() const
{
+ if (isEmpty())
+ return false;
+
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1264,6 +1295,9 @@ bool FilePath::isWritableDir() const
*/
bool FilePath::isWritableFile() const
{
+ if (isEmpty())
+ return false;
+
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1271,9 +1305,11 @@ bool FilePath::isWritableFile() const
return (*access)->isWritableFile(*this);
}
-
bool FilePath::isReadableFile() const
{
+ if (isEmpty())
+ return false;
+
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1283,6 +1319,9 @@ bool FilePath::isReadableFile() const
bool FilePath::isReadableDir() const
{
+ if (isEmpty())
+ return false;
+
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1292,6 +1331,9 @@ bool FilePath::isReadableDir() const
bool FilePath::isFile() const
{
+ if (isEmpty())
+ return false;
+
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1301,6 +1343,9 @@ bool FilePath::isFile() const
bool FilePath::isDir() const
{
+ if (isEmpty())
+ return false;
+
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1310,6 +1355,9 @@ bool FilePath::isDir() const
bool FilePath::isSymLink() const
{
+ if (isEmpty())
+ return false;
+
const expected_str<DeviceFileAccess *> access = getFileAccess(*this);
if (!access)
return false;
@@ -1365,7 +1413,7 @@ FilePath FilePath::fromUtf8(const char *filename, int filenameSize)
FilePath FilePath::fromSettings(const QVariant &variant)
{
- if (variant.type() == QVariant::Url) {
+ if (variant.typeId() == QMetaType::QUrl) {
const QUrl url = variant.toUrl();
return FilePath::fromParts(url.scheme(), url.host(), url.path());
}
@@ -1497,21 +1545,28 @@ FilePath FilePath::relativePathFrom(const FilePath &anchor) const
FilePath absPath;
QString filename;
- if (isFile()) {
+
+ const QList<FilePathInfo> infos
+ = QtConcurrent::blockingMapped(QList<FilePath>{*this, anchor}, [](const FilePath &path) {
+ return path.filePathInfo();
+ });
+
+ if (infos.first().fileFlags.testFlag(FilePathInfo::FileFlag::FileType)) {
absPath = absolutePath();
filename = fileName();
- } else if (isDir()) {
+ } else if (infos.first().fileFlags.testFlag(FilePathInfo::FileFlag::DirectoryType)) {
absPath = absoluteFilePath();
} else {
return {};
}
FilePath absoluteAnchorPath;
- if (anchor.isFile())
+ if (infos.last().fileFlags.testFlag(FilePathInfo::FileFlag::FileType))
absoluteAnchorPath = anchor.absolutePath();
- else if (anchor.isDir())
+ else if (infos.last().fileFlags.testFlag(FilePathInfo::FileFlag::DirectoryType))
absoluteAnchorPath = anchor.absoluteFilePath();
else
return {};
+
QString relativeFilePath = calcRelativePath(absPath.pathView(), absoluteAnchorPath.pathView());
if (!filename.isEmpty()) {
if (relativeFilePath == ".")
@@ -2319,12 +2374,7 @@ DeviceFileHooks &DeviceFileHooks::instance()
QTCREATOR_UTILS_EXPORT bool operator==(const FilePath &first, const FilePath &second)
{
- if (first.m_hash != 0 && second.m_hash != 0 && first.m_hash != second.m_hash)
- return false;
-
- return first.pathView().compare(second.pathView(), first.caseSensitivity()) == 0
- && first.host() == second.host()
- && first.scheme() == second.scheme();
+ return FilePath::equals(first, second, first.caseSensitivity());
}
QTCREATOR_UTILS_EXPORT bool operator!=(const FilePath &first, const FilePath &second)
diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h
index 2b1eb14069..e742bf1a0d 100644
--- a/src/libs/utils/filepath.h
+++ b/src/libs/utils/filepath.h
@@ -139,7 +139,7 @@ public:
FilePaths dirEntries(const FileFilter &filter, QDir::SortFlags sort = QDir::NoSort) const;
FilePaths dirEntries(QDir::Filters filters) const;
expected_str<QByteArray> fileContents(qint64 maxSize = -1, qint64 offset = 0) const;
- expected_str<qint64> writeFileContents(const QByteArray &data, qint64 offset = 0) const;
+ expected_str<qint64> writeFileContents(const QByteArray &data) const;
FilePathInfo filePathInfo() const;
[[nodiscard]] FilePath operator/(const QString &str) const;
@@ -268,6 +268,8 @@ public:
// FIXME: Avoid. See toSettings, toVariant, toUserOutput, toFSPathString, path, nativePath.
QString toString() const;
+ bool equalsCaseSensitive(const FilePath &other) const;
+
private:
// These are needed.
QTCREATOR_UTILS_EXPORT friend bool operator==(const FilePath &first, const FilePath &second);
@@ -282,6 +284,8 @@ private:
QTCREATOR_UTILS_EXPORT friend QDebug operator<<(QDebug dbg, const FilePath &c);
+ static bool equals(const FilePath &first, const FilePath &second, Qt::CaseSensitivity cs);
+
// Implementation details. May change.
friend class ::tst_fileutils;
void setPath(QStringView path);
diff --git a/src/libs/utils/filestreamer.cpp b/src/libs/utils/filestreamer.cpp
index 2fd4007924..663f505b88 100644
--- a/src/libs/utils/filestreamer.cpp
+++ b/src/libs/utils/filestreamer.cpp
@@ -4,7 +4,7 @@
#include "filestreamer.h"
#include "async.h"
-#include "process.h"
+#include "qtcprocess.h"
#include <solutions/tasking/barrier.h>
#include <solutions/tasking/tasktreerunner.h>
diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp
index b3649a457d..d82353e246 100644
--- a/src/libs/utils/fileutils.cpp
+++ b/src/libs/utils/fileutils.cpp
@@ -5,7 +5,6 @@
#include "savefile.h"
#include "algorithm.h"
-#include "devicefileaccess.h"
#include "environment.h"
#include "qtcassert.h"
#include "utilstr.h"
@@ -24,6 +23,8 @@
#include <qplatformdefs.h>
#ifdef QT_GUI_LIB
+#include "guiutils.h"
+
#include <QMessageBox>
#include <QGuiApplication>
#endif
@@ -414,18 +415,6 @@ void withNtfsPermissions(const std::function<void()> &task)
#ifdef QT_WIDGETS_LIB
-static std::function<QWidget *()> s_dialogParentGetter;
-
-void FileUtils::setDialogParentGetter(const std::function<QWidget *()> &getter)
-{
- s_dialogParentGetter = getter;
-}
-
-static QWidget *dialogParent(QWidget *parent)
-{
- return parent ? parent : s_dialogParentGetter ? s_dialogParentGetter() : nullptr;
-}
-
static QUrl filePathToQUrl(const FilePath &filePath)
{
return QUrl::fromLocalFile(filePath.toFSPathString());
diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h
index b20f7568db..bdf7a7e3ba 100644
--- a/src/libs/utils/fileutils.h
+++ b/src/libs/utils/fileutils.h
@@ -89,8 +89,6 @@ public:
static FilePaths usefulExtraSearchPaths();
#ifdef QT_WIDGETS_LIB
- static void setDialogParentGetter(const std::function<QWidget *()> &getter);
-
static bool hasNativeFileDialog();
static FilePath getOpenFilePath(QWidget *parent,
@@ -236,7 +234,5 @@ private:
QTCREATOR_UTILS_EXPORT QTextStream &operator<<(QTextStream &s, const FilePath &fn);
-bool isRelativePathHelper(const QString &path, OsType osType);
-
} // namespace Utils
diff --git a/src/libs/utils/fsengine/fsengine_impl.cpp b/src/libs/utils/fsengine/fsengine_impl.cpp
index 44a53289d6..295f0cac74 100644
--- a/src/libs/utils/fsengine/fsengine_impl.cpp
+++ b/src/libs/utils/fsengine/fsengine_impl.cpp
@@ -35,11 +35,7 @@ FSEngineImpl::~FSEngineImpl()
delete m_tempStorage;
}
-#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
bool FSEngineImpl::open(QIODeviceBase::OpenMode openMode, std::optional<QFile::Permissions>)
-#else
-bool FSEngineImpl::open(QIODevice::OpenMode openMode)
-#endif
{
const FilePathInfoCache::CachedData data = g_filePathInfoCache.cached(m_filePath,
createCacheData);
@@ -162,12 +158,8 @@ bool FSEngineImpl::link(const QString &newName)
return false;
}
-#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
bool FSEngineImpl::mkdir(const QString &dirName, bool createParentDirectories,
std::optional<QFile::Permissions>) const
-#else
-bool FSEngineImpl::mkdir(const QString &dirName, bool createParentDirectories) const
-#endif
{
Q_UNUSED(createParentDirectories)
return FilePath::fromString(dirName).createDir();
diff --git a/src/libs/utils/fsengine/fsengine_impl.h b/src/libs/utils/fsengine/fsengine_impl.h
index 7a68119c83..1a49f77cd2 100644
--- a/src/libs/utils/fsengine/fsengine_impl.h
+++ b/src/libs/utils/fsengine/fsengine_impl.h
@@ -20,15 +20,10 @@ public:
~FSEngineImpl();
public:
-#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
bool open(QIODeviceBase::OpenMode openMode,
std::optional<QFile::Permissions> permissions = std::nullopt) override;
bool mkdir(const QString &dirName, bool createParentDirectories,
std::optional<QFile::Permissions> permissions = std::nullopt) const override;
-#else
- bool open(QIODevice::OpenMode openMode) override;
- bool mkdir(const QString &dirName, bool createParentDirectories) const override;
-#endif
bool close() override;
bool flush() override;
bool syncToDisk() override;
diff --git a/src/libs/utils/fsengine/fsenginehandler.cpp b/src/libs/utils/fsengine/fsenginehandler.cpp
index 29fe759a86..d6751d8b6a 100644
--- a/src/libs/utils/fsengine/fsenginehandler.cpp
+++ b/src/libs/utils/fsengine/fsenginehandler.cpp
@@ -3,17 +3,42 @@
#include "fsenginehandler.h"
+#include "fileiteratordevicesappender.h"
#include "fixedlistfsengine.h"
-#include "fsengine_impl.h"
-#include "rootinjectfsengine.h"
-
#include "fsengine.h"
+#include "fsengine_impl.h"
#include "../algorithm.h"
#include "../hostosinfo.h"
+#include <QtCore/private/qfsfileengine_p.h>
+
namespace Utils::Internal {
+class RootInjectFSEngine final : public QFSFileEngine
+{
+public:
+ using QFSFileEngine::QFSFileEngine;
+
+public:
+#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
+ IteratorUniquePtr beginEntryList(const QString &path,
+ QDir::Filters filters,
+ const QStringList &filterNames) override
+ {
+ return std::make_unique<FileIteratorWrapper>(
+ QFSFileEngine::beginEntryList(path, filters, filterNames));
+ }
+#else
+ Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override
+ {
+ std::unique_ptr<QAbstractFileEngineIterator> baseIterator(
+ QFSFileEngine::beginEntryList(filters, filterNames));
+ return new FileIteratorWrapper(std::move(baseIterator));
+ }
+#endif
+};
+
static FilePath removeDoubleSlash(const QString &fileName)
{
// Reduce every two or more slashes to a single slash.
diff --git a/src/libs/utils/fsengine/rootinjectfsengine.h b/src/libs/utils/fsengine/rootinjectfsengine.h
deleted file mode 100644
index 9eb6a8a832..0000000000
--- a/src/libs/utils/fsengine/rootinjectfsengine.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2022 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-#pragma once
-
-#include "fileiteratordevicesappender.h"
-
-#include <QtCore/private/qfsfileengine_p.h>
-
-namespace Utils {
-namespace Internal {
-
-class RootInjectFSEngine : public QFSFileEngine
-{
-public:
- using QFSFileEngine::QFSFileEngine;
-
-public:
-#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
- IteratorUniquePtr beginEntryList(const QString &path,
- QDir::Filters filters,
- const QStringList &filterNames) override
- {
- return std::make_unique<FileIteratorWrapper>(
- QFSFileEngine::beginEntryList(path, filters, filterNames));
- }
-#else
- Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override
- {
- std::unique_ptr<QAbstractFileEngineIterator> baseIterator(
- QFSFileEngine::beginEntryList(filters, filterNames));
- return new FileIteratorWrapper(std::move(baseIterator));
- }
-#endif
-};
-
-} // namespace Internal
-} // namespace Utils
diff --git a/src/libs/utils/futuresynchronizer.cpp b/src/libs/utils/futuresynchronizer.cpp
index da3347f35e..912de677b9 100644
--- a/src/libs/utils/futuresynchronizer.cpp
+++ b/src/libs/utils/futuresynchronizer.cpp
@@ -3,6 +3,9 @@
#include "futuresynchronizer.h"
+#include "qtcassert.h"
+#include "threadutils.h"
+
/*!
\class Utils::FutureSynchronizer
\inmodule QtCreator
@@ -62,4 +65,18 @@ void FutureSynchronizer::flushFinishedFutures()
m_futures = newFutures;
}
+Q_GLOBAL_STATIC(FutureSynchronizer, s_futureSynchronizer);
+
+/*!
+ Returns a global FutureSynchronizer.
+ The application should cancel and wait for the tasks in this synchronizer before actually
+ unloading any libraries. This is for example done by the plugin manager in Qt Creator.
+ May only be accessed by the main thread.
+*/
+FutureSynchronizer *futureSynchronizer()
+{
+ QTC_ASSERT(isMainThread(), return nullptr);
+ return s_futureSynchronizer;
+}
+
} // namespace Utils
diff --git a/src/libs/utils/futuresynchronizer.h b/src/libs/utils/futuresynchronizer.h
index 29b1f5e456..83391dbc72 100644
--- a/src/libs/utils/futuresynchronizer.h
+++ b/src/libs/utils/futuresynchronizer.h
@@ -42,4 +42,6 @@ private:
bool m_cancelOnWait = true;
};
+QTCREATOR_UTILS_EXPORT FutureSynchronizer *futureSynchronizer();
+
} // namespace Utils
diff --git a/src/libs/utils/guiutils.cpp b/src/libs/utils/guiutils.cpp
index 17cc905300..d7d9cb5f84 100644
--- a/src/libs/utils/guiutils.cpp
+++ b/src/libs/utils/guiutils.cpp
@@ -46,4 +46,16 @@ void QTCREATOR_UTILS_EXPORT setWheelScrollingWithoutFocusBlocked(QWidget *widget
widget->setFocusPolicy(Qt::StrongFocus);
}
+static QWidget *(*s_dialogParentGetter)() = nullptr;
+
+void setDialogParentGetter(QWidget *(*getter)())
+{
+ s_dialogParentGetter = getter;
+}
+
+QWidget *dialogParent(QWidget *parent)
+{
+ return parent ? parent : s_dialogParentGetter ? s_dialogParentGetter() : nullptr;
+}
+
} // namespace Utils
diff --git a/src/libs/utils/guiutils.h b/src/libs/utils/guiutils.h
index 8316377718..fea37f98f3 100644
--- a/src/libs/utils/guiutils.h
+++ b/src/libs/utils/guiutils.h
@@ -9,6 +9,9 @@ class QWidget;
namespace Utils {
-void QTCREATOR_UTILS_EXPORT setWheelScrollingWithoutFocusBlocked(QWidget *widget);
+QTCREATOR_UTILS_EXPORT void setWheelScrollingWithoutFocusBlocked(QWidget *widget);
+
+QTCREATOR_UTILS_EXPORT QWidget *dialogParent(QWidget *parent);
+QTCREATOR_UTILS_EXPORT void setDialogParentGetter(QWidget *(*getter)());
} // namespace Utils
diff --git a/src/libs/utils/highlightingitemdelegate.cpp b/src/libs/utils/highlightingitemdelegate.cpp
index 7fe0507311..9ad7abb01a 100644
--- a/src/libs/utils/highlightingitemdelegate.cpp
+++ b/src/libs/utils/highlightingitemdelegate.cpp
@@ -91,7 +91,7 @@ int HighlightingItemDelegate::drawLineNumber(QPainter *painter, const QStyleOpti
return 0;
const bool isSelected = option.state & QStyle::State_Selected;
const QString lineText = QString::number(lineNumber);
- const int minimumLineNumberDigits = qMax(kMinimumLineNumberDigits, lineText.count());
+ const int minimumLineNumberDigits = qMax(kMinimumLineNumberDigits, lineText.size());
const int fontWidth =
painter->fontMetrics().horizontalAdvance(QString(minimumLineNumberDigits, '0'));
const int lineNumberAreaWidth = lineNumberAreaHorizontalPadding + fontWidth
diff --git a/src/libs/utils/historycompleter.cpp b/src/libs/utils/historycompleter.cpp
index 8eff3fdad8..8dc9ec3887 100644
--- a/src/libs/utils/historycompleter.cpp
+++ b/src/libs/utils/historycompleter.cpp
@@ -54,11 +54,11 @@ public:
optCopy.state |= QStyle::State_HasFocus;
QItemDelegate::paint(painter,option,index);
// add remove button
- QWindow *window = view->window()->windowHandle();
- const QPixmap iconPixmap = icon.pixmap(window, option.rect.size());
+ const qreal devicePixelRatio = painter->device()->devicePixelRatio();
+ const QPixmap iconPixmap = icon.pixmap(option.rect.size(), devicePixelRatio);
QRect pixmapRect = QStyle::alignedRect(option.direction,
Qt::AlignRight | Qt::AlignVCenter,
- iconPixmap.size() / window->devicePixelRatio(),
+ iconPixmap.size() / devicePixelRatio,
option.rect);
if (!clearIconSize.isValid())
clearIconSize = pixmapRect.size();
diff --git a/src/libs/utils/hostosinfo.cpp b/src/libs/utils/hostosinfo.cpp
index d8361266f0..717dc900cd 100644
--- a/src/libs/utils/hostosinfo.cpp
+++ b/src/libs/utils/hostosinfo.cpp
@@ -29,36 +29,10 @@ namespace Utils {
Qt::CaseSensitivity HostOsInfo::m_overrideFileNameCaseSensitivity = Qt::CaseSensitive;
bool HostOsInfo::m_useOverrideFileNameCaseSensitivity = false;
-#ifdef Q_OS_WIN
-static WORD hostProcessorArchitecture()
-{
- SYSTEM_INFO info;
- GetNativeSystemInfo(&info);
- return info.wProcessorArchitecture;
-}
-#endif
-
-HostOsInfo::HostArchitecture HostOsInfo::hostArchitecture()
+OsArch HostOsInfo::hostArchitecture()
{
-#ifdef Q_OS_WIN
- static const WORD processorArchitecture = hostProcessorArchitecture();
- switch (processorArchitecture) {
- case PROCESSOR_ARCHITECTURE_AMD64:
- return HostOsInfo::HostArchitectureAMD64;
- case PROCESSOR_ARCHITECTURE_INTEL:
- return HostOsInfo::HostArchitectureX86;
- case PROCESSOR_ARCHITECTURE_IA64:
- return HostOsInfo::HostArchitectureItanium;
- case PROCESSOR_ARCHITECTURE_ARM:
- return HostOsInfo::HostArchitectureArm;
- case PROCESSOR_ARCHITECTURE_ARM64:
- return HostOsInfo::HostArchitectureArm64;
- default:
- return HostOsInfo::HostArchitectureUnknown;
- }
-#else
- return HostOsInfo::HostArchitectureUnknown;
-#endif
+ static const OsArch arch = osArchFromString(QSysInfo::currentCpuArchitecture());
+ return arch;
}
bool HostOsInfo::isRunningUnderRosetta()
diff --git a/src/libs/utils/hostosinfo.h b/src/libs/utils/hostosinfo.h
index 44880f7885..fa153f24d5 100644
--- a/src/libs/utils/hostosinfo.h
+++ b/src/libs/utils/hostosinfo.h
@@ -41,9 +41,7 @@ public:
#endif
}
- enum HostArchitecture { HostArchitectureX86, HostArchitectureAMD64, HostArchitectureItanium,
- HostArchitectureArm, HostArchitectureArm64, HostArchitectureUnknown };
- static HostArchitecture hostArchitecture();
+ static OsArch hostArchitecture();
static constexpr bool isWindowsHost() { return hostOs() == OsTypeWindows; }
static constexpr bool isLinuxHost() { return hostOs() == OsTypeLinux; }
diff --git a/src/libs/utils/icon.cpp b/src/libs/utils/icon.cpp
index 5bd862c8f1..06570e6505 100644
--- a/src/libs/utils/icon.cpp
+++ b/src/libs/utils/icon.cpp
@@ -42,7 +42,7 @@ static MasksAndColors masksAndColors(const QList<IconMaskAndColor> &icon, int dp
MasksAndColors result;
for (const IconMaskAndColor &i: icon) {
const QString &fileName = i.first.toString();
- const QColor color = creatorTheme()->color(i.second);
+ const QColor color = creatorColor(i.second);
const QString dprFileName = StyleHelper::availableImageResolutions(i.first.toString())
.contains(dpr)
? StyleHelper::imageFileWithResolution(fileName, dpr)
@@ -165,7 +165,7 @@ QIcon Icon::icon() const
const QPixmap combinedMask = Utils::combinedMask(masks, m_style);
m_lastIcon.addPixmap(masksToIcon(masks, combinedMask, m_style));
- const QColor disabledColor = creatorTheme()->color(Theme::IconsDisabledColor);
+ const QColor disabledColor = creatorColor(Theme::IconsDisabledColor);
m_lastIcon.addPixmap(maskToColorAndAlpha(combinedMask, disabledColor), QIcon::Disabled);
}
return m_lastIcon;
@@ -182,7 +182,7 @@ QPixmap Icon::pixmap(QIcon::Mode iconMode) const
masksAndColors(m_iconSourceList, qRound(qApp->devicePixelRatio()));
const QPixmap combinedMask = Utils::combinedMask(masks, m_style);
return iconMode == QIcon::Disabled
- ? maskToColorAndAlpha(combinedMask, creatorTheme()->color(Theme::IconsDisabledColor))
+ ? maskToColorAndAlpha(combinedMask, creatorColor(Theme::IconsDisabledColor))
: masksToIcon(masks, combinedMask, m_style);
}
}
@@ -210,22 +210,21 @@ QIcon Icon::sideBarIcon(const Icon &classic, const Icon &flat)
return result;
}
-QIcon Icon::modeIcon(const Icon &classic, const Icon &flat, const Icon &flatActive)
+QIcon Icon::modeIcon(const Icon &classic, const Icon &flat,
+ [[__maybe_unused__]] const Icon &flatActive)
{
QIcon result = sideBarIcon(classic, flat);
- if (creatorTheme()->flag(Theme::FlatSideBarIcons))
- result.addPixmap(flatActive.pixmap(), QIcon::Active);
return result;
}
QIcon Icon::combinedIcon(const QList<QIcon> &icons)
{
QIcon result;
- QWindow *window = QApplication::allWidgets().constFirst()->windowHandle();
+ const qreal devicePixelRatio = QApplication::allWidgets().constFirst()->devicePixelRatio();
for (const QIcon &icon: icons)
for (const QIcon::Mode mode: {QIcon::Disabled, QIcon::Normal})
for (const QSize &size: icon.availableSizes(mode))
- result.addPixmap(icon.pixmap(window, size, mode), mode);
+ result.addPixmap(icon.pixmap(size, devicePixelRatio, mode), mode);
return result;
}
diff --git a/src/libs/utils/iconbutton.cpp b/src/libs/utils/iconbutton.cpp
index 746cf0ecb1..3be368c43b 100644
--- a/src/libs/utils/iconbutton.cpp
+++ b/src/libs/utils/iconbutton.cpp
@@ -26,7 +26,7 @@ void IconButton::paintEvent(QPaintEvent *e)
QRect r(QPoint(), size());
if (m_containsMouse && isEnabled()) {
- QColor c = creatorTheme()->color(Theme::TextColorDisabled);
+ QColor c = creatorColor(Theme::TextColorDisabled);
c.setAlphaF(c.alphaF() * .5);
StyleHelper::drawPanelBgRect(&p, r, c);
}
diff --git a/src/libs/utils/images/debugger_overlay_small.png b/src/libs/utils/images/debugger_overlay_small.png
index 809bf34732..440d2b59d2 100644
--- a/src/libs/utils/images/debugger_overlay_small.png
+++ b/src/libs/utils/images/debugger_overlay_small.png
Binary files differ
diff --git a/src/libs/utils/images/debugger_overlay_small@2x.png b/src/libs/utils/images/debugger_overlay_small@2x.png
index c24c861cbb..40ea20ed47 100644
--- a/src/libs/utils/images/debugger_overlay_small@2x.png
+++ b/src/libs/utils/images/debugger_overlay_small@2x.png
Binary files differ
diff --git a/src/libs/utils/infobar.cpp b/src/libs/utils/infobar.cpp
index bd6429a762..dc5fb4878d 100644
--- a/src/libs/utils/infobar.cpp
+++ b/src/libs/utils/infobar.cpp
@@ -47,10 +47,10 @@ void InfoBarWidget::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
QPainter p(this);
- p.fillRect(rect(), creatorTheme()->color(Theme::InfoBarBackground));
+ p.fillRect(rect(), creatorColor(Theme::InfoBarBackground));
const QRectF adjustedRect = QRectF(rect()).adjusted(0.5, 0.5, -0.5, -0.5);
const bool topEdge = m_edge == Qt::TopEdge;
- p.setPen(creatorTheme()->color(Theme::FancyToolBarSeparatorColor));
+ p.setPen(creatorColor(Theme::FancyToolBarSeparatorColor));
p.drawLine(QLineF(topEdge ? adjustedRect.bottomLeft() : adjustedRect.topLeft(),
topEdge ? adjustedRect.bottomRight() : adjustedRect.topRight()));
}
diff --git a/src/libs/utils/infolabel.cpp b/src/libs/utils/infolabel.cpp
index dbc762d9fe..e05c093ca5 100644
--- a/src/libs/utils/infolabel.cpp
+++ b/src/libs/utils/infolabel.cpp
@@ -114,14 +114,13 @@ void InfoLabel::paintEvent(QPaintEvent *event)
if (m_filled && isEnabled()) {
p.save();
p.setOpacity(0.175);
- p.fillRect(rect(), creatorTheme()->color(fillColorForType(m_type)));
+ p.fillRect(rect(), creatorColor(fillColorForType(m_type)));
p.restore();
}
const QIcon &icon = iconForType(m_type);
- QWindow *window = this->window()->windowHandle();
const QIcon::Mode mode = !this->isEnabled() ? QIcon::Disabled : QIcon::Normal;
const QPixmap iconPx =
- icon.pixmap(window, QSize(iconSize, iconSize) * devicePixelRatio(), mode);
+ icon.pixmap(QSize(iconSize, iconSize) * devicePixelRatio(), devicePixelRatio(), mode);
p.drawPixmap(iconRect, iconPx);
ElidingLabel::paintEvent(event);
}
diff --git a/src/libs/utils/launcherpackets.cpp b/src/libs/utils/launcherpackets.cpp
index 37261224ef..a770e8865d 100644
--- a/src/libs/utils/launcherpackets.cpp
+++ b/src/libs/utils/launcherpackets.cpp
@@ -49,7 +49,8 @@ void StartProcessPacket::doSerialize(QDataStream &stream) const
<< unixTerminalDisabled
<< useCtrlCStub
<< reaperTimeout
- << createConsoleOnWindows;
+ << createConsoleOnWindows
+ << forceDefaultErrorMode;
}
void StartProcessPacket::doDeserialize(QDataStream &stream)
@@ -70,7 +71,8 @@ void StartProcessPacket::doDeserialize(QDataStream &stream)
>> unixTerminalDisabled
>> useCtrlCStub
>> reaperTimeout
- >> createConsoleOnWindows;
+ >> createConsoleOnWindows
+ >> forceDefaultErrorMode;
processMode = Utils::ProcessMode(processModeInt);
processChannelMode = QProcess::ProcessChannelMode(processChannelModeInt);
}
diff --git a/src/libs/utils/launcherpackets.h b/src/libs/utils/launcherpackets.h
index 27e98a74e5..d00169c1f6 100644
--- a/src/libs/utils/launcherpackets.h
+++ b/src/libs/utils/launcherpackets.h
@@ -99,6 +99,7 @@ public:
bool useCtrlCStub = false;
int reaperTimeout = 500;
bool createConsoleOnWindows = false;
+ bool forceDefaultErrorMode = false;
private:
void doSerialize(QDataStream &stream) const override;
diff --git a/src/libs/utils/launchersocket.cpp b/src/libs/utils/launchersocket.cpp
index 23b7d39fcd..61796a2bb3 100644
--- a/src/libs/utils/launchersocket.cpp
+++ b/src/libs/utils/launchersocket.cpp
@@ -260,6 +260,7 @@ void CallerHandle::start(const QString &program, const QStringList &arguments)
p.useCtrlCStub = m_setup->m_useCtrlCStub;
p.reaperTimeout = m_setup->m_reaperTimeout.count();
p.createConsoleOnWindows = m_setup->m_createConsoleOnWindows;
+ p.forceDefaultErrorMode = m_setup->m_forceDefaultErrorMode;
sendPacket(p);
}
diff --git a/src/libs/utils/layoutbuilder.cpp b/src/libs/utils/layoutbuilder.cpp
index 31c67e4d0f..6f33e4f5ca 100644
--- a/src/libs/utils/layoutbuilder.cpp
+++ b/src/libs/utils/layoutbuilder.cpp
@@ -3,7 +3,6 @@
#include "layoutbuilder.h"
-#include <QApplication>
#include <QDebug>
#include <QFormLayout>
#include <QGridLayout>
@@ -29,10 +28,23 @@ namespace Layouting {
#define QTC_ASSERT(cond, action) if (Q_LIKELY(cond)) {} else { QTC_STRING(#cond); action; } do {} while (0)
#define QTC_CHECK(cond) if (cond) {} else { QTC_STRING(#cond); } do {} while (0)
-class FlowLayout final : public QLayout
+template <typename X>
+typename X::Implementation *access(const X *x)
{
- Q_OBJECT
+ return static_cast<typename X::Implementation *>(x->ptr);
+}
+
+template <typename X>
+void apply(X *x, std::initializer_list<typename X::I> ps)
+{
+ for (auto && p : ps)
+ p.apply(x);
+}
+
+// FlowLayout
+class FlowLayout : public QLayout
+{
public:
explicit FlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1)
: QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing)
@@ -181,29 +193,56 @@ private:
\namespace Layouting
\inmodule QtCreator
- \brief The Layouting namespace contains classes for use with layout builders.
+ \brief The Layouting namespace contains classes and functions to conveniently
+ create layouts in code.
+
+ Classes in the namespace help to create create QLayout or QWidget derived class,
+ instances should be used locally within a function and never stored.
+
+ \sa Layouting::Widget, Layouting::Layout
*/
/*!
- \class Layouting::LayoutItem
+ \class Layouting::Layout
\inmodule QtCreator
- \brief The LayoutItem class represents widgets, layouts, and aggregate
- items for use in conjunction with layout builders.
+ The Layout class is a base class for more specific builder
+ classes to create QLayout derived objects.
+ */
- Layout items are typically implicitly constructed when adding items to a
- \c LayoutBuilder instance using \c LayoutBuilder::addItem() or
- \c LayoutBuilder::addItems() and never stored in user code.
+/*!
+ \class Layouting::Widget
+ \inmodule QtCreator
+
+ The Widget class is a base class for more specific builder
+ classes to create QWidget derived objects.
*/
/*!
- Constructs a layout item instance representing an empty cell.
- */
+ \class Layouting::LayoutItem
+ \inmodule QtCreator
+
+ The LayoutItem class is used for intermediate results
+ while creating layouts with a concept of rows and spans, such
+ as Form and Grid.
+*/
+
LayoutItem::LayoutItem() = default;
LayoutItem::~LayoutItem() = default;
+LayoutItem::LayoutItem(QLayout *l)
+ : layout(l), empty(!l)
+{}
+
+LayoutItem::LayoutItem(QWidget *w)
+ : widget(w), empty(!w)
+{}
+
+LayoutItem::LayoutItem(const QString &t)
+ : text(t), empty(t.isEmpty())
+{}
/*!
\fn template <class T> LayoutItem(const T &t)
@@ -217,44 +256,15 @@ LayoutItem::~LayoutItem() = default;
\li \c {QWidget *}
\li \c {QLayout *}
\endlist
- */
-
-struct ResultItem
-{
- ResultItem() = default;
- explicit ResultItem(QLayout *l) : layout(l), empty(!l) {}
- explicit ResultItem(QWidget *w) : widget(w), empty(!w) {}
+*/
- QString text;
- QLayout *layout = nullptr;
- QWidget *widget = nullptr;
- int space = -1;
- int stretch = -1;
- int span = 1;
- bool empty = false;
-};
+// Object
-struct Slice
+Object::Object(std::initializer_list<I> ps)
{
- Slice() = default;
- Slice(QLayout *l) : layout(l) {}
- Slice(QWidget *w, bool isLayouting=false) : widget(w), isLayouting(isLayouting) {}
-
- QLayout *layout = nullptr;
- QWidget *widget = nullptr;
-
- void flush();
-
- // Grid-specific
- int currentGridColumn = 0;
- int currentGridRow = 0;
- bool isFormAlignment = false;
- bool isLayouting = false;
- Qt::Alignment align = {}; // Can be changed to
-
- // Grid or Form
- QList<ResultItem> pendingItems;
-};
+ ptr = new Implementation;
+ apply(this, ps);
+}
static QWidget *widgetForItem(QLayoutItem *item)
{
@@ -262,12 +272,11 @@ static QWidget *widgetForItem(QLayoutItem *item)
return w;
if (item->spacerItem())
return nullptr;
- QLayout *l = item->layout();
- if (!l)
- return nullptr;
- for (int i = 0, n = l->count(); i < n; ++i) {
- if (QWidget *w = widgetForItem(l->itemAt(i)))
- return w;
+ if (QLayout *l = item->layout()) {
+ for (int i = 0, n = l->count(); i < n; ++i) {
+ if (QWidget *w = widgetForItem(l->itemAt(i)))
+ return w;
+ }
}
return nullptr;
}
@@ -279,7 +288,7 @@ static QLabel *createLabel(const QString &text)
return label;
}
-static void addItemToBoxLayout(QBoxLayout *layout, const ResultItem &item)
+static void addItemToBoxLayout(QBoxLayout *layout, const LayoutItem &item)
{
if (QWidget *w = item.widget) {
layout->addWidget(w);
@@ -287,8 +296,6 @@ static void addItemToBoxLayout(QBoxLayout *layout, const ResultItem &item)
layout->addLayout(l);
} else if (item.stretch != -1) {
layout->addStretch(item.stretch);
- } else if (item.space != -1) {
- layout->addSpacing(item.space);
} else if (!item.text.isEmpty()) {
layout->addWidget(createLabel(item.text));
} else if (item.empty) {
@@ -298,7 +305,7 @@ static void addItemToBoxLayout(QBoxLayout *layout, const ResultItem &item)
}
}
-static void addItemToFlowLayout(FlowLayout *layout, const ResultItem &item)
+static void addItemToFlowLayout(FlowLayout *layout, const LayoutItem &item)
{
if (QWidget *w = item.widget) {
layout->addWidget(w);
@@ -306,8 +313,6 @@ static void addItemToFlowLayout(FlowLayout *layout, const ResultItem &item)
layout->addItem(l);
// } else if (item.stretch != -1) {
// layout->addStretch(item.stretch);
-// } else if (item.space != -1) {
-// layout->addSpacing(item.space);
} else if (item.empty) {
// Nothing to do, but no reason to warn, either
} else if (!item.text.isEmpty()) {
@@ -317,759 +322,682 @@ static void addItemToFlowLayout(FlowLayout *layout, const ResultItem &item)
}
}
-void Slice::flush()
-{
- if (pendingItems.empty())
- return;
-
- if (auto formLayout = qobject_cast<QFormLayout *>(layout)) {
-
- // If there are more than two items, we cram the last ones in one hbox.
- if (pendingItems.size() > 2) {
- auto hbox = new QHBoxLayout;
- hbox->setContentsMargins(0, 0, 0, 0);
- for (int i = 1; i < pendingItems.size(); ++i)
- addItemToBoxLayout(hbox, pendingItems.at(i));
- while (pendingItems.size() > 1)
- pendingItems.pop_back();
- pendingItems.append(ResultItem(hbox));
- }
-
- if (pendingItems.size() == 1) { // One one item given, so this spans both columns.
- const ResultItem &f0 = pendingItems.at(0);
- if (auto layout = f0.layout)
- formLayout->addRow(layout);
- else if (auto widget = f0.widget)
- formLayout->addRow(widget);
- } else if (pendingItems.size() == 2) { // Normal case, both columns used.
- ResultItem &f1 = pendingItems[1];
- const ResultItem &f0 = pendingItems.at(0);
- if (!f1.widget && !f1.layout && !f1.text.isEmpty())
- f1.widget = createLabel(f1.text);
-
- if (f0.widget) {
- if (f1.layout)
- formLayout->addRow(f0.widget, f1.layout);
- else if (f1.widget)
- formLayout->addRow(f0.widget, f1.widget);
- } else {
- if (f1.layout)
- formLayout->addRow(createLabel(f0.text), f1.layout);
- else if (f1.widget)
- formLayout->addRow(createLabel(f0.text), f1.widget);
- }
- } else {
- QTC_CHECK(false);
- }
-
- // Set up label as buddy if possible.
- const int lastRow = formLayout->rowCount() - 1;
- QLayoutItem *l = formLayout->itemAt(lastRow, QFormLayout::LabelRole);
- QLayoutItem *f = formLayout->itemAt(lastRow, QFormLayout::FieldRole);
- if (l && f) {
- if (QLabel *label = qobject_cast<QLabel *>(l->widget())) {
- if (QWidget *widget = widgetForItem(f))
- label->setBuddy(widget);
- }
- }
-
- } else if (auto gridLayout = qobject_cast<QGridLayout *>(layout)) {
-
- for (const ResultItem &item : std::as_const(pendingItems)) {
- Qt::Alignment a = currentGridColumn == 0 ? align : Qt::Alignment();
- if (item.widget)
- gridLayout->addWidget(item.widget, currentGridRow, currentGridColumn, 1, item.span, a);
- else if (item.layout)
- gridLayout->addLayout(item.layout, currentGridRow, currentGridColumn, 1, item.span, a);
- else if (!item.text.isEmpty())
- gridLayout->addWidget(createLabel(item.text), currentGridRow, currentGridColumn, 1, 1, a);
- currentGridColumn += item.span;
- }
- ++currentGridRow;
- currentGridColumn = 0;
+/*!
+ \class Layouting::Space
+ \inmodule QtCreator
- } else if (auto boxLayout = qobject_cast<QBoxLayout *>(layout)) {
+ \brief The Space class represents some empty space in a layout.
+ */
- for (const ResultItem &item : std::as_const(pendingItems))
- addItemToBoxLayout(boxLayout, item);
+/*!
+ \class Layouting::Stretch
+ \inmodule QtCreator
- } else if (auto flowLayout = qobject_cast<FlowLayout *>(layout)) {
+ \brief The Stretch class represents some stretch in a layout.
+ */
- for (const ResultItem &item : std::as_const(pendingItems))
- addItemToFlowLayout(flowLayout, item);
- } else {
- QTC_CHECK(false);
- }
+// Layout
- pendingItems.clear();
+void Layout::span(int cols, int rows)
+{
+ QTC_ASSERT(!pendingItems.empty(), return);
+ pendingItems.back().spanCols = cols;
+ pendingItems.back().spanRows = rows;
}
-// LayoutBuilder
-
-class LayoutBuilder
+void Layout::setNoMargins()
{
- Q_DISABLE_COPY_MOVE(LayoutBuilder)
-
-public:
- LayoutBuilder();
- ~LayoutBuilder();
-
- void addItem(const LayoutItem &item);
- void addItems(const LayoutItems &items);
-
- QList<Slice> stack;
-};
+ setContentsMargins(0, 0, 0, 0);
+}
-static void addItemHelper(LayoutBuilder &builder, const LayoutItem &item)
+void Layout::setNormalMargins()
{
- if (item.onAdd)
- item.onAdd(builder);
-
- if (item.setter) {
- if (QWidget *widget = builder.stack.last().widget)
- item.setter(widget);
- else if (QLayout *layout = builder.stack.last().layout)
- item.setter(layout);
- else
- QTC_CHECK(false);
- }
-
- for (const LayoutItem &subItem : item.subItems)
- addItemHelper(builder, subItem);
-
- if (item.onExit)
- item.onExit(builder);
+ setContentsMargins(9, 9, 9, 9);
}
-void doAddText(LayoutBuilder &builder, const QString &text)
+void Layout::setContentsMargins(int left, int top, int right, int bottom)
{
- ResultItem fi;
- fi.text = text;
- builder.stack.last().pendingItems.append(fi);
+ access(this)->setContentsMargins(left, top, right, bottom);
}
-void doAddSpace(LayoutBuilder &builder, const Space &space)
+/*!
+ Attaches the constructed layout to the provided QWidget \a w.
+
+ This operation can only be performed once per LayoutBuilder instance.
+ */
+void Layout::attachTo(QWidget *widget)
{
- ResultItem fi;
- fi.space = space.space;
- builder.stack.last().pendingItems.append(fi);
+ flush();
+ widget->setLayout(access(this));
}
-void doAddStretch(LayoutBuilder &builder, const Stretch &stretch)
+/*!
+ Adds the layout item \a item as sub items.
+ */
+void Layout::addItem(I item)
{
- ResultItem fi;
- fi.stretch = stretch.stretch;
- builder.stack.last().pendingItems.append(fi);
+ item.apply(this);
}
-void doAddLayout(LayoutBuilder &builder, QLayout *layout)
+void Layout::addLayoutItem(const LayoutItem &item)
{
- builder.stack.last().pendingItems.append(ResultItem(layout));
+ if (QBoxLayout *lt = asBox())
+ addItemToBoxLayout(lt, item);
+ else if (FlowLayout *lt = asFlow())
+ addItemToFlowLayout(lt, item);
+ else
+ pendingItems.push_back(item);
}
-void doAddWidget(LayoutBuilder &builder, QWidget *widget)
+/*!
+ Adds the layout items \a items as sub items.
+ */
+void Layout::addItems(std::initializer_list<I> items)
{
- builder.stack.last().pendingItems.append(ResultItem(widget));
+ for (const I &item : items)
+ item.apply(this);
}
-
/*!
- \class Layouting::Space
- \inmodule QtCreator
+ Starts a new row containing \a items. The row can be further extended by
+ other items using \c addItem() or \c addItems().
- \brief The Space class represents some empty space in a layout.
+ \sa addItem(), addItems()
*/
-/*!
- \class Layouting::Stretch
- \inmodule QtCreator
+void Layout::addRow(std::initializer_list<I> items)
+{
+ for (const I &item : items)
+ item.apply(this);
+ flush();
+}
- \brief The Stretch class represents some stretch in a layout.
- */
+void Layout::setSpacing(int spacing)
+{
+ access(this)->setSpacing(spacing);
+}
-/*!
- \class Layouting::LayoutBuilder
- \internal
- \inmodule QtCreator
+void Layout::setColumnStretch(int column, int stretch)
+{
+ if (auto grid = qobject_cast<QGridLayout *>(access(this))) {
+ grid->setColumnStretch(column, stretch);
+ } else {
+ QTC_CHECK(false);
+ }
+}
- \brief The LayoutBuilder class provides a convenient way to fill \c QFormLayout
- and \c QGridLayouts with contents.
+void addToWidget(Widget *widget, const Layout &layout)
+{
+ layout.flush_();
+ access(widget)->setLayout(access(&layout));
+}
- Filling a layout with items happens item-by-item, row-by-row.
+void addToLayout(Layout *layout, const Widget &inner)
+{
+ layout->addLayoutItem(access(&inner));
+}
- A LayoutBuilder instance is typically used locally within a function and never stored.
+void addToLayout(Layout *layout, QWidget *inner)
+{
+ layout->addLayoutItem(inner);
+}
- \sa addItem(), addItems()
-*/
+void addToLayout(Layout *layout, QLayout *inner)
+{
+ layout->addLayoutItem(inner);
+}
+void addToLayout(Layout *layout, const Layout &inner)
+{
+ inner.flush_();
+ layout->addLayoutItem(access(&inner));
+}
-LayoutBuilder::LayoutBuilder() = default;
+void addToLayout(Layout *layout, const LayoutModifier &inner)
+{
+ inner(layout);
+}
-/*!
- \internal
- Destructs a layout builder.
- */
-LayoutBuilder::~LayoutBuilder() = default;
+void addToLayout(Layout *layout, const QString &inner)
+{
+ layout->addLayoutItem(inner);
+}
-void LayoutBuilder::addItem(const LayoutItem &item)
+void empty(Layout *layout)
{
- addItemHelper(*this, item);
+ LayoutItem item;
+ item.empty = true;
+ layout->addLayoutItem(item);
}
-void LayoutBuilder::addItems(const LayoutItems &items)
+void hr(Layout *layout)
{
- for (const LayoutItem &item : items)
- addItemHelper(*this, item);
+ layout->addLayoutItem(createHr());
}
-/*!
- Starts a new row containing \a items. The row can be further extended by
- other items using \c addItem() or \c addItems().
+void br(Layout *layout)
+{
+ layout->flush();
+}
- \sa addItem(), addItems()
- */
-void LayoutItem::addRow(const LayoutItems &items)
+void st(Layout *layout)
{
- addItem(br);
- addItems(items);
+ LayoutItem item;
+ item.stretch = 1;
+ layout->addLayoutItem(item);
}
-/*!
- Adds the layout item \a item as sub items.
- */
-void LayoutItem::addItem(const LayoutItem &item)
+void noMargin(Layout *layout)
{
- subItems.append(item);
+ layout->setNoMargins();
}
-/*!
- Adds the layout items \a items as sub items.
- */
-void LayoutItem::addItems(const LayoutItems &items)
+void normalMargin(Layout *layout)
{
- subItems.append(items);
+ layout->setNormalMargins();
}
-/*!
- Attaches the constructed layout to the provided QWidget \a w.
+QFormLayout *Layout::asForm()
+{
+ return qobject_cast<QFormLayout *>(access(this));
+}
- This operation can only be performed once per LayoutBuilder instance.
- */
+QGridLayout *Layout::asGrid()
+{
+ return qobject_cast<QGridLayout *>(access(this));
+}
-void LayoutItem::attachTo(QWidget *w) const
+QBoxLayout *Layout::asBox()
{
- LayoutBuilder builder;
+ return qobject_cast<QBoxLayout *>(access(this));
+}
- builder.stack.append(w);
- addItemHelper(builder, *this);
+FlowLayout *Layout::asFlow()
+{
+ return dynamic_cast<FlowLayout *>(access(this));
}
-QWidget *LayoutItem::emerge()
+void Layout::flush()
{
- LayoutBuilder builder;
+ if (pendingItems.empty())
+ return;
- builder.stack.append(Slice());
- addItemHelper(builder, *this);
+ if (QGridLayout *lt = asGrid()) {
+ for (const LayoutItem &item : std::as_const(pendingItems)) {
+ Qt::Alignment a;
+ if (currentGridColumn == 0 && useFormAlignment) {
+ // if (auto widget = builder.stack.at(builder.stack.size() - 2).widget) {
+ // a = widget->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment);
+ }
+ if (item.widget)
+ lt->addWidget(item.widget, currentGridRow, currentGridColumn, item.spanRows, item.spanCols, a);
+ else if (item.layout)
+ lt->addLayout(item.layout, currentGridRow, currentGridColumn, item.spanRows, item.spanCols, a);
+ else if (!item.text.isEmpty())
+ lt->addWidget(createLabel(item.text), currentGridRow, currentGridColumn, item.spanRows, item.spanCols, a);
+ currentGridColumn += item.spanCols;
+ // Intentionally not used, use 'br'/'empty' for vertical progress.
+ // currentGridRow += item.spanRows;
+ }
+ ++currentGridRow;
+ currentGridColumn = 0;
+ pendingItems.clear();
+ return;
+ }
- if (builder.stack.empty())
- return nullptr;
+ if (QFormLayout *fl = asForm()) {
+ if (pendingItems.size() > 2) {
+ auto hbox = new QHBoxLayout;
+ hbox->setContentsMargins(0, 0, 0, 0);
+ for (size_t i = 1; i < pendingItems.size(); ++i)
+ addItemToBoxLayout(hbox, pendingItems.at(i));
+ while (pendingItems.size() > 1)
+ pendingItems.pop_back();
+ pendingItems.push_back(hbox);
+ }
- QTC_ASSERT(builder.stack.last().pendingItems.size() == 1, return nullptr);
- ResultItem ri = builder.stack.last().pendingItems.takeFirst();
+ if (pendingItems.size() == 1) { // Only one item given, so this spans both columns.
+ const LayoutItem &f0 = pendingItems.at(0);
+ if (auto layout = f0.layout)
+ fl->addRow(layout);
+ else if (auto widget = f0.widget)
+ fl->addRow(widget);
+ } else if (pendingItems.size() == 2) { // Normal case, both columns used.
+ LayoutItem &f1 = pendingItems[1];
+ const LayoutItem &f0 = pendingItems.at(0);
+ if (!f1.widget && !f1.layout && !f1.text.isEmpty())
+ f1.widget = createLabel(f1.text);
- QTC_ASSERT(ri.layout || ri.widget, return nullptr);
+ // QFormLayout accepts only widgets or text in the first column.
+ // FIXME: Should we be more generous?
+ if (f0.widget) {
+ if (f1.layout)
+ fl->addRow(f0.widget, f1.layout);
+ else if (f1.widget)
+ fl->addRow(f0.widget, f1.widget);
+ } else {
+ if (f1.layout)
+ fl->addRow(createLabel(f0.text), f1.layout);
+ else if (f1.widget)
+ fl->addRow(createLabel(f0.text), f1.widget);
+ }
+ } else {
+ QTC_CHECK(false);
+ }
- if (ri.layout) {
- auto w = new QWidget;
- w->setLayout(ri.layout);
- return w;
+ // Set up label as buddy if possible.
+ const int lastRow = fl->rowCount() - 1;
+ QLayoutItem *l = fl->itemAt(lastRow, QFormLayout::LabelRole);
+ QLayoutItem *f = fl->itemAt(lastRow, QFormLayout::FieldRole);
+ if (l && f) {
+ if (QLabel *label = qobject_cast<QLabel *>(l->widget())) {
+ if (QWidget *widget = widgetForItem(f))
+ label->setBuddy(widget);
+ }
+ }
+
+ pendingItems.clear();
+ return;
}
- return ri.widget;
+ QTC_CHECK(false); // The other layouts shouldn't use flush()
}
-static void layoutExit(LayoutBuilder &builder)
+void Layout::flush_() const
{
- builder.stack.last().flush();
- QLayout *layout = builder.stack.last().layout;
- builder.stack.pop_back();
-
- if (builder.stack.last().isLayouting) {
- builder.stack.last().pendingItems.append(ResultItem(layout));
- } else if (QWidget *widget = builder.stack.last().widget) {
- widget->setLayout(layout);
- } else
- builder.stack.last().pendingItems.append(ResultItem(layout));
+ const_cast<Layout *>(this)->flush();
}
-template<class T>
-static void layoutingWidgetExit(LayoutBuilder &builder)
+void withFormAlignment(Layout *layout)
{
- const Slice slice = builder.stack.last();
- T *w = qobject_cast<T *>(slice.widget);
- for (const ResultItem &ri : slice.pendingItems) {
- if (ri.widget) {
- w->addWidget(ri.widget);
- } else if (ri.layout) {
- auto child = new QWidget;
- child->setLayout(ri.layout);
- w->addWidget(child);
- }
- }
- builder.stack.pop_back();
- builder.stack.last().pendingItems.append(ResultItem(w));
+ layout->useFormAlignment = true;
}
-static void widgetExit(LayoutBuilder &builder)
+// Flow
+
+Flow::Flow(std::initializer_list<I> ps)
{
- QWidget *widget = builder.stack.last().widget;
- builder.stack.pop_back();
- builder.stack.last().pendingItems.append(ResultItem(widget));
+ ptr = new FlowLayout;
+ apply(this, ps);
+ flush();
}
-Column::Column(std::initializer_list<LayoutItem> items)
+// Row & Column
+
+Row::Row(std::initializer_list<I> ps)
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QVBoxLayout); };
- onExit = layoutExit;
+ ptr = new QHBoxLayout;
+ apply(this, ps);
+ flush();
}
-Row::Row(std::initializer_list<LayoutItem> items)
+Column::Column(std::initializer_list<I> ps)
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QHBoxLayout); };
- onExit = layoutExit;
+ ptr = new QVBoxLayout;
+ apply(this, ps);
+ flush();
}
-Flow::Flow(std::initializer_list<LayoutItem> items)
+// Grid
+
+Grid::Grid()
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(new FlowLayout); };
- onExit = layoutExit;
+ ptr = new QGridLayout;
}
-Grid::Grid(std::initializer_list<LayoutItem> items)
+Grid::Grid(std::initializer_list<I> ps)
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(new QGridLayout); };
- onExit = layoutExit;
+ ptr = new QGridLayout;
+ apply(this, ps);
+ flush();
}
-static QFormLayout *newFormLayout()
+// Form
+
+Form::Form()
{
- auto formLayout = new QFormLayout;
- formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
- return formLayout;
+ ptr = new QFormLayout;
}
-Form::Form(std::initializer_list<LayoutItem> items)
+Form::Form(std::initializer_list<I> ps)
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) { builder.stack.append(newFormLayout()); };
- onExit = layoutExit;
+ auto lt = new QFormLayout;
+ ptr = lt;
+ lt->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
+ apply(this, ps);
+ flush();
}
-LayoutItem br()
+void Layout::setFieldGrowthPolicy(int policy)
{
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) {
- builder.stack.last().flush();
- };
- return item;
+ if (auto lt = asForm())
+ lt->setFieldGrowthPolicy(QFormLayout::FieldGrowthPolicy(policy));
}
-LayoutItem empty()
+QWidget *Layout::emerge() const
{
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) {
- ResultItem ri;
- ri.empty = true;
- builder.stack.last().pendingItems.append(ri);
- };
- return item;
+ const_cast<Layout *>(this)->flush();
+ QWidget *widget = new QWidget;
+ widget->setLayout(access(this));
+ return widget;
}
-LayoutItem hr()
+void Layout::show() const
{
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) { doAddWidget(builder, createHr()); };
- return item;
+ return emerge()->show();
}
-LayoutItem st()
+// "Widgets"
+
+Widget::Widget(std::initializer_list<I> ps)
{
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) { doAddStretch(builder, Stretch(1)); };
- return item;
+ ptr = new Implementation;
+ apply(this, ps);
}
-LayoutItem noMargin()
+void Widget::setSize(int w, int h)
{
- return customMargin({});
+ access(this)->resize(w, h);
}
-LayoutItem normalMargin()
+void Widget::setLayout(const Layout &layout)
{
- return customMargin({9, 9, 9, 9});
+ access(this)->setLayout(access(&layout));
}
-LayoutItem customMargin(const QMargins &margin)
+void Widget::setWindowTitle(const QString &title)
{
- LayoutItem item;
- item.onAdd = [margin](LayoutBuilder &builder) {
- if (auto layout = builder.stack.last().layout)
- layout->setContentsMargins(margin);
- else if (auto widget = builder.stack.last().widget)
- widget->setContentsMargins(margin);
- };
- return item;
+ access(this)->setWindowTitle(title);
}
-LayoutItem withFormAlignment()
+void Widget::setToolTip(const QString &title)
{
- LayoutItem item;
- item.onAdd = [](LayoutBuilder &builder) {
- if (builder.stack.size() >= 2) {
- if (auto widget = builder.stack.at(builder.stack.size() - 2).widget) {
- const Qt::Alignment align(widget->style()->styleHint(QStyle::SH_FormLayoutLabelAlignment));
- builder.stack.last().align = align;
- }
- }
- };
- return item;
+ access(this)->setToolTip(title);
}
-// "Widgets"
+void Widget::show()
+{
+ access(this)->show();
+}
-template <class T>
-void setupWidget(LayoutItem *item)
+void Widget::setNoMargins(int)
{
- item->onAdd = [](LayoutBuilder &builder) { builder.stack.append(new T); };
- item->onExit = widgetExit;
-};
+ setContentsMargins(0, 0, 0, 0);
+}
-Widget::Widget(std::initializer_list<LayoutItem> items)
+void Widget::setNormalMargins(int)
{
- this->subItems = items;
- setupWidget<QWidget>(this);
+ setContentsMargins(9, 9, 9, 9);
}
-Group::Group(std::initializer_list<LayoutItem> items)
+void Widget::setContentsMargins(int left, int top, int right, int bottom)
{
- this->subItems = items;
- setupWidget<QGroupBox>(this);
+ access(this)->setContentsMargins(left, top, right, bottom);
}
-Stack::Stack(std::initializer_list<LayoutItem> items)
+QWidget *Widget::emerge() const
{
- // We use a QStackedWidget instead of a QStackedLayout here because the latter will call
- // "setVisible()" when a child is added, which can lead to the widget being spawned as a
- // top-level widget. This can lead to the focus shifting away from the main application.
- subItems = items;
- onAdd = [](LayoutBuilder &builder) {
- builder.stack.append(Slice(new QStackedWidget, true));
- };
- onExit = layoutingWidgetExit<QStackedWidget>;
+ return access(this);
}
-PushButton::PushButton(std::initializer_list<LayoutItem> items)
+// Label
+
+Label::Label(std::initializer_list<I> ps)
{
- this->subItems = items;
- setupWidget<QPushButton>(this);
+ ptr = new Implementation;
+ apply(this, ps);
}
-SpinBox::SpinBox(std::initializer_list<LayoutItem> items)
+Label::Label(const QString &text)
{
- this->subItems = items;
- setupWidget<QSpinBox>(this);
+ ptr = new Implementation;
+ setText(text);
}
-TextEdit::TextEdit(std::initializer_list<LayoutItem> items)
+void Label::setText(const QString &text)
{
- this->subItems = items;
- setupWidget<QTextEdit>(this);
+ access(this)->setText(text);
}
-Splitter::Splitter(std::initializer_list<LayoutItem> items)
+void Label::setTextFormat(Qt::TextFormat format)
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) {
- auto splitter = new QSplitter;
- splitter->setOrientation(Qt::Vertical);
- builder.stack.append(Slice(splitter, true));
- };
- onExit = layoutingWidgetExit<QSplitter>;
+ access(this)->setTextFormat(format);
}
-ToolBar::ToolBar(std::initializer_list<LayoutItem> items)
+void Label::setWordWrap(bool on)
{
- subItems = items;
- onAdd = [](LayoutBuilder &builder) {
- auto toolbar = new QToolBar;
- toolbar->setOrientation(Qt::Horizontal);
- builder.stack.append(Slice(toolbar, true));
- };
- onExit = layoutingWidgetExit<QToolBar>;
+ access(this)->setWordWrap(on);
}
-TabWidget::TabWidget(std::initializer_list<LayoutItem> items)
+void Label::setTextInteractionFlags(Qt::TextInteractionFlags flags)
{
- this->subItems = items;
- setupWidget<QTabWidget>(this);
+ access(this)->setTextInteractionFlags(flags);
}
-// Special Tab
+void Label::setOpenExternalLinks(bool on)
+{
+ access(this)->setOpenExternalLinks(on);
+}
-Tab::Tab(const QString &tabName, const LayoutItem &item)
+void Label::onLinkHovered(const std::function<void (const QString &)> &func, QObject *guard)
{
- onAdd = [item](LayoutBuilder &builder) {
- auto tab = new QWidget;
- builder.stack.append(tab);
- item.attachTo(tab);
- };
- onExit = [tabName](LayoutBuilder &builder) {
- QWidget *inner = builder.stack.last().widget;
- builder.stack.pop_back();
- auto tabWidget = qobject_cast<QTabWidget *>(builder.stack.last().widget);
- QTC_ASSERT(tabWidget, return);
- tabWidget->addTab(inner, tabName);
- };
+ QObject::connect(access(this), &QLabel::linkHovered, guard, func);
}
-// Special If
+// Group
-If::If(bool condition, const LayoutItems &items, const LayoutItems &other)
+Group::Group(std::initializer_list<I> ps)
{
- subItems.append(condition ? items : other);
+ ptr = new Implementation;
+ apply(this, ps);
}
-// Special Application
-
-Application::Application(std::initializer_list<LayoutItem> items)
+void Group::setTitle(const QString &title)
{
- subItems = items;
- setupWidget<QWidget>(this);
- onExit = {}; // Hack: Don't dropp the last slice, we need the resulting widget.
+ access(this)->setTitle(title);
+ access(this)->setObjectName(title);
}
-int Application::exec(int &argc, char *argv[])
+void Group::setGroupChecker(const std::function<void (QObject *)> &checker)
{
- QApplication app(argc, argv);
- LayoutBuilder builder;
- addItemHelper(builder, *this);
- if (QWidget *widget = builder.stack.last().widget)
- widget->show();
- return app.exec();
+ checker(access(this));
}
-// "Properties"
+// SpinBox
-LayoutItem title(const QString &title)
+SpinBox::SpinBox(std::initializer_list<I> ps)
{
- return [title](QObject *target) {
- if (auto groupBox = qobject_cast<QGroupBox *>(target)) {
- groupBox->setTitle(title);
- groupBox->setObjectName(title);
- } else if (auto widget = qobject_cast<QWidget *>(target)) {
- widget->setWindowTitle(title);
- } else {
- QTC_CHECK(false);
- }
- };
+ ptr = new Implementation;
+ apply(this, ps);
}
-LayoutItem windowTitle(const QString &windowTitle)
+void SpinBox::setValue(int val)
{
- return [windowTitle](QObject *target) {
- if (auto widget = qobject_cast<QWidget *>(target)) {
- widget->setWindowTitle(windowTitle);
- } else {
- QTC_CHECK(false);
- }
- };
+ access(this)->setValue(val);
}
-LayoutItem text(const QString &text)
+void SpinBox::onTextChanged(const std::function<void (QString)> &func)
{
- return [text](QObject *target) {
- if (auto button = qobject_cast<QAbstractButton *>(target)) {
- button->setText(text);
- } else if (auto textEdit = qobject_cast<QTextEdit *>(target)) {
- textEdit->setText(text);
- } else {
- QTC_CHECK(false);
- }
- };
+ QObject::connect(access(this), &QSpinBox::textChanged, func);
}
-LayoutItem tooltip(const QString &toolTip)
+// TextEdit
+
+TextEdit::TextEdit(std::initializer_list<I> ps)
{
- return [toolTip](QObject *target) {
- if (auto widget = qobject_cast<QWidget *>(target)) {
- widget->setToolTip(toolTip);
- } else {
- QTC_CHECK(false);
- }
- };
+ ptr = new Implementation;
+ apply(this, ps);
}
-LayoutItem spacing(int spacing)
+void TextEdit::setText(const QString &text)
{
- return [spacing](QObject *target) {
- if (auto layout = qobject_cast<QLayout *>(target)) {
- layout->setSpacing(spacing);
- } else {
- QTC_CHECK(false);
- }
- };
+ access(this)->setText(text);
}
-LayoutItem resize(int w, int h)
+// PushButton
+
+PushButton::PushButton(std::initializer_list<I> ps)
{
- return [w, h](QObject *target) {
- if (auto widget = qobject_cast<QWidget *>(target)) {
- widget->resize(w, h);
- } else {
- QTC_CHECK(false);
- }
- };
+ ptr = new Implementation;
+ apply(this, ps);
}
-LayoutItem columnStretch(int column, int stretch)
+void PushButton::setText(const QString &text)
{
- return [column, stretch](QObject *target) {
- if (auto grid = qobject_cast<QGridLayout *>(target)) {
- grid->setColumnStretch(column, stretch);
- } else {
- QTC_CHECK(false);
- }
- };
+ access(this)->setText(text);
}
-LayoutItem fieldGrowthPolicy(QFormLayout::FieldGrowthPolicy policy)
+void PushButton::onClicked(const std::function<void ()> &func, QObject *guard)
{
- return [policy](QObject *target) {
- if (auto form = qobject_cast<QFormLayout *>(target)) {
- form->setFieldGrowthPolicy(policy);
- } else {
- QTC_CHECK(false);
- }
- };
+ QObject::connect(access(this), &QAbstractButton::clicked, guard, func);
}
+// Stack
-// Id based setters
-
-LayoutItem id(ID &out)
+// We use a QStackedWidget instead of a QStackedLayout here because the latter will call
+// "setVisible()" when a child is added, which can lead to the widget being spawned as a
+// top-level widget. This can lead to the focus shifting away from the main application.
+Stack::Stack(std::initializer_list<I> ps)
{
- return [&out](QObject *target) { out.ob = target; };
+ ptr = new Implementation;
+ apply(this, ps);
}
-void setText(ID id, const QString &text)
+void addToStack(Stack *stack, const Widget &inner)
{
- if (auto textEdit = qobject_cast<QTextEdit *>(id.ob))
- textEdit->setText(text);
+ access(stack)->addWidget(inner.emerge());
}
-// Signals
+void addToStack(Stack *stack, const Layout &inner)
+{
+ inner.flush_();
+ access(stack)->addWidget(inner.emerge());
+}
-LayoutItem onClicked(const std::function<void ()> &func, QObject *guard)
+void addToStack(Stack *stack, QWidget *inner)
{
- return [func, guard](QObject *target) {
- if (auto button = qobject_cast<QAbstractButton *>(target)) {
- QObject::connect(button, &QAbstractButton::clicked, guard ? guard : target, func);
- } else {
- QTC_CHECK(false);
- }
- };
+ access(stack)->addWidget(inner);
}
-LayoutItem onTextChanged(const std::function<void (const QString &)> &func, QObject *guard)
+// Splitter
+
+Splitter::Splitter(std::initializer_list<I> ps)
{
- return [func, guard](QObject *target) {
- if (auto button = qobject_cast<QSpinBox *>(target)) {
- QObject::connect(button, &QSpinBox::textChanged, guard ? guard : target, func);
- } else {
- QTC_CHECK(false);
- }
- };
+ ptr = new Implementation;
+ access(this)->setOrientation(Qt::Vertical);
+ apply(this, ps);
}
-LayoutItem onValueChanged(const std::function<void (int)> &func, QObject *guard)
+void addToSplitter(Splitter *splitter, QWidget *inner)
{
- return [func, guard](QObject *target) {
- if (auto button = qobject_cast<QSpinBox *>(target)) {
- QObject::connect(button, &QSpinBox::valueChanged, guard ? guard : target, func);
- } else {
- QTC_CHECK(false);
- }
- };
+ access(splitter)->addWidget(inner);
}
-// Convenience
+void addToSplitter(Splitter *splitter, const Widget &inner)
+{
+ access(splitter)->addWidget(inner.emerge());
+}
-QWidget *createHr(QWidget *parent)
+void addToSplitter(Splitter *splitter, const Layout &inner)
{
- auto frame = new QFrame(parent);
- frame->setFrameShape(QFrame::HLine);
- frame->setFrameShadow(QFrame::Sunken);
- return frame;
+ inner.flush_();
+ access(splitter)->addWidget(inner.emerge());
}
-// Singletons.
+// ToolBar
-LayoutItem::LayoutItem(const LayoutItem &t)
+ToolBar::ToolBar(std::initializer_list<I> ps)
{
- operator=(t);
+ ptr = new Implementation;
+ apply(this, ps);
+ access(this)->setOrientation(Qt::Horizontal);
}
-void createItem(LayoutItem *item, LayoutItem(*t)())
+// TabWidget
+
+TabWidget::TabWidget(std::initializer_list<I> ps)
{
- *item = t();
+ ptr = new Implementation;
+ apply(this, ps);
}
-void createItem(LayoutItem *item, const std::function<void(QObject *target)> &t)
+Tab::Tab(const QString &tabName, const Layout &inner)
+ : tabName(tabName), inner(inner)
+{}
+
+void addToTabWidget(TabWidget *tabWidget, const Tab &tab)
{
- item->setter = t;
+ access(tabWidget)->addTab(tab.inner.emerge(), tab.tabName);
}
-void createItem(LayoutItem *item, QWidget *t)
-{
- if (auto l = qobject_cast<QLabel *>(t))
- l->setTextInteractionFlags(l->textInteractionFlags() | Qt::TextSelectableByMouse);
+// Special If
+
+If::If(bool condition,
+ const std::initializer_list<Layout::I> ifcase,
+ const std::initializer_list<Layout::I> thencase)
+ : used(condition ? ifcase : thencase)
+{}
- item->onAdd = [t](LayoutBuilder &builder) { doAddWidget(builder, t); };
+void addToLayout(Layout *layout, const If &inner)
+{
+ for (const Layout::I &item : inner.used)
+ item.apply(layout);
}
-void createItem(LayoutItem *item, QLayout *t)
+// Specials
+
+QWidget *createHr(QWidget *parent)
{
- item->onAdd = [t](LayoutBuilder &builder) { doAddLayout(builder, t); };
+ auto frame = new QFrame(parent);
+ frame->setFrameShape(QFrame::HLine);
+ frame->setFrameShadow(QFrame::Sunken);
+ return frame;
}
-void createItem(LayoutItem *item, const QString &t)
+Span::Span(int cols, const Layout::I &item)
+ : item(item), spanCols(cols)
+{}
+
+Span::Span(int cols, int rows, const Layout::I &item)
+ : item(item), spanCols(cols), spanRows(rows)
+{}
+
+void addToLayout(Layout *layout, const Span &inner)
{
- item->onAdd = [t](LayoutBuilder &builder) { doAddText(builder, t); };
+ layout->addItem(inner.item);
+ if (layout->pendingItems.empty()) {
+ QTC_CHECK(inner.spanCols == 1 && inner.spanRows == 1);
+ return;
+ }
+ layout->pendingItems.back().spanCols = inner.spanCols;
+ layout->pendingItems.back().spanRows = inner.spanRows;
}
-void createItem(LayoutItem *item, const Space &t)
+LayoutModifier spacing(int space)
{
- item->onAdd = [t](LayoutBuilder &builder) { doAddSpace(builder, t); };
+ return [space](Layout *layout) { layout->setSpacing(space); };
}
-void createItem(LayoutItem *item, const Stretch &t)
+void addToLayout(Layout *layout, const Space &inner)
{
- item->onAdd = [t](LayoutBuilder &builder) { doAddStretch(builder, t); };
+ if (auto lt = layout->asBox())
+ lt->addSpacing(inner.space);
}
-void createItem(LayoutItem *item, const Span &t)
+void addToLayout(Layout *layout, const Stretch &inner)
{
- item->onAdd = [t](LayoutBuilder &builder) {
- addItemHelper(builder, t.item);
- builder.stack.last().pendingItems.last().span = t.span;
- };
+ if (auto lt = layout->asBox())
+ lt->addStretch(inner.stretch);
}
-} // Layouting
+// void createItem(LayoutItem *item, QWidget *t)
+// {
+// if (auto l = qobject_cast<QLabel *>(t))
+// l->setTextInteractionFlags(l->textInteractionFlags() | Qt::TextSelectableByMouse);
+
+// item->onAdd = [t](LayoutBuilder &builder) { doAddWidget(builder, t); };
+// }
-#include "layoutbuilder.moc"
+
+} // Layouting
diff --git a/src/libs/utils/layoutbuilder.h b/src/libs/utils/layoutbuilder.h
index c9fac7d838..a411ea4763 100644
--- a/src/libs/utils/layoutbuilder.h
+++ b/src/libs/utils/layoutbuilder.h
@@ -3,12 +3,11 @@
#pragma once
-#include <QFormLayout>
-#include <QList>
#include <QString>
-#include <QtGlobal>
-#include <optional>
+#include <functional>
+#include <initializer_list>
+#include <vector>
#if defined(UTILS_LIBRARY)
# define QTCREATOR_UTILS_EXPORT Q_DECL_EXPORT
@@ -19,251 +18,570 @@
#endif
QT_BEGIN_NAMESPACE
+class QBoxLayout;
+class QFormLayout;
+class QGridLayout;
+class QGroupBox;
+class QHBoxLayout;
+class QLabel;
class QLayout;
-class QMargins;
class QObject;
+class QPushButton;
+class QSpinBox;
+class QSplitter;
+class QStackedWidget;
+class QTabWidget;
+class QTextEdit;
+class QToolBar;
+class QVBoxLayout;
class QWidget;
-template <class T> T qobject_cast(QObject *object);
QT_END_NAMESPACE
namespace Layouting {
-// LayoutItem
+class NestId {};
-class LayoutBuilder;
-class LayoutItem;
-using LayoutItems = QList<LayoutItem>;
-
-class QTCREATOR_UTILS_EXPORT LayoutItem
+template <typename Id, typename Arg>
+class IdAndArg
{
public:
- using Setter = std::function<void(QObject *target)>;
+ IdAndArg(Id, const Arg &arg) : arg(arg) {}
+ const Arg arg; // FIXME: Could be const &, but this would currently break bindTo().
+};
- LayoutItem();
- ~LayoutItem();
+template<typename T1, typename T2>
+struct Arg2
+{
+ Arg2(const T1 &a1, const T2 &a2)
+ : p1(a1)
+ , p2(a2)
+ {}
+ const T1 p1;
+ const T2 p2;
+};
+
+template<typename T1, typename T2, typename T3>
+struct Arg3
+{
+ Arg3(const T1 &a1, const T2 &a2, const T3 &a3)
+ : p1(a1)
+ , p2(a2)
+ , p3(a3)
+ {}
+ const T1 p1;
+ const T2 p2;
+ const T3 p3;
+};
+
+template<typename T1, typename T2, typename T3, typename T4>
+struct Arg4
+{
+ Arg4(const T1 &a1, const T2 &a2, const T3 &a3, const T4 &a4)
+ : p1(a1)
+ , p2(a2)
+ , p3(a3)
+ , p4(a4)
+ {}
+ const T1 p1;
+ const T2 p2;
+ const T3 p3;
+ const T4 p4;
+};
+
+// The main dispatcher
- LayoutItem(const LayoutItem &t);
- LayoutItem &operator=(const LayoutItem &t) = default;
+void doit(auto x, auto id, auto p);
- template <class T> LayoutItem(const T &t)
+template <typename X> class BuilderItem
+{
+public:
+ // Nested child object
+ template <typename Inner>
+ BuilderItem(Inner && p)
{
- if constexpr (std::is_base_of_v<LayoutItem, T>)
- LayoutItem::operator=(t);
- else
- createItem(this, t);
+ apply = [&p](X *x) { doit(x, NestId{}, std::forward<Inner>(p)); };
}
- void attachTo(QWidget *w) const;
- QWidget *emerge();
-
- void addItem(const LayoutItem &item);
- void addItems(const LayoutItems &items);
- void addRow(const LayoutItems &items);
+ // Property setter
+ template <typename Id, typename Arg>
+ BuilderItem(IdAndArg<Id, Arg> && idarg)
+ {
+ apply = [&idarg](X *x) { doit(x, Id{}, idarg.arg); };
+ }
- std::function<void(LayoutBuilder &)> onAdd;
- std::function<void(LayoutBuilder &)> onExit;
- std::function<void(QObject *target)> setter;
- LayoutItems subItems;
+ std::function<void(X *)> apply;
};
-// Special items
-class QTCREATOR_UTILS_EXPORT Space
+//////////////////////////////////////////////
+
+//
+// Basic
+//
+
+class QTCREATOR_UTILS_EXPORT Thing
{
public:
- explicit Space(int space) : space(space) {}
- const int space;
+ void *ptr; // The product.
};
-class QTCREATOR_UTILS_EXPORT Stretch
+class QTCREATOR_UTILS_EXPORT Object : public Thing
{
public:
- explicit Stretch(int stretch = 1) : stretch(stretch) {}
- const int stretch;
+ using Implementation = QObject;
+ using I = BuilderItem<Object>;
+
+ Object() = default;
+ Object(std::initializer_list<I> ps);
};
-class QTCREATOR_UTILS_EXPORT Span
+//
+// Layouts
+//
+
+class FlowLayout;
+class Layout;
+using LayoutModifier = std::function<void(Layout *)>;
+
+class QTCREATOR_UTILS_EXPORT LayoutItem
{
public:
- Span(int span, const LayoutItem &item) : span(span), item(item) {}
- const int span;
- LayoutItem item;
+ ~LayoutItem();
+ LayoutItem();
+ LayoutItem(QLayout *l);
+ LayoutItem(QWidget *w);
+ LayoutItem(const QString &t);
+
+ QString text;
+ QLayout *layout = nullptr;
+ QWidget *widget = nullptr;
+ int stretch = -1;
+ int spanCols = 1;
+ int spanRows = 1;
+ bool empty = false;
};
-class QTCREATOR_UTILS_EXPORT Column : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Layout : public Object
{
public:
- Column(std::initializer_list<LayoutItem> items);
+ using Implementation = QLayout;
+ using I = BuilderItem<Layout>;
+
+ Layout() = default;
+ Layout(Implementation *w) { ptr = w; }
+
+ void span(int cols, int rows);
+
+ void setNoMargins();
+ void setNormalMargins();
+ void setContentsMargins(int left, int top, int right, int bottom);
+ void setColumnStretch(int cols, int rows);
+ void setSpacing(int space);
+ void setFieldGrowthPolicy(int policy);
+
+ void attachTo(QWidget *);
+
+ void addItem(I item);
+ void addItems(std::initializer_list<I> items);
+ void addRow(std::initializer_list<I> items);
+ void addLayoutItem(const LayoutItem &item);
+
+ void flush();
+ void flush_() const;
+
+ QWidget *emerge() const;
+ void show() const;
+
+ QFormLayout *asForm();
+ QGridLayout *asGrid();
+ QBoxLayout *asBox();
+ FlowLayout *asFlow();
+
+ // Grid-only
+ int currentGridColumn = 0;
+ int currentGridRow = 0;
+ //Qt::Alignment align = {};
+ bool useFormAlignment = false;
+
+ std::vector<LayoutItem> pendingItems;
};
-class QTCREATOR_UTILS_EXPORT Row : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Column : public Layout
{
public:
- Row(std::initializer_list<LayoutItem> items);
+ using Implementation = QVBoxLayout;
+ using I = BuilderItem<Column>;
+
+ Column(std::initializer_list<I> ps);
};
-class QTCREATOR_UTILS_EXPORT Flow : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Row : public Layout
{
public:
- Flow(std::initializer_list<LayoutItem> items);
+ using Implementation = QHBoxLayout;
+ using I = BuilderItem<Row>;
+
+ Row(std::initializer_list<I> ps);
};
-class QTCREATOR_UTILS_EXPORT Grid : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Form : public Layout
{
public:
- Grid() : Grid({}) {}
- Grid(std::initializer_list<LayoutItem> items);
+ using Implementation = QFormLayout;
+ using I = BuilderItem<Form>;
+
+ Form();
+ Form(std::initializer_list<I> ps);
};
-class QTCREATOR_UTILS_EXPORT Form : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Grid : public Layout
{
public:
- Form() : Form({}) {}
- Form(std::initializer_list<LayoutItem> items);
+ using Implementation = QGridLayout;
+ using I = BuilderItem<Grid>;
+
+ Grid();
+ Grid(std::initializer_list<I> ps);
};
-class QTCREATOR_UTILS_EXPORT Widget : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Flow : public Layout
{
public:
- Widget(std::initializer_list<LayoutItem> items);
+ Flow(std::initializer_list<I> ps);
};
-class QTCREATOR_UTILS_EXPORT Stack : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Stretch
{
public:
- Stack() : Stack({}) {}
- Stack(std::initializer_list<LayoutItem> items);
+ explicit Stretch(int stretch) : stretch(stretch) {}
+
+ int stretch;
};
-class QTCREATOR_UTILS_EXPORT Tab : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Space
{
public:
- Tab(const QString &tabName, const LayoutItem &item);
+ explicit Space(int space) : space(space) {}
+
+ int space;
};
-class QTCREATOR_UTILS_EXPORT If : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Span
{
public:
- If(bool condition, const LayoutItems &item, const LayoutItems &other = {});
+ Span(int cols, const Layout::I &item);
+ Span(int cols, int rows, const Layout::I &item);
+
+ Layout::I item;
+ int spanCols = 1;
+ int spanRows = 1;
};
-class QTCREATOR_UTILS_EXPORT Group : public LayoutItem
+//
+// Widgets
+//
+
+class QTCREATOR_UTILS_EXPORT Widget : public Object
{
public:
- Group(std::initializer_list<LayoutItem> items);
+ using Implementation = QWidget;
+ using I = BuilderItem<Widget>;
+
+ Widget() = default;
+ Widget(std::initializer_list<I> ps);
+ Widget(Implementation *w) { ptr = w; }
+
+ QWidget *emerge() const;
+ void show();
+
+ void setLayout(const Layout &layout);
+ void setSize(int, int);
+ void setWindowTitle(const QString &);
+ void setToolTip(const QString &);
+ void setNoMargins(int = 0);
+ void setNormalMargins(int = 0);
+ void setContentsMargins(int left, int top, int right, int bottom);
};
-class QTCREATOR_UTILS_EXPORT TextEdit : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Label : public Widget
{
public:
- TextEdit(std::initializer_list<LayoutItem> items);
+ using Implementation = QLabel;
+ using I = BuilderItem<Label>;
+
+ Label(std::initializer_list<I> ps);
+ Label(const QString &text);
+
+ void setText(const QString &);
+ void setTextFormat(Qt::TextFormat);
+ void setWordWrap(bool);
+ void setTextInteractionFlags(Qt::TextInteractionFlags);
+ void setOpenExternalLinks(bool);
+ void onLinkHovered(const std::function<void(const QString &)> &, QObject *guard);
};
-class QTCREATOR_UTILS_EXPORT PushButton : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Group : public Widget
{
public:
- PushButton(std::initializer_list<LayoutItem> items);
+ using Implementation = QGroupBox;
+ using I = BuilderItem<Group>;
+
+ Group(std::initializer_list<I> ps);
+
+ void setTitle(const QString &);
+ void setGroupChecker(const std::function<void(QObject *)> &);
};
-class QTCREATOR_UTILS_EXPORT SpinBox : public LayoutItem
+class QTCREATOR_UTILS_EXPORT SpinBox : public Widget
{
public:
- SpinBox(std::initializer_list<LayoutItem> items);
+ using Implementation = QSpinBox;
+ using I = BuilderItem<SpinBox>;
+
+ SpinBox(std::initializer_list<I> ps);
+
+ void setValue(int);
+ void onTextChanged(const std::function<void(QString)> &);
};
-class QTCREATOR_UTILS_EXPORT Splitter : public LayoutItem
+class QTCREATOR_UTILS_EXPORT PushButton : public Widget
{
public:
- Splitter(std::initializer_list<LayoutItem> items);
+ using Implementation = QPushButton;
+ using I = BuilderItem<PushButton>;
+
+ PushButton(std::initializer_list<I> ps);
+
+ void setText(const QString &);
+ void onClicked(const std::function<void()> &, QObject *guard);
};
-class QTCREATOR_UTILS_EXPORT ToolBar : public LayoutItem
+class QTCREATOR_UTILS_EXPORT TextEdit : public Widget
{
public:
- ToolBar(std::initializer_list<LayoutItem> items);
+ using Implementation = QTextEdit;
+ using I = BuilderItem<TextEdit>;
+ using Id = Implementation *;
+
+ TextEdit(std::initializer_list<I> ps);
+
+ void setText(const QString &);
};
-class QTCREATOR_UTILS_EXPORT TabWidget : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Splitter : public Widget
{
public:
- TabWidget(std::initializer_list<LayoutItem> items);
+ using Implementation = QSplitter;
+ using I = BuilderItem<Splitter>;
+
+ Splitter(std::initializer_list<I> items);
};
-class QTCREATOR_UTILS_EXPORT Application : public LayoutItem
+class QTCREATOR_UTILS_EXPORT Stack : public Widget
{
public:
- Application(std::initializer_list<LayoutItem> items);
+ using Implementation = QStackedWidget;
+ using I = BuilderItem<Stack>;
- int exec(int &argc, char *argv[]);
+ Stack() : Stack({}) {}
+ Stack(std::initializer_list<I> items);
};
+class QTCREATOR_UTILS_EXPORT Tab : public Widget
+{
+public:
+ using Implementation = QWidget;
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const std::function<void(QObject *target)> &t);
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, QWidget *t);
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, QLayout *t);
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, LayoutItem(*t)());
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const QString &t);
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const Span &t);
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const Space &t);
-void QTCREATOR_UTILS_EXPORT createItem(LayoutItem *item, const Stretch &t);
-
+ Tab(const QString &tabName, const Layout &inner);
-// "Singletons"
+ const QString tabName;
+ const Layout inner;
+};
-QTCREATOR_UTILS_EXPORT LayoutItem br();
-QTCREATOR_UTILS_EXPORT LayoutItem st();
-QTCREATOR_UTILS_EXPORT LayoutItem empty();
-QTCREATOR_UTILS_EXPORT LayoutItem hr();
-QTCREATOR_UTILS_EXPORT LayoutItem noMargin();
-QTCREATOR_UTILS_EXPORT LayoutItem normalMargin();
-QTCREATOR_UTILS_EXPORT LayoutItem customMargin(const QMargins &margin);
-QTCREATOR_UTILS_EXPORT LayoutItem withFormAlignment();
+class QTCREATOR_UTILS_EXPORT TabWidget : public Widget
+{
+public:
+ using Implementation = QTabWidget;
+ using I = BuilderItem<TabWidget>;
-// "Setters"
+ TabWidget(std::initializer_list<I> items);
+};
-QTCREATOR_UTILS_EXPORT LayoutItem title(const QString &title);
-QTCREATOR_UTILS_EXPORT LayoutItem text(const QString &text);
-QTCREATOR_UTILS_EXPORT LayoutItem tooltip(const QString &toolTip);
-QTCREATOR_UTILS_EXPORT LayoutItem resize(int, int);
-QTCREATOR_UTILS_EXPORT LayoutItem columnStretch(int column, int stretch);
-QTCREATOR_UTILS_EXPORT LayoutItem spacing(int);
-QTCREATOR_UTILS_EXPORT LayoutItem windowTitle(const QString &windowTitle);
-QTCREATOR_UTILS_EXPORT LayoutItem fieldGrowthPolicy(QFormLayout::FieldGrowthPolicy policy);
+class QTCREATOR_UTILS_EXPORT ToolBar : public Widget
+{
+public:
+ using Implementation = QToolBar;
+ using I = Layouting::BuilderItem<ToolBar>;
+ ToolBar(std::initializer_list<I> items);
+};
-// "Getters"
+// Special
-class ID
+class QTCREATOR_UTILS_EXPORT If
{
public:
- QObject *ob = nullptr;
+ If(bool condition,
+ const std::initializer_list<Layout::I> ifcase,
+ const std::initializer_list<Layout::I> thencase = {});
+
+ const std::initializer_list<Layout::I> used;
};
-QTCREATOR_UTILS_EXPORT LayoutItem id(ID &out);
+//
+// Dispatchers
+//
-QTCREATOR_UTILS_EXPORT void setText(ID id, const QString &text);
+// We need one 'Id' (and a corresponding function wrapping arguments into a
+// tuple marked by this id) per 'name' of "backend" setter member function,
+// i.e. one 'text' is sufficient for QLabel::setText, QLineEdit::setText.
+// The name of the Id does not have to match the backend names as it
+// is mapped per-backend-type in the respective setter implementation
+// but we assume that it generally makes sense to stay close to the
+// wrapped API name-wise.
+// These are free functions overloaded on the type of builder object
+// and setter id. The function implementations are independent, but
+// the base expectation is that they will forwards to the backend
+// type's setter.
-// "Signals"
+// Special dispatchers
-QTCREATOR_UTILS_EXPORT LayoutItem onClicked(const std::function<void()> &,
- QObject *guard = nullptr);
-QTCREATOR_UTILS_EXPORT LayoutItem onTextChanged(const std::function<void(const QString &)> &,
- QObject *guard = nullptr);
-QTCREATOR_UTILS_EXPORT LayoutItem onValueChanged(const std::function<void(int)> &,
- QObject *guard = nullptr);
-QTCREATOR_UTILS_EXPORT LayoutItem onTextChanged(ID &id, QVariant(*sig)(QObject *));
+class BindToId {};
-// Convenience
+template <typename T>
+auto bindTo(T **p)
+{
+ return IdAndArg{BindToId{}, p};
+}
-QTCREATOR_UTILS_EXPORT QWidget *createHr(QWidget *parent = nullptr);
+template <typename Interface>
+void doit(Interface *x, BindToId, auto p)
+{
+ *p = static_cast<typename Interface::Implementation *>(x->ptr);
+}
+
+class IdId {};
+auto id(auto p) { return IdAndArg{IdId{}, p}; }
+
+template <typename Interface>
+void doit(Interface *x, IdId, auto p)
+{
+ **p = static_cast<typename Interface::Implementation *>(x->ptr);
+}
+
+// Setter dispatchers
+
+#define QTCREATOR_SETTER(name, setter) \
+ class name##_TAG {}; \
+ inline auto name(auto p) { return IdAndArg{name##_TAG{}, p}; } \
+ inline void doit(auto x, name##_TAG, auto p) { x->setter(p); }
+
+#define QTCREATOR_SETTER2(name, setter) \
+ class name##_TAG {}; \
+ inline auto name(auto p1, auto p2) { return IdAndArg{name##_TAG{}, Arg2{p1, p2}}; } \
+ inline void doit(auto x, name##_TAG, auto p) { x->setter(p.p1, p.p2); }
+
+#define QTCREATOR_SETTER3(name, setter) \
+ class name##_TAG {}; \
+ inline auto name(auto p1, auto p2, auto p3) { return IdAndArg{name##_TAG{}, Arg3{p1, p2, p3}}; } \
+ inline void doit(auto x, name##_TAG, auto p) { x->setter(p.p1, p.p2, p.p3); }
+
+#define QTCREATOR_SETTER4(name, setter) \
+ class name##_TAG {}; \
+ inline auto name(auto p1, auto p2, auto p3, auto p4) { return IdAndArg{name##_TAG{}, Arg4{p1, p2, p3, p4}}; } \
+ inline void doit(auto x, name##_TAG, auto p) { x->setter(p.p1, p.p2, p.p3, p.p4); }
+
+QTCREATOR_SETTER(fieldGrowthPolicy, setFieldGrowthPolicy);
+QTCREATOR_SETTER(groupChecker, setGroupChecker);
+QTCREATOR_SETTER(openExternalLinks, setOpenExternalLinks);
+QTCREATOR_SETTER2(size, setSize)
+QTCREATOR_SETTER(text, setText)
+QTCREATOR_SETTER(textFormat, setTextFormat);
+QTCREATOR_SETTER(textInteractionFlags, setTextInteractionFlags);
+QTCREATOR_SETTER(title, setTitle)
+QTCREATOR_SETTER(toolTip, setToolTip);
+QTCREATOR_SETTER(windowTitle, setWindowTitle);
+QTCREATOR_SETTER(wordWrap, setWordWrap);
+QTCREATOR_SETTER2(columnStretch, setColumnStretch);
+QTCREATOR_SETTER2(onClicked, onClicked);
+QTCREATOR_SETTER2(onLinkHovered, onLinkHovered);
+QTCREATOR_SETTER2(onTextChanged, onTextChanged);
+QTCREATOR_SETTER4(customMargins, setContentsMargins);
+
+// Nesting dispatchers
+
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Layout &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Widget &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, QWidget *inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, QLayout *inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const LayoutModifier &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const QString &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Space &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Stretch &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const If &inner);
+QTCREATOR_UTILS_EXPORT void addToLayout(Layout *layout, const Span &inner);
+// ... can be added to anywhere later to support "user types"
+
+QTCREATOR_UTILS_EXPORT void addToWidget(Widget *widget, const Layout &layout);
+
+QTCREATOR_UTILS_EXPORT void addToTabWidget(TabWidget *tabWidget, const Tab &inner);
+
+QTCREATOR_UTILS_EXPORT void addToSplitter(Splitter *splitter, QWidget *inner);
+QTCREATOR_UTILS_EXPORT void addToSplitter(Splitter *splitter, const Widget &inner);
+QTCREATOR_UTILS_EXPORT void addToSplitter(Splitter *splitter, const Layout &inner);
+
+QTCREATOR_UTILS_EXPORT void addToStack(Stack *stack, QWidget *inner);
+QTCREATOR_UTILS_EXPORT void addToStack(Stack *stack, const Widget &inner);
+QTCREATOR_UTILS_EXPORT void addToStack(Stack *stack, const Layout &inner);
+
+template <class Inner>
+void doit_nested(Layout *outer, Inner && inner)
+{
+ addToLayout(outer, std::forward<Inner>(inner));
+}
+
+void doit_nested(Widget *outer, auto inner)
+{
+ addToWidget(outer, inner);
+}
+
+void doit_nested(TabWidget *outer, auto inner)
+{
+ addToTabWidget(outer, inner);
+}
+
+void doit_nested(Stack *outer, auto inner)
+{
+ addToStack(outer, inner);
+}
+
+void doit_nested(Splitter *outer, auto inner)
+{
+ addToSplitter(outer, inner);
+}
-template <class T>
-LayoutItem bindTo(T **out)
+template <class Inner>
+void doit(auto outer, NestId, Inner && inner)
{
- return [out](QObject *target) { *out = qobject_cast<T *>(target); };
+ doit_nested(outer, std::forward<Inner>(inner));
}
+// Special layout items
+
+QTCREATOR_UTILS_EXPORT void empty(Layout *);
+QTCREATOR_UTILS_EXPORT void br(Layout *);
+QTCREATOR_UTILS_EXPORT void st(Layout *);
+QTCREATOR_UTILS_EXPORT void noMargin(Layout *);
+QTCREATOR_UTILS_EXPORT void normalMargin(Layout *);
+QTCREATOR_UTILS_EXPORT void withFormAlignment(Layout *);
+QTCREATOR_UTILS_EXPORT void hr(Layout *);
+
+QTCREATOR_UTILS_EXPORT LayoutModifier spacing(int space);
+
+// Convenience
+
+QTCREATOR_UTILS_EXPORT QWidget *createHr(QWidget *parent = nullptr);
} // Layouting
diff --git a/src/libs/utils/lua.cpp b/src/libs/utils/lua.cpp
new file mode 100644
index 0000000000..4f12c626f1
--- /dev/null
+++ b/src/libs/utils/lua.cpp
@@ -0,0 +1,30 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "lua.h"
+
+#include "utilstr.h"
+
+namespace Utils {
+
+static LuaInterface *s_luaInterface = nullptr;
+
+void setLuaInterface(LuaInterface *luaInterface)
+{
+ s_luaInterface = luaInterface;
+}
+
+LuaInterface *luaInterface()
+{
+ return s_luaInterface;
+}
+
+expected_str<std::unique_ptr<LuaState>> runScript(const QString &script, const QString &name)
+{
+ if (!s_luaInterface)
+ return make_unexpected(Tr::tr("No Lua interface set"));
+
+ return s_luaInterface->runScript(script, name);
+}
+
+} // namespace Utils
diff --git a/src/libs/utils/lua.h b/src/libs/utils/lua.h
new file mode 100644
index 0000000000..7a3b8f7833
--- /dev/null
+++ b/src/libs/utils/lua.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include <QObject>
+
+#include "expected.h"
+#include "utils_global.h"
+
+namespace Utils {
+
+class LuaState
+{
+public:
+ virtual ~LuaState() = default;
+};
+
+class QTCREATOR_UTILS_EXPORT LuaInterface
+{
+public:
+ virtual ~LuaInterface() = default;
+ virtual expected_str<std::unique_ptr<LuaState>> runScript(
+ const QString &script, const QString &name)
+ = 0;
+};
+
+QTCREATOR_UTILS_EXPORT void setLuaInterface(LuaInterface *luaInterface);
+QTCREATOR_UTILS_EXPORT LuaInterface *luaInterface();
+
+QTCREATOR_UTILS_EXPORT expected_str<std::unique_ptr<LuaState>> runScript(
+ const QString &script, const QString &name);
+
+} // namespace Utils
diff --git a/src/libs/utils/macroexpander.cpp b/src/libs/utils/macroexpander.cpp
index dd57cd6b5a..fc13897474 100644
--- a/src/libs/utils/macroexpander.cpp
+++ b/src/libs/utils/macroexpander.cpp
@@ -284,7 +284,7 @@ QByteArray MacroExpander::expand(const QByteArray &stringWithVariables) const
QVariant MacroExpander::expandVariant(const QVariant &v) const
{
- const auto type = QMetaType::Type(v.type());
+ const int type = v.typeId();
if (type == QMetaType::QString) {
return expand(v.toString());
} else if (type == QMetaType::QStringList) {
diff --git a/src/libs/utils/macroexpander.h b/src/libs/utils/macroexpander.h
index adde4db1d3..fe98d46872 100644
--- a/src/libs/utils/macroexpander.h
+++ b/src/libs/utils/macroexpander.h
@@ -5,7 +5,6 @@
#include "utils_global.h"
-#include <QCoreApplication>
#include <QList>
#include <functional>
diff --git a/src/libs/utils/namevalueitem.cpp b/src/libs/utils/namevalueitem.cpp
index 3f4cdbd4b2..cb34f8a550 100644
--- a/src/libs/utils/namevalueitem.cpp
+++ b/src/libs/utils/namevalueitem.cpp
@@ -19,6 +19,10 @@ EnvironmentItems EnvironmentItem::fromStringList(const QStringList &list)
{
EnvironmentItems result;
for (const QString &string : list) {
+ if (string.startsWith("##")) {
+ result.append({string.mid(2), {}, EnvironmentItem::Comment});
+ continue;
+ }
int pos = string.indexOf("+=");
if (pos != -1) {
result.append({string.left(pos), string.mid(pos + 2), EnvironmentItem::Append});
@@ -59,6 +63,8 @@ QStringList EnvironmentItem::toStringList(const EnvironmentItems &list)
return QString('#' + item.name + '=' + item.value);
case EnvironmentItem::SetEnabled:
return QString(item.name + '=' + item.value);
+ case EnvironmentItem::Comment:
+ return QString("##" + item.name);
}
return QString();
});
@@ -170,6 +176,8 @@ void EnvironmentItem::apply(NameValueDictionary *dictionary, Operation op) const
apply(dictionary, SetEnabled);
}
} break;
+ case Comment: // ignore comments when applying to environment
+ break;
}
}
@@ -195,6 +203,9 @@ QDebug operator<<(QDebug debug, const EnvironmentItem &i)
case EnvironmentItem::Append:
debug << "append to \"" << i.name << "\":\"" << i.value << '"';
break;
+ case EnvironmentItem::Comment:
+ debug << "comment:" << i.name;
+ break;
}
debug << ')';
return debug;
diff --git a/src/libs/utils/namevalueitem.h b/src/libs/utils/namevalueitem.h
index 6ad5ed0379..c518b17e66 100644
--- a/src/libs/utils/namevalueitem.h
+++ b/src/libs/utils/namevalueitem.h
@@ -16,7 +16,7 @@ namespace Utils {
class QTCREATOR_UTILS_EXPORT EnvironmentItem
{
public:
- enum Operation : char { SetEnabled, Unset, Prepend, Append, SetDisabled };
+ enum Operation : char { SetEnabled, Unset, Prepend, Append, SetDisabled, Comment };
EnvironmentItem() = default;
EnvironmentItem(const QString &key, const QString &value, Operation operation = SetEnabled)
: name(key)
diff --git a/src/libs/utils/namevaluesdialog.cpp b/src/libs/utils/namevaluesdialog.cpp
index 0b009c7568..5a45c54a75 100644
--- a/src/libs/utils/namevaluesdialog.cpp
+++ b/src/libs/utils/namevaluesdialog.cpp
@@ -60,11 +60,12 @@ NameValueItemsWidget::NameValueItemsWidget(QWidget *parent)
const QString helpText = Tr::tr(
"Enter one environment variable per line.\n"
"To set or change a variable, use VARIABLE=VALUE.\n"
+ "To disable a variable, prefix this line with \"#\".\n"
"To append to a variable, use VARIABLE+=VALUE.\n"
"To prepend to a variable, use VARIABLE=+VALUE.\n"
"Existing variables can be referenced in a VALUE with ${OTHER}.\n"
"To clear a variable, put its name on a line with nothing else on it.\n"
- "To disable a variable, prefix the line with \"#\".");
+ "Lines starting with \"##\" will be treated as comments.");
m_editor = new Internal::TextEditHelper(this);
auto layout = new QVBoxLayout(this);
@@ -141,7 +142,7 @@ bool NameValueItemsWidget::editVariable(const QString &name, Selection selection
skipWhiteSpace();
if (offset < line.length()) {
QChar nextChar = line.at(offset);
- if (nextChar.isLetterOrNumber())
+ if (nextChar.isLetterOrNumber() || nextChar == '_')
continue;
if (nextChar == '=') {
if (++offset < line.length() && line.at(offset) == '+')
diff --git a/src/libs/utils/osspecificaspects.h b/src/libs/utils/osspecificaspects.h
index c735f313ab..239290faec 100644
--- a/src/libs/utils/osspecificaspects.h
+++ b/src/libs/utils/osspecificaspects.h
@@ -14,6 +14,8 @@ namespace Utils {
// Add more as needed.
enum OsType { OsTypeWindows, OsTypeLinux, OsTypeMac, OsTypeOtherUnix, OsTypeOther };
+enum OsArch { OsArchUnknown, OsArchX86, OsArchAMD64, OsArchItanium, OsArchArm, OsArchArm64 };
+
inline QString osTypeToString(OsType osType)
{
switch (osType) {
@@ -33,17 +35,33 @@ inline QString osTypeToString(OsType osType)
inline OsType osTypeFromString(const QString &string)
{
- if (string == "Windows")
+ if (string.compare("windows", Qt::CaseInsensitive) == 0)
return OsTypeWindows;
- if (string == "Linux")
+ if (string.compare("linux", Qt::CaseInsensitive) == 0)
return OsTypeLinux;
- if (string == "Mac")
+ if (string.compare("mac", Qt::CaseInsensitive) == 0
+ || string.compare("darwin", Qt::CaseInsensitive) == 0)
return OsTypeMac;
- if (string == "Other Unix")
+ if (string.compare("other unix", Qt::CaseInsensitive) == 0)
return OsTypeOtherUnix;
return OsTypeOther;
}
+inline OsArch osArchFromString(const QString &architecture)
+{
+ if (architecture == QLatin1String("x86_64"))
+ return OsArchAMD64;
+ if (architecture == QLatin1String("x86"))
+ return OsArchX86;
+ if (architecture == QLatin1String("ia64"))
+ return OsArchItanium;
+ if (architecture == QLatin1String("arm"))
+ return OsArchArm;
+ if (architecture == QLatin1String("arm64"))
+ return OsArchArm64;
+ return OsArchUnknown;
+}
+
namespace OsSpecificAspects {
inline QString withExecutableSuffix(OsType osType, const QString &executable)
diff --git a/src/libs/utils/outputformatter.cpp b/src/libs/utils/outputformatter.cpp
index f8b3d8cec7..d36662f4d3 100644
--- a/src/libs/utils/outputformatter.cpp
+++ b/src/libs/utils/outputformatter.cpp
@@ -58,7 +58,7 @@ Link OutputLineParser::parseLinkTarget(const QString &target)
return {};
return Link(FilePath::fromString(parts.first()),
parts.length() > 1 ? parts.at(1).toInt() : 0,
- parts.length() > 2 ? parts.at(2).toInt() : 0);
+ parts.length() > 2 ? parts.at(2).toInt() - 1 : 0);
}
// The redirection mechanism is needed for broken build tools (e.g. xcodebuild) that get invoked
@@ -141,26 +141,39 @@ FilePath OutputLineParser::absoluteFilePath(const FilePath &filePath) const
return filePath;
}
-void OutputLineParser::addLinkSpecForAbsoluteFilePath(OutputLineParser::LinkSpecs &linkSpecs,
- const FilePath &filePath, int lineNo, int pos, int len)
+void OutputLineParser::addLinkSpecForAbsoluteFilePath(
+ OutputLineParser::LinkSpecs &linkSpecs,
+ const FilePath &filePath,
+ int lineNo,
+ int column,
+ int pos,
+ int len)
{
if (filePath.toFileInfo().isAbsolute())
- linkSpecs.append({pos, len, createLinkTarget(filePath, lineNo)});
+ linkSpecs.append({pos, len, createLinkTarget(filePath, lineNo, column)});
}
-void OutputLineParser::addLinkSpecForAbsoluteFilePath(OutputLineParser::LinkSpecs &linkSpecs,
- const FilePath &filePath, int lineNo, const QRegularExpressionMatch &match,
- int capIndex)
+void OutputLineParser::addLinkSpecForAbsoluteFilePath(
+ OutputLineParser::LinkSpecs &linkSpecs,
+ const FilePath &filePath,
+ int lineNo,
+ int column,
+ const QRegularExpressionMatch &match,
+ int capIndex)
{
- addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match.capturedStart(capIndex),
- match.capturedLength(capIndex));
+ addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, column,
+ match.capturedStart(capIndex), match.capturedLength(capIndex));
}
-void OutputLineParser::addLinkSpecForAbsoluteFilePath(OutputLineParser::LinkSpecs &linkSpecs,
- const FilePath &filePath, int lineNo, const QRegularExpressionMatch &match,
- const QString &capName)
+void OutputLineParser::addLinkSpecForAbsoluteFilePath(
+ OutputLineParser::LinkSpecs &linkSpecs,
+ const FilePath &filePath,
+ int lineNo,
+ int column,
+ const QRegularExpressionMatch &match,
+ const QString &capName)
{
- addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, match.capturedStart(capName),
- match.capturedLength(capName));
+ addLinkSpecForAbsoluteFilePath(linkSpecs, filePath, lineNo, column,
+ match.capturedStart(capName), match.capturedLength(capName));
}
bool Utils::OutputLineParser::fileExists(const FilePath &fp) const
@@ -342,6 +355,7 @@ OutputLineParser::Result OutputFormatter::handleMessage(const QString &text, Out
= d->nextParser->handleLine(text, outputTypeForParser(d->nextParser, format));
switch (res.status) {
case OutputLineParser::Status::Done:
+ d->nextParser->flush();
d->nextParser = nullptr;
return res;
case OutputLineParser::Status::InProgress:
@@ -359,6 +373,7 @@ OutputLineParser::Result OutputFormatter::handleMessage(const QString &text, Out
= parser->handleLine(text, outputTypeForParser(parser, format));
switch (res.status) {
case OutputLineParser::Status::Done:
+ parser->flush();
involvedParsers << parser;
return res;
case OutputLineParser::Status::InProgress:
@@ -402,23 +417,32 @@ const QList<FormattedText> OutputFormatter::linkifiedText(
}
for (int nextLocalTextPos = 0; nextLocalTextPos < t.text.size(); ) {
-
- // There are no more links in this part, so copy the rest of the text as-is.
- if (nextLinkSpecIndex >= linkSpecs.size()) {
+ const auto copyRestOfSegmentAsIs = [&] {
linkified << FormattedText(t.text.mid(nextLocalTextPos), t.format);
totalTextLengthSoFar += t.text.length() - nextLocalTextPos;
+ };
+
+ // We are out of links.
+ if (nextLinkSpecIndex >= linkSpecs.size()) {
+ copyRestOfSegmentAsIs();
break;
}
const OutputLineParser::LinkSpec &linkSpec = linkSpecs.at(nextLinkSpecIndex);
const int localLinkStartPos = linkSpec.startPos - totalPreviousTextLength;
+
+ // There are more links, but not in this segment.
+ if (localLinkStartPos >= t.text.size()) {
+ copyRestOfSegmentAsIs();
+ break;
+ }
+
++nextLinkSpecIndex;
// We ignore links that would cross format boundaries.
if (localLinkStartPos < nextLocalTextPos
|| localLinkStartPos + linkSpec.length > t.text.length()) {
- linkified << FormattedText(t.text.mid(nextLocalTextPos), t.format);
- totalTextLengthSoFar += t.text.length() - nextLocalTextPos;
+ copyRestOfSegmentAsIs();
break;
}
@@ -456,7 +480,7 @@ void OutputFormatter::append(const QString &text, const QTextCharFormat &format)
QTextCharFormat OutputFormatter::linkFormat(const QTextCharFormat &inputFormat, const QString &href)
{
QTextCharFormat result = inputFormat;
- result.setForeground(creatorTheme()->color(Theme::TextColorLink));
+ result.setForeground(creatorColor(Theme::TextColorLink));
result.setUnderlineStyle(QTextCharFormat::SingleUnderline);
result.setAnchor(true);
result.setAnchorHref(href);
@@ -491,14 +515,13 @@ void OutputFormatter::initFormats()
if (!plainTextEdit())
return;
- Theme *theme = creatorTheme();
- d->formats[NormalMessageFormat].setForeground(theme->color(Theme::OutputPanes_NormalMessageTextColor));
- d->formats[ErrorMessageFormat].setForeground(theme->color(Theme::OutputPanes_ErrorMessageTextColor));
- d->formats[LogMessageFormat].setForeground(theme->color(Theme::OutputPanes_WarningMessageTextColor));
- d->formats[StdOutFormat].setForeground(theme->color(Theme::OutputPanes_StdOutTextColor));
- d->formats[StdErrFormat].setForeground(theme->color(Theme::OutputPanes_StdErrTextColor));
- d->formats[DebugFormat].setForeground(theme->color(Theme::OutputPanes_DebugTextColor));
- d->formats[GeneralMessageFormat].setForeground(theme->color(Theme::OutputPanes_DebugTextColor));
+ d->formats[NormalMessageFormat].setForeground(creatorColor(Theme::OutputPanes_NormalMessageTextColor));
+ d->formats[ErrorMessageFormat].setForeground(creatorColor(Theme::OutputPanes_ErrorMessageTextColor));
+ d->formats[LogMessageFormat].setForeground(creatorColor(Theme::OutputPanes_WarningMessageTextColor));
+ d->formats[StdOutFormat].setForeground(creatorColor(Theme::OutputPanes_StdOutTextColor));
+ d->formats[StdErrFormat].setForeground(creatorColor(Theme::OutputPanes_StdErrTextColor));
+ d->formats[DebugFormat].setForeground(creatorColor(Theme::OutputPanes_DebugTextColor));
+ d->formats[GeneralMessageFormat].setForeground(creatorColor(Theme::OutputPanes_DebugTextColor));
setBoldFontEnabled(d->boldFontEnabled);
}
diff --git a/src/libs/utils/outputformatter.h b/src/libs/utils/outputformatter.h
index d26dc64327..da4dfe8a3a 100644
--- a/src/libs/utils/outputformatter.h
+++ b/src/libs/utils/outputformatter.h
@@ -93,12 +93,13 @@ protected:
Utils::FilePath absoluteFilePath(const Utils::FilePath &filePath) const;
static QString createLinkTarget(const FilePath &filePath, int line, int column);
static void addLinkSpecForAbsoluteFilePath(LinkSpecs &linkSpecs, const FilePath &filePath,
- int lineNo, int pos, int len);
+ int lineNo, int column, int pos, int len);
static void addLinkSpecForAbsoluteFilePath(LinkSpecs &linkSpecs, const FilePath &filePath,
- int lineNo, const QRegularExpressionMatch &match,
- int capIndex);
+ int lineNo, int column,
+ const QRegularExpressionMatch &match, int capIndex);
static void addLinkSpecForAbsoluteFilePath(LinkSpecs &linkSpecs, const FilePath &filePath,
- int lineNo, const QRegularExpressionMatch &match,
+ int lineNo, int column,
+ const QRegularExpressionMatch &match,
const QString &capName);
bool fileExists(const Utils::FilePath &fp) const;
diff --git a/src/libs/utils/overlaywidget.cpp b/src/libs/utils/overlaywidget.cpp
index 720c5a90e1..950dc4b5dc 100644
--- a/src/libs/utils/overlaywidget.cpp
+++ b/src/libs/utils/overlaywidget.cpp
@@ -8,16 +8,34 @@
#include <QEvent>
#include <QPainter>
+namespace Utils::Internal {
+class OverlayWidgetPrivate
+{
+public:
+ OverlayWidget::PaintFunction m_paint;
+ OverlayWidget::ResizeFunction m_resize;
+};
+} // namespace Utils::Internal
+
Utils::OverlayWidget::OverlayWidget(QWidget *parent)
+ : d(new Internal::OverlayWidgetPrivate)
{
setAttribute(Qt::WA_TransparentForMouseEvents);
if (parent)
attachToWidget(parent);
+ d->m_resize = [](QWidget *w, const QSize &size) { w->setGeometry(QRect(QPoint(0, 0), size)); };
}
+Utils::OverlayWidget::~OverlayWidget() = default;
+
void Utils::OverlayWidget::setPaintFunction(const Utils::OverlayWidget::PaintFunction &paint)
{
- m_paint = paint;
+ d->m_paint = paint;
+}
+
+void Utils::OverlayWidget::setResizeFunction(const ResizeFunction &resize)
+{
+ d->m_resize = resize;
}
bool Utils::OverlayWidget::eventFilter(QObject *obj, QEvent *ev)
@@ -29,9 +47,9 @@ bool Utils::OverlayWidget::eventFilter(QObject *obj, QEvent *ev)
void Utils::OverlayWidget::paintEvent(QPaintEvent *ev)
{
- if (m_paint) {
+ if (d->m_paint) {
QPainter p(this);
- m_paint(this, p, ev);
+ d->m_paint(this, p, ev);
}
}
@@ -50,5 +68,6 @@ void Utils::OverlayWidget::attachToWidget(QWidget *parent)
void Utils::OverlayWidget::resizeToParent()
{
QTC_ASSERT(parentWidget(), return );
- setGeometry(QRect(QPoint(0, 0), parentWidget()->size()));
+ if (d->m_resize)
+ d->m_resize(this, parentWidget()->size());
}
diff --git a/src/libs/utils/overlaywidget.h b/src/libs/utils/overlaywidget.h
index 8bc5b7d1eb..c5686ad171 100644
--- a/src/libs/utils/overlaywidget.h
+++ b/src/libs/utils/overlaywidget.h
@@ -8,18 +8,26 @@
#include <QWidget>
#include <functional>
+#include <memory>
namespace Utils {
+namespace Internal {
+class OverlayWidgetPrivate;
+}
+
class QTCREATOR_UTILS_EXPORT OverlayWidget : public QWidget
{
public:
using PaintFunction = std::function<void(QWidget *, QPainter &, QPaintEvent *)>;
+ using ResizeFunction = std::function<void(QWidget *, QSize)>;
explicit OverlayWidget(QWidget *parent = nullptr);
+ ~OverlayWidget();
void attachToWidget(QWidget *parent);
void setPaintFunction(const PaintFunction &paint);
+ void setResizeFunction(const ResizeFunction &resize);
protected:
bool eventFilter(QObject *obj, QEvent *ev) override;
@@ -28,7 +36,7 @@ protected:
private:
void resizeToParent();
- PaintFunction m_paint;
+ std::unique_ptr<Internal::OverlayWidgetPrivate> d;
};
} // namespace Utils
diff --git a/src/libs/utils/passworddialog.cpp b/src/libs/utils/passworddialog.cpp
index 7958f7d6bd..94de5f7f0f 100644
--- a/src/libs/utils/passworddialog.cpp
+++ b/src/libs/utils/passworddialog.cpp
@@ -34,10 +34,9 @@ void ShowPasswordButton::paintEvent(QPaintEvent *e)
QRect r(QPoint(), size());
if (m_containsMouse && isEnabled())
- StyleHelper::drawPanelBgRect(&p, r, creatorTheme()->color(Theme::FancyToolButtonHoverColor));
+ StyleHelper::drawPanelBgRect(&p, r, creatorColor(Theme::FancyToolButtonHoverColor));
- QWindow *window = this->window()->windowHandle();
- QSize s = icon.actualSize(window, QSize(32, 16));
+ QSize s = icon.actualSize(QSize(32, 16));
QPixmap px = icon.pixmap(s);
QRect iRect(QPoint(), s);
@@ -61,8 +60,7 @@ void ShowPasswordButton::leaveEvent(QEvent *e)
QSize ShowPasswordButton::sizeHint() const
{
- QWindow *window = this->window()->windowHandle();
- QSize s = Utils::Icons::EYE_OPEN_TOOLBAR.icon().actualSize(window, QSize(32, 16)) + QSize(8, 8);
+ QSize s = Utils::Icons::EYE_OPEN_TOOLBAR.icon().actualSize(QSize(32, 16)) + QSize(8, 8);
if (StyleHelper::toolbarStyle() == StyleHelper::ToolbarStyleRelaxed)
s += QSize(5, 5);
diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp
index 437b94bb5c..9b2177fedb 100644
--- a/src/libs/utils/pathchooser.cpp
+++ b/src/libs/utils/pathchooser.cpp
@@ -5,19 +5,21 @@
#include "async.h"
#include "commandline.h"
+#include "datafromprocess.h"
#include "environment.h"
#include "fileutils.h"
#include "guard.h"
#include "hostosinfo.h"
#include "macroexpander.h"
#include "optionpushbutton.h"
-#include "process.h"
+#include "qtcprocess.h"
#include "qtcassert.h"
#include "utilstr.h"
#include <QFileDialog>
#include <QFuture>
#include <QGuiApplication>
+#include <QHelpEvent>
#include <QHBoxLayout>
#include <QMenu>
#include <QPushButton>
@@ -84,8 +86,6 @@ public:
QStringList arguments() const { return m_arguments; }
void setArguments(const QStringList &arguments) { m_arguments = arguments; }
- static QString toolVersion(const CommandLine &cmd);
-
private:
// Extension point for concatenating existing tooltips.
virtual QString defaultToolTip() const { return QString(); }
@@ -107,38 +107,40 @@ bool BinaryVersionToolTipEventFilter::eventFilter(QObject *o, QEvent *e)
QTC_ASSERT(le, return false);
const QString binary = le->text();
- if (!binary.isEmpty()) {
- const QString version = BinaryVersionToolTipEventFilter::toolVersion(
- CommandLine(FilePath::fromString(QDir::cleanPath(binary)), m_arguments));
- if (!version.isEmpty()) {
- // Concatenate tooltips.
- QString tooltip = "<html><head/><body>";
- const QString defaultValue = defaultToolTip();
- if (!defaultValue.isEmpty()) {
- tooltip += "<p>";
- tooltip += defaultValue;
- tooltip += "</p>";
- }
- tooltip += "<pre>";
- tooltip += version;
- tooltip += "</pre><body></html>";
- le->setToolTip(tooltip);
+ DataFromProcess<QString>::Parameters params(CommandLine(FilePath::fromUserInput(binary),
+ m_arguments),
+ [](const QString &output) { return output; });
+ params.callback = [binary, self = QPointer(this),
+ le = QPointer(le)](const std::optional<QString> &version) {
+ if (!self || !le)
+ return;
+ if (!version || version->isEmpty())
+ return;
+ if (binary != le->text())
+ return;
+
+ // Concatenate tooltips.
+ QString tooltip = "<html><head/><body>";
+ const QString defaultValue = self->defaultToolTip();
+ if (!defaultValue.isEmpty()) {
+ tooltip += "<p>";
+ tooltip += defaultValue;
+ tooltip += "</p>";
}
- }
- return false;
-}
+ tooltip += "<pre>";
+ tooltip += *version;
+ tooltip += "</pre><body></html>";
+ le->setToolTip(tooltip);
+ if (QRect(le->mapToGlobal(QPoint(0, 0)), le->size()).contains(QCursor::pos())) {
+ QCoreApplication::postEvent(le,
+ new QHelpEvent(QEvent::ToolTip,
+ le->mapFromGlobal(QCursor::pos()),
+ QCursor::pos()));
+ }
+ };
+ DataFromProcess<QString>::provideData(params);
-QString BinaryVersionToolTipEventFilter::toolVersion(const CommandLine &cmd)
-{
- if (cmd.executable().isEmpty())
- return QString();
- Process proc;
- proc.setCommand(cmd);
- using namespace std::chrono_literals;
- proc.runBlocking(1s);
- if (proc.result() != ProcessResult::FinishedWithSuccess)
- return QString();
- return proc.allOutput();
+ return false;
}
// Extends BinaryVersionToolTipEventFilter to prepend the existing pathchooser
@@ -724,11 +726,6 @@ FancyLineEdit *PathChooser::lineEdit() const
return d->m_lineEdit;
}
-QString PathChooser::toolVersion(const CommandLine &cmd)
-{
- return BinaryVersionToolTipEventFilter::toolVersion(cmd);
-}
-
void PathChooser::installLineEditVersionToolTip(QLineEdit *le, const QStringList &arguments)
{
auto ef = new BinaryVersionToolTipEventFilter(le);
diff --git a/src/libs/utils/pathchooser.h b/src/libs/utils/pathchooser.h
index a91a853938..a3e259a748 100644
--- a/src/libs/utils/pathchooser.h
+++ b/src/libs/utils/pathchooser.h
@@ -99,8 +99,6 @@ public:
QStringList commandVersionArguments() const;
void setCommandVersionArguments(const QStringList &arguments);
- // Utility to run a tool and return its stdout.
- static QString toolVersion(const CommandLine &cmd);
// Install a tooltip on lineedits used for binaries showing the version.
static void installLineEditVersionToolTip(QLineEdit *le, const QStringList &arguments);
diff --git a/src/libs/utils/persistentsettings.cpp b/src/libs/utils/persistentsettings.cpp
index 54a829004b..3702ecf960 100644
--- a/src/libs/utils/persistentsettings.cpp
+++ b/src/libs/utils/persistentsettings.cpp
@@ -106,13 +106,13 @@ const QString keyAttribute("key");
struct ParseValueStackEntry
{
- explicit ParseValueStackEntry(QVariant::Type t = QVariant::Invalid, const QString &k = {}) : type(t), key(k) {}
+ explicit ParseValueStackEntry(QMetaType::Type t = QMetaType::UnknownType, const QString &k = {}) : typeId(t), key(k) {}
explicit ParseValueStackEntry(const QVariant &aSimpleValue, const QString &k);
QVariant value() const;
void addChild(const QString &key, const QVariant &v);
- QVariant::Type type;
+ QMetaType::Type typeId;
QString key;
QVariant simpleValue;
QVariantList listValue;
@@ -120,19 +120,19 @@ struct ParseValueStackEntry
};
ParseValueStackEntry::ParseValueStackEntry(const QVariant &aSimpleValue, const QString &k)
- : type(aSimpleValue.type()), key(k), simpleValue(aSimpleValue)
+ : typeId(QMetaType::Type(aSimpleValue.typeId())), key(k), simpleValue(aSimpleValue)
{
QTC_ASSERT(simpleValue.isValid(), return);
}
QVariant ParseValueStackEntry::value() const
{
- switch (type) {
- case QVariant::Invalid:
+ switch (typeId) {
+ case QMetaType::UnknownType:
return QVariant();
- case QVariant::Map:
+ case QMetaType::QVariantMap:
return QVariant(mapValue);
- case QVariant::List:
+ case QMetaType::QVariantList:
return QVariant(listValue);
default:
break;
@@ -142,16 +142,16 @@ QVariant ParseValueStackEntry::value() const
void ParseValueStackEntry::addChild(const QString &key, const QVariant &v)
{
- switch (type) {
- case QVariant::Map:
+ switch (typeId) {
+ case QMetaType::QVariantMap:
mapValue.insert(key, v);
break;
- case QVariant::List:
+ case QMetaType::QVariantList:
listValue.push_back(v);
break;
default:
qWarning() << "ParseValueStackEntry::Internal error adding " << key << v << " to "
- << QVariant::typeToName(type) << value();
+ << QMetaType(typeId).name() << value();
break;
}
}
@@ -226,14 +226,14 @@ bool ParseContext::handleStartElement(QXmlStreamReader &r)
const QXmlStreamAttributes attributes = r.attributes();
const QString key = attributes.hasAttribute(keyAttribute) ?
attributes.value(keyAttribute).toString() : QString();
- m_valueStack.push_back(ParseValueStackEntry(QVariant::List, key));
+ m_valueStack.push_back(ParseValueStackEntry(QMetaType::QVariantList, key));
return false;
}
if (name == valueMapElement) {
const QXmlStreamAttributes attributes = r.attributes();
const QString key = attributes.hasAttribute(keyAttribute) ?
attributes.value(keyAttribute).toString() : QString();
- m_valueStack.push_back(ParseValueStackEntry(QVariant::Map, key));
+ m_valueStack.push_back(ParseValueStackEntry(QMetaType::QVariantMap, key));
return false;
}
return false;
@@ -369,8 +369,8 @@ static void writeVariantValue(QXmlStreamWriter &w, const QVariant &variant, cons
w.writeAttribute(typeAttribute, QLatin1String(variant.typeName()));
if (!key.isEmpty())
w.writeAttribute(keyAttribute, xmlAttrFromKey(key));
- switch (variant.type()) {
- case QVariant::Rect:
+ switch (variant.typeId()) {
+ case QMetaType::QRect:
w.writeCharacters(rectangleToString(variant.toRect()));
break;
default:
diff --git a/src/libs/utils/processhandle_mac.mm b/src/libs/utils/processhandle_mac.mm
index 52cc0d3e07..f492dae37d 100644
--- a/src/libs/utils/processhandle_mac.mm
+++ b/src/libs/utils/processhandle_mac.mm
@@ -10,8 +10,7 @@ bool ProcessHandle::activate()
{
NSRunningApplication *app = [NSRunningApplication
runningApplicationWithProcessIdentifier:pid()];
- return app && [app activateWithOptions:static_cast<NSApplicationActivationOptions>(
- NSApplicationActivateIgnoringOtherApps)];
+ return app && [app activateWithOptions:NSApplicationActivateAllWindows];
}
} // Utils
diff --git a/src/libs/utils/processhelper.cpp b/src/libs/utils/processhelper.cpp
index 6045b77bec..7e7f34cf42 100644
--- a/src/libs/utils/processhelper.cpp
+++ b/src/libs/utils/processhelper.cpp
@@ -55,12 +55,13 @@ void ProcessStartHandler::setNativeArguments(const QString &arguments)
#endif // Q_OS_WIN
}
-void ProcessStartHandler::setWindowsSpecificStartupFlags(bool belowNormalPriority,
- bool createConsoleWindow)
+void ProcessStartHandler::setWindowsSpecificStartupFlags(
+ bool belowNormalPriority, bool createConsoleWindow, bool forceDefaultErrorMode)
{
#ifdef Q_OS_WIN
m_process->setCreateProcessArgumentsModifier(
- [belowNormalPriority, createConsoleWindow](QProcess::CreateProcessArguments *args) {
+ [belowNormalPriority, createConsoleWindow, forceDefaultErrorMode](
+ QProcess::CreateProcessArguments *args) {
if (createConsoleWindow) {
args->flags |= CREATE_NEW_CONSOLE;
args->startupInfo->dwFlags &= ~STARTF_USESTDHANDLES;
@@ -69,11 +70,13 @@ void ProcessStartHandler::setWindowsSpecificStartupFlags(bool belowNormalPriorit
if (belowNormalPriority)
args->flags |= BELOW_NORMAL_PRIORITY_CLASS;
- args->flags |= CREATE_DEFAULT_ERROR_MODE;
+ if (forceDefaultErrorMode)
+ args->flags |= CREATE_DEFAULT_ERROR_MODE;
});
#else // Q_OS_WIN
Q_UNUSED(belowNormalPriority)
Q_UNUSED(createConsoleWindow)
+ Q_UNUSED(forceDefaultErrorMode)
#endif
}
diff --git a/src/libs/utils/processhelper.h b/src/libs/utils/processhelper.h
index a4efc17ae5..7c40cb3faa 100644
--- a/src/libs/utils/processhelper.h
+++ b/src/libs/utils/processhelper.h
@@ -20,7 +20,8 @@ public:
void handleProcessStart();
void handleProcessStarted();
void setNativeArguments(const QString &arguments);
- void setWindowsSpecificStartupFlags(bool belowNormalPriority, bool createConsoleWindow);
+ void setWindowsSpecificStartupFlags(
+ bool belowNormalPriority, bool createConsoleWindow, bool forceDefaultErrorMode);
private:
ProcessMode m_processMode = ProcessMode::Reader;
diff --git a/src/libs/utils/processinfo.cpp b/src/libs/utils/processinfo.cpp
index 499745473e..0717bddbe9 100644
--- a/src/libs/utils/processinfo.cpp
+++ b/src/libs/utils/processinfo.cpp
@@ -4,7 +4,7 @@
#include "processinfo.h"
#include "algorithm.h"
-#include "process.h"
+#include "qtcprocess.h"
#include <QDir>
#include <QRegularExpression>
diff --git a/src/libs/utils/processinterface.h b/src/libs/utils/processinterface.h
index 1e233b88ab..4d230c4ef9 100644
--- a/src/libs/utils/processinterface.h
+++ b/src/libs/utils/processinterface.h
@@ -94,6 +94,7 @@ public:
bool m_useCtrlCStub = false;
bool m_belowNormalPriority = false; // internal, dependent on other fields and specific code path
bool m_createConsoleOnWindows = false;
+ bool m_forceDefaultErrorMode = false;
};
class QTCREATOR_UTILS_EXPORT ProcessResultData
diff --git a/src/libs/utils/projectintropage.cpp b/src/libs/utils/projectintropage.cpp
index 1f32cce6bc..2c8b43b66a 100644
--- a/src/libs/utils/projectintropage.cpp
+++ b/src/libs/utils/projectintropage.cpp
@@ -110,7 +110,8 @@ ProjectIntroPage::ProjectIntroPage(QWidget *parent) :
using namespace Layouting;
Form {
- Tr::tr("Name:"), d->m_nameLineEdit, br,
+ Tr::tr("Name:"), d->m_nameLineEdit,
+ br,
d->m_projectLabel, d->m_projectComboBox, br,
Column { Space(12) }, br,
Tr::tr("Create in:"), d->m_pathChooser, br,
@@ -135,7 +136,7 @@ ProjectIntroPage::ProjectIntroPage(QWidget *parent) :
connect(d->m_nameLineEdit, &FancyLineEdit::validReturnPressed,
this, &ProjectIntroPage::slotActivated);
connect(d->m_projectComboBox, &QComboBox::currentIndexChanged,
- this, &ProjectIntroPage::slotChanged);
+ this, &ProjectIntroPage::onCurrentProjectIndexChanged);
setProperty(SHORT_TITLE_PROPERTY, Tr::tr("Location"));
registerFieldWithName(QLatin1String("Path"), d->m_pathChooser, "path", SIGNAL(textChanged(QString)));
@@ -192,15 +193,10 @@ bool ProjectIntroPage::isComplete() const
bool ProjectIntroPage::validate()
{
- if (d->m_forceSubProject) {
- int index = d->m_projectComboBox->currentIndex();
- if (index == 0)
- return false;
- d->m_pathChooser->setFilePath(d->m_projectDirectories.at(index));
- }
// Validate and display status
if (!d->m_pathChooser->isValid()) {
- displayStatusMessage(InfoLabel::Error, d->m_pathChooser->errorMessage());
+ if (const QString msg = d->m_pathChooser->errorMessage(); !msg.isEmpty())
+ displayStatusMessage(InfoLabel::Error, msg);
return false;
}
@@ -259,6 +255,25 @@ void ProjectIntroPage::slotActivated()
emit activated();
}
+void ProjectIntroPage::onCurrentProjectIndexChanged(int index)
+{
+ if (d->m_forceSubProject) {
+ const int available = d->m_projectDirectories.size();
+ if (available == 0)
+ return;
+ QTC_ASSERT(index < available, return);
+ if (index < 0)
+ return;
+
+ const FilePath current = d->m_projectDirectories.at(index);
+ const FilePath visible = d->m_pathChooser->filePath();
+ if (visible != current && !visible.isChildOf(current))
+ d->m_pathChooser->setFilePath(current);
+
+ fieldsUpdated();
+ }
+}
+
bool ProjectIntroPage::forceSubProject() const
{
return d->m_forceSubProject;
diff --git a/src/libs/utils/projectintropage.h b/src/libs/utils/projectintropage.h
index 48b2f6b036..562b5b06e4 100644
--- a/src/libs/utils/projectintropage.h
+++ b/src/libs/utils/projectintropage.h
@@ -58,6 +58,7 @@ public slots:
private:
void slotChanged();
void slotActivated();
+ void onCurrentProjectIndexChanged(int index);
bool validate();
void displayStatusMessage(InfoLabel::InfoType t, const QString &);
diff --git a/src/libs/utils/qtcolorbutton.cpp b/src/libs/utils/qtcolorbutton.cpp
index 6a8537bd5f..5be9c9c069 100644
--- a/src/libs/utils/qtcolorbutton.cpp
+++ b/src/libs/utils/qtcolorbutton.cpp
@@ -171,7 +171,7 @@ void QtColorButton::paintEvent(QPaintEvent *event)
constexpr int size = 11;
const qreal horPadding = (width() - size) / 2.0;
const qreal verPadding = (height() - size) / 2.0;
- const QPen pen(creatorTheme()->color(overlayColor), 2);
+ const QPen pen(creatorColor(overlayColor), 2);
p.save();
p.setOpacity(overlayOpacity);
@@ -202,7 +202,7 @@ void QtColorButton::paintEvent(QPaintEvent *event)
p.setPen(pen);
p.setCompositionMode(QPainter::CompositionMode_Difference);
} else {
- p.setPen(creatorTheme()->color(overlayColor));
+ p.setPen(creatorColor(overlayColor));
p.setOpacity(overlayOpacity);
}
p.drawRect(rect().adjusted(0, 0, -1, -1));
diff --git a/src/libs/utils/process.cpp b/src/libs/utils/qtcprocess.cpp
index d97fea174d..5e0918ed70 100644
--- a/src/libs/utils/process.cpp
+++ b/src/libs/utils/qtcprocess.cpp
@@ -1,7 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#include "process.h"
+#include "qtcprocess.h"
#include "algorithm.h"
#include "environment.h"
@@ -511,7 +511,8 @@ private:
handler->setWriteData(m_setup.m_writeData);
handler->setNativeArguments(m_setup.m_nativeArguments);
handler->setWindowsSpecificStartupFlags(m_setup.m_belowNormalPriority,
- m_setup.m_createConsoleOnWindows);
+ m_setup.m_createConsoleOnWindows,
+ m_setup.m_forceDefaultErrorMode);
const QProcessEnvironment penv = m_setup.m_environment.toProcessEnvironment();
if (!penv.isEmpty())
@@ -1360,7 +1361,7 @@ QString Process::toStandaloneCommandLine() const
parts.append("/usr/bin/env");
if (!d->m_setup.m_workingDirectory.isEmpty()) {
parts.append("-C");
- d->m_setup.m_workingDirectory.path();
+ parts.append(d->m_setup.m_workingDirectory.path());
}
parts.append("-i");
if (d->m_setup.m_environment.hasChanges()) {
@@ -1383,6 +1384,16 @@ bool Process::createConsoleOnWindows() const
return d->m_setup.m_createConsoleOnWindows;
}
+void Process::setForceDefaultErrorModeOnWindows(bool force)
+{
+ d->m_setup.m_forceDefaultErrorMode = force;
+}
+
+bool Process::forceDefaultErrorModeOnWindows() const
+{
+ return d->m_setup.m_forceDefaultErrorMode;
+}
+
void Process::setExtraData(const QString &key, const QVariant &value)
{
d->m_setup.m_extraData.insert(key, value);
@@ -2181,4 +2192,4 @@ void ProcessTaskAdapter::start()
} // namespace Utils
-#include "process.moc"
+#include "qtcprocess.moc"
diff --git a/src/libs/utils/process.h b/src/libs/utils/qtcprocess.h
index 694c1a12a8..3f043214f9 100644
--- a/src/libs/utils/process.h
+++ b/src/libs/utils/qtcprocess.h
@@ -1,11 +1,7 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-#if defined(Q_CC_MINGW) && defined(WIN_PTHREADS_H) && !defined(_INC_PROCESS)
- // Arrived here via <pthread.h> which wants to include <process.h>
- #include_next <process.h>
-#elif !defined(UTILS_PROCESS_H)
-#define UTILS_PROCESS_H
+#pragma once
#include "utils_global.h"
@@ -191,6 +187,9 @@ public:
void setCreateConsoleOnWindows(bool create);
bool createConsoleOnWindows() const;
+ void setForceDefaultErrorModeOnWindows(bool force);
+ bool forceDefaultErrorModeOnWindows() const;
+
signals:
void starting(); // On NotRunning -> Starting state transition
void started(); // On Starting -> Running state transition
@@ -223,5 +222,3 @@ public:
using ProcessTask = Tasking::CustomTask<ProcessTaskAdapter>;
} // namespace Utils
-
-#endif // UTILS_PROCESS_H
diff --git a/src/libs/utils/ranges.h b/src/libs/utils/ranges.h
index 8b574e08d6..825b4da41b 100644
--- a/src/libs/utils/ranges.h
+++ b/src/libs/utils/ranges.h
@@ -3,7 +3,7 @@
#pragma once
-#if __cplusplus >= 202002L
+#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 201911L
#include <ranges>
namespace Utils {
diff --git a/src/libs/utils/smallstring.h b/src/libs/utils/smallstring.h
index c424e9e1ab..e8c3d74b92 100644
--- a/src/libs/utils/smallstring.h
+++ b/src/libs/utils/smallstring.h
@@ -93,7 +93,7 @@ public:
static_cast<std::size_t>(std::distance(begin, end))}
{}
- template<typename Type, typename = std::enable_if_t<std::is_pointer<Type>::value>>
+ template<typename Type, typename std::enable_if_t<std::is_pointer<Type>::value, bool> = true>
BasicSmallString(Type characterPointer) noexcept
: BasicSmallString(characterPointer, std::char_traits<char>::length(characterPointer))
{
@@ -118,7 +118,7 @@ public:
template<typename BeginIterator,
typename EndIterator,
- typename = std::enable_if_t<std::is_same<BeginIterator, EndIterator>::value>>
+ typename std::enable_if_t<std::is_same<BeginIterator, EndIterator>::value, bool> = true>
BasicSmallString(BeginIterator begin, EndIterator end) noexcept
: BasicSmallString(&(*begin), size_type(end - begin))
{}
@@ -200,12 +200,10 @@ public:
operator SmallStringView() const noexcept { return SmallStringView(data(), size()); }
- #if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
explicit operator QLatin1StringView() const noexcept
{
return QLatin1StringView(data(), Utils::ssize(*this));
}
-#endif
operator QUtf8StringView() const noexcept
{
@@ -356,6 +354,14 @@ public:
return false;
}
+ bool startsWith(QStringView subStringToSearch) const noexcept
+ {
+ if (size() >= Utils::usize(subStringToSearch))
+ return subStringToSearch == QLatin1StringView{data(), subStringToSearch.size()};
+
+ return false;
+ }
+
bool startsWith(char characterToSearch) const noexcept
{
return data()[0] == characterToSearch;
@@ -425,13 +431,55 @@ public:
size_type oldSize = size();
size_type newSize = oldSize + string.size();
- reserve(optimalCapacity(newSize));
+ if (fitsNotInCapacity(newSize))
+ reserve(optimalCapacity(newSize));
+
std::char_traits<char>::copy(std::next(data(), static_cast<std::ptrdiff_t>(oldSize)),
string.data(),
string.size());
setSize(newSize);
}
+ void append(char character) noexcept
+ {
+ size_type oldSize = size();
+ size_type newSize = oldSize + 1;
+
+ if (fitsNotInCapacity(newSize))
+ reserve(optimalCapacity(newSize));
+
+ auto current = std::next(data(), static_cast<std::ptrdiff_t>(oldSize));
+ *current = character;
+ setSize(newSize);
+ }
+
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ void append(Type number)
+ {
+#if defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L)
+ // 2 bytes for the sign and because digits10 returns the floor
+ char buffer[std::numeric_limits<Type>::digits10 + 2];
+ auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
+ auto endOfConversionString = result.ptr;
+
+ append({buffer, endOfConversionString});
+#else
+ if constexpr (std::is_floating_point_v<Type>) {
+ QLocale locale{QLocale::Language::C};
+ append(locale.toString(number));
+ return;
+ } else {
+ // 2 bytes for the sign and because digits10 returns the floor
+ char buffer[std::numeric_limits<Type>::digits10 + 2];
+ auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
+ auto endOfConversionString = result.ptr;
+
+ append({buffer, endOfConversionString});
+ }
+
+#endif
+ }
+
void append(QStringView string) noexcept
{
QStringEncoder encoder{QStringEncoder::Utf8};
@@ -471,6 +519,13 @@ public:
return *this;
}
+ BasicSmallString &operator+=(char character) noexcept
+ {
+ append(character);
+
+ return *this;
+ }
+
BasicSmallString &operator+=(QStringView string) noexcept
{
append(string);
@@ -478,6 +533,14 @@ public:
return *this;
}
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ BasicSmallString &operator+=(Type number) noexcept
+ {
+ append(number);
+
+ return *this;
+ }
+
BasicSmallString &operator+=(std::initializer_list<SmallStringView> list) noexcept
{
appendInitializerList(list, size());
@@ -548,52 +611,46 @@ public:
return m_data.control.shortStringSize();
}
- static BasicSmallString join(std::initializer_list<SmallStringView> list) noexcept
+ static BasicSmallString join(std::initializer_list<SmallStringView> list,
+ Utils::SmallStringView separator) noexcept
{
size_type totalSize = 0;
for (SmallStringView string : list)
- totalSize += string.size();
+ totalSize += string.size() + separator.size();
BasicSmallString joinedString;
joinedString.reserve(totalSize);
- for (SmallStringView string : list)
- joinedString.append(string);
+ for (auto it = list.begin(); it != list.end(); ++it) {
+ joinedString.append(*it);
+ if (std::next(it) != list.end())
+ joinedString.append(separator);
+ }
return joinedString;
}
- static
- BasicSmallString number(int number)
+ static BasicSmallString join(std::initializer_list<SmallStringView> list) noexcept
{
- // 2 bytes for the sign and because digits10 returns the floor
- char buffer[std::numeric_limits<int>::digits10 + 2];
- auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
- auto endOfConversionString = result.ptr;
- return BasicSmallString(buffer, endOfConversionString);
- }
+ size_type totalSize = 0;
+ for (SmallStringView string : list)
+ totalSize += string.size();
- static BasicSmallString number(long long int number) noexcept
- {
- // 2 bytes for the sign and because digits10 returns the floor
- char buffer[std::numeric_limits<long long int>::digits10 + 2];
- auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
- auto endOfConversionString = result.ptr;
- return BasicSmallString(buffer, endOfConversionString);
+ BasicSmallString joinedString;
+ joinedString.reserve(totalSize);
+
+ for (SmallStringView string : list)
+ joinedString.append(string);
+
+ return joinedString;
}
- static BasicSmallString number(double number) noexcept
+ template<typename Type, typename std::enable_if_t<std::is_arithmetic_v<Type>, bool> = true>
+ static BasicSmallString number(Type number)
{
-#if defined(__cpp_lib_to_chars) && (__cpp_lib_to_chars >= 201611L)
- // 2 bytes for the sign and because digits10 returns the floor
- char buffer[std::numeric_limits<double>::digits10 + 2];
- auto result = std::to_chars(buffer, buffer + sizeof(buffer), number);
- auto endOfConversionString = result.ptr;
- return BasicSmallString(buffer, endOfConversionString);
-#else
- QLocale locale{QLocale::Language::C};
- return BasicSmallString{locale.toString(number)};
-#endif
+ BasicSmallString string;
+ string.append(number);
+ return string;
}
char &operator[](std::size_t index) noexcept { return *(data() + index); }
@@ -638,7 +695,6 @@ public:
friend BasicSmallString operator+(const BasicSmallString &first,
const char (&second)[ArraySize]) noexcept
{
-
return operator+(first, SmallStringView(second));
}
@@ -670,8 +726,10 @@ unittest_public:
bool fitsNotInCapacity(size_type capacity) const noexcept
{
- return (isShortString() && capacity > shortStringCapacity())
- || (!isShortString() && capacity > m_data.reference.capacity);
+ if (isShortString())
+ return capacity > shortStringCapacity();
+
+ return capacity > m_data.reference.capacity;
}
static size_type optimalHeapCapacity(const size_type size) noexcept
diff --git a/src/libs/utils/smallstringview.h b/src/libs/utils/smallstringview.h
index 5a27217819..28f8b78150 100644
--- a/src/libs/utils/smallstringview.h
+++ b/src/libs/utils/smallstringview.h
@@ -79,12 +79,10 @@ public:
explicit operator QByteArray() const { return QByteArray(data(), int(size())); }
-#if QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)
explicit operator QLatin1StringView() const noexcept
{
return QLatin1StringView(data(), Utils::ssize(*this));
}
-#endif
operator QUtf8StringView() const noexcept
{
diff --git a/src/libs/utils/span.h b/src/libs/utils/span.h
index b1fba81447..d21bb9ab00 100644
--- a/src/libs/utils/span.h
+++ b/src/libs/utils/span.h
@@ -5,7 +5,9 @@
#include <qcompilerdetection.h>
-#if __cplusplus >= 202002L
+// The (Apple) Clang implementation of span is incomplete until LLVM 15 / Xcode 14.3 / macOS 13
+#if __cplusplus >= 202002L \
+ && !(defined(__apple_build_version__) && __apple_build_version__ < 14030022)
#include <span>
namespace Utils {
@@ -22,6 +24,11 @@ QT_WARNING_PUSH
#elif defined(Q_CC_GNU) || defined(Q_CC_CLANG)
#pragma GCC system_header
#endif
+
+// disable automatic usage of std::span in span-lite
+// since we make that decision ourselves at the top of this header
+#define span_CONFIG_SELECT_SPAN span_SPAN_NONSTD
+
#include <3rdparty/span/span.hpp>
namespace Utils {
using namespace nonstd;
diff --git a/src/libs/utils/store.cpp b/src/libs/utils/store.cpp
index fa8d4232c0..8c0e2a6046 100644
--- a/src/libs/utils/store.cpp
+++ b/src/libs/utils/store.cpp
@@ -50,10 +50,10 @@ static QVariantList mapListFromStoreList(const QVariantList &storeList);
QVariant storeEntryFromMapEntry(const QVariant &mapEntry)
{
- if (mapEntry.type() == QVariant::Map)
+ if (mapEntry.typeId() == QMetaType::QVariantMap)
return QVariant::fromValue(storeFromMap(mapEntry.toMap()));
- if (mapEntry.type() == QVariant::List)
+ if (mapEntry.typeId() == QMetaType::QVariantList)
return QVariant::fromValue(storeListFromMapList(mapEntry.toList()));
return mapEntry;
@@ -64,7 +64,7 @@ QVariant mapEntryFromStoreEntry(const QVariant &storeEntry)
if (storeEntry.metaType() == QMetaType::fromType<Store>())
return QVariant::fromValue(mapFromStore(storeEntry.value<Store>()));
- if (storeEntry.type() == QVariant::List)
+ if (storeEntry.typeId() == QMetaType::QVariantList)
return QVariant::fromValue(mapListFromStoreList(storeEntry.toList()));
return storeEntry;
diff --git a/src/libs/utils/stringutils.cpp b/src/libs/utils/stringutils.cpp
index d8de512b32..d21c7cb9bc 100644
--- a/src/libs/utils/stringutils.cpp
+++ b/src/libs/utils/stringutils.cpp
@@ -637,7 +637,7 @@ void MarkdownHighlighter::highlightBlock(const QString &text)
image.fill(QColor(0, 0, 0, 0).rgba());
image.setPixel(0,
height - 1,
- Utils::creatorTheme()->color(Theme::TextColorDisabled).rgba());
+ Utils::creatorColor(Theme::TextColorDisabled).rgba());
h2Brush = QBrush(image);
}
@@ -672,4 +672,10 @@ void MarkdownHighlighter::highlightBlock(const QString &text)
}
}
+QString ansiColoredText(const QString &text, const QColor &color)
+{
+ static const QString formatString("\033[38;2;%1;%2;%3m%4\033[0m");
+ return formatString.arg(color.red()).arg(color.green()).arg(color.blue()).arg(text);
+}
+
} // namespace Utils
diff --git a/src/libs/utils/stringutils.h b/src/libs/utils/stringutils.h
index 3e729e672c..cab6d774f3 100644
--- a/src/libs/utils/stringutils.h
+++ b/src/libs/utils/stringutils.h
@@ -138,4 +138,6 @@ private:
QBrush m_codeBgBrush;
};
+QTCREATOR_UTILS_EXPORT QString ansiColoredText(const QString &text, const QColor &color);
+
} // namespace Utils
diff --git a/src/libs/utils/stylehelper.cpp b/src/libs/utils/stylehelper.cpp
index 69b5e39556..080bb8a382 100644
--- a/src/libs/utils/stylehelper.cpp
+++ b/src/libs/utils/stylehelper.cpp
@@ -107,7 +107,7 @@ QColor StyleHelper::notTooBrightHighlightColor()
QPalette StyleHelper::sidebarFontPalette(const QPalette &original)
{
QPalette palette = original;
- const QColor textColor = creatorTheme()->color(Theme::ProgressBarTitleColor);
+ const QColor textColor = creatorColor(Theme::ProgressBarTitleColor);
palette.setColor(QPalette::WindowText, textColor);
palette.setColor(QPalette::Text, textColor);
return palette;
@@ -137,7 +137,7 @@ QColor StyleHelper::requestedBaseColor()
QColor StyleHelper::toolbarBaseColor(bool lightColored)
{
if (creatorTheme()->flag(Theme::QDSTheme))
- return creatorTheme()->color(Utils::Theme::DStoolbarBackground);
+ return creatorColor(Utils::Theme::DStoolbarBackground);
else
return StyleHelper::baseColor(lightColored);
}
@@ -194,7 +194,7 @@ void StyleHelper::setBaseColor(const QColor &newcolor)
{
s_requestedBaseColor = newcolor;
- const QColor themeBaseColor = creatorTheme()->color(Theme::PanelStatusBarBackgroundColor);
+ const QColor themeBaseColor = creatorColor(Theme::PanelStatusBarBackgroundColor);
const QColor defaultBaseColor = QColor(DEFAULT_BASE_COLOR);
QColor color;
@@ -366,11 +366,11 @@ void StyleHelper::drawArrow(QStyle::PrimitiveElement element, QPainter *painter,
};
if (!enabled) {
- drawCommonStyleArrow(image.rect(), creatorTheme()->color(Theme::IconsDisabledColor));
+ drawCommonStyleArrow(image.rect(), creatorColor(Theme::IconsDisabledColor));
} else {
if (creatorTheme()->flag(Theme::ToolBarIconShadow))
drawCommonStyleArrow(image.rect().translated(0, devicePixelRatio), toolBarDropShadowColor());
- drawCommonStyleArrow(image.rect(), creatorTheme()->color(Theme::IconsBaseColor));
+ drawCommonStyleArrow(image.rect(), creatorColor(Theme::IconsBaseColor));
}
painter.end();
pixmap = QPixmap::fromImage(image);
@@ -463,9 +463,9 @@ void StyleHelper::drawMinimalArrow(QStyle::PrimitiveElement element, QPainter *p
if (enabled) {
if (creatorTheme()->flag(Theme::ToolBarIconShadow))
drawArrow(image.rect().translated(0, devicePixelRatio), toolBarDropShadowColor());
- drawArrow(image.rect(), creatorTheme()->color(Theme::IconsBaseColor));
+ drawArrow(image.rect(), creatorColor(Theme::IconsBaseColor));
} else {
- drawArrow(image.rect(), creatorTheme()->color(Theme::IconsDisabledColor));
+ drawArrow(image.rect(), creatorColor(Theme::IconsDisabledColor));
}
painter.end();
pixmap = QPixmap::fromImage(image);
@@ -551,8 +551,7 @@ void StyleHelper::drawIconWithShadow(const QIcon &icon, const QRect &rect,
// return a high-dpi pixmap, which will in that case have a devicePixelRatio
// different than 1. The shadow drawing caluculations are done in device
// pixels.
- QWindow *window = dynamic_cast<QWidget*>(p->device())->window()->windowHandle();
- QPixmap px = icon.pixmap(window, rect.size(), iconMode);
+ QPixmap px = icon.pixmap(rect.size(), devicePixelRatio, iconMode);
int radius = int(dipRadius * devicePixelRatio);
QPoint offset = dipOffset * devicePixelRatio;
cache = QPixmap(px.size() + QSize(radius * 2, radius * 2));
@@ -563,7 +562,7 @@ void StyleHelper::drawIconWithShadow(const QIcon &icon, const QRect &rect,
const bool hasDisabledState =
icon.availableSizes().count() == icon.availableSizes(QIcon::Disabled).count();
if (!hasDisabledState)
- px = disabledSideBarIcon(icon.pixmap(window, rect.size()));
+ px = disabledSideBarIcon(icon.pixmap(rect.size(), devicePixelRatio));
} else if (creatorTheme()->flag(Theme::ToolBarIconShadow)) {
// Draw shadow
QImage tmp(px.size() + QSize(radius * 2, radius * 2 + 1), QImage::Format_ARGB32_Premultiplied);
@@ -716,12 +715,7 @@ Qt::HighDpiScaleFactorRoundingPolicy StyleHelper::defaultHighDpiScaleFactorRound
QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QList<IconFontHelper> &parameters)
{
- QFontDatabase a;
-
- QTC_ASSERT(a.hasFamily(fontName), {});
-
- if (!a.hasFamily(fontName))
- return {};
+ QTC_ASSERT(QFontDatabase::hasFamily(fontName), {});
QIcon icon;
@@ -751,38 +745,31 @@ QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QList<Icon
QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize, QColor color)
{
- QFontDatabase a;
-
- QTC_ASSERT(a.hasFamily(fontName), {});
-
- if (a.hasFamily(fontName)) {
-
- QIcon icon;
- QSize size(iconSize, iconSize);
+ QTC_ASSERT(QFontDatabase::hasFamily(fontName), {});
- const int maxDpr = qRound(qApp->devicePixelRatio());
- for (int dpr = 1; dpr <= maxDpr; dpr++) {
- QPixmap pixmap(size * dpr);
- pixmap.setDevicePixelRatio(dpr);
- pixmap.fill(Qt::transparent);
+ QIcon icon;
+ QSize size(iconSize, iconSize);
- QFont font(fontName);
- font.setPixelSize(fontSize);
+ const int maxDpr = qRound(qApp->devicePixelRatio());
+ for (int dpr = 1; dpr <= maxDpr; dpr++) {
+ QPixmap pixmap(size * dpr);
+ pixmap.setDevicePixelRatio(dpr);
+ pixmap.fill(Qt::transparent);
- QPainter painter(&pixmap);
- painter.save();
- painter.setPen(color);
- painter.setFont(font);
- painter.drawText(QRectF(QPoint(0, 0), size), Qt::AlignCenter, iconSymbol);
- painter.restore();
+ QFont font(fontName);
+ font.setPixelSize(fontSize);
- icon.addPixmap(pixmap);
- }
+ QPainter painter(&pixmap);
+ painter.save();
+ painter.setPen(color);
+ painter.setFont(font);
+ painter.drawText(QRectF(QPoint(0, 0), size), Qt::AlignCenter, iconSymbol);
+ painter.restore();
- return icon;
+ icon.addPixmap(pixmap);
}
- return {};
+ return icon;
}
QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QString &iconSymbol, int fontSize, int iconSize)
@@ -794,55 +781,47 @@ QIcon StyleHelper::getIconFromIconFont(const QString &fontName, const QString &i
QIcon StyleHelper::getCursorFromIconFont(const QString &fontName, const QString &cursorFill, const QString &cursorOutline,
int fontSize, int iconSize)
{
- QFontDatabase a;
-
- QTC_ASSERT(a.hasFamily(fontName), {});
+ QTC_ASSERT(QFontDatabase::hasFamily(fontName), {});
const QColor outlineColor = Qt::black;
const QColor fillColor = Qt::white;
- if (a.hasFamily(fontName)) {
-
- QIcon icon;
- QSize size(iconSize, iconSize);
-
- const int maxDpr = qRound(qApp->devicePixelRatio());
- for (int dpr = 1; dpr <= maxDpr; dpr++) {
- QPixmap pixmap(size * dpr);
- pixmap.setDevicePixelRatio(dpr);
- pixmap.fill(Qt::transparent);
+ QIcon icon;
+ QSize size(iconSize, iconSize);
- QFont font(fontName);
- font.setPixelSize(fontSize);
+ const int maxDpr = qRound(qApp->devicePixelRatio());
+ for (int dpr = 1; dpr <= maxDpr; dpr++) {
+ QPixmap pixmap(size * dpr);
+ pixmap.setDevicePixelRatio(dpr);
+ pixmap.fill(Qt::transparent);
- QPainter painter(&pixmap);
- painter.save();
- painter.setRenderHint(QPainter::Antialiasing, true);
- painter.setRenderHint(QPainter::TextAntialiasing, true);
- painter.setRenderHint(QPainter::LosslessImageRendering, true);
- painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
+ QFont font(fontName);
+ font.setPixelSize(fontSize);
- painter.setFont(font);
- painter.setPen(outlineColor);
- painter.drawText(QRectF(QPointF(0.0, 0.0), size),
- Qt::AlignCenter, cursorOutline);
+ QPainter painter(&pixmap);
+ painter.save();
+ painter.setRenderHint(QPainter::Antialiasing, true);
+ painter.setRenderHint(QPainter::TextAntialiasing, true);
+ painter.setRenderHint(QPainter::LosslessImageRendering, true);
+ painter.setRenderHint(QPainter::SmoothPixmapTransform, true);
- painter.setPen(fillColor);
- painter.drawText(QRectF(QPointF(0.0, 0.0), size),
- Qt::AlignCenter, cursorFill);
+ painter.setFont(font);
+ painter.setPen(outlineColor);
+ painter.drawText(QRectF(QPointF(0.0, 0.0), size),
+ Qt::AlignCenter, cursorOutline);
- painter.restore();
+ painter.setPen(fillColor);
+ painter.drawText(QRectF(QPointF(0.0, 0.0), size),
+ Qt::AlignCenter, cursorFill);
- icon.addPixmap(pixmap);
- }
+ painter.restore();
- return icon;
+ icon.addPixmap(pixmap);
}
- return {};
+ return icon;
}
-
QString StyleHelper::dpiSpecificImageFile(const QString &fileName)
{
// See QIcon::addFile()
@@ -970,6 +949,8 @@ static const UiFontMetrics& uiFontMetrics(StyleHelper::UiElement element)
{StyleHelper::UiElementBody2, {12, 20, QFont::Light}},
{StyleHelper::UiElementButtonMedium, {12, 16, QFont::Bold}},
{StyleHelper::UiElementButtonSmall, {10, 12, QFont::Bold}},
+ {StyleHelper::UiElementLabelMedium, {12, 16, QFont::DemiBold}},
+ {StyleHelper::UiElementLabelSmall, {10, 12, QFont::DemiBold}},
{StyleHelper::UiElementCaptionStrong, {10, 12, QFont::DemiBold}},
{StyleHelper::UiElementCaption, {10, 12, QFont::Normal}},
{StyleHelper::UiElementIconStandard, {12, 16, QFont::Medium}},
@@ -1010,7 +991,13 @@ QFont StyleHelper::uiFont(UiElement element)
const qreal qrealPointSize = metrics.pixelSize * pixelsToPointSizeFactor;
font.setPointSizeF(qrealPointSize);
- font.setWeight(metrics.weight);
+ // Intermediate font weights can produce blurry rendering and are harder to read.
+ // For "non-retina" screens, apply the weight only for some fonts.
+ static const bool isHighDpi = qApp->devicePixelRatio() >= 2;
+ const bool setWeight = isHighDpi || element == UiElementCaptionStrong
+ || element <= UiElementH4;
+ if (setWeight)
+ font.setWeight(metrics.weight);
return font;
}
diff --git a/src/libs/utils/stylehelper.h b/src/libs/utils/stylehelper.h
index 7d434925e6..ecc2eea2b9 100644
--- a/src/libs/utils/stylehelper.h
+++ b/src/libs/utils/stylehelper.h
@@ -79,6 +79,8 @@ enum ToolbarStyle {
};
constexpr ToolbarStyle defaultToolbarStyle = ToolbarStyleCompact;
+// Keep in sync with:
+// SyleHelper::uiFontMetrics, ICore::uiConfigInformation, tst_manual_widgets_uifonts::main
enum UiElement {
UiElementH1,
UiElementH2,
@@ -91,6 +93,8 @@ enum UiElement {
UiElementBody2,
UiElementButtonMedium,
UiElementButtonSmall,
+ UiElementLabelMedium,
+ UiElementLabelSmall,
UiElementCaptionStrong,
UiElementCaption,
UiElementIconStandard,
diff --git a/src/libs/utils/terminalhooks.cpp b/src/libs/utils/terminalhooks.cpp
index 52bcc05629..3e5151e6f3 100644
--- a/src/libs/utils/terminalhooks.cpp
+++ b/src/libs/utils/terminalhooks.cpp
@@ -5,7 +5,7 @@
#include "externalterminalprocessimpl.h"
#include "filepath.h"
-#include "process.h"
+#include "qtcprocess.h"
#include "utilstr.h"
#include <QMutex>
diff --git a/src/libs/utils/terminalinterface.cpp b/src/libs/utils/terminalinterface.cpp
index 0d5a3be020..6f7bb11f81 100644
--- a/src/libs/utils/terminalinterface.cpp
+++ b/src/libs/utils/terminalinterface.cpp
@@ -334,22 +334,12 @@ void TerminalInterface::start()
Environment finalEnv = m_setup.m_environment;
- if (HostOsInfo::isWindowsHost()) {
- if (!finalEnv.hasKey("PATH")) {
- const QString path = qtcEnvironmentVariable("PATH");
- if (!path.isEmpty())
- finalEnv.set("PATH", path);
- }
- if (!finalEnv.hasKey("SystemRoot")) {
- const QString systemRoot = qtcEnvironmentVariable("SystemRoot");
- if (!systemRoot.isEmpty())
- finalEnv.set("SystemRoot", systemRoot);
- }
- } else if (HostOsInfo::isMacHost()) {
+ if (HostOsInfo::isMacHost())
finalEnv.set("TERM", "xterm-256color");
- }
if (finalEnv.hasChanges()) {
+ finalEnv = finalEnv.appliedToEnvironment(Environment::systemEnvironment());
+
d->envListFile = std::make_unique<QTemporaryFile>(this);
if (!d->envListFile->open()) {
cleanupAfterStartFailure(msgCannotCreateTempFile(d->envListFile->errorString()));
@@ -402,7 +392,7 @@ void TerminalInterface::start()
m_setup.m_commandLine.executable().fileName());
if (m_setup.m_runAsRoot && !HostOsInfo::isWindowsHost()) {
- CommandLine rootCommand("sudo", {});
+ CommandLine rootCommand("sudo");
rootCommand.addCommandLineAsArgs(cmd);
stubSetupData.m_commandLine = rootCommand;
} else {
diff --git a/src/libs/utils/theme/theme.cpp b/src/libs/utils/theme/theme.cpp
index 9b24078f86..ef20ddddb7 100644
--- a/src/libs/utils/theme/theme.cpp
+++ b/src/libs/utils/theme/theme.cpp
@@ -18,6 +18,7 @@
namespace Utils {
static Theme *m_creatorTheme = nullptr;
+static std::optional<QPalette> m_initialPalette;
ThemePrivate::ThemePrivate()
{
@@ -37,9 +38,31 @@ Theme *proxyTheme()
return new Theme(m_creatorTheme);
}
+// Convenience
+QColor creatorColor(Theme::Color role)
+{
+ return m_creatorTheme->color(role);
+}
+
+static bool paletteIsDark(const QPalette &pal)
+{
+ return pal.color(QPalette::Window).lightnessF() < pal.color(QPalette::WindowText).lightnessF();
+}
+
+static bool isOverridingPalette(const Theme *theme)
+{
+ if (theme->flag(Theme::DerivePaletteFromTheme))
+ return true;
+ if (theme->flag(Theme::DerivePaletteFromThemeIfNeeded)
+ && paletteIsDark(Theme::initialPalette()) != theme->flag(Theme::DarkUserInterface)) {
+ return true;
+ }
+ return false;
+}
+
void setThemeApplicationPalette()
{
- if (m_creatorTheme && m_creatorTheme->flag(Theme::ApplyThemePaletteGlobally))
+ if (m_creatorTheme && isOverridingPalette(m_creatorTheme))
QApplication::setPalette(m_creatorTheme->palette());
}
@@ -185,7 +208,7 @@ void Theme::setDisplayName(const QString &name)
void Theme::readSettingsInternal(QSettings &settings)
{
- const QStringList includes = settings.value("Includes").toString().split(",", Qt::SkipEmptyParts);
+ const QStringList includes = settings.value("Includes").toStringList();
for (const QString &include : includes) {
FilePath path = FilePath::fromString(d->fileName);
@@ -311,7 +334,13 @@ bool Theme::systemUsesDarkMode()
if (HostOsInfo::isMacHost())
return macOSSystemIsDark();
- return false;
+ // Avoid enforcing the initial palette.
+ // The initial palette must be set after setting the macOS appearance in setInitialPalette,
+ // but systemUsesDarkMode is used to determine the default theme, which is in turn required
+ // for the setInitialPalette call
+ if (m_initialPalette)
+ return paletteIsDark(*m_initialPalette);
+ return paletteIsDark(QApplication::palette());
}
// If you copy QPalette, default values stay at default, even if that default is different
@@ -347,14 +376,17 @@ void Theme::setHelpMenu(QMenu *menu)
QPalette Theme::initialPalette()
{
- static QPalette palette = copyPalette(QApplication::palette());
- return palette;
+ if (!m_initialPalette) {
+ m_initialPalette = copyPalette(QApplication::palette());
+ QApplication::setPalette(*m_initialPalette);
+ }
+ return *m_initialPalette;
}
QPalette Theme::palette() const
{
QPalette pal = initialPalette();
- if (!flag(DerivePaletteFromTheme))
+ if (!isOverridingPalette(this))
return pal;
const static struct {
diff --git a/src/libs/utils/theme/theme.h b/src/libs/utils/theme/theme.h
index 6b3bf97d45..8d7e922e79 100644
--- a/src/libs/utils/theme/theme.h
+++ b/src/libs/utils/theme/theme.h
@@ -247,6 +247,10 @@ public:
Token_Notification_Success,
Token_Notification_Neutral,
Token_Notification_Danger,
+ Token_Gradient01_Start,
+ Token_Gradient01_End,
+ Token_Gradient02_Start,
+ Token_Gradient02_End,
/* Timeline Library */
Timeline_TextColor,
@@ -527,7 +531,7 @@ public:
DrawToolBarBorders,
ComboBoxDrawTextShadow,
DerivePaletteFromTheme,
- ApplyThemePaletteGlobally,
+ DerivePaletteFromThemeIfNeeded,
FlatToolBars,
FlatSideBarIcons,
FlatProjectsMode,
@@ -577,5 +581,6 @@ private:
QTCREATOR_UTILS_EXPORT Theme *creatorTheme();
QTCREATOR_UTILS_EXPORT Theme *proxyTheme();
+QTCREATOR_UTILS_EXPORT QColor creatorColor(Theme::Color role);
} // namespace Utils
diff --git a/src/libs/utils/threadutils.cpp b/src/libs/utils/threadutils.cpp
index 344779dc93..cfb18059a6 100644
--- a/src/libs/utils/threadutils.cpp
+++ b/src/libs/utils/threadutils.cpp
@@ -10,7 +10,12 @@ namespace Utils {
bool isMainThread()
{
+#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0)
+ // TODO: Remove threadutils.h and replace all usages with:
+ return QThread::isMainThread();
+#else
return QThread::currentThread() == qApp->thread();
+#endif
}
} // namespace Utils
diff --git a/src/libs/utils/treemodel.cpp b/src/libs/utils/treemodel.cpp
index 4649bb43c8..edddb2d484 100644
--- a/src/libs/utils/treemodel.cpp
+++ b/src/libs/utils/treemodel.cpp
@@ -459,13 +459,13 @@ void ModelTest::data()
// General Purpose roles that should return a QString
QVariant variant = model->data(model->index(0, 0), Qt::ToolTipRole);
if (variant.isValid())
- Q_ASSERT(variant.canConvert(QVariant::String));
+ Q_ASSERT(variant.canConvert(QMetaType::QString));
variant = model->data(model->index(0, 0), Qt::StatusTipRole);
if (variant.isValid())
- Q_ASSERT(variant.canConvert(QVariant::String));
+ Q_ASSERT(variant.canConvert(QMetaType::QString));
variant = model->data(model->index(0, 0), Qt::WhatsThisRole);
if (variant.isValid())
- Q_ASSERT(variant.canConvert(QVariant::String));
+ Q_ASSERT(variant.canConvert(QMetaType::QString));
// General Purpose roles that should return a QSize
variant = model->data(model->index(0, 0), Qt::SizeHintRole);
diff --git a/src/libs/utils/unarchiver.cpp b/src/libs/utils/unarchiver.cpp
index 8436565425..d9092e4ca9 100644
--- a/src/libs/utils/unarchiver.cpp
+++ b/src/libs/utils/unarchiver.cpp
@@ -43,6 +43,7 @@ static const QList<Tool> &sTools()
{
static QList<Tool> tools;
if (tools.isEmpty()) {
+ // clang-format off
if (HostOsInfo::isWindowsHost()) {
tools << Tool{{"powershell", "-command Expand-Archive -Force '%{src}' '%{dest}'", CommandLine::Raw},
{"application/zip"},
@@ -74,6 +75,10 @@ static const QList<Tool> &sTools()
tools << Tool{{"cmake", {"-E", "tar", "xvjf", "%{src}"}},
{"application/x-bzip-compressed-tar"},
additionalCMakeDirs};
+ // Keep this at the end so its only used as last resort. Otherwise it might be used for
+ // .tar.gz files.
+ tools << Tool{{"gzip", {"-d", "%{src}", "-c"}}, {"application/gzip"}, {}};
+ // clang-format on
}
return tools;
}
@@ -147,6 +152,45 @@ void Unarchiver::start()
m_sourceAndCommand->m_sourceFile, m_destDir);
m_destDir.ensureWritableDir();
+ if (command.executable().fileName() == "gzip") {
+ std::shared_ptr<QFile> outputFile = std::make_shared<QFile>(
+ (m_destDir / m_gzipFileDestName).toFSPathString());
+
+ if (!outputFile->open(QIODevice::WriteOnly)) {
+ emit outputReceived(Tr::tr("Failed to open output file."));
+ emit done(DoneResult::Error);
+ return;
+ }
+
+ m_process.reset(new Process);
+ QObject::connect(m_process.get(), &Process::readyReadStandardOutput, this, [this, outputFile] {
+ const QByteArray data = m_process->readAllRawStandardOutput();
+ if (outputFile->write(data) != data.size()) {
+ emit outputReceived(Tr::tr("Failed to write output file."));
+ emit done(DoneResult::Error);
+ }
+ });
+ QObject::connect(m_process.get(), &Process::readyReadStandardError, this, [this] {
+ emit outputReceived(m_process->readAllStandardError());
+ });
+ QObject::connect(m_process.get(), &Process::done, this, [outputFile, this] {
+ outputFile->close();
+ const bool success = m_process->result() == ProcessResult::FinishedWithSuccess;
+ if (!success) {
+ outputFile->remove();
+ emit outputReceived(Tr::tr("Command failed."));
+ }
+ emit done(toDoneResult(success));
+ });
+ emit outputReceived(
+ Tr::tr("Running %1\nin \"%2\".\n\n", "Running <cmd> in <workingdirectory>")
+ .arg(command.toUserOutput(), m_destDir.toUserOutput()));
+ m_process->setCommand(command);
+ m_process->setWorkingDirectory(m_destDir);
+ m_process->start();
+ return;
+ }
+
m_process.reset(new Process);
m_process->setProcessChannelMode(QProcess::MergedChannels);
QObject::connect(m_process.get(), &Process::readyReadStandardOutput, this, [this] {
diff --git a/src/libs/utils/unarchiver.h b/src/libs/utils/unarchiver.h
index b255cd4990..23fcad07a8 100644
--- a/src/libs/utils/unarchiver.h
+++ b/src/libs/utils/unarchiver.h
@@ -6,7 +6,7 @@
#include "utils_global.h"
#include "commandline.h"
-#include "process.h"
+#include "qtcprocess.h"
#include <solutions/tasking/tasktree.h>
@@ -32,7 +32,10 @@ public:
void setSourceAndCommand(const SourceAndCommand &data) { m_sourceAndCommand = data; }
void setDestDir(const FilePath &destDir) { m_destDir = destDir; }
-
+ void setGZipFileDestName(const QString &gzipFileDestName)
+ {
+ m_gzipFileDestName = gzipFileDestName;
+ }
void start();
signals:
@@ -43,6 +46,7 @@ private:
std::optional<SourceAndCommand> m_sourceAndCommand;
FilePath m_destDir;
std::unique_ptr<Process> m_process;
+ QString m_gzipFileDestName;
};
class QTCREATOR_UTILS_EXPORT UnarchiverTaskAdapter : public Tasking::TaskAdapter<Unarchiver>
diff --git a/src/libs/utils/utility.h b/src/libs/utils/utility.h
new file mode 100644
index 0000000000..27de88d339
--- /dev/null
+++ b/src/libs/utils/utility.h
@@ -0,0 +1,14 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+namespace Utils {
+
+template<typename Enumeration>
+[[nodiscard]] constexpr std::underlying_type_t<Enumeration> to_underlying(Enumeration enumeration) noexcept
+{
+ return static_cast<std::underlying_type_t<Enumeration>>(enumeration);
+}
+
+} // namespace Utils
diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs
index 20e3a707bc..afc0f202aa 100644
--- a/src/libs/utils/utils.qbs
+++ b/src/libs/utils/utils.qbs
@@ -77,6 +77,7 @@ QtcLibrary {
"cpplanguage_details.h",
"crumblepath.cpp",
"crumblepath.h",
+ "datafromprocess.h",
"delegates.cpp",
"delegates.h",
"detailsbutton.cpp",
@@ -184,6 +185,8 @@ QtcLibrary {
"link.h",
"listmodel.h",
"listutils.h",
+ "lua.cpp",
+ "lua.h",
"macroexpander.cpp",
"macroexpander.h",
"mathutils.cpp",
@@ -230,8 +233,8 @@ QtcLibrary {
"portlist.cpp",
"portlist.h",
"predicates.h",
- "process.cpp",
- "process.h",
+ "qtcprocess.cpp",
+ "qtcprocess.h",
"processenums.h",
"processhandle.cpp",
"processhandle.h",
@@ -257,6 +260,7 @@ QtcLibrary {
"qtcolorbutton.h",
"qtcsettings.cpp",
"qtcsettings.h",
+ "ranges.h",
"reloadpromptutils.cpp",
"reloadpromptutils.h",
"removefiledialog.cpp",
@@ -372,8 +376,7 @@ QtcLibrary {
"fsenginehandler.cpp",
"fsenginehandler.h",
"fsengine_impl.cpp",
- "fsengine_impl.h",
- "rootinjectfsengine.h",
+ "fsengine_impl.h"
]
}
diff --git a/src/libs/utils/wizard.cpp b/src/libs/utils/wizard.cpp
index 10d801c521..29bfb787a6 100644
--- a/src/libs/utils/wizard.cpp
+++ b/src/libs/utils/wizard.cpp
@@ -45,7 +45,9 @@ public:
m_indicatorPixmap(indicatorPixmap)
{
m_indicatorLabel = new QLabel(this);
- m_indicatorLabel->setFixedSize(m_indicatorPixmap.size());
+ const QSizeF indicatorSize = m_indicatorPixmap.deviceIndependentSize();
+ m_indicatorLabel->setFixedSize(
+ {qCeil(indicatorSize.width()), qCeil(indicatorSize.height())});
m_titleLabel = new QLabel(title, this);
auto l = new QHBoxLayout(this);
l->setContentsMargins(0, 0, 0, 0);
@@ -276,6 +278,7 @@ public:
bool m_automaticProgressCreation = true;
WizardProgress *m_wizardProgress = nullptr;
QSet<QString> m_fieldNames;
+ bool m_skipForSubproject = false;
};
Wizard::Wizard(QWidget *parent, Qt::WindowFlags flags) :
@@ -365,8 +368,8 @@ QHash<QString, QVariant> Wizard::variables() const
QString typeOf(const QVariant &v)
{
QString result;
- switch (v.type()) {
- case QVariant::Map:
+ switch (v.typeId()) {
+ case QMetaType::QVariantMap:
result = QLatin1String("Object");
break;
default:
@@ -525,6 +528,32 @@ void Wizard::_q_pageRemoved(int pageId)
d->m_wizardProgress->removeItem(item);
}
+void Wizard::setSkipForSubprojects(bool skip)
+{
+ Q_D(Wizard);
+ d->m_skipForSubproject = skip;
+}
+
+int Wizard::nextId() const
+{
+ Q_D(const Wizard);
+ if (!d->m_skipForSubproject)
+ return QWizard::nextId();
+
+ const QList<int> allIds = pageIds();
+ int index = allIds.indexOf(currentId());
+ QTC_ASSERT(index > -1, return QWizard::nextId());
+
+ while (++index < allIds.size()) {
+ if (auto wp = qobject_cast<WizardPage *>(page(index))) {
+ if (!wp->skipForSubprojects())
+ return index;
+ }
+ }
+ QTC_CHECK(false); // should not happen
+ return QWizard::nextId();
+}
+
class WizardProgressPrivate
{
WizardProgress *q_ptr;
diff --git a/src/libs/utils/wizard.h b/src/libs/utils/wizard.h
index 35abff2f6d..ac3ad795bf 100644
--- a/src/libs/utils/wizard.h
+++ b/src/libs/utils/wizard.h
@@ -50,6 +50,10 @@ public:
void showVariables();
+ // allows to skip pages
+ void setSkipForSubprojects(bool skip);
+ int nextId() const override;
+
protected:
virtual QString stringify(const QVariant &v) const;
virtual QString evaluate(const QVariant &v) const;
diff --git a/src/libs/utils/wizardpage.h b/src/libs/utils/wizardpage.h
index 76d3039a67..1ab23f92b5 100644
--- a/src/libs/utils/wizardpage.h
+++ b/src/libs/utils/wizardpage.h
@@ -74,6 +74,9 @@ public:
void registerFieldWithName(const QString &name, QWidget *widget,
const char *property = nullptr, const char *changedSignal = nullptr);
+ void setSkipForSubprojects(bool skip) { m_skipForSubproject = skip; }
+ bool skipForSubprojects() const { return m_skipForSubproject; }
+
virtual bool handleReject();
virtual bool handleAccept();
@@ -85,6 +88,7 @@ private:
void registerFieldName(const QString &name);
QSet<QString> m_toRegister;
+ bool m_skipForSubproject = false;
};
} // namespace Utils